source: trunk/gli/src/org/greenstone/gatherer/gui/tree/DragTree.java@ 4801

Last change on this file since 4801 was 4801, checked in by jmt12, 21 years ago

bug#2030155: When changing FileNode I noticed the tree-expansion listener was being added several times. Slight tweak to fix this bug

  • Property svn:keywords set to Author Date Id Revision
File size: 24.8 KB
Line 
1package org.greenstone.gatherer.gui.tree;
2
3import java.awt.*;
4import java.awt.datatransfer.*;
5import java.awt.dnd.*;
6import java.awt.geom.AffineTransform;
7import java.awt.image.BufferedImage;
8import java.io.File;
9import java.util.*;
10import javax.swing.*;
11import javax.swing.event.*;
12import javax.swing.tree.*;
13import org.greenstone.gatherer.Gatherer;
14import org.greenstone.gatherer.file.FileNode;
15import org.greenstone.gatherer.file.FileSystemModel;
16import org.greenstone.gatherer.util.ArrayTools;
17import org.greenstone.gatherer.util.DragComponent;
18import org.greenstone.gatherer.util.DragGroup;
19import org.greenstone.gatherer.util.DragTreeSelectionModel;
20import org.greenstone.gatherer.util.Utility;
21
22public class DragTree
23 extends JTree
24 implements Autoscroll, DragGestureListener, DragSourceListener, DropTargetListener, DragComponent, TreeSelectionListener {
25 /** The normal background color. */
26 private Color background_color;
27 /** The normal foreground color. */
28 private Color foreground_color;
29 /** The Group this component belongs to. */
30 private DragGroup group;
31 /** The image to use for the disabled background. */
32 private ImageIcon disabled_background;
33 /** The image to use for a normal background. */
34 private ImageIcon normal_background;
35 /** The icon to use for multiple node drag'n'drops. We decided against using the windows paradigm or a block of x horizontal lines for x files. */
36 private ImageIcon multiple_icon = new ImageIcon("resource"+File.separator+"multiple.gif");
37 /** The default drag action, although its not that important as we provide custom icons during drags. */
38 private int drag_action = DnDConstants.ACTION_MOVE;
39 /** The location of the last ghost drawn, so that we can repair the 'spoilt' area. */
40 private Point pt_last = null;
41 /** The region borderer by the lower cue line. */
42 private Rectangle lower_cue_line;
43 /** The region covered by the drag ghost icon. */
44 private Rectangle ra_ghost = new Rectangle();
45 /** The region borderer by the upper cue line. */
46 private Rectangle upper_cue_line;
47 /** The identifying name of this Tree. */
48 private String name;
49 /** The last tree path the drag was hovered over. */
50 private TreePath previous_path = null;
51 static private final Color TRANSPARENT_COLOR = new Color(0,0,0,0);
52 /** The distance from the edge of the current view within the scroll bar which if entered causes the view to scroll. */
53 static private final int AUTOSCROLL_MARGIN = 12;
54
55 public DragTree(String name, String background_name, boolean mixed_selection) {
56 super();
57 init(name, background_name, mixed_selection);
58 }
59
60 public DragTree(String name, TreeModel model, String background_name, boolean mixed_selection) {
61 super(model);
62 init(name, background_name, mixed_selection);
63 // Connection
64 setModel(model);
65 }
66
67 public void init(String name, String background_name, boolean mixed_selection) {
68 // Init
69 this.name = name;
70 if(background_name != null) {
71 this.disabled_background = new ImageIcon("background.gif");
72 this.normal_background = new ImageIcon(background_name);
73 }
74
75 // Creation
76 this.putClientProperty("JTree.lineStyle", "Angled");
77 this.setAutoscrolls(true);
78 this.setEditable(false);
79 this.setLargeModel(true);
80 this.setOpaque(true);
81 this.setSelectionModel(new DragTreeSelectionModel(this, mixed_selection));
82 // Connection
83 addTreeSelectionListener(this);
84
85 DragTreeCellRenderer renderer = new DragTreeCellRenderer();
86 //make the renderer paint nodes as transparent when not selected
87 //renderer.setBackgroundNonSelectionColor(new Color(0,0,0,0));
88 setCellRenderer(renderer);
89
90 // Drag'n'drop Setup
91 // Drag source setup.
92 DragSource.getDefaultDragSource().createDefaultDragGestureRecognizer(this, drag_action, this);
93 // Drop destination setup.
94 new DropTarget(this, drag_action, this, true);
95 }
96
97 // Autoscroll Interface - Scroll because the mouse cursor is in our scroll zone.<br>
98 // The following code was borrowed from the book:<br>
99 // Java Swing<br>
100 // By Robert Eckstein, Marc Loy & Dave Wood<br>
101 // Paperback - 1221 pages 1 Ed edition (September 1998)<br>
102 // O'Reilly & Associates; ISBN: 156592455X<br>
103 // The relevant chapter of which can be found at:<br>
104 // http://www.oreilly.com/catalog/jswing/chapter/dnd.beta.pdf<br>
105 // But I've probably tortured it beyond all recognition anyway.
106 public void autoscroll(Point pt) {
107 // Figure out which row we're on.
108 int row = getRowForLocation(pt.x, pt.y);
109 // If we are not on a row then ignore this autoscroll request
110 if (row < 0) return;
111 Rectangle bounds = getBounds();// Yes, scroll up one row
112 // Now decide if the row is at the top of the screen or at the bottom. We do this to make the previous row (or the next row) visible as appropriate. If we're at the absolute top or bottom, just return the first or last row respectively.
113 // Is row at top of screen?
114 if(pt.y + bounds.y <= AUTOSCROLL_MARGIN) {
115 // Yes, scroll up one row
116 if(row <= 0) {
117 row = 0;
118 }
119 else {
120 row = row - 1;
121 }
122 }
123 else {
124 // No, scroll down one row
125 if(row < getRowCount() - 1) {
126 row = row + 1;
127 }
128 }
129 this.scrollRowToVisible(row);
130 }
131
132 /** In order for the appearance to be consistant, given we may be in the situation where the pointer has left our focus but the ghost remains, this method allows other members of the GGroup to tell this component to clear its ghost.
133 */
134 public void clearGhost() {
135 // Erase the last ghost image and cue line
136 paintImmediately(ra_ghost.getBounds());
137 }
138
139 /** Any implementation of DragSourceListener must include this method so we can be notified when the drag event ends (somewhere else), which will in turn remove actions.
140 * @param event A <strong>DragSourceDropEvent</strong> containing all the information about the end of the drag event.
141 */
142 public void dragDropEnd(DragSourceDropEvent event) {
143 if(event.getDropSuccess()) {
144 // Do whatever I do when the drop is successful.
145 }
146 }
147 /** Any implementation of DragSourceListener must include this method so we can be notified when the drag focus enters this component.
148 * @param event A <strong>DragSourceDragEvent</strong> containing all the information
149 * about the drag event.
150 */
151 public void dragEnter(DragSourceDragEvent event) {
152 // Handled elsewhere.
153 }
154 /** Any implementation of DropTargetListener must include this method so we can be notified when the drag focus enters this component, which in this case is to grab focus from within our group.
155 * @param event A <strong>DropTargetDragEvent</strong> containing all the information about the drag event.
156 */
157 public void dragEnter(DropTargetDragEvent event) {
158 group.grabFocus(this);
159 }
160 /** Any implementation of DragSourceListener must include this method so we can be notified when the drag focus leaves this component.
161 * @param event A <strong>DragSourceEvent</strong> containing all the information about the drag event.
162 */
163 public void dragExit(DragSourceEvent event) {
164 clearGhost();
165 }
166
167 /** Any implementation of DropTargetListener must include this method
168 * so we can be notified when the drag focus leaves this component.
169 * @param event A DropTargetEvent containing all the information
170 * about the drag event.
171 */
172 public void dragExit(DropTargetEvent event) {
173 clearGhost();
174 }
175
176 /** Any implementation of DragGestureListener must include this method
177 * so we can be notified when a drag action has been noticed, thus a
178 * drag action has begun.
179 * @param event A DragGestureEvent containing all the information about
180 * the drag event.
181 */
182 public void dragGestureRecognized(DragGestureEvent event) {
183 // Can never drag from MetaEdit tree
184 if(name.equals("MetaEdit")) {
185 return;
186 }
187 // Disable editing, unless you want to have the edit box pop-up part way through dragging.
188 this.setEditable(false);
189 // We need this to find one of the selected nodes.
190 Point origin = event.getDragOrigin();
191 TreePath path = this.getPathForLocation(origin.x, origin.y);
192 // Taking into account our delayed model of selection, it is possible the user has performed a select and drag in one click. Here we utilize the Windows paradigm like so: If the node at the origin of the drag and drop is already in our selection then we perform multiple drag and drop. Otherwise we recognise that this is a distinct drag-drop and move only the origin node.
193 if(!isPathSelected(path)) {
194 ((DragTreeSelectionModel)selectionModel).setImmediate(true);
195 setSelectionPath(path);
196 ((DragTreeSelectionModel)selectionModel).setImmediate(false);
197 }
198 if(path == null) {
199 return;
200 }
201 if (!isValidDrag()) {
202 return;
203 }
204 // Now update the selection stored as far as the group is concerned.
205 group.setSelection(getSelectionPaths());
206 group.setSource(this);
207 // First grab ghost.
208 group.grabFocus(this);
209 // Ghost Image stuff.
210 Rectangle rect = this.getPathBounds(path);
211 group.mouse_offset = new Point(origin.x - rect.x, origin.y - rect.y);
212 // Create the ghost image.
213 // Retrieve the selected files.
214 int selection_count = getSelectionCount();
215 if(selection_count > 0) {
216 JLabel label;
217 if(selection_count == 1) {
218 TreePath node_path = getSelectionPath();
219 FileNode node = (FileNode) path.getLastPathComponent();
220 label = new JLabel(node.toString(), ((DefaultTreeCellRenderer)getCellRenderer()).getLeafIcon(), JLabel.CENTER);
221 }
222 else {
223 String title = getSelectionCount() + " files";
224 label = new JLabel(title, ((DefaultTreeCellRenderer)getCellRenderer()).getClosedIcon(), JLabel.CENTER);
225 title = null;
226 }
227 // The layout manager normally does this.
228 Dimension label_size = label.getPreferredSize();
229 label.setSize(label_size);
230 label.setBackground(TRANSPARENT_COLOR);
231 label.setOpaque(true);
232 // Get a buffered image of the selection for dragging a ghost image.
233 group.image_ghost = new BufferedImage(label_size.width, label_size.height, BufferedImage.TYPE_INT_ARGB_PRE);
234 label_size = null;
235 // Get a graphics context for this image.
236 Graphics2D g2 = group.image_ghost.createGraphics();
237 // Make the image ghostlike
238 g2.setComposite(AlphaComposite.getInstance (AlphaComposite.SRC, 0.5f));
239 // Ask the cell renderer to paint itself into the BufferedImage
240 label.paint(g2);
241 g2 = null;
242 label = null;
243 try {
244 event.startDrag(new Cursor(Cursor.DEFAULT_CURSOR), group.image_ghost, new Point(5,5), new StringSelection("dummy"), this);
245 //dragging = true;
246 }
247 catch(Exception error) {
248 error.printStackTrace();
249 }
250 }
251
252 }
253
254 /** Implementation side-effect.
255 * @param event A DragSourceDragEvent containing all the information about the drag event.
256 */
257 public void dragOver(DragSourceDragEvent event) {
258 }
259
260 /** Any implementation of DropTargetListener must include this method
261 * so we can be notified when the drag moves in this component.
262 * @param event A DropTargetDragEvent containing all the information
263 * about the drag event.
264 */
265 public void dragOver(DropTargetDragEvent event) {
266 // Draw the mouse ghost
267 Graphics2D g2 = (Graphics2D) getGraphics();
268 Point pt = event.getLocation();
269 if(pt_last != null && pt.equals(pt_last)) {
270 return;
271 }
272 pt_last = pt;
273 if(!DragSource.isDragImageSupported()) {
274 // Erase the last ghost image and cue line
275 paintImmediately(ra_ghost.getBounds());
276 // Remember where you are about to draw the new ghost image
277 ra_ghost.setRect(pt.x - group.mouse_offset.x, pt.y - group.mouse_offset.y, group.image_ghost.getWidth(), group.image_ghost.getHeight());
278 // Draw the ghost image
279 g2.drawImage(group.image_ghost, AffineTransform.getTranslateInstance(ra_ghost.getX(), ra_ghost.getY()), null);
280 }
281 // Now we highlight the target node if it is a valid drop target. Of course we don't bother if we are still over a node which has already been identified as to whether its a drop target.
282 TreePath target_path = this.getPathForLocation(pt.x, pt.y);
283 if(previous_path == null || target_path != null && !target_path.equals(previous_path)) {
284 // Immediately clear the old cue lines.
285 if(upper_cue_line != null && lower_cue_line != null) {
286 paintImmediately(upper_cue_line.getBounds());
287 paintImmediately(lower_cue_line.getBounds());
288 }
289 if(isValidDrop(target_path)) {
290 ///ystem.err.println("Valid. Painting cues.");
291 // Get the drop target's bounding rectangle
292 Rectangle raPath = getPathBounds(target_path);
293 // Cue line bounds (2 pixels beneath the drop target)
294 upper_cue_line = new Rectangle(0, raPath.y + (int)raPath.getHeight(), getWidth(), 2);
295 lower_cue_line = new Rectangle(0, raPath.y, getWidth(), 2);
296 g2.setColor(((DefaultTreeCellRenderer)cellRenderer).getBackgroundSelectionColor()); // The cue line color
297 g2.fill(upper_cue_line); // Draw the cue line
298 g2.fill(lower_cue_line);
299 }
300 else {
301 upper_cue_line = null;
302 lower_cue_line = null;
303 }
304 }
305 }
306
307 /** Any implementation of DropTargetListener must include this method
308 * so we can be notified when the drag ends, ie the transferable is
309 * dropped.
310 * @param event A DropTargetDropEvent containing all the information
311 * about the end of the drag event.
312 */
313 public void drop(DropTargetDropEvent event) {
314 ///start = System.currentTimeMillis();
315 ///ystem.err.println("Drop target drop: " + this);
316 event.acceptDrop(drag_action);
317 if(!name.equals(Utility.WORKSPACE_TREE)) {
318 // Determine what node we dropped over.
319 Point pt = event.getLocation();
320 TreePath target_path = this.getPathForLocation(pt.x, pt.y);
321 FileNode target = null;
322 if(target_path != null) {
323 if(isValidDrop(target_path)) {
324 target = (FileNode) target_path.getLastPathComponent();
325 }
326 //else {
327 // // Warn that this is an invalid drop.
328 // MessageTask invalid_target_task = new MessageTask(Gatherer.dictionary.get("General.Error"), Gatherer.dictionary.get("FileActions.InvalidTarget"));
329 // invalid_target_task.start();
330 // invalid_target_task = null;
331 //}
332 }
333 else {
334 TreeModel m = getModel();
335 // check that we have a model and is a FileSystemModel (ie check that a collection is loaded
336 if (m != null && m instanceof FileSystemModel) {
337 target = (FileNode) m.getRoot();
338 }
339 }
340 target_path = null;
341 pt = null;
342 if(target != null) {
343 ///ystem.err.println("Valid drop.");
344 TreePath[] selection = group.getSelection();
345 if(target != null) {
346 FileNode[] source_nodes = new FileNode[selection.length];
347 for(int i = 0; i < source_nodes.length; i++) {
348 source_nodes[i] = (FileNode) selection[i].getLastPathComponent();
349 }
350 Gatherer.f_man.action(group.getSource(), source_nodes, this, target);
351 source_nodes = null;
352 }
353 group.setSelection(null);
354 group.setSource(null);
355 selection = null;
356 target = null;
357 }
358 }
359 //else {
360 // Warn that this is an invalid drop.
361 // ///ystem.err.println("About to show a modal dialog box from JOptionPane.");
362 // MessageTask read_only_task = new MessageTask(Gatherer.dictionary.get("General.Error"), Gatherer.dictionary.get("FileActions.ReadOnlyTarget"));
363 // read_only_task.start();
364 // read_only_task = null;
365 // ///ystem.err.println("Returning from a modal dialog box.");
366 //}
367 // Clear up the group.image_ghost
368 paintImmediately(ra_ghost.getBounds());
369 event.getDropTargetContext().dropComplete(true);
370 }
371
372 //private class MessageTask
373 // extends Thread {
374 // private String message;
375 // private String title;
376 //
377 // MessageTask(String title, String message) {
378 // this.message = message;
379 // this.title = title;
380 // }
381 //
382 // public void run() {
383 // JOptionPane.showMessageDialog(Gatherer.g_man, message, title, JOptionPane.ERROR_MESSAGE);
384 // }
385 //}
386
387 /** Any implementation of DragSourceListener must include this method
388 * so we can be notified when the action to be taken upon drop changes.
389 * @param event A DragSourceDragEvent containing all the information
390 * about the drag event.
391 */
392 public void dropActionChanged(DragSourceDragEvent event) {
393 }
394
395 /** Any implementation of DropTargetListener must include this method
396 * so we can be notified when the action to be taken upon drop changes.
397 * @param event A DropTargetDragEvent containing all the information
398 * about the drag event.
399 */
400 public void dropActionChanged(DropTargetDragEvent event) {
401 }
402
403 /** Used to notify this component that it has gained focus. It should
404 * make some effort to inform the user of this.
405 */
406 public void gainFocus() {
407 ///ystem.err.println("Gained focus: " + this);
408 ((DragTreeCellRenderer)cellRenderer).gainFocus();
409 repaint();
410 }
411
412 /** Autoscroll Interface...
413 * The following code was borrowed from the book:
414 * Java Swing
415 * By Robert Eckstein, Marc Loy & Dave Wood
416 * Paperback - 1221 pages 1 Ed edition (September 1998)
417 * O'Reilly & Associates; ISBN: 156592455X
418 *
419 * The relevant chapter of which can be found at:
420 * http://www.oreilly.com/catalog/jswing/chapter/dnd.beta.pdf
421 * Calculate the insets for the *JTREE*, not the viewport
422 * the tree is in. This makes it a bit messy.
423 */
424 public Insets getAutoscrollInsets()
425 {
426 Rectangle raOuter = this.getBounds();
427 Rectangle raInner = this.getParent().getBounds();
428 return new Insets(raInner.y - raOuter.y + AUTOSCROLL_MARGIN,
429 raInner.x - raOuter.x + AUTOSCROLL_MARGIN,
430 raOuter.height - raInner.height - raInner.y + raOuter.y + AUTOSCROLL_MARGIN,
431 raOuter.width - raInner.width - raInner.x + raOuter.x + AUTOSCROLL_MARGIN);
432 }
433
434 public String getSelectionDetails() {
435 return ((DragTreeSelectionModel)selectionModel).getDetails();
436 }
437
438 public FileSystemModel getTreeModel() {
439 return (FileSystemModel) getModel();
440 }
441
442 /** This method is used to inform this component when it loses focus,
443 * and should indicate this somehow.
444 */
445 public void loseFocus() {
446 ///ystem.err.println("Lost focus: " + this);
447 ((DragTreeCellRenderer)cellRenderer).loseFocus();
448 repaint();
449 }
450
451 public void mapDirectory(File file, String title) {
452 try {
453 ((FileSystemModel)treeModel).mapDirectory(file, title);
454 }
455 catch (Exception error) {
456 error.printStackTrace();
457 }
458 }
459
460 public void paint(Graphics g) {
461 if(disabled_background != null) {
462 int height = getSize().height;
463 int offset = 0;
464 ImageIcon background;
465 if(isEnabled()) {
466 background = normal_background;
467 }
468 else {
469 background = disabled_background;
470 }
471 while((height - offset) > 0) {
472 g.drawImage(background.getImage(), 0, offset, null);
473 offset = offset + background.getIconHeight();
474 }
475 background = null;
476 }
477 super.paint(g);
478 }
479
480 public void refresh(TreePath path) {
481 ((FileSystemModel)treeModel).refresh(path);
482 }
483
484 public void setBackgroundNonSelectionColor(Color color) {
485 background_color = color;
486 if(isEnabled()) {
487 setBackground(color);
488 ((DefaultTreeCellRenderer)cellRenderer).setBackgroundNonSelectionColor(color);
489 }
490 else {
491 setBackground(Color.lightGray);
492 ((DefaultTreeCellRenderer)cellRenderer).setBackgroundNonSelectionColor(Color.lightGray);
493 }
494 repaint();
495 }
496
497 public void setBackgroundSelectionColor(Color color) {
498 ((DefaultTreeCellRenderer)cellRenderer).setBackgroundSelectionColor(color);
499 repaint();
500 }
501
502 /** Override the normal setEnabled so the Tree exhibits a little more
503 * change, which in this instance is the background colour changing.
504 * @param state Whether this GTree should be in an enabled state.
505 */
506 public void setEnabled(boolean state) {
507 super.setEnabled(state);
508 clearSelection();
509 // Change some colors
510 if(state) {
511 setBackground(background_color);
512 ((DefaultTreeCellRenderer)cellRenderer).setBackgroundNonSelectionColor(background_color);
513 ((DefaultTreeCellRenderer)cellRenderer).setTextNonSelectionColor(foreground_color);
514 }
515 else {
516 setBackground(Color.lightGray);
517 ((DefaultTreeCellRenderer)cellRenderer).setBackgroundNonSelectionColor(Color.lightGray);
518 ((DefaultTreeCellRenderer)cellRenderer).setTextNonSelectionColor(Color.black);
519 }
520 repaint();
521 }
522
523 public void setGroup(DragGroup group) {
524 this.group = group;
525 }
526
527 /** Determines whether the following selection attempts should go through the normal delayed selection model, or should happen immediately.*/
528 public void setImmediate(boolean state) {
529 ((DragTreeSelectionModel)selectionModel).setImmediate(state);
530 }
531
532 public void setModel(TreeModel model) {
533 super.setModel(model);
534 if(model instanceof FileSystemModel) {
535 FileSystemModel file_system_model = (FileSystemModel) model;
536 file_system_model.setTree(this);
537 removeTreeExpansionListener(file_system_model);
538 addTreeExpansionListener(file_system_model);
539 removeTreeWillExpandListener(file_system_model);
540 addTreeWillExpandListener(file_system_model);
541 file_system_model = null;
542 }
543 }
544
545 /** Ensure that that file node denoted by the given file is selected. */
546 public void setSelection(File file) {
547 // We know the file exists, and thus that it must exists somewhere in our tree hierarchy.
548 // 1. Retrieve the root node of our tree.
549 FileNode current = (FileNode) getModel().getRoot();
550 // 2. Find that node in the file parents, keeping track of each intermediate file.
551 ArrayList files = new ArrayList();
552 while(file != null && !current.toString().equals(file.getName())) {
553 files.add(0, file);
554 file = file.getParentFile();
555 }
556 if(file == null) {
557 return;
558 }
559 // 3. While there are still remaining intermediate files.
560 while(files.size() > 0) {
561 file = (File) files.remove(0);
562 // 3a. Find the next file in the current nodes children.
563 boolean found = false;
564 current.map();
565 for(int i = 0; !found && i < current.getChildCount(); i++) {
566 FileNode child = (FileNode) current.getChildAt(i);
567 if(child.toString().equals(file.getName())) {
568 // 3b. Make the current node this node (if found) and continue.
569 found = true;
570 current = child;
571 }
572 }
573 // 3c. If not found then return as this node can't exists somehow.
574 if(!found) {
575 return;
576 }
577 }
578 // 4. We should now have the desired node. Remember to make the selection immediate.
579 TreePath path = new TreePath(current.getPath());
580 setImmediate(true);
581 setSelectionPath(path);
582 setImmediate(false);
583 }
584
585 public void setTextNonSelectionColor(Color color) {
586 foreground_color = color;
587 if(isEnabled()) {
588 ((DefaultTreeCellRenderer)cellRenderer).setTextNonSelectionColor(color);
589 }
590 else {
591 ((DefaultTreeCellRenderer)cellRenderer).setTextNonSelectionColor(Color.black);
592 }
593 repaint();
594 }
595
596 public void setTextSelectionColor(Color color) {
597 ((DefaultTreeCellRenderer)cellRenderer).setTextSelectionColor(color);
598 repaint();
599 }
600
601 public String toString() {
602 return name;
603 }
604
605 public void valueChanged(TreeSelectionEvent event) {
606 if(group == null) {
607 ///ystem.err.println("Oh my god, this tree has no group: " + this);
608 }
609 else {
610 group.grabFocus(this);
611 }
612 if(name.equals("Collection") || name.equals("MetaEdit")) {
613 if(event.getNewLeadSelectionPath() == null) {
614 Gatherer.g_man.menu_bar.setMetaAuditSuffix(null);
615 }
616 else {
617 Gatherer.g_man.menu_bar.setMetaAuditSuffix(getSelectionDetails());
618 }
619 }
620 }
621
622 /** returns false for dummy nodes (ones without files), and system root
623 * nodes */
624 private boolean isValidDrag() {
625 //because you cant select nodes that are children of another selection, and we use a contiguous selection model, we just test the first selection path
626 TreePath node_path = getSelectionPath();
627 FileNode node = (FileNode) node_path.getLastPathComponent();
628
629 if (node.getFile() == null) {
630 return false;
631 }
632 if (node.isFileSystemRoot()) {
633 return false;
634 }
635 return true;
636 }
637
638 private boolean isValidDrop(TreePath target_path) {
639 boolean valid = false;
640 if(target_path != null) {
641 FileNode target_node = (FileNode) target_path.getLastPathComponent();
642 // We can only continue testing if the node is a folder.
643 if(!target_node.isLeaf()) {
644 // Now we check if the node is readonly.
645 if(!target_node.isReadOnly()) {
646 // Finally we check the target path against the paths in the selection to ensure we are not adding to our own ancestors!
647 TreePath[] selection = group.getSelection();
648 boolean failed = false;
649 for(int i = 0; !failed && selection != null && i < selection.length; i++) {
650 failed = selection[i].isDescendant(target_path);
651 }
652 // Having finally completed all the tests, we can highlight the drop target.
653 if(!failed) {
654 valid = true;
655 }
656 else {
657 ///ystem.err.println("Invalid. Target is descendant of itself.");
658 }
659 }
660 else {
661 ///ystem.err.println("Read only.");
662 }
663 }
664 else {
665 ///ystem.err.println("Leaf node. Children not allowed.");
666 }
667 previous_path = target_path;
668 }
669 else {
670 if(target_path == null) {
671 previous_path = null;
672 }
673 }
674 return valid;
675 }
676}
Note: See TracBrowser for help on using the repository browser.