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