Squiz Matrix  4.12.2
 All Data Structures Namespaces Functions Variables Pages
MatrixTree.java
1 
16  /*
17  * :tabSize=4:indentSize=4:noTabs=false:
18  * :folding=explicit:collapseFolds=1:
19  */
20 
21 package net.squiz.matrix.matrixtree;
22 
23 import net.squiz.cuetree.*;
24 import net.squiz.matrix.core.*;
25 import net.squiz.matrix.ui.*;
26 import net.squiz.matrix.assetmap.*;
27 import net.squiz.matrix.debug.*;
28 
29 import javax.swing.tree.*;
30 import javax.swing.event.*;
31 import javax.swing.*;
32 
33 import java.io.IOException;
34 import java.util.*;
35 import java.net.*;
36 
37 import java.awt.*;
38 import java.awt.event.*;
39 import java.awt.image.*;
40 import java.awt.geom.*;
41 import java.awt.dnd.*;
42 import java.awt.datatransfer.*;
43 
44 
45 
46 
51 public class MatrixTree extends CueTree
52  implements CueGestureListener, TreeWillExpandListener, Draggable, Autoscroll {
53 
54  private MenuHandler menuHandler;
55  private DragHandler dragHandler;
56  private DropHandler dropHandler;
57  private DoubleClickHandler dcHandler;
58  private SelectionTool selTool;
59 
60  private String lastTypeCodeCreated = null;
61 
62  private BufferedImage dblBuffer = null;
63  private DragSource dragSource = null;
64  private boolean isInAssetFinderMode = false;
65  private boolean hasExpandNextNode = false;
66  private boolean hasExpandPrevNode = false;
67  private static final int AUTOSCROLL_MARGIN = 12;
68 
69  public static final Color ASSET_FINDER_BG_COLOR = new Color(0xE9D4F4);
70 
71  //TODO: (MM) this is a dirty hack to get the menu to show up before
72  // the cue tree knows anything about it
73  private String multipleMoveType = null;
74  private JTree tree = this;
75 
76  //{{{ Public Methods
77 
81  public MatrixTree() {
82  super();
83  addCueGestureListener(this);
84  }
85 
90  public MatrixTree(TreeModel model) {
91  super(model);
92 
93  selTool = new SelectionTool(this);
94 
95  menuHandler = getMenuHandler();
96  dragHandler = getDragHandler();
97  dropHandler = getDropHandler();
98  dcHandler = getDoubleClickHandler();
99 
100  addCueGestureListener(this);
101  addTreeWillExpandListener(this);
102 
103  addMouseListener(selTool);
104  addMouseListener(menuHandler);
105  addMouseListener(dcHandler);
106  addMouseMotionListener(selTool);
107 
108  setUI(new MatrixTreeUI());
109 
110  dragSource = DragSource.getDefaultDragSource();
111  DragGestureRecognizer dgr =
112  dragSource.createDefaultDragGestureRecognizer(
113  this, // DragSource
114  DnDConstants.ACTION_COPY_OR_MOVE, // specifies valid actions
115  dragHandler // DragGestureListener
116  );
117 
118  dgr.setSourceActions(dgr.getSourceActions() & ~InputEvent.BUTTON3_MASK);
119  DropTarget dropTarget = new DropTarget(this, dropHandler);
120 
121  // create a mouse motion listener to update the ViewPort when we
122  // do a drag operation where the drag extends greater than the tree size
123  MouseMotionListener mmListener = new MouseMotionAdapter() {
124  public void mouseDragged(MouseEvent evt) {
125  Rectangle r = new Rectangle(evt.getX(), evt.getY(), 6, 6);
126  scrollRectToVisible(r);
127  }
128  };
129  setAutoscrolls(true);
130  addMouseMotionListener(mmListener);
131  ToolTipManager.sharedInstance().registerComponent(this);
133 
134  }
135 
140  public void autoscroll(Point pt) {
141  int row = getRowForLocation(pt.x, pt.y);
142  Rectangle bounds = getBounds();
143 
144  if (pt.y + bounds.y <= AUTOSCROLL_MARGIN) {
145  if (row > 0) --row;
146  } else {
147  if (row < getRowCount() - 1) ++row;
148  }
149  scrollRowToVisible(row);
150  }
151 
156  public Insets getAutoscrollInsets() {
157  Rectangle outer = getBounds();
158  Rectangle inner = getParent().getBounds();
159 
160  return new Insets(inner.y - outer.y + AUTOSCROLL_MARGIN,
161  inner.x - outer.x + AUTOSCROLL_MARGIN,
162  outer.height - inner.height - inner.y + outer.y + AUTOSCROLL_MARGIN,
163  outer.width - inner.width - inner.x + outer.x + AUTOSCROLL_MARGIN);
164  }
165 
171  listenerList.add(NewLinkListener.class, l);
172  }
173 
179  listenerList.remove(NewLinkListener.class, l);
180  }
181 
187  listenerList.add(NewAssetListener.class, l);
188  }
189 
195  listenerList.remove(NewAssetListener.class, l);
196  }
197 
203  listenerList.add(NodeDoubleClickedListener.class, cl);
204  }
205 
211  listenerList.remove(NodeDoubleClickedListener.class, cl);
212  }
213 
221  TreePath[] paths = getSelectionPaths();
222  if (paths == null)
223  return null;
224  MatrixTreeNode[] nodes = new MatrixTreeNode[paths.length];
225  for (int i = 0; i < paths.length; i++)
226  nodes[i] = (MatrixTreeNode) paths[i].getLastPathComponent();
227  return nodes;
228  }
229 
237  TreePath path = getSelectionPath();
238  if (path == null)
239  return null;
240  return (MatrixTreeNode) path.getLastPathComponent();
241  }
242 
248  public boolean isMultipleSelection() {
249  TreePath[] paths = getSelectionPaths();
250  return (paths != null && paths.length > 1);
251  }
252 
258  public boolean isEmptySelection() {
259  return (getSelectionPath() == null);
260  }
261 
267  public TreePath getPathToRoot(MatrixTreeNode node) {
268  Object[] nodes = ((DefaultTreeModel) getModel()).getPathToRoot(node);
269  if (nodes == null)
270  return null;
271  return new TreePath(nodes);
272  }
273 
279  public TreePath[] pathsToArray(java.util.List paths) {
280  return (TreePath[]) paths.toArray(new TreePath[paths.size()]);
281  }
282 
287  public void startAssetFinderMode() {
288  isInAssetFinderMode = true;
289  setBackground(ASSET_FINDER_BG_COLOR);
290 
291  removeMouseListener(selTool);
292  removeMouseMotionListener(selTool);
293  }
294 
299  public void stopAssetFinderMode() {
300  isInAssetFinderMode = false;
301  setBackground(Color.WHITE);
302 
303  addMouseListener(selTool);
304  addMouseMotionListener(selTool);
305  }
306 
312  public void createSelection() {
313 
314  MatrixTreeNode[] nodes = getSelectionNodes();
315  if (nodes == null)
316  return;
317  Selection.setNodes(nodes);
318  if (MatrixDialog.hasDialog(SelectionDialog.class)) {
319  SelectionDialog selectionDialog
320  = (SelectionDialog) MatrixDialog.getDialog(SelectionDialog.class);
321  selectionDialog.setNodes(nodes);
322  }
323  }
324 
329  public void treeWillExpand(TreeExpansionEvent evt) {
330  TreePath path = evt.getPath();
331  MatrixTreeNode treeNode = (MatrixTreeNode) path.getLastPathComponent();
332 
333  // if user does not have access to this asset then they cannot see its kids
334  if (treeNode.getAsset().isAccessible()) {
335  loadChildAssets(treeNode);
336  }
337  }
338 
344  public boolean parentIsRoot(MatrixTreeNode child) {
345  MatrixTreeNode parent = (MatrixTreeNode)getModel().getRoot();
346  if (parent == child.getParent()) {
347  return true;
348  }
349  return false;
350  }
351 
357  public boolean nodeIsRoot(MatrixTreeNode node) {
358  MatrixTreeNode parent = (MatrixTreeNode)getModel().getRoot();
359  if (parent == node) {
360  return true;
361  }
362  return false;
363  }
364 
370  public boolean hasPreviousNode(MatrixTreeNode parent) {
371  if (parent.getChildCount() != 0) {
372  MatrixTreeNode node = (MatrixTreeNode)parent.getChildAt(0);
373  if (node instanceof ExpandingPreviousNode) {
374  return true;
375  }
376  }
377  return false;
378  }
379 
385  public boolean hasNextNode(MatrixTreeNode parent) {
386  if (parent.getChildCount() != 0) {
387  MatrixTreeNode node = (MatrixTreeNode)parent.getChildAt(parent.getChildCount()-1);
388  if (node instanceof ExpandingNextNode) {
389  return true;
390  }
391  }
392  return false;
393  }
394 
402  private MatrixTreeNode findAssetUnderParent(MatrixTreeNode parent, String assetid, boolean incChildren) {
403  try {
404  boolean found = false;
405  int childCount = 0;
406  int loc = 0;
407 
408  while (!found) {
409  if (loc != 0 || parent.getChildCount() == 0) {
410  removeChildNodes(parent);
411  AssetManager.refreshAsset(parent, "", loc, -1);
412  }
413  childCount = parent.getChildCount();
414  if (childCount > 0) {
415  for (Enumeration children = parent.children(); children.hasMoreElements();) {
416  MatrixTreeNode nextNode = (MatrixTreeNode) children.nextElement();
417  if (nextNode.getAsset().getId().equals(assetid)) {
418  return nextNode;
419  } else if (incChildren) {
420  // look under this node
421  nextNode = findAssetUnderParent(nextNode, assetid, true);
422  if (nextNode != null) {
423  return nextNode;
424  }
425  }
426  }
427  } else {
428  // not found
429  return null;
430  }
431  if (childCount >= AssetManager.getLimit()) {
432  // look at the next set of assets
433  loc += AssetManager.getLimit();
434  } else {
435  return null;
436  }
437  }
438  } catch (IOException ex) { }
439 
440  return null;
441  }
442 
443 
452  public void loadChildAssets(final String[] assetids, final String[] sort_orders, final boolean selectAll, final boolean teleport) {
453  MatrixStatusBar.setStatus(Matrix.translate("asset_map_status_bar_requesting"));
454  Runnable runner = new Runnable() {
455  public void run() {
456 
457  // if we have a different root and not the actual root then this will not work
458  // or should it?
459  tree.setRootVisible(false);
460  if (((DefaultTreeModel) tree.getModel()).getRoot() != AssetManager.getRootFolderNode()) {
461  ((DefaultTreeModel) tree.getModel()).setRoot(AssetManager.getRootFolderNode());
462  }
463 
464  MatrixTreeNode parent = AssetManager.getRootFolderNode();
465  int numAssets = assetids.length;
466  int level = 0;
467  int loadedNodes = 0;
468  boolean found = false;
469  TreePath path = null;
470 
471  try {
472  // clear all other selections
473  tree.clearSelection();
474 
475  int sort_order = 0;
476  int totalKids = 0;
477 
478  TreePath[] paths = new TreePath[numAssets+1];
479  paths[0] = path;
480 
481  while (level < numAssets) {
482 
483  found = false;
484  Asset asset = parent.getAsset();
485  totalKids = asset.getTotalKidsLoaded();
486  sort_order = Integer.parseInt(sort_orders[level]);
487 
488  if (!sort_orders[level].equals("-1")) {
489  int modifier = (int)(sort_order/AssetManager.getLimit());
490  int loc = 0;
491  if (parent.getChildCount() == 0 || (AssetManager.getLimit()*modifier != totalKids)) {
492  for (int i=(AssetManager.getLimit()*modifier); i > 0; i--) {
493  if (parent.getAsset().getNumKids() >= AssetManager.getLimit()*modifier) {
494  break;
495  } else {
496  modifier--;
497  }
498  }
499  if (parent.getAsset().getNumKids() >= AssetManager.getLimit()*modifier) {
500  // load another set of assets
501  removeChildNodes(parent);
502  AssetManager.refreshAsset(parent, "", AssetManager.getLimit()*modifier, -1);
503  loadedNodes += parent.getChildCount();
504  }
505  }
506 
507  if (parent.getChildCount() > 0) {
508  loc += (sort_order%AssetManager.getLimit());
509 
510  if (loc >= parent.getChildCount()) {
511  loc = parent.getChildCount()-1;
512  } else if (loc < 0) {
513  loc = 0;
514  }
515 
516  MatrixTreeNode foundChild = null;
517  foundChild = (MatrixTreeNode)parent.getChildAt(loc);
518  if (foundChild.getAsset().getId().equals(assetids[level])) {
519  found = true;
520  parent = foundChild;
521  } else {
522 
523  for (int i = 0; i < parent.getChildCount(); i++) {
524  foundChild = (MatrixTreeNode)parent.getChildAt(i);
525  if (foundChild.getAsset().getId().equals(assetids[level])) {
526  found = true;
527  parent = foundChild;
528  break;
529  }
530  }
531  }
532  }
533  } else {
534  parent = findAssetUnderParent(parent, assetids[level], false);
535  if (parent != null) {
536  found = true;
537  }
538  }
539 
540  if (!found) {
541  break;
542  } else {
543  path = getPathToRoot(parent);
544  paths[level] = path;
545  level++;
546  }
547  }
548 
549  // scroll to the last selected node
550  if (selectAll) {
551  tree.addSelectionPaths(paths);
552  } else {
553  tree.addSelectionPath(path);
554  }
555 
556  if (teleport) {
557  teleportToRoot((MatrixTreeNode)path.getLastPathComponent());
558  }
559 
560  scrollPathToVisible(path);
561 
562 
563 
564  if (!found) {
565  // asset not found
566  MatrixStatusBar.setStatusAndClear(Matrix.translate("asset_map_status_bar_requesting"), 1000);
567  Object[] transArgs = {
568  assetids[level]
569  };
570  String message = Matrix.translate("asset_map_error_locate_asset", transArgs);
571  GUIUtilities.error(message, Matrix.translate("asset_map_dialog_title_error"));
572  return;
573  }
574 
575  } catch (Exception ioe) {
576  MatrixStatusBar.setStatusAndClear(Matrix.translate("asset_map_status_bar_requesting"), 1000);
577  Object[] transArgs = {
578  ioe.getMessage()
579  };
580  String message = Matrix.translate("asset_map_error_loading_children", transArgs);
581  GUIUtilities.error(message, Matrix.translate("asset_map_dialog_title_error"));
582  Log.log(message, MatrixTree.class, ioe);
583  return;
584  }
585 
586  if (loadedNodes == 1) {
587  MatrixStatusBar.setStatusAndClear(Matrix.translate("asset_map_status_bar_loaded_child"), 3000);
588  } else {
589  Object[] transArgs = {
590  new Integer(loadedNodes)
591  };
592  MatrixStatusBar.setStatusAndClear(Matrix.translate("asset_map_status_bar_loaded_children", transArgs), 3000);
593  }
594  }
595  };
596  SwingUtilities.invokeLater(runner);
597  }
598 
599  public void loadChildAssets(final MatrixTreeNode node) {
600  loadChildAssets(node, "", -1, -1);
601  }
602 
618  public void loadChildAssets(final MatrixTreeNode node, final String direction, final int start, final int limit) {
619  if (node == null) {
620  return;
621  }
622 
623  if (node.getChildCount() > 0 && direction.equals("") && start < 0) {
624  removeExpandPreviousNode(node);
625  removeExpandNextNode(node);
626 
628  if (node.getChildCount() == 0) {
629  node.getAsset().propagateChildren(node);
630  }
631  insertExpandNextNode(node);
632  } else {
633  insertLoadingNode(node);
634  MatrixStatusBar.setStatus(Matrix.translate("asset_map_status_bar_requesting"));
635  Runnable runner = new Runnable() {
636  public void run() {
637  try {
638  Asset asset = node.getAsset();
639  if (asset.getId().equals("1")) {
640  AssetManager.refreshAsset(node, "");
641  removeLoadingNode(node);
642  } else {
643  removeChildNodes(node);
644  AssetManager.refreshAsset(node, direction, start, limit);
646  insertExpandNextNode(node);
647  TreePath path = getPathToRoot(node);
648  if (!isExpanded(path)) {
649  expandPath(path);
650  }
651  }
652 
653  if (node.getChildCount() == 1) {
654  MatrixStatusBar.setStatusAndClear(Matrix.translate("asset_map_status_bar_loaded_child"), 1000);
655  } else {
656  Object[] transArgs = {
657  new Integer(node.getChildCount())
658  };
659  MatrixStatusBar.setStatusAndClear(Matrix.translate("asset_map_status_bar_loaded_children", transArgs), 1000);
660  }
661 
662  } catch (IOException ioe) {
663  MatrixStatusBar.setStatusAndClear(Matrix.translate("asset_map_status_bar_requesting"), 1000);
664  Object[] transArgs = {
665  ioe.getMessage()
666  };
667  String message = Matrix.translate("asset_map_error_loading_children", transArgs);
668  GUIUtilities.error(message, Matrix.translate("asset_map_dialog_title_error"));
669  Log.log(message, MatrixTree.class, ioe);
670  }
671  }
672  };
673  SwingUtilities.invokeLater(runner);
674  }
675  }
676 
677  public void treeWillCollapse(TreeExpansionEvent evt) {}
678  public void moveGestureRecognized(CueEvent evt) {}
679  public void addGestureRecognized(CueEvent evt) {}
680  public void multipleMoveGestureRecognized(CueEvent evt) {}
681  public void multipleAddGestureRecognized(CueEvent evt) {}
682  public void multipleAddGestureCompleted(CueEvent evt) {}
683 
690  public void moveGestureCompleted(CueEvent evt) {
691  TreePath[] sourcePaths = evt.getSourcePaths();
692  MatrixTreeNode[] sourceNodes = new MatrixTreeNode[sourcePaths.length];
693 
694  for (int i = 0; i < sourcePaths.length; i++) {
695  sourceNodes[i] = (MatrixTreeNode) sourcePaths[i].getLastPathComponent();
696  }
697 
698  // Ensure that nodes to be moved are sorted by "sort order"
699 
700  // Bubble Sort
701  if (sourceNodes.length > 1) {
702  int numSorted = 1;
703  while (numSorted > 0) {
704  numSorted = 0;
705  for (int i = 0; i < sourceNodes.length; i++) {
706  if (i+1 < sourceNodes.length) {
707  MatrixTreeNode firstTreeNode = sourceNodes[i];
708  MatrixTreeNode nextTreeNode = sourceNodes[i+1];
709 
710  // Get the sort order of the selected items
711  int firstSortOrder = firstTreeNode.getSortOrder();
712  int nextSortOrder = nextTreeNode.getSortOrder();
713 
714  // Swap elements if they are the wrong way around
715  if (nextSortOrder < firstSortOrder) {
716  sourceNodes[i] = nextTreeNode;
717  sourceNodes[i+1] = firstTreeNode;
718  numSorted++;
719  }
720  }
721  }
722  }
723  }
724 
725  JPopupMenu newLinkMenu = getNewLinkMenu(
726  sourceNodes,
727  (MatrixTreeNode) evt.getParentPath().getLastPathComponent(),
728  evt.getIndex(), evt.getPrevIndex());
729  newLinkMenu.show(this, evt.getX(), evt.getY());
730  }
731 
738  public void addGestureCompleted(CueEvent evt) {
739  String typeCode = (String) evt.getSourcePath().getLastPathComponent();
740  JPopupMenu newAssetMenu = getNewAssetMenu(
741  typeCode,
742  (MatrixTreeNode) evt.getParentPath().getLastPathComponent(),
743  evt.getIndex()
744  );
745  newAssetMenu.show(this, evt.getX(), evt.getY());
746  }
747 
754  public void multipleMoveGestureCompleted(CueEvent evt) {
755 
756  if (multipleMoveType != null) {
757  TreePath[] sourcePaths = evt.getSourcePaths();
758  MatrixTreeNode[] sourceNodes = new MatrixTreeNode[sourcePaths.length];
759  for (int i = 0; i < sourcePaths.length; i++) {
760  sourceNodes[i] = (MatrixTreeNode) sourcePaths[i].getLastPathComponent();
761  }
762 
763  MatrixTreeNode parent = (MatrixTreeNode) evt.getParentPath().getLastPathComponent();
764  fireCreateLink(multipleMoveType, sourceNodes, parent, evt.getIndex(), evt.getPrevIndex());
765  multipleMoveType = null;
766  } else {
768 
769 
770  }
771  }
772 
777  public void teleportToRoot(MatrixTreeNode node) {
778  loadChildAssets(node);
779  setRootVisible(true);
780  ((DefaultTreeModel) getModel()).setRoot(node);
781  }
782 
790  public void fireCreateLink(
791  String type,
792  MatrixTreeNode[] sources,
793  MatrixTreeNode parent,
794  int index,
795  int prevIndex) {
796  // Guaranteed to return a non-null array
797  Object[] listeners = listenerList.getListenerList();
798  NewLinkEvent evt = null;
799 
800  // Process the listeners last to first, notifying
801  // those that are interested in this event
802  for (int i = listeners.length - 2; i >= 0; i -= 2) {
803  if (listeners[i] == NewLinkListener.class) {
804  // Lazily create the event:
805  String[] parentIds = getCueModeParentIds();
806  setCueModeParentIds(null);
807  if (evt == null)
808  evt = new NewLinkEvent(this, type, sources, parent, index, prevIndex, parentIds);
809  ((NewLinkListener) listeners[i + 1]).
810  requestForNewLink(evt);
811  }
812  }
813  }
814 
815  public void fireCreateLink(
816  String type,
817  MatrixTreeNode[] sources,
818  MatrixTreeNode parent,
819  int index) {
820  fireCreateLink(type,sources,parent,index,0);
821  }
822 
829  //TODO MM: this is overkill. I cant see when someone else wants to listen
830  // to new assets. When the asset is added, the model will be updated anyway
831  // you can listen to that if you want
832 
833  public void fireNewAsset(
834  String typeCode,
835  MatrixTreeNode parent,
836  int index) {
837  // Guaranteed to return a non-null array
838  Object[] listeners = listenerList.getListenerList();
839  NewAssetEvent evt = null;
840 
841  // Process the listeners last to first, notifying
842  // those that are interested in this event
843  for (int i = listeners.length - 2; i >= 0; i -= 2) {
844  if (listeners[i] == NewAssetListener.class) {
845  // Lazily create the event:
846  if (evt == null)
847  evt = new NewAssetEvent(this, typeCode, parent, index);
848  ((NewAssetListener) listeners[i + 1]).
849  requestForNewAsset(evt);
850  }
851  }
852  // Store last created asset type code in ivar
853  lastTypeCodeCreated = typeCode;
854 
855  }
856 
862  public void fireNodeDoubleClicked(TreePath clickedPath, Point point) {
863  Object[] listeners = listenerList.getListenerList();
864  NodeDoubleClickedEvent evt = null;
865 
866  // Process the listeners last to first, notifying
867  // those that are interested in this event
868  for (int i = listeners.length - 2; i >= 0; i -= 2) {
869  if (listeners[i] == NodeDoubleClickedListener.class) {
870  // Lazily create the event:
871  if (evt == null)
872  evt = new NodeDoubleClickedEvent(this, clickedPath, point);
873  ((NodeDoubleClickedListener) listeners[i + 1]).
874  nodeDoubleClicked(evt);
875  }
876  }
877  }
878 
884  public void paintComponent(Graphics g) {
885  Graphics2D g2 = (Graphics2D) g;
886  // this gets executed once
887  if (dblBuffer == null) {
888  initDoubleBufferImage();
889  }
890  g2.drawImage(dblBuffer, null, 0, 0);
891  super.paintComponent(g);
892 
893  if (dropHandler == null)
894  return;
895 
896  if (selTool.isDragging())
897  selTool.paintSelectionTool(g2);
898  else if (dropHandler.isDropping())
899  dropHandler.paintDropImage(g2);
900  }
901 
909  public Image getDragImage(TreePath[] paths) {
910  return (paths.length == 1) ? getGhostedNode(paths[0]) : getGhostedNode(paths);
911  }
912 
918  public boolean isInAssetFinderMode() {
919  return isInAssetFinderMode;
920  }
921 
922  //}}}
923 
924  //{{{ Protected Methods
925 
931  return new MenuHandler();
932  }
933 
939  return new DoubleClickHandler();
940  }
941 
947  return new DragHandler();
948  }
949 
955  return new DropHandler();
956  }
957 
962  protected CueGestureHandler getCueGestureHandler() {
963  return new MatrixCueGestureHandler();
964  }
965 
973  protected Image getDragImageForPaths(TreePath[] paths) {
974  if (paths == null)
975  throw new IllegalArgumentException("paths is null");
976  Image ghostedImage = (paths.length == 1)
977  ? getGhostedNode(paths[0])
978  : getGhostedNode(paths);
979 
980  return ghostedImage;
981  }
982 
988  protected boolean canMoveNode(Object node) {
989  if (getModel().getRoot() == node)
990  return false;
991  MatrixTreeNode treeNode = (MatrixTreeNode) node;
992  if (treeNode.getLinkid().equals("0"))
993  return false;
994  if (!treeNode.getAsset().isAccessible())
995  return false;
996  return true;
997  }
998 
1004  protected boolean canMoveNodes(Object[] nodes) {
1005  for (int i = 0; i < nodes.length; i++) {
1006  if (!canMoveNode(nodes[i]))
1007  return false;
1008  }
1009  return true;
1010  }
1011 
1012  private int currentFontSize = 10;
1013  private int previousFontSize = 0;
1014  private int initialFontSize = 10;
1015  private Font nodeFont;
1016 
1017  public Font getFontInUse() {
1018  if (currentFontSize != previousFontSize) {
1019  nodeFont = null;
1020  nodeFont = new Font("nodeFont", Font.PLAIN, currentFontSize);
1021  // update previous font size so we create font when size changes
1022  previousFontSize = currentFontSize;
1023  }
1024  return nodeFont;
1025  }
1026 
1027  private void setFontSize(int size) {
1028  if (size > 7 && size < 18) {
1029  currentFontSize = size;
1030  }
1031  }
1032 
1033  private java.util.List getExpandedChildren(TreeNode parent) {
1034  java.util.List paths = new ArrayList();
1035  int count = parent.getChildCount();
1036  for (int i = 0; i < count; i++) {
1037  MatrixTreeNode node = (MatrixTreeNode)parent.getChildAt(i);
1038  TreePath path = getPathToRoot(node);
1039  if (isExpanded(path)) {
1040  paths.add(path);
1041  paths.addAll(getExpandedChildren(node));
1042  }
1043  }
1044  return paths;
1045  }
1046 
1047  private int[] getRowsForPaths(java.util.List paths) {
1048  int rows[] = new int[paths.size()];
1049  for (int i = 0; i < paths.size(); i++) {
1050  int row = getRowForPath((TreePath)paths.get(i));
1051  rows[i] = row;
1052  }
1053  return rows;
1054  }
1055 
1056  private void expandRows(int rows[]) {
1057  for (int i=0; i< rows.length; i++) {
1058  expandRow(rows[i]);
1059  }
1060  }
1061 
1062  private void expandPaths(java.util.List paths, boolean scrollPathToVisible) {
1063  for (int i=0; i< paths.size(); i++) {
1064  expandPath((TreePath)paths.get(i));
1065  if (scrollPathToVisible) {
1066  scrollPathToVisible((TreePath)paths.get(i));
1067  }
1068  }
1069  }
1070 
1074  protected void setKeyboardActions() {
1075  Action deleteAction = new AbstractAction() {
1076  public void actionPerformed(ActionEvent evt) {
1077  MatrixTreeNode[] nodes = getSelectionNodes();
1078  if (nodes == null) {
1079  return;
1080  }
1081  DeleteDialog deleteDialog = DeleteDialog.getDeleteDialog(nodes, getLocationOnScreen(), getSize());
1082  deleteDialog.show();
1083  }
1084  };
1085 
1086  Action searchAction = new AbstractAction() {
1087  public void actionPerformed(ActionEvent evt) {
1088  Point topLeft = new Point(getLocationOnScreen());
1089  SearchDialog searchDialog = SearchDialog.getSearchDialog(topLeft, getSize());
1090  searchDialog.show();
1091  }
1092  };
1093 
1094  Action increaseFontAction = new AbstractAction() {
1095  public void actionPerformed(ActionEvent evt) {
1096  setFontSize(currentFontSize+1);
1097  java.util.List expandedChildren = getExpandedChildren((TreeNode)getModel().getRoot());
1098  ((DefaultTreeModel) getModel()).reload();
1099  expandPaths(expandedChildren, false);
1100 
1101  }
1102  };
1103 
1104  Action decreaseFontAction = new AbstractAction() {
1105  public void actionPerformed(ActionEvent evt) {
1106  setFontSize(currentFontSize-1);
1107  java.util.List expandedChildren = getExpandedChildren((TreeNode)getModel().getRoot());
1108  ((DefaultTreeModel) getModel()).reload();
1109  expandPaths(expandedChildren, false);
1110  }
1111  };
1112 
1113  Action normalFontAction = new AbstractAction() {
1114  public void actionPerformed(ActionEvent evt) {
1115  setFontSize(initialFontSize);
1116  java.util.List expandedChildren = getExpandedChildren((TreeNode)getModel().getRoot());
1117  ((DefaultTreeModel) getModel()).reload();
1118  expandPaths(expandedChildren, false);
1119  }
1120  };
1121 
1122 
1123  getInputMap().put(KeyStroke.getKeyStroke("DELETE"), "delete");
1124  getActionMap().put("delete", deleteAction);
1125  getInputMap().put(KeyStroke.getKeyStroke("shift ctrl J"), "search");
1126  getActionMap().put("search", searchAction);
1127  getInputMap().put(KeyStroke.getKeyStroke("shift ctrl EQUALS"), "increase_font_size");
1128  getActionMap().put("increase_font_size", increaseFontAction);
1129  getInputMap().put(KeyStroke.getKeyStroke("shift ctrl ADD"), "increase_font_size");
1130  getActionMap().put("increase_font_size", increaseFontAction);
1131  getInputMap().put(KeyStroke.getKeyStroke("shift ctrl MINUS"), "decrease_font_size");
1132  getActionMap().put("decrease_font_size", decreaseFontAction);
1133  getInputMap().put(KeyStroke.getKeyStroke("shift ctrl SUBTRACT"), "decrease_font_size");
1134  getActionMap().put("decrease_font_size", decreaseFontAction);
1135  getInputMap().put(KeyStroke.getKeyStroke("shift ctrl BACK_SPACE"), "normal_font_size");
1136  getActionMap().put("normal_font_size", normalFontAction);
1137 
1138  }
1139 
1140  public void removeKeyStroke(String key) {
1141  getInputMap().put(KeyStroke.getKeyStroke(key), "none");
1142  }
1143 
1144 
1145  //}}}
1146 
1147  //{{{ Private Methods
1148 
1154  private void insertLoadingNode(MatrixTreeNode parentNode) {
1155  int insertIndex = 0;
1156  if (parentNode.getChildCount() > 0) {
1157  insertIndex = parentNode.getChildCount()-1;
1158  }
1159  ((DefaultTreeModel) getModel()).insertNodeInto(new LoadingNode(), parentNode, insertIndex);
1160  }
1161 
1166  private void removeLoadingNode(MatrixTreeNode parent) {
1167  // we only want to remove the loading node from the specifed parent
1168  // so we can't use TreeModel.removeNodeFromParent()
1169  int[] childIndex = new int[1];
1170  Object[] removedArray = new Object[1];
1171 
1172  LoadingNode loadingNode = null;
1173  for (Enumeration children = parent.children(); children.hasMoreElements();) {
1174  MatrixTreeNode nextNode = (MatrixTreeNode) children.nextElement();
1175  if (nextNode instanceof LoadingNode) {
1176  loadingNode = (LoadingNode) nextNode;
1177  break;
1178  }
1179  }
1180  if (loadingNode != null) {
1181  childIndex[0] = parent.getIndex(loadingNode);
1182  parent.remove(childIndex[0]);
1183  loadingNode = null;
1184  removedArray[0] = loadingNode;
1185  ((DefaultTreeModel) getModel()).nodesWereRemoved(parent, childIndex, removedArray);
1186  }
1187  }
1188 
1195  private void removeChildNodes(MatrixTreeNode parent) {
1196  Object[] removedArray = new Object[parent.getChildCount()];
1197  int i = 0;
1198 
1199  for (Enumeration children = parent.children(); children.hasMoreElements();) {
1200  MatrixTreeNode nextNode = (MatrixTreeNode) children.nextElement();
1201  if (nextNode != null) {
1202  removeChildNodes(nextNode);
1203  removedArray[i] = nextNode;
1204  i++;
1205  }
1206  }
1207 
1208  if (i > 0) {
1209  for (int j = i; j > 0; j--) {
1210  MatrixTreeModelBus.removeNodeFromParent((MatrixTreeNode)removedArray[j-1]);
1211  }
1212  MatrixTreeModelBus.nodeChanged(parent);
1213  }
1214  }
1215 
1216 
1222  public void insertExpandNextNode(MatrixTreeNode parentNode) {
1223  int modifier = 0;
1224  if (parentNode.getAsset().getTotalKidsLoaded() > 0) {
1225  modifier = 1;
1226  }
1227 
1228  if ((AssetManager.getLimit() <= (parentNode.getChildCount()-modifier)) && (parentNode.getAsset().getNumKids() == -1 || (parentNode.getAsset().getNumKids() > (parentNode.getAsset().getTotalKidsLoaded()+AssetManager.getLimit())))) {
1229  MatrixTreeModelBus.insertNodeInto((MatrixTreeNode)new ExpandingNextNode(parentNode.getAsset().getNumKids(),parentNode.getChildCount(),parentNode.getAsset().getTotalKidsLoaded()), parentNode, parentNode.getChildCount());
1230  }
1231  }
1232 
1237  private void removeExpandNextNode(MatrixTreeNode parent) {
1238  if (hasNextNode(parent)) {
1239  MatrixTreeNode node = (MatrixTreeNode) parent.getChildAt(parent.getChildCount()-1);
1241  }
1242  }
1243 
1249  public void insertExpandPreviousNode(MatrixTreeNode parentNode) {
1250  if ((parentNode.getAsset().getTotalKidsLoaded() > 0)) {
1251  MatrixTreeModelBus.insertNodeInto((MatrixTreeNode)new ExpandingPreviousNode(parentNode.getAsset().getNumKids(),parentNode.getChildCount(),parentNode.getAsset().getTotalKidsLoaded()), parentNode, 0);
1252  }
1253  }
1254 
1259  private void removeExpandPreviousNode(MatrixTreeNode parent) {
1260 
1261  if (hasPreviousNode(parent)) {
1262  MatrixTreeNode node = (MatrixTreeNode) parent.getChildAt(0);
1264  }
1265  }
1266 
1267 
1274  private void addMenuItem(JMenuItem item, JPopupMenu menu, ActionListener l) {
1275  item.addActionListener(l);
1276  item.setFont(getFontInUse());
1277  menu.add(item);
1278  }
1279 
1287  private JPopupMenu getNewLinkMenu(
1288  final MatrixTreeNode[] sources,
1289  final MatrixTreeNode parent,
1290  final int index,
1291  final int prevIndex) {
1292 
1293  JPopupMenu newLinkMenu = new JPopupMenu();
1294 
1295  final JMenuItem moveMenuItem = new JMenuItem(Matrix.translate("asset_map_menu_move_here"));
1296  final JMenuItem newLinkMenuItem = new JMenuItem(Matrix.translate("asset_map_menu_link_here"));
1297  final JMenuItem cloneMenuItem = new JMenuItem(Matrix.translate("asset_map_menu_clone_here"));
1298  final JMenuItem cancelMenuItem = new JMenuItem(Matrix.translate("asset_map_menu_cancel"));
1299 
1300  ActionListener listener = new ActionListener() {
1301  public void actionPerformed(ActionEvent evt) {
1302  String type = NewLinkEvent.LINK_TYPE_MOVE;
1303  if (evt.getSource().equals(moveMenuItem)) {
1304  type = NewLinkEvent.LINK_TYPE_MOVE;
1305  } else if (evt.getSource().equals(newLinkMenuItem)) {
1306  type = NewLinkEvent.LINK_TYPE_NEW_LINK;
1307  } else if (evt.getSource().equals(cloneMenuItem)) {
1308  type = NewLinkEvent.LINK_TYPE_CLONE;
1309  } else if (evt.getSource().equals(cancelMenuItem)) {
1310  return;
1311  }
1312  fireCreateLink(type, sources, parent, index, prevIndex);
1313  }
1314  };
1315 
1316  addMenuItem(moveMenuItem, newLinkMenu, listener);
1317  addMenuItem(newLinkMenuItem, newLinkMenu, listener);
1318  addMenuItem(cloneMenuItem, newLinkMenu, listener);
1319  newLinkMenu.addSeparator();
1320  addMenuItem(cancelMenuItem, newLinkMenu, listener);
1321 
1322  return newLinkMenu;
1323  }
1324 
1332  private JPopupMenu getNewAssetMenu(
1333  final String typeCode,
1334  final MatrixTreeNode parent,
1335  final int index) {
1336  JPopupMenu createMenu = new JPopupMenu();
1337 
1338  final JMenuItem createItem = new JMenuItem(Matrix.translate("asset_map_menu_create_here"));
1339  final JMenuItem cancelItem = new JMenuItem(Matrix.translate("asset_map_menu_cancel"));
1340 
1341  ActionListener listener = new ActionListener() {
1342  public void actionPerformed(ActionEvent evt) {
1343  if (evt.getSource().equals(createItem)) {
1344  fireNewAsset(typeCode, parent, index);
1345  }
1346  }
1347  };
1348  addMenuItem(createItem, createMenu, listener);
1349  addMenuItem(cancelItem, createMenu, listener);
1350 
1351  return createMenu;
1352  }
1353 
1357  private void initDoubleBufferImage() {
1358  int w = getWidth();
1359  int h = getHeight();
1360  dblBuffer = (BufferedImage) createImage(w, h);
1361  Graphics2D gc = dblBuffer.createGraphics();
1362  gc.setColor(getBackground());
1363  gc.fillRect(0, 0, w, h);
1364  }
1365 
1366 
1367  //}}}
1368 
1369  //{{{ Inner Classes
1370 
1375  protected class DoubleClickHandler extends MouseAdapter {
1376  private TreePath[] selPaths = null;
1377 
1378  public void mouseReleased(MouseEvent evt) {
1379  selPaths = getSelectionPaths();
1380  }
1381 
1382  public void mousePressed(MouseEvent evt) {
1383  // we should remove the Expending Nodes from node selections
1384  TreePath[] paths = getSelectionPaths();
1385  if (paths != null) {
1386  int c = 0;
1387  TreePath[] newPaths = new TreePath[paths.length+1];
1388  for (int i=0; i < paths.length; i++) {
1389  Object node = paths[i].getLastPathComponent();
1390  if (!(node instanceof ExpandingNode)) {
1391  newPaths[c++] = paths[i];
1392  }
1393  }
1394  setSelectionPaths(newPaths);
1395  }
1396  }
1397 
1398  public void mouseClicked(MouseEvent evt) {
1399  if (evt.getClickCount() == 1) {
1400  final TreePath treePath = getPathForLocation(evt.getX(), evt.getY());
1401  if (treePath == null)
1402  return;
1403  final MatrixTreeNode node = (MatrixTreeNode) treePath.getLastPathComponent();
1404  if (node instanceof ExpandingNode) {
1405  ((ExpandingNode)node).switchName(evt.getX(),tree.getPathBounds(treePath).getX());
1406  ((DefaultTreeModel) getModel()).nodeChanged(node);
1407  // keep the selection
1408  if (selPaths != null) {
1409  setSelectionPaths(selPaths);
1410  selPaths = null;
1411  }
1412  }
1413  return;
1414  }
1415 
1416  if (evt.getClickCount() != 2)
1417  return;
1418 
1419  final TreePath treePath = getPathForLocation(evt.getX(), evt.getY());
1420  if (treePath == null)
1421  return;
1422 
1423  if (getToggleClickCount() != 2) {
1424  final Point point = evt.getPoint();
1425  final MatrixTreeNode node = (MatrixTreeNode) treePath.getLastPathComponent();
1426 
1427  if (!node.getAsset().childrenLoaded()) {
1428  AssetRefreshWorker worker = new AssetRefreshWorker(node, true);
1429  worker.start();
1430  } else {
1431  fireNodeDoubleClicked(treePath, point);
1432  }
1433  } else {
1434  final MatrixTreeNode node = (MatrixTreeNode) treePath.getLastPathComponent();
1435 
1436  if (node instanceof ExpandingNextNode) {
1437  final MatrixTreeNode parent = (MatrixTreeNode)node.getParent();
1438  int start = ((ExpandingNextNode)node).getStartLoc(evt.getX(),tree.getPathBounds(treePath).getX());
1439  if (start > -1) {
1440  loadChildAssets(parent, "next", start, -1);
1441  }
1442  return;
1443  } else if (node instanceof ExpandingPreviousNode) {
1444  final MatrixTreeNode parent = (MatrixTreeNode)node.getParent();
1445  int start = ((ExpandingPreviousNode)node).getStartLoc(evt.getX(),tree.getPathBounds(treePath).getX());
1446  if (start > -1) {
1447  loadChildAssets(parent, "prev", start, -1);
1448  }
1449  return;
1450  }
1451  }
1452  }
1453  }//end class DoubleClickHandler
1454 
1455 
1464  protected class MenuHandler extends MouseAdapter {
1465 
1466  private ActionListener addMenuListener;
1467 
1472  public MenuHandler() {
1473  addMenuListener = MatrixMenus.getMatrixTreeAddMenuListener(MatrixTree.this);
1474  }
1475 
1480  public void mouseClicked(MouseEvent evt) {
1481 
1482  if (!GUIUtilities.isRightMouseButton(evt))
1483  return;
1484  // if we are in cue mode. we dont want to show any of the
1485  // right click menus
1486  if (inCueMode)
1487  return;
1488 
1489  JPopupMenu menu = null;
1490 
1491  // if the click occured where there was no node, get a menu
1492  // for void space
1493  if (getPathForLocation(evt.getX(), evt.getY()) == null) {
1494  menu = getMenuForVoidSpace();
1495  } else {
1496  TreePath[] selectedPaths
1497  = getSelectionPathsForLocation(evt.getX(), evt.getY());
1498  setSelectionPaths(selectedPaths);
1499 
1500  menu = (selectedPaths.length == 1)
1503  }
1504  if (menu != null)
1505  menu.show(MatrixTree.this, evt.getX(), evt.getY());
1506  }
1507 
1513  protected JPopupMenu getMenuForVoidSpace() {
1514  if (isInAssetFinderMode) return null;
1515  return MatrixMenus.getPopupAddMenu(addMenuListener);
1516  }
1517 
1524  protected TreePath[] getSelectionPathsForLocation(int x, int y) {
1525  TreePath path = getPathForLocation(x, y);
1526  TreePath[] selPaths = getSelectionPaths();
1527 
1528  // if the path for the right click does not exist, set the
1529  // selection path to the closest path near the x,y co-ordinate set
1530  if (path == null) {
1531  path = getClosestPathForLocation(x, y);
1532  } else if (selPaths != null) {
1533  // check to see if the clicked node was in
1534  // the current selection path
1535  if (selPaths.length > 1) {
1536  boolean found = false;
1537  for (int i = 0; i < selPaths.length; i++) {
1538  if (selPaths[i].getLastPathComponent()
1539  == path.getLastPathComponent()) {
1540  found = true;
1541  break;
1542  }
1543  }
1544  // if the clicked node is in the selected nodes, then
1545  // keep the selection
1546  if (found)
1547  return selPaths;
1548  }
1549  }
1550  // if the clicked node was not in the selection, or there was
1551  // zero or one selected node, set the selection to the clicked node
1552  TreePath[] paths = new TreePath[] { path };
1553 
1554  return paths;
1555  }
1556 
1561  protected JPopupMenu getMenuForSingleSelection() {
1562 
1563  JPopupMenu menu = null;
1564  final MatrixTreeNode node = getSelectionNode();
1565 
1566  // if the node is not accessible, we don't want the users
1567  // to be able bring up an menu for it
1568  if (!node.getAsset().isAccessible())
1569  return null;
1570 
1571  if (isInAssetFinderMode) {
1572  if (MatrixTreeBus.typeIsRestricted(node.getAsset().getType())) {
1573  menu = MatrixMenus.getUseMeMenu(node);
1574  } else {
1575  return null;
1576  }
1577  } else {
1578  menu = MatrixMenus.getPopupScreenMenu(node);
1579  menu.addSeparator();
1580 
1581  // if there are any ancillery items add them after the sperator
1582  // and before the add menu
1583  JMenuItem[] items = getAncillaryMenuItems();
1584  if (items != null) {
1585  for (int i = 0; i < items.length; i++) {
1586  items[i].setFont(getFontInUse());
1587  menu.add(items[i]);
1588  }
1589  }
1590 
1591  // when we click on a node and choose add, we want to go
1592  // straight into add mode in matrix with the node clicked on
1593  // as the parent of the new node
1594  ActionListener explicitAddListener = new ActionListener() {
1595  public void actionPerformed(ActionEvent evt) {
1596  fireNewAsset(
1597  MatrixMenus.getTypeCodeFromEvent(evt),
1598  node,
1599  -1 // let the MatrixTreeCom handle to pos
1600  );
1601  }
1602  };
1603 
1604  JMenu addMenu = MatrixMenus.getAddMenu(explicitAddListener);
1605  addMenu.setText(Matrix.translate("asset_map_menu_new_child"));
1606  addMenu.setFont(getFontInUse());
1607  menu.add(addMenu);
1608  }
1609 
1610  return menu;
1611  }
1612 
1617  protected JPopupMenu getMenuForMultipleSelection() {
1618  if (isInAssetFinderMode) return null;
1619 
1620  JPopupMenu menu = new JPopupMenu();
1621  final JMenuItem moveItem = new JMenuItem(Matrix.translate("asset_map_menu_move"));
1622  final JMenuItem newLinkItem = new JMenuItem(Matrix.translate("asset_map_menu_link"));
1623  final JMenuItem cloneItem = new JMenuItem(Matrix.translate("asset_map_menu_clone"));
1624 
1625  ActionListener multiplelistener = new ActionListener() {
1626  public void actionPerformed(ActionEvent evt) {
1627  if (evt.getSource() == moveItem)
1628  multipleMoveType = NewLinkEvent.LINK_TYPE_MOVE;
1629  else if (evt.getSource() == newLinkItem)
1630  multipleMoveType = NewLinkEvent.LINK_TYPE_NEW_LINK;
1631  else if (evt.getSource() == cloneItem)
1632  multipleMoveType = NewLinkEvent.LINK_TYPE_CLONE;
1633  startCueMode(getSelectionPaths());
1634  }
1635  };
1636 
1637  moveItem.addActionListener(multiplelistener);
1638  newLinkItem.addActionListener(multiplelistener);
1639  cloneItem.addActionListener(multiplelistener);
1640 
1641  // fonts
1642  moveItem.setFont(getFontInUse());
1643  newLinkItem.setFont(getFontInUse());
1644  cloneItem.setFont(getFontInUse());
1645 
1646  menu.add(moveItem);
1647  menu.add(newLinkItem);
1648  menu.add(cloneItem);
1649 
1650  return menu;
1651  }
1652 
1658  protected JMenuItem[] getAncillaryMenuItems() {
1659  JMenuItem[] items = new JMenuItem[3];
1660  final JMenuItem teleportItem = new JMenuItem(Matrix.translate("asset_map_menu_teleport"));
1661  final JMenuItem refreshItem = new JMenuItem(Matrix.translate("asset_map_menu_refresh"));
1662 
1663  // Work out the title of the new previous child menu item
1664  final String newChildPreviousItemTitle;
1665  if (lastTypeCodeCreated != null) {
1666  Object[] transArgs = { ((AssetType) AssetManager.getAssetType(lastTypeCodeCreated)).getName() };
1667  newChildPreviousItemTitle = Matrix.translate("asset_map_menu_new_child_previous", transArgs);
1668  } else {
1669  newChildPreviousItemTitle = Matrix.translate("asset_map_menu_no_previous_child");
1670  }
1671 
1672  final JMenuItem newChildPreviousItem = new JMenuItem(newChildPreviousItemTitle);
1673  // Disable the new child previous item if assets have not previously been created yet
1674  if (lastTypeCodeCreated == null) {
1675  newChildPreviousItem.setEnabled(false);
1676  }
1677 
1678  ActionListener extrasListener = new ActionListener() {
1679  public void actionPerformed(ActionEvent evt) {
1680  if (evt.getSource().equals(teleportItem)) {
1682  } else if (evt.getSource().equals(refreshItem)) {
1683  String [] assetids = new String[] { getSelectionNode().getAsset().getId() };
1684  AssetRefreshWorker worker = new AssetRefreshWorker(assetids, true);
1685  worker.start();
1686  } else if (evt.getSource().equals(newChildPreviousItem)) {
1687  // Get the selected node to use as the parent and let MatrixTreeComm handle the tree position
1688  final MatrixTreeNode node = getSelectionNode();
1689  fireNewAsset(lastTypeCodeCreated, node, -1);
1690  }
1691  }
1692  };
1693 
1694  // only show the teleport menu item if there is only one selected
1695  // node and its a leaf, and its not the root node in the current tree
1696  if (!(getSelectionNode().isLeaf())
1697  && (getSelectionNode() != getModel().getRoot())
1698  && !(isMultipleSelection())) {
1699  teleportItem.addActionListener(extrasListener);
1700  } else {
1701  teleportItem.setEnabled(false);
1702  }
1703 
1704  refreshItem.addActionListener(extrasListener);
1705  newChildPreviousItem.addActionListener(extrasListener);
1706  items[0] = teleportItem;
1707  items[1] = refreshItem;
1708  items[2] = newChildPreviousItem;
1709 
1710  return items;
1711  }
1712  }//end class MenuHandler
1713 
1718  protected class DragHandler extends DragSourceAdapter
1719  implements DragGestureListener {
1720 
1721  protected Point dragOffset = new Point(5, 5);
1722  protected TreePath[] dragPaths;
1723  private boolean dragImageSupport = false;
1724 
1725  /* DragGestureListener methods */
1726 
1732  public void dragGestureRecognized(DragGestureEvent dge) {
1733 
1734  if (isInAssetFinderMode()) return;
1735 
1736  Point initPoint = dge.getDragOrigin();
1737  if (getPathForLocation(initPoint.x, initPoint.y) == null)
1738  return;
1739 
1740  dragPaths = getSelectionPaths();
1741 
1742  if (dragPaths != null) {
1743  BufferedImage dragImage = (BufferedImage) getDragImageForPaths(dragPaths);
1744 
1745  Point topLeft = new Point(getPathBounds(dragPaths).getLocation());
1746  Point origin = dge.getDragOrigin();
1747  dragOffset.setLocation(origin.getX() - topLeft.getX(), origin.getY() - topLeft.getY());
1748 
1749  MatrixTreeTransferable transferable = new MatrixTreeTransferable(dragPaths);
1750  DragImageExchange.setDragImage(dragImage, dragOffset);
1751  dragPaths = null;
1752 
1753  // only add the drag image if its supported
1754  if (dge.getDragSource().isDragImageSupported()) {
1755  dragImageSupport = true;
1756  dge.startDrag(
1757  new Cursor(Cursor.DEFAULT_CURSOR),
1758  dragImage,
1759  dragOffset,
1760  transferable,
1761  this
1762  );
1763  } else {
1764  dge.startDrag(
1765  new Cursor(Cursor.DEFAULT_CURSOR),
1766  transferable,
1767  this
1768  );
1769  }
1770  }
1771  }
1772 
1773  public boolean isDragImageSupported() {
1774  return dragImageSupport;
1775  }
1776 
1777  }//end class DragHandler
1778 
1785  protected class DropHandler implements DropTargetListener {
1786 
1787  private boolean isDropping = false;
1788  protected Point initMousePt;
1789  private Point lastMousePt = null;
1790  private BufferedImage dragImage;
1791  private Point mouseOffset = new Point(5,5);
1792 
1797  public boolean isDropping() {
1798  return isDropping;
1799  }
1800 
1806  public void dragEnter(DropTargetDragEvent dtde) {
1807  dragImage = DragImageExchange.getDragImage();
1808  mouseOffset = DragImageExchange.getMouseOffset();
1809  isDropping = true;
1810  }
1811 
1817  public void dragExit(DropTargetEvent dte) {
1818  if (dragHandler == null) return;
1819 
1820  isDropping = false;
1821  dragImage = null;
1822 
1823  if (!dragHandler.isDragImageSupported())
1824  repaint();
1825  }
1826 
1832  public void dragOver(DropTargetDragEvent dtde) {
1833  if (dragHandler == null) return;
1834 
1835  if (lastMousePt != null && lastMousePt.equals(dtde.getLocation()))
1836  return;
1837  if (initMousePt == null) {
1838  initMousePt = dtde.getLocation();
1839  SwingUtilities.convertPointFromScreen(initMousePt, MatrixTree.this);
1840  }
1841  lastMousePt = dtde.getLocation();
1842  if (!dragHandler.isDragImageSupported())
1843  repaint();
1844  }
1845 
1851  public void drop(DropTargetDropEvent dtde) {
1852  if (dragHandler == null) return;
1853 
1854  Transferable transfer = dtde.getTransferable();
1855  java.util.List paths = null;
1856  try {
1857  paths = (java.util.List) transfer.getTransferData(
1858  MatrixTreeTransferable.TREE_NODE_FLAVOUR);
1859  } catch (UnsupportedFlavorException ufe) {
1860  ufe.printStackTrace();
1861  } catch (IOException ioe) {
1862  ioe.printStackTrace();
1863  }
1864 
1865  Iterator iterator = paths.iterator();
1866  while (iterator.hasNext()) {
1867  TreePath path = (TreePath) iterator.next();
1868  MatrixTreeNode node = (MatrixTreeNode) path.getLastPathComponent();
1869  if (!canMoveNode(node)) {
1870  GUIUtilities.error(Matrix.translate("asset_map_error_move_shadow_nodes"), Matrix.translate("asset_map_dialog_title_error"));
1871  dtde.rejectDrop();
1872  isDropping = false;
1873  if (!dragHandler.isDragImageSupported())
1874  repaint();
1875  return;
1876  }
1877  }
1878 
1879  DragImageExchange.completeExchange();
1880  isDropping = false;
1881  lastMousePt = null;
1882 
1883  if (!dragHandler.isDragImageSupported())
1884  repaint();
1885  startCueMode((TreePath[]) paths.toArray(new TreePath[paths.size()]));
1886  Point p = dtde.getLocation();
1887  TreePath path = getClosestPathForLocation((int) p.getX(),(int) p.getY());
1888  if (path != null)
1889  drawCueLine(path, (int) p.getY());
1890  }
1891 
1897  protected void paintDropImage(Graphics2D g2d) {
1898  if (dragHandler == null) return;
1899 
1900  if (dragImage == null || dragHandler.isDragImageSupported())
1901  return;
1902 
1903  int x = lastMousePt.x - mouseOffset.x;
1904  int y = lastMousePt.y - mouseOffset.y;
1905 
1906  g2d.drawImage(dragImage, x, y, MatrixTree.this);
1907  }
1908 
1913  public void dropActionChanged(DropTargetDragEvent dtde) {}
1914 
1915  }//end class DropHandler
1916 
1923  protected class MatrixCueGestureHandler extends CueGestureHandler {
1924 
1931  protected TreePath[] filterMultipleNodes(TreePath[] sourcePaths) {
1932  // we want to filter out the nodes whos parents already exist in
1933  // the move operation, as these nodes will be moved anyway
1934  java.util.List realSourcePaths = new ArrayList();
1935  boolean chuck = false;
1936 
1937  for (int i = 0; i < sourcePaths.length; i++) {
1938  chuck = false;
1939  for (int j = 0; j < sourcePaths.length; j++) {
1940  if (i == j)
1941  continue;
1942  if (sourcePaths[j].isDescendant(sourcePaths[i]))
1943  chuck = true;
1944  }
1945  if (!chuck)
1946  realSourcePaths.add(sourcePaths[i]);
1947  }
1948  return (TreePath[]) realSourcePaths.toArray(new TreePath[realSourcePaths.size()]);
1949  }
1950 
1956  protected boolean pointTriggersMove(Point p) {
1957  if (isInAssetFinderMode()) {
1958  return false;
1959  } else {
1960  return nodeIconContainsPoint(p);
1961  }
1962  }
1963 
1964  }//end MatrixCueGestureHandler
1965 
1971  class MatrixTreeUI extends CueTree.CueTreeUI {
1972 
1973  public MatrixTreeUI() {
1974  setCueLineColor(UIManager.getColor("CueLine.stroke"));
1975  }
1976 
1981  protected MouseListener createMouseListener() {
1982  return new MatrixMouseHandler();
1983  }
1984 
1990  public class MatrixMouseHandler extends MouseHandler
1991  implements MouseMotionListener {
1992 
1993  boolean isDragging = false;
1994 
1999  public void mouseDragged(MouseEvent evt) {
2000  isDragging = true;
2001  }
2002 
2007  public void mouseMoved(MouseEvent evt) {}
2008 
2013  public void mousePressed(MouseEvent evt) {
2014  // we will set this tree as the last active tree
2015  MatrixTreeBus.setActiveTree((MatrixTree)evt.getSource());
2016  }
2017 
2022  public void mouseReleased(MouseEvent evt) {
2023  if (isDragging) {
2024  isDragging = false;
2025  return;
2026  }
2027  int mouseX = evt.getX();
2028  int mouseY = evt.getY();
2029 
2030  // we want it so that if we click outside of an exapansion control
2031  // and not on a node (eg. void space) then the selection is cleared
2032 
2033  TreePath path = getClosestPathForLocation(tree, mouseX, mouseY);
2034  boolean isControl = isLocationInExpandControl(path, mouseX, mouseY);
2035  if ((getPathForLocation(mouseX, mouseY) == null) && !isControl && !GUIUtilities.isRightMouseButton(evt))
2036  clearSelection();
2037  else {
2038  // copied from java 1.5 instead of using super.mouseReleased(evt)
2039  // condition checking in BasicTreeUI$Handler.mouseReleased is broken in 1.6
2040  if ((!evt.isConsumed())) {
2041  handleSelection(evt);
2042  }
2043  }
2044  }
2045 
2046  // copied from java 1.5 source code
2047  // drag-n-drop feature added in 1.6 breaks MatrixTree expansion and selection
2048  void handleSelection(MouseEvent e) {
2049  if(tree != null && tree.isEnabled()) {
2050  if (isEditing(tree) && tree.getInvokesStopCellEditing() && !stopEditing(tree)) {
2051  return;
2052  }
2053  if (tree.isRequestFocusEnabled()) {
2054  tree.requestFocus();
2055  }
2056 
2057  TreePath path = getClosestPathForLocation(tree, e.getX(), e.getY());
2058  if(path != null) {
2059  Rectangle bounds = getPathBounds(tree, path);
2060  if(e.getY() > (bounds.y + bounds.height)) {
2061  return;
2062  }
2063 
2064  // Preferably checkForClickInExpandControl could take
2065  // the Event to do this it self!
2066  if(SwingUtilities.isLeftMouseButton(e))
2067  checkForClickInExpandControl(path, e.getX(), e.getY());
2068 
2069  int x = e.getX();
2070 
2071  // Perhaps they clicked the cell itself. If so,
2072  // select it.
2073  if (x > bounds.x) {
2074  if (x <= (bounds.x + bounds.width) && !startEditing(path, e)) {
2075  selectPathForEvent(path, e);
2076  }
2077  }
2078  }
2079  }
2080  }//end handleSelection
2081 
2082  }//end class MatrixMouseListener
2083  }//end class MatrixTreeUI
2084 
2085 }//end class MatrixTree
2086 
2087