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