001: /*
002: * Copyright 2001-2006 C:1 Financial Services GmbH
003: *
004: * This software is free software; you can redistribute it and/or
005: * modify it under the terms of the GNU Lesser General Public
006: * License Version 2.1, as published by the Free Software Foundation.
007: *
008: * This software is distributed in the hope that it will be useful,
009: * but WITHOUT ANY WARRANTY; without even the implied warranty of
010: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
011: * Lesser General Public License for more details.
012: *
013: * You should have received a copy of the GNU Lesser General Public
014: * License along with this library; if not, write to the Free Software
015: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
016: */
017:
018: package de.finix.contelligent.client.util.dnd;
019:
020: import java.awt.Insets;
021: import java.awt.Point;
022: import java.awt.Rectangle;
023: import java.awt.datatransfer.DataFlavor;
024: import java.awt.datatransfer.Transferable;
025: import java.awt.datatransfer.UnsupportedFlavorException;
026: import java.awt.dnd.Autoscroll;
027: import java.awt.dnd.DnDConstants;
028: import java.awt.dnd.DropTarget;
029: import java.awt.dnd.DropTargetDragEvent;
030: import java.awt.dnd.DropTargetDropEvent;
031: import java.awt.dnd.DropTargetEvent;
032: import java.awt.dnd.DropTargetListener;
033: import java.io.IOException;
034: import java.io.Serializable;
035: import java.util.ArrayList;
036: import java.util.List;
037: import java.util.logging.Level;
038: import java.util.logging.Logger;
039:
040: import javax.swing.JComponent;
041: import javax.swing.JTree;
042: import javax.swing.TransferHandler;
043: import javax.swing.tree.DefaultMutableTreeNode;
044: import javax.swing.tree.TreeNode;
045: import javax.swing.tree.TreePath;
046:
047: import de.finix.contelligent.client.base.ComponentNotFoundException;
048: import de.finix.contelligent.client.base.ComponentReference;
049: import de.finix.contelligent.client.base.ContelligentComponent;
050: import de.finix.contelligent.client.gui.PathSelectionManager;
051: import de.finix.contelligent.client.i18n.Resources;
052: import de.finix.contelligent.client.util.ComponentMovement;
053: import de.finix.contelligent.client.util.CopyOptionPane;
054:
055: public class JTreeDND extends JTree implements Autoscroll {
056:
057: private static Logger logger = Logger.getLogger(JTreeDND.class
058: .getName());
059:
060: private static int AUTOSCROLL_WEIGHT = 14;
061:
062: private int sourceActions = DnDConstants.ACTION_COPY_OR_MOVE;
063:
064: private boolean dropEnabled;
065:
066: private String environment;
067:
068: private PathSelectionManager pathSelectionManager;
069:
070: public JTreeDND(String environment, TreeNode rootNode,
071: int dragAndDropActions, boolean dropEnabled) {
072: super (rootNode);
073: this .sourceActions = dragAndDropActions;
074: this .dropEnabled = dropEnabled;
075: this .environment = environment;
076: // init drag and drop...
077: setDragEnabled(true);
078: setTransferHandler(new TreeTransferHandler());
079: setDropTarget(new DropTarget(this , new DropHandler(
080: dragAndDropActions)));
081: }
082:
083: public void setPathSelectionManager(
084: PathSelectionManager pathSelectionManager) {
085: this .pathSelectionManager = pathSelectionManager;
086: }
087:
088: public void setDropEnabled(boolean dropEnabled) {
089: this .dropEnabled = dropEnabled;
090: }
091:
092: public void insert(DefaultMutableTreeNode targetNode,
093: Transferable transferable, int requestedAction) {
094: if (targetNode != null
095: && targetNode.getUserObject() instanceof ComponentReference) {
096: try {
097: ContelligentComponent targetComponent = ((ComponentReference) targetNode
098: .getUserObject()).getComponent();
099: CopyOptionPane copyOptionPane = new CopyOptionPane();
100: if (transferable
101: .isDataFlavorSupported(MultipleComponentTransferable.flavor)) {
102: String sourceEnvironment = (String) transferable
103: .getTransferData(SingleComponentTransferable.sourceEnvironmentFlavor);
104: ContelligentComponent[] draggedComponents = (ContelligentComponent[]) transferable
105: .getTransferData(MultipleComponentTransferable.flavor);
106: int sActions = sourceActions;
107: for (int i = 0; i < draggedComponents.length; i++) {
108: if (draggedComponents[i].isReadOnly()
109: || !draggedComponents[i].canWrite()) {
110: sActions = sActions
111: & (~DnDConstants.ACTION_MOVE);
112: if (requestedAction == DnDConstants.ACTION_MOVE) {
113: requestedAction = DnDConstants.ACTION_COPY;
114: }
115: }
116: }
117: if (copyOptionPane
118: .showCopyDialog(
119: Resources
120: .getLocalString("copy_component"),
121: Resources
122: .getLocalString("target_component_name")
123: + ":", null,
124: requestedAction, false, sActions) != CopyOptionPane.OK_OPTION) {
125: return;
126: }
127: int action = copyOptionPane.getAction();
128: performAction(draggedComponents, targetComponent,
129: action, sourceEnvironment);
130: } else if (transferable
131: .isDataFlavorSupported(SingleComponentTransferable.componentFlavor)) {
132: String sourceEnvironment = (String) transferable
133: .getTransferData(SingleComponentTransferable.sourceEnvironmentFlavor);
134: ContelligentComponent draggedComponent = (ContelligentComponent) transferable
135: .getTransferData(SingleComponentTransferable.componentFlavor);
136: int sActions = sourceActions;
137: if (draggedComponent.isReadOnly()
138: || !draggedComponent.canWrite()) {
139: sActions = sActions
140: & (~DnDConstants.ACTION_MOVE);
141: if (requestedAction == DnDConstants.ACTION_MOVE) {
142: requestedAction = DnDConstants.ACTION_COPY;
143: }
144: }
145: if (copyOptionPane
146: .showCopyDialog(
147: Resources
148: .getLocalString("copy_component"),
149: Resources
150: .getLocalString("target_component_name")
151: + ":", draggedComponent
152: .getName().trim(),
153: requestedAction, draggedComponent
154: .isFinal(), sActions) != CopyOptionPane.OK_OPTION) {
155: return;
156: }
157: String name = copyOptionPane.getPath();
158: if (name.length() <= 0) {
159: return;
160: }
161: ActionSelection selection = new ActionSelection(
162: name, copyOptionPane.getAction());
163: performAction(draggedComponent, targetComponent,
164: selection.getName(), selection.getAction(),
165: sourceEnvironment);
166: }
167: } catch (UnsupportedFlavorException ufe) {
168: logger
169: .log(
170: Level.WARNING,
171: "Unsupported transferable was dropped on a tree",
172: ufe);
173: } catch (IOException ioe) {
174: logger.log(Level.WARNING,
175: "Transferable could not be imported", ioe);
176: } catch (ComponentNotFoundException cnfe) {
177: logger
178: .log(
179: Level.SEVERE,
180: "Target component does not exist! Some concurrent modifications might have occured!",
181: cnfe);
182: }
183: }
184: }
185:
186: protected void performAction(ContelligentComponent source,
187: ContelligentComponent target, String name, int action,
188: String sourceEnvironment) {
189: switch (action) {
190: case DnDConstants.ACTION_COPY: {
191: ComponentMovement.nonBlockingCopy(environment,
192: sourceEnvironment, source, target, name, false,
193: false);
194: break;
195: }
196: case DnDConstants.ACTION_COPY_OR_MOVE: {
197: // "Cloning copy" option
198: ComponentMovement.nonBlockingCopy(environment,
199: sourceEnvironment, source, target, name, false,
200: false, false, true);
201: break;
202: }
203: case DnDConstants.ACTION_MOVE: {
204: ComponentMovement.nonBlockingMove(environment,
205: sourceEnvironment, source, target, name, false,
206: false, pathSelectionManager);
207: break;
208: }
209: case DnDConstants.ACTION_LINK: {
210: ComponentMovement.nonBlockingCopy(environment,
211: sourceEnvironment, source, target, name, true,
212: false);
213: break;
214: }
215: }
216: }
217:
218: protected void performAction(ContelligentComponent[] sources,
219: ContelligentComponent target, int action,
220: String sourceEnvironment) {
221: switch (action) {
222: case DnDConstants.ACTION_COPY: {
223: ComponentMovement.nonBlockingCopy(environment,
224: sourceEnvironment, sources, target, false, false);
225: break;
226: }
227: case DnDConstants.ACTION_MOVE: {
228: ComponentMovement.nonBlockingMove(environment,
229: sourceEnvironment, sources, target, false, false,
230: pathSelectionManager);
231: break;
232: }
233: // case DnDConstants.ACTION_LINK:
234: // {
235: // ComponentMovement.nonBlockingCopy(source, target, name, true, false);
236: // break;
237: // }
238: }
239: }
240:
241: public void autoscroll(Point p) {
242: int currentRow = getClosestRowForLocation(p.x, p.y);
243:
244: Rectangle bounds = getBounds();
245:
246: currentRow = (p.y + bounds.y <= AUTOSCROLL_WEIGHT ? currentRow < 1 ? 0
247: : currentRow - 1
248: : (currentRow < getRowCount() - 1 ? currentRow + 1
249: : currentRow));
250:
251: scrollRowToVisible(currentRow);
252: }
253:
254: public Insets getAutoscrollInsets() {
255: Rectangle outer = getBounds();
256: Rectangle inner = getParent().getBounds();
257:
258: return new Insets(inner.y - outer.y + AUTOSCROLL_WEIGHT,
259: inner.x - outer.x + AUTOSCROLL_WEIGHT, outer.height
260: - inner.height - inner.y + outer.y
261: + AUTOSCROLL_WEIGHT, outer.width - inner.width
262: - inner.x + outer.x + AUTOSCROLL_WEIGHT);
263:
264: }
265:
266: private class TreeTransferHandler extends TransferHandler {
267: public boolean canImport(JComponent component,
268: DataFlavor[] transferFlavors) {
269: // this tree can only import contelligent components...
270: for (int i = 0; i < transferFlavors.length; i++) {
271: if (transferFlavors[i]
272: .equals(SingleComponentTransferable.componentFlavor)
273: || transferFlavors[i]
274: .equals(MultipleComponentTransferable.flavor)) {
275: return true;
276: }
277: }
278: return false;
279: }
280:
281: protected Transferable createTransferable(JComponent c) {
282: try {
283: TreePath[] selectedPaths = JTreeDND.this
284: .getSelectionPaths();
285: if (selectedPaths.length == 1) {
286: return new SingleComponentTransferable(
287: ((ComponentReference) ((DefaultMutableTreeNode) selectedPaths[0]
288: .getLastPathComponent())
289: .getUserObject()).getComponent(),
290: environment);
291: } else if (selectedPaths.length > 1) {
292: List<ContelligentComponent> selectedComponents = new ArrayList<ContelligentComponent>(
293: 10);
294: for (int i = 0; i < selectedPaths.length; i++) {
295: selectedComponents
296: .add(((ComponentReference) ((DefaultMutableTreeNode) selectedPaths[i]
297: .getLastPathComponent())
298: .getUserObject())
299: .getComponent());
300: }
301: return new MultipleComponentTransferable(
302: (ContelligentComponent[]) selectedComponents
303: .toArray(new ContelligentComponent[0]),
304: environment);
305: }
306: } catch (ComponentNotFoundException e) {
307: logger.log(Level.SEVERE, "Drag on explorer failed!", e);
308: }
309: return null;
310: }
311:
312: public int getSourceActions(JComponent component) {
313: return sourceActions;
314: }
315: }
316:
317: private class DropHandler implements DropTargetListener,
318: Serializable {
319: private int sourceActions;
320:
321: public DropHandler(int sourceActions) {
322: this .sourceActions = sourceActions;
323: }
324:
325: private boolean canImport = true;
326:
327: private boolean actionSupported(int action) {
328: return ((sourceActions & action) != 0);
329: }
330:
331: // --- DropTargetListener methods -----------------------------------
332:
333: public void dragEnter(DropTargetDragEvent e) {
334: DataFlavor[] flavors = e.getCurrentDataFlavors();
335:
336: JComponent c = (JComponent) e.getDropTargetContext()
337: .getComponent();
338: TransferHandler importer = c.getTransferHandler();
339:
340: if (importer != null && importer.canImport(c, flavors)) {
341: canImport = true;
342: } else {
343: canImport = false;
344: }
345:
346: int dropAction = e.getDropAction();
347:
348: if (canImport && actionSupported(dropAction) && dropEnabled) {
349: e.acceptDrag(dropAction);
350: } else {
351: e.rejectDrag();
352: }
353: }
354:
355: public void dragOver(DropTargetDragEvent e) {
356: int dropAction = e.getDropAction();
357: if (canImport && actionSupported(dropAction) && dropEnabled) {
358: Point p = e.getLocation();
359: TreePath path = JTreeDND.this
360: .getClosestPathForLocation(p.x, p.y);
361: DefaultMutableTreeNode targetNode = (DefaultMutableTreeNode) path
362: .getLastPathComponent();
363: try {
364: if (targetNode != null
365: && targetNode.getUserObject() instanceof ComponentReference) {
366: ContelligentComponent targetComponent = ((ComponentReference) targetNode
367: .getUserObject()).getComponent();
368: if (!targetComponent.isReadOnly()
369: && targetComponent.canWrite()) {
370: if ((e
371: .isDataFlavorSupported(SingleComponentTransferable.componentFlavor))
372: || e
373: .isDataFlavorSupported(MultipleComponentTransferable.flavor)) {
374: e.acceptDrag(dropAction);
375: if (path != null) {
376: JTreeDND.this
377: .setSelectionPath(path);
378: }
379: } else {
380: e.rejectDrag();
381: }
382: } else {
383: e.rejectDrag();
384: }
385: } else {
386: e.rejectDrag();
387: }
388: } catch (ComponentNotFoundException cne) {
389: e.rejectDrag();
390: }
391: } else {
392: e.rejectDrag();
393: }
394: }
395:
396: public void dragExit(DropTargetEvent e) {
397: }
398:
399: public void drop(DropTargetDropEvent e) {
400: final int dropAction = e.getDropAction();
401: // final int sourceActions = e.getSourceActions();
402:
403: JComponent c = (JComponent) e.getDropTargetContext()
404: .getComponent();
405: TransferHandler importer = c.getTransferHandler();
406:
407: if (canImport && importer != null
408: && actionSupported(dropAction) && dropEnabled) {
409: e.acceptDrop(dropAction);
410: Transferable transferable = e.getTransferable();
411: e.dropComplete(true);
412: DefaultMutableTreeNode targetNode = (DefaultMutableTreeNode) JTreeDND.this
413: .getSelectionPath().getLastPathComponent();
414: JTreeDND.this .insert(targetNode, transferable,
415: dropAction);
416: } else {
417: e.rejectDrop();
418: }
419: }
420:
421: public void dropActionChanged(DropTargetDragEvent e) {
422: int dropAction = e.getDropAction();
423:
424: if (canImport && actionSupported(dropAction) && dropEnabled) {
425: e.acceptDrag(dropAction);
426: } else {
427: e.rejectDrag();
428: }
429: }
430: }
431:
432: class ActionSelection {
433: private String name;
434:
435: private int action;
436:
437: public ActionSelection(String name, int action) {
438: this .name = name;
439: this .action = action;
440: }
441:
442: public String getName() {
443: return name;
444: }
445:
446: public int getAction() {
447: return action;
448: }
449: }
450: }
|