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