001/*
002 * Copyright (c) 2005-2010 The Regents of the University of California.
003 * All rights reserved.
004 *
005 * '$Author: welker $'
006 * '$Date: 2010-05-06 05:21:26 +0000 (Thu, 06 May 2010) $' 
007 * '$Revision: 24234 $'
008 * 
009 * Permission is hereby granted, without written agreement and without
010 * license or royalty fees, to use, copy, modify, and distribute this
011 * software and its documentation for any purpose, provided that the above
012 * copyright notice and the following two paragraphs appear in all copies
013 * of this software.
014 *
015 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
016 * FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
017 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
018 * THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
019 * SUCH DAMAGE.
020 *
021 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
022 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
023 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
024 * PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
025 * CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
026 * ENHANCEMENTS, OR MODIFICATIONS.
027 *
028 */
029
030package org.kepler.sms.gui;
031
032import java.awt.Component;
033import java.awt.Dimension;
034import java.awt.Point;
035import java.awt.datatransfer.DataFlavor;
036import java.awt.dnd.DnDConstants;
037import java.awt.dnd.DragGestureEvent;
038import java.awt.dnd.DragGestureListener;
039import java.awt.dnd.DragSource;
040import java.awt.dnd.DragSourceContext;
041import java.awt.dnd.DragSourceDragEvent;
042import java.awt.dnd.DragSourceDropEvent;
043import java.awt.dnd.DragSourceEvent;
044import java.awt.dnd.DragSourceListener;
045import java.awt.dnd.DropTarget;
046import java.awt.dnd.DropTargetDragEvent;
047import java.awt.dnd.DropTargetDropEvent;
048import java.awt.dnd.DropTargetEvent;
049import java.awt.dnd.DropTargetListener;
050import java.awt.event.ActionEvent;
051import java.awt.event.ActionListener;
052import java.awt.event.KeyEvent;
053import java.awt.event.MouseAdapter;
054import java.awt.event.MouseEvent;
055import java.net.URL;
056import java.util.Enumeration;
057import java.util.Iterator;
058import java.util.Vector;
059
060import javax.swing.BorderFactory;
061import javax.swing.Box;
062import javax.swing.BoxLayout;
063import javax.swing.DefaultListModel;
064import javax.swing.ImageIcon;
065import javax.swing.JButton;
066import javax.swing.JComponent;
067import javax.swing.JLabel;
068import javax.swing.JList;
069import javax.swing.JMenu;
070import javax.swing.JMenuItem;
071import javax.swing.JPanel;
072import javax.swing.JPopupMenu;
073import javax.swing.JScrollPane;
074import javax.swing.JTextArea;
075import javax.swing.JTextField;
076import javax.swing.JTree;
077import javax.swing.ListModel;
078import javax.swing.SwingConstants;
079import javax.swing.event.ListSelectionEvent;
080import javax.swing.event.ListSelectionListener;
081import javax.swing.event.TreeSelectionEvent;
082import javax.swing.event.TreeSelectionListener;
083import javax.swing.tree.DefaultMutableTreeNode;
084import javax.swing.tree.DefaultTreeCellRenderer;
085import javax.swing.tree.TreeNode;
086import javax.swing.tree.TreePath;
087
088import org.kepler.sms.NamedOntClass;
089import org.kepler.sms.NamedOntModel;
090import org.kepler.sms.NamedOntProperty;
091import org.kepler.sms.OntologyCatalog;
092import org.kepler.util.StaticResources;
093
094/**
095 * This class implements a simple panel for selecting classes from a tree widget
096 * and adding them to a target list. The panel uses drag and drop events,
097 * buttons, and right-click menu items for adding and removing classes.
098 * Right-click events also are used to navigate properties. This panel also
099 * provides a simple term search mechanism.
100 * 
101 * @author Shawn Bowers
102 */
103public class OntoClassSelectionJPanel extends JPanel {
104
105        //private JPopupMenu popup;
106        private OntoClassSelectionJTree _ontoTree;
107        private OntoClassSelectionJList _classList;
108        private JTextField _searchTxt;
109        private JTextArea _commentTxt;
110        private JButton _searchBtn;
111        private JButton _addBtn;
112        private JButton _removeBtn;
113        
114        /**
115         * Default constructor that initializes the panel, accepting all ontologies
116         * and having a default width and height.
117         */
118        public OntoClassSelectionJPanel() {
119                super();
120                init(false, 525, 350);
121        }
122
123        /**
124         * Initializes the panel with default width and height.
125         * 
126         * @param libraryOnly
127         *            if true, only loads the library indexed ontologies
128         */
129        public OntoClassSelectionJPanel(boolean libraryOnly) {
130                super();
131                init(libraryOnly, 525, 350);
132        }
133
134        /**
135         * Initializes the panel accepting all ontologies.
136         * 
137         * @param width
138         *            the width of the component
139         * @param height
140         *            the height of the component
141         */
142        public OntoClassSelectionJPanel(int width, int height) {
143                super();
144                init(false, width, height);
145        }
146
147        /**
148         * Initializes the panel with the appropriate ontologies, width, and height
149         * 
150         * @param libraryOnly
151         *            if true, only loads the library indexed ontologies
152         * @param width
153         *            the width of the component
154         * @param height
155         *            the height of the component
156         */
157        public OntoClassSelectionJPanel(boolean libraryOnly, int width, int height) {
158                super();
159                init(libraryOnly, width, height);
160        }
161
162        /**
163         * Provides access to the selected ontology classes
164         * 
165         * @return the current set of selections in the panel
166         */
167        public Vector<NamedOntClass> getNamedOntClasses() {
168                return getListAsVector();
169        }
170
171        /**
172         * adds the given NamedOntClass objects to the panel.
173         * 
174         * @param ontClass
175         *            the ontology class to add
176         */
177        public void addNamedOntClass(NamedOntClass ontClass) {
178                if (!getNamedOntClasses().contains(ontClass))
179                        addToList(ontClass);
180        }
181
182        /**
183         * Remove all selected classes.
184         */
185        public void clear() {
186                clearList();
187        }
188
189        // PRIVATE METHODS
190
191        /**
192         * Private method for initializing the panel
193         */
194        private void init(boolean libraryOnly, int length, int height) {
195
196                _searchTxt = new JTextField(14);
197                _searchBtn = new JButton("Search");
198                _addBtn = new JButton(">>");
199                _removeBtn = new JButton("<<");
200                
201                JScrollPane treeView = createTreeView(libraryOnly);
202                _ontoTree.setBorder(BorderFactory.createEmptyBorder(2, 5, 2, 5));
203
204                JPanel listView = createListView();
205                _classList.setBorder(BorderFactory.createEmptyBorder(2, 5, 2, 5));
206
207                // set up search button
208                _searchBtn.addActionListener(new ClassSearchButtonListener());
209                _searchTxt.addActionListener(new ClassSearchButtonListener());
210
211                // initialize the buttons
212                _addBtn.addActionListener(new AddButtonListener());
213                _addBtn.setEnabled(false);
214                _removeBtn.addActionListener(new RemoveButtonListener());
215                _removeBtn.setEnabled(false);
216
217                // the description text area
218                _commentTxt = new JTextArea();
219                _commentTxt.setEditable(false);
220                _commentTxt.setLineWrap(true);
221                _commentTxt.setWrapStyleWord(true);
222                _commentTxt.setEnabled(false);
223                JScrollPane commentView = new JScrollPane(_commentTxt,
224                                JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
225                                JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
226                commentView.setMaximumSize(new Dimension(length, 150));
227                commentView.setPreferredSize(new Dimension(length, 150));
228
229                // onto tree label
230                JPanel panel1 = new JPanel();
231                panel1.setLayout(new BoxLayout(panel1, BoxLayout.X_AXIS));
232                panel1.add(new JLabel("All Categories:", SwingConstants.LEFT));
233                panel1.add(Box.createHorizontalGlue());
234
235                // search
236                JPanel panel2 = new JPanel();
237                panel2.setLayout(new BoxLayout(panel2, BoxLayout.X_AXIS));
238                panel2.add(_searchTxt);
239                panel2.add(Box.createRigidArea(new Dimension(5, 0)));
240                panel2.add(_searchBtn);
241
242                // onto tree
243                JPanel panel3 = new JPanel();
244                panel3.setLayout(new BoxLayout(panel3, BoxLayout.Y_AXIS));
245                panel3.add(panel1);
246                panel3.add(Box.createRigidArea(new Dimension(0, 2)));
247                panel3.add(treeView);
248                panel3.add(Box.createRigidArea(new Dimension(0, 5)));
249                panel3.add(panel2);
250
251                // add and remove button panel
252                JPanel panel4 = new JPanel();
253                panel4.setLayout(new BoxLayout(panel4, BoxLayout.Y_AXIS));
254                panel4.add(_addBtn);
255                panel4.add(Box.createRigidArea(new Dimension(0, 5)));
256                panel4.add(_removeBtn);
257
258                // class list label
259                JPanel panel5 = new JPanel();
260                panel5.setLayout(new BoxLayout(panel5, BoxLayout.X_AXIS));
261                panel5.add(new JLabel("Selected Categories:", SwingConstants.LEFT));
262                panel5.add(Box.createHorizontalGlue());
263
264                // class list panel
265                JPanel panel6 = new JPanel();
266                panel6.setLayout(new BoxLayout(panel6, BoxLayout.Y_AXIS));
267                panel6.add(panel5);
268                panel6.add(Box.createRigidArea(new Dimension(0, 2)));
269                panel6.add(listView);
270
271                // top portion
272                JPanel panel7 = new JPanel();
273                panel7.setLayout(new BoxLayout(panel7, BoxLayout.X_AXIS));
274                panel7.add(panel3);
275                panel7.add(Box.createRigidArea(new Dimension(5, 0)));
276                panel7.add(panel4);
277                panel7.add(Box.createRigidArea(new Dimension(5, 0)));
278                panel7.add(panel6);
279
280                // comment/description label
281                JPanel panel8 = new JPanel();
282                panel8.setLayout(new BoxLayout(panel8, BoxLayout.X_AXIS));
283                panel8.add(new JLabel("Category Description:", SwingConstants.LEFT));
284                panel8.add(Box.createHorizontalGlue());
285
286                // top-level panel
287                JPanel panel = new JPanel();
288                panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
289                panel.add(panel7);
290                panel.add(Box.createRigidArea(new Dimension(0, 7)));
291                panel.add(panel8);
292                panel.add(Box.createRigidArea(new Dimension(0, 2)));
293                panel.add(commentView);
294
295                panel.setMaximumSize(new Dimension(length, height));
296                panel.setPreferredSize(new Dimension(length, height));
297
298                add(panel);
299        }
300
301        /**
302         * Private method that initiliazes and creates the tree view sub-panel
303         * 
304         * @param libraryOnly
305         *            true if only the library ontologies are selected
306         * @return a scroll pane containing the ontologies
307         */
308        private JScrollPane createTreeView(boolean libraryOnly) {
309                // create the default root node
310                DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode("");
311
312                // get each ont model for the library
313                Iterator ontModels = OntologyCatalog.instance().getNamedOntModels(
314                                libraryOnly);
315
316                while (ontModels.hasNext()) {
317                        // add ontologies to root
318                        NamedOntModel m = (NamedOntModel) ontModels.next();
319                        DefaultMutableTreeNode childNode = new DefaultMutableTreeNode(m);
320                        rootNode.add(childNode);
321                        // get each root class of the model
322                        Iterator rootClasses = m.getRootClasses(true);
323                        while (rootClasses.hasNext()) {
324                                // build tree from the root
325                                NamedOntClass root = (NamedOntClass) rootClasses.next();
326                                buildTree(root, childNode);
327                        }
328                }
329
330                // assign root to tree
331                _ontoTree = new OntoClassSelectionJTree(rootNode);
332                // configure tree
333                _ontoTree.setRootVisible(false);
334                _ontoTree.setCellRenderer(new OntoClassSelectionJTreeRenderer());
335                _ontoTree.setDragEnabled(false); // if true, causes problems on linux
336                _ontoTree
337                                .addTreeSelectionListener(new OntoClassTreeSelectionListener());
338                _ontoTree.setShowsRootHandles(true);
339
340                // wrap tree in scroll pane
341                return new JScrollPane(_ontoTree,
342                                JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
343                                JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
344        }
345
346        /**
347         * Private method that recursively initializes the tree
348         * 
349         * @param c
350         *            the named ont class parent
351         * @param parentNode
352         *            the parent tree node
353         */
354        private void buildTree(NamedOntClass c, DefaultMutableTreeNode parentNode) {
355                // add the class to the parent
356                DefaultMutableTreeNode childNode = new DefaultMutableTreeNode(c);
357                parentNode.add(childNode);
358                // get subclasses
359                Iterator subclasses = c.getNamedSubClasses(true);
360                while (subclasses.hasNext()) {
361                        NamedOntClass subclass = (NamedOntClass) subclasses.next();
362                        buildTree(subclass, childNode);
363                }
364        }
365
366        /**
367         * Private method that initiliazes and creates the list sub-panel
368         * 
369         * @return a pane containing the selected class list
370         */
371        private JPanel createListView() {
372                // initialize the class list
373                _classList = new OntoClassSelectionJList(new DefaultListModel());
374                _classList.setFixedCellWidth(175);
375                _classList.addListSelectionListener(new ClassListSelectionListener());
376
377                // add to drop target
378                OntoClassSelectionJListDropTarget t = new OntoClassSelectionJListDropTarget(
379                                _classList);
380
381                // overall pane
382                JPanel panel = new JPanel();
383                panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS));
384                panel.add(new JScrollPane(_classList,
385                                JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
386                                JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS));
387                return panel;
388        }
389
390        /**
391         * Private method to add ont classes to the list
392         * 
393         * @param ontClass
394         *            the NamedOntClass to add to the list
395         */
396        private void addToList(NamedOntClass ontClass) {
397                DefaultListModel mdl = (DefaultListModel) _classList.getModel();
398                if (!mdl.contains(ontClass)) {
399                        // String txt = ontClass.getOntologyName();
400                        mdl.addElement(ontClass);
401                }
402        }
403
404        /**
405         * Private method to clear the list
406         */
407        private void clearList() {
408                DefaultListModel mdl = (DefaultListModel) _classList.getModel();
409                mdl.removeAllElements();
410        }
411
412        /**
413         * Private method to return all items in the class list
414         * 
415         * @return a list of the selected NamedOntClasses
416         */
417        private Vector<NamedOntClass> getListAsVector() {
418                DefaultListModel mdl = (DefaultListModel) _classList.getModel();
419                Vector<NamedOntClass> result = new Vector<NamedOntClass>();
420                for (int i = 0; i < mdl.getSize(); i++)
421                        result.add((NamedOntClass)mdl.getElementAt(i));
422                return result;
423        }
424
425        /**
426         * Private method to return all items in the class list
427         * 
428         * @param ontClass
429         *            the NamedOntClass to remove from the list
430         */
431        private void removeFromList(NamedOntClass ontClass) {
432                DefaultListModel mdl = (DefaultListModel) _classList.getModel();
433                mdl.removeElement(ontClass);
434        }
435
436        /**
437         * Private method that returns tree nodes containing OntTreeNodes
438         * 
439         * @param str
440         *            search string
441         * @return set of paths in the ontology tree
442         */
443        private Vector findMatchingClasses(String str) {
444                DefaultMutableTreeNode root = (DefaultMutableTreeNode) _ontoTree
445                                .getModel().getRoot();
446                return findMatchingClasses(root, str);
447        }
448
449        /**
450         * Private method that returns tree nodes containing OntTreeNodes
451         * 
452         * @param root
453         *            The root of the tree to search
454         * @param str
455         *            The search string
456         */
457        private Vector findMatchingClasses(DefaultMutableTreeNode root, String str) {
458                Vector result = new Vector();
459                Object obj = root.getUserObject();
460                if (obj instanceof NamedOntClass) {
461                        NamedOntClass cls = (NamedOntClass) obj;
462                        if (approxMatch(cls.getName(), str)) {
463                                result.add(root);
464                                return result;
465                        }
466                }
467                Enumeration children = root.children();
468                while (children.hasMoreElements()) {
469                        DefaultMutableTreeNode child = (DefaultMutableTreeNode) children
470                                        .nextElement();
471                        Iterator ancestors = findMatchingClasses(child, str).iterator();
472                        while (ancestors.hasNext())
473                                result.add(ancestors.next());
474                }
475                return result;
476        }
477
478        /**
479         * Private method for checking whether two strings match
480         * 
481         * @param val1
482         *            first string to compare
483         * @param val2
484         *            second string to compare
485         * @return true if strings approximately match
486         */
487        private boolean approxMatch(String val1, String val2) {
488                val1 = val1.toLowerCase();
489                val2 = val2.toLowerCase();
490                if (val1.indexOf(val2) != -1 || val2.indexOf(val1) != -1)
491                        return true;
492                return false;
493        }
494
495        /**
496         * Private method for collapsing the ontology tree to just the ontology
497         * nodes
498         */
499        private void collapseTree() {
500                int row = _ontoTree.getRowCount() - 1;
501                while (row >= 0) {
502                        _ontoTree.collapseRow(row);
503                        row--;
504                }
505        }
506
507        /**
508         * Private method for executing a search
509         * 
510         * @param searchStr
511         *            the string to search for
512         */
513        private void doSearch(String searchStr) {
514                // reset the selections
515                _ontoTree.clearSelection();
516                // collapse the tree
517                collapseTree();
518                // if empty return
519                if (searchStr.trim().equals(""))
520                        return;
521                // get all the matches
522                Iterator results = findMatchingClasses(searchStr).iterator();
523                while (results.hasNext()) {
524                        // add selection for each match
525                        DefaultMutableTreeNode node = (DefaultMutableTreeNode) results
526                                        .next();
527                        _ontoTree.addSelectionPath(new TreePath(node.getPath()));
528                }
529                _commentTxt.setText("");
530        }
531
532        /**
533         * Private method to select (highlight) a NamedOntClass in the onto tree
534         * 
535         * @param cls
536         *            the NamedOntClass to select
537         */
538        private void doSelect(NamedOntClass cls) {
539                if (cls == null)
540                        return;
541                // clear current selection
542                _ontoTree.clearSelection();
543                // collapse the tree
544                collapseTree();
545                // get the matches
546                DefaultMutableTreeNode root = (DefaultMutableTreeNode) _ontoTree
547                                .getModel().getRoot();
548                Iterator paths = findPaths(cls, root).iterator();
549                while (paths.hasNext()) {
550                        TreePath path = (TreePath) paths.next();
551                        _ontoTree.addSelectionPath(path);
552                }
553        }
554
555        /**
556         * Private method for finding all paths in onto tree to given NamedOntClass
557         * 
558         * @param cls
559         *            the NamedOntClass to find
560         * @param root
561         *            the root of the ontology tree
562         * @return a set of paths that lead to the NamedOntClass
563         */
564        private Vector findPaths(NamedOntClass cls, DefaultMutableTreeNode root) {
565                Vector paths = new Vector();
566                Object obj = root.getUserObject();
567                if (obj instanceof NamedOntClass) {
568                        if (cls.equals((NamedOntClass) obj)) {
569                                // add path to paths
570                                TreeNode[] ps = root.getPath();
571                                paths.add(new TreePath(root.getPath()));
572                        }
573                }
574                Enumeration children = root.children();
575                while (children.hasMoreElements()) {
576                        DefaultMutableTreeNode child = (DefaultMutableTreeNode) children
577                                        .nextElement();
578                        Iterator descendants = findPaths(cls, child).iterator();
579                        while (descendants.hasNext())
580                                paths.add(descendants.next());
581                }
582                return paths;
583
584        }
585
586        // PRIVATE CLASSES
587
588        /**
589         * Private class for rendering the ontology tree. Uses different icons for
590         * ontology nodes and class nodes.
591         */
592        private class OntoClassSelectionJTreeRenderer extends
593                        DefaultTreeCellRenderer {
594
595                private ImageIcon _classIcon, _ontoIcon;
596                
597                private final String CLASS_ICON = StaticResources.getSettingsString(
598                                "ONTOL_CLASS_TREEICON_PATH", "");
599                private final String ONTO_ICON = StaticResources.getSettingsString(
600                                "ONTOLOGY_TREEICON_CLOSED_PATH", "");
601        
602                /** initializes the renderer */
603                public OntoClassSelectionJTreeRenderer() {
604
605                        URL ontoImgURL = OntoClassSelectionJTreeRenderer.class
606                                        .getResource(ONTO_ICON);
607                        if (ontoImgURL != null) {
608                                _ontoIcon = new ImageIcon(ontoImgURL);
609                        }
610                        URL classImgURL = null;
611                        //OntoClassSelectionJTreeRenderer.class.getResource(CLASS_ICON);
612                        if (classImgURL != null) {
613                                _classIcon = new ImageIcon(classImgURL);
614                        }
615
616                        // _classIcon = new ImageIcon(CLASS_ICON);
617                        // _ontoIcon = new ImageIcon(ONTO_ICON);
618                }
619
620                /** returns tree cell renderer */
621                public Component getTreeCellRendererComponent(JTree tree, Object value,
622                                boolean sel, boolean expanded, boolean leaf, int row,
623                                boolean hasFocus) {
624                        super.getTreeCellRendererComponent(tree, value, sel, expanded,
625                                        leaf, row, hasFocus);
626                        if (isClassNode(value)) {
627                                setIcon(_classIcon);
628                                setToolTipText("This is a class ... ");
629                        } else if (isOntoNode(value)) {
630                                setIcon(_ontoIcon);
631                                setToolTipText("This is an ontology ... ");
632                        }
633                        return this;
634                }
635
636                /**
637                 * Determines if a given value is a class node
638                 * 
639                 * @return true if the given node object is a NamedOntClass
640                 */
641                protected boolean isClassNode(Object value) {
642                        DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
643                        Object obj = node.getUserObject();
644                        if (obj instanceof NamedOntClass)
645                                return true;
646                        return false;
647                }
648
649                /**
650                 * Determines if a given value is an ontology node
651                 * 
652                 * @return true if the given node object is a NamedOntModel
653                 */
654                protected boolean isOntoNode(Object value) {
655                        DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
656                        Object obj = node.getUserObject();
657                        if (obj instanceof NamedOntModel)
658                                return true;
659                        return false;
660                }
661
662        };
663
664        /**
665         * Private class implementing a listener for search button
666         */
667        private class ClassSearchButtonListener implements ActionListener {
668                public void actionPerformed(ActionEvent ev) {
669                        doSearch(_searchTxt.getText());
670                }
671        };
672
673        /**
674         * Private class for implementing a listener for add button
675         */
676        private class AddButtonListener implements ActionListener {
677                public void actionPerformed(ActionEvent ev) {
678                        TreePath[] paths = _ontoTree.getSelectionPaths();
679                        if (paths.length < 1)
680                                return;
681                        for (int i = 0; i < paths.length; i++) {
682                                DefaultMutableTreeNode node = (DefaultMutableTreeNode) paths[i]
683                                                .getLastPathComponent();
684                                if (node.getUserObject() instanceof NamedOntClass) {
685                                        NamedOntClass cls = (NamedOntClass) node.getUserObject();
686                                        addToList(cls);
687                                }
688                        }
689                }
690        };
691
692        /**
693         * Private class for implementing a listener for remove button
694         */
695        private class RemoveButtonListener implements ActionListener {
696                public void actionPerformed(ActionEvent ev) {
697                        Object[] vals = _classList.getSelectedValues();
698                        for (int i = 0; i < vals.length; i++) {
699                                NamedOntClass cls = (NamedOntClass) vals[i];
700                                removeFromList(cls);
701                        }
702                }
703        };
704
705        /**
706         * Private class that extends JTree for icons and drag and drop
707         */
708        private class OntoClassSelectionJTree extends JTree implements
709                        ActionListener {
710                private JPopupMenu popup;
711                private JMenuItem desc;
712                private JMenuItem select;
713
714                public OntoClassSelectionJTree(DefaultMutableTreeNode node) {
715                        super(node);
716                        popup = new JPopupMenu();
717                        popup.setOpaque(true);
718                        popup.setLightWeightPopupEnabled(true);
719                        initPopup();
720                        MouseAdapter mouseAdapter = new MouseAdapter() {
721                                public void mouseReleased(MouseEvent e) {
722                                        if (e.isPopupTrigger())
723                                                doPopup(e);
724                                }
725
726                                public void mousePressed(MouseEvent e) {
727                                        if (e.isPopupTrigger())
728                                                doPopup(e);
729                                }
730                        };
731                        addMouseListener(mouseAdapter);
732                        DragSource ds = DragSource.getDefaultDragSource();
733                        ds.createDefaultDragGestureRecognizer(this,
734                                        DnDConstants.ACTION_COPY_OR_MOVE,
735                                        new _OntoClassSelectionTreeGestureListener());
736                }
737
738                private void initPopup() {
739                        popup.removeAll();
740
741                        select = new JMenuItem("Add as Selection", KeyEvent.VK_A);
742                        select.addActionListener(this);
743                        select.setActionCommand("__SELECT");
744                        popup.add(select);
745                        popup.addSeparator();
746                }
747
748                private void doPopup(MouseEvent e) {
749
750                        // unhighlight nodes
751                        clearSelection();
752
753                        // highlight selected node
754                        int row = getRowForLocation(e.getX(), e.getY());
755                        if (row == -1)
756                                return;
757                        TreePath path = getPathForRow(row);
758                        DefaultMutableTreeNode node = (DefaultMutableTreeNode) path
759                                        .getLastPathComponent();
760                        if (!(node.getUserObject() instanceof NamedOntClass))
761                                return;
762
763                        setSelectionRow(row);
764
765                        // configure popup
766                        initPopup();
767                        NamedOntClass cls = (NamedOntClass) node.getUserObject();
768                        Iterator props = cls.getNamedProperties(true);
769                        while (props.hasNext()) {
770                                NamedOntProperty p = (NamedOntProperty) props.next();
771                                JMenu submenu = new JMenu(p.getName());
772                                popup.add(submenu);
773                                Iterator ranges = p.getRange(true);
774                                while (ranges.hasNext()) {
775                                        NamedOntClass range = (NamedOntClass) ranges.next();
776                                        JMenuItem rangeitem = new JMenuItem(range.getName());
777                                        rangeitem.addActionListener(this);
778                                        rangeitem.setActionCommand(range.getNameSpace()
779                                                        + range.getName());
780                                        submenu.add(rangeitem);
781                                }
782                        }
783
784                        // show popup
785                        popup.show((JComponent) e.getSource(), e.getX(), e.getY());
786                }
787
788                public void actionPerformed(ActionEvent ae) {
789                        TreePath path = getSelectionPath();
790                        DefaultMutableTreeNode node = (DefaultMutableTreeNode) path
791                                        .getLastPathComponent();
792                        NamedOntClass cls = (NamedOntClass) node.getUserObject();
793                        if (ae.getActionCommand().equals("__SELECT")) {
794                                addToList(cls);
795                        } else {
796                                String searchStr = ae.getActionCommand();
797                                NamedOntClass searchCls = OntologyCatalog.instance()
798                                                .getNamedOntClass(searchStr);
799                                if (searchCls != null)
800                                        doSelect(searchCls);
801                        }
802                }
803        };
804
805        /**
806         * Private class for handling drag and drop initiation from the JTree
807         */
808        private class _OntoClassSelectionTreeGestureListener implements
809                        DragGestureListener {
810                public void dragGestureRecognized(DragGestureEvent e) {
811                        DragSourceListener dsl = new DragSourceListener() {
812                                public void dragDropEnd(DragSourceDropEvent dsde) {
813                                }
814
815                                public void dragEnter(DragSourceDragEvent dsde) {
816                                        DragSourceContext context = dsde.getDragSourceContext();
817                                        // Intersection of the users selected action,
818                                        // and the source and target actions
819                                        int myaction = dsde.getDropAction();
820                                        // if((myaction & DnDConstants.ACTION_COPY_OR_MOVE) != 0)
821                                        if ((myaction & DnDConstants.ACTION_COPY_OR_MOVE) != 0)
822                                                context.setCursor(DragSource.DefaultCopyDrop);
823                                        else
824                                                context.setCursor(DragSource.DefaultCopyNoDrop);
825                                }
826
827                                public void dragExit(DragSourceEvent dse) {
828                                }
829
830                                public void dragOver(DragSourceDragEvent dsde) {
831                                }
832
833                                public void dropActionChanged(DragSourceDragEvent dsde) {
834                                }
835
836                        };
837                        Component source = e.getComponent();
838                        if (source instanceof OntoClassSelectionJTree) {
839                                OntoClassSelectionJTree tree = (OntoClassSelectionJTree) source;
840                                Point sourcePoint = e.getDragOrigin();
841                                TreePath path = tree.getPathForLocation(sourcePoint.x,
842                                                sourcePoint.y);
843                                // If we didn't select anything.. then don't drag.
844                                if (path == null)
845                                        return;
846                                DefaultMutableTreeNode node = (DefaultMutableTreeNode) path
847                                                .getLastPathComponent();
848                                if (node == null)
849                                        return;
850                                if (node.getUserObject() instanceof NamedOntClass) {
851                                        NamedOntClassTransferable transferable = new NamedOntClassTransferable();
852                                        transferable
853                                                        .addObject((NamedOntClass) node.getUserObject());
854                                        e
855                                                        .startDrag(DragSource.DefaultCopyNoDrop,
856                                                                        transferable, dsl);
857                                }
858                        }
859                }
860        };
861
862        /**
863         * Private class that extends JList for right click menus
864         */
865        private class OntoClassSelectionJList extends JList implements
866                        ActionListener {
867                private JPopupMenu popup;
868                private JMenuItem navigate;
869                private JMenuItem remove;
870
871                public OntoClassSelectionJList(ListModel model) {
872                        super(model);
873                        popup = new JPopupMenu();
874                        popup.setOpaque(true);
875                        popup.setLightWeightPopupEnabled(true);
876                        initPopup();
877                        MouseAdapter mouseAdapter = new MouseAdapter() {
878                                public void mouseReleased(MouseEvent e) {
879                                        if (e.isPopupTrigger())
880                                                doPopup(e);
881                                }
882
883                                public void mousePressed(MouseEvent e) {
884                                        if (e.isPopupTrigger())
885                                                doPopup(e);
886                                }
887
888                                public void mouseClicked(MouseEvent ev) {
889                                        // double click
890                                        if (ev.getClickCount() == 2) {
891                                                Object[] items = _classList.getSelectedValues();
892                                                NamedOntClass cls = (NamedOntClass) items[0];
893                                                doSelect(cls);
894                                        }
895                                }
896                        };
897                        addMouseListener(mouseAdapter);
898                }
899
900                private void initPopup() {
901                        navigate = new JMenuItem("Show in Class Browser", KeyEvent.VK_S);
902                        navigate.addActionListener(this);
903                        navigate.setActionCommand("__NAVIGATE");
904                        popup.add(navigate);
905                        remove = new JMenuItem("Remove Selection", KeyEvent.VK_R);
906                        remove.addActionListener(this);
907                        remove.setActionCommand("__REMOVE");
908                        popup.add(remove);
909                }
910
911                private void doPopup(MouseEvent e) {
912                        // clear selection
913                        _classList.clearSelection();
914                        // figure out which item
915                        int index = _classList
916                                        .locationToIndex(new Point(e.getX(), e.getY()));
917                        if (index == -1
918                                        || !_classList.getCellBounds(index, index).contains(
919                                                        e.getX(), e.getY()))
920                                return;
921                        // select the item
922                        _classList.setSelectedIndex(index);
923                        // show popup
924                        popup.show((JComponent) e.getSource(), e.getX(), e.getY());
925                }
926
927                public void actionPerformed(ActionEvent ae) {
928                        Object[] items = _classList.getSelectedValues();
929                        if (items == null || items.length < 1)
930                                return;
931                        NamedOntClass cls = (NamedOntClass) items[0];
932
933                        if (ae.getActionCommand().equals("__REMOVE"))
934                                removeFromList(cls);
935                        else if (ae.getActionCommand().equals("__NAVIGATE"))
936                                doSelect(cls);
937                }
938        };
939
940        /**
941         * Private class for drop targets; copied/modified from ptolemy code base
942         */
943        private class OntoClassSelectionJListDropTarget extends DropTarget {
944                public OntoClassSelectionJListDropTarget(OntoClassSelectionJList list) {
945                        setComponent(list);
946                        try {
947                                addDropTargetListener(new OntoClassSelectionDropTargetListener(
948                                                list));
949                        } catch (java.util.TooManyListenersException wow) {
950                        }
951                }
952        };
953
954        /**
955         * Private class implementing a drop target listener for the list;
956         * copied/modified from ptolemy code base
957         */
958        private class OntoClassSelectionDropTargetListener implements
959                        DropTargetListener {
960
961                public OntoClassSelectionDropTargetListener(OntoClassSelectionJList list) {
962                        _list = list;
963                }
964
965                /**
966                 * This is called while a drag operation is ongoing, when the mouse
967                 * pointer enters the operable part of the drop site for the DropTarget
968                 * registered with this listener.
969                 * 
970                 * @param dtde
971                 *            The drop event.
972                 */
973                public void dragEnter(DropTargetDragEvent dtde) {
974                        DataFlavor df = NamedOntClassTransferable.getNamedOntClassFlavor();
975                        if (dtde.isDataFlavorSupported(df))
976                                dtde.acceptDrag(DnDConstants.ACTION_MOVE);
977                        else
978                                dtde.rejectDrag();
979                }
980
981                /**
982                 * Remove any highlighting that might be active. This is called while a
983                 * drag operation is ongoing, when the mouse pointer has exited the
984                 * operable part of the drop site for the DropTarget registered with
985                 * this listener.
986                 * 
987                 * @param dtde
988                 *            The drop event.
989                 */
990                public void dragExit(DropTargetEvent dtde) {
991                        // _list.clearSelection();
992                }
993
994                /**
995                 * This is called when a drag operation is ongoing, while the mouse
996                 * pointer is still over the operable part of the drop site for the
997                 * DropTarget registered with this listener.
998                 * 
999                 * @param dtde
1000                 *            The drop event.
1001                 */
1002                public void dragOver(DropTargetDragEvent dtde) {
1003                        _list.clearSelection();
1004                }
1005
1006                /**
1007                 * This is called when the drag operation has terminated with a drop on
1008                 * the operable part of the drop site for the DropTarget registered with
1009                 * this listener.
1010                 * 
1011                 * @param dtde
1012                 *            The drop event.
1013                 */
1014                public void drop(DropTargetDropEvent dtde) {
1015                        // Unhighlight the target.
1016                        _list.clearSelection();
1017
1018                        Iterator iterator = null;
1019                        DataFlavor df = NamedOntClassTransferable.getNamedOntClassFlavor();
1020                        if (dtde.isDataFlavorSupported(df)) {
1021                                try {
1022                                        dtde.acceptDrop(DnDConstants.ACTION_MOVE);
1023                                        iterator = (Iterator) dtde.getTransferable()
1024                                                        .getTransferData(df);
1025                                } catch (Exception e) {
1026                                }
1027                        } else
1028                                dtde.rejectDrop();
1029
1030                        // nothing to drop!
1031                        if (iterator == null)
1032                                return;
1033
1034                        // add the object
1035                        while (iterator.hasNext()) {
1036                                NamedOntClass obj = (NamedOntClass) iterator.next();
1037                                addNamedOntClass(obj);
1038                        }
1039
1040                        // notify drag source that the action is complete and successful
1041                        dtde.dropComplete(true);
1042                }
1043
1044                /**
1045                 * This is called if the user has modified the current drop gesture.
1046                 * 
1047                 * @param dtde
1048                 *            The drop event.
1049                 */
1050                public void dropActionChanged(DropTargetDragEvent dtde) {
1051                }
1052
1053                private OntoClassSelectionJList _list;
1054
1055        };
1056
1057        /**
1058         * Private class for handling tree selections. On selection, displays the
1059         * comment for the selected class (if a comment exists).
1060         */
1061        private class OntoClassTreeSelectionListener implements
1062                        TreeSelectionListener {
1063                public void valueChanged(TreeSelectionEvent ev) {
1064                        TreePath[] paths = _ontoTree.getSelectionPaths();
1065
1066                        if (paths == null || paths.length < 1) {
1067                                _commentTxt.setText("");
1068                                return;
1069                        }
1070
1071                        if (paths.length > 1) {
1072                                _commentTxt.setText("");
1073                        } else {
1074                                TreePath path = paths[0];
1075                                DefaultMutableTreeNode node = (DefaultMutableTreeNode) path
1076                                                .getLastPathComponent();
1077                                Object obj = node.getUserObject();
1078
1079                                if (!(obj instanceof NamedOntClass))
1080                                        _commentTxt.setText("");
1081                                else {
1082                                        NamedOntClass cls = (NamedOntClass) obj;
1083                                        _commentTxt.setText(cls.getComment());
1084                                }
1085                        }
1086
1087                        // check if the only path is NamedOntModel
1088                        _addBtn.setEnabled(false);
1089                        for (int i = 0; i < paths.length; i++) {
1090                                DefaultMutableTreeNode node = (DefaultMutableTreeNode) paths[i]
1091                                                .getLastPathComponent();
1092                                if (node.getUserObject() instanceof NamedOntClass)
1093                                        _addBtn.setEnabled(true);
1094                        }
1095                }
1096        };
1097
1098        /**
1099         * Private class for handling class list selections. On selection, enables
1100         * remove button.
1101         */
1102        private class ClassListSelectionListener implements ListSelectionListener {
1103                public void valueChanged(ListSelectionEvent ev) {
1104                        if (!ev.getValueIsAdjusting()) {
1105                                Object[] selections = _classList.getSelectedValues();
1106                                if (selections.length < 1)
1107                                        _removeBtn.setEnabled(false);
1108                                else
1109                                        _removeBtn.setEnabled(true);
1110                        }
1111
1112                }
1113        };
1114
1115        // TESTING
1116
1117        /**
1118         * Main method for testing
1119         *
1120        public static void main(String[] args) {
1121                JFrame frame = new JFrame();
1122                frame.getContentPane().add(new OntoClassSelectionJPanel());
1123                frame.setTitle("Test Frame");
1124                frame.pack();
1125                frame.show();
1126        }*/
1127
1128}