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

Last change on this file since 8480 was 8352, checked in by mdewsnip, 20 years ago

Made some static variables final.

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