1   package com.ontotext.gate.vr;
2   
3   import javax.swing.*;
4   import javax.swing.tree.*;
5   import javax.swing.event.*;
6   
7   
8   import com.ontotext.gate.vr.*;
9   
10  
11  import java.awt.event.*;
12  import java.awt.*;
13  import java.awt.datatransfer.*;
14  import java.awt.dnd.*;
15  import java.awt.dnd.peer.*;
16  
17  import java.io.*;
18  import java.util.*;
19  import java.util.List;
20  
21  import gate.creole.ontology.*;
22  import gate.util.GateRuntimeException;
23  import gate.util.Out;
24  
25  /**Extends {@link javax.swing.JTree} in order to provide
26   * means for editing the hierarchy of an ontology
27   * borislav popov */
28  public class EditableTreeView extends JTree
29    implements  TreeSelectionListener,DragGestureListener,DropTargetListener,
30                DragSourceListener
31  {
32  
33    /** Stores the selected node info */
34    protected TreePath selectedTreePath = null;
35  
36    /** The currently selected node*/
37    protected ClassNode selectedNode = null;
38  
39  
40    private JPopupMenu m_popUpMenu;
41  
42    private JMenuItem add_item;
43    private JMenuItem rename_item;
44    private JMenuItem editURI_item;
45    private JMenuItem remove_item;
46    private JMenuItem view_properties_item;
47  
48    private OEMainPanel mainPanel;
49  
50    /** dragSource needed for Drag'n'Drop */
51    private DragSource dragSource = null;
52    /** dragSourceContext needed for Drag'n'Drop */
53    private DragSourceContext dragSourceContext = null;
54    /** transferable obejct needed for Drag'n'Drop */
55    private Transferable transferable = null;
56  
57  
58    public EditableTreeView(OntoTreeModel model) {
59        super(model);
60        init();
61    }
62  
63    /**
64     * Synchronizes the expansion of the given trees.
65     * @param orig the original tree
66     * @param mirror the tree to mimic the expansion of the original
67     */
68    public static void synchronizeTreeExpansion(JTree orig, JTree mirror) {
69      /*create a Set of expanded node names*/
70      /*below will :
71        iterate all nodes of the tree
72        accumulate the path for each node as an arraylist
73        check for each passed node whether the treepath is expanded
74        and if expanded add it to the expanded list as a string.
75      */
76      Set expanded = new HashSet();
77      TreeModel model =  orig.getModel();
78  //    ArrayList expPaths = new ArrayList();
79  
80      ArrayList remains = new ArrayList();
81      ArrayList remainPaths = new ArrayList();
82  
83      remains.add(model.getRoot());
84      ArrayList rootPath = new ArrayList();
85      rootPath.add(model.getRoot());
86      remainPaths.add(rootPath);
87  
88      while (remains.size() > 0 ) {
89        Object node = remains.get(0);
90        int cc = model.getChildCount(node);
91        ArrayList parentPath = (ArrayList)remainPaths.get(0);
92        for ( int c = 0 ; c < cc ; c++) {
93          Object child = model.getChild(node,c);
94          remains.add(child);
95          ArrayList pp = new ArrayList(parentPath);
96          pp.add(child);
97          remainPaths.add(pp);
98        }
99        TreePath tp = new TreePath(parentPath.toArray());
100       if (orig.isExpanded(tp)) {
101         expanded.add(node.toString());
102       }
103       remains.remove(0);
104       remainPaths.remove(0);
105     } // while nodes remain
106 
107     /*expand the mirror tree according to the expanded nodes set*/
108     /*
109       iterate all the nodes and keep their paths
110       if a node is found as a string then expand it
111     */
112 
113     remains = new ArrayList();
114     remainPaths = new ArrayList();
115 
116     model = mirror.getModel();
117     remains.add(model.getRoot());
118     rootPath = new ArrayList();
119     rootPath.add(model.getRoot());
120     remainPaths.add(rootPath);
121 
122     while (remains.size() > 0 ) {
123       Object node = remains.get(0);
124       int cc = model.getChildCount(node);
125       ArrayList parentPath = (ArrayList)remainPaths.get(0);
126       for ( int c = 0 ; c < cc ; c++) {
127         Object child = model.getChild(node,c);
128         remains.add(child);
129         ArrayList pp = new ArrayList(parentPath);
130         pp.add(child);
131         remainPaths.add(pp);
132       }
133 
134       if (expanded.contains(node.toString()) ) {
135         TreePath tp = new TreePath(parentPath.toArray());
136         mirror.expandPath(tp);
137       }
138       remains.remove(0);
139       remainPaths.remove(0);
140     } // while nodes remain
141 
142   } // synchronizeTreeExpansion(JTree,JTree)
143 
144   /**
145    * Sets the main panel of this tree view.
146    * should be called anytime a tree is created.
147    * @param panel the main panel
148    */
149   public void setMainPanel(OEMainPanel panel) {
150     mainPanel = panel;
151   }
152 
153   /**Gets the main panel of this tree view
154    * @return the main panel   */
155   public OEMainPanel getmainPanel(){
156      return mainPanel;
157   }
158 
159   public void setModel(OntoTreeModel model){
160       if( model != null)
161         super.setModel(model);
162       else
163         super.setModel(null);
164   }
165 
166   /** Initializes the tree view.*/
167   private void init(){
168     getSelectionModel().setSelectionMode(
169     TreeSelectionModel.SINGLE_TREE_SELECTION);
170     DefaultTreeCellRenderer renderer = new DefaultTreeCellRenderer();
171     renderer.setLeafIcon(renderer.getDefaultClosedIcon());
172     this.setCellRenderer(renderer);
173     addMouseListener(new MyMouseAdapter(this));
174     m_popUpMenu= new  JPopupMenu();
175 
176     add_item = new JMenuItem("Add sub class");
177     add_item.addActionListener(new AddSubActListener());
178 
179     rename_item = new JMenuItem("Rename class");
180     rename_item.addActionListener(new RenameListener());
181 
182     remove_item = new JMenuItem("Remove class");
183     remove_item.addActionListener(new RemoveActListener());
184 
185     editURI_item =  new JMenuItem("Edit URI");
186     editURI_item.addActionListener(new EditURIListener());
187 
188 
189     m_popUpMenu.add(add_item);
190     m_popUpMenu.add(rename_item);
191     m_popUpMenu.add(editURI_item);
192     m_popUpMenu.add(remove_item);
193 
194     /* ------- DnD --------- */
195     /* in order to keep track of selecteNode and selectedPAth*/
196     addTreeSelectionListener(this);
197 
198     dragSource = DragSource.getDefaultDragSource() ;
199 
200 
201     DragGestureRecognizer dgr =
202       dragSource.createDefaultDragGestureRecognizer(
203         this,  //DragSource
204         DnDConstants.ACTION_MOVE, //specifies valid actions
205         this                              //DragGestureListener
206       );
207 
208     /* Eliminates right mouse clicks as valid actions - useful especially
209      * if you implement a JPopupMenu for the JTree*/
210     dgr.setSourceActions(dgr.getSourceActions() & ~InputEvent.BUTTON3_MASK);
211 
212     /* First argument:  Component to associate the target with
213      * Second argument: DropTargetListener */
214     DropTarget dropTarget = new DropTarget(this, this);
215 
216     putClientProperty("JTree.lineStyle", "Angled");
217 
218  } // init
219 
220   /*---------------drag and drop--------------*/
221 
222   public void dragGestureRecognized(DragGestureEvent event) {
223     //Get the selected node
224     ClassNode dragNode = this.getSelectedNode();
225 
226     if (dragNode != null) {
227       //Get the Transferable Object
228       transferable = (Transferable) dragNode;
229 
230 
231       //Select the appropriate cursor;
232       Cursor cursor = DragSource.DefaultMoveNoDrop;
233 
234       //begin the drag
235       dragSource.startDrag(event, cursor, transferable, this);
236     }
237 
238   } // dragGestureRecognized()
239 
240   public void drop(DropTargetDropEvent e) {
241 
242     Transferable tr = e.getTransferable();
243 
244     //flavor not supported, reject drop
245     if (!tr.isDataFlavorSupported( ClassNode.CLASS_NODE_FLAVOR)) {
246       e.rejectDrop();
247     }
248 
249     //get new parent node
250     Point loc = e.getLocation();
251     TreePath destinationPath = getPathForLocation(loc.x, loc.y);
252 
253     final String msg = testDropTarget(destinationPath, selectedTreePath);
254     if (msg != null) {
255       e.rejectDrop();
256 
257       SwingUtilities.invokeLater(new Runnable() {
258         public void run() {
259           JOptionPane.showMessageDialog(
260                mainPanel, msg, "Error Dialog", JOptionPane.ERROR_MESSAGE
261           );
262         }
263       });
264     } // drop
265 
266 
267     ClassNode newParent =
268       (ClassNode) destinationPath.getLastPathComponent();
269 
270     /*get old parent node*/
271     TreePath temPath;
272     if (null == (temPath = selectedTreePath.getParentPath())) {
273       throw new GateRuntimeException (
274         "The node being dragged has no parent." );
275     }
276     ClassNode oldParent = (ClassNode) temPath.getLastPathComponent();
277 
278     int action = e.getDropAction();
279 
280     try {
281       oldParent.removeSubNode((ClassNode)transferable);
282       newParent.addSubNode((ClassNode)transferable);
283       e.acceptDrop (DnDConstants.ACTION_MOVE);
284     }
285     catch (java.lang.IllegalStateException ils) {
286       e.rejectDrop();
287     }
288 
289     e.getDropTargetContext().dropComplete(true);
290 
291 
292     this.setExpandedState(temPath,true);
293     this.setExpandedState(destinationPath,true);
294 
295     this.updateUI();
296 
297 
298   } // drop
299 
300   public void dragExit(DragSourceEvent dsde) {
301     dsde.getDragSourceContext().setCursor(DragSource.DefaultMoveNoDrop);
302   }
303 
304   public void dragExit(DropTargetEvent e) {
305   }
306 
307 
308   public void dragEnter(DragSourceDragEvent dsde) {
309     dragSourceContext = dsde.getDragSourceContext();
310   }
311 
312   public void dragEnter(DropTargetDragEvent e) {
313 
314   }
315 
316   public void dropActionChanged(DragSourceDragEvent e) {
317     dragSourceContext = e.getDragSourceContext();
318   }
319 
320   public void dropActionChanged(DropTargetDragEvent e) {
321     e.acceptDrag(DnDConstants.ACTION_MOVE);
322   }
323 
324   public void dragOver(DragSourceDragEvent e) {
325     dragSourceContext = e.getDragSourceContext();
326   }
327 
328   public void dragOver(DropTargetDragEvent e) {
329     //set cursor location. Needed in setCursor method
330     Point cursorLocationBis = e.getLocation();
331     TreePath destinationPath =
332       getPathForLocation(cursorLocationBis.x, cursorLocationBis.y);
333 
334 
335     // if destination path is okay accept drop...
336     if (testDropTarget(destinationPath, selectedTreePath) == null){
337 
338         if ( null != dragSourceContext )
339           dragSourceContext.setCursor(DragSource.DefaultMoveDrop);
340 
341         e.acceptDrag(DnDConstants.ACTION_MOVE);
342     }
343     // ...otherwise reject drop
344     else {
345 
346         if ( null != dragSourceContext )
347           dragSourceContext.setCursor(DragSource.DefaultMoveNoDrop);
348 
349         e.rejectDrag() ;
350     } // else
351   } // dragOver()
352 
353 
354   public void dragDropEnd(DragSourceDropEvent e) {
355   } // dragDropEnd()
356 
357 
358   /** Convenience method to test whether drop location is valid
359   @param destination The destination path
360   @param dropper The path for the node to be dropped
361   @return null if no problems, otherwise an explanation
362   */
363   private String testDropTarget(TreePath destination, TreePath dropper) {
364     //Test 1.
365     boolean destinationPathIsNull = destination == null;
366     if (destinationPathIsNull)
367       return "Invalid drop location.";
368 
369     //Test 2.
370     ClassNode node = (ClassNode) destination.getLastPathComponent();
371 
372     if (destination.equals(dropper))
373       return "Destination cannot be same as source";
374 
375     //Test 3.
376     if ( dropper.isDescendant(destination))
377        return "Destination node cannot be a descendant.";
378 
379     //Test 4.
380     if ( dropper.getParentPath().equals(destination))
381        return "Destination node cannot be a parent.";
382 
383     return null;
384   } //testDropTarget()
385 
386   /** sets selected node */
387   public void valueChanged(TreeSelectionEvent evt) {
388     selectedTreePath = evt.getNewLeadSelectionPath();
389     if (selectedTreePath == null) {
390       selectedNode = null;
391       return;
392     }
393     selectedNode =
394       (ClassNode)selectedTreePath.getLastPathComponent();
395   } // valueChanged()
396 
397   /** Returns the selected node */
398   public ClassNode getSelectedNode() {
399     return selectedNode;
400   }
401 
402 
403 
404 
405   /*END---------------drag and drop--------------*/
406 
407 
408   class MyMouseAdapter extends MouseAdapter{
409       private EditableTreeView view;
410 
411       public MyMouseAdapter(EditableTreeView view){
412           this.view=view;
413       }
414 
415       public void mouseClicked(MouseEvent e){
416           TreePath path=view.getSelectionPath();
417           javax.swing.JTree tree = new javax.swing.JTree();
418           //IOntoFolder node =null;
419           IFolder node =null;
420           if(SwingUtilities.isRightMouseButton(e)){
421             if( path != null){
422               //node = (IOntoFolder)path.getLastPathComponent();
423               node = (IFolder)path.getLastPathComponent();
424               ClassNode cnode = (ClassNode) node;
425               if (cnode.getSource() instanceof Ontology) {
426                 rename_item.setEnabled(false);
427                 remove_item.setEnabled(false);
428               }
429               else {
430                 rename_item.setEnabled(true);
431                 remove_item.setEnabled(true);
432               }
433 
434               m_popUpMenu.show(view,e.getX(),e.getY());
435             }
436           }
437       }
438   } //class MyMouseAdapter
439 
440   /*Action Listener of the add pop up menu item */
441   class AddSubActListener implements ActionListener{
442     public void actionPerformed(ActionEvent e) {
443       JMenuItem item = (JMenuItem)e.getSource();
444       JPopupMenu popup = (JPopupMenu)item.getParent();
445       EditableTreeView tree = (EditableTreeView)popup.getInvoker();
446       OEMainPanel panel = tree.getmainPanel();
447 
448       if (null == panel) {
449         throw new GateRuntimeException(
450         "the main panel of the editor is not reachable\n "+
451         "upon add sub class from the popup");
452       }// if null
453 
454       OntologyEditor oe = panel.getOntologyEditor();
455 
456       if (null == oe) {
457         throw new GateRuntimeException(
458         "the ontology editor of the main panel is not reachable\n "+
459         "upon add sub class from the popup");
460       }// if null
461 
462       oe.addSubClass((int)EditableTreeView.this.getLocationOnScreen().getX()+50,
463         (int)EditableTreeView.this.getLocationOnScreen().getY()+50);
464 
465     } // actionPerformed();
466   } // class AddSubActListener
467 
468   /*Action Listener of the remove pop up menu item */
469   class RemoveActListener implements ActionListener{
470     public void actionPerformed(ActionEvent e) {
471       JMenuItem item = (JMenuItem)e.getSource();
472       JPopupMenu popup = (JPopupMenu)item.getParent();
473       EditableTreeView tree = (EditableTreeView)popup.getInvoker();
474       ClassNode node = (ClassNode)tree.getLastSelectedPathComponent();
475       OEMainPanel panel = tree.getmainPanel();
476 
477       if (null == panel) {
478         throw new GateRuntimeException(
479         "the main panel of the editor is not reachable\n "+
480         "upon add sub class from the popup");
481       }// if null
482 
483       OntologyEditor oe = panel.getOntologyEditor();
484 
485       if (null == oe) {
486         throw new GateRuntimeException(
487         "the ontology editor of the main panel is not reachable\n "+
488         "upon add sub class from the popup");
489       }// if null
490 
491       oe.removeClass(node);
492 
493     } // actionPerformed()
494   } // class RemoveActListener
495 
496   /*Action Listener of the edit URI pop up menu item */
497   class EditURIListener implements ActionListener{
498     public void actionPerformed(ActionEvent e) {
499       JMenuItem item = (JMenuItem)e.getSource();
500       JPopupMenu popup = (JPopupMenu)item.getParent();
501       EditableTreeView tree = (EditableTreeView)popup.getInvoker();
502       ClassNode node = (ClassNode)tree.getLastSelectedPathComponent();
503       OEMainPanel panel = tree.getmainPanel();
504 
505       if (null == panel) {
506         throw new GateRuntimeException(
507         "the main panel of the editor is not reachable\n "+
508         "upon rename class from the popup");
509       }// if null
510 
511       OntologyEditor oe = panel.getOntologyEditor();
512 
513       if (null == oe) {
514         throw new GateRuntimeException(
515         "the ontology editor of the main panel is not reachable\n "+
516         "upon rename class from the popup");
517       }// if null
518 
519       Object obj = node.getSource();
520       if ( null == obj ) {
521         throw new GateRuntimeException(
522           "the class/ontology is null; in EditURIListener");
523       }
524       if (obj instanceof OClass) {
525         oe.editClassURI((OClass)obj,0,0);
526       }
527       if (obj instanceof Ontology) {
528         oe.editURI((Ontology)obj,0,0);
529       }
530     } // actionPerformed()
531   } // class RemoveActListener
532 
533   /*Action Listener of the rename pop up menu item */
534   class RenameListener implements ActionListener{
535     public void actionPerformed(ActionEvent e) {
536       JMenuItem item = (JMenuItem)e.getSource();
537       JPopupMenu popup = (JPopupMenu)item.getParent();
538       EditableTreeView tree = (EditableTreeView)popup.getInvoker();
539       ClassNode node = (ClassNode)tree.getLastSelectedPathComponent();
540       OEMainPanel panel = tree.getmainPanel();
541 
542       if (null == panel) {
543         throw new GateRuntimeException(
544         "the main panel of the editor is not reachable\n "+
545         "upon rename class from the popup");
546       }// if null
547 
548       OntologyEditor oe = panel.getOntologyEditor();
549 
550       if (null == oe) {
551         throw new GateRuntimeException(
552         "the ontology editor of the main panel is not reachable\n "+
553         "upon rename class from the popup");
554       }// if null
555 
556       Object obj = node.getSource();
557       if ( null == obj ) {
558         throw new GateRuntimeException(
559           "the class/ontology is null; in EditURIListener");
560       }
561       if (obj instanceof OClass) {
562         OClass rc = (OClass)obj;
563         oe.renameClass(rc,node,0,0);
564       }
565       if (obj instanceof Ontology) {
566         Ontology ro = (Ontology) obj;
567         oe.renameOntology(ro,0,0);
568       }
569       EditableTreeView.this.updateUI();
570     } // actionPerformed()
571 
572   } // class RemoveActListener
573 
574 
575 } // class EditableTreeView