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}