|
DocumentEditor |
|
1 /* 2 * Copyright (c) 1998-2001, The University of Sheffield. 3 * 4 * This file is part of GATE (see http://gate.ac.uk/), and is free 5 * software, licenced under the GNU Library General Public License, 6 * Version 2, June 1991 (in the distribution as file licence.html, 7 * and also available at http://gate.ac.uk/gate/licence.html). 8 * 9 * Valentin Tablan 13/11/2000 10 * 11 * $Id: DocumentEditor.java,v 1.67 2002/03/06 17:15:46 kalina Exp $ 12 * 13 */ 14 package gate.gui; 15 16 import gate.*; 17 import gate.util.*; 18 import gate.corpora.TestDocument; 19 import gate.creole.tokeniser.DefaultTokeniser; 20 import gate.creole.*; 21 import gate.event.*; 22 import gate.swing.*; 23 import gate.print.*; 24 25 import gnu.regexp.*; 26 27 import javax.swing.*; 28 import javax.swing.event.*; 29 import javax.swing.table.*; 30 import javax.swing.text.*; 31 import javax.swing.tree.*; 32 import javax.swing.border.*; 33 34 import java.awt.*; 35 import java.awt.font.*; 36 import java.awt.event.*; 37 import java.awt.image.BufferedImage; 38 import java.awt.print.*; 39 40 import java.beans.*; 41 import java.util.*; 42 import java.net.*; 43 import java.io.*; 44 45 /** 46 * This class implements a viewer/editor for the annotations on a document. 47 * As a viewer, this visual resource will display all the annotations found on 48 * the document. The editor needs to have some data about annotation types in 49 * order to allow the editing of annotations. This data comes from the 50 * {@link gate.creole.AnnotationSchema} objects that are loaded in the Gate 51 * system at a given moment. If there are no such objects the editing of 52 * annotations will be restricted to a very crude method allowing the user to 53 * add any type of annotations having any features with any String values. 54 */ 55 public class DocumentEditor extends AbstractVisualResource 56 implements ANNIEConstants{ 57 //properties 58 private transient PropertyChangeSupport propertyChangeListeners = 59 new PropertyChangeSupport(this); 60 /** 61 * The {@link gate.Document} currently displayed. 62 */ 63 private gate.Document document; 64 65 /** 66 * A random colour generator used to generate initial default colours for 67 * highlighting various types of annotations. 68 */ 69 protected ColorGenerator colGenerator = new ColorGenerator(); 70 71 //GUI components 72 /** The text display.*/ 73 protected JTextPane textPane; 74 75 /** Scroller used for the text diaplay*/ 76 protected JScrollPane textScroll; 77 78 /** The table placed below the text display used for showing annotations*/ 79 protected XJTable annotationsTable; 80 81 /**Model for the annotations table*/ 82 protected AnnotationsTableModel annotationsTableModel; 83 84 /** Scroller for the annotations table*/ 85 protected JScrollPane tableScroll; 86 87 /*The split that contains the text(top) and the annotations table(bottom)*/ 88 protected JSplitPane leftSplit; 89 90 /** 91 * The split that contains the styles tree and the coreference viewer. 92 */ 93 protected JSplitPane rightSplit; 94 95 /** 96 * The main horizontal split that contains all the contents of this viewer 97 */ 98 protected JSplitPane mainSplit; 99 100 /** 101 * The right hand side tree with all the annotation sets and types of 102 * annotations 103 */ 104 protected JTree stylesTree; 105 106 /** 107 * The toolbar displayed on the top part of the component 108 */ 109 protected JToolBar toolbar; 110 111 /**Scroller for the styles tree*/ 112 protected JScrollPane stylesTreeScroll; 113 114 /**The root for the styles tree*/ 115 protected DefaultMutableTreeNode stylesTreeRoot; 116 117 /**The model for the styles tree*/ 118 protected DefaultTreeModel stylesTreeModel; 119 120 /**The dialog used for text search*/ 121 protected SearchDialog searchDialog; 122 123 /**The dialog used for editing the styles used to highlight annotations*/ 124 protected TextAttributesChooser styleChooser; 125 126 127 /** 128 * The Jtree that displays the coreference data 129 */ 130 protected JTree corefTree; 131 /** 132 * The root for the coref tree 133 */ 134 protected DefaultMutableTreeNode corefTreeRoot; 135 136 /** 137 * The model for the coref tree 138 */ 139 protected DefaultTreeModel corefTreeModel; 140 141 /** The scroller for the coref list*/ 142 protected JScrollPane corefScroll; 143 144 /** 145 * A box containing a {@link javax.swing.JProgressBar} used to keep the user 146 * entertained while the text display is being updated 147 */ 148 protected Box progressBox; 149 150 /**The progress bar used during updating the text*/ 151 protected JProgressBar progressBar; 152 153 /** 154 * The highlighter used to help the user select annotations that overlap 155 * and for highligting in the text the annotations selected in the lower 156 * table. 157 */ 158 protected Highlighter highlighter; 159 160 /** 161 * This highlighter is actually used as a data structure. It is used to keep 162 * the data for the selected annotations; the actual highlighting will be 163 * done by the {@link AnnotationEditor#highlighter} as using two different 164 * highlighters on the same text component is looking for trouble. 165 */ 166 protected Highlighter selectionHighlighter; 167 168 /** 169 * The object responsible with blinking the selected annotations. 170 */ 171 protected SelectionBlinker selectionBlinker; 172 173 174 protected Handle myHandle; 175 176 /** 177 * holds the data for the annotations table: a list of Annotation objects 178 */ 179 protected java.util.List data; 180 181 /** 182 * a list containing {@link AnnotationEditor.Range} objects. These are the 183 * ranges in the {@link AnnotationEditor#data} structure. A range is a bunch 184 * of annotations belonging to the same annotation set that are contiguous 185 * in the {@link AnnotationEditor#data} structure. 186 */ 187 protected java.util.List ranges; 188 189 /** 190 * A composed map used to get the metadata for an annotation type starting 191 * from the annotation set name and the type name. 192 * Annotation set name -> Annotation type -> {@link AnnotationEditor.TypeData} 193 * Maps from String to Map to {@link AnnotationEditor.TypeData}. 194 */ 195 protected Map typeDataMap; 196 197 /** 198 * The listener for the events coming from the document (annotations and 199 * annotation sets added or removed). 200 */ 201 protected EventsHandler eventHandler; 202 203 204 /** 205 * Object used to sychronise all the various threads involved in GUI 206 * updating; 207 */ 208 protected Object lock; 209 210 /**Should the table be visible*/ 211 212 /**Should the text be visible*/ 213 214 /** 215 * Should the right hand side tree be visible. That tree is used to select 216 * what types of annotations are visible in the text display, hence the name 217 * filters. 218 */ 219 220 /**Should this component bahave as an editor as well as an viewer*/ 221 private boolean editable = true; 222 223 224 225 private JToggleButton textVisibleBtn; 226 private JToggleButton typesTreeVisibleBtn; 227 private JToggleButton annotationsTableVisibleBtn; 228 private JToggleButton coreferenceVisibleBtn; 229 private boolean annotationsTableVisible = false; 230 private boolean coreferenceVisible = false; 231 private boolean textVisible = true; 232 private boolean typesTreeVisible = false; 233 private boolean corefOptionAvailable = false; 234 235 /** 236 * Default constructor. Creats all the components and initialises all the 237 * internal data to default values where possible. 238 */ 239 public DocumentEditor() { 240 } 241 242 public Resource init(){ 243 initLocalData(); 244 initGuiComponents(); 245 initListeners(); 246 return this; 247 } 248 249 /** Test code*/ 250 public static void main(String[] args) { 251 try{ 252 UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); 253 Gate.setLocalWebServer(false); 254 Gate.setNetConnected(false); 255 Gate.init(); 256 JFrame frame = new JFrame("Gate Document Editor Test"); 257 frame.addWindowListener(new WindowAdapter(){ 258 public void windowClosing(WindowEvent e){ 259 System.exit(0); 260 } 261 }); 262 263 //get a document 264 FeatureMap params = Factory.newFeatureMap(); 265 params.put(gate.Document.DOCUMENT_MARKUP_AWARE_PARAMETER_NAME, 266 new Boolean(true)); 267 268 params.put(gate.Document.DOCUMENT_URL_PARAMETER_NAME, 269 "file:///d:/tmp/help-doc.html"); 270 //"file:///d:/tmp/F7V.xml"); 271 //"http://redmires.dcs.shef.ac.uk/admin/index.html"); 272 //"http://redmires.dcs.shef.ac.uk/java1.3docs/api/javax/ 273 // swing/Action.html"); 274 //"http://redmires.dcs.shef.ac.uk/java1.3docs/api/java/awt 275 ///AWTEventMulticaster.html"); 276 gate.Document doc = (gate.Document)Factory.createResource( 277 "gate.corpora.DocumentImpl", params); 278 //create a default tokeniser 279 params.clear(); 280 params.put("rulesResourceName", "creole/tokeniser/DefaultTokeniser.rules"); 281 DefaultTokeniser tokeniser = (DefaultTokeniser) Factory.createResource( 282 "gate.creole.tokeniser.DefaultTokeniser", params); 283 284 tokeniser.setDocument(doc); 285 tokeniser.setAnnotationSetName("TokeniserAS"); 286 tokeniser.execute(); 287 288 DocumentEditor editor = (DocumentEditor)Factory.createResource( 289 "gate.gui.DocumentEditor", Factory.newFeatureMap()); 290 frame.getContentPane().add(editor); 291 frame.pack(); 292 frame.setVisible(true); 293 editor.setTarget(doc); 294 295 //get the annotation schemas 296 params = Factory.newFeatureMap(); 297 params.put("xmlFileUrl", DocumentEditor.class.getResource( 298 "/gate/resources/creole/schema/PosSchema.xml")); 299 300 AnnotationSchema annotSchema = (AnnotationSchema) 301 Factory.createResource("gate.creole.AnnotationSchema", params); 302 Set annotationSchemas = new HashSet(); 303 annotationSchemas.add(annotSchema); 304 305 }catch(Exception e){ 306 e.printStackTrace(Err.getPrintWriter()); 307 } 308 }//public static void main(String[] args) 309 310 311 /** 312 * Initialises all the listeners that this component has to register with 313 * other classes. 314 */ 315 protected void initListeners() { 316 //listen for our own properties change events 317 this.addPropertyChangeListener(new PropertyChangeListener() { 318 public void propertyChange(PropertyChangeEvent e) { 319 if(e.getPropertyName().equals("annotationsTableVisible") || 320 e.getPropertyName().equals("coreferenceVisible") || 321 e.getPropertyName().equals("textVisible") || 322 e.getPropertyName().equals("typesTreeVisible")){ 323 layoutComponents(); 324 }else if(e.getPropertyName().equals("corefOptionAvailable")){ 325 if(((Boolean)e.getNewValue()).booleanValue()){ 326 if(toolbar.getComponentIndex(coreferenceVisibleBtn) == -1) 327 toolbar.add(coreferenceVisibleBtn, 3); 328 }else{ 329 toolbar.remove(coreferenceVisibleBtn); 330 } 331 layoutComponents(); 332 } 333 } 334 }); 335 336 textVisibleBtn.addActionListener(new ActionListener() { 337 public void actionPerformed(ActionEvent e) { 338 setTextVisible(textVisibleBtn.isSelected()); 339 } 340 }); 341 342 annotationsTableVisibleBtn.addActionListener(new ActionListener() { 343 public void actionPerformed(ActionEvent e) { 344 setAnnotationsTableVisible(annotationsTableVisibleBtn.isSelected()); 345 } 346 }); 347 348 349 typesTreeVisibleBtn.addActionListener(new ActionListener() { 350 public void actionPerformed(ActionEvent e) { 351 setTypesTreeVisible(typesTreeVisibleBtn.isSelected()); 352 } 353 }); 354 355 356 coreferenceVisibleBtn.addActionListener(new ActionListener() { 357 public void actionPerformed(ActionEvent e) { 358 setCoreferenceVisible(coreferenceVisibleBtn.isSelected()); 359 } 360 }); 361 362 stylesTree.addMouseListener(new MouseAdapter() { 363 public void mouseClicked(MouseEvent e) { 364 if(SwingUtilities.isLeftMouseButton(e)){ 365 //where inside the tree? 366 int x = e.getX(); 367 int y = e.getY(); 368 TreePath path = stylesTree.getPathForLocation(x, y); 369 if(path != null){ 370 DefaultMutableTreeNode node = (DefaultMutableTreeNode)path. 371 getLastPathComponent(); 372 TypeData nData = (TypeData)node.getUserObject(); 373 //where inside the cell? 374 Rectangle cellRect = stylesTree.getPathBounds(path); 375 x -= cellRect.x; 376 y -= cellRect.y; 377 Component cellComp = stylesTree.getCellRenderer(). 378 getTreeCellRendererComponent(stylesTree, 379 node, true, 380 false, false, 381 0, true); 382 // cellComp.setSize(cellRect.width, cellRect.height); 383 cellComp.setBounds(cellRect); 384 Component clickedComp = cellComp.getComponentAt(x, y); 385 386 if(clickedComp instanceof JCheckBox){ 387 nData.setVisible(! nData.getVisible()); 388 // stylesTree.repaint(cellRect); 389 stylesTreeModel.nodeChanged(node); 390 // Check if the click indicates a shortcut to create an annotation 391 }else if( e.getClickCount() == 1 && 392 clickedComp instanceof JLabel && 393 isTextSelected()){ 394 // Here creates an annotation with the selected text into the 395 // target annotation set 396 397 if(!editable) return; 398 Long startOffset = new Long(textPane.getSelectionStart()); 399 Long endOffset = new Long(textPane.getSelectionEnd()); 400 TreePath treePath = stylesTree.getSelectionPath(); 401 TypeData typeData = (TypeData)((DefaultMutableTreeNode) 402 treePath.getLastPathComponent()).getUserObject(); 403 String setName = typeData.getSet(); 404 if(typeData.getType() == null){ 405 // The set is empty. It will not create an annotation. 406 // Loose the selection and return 407 textPane.setSelectionStart(startOffset.intValue()); 408 textPane.setSelectionEnd(startOffset.intValue()); 409 return; 410 }// End if 411 try{ 412 if ("Default".equals(setName)){ 413 document.getAnnotations().add(startOffset, 414 endOffset, 415 typeData.getType(), 416 Factory.newFeatureMap()); 417 }else{ 418 document.getAnnotations(setName).add( startOffset, 419 endOffset, 420 typeData.getType(), 421 Factory.newFeatureMap()); 422 }// End if 423 } catch(gate.util.InvalidOffsetException ioe){ 424 throw new GateRuntimeException(ioe.getMessage()); 425 }// End try 426 // Loose the selection 427 textPane.setSelectionStart(startOffset.intValue()); 428 textPane.setSelectionEnd(startOffset.intValue()); 429 }else if(clickedComp instanceof JLabel && 430 e.getClickCount() == 2){ 431 if(styleChooser == null){ 432 Window parent = SwingUtilities.getWindowAncestor( 433 DocumentEditor.this); 434 styleChooser = parent instanceof Frame ? 435 new TextAttributesChooser((Frame)parent, 436 "Please select your options", 437 true) : 438 new TextAttributesChooser((Dialog)parent, 439 "Please select your options", 440 true); 441 442 } 443 444 styleChooser.setLocationRelativeTo(stylesTree); 445 nData.setAttributes( 446 styleChooser.show(nData.getAttributes().copyAttributes())); 447 stylesTreeModel.nodeChanged(node); 448 // stylesTree.repaint(cellRect); 449 } 450 } 451 } 452 } 453 }); 454 455 stylesTree.addComponentListener(new ComponentAdapter() { 456 public void componentHidden(ComponentEvent e) { 457 458 } 459 460 public void componentMoved(ComponentEvent e) { 461 } 462 463 public void componentResized(ComponentEvent e) { 464 SwingUtilities.invokeLater(new Runnable(){ 465 public void run(){ 466 Enumeration nodes = stylesTreeRoot.depthFirstEnumeration(); 467 while(nodes.hasMoreElements()){ 468 stylesTreeModel.nodeChanged((TreeNode)nodes.nextElement()); 469 } 470 } 471 }); 472 } 473 474 public void componentShown(ComponentEvent e) { 475 } 476 }); 477 478 //clear selection in table on outside clicks 479 tableScroll.addMouseListener(new MouseAdapter() { 480 public void mouseClicked(MouseEvent e) { 481 Point location = e.getPoint(); 482 if(!tableScroll.getViewport().getView().getBounds().contains(location)){ 483 //deselect everything in the table 484 annotationsTable.clearSelection(); 485 } 486 } 487 }); 488 489 annotationsTable.addMouseListener(new MouseAdapter() { 490 public void mouseClicked(MouseEvent e) { 491 int row = annotationsTable.rowAtPoint(e.getPoint()); 492 Annotation ann = (Annotation)annotationsTable.getModel(). 493 getValueAt(row, -1); 494 //find the annotation set 495 String setName = (String)annotationsTable.getModel(). 496 getValueAt(row, 1); 497 AnnotationSet set = setName.equals("Default")? 498 document.getAnnotations() : 499 document.getAnnotations(setName); 500 501 EditAnnotationAction editAnnAct = new EditAnnotationAction(set, ann); 502 if(SwingUtilities.isLeftMouseButton(e)){ 503 if(e.getClickCount() == 1){ 504 }else if(e.getClickCount() == 2){ 505 //double left click -> edit the annotation 506 if(editable) editAnnAct.actionPerformed(null); 507 } 508 } else if(SwingUtilities.isRightMouseButton(e)) { 509 //right click 510 //add select all option 511 JPopupMenu popup = new JPopupMenu(); 512 popup.add(new AbstractAction(){ 513 { 514 putValue(NAME, "Select all"); 515 } 516 public void actionPerformed(ActionEvent evt){ 517 annotationsTable.selectAll(); 518 } 519 }); 520 521 // popup.addSeparator(); 522 //add save preserving format 523 // popup.add(new DumpPreserveFormatAction()); 524 if(editable){ 525 //add delete option 526 popup.addSeparator(); 527 popup.add(new DeleteSelectedAnnotationsAction(annotationsTable)); 528 popup.addSeparator(); 529 popup.add(new XJMenuItem(editAnnAct, myHandle)); 530 } 531 popup.show(annotationsTable, e.getX(), e.getY()); 532 } 533 } 534 });//annotationsTable.addMouseListener(new MouseAdapter() 535 536 537 538 annotationsTable.getInputMap().put( 539 KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_DELETE, 0), 540 "Delete"); 541 annotationsTable.getActionMap().put( 542 "Delete", 543 new DeleteSelectedAnnotationsAction(annotationsTable)); 544 545 stylesTree.getInputMap().put( 546 KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_DELETE, 0), 547 "Delete"); 548 stylesTree.getActionMap().put( 549 "Delete", 550 new DeleteSelectedAnnotationsAction(stylesTree)); 551 552 corefTree.getInputMap().put( 553 KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_DELETE, 0), 554 "Delete"); 555 corefTree.getActionMap().put( 556 "Delete", 557 new DeleteSelectedAnnotationsAction(corefTree)); 558 559 560 //takes care of highliting the selected annotations 561 annotationsTable.getSelectionModel().addListSelectionListener( 562 new ListSelectionListener(){ 563 public void valueChanged(ListSelectionEvent e){ 564 int[] rows = annotationsTable.getSelectedRows(); 565 synchronized(selectionHighlighter){ 566 selectionHighlighter.removeAllHighlights(); 567 } 568 for(int i = 0; i < rows.length; i++){ 569 int start = ((Long)annotationsTable.getModel(). 570 getValueAt(rows[i], 2) 571 ).intValue(); 572 int end = ((Long)annotationsTable.getModel(). 573 getValueAt(rows[i], 3) 574 ).intValue(); 575 //bring the annotation in view 576 try{ 577 Rectangle startRect = textPane.modelToView(start); 578 Rectangle endRect = textPane.modelToView(end); 579 SwingUtilities.computeUnion(endRect.x, endRect.y, 580 endRect.width, endRect.height, 581 startRect); 582 textPane.scrollRectToVisible(startRect); 583 annotationsTable.requestFocus(); 584 }catch(BadLocationException ble){ 585 throw new GateRuntimeException(ble.toString()); 586 } 587 //start blinking the annotation 588 try{ 589 synchronized (selectionHighlighter){ 590 selectionHighlighter.addHighlight(start, end, 591 DefaultHighlighter.DefaultPainter); 592 } 593 }catch(BadLocationException ble){ 594 throw new GateRuntimeException(ble.toString()); 595 } 596 }//for(int i = 0; i < rows.length; i++) 597 //start the blinker 598 selectionBlinker.testAndStart(); 599 } 600 }); 601 602 603 textPane.addMouseListener(new MouseAdapter() { 604 public void mouseClicked(MouseEvent e) { 605 if(SwingUtilities.isRightMouseButton(e)){ 606 int position = textPane.viewToModel(e.getPoint()); 607 if(textPane.getSelectionStart() == textPane.getSelectionEnd()){ 608 //no selection -> select an annotation 609 JPopupMenu popup = new JPopupMenu("Select:"); 610 //find annotations at this position 611 Iterator annIter = document.getAnnotations(). 612 get(new Long(position), 613 new Long(position) 614 ).iterator(); 615 if(annIter.hasNext()){ 616 JMenu menu = new JMenu("Default"); 617 popup.add(menu); 618 while(annIter.hasNext()){ 619 Annotation ann = (Annotation)annIter.next(); 620 menu.add(new HighlightAnnotationMenu(ann, 621 document.getAnnotations())); 622 } 623 } 624 Map namedASs = document.getNamedAnnotationSets(); 625 if(namedASs != null){ 626 Iterator namedASiter = namedASs.values().iterator(); 627 while(namedASiter.hasNext()){ 628 //find annotations at this position 629 AnnotationSet set = (AnnotationSet)namedASiter.next(); 630 annIter = set.get(new Long(position), new Long(position)). 631 iterator(); 632 if(annIter.hasNext()){ 633 JMenu menu = new JMenu(set.getName()); 634 popup.add(menu); 635 while(annIter.hasNext()){ 636 Annotation ann = (Annotation)annIter.next(); 637 menu.add(new HighlightAnnotationMenu(ann,set)); 638 } 639 } 640 } 641 } 642 popup.show(textPane, e.getPoint().x, e.getPoint().y); 643 } else { 644 //there is selected text -> create a new annotation 645 if(!editable) return; 646 Long startOffset = new Long(textPane.getSelectionStart()); 647 Long endOffset = new Long(textPane.getSelectionEnd()); 648 JPopupMenu popup = new JPopupMenu(); 649 //add new annotation in the Default AS 650 JMenu menu = new JMenu("Add annotation to \"Default\""); 651 menu.add(new XJMenuItem( 652 new NewAnnotationAction(document.getAnnotations(), 653 startOffset, endOffset), 654 myHandle)); 655 java.util.List customisedAnnTypes = Gate.getCreoleRegister(). 656 getVREnabledAnnotationTypes(); 657 if(!customisedAnnTypes.isEmpty()){ 658 menu.addSeparator(); 659 Iterator typesIter = customisedAnnTypes.iterator(); 660 while(typesIter.hasNext()){ 661 menu.add(new XJMenuItem( 662 new NewAnnotationAction(document.getAnnotations(), 663 (String)typesIter.next(), 664 startOffset, endOffset), 665 myHandle)); 666 } 667 }//if(!customisedAnnTypes.isEmpty()) 668 popup.add(menu); 669 670 //add a new annotation to a named AnnotationSet 671 if(document.getNamedAnnotationSets() != null){ 672 Iterator annSetsIter = document.getNamedAnnotationSets(). 673 keySet().iterator(); 674 if(annSetsIter.hasNext()) popup.addSeparator(); 675 while(annSetsIter.hasNext()){ 676 AnnotationSet set = document.getAnnotations( 677 (String)annSetsIter.next()); 678 679 680 menu = new JMenu("Add annotation to \"" + set.getName() + "\""); 681 menu.add(new XJMenuItem( 682 new NewAnnotationAction(set, startOffset, endOffset), 683 myHandle)); 684 if(!customisedAnnTypes.isEmpty()){ 685 menu.addSeparator(); 686 Iterator typesIter = customisedAnnTypes.iterator(); 687 while(typesIter.hasNext()){ 688 menu.add(new XJMenuItem( 689 new NewAnnotationAction(set, 690 (String)typesIter.next(), 691 startOffset, endOffset), 692 myHandle)); 693 } 694 }//if(!customisedAnnTypes.isEmpty()) 695 popup.add(menu); 696 }//while(annSetsIter.hasNext()) 697 } 698 699 //add to a new annotation set 700 menu = new JMenu("Add annotation to a new set"); 701 menu.add(new XJMenuItem( 702 new NewAnnotationAction(null, startOffset, endOffset), 703 myHandle)); 704 if(!customisedAnnTypes.isEmpty()){ 705 menu.addSeparator(); 706 Iterator typesIter = customisedAnnTypes.iterator(); 707 while(typesIter.hasNext()){ 708 menu.add(new XJMenuItem( 709 new NewAnnotationAction(null, 710 (String)typesIter.next(), 711 startOffset, endOffset), 712 myHandle)); 713 } 714 }//if(!customisedAnnTypes.isEmpty()) 715 popup.add(menu); 716 //show the popup 717 popup.show(textPane, e.getPoint().x, e.getPoint().y); 718 }//there is selected text 719 }//if(SwingUtilities.isRightMouseButton(e)) 720 }//mouse clicked 721 722 public void mousePressed(MouseEvent e) { 723 } 724 725 public void mouseReleased(MouseEvent e) { 726 } 727 728 public void mouseEntered(MouseEvent e) { 729 } 730 731 public void mouseExited(MouseEvent e) { 732 } 733 }); 734 735 //when the highlighter changes we need to get a hold of the new one 736 textPane.addPropertyChangeListener(new PropertyChangeListener() { 737 public void propertyChange(PropertyChangeEvent e) { 738 if(e.getPropertyName().equals("highlighter")){ 739 highlighter = textPane.getHighlighter(); 740 selectionHighlighter.install(textPane); 741 } 742 } 743 }); 744 745 corefTree.addMouseListener(new MouseAdapter() { 746 public void mouseClicked(MouseEvent e) { 747 if(SwingUtilities.isLeftMouseButton(e)){ 748 //where inside the tree? 749 int x = e.getX(); 750 int y = e.getY(); 751 TreePath path = corefTree.getPathForLocation(x, y); 752 if(path != null){ 753 DefaultMutableTreeNode node = (DefaultMutableTreeNode)path. 754 getLastPathComponent(); 755 //where inside the cell? 756 Rectangle cellRect = corefTree.getPathBounds(path); 757 x -= cellRect.x; 758 y -= cellRect.y; 759 Component cellComp = corefTree.getCellRenderer(). 760 getTreeCellRendererComponent(corefTree, 761 node, true, 762 false, false, 763 0, true); 764 cellComp.setBounds(cellRect); 765 Component clickedComp = cellComp.getComponentAt(x, y); 766 if(clickedComp instanceof LazyJPanel) 767 clickedComp = clickedComp.getComponentAt(x, y); 768 if(node.getUserObject() instanceof CorefData && 769 clickedComp instanceof JCheckBox){ 770 CorefData cData = (CorefData)node.getUserObject(); 771 cData.setVisible(!cData.getVisible()); 772 corefTreeModel.nodeChanged(node); 773 } 774 } 775 } 776 } 777 778 public void mousePressed(MouseEvent e) { 779 } 780 781 public void mouseReleased(MouseEvent e) { 782 } 783 784 public void mouseEntered(MouseEvent e) { 785 } 786 787 public void mouseExited(MouseEvent e) { 788 } 789 }); 790 791 792 793 corefTree.addComponentListener(new ComponentAdapter() { 794 public void componentHidden(ComponentEvent e) { 795 796 } 797 798 public void componentMoved(ComponentEvent e) { 799 } 800 801 public void componentResized(ComponentEvent e) { 802 SwingUtilities.invokeLater(new Runnable(){ 803 public void run(){ 804 Enumeration nodes = corefTreeRoot.depthFirstEnumeration(); 805 while(nodes.hasMoreElements()){ 806 corefTreeModel.nodeChanged((TreeNode)nodes.nextElement()); 807 } 808 } 809 }); 810 } 811 812 public void componentShown(ComponentEvent e) { 813 } 814 }); 815 }//protected void initListeners() 816 817 /** 818 * Initialises the local variables to their default values 819 */ 820 protected void initLocalData(){ 821 //init local vars 822 lock = new Object(); 823 824 data = Collections.synchronizedList(new ArrayList()); 825 //dataAsAS = new gate.annotation.AnnotationSetImpl(document); 826 ranges = new ArrayList(); 827 828 typeDataMap = new HashMap(); 829 830 eventHandler = new EventsHandler(); 831 832 }//protected void initLocalData() 833 834 /**Builds all the graphical components*/ 835 protected void initGuiComponents(){ 836 //initialise GUI components 837 // this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); 838 this.setLayout(new BorderLayout()); 839 840 //the toolbar 841 toolbar = new JToolBar(JToolBar.HORIZONTAL); 842 toolbar.setAlignmentX(Component.LEFT_ALIGNMENT); 843 toolbar.setAlignmentY(Component.TOP_ALIGNMENT); 844 toolbar.setFloatable(false); 845 this.add(toolbar, BorderLayout.NORTH); 846 847 textVisibleBtn = new JToggleButton("Text", textVisible); 848 toolbar.add(textVisibleBtn); 849 850 annotationsTableVisibleBtn = new JToggleButton("Annotations", 851 annotationsTableVisible); 852 toolbar.add(annotationsTableVisibleBtn); 853 854 typesTreeVisibleBtn = new JToggleButton("Annotation Sets", typesTreeVisible); 855 toolbar.add(typesTreeVisibleBtn); 856 857 858 coreferenceVisibleBtn = new JToggleButton("Coreference", coreferenceVisible); 859 if(isCorefOptionAvailable()) toolbar.add(coreferenceVisibleBtn); 860 861 862 //printing 863 toolbar.add(Box.createHorizontalStrut(20)); 864 toolbar.add(new PrintAction()); 865 toolbar.add(new SearchAction()); 866 867 868 869 toolbar.add(Box.createHorizontalGlue()); 870 871 //The text 872 textPane = new XJTextPane(); 873 textPane.setEditable(false); 874 textPane.setEnabled(true); 875 textPane.setEditorKit(new CustomStyledEditorKit()); 876 Style defaultStyle = textPane.getStyle("default"); 877 StyleConstants.setBackground(defaultStyle, Color.white); 878 StyleConstants.setFontFamily(defaultStyle, "Arial Unicode MS"); 879 textScroll = new JScrollPane(textPane); 880 textScroll.setAlignmentY(Component.TOP_ALIGNMENT); 881 textScroll.setAlignmentX(Component.LEFT_ALIGNMENT); 882 883 884 //The table 885 annotationsTableModel = new AnnotationsTableModel(); 886 annotationsTable = new XJTable(annotationsTableModel); 887 annotationsTable.setIntercellSpacing(new Dimension(10, 5)); 888 889 tableScroll = new JScrollPane(annotationsTable); 890 tableScroll.setOpaque(true); 891 tableScroll.setAlignmentY(Component.TOP_ALIGNMENT); 892 tableScroll.setAlignmentX(Component.LEFT_ALIGNMENT); 893 894 895 //RIGHT SIDE - the big tree 896 stylesTreeRoot = new DefaultMutableTreeNode(null, true); 897 stylesTreeModel = new DefaultTreeModel(stylesTreeRoot, true); 898 stylesTree = new JTree(stylesTreeModel){ 899 public void updateUI(){ 900 super.updateUI(); 901 setRowHeight(0); 902 } 903 }; 904 905 stylesTree.setRootVisible(false); 906 stylesTree.setCellRenderer(new NodeRenderer()); 907 //TIP: setting rowHeight to 0 tells the tree to query its renderer for each 908 //row's size 909 stylesTree.setRowHeight(0); 910 stylesTree.setShowsRootHandles(true); 911 stylesTree.setToggleClickCount(0); 912 stylesTreeScroll = new JScrollPane(stylesTree); 913 stylesTreeScroll.setAlignmentY(Component.TOP_ALIGNMENT); 914 stylesTreeScroll.setAlignmentX(Component.LEFT_ALIGNMENT); 915 916 917 //coreference 918 corefTreeRoot = new DefaultMutableTreeNode("Coreference data", true); 919 corefTree = new JTree(corefTreeModel = new DefaultTreeModel(corefTreeRoot, 920 true)); 921 corefTree.setCellRenderer(new CorefNodeRenderer()); 922 corefTree.setRowHeight(0); 923 corefTree.setRootVisible(true); 924 corefTree.setShowsRootHandles(false); 925 corefScroll = new JScrollPane(corefTree); 926 corefScroll.setAlignmentX(Component.LEFT_ALIGNMENT); 927 corefScroll.setAlignmentY(Component.TOP_ALIGNMENT); 928 updateCorefTree(); 929 930 //various containers 931 leftSplit = new JSplitPane(JSplitPane.VERTICAL_SPLIT, false); 932 leftSplit.setOneTouchExpandable(true); 933 leftSplit.setOpaque(true); 934 leftSplit.setAlignmentY(Component.TOP_ALIGNMENT); 935 leftSplit.setAlignmentX(Component.LEFT_ALIGNMENT); 936 leftSplit.setResizeWeight((double)0.75); 937 938 rightSplit = new JSplitPane(JSplitPane.VERTICAL_SPLIT, false); 939 rightSplit.setOneTouchExpandable(true); 940 rightSplit.setOpaque(true); 941 rightSplit.setAlignmentY(Component.TOP_ALIGNMENT); 942 rightSplit.setAlignmentX(Component.LEFT_ALIGNMENT); 943 rightSplit.setResizeWeight((double)0.75); 944 945 946 mainSplit = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, false); 947 mainSplit.setOneTouchExpandable(true); 948 mainSplit.setOpaque(true); 949 mainSplit.setAlignmentY(Component.TOP_ALIGNMENT); 950 mainSplit.setAlignmentX(Component.LEFT_ALIGNMENT); 951 mainSplit.setResizeWeight((double)0.75); 952 953 //put everything together 954 layoutComponents(); 955 956 //Extra Stuff 957 958 progressBox = new Box(BoxLayout.X_AXIS); 959 progressBox.add(Box.createHorizontalStrut(5)); 960 progressBar = new JProgressBar(JProgressBar.HORIZONTAL, 0, 100); 961 progressBox.add(progressBar); 962 progressBox.add(Box.createHorizontalStrut(5)); 963 964 highlighter = textPane.getHighlighter(); 965 if(highlighter instanceof javax.swing.text.DefaultHighlighter){ 966 ((javax.swing.text.DefaultHighlighter)highlighter). 967 setDrawsLayeredHighlights(true); 968 } 969 970 selectionHighlighter = new DefaultHighlighter(); 971 selectionHighlighter.install(textPane); 972 selectionBlinker = new SelectionBlinker(); 973 974 }//protected void initGuiComponents() 975 976 977 /** This method returns true if a text is selected in the textPane*/ 978 private boolean isTextSelected(){ 979 return !(textPane.getSelectionStart()==textPane.getSelectionEnd()); 980 }// isTextSelected() 981 /** 982 * Gets all the {@link gate.creole.AnnotationSchema} objects currently 983 * loaded in the system. 984 */ 985 protected Set getAnnotationSchemas(){ 986 Set result = new HashSet(); 987 ResourceData rData = (ResourceData)Gate.getCreoleRegister(). 988 get("gate.creole.AnnotationSchema"); 989 if(rData != null){ 990 result.addAll(rData.getInstantiations()); 991 } 992 return result; 993 }//protected Set getAnnotationSchemas() 994 995 public synchronized void removePropertyChangeListener( 996 PropertyChangeListener l) { 997 super.removePropertyChangeListener(l); 998 propertyChangeListeners.removePropertyChangeListener(l); 999 } 1000 1001 public synchronized void addPropertyChangeListener(PropertyChangeListener l) { 1002 super.addPropertyChangeListener(l); 1003 propertyChangeListeners.addPropertyChangeListener(l); 1004 } 1005 1006 public synchronized void addPropertyChangeListener(String propertyName, 1007 PropertyChangeListener l) { 1008 super.addPropertyChangeListener(propertyName, l); 1009 propertyChangeListeners.addPropertyChangeListener(propertyName, l); 1010 } 1011 1012 /** 1013 * Sets the document to be displayed 1014 */ 1015 public void setTarget(Object target){ 1016 if(target == null){ 1017 document = null; 1018 return; 1019 } 1020 if(!(target instanceof gate.Document)){ 1021 throw new IllegalArgumentException( 1022 "The document editor can only display Gate documents!\n" + 1023 "The provided resource is not a document but a: " + 1024 target.getClass().toString() + "!"); 1025 } 1026 gate.Document oldDocument = document; 1027 document = (gate.Document)target; 1028 //this needs to be executed even if the new document equals(oldDocument) 1029 //in order to update the pointers 1030 if(oldDocument != document) this_documentChanged(); 1031 1032 propertyChangeListeners.firePropertyChange("document", oldDocument, 1033 target); 1034 }//public void setTarget(Object target) 1035 1036 public void setHandle(Handle handle){ 1037 myHandle = handle; 1038 } 1039 1040 public void cleanup(){ 1041 document = null; 1042 stylesTreeRoot.removeAllChildren(); 1043 data.clear(); 1044 ranges.clear(); 1045 myHandle = null; 1046 } 1047 1048 /** 1049 * This method returns a list of annotations which are currently shown in 1050 * the annotations table or null of the table is empty. 1051 */ 1052 public java.util.Set getDisplayedAnnotations() { 1053 //if the annotations table is empty, then return null 1054 if (annotationsTableModel == null||annotationsTableModel.getRowCount() == 0) 1055 return null; 1056 1057 // Read the displayed annotations and insert them into a list 1058 java.util.Set shownAnnots = new HashSet(); 1059 for(int i = 0; i < annotationsTableModel.getRowCount(); i++){ 1060 //Find an annotation and add it to the annotationsToDump set. 1061 Annotation ann = (Annotation)annotationsTableModel.getValueAt(i, -1); 1062 shownAnnots.add(ann); 1063 }// End for 1064 1065 return shownAnnots; 1066 } 1067 1068 /** 1069 * Updates this component when the underlying document is changed. This method 1070 * is only triggered when the document is changed to a new one and not when 1071 * the internal data from the document changes. For the document internal 1072 * events {@see #DelayedListener}. 1073 */ 1074 protected void this_documentChanged(){ 1075 initLocalData(); 1076 annotationsTableModel.fireTableDataChanged(); 1077 document.getFeatures().addFeatureMapListener(new FeatureMapListener(){ 1078 public void featureMapUpdated(){ 1079 updateCorefTree(); 1080 } 1081 }); 1082 updateCorefTree(); 1083 1084 Enumeration enum = stylesTreeRoot.children(); 1085 while(enum.hasMoreElements()){ 1086 stylesTreeModel.removeNodeFromParent((DefaultMutableTreeNode) 1087 enum.nextElement()); 1088 } 1089 if(document == null) return; 1090 textPane.setText(document.getContent().toString()); 1091 1092 //add the default annotation set 1093 eventHandler.annotationSetAdded(new gate.event.DocumentEvent( 1094 document, 1095 gate.event.DocumentEvent.ANNOTATION_SET_ADDED, null)); 1096 1097 //register the for this new document's events 1098 document.addDocumentListener(eventHandler); 1099 1100 //add all the other annotation sets 1101 Map namedASs = document.getNamedAnnotationSets(); 1102 if(namedASs != null){ 1103 Iterator setsIter = namedASs.values().iterator(); 1104 while(setsIter.hasNext()){ 1105 AnnotationSet currentAS = (AnnotationSet)setsIter.next(); 1106 if(currentAS != null){ 1107 eventHandler.annotationSetAdded(new gate.event.DocumentEvent( 1108 document, 1109 gate.event.DocumentEvent.ANNOTATION_SET_ADDED, 1110 currentAS.getName())); 1111 } 1112 } 1113 } 1114 }//protected void this_documentChanged() 1115 1116 /** 1117 * Gets the data related to a given annotation type. 1118 * An annotation type is uniquely identified by the name of its AnnotationSet 1119 * and the name of the type. 1120 * For the default annotation set of a document (which has no name) the 1121 * "<Default>" value is used. 1122 * 1123 * Once a {@link AnnotationEditor.TypeData} value has been obtained it can be used to change 1124 * the way the respective type of annotations are displayed. 1125 * @param setName a {@link java.lang.String}, the name of the annotation set 1126 * @param type a {@link java.lang.String}, the name of the type. 1127 * @return a {@link AnnotationEditor.TypeData} value 1128 */ 1129 protected TypeData getTypeData(String setName, String type){ 1130 Map setMap = (Map)typeDataMap.get(setName); 1131 if(setMap != null) return (TypeData)setMap.get(type); 1132 else return null; 1133 }// protected TypeData getTypeData(String setName, String type) 1134 1135 1136 /** 1137 * Repaints the highlighting for annotation types in the text display. 1138 */ 1139 protected void showHighlights(Set annotations, AttributeSet style) { 1140 //store the state of the text display 1141 int selStart = textPane.getSelectionStart(); 1142 int selEnd = textPane.getSelectionEnd(); 1143 final int position = textPane.viewToModel( 1144 textScroll.getViewport().getViewPosition()); 1145 //hide the text 1146 SwingUtilities.invokeLater(new Runnable() { 1147 public void run() { 1148 progressBar.setValue(0); 1149 //progressBar.setMaximumSize(new Dimension(textScroll.getWidth(),20)); 1150 textScroll.getViewport().setView(progressBox); 1151 textScroll.paintImmediately(textScroll.getBounds()); 1152 } 1153 }); 1154 1155 //highlight the annotations 1156 int size = annotations.size(); 1157 int i = 0; 1158 int lastValue = 0; 1159 int value; 1160 Iterator annIter = annotations.iterator(); 1161 while(annIter.hasNext()){ 1162 Annotation ann = (Annotation)annIter.next(); 1163 textPane.select(ann.getStartNode().getOffset().intValue(), 1164 ann.getEndNode().getOffset().intValue()); 1165 textPane.setCharacterAttributes(style, true); 1166 value = i * 100 / size; 1167 if(value - lastValue >= 5){ 1168 progressBar.setValue(value); 1169 progressBar.paintImmediately(progressBar.getBounds()); 1170 lastValue = value; 1171 } 1172 i++; 1173 } 1174 //restore the state 1175 textPane.select(selStart, selEnd); 1176 SwingUtilities.invokeLater(new Runnable(){ 1177 public void run(){ 1178 //show the text 1179 textScroll.getViewport().setView(textPane); 1180 try{ 1181 textScroll.getViewport().setViewPosition( 1182 textPane.modelToView(position).getLocation()); 1183 textScroll.paintImmediately(textScroll.getBounds()); 1184 }catch(BadLocationException ble){ 1185 } 1186 } 1187 }); 1188 }//protected void showHighlights() 1189 1190 /** 1191 * Updates the GUI when the user has selected an annotation e.g. by using the 1192 * right click popup. That basically means make the appropiate type of 1193 * annotations visible in case it isn't already. 1194 */ 1195 protected void selectAnnotation(String set, Annotation ann) { 1196 TypeData tData = getTypeData(set, ann.getType()); 1197 if(!tData.getVisible()){ 1198 tData.setVisible(true); 1199 //sleep a while so the gui updater thread has time to start 1200 try{ 1201 Thread.sleep(100); 1202 }catch(InterruptedException ie){} 1203 //refresh the display for the type 1204 //(the checkbox has to be shown selected) 1205 DefaultMutableTreeNode node = (DefaultMutableTreeNode) 1206 ((DefaultMutableTreeNode)stylesTreeRoot). 1207 getFirstChild(); 1208 while(node != null && 1209 !((TypeData)node.getUserObject()).getSet().equals(set)) 1210 node = node.getNextSibling(); 1211 if(node != null){ 1212 node = (DefaultMutableTreeNode)node.getFirstChild(); 1213 String type = ann.getType(); 1214 while(node != null && 1215 !((TypeData)node.getUserObject()).getType().equals(type)) 1216 node = node.getNextSibling(); 1217 if(node != null) stylesTreeModel.nodeChanged(node); 1218 } 1219 } 1220 int position = -1; 1221 position = data.indexOf(ann); 1222 if(position != -1){ 1223 position = annotationsTable.getTableRow(position); 1224 if(position != -1){ 1225 annotationsTable.clearSelection(); 1226 annotationsTable.addRowSelectionInterval(position, position); 1227 annotationsTable.scrollRectToVisible( 1228 annotationsTable.getCellRect(position, 0, true)); 1229 } 1230 } 1231 }//protected void selectAnnotation(String set, Annotation ann) 1232 1233 1234 /** 1235 * Creates the layout of this component acording to the set of subcomponents 1236 * (text display, annotations table, etc.) that need to be visible. 1237 */ 1238 protected void layoutComponents(){ 1239 SwingUtilities.invokeLater(new Runnable(){ 1240 public void run(){ 1241 Component leftComp = null, rightComp = null; 1242 if(isTextVisible() && isAnnotationsTableVisible()){ 1243 leftSplit.setTopComponent(textScroll); 1244 leftSplit.setBottomComponent(tableScroll); 1245 leftComp = leftSplit; 1246 }else{ 1247 if(isTextVisible()) leftComp = textScroll; 1248 else if(isAnnotationsTableVisible()) leftComp = tableScroll; 1249 } 1250 1251 boolean corefDisplayed = isCoreferenceVisible() && 1252 isCorefOptionAvailable(); 1253 if(isTypesTreeVisible() && corefDisplayed){ 1254 rightSplit.setTopComponent(stylesTreeScroll); 1255 rightSplit.setBottomComponent(corefScroll); 1256 rightComp = rightSplit; 1257 }else{ 1258 if(isTypesTreeVisible()) rightComp = stylesTreeScroll; 1259 else if(corefDisplayed) rightComp = corefScroll; 1260 } 1261 1262 if(DocumentEditor.this.getComponentCount() > 1) 1263 DocumentEditor.this.remove(1); 1264 if(leftComp != null && rightComp != null){ 1265 //we need the main split 1266 mainSplit.setLeftComponent(leftComp); 1267 mainSplit.setRightComponent(rightComp); 1268 DocumentEditor.this.add(mainSplit, BorderLayout.CENTER); 1269 }else{ 1270 if(leftComp != null) DocumentEditor.this.add(leftComp, 1271 BorderLayout.CENTER); 1272 else if(rightComp != null)DocumentEditor.this.add(rightComp, 1273 BorderLayout.CENTER); 1274 } 1275 1276 DocumentEditor.this.validate(); 1277 DocumentEditor.this.repaint(); 1278 } 1279 }); 1280 } 1281 1282 1283 /** 1284 * Updates the coref tree from the coref data on the document's features 1285 */ 1286 protected void updateCorefTree(){ 1287 if(document == null || document.getFeatures() == null){ 1288 //no coref data; clear the tree 1289 corefTreeRoot.removeAllChildren(); 1290 corefTreeModel.nodeStructureChanged(corefTreeRoot); 1291 setCorefOptionAvailable(false); 1292 return; 1293 } 1294 1295 Map matchesMap = null; 1296 try{ 1297 matchesMap = (Map)document.getFeatures().get(DOCUMENT_COREF_FEATURE_NAME); 1298 }catch(Exception e){ 1299 } 1300 if(matchesMap == null){ 1301 //no coref data; clear the tree 1302 Enumeration nodes = corefTreeRoot.breadthFirstEnumeration(); 1303 while(nodes.hasMoreElements()){ 1304 DefaultMutableTreeNode node = (DefaultMutableTreeNode) 1305 nodes.nextElement(); 1306 if(node.getUserObject() instanceof CorefData){ 1307 ((CorefData)node.getUserObject()).setVisible(false); 1308 } 1309 } 1310 corefTreeRoot.removeAllChildren(); 1311 corefTreeModel.nodeStructureChanged(corefTreeRoot); 1312 setCorefOptionAvailable(false); 1313 return; 1314 } 1315 1316 //matches map is not null; check whether it's valid 1317 Iterator setsIter = matchesMap.keySet().iterator(); 1318 setsLoop: while(setsIter.hasNext()){ 1319 String setName = (String)setsIter.next(); 1320 AnnotationSet annSet = setName == null ? document.getAnnotations() : 1321 document.getAnnotations(setName); 1322 Iterator entitiesIter = ((java.util.List)matchesMap.get(setName)). 1323 iterator(); 1324 //each entity is a list of annotation IDs 1325 while(entitiesIter.hasNext()){ 1326 Iterator idsIter = ((java.util.List)entitiesIter.next()).iterator(); 1327 while(idsIter.hasNext()){ 1328 if(annSet.get((Integer)idsIter.next()) == null){ 1329 //remove the data for this set 1330 setsIter.remove(); 1331 Err.prln("Coreference data for the \"" + 1332 (setName == null ? "Default" : setName) + 1333 "\" annotation set of document \"" + document.getName() + 1334 "\" was invalid and has been removed"); 1335 continue setsLoop; 1336 } 1337 } 1338 } 1339 } 1340 1341 if(matchesMap.isEmpty()){ 1342 //no more coref data 1343 corefTreeRoot.removeAllChildren(); 1344 corefTreeModel.nodeStructureChanged(corefTreeRoot); 1345 setCorefOptionAvailable(false); 1346 return; 1347 } 1348 1349 String[] newSetNames = (String[]) 1350 matchesMap.keySet().toArray(new String[]{}); 1351 Arrays.sort(newSetNames); 1352 1353 ArrayList oldSetNames = new ArrayList(corefTreeRoot.getChildCount()); 1354 Enumeration setNodes = corefTreeRoot.children(); 1355 while(setNodes.hasMoreElements()){ 1356 String oldSetName = (String) 1357 ((DefaultMutableTreeNode)setNodes.nextElement()). 1358 getUserObject(); 1359 oldSetNames.add(oldSetName.equals("Default") ? null : oldSetName); 1360 } 1361 1362 1363 // stores the new set nodes; they will be added to root after the 1364 // processing is done 1365 ArrayList newSetNodes = new ArrayList(); 1366 //for each new set update the children 1367 for(int i =0; i < newSetNames.length; i++){ 1368 String setName = newSetNames[i]; 1369 int oldNodeIndex = oldSetNames.indexOf(setName); 1370 DefaultMutableTreeNode setNode = 1371 (oldNodeIndex != -1) ? 1372 (DefaultMutableTreeNode) 1373 corefTreeRoot.getChildAt(oldNodeIndex) : 1374 new DefaultMutableTreeNode((setName == null ? "Default" : setName), 1375 true); 1376 //if found it will be reused so delete it from the list 1377 if(oldNodeIndex != -1) oldSetNames.remove(oldNodeIndex); 1378 1379 // temporarily stores the new nodes 1380 ArrayList newEntityNodes = new ArrayList(); 1381 //for each set the coref data is a list of lists 1382 Iterator corefDataIter = ((java.util.List)matchesMap.get(setName)). 1383 iterator(); 1384 while(corefDataIter.hasNext()){ 1385 java.util.List newAnnotIDs = (java.util.List)corefDataIter.next(); 1386 CorefData cData = null; 1387 DefaultMutableTreeNode entityNode = null; 1388 //try to find the old coref data 1389 Enumeration entityNodes = setNode.children(); 1390 while(cData == null && entityNodes.hasMoreElements()){ 1391 entityNode = (DefaultMutableTreeNode)entityNodes.nextElement(); 1392 java.util.List oldAnnotIDs = ((CorefData)entityNode.getUserObject()). 1393 getAnnoationIDs(); 1394 java.util.List intersection = new ArrayList(oldAnnotIDs); 1395 intersection.retainAll(newAnnotIDs); 1396 if(!intersection.isEmpty()){ 1397 //we have some common values; assume we found it 1398 cData = (CorefData)entityNode.getUserObject(); 1399 if(intersection.size() == newAnnotIDs.size()){ 1400 //identical values, we just got lucky: noting to do 1401 }else{ 1402 cData.setAnnotationIDs(newAnnotIDs); 1403 } 1404 } 1405 } 1406 if(cData == null){ 1407 //we couldn't find a suitable node, create a new one 1408 cData = new CorefData(newAnnotIDs, false, setName == null ? 1409 "Default" : setName); 1410 entityNode = new DefaultMutableTreeNode(cData, false); 1411 } 1412 newEntityNodes.add(entityNode); 1413 }//while(corefDataIter.hasNext()) 1414 //we're done with this set: add all the nodes to the set node 1415 //set visible to false for all nodes that will not be kept 1416 for(Enumeration entityNodes = setNode.children(); 1417 entityNodes.hasMoreElements();){ 1418 Object anOldNode = entityNodes.nextElement(); 1419 if(!newEntityNodes.contains(anOldNode)){ 1420 ((CorefData)((DefaultMutableTreeNode)anOldNode). 1421 getUserObject()).setVisible(false); 1422 } 1423 } 1424 1425 setNode.removeAllChildren(); 1426 for(Iterator nodesIter = newEntityNodes.iterator(); 1427 nodesIter.hasNext(); 1428 setNode.add((DefaultMutableTreeNode)nodesIter.next())){ 1429 } 1430 newSetNodes.add(setNode); 1431 }//for(int i =0; i < newSetNames.length; i++) 1432 //we're done with all the sets: add the nodes to the tree root 1433 corefTreeRoot.removeAllChildren(); 1434 for(Iterator nodesIter = newSetNodes.iterator(); 1435 nodesIter.hasNext();){ 1436 DefaultMutableTreeNode setNode = (DefaultMutableTreeNode)nodesIter.next(); 1437 corefTreeRoot.add(setNode); 1438 } 1439 SwingUtilities.invokeLater(new Runnable(){ 1440 public void run(){ 1441 corefTreeModel.nodeStructureChanged(corefTreeRoot); 1442 //expand the root 1443 corefTree.expandPath(new TreePath(new Object[]{corefTreeRoot})); 1444 //expand all of root's children 1445 Enumeration children = corefTreeRoot.children(); 1446 while(children.hasMoreElements()){ 1447 corefTree.expandPath(new TreePath(corefTreeModel.getPathToRoot( 1448 (DefaultMutableTreeNode)children.nextElement()))); 1449 } 1450 } 1451 }); 1452 setCorefOptionAvailable(true); 1453 }//protected void updateCorefTree() 1454 1455 1456 /**Should the editor functionality of this component be enabled*/ 1457 public void setEditable(boolean newEditable) { 1458 editable = newEditable; 1459 } 1460 1461 /**Is the editor functionality enabled*/ 1462 public boolean isEditable() { 1463 return editable; 1464 } 1465 public void setAnnotationsTableVisible(boolean annotationsTableVisible) { 1466 boolean oldAnnotationsTableVisible = this.annotationsTableVisible; 1467 this.annotationsTableVisible = annotationsTableVisible; 1468 propertyChangeListeners.firePropertyChange( 1469 "annotationsTableVisible", 1470 new Boolean(oldAnnotationsTableVisible), 1471 new Boolean(annotationsTableVisible)); 1472 } 1473 public boolean isAnnotationsTableVisible() { 1474 return annotationsTableVisible; 1475 } 1476 public void setCoreferenceVisible(boolean coreferenceVisible) { 1477 boolean oldCoreferenceVisible = this.coreferenceVisible; 1478 this.coreferenceVisible = coreferenceVisible; 1479 propertyChangeListeners.firePropertyChange( 1480 "coreferenceVisible", 1481 new Boolean(oldCoreferenceVisible), 1482 new Boolean(coreferenceVisible)); 1483 } 1484 1485 public boolean isCoreferenceVisible() { 1486 return coreferenceVisible; 1487 } 1488 public void setTextVisible(boolean textVisible) { 1489 boolean oldTextVisible = this.textVisible; 1490 this.textVisible = textVisible; 1491 propertyChangeListeners.firePropertyChange("textVisible", 1492 new Boolean(oldTextVisible), 1493 new Boolean(textVisible)); 1494 } 1495 public boolean isTextVisible() { 1496 return textVisible; 1497 } 1498 public void setTypesTreeVisible(boolean typesTreeVisible) { 1499 boolean oldTypesTreeVisible = this.typesTreeVisible; 1500 this.typesTreeVisible = typesTreeVisible; 1501 propertyChangeListeners.firePropertyChange("typesTreeVisible", 1502 new Boolean(oldTypesTreeVisible), 1503 new Boolean(typesTreeVisible)); 1504 } 1505 public boolean isTypesTreeVisible() { 1506 return typesTreeVisible; 1507 } 1508 public void setCorefOptionAvailable(boolean corefOptionAvailable) { 1509 boolean oldCorefOptionAvailable = this.corefOptionAvailable; 1510 this.corefOptionAvailable = corefOptionAvailable; 1511 propertyChangeListeners.firePropertyChange( 1512 "corefOptionAvailable", new Boolean(oldCorefOptionAvailable), 1513 new Boolean(corefOptionAvailable)); 1514 } 1515 1516 public boolean isCorefOptionAvailable() { 1517 return corefOptionAvailable; 1518 } 1519 1520 //inner classes 1521 /** 1522 * A custom table model used to render a table containing the annotations 1523 * from a set of annotation sets. 1524 * The columns will be: Type, Set, Start, End, Features 1525 */ 1526 protected class AnnotationsTableModel extends AbstractTableModel{ 1527 public AnnotationsTableModel(){ 1528 } 1529 1530 public int getRowCount(){ 1531 return data.size(); 1532 } 1533 1534 public int getColumnCount(){ 1535 return 5; 1536 } 1537 1538 public String getColumnName(int column){ 1539 switch(column){ 1540 case 0: return "Type"; 1541 case 1: return "Set"; 1542 case 2: return "Start"; 1543 case 3: return "End"; 1544 case 4: return "Features"; 1545 default:return "?"; 1546 } 1547 } 1548 1549 public Class getColumnClass(int column){ 1550 switch(column){ 1551 case 0: return String.class; 1552 case 1: return String.class; 1553 case 2: return Long.class; 1554 case 3: return Long.class; 1555 case 4: return String.class; 1556 default:return Object.class; 1557 } 1558 } 1559 1560 public Object getValueAt(int row, int column){ 1561 Annotation ann; 1562 ann = (Annotation)data.get(row); 1563 switch(column){ 1564 case -1:{//The actual annotation 1565 return ann; 1566 } 1567 case 0:{//Type 1568 return ann.getType(); 1569 } 1570 case 1:{//Set 1571 Iterator rangesIter = ranges.iterator(); 1572 while(rangesIter.hasNext()){ 1573 Range range = (Range)rangesIter.next(); 1574 if(range.start <= row && row < range.end) return range.setName; 1575 } 1576 return "?"; 1577 } 1578 case 2:{//Start 1579 return ann.getStartNode().getOffset(); 1580 } 1581 case 3:{//End 1582 return ann.getEndNode().getOffset(); 1583 } 1584 case 4:{//Features 1585 if(ann.getFeatures() == null) return null; 1586 else return ann.getFeatures().toString(); 1587 } 1588 default:{ 1589 } 1590 } 1591 return null; 1592 } 1593 }//class AnnotationsTableModel extends AbstractTableModel 1594 1595 1596 protected class CorefData{ 1597 CorefData(java.util.List annotationIDs, boolean visible, String setName){ 1598 this.visible = visible; 1599 this.setName = setName; 1600 this.colour = colGenerator.getNextColor(); 1601 highlights = new ArrayList(); 1602 this.annotationIDs = annotationIDs; 1603 this.title = getNameForCorefList(annotationIDs); 1604 } 1605 1606 /** 1607 * Finds the name for a set of co refering entities (uses the string of the 1608 * first one). 1609 * @param list a list of annotation IDs 1610 */ 1611 String getNameForCorefList(java.util.List list){ 1612 if(list == null || list.isEmpty()) return null; 1613 Integer id = (Integer)list.get(0); 1614 AnnotationSet set = setName.equals("Default") ? 1615 document.getAnnotations() : 1616 document.getAnnotations(setName); 1617 Annotation ann = set.get(id); 1618 1619 String name = null; 1620 try{ 1621 name = document.getContent(). 1622 getContent(ann.getStartNode().getOffset(), 1623 ann.getEndNode().getOffset()).toString(); 1624 }catch(InvalidOffsetException ioe){ 1625 } 1626 return name; 1627 } 1628 1629 public boolean getVisible(){ 1630 return visible; 1631 } 1632 1633 public void removeAnnotations(){ 1634 AnnotationSet set = setName.equals("Default") ? 1635 document.getAnnotations() : 1636 document.getAnnotations(setName); 1637 1638 Iterator idIter = annotationIDs.iterator(); 1639 while(idIter.hasNext()){ 1640 set.remove(set.get((Integer)idIter.next())); 1641 } 1642 ((java.util.List)((Map)document.getFeatures(). 1643 get(ANNIEConstants.DOCUMENT_COREF_FEATURE_NAME)). 1644 get(setName.equals("Default") ? null : setName)).remove(annotationIDs); 1645 annotationIDs.clear(); 1646 updateCorefTree(); 1647 } 1648 1649 public void setVisible(boolean isVisible){ 1650 if(this.visible == isVisible) return; 1651 this.visible = isVisible; 1652 if(visible){ 1653 //add new highlights and store them 1654 AnnotationSet set = setName.equals("Default") ? 1655 document.getAnnotations() : 1656 document.getAnnotations(setName); 1657 Iterator idIter = annotationIDs.iterator(); 1658 ArrayList invalidIDs = new ArrayList(); 1659 while(idIter.hasNext()){ 1660 Integer id = (Integer)idIter.next(); 1661 Annotation ann = set.get(id); 1662 if(ann == null){ 1663 invalidIDs.add(id); 1664 }else try{ 1665 highlights.add(highlighter.addHighlight( 1666 ann.getStartNode().getOffset().intValue(), 1667 ann.getEndNode().getOffset().intValue(), 1668 new DefaultHighlighter.DefaultHighlightPainter(colour))); 1669 }catch(BadLocationException ble){ 1670 ble.printStackTrace(); 1671 } 1672 } 1673 if(!invalidIDs.isEmpty()){ 1674 annotationIDs.removeAll(invalidIDs); 1675 } 1676 }else{ 1677 //remove the highlights 1678 if(!highlights.isEmpty()){ 1679 Iterator hlIter = highlights.iterator(); 1680 while(hlIter.hasNext()){ 1681 Object tag = hlIter.next(); 1682 highlighter.removeHighlight(tag); 1683 hlIter.remove(); 1684 } 1685 } 1686 } 1687 } 1688 1689 public String getTitle(){ 1690 return title; 1691 } 1692 1693 public Color getColour(){ 1694 return colour; 1695 } 1696 1697 public void setColour(Color newColour){ 1698 this.colour = newColour; 1699 if(visible){ 1700 //update the highlights 1701 setVisible(false); 1702 setVisible(true); 1703 } 1704 } 1705 1706 public java.util.List getAnnoationIDs(){ 1707 return annotationIDs; 1708 } 1709 1710 public String getSetName(){ 1711 return setName; 1712 } 1713 public String toString(){ 1714 return title; 1715 } 1716 1717 public void setAnnotationIDs(java.util.List newAnnIDs){ 1718 this.annotationIDs =newAnnIDs; 1719 this.title = getNameForCorefList(annotationIDs); 1720 if(visible){ 1721 //restore the highlights 1722 setVisible(false); 1723 setVisible(true); 1724 } 1725 } 1726 1727 private boolean visible; 1728 private String title; 1729 private String setName; 1730 private Color colour; 1731 private java.util.List highlights; 1732 private java.util.List annotationIDs; 1733 } 1734 1735/* 1736 protected class CorefComboModel extends AbstractListModel 1737 implements ComboBoxModel{ 1738 1739 CorefComboModel(){ 1740 lastReturnedSize = 0; 1741 } 1742 1743 public int getSize(){ 1744 if(document == null || document.getFeatures() == null) return 0; 1745 Map matchesMap = null; 1746 try{ 1747 matchesMap = (Map)document.getFeatures().get(DOCUMENT_COREF_FEATURE_NAME); 1748 }catch(Exception e){ 1749 e.printStackTrace(); 1750 } 1751 int size = (matchesMap == null) ? 0 : matchesMap.size(); 1752 if(lastReturnedSize != size){ 1753 lastReturnedSize = size; 1754 fireDataChanged(); 1755 } 1756 return lastReturnedSize; 1757 } 1758 1759 1760 public Object getElementAt(int index){ 1761 if(document == null || document.getFeatures() == null) return null; 1762 Map matchesMap = null; 1763 try{ 1764 matchesMap = (Map)document.getFeatures().get(DOCUMENT_COREF_FEATURE_NAME); 1765 }catch(Exception e){ 1766 e.printStackTrace(); 1767 } 1768 if(matchesMap == null) return null; 1769 java.util.List setsList = new ArrayList(matchesMap.keySet()); 1770 boolean nullPresent = setsList.remove(null); 1771 Collections.sort(setsList); 1772 if(nullPresent) setsList.add(0, null); 1773 String res = (String)setsList.get(index); 1774 return (res == null) ? "Default" : res; 1775 } 1776 1777 public void setSelectedItem(Object anItem){ 1778 if(anItem == null) selectedItem = null; 1779 else selectedItem = ((String)anItem).equals("Default") ? null : anItem; 1780 } 1781 1782 public Object getSelectedItem(){ 1783 return selectedItem == null ? "Default" : selectedItem; 1784 } 1785 1786 void fireDataChanged(){ 1787 fireContentsChanged(this, 0, getSize()); 1788 } 1789 1790 Object selectedItem = null; 1791 int lastReturnedSize; 1792 } 1793*/ 1794 1795 /** 1796 * Panels used in cell/node renderers 1797 */ 1798 class LazyJPanel extends JPanel{ 1799 /** 1800 * Overridden for performance reasons. 1801 */ 1802 public void revalidate() {} 1803 1804 /** 1805 * Overridden for performance reasons. 1806 */ 1807 public void repaint(long tm, int x, int y, int width, int height) {} 1808 1809 /** 1810 * Overridden for performance reasons. 1811 */ 1812 public void repaint(Rectangle r) {} 1813 1814 /** 1815 * Overridden for performance reasons. 1816 */ 1817 protected void firePropertyChange(String propertyName, Object oldValue, 1818 Object newValue) {} 1819 1820 /** 1821 * Overridden for performance reasons. 1822 */ 1823 public void firePropertyChange(String propertyName, byte oldValue, 1824 byte newValue) {} 1825 1826 /** 1827 * Overridden for performance reasons. 1828 */ 1829 public void firePropertyChange(String propertyName, char oldValue, 1830 char newValue) {} 1831 1832 /** 1833 * Overridden for performance reasons. 1834 */ 1835 public void firePropertyChange(String propertyName, short oldValue, 1836 short newValue) {} 1837 1838 /** 1839 * Overridden for performance reasons. 1840 */ 1841 public void firePropertyChange(String propertyName, int oldValue, 1842 int newValue) {} 1843 1844 /** 1845 * Overridden for performance reasons. 1846 */ 1847 public void firePropertyChange(String propertyName, long oldValue, 1848 long newValue) {} 1849 1850 /** 1851 * Overridden for performance reasons. 1852 */ 1853 public void firePropertyChange(String propertyName, float oldValue, 1854 float newValue) {} 1855 1856 /** 1857 * Overridden for performance reasons. 1858 */ 1859 public void firePropertyChange(String propertyName, double oldValue, 1860 double newValue) {} 1861 1862 /** 1863 * Overridden for performance reasons. 1864 */ 1865 public void firePropertyChange(String propertyName, boolean oldValue, 1866 boolean newValue) {} 1867 } 1868 1869 /** 1870 * A tree node renderer used by the coref tree 1871 */ 1872 class CorefNodeRenderer implements TreeCellRenderer{ 1873 1874 CorefNodeRenderer(){ 1875 label = new JLabel(); 1876 label.setOpaque(true); 1877 1878 checkBox = new JCheckBox(); 1879 checkBox.setBorderPaintedFlat(true); 1880 1881 panel = new LazyJPanel(); 1882 panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); 1883 panel.setOpaque(false); 1884 1885 hBox = new LazyJPanel(); 1886 hBox.setLayout(new BoxLayout(hBox, BoxLayout.X_AXIS)); 1887 hBox.setOpaque(false); 1888 1889 panel.add(Box.createVerticalStrut(2)); 1890 panel.add(hBox); 1891 panel.add(Box.createVerticalStrut(2)); 1892 1893 leftSpacer = Box.createHorizontalStrut(3); 1894 rightSpacer = Box.createHorizontalStrut(3); 1895 1896 selectedBorder = BorderFactory.createLineBorder(Color.blue, 1); 1897 normalBorder = BorderFactory.createEmptyBorder(1, 1, 1, 1); 1898 } 1899 1900 public Component getTreeCellRendererComponent(JTree tree, 1901 Object value, 1902 boolean selected, 1903 boolean expanded, 1904 boolean leaf, 1905 int row, 1906 boolean hasFocus){ 1907 1908 hBox.removeAll(); 1909 hBox.add(leftSpacer); 1910 1911 if(value instanceof DefaultMutableTreeNode){ 1912 value = ((DefaultMutableTreeNode)value).getUserObject(); 1913 } 1914 if(value instanceof CorefData){ 1915 CorefData cData = (CorefData)value; 1916 checkBox.setSelected(cData.getVisible()); 1917 checkBox.setBackground(tree.getBackground()); 1918 1919 label.setBackground(cData.getColour()); 1920 label.setForeground(tree.getForeground()); 1921 label.setText(cData.getTitle()); 1922 label.setFont(tree.getFont()); 1923 hBox.add(checkBox); 1924 hBox.add(label); 1925 hBox.add(rightSpacer); 1926 }else{ 1927 label.setText(value == null ? "" : value.toString()); 1928 label.setForeground(tree.getForeground()); 1929 label.setBackground(tree.getBackground()); 1930 label.setFont(tree.getFont()); 1931 hBox.add(label); 1932 } 1933 if(selected) panel.setBorder(selectedBorder); 1934 else panel.setBorder(normalBorder); 1935 return panel; 1936 } 1937 1938 JLabel label; 1939 JCheckBox checkBox; 1940 JPanel panel; 1941 JPanel hBox; 1942 Border selectedBorder; 1943 Border normalBorder; 1944 Component leftSpacer, rightSpacer; 1945 } 1946 1947 /** 1948 * A tree node renderer used byt the coref tree 1949 */ 1950 class CorefNodeRenderer1 implements TreeCellRenderer{ 1951 1952 CorefNodeRenderer1(){ 1953 label = new JLabel(); 1954 label.setOpaque(true); 1955 1956 toggleButton = new JToggleButton(); 1957 toggleButton.setMargin(new Insets(0,3,0,3)); 1958 1959 panel = new LazyJPanel(); 1960 panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); 1961 panel.setOpaque(false); 1962 topSpacer = Box.createVerticalStrut(2); 1963 bottomSpacer = Box.createVerticalStrut(2); 1964 1965 selectedBorder = BorderFactory.createLineBorder(Color.blue, 1); 1966 normalBorder = BorderFactory.createEmptyBorder(1, 1, 1, 1); 1967 1968 } 1969 1970 public Component getTreeCellRendererComponent(JTree tree, 1971 Object value, 1972 boolean selected, 1973 boolean expanded, 1974 boolean leaf, 1975 int row, 1976 boolean hasFocus){ 1977 1978 panel.removeAll(); 1979 panel.add(topSpacer); 1980 1981 if(value instanceof DefaultMutableTreeNode){ 1982 value = ((DefaultMutableTreeNode)value).getUserObject(); 1983 } 1984 if(value instanceof CorefData){ 1985 CorefData cData = (CorefData)value; 1986 toggleButton.setSelected(cData.getVisible()); 1987 toggleButton.setBackground(cData.getColour()); 1988 toggleButton.setForeground(tree.getForeground()); 1989 toggleButton.setText(cData.getTitle()); 1990 toggleButton.setFont(tree.getFont()); 1991 panel.add(toggleButton); 1992 }else{ 1993 label.setText(value.toString()); 1994 label.setForeground(tree.getForeground()); 1995 label.setBackground(tree.getBackground()); 1996 label.setFont(tree.getFont()); 1997 panel.add(label); 1998 } 1999 panel.add(bottomSpacer); 2000 if(selected) panel.setBorder(selectedBorder); 2001 else panel.setBorder(normalBorder); 2002 return panel; 2003 } 2004 2005 JLabel label; 2006 JToggleButton toggleButton; 2007 JPanel panel; 2008 Border selectedBorder; 2009 Border normalBorder; 2010 Component topSpacer, bottomSpacer; 2011 } 2012 2013 2014 /** 2015 * Displays an entry in the right hand side tree. 2016 * <strong>Implementation Note:</strong> 2017 * This class overrides 2018 * <code>revalidate</code>, 2019 * <code>repaint</code>, 2020 * and 2021 * <code>firePropertyChange</code> 2022 * solely to improve performance. 2023 * If not overridden, these frequently called methods would execute code paths 2024 * that are unnecessary for a tree cell renderer. 2025 */ 2026 class NodeRenderer extends LazyJPanel implements TreeCellRenderer{ 2027 2028 public NodeRenderer(){ 2029 visibleChk = new JCheckBox("",false); 2030 visibleChk.setOpaque(false); 2031 visibleChk.setBorderPaintedFlat(true); 2032 2033 label = new JLabel(); 2034 label.setOpaque(true); 2035 fontAttrs = new HashMap(); 2036 selectedBorder = BorderFactory.createLineBorder(Color.blue, 1); 2037 normalBorder = BorderFactory.createEmptyBorder(1, 1, 1, 1); 2038 setLayout(new BoxLayout(this, BoxLayout.X_AXIS)); 2039 setOpaque(false); 2040 spacer = Box.createHorizontalStrut(3); 2041 } 2042 2043 public Component getTreeCellRendererComponent(JTree tree, 2044 Object value, 2045 boolean selected, 2046 boolean expanded, 2047 boolean leaf, 2048 int row, 2049 boolean hasFocus){ 2050 removeAll(); 2051 add(spacer); 2052 2053 int width = spacer.getWidth(); 2054 2055 2056 TypeData nData = (TypeData) 2057 ((DefaultMutableTreeNode)value).getUserObject(); 2058 2059 if(nData != null){ 2060 label.setText(nData.getTitle()); 2061 setLabelAttributes(nData.getAttributes()); 2062 2063 if(nData.getType() != null) { 2064 visibleChk.setSelected(nData.getVisible()); 2065 add(visibleChk); 2066 width += visibleChk.getMinimumSize().width; 2067 } 2068 }else{ 2069 label.setText(((value == null || value.toString() == null) ? 2070 "" : value.toString())); 2071 } 2072 add(label); 2073 2074 if(selected) setBorder(selectedBorder); 2075 else setBorder(normalBorder); 2076 return this; 2077 }//public Component getTreeCellRendererComponent 2078 2079 protected void setLabelAttributes(AttributeSet style){ 2080 label.setForeground(StyleConstants.getForeground(style)); 2081 label.setBackground(StyleConstants.getBackground(style)); 2082 fontAttrs.clear(); 2083 fontAttrs.put(TextAttribute.FAMILY, StyleConstants.getFontFamily(style)); 2084 fontAttrs.put(TextAttribute.SIZE, new Float(StyleConstants.getFontSize(style))); 2085 if(StyleConstants.isBold(style)) 2086 fontAttrs.put(TextAttribute.WEIGHT, TextAttribute.WEIGHT_BOLD); 2087 else fontAttrs.put(TextAttribute.WEIGHT, TextAttribute.WEIGHT_REGULAR); 2088 if(StyleConstants.isItalic(style)) 2089 fontAttrs.put(TextAttribute.POSTURE, TextAttribute.POSTURE_OBLIQUE); 2090 else fontAttrs.put(TextAttribute.POSTURE, TextAttribute.POSTURE_REGULAR); 2091 if(StyleConstants.isUnderline(style)) 2092 fontAttrs.put(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON); 2093 else fontAttrs.remove(TextAttribute.UNDERLINE); 2094 if(StyleConstants.isStrikeThrough(style)) 2095 fontAttrs.put(TextAttribute.STRIKETHROUGH, TextAttribute.STRIKETHROUGH_ON); 2096 else fontAttrs.remove(TextAttribute.STRIKETHROUGH); 2097 if(StyleConstants.isSuperscript(style)) 2098 fontAttrs.put(TextAttribute.SUPERSCRIPT, TextAttribute.SUPERSCRIPT_SUPER); 2099 else if(StyleConstants.isSubscript(style)) 2100 fontAttrs.put(TextAttribute.SUPERSCRIPT, TextAttribute.SUPERSCRIPT_SUB); 2101 else fontAttrs.remove(TextAttribute.SUPERSCRIPT); 2102 2103 label.setFont(new Font(fontAttrs)); 2104 } 2105 2106 Border selectedBorder; 2107 Border normalBorder; 2108 JCheckBox visibleChk; 2109 JLabel label; 2110 Map fontAttrs; 2111 Component spacer; 2112 }//class NodeRenderer extends JPanel implements TreeCellRenderer 2113 /** 2114 * Displays an entry in the right hand side tree. 2115 * <strong>Implementation Note:</strong> 2116 * This class overrides 2117 * <code>revalidate</code>, 2118 * <code>repaint</code>, 2119 * and 2120 * <code>firePropertyChange</code> 2121 * solely to improve performance. 2122 * If not overridden, these frequently called methods would execute code paths 2123 * that are unnecessary for a tree cell renderer. 2124 */ 2125 class NodeRenderer1 extends LazyJPanel implements TreeCellRenderer{ 2126 2127 public NodeRenderer1(){ 2128 visibleChk = new JCheckBox("",false); 2129 visibleChk.setOpaque(false); 2130 visibleChk.setBorderPaintedFlat(true); 2131 textComponent = new JTextPane(); 2132 selectedBorder = BorderFactory.createLineBorder(Color.blue, 1); 2133 normalBorder = BorderFactory.createEmptyBorder(1, 1, 1, 1); 2134 setLayout(new BoxLayout(this, BoxLayout.X_AXIS)); 2135 setOpaque(false); 2136 spacer = Box.createHorizontalStrut(3); 2137 } 2138 2139 public Component getTreeCellRendererComponent(JTree tree, 2140 Object value, 2141 boolean selected, 2142 boolean expanded, 2143 boolean leaf, 2144 int row, 2145 boolean hasFocus){ 2146 removeAll(); 2147 add(spacer); 2148 2149 int width = spacer.getWidth(); 2150 2151 //the text pane needs to be sized for modelToView() to work 2152 textComponent.setSize(1000, 1000); 2153 2154 TypeData nData = (TypeData) 2155 ((DefaultMutableTreeNode)value).getUserObject(); 2156// javax.swing.text.Document doc = textComponent.getDocument(); 2157 2158 if(nData != null){ 2159 textComponent.setText(nData.getTitle()); 2160 textComponent.selectAll(); 2161 textComponent.setCharacterAttributes(nData.getAttributes(), false); 2162 textComponent.select(0, 0); 2163// try{ 2164// doc.remove(0, doc.getLength()); 2165// doc.insertString(0, nData.getTitle(), 2166// nData.getAttributes()); 2167// }catch(BadLocationException ble){ 2168// ble.printStackTrace(); 2169// } 2170 2171 if(nData.getType() != null) { 2172 visibleChk.setSelected(nData.getVisible()); 2173 add(visibleChk); 2174 width += visibleChk.getMinimumSize().width; 2175 } 2176 }else{ 2177 textComponent.setText(((value == null || value.toString() == null) ? 2178 "" : value.toString())); 2179// try{ 2180// doc.remove(0, doc.getLength()); 2181// doc.insertString(0, value.toString(), 2182// textComponent.getStyle("default")); 2183// }catch(BadLocationException ble){ 2184// ble.printStackTrace(); 2185// } 2186 } 2187 setTextComponentSize(textComponent); 2188 add(textComponent); 2189 width += textComponent.getPreferredSize().width; 2190 if(selected) setBorder(selectedBorder); 2191 else setBorder(normalBorder); 2192 width += getInsets().left + getInsets().right; 2193 setPreferredSize(null); 2194 setPreferredSize(new Dimension(width, super.getPreferredSize().height)); 2195 return this; 2196 }//public Component getTreeCellRendererComponent 2197 2198 protected void setTextComponentSize(JTextComponent comp){ 2199 try{ 2200 if(comp.getDocument() == null || comp.getDocument().getLength() <= 0){ 2201 return; 2202 } 2203 int width = 0; 2204 Rectangle rect = comp.modelToView(0); 2205 int height = rect.height; 2206 int length = comp.getDocument().getLength(); 2207 if(length > 0){ 2208 Rectangle rect2 = comp.modelToView(length ); 2209 if(rect2 != null){ 2210 if(rect.x < rect2.x){ 2211 //left to right 2212 width = rect2.x + rect2.width - rect.x; 2213 }else{ 2214 //RtL 2215 width = rect.x +rect.width - rect2.x; 2216 } 2217 height = Math.max(height, rect2.height); 2218 } 2219 } 2220 Insets insets = comp.getInsets(); 2221 Dimension dim = new Dimension(width + insets.left + insets.right + 5, 2222 height + insets.top + insets.bottom); 2223 comp.setPreferredSize(dim); 2224 }catch(BadLocationException ble){ 2225 //this will work the next time around so it's safe to ignore it now 2226 } 2227 } 2228 Border selectedBorder; 2229 Border normalBorder; 2230 JCheckBox visibleChk; 2231 JTextPane textComponent; 2232 Component spacer; 2233 }//class NodeRenderer extends JPanel implements TreeCellRenderer 2234 2235 /** 2236 * Displays an entry in the right hand side tree. 2237 * <strong><a name="override">Implementation Note:</a></strong> 2238 * This class overrides 2239 * <code>revalidate</code>, 2240 * <code>repaint</code>, 2241 * and 2242 * <code>firePropertyChange</code> 2243 * solely to improve performance. 2244 * If not overridden, these frequently called methods would execute code paths 2245 * that are unnecessary for a tree cell renderer. 2246 */ 2247/* 2248 class NodeRenderer1 extends JPanel implements TreeCellRenderer{ 2249 2250 public NodeRenderer1(){ 2251 visibleChk = new JCheckBox("",false); 2252 visibleChk.setOpaque(false); 2253 typeComponent = new JTextPane(); 2254 setComponent = new JTextPane(); 2255 selectedBorder = BorderFactory.createLineBorder(Color.blue); 2256 normalBorder = BorderFactory.createEmptyBorder(1,1,1,1); 2257 2258 setPanel = new LazyJPanel(); 2259 setPanel.setOpaque(false); 2260 setPanel.setLayout(new BoxLayout(setPanel, BoxLayout.X_AXIS)); 2261 setPanel.add(setComponent); 2262 typePanel = new LazyJPanel(); 2263 typePanel.setOpaque(false); 2264 typePanel.setLayout(new BoxLayout(typePanel, BoxLayout.X_AXIS)); 2265 typePanel.add(visibleChk); 2266 typePanel.add(typeComponent); 2267 } 2268 2269 public Component getTreeCellRendererComponent(JTree tree, 2270 Object value, 2271 boolean selected, 2272 boolean expanded, 2273 boolean leaf, 2274 int row, 2275 boolean hasFocus){ 2276 JComponent renderer = null; 2277 TypeData nData = (TypeData) 2278 ((DefaultMutableTreeNode)value).getUserObject(); 2279 if(nData != null){ 2280 if(nData.getType() != null) { 2281 visibleChk.setSelected(nData.getVisible()); 2282 typeComponent.setSize(1000, 1000); 2283 javax.swing.text.Document doc = typeComponent.getDocument(); 2284 try{ 2285 doc.remove(0, doc.getLength()); 2286 doc.insertString(0, nData.getTitle(), nData.getAttributes()); 2287 }catch(BadLocationException ble){ 2288 ble.printStackTrace(); 2289 } 2290 setTextComponentSize(typeComponent); 2291// typePanel.removeAll(); 2292// typePanel.add(visibleChk); 2293// typePanel.add(typeComponent); 2294 renderer = typePanel; 2295 }else{ 2296 setComponent.setSize(1000, 1000); 2297 javax.swing.text.Document doc = setComponent.getDocument(); 2298 try{ 2299 doc.remove(0, doc.getLength()); 2300 doc.insertString(0, nData.getTitle(), nData.getAttributes()); 2301 }catch(BadLocationException ble){ 2302 ble.printStackTrace(); 2303 } 2304 setTextComponentSize(setComponent); 2305// setPanel.removeAll(); 2306// setPanel.add(setComponent); 2307 renderer = setPanel; 2308 } 2309 }else{ 2310 setComponent.setSize(1000, 1000); 2311 javax.swing.text.Document doc = setComponent.getDocument(); 2312 try{ 2313 doc.remove(0, doc.getLength()); 2314 doc.insertString(0, value.toString(), setComponent.getStyle("default")); 2315 }catch(BadLocationException ble){ 2316 ble.printStackTrace(); 2317 } 2318 setTextComponentSize(setComponent); 2319// setPanel.removeAll(); 2320// setPanel.add(setComponent); 2321 renderer = setPanel; 2322 } 2323 if(selected) renderer.setBorder(selectedBorder); 2324 else renderer.setBorder(normalBorder); 2325 return renderer; 2326 }//public Component getTreeCellRendererComponent 2327 2328 protected void setTextComponentSize(JTextComponent comp){ 2329 try{ 2330 Rectangle rect = comp.modelToView(0); 2331 int length = comp.getDocument().getLength(); 2332 if(length > 0){ 2333 Rectangle rect2 = comp.modelToView(length - 1); 2334 if(rect2 != null){ 2335Out.pr("Rect2.x " + rect2.x); 2336 //this mutates rect 2337 rect = SwingUtilities.computeUnion(rect2.x, rect2.y, rect2.width, 2338 rect2.height, rect); 2339Out.prln("Rect.width " + rect.width); 2340 }else{ 2341Out.prln("NULL size"); 2342 } 2343 } 2344 Insets insets = comp.getInsets(); 2345 Dimension dim = new Dimension(rect.width + insets.left + insets.right, 2346 rect.height + insets.top + insets.bottom); 2347 comp.setPreferredSize(dim); 2348 }catch(BadLocationException ble){ 2349 ble.printStackTrace(); 2350 } 2351 } 2352 2353 Border selectedBorder; 2354 Border normalBorder; 2355 JCheckBox visibleChk; 2356 JTextPane setComponent; 2357 JTextPane typeComponent; 2358 JPanel setPanel; 2359 JPanel typePanel; 2360 }//class NodeRenderer extends JPanel implements TreeCellRenderer 2361*/ 2362 /** 2363 * Holds the GUI metadata for a given annotation type. An annotation type is 2364 * uniquely identified by the name of its AnnotationSet and the name of the 2365 * type. 2366 * For the default annotation set of a document (which has no name) the 2367 * "<Default>" value is used. 2368 * The GUI metadata contains, amongst other things, the style used for 2369 * highlighting the annotations of this type. 2370 * These styles are cascading styles (there is a relation of inheritance 2371 * between them) so the annotation type style inherits the characteristics 2372 * from the style associated with the annotation set it belongs to. 2373 * 2374 * For eficiency reasons there are some intermediary styles between a parent 2375 * and a child style that used for changing the display in one operation. 2376 */ 2377 public class TypeData { 2378 2379 public TypeData(String set, String type, boolean visible){ 2380 this.set = set; 2381 this.type = type; 2382 this.visible = visible; 2383 Map setMap = (Map)typeDataMap.get(set); 2384 if(setMap == null){ 2385 setMap = new HashMap(); 2386 typeDataMap.put(set, setMap); 2387 } 2388 if(type == null) { 2389 //this node represents a Set 2390 style = textPane.addStyle(set, textPane.getStyle("default")); 2391 } else { 2392 style = textPane.addStyle(set + "." + type, textPane.getStyle(set)); 2393 StyleConstants.setBackground(style, 2394 colGenerator.getNextColor().brighter()); 2395 //add an intermediary style that will be used for the actual display 2396 textPane.addStyle("_" + set + "." + type, style); 2397 //add the style that will be used for the actual display 2398 textPane.addStyle("_" + set + "." + type + "_", 2399 textPane.getStyle("_" + set + "." + type)); 2400 setMap.put(type, this); 2401 } 2402 } 2403 2404 public String getSet() { return set;} 2405 2406 public void setSet(String set) {this.set = set;} 2407 2408 public String getType() {return type;} 2409 2410 public String getTitle() {return (type == null) ? set + " annotations" : 2411 type;} 2412 public boolean getVisible() {return visible;} 2413 2414 public void setVisible(boolean isVisible) { 2415 if(this.visible == isVisible) return; 2416 this.visible = isVisible; 2417 //this is most likely called from the SWING thread so we want to get 2418 //out of here as quickly as possible. We'll start a new thread that will 2419 //do all that needs doing 2420 Runnable runnable = new Runnable() { 2421 public void run() { 2422 if(visible) { 2423 //make the corresponding range visible 2424 //update the annotations table 2425 synchronized(data) { 2426 range = new Range(set, type, data.size(), 2427 data.size() + annotations.size()); 2428 ranges.add(range); 2429 data.addAll(annotations); 2430 SwingUtilities.invokeLater(new Runnable() { 2431 public void run() { 2432 annotationsTableModel.fireTableDataChanged(); 2433 } 2434 }); 2435 } 2436 2437 //update the text display 2438 Style actualStyle = textPane.getStyle("_" + set + "." + type); 2439 actualStyle.setResolveParent(style); 2440 showHighlights(annotations, textPane.getStyle("_" + set + "." 2441 + type + "_")); 2442 } else { 2443 //hide the corresponding range 2444 //update the annotations table 2445 Collections.sort(ranges); 2446 Iterator rangesIter = ranges.iterator(); 2447 while(rangesIter.hasNext()) { 2448 //find my range 2449 Range aRange = (Range)rangesIter.next(); 2450 if(aRange == range){ 2451 rangesIter.remove(); 2452 int size = range.end - range.start; 2453 //remove the elements from Data 2454 data.subList(range.start, range.end).clear(); 2455 //shift back all the remaining ranges 2456 while(rangesIter.hasNext()) { 2457 aRange = (Range)rangesIter.next(); 2458 aRange.start -= size; 2459 aRange.end -= size; 2460 } 2461 } 2462 } 2463 range = null; 2464 SwingUtilities.invokeLater(new Runnable() { 2465 public void run() { 2466 annotationsTableModel.fireTableDataChanged(); 2467 } 2468 }); 2469 //update the text display 2470 Style actualStyle = textPane.getStyle("_" + set + "." + type); 2471 actualStyle.setResolveParent(textPane.getStyle("default")); 2472 }//if(visible) 2473 }//public void run() 2474 };//Runnable runnable = new Runnable() 2475 Thread thread = new Thread(Thread.currentThread().getThreadGroup(), 2476 runnable, 2477 "AnnotationEditor4"); 2478 thread.setPriority(Thread.MIN_PRIORITY); 2479 thread.start(); 2480 }//public void setVisible(boolean isVisible) 2481 2482 public AttributeSet getAttributes() { return style;} 2483 2484 public void setAttributes(AttributeSet newAttributes) { 2485 style.removeAttributes(style.copyAttributes()); 2486 style.addAttributes(newAttributes); 2487 } 2488 2489 2490 public void setAnnotations(Set as) { 2491 this.annotations = as; 2492 } 2493 2494 public Set getAnnotations() { 2495 return annotations; 2496 } 2497 2498 public void setNode(DefaultMutableTreeNode node){ 2499 this.node = node; 2500 } 2501 2502 public DefaultMutableTreeNode getNode(){ 2503 return node; 2504 } 2505 2506 public String toString() {return getTitle();} 2507 2508 private String set; 2509 private String type; 2510 private boolean visible; 2511 private Style style; 2512 private Set annotations = null; 2513 private Range range = null; 2514 2515 /** The node that represents this set/type in the types tree*/ 2516 private DefaultMutableTreeNode node = null; 2517 }//class TypeData 2518 2519 2520 /** 2521 * Describes a range in the {@link #data} structure. A range is a bunch of 2522 * annotations of the same type belonging to the same annotation set that 2523 * are contiguous in the {@link #data} structure. 2524 */ 2525 class Range implements Comparable { 2526 public Range(String setName, String type, int start, int end) { 2527 this.setName = setName; 2528 this.type = type; 2529 this.start = start; 2530 this.end = end; 2531 } 2532 2533 public String toString() { 2534 return setName + ", " + type + " (" + start + ", " + end + ")"; 2535 } 2536 2537 public int compareTo(Object other) { 2538 if(other instanceof Range) return start - ((Range)other).start; 2539 else throw new ClassCastException("Can't compare a " + 2540 other.getClass() + " to a " + 2541 getClass() + "!"); 2542 } 2543 2544 String setName; 2545 String type; 2546 int start; 2547 int end; 2548 }//class Range 2549 2550 2551 /** 2552 * All the events from the document or its annotation sets are handled by 2553 * this inner class. 2554 */ 2555 class EventsHandler implements gate.event.DocumentListener, 2556 AnnotationSetListener{ 2557 2558 public void annotationSetAdded(gate.event.DocumentEvent e) { 2559 String setName = e.getAnnotationSetName(); 2560 AnnotationSet as = (setName == null ? document.getAnnotations() : 2561 document.getAnnotations(setName)); 2562 2563 as.addAnnotationSetListener(this); 2564 if(setName == null) setName = "Default"; 2565 TypeData setData = new TypeData(setName, null, false); 2566 setData.setAnnotations(as); 2567 2568 SwingUtilities.invokeLater(new NodeAdder(setData)); 2569 2570 ArrayList typesLst = new ArrayList(as.getAllTypes()); 2571 Collections.sort(typesLst); 2572 2573 Iterator typesIter = typesLst.iterator(); 2574 while(typesIter.hasNext()){ 2575 String type = (String)typesIter.next(); 2576 TypeData typeData = new TypeData(setName, type, false); 2577 AnnotationSet sameType = as.get(type); 2578 typeData.setAnnotations(sameType); 2579 2580 SwingUtilities.invokeLater(new NodeAdder(typeData)); 2581 } 2582 } 2583 2584 public void annotationSetRemoved(gate.event.DocumentEvent e) { 2585 //we access the GUI a lot here so we'll do everything from the 2586 //Swing thread 2587 SwingUtilities.invokeLater( 2588 new SetRemovedOperation(e.getAnnotationSetName())); 2589 }//public void annotationSetRemoved(gate.event.DocumentEvent e) 2590 2591 public void annotationAdded(AnnotationSetEvent e) { 2592 AnnotationSet set = (AnnotationSet)e.getSource(); 2593 String setName = set.getName(); 2594 if(setName == null) setName = "Default"; 2595 Annotation ann = e.getAnnotation(); 2596 String type = ann.getType(); 2597 TypeData tData = getTypeData(setName, type); 2598 2599 boolean tableChanged = false; 2600 if(tData != null){ 2601// tData.annotations.add(ann); 2602 if(tData.getVisible()){ 2603 //1) update the table 2604 data.add(tData.range.end, ann); 2605 tData.range.end++; 2606 Iterator rangesIter = ranges. 2607 subList( 2608 ranges.indexOf(tData.range) + 1, 2609 ranges.size()). 2610 iterator(); 2611 while(rangesIter.hasNext()){ 2612 Range aRange = (Range) rangesIter.next(); 2613 aRange.start++; 2614 aRange.end++; 2615 }//while(rangesIter.hasNext()) 2616 tableChanged = true; 2617 2618 //2) update the text 2619 SwingUtilities.invokeLater( 2620 new HihglightsShower(ann, 2621 textPane.getStyle( 2622 "_" + setName + "." + 2623 type + "_"))); 2624 }//if(tData.getVisible()) 2625 } else { 2626 //new type 2627 Map setMap = (Map)typeDataMap.get(setName); 2628 if(setMap == null){ 2629 setMap = new HashMap(); 2630 typeDataMap.put(setName, setMap); 2631 } 2632 tData = new TypeData(setName, type, false); 2633 tData.setAnnotations(set.get(type)); 2634 setMap.put(type, tData); 2635 SwingUtilities.invokeLater(new NodeAdder(tData)); 2636 2637 }//new type 2638 2639 if(tableChanged){ 2640 SwingUtilities.invokeLater(new Runnable() { 2641 public void run(){ 2642 if(annotationsTableModel != null){ 2643 annotationsTableModel.fireTableDataChanged(); 2644 } 2645 } 2646 }); 2647 }//if(tableChanged) 2648 }//public void annotationAdded(AnnotationSetEvent e) 2649 2650 public void annotationRemoved(AnnotationSetEvent e){ 2651 AnnotationSet set = (AnnotationSet)e.getSource(); 2652 String setName = set.getName(); 2653 if(setName == null) setName = "Default"; 2654 Annotation ann = e.getAnnotation(); 2655 String type = ann.getType(); 2656 TypeData tData = getTypeData(setName, type); 2657 boolean tableChanged = false; 2658 2659 if(tData != null){ 2660// tData.annotations.remove(ann); 2661 if(tData.getVisible()){ 2662 //1) update the annotations table 2663 data.remove(ann); 2664 //shorten the range conatining the annotation 2665 tData.range.end--; 2666 //shift all the remaining ranges 2667 Iterator rangesIter = ranges. 2668 subList(ranges.indexOf(tData.range) + 1, 2669 ranges.size()). 2670 iterator(); 2671 while(rangesIter.hasNext()){ 2672 Range aRange = (Range) rangesIter.next(); 2673 aRange.start--; 2674 aRange.end--; 2675 }//while(rangesIter.hasNext()) 2676 tableChanged = true; 2677 2678 //update the text -> hide the highlight 2679 SwingUtilities.invokeLater(new HighlightsRemover(ann)); 2680 }//if(tData.getVisible()) 2681 //if this was the last annotation of this type remove the type node 2682 if((tData.annotations.size() == 1 && 2683 tData.annotations.iterator().next() == ann) || 2684 tData.annotations.size() == 0){ 2685 //no more annotations of this type -> delete the node 2686 SwingUtilities.invokeLater(new NodeRemover(tData)); 2687 //remove the data for this type 2688 Map setMap = (Map)typeDataMap.get(setName); 2689 setMap.remove(tData.getType()); 2690 }//if(tData.getAnnotations().isEmpty()) 2691 }//if(tData != null) 2692 2693 if(tableChanged){ 2694 SwingUtilities.invokeLater(new Runnable() { 2695 public void run(){ 2696 if(annotationsTableModel != null){ 2697 annotationsTableModel.fireTableDataChanged(); 2698 } 2699 } 2700 }); 2701 }//if(tableChanged) 2702 }//public void annotationRemoved(AnnotationSetEvent e) 2703 2704 /** 2705 * Helper class that removes one highlight corresponding to an annotation. 2706 */ 2707 class HighlightsRemover implements Runnable{ 2708 HighlightsRemover(Annotation ann){ 2709 this.ann = ann; 2710 } 2711 public void run(){ 2712 int selStart = textPane.getSelectionStart(); 2713 int selEnd = textPane.getSelectionEnd(); 2714 textPane.select(ann.getStartNode().getOffset().intValue(), 2715 ann.getEndNode().getOffset().intValue()); 2716 textPane.setCharacterAttributes( 2717 textPane.getStyle("default"), true); 2718 textPane.select(selStart, selEnd); 2719 } 2720 Annotation ann; 2721 }//class HihglightsRemover implements Runnable 2722 2723 /** 2724 * Helper class that highlights a given annotation with the specified style. 2725 */ 2726 class HihglightsShower implements Runnable{ 2727 HihglightsShower(Annotation ann, Style style){ 2728 this.ann = ann; 2729 this.style = style; 2730 } 2731 public void run(){ 2732 textPane.select(ann.getStartNode().getOffset().intValue(), 2733 ann.getEndNode().getOffset().intValue()); 2734 textPane.setCharacterAttributes(style, true); 2735 } 2736 Annotation ann; 2737 Style style; 2738 }//class HihglightsRemover implements Runnable 2739 2740 /** 2741 * Helper class that removes one node from the types tree. 2742 */ 2743 class NodeRemover implements Runnable{ 2744 NodeRemover(TypeData tData){ 2745 this.tData = tData; 2746 } 2747 public void run(){ 2748 DefaultMutableTreeNode node = tData.getNode(); 2749 if(node != null){ 2750 stylesTreeModel.removeNodeFromParent(tData.getNode()); 2751 }else{ 2752 Err.prln("Could not find node for " + tData.set + "->" + tData.type); 2753 } 2754 } 2755 TypeData tData; 2756 }//class NodeRemover implements Runnable 2757 2758 /** 2759 * Helper class that adds a specified tree node 2760 */ 2761 class NodeAdder implements Runnable{ 2762 NodeAdder(TypeData tData){ 2763 this.tData = tData; 2764 } 2765 public void run(){ 2766 //create the new node 2767 DefaultMutableTreeNode newNode = 2768 new DefaultMutableTreeNode(tData, tData.getType() == null); 2769 tData.setNode(newNode); 2770 //find its parent 2771 DefaultMutableTreeNode node = null; 2772 if(tData.getType() == null){ 2773 //set node 2774 node = (DefaultMutableTreeNode)stylesTreeRoot; 2775//System.out.println("Set node " + tData.getSet()); 2776 }else{ 2777//System.out.println("Type node " + tData.getSet() + ":" + tData.getType()); 2778 2779 //the document should at least have the default annotation set 2780 //if it doesn't, then something's fishy -> return; 2781 if(((DefaultMutableTreeNode)stylesTreeRoot).getChildCount() == 0) 2782 return; 2783 node = (DefaultMutableTreeNode) 2784 ((DefaultMutableTreeNode)stylesTreeRoot).getFirstChild(); 2785 while(node != null && 2786 !((TypeData)node.getUserObject()).getSet().equals(tData.getSet())) 2787 node = node.getNextSibling(); 2788 } 2789 2790 //we have to add typeNode to node 2791 //find the right place 2792 int i = 0; 2793 if(tData.getType() == null){ 2794 while (i < node.getChildCount() && 2795 ((TypeData) 2796 ((DefaultMutableTreeNode)node.getChildAt(i)). 2797 getUserObject() 2798 ).getSet().compareTo(tData.getSet())<0) i++; 2799 }else{ 2800 while (i < node.getChildCount() && 2801 ((TypeData) 2802 ((DefaultMutableTreeNode)node.getChildAt(i)). 2803 getUserObject() 2804 ).getType().compareTo(tData.getType())<0) i++; 2805 } 2806 2807 //insert it! 2808 stylesTreeModel.insertNodeInto(newNode, node, i); 2809 2810 if(tData.getType() == null){ 2811 //set node, expand it! 2812 stylesTree.expandPath(new TreePath(new Object[]{stylesTreeRoot, 2813 newNode})); 2814 } 2815 } 2816 2817 TypeData tData; 2818 }//class NodeAdder implements Runnable 2819 2820 /** 2821 * Helper class that handles the removal of a named annotation set. 2822 * This runnable should only be called from the Swing thread 2823 */ 2824 class SetRemovedOperation implements Runnable{ 2825 SetRemovedOperation(String setName){ 2826 this.setName = setName; 2827 } 2828 2829 public void run(){ 2830 //find the set node 2831 Enumeration setNodesEnum = stylesTreeRoot.children(); 2832 DefaultMutableTreeNode setNode = null; 2833 boolean done = false; 2834 while(!done && setNodesEnum.hasMoreElements()){ 2835 setNode = (DefaultMutableTreeNode)setNodesEnum.nextElement(); 2836 done = ((TypeData)setNode.getUserObject()).getSet().equals(setName); 2837 } 2838 2839 if(!((TypeData)setNode.getUserObject()).getSet().equals(setName)){ 2840 throw new GateRuntimeException( 2841 "Could not find the tree node for the " + setName + 2842 " annotation set!"); 2843 } 2844 2845 boolean tableChanged = false; 2846 Enumeration typeNodesEnum = setNode.children(); 2847 while(typeNodesEnum.hasMoreElements()){ 2848 DefaultMutableTreeNode typeNode = 2849 (DefaultMutableTreeNode)typeNodesEnum.nextElement(); 2850 TypeData tData = (TypeData)typeNode.getUserObject(); 2851 if(tData.getVisible()){ 2852 //1) update the annotations table 2853 data.subList(tData.range.start, tData.range.end).clear(); 2854 //remove the range 2855 int delta = tData.range.end - tData.range.start; 2856 //1a)first shift all following ranges 2857 Iterator rangesIter = ranges. 2858 subList(ranges.indexOf(tData.range) + 1, 2859 ranges.size()). 2860 iterator(); 2861 while(rangesIter.hasNext()){ 2862 Range aRange = (Range) rangesIter.next(); 2863 aRange.start -= delta; 2864 aRange.end -= delta; 2865 }//while(rangesIter.hasNext()) 2866 //1b)now remove the range 2867 ranges.remove(tData.range); 2868 tableChanged = true; 2869 2870 //2)update the text 2871 //hide the highlights 2872 2873 Iterator annIter = tData.getAnnotations().iterator(); 2874 while(annIter.hasNext()){ 2875 Annotation ann = (Annotation)annIter.next(); 2876 new HighlightsRemover(ann).run(); 2877 }//while(annIter.hasNext()) 2878 }//if(tData.getVisible()) 2879 }//while(typeNodesEnum.hasMoreElements()) 2880 2881 if(tableChanged){ 2882 if(annotationsTableModel != null){ 2883 annotationsTableModel.fireTableDataChanged(); 2884 } 2885 }//if(tableChanged) 2886 2887 //remove the node for the set 2888 typeDataMap.remove(setName); 2889 stylesTreeModel.removeNodeFromParent(setNode); 2890 }//public void run() 2891 2892 String setName; 2893 } 2894 2895 }//class EventsHandler 2896 2897 /** 2898 * This class handles the blinking for the selected annotations in the 2899 * text display. 2900 */ 2901 class SelectionBlinker implements Runnable{ 2902 public void run(){ 2903 synchronized(selectionHighlighter){ 2904 highlights = selectionHighlighter.getHighlights(); 2905 } 2906 2907 2908 while(highlights != null && highlights.length > 0){ 2909 SwingUtilities.invokeLater(new Runnable(){ 2910 public void run(){ 2911 showHighlights(); 2912 } 2913 }); 2914 try{ 2915 Thread.sleep(400); 2916 }catch(InterruptedException ie){ 2917 ie.printStackTrace(Err.getPrintWriter()); 2918 } 2919 SwingUtilities.invokeLater(new Runnable(){ 2920 public void run(){ 2921 hideHighlights(); 2922 } 2923 }); 2924 2925 try{ 2926 Thread.sleep(600); 2927 }catch(InterruptedException ie){ 2928 ie.printStackTrace(Err.getPrintWriter()); 2929 } 2930 synchronized(selectionHighlighter){ 2931 highlights = selectionHighlighter.getHighlights(); 2932 } 2933 }//while we have highlights 2934 //no more highlights; stop the thread by exiting run(); 2935 synchronized(selectionHighlighter){ 2936 thread = null; 2937 } 2938 }//run() 2939 2940 /** 2941 * If there is no running thread then starts one and stores it in 2942 * the <tt>thread</tt> member. 2943 */ 2944 public synchronized void testAndStart(){ 2945 synchronized(selectionHighlighter){ 2946 if(thread == null){ 2947 thread = new Thread(Thread.currentThread().getThreadGroup(), 2948 this, "AnnotationEditor2"); 2949 thread.setPriority(Thread.MIN_PRIORITY); 2950 thread.start(); 2951 } 2952 } 2953 } 2954 2955 protected void showHighlights(){ 2956 actualHighlights.clear(); 2957 try{ 2958 for(int i = 0; i < highlights.length; i++){ 2959 actualHighlights.add(highlighter.addHighlight( 2960 highlights[i].getStartOffset(), 2961 highlights[i].getEndOffset(), 2962 highlights[i].getPainter())); 2963 } 2964 }catch(BadLocationException ble){ 2965 ble.printStackTrace(Err.getPrintWriter()); 2966 } 2967 } 2968 2969 protected void hideHighlights(){ 2970 Iterator hIter = actualHighlights.iterator(); 2971 while(hIter.hasNext()) highlighter.removeHighlight(hIter.next()); 2972 } 2973 2974 ArrayList actualHighlights = new ArrayList(); 2975 Thread thread; 2976 Highlighter.Highlight[] highlights; 2977 }//class SelectionBlinker implements Runnable 2978 2979 /** 2980 * Fixes the <a 2981 * href="http://developer.java.sun.com/developer/bugParade/bugs/4406598.html"> 2982 * 4406598 bug</a> in swing text components. 2983 * The bug consists in the fact that the Background attribute is ignored by 2984 * the text component whent it is defined in a style from which the current 2985 * style inherits. 2986 */ 2987 public class CustomLabelView extends javax.swing.text.LabelView { 2988 public CustomLabelView(Element elem) { 2989 super(elem); 2990 } 2991 2992 public Color getBackground() { 2993 AttributeSet attr = getAttributes(); 2994 if (attr != null) { 2995 javax.swing.text.Document d = super.getDocument(); 2996 if (d instanceof StyledDocument){ 2997 StyledDocument doc = (StyledDocument) d; 2998 return doc.getBackground(attr); 2999 }else{ 3000 return null; 3001 } 3002 } 3003 return null; 3004 } 3005 } 3006 3007 /** 3008 * The popup menu items used to select annotations at right click. 3009 * Apart from the normal {@link javax.swing.JMenuItem} behaviour, this menu 3010 * item also highlits the annotation which it would select if pressed. 3011 */ 3012 protected class HighlightAnnotationMenu extends JMenu { 3013 public HighlightAnnotationMenu(Annotation ann, AnnotationSet aSet) { 3014 super(ann.getType()); 3015 setToolTipText("<html><b>Features:</b><br>" + 3016 (ann.getFeatures() == null ? "" : 3017 ann.getFeatures().toString()) + "</html>"); 3018 this.annotation = ann; 3019 this.set = aSet; 3020 this.setName = (set.getName() == null) ? "Default" : set.getName(); 3021 start = ann.getStartNode().getOffset().intValue(); 3022 end = ann.getEndNode().getOffset().intValue(); 3023 this.addMouseListener(new MouseAdapter() { 3024 public void mouseEntered(MouseEvent e) { 3025 try { 3026 highlight = highlighter.addHighlight(start, end, 3027 DefaultHighlighter.DefaultPainter); 3028 }catch(BadLocationException ble){ 3029 throw new GateRuntimeException(ble.toString()); 3030 } 3031 } 3032 3033 public void mouseExited(MouseEvent e) { 3034 if(highlight != null){ 3035 highlighter.removeHighlight(highlight); 3036 highlight = null; 3037 } 3038 } 3039 }); 3040 3041 this.add(new AbstractAction(){ 3042 { 3043 putValue(NAME, "Select"); 3044 } 3045 public void actionPerformed(ActionEvent e) { 3046 Runnable runnable = new Runnable(){ 3047 public void run(){ 3048 if(highlight != null){ 3049 highlighter.removeHighlight(highlight); 3050 highlight = null; 3051 } 3052 selectAnnotation(setName, annotation); 3053 } 3054 }; 3055 Thread thread = new Thread(Thread.currentThread().getThreadGroup(), 3056 runnable, 3057 "AnnotationEditor5"); 3058 thread.start(); 3059 } 3060 }); 3061 3062 this.add(new AbstractAction(){ 3063 { 3064 putValue(NAME, "Delete"); 3065 } 3066 public void actionPerformed(ActionEvent e) { 3067 Runnable runnable = new Runnable(){ 3068 public void run(){ 3069 if(highlight != null){ 3070 highlighter.removeHighlight(highlight); 3071 highlight = null; 3072 } 3073 set.remove(annotation); 3074 } 3075 }; 3076 Thread thread = new Thread(Thread.currentThread().getThreadGroup(), 3077 runnable, 3078 "AnnotationEditor5"); 3079 thread.start(); 3080 } 3081 }); 3082 3083 } 3084 3085 int start; 3086 int end; 3087 AnnotationSet set; 3088 String setName; 3089 Annotation annotation; 3090 Object highlight; 3091 } 3092 3093 3094 protected class DeleteSelectedAnnotationsAction extends AbstractAction { 3095 public DeleteSelectedAnnotationsAction(JComponent source){ 3096 super("Delete selected annotations"); 3097 this.source = source; 3098 } 3099 3100 public void actionPerformed(ActionEvent evt){ 3101 if(source == annotationsTable){ 3102 //collect the list of annotations to be removed 3103 //maps from set name to list of annotations to be removed 3104 Map annotationsBySet = new HashMap(); 3105 int[] rows = annotationsTable.getSelectedRows(); 3106 String setName; 3107 for(int i = 0; i < rows.length; i++){ 3108 int row = rows[i]; 3109 //find the annotation 3110 Annotation ann = (Annotation)annotationsTable. 3111 getModel().getValueAt(row, -1); 3112 //find the annotation set 3113 setName = (String)annotationsTable.getModel(). 3114 getValueAt(row, 1); 3115 java.util.List existingList = (java.util.List) 3116 annotationsBySet.get(setName); 3117 if(existingList == null){ 3118 existingList = new ArrayList(); 3119 annotationsBySet.put(setName, existingList); 3120 } 3121 existingList.add(ann); 3122 }//for(int i = 0; i < rows.length; i++) 3123 //remove the collected annotations 3124 Iterator setsIter = annotationsBySet.keySet().iterator(); 3125 while(setsIter.hasNext()){ 3126 setName = (String)setsIter.next(); 3127 AnnotationSet set = setName.equals("Default")? 3128 document.getAnnotations() : 3129 document.getAnnotations(setName); 3130 set.removeAll((java.util.List)annotationsBySet.get(setName)); 3131 }//while(setsIter.hasNext()) 3132 }else if(source == stylesTree){ 3133 TreePath[] paths = stylesTree.getSelectionPaths(); 3134 for(int i = 0; i < paths.length; i++){ 3135 TypeData tData = (TypeData)((DefaultMutableTreeNode) 3136 paths[i].getLastPathComponent()).getUserObject(); 3137 String setName = tData.getSet(); 3138 if(tData.getType() == null){ 3139 //set node 3140 if(setName.equals("Default")){ 3141 JOptionPane.showMessageDialog( 3142 DocumentEditor.this, 3143 "The default annotation set cannot be deleted!\n" + 3144 "It will only be cleared...", 3145 "Gate", JOptionPane.ERROR_MESSAGE); 3146 document.getAnnotations().clear(); 3147 }else{ 3148 document.removeAnnotationSet(setName); 3149 } 3150 }else{ 3151 //type node 3152 if(!setName.equals("Default") && 3153 !document.getNamedAnnotationSets().containsKey(setName)){ 3154 //the set for this type has already been removed completely 3155 //nothing more do (that's nice :) ) 3156 return; 3157 } 3158 AnnotationSet set = setName.equals("Default") ? 3159 document.getAnnotations() : 3160 document.getAnnotations(setName); 3161 if(set != null){ 3162 AnnotationSet subset = set.get(tData.getType()); 3163 if(subset != null) set.removeAll(new ArrayList(subset)); 3164 }//if(set != null) 3165 }//type node 3166 }//for(int i = 0; i < paths.length; i++) 3167 }else if(source == corefTree){ 3168 TreePath[] paths = corefTree.getSelectionPaths(); 3169 for(int i = 0; i < paths.length; i++){ 3170 CorefData cData = (CorefData)((DefaultMutableTreeNode) 3171 paths[i].getLastPathComponent()).getUserObject(); 3172 class CorefClearer implements Runnable{ 3173 CorefClearer(CorefData cData){ 3174 this.cData = cData; 3175 } 3176 public void run(){ 3177 cData.removeAnnotations(); 3178 } 3179 CorefData cData; 3180 } 3181 Thread thread = new Thread(new CorefClearer(cData)); 3182 thread.setPriority(Thread.MIN_PRIORITY); 3183 thread.start(); 3184 } 3185 } 3186 }//public void actionPerformed(ActionEvent evt) 3187 JComponent source; 3188 }//protected class DeleteSelectedAnnotationsAction 3189 3190 protected class SearchAction extends AbstractAction { 3191 public SearchAction(){ 3192 super("Search"); 3193 putValue(SHORT_DESCRIPTION, "Search within the text"); 3194 putValue(SMALL_ICON, MainFrame.getIcon("search.gif")); 3195 } 3196 3197 public void actionPerformed(ActionEvent evt){ 3198 if(searchDialog == null){ 3199 Window parent = SwingUtilities.getWindowAncestor(DocumentEditor.this); 3200 searchDialog = (parent instanceof Dialog) ? 3201 new SearchDialog((Dialog)parent) : 3202 new SearchDialog((Frame)parent); 3203 searchDialog.pack(); 3204 searchDialog.setLocationRelativeTo(DocumentEditor.this); 3205 searchDialog.setResizable(false); 3206 MainFrame.getGuiRoots().add(searchDialog); 3207 } 3208 3209 if(searchDialog.isVisible()){ 3210 searchDialog.toFront(); 3211 }else{ 3212 searchDialog.show(); 3213 } 3214 } 3215 } 3216 3217 protected class SearchDialog extends JDialog{ 3218 SearchDialog(Frame owner){ 3219 super(owner, false); 3220 setTitle( "Find in \"" + document.getName() + "\""); 3221 initLocalData(); 3222 initGuiComponents(); 3223 initListeners(); 3224 } 3225 3226 SearchDialog(Dialog owner){ 3227 super(owner, false); 3228 setTitle("Find in \"" + document.getName() + "\""); 3229 initLocalData(); 3230 initGuiComponents(); 3231 initListeners(); 3232 } 3233 protected void initLocalData(){ 3234 patternRE = null; 3235 nextMatchStartsFrom = 0; 3236 content = document.getContent().toString(); 3237 3238 findFirstAction = new AbstractAction("Find first"){ 3239 { 3240 putValue(SHORT_DESCRIPTION, "Finds first match"); 3241 } 3242 3243 public void actionPerformed(ActionEvent evt){ 3244 //needed to create the right RE 3245 refresh(); 3246 //remove selection 3247 textPane.setCaretPosition(textPane.getCaretPosition()); 3248 boolean done = false; 3249 REMatch match; 3250 int start = -1; 3251 int end = -1; 3252 do{ 3253 match = patternRE.getMatch(content, start +1); 3254 3255 if(match == null) break; 3256 start = match.getStartIndex(); 3257 end = match.getEndIndex(); 3258 3259 if(wholeWordsChk.isSelected()){ 3260 //validate the result 3261 done = (start == 0 || 3262 !Character.isLetterOrDigit(content.charAt(start - 1))) 3263 && 3264 (end == content.length() || 3265 !Character.isLetterOrDigit(content.charAt(end))); 3266 }else done = true; 3267 }while(!done); 3268 if(match != null){ 3269 nextMatchStartsFrom = start + 1; 3270 //display the result 3271 SwingUtilities.getWindowAncestor(textPane).requestFocus(); 3272 textPane.requestFocus(); 3273 3274 textPane.setCaretPosition(start); 3275 textPane.moveCaretPosition(end); 3276 }else{ 3277 JOptionPane.showMessageDialog( 3278 searchDialog, 3279 "String not found!", 3280 "Gate", JOptionPane.INFORMATION_MESSAGE); 3281 } 3282 } 3283 }; 3284 3285 3286 findNextAction = new AbstractAction("Find next"){ 3287 { 3288 putValue(SHORT_DESCRIPTION, "Finds next match"); 3289 } 3290 public void actionPerformed(ActionEvent evt){ 3291 //needed to create the right RE 3292 refresh(); 3293 //remove selection 3294 textPane.setCaretPosition(textPane.getCaretPosition()); 3295 boolean done = false; 3296 REMatch match; 3297 int start = nextMatchStartsFrom -1; 3298 int end = -1; 3299 3300 do{ 3301 match = patternRE.getMatch(content, start +1); 3302 3303 if(match == null) break; 3304 start = match.getStartIndex(); 3305 end = match.getEndIndex(); 3306 3307 if(wholeWordsChk.isSelected()){ 3308 //validate the result 3309 done = (start == 0 || 3310 !Character.isLetterOrDigit(content.charAt(start - 1))) 3311 && 3312 (end == content.length() || 3313 !Character.isLetterOrDigit(content.charAt(end))); 3314 }else done = true; 3315 }while(!done); 3316 if(match != null){ 3317 nextMatchStartsFrom = start + 1; 3318 //display the result 3319 SwingUtilities.getWindowAncestor(textPane).requestFocus(); 3320 textPane.requestFocus(); 3321 3322 textPane.setCaretPosition(start); 3323 textPane.moveCaretPosition(end); 3324 }else{ 3325 JOptionPane.showMessageDialog( 3326 searchDialog, 3327 "String not found!", 3328 "Gate", JOptionPane.INFORMATION_MESSAGE); 3329 } 3330 } 3331 }; 3332 3333 cancelAction = new AbstractAction("Cancel"){ 3334 { 3335 putValue(SHORT_DESCRIPTION, "Cancel"); 3336 } 3337 public void actionPerformed(ActionEvent evt){ 3338 searchDialog.hide(); 3339 } 3340 }; 3341 3342 } 3343 3344 3345 protected void initGuiComponents(){ 3346 getContentPane().setLayout(new BoxLayout(getContentPane(), 3347 BoxLayout.Y_AXIS)); 3348 3349 getContentPane().add(Box.createVerticalStrut(5)); 3350 Box hBox = Box.createHorizontalBox(); 3351 hBox.add(Box.createHorizontalStrut(5)); 3352 hBox.add(new JLabel("Find what:")); 3353 hBox.add(Box.createHorizontalStrut(5)); 3354 hBox.add(patternTextField = new JTextField(20)); 3355 hBox.add(Box.createHorizontalStrut(5)); 3356 hBox.add(Box.createHorizontalGlue()); 3357 getContentPane().add(hBox); 3358 3359 getContentPane().add(Box.createVerticalStrut(5)); 3360 hBox = Box.createHorizontalBox(); 3361 hBox.add(Box.createHorizontalStrut(5)); 3362 hBox.add(ignoreCaseChk = new JCheckBox("Ignore case", false)); 3363 hBox.add(Box.createHorizontalStrut(5)); 3364 hBox.add(wholeWordsChk = new JCheckBox("Whole words only", false)); 3365 hBox.add(Box.createHorizontalStrut(5)); 3366 hBox.add(Box.createHorizontalGlue()); 3367 getContentPane().add(hBox); 3368 3369 getContentPane().add(Box.createVerticalStrut(5)); 3370 hBox = Box.createHorizontalBox(); 3371 hBox.add(Box.createHorizontalGlue()); 3372 hBox.add(new JButton(findFirstAction)); 3373 hBox.add(Box.createHorizontalStrut(5)); 3374 hBox.add(new JButton(findNextAction)); 3375 hBox.add(Box.createHorizontalStrut(5)); 3376 hBox.add(new JButton(cancelAction)); 3377 hBox.add(Box.createHorizontalGlue()); 3378 getContentPane().add(hBox); 3379 getContentPane().add(Box.createVerticalStrut(5)); 3380 } 3381 3382 protected void initListeners(){ 3383 addComponentListener(new ComponentAdapter() { 3384 public void componentHidden(ComponentEvent e) { 3385 } 3386 3387 public void componentMoved(ComponentEvent e) { 3388 } 3389 3390 public void componentResized(ComponentEvent e) { 3391 } 3392 3393 public void componentShown(ComponentEvent e) { 3394 refresh(); 3395 } 3396 }); 3397 3398 patternTextField.getDocument().addDocumentListener( 3399 new javax.swing.event.DocumentListener() { 3400 public void insertUpdate(javax.swing.event.DocumentEvent e) { 3401 refresh(); 3402 } 3403 3404 public void removeUpdate(javax.swing.event.DocumentEvent e) { 3405 refresh(); 3406 } 3407 3408 public void changedUpdate(javax.swing.event.DocumentEvent e) { 3409 refresh(); 3410 } 3411 }); 3412 3413 } 3414 3415 protected void refresh(){ 3416 String patternText = patternTextField.getText(); 3417 if(patternText != null && patternText.length() > 0){ 3418 //update actions state 3419 findFirstAction.setEnabled(true); 3420 findNextAction.setEnabled(true); 3421 3422 //update patternRE 3423 try{ 3424 patternRE = ignoreCaseChk.isSelected() ? 3425 new RE(patternText, RE.REG_ICASE) : 3426 new RE(patternText); 3427 }catch(REException ree){ 3428 JOptionPane.showMessageDialog( 3429 searchDialog, 3430 "Invalid pattern!\n" + 3431 ree.toString(), 3432 "Gate", JOptionPane.ERROR_MESSAGE); 3433 } 3434 }else{ 3435 findFirstAction.setEnabled(false); 3436 findNextAction.setEnabled(false); 3437 } 3438 3439 if(patternRE == null){ 3440 } 3441 } 3442 JTextField patternTextField; 3443 JCheckBox ignoreCaseChk; 3444 JCheckBox wholeWordsChk; 3445 RE patternRE; 3446 int nextMatchStartsFrom; 3447 String content; 3448 3449 Action findFirstAction; 3450 Action findNextAction; 3451 Action cancelAction; 3452 } 3453 3454 protected class PrintAction extends AbstractAction{ 3455 public PrintAction(){ 3456 super("Print"); 3457 } 3458 3459 public void actionPerformed(ActionEvent e){ 3460 Runnable runnable = new Runnable(){ 3461 public void run(){ 3462 PrinterJob printerJob = PrinterJob.getPrinterJob(); 3463 3464 if (printerJob.printDialog()) { 3465 try{ 3466 3467// PageFormat pageFormat = printerJob.pageDialog(printerJob.defaultPage()); 3468 PageFormat pageFormat = printerJob.defaultPage(); 3469 Pageable pageable = new JComponentPrinter(textPane, pageFormat); 3470 printerJob.setPageable(pageable); 3471 3472 printerJob.print(); 3473 //fire the events 3474 StatusListener sListener = (StatusListener)MainFrame. 3475 getListeners(). 3476 get("gate.event.StatusListener"); 3477 if(sListener != null){ 3478 sListener.statusChanged("Document printed!"); 3479 } 3480 3481 }catch(Exception ex) { 3482 ex.printStackTrace(); 3483 } 3484 } 3485 } 3486 }; 3487 3488 Thread thread = new Thread(Thread.currentThread().getThreadGroup(), 3489 runnable, "Print thread"); 3490 thread.setPriority(Thread.MIN_PRIORITY); 3491 thread.start(); 3492 } 3493 } 3494 3495 3496 /** 3497 * The action that is fired when the user wants to edit an annotation. 3498 * It will build a dialog containing all the valid annotation editors. 3499 */ 3500 protected class EditAnnotationAction extends AbstractAction { 3501 public EditAnnotationAction(AnnotationSet set, Annotation annotation){ 3502 super("Edit"); 3503 this.set = set; 3504 this.annotation = annotation; 3505 putValue(SHORT_DESCRIPTION, "Edits the annotation"); 3506 } 3507 3508 public void actionPerformed(ActionEvent e){ 3509 //get the list of editors 3510 java.util.List specificEditors = Gate.getCreoleRegister(). 3511 getAnnotationVRs(annotation.getType()); 3512 java.util.List genericEditors = Gate.getCreoleRegister(). 3513 getAnnotationVRs(); 3514 //create the GUI 3515 JTabbedPane tabbedPane = new JTabbedPane(JTabbedPane.BOTTOM); 3516 //add all the specific editors 3517 Iterator editorIter = specificEditors.iterator(); 3518 while(editorIter.hasNext()){ 3519 String editorType = (String)editorIter.next(); 3520 //create the editor 3521 AnnotationVisualResource editor; 3522 try{ 3523 editor = (AnnotationVisualResource) 3524 Factory.createResource(editorType); 3525 if(editor instanceof ResizableVisualResource){ 3526 tabbedPane.add((Component)editor, 3527 ((ResourceData)Gate.getCreoleRegister(). 3528 get(editorType)).getName()); 3529 }else{ 3530 JScrollPane scroller = new JScrollPane((Component)editor); 3531// scroller.setPreferredSize(((Component) editor).getPreferredSize()); 3532 tabbedPane.add(scroller, 3533 ((ResourceData)Gate.getCreoleRegister(). 3534 get(editorType)).getName()); 3535 } 3536 3537 3538 editor.setTarget(set); 3539 editor.setAnnotation(annotation); 3540 }catch(ResourceInstantiationException rie){ 3541 rie.printStackTrace(Err.getPrintWriter()); 3542 } 3543 } 3544 3545 //add all the generic editors 3546 editorIter = genericEditors.iterator(); 3547 while(editorIter.hasNext()){ 3548 String editorType = (String)editorIter.next(); 3549 //create the editor 3550 AnnotationVisualResource editor; 3551 try{ 3552 editor = (AnnotationVisualResource) 3553 Factory.createResource(editorType); 3554 if(editor.canDisplayAnnotationType(annotation.getType())){ 3555 editor.setTarget(set); 3556 editor.setAnnotation(annotation); 3557 if(editor instanceof ResizableVisualResource){ 3558 tabbedPane.add((Component)editor, 3559 ((ResourceData)Gate.getCreoleRegister(). 3560 get(editorType)).getName()); 3561 }else{ 3562 tabbedPane.add(new JScrollPane((Component)editor), 3563 ((ResourceData)Gate.getCreoleRegister(). 3564 get(editorType)).getName()); 3565 } 3566 } 3567 }catch(ResourceInstantiationException rie){ 3568 rie.printStackTrace(Err.getPrintWriter()); 3569 } 3570 3571 } 3572 3573 //show the modal dialog until the data is OK or the user cancels 3574 boolean allOK = false; 3575 while(!allOK){ 3576 if(OkCancelDialog.showDialog(DocumentEditor.this, 3577 tabbedPane, 3578 "Edit Annotation")){ 3579 try{ 3580 Component comp = tabbedPane.getSelectedComponent(); 3581 if(comp instanceof AnnotationVisualResource){ 3582 ((AnnotationVisualResource)comp).okAction(); 3583 }else if(comp instanceof JScrollPane){ 3584 ((AnnotationVisualResource)((JScrollPane)comp). 3585 getViewport().getView()).okAction(); 3586 }else{ 3587 throw new LuckyException("DocumentEditor.EditAnnotationAction1"); 3588 } 3589 3590 allOK = true; 3591 }catch(GateException ge){ 3592 JOptionPane.showMessageDialog( 3593 DocumentEditor.this, 3594 "There was an error:\n" + 3595 ge.toString(), 3596 "Gate", JOptionPane.ERROR_MESSAGE); 3597 ge.printStackTrace(Err.getPrintWriter()); 3598 allOK = false; 3599 } 3600 }else{ 3601 if (OkCancelDialog.userHasPressedCancel) 3602 try{ 3603 Component comp = tabbedPane.getSelectedComponent(); 3604 if(comp instanceof AnnotationVisualResource){ 3605 ((AnnotationVisualResource)comp).cancelAction(); 3606 }else if(comp instanceof JScrollPane){ 3607 ((AnnotationVisualResource) 3608 ((JScrollPane)comp).getViewport().getView()).cancelAction(); 3609 }else{ 3610 throw new LuckyException("DocumentEditor.EditAnnotationAction"); 3611 } 3612 allOK = true; 3613 } catch(GateException ge){ 3614 JOptionPane.showMessageDialog( 3615 DocumentEditor.this, 3616 "There was an error:\n" + 3617 ge.toString(), 3618 "Gate", JOptionPane.ERROR_MESSAGE); 3619 allOK = false; 3620 } 3621 allOK = true; 3622 } 3623 }//while(!allOK) 3624 }//public void actionPerformed(ActionEvent e) 3625 3626 protected AnnotationSet set; 3627 protected Annotation annotation; 3628 }//class EditAnnotationAction 3629 3630 /** 3631 * The action that is fired when the user wants to create a new annotation. 3632 * It will build a dialog containing all the valid annotation editors. 3633 */ 3634 class NewAnnotationAction extends AbstractAction{ 3635 public NewAnnotationAction(AnnotationSet set, 3636 Long startOffset, 3637 Long endOffset){ 3638 super("New annotation"); 3639 putValue(SHORT_DESCRIPTION, "Creates a new annotation"); 3640 this.set = set; 3641 this.startOffset = startOffset; 3642 this.endOffset = endOffset; 3643 this.type = null; 3644 } 3645 3646 public NewAnnotationAction(AnnotationSet set, String type, 3647 Long startOffset, Long endOffset){ 3648 super("New \"" + type + "\" annotation"); 3649 putValue(SHORT_DESCRIPTION, "Creates a new annotation of type \"" + 3650 type + "\""); 3651 this.set = set; 3652 this.startOffset = startOffset; 3653 this.endOffset = endOffset; 3654 this.type = type; 3655 } 3656 3657 public void actionPerformed(ActionEvent e){ 3658 if(set == null){ 3659 //get the name from the user 3660 String setName = JOptionPane.showInputDialog( 3661 DocumentEditor.this, 3662 "Please provide a name for the new annotation set", 3663 "Gate", JOptionPane.QUESTION_MESSAGE); 3664 if(setName == null) return; 3665 this.set = document.getAnnotations(setName); 3666 } 3667 //get the lists of editors 3668 java.util.List specificEditors; 3669 if(type != null) specificEditors = Gate.getCreoleRegister(). 3670 getAnnotationVRs(type); 3671 else specificEditors = new ArrayList(); 3672 3673 java.util.List genericEditors = Gate.getCreoleRegister(). 3674 getAnnotationVRs(); 3675 //create the GUI 3676 JTabbedPane tabbedPane = new JTabbedPane(JTabbedPane.BOTTOM); 3677 //add all the specific editors 3678 Iterator editorIter = specificEditors.iterator(); 3679 while(editorIter.hasNext()){ 3680 String editorType = (String)editorIter.next(); 3681 //create the editor 3682 AnnotationVisualResource editor; 3683 try{ 3684 editor = (AnnotationVisualResource) 3685 Factory.createResource(editorType); 3686 tabbedPane.add(new JScrollPane((Component)editor), 3687 ((ResourceData)Gate.getCreoleRegister().get(editorType)). 3688 getName()); 3689 editor.setTarget(set); 3690 editor.setSpan(startOffset, endOffset, type); 3691 3692 }catch(ResourceInstantiationException rie){ 3693 rie.printStackTrace(Err.getPrintWriter()); 3694 } 3695 } 3696 3697 //add all the generic editors 3698 editorIter = genericEditors.iterator(); 3699 while(editorIter.hasNext()){ 3700 String editorType = (String)editorIter.next(); 3701 //create the editor 3702 AnnotationVisualResource editor; 3703 try{ 3704 editor = (AnnotationVisualResource) 3705 Factory.createResource(editorType); 3706 3707 if(type == null || 3708 (type != null && editor.canDisplayAnnotationType(type))){ 3709 editor.setTarget(set); 3710 editor.setSpan(startOffset, endOffset, type); 3711 tabbedPane.add(new JScrollPane((Component)editor), 3712 ((ResourceData)Gate.getCreoleRegister(). 3713 get(editorType)).getName()); 3714 } 3715 }catch(ResourceInstantiationException rie){ 3716 rie.printStackTrace(Err.getPrintWriter()); 3717 } 3718 3719 } 3720 3721 //show the modal dialog until the data is OK or the user cancels 3722 boolean allOK = false; 3723 while(!allOK){ 3724 if(OkCancelDialog.showDialog(DocumentEditor.this, 3725 tabbedPane, "Edit Annotation")){ 3726 try{ 3727 ((AnnotationVisualResource)((JScrollPane)tabbedPane. 3728 getSelectedComponent()).getViewport(). 3729 getView() 3730 ).okAction(); 3731 allOK = true; 3732 }catch(GateException ge){ 3733 JOptionPane.showMessageDialog( 3734 DocumentEditor.this, 3735 "There was an error:\n" + 3736 ge.toString(), 3737 "Gate", JOptionPane.ERROR_MESSAGE); 3738// ge.printStackTrace(Err.getPrintWriter()); 3739 allOK = false; 3740 } 3741 }else{ 3742 allOK = true; 3743 } 3744 }//while(!allOK) 3745 3746 3747 }//public void actionPerformed(ActionEvent e) 3748 3749 AnnotationSet set; 3750 Long startOffset; 3751 Long endOffset; 3752 String type; 3753 }//class NewAnnotationAction extends AbstractAction 3754 3755 /** 3756 * Fixes the <a 3757 * href="http://developer.java.sun.com/developer/bugParade/bugs/4406598.html"> 3758 * 4406598 bug</a> in swing text components. 3759 * The bug consists in the fact that the Background attribute is ignored by 3760 * the text component whent it is defined in a style from which the current 3761 * style inherits. 3762 */ 3763 public class CustomStyledEditorKit extends StyledEditorKit{ 3764 private final ViewFactory defaultFactory = new CustomStyledViewFactory(); 3765 public ViewFactory getViewFactory() { 3766 return defaultFactory; 3767 } 3768 3769 /** 3770 * Inserts content from the given stream, which will be 3771 * treated as plain text. 3772 * This insertion is done without checking \r or \r \n sequence. 3773 * It takes the text from the Reader and place it into Document at position 3774 * pos 3775 */ 3776 public void read(Reader in, javax.swing.text.Document doc, int pos) 3777 throws IOException, BadLocationException { 3778 3779 char[] buff = new char[65536]; 3780 int charsRead = 0; 3781 while ((charsRead = in.read(buff, 0, buff.length)) != -1) { 3782 doc.insertString(pos, new String(buff, 0, charsRead), null); 3783 pos += charsRead; 3784 }// while 3785 }// read 3786 } 3787 3788 /** 3789 * Fixes the <a 3790 * href="http://developer.java.sun.com/developer/bugParade/bugs/4406598.html"> 3791 * 4406598 bug</a> in swing text components. 3792 * The bug consists in the fact that the Background attribute is ignored by 3793 * the text component whent it is defined in a style from which the current 3794 * style inherits. 3795 */ 3796 public class CustomStyledViewFactory implements ViewFactory{ 3797 public View create(Element elem) { 3798 String kind = elem.getName(); 3799 if (kind != null) { 3800 if (kind.equals(AbstractDocument.ContentElementName)) { 3801 return new CustomLabelView(elem); 3802 }else if (kind.equals(AbstractDocument.ParagraphElementName)) { 3803 return new ParagraphView(elem); 3804 }else if (kind.equals(AbstractDocument.SectionElementName)) { 3805 return new BoxView(elem, View.Y_AXIS); 3806 }else if (kind.equals(StyleConstants.ComponentElementName)) { 3807 return new ComponentView(elem); 3808 }else if (kind.equals(StyleConstants.IconElementName)) { 3809 return new IconView(elem); 3810 } 3811 } 3812 // default to text display 3813 return new CustomLabelView(elem); 3814 } 3815 } 3816 }//class AnnotationEditor
|
DocumentEditor |
|