001: /*
002: * Copyright (C) 2004 NNL Technology AB
003: * Visit www.infonode.net for information about InfoNode(R)
004: * products and how to contact NNL Technology AB.
005: *
006: * This program is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU General Public License
008: * as published by the Free Software Foundation; either version 2
009: * of the License, or (at your option) any later version.
010: *
011: * This program is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014: * GNU General Public License for more details.
015: *
016: * You should have received a copy of the GNU General Public License
017: * along with this program; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
019: * MA 02111-1307, USA.
020: */
021:
022: // $Id: DraggableComponent.java,v 1.31 2005/12/04 13:46:03 jesper Exp $
023: package net.infonode.gui.draggable;
024:
025: import net.infonode.gui.ComponentUtil;
026: import net.infonode.gui.EventUtil;
027:
028: import javax.swing.*;
029: import javax.swing.event.MouseInputAdapter;
030: import javax.swing.event.MouseInputListener;
031: import java.awt.*;
032: import java.awt.event.ComponentAdapter;
033: import java.awt.event.ComponentEvent;
034: import java.awt.event.KeyEvent;
035: import java.awt.event.MouseEvent;
036: import java.util.ArrayList;
037:
038: public class DraggableComponent {
039: private static long MAX_EVENT_DELAY = 50;
040: private JComponent component;
041: private JComponent[] eventComponents;
042:
043: private boolean reorderEnabled = true;
044: private boolean enabled = true;
045: private boolean reorderRestoreOnDrag;
046: private boolean detectOuterAreaAsLine = true;
047: private boolean enableInsideDrag;
048: private boolean selectOnMousePress;
049:
050: private boolean mousePressed;
051: private boolean dragEventFired;
052: private boolean dragStarted;
053:
054: private boolean ignoreAddNotify = false;
055:
056: private int dragIndex;
057: private int dragFromIndex;
058: private int abortDragKeyCode = KeyEvent.VK_ESCAPE;
059:
060: private ArrayList layoutOrderList;
061:
062: private ArrayList listeners;
063: private JComponent outerParentArea;
064:
065: private KeyEventDispatcher abortDragKeyDispatcher = new KeyEventDispatcher() {
066: public boolean dispatchKeyEvent(KeyEvent e) {
067: if (mousePressed && e.getKeyCode() == abortDragKeyCode) {
068: if (e.getID() == KeyEvent.KEY_PRESSED)
069: dragCompleted(null);
070: return true;
071: }
072: return false;
073: }
074: };
075:
076: private MouseInputListener mouseInputListener = new MouseInputAdapter() {
077: public void mousePressed(MouseEvent e) {
078: //if (MouseEventCoalesceManager.getInstance().isPressedAllowed(e))
079: pressed(e);
080: }
081:
082: public void mouseReleased(MouseEvent e) {
083: //if (MouseEventCoalesceManager.getInstance().isReleasedAllowed(e))
084: released(e);
085: }
086:
087: public void mouseDragged(MouseEvent e) {
088: //if (MouseEventCoalesceManager.getInstance().isDraggedAllowed(e))
089: dragged(e);
090: }
091: };
092:
093: public DraggableComponent(JComponent component) {
094: this (component, component);
095: }
096:
097: public DraggableComponent(JComponent component,
098: JComponent eventComponent) {
099: this (component, new JComponent[] { eventComponent });
100: }
101:
102: public DraggableComponent(JComponent component,
103: JComponent[] eventComponents) {
104: this .component = component;
105: component.addComponentListener(new ComponentAdapter() {
106:
107: public void componentResized(ComponentEvent e) {
108: fireChangedEvent(DraggableComponentEvent.TYPE_UNDEFINED);
109: }
110:
111: /*public void componentMoved(ComponentEvent e) {
112: fireChangedEvent(DraggableComponentEvent.TYPE_MOVED);
113: }*/
114: });
115: setEventComponents(eventComponents);
116: }
117:
118: public void addListener(DraggableComponentListener l) {
119: if (listeners == null)
120: listeners = new ArrayList(2);
121:
122: listeners.add(l);
123: }
124:
125: public void removeListener(DraggableComponentListener l) {
126: if (listeners != null) {
127: listeners.remove(l);
128:
129: if (listeners.size() == 0)
130: listeners = null;
131: }
132: }
133:
134: public JComponent getComponent() {
135: return component;
136: }
137:
138: public JComponent[] getEventComponents() {
139: return eventComponents;
140: }
141:
142: public void setEventComponents(JComponent[] eventComponents) {
143: if (this .eventComponents != null) {
144: for (int i = 0; i < this .eventComponents.length; i++) {
145: this .eventComponents[i]
146: .removeMouseListener(mouseInputListener);
147: this .eventComponents[i]
148: .removeMouseMotionListener(mouseInputListener);
149: }
150: }
151:
152: this .eventComponents = eventComponents;
153:
154: if (this .eventComponents != null) {
155: for (int i = 0; i < this .eventComponents.length; i++) {
156: this .eventComponents[i]
157: .addMouseListener(mouseInputListener);
158: this .eventComponents[i]
159: .addMouseMotionListener(mouseInputListener);
160: }
161: }
162: }
163:
164: public int getAbortDragKeyCode() {
165: return abortDragKeyCode;
166: }
167:
168: public void setAbortDragKeyCode(int abortDragKeyCode) {
169: this .abortDragKeyCode = abortDragKeyCode;
170: }
171:
172: public boolean isEnabled() {
173: return enabled;
174: }
175:
176: public void setEnabled(boolean enabled) {
177: if (this .enabled != enabled) {
178: this .enabled = enabled;
179: fireChangedEvent(enabled ? DraggableComponentEvent.TYPE_ENABLED
180: : DraggableComponentEvent.TYPE_DISABLED);
181: }
182: }
183:
184: public boolean isReorderEnabled() {
185: return reorderEnabled;
186: }
187:
188: public void setReorderEnabled(boolean reorderEnabled) {
189: this .reorderEnabled = reorderEnabled;
190: }
191:
192: public boolean isReorderRestoreOnDrag() {
193: return reorderRestoreOnDrag;
194: }
195:
196: public void setReorderRestoreOnDrag(boolean reorderRestoreOnDrag) {
197: this .reorderRestoreOnDrag = reorderRestoreOnDrag;
198: }
199:
200: public boolean isDetectOuterAreaAsLine() {
201: return detectOuterAreaAsLine;
202: }
203:
204: public void setDetectOuterAreaAsLine(boolean detectOuterAreaAsLine) {
205: this .detectOuterAreaAsLine = detectOuterAreaAsLine;
206: }
207:
208: public boolean isEnableInsideDrag() {
209: return enableInsideDrag;
210: }
211:
212: public void setEnableInsideDrag(boolean enableInsideDrag) {
213: this .enableInsideDrag = enableInsideDrag;
214: }
215:
216: public boolean isSelectOnMousePress() {
217: return selectOnMousePress;
218: }
219:
220: public void setSelectOnMousePress(boolean selectOnMousePress) {
221: this .selectOnMousePress = selectOnMousePress;
222: }
223:
224: public void drag(Point p) {
225: if (enabled) {
226: dragIndex = getComponentIndex(component);
227: dragFromIndex = dragIndex;
228: doDrag(p);
229: }
230: }
231:
232: public void abortDrag() {
233: if (dragStarted)
234: dragCompleted(null);
235: }
236:
237: public void setLayoutOrderList(ArrayList layoutOrderList) {
238: this .layoutOrderList = layoutOrderList;
239: }
240:
241: public void select() {
242: if (enabled)
243: fireSelectedEvent();
244: }
245:
246: public void setOuterParentArea(JComponent outerParentArea) {
247: this .outerParentArea = outerParentArea;
248: }
249:
250: public boolean isIgnoreAddNotify() {
251: return ignoreAddNotify;
252: }
253:
254: public void setIgnoreAddNotify(boolean ignoreAddNotify) {
255: this .ignoreAddNotify = ignoreAddNotify;
256: }
257:
258: private void pressed(MouseEvent e) {
259: if (enabled && e.getButton() == MouseEvent.BUTTON1) {
260: if (selectOnMousePress && !e.isShiftDown())
261: select();
262: dragStarted = false;
263: KeyboardFocusManager.getCurrentKeyboardFocusManager()
264: .addKeyEventDispatcher(abortDragKeyDispatcher);
265: mousePressed = true;
266: dragIndex = getComponentIndex(component);
267: dragFromIndex = dragIndex;
268:
269: fireChangedEvent(DraggableComponentEvent.TYPE_PRESSED);
270: }
271: }
272:
273: private void released(MouseEvent e) {
274: if (mousePressed) {
275: if (e.getButton() == MouseEvent.BUTTON1)
276: dragCompleted(e);
277: else {
278: dragCompleted(null);
279: e.consume();
280: }
281: }
282: }
283:
284: private void dragged(MouseEvent e) {
285: if (enabled && mousePressed) {
286: Point p = SwingUtilities.convertPoint((JComponent) e
287: .getSource(), e.getPoint(), component);
288: if (dragStarted || enableInsideDrag
289: || !component.contains(p)) {
290: if (reorderEnabled)
291: doDrag(p);
292: else
293: dragStarted = true;
294:
295: fireDraggedEvent(EventUtil.convert(e, component, p));
296: }
297: }
298: }
299:
300: private void dragCompleted(MouseEvent e) {
301: mousePressed = false;
302: dragStarted = false;
303:
304: KeyboardFocusManager.getCurrentKeyboardFocusManager()
305: .removeKeyEventDispatcher(abortDragKeyDispatcher);
306:
307: if (e == null) {
308: restoreComponentOrder();
309: fireNotDroppedEvent();
310: } else if (!checkParentContains(SwingUtilities.convertPoint(
311: (JComponent) e.getSource(), e.getPoint(), component
312: .getParent()))) {
313: restoreComponentOrder();
314: fireDroppedEvent(EventUtil.convert(e, component));
315: } else {
316: fireDroppedEvent(EventUtil.convert(e, component));
317: //if (component.contains(p))
318: if (!selectOnMousePress && !e.isShiftDown())
319: fireSelectedEvent();
320: }
321:
322: fireChangedEvent(DraggableComponentEvent.TYPE_RELEASED);
323: }
324:
325: private void updateParent() {
326: if (component.getParent() != null) {
327: ComponentUtil.validate(component.getParent());
328: //component.getParent().repaint();
329: }
330: }
331:
332: private void doDrag(Point p) {
333: dragStarted = true;
334: JComponent parent = (JComponent) component.getParent();
335:
336: if (parent.getComponentCount() == 1)
337: return;
338:
339: Point p2 = SwingUtilities.convertPoint(component, p, parent);
340: int toIndex = getMoveComponentIndex(p2);
341: if (toIndex != -1) {
342: toIndex = Math.min(toIndex, parent.getComponentCount() - 1);
343: Component fromComponent = getComponent(parent, dragIndex);
344: int fromDimension;
345: int toPos;
346: int toDimension;
347:
348: if (isVerticalDrag()) {
349: fromDimension = fromComponent.getHeight();
350: toPos = (int) SwingUtilities.convertPoint(parent, p2,
351: getComponent(parent, toIndex)).getY();
352: toDimension = getComponent(parent, toIndex).getHeight();
353: } else {
354: fromDimension = fromComponent.getWidth();
355: toPos = (int) SwingUtilities.convertPoint(parent, p2,
356: getComponent(parent, toIndex)).getX();
357: toDimension = getComponent(parent, toIndex).getWidth();
358: }
359:
360: if ((toIndex > dragIndex && toDimension - toPos > fromDimension)
361: || ((dragIndex == -1 || toIndex < dragIndex) && toPos > fromDimension))
362: return;
363:
364: if (dragIndex != -1 && dragIndex != toIndex) {
365: removeComponent(parent, fromComponent, dragIndex);
366: addComponent(parent, fromComponent, toIndex);
367: fireChangedEvent(DraggableComponentEvent.TYPE_MOVED);
368: }
369: }
370:
371: if (toIndex < 0) {
372: restoreComponentOrder();
373: } else
374: dragIndex = toIndex;
375: }
376:
377: private boolean isVerticalDrag() {
378: JComponent parent = (JComponent) component.getParent();
379: if (parent.getComponentCount() > 1)
380: return getComponent(parent, 0).getY() < getComponent(
381: parent, 1).getY();
382:
383: return false;
384: }
385:
386: private boolean checkParentContains(Point p) {
387: if (outerParentArea == null)
388: return component.getParent().contains(p);
389:
390: Point p2 = SwingUtilities.convertPoint(component.getParent(),
391: p, outerParentArea);
392: if (detectOuterAreaAsLine) {
393: Insets i = new Insets(0, 0, 0, 0);//outerParentArea.getInsets();
394: return component.getParent().contains(p)
395: || (outerParentArea.contains(p2) && (isVerticalDrag() ? (p2
396: .getX() >= i.left && p2.getX() < (outerParentArea
397: .getWidth() - i.right))
398: : (p2.getY() >= i.top && p2.getY() < (outerParentArea
399: .getHeight() - i.bottom))));
400: }
401:
402: return component.getParent().contains(p)
403: || outerParentArea.contains(p2);
404: }
405:
406: private int getMoveComponentIndex(Point p) {
407: JComponent parent = (JComponent) component.getParent();
408: if (checkParentContains(p)) {
409: boolean vertical = isVerticalDrag();
410: for (int i = 0; i < parent.getComponentCount() - 1; i++) {
411: Point p2 = getComponent(parent, i + 1).getLocation();
412:
413: if (vertical) {
414: if (p.getY() >= 0 && p.getY() < p2.getY())
415: return i;
416: } else {
417: if (p.getX() >= 0 && p.getX() < p2.getX())
418: return i;
419: }
420: }
421:
422: if (dragIndex == -1)
423: return parent.getComponentCount();
424: else if (vertical)
425: return p.getY() < 0 ? 0
426: : parent.getComponentCount() - 1;
427: else
428: return p.getX() < 0 ? 0
429: : parent.getComponentCount() - 1;
430: }
431:
432: return -1;
433: }
434:
435: private JComponent getComponent(Container parent, int index) {
436: if (layoutOrderList != null)
437: return (JComponent) layoutOrderList.get(index);
438:
439: return (JComponent) parent.getComponent(index);
440: }
441:
442: private int getComponentIndex(Component c) {
443: if (layoutOrderList != null)
444: return layoutOrderList.indexOf(c);
445:
446: return ComponentUtil.getComponentIndex(c);
447: }
448:
449: private void addComponent(Container parent, Component c, int index) {
450: if (layoutOrderList != null) {
451: layoutOrderList.add(index, c);
452: parent.add(c, index);
453: } else
454: parent.add(c, index);
455:
456: revalidateComponentTree((JComponent) c);
457: }
458:
459: private void removeComponent(Container parent, Component c,
460: int index) {
461: revalidateComponentTree((JComponent) c);
462:
463: if (layoutOrderList != null)
464: if (index < 0) {
465: layoutOrderList.remove(c);
466: parent.remove(c);
467: } else {
468: Component tmp = (Component) layoutOrderList.get(index);
469: layoutOrderList.remove(index);
470: parent.remove(tmp);
471: }
472: else if (index < 0)
473: parent.remove(c);
474: else
475: parent.remove(index);
476: }
477:
478: private void revalidateComponentTree(JComponent c) {
479: Container parent = c.getParent();
480: int index = ComponentUtil.getComponentIndex(c);
481: if (index > 0)
482: doRevalidateComponentTree((JComponent) parent
483: .getComponent(index - 1));
484: doRevalidateComponentTree(c);
485: if (index < parent.getComponentCount() - 1)
486: doRevalidateComponentTree((JComponent) parent
487: .getComponent(index + 1));
488: }
489:
490: private void doRevalidateComponentTree(JComponent c) {
491: c.revalidate();
492: int count = c.getComponentCount();
493: for (int i = 0; i < count; i++)
494: doRevalidateComponentTree(((JComponent) c.getComponent(i)));
495: }
496:
497: private void restoreComponentOrder() {
498: if (reorderEnabled && dragIndex != -1 && dragFromIndex != -1
499: && dragIndex != dragFromIndex) {
500: Container parent = component.getParent();
501: Component comp = getComponent(parent, dragIndex);
502: removeComponent(parent, comp, -1);
503: dragIndex = dragFromIndex;
504: addComponent(parent, comp, dragIndex);
505: fireChangedEvent(DraggableComponentEvent.TYPE_MOVED);
506: }
507: }
508:
509: private void fireChangedEvent(int type) {
510: updateParent();
511:
512: if (listeners != null) {
513: DraggableComponentEvent event = new DraggableComponentEvent(
514: this , type);
515: Object[] l = listeners.toArray();
516: for (int i = 0; i < l.length; i++)
517: ((DraggableComponentListener) l[i]).changed(event);
518: }
519: }
520:
521: private void fireSelectedEvent() {
522: updateParent();
523:
524: if (listeners != null) {
525: DraggableComponentEvent event = new DraggableComponentEvent(
526: this );
527: Object[] l = listeners.toArray();
528: for (int i = 0; i < l.length; i++)
529: ((DraggableComponentListener) l[i]).selected(event);
530: }
531: }
532:
533: private void fireDraggedEvent(MouseEvent mouseEvent) {
534: dragEventFired = true;
535: if (listeners != null) {
536: DraggableComponentEvent event = new DraggableComponentEvent(
537: this , mouseEvent);
538: Object[] l = listeners.toArray();
539: for (int i = 0; i < l.length; i++)
540: ((DraggableComponentListener) l[i]).dragged(event);
541: }
542: }
543:
544: private void fireDroppedEvent(MouseEvent mouseEvent) {
545: updateParent();
546:
547: if (dragEventFired) {
548: dragEventFired = false;
549: if (listeners != null) {
550: DraggableComponentEvent event = new DraggableComponentEvent(
551: this , mouseEvent);
552: Object[] l = listeners.toArray();
553: for (int i = 0; i < l.length; i++)
554: ((DraggableComponentListener) l[i]).dropped(event);
555: }
556: }
557: }
558:
559: private void fireNotDroppedEvent() {
560: updateParent();
561:
562: if (dragEventFired) {
563: dragEventFired = false;
564: if (listeners != null) {
565: DraggableComponentEvent event = new DraggableComponentEvent(
566: this );
567: Object[] l = listeners.toArray();
568: for (int i = 0; i < l.length; i++)
569: ((DraggableComponentListener) l[i])
570: .dragAborted(event);
571: }
572: }
573: }
574: }
|