0001 /*
0002 * Copyright (c) 1995-2010, The University of Sheffield. See the file
0003 * COPYRIGHT.txt in the software or at http://gate.ac.uk/gate/COPYRIGHT.txt
0004 *
0005 * This file is part of GATE (see http://gate.ac.uk/), and is free
0006 * software, licenced under the GNU Library General Public License,
0007 * Version 2, June 1991 (in the distribution as file licence.html,
0008 * and also available at http://gate.ac.uk/gate/licence.html).
0009 *
0010 * Valentin Tablan 13/11/2000
0011 *
0012 * $Id: DocumentEditor.java 12950 2010-08-11 18:58:56Z bensonmargulies $
0013 *
0014 */
0015 package gate.gui;
0016
0017 import java.awt.*;
0018 import java.awt.event.*;
0019 import java.awt.font.TextAttribute;
0020 import java.awt.print.*;
0021 import java.beans.*;
0022 import java.io.IOException;
0023 import java.io.Reader;
0024 import java.util.*;
0025 import java.util.regex.Matcher;
0026 import java.util.regex.Pattern;
0027 import javax.swing.*;
0028 import javax.swing.border.Border;
0029 import javax.swing.event.ListSelectionEvent;
0030 import javax.swing.event.ListSelectionListener;
0031 import javax.swing.table.AbstractTableModel;
0032 import javax.swing.text.*;
0033 import javax.swing.tree.*;
0034 import gate.*;
0035 import gate.corpora.DocumentContentImpl;
0036 import gate.creole.*;
0037 import gate.event.*;
0038 import gate.print.JComponentPrinter;
0039 import gate.swing.*;
0040 import gate.util.*;
0041
0042 /**
0043 * This class implements a viewer/editor for the annotations on a document.
0044 * As a viewer, this visual resource will display all the annotations found on
0045 * the document. The editor needs to have some data about annotation types in
0046 * order to allow the editing of annotations. This data comes from the
0047 * {@link gate.creole.AnnotationSchema} objects that are loaded in the Gate
0048 * system at a given moment. If there are no such objects the editing of
0049 * annotations will be restricted to a very crude method allowing the user to
0050 * add any type of annotations having any features with any String values.
0051 *
0052 * This class has been deprecated! The document editing functionality is now
0053 * provided by the {@link gate.gui.docview.DocumentEditor} class.
0054 *
0055 * @deprecated
0056 */
0057 public class DocumentEditor extends AbstractVisualResource
0058 implements ANNIEConstants{
0059 //properties
0060 private transient PropertyChangeSupport propertyChangeListeners =
0061 new PropertyChangeSupport(this);
0062 /**
0063 * The {@link gate.Document} currently displayed.
0064 */
0065 private gate.Document document;
0066
0067 /**
0068 * A random colour generator used to generate initial default colours for
0069 * highlighting various types of annotations.
0070 */
0071 protected ColorGenerator colGenerator = new ColorGenerator();
0072
0073 //GUI components
0074 /** The text display.*/
0075 protected JTextPane textPane;
0076
0077 /** Scroller used for the text diaplay*/
0078 protected JScrollPane textScroll;
0079
0080 /** The table placed below the text display used for showing annotations*/
0081 protected XJTable annotationsTable;
0082
0083 /**Model for the annotations table*/
0084 protected AnnotationsTableModel annotationsTableModel;
0085
0086 /** Scroller for the annotations table*/
0087 protected JScrollPane tableScroll;
0088
0089 /*The split that contains the text(top) and the annotations table(bottom)*/
0090 protected JSplitPane leftSplit;
0091
0092 /**
0093 * The split that contains the styles tree and the coreference viewer.
0094 */
0095 protected JSplitPane rightSplit;
0096
0097 /**
0098 * The main horizontal split that contains all the contents of this viewer
0099 */
0100 protected JSplitPane mainSplit;
0101
0102 /**
0103 * The right hand side tree with all the annotation sets and types of
0104 * annotations
0105 */
0106 protected JTree stylesTree;
0107
0108 /**
0109 * The toolbar displayed on the top part of the component
0110 */
0111 protected JToolBar toolbar;
0112
0113 /**Scroller for the styles tree*/
0114 protected JScrollPane stylesTreeScroll;
0115
0116 /**The root for the styles tree*/
0117 protected DefaultMutableTreeNode stylesTreeRoot;
0118
0119 /**The model for the styles tree*/
0120 protected DefaultTreeModel stylesTreeModel;
0121
0122 /**The dialog used for text search*/
0123 protected SearchDialog searchDialog;
0124
0125 /**The dialog used for editing the styles used to highlight annotations*/
0126 protected TextAttributesChooser styleChooser;
0127
0128
0129 /**
0130 * The Jtree that displays the coreference data
0131 */
0132 protected JTree corefTree;
0133 /**
0134 * The root for the coref tree
0135 */
0136 protected DefaultMutableTreeNode corefTreeRoot;
0137
0138 /**
0139 * The model for the coref tree
0140 */
0141 protected DefaultTreeModel corefTreeModel;
0142
0143 /** The scroller for the coref list*/
0144 protected JScrollPane corefScroll;
0145
0146 /**
0147 * A box containing a {@link javax.swing.JProgressBar} used to keep the user
0148 * entertained while the text display is being updated
0149 */
0150 protected Box progressBox;
0151
0152 /**The progress bar used during updating the text*/
0153 protected JProgressBar progressBar;
0154
0155 /**
0156 * The highlighter used to help the user select annotations that overlap
0157 * and for highligting in the text the annotations selected in the lower
0158 * table.
0159 */
0160 protected Highlighter highlighter;
0161
0162 /**
0163 * This highlighter is actually used as a data structure. It is used to keep
0164 * the data for the selected annotations; the actual highlighting will be
0165 * done by the {@link #highlighter} as using two different
0166 * highlighters on the same text component is looking for trouble.
0167 */
0168 protected Highlighter selectionHighlighter;
0169
0170 /**
0171 * The object responsible with blinking the selected annotations.
0172 */
0173 protected SelectionBlinker selectionBlinker;
0174
0175
0176 protected Handle myHandle;
0177
0178 /**
0179 * holds the data for the annotations table: a list of Annotation objects
0180 */
0181 protected java.util.List data;
0182
0183 /**
0184 * a list containing {@link Range} objects. These are the
0185 * ranges in the {@link #data} structure. A range is a bunch
0186 * of annotations belonging to the same annotation set that are contiguous
0187 * in the {@link #data} structure.
0188 */
0189 protected java.util.List ranges;
0190
0191 /**
0192 * A composed map used to get the metadata for an annotation type starting
0193 * from the annotation set name and the type name.
0194 * Annotation set name -> Annotation type -> {@link TypeData}
0195 * Maps from String to Map to {@link TypeData}.
0196 */
0197 protected Map typeDataMap;
0198
0199 /**
0200 * The listener for the events coming from the document (annotations and
0201 * annotation sets added or removed).
0202 */
0203 protected EventsHandler eventHandler;
0204
0205
0206 /**
0207 * Object used to sychronise all the various threads involved in GUI
0208 * updating;
0209 */
0210 protected Object lock;
0211
0212 /**Should the table be visible*/
0213
0214 /**Should the text be visible*/
0215
0216 /**
0217 * Should the right hand side tree be visible. That tree is used to select
0218 * what types of annotations are visible in the text display, hence the name
0219 * filters.
0220 */
0221
0222 /**Should this component bahave as an editor as well as an viewer*/
0223 private boolean editable = true;
0224
0225
0226
0227 private JToggleButton textVisibleBtn;
0228 private JToggleButton typesTreeVisibleBtn;
0229 private JToggleButton annotationsTableVisibleBtn;
0230 private JToggleButton coreferenceVisibleBtn;
0231 private boolean annotationsTableVisible = false;
0232 private boolean coreferenceVisible = false;
0233 private boolean textVisible = true;
0234 private boolean typesTreeVisible = false;
0235 private boolean corefOptionAvailable = false;
0236
0237 /**
0238 * Default constructor. Creats all the components and initialises all the
0239 * internal data to default values where possible.
0240 */
0241 public DocumentEditor() {
0242 }
0243
0244 public Resource init(){
0245 initLocalData();
0246 initGuiComponents();
0247 initListeners();
0248 return this;
0249 }
0250
0251 /**
0252 * Initialises all the listeners that this component has to register with
0253 * other classes.
0254 */
0255 protected void initListeners() {
0256 //listen for our own properties change events
0257 this.addPropertyChangeListener(new PropertyChangeListener() {
0258 public void propertyChange(PropertyChangeEvent e) {
0259 if(e.getPropertyName().equals("annotationsTableVisible") ||
0260 e.getPropertyName().equals("coreferenceVisible") ||
0261 e.getPropertyName().equals("textVisible") ||
0262 e.getPropertyName().equals("typesTreeVisible")){
0263 layoutComponents();
0264 }else if(e.getPropertyName().equals("corefOptionAvailable")){
0265 if(((Boolean)e.getNewValue()).booleanValue()){
0266 if(toolbar.getComponentIndex(coreferenceVisibleBtn) == -1)
0267 toolbar.add(coreferenceVisibleBtn, 3);
0268 }else{
0269 toolbar.remove(coreferenceVisibleBtn);
0270 }
0271 layoutComponents();
0272 }
0273 }
0274 });
0275
0276 textVisibleBtn.addActionListener(new ActionListener() {
0277 public void actionPerformed(ActionEvent e) {
0278 setTextVisible(textVisibleBtn.isSelected());
0279 }
0280 });
0281
0282 annotationsTableVisibleBtn.addActionListener(new ActionListener() {
0283 public void actionPerformed(ActionEvent e) {
0284 setAnnotationsTableVisible(annotationsTableVisibleBtn.isSelected());
0285 }
0286 });
0287
0288
0289 typesTreeVisibleBtn.addActionListener(new ActionListener() {
0290 public void actionPerformed(ActionEvent e) {
0291 setTypesTreeVisible(typesTreeVisibleBtn.isSelected());
0292 }
0293 });
0294
0295
0296 coreferenceVisibleBtn.addActionListener(new ActionListener() {
0297 public void actionPerformed(ActionEvent e) {
0298 setCoreferenceVisible(coreferenceVisibleBtn.isSelected());
0299 }
0300 });
0301
0302 stylesTree.addMouseListener(new MouseAdapter() {
0303 public void mouseClicked(MouseEvent e) {
0304 if(SwingUtilities.isLeftMouseButton(e)){
0305 //where inside the tree?
0306 int x = e.getX();
0307 int y = e.getY();
0308 TreePath path = stylesTree.getPathForLocation(x, y);
0309 if(path != null){
0310 DefaultMutableTreeNode node = (DefaultMutableTreeNode)path.
0311 getLastPathComponent();
0312 TypeData nData = (TypeData)node.getUserObject();
0313 //where inside the cell?
0314 Rectangle cellRect = stylesTree.getPathBounds(path);
0315 x -= cellRect.x;
0316 y -= cellRect.y;
0317 Component cellComp = stylesTree.getCellRenderer().
0318 getTreeCellRendererComponent(stylesTree,
0319 node, true,
0320 false, false,
0321 0, true);
0322 // cellComp.setSize(cellRect.width, cellRect.height);
0323 cellComp.setBounds(cellRect);
0324 Component clickedComp = cellComp.getComponentAt(x, y);
0325
0326 if(clickedComp instanceof JCheckBox){
0327 nData.setVisible(! nData.getVisible());
0328 // stylesTree.repaint(cellRect);
0329 stylesTreeModel.nodeChanged(node);
0330 // Check if the click indicates a shortcut to create an annotation
0331 }else if( e.getClickCount() == 1 &&
0332 clickedComp instanceof JLabel &&
0333 isTextSelected()){
0334 // Here creates an annotation with the selected text into the
0335 // target annotation set
0336
0337 if(!editable) return;
0338 Long startOffset = new Long(textPane.getSelectionStart());
0339 Long endOffset = new Long(textPane.getSelectionEnd());
0340 TreePath treePath = stylesTree.getSelectionPath();
0341 TypeData typeData = (TypeData)((DefaultMutableTreeNode)
0342 treePath.getLastPathComponent()).getUserObject();
0343 String setName = typeData.getSet();
0344 if(typeData.getType() == null){
0345 // The set is empty. It will not create an annotation.
0346 // Loose the selection and return
0347 textPane.setSelectionStart(startOffset.intValue());
0348 textPane.setSelectionEnd(startOffset.intValue());
0349 return;
0350 }// End if
0351 try{
0352 if ("Default".equals(setName)){
0353 document.getAnnotations().add(startOffset,
0354 endOffset,
0355 typeData.getType(),
0356 Factory.newFeatureMap());
0357 }else{
0358 document.getAnnotations(setName).add( startOffset,
0359 endOffset,
0360 typeData.getType(),
0361 Factory.newFeatureMap());
0362 }// End if
0363 } catch(gate.util.InvalidOffsetException ioe){
0364 throw new GateRuntimeException(ioe.getMessage());
0365 }// End try
0366 // Loose the selection
0367 textPane.setSelectionStart(startOffset.intValue());
0368 textPane.setSelectionEnd(startOffset.intValue());
0369 }else if(clickedComp instanceof JLabel &&
0370 e.getClickCount() == 2){
0371 if(styleChooser == null){
0372 Window parent = SwingUtilities.getWindowAncestor(
0373 DocumentEditor.this);
0374 styleChooser = parent instanceof Frame ?
0375 new TextAttributesChooser((Frame)parent,
0376 "Please select your options",
0377 true) :
0378 new TextAttributesChooser((Dialog)parent,
0379 "Please select your options",
0380 true);
0381
0382 }
0383
0384 styleChooser.setLocationRelativeTo(stylesTree);
0385 nData.setAttributes(
0386 styleChooser.show(nData.getAttributes().copyAttributes()));
0387 stylesTreeModel.nodeChanged(node);
0388 // stylesTree.repaint(cellRect);
0389 }
0390 }
0391 }
0392 }
0393 });
0394
0395 stylesTree.addComponentListener(new ComponentAdapter() {
0396 public void componentHidden(ComponentEvent e) {
0397
0398 }
0399
0400 public void componentMoved(ComponentEvent e) {
0401 }
0402
0403 public void componentResized(ComponentEvent e) {
0404 SwingUtilities.invokeLater(new Runnable(){
0405 public void run(){
0406 Enumeration nodes = stylesTreeRoot.depthFirstEnumeration();
0407 while(nodes.hasMoreElements()){
0408 stylesTreeModel.nodeChanged((TreeNode)nodes.nextElement());
0409 }
0410 }
0411 });
0412 }
0413
0414 public void componentShown(ComponentEvent e) {
0415 }
0416 });
0417
0418 //clear selection in table on outside clicks
0419 tableScroll.addMouseListener(new MouseAdapter() {
0420 public void mouseClicked(MouseEvent e) {
0421 Point location = e.getPoint();
0422 if(!tableScroll.getViewport().getView().getBounds().contains(location)){
0423 //deselect everything in the table
0424 annotationsTable.clearSelection();
0425 }
0426 }
0427 });
0428
0429 annotationsTable.addMouseListener(new MouseAdapter() {
0430 public void mouseClicked(MouseEvent e) {
0431 int row = annotationsTable.rowAtPoint(e.getPoint());
0432 Annotation ann = (Annotation)annotationsTable.getModel().
0433 getValueAt(row, -1);
0434 //find the annotation set
0435 String setName = (String)annotationsTable.getModel().
0436 getValueAt(row, 1);
0437 AnnotationSet set = setName.equals("Default")?
0438 document.getAnnotations() :
0439 document.getAnnotations(setName);
0440
0441 EditAnnotationAction editAnnAct = new EditAnnotationAction(set, ann);
0442 if(SwingUtilities.isLeftMouseButton(e)){
0443 if(e.getClickCount() == 1){
0444 }else if(e.getClickCount() == 2){
0445 //double left click -> edit the annotation
0446 if(editable) editAnnAct.actionPerformed(null);
0447 }
0448 } else if(SwingUtilities.isRightMouseButton(e)) {
0449 //right click
0450 //add select all option
0451 JPopupMenu popup = new XJPopupMenu();
0452 popup.add(new AbstractAction(){
0453 {
0454 putValue(NAME, "Select all");
0455 }
0456 public void actionPerformed(ActionEvent evt){
0457 annotationsTable.selectAll();
0458 }
0459 });
0460
0461 // popup.addSeparator();
0462 //add save preserving format
0463 // popup.add(new DumpPreserveFormatAction());
0464 if(editable){
0465 //add delete option
0466 popup.addSeparator();
0467 popup.add(new DeleteSelectedAnnotationsAction(annotationsTable));
0468 popup.addSeparator();
0469 popup.add(new XJMenuItem(editAnnAct, myHandle));
0470 }
0471 popup.show(annotationsTable, e.getX(), e.getY());
0472 }
0473 }
0474 });//annotationsTable.addMouseListener(new MouseAdapter()
0475
0476
0477
0478 annotationsTable.getInputMap().put(
0479 KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_DELETE, 0),
0480 "Delete");
0481 annotationsTable.getActionMap().put(
0482 "Delete",
0483 new DeleteSelectedAnnotationsAction(annotationsTable));
0484
0485 stylesTree.getInputMap().put(
0486 KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_DELETE, 0),
0487 "Delete");
0488 stylesTree.getActionMap().put(
0489 "Delete",
0490 new DeleteSelectedAnnotationsAction(stylesTree));
0491
0492 corefTree.getInputMap().put(
0493 KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_DELETE, 0),
0494 "Delete");
0495 corefTree.getActionMap().put(
0496 "Delete",
0497 new DeleteSelectedAnnotationsAction(corefTree));
0498
0499
0500 //takes care of highliting the selected annotations
0501 annotationsTable.getSelectionModel().addListSelectionListener(
0502 new ListSelectionListener(){
0503 public void valueChanged(ListSelectionEvent e){
0504 int[] rows = annotationsTable.getSelectedRows();
0505 synchronized(selectionHighlighter){
0506 selectionHighlighter.removeAllHighlights();
0507 }
0508 for(int i = 0; i < rows.length; i++){
0509 int start = ((Long)annotationsTable.getModel().
0510 getValueAt(rows[i], 2)
0511 ).intValue();
0512 int end = ((Long)annotationsTable.getModel().
0513 getValueAt(rows[i], 3)
0514 ).intValue();
0515
0516 // compute correction for new line breaks in long lines
0517 start += longLinesCorrection(start);
0518 end += longLinesCorrection(end);
0519
0520 //bring the annotation in view
0521 try{
0522 Rectangle startRect = textPane.modelToView(start);
0523 Rectangle endRect = textPane.modelToView(end);
0524 SwingUtilities.computeUnion(endRect.x, endRect.y,
0525 endRect.width, endRect.height,
0526 startRect);
0527 textPane.scrollRectToVisible(startRect);
0528 annotationsTable.requestFocus();
0529 }catch(BadLocationException ble){
0530 throw new GateRuntimeException(ble.toString());
0531 }
0532 //start blinking the annotation
0533 try{
0534 synchronized (selectionHighlighter){
0535 selectionHighlighter.addHighlight(start, end,
0536 DefaultHighlighter.DefaultPainter);
0537 }
0538 }catch(BadLocationException ble){
0539 throw new GateRuntimeException(ble.toString());
0540 }
0541 }//for(int i = 0; i < rows.length; i++)
0542 //start the blinker
0543 selectionBlinker.testAndStart();
0544 }
0545 });
0546
0547
0548 textPane.addMouseListener(new MouseAdapter() {
0549 public void mouseClicked(MouseEvent e) {
0550 if(SwingUtilities.isRightMouseButton(e)){
0551 int position = textPane.viewToModel(e.getPoint());
0552 if(textPane.getSelectionStart() == textPane.getSelectionEnd()){
0553 //no selection -> select an annotation
0554 JPopupMenu popup = new XJPopupMenu("Select:");
0555 //find annotations at this position
0556 Iterator<Annotation> annIter = document.getAnnotations().
0557 get(new Long(position),
0558 new Long(position)
0559 ).iterator();
0560 if(annIter.hasNext()){
0561 JMenu menu = new XJMenu("Default");
0562 popup.add(menu);
0563 while(annIter.hasNext()){
0564 Annotation ann = annIter.next();
0565 menu.add(new HighlightAnnotationMenu(ann,
0566 document.getAnnotations()));
0567 }
0568 }
0569 Map namedASs = document.getNamedAnnotationSets();
0570 if(namedASs != null){
0571 Iterator namedASiter = namedASs.values().iterator();
0572 while(namedASiter.hasNext()){
0573 //find annotations at this position
0574 AnnotationSet set = (AnnotationSet)namedASiter.next();
0575 annIter = set.get(new Long(position), new Long(position)).
0576 iterator();
0577 if(annIter.hasNext()){
0578 JMenu menu = new XJMenu(set.getName());
0579 popup.add(menu);
0580 while(annIter.hasNext()){
0581 Annotation ann = annIter.next();
0582 menu.add(new HighlightAnnotationMenu(ann,set));
0583 }
0584 }
0585 }
0586 }
0587 popup.show(textPane, e.getPoint().x, e.getPoint().y);
0588 } else {
0589 //there is selected text -> create a new annotation
0590 if(!editable) return;
0591 Long startOffset = new Long(textPane.getSelectionStart());
0592 Long endOffset = new Long(textPane.getSelectionEnd());
0593 JPopupMenu popup = new XJPopupMenu();
0594 //add new annotation in the Default AS
0595 JMenu menu = new XJMenu("Add annotation to \"Default\"");
0596 menu.add(new XJMenuItem(
0597 new NewAnnotationAction(document.getAnnotations(),
0598 startOffset, endOffset),
0599 myHandle));
0600 java.util.List customisedAnnTypes = Gate.getCreoleRegister().
0601 getVREnabledAnnotationTypes();
0602 if(!customisedAnnTypes.isEmpty()){
0603 menu.addSeparator();
0604 Iterator typesIter = customisedAnnTypes.iterator();
0605 while(typesIter.hasNext()){
0606 menu.add(new XJMenuItem(
0607 new NewAnnotationAction(document.getAnnotations(),
0608 (String)typesIter.next(),
0609 startOffset, endOffset),
0610 myHandle));
0611 }
0612 }//if(!customisedAnnTypes.isEmpty())
0613 popup.add(menu);
0614
0615 //add a new annotation to a named AnnotationSet
0616 if(document.getNamedAnnotationSets() != null){
0617 Iterator annSetsIter = document.getNamedAnnotationSets().
0618 keySet().iterator();
0619 if(annSetsIter.hasNext()) popup.addSeparator();
0620 while(annSetsIter.hasNext()){
0621 AnnotationSet set = document.getAnnotations(
0622 (String)annSetsIter.next());
0623
0624
0625 menu = new XJMenu("Add annotation to \"" + set.getName() + "\"");
0626 menu.add(new XJMenuItem(
0627 new NewAnnotationAction(set, startOffset, endOffset),
0628 myHandle));
0629 if(!customisedAnnTypes.isEmpty()){
0630 menu.addSeparator();
0631 Iterator typesIter = customisedAnnTypes.iterator();
0632 while(typesIter.hasNext()){
0633 menu.add(new XJMenuItem(
0634 new NewAnnotationAction(set,
0635 (String)typesIter.next(),
0636 startOffset, endOffset),
0637 myHandle));
0638 }
0639 }//if(!customisedAnnTypes.isEmpty())
0640 popup.add(menu);
0641 }//while(annSetsIter.hasNext())
0642 }
0643
0644 //add to a new annotation set
0645 menu = new XJMenu("Add annotation to a new set");
0646 menu.add(new XJMenuItem(
0647 new NewAnnotationAction(null, startOffset, endOffset),
0648 myHandle));
0649 if(!customisedAnnTypes.isEmpty()){
0650 menu.addSeparator();
0651 Iterator typesIter = customisedAnnTypes.iterator();
0652 while(typesIter.hasNext()){
0653 menu.add(new XJMenuItem(
0654 new NewAnnotationAction(null,
0655 (String)typesIter.next(),
0656 startOffset, endOffset),
0657 myHandle));
0658 }
0659 }//if(!customisedAnnTypes.isEmpty())
0660 popup.add(menu);
0661 //show the popup
0662 popup.show(textPane, e.getPoint().x, e.getPoint().y);
0663 }//there is selected text
0664 }//if(SwingUtilities.isRightMouseButton(e))
0665 }//mouse clicked
0666
0667 public void mousePressed(MouseEvent e) {
0668 }
0669
0670 public void mouseReleased(MouseEvent e) {
0671 }
0672
0673 public void mouseEntered(MouseEvent e) {
0674 }
0675
0676 public void mouseExited(MouseEvent e) {
0677 }
0678 });
0679
0680 //when the highlighter changes we need to get a hold of the new one
0681 textPane.addPropertyChangeListener(new PropertyChangeListener() {
0682 public void propertyChange(PropertyChangeEvent e) {
0683 if(e.getPropertyName().equals("highlighter")){
0684 highlighter = textPane.getHighlighter();
0685 selectionHighlighter.install(textPane);
0686 }
0687 }
0688 });
0689
0690 corefTree.addMouseListener(new MouseAdapter() {
0691 public void mouseClicked(MouseEvent e) {
0692 if(SwingUtilities.isLeftMouseButton(e)){
0693 //where inside the tree?
0694 int x = e.getX();
0695 int y = e.getY();
0696 TreePath path = corefTree.getPathForLocation(x, y);
0697 if(path != null){
0698 DefaultMutableTreeNode node = (DefaultMutableTreeNode)path.
0699 getLastPathComponent();
0700 //where inside the cell?
0701 Rectangle cellRect = corefTree.getPathBounds(path);
0702 x -= cellRect.x;
0703 y -= cellRect.y;
0704 Component cellComp = corefTree.getCellRenderer().
0705 getTreeCellRendererComponent(corefTree,
0706 node, true,
0707 false, false,
0708 0, true);
0709 cellComp.setBounds(cellRect);
0710 Component clickedComp = cellComp.getComponentAt(x, y);
0711 if(clickedComp instanceof LazyJPanel)
0712 clickedComp = clickedComp.getComponentAt(x, y);
0713 if(node.getUserObject() instanceof CorefData &&
0714 clickedComp instanceof JCheckBox){
0715 CorefData cData = (CorefData)node.getUserObject();
0716 cData.setVisible(!cData.getVisible());
0717 corefTreeModel.nodeChanged(node);
0718 }
0719 }
0720 }
0721 }
0722
0723 public void mousePressed(MouseEvent e) {
0724 }
0725
0726 public void mouseReleased(MouseEvent e) {
0727 }
0728
0729 public void mouseEntered(MouseEvent e) {
0730 }
0731
0732 public void mouseExited(MouseEvent e) {
0733 }
0734 });
0735
0736
0737
0738 corefTree.addComponentListener(new ComponentAdapter() {
0739 public void componentHidden(ComponentEvent e) {
0740
0741 }
0742
0743 public void componentMoved(ComponentEvent e) {
0744 }
0745
0746 public void componentResized(ComponentEvent e) {
0747 SwingUtilities.invokeLater(new Runnable(){
0748 public void run(){
0749 Enumeration nodes = corefTreeRoot.depthFirstEnumeration();
0750 while(nodes.hasMoreElements()){
0751 corefTreeModel.nodeChanged((TreeNode)nodes.nextElement());
0752 }
0753 }
0754 });
0755 }
0756
0757 public void componentShown(ComponentEvent e) {
0758 }
0759 });
0760 }//protected void initListeners()
0761
0762 /**
0763 * Initialises the local variables to their default values
0764 */
0765 protected void initLocalData(){
0766 //init local vars
0767 lock = new Object();
0768
0769 data = Collections.synchronizedList(new ArrayList());
0770 //dataAsAS = new gate.annotation.AnnotationSetImpl(document);
0771 ranges = new ArrayList();
0772
0773 typeDataMap = new HashMap();
0774
0775 eventHandler = new EventsHandler();
0776
0777 }//protected void initLocalData()
0778
0779 /**Builds all the graphical components*/
0780 protected void initGuiComponents(){
0781 //initialise GUI components
0782 // this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
0783 this.setLayout(new BorderLayout());
0784
0785 //the toolbar
0786 toolbar = new JToolBar(JToolBar.HORIZONTAL);
0787 toolbar.setAlignmentX(Component.LEFT_ALIGNMENT);
0788 toolbar.setAlignmentY(Component.TOP_ALIGNMENT);
0789 toolbar.setFloatable(false);
0790 this.add(toolbar, BorderLayout.NORTH);
0791
0792 textVisibleBtn = new JToggleButton("Text", textVisible);
0793 toolbar.add(textVisibleBtn);
0794
0795 annotationsTableVisibleBtn = new JToggleButton("Annotations",
0796 annotationsTableVisible);
0797 toolbar.add(annotationsTableVisibleBtn);
0798
0799 typesTreeVisibleBtn = new JToggleButton("Annotation Sets", typesTreeVisible);
0800 toolbar.add(typesTreeVisibleBtn);
0801
0802
0803 coreferenceVisibleBtn = new JToggleButton("Coreference", coreferenceVisible);
0804 if(isCorefOptionAvailable()) toolbar.add(coreferenceVisibleBtn);
0805
0806
0807 //printing
0808 toolbar.add(Box.createHorizontalStrut(20));
0809 toolbar.add(new PrintAction());
0810 toolbar.add(new SearchAction());
0811
0812
0813
0814 toolbar.add(Box.createHorizontalGlue());
0815
0816 //The text
0817 textPane = new XJTextPane();
0818 // textPane.setEditable(false);
0819 textPane.setEnabled(true);
0820 textPane.setEditorKit(new CustomStyledEditorKit());
0821 Style defaultStyle = textPane.getStyle("default");
0822 StyleConstants.setBackground(defaultStyle, Color.white);
0823 StyleConstants.setFontFamily(defaultStyle, "Arial Unicode MS");
0824 textScroll = new JScrollPane(textPane);
0825 textScroll.setAlignmentY(Component.TOP_ALIGNMENT);
0826 textScroll.setAlignmentX(Component.LEFT_ALIGNMENT);
0827
0828
0829 //The table
0830 annotationsTableModel = new AnnotationsTableModel();
0831 annotationsTable = new XJTable(annotationsTableModel);
0832 // annotationsTable.setIntercellSpacing(new Dimension(10, 5));
0833
0834 tableScroll = new JScrollPane(annotationsTable);
0835 tableScroll.setOpaque(true);
0836 tableScroll.setAlignmentY(Component.TOP_ALIGNMENT);
0837 tableScroll.setAlignmentX(Component.LEFT_ALIGNMENT);
0838
0839
0840 //RIGHT SIDE - the big tree
0841 stylesTreeRoot = new DefaultMutableTreeNode(null, true);
0842 stylesTreeModel = new DefaultTreeModel(stylesTreeRoot, true);
0843 stylesTree = new JTree(stylesTreeModel){
0844 public void updateUI(){
0845 super.updateUI();
0846 setRowHeight(0);
0847 }
0848 };
0849
0850 stylesTree.setRootVisible(false);
0851 stylesTree.setCellRenderer(new NodeRenderer());
0852 //TIP: setting rowHeight to 0 tells the tree to query its renderer for each
0853 //row's size
0854 stylesTree.setRowHeight(0);
0855 stylesTree.setShowsRootHandles(true);
0856 stylesTree.setToggleClickCount(0);
0857 stylesTreeScroll = new JScrollPane(stylesTree);
0858 stylesTreeScroll.setAlignmentY(Component.TOP_ALIGNMENT);
0859 stylesTreeScroll.setAlignmentX(Component.LEFT_ALIGNMENT);
0860
0861
0862 //coreference
0863 corefTreeRoot = new DefaultMutableTreeNode("Co-reference data", true);
0864 corefTree = new JTree(corefTreeModel = new DefaultTreeModel(corefTreeRoot,
0865 true));
0866 corefTree.setCellRenderer(new CorefNodeRenderer());
0867 corefTree.setRowHeight(0);
0868 corefTree.setRootVisible(true);
0869 corefTree.setShowsRootHandles(false);
0870 corefScroll = new JScrollPane(corefTree);
0871 corefScroll.setAlignmentX(Component.LEFT_ALIGNMENT);
0872 corefScroll.setAlignmentY(Component.TOP_ALIGNMENT);
0873 updateCorefTree();
0874
0875 //various containers
0876 leftSplit = new JSplitPane(JSplitPane.VERTICAL_SPLIT, false);
0877 leftSplit.setOneTouchExpandable(true);
0878 leftSplit.setOpaque(true);
0879 leftSplit.setAlignmentY(Component.TOP_ALIGNMENT);
0880 leftSplit.setAlignmentX(Component.LEFT_ALIGNMENT);
0881 leftSplit.setResizeWeight((double)0.75);
0882
0883 rightSplit = new JSplitPane(JSplitPane.VERTICAL_SPLIT, false);
0884 rightSplit.setOneTouchExpandable(true);
0885 rightSplit.setOpaque(true);
0886 rightSplit.setAlignmentY(Component.TOP_ALIGNMENT);
0887 rightSplit.setAlignmentX(Component.LEFT_ALIGNMENT);
0888 rightSplit.setResizeWeight((double)0.75);
0889
0890
0891 mainSplit = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, false);
0892 mainSplit.setOneTouchExpandable(true);
0893 mainSplit.setOpaque(true);
0894 mainSplit.setAlignmentY(Component.TOP_ALIGNMENT);
0895 mainSplit.setAlignmentX(Component.LEFT_ALIGNMENT);
0896 mainSplit.setResizeWeight((double)0.75);
0897
0898 //put everything together
0899 layoutComponents();
0900
0901 //Extra Stuff
0902
0903 progressBox = new Box(BoxLayout.X_AXIS);
0904 progressBox.add(Box.createHorizontalStrut(5));
0905 progressBar = new JProgressBar(JProgressBar.HORIZONTAL, 0, 100);
0906 progressBox.add(progressBar);
0907 progressBox.add(Box.createHorizontalStrut(5));
0908
0909 highlighter = textPane.getHighlighter();
0910 if(highlighter instanceof javax.swing.text.DefaultHighlighter){
0911 ((javax.swing.text.DefaultHighlighter)highlighter).
0912 setDrawsLayeredHighlights(true);
0913 }
0914
0915 selectionHighlighter = new DefaultHighlighter();
0916 selectionHighlighter.install(textPane);
0917 selectionBlinker = new SelectionBlinker();
0918
0919 }//protected void initGuiComponents()
0920
0921
0922 /** This method returns true if a text is selected in the textPane*/
0923 private boolean isTextSelected(){
0924 return !(textPane.getSelectionStart()==textPane.getSelectionEnd());
0925 }// isTextSelected()
0926 /**
0927 * Gets all the {@link gate.creole.AnnotationSchema} objects currently
0928 * loaded in the system.
0929 */
0930 protected Set getAnnotationSchemas(){
0931 Set result = new HashSet();
0932 ResourceData rData = (ResourceData)Gate.getCreoleRegister().
0933 get("gate.creole.AnnotationSchema");
0934 if(rData != null){
0935 result.addAll(rData.getInstantiations());
0936 }
0937 return result;
0938 }//protected Set getAnnotationSchemas()
0939
0940 public synchronized void removePropertyChangeListener(
0941 PropertyChangeListener l) {
0942 super.removePropertyChangeListener(l);
0943 propertyChangeListeners.removePropertyChangeListener(l);
0944 }
0945
0946 public synchronized void addPropertyChangeListener(PropertyChangeListener l) {
0947 super.addPropertyChangeListener(l);
0948 propertyChangeListeners.addPropertyChangeListener(l);
0949 }
0950
0951 public synchronized void addPropertyChangeListener(String propertyName,
0952 PropertyChangeListener l) {
0953 super.addPropertyChangeListener(propertyName, l);
0954 propertyChangeListeners.addPropertyChangeListener(propertyName, l);
0955 }
0956
0957
0958 /** Return the current selected document */
0959 public gate.Document getDocument() {
0960 return document;
0961 } // Document getDocument()
0962
0963 /**
0964 * Sets the document to be displayed
0965 */
0966 public void setTarget(Object target){
0967 if(target == null){
0968 document = null;
0969 return;
0970 }
0971 if(!(target instanceof gate.Document)){
0972 throw new IllegalArgumentException(
0973 "The document editor can only display GATE documents!\n" +
0974 "The provided resource is not a document but a: " +
0975 target.getClass().toString() + "!");
0976 }
0977 gate.Document oldDocument = document;
0978 document = (gate.Document)target;
0979 //this needs to be executed even if the new document equals(oldDocument)
0980 //in order to update the pointers
0981 if(oldDocument != document) this_documentChanged();
0982
0983 propertyChangeListeners.firePropertyChange("document", oldDocument,
0984 target);
0985 }//public void setTarget(Object target)
0986
0987 public void setHandle(Handle handle){
0988 myHandle = handle;
0989 }
0990
0991 public void cleanup(){
0992 document = null;
0993 stylesTreeRoot.removeAllChildren();
0994 data.clear();
0995 ranges.clear();
0996 myHandle = null;
0997 }
0998
0999 /**
1000 * This method returns a list of annotations which are currently shown in
1001 * the annotations table or null of the table is empty.
1002 */
1003 public java.util.Set getDisplayedAnnotations() {
1004 //if the annotations table is empty, then return null
1005 if (annotationsTableModel == null||annotationsTableModel.getRowCount() == 0)
1006 return null;
1007
1008 // Read the displayed annotations and insert them into a list
1009 java.util.Set shownAnnots = new HashSet();
1010 for(int i = 0; i < annotationsTableModel.getRowCount(); i++){
1011 //Find an annotation and add it to the annotationsToDump set.
1012 Annotation ann = (Annotation)annotationsTableModel.getValueAt(i, -1);
1013 shownAnnots.add(ann);
1014 }// End for
1015
1016 return shownAnnots;
1017 }
1018
1019 /**
1020 * Get positions of cut points inside a very large text without new line
1021 */
1022 private Vector getBreakPositions(String content) {
1023 Vector breakPositions = new Vector();
1024
1025 int lastNewLinePos = -1;
1026 int spacePos = -1;
1027 int unbreakedLineSize = 0;
1028 char ch;
1029 int contentSize = content.length();
1030
1031 for(int i=0; i<contentSize; ++i) {
1032 ch = content.charAt(i);
1033
1034 switch(ch) {
1035 case '\n' :
1036 unbreakedLineSize = 0;
1037 spacePos = -1;
1038 break;
1039 case '\r' :
1040 unbreakedLineSize = 0;
1041 spacePos = -1;
1042 break;
1043 case '\t' :
1044 spacePos = i;
1045 ++unbreakedLineSize;
1046 break;
1047 case ' ' :
1048 spacePos = i;
1049 ++unbreakedLineSize;
1050 break;
1051
1052 default:
1053 if(unbreakedLineSize >= MAX_LINE_SIZE) {
1054 // insert break
1055 if(spacePos == -1) {
1056 // break without space
1057 spacePos = i;
1058 } // if
1059
1060 breakPositions.add(new Integer(spacePos+1));
1061 unbreakedLineSize = i - spacePos;
1062 spacePos = -1;
1063 }
1064 else {
1065 ++unbreakedLineSize;
1066 } // if
1067 } // switch
1068 } // for
1069
1070 return breakPositions;
1071 } // getBreakPositions(String content)
1072
1073 /** Max unbreaked line size */
1074 private final int MAX_LINE_SIZE = 2048;
1075
1076 /**
1077 * Cut very long lines to pieces not grater than MAX_LINE_SIZE
1078 * This is a correction of SWING problem with very long lines of text
1079 * <BR>
1080 * Return positions of new line insertion.
1081 */
1082 private Vector correctLongLines(StringBuffer buff) {
1083 // analyze for long unbreaked line of text
1084 Vector breaks = getBreakPositions(buff.toString());
1085 //if(breaks.size() > 0) System.out.println("Breaks: "+breaks);
1086
1087 Integer currentBreak;
1088 int intValue;
1089 // put new line in break positions
1090 for(int i = breaks.size()-1; i>=0; --i) {
1091 currentBreak = (Integer) breaks.get(i);
1092 intValue = currentBreak.intValue();
1093 buff.insert(intValue, '\n');
1094 } // for
1095
1096 if(breaks.size() > 0) {
1097 return breaks;
1098 }
1099 else {
1100 return null;
1101 }
1102 } // correctLongLines(StringBuffer buff)
1103
1104 /** Compute correction for additional new line in very long lines of text */
1105 private int longLinesCorrection(int position) {
1106 int result = 0;
1107
1108 if(longLinesCorrectionPositions != null) {
1109 boolean underPosition = true;
1110 Integer current;
1111 Iterator it = longLinesCorrectionPositions.iterator();
1112
1113 while(underPosition && it.hasNext()) {
1114 current = (Integer) it.next();
1115 if(position > (current.intValue()+result)) {
1116 // cross this new line point
1117 ++result;
1118 }
1119 else {
1120 // all new lines computed
1121 underPosition = false;
1122 } // if
1123 } // while
1124 } // if
1125
1126 return result;
1127 } // int longLinesCorrection(int position)
1128
1129 /** Keep cut places in very long lines inside document */
1130 private Vector longLinesCorrectionPositions;
1131
1132 /**
1133 * Updates this component when the underlying document is changed. This method
1134 * is only triggered when the document is changed to a new one and not when
1135 * the internal data from the document changes.
1136 */
1137 protected void this_documentChanged(){
1138 initLocalData();
1139
1140 Enumeration enumeration = stylesTreeRoot.children();
1141 while(enumeration.hasMoreElements()){
1142 stylesTreeModel.removeNodeFromParent((DefaultMutableTreeNode)
1143 enumeration.nextElement());
1144 }
1145 if(document == null) return;
1146
1147 // check for very long lines of text in order to correct SWING bug
1148 String documentContent = document.getContent().toString();
1149 StringBuffer buffContent = new StringBuffer(documentContent);
1150 // cut very long lines to pieces not grater than MAX_LINE_SIZE
1151 longLinesCorrectionPositions = correctLongLines(buffContent);
1152 if(longLinesCorrectionPositions != null) {
1153 documentContent = buffContent.toString();
1154 } // if
1155
1156 textPane.setText(documentContent);
1157 //listen for events from the document content editor
1158 textPane.getDocument().addDocumentListener(new SwingDocumentListener());
1159
1160 //add the default annotation set
1161 eventHandler.annotationSetAdded(new gate.event.DocumentEvent(
1162 document,
1163 gate.event.DocumentEvent.ANNOTATION_SET_ADDED, null));
1164
1165 //register the for this new document's events
1166 document.addDocumentListener(eventHandler);
1167
1168 annotationsTableModel.fireTableDataChanged();
1169 document.getFeatures().addFeatureMapListener(new FeatureMapListener(){
1170 public void featureMapUpdated(){
1171 updateCorefTree();
1172 }
1173 });
1174 updateCorefTree();
1175
1176
1177 //add all the other annotation sets
1178 Map namedASs = document.getNamedAnnotationSets();
1179 if(namedASs != null){
1180 Iterator setsIter = namedASs.values().iterator();
1181 while(setsIter.hasNext()){
1182 AnnotationSet currentAS = (AnnotationSet)setsIter.next();
1183 if(currentAS != null){
1184 eventHandler.annotationSetAdded(new gate.event.DocumentEvent(
1185 document,
1186 gate.event.DocumentEvent.ANNOTATION_SET_ADDED,
1187 currentAS.getName()));
1188 }
1189 }
1190 }
1191 }//protected void this_documentChanged()
1192
1193 /**
1194 * Gets the data related to a given annotation type.
1195 * An annotation type is uniquely identified by the name of its AnnotationSet
1196 * and the name of the type.
1197 * For the default annotation set of a document (which has no name) the
1198 * "<Default>" value is used.
1199 *
1200 * Once a {@link TypeData} value has been obtained it can be used to change
1201 * the way the respective type of annotations are displayed.
1202 * @param setName a {@link java.lang.String}, the name of the annotation set
1203 * @param type a {@link java.lang.String}, the name of the type.
1204 * @return a {@link TypeData} value
1205 */
1206 protected TypeData getTypeData(String setName, String type){
1207 Map setMap = (Map)typeDataMap.get(setName);
1208 if(setMap != null) return (TypeData)setMap.get(type);
1209 else return null;
1210 }// protected TypeData getTypeData(String setName, String type)
1211
1212
1213 /**
1214 * Repaints the highlighting for annotation types in the text display.
1215 */
1216 protected void showHighlights(Set annotations, AttributeSet style) {
1217 //store the state of the text display
1218 int selStart = textPane.getSelectionStart();
1219 int selEnd = textPane.getSelectionEnd();
1220 final int position = textPane.viewToModel(
1221 textScroll.getViewport().getViewPosition());
1222 //hide the text
1223 SwingUtilities.invokeLater(new Runnable() {
1224 public void run() {
1225 progressBar.setValue(0);
1226 //progressBar.setMaximumSize(new Dimension(textScroll.getWidth(),20));
1227 textScroll.getViewport().setView(progressBox);
1228 textScroll.paintImmediately(textScroll.getBounds());
1229 }
1230 });
1231
1232 paintHighlights(annotations, style);
1233
1234 //restore the state
1235 textPane.select(selStart, selEnd);
1236 SwingUtilities.invokeLater(new Runnable() {
1237 public void run() {
1238 //show the text
1239 textScroll.getViewport().setView(textPane);
1240 try {
1241 textScroll.getViewport().setViewPosition(
1242 textPane.modelToView(position).getLocation());
1243 textScroll.paintImmediately(textScroll.getBounds());
1244 }
1245 catch (BadLocationException ble) {
1246 }
1247 }
1248 });
1249 }//protected void showHighlights()
1250
1251 protected void paintHighlights(Set annotations, AttributeSet style){
1252 //highlight the annotations
1253 int size = annotations.size();
1254 int i = 0;
1255 int lastValue = 0;
1256 int value;
1257
1258 int start, end;
1259 Iterator annIter = annotations.iterator();
1260 while(annIter.hasNext()){
1261 Annotation ann = (Annotation)annIter.next();
1262 start = ann.getStartNode().getOffset().intValue();
1263 end = ann.getEndNode().getOffset().intValue();
1264 // compute correction for new line breaks in long lines
1265 start += longLinesCorrection(start);
1266 end += longLinesCorrection(end);
1267
1268 textPane.select(start, end);
1269 textPane.setCharacterAttributes(style, true);
1270 if(progressBar.isVisible()){
1271 value = i * 100 / size;
1272 if (value - lastValue >= 5) {
1273 progressBar.setValue(value);
1274 progressBar.paintImmediately(progressBar.getBounds());
1275 lastValue = value;
1276 }
1277 i++;
1278 }
1279 }
1280 }
1281
1282 /**
1283 * Called whenever a part of the textual display needs to be repainted
1284 * because, for instance, of an edit operation.
1285 * @param start the start offset for the area to be repainted
1286 * @param end the end offset for the area to be repainted.
1287 */
1288 protected void repairHighlights(int start, int end) {
1289 //we need to fix the character ranges for all types visible or not
1290 //clear everything
1291 int selStart = textPane.getSelectionStart();
1292 int selEnd = textPane.getSelectionEnd();
1293 //clear the styles in the affected area
1294 textPane.select(start, end);
1295 textPane.setCharacterAttributes(textPane.getStyle("default"), true);
1296
1297 //repaint the highlights for the annotations going through the affected area
1298 Iterator setsIter = typeDataMap.keySet().iterator();
1299 while(setsIter.hasNext()){
1300 Map typesMap = (Map)typeDataMap.get(setsIter.next());
1301 Iterator typesIter = typesMap.keySet().iterator();
1302 while(typesIter.hasNext()){
1303 TypeData tData = (TypeData) typesMap.get(typesIter.next());
1304 if (tData.getVisible()) {
1305 String setName = tData.getSet();
1306 AnnotationSet annSet = setName.equals("Default") ?
1307 document.getAnnotations() :
1308 document.getAnnotations(setName);
1309 annSet = annSet.get(tData.getType());
1310 if(annSet != null){
1311 AnnotationSet annotationsToRepaint = annSet.get(new Long(start),
1312 new Long(end));
1313 // Set annotationsToRepaint = new HashSet();
1314 // Iterator annIter = tData.getAnnotations().iterator();
1315 // while (annIter.hasNext()) {
1316 // Annotation anAnnotation = (Annotation) annIter.next();
1317 // long annStart = anAnnotation.getStartNode().getOffset().longValue();
1318 // long annEnd = anAnnotation.getEndNode().getOffset().longValue();
1319 // if ( (annStart < start && annEnd >= start) ||
1320 // (start <= annStart && annStart <= end)
1321 // )
1322 // annotationsToRepaint.add(anAnnotation);
1323 // }
1324 paintHighlights(annotationsToRepaint, tData.getActualStyle());
1325 }
1326 }
1327 }
1328 }
1329 //restore selection
1330 textPane.select(selStart, selEnd);
1331 // textPane.requestFocus();
1332 }
1333
1334 /**
1335 * Updates the GUI when the user has selected an annotation e.g. by using the
1336 * right click popup. That basically means make the appropiate type of
1337 * annotations visible in case it isn't already.
1338 */
1339 protected void selectAnnotation(String set, Annotation ann) {
1340 TypeData tData = getTypeData(set, ann.getType());
1341 if(!tData.getVisible()){
1342 tData.setVisible(true);
1343 //sleep a while so the gui updater thread has time to start
1344 try{
1345 Thread.sleep(100);
1346 }catch(InterruptedException ie){}
1347 //refresh the display for the type
1348 //(the checkbox has to be shown selected)
1349 DefaultMutableTreeNode node = (DefaultMutableTreeNode)
1350 ((DefaultMutableTreeNode)stylesTreeRoot).
1351 getFirstChild();
1352 while(node != null &&
1353 !((TypeData)node.getUserObject()).getSet().equals(set))
1354 node = node.getNextSibling();
1355 if(node != null){
1356 node = (DefaultMutableTreeNode)node.getFirstChild();
1357 String type = ann.getType();
1358 while(node != null &&
1359 !((TypeData)node.getUserObject()).getType().equals(type))
1360 node = node.getNextSibling();
1361 if(node != null) stylesTreeModel.nodeChanged(node);
1362 }
1363 }
1364 int position = -1;
1365 position = data.indexOf(ann);
1366 if(position != -1){
1367 position = annotationsTable.getTableRow(position);
1368 if(position != -1){
1369 annotationsTable.clearSelection();
1370 annotationsTable.addRowSelectionInterval(position, position);
1371 annotationsTable.scrollRectToVisible(
1372 annotationsTable.getCellRect(position, 0, true));
1373 }
1374 }
1375 }//protected void selectAnnotation(String set, Annotation ann)
1376
1377
1378 /**
1379 * Creates the layout of this component acording to the set of subcomponents
1380 * (text display, annotations table, etc.) that need to be visible.
1381 */
1382 protected void layoutComponents(){
1383 SwingUtilities.invokeLater(new Runnable(){
1384 public void run(){
1385 Component leftComp = null, rightComp = null;
1386 if(isTextVisible() && isAnnotationsTableVisible()){
1387 leftSplit.setTopComponent(textScroll);
1388 leftSplit.setBottomComponent(tableScroll);
1389 leftComp = leftSplit;
1390 }else{
1391 if(isTextVisible()) leftComp = textScroll;
1392 else if(isAnnotationsTableVisible()) leftComp = tableScroll;
1393 }
1394
1395 boolean corefDisplayed = isCoreferenceVisible() &&
1396 isCorefOptionAvailable();
1397 if(corefDisplayed) updateCorefTree();
1398 if(isTypesTreeVisible() && corefDisplayed){
1399 rightSplit.setTopComponent(stylesTreeScroll);
1400 rightSplit.setBottomComponent(corefScroll);
1401 rightComp = rightSplit;
1402 }else{
1403 if(isTypesTreeVisible()) rightComp = stylesTreeScroll;
1404 else if(corefDisplayed) rightComp = corefScroll;
1405 }
1406
1407 if(DocumentEditor.this.getComponentCount() > 1)
1408 DocumentEditor.this.remove(1);
1409 if(leftComp != null && rightComp != null){
1410 //we need the main split
1411 mainSplit.setLeftComponent(leftComp);
1412 mainSplit.setRightComponent(rightComp);
1413 DocumentEditor.this.add(mainSplit, BorderLayout.CENTER);
1414 }else{
1415 if(leftComp != null) DocumentEditor.this.add(leftComp,
1416 BorderLayout.CENTER);
1417 else if(rightComp != null)DocumentEditor.this.add(rightComp,
1418 BorderLayout.CENTER);
1419 }
1420
1421 DocumentEditor.this.validate();
1422 DocumentEditor.this.repaint();
1423 }
1424 });
1425 }
1426
1427
1428 /**
1429 * Updates the coref tree from the coref data on the document's features
1430 */
1431 protected void updateCorefTree(){
1432 if(document == null || document.getFeatures() == null){
1433 //no coref data; clear the tree
1434 corefTreeRoot.removeAllChildren();
1435 corefTreeModel.nodeStructureChanged(corefTreeRoot);
1436 setCorefOptionAvailable(false);
1437 return;
1438 }
1439
1440 Map matchesMap = null;
1441 try{
1442 matchesMap = (Map)document.getFeatures().get(DOCUMENT_COREF_FEATURE_NAME);
1443 }catch(Exception e){
1444 }
1445 if(matchesMap == null){
1446 //no coref data; clear the tree
1447 Enumeration nodes = corefTreeRoot.breadthFirstEnumeration();
1448 while(nodes.hasMoreElements()){
1449 DefaultMutableTreeNode node = (DefaultMutableTreeNode)
1450 nodes.nextElement();
1451 if(node.getUserObject() instanceof CorefData){
1452 ((CorefData)node.getUserObject()).setVisible(false);
1453 }
1454 }
1455 corefTreeRoot.removeAllChildren();
1456 corefTreeModel.nodeStructureChanged(corefTreeRoot);
1457 setCorefOptionAvailable(false);
1458 return;
1459 }
1460
1461 //matches map is not null; check whether it's valid
1462 Iterator setsIter = matchesMap.keySet().iterator();
1463 setsLoop: while(setsIter.hasNext()){
1464 String setName = (String)setsIter.next();
1465 AnnotationSet annSet = setName == null ? document.getAnnotations() :
1466 document.getAnnotations(setName);
1467 Iterator entitiesIter = ((java.util.List)matchesMap.get(setName)).
1468 iterator();
1469 //each entity is a list of annotation IDs
1470 while(entitiesIter.hasNext()){
1471 Iterator idsIter = ((java.util.List)entitiesIter.next()).iterator();
1472 while(idsIter.hasNext()){
1473 if(annSet.get((Integer)idsIter.next()) == null){
1474 //remove the data for this set
1475 setsIter.remove();
1476 Err.prln("Coreference data for the \"" +
1477 (setName == null ? "Default" : setName) +
1478 "\" annotation set of document \"" + document.getName() +
1479 "\" was invalid and has been removed");
1480 continue setsLoop;
1481 }
1482 }
1483 }
1484 }
1485
1486 if(matchesMap.isEmpty()){
1487 //no more coref data
1488 corefTreeRoot.removeAllChildren();
1489 corefTreeModel.nodeStructureChanged(corefTreeRoot);
1490 setCorefOptionAvailable(false);
1491 return;
1492 }
1493
1494 String[] newSetNames = (String[])
1495 matchesMap.keySet().toArray(new String[]{});
1496 Arrays.sort(newSetNames);
1497
1498 ArrayList oldSetNames = new ArrayList(corefTreeRoot.getChildCount());
1499 Enumeration setNodes = corefTreeRoot.children();
1500 while(setNodes.hasMoreElements()){
1501 String oldSetName = (String)
1502 ((DefaultMutableTreeNode)setNodes.nextElement()).
1503 getUserObject();
1504 oldSetNames.add(oldSetName.equals("Default") ? null : oldSetName);
1505 }
1506
1507
1508 // stores the new set nodes; they will be added to root after the
1509 // processing is done
1510 ArrayList newSetNodes = new ArrayList();
1511 //for each new set update the children
1512 for(int i =0; i < newSetNames.length; i++){
1513 String setName = newSetNames[i];
1514 int oldNodeIndex = oldSetNames.indexOf(setName);
1515 DefaultMutableTreeNode setNode =
1516 (oldNodeIndex != -1) ?
1517 (DefaultMutableTreeNode)
1518 corefTreeRoot.getChildAt(oldNodeIndex) :
1519 new DefaultMutableTreeNode((setName == null ? "Default" : setName),
1520 true);
1521 //if found it will be reused so delete it from the list
1522 if(oldNodeIndex != -1) oldSetNames.remove(oldNodeIndex);
1523
1524 // temporarily stores the new nodes
1525 ArrayList newEntityNodes = new ArrayList();
1526 //for each set the coref data is a list of lists
1527 Iterator corefDataIter = ((java.util.List)matchesMap.get(setName)).
1528 iterator();
1529 while(corefDataIter.hasNext()){
1530 java.util.List newAnnotIDs = (java.util.List)corefDataIter.next();
1531 CorefData cData = null;
1532 DefaultMutableTreeNode entityNode = null;
1533 //try to find the old coref data
1534 Enumeration entityNodes = setNode.children();
1535 while(cData == null && entityNodes.hasMoreElements()){
1536 entityNode = (DefaultMutableTreeNode)entityNodes.nextElement();
1537 java.util.List oldAnnotIDs = ((CorefData)entityNode.getUserObject()).
1538 getAnnoationIDs();
1539 java.util.List intersection = new ArrayList(oldAnnotIDs);
1540 intersection.retainAll(newAnnotIDs);
1541 if(!intersection.isEmpty()){
1542 //we have some common values; assume we found it
1543 cData = (CorefData)entityNode.getUserObject();
1544 if(intersection.size() == newAnnotIDs.size()){
1545 //identical values, we just got lucky: noting to do
1546 }else{
1547 cData.setAnnotationIDs(newAnnotIDs);
1548 }
1549 }
1550 }
1551 if(cData == null){
1552 //we couldn't find a suitable node, create a new one
1553 cData = new CorefData(newAnnotIDs, false, setName == null ?
1554 "Default" : setName);
1555 entityNode = new DefaultMutableTreeNode(cData, false);
1556 }
1557 newEntityNodes.add(entityNode);
1558 }//while(corefDataIter.hasNext())
1559 //we're done with this set: add all the nodes to the set node
1560 //set visible to false for all nodes that will not be kept
1561 // for(Enumeration entityNodes = setNode.children();
1562 // entityNodes.hasMoreElements();){
1563 // Object anOldNode = entityNodes.nextElement();
1564 // if(!newEntityNodes.contains(anOldNode)){
1565 // ((CorefData)((DefaultMutableTreeNode)anOldNode).
1566 // getUserObject()).setVisible(false);
1567 // }
1568 // }
1569
1570 setNode.removeAllChildren();
1571 for(Iterator nodesIter = newEntityNodes.iterator();
1572 nodesIter.hasNext();
1573 setNode.add((DefaultMutableTreeNode)nodesIter.next())){
1574 }
1575 newSetNodes.add(setNode);
1576 }//for(int i =0; i < newSetNames.length; i++)
1577 //we're done with all the sets: add the nodes to the tree root
1578 corefTreeRoot.removeAllChildren();
1579 for(Iterator nodesIter = newSetNodes.iterator();
1580 nodesIter.hasNext();){
1581 DefaultMutableTreeNode setNode = (DefaultMutableTreeNode)nodesIter.next();
1582 corefTreeRoot.add(setNode);
1583 }
1584 SwingUtilities.invokeLater(new Runnable(){
1585 public void run(){
1586 highlighter.removeAllHighlights();
1587 corefTreeModel.nodeStructureChanged(corefTreeRoot);
1588 //expand the root
1589 corefTree.expandPath(new TreePath(new Object[]{corefTreeRoot}));
1590 //expand all of root's children
1591 Enumeration children = corefTreeRoot.children();
1592 while(children.hasMoreElements()){
1593 DefaultMutableTreeNode aNode =
1594 (DefaultMutableTreeNode)children.nextElement();
1595 corefTree.expandPath(
1596 new TreePath(corefTreeModel.getPathToRoot(aNode)));
1597 if(aNode.getUserObject() instanceof CorefData){
1598 CorefData cData = (CorefData)aNode.getUserObject();
1599 //trigger highlights repaint
1600 cData.setVisible(cData.getVisible());
1601 }
1602 }
1603 }
1604 });
1605 setCorefOptionAvailable(true);
1606 }//protected void updateCorefTree()
1607
1608
1609 /**Should the editor functionality of this component be enabled*/
1610 public void setEditable(boolean newEditable) {
1611 editable = newEditable;
1612 }
1613
1614 /**Is the editor functionality enabled*/
1615 public boolean isEditable() {
1616 return editable;
1617 }
1618 public void setAnnotationsTableVisible(boolean annotationsTableVisible) {
1619 boolean oldAnnotationsTableVisible = this.annotationsTableVisible;
1620 this.annotationsTableVisible = annotationsTableVisible;
1621 propertyChangeListeners.firePropertyChange(
1622 "annotationsTableVisible",
1623 new Boolean(oldAnnotationsTableVisible),
1624 new Boolean(annotationsTableVisible));
1625 }
1626 public boolean isAnnotationsTableVisible() {
1627 return annotationsTableVisible;
1628 }
1629 public void setCoreferenceVisible(boolean coreferenceVisible) {
1630 boolean oldCoreferenceVisible = this.coreferenceVisible;
1631 this.coreferenceVisible = coreferenceVisible;
1632 propertyChangeListeners.firePropertyChange(
1633 "coreferenceVisible",
1634 new Boolean(oldCoreferenceVisible),
1635 new Boolean(coreferenceVisible));
1636 }
1637
1638 public boolean isCoreferenceVisible() {
1639 return coreferenceVisible;
1640 }
1641 public void setTextVisible(boolean textVisible) {
1642 boolean oldTextVisible = this.textVisible;
1643 this.textVisible = textVisible;
1644 propertyChangeListeners.firePropertyChange("textVisible",
1645 new Boolean(oldTextVisible),
1646 new Boolean(textVisible));
1647 }
1648 public boolean isTextVisible() {
1649 return textVisible;
1650 }
1651 public void setTypesTreeVisible(boolean typesTreeVisible) {
1652 boolean oldTypesTreeVisible = this.typesTreeVisible;
1653 this.typesTreeVisible = typesTreeVisible;
1654 propertyChangeListeners.firePropertyChange("typesTreeVisible",
1655 new Boolean(oldTypesTreeVisible),
1656 new Boolean(typesTreeVisible));
1657 }
1658 public boolean isTypesTreeVisible() {
1659 return typesTreeVisible;
1660 }
1661 public void setCorefOptionAvailable(boolean corefOptionAvailable) {
1662 boolean oldCorefOptionAvailable = this.corefOptionAvailable;
1663 this.corefOptionAvailable = corefOptionAvailable;
1664 propertyChangeListeners.firePropertyChange(
1665 "corefOptionAvailable", new Boolean(oldCorefOptionAvailable),
1666 new Boolean(corefOptionAvailable));
1667 }
1668
1669 public boolean isCorefOptionAvailable() {
1670 return corefOptionAvailable;
1671 }
1672
1673 //inner classes
1674 /**
1675 * A custom table model used to render a table containing the annotations
1676 * from a set of annotation sets.
1677 * The columns will be: Type, Set, Start, End, Features
1678 */
1679 protected class AnnotationsTableModel extends AbstractTableModel{
1680 public AnnotationsTableModel(){
1681 }
1682
1683 public int getRowCount(){
1684 return data.size();
1685 }
1686
1687 public int getColumnCount(){
1688 return 5;
1689 }
1690
1691 public String getColumnName(int column){
1692 switch(column){
1693 case 0: return "Type";
1694 case 1: return "Set";
1695 case 2: return "Start";
1696 case 3: return "End";
1697 case 4: return "Features";
1698 default:return "?";
1699 }
1700 }
1701
1702 public Class getColumnClass(int column){
1703 switch(column){
1704 case 0: return String.class;
1705 case 1: return String.class;
1706 case 2: return Long.class;
1707 case 3: return Long.class;
1708 case 4: return String.class;
1709 default:return Object.class;
1710 }
1711 }
1712
1713 public Object getValueAt(int row, int column){
1714 Annotation ann;
1715 ann = (Annotation)data.get(row);
1716 switch(column){
1717 case -1:{//The actual annotation
1718 return ann;
1719 }
1720 case 0:{//Type
1721 return ann.getType();
1722 }
1723 case 1:{//Set
1724 Iterator rangesIter = ranges.iterator();
1725 while(rangesIter.hasNext()){
1726 Range range = (Range)rangesIter.next();
1727 if(range.start <= row && row < range.end) return range.setName;
1728 }
1729 return "?";
1730 }
1731 case 2:{//Start
1732 return ann.getStartNode().getOffset();
1733 }
1734 case 3:{//End
1735 return ann.getEndNode().getOffset();
1736 }
1737 case 4:{//Features
1738 if(ann.getFeatures() == null) return null;
1739 else return ann.getFeatures().toString();
1740 }
1741 default:{
1742 }
1743 }
1744 return null;
1745 }
1746 }//class AnnotationsTableModel extends AbstractTableModel
1747
1748
1749 protected class CorefData{
1750 CorefData(java.util.List annotationIDs, boolean visible, String setName){
1751 this.visible = visible;
1752 this.setName = setName;
1753 this.colour = colGenerator.getNextColor();
1754 highlights = new ArrayList();
1755 this.annotationIDs = annotationIDs;
1756 this.title = getNameForCorefList(annotationIDs);
1757 }
1758
1759 /**
1760 * Finds the name for a set of co refering entities (uses the string of the
1761 * first one).
1762 * @param list a list of annotation IDs
1763 */
1764 String getNameForCorefList(java.util.List list){
1765 if(list == null || list.isEmpty()) return null;
1766 Integer id = (Integer)list.get(0);
1767 AnnotationSet set = setName.equals("Default") ?
1768 document.getAnnotations() :
1769 document.getAnnotations(setName);
1770 Annotation ann = set.get(id);
1771
1772 String name = null;
1773 try{
1774 name = document.getContent().
1775 getContent(ann.getStartNode().getOffset(),
1776 ann.getEndNode().getOffset()).toString();
1777 }catch(InvalidOffsetException ioe){
1778 }
1779 return name;
1780 }
1781
1782 public boolean getVisible(){
1783 return visible;
1784 }
1785
1786 public void removeAnnotations(){
1787 AnnotationSet set = setName.equals("Default") ?
1788 document.getAnnotations() :
1789 document.getAnnotations(setName);
1790
1791 Iterator idIter = annotationIDs.iterator();
1792 while(idIter.hasNext()){
1793 set.remove(set.get((Integer)idIter.next()));
1794 }
1795 ((java.util.List)((Map)document.getFeatures().
1796 get(ANNIEConstants.DOCUMENT_COREF_FEATURE_NAME)).
1797 get(setName.equals("Default") ? null : setName)).remove(annotationIDs);
1798 annotationIDs.clear();
1799 updateCorefTree();
1800 }
1801
1802 public void setVisible(boolean isVisible){
1803 if(this.visible == isVisible) return;
1804 this.visible = isVisible;
1805 if(visible){
1806 //add new highlights and store them
1807 AnnotationSet set = setName.equals("Default") ?
1808 document.getAnnotations() :
1809 document.getAnnotations(setName);
1810 Iterator idIter = annotationIDs.iterator();
1811 ArrayList invalidIDs = new ArrayList();
1812 while(idIter.hasNext()){
1813 Integer id = (Integer)idIter.next();
1814 Annotation ann = set.get(id);
1815 if(ann == null){
1816 invalidIDs.add(id);
1817 }else try{
1818 highlights.add(highlighter.addHighlight(
1819 ann.getStartNode().getOffset().intValue(),
1820 ann.getEndNode().getOffset().intValue(),
1821 new DefaultHighlighter.DefaultHighlightPainter(colour)));
1822 }catch(BadLocationException ble){
1823 ble.printStackTrace();
1824 }
1825 }
1826 if(!invalidIDs.isEmpty()){
1827 annotationIDs.removeAll(invalidIDs);
1828 }
1829 }else{
1830 //remove the highlights
1831 if(!highlights.isEmpty()){
1832 Iterator hlIter = highlights.iterator();
1833 while(hlIter.hasNext()){
1834 Object tag = hlIter.next();
1835 highlighter.removeHighlight(tag);
1836 hlIter.remove();
1837 }
1838 }
1839 }
1840 }
1841
1842 public String getTitle(){
1843 return title;
1844 }
1845
1846 public Color getColour(){
1847 return colour;
1848 }
1849
1850 public void setColour(Color newColour){
1851 this.colour = newColour;
1852 if(visible){
1853 //update the highlights
1854 setVisible(false);
1855 setVisible(true);
1856 }
1857 }
1858
1859 public java.util.List getAnnoationIDs(){
1860 return annotationIDs;
1861 }
1862
1863 public String getSetName(){
1864 return setName;
1865 }
1866 public String toString(){
1867 return title;
1868 }
1869
1870 public void setAnnotationIDs(java.util.List newAnnIDs){
1871 this.annotationIDs =newAnnIDs;
1872 this.title = getNameForCorefList(annotationIDs);
1873 if(visible){
1874 //restore the highlights
1875 setVisible(false);
1876 setVisible(true);
1877 }
1878 }
1879
1880 private boolean visible;
1881 private String title;
1882 private String setName;
1883 private Color colour;
1884 private java.util.List highlights;
1885 private java.util.List annotationIDs;
1886 }
1887
1888 /*
1889 protected class CorefComboModel extends AbstractListModel
1890 implements ComboBoxModel{
1891
1892 CorefComboModel(){
1893 lastReturnedSize = 0;
1894 }
1895
1896 public int getSize(){
1897 if(document == null || document.getFeatures() == null) return 0;
1898 Map matchesMap = null;
1899 try{
1900 matchesMap = (Map)document.getFeatures().get(DOCUMENT_COREF_FEATURE_NAME);
1901 }catch(Exception e){
1902 e.printStackTrace();
1903 }
1904 int size = (matchesMap == null) ? 0 : matchesMap.size();
1905 if(lastReturnedSize != size){
1906 lastReturnedSize = size;
1907 fireDataChanged();
1908 }
1909 return lastReturnedSize;
1910 }
1911
1912
1913 public Object getElementAt(int index){
1914 if(document == null || document.getFeatures() == null) return null;
1915 Map matchesMap = null;
1916 try{
1917 matchesMap = (Map)document.getFeatures().get(DOCUMENT_COREF_FEATURE_NAME);
1918 }catch(Exception e){
1919 e.printStackTrace();
1920 }
1921 if(matchesMap == null) return null;
1922 java.util.List setsList = new ArrayList(matchesMap.keySet());
1923 boolean nullPresent = setsList.remove(null);
1924 Collections.sort(setsList);
1925 if(nullPresent) setsList.add(0, null);
1926 String res = (String)setsList.get(index);
1927 return (res == null) ? "Default" : res;
1928 }
1929
1930 public void setSelectedItem(Object anItem){
1931 if(anItem == null) selectedItem = null;
1932 else selectedItem = ((String)anItem).equals("Default") ? null : anItem;
1933 }
1934
1935 public Object getSelectedItem(){
1936 return selectedItem == null ? "Default" : selectedItem;
1937 }
1938
1939 void fireDataChanged(){
1940 fireContentsChanged(this, 0, getSize());
1941 }
1942
1943 Object selectedItem = null;
1944 int lastReturnedSize;
1945 }
1946 */
1947
1948 /**
1949 * Panels used in cell/node renderers
1950 */
1951 class LazyJPanel extends JPanel{
1952 /**
1953 * Overridden for performance reasons.
1954 */
1955 public void revalidate() {}
1956
1957 /**
1958 * Overridden for performance reasons.
1959 */
1960 public void repaint(long tm, int x, int y, int width, int height) {}
1961
1962 /**
1963 * Overridden for performance reasons.
1964 */
1965 public void repaint(Rectangle r) {}
1966
1967 /**
1968 * Overridden for performance reasons.
1969 */
1970 protected void firePropertyChange(String propertyName, Object oldValue,
1971 Object newValue) {}
1972
1973 /**
1974 * Overridden for performance reasons.
1975 */
1976 public void firePropertyChange(String propertyName, byte oldValue,
1977 byte newValue) {}
1978
1979 /**
1980 * Overridden for performance reasons.
1981 */
1982 public void firePropertyChange(String propertyName, char oldValue,
1983 char newValue) {}
1984
1985 /**
1986 * Overridden for performance reasons.
1987 */
1988 public void firePropertyChange(String propertyName, short oldValue,
1989 short newValue) {}
1990
1991 /**
1992 * Overridden for performance reasons.
1993 */
1994 public void firePropertyChange(String propertyName, int oldValue,
1995 int newValue) {}
1996
1997 /**
1998 * Overridden for performance reasons.
1999 */
2000 public void firePropertyChange(String propertyName, long oldValue,
2001 long newValue) {}
2002
2003 /**
2004 * Overridden for performance reasons.
2005 */
2006 public void firePropertyChange(String propertyName, float oldValue,
2007 float newValue) {}
2008
2009 /**
2010 * Overridden for performance reasons.
2011 */
2012 public void firePropertyChange(String propertyName, double oldValue,
2013 double newValue) {}
2014
2015 /**
2016 * Overridden for performance reasons.
2017 */
2018 public void firePropertyChange(String propertyName, boolean oldValue,
2019 boolean newValue) {}
2020 }
2021
2022 /**
2023 * A tree node renderer used by the coref tree
2024 */
2025 class CorefNodeRenderer implements TreeCellRenderer{
2026
2027 CorefNodeRenderer(){
2028 label = new JLabel();
2029 label.setOpaque(true);
2030
2031 checkBox = new JCheckBox();
2032 checkBox.setBorderPaintedFlat(true);
2033
2034 panel = new LazyJPanel();
2035 panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
2036 panel.setOpaque(false);
2037
2038 hBox = new LazyJPanel();
2039 hBox.setLayout(new BoxLayout(hBox, BoxLayout.X_AXIS));
2040 hBox.setOpaque(false);
2041
2042 panel.add(Box.createVerticalStrut(2));
2043 panel.add(hBox);
2044 panel.add(Box.createVerticalStrut(2));
2045
2046 leftSpacer = Box.createHorizontalStrut(3);
2047 rightSpacer = Box.createHorizontalStrut(3);
2048
2049 selectedBorder = BorderFactory.createLineBorder(Color.blue, 1);
2050 normalBorder = BorderFactory.createEmptyBorder(1, 1, 1, 1);
2051 }
2052
2053 public Component getTreeCellRendererComponent(JTree tree,
2054 Object value,
2055 boolean selected,
2056 boolean expanded,
2057 boolean leaf,
2058 int row,
2059 boolean hasFocus){
2060
2061 hBox.removeAll();
2062 hBox.add(leftSpacer);
2063
2064 if(value instanceof DefaultMutableTreeNode){
2065 value = ((DefaultMutableTreeNode)value).getUserObject();
2066 }
2067 if(value instanceof CorefData){
2068 CorefData cData = (CorefData)value;
2069 checkBox.setSelected(cData.getVisible());
2070 checkBox.setBackground(tree.getBackground());
2071
2072 label.setBackground(cData.getColour());
2073 label.setForeground(tree.getForeground());
2074 label.setText(cData.getTitle());
2075 label.setFont(tree.getFont());
2076 hBox.add(checkBox);
2077 hBox.add(label);
2078 hBox.add(rightSpacer);
2079 }else{
2080 label.setText(value == null ? "" : value.toString());
2081 label.setForeground(tree.getForeground());
2082 label.setBackground(tree.getBackground());
2083 label.setFont(tree.getFont());
2084 hBox.add(label);
2085 }
2086 if(selected) panel.setBorder(selectedBorder);
2087 else panel.setBorder(normalBorder);
2088 return panel;
2089 }
2090
2091 JLabel label;
2092 JCheckBox checkBox;
2093 JPanel panel;
2094 JPanel hBox;
2095 Border selectedBorder;
2096 Border normalBorder;
2097 Component leftSpacer, rightSpacer;
2098 }
2099
2100 /**
2101 * A tree node renderer used byt the coref tree
2102 */
2103 class CorefNodeRenderer1 implements TreeCellRenderer{
2104
2105 CorefNodeRenderer1(){
2106 label = new JLabel();
2107 label.setOpaque(true);
2108
2109 toggleButton = new JToggleButton();
2110 toggleButton.setMargin(new Insets(0,3,0,3));
2111
2112 panel = new LazyJPanel();
2113 panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
2114 panel.setOpaque(false);
2115 topSpacer = Box.createVerticalStrut(2);
2116 bottomSpacer = Box.createVerticalStrut(2);
2117
2118 selectedBorder = BorderFactory.createLineBorder(Color.blue, 1);
2119 normalBorder = BorderFactory.createEmptyBorder(1, 1, 1, 1);
2120
2121 }
2122
2123 public Component getTreeCellRendererComponent(JTree tree,
2124 Object value,
2125 boolean selected,
2126 boolean expanded,
2127 boolean leaf,
2128 int row,
2129 boolean hasFocus){
2130
2131 panel.removeAll();
2132 panel.add(topSpacer);
2133
2134 if(value instanceof DefaultMutableTreeNode){
2135 value = ((DefaultMutableTreeNode)value).getUserObject();
2136 }
2137 if(value instanceof CorefData){
2138 CorefData cData = (CorefData)value;
2139 toggleButton.setSelected(cData.getVisible());
2140 toggleButton.setBackground(cData.getColour());
2141 toggleButton.setForeground(tree.getForeground());
2142 toggleButton.setText(cData.getTitle());
2143 toggleButton.setFont(tree.getFont());
2144 panel.add(toggleButton);
2145 }else{
2146 label.setText(value.toString());
2147 label.setForeground(tree.getForeground());
2148 label.setBackground(tree.getBackground());
2149 label.setFont(tree.getFont());
2150 panel.add(label);
2151 }
2152 panel.add(bottomSpacer);
2153 if(selected) panel.setBorder(selectedBorder);
2154 else panel.setBorder(normalBorder);
2155 return panel;
2156 }
2157
2158 JLabel label;
2159 JToggleButton toggleButton;
2160 JPanel panel;
2161 Border selectedBorder;
2162 Border normalBorder;
2163 Component topSpacer, bottomSpacer;
2164 }
2165
2166
2167 /**
2168 * Displays an entry in the right hand side tree.
2169 * <strong>Implementation Note:</strong>
2170 * This class overrides
2171 * <code>revalidate</code>,
2172 * <code>repaint</code>,
2173 * and
2174 * <code>firePropertyChange</code>
2175 * solely to improve performance.
2176 * If not overridden, these frequently called methods would execute code paths
2177 * that are unnecessary for a tree cell renderer.
2178 */
2179 class NodeRenderer extends LazyJPanel implements TreeCellRenderer{
2180
2181 public NodeRenderer(){
2182 visibleChk = new JCheckBox("",false);
2183 visibleChk.setOpaque(false);
2184 visibleChk.setBorderPaintedFlat(true);
2185
2186 label = new JLabel();
2187 label.setOpaque(true);
2188 fontAttrs = new HashMap();
2189 selectedBorder = BorderFactory.createLineBorder(Color.blue, 1);
2190 normalBorder = BorderFactory.createEmptyBorder(1, 1, 1, 1);
2191 setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
2192 setOpaque(false);
2193 spacer = Box.createHorizontalStrut(3);
2194 }
2195
2196 public Component getTreeCellRendererComponent(JTree tree,
2197 Object value,
2198 boolean selected,
2199 boolean expanded,
2200 boolean leaf,
2201 int row,
2202 boolean hasFocus){
2203 removeAll();
2204 add(spacer);
2205
2206 int width = spacer.getWidth();
2207
2208
2209 TypeData nData = (TypeData)
2210 ((DefaultMutableTreeNode)value).getUserObject();
2211
2212 if(nData != null){
2213 label.setText(nData.getTitle());
2214 setLabelAttributes(nData.getAttributes());
2215
2216 if(nData.getType() != null) {
2217 visibleChk.setSelected(nData.getVisible());
2218 add(visibleChk);
2219 width += visibleChk.getMinimumSize().width;
2220 }
2221 }else{
2222 label.setText(((value == null || value.toString() == null) ?
2223 "" : value.toString()));
2224 }
2225 add(label);
2226
2227 if(selected) setBorder(selectedBorder);
2228 else setBorder(normalBorder);
2229 return this;
2230 }//public Component getTreeCellRendererComponent
2231
2232 protected void setLabelAttributes(AttributeSet style){
2233 label.setForeground(StyleConstants.getForeground(style));
2234 label.setBackground(StyleConstants.getBackground(style));
2235 fontAttrs.clear();
2236 fontAttrs.put(TextAttribute.FAMILY, StyleConstants.getFontFamily(style));
2237 fontAttrs.put(TextAttribute.SIZE, new Float(StyleConstants.getFontSize(style)));
2238 if(StyleConstants.isBold(style))
2239 fontAttrs.put(TextAttribute.WEIGHT, TextAttribute.WEIGHT_BOLD);
2240 else fontAttrs.put(TextAttribute.WEIGHT, TextAttribute.WEIGHT_REGULAR);
2241 if(StyleConstants.isItalic(style))
2242 fontAttrs.put(TextAttribute.POSTURE, TextAttribute.POSTURE_OBLIQUE);
2243 else fontAttrs.put(TextAttribute.POSTURE, TextAttribute.POSTURE_REGULAR);
2244 if(StyleConstants.isUnderline(style))
2245 fontAttrs.put(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON);
2246 else fontAttrs.remove(TextAttribute.UNDERLINE);
2247 if(StyleConstants.isStrikeThrough(style))
2248 fontAttrs.put(TextAttribute.STRIKETHROUGH, TextAttribute.STRIKETHROUGH_ON);
2249 else fontAttrs.remove(TextAttribute.STRIKETHROUGH);
2250 if(StyleConstants.isSuperscript(style))
2251 fontAttrs.put(TextAttribute.SUPERSCRIPT, TextAttribute.SUPERSCRIPT_SUPER);
2252 else if(StyleConstants.isSubscript(style))
2253 fontAttrs.put(TextAttribute.SUPERSCRIPT, TextAttribute.SUPERSCRIPT_SUB);
2254 else fontAttrs.remove(TextAttribute.SUPERSCRIPT);
2255
2256 label.setFont(new Font(fontAttrs));
2257 }
2258
2259 Border selectedBorder;
2260 Border normalBorder;
2261 JCheckBox visibleChk;
2262 JLabel label;
2263 Map fontAttrs;
2264 Component spacer;
2265 }//class NodeRenderer extends JPanel implements TreeCellRenderer
2266 /**
2267 * Displays an entry in the right hand side tree.
2268 * <strong>Implementation Note:</strong>
2269 * This class overrides
2270 * <code>revalidate</code>,
2271 * <code>repaint</code>,
2272 * and
2273 * <code>firePropertyChange</code>
2274 * solely to improve performance.
2275 * If not overridden, these frequently called methods would execute code paths
2276 * that are unnecessary for a tree cell renderer.
2277 */
2278 class NodeRenderer1 extends LazyJPanel implements TreeCellRenderer{
2279
2280 public NodeRenderer1(){
2281 visibleChk = new JCheckBox("",false);
2282 visibleChk.setOpaque(false);
2283 visibleChk.setBorderPaintedFlat(true);
2284 textComponent = new JTextPane();
2285 selectedBorder = BorderFactory.createLineBorder(Color.blue, 1);
2286 normalBorder = BorderFactory.createEmptyBorder(1, 1, 1, 1);
2287 setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
2288 setOpaque(false);
2289 spacer = Box.createHorizontalStrut(3);
2290 }
2291
2292 public Component getTreeCellRendererComponent(JTree tree,
2293 Object value,
2294 boolean selected,
2295 boolean expanded,
2296 boolean leaf,
2297 int row,
2298 boolean hasFocus){
2299 removeAll();
2300 add(spacer);
2301
2302 int width = spacer.getWidth();
2303
2304 //the text pane needs to be sized for modelToView() to work
2305 textComponent.setSize(1000, 1000);
2306
2307 TypeData nData = (TypeData)
2308 ((DefaultMutableTreeNode)value).getUserObject();
2309 // javax.swing.text.Document doc = textComponent.getDocument();
2310
2311 if(nData != null){
2312 textComponent.setText(nData.getTitle());
2313 textComponent.selectAll();
2314 textComponent.setCharacterAttributes(nData.getAttributes(), false);
2315 textComponent.select(0, 0);
2316 // try{
2317 // doc.remove(0, doc.getLength());
2318 // doc.insertString(0, nData.getTitle(),
2319 // nData.getAttributes());
2320 // }catch(BadLocationException ble){
2321 // ble.printStackTrace();
2322 // }
2323
2324 if(nData.getType() != null) {
2325 visibleChk.setSelected(nData.getVisible());
2326 add(visibleChk);
2327 width += visibleChk.getMinimumSize().width;
2328 }
2329 }else{
2330 textComponent.setText(((value == null || value.toString() == null) ?
2331 "" : value.toString()));
2332 // try{
2333 // doc.remove(0, doc.getLength());
2334 // doc.insertString(0, value.toString(),
2335 // textComponent.getStyle("default"));
2336 // }catch(BadLocationException ble){
2337 // ble.printStackTrace();
2338 // }
2339 }
2340 setTextComponentSize(textComponent);
2341 add(textComponent);
2342 width += textComponent.getPreferredSize().width;
2343 if(selected) setBorder(selectedBorder);
2344 else setBorder(normalBorder);
2345 width += getInsets().left + getInsets().right;
2346 setPreferredSize(null);
2347 setPreferredSize(new Dimension(width, super.getPreferredSize().height));
2348 return this;
2349 }//public Component getTreeCellRendererComponent
2350
2351 protected void setTextComponentSize(JTextComponent comp){
2352 try{
2353 if(comp.getDocument() == null || comp.getDocument().getLength() <= 0){
2354 return;
2355 }
2356 int width = 0;
2357 Rectangle rect = comp.modelToView(0);
2358 int height = rect.height;
2359 int length = comp.getDocument().getLength();
2360 if(length > 0){
2361 Rectangle rect2 = comp.modelToView(length );
2362 if(rect2 != null){
2363 if(rect.x < rect2.x){
2364 //left to right
2365 width = rect2.x + rect2.width - rect.x;
2366 }else{
2367 //RtL
2368 width = rect.x +rect.width - rect2.x;
2369 }
2370 height = Math.max(height, rect2.height);
2371 }
2372 }
2373 Insets insets = comp.getInsets();
2374 Dimension dim = new Dimension(width + insets.left + insets.right + 5,
2375 height + insets.top + insets.bottom);
2376 comp.setPreferredSize(dim);
2377 }catch(BadLocationException ble){
2378 //this will work the next time around so it's safe to ignore it now
2379 }
2380 }
2381 Border selectedBorder;
2382 Border normalBorder;
2383 JCheckBox visibleChk;
2384 JTextPane textComponent;
2385 Component spacer;
2386 }//class NodeRenderer extends JPanel implements TreeCellRenderer
2387
2388 /**
2389 * Displays an entry in the right hand side tree.
2390 * <strong><a name="override">Implementation Note:</a></strong>
2391 * This class overrides
2392 * <code>revalidate</code>,
2393 * <code>repaint</code>,
2394 * and
2395 * <code>firePropertyChange</code>
2396 * solely to improve performance.
2397 * If not overridden, these frequently called methods would execute code paths
2398 * that are unnecessary for a tree cell renderer.
2399 */
2400 /*
2401 class NodeRenderer1 extends JPanel implements TreeCellRenderer{
2402
2403 public NodeRenderer1(){
2404 visibleChk = new JCheckBox("",false);
2405 visibleChk.setOpaque(false);
2406 typeComponent = new JTextPane();
2407 setComponent = new JTextPane();
2408 selectedBorder = BorderFactory.createLineBorder(Color.blue);
2409 normalBorder = BorderFactory.createEmptyBorder(1,1,1,1);
2410
2411 setPanel = new LazyJPanel();
2412 setPanel.setOpaque(false);
2413 setPanel.setLayout(new BoxLayout(setPanel, BoxLayout.X_AXIS));
2414 setPanel.add(setComponent);
2415 typePanel = new LazyJPanel();
2416 typePanel.setOpaque(false);
2417 typePanel.setLayout(new BoxLayout(typePanel, BoxLayout.X_AXIS));
2418 typePanel.add(visibleChk);
2419 typePanel.add(typeComponent);
2420 }
2421
2422 public Component getTreeCellRendererComponent(JTree tree,
2423 Object value,
2424 boolean selected,
2425 boolean expanded,
2426 boolean leaf,
2427 int row,
2428 boolean hasFocus){
2429 JComponent renderer = null;
2430 TypeData nData = (TypeData)
2431 ((DefaultMutableTreeNode)value).getUserObject();
2432 if(nData != null){
2433 if(nData.getType() != null) {
2434 visibleChk.setSelected(nData.getVisible());
2435 typeComponent.setSize(1000, 1000);
2436 javax.swing.text.Document doc = typeComponent.getDocument();
2437 try{
2438 doc.remove(0, doc.getLength());
2439 doc.insertString(0, nData.getTitle(), nData.getAttributes());
2440 }catch(BadLocationException ble){
2441 ble.printStackTrace();
2442 }
2443 setTextComponentSize(typeComponent);
2444 // typePanel.removeAll();
2445 // typePanel.add(visibleChk);
2446 // typePanel.add(typeComponent);
2447 renderer = typePanel;
2448 }else{
2449 setComponent.setSize(1000, 1000);
2450 javax.swing.text.Document doc = setComponent.getDocument();
2451 try{
2452 doc.remove(0, doc.getLength());
2453 doc.insertString(0, nData.getTitle(), nData.getAttributes());
2454 }catch(BadLocationException ble){
2455 ble.printStackTrace();
2456 }
2457 setTextComponentSize(setComponent);
2458 // setPanel.removeAll();
2459 // setPanel.add(setComponent);
2460 renderer = setPanel;
2461 }
2462 }else{
2463 setComponent.setSize(1000, 1000);
2464 javax.swing.text.Document doc = setComponent.getDocument();
2465 try{
2466 doc.remove(0, doc.getLength());
2467 doc.insertString(0, value.toString(), setComponent.getStyle("default"));
2468 }catch(BadLocationException ble){
2469 ble.printStackTrace();
2470 }
2471 setTextComponentSize(setComponent);
2472 // setPanel.removeAll();
2473 // setPanel.add(setComponent);
2474 renderer = setPanel;
2475 }
2476 if(selected) renderer.setBorder(selectedBorder);
2477 else renderer.setBorder(normalBorder);
2478 return renderer;
2479 }//public Component getTreeCellRendererComponent
2480
2481 protected void setTextComponentSize(JTextComponent comp){
2482 try{
2483 Rectangle rect = comp.modelToView(0);
2484 int length = comp.getDocument().getLength();
2485 if(length > 0){
2486 Rectangle rect2 = comp.modelToView(length - 1);
2487 if(rect2 != null){
2488 Out.pr("Rect2.x " + rect2.x);
2489 //this mutates rect
2490 rect = SwingUtilities.computeUnion(rect2.x, rect2.y, rect2.width,
2491 rect2.height, rect);
2492 Out.prln("Rect.width " + rect.width);
2493 }else{
2494 Out.prln("NULL size");
2495 }
2496 }
2497 Insets insets = comp.getInsets();
2498 Dimension dim = new Dimension(rect.width + insets.left + insets.right,
2499 rect.height + insets.top + insets.bottom);
2500 comp.setPreferredSize(dim);
2501 }catch(BadLocationException ble){
2502 ble.printStackTrace();
2503 }
2504 }
2505
2506 Border selectedBorder;
2507 Border normalBorder;
2508 JCheckBox visibleChk;
2509 JTextPane setComponent;
2510 JTextPane typeComponent;
2511 JPanel setPanel;
2512 JPanel typePanel;
2513 }//class NodeRenderer extends JPanel implements TreeCellRenderer
2514 */
2515 /**
2516 * Holds the GUI metadata for a given annotation type. An annotation type is
2517 * uniquely identified by the name of its AnnotationSet and the name of the
2518 * type.
2519 * For the default annotation set of a document (which has no name) the
2520 * "<Default>" value is used.
2521 * The GUI metadata contains, amongst other things, the style used for
2522 * highlighting the annotations of this type.
2523 * These styles are cascading styles (there is a relation of inheritance
2524 * between them) so the annotation type style inherits the characteristics
2525 * from the style associated with the annotation set it belongs to.
2526 *
2527 * For eficiency reasons there are some intermediary styles between a parent
2528 * and a child style that used for changing the display in one operation.
2529 */
2530 public class TypeData {
2531
2532 public TypeData(String set, String type, boolean visible){
2533 this.set = set;
2534 this.type = type;
2535 this.visible = visible;
2536 Map setMap = (Map)typeDataMap.get(set);
2537 if(setMap == null){
2538 setMap = new HashMap();
2539 typeDataMap.put(set, setMap);
2540 }
2541 if(type == null) {
2542 //this node represents a Set
2543 style = textPane.addStyle(set, textPane.getStyle("default"));
2544 } else {
2545 style = textPane.addStyle(set + "." + type, textPane.getStyle(set));
2546 StyleConstants.setBackground(style,
2547 colGenerator.getNextColor().brighter());
2548 //add an intermediary style that will be used attribute inheritance tricks
2549 middleStyle = visible ?
2550 textPane.addStyle("_" + set + "." + type, style) :
2551 textPane.addStyle("_" + set + "." + type,
2552 textPane.getStyle("default"));
2553 //add the style that will be used for the actual display
2554 actualStyle = textPane.addStyle("_" + set + "." + type + "_",
2555 textPane.getStyle("_" + set + "." + type));
2556 setMap.put(type, this);
2557 }
2558 }
2559
2560 public String getSet() { return set;}
2561
2562 public void setSet(String set) {this.set = set;}
2563
2564 public String getType() {return type;}
2565
2566 public String getTitle() {return (type == null) ? set + " annotations" :
2567 type;}
2568 public boolean getVisible() {return visible;}
2569
2570 public void setVisible(boolean isVisible) {
2571 if(this.visible == isVisible) return;
2572 this.visible = isVisible;
2573 //this is most likely called from the SWING thread so we want to get
2574 //out of here as quickly as possible. We'll start a new thread that will
2575 //do all that needs doing
2576 Runnable runnable = new Runnable() {
2577 public void run() {
2578 if(visible) {
2579 //make the corresponding range visible
2580 //update the annotations table
2581 synchronized(data) {
2582 range = new Range(set, type, data.size(),
2583 data.size() + annotations.size());
2584 ranges.add(range);
2585 data.addAll(annotations);
2586 SwingUtilities.invokeLater(new Runnable() {
2587 public void run() {
2588 annotationsTableModel.fireTableDataChanged();
2589 }
2590 });
2591 }
2592
2593 //update the text display
2594 // Style tempStyle = textPane.getStyle("_" + set + "." + type);
2595 middleStyle.setResolveParent(style);
2596 showHighlights(annotations, actualStyle);
2597 } else {
2598 //hide the corresponding range
2599 //update the annotations table
2600 Collections.sort(ranges);
2601 Iterator rangesIter = ranges.iterator();
2602 while(rangesIter.hasNext()) {
2603 //find my range
2604 Range aRange = (Range)rangesIter.next();
2605 if(aRange == range){
2606 rangesIter.remove();
2607 int size = range.end - range.start;
2608 //remove the elements from Data
2609 data.subList(range.start, range.end).clear();
2610 //shift back all the remaining ranges
2611 while(rangesIter.hasNext()) {
2612 aRange = (Range)rangesIter.next();
2613 aRange.start -= size;
2614 aRange.end -= size;
2615 }
2616 }
2617 }
2618 range = null;
2619 SwingUtilities.invokeLater(new Runnable() {
2620 public void run() {
2621 annotationsTableModel.fireTableDataChanged();
2622 }
2623 });
2624 //update the text display
2625 // Style middleStyle = textPane.getStyle("_" + set + "." + type);
2626 middleStyle.setResolveParent(textPane.getStyle("default"));
2627 }//if(visible)
2628 }//public void run()
2629 };//Runnable runnable = new Runnable()
2630 Thread thread = new Thread(Thread.currentThread().getThreadGroup(),
2631 runnable,
2632 "AnnotationEditor4");
2633 thread.setPriority(Thread.MIN_PRIORITY);
2634 thread.start();
2635 }//public void setVisible(boolean isVisible)
2636
2637 public AttributeSet getAttributes() { return style;}
2638
2639 private AttributeSet getActualStyle() { return actualStyle;}
2640
2641 public void setAttributes(AttributeSet newAttributes) {
2642 style.removeAttributes(style.copyAttributes());
2643 style.addAttributes(newAttributes);
2644 }
2645
2646
2647 public void setAnnotations(Set as) {
2648 this.annotations = as;
2649 }
2650
2651 public Set getAnnotations() {
2652 return annotations;
2653 }
2654
2655 public void setNode(DefaultMutableTreeNode node){
2656 this.node = node;
2657 }
2658
2659 public DefaultMutableTreeNode getNode(){
2660 return node;
2661 }
2662
2663 public String toString() {return getTitle();}
2664
2665 private String set;
2666 private String type;
2667 private boolean visible;
2668 /**
2669 * The style used for annotations of this type
2670 */
2671 private Style style;
2672
2673 /**
2674 * Used internally for attribute inheritance tricks.
2675 */
2676 private Style middleStyle;
2677
2678 /**
2679 * The style actually used to affect the text.
2680 */
2681 private Style actualStyle;
2682 private Set annotations = null;
2683 private Range range = null;
2684
2685 /** The node that represents this set/type in the types tree*/
2686 private DefaultMutableTreeNode node = null;
2687 }//class TypeData
2688
2689
2690 /**
2691 * Describes a range in the {@link #data} structure. A range is a bunch of
2692 * annotations of the same type belonging to the same annotation set that
2693 * are contiguous in the {@link #data} structure.
2694 */
2695 class Range implements Comparable {
2696 public Range(String setName, String type, int start, int end) {
2697 this.setName = setName;
2698 this.type = type;
2699 this.start = start;
2700 this.end = end;
2701 }
2702
2703 public String toString() {
2704 return setName + ", " + type + " (" + start + ", " + end + ")";
2705 }
2706
2707 public int compareTo(Object other) {
2708 if(other instanceof Range) return start - ((Range)other).start;
2709 else throw new ClassCastException("Can't compare a " +
2710 other.getClass() + " to a " +
2711 getClass() + "!");
2712 }
2713
2714 String setName;
2715 String type;
2716 int start;
2717 int end;
2718 }//class Range
2719
2720
2721 /**
2722 * All the events from the document or its annotation sets are handled by
2723 * this inner class.
2724 */
2725 class EventsHandler implements gate.event.DocumentListener,
2726 AnnotationSetListener{
2727
2728 public void annotationSetAdded(gate.event.DocumentEvent e) {
2729 String setName = e.getAnnotationSetName();
2730 AnnotationSet as = (setName == null ? document.getAnnotations() :
2731 document.getAnnotations(setName));
2732
2733 as.addAnnotationSetListener(this);
2734 if(setName == null) setName = "Default";
2735 TypeData setData = new TypeData(setName, null, false);
2736 setData.setAnnotations(as);
2737
2738 SwingUtilities.invokeLater(new NodeAdder(setData));
2739
2740 ArrayList typesLst = new ArrayList(as.getAllTypes());
2741 Collections.sort(typesLst);
2742
2743 Iterator typesIter = typesLst.iterator();
2744 while(typesIter.hasNext()){
2745 String type = (String)typesIter.next();
2746 TypeData typeData = new TypeData(setName, type, false);
2747 AnnotationSet sameType = as.get(type);
2748 typeData.setAnnotations(sameType);
2749
2750 SwingUtilities.invokeLater(new NodeAdder(typeData));
2751 }
2752 }
2753
2754 public void annotationSetRemoved(gate.event.DocumentEvent e) {
2755 //we access the GUI a lot here so we'll do everything from the
2756 //Swing thread
2757 SwingUtilities.invokeLater(
2758 new SetRemovedOperation(e.getAnnotationSetName()));
2759 }//public void annotationSetRemoved(gate.event.DocumentEvent e)
2760
2761 /**Called when the content of the document has changed through an edit
2762 * operation.
2763 */
2764 public void contentEdited(DocumentEvent e){
2765 //ignore
2766 }
2767
2768 public void annotationAdded(AnnotationSetEvent e) {
2769 AnnotationSet set = (AnnotationSet)e.getSource();
2770 String setName = set.getName();
2771 if(setName == null) setName = "Default";
2772 Annotation ann = e.getAnnotation();
2773 String type = ann.getType();
2774 TypeData tData = getTypeData(setName, type);
2775
2776 boolean tableChanged = false;
2777 if(tData != null){
2778 // tData.annotations.add(ann);
2779 if(tData.getVisible()){
2780 //1) update the table
2781 data.add(tData.range.end, ann);
2782 tData.range.end++;
2783 Iterator rangesIter = ranges.
2784 subList(
2785 ranges.indexOf(tData.range) + 1,
2786 ranges.size()).
2787 iterator();
2788 while(rangesIter.hasNext()){
2789 Range aRange = (Range) rangesIter.next();
2790 aRange.start++;
2791 aRange.end++;
2792 }//while(rangesIter.hasNext())
2793 tableChanged = true;
2794
2795 //2) update the text
2796 SwingUtilities.invokeLater(
2797 new HihglightsShower(ann,
2798 textPane.getStyle(
2799 "_" + setName + "." +
2800 type + "_")));
2801 }//if(tData.getVisible())
2802 } else {
2803 //new type
2804 Map setMap = (Map)typeDataMap.get(setName);
2805 if(setMap == null){
2806 setMap = new HashMap();
2807 typeDataMap.put(setName, setMap);
2808 }
2809 tData = new TypeData(setName, type, false);
2810 tData.setAnnotations(set.get(type));
2811 setMap.put(type, tData);
2812 SwingUtilities.invokeLater(new NodeAdder(tData));
2813
2814 }//new type
2815
2816 if(tableChanged){
2817 SwingUtilities.invokeLater(new Runnable() {
2818 public void run(){
2819 if(annotationsTableModel != null){
2820 annotationsTableModel.fireTableDataChanged();
2821 }
2822 }
2823 });
2824 }//if(tableChanged)
2825 }//public void annotationAdded(AnnotationSetEvent e)
2826
2827 public void annotationRemoved(AnnotationSetEvent e){
2828 AnnotationSet set = (AnnotationSet)e.getSource();
2829 String setName = set.getName();
2830 if(setName == null) setName = "Default";
2831 Annotation ann = e.getAnnotation();
2832 String type = ann.getType();
2833 TypeData tData = getTypeData(setName, type);
2834 boolean tableChanged = false;
2835
2836 if(tData != null){
2837 // tData.annotations.remove(ann);
2838 if(tData.getVisible()){
2839 //1) update the annotations table
2840 data.remove(ann);
2841 //shorten the range conatining the annotation
2842 tData.range.end--;
2843 //shift all the remaining ranges
2844 Iterator rangesIter = ranges.
2845 subList(ranges.indexOf(tData.range) + 1,
2846 ranges.size()).
2847 iterator();
2848 while(rangesIter.hasNext()){
2849 Range aRange = (Range) rangesIter.next();
2850 aRange.start--;
2851 aRange.end--;
2852 }//while(rangesIter.hasNext())
2853 tableChanged = true;
2854 }//if(tData.getVisible())
2855 //update the text -> hide the highlight
2856 //required even if not visible
2857 SwingUtilities.invokeLater(new HighlightsRemover(ann));
2858
2859 //if this was the last annotation of this type remove the type node
2860 if((tData.annotations.size() == 1 &&
2861 tData.annotations.iterator().next() == ann) ||
2862 tData.annotations.size() == 0){
2863 //no more annotations of this type -> delete the node
2864 SwingUtilities.invokeLater(new NodeRemover(tData));
2865 //remove the data for this type
2866 Map setMap = (Map)typeDataMap.get(setName);
2867 setMap.remove(tData.getType());
2868 }//if(tData.getAnnotations().isEmpty())
2869 }//if(tData != null)
2870
2871 if(tableChanged){
2872 SwingUtilities.invokeLater(new Runnable() {
2873 public void run(){
2874 if(annotationsTableModel != null){
2875 annotationsTableModel.fireTableDataChanged();
2876 }
2877 }
2878 });
2879 }//if(tableChanged)
2880 }//public void annotationRemoved(AnnotationSetEvent e)
2881
2882 /**
2883 * Helper class that removes one highlight corresponding to an annotation.
2884 */
2885 class HighlightsRemover implements Runnable{
2886 HighlightsRemover(Annotation ann){
2887 this.ann = ann;
2888 }
2889 public void run(){
2890 int selStart = textPane.getSelectionStart();
2891 int selEnd = textPane.getSelectionEnd();
2892 textPane.select(ann.getStartNode().getOffset().intValue(),
2893 ann.getEndNode().getOffset().intValue());
2894 textPane.setCharacterAttributes(
2895 textPane.getStyle("default"), true);
2896 textPane.select(selStart, selEnd);
2897 }
2898 Annotation ann;
2899 }//class HihglightsRemover implements Runnable
2900
2901 /**
2902 * Helper class that highlights a given annotation with the specified style.
2903 */
2904 class HihglightsShower implements Runnable{
2905 HihglightsShower(Annotation ann, Style style){
2906 this.ann = ann;
2907 this.style = style;
2908 }
2909 public void run(){
2910 textPane.select(ann.getStartNode().getOffset().intValue(),
2911 ann.getEndNode().getOffset().intValue());
2912 textPane.setCharacterAttributes(style, true);
2913 }
2914 Annotation ann;
2915 Style style;
2916 }//class HihglightsRemover implements Runnable
2917
2918 /**
2919 * Helper class that removes one node from the types tree.
2920 */
2921 class NodeRemover implements Runnable{
2922 NodeRemover(TypeData tData){
2923 this.tData = tData;
2924 }
2925 public void run(){
2926 DefaultMutableTreeNode node = tData.getNode();
2927 if(node != null){
2928 stylesTreeModel.removeNodeFromParent(tData.getNode());
2929 }else{
2930 Err.prln("Could not find node for " + tData.set + "->" + tData.type);
2931 }
2932 }
2933 TypeData tData;
2934 }//class NodeRemover implements Runnable
2935
2936 /**
2937 * Helper class that adds a specified tree node
2938 */
2939 class NodeAdder implements Runnable{
2940 NodeAdder(TypeData tData){
2941 this.tData = tData;
2942 }
2943 public void run(){
2944 //create the new node
2945 DefaultMutableTreeNode newNode =
2946 new DefaultMutableTreeNode(tData, tData.getType() == null);
2947 tData.setNode(newNode);
2948 //find its parent
2949 DefaultMutableTreeNode node = null;
2950 if(tData.getType() == null){
2951 //set node
2952 node = (DefaultMutableTreeNode)stylesTreeRoot;
2953 //System.out.println("Set node " + tData.getSet());
2954 }else{
2955 //System.out.println("Type node " + tData.getSet() + ":" + tData.getType());
2956
2957 //the document should at least have the default annotation set
2958 //if it doesn't, then something's fishy -> return;
2959 if(((DefaultMutableTreeNode)stylesTreeRoot).getChildCount() == 0)
2960 return;
2961 node = (DefaultMutableTreeNode)
2962 ((DefaultMutableTreeNode)stylesTreeRoot).getFirstChild();
2963 while(node != null &&
2964 !((TypeData)node.getUserObject()).getSet().equals(tData.getSet()))
2965 node = node.getNextSibling();
2966 }
2967
2968 //we have to add typeNode to node
2969 //find the right place
2970 int i = 0;
2971 if(tData.getType() == null){
2972 while (i < node.getChildCount() &&
2973 ((TypeData)
2974 ((DefaultMutableTreeNode)node.getChildAt(i)).
2975 getUserObject()
2976 ).getSet().compareTo(tData.getSet())<0) i++;
2977 }else{
2978 while (i < node.getChildCount() &&
2979 ((TypeData)
2980 ((DefaultMutableTreeNode)node.getChildAt(i)).
2981 getUserObject()
2982 ).getType().compareTo(tData.getType())<0) i++;
2983 }
2984
2985 //insert it!
2986 stylesTreeModel.insertNodeInto(newNode, node, i);
2987
2988 if(tData.getType() == null){
2989 //set node, expand it!
2990 stylesTree.expandPath(new TreePath(new Object[]{stylesTreeRoot,
2991 newNode}));
2992 }
2993 }
2994
2995 TypeData tData;
2996 }//class NodeAdder implements Runnable
2997
2998 /**
2999 * Helper class that handles the removal of a named annotation set.
3000 * This runnable should only be called from the Swing thread
3001 */
3002 class SetRemovedOperation implements Runnable{
3003 SetRemovedOperation(String setName){
3004 this.setName = setName;
3005 }
3006
3007 public void run(){
3008 //find the set node
3009 Enumeration setNodesEnum = stylesTreeRoot.children();
3010 DefaultMutableTreeNode setNode = null;
3011 boolean done = false;
3012 while(!done && setNodesEnum.hasMoreElements()){
3013 setNode = (DefaultMutableTreeNode)setNodesEnum.nextElement();
3014 done = ((TypeData)setNode.getUserObject()).getSet().equals(setName);
3015 }
3016
3017 if(!((TypeData)setNode.getUserObject()).getSet().equals(setName)){
3018 throw new GateRuntimeException(
3019 "Could not find the tree node for the " + setName +
3020 " annotation set!");
3021 }
3022
3023 boolean tableChanged = false;
3024 Enumeration typeNodesEnum = setNode.children();
3025 while(typeNodesEnum.hasMoreElements()){
3026 DefaultMutableTreeNode typeNode =
3027 (DefaultMutableTreeNode)typeNodesEnum.nextElement();
3028 TypeData tData = (TypeData)typeNode.getUserObject();
3029 if(tData.getVisible()){
3030 //1) update the annotations table
3031 data.subList(tData.range.start, tData.range.end).clear();
3032 //remove the range
3033 int delta = tData.range.end - tData.range.start;
3034 //1a)first shift all following ranges
3035 Iterator rangesIter = ranges.
3036 subList(ranges.indexOf(tData.range) + 1,
3037 ranges.size()).
3038 iterator();
3039 while(rangesIter.hasNext()){
3040 Range aRange = (Range) rangesIter.next();
3041 aRange.start -= delta;
3042 aRange.end -= delta;
3043 }//while(rangesIter.hasNext())
3044 //1b)now remove the range
3045 ranges.remove(tData.range);
3046 tableChanged = true;
3047
3048 //2)update the text
3049 //hide the highlights
3050
3051 Iterator annIter = tData.getAnnotations().iterator();
3052 while(annIter.hasNext()){
3053 Annotation ann = (Annotation)annIter.next();
3054 new HighlightsRemover(ann).run();
3055 }//while(annIter.hasNext())
3056 }//if(tData.getVisible())
3057 }//while(typeNodesEnum.hasMoreElements())
3058
3059 if(tableChanged){
3060 if(annotationsTableModel != null){
3061 annotationsTableModel.fireTableDataChanged();
3062 }
3063 }//if(tableChanged)
3064
3065 //remove the node for the set
3066 typeDataMap.remove(setName);
3067 stylesTreeModel.removeNodeFromParent(setNode);
3068 }//public void run()
3069
3070 String setName;
3071 }
3072
3073 }//class EventsHandler
3074
3075 /**
3076 * Listens for updates from the text editor and updates the GATE document
3077 * accordingly
3078 */
3079 class SwingDocumentListener implements javax.swing.event.DocumentListener{
3080 public void insertUpdate(final javax.swing.event.DocumentEvent e) {
3081 try{
3082 document.edit(new Long(e.getOffset()), new Long(e.getOffset()),
3083 new DocumentContentImpl(
3084 e.getDocument().getText(e.getOffset(), e.getLength())));
3085 SwingUtilities.invokeLater(new Runnable(){
3086 public void run(){
3087 annotationsTable.repaint();
3088 repairHighlights(e.getOffset(), e.getOffset() + e.getLength());
3089 }
3090 });
3091 updateBlinks();
3092 }catch(BadLocationException ble){
3093 ble.printStackTrace(Err.getPrintWriter());
3094 }catch(InvalidOffsetException ioe){
3095 ioe.printStackTrace(Err.getPrintWriter());
3096 }
3097 }
3098
3099 public void removeUpdate(javax.swing.event.DocumentEvent e) {
3100 try{
3101 document.edit(new Long(e.getOffset()),
3102 new Long(e.getOffset() + e.getLength()),
3103 new DocumentContentImpl(""));
3104 SwingUtilities.invokeLater(new Runnable(){
3105 public void run(){
3106 annotationsTable.repaint();
3107 }
3108 });
3109 updateBlinks();
3110 }catch(InvalidOffsetException ioe){
3111 ioe.printStackTrace(Err.getPrintWriter());
3112 }
3113 }
3114
3115 public void changedUpdate(javax.swing.event.DocumentEvent e) {
3116 //some attributes changed: we don't care about that
3117 }
3118 /**
3119 * Restores the blinking selection if any
3120 */
3121 protected void updateBlinks(){
3122 int[] rows = annotationsTable.getSelectedRows();
3123 if(rows != null && rows.length > 0){
3124 selectionHighlighter.removeAllHighlights();
3125 for(int i = 0; i < rows.length; i++){
3126 int start = ((Long)annotationsTable.getModel().
3127 getValueAt(rows[i], 2)
3128 ).intValue();
3129 int end = ((Long)annotationsTable.getModel().
3130 getValueAt(rows[i], 3)
3131 ).intValue();
3132
3133 // compute correction for new line breaks in long lines
3134 start += longLinesCorrection(start);
3135 end += longLinesCorrection(end);
3136
3137 //start blinking the annotation
3138 try{
3139 synchronized (selectionHighlighter){
3140 selectionHighlighter.addHighlight(start, end,
3141 DefaultHighlighter.DefaultPainter);
3142 }
3143 }catch(BadLocationException ble){
3144 throw new GateRuntimeException(ble.toString());
3145 }
3146 }//for(int i = 0; i < rows.length; i++)
3147
3148 // annotationsTable.clearSelection();
3149 // for (int i = 0; i < rows.length; i++) {
3150 // annotationsTable.addRowSelectionInterval(rows[i], rows[i]);
3151 // }
3152 }
3153
3154 }
3155 }//class SwingDocumentListener implements javax.swing.event.DocumentListener
3156
3157 /**
3158 * This class handles the blinking for the selected annotations in the
3159 * text display.
3160 */
3161 class SelectionBlinker implements Runnable{
3162 public void run(){
3163 synchronized(selectionHighlighter){
3164 highlights = selectionHighlighter.getHighlights();
3165 }
3166
3167
3168 while(highlights != null && highlights.length > 0){
3169 SwingUtilities.invokeLater(new Runnable(){
3170 public void run(){
3171 showHighlights();
3172 }
3173 });
3174 try{
3175 Thread.sleep(400);
3176 }catch(InterruptedException ie){
3177 ie.printStackTrace(Err.getPrintWriter());
3178 }
3179 SwingUtilities.invokeLater(new Runnable(){
3180 public void run(){
3181 hideHighlights();
3182 }
3183 });
3184
3185 try{
3186 Thread.sleep(600);
3187 }catch(InterruptedException ie){
3188 ie.printStackTrace(Err.getPrintWriter());
3189 }
3190 synchronized(selectionHighlighter){
3191 highlights = selectionHighlighter.getHighlights();
3192 }
3193 }//while we have highlights
3194 //no more highlights; stop the thread by exiting run();
3195 synchronized(selectionHighlighter){
3196 thread = null;
3197 }
3198 }//run()
3199
3200 /**
3201 * If there is no running thread then starts one and stores it in
3202 * the <tt>thread</tt> member.
3203 */
3204 public synchronized void testAndStart(){
3205 synchronized(selectionHighlighter){
3206 if(thread == null){
3207 thread = new Thread(Thread.currentThread().getThreadGroup(),
3208 this, "AnnotationEditor2");
3209 thread.setPriority(Thread.MIN_PRIORITY);
3210 thread.start();
3211 }
3212 }
3213 }
3214
3215 protected void showHighlights(){
3216 actualHighlights.clear();
3217 try{
3218 for(int i = 0; i < highlights.length; i++){
3219 actualHighlights.add(highlighter.addHighlight(
3220 highlights[i].getStartOffset(),
3221 highlights[i].getEndOffset(),
3222 highlights[i].getPainter()));
3223 }
3224 }catch(BadLocationException ble){
3225 ble.printStackTrace(Err.getPrintWriter());
3226 }
3227 }
3228
3229 protected void hideHighlights(){
3230 Iterator hIter = actualHighlights.iterator();
3231 while(hIter.hasNext()) highlighter.removeHighlight(hIter.next());
3232 }
3233
3234 ArrayList actualHighlights = new ArrayList();
3235 Thread thread;
3236 Highlighter.Highlight[] highlights;
3237 }//class SelectionBlinker implements Runnable
3238
3239 /**
3240 * Fixes the <a
3241 * href="http://developer.java.sun.com/developer/bugParade/bugs/4406598.html">
3242 * 4406598 bug</a> in swing text components.
3243 * The bug consists in the fact that the Background attribute is ignored by
3244 * the text component whent it is defined in a style from which the current
3245 * style inherits.
3246 */
3247 public class CustomLabelView extends javax.swing.text.LabelView {
3248 public CustomLabelView(Element elem) {
3249 super(elem);
3250 }
3251
3252 public Color getBackground() {
3253 AttributeSet attr = getAttributes();
3254 if (attr != null) {
3255 javax.swing.text.Document d = super.getDocument();
3256 if (d instanceof StyledDocument){
3257 StyledDocument doc = (StyledDocument) d;
3258 return doc.getBackground(attr);
3259 }else{
3260 return null;
3261 }
3262 }
3263 return null;
3264 }
3265 }
3266
3267 /**
3268 * The popup menu items used to select annotations at right click.
3269 * Apart from the normal {@link javax.swing.JMenuItem} behaviour, this menu
3270 * item also highlits the annotation which it would select if pressed.
3271 */
3272 protected class HighlightAnnotationMenu extends JMenu {
3273 public HighlightAnnotationMenu(Annotation ann, AnnotationSet aSet) {
3274 super(ann.getType());
3275 setToolTipText("<html><b>Features:</b><br>" +
3276 (ann.getFeatures() == null ? "" :
3277 ann.getFeatures().toString()) + "</html>");
3278 this.annotation = ann;
3279 this.set = aSet;
3280 this.setName = (set.getName() == null) ? "Default" : set.getName();
3281 start = ann.getStartNode().getOffset().intValue();
3282 end = ann.getEndNode().getOffset().intValue();
3283 this.addMouseListener(new MouseAdapter() {
3284 public void mouseEntered(MouseEvent e) {
3285 try {
3286 highlight = highlighter.addHighlight(start, end,
3287 DefaultHighlighter.DefaultPainter);
3288 }catch(BadLocationException ble){
3289 throw new GateRuntimeException(ble.toString());
3290 }
3291 }
3292
3293 public void mouseExited(MouseEvent e) {
3294 if(highlight != null){
3295 highlighter.removeHighlight(highlight);
3296 highlight = null;
3297 }
3298 }
3299 });
3300
3301 this.add(new AbstractAction(){
3302 {
3303 putValue(NAME, "Select");
3304 }
3305 public void actionPerformed(ActionEvent e) {
3306 Runnable runnable = new Runnable(){
3307 public void run(){
3308 if(highlight != null){
3309 highlighter.removeHighlight(highlight);
3310 highlight = null;
3311 }
3312 selectAnnotation(setName, annotation);
3313 }
3314 };
3315 Thread thread = new Thread(Thread.currentThread().getThreadGroup(),
3316 runnable,
3317 "AnnotationEditor5");
3318 thread.start();
3319 }
3320 });
3321
3322 this.add(new AbstractAction(){
3323 {
3324 putValue(NAME, "Delete");
3325 }
3326 public void actionPerformed(ActionEvent e) {
3327 Runnable runnable = new Runnable(){
3328 public void run(){
3329 if(highlight != null){
3330 highlighter.removeHighlight(highlight);
3331 highlight = null;
3332 }
3333 set.remove(annotation);
3334 }
3335 };
3336 Thread thread = new Thread(Thread.currentThread().getThreadGroup(),
3337 runnable,
3338 "AnnotationEditor5");
3339 thread.start();
3340 }
3341 });
3342
3343 }
3344
3345 int start;
3346 int end;
3347 AnnotationSet set;
3348 String setName;
3349 Annotation annotation;
3350 Object highlight;
3351 }
3352
3353
3354 protected class DeleteSelectedAnnotationsAction extends AbstractAction {
3355 public DeleteSelectedAnnotationsAction(JComponent source){
3356 super("Delete selected annotations");
3357 this.source = source;
3358 }
3359
3360 public void actionPerformed(ActionEvent evt){
3361 if(source == annotationsTable){
3362 //collect the list of annotations to be removed
3363 //maps from set name to list of annotations to be removed
3364 Map annotationsBySet = new HashMap();
3365 int[] rows = annotationsTable.getSelectedRows();
3366 String setName;
3367 for(int i = 0; i < rows.length; i++){
3368 int row = rows[i];
3369 //find the annotation
3370 Annotation ann = (Annotation)annotationsTable.
3371 getModel().getValueAt(row, -1);
3372 //find the annotation set
3373 setName = (String)annotationsTable.getModel().
3374 getValueAt(row, 1);
3375 java.util.List existingList = (java.util.List)
3376 annotationsBySet.get(setName);
3377 if(existingList == null){
3378 existingList = new ArrayList();
3379 annotationsBySet.put(setName, existingList);
3380 }
3381 existingList.add(ann);
3382 }//for(int i = 0; i < rows.length; i++)
3383 //remove the collected annotations
3384 Iterator setsIter = annotationsBySet.keySet().iterator();
3385 while(setsIter.hasNext()){
3386 setName = (String)setsIter.next();
3387 AnnotationSet set = setName.equals("Default")?
3388 document.getAnnotations() :
3389 document.getAnnotations(setName);
3390 set.removeAll((java.util.List)annotationsBySet.get(setName));
3391 }//while(setsIter.hasNext())
3392 }else if(source == stylesTree){
3393 TreePath[] paths = stylesTree.getSelectionPaths();
3394 for(int i = 0; i < paths.length; i++){
3395 TypeData tData = (TypeData)((DefaultMutableTreeNode)
3396 paths[i].getLastPathComponent()).getUserObject();
3397 String setName = tData.getSet();
3398 if(tData.getType() == null){
3399 //set node
3400 if(setName.equals("Default")){
3401 JOptionPane.showMessageDialog(
3402 DocumentEditor.this,
3403 "The default annotation set cannot be deleted!\n" +
3404 "It will only be cleared...",
3405 "GATE", JOptionPane.ERROR_MESSAGE);
3406 document.getAnnotations().clear();
3407 }else{
3408 document.removeAnnotationSet(setName);
3409 }
3410 }else{
3411 //type node
3412 if(!setName.equals("Default") &&
3413 !document.getNamedAnnotationSets().containsKey(setName)){
3414 //the set for this type has already been removed completely
3415 //nothing more do (that's nice :) )
3416 return;
3417 }
3418 AnnotationSet set = setName.equals("Default") ?
3419 document.getAnnotations() :
3420 document.getAnnotations(setName);
3421 if(set != null){
3422 AnnotationSet subset = set.get(tData.getType());
3423 if(subset != null) set.removeAll(new ArrayList(subset));
3424 }//if(set != null)
3425 }//type node
3426 }//for(int i = 0; i < paths.length; i++)
3427 }else if(source == corefTree){
3428 TreePath[] paths = corefTree.getSelectionPaths();
3429 for(int i = 0; i < paths.length; i++){
3430 CorefData cData = (CorefData)((DefaultMutableTreeNode)
3431 paths[i].getLastPathComponent()).getUserObject();
3432 class CorefClearer implements Runnable{
3433 CorefClearer(CorefData cData){
3434 this.cData = cData;
3435 }
3436 public void run(){
3437 cData.removeAnnotations();
3438 }
3439 CorefData cData;
3440 }
3441 Thread thread = new Thread(new CorefClearer(cData));
3442 thread.setPriority(Thread.MIN_PRIORITY);
3443 thread.start();
3444 }
3445 }
3446 }//public void actionPerformed(ActionEvent evt)
3447 JComponent source;
3448 }//protected class DeleteSelectedAnnotationsAction
3449
3450 protected class SearchAction extends AbstractAction {
3451 public SearchAction(){
3452 super("Search");
3453 putValue(SHORT_DESCRIPTION, "Search within the text");
3454 putValue(SMALL_ICON, MainFrame.getIcon("search"));
3455 }
3456
3457 public void actionPerformed(ActionEvent evt){
3458 if(searchDialog == null){
3459 Window parent = SwingUtilities.getWindowAncestor(DocumentEditor.this);
3460 searchDialog = (parent instanceof Dialog) ?
3461 new SearchDialog((Dialog)parent) :
3462 new SearchDialog((Frame)parent);
3463 searchDialog.pack();
3464 searchDialog.setLocationRelativeTo(DocumentEditor.this);
3465 searchDialog.setResizable(false);
3466 MainFrame.getGuiRoots().add(searchDialog);
3467 }
3468
3469 if(searchDialog.isVisible()){
3470 searchDialog.toFront();
3471 }else{
3472 searchDialog.setVisible(true);
3473 }
3474 }
3475 }
3476
3477 protected class SearchDialog extends JDialog{
3478 SearchDialog(Frame owner){
3479 super(owner, false);
3480 setTitle( "Find in \"" + document.getName() + "\"");
3481 initLocalData();
3482 initGuiComponents();
3483 initListeners();
3484 }
3485
3486 SearchDialog(Dialog owner){
3487 super(owner, false);
3488 setTitle("Find in \"" + document.getName() + "\"");
3489 initLocalData();
3490 initGuiComponents();
3491 initListeners();
3492 }
3493 protected void initLocalData(){
3494 pattern = null;
3495 nextMatchStartsFrom = 0;
3496 content = document.getContent().toString();
3497
3498 findFirstAction = new AbstractAction("Find first"){
3499 {
3500 putValue(SHORT_DESCRIPTION, "Finds first match");
3501 }
3502
3503 public void actionPerformed(ActionEvent evt){
3504 //needed to create the right RE
3505 refresh();
3506 //remove selection
3507 textPane.setCaretPosition(textPane.getCaretPosition());
3508 boolean found = false;
3509 int start = nextMatchStartsFrom - 1;
3510 int end = -1;
3511
3512 Matcher matcher = pattern.matcher(content);
3513 while (matcher.find() && (found == false))
3514 {
3515 start = matcher.start();
3516 end = matcher.end();
3517 if (wholeWordsChk.isSelected())
3518 {
3519 //validate the result
3520 found = (start == 0 || !Character.isLetterOrDigit(content.charAt(start - 1)))
3521 && (end == content.length() || !Character.isLetterOrDigit(content.charAt(end)));
3522 }
3523 else
3524 found = true;
3525 }
3526
3527 if (found)
3528 {
3529 nextMatchStartsFrom = start + 1;
3530 //display the result
3531 SwingUtilities.getWindowAncestor(textPane).requestFocus();
3532 textPane.requestFocus();
3533 textPane.setCaretPosition(start);
3534 textPane.moveCaretPosition(end);
3535 }
3536 else
3537 {
3538 JOptionPane.showMessageDialog(searchDialog, "String not found!", "GATE", JOptionPane.INFORMATION_MESSAGE);
3539 }
3540 }
3541 };
3542
3543
3544 findNextAction = new AbstractAction("Find next"){
3545 {
3546 putValue(SHORT_DESCRIPTION, "Finds next match");
3547 }
3548 public void actionPerformed(ActionEvent evt){
3549 //needed to create the right RE
3550 refresh();
3551 //remove selection
3552 textPane.setCaretPosition(textPane.getCaretPosition());
3553 boolean found = false;
3554 int start = nextMatchStartsFrom - 1;
3555 int end = -1;
3556
3557 Matcher matcher = pattern.matcher(content);
3558 while (matcher.find() && (found == false))
3559 {
3560 start = matcher.start();
3561 end = matcher.end();
3562 if (wholeWordsChk.isSelected())
3563 {
3564 //validate the result
3565 found = (start == 0 || !Character.isLetterOrDigit(content.charAt(start - 1)))
3566 && (end == content.length() || !Character.isLetterOrDigit(content.charAt(end)));
3567 }
3568 else
3569 found = true;
3570 }
3571
3572 if (found)
3573 {
3574 nextMatchStartsFrom = start + 1;
3575 //display the result
3576 SwingUtilities.getWindowAncestor(textPane).requestFocus();
3577 textPane.requestFocus();
3578 textPane.setCaretPosition(start);
3579 textPane.moveCaretPosition(end);
3580 }
3581 else
3582 {
3583 JOptionPane.showMessageDialog(searchDialog, "String not found!", "GATE", JOptionPane.INFORMATION_MESSAGE);
3584 }
3585 }
3586 };
3587
3588 cancelAction = new AbstractAction("Cancel"){
3589 {
3590 putValue(SHORT_DESCRIPTION, "Cancel");
3591 }
3592 public void actionPerformed(ActionEvent evt){
3593 searchDialog.setVisible(false);
3594 }
3595 };
3596
3597 }
3598
3599
3600 protected void initGuiComponents(){
3601 getContentPane().setLayout(new BoxLayout(getContentPane(),
3602 BoxLayout.Y_AXIS));
3603
3604 getContentPane().add(Box.createVerticalStrut(5));
3605 Box hBox = Box.createHorizontalBox();
3606 hBox.add(Box.createHorizontalStrut(5));
3607 hBox.add(new JLabel("Find what:"));
3608 hBox.add(Box.createHorizontalStrut(5));
3609 hBox.add(patternTextField = new JTextField(20));
3610 hBox.add(Box.createHorizontalStrut(5));
3611 hBox.add(Box.createHorizontalGlue());
3612 getContentPane().add(hBox);
3613
3614 getContentPane().add(Box.createVerticalStrut(5));
3615 hBox = Box.createHorizontalBox();
3616 hBox.add(Box.createHorizontalStrut(5));
3617 hBox.add(ignoreCaseChk = new JCheckBox("Ignore case", false));
3618 hBox.add(Box.createHorizontalStrut(5));
3619 hBox.add(wholeWordsChk = new JCheckBox("Whole words only", false));
3620 hBox.add(Box.createHorizontalStrut(5));
3621 hBox.add(Box.createHorizontalGlue());
3622 getContentPane().add(hBox);
3623
3624 getContentPane().add(Box.createVerticalStrut(5));
3625 hBox = Box.createHorizontalBox();
3626 hBox.add(Box.createHorizontalGlue());
3627 hBox.add(new JButton(findFirstAction));
3628 hBox.add(Box.createHorizontalStrut(5));
3629 hBox.add(new JButton(findNextAction));
3630 hBox.add(Box.createHorizontalStrut(5));
3631 hBox.add(new JButton(cancelAction));
3632 hBox.add(Box.createHorizontalGlue());
3633 getContentPane().add(hBox);
3634 getContentPane().add(Box.createVerticalStrut(5));
3635 }
3636
3637 protected void initListeners(){
3638 addComponentListener(new ComponentAdapter() {
3639 public void componentHidden(ComponentEvent e) {
3640 }
3641
3642 public void componentMoved(ComponentEvent e) {
3643 }
3644
3645 public void componentResized(ComponentEvent e) {
3646 }
3647
3648 public void componentShown(ComponentEvent e) {
3649 refresh();
3650 }
3651 });
3652
3653 patternTextField.getDocument().addDocumentListener(
3654 new javax.swing.event.DocumentListener() {
3655 public void insertUpdate(javax.swing.event.DocumentEvent e) {
3656 refresh();
3657 }
3658
3659 public void removeUpdate(javax.swing.event.DocumentEvent e) {
3660 refresh();
3661 }
3662
3663 public void changedUpdate(javax.swing.event.DocumentEvent e) {
3664 refresh();
3665 }
3666 });
3667
3668 }
3669
3670 protected void refresh(){
3671 String patternText = patternTextField.getText();
3672 if(patternText != null && patternText.length() > 0){
3673 //update actions state
3674 findFirstAction.setEnabled(true);
3675 findNextAction.setEnabled(true);
3676
3677 //update patternRE
3678 try
3679 {
3680 pattern = ignoreCaseChk.isSelected() ? Pattern.compile(patternText, Pattern.CASE_INSENSITIVE) : Pattern
3681 .compile(patternText);
3682 }
3683 catch (Exception ree)
3684 {
3685 JOptionPane.showMessageDialog(searchDialog, "Invalid pattern!\n" + ree.toString(), "GATE", JOptionPane.ERROR_MESSAGE);
3686 }
3687 }else{
3688 findFirstAction.setEnabled(false);
3689 findNextAction.setEnabled(false);
3690 }
3691
3692 if(pattern == null){
3693 }
3694 }
3695 JTextField patternTextField;
3696 JCheckBox ignoreCaseChk;
3697 JCheckBox wholeWordsChk;
3698 Pattern pattern;
3699 int nextMatchStartsFrom;
3700 String content;
3701
3702 Action findFirstAction;
3703 Action findNextAction;
3704 Action cancelAction;
3705 }
3706
3707 protected class PrintAction extends AbstractAction{
3708 public PrintAction(){
3709 super("Print");
3710 }
3711
3712 public void actionPerformed(ActionEvent e){
3713 Runnable runnable = new Runnable(){
3714 public void run(){
3715 PrinterJob printerJob = PrinterJob.getPrinterJob();
3716
3717 if (printerJob.printDialog()) {
3718 try{
3719
3720 // PageFormat pageFormat = printerJob.pageDialog(printerJob.defaultPage());
3721 PageFormat pageFormat = printerJob.defaultPage();
3722 Pageable pageable = new JComponentPrinter(textPane, pageFormat);
3723 printerJob.setPageable(pageable);
3724
3725 printerJob.print();
3726 //fire the events
3727 StatusListener sListener = (StatusListener)Gate.
3728 getListeners().
3729 get("gate.event.StatusListener");
3730 if(sListener != null){
3731 sListener.statusChanged("Document printed!");
3732 }
3733
3734 }catch(Exception ex) {
3735 ex.printStackTrace();
3736 }
3737 }
3738 }
3739 };
3740
3741 Thread thread = new Thread(Thread.currentThread().getThreadGroup(),
3742 runnable, "Print thread");
3743 thread.setPriority(Thread.MIN_PRIORITY);
3744 thread.start();
3745 }
3746 }
3747
3748
3749 /**
3750 * The action that is fired when the user wants to edit an annotation.
3751 * It will build a dialog containing all the valid annotation editors.
3752 */
3753 protected class EditAnnotationAction extends AbstractAction {
3754 public EditAnnotationAction(AnnotationSet set, Annotation annotation){
3755 super("Edit");
3756 this.set = set;
3757 this.annotation = annotation;
3758 putValue(SHORT_DESCRIPTION, "Edits the annotation");
3759 }
3760
3761 public void actionPerformed(ActionEvent e){
3762 //get the list of editors
3763 java.util.List specificEditors = Gate.getCreoleRegister().
3764 getAnnotationVRs(annotation.getType());
3765 java.util.List genericEditors = Gate.getCreoleRegister().
3766 getAnnotationVRs();
3767 //create the GUI
3768 JTabbedPane tabbedPane = new JTabbedPane(JTabbedPane.BOTTOM);
3769 //add all the specific editors
3770 Iterator editorIter = specificEditors.iterator();
3771 while(editorIter.hasNext()){
3772 String editorType = (String)editorIter.next();
3773 //create the editor
3774 AnnotationVisualResource editor;
3775 try{
3776 editor = (AnnotationVisualResource)
3777 Factory.createResource(editorType);
3778 if(editor instanceof ResizableVisualResource){
3779 tabbedPane.add((Component)editor,
3780 ((ResourceData)Gate.getCreoleRegister().
3781 get(editorType)).getName());
3782 }else{
3783 JScrollPane scroller = new JScrollPane((Component)editor);
3784 // scroller.setPreferredSize(((Component) editor).getPreferredSize());
3785 tabbedPane.add(scroller,
3786 ((ResourceData)Gate.getCreoleRegister().
3787 get(editorType)).getName());
3788 }
3789
3790 // editor.setTarget(set);
3791 // editor.setAnnotation(annotation);
3792 editor.editAnnotation(annotation, set);
3793 }catch(ResourceInstantiationException rie){
3794 rie.printStackTrace(Err.getPrintWriter());
3795 }
3796 }
3797
3798 //add all the generic editors
3799 editorIter = genericEditors.iterator();
3800 while(editorIter.hasNext()){
3801 String editorType = (String)editorIter.next();
3802 //create the editor
3803 AnnotationVisualResource editor;
3804 try{
3805 editor = (AnnotationVisualResource)
3806 Factory.createResource(editorType);
3807 if(editor.canDisplayAnnotationType(annotation.getType())){
3808 // editor.setTarget(set);
3809 // editor.setAnnotation(annotation);
3810 editor.editAnnotation(annotation, set);
3811 if(editor instanceof ResizableVisualResource){
3812 tabbedPane.add((Component)editor,
3813 ((ResourceData)Gate.getCreoleRegister().
3814 get(editorType)).getName());
3815 }else{
3816 tabbedPane.add(new JScrollPane((Component)editor),
3817 ((ResourceData)Gate.getCreoleRegister().
3818 get(editorType)).getName());
3819 }
3820 }
3821 }catch(ResourceInstantiationException rie){
3822 rie.printStackTrace(Err.getPrintWriter());
3823 }
3824
3825 }
3826
3827 //show the modal dialog until the data is OK or the user cancels
3828 boolean allOK = false;
3829 while(!allOK){
3830 if(OkCancelDialog.showDialog(DocumentEditor.this,
3831 tabbedPane,
3832 "Edit Annotation")){
3833 try{
3834 Component comp = tabbedPane.getSelectedComponent();
3835 if(comp instanceof AnnotationVisualResource){
3836 ((AnnotationVisualResource)comp).okAction();
3837 }else if(comp instanceof JScrollPane){
3838 ((AnnotationVisualResource)((JScrollPane)comp).
3839 getViewport().getView()).okAction();
3840 }else{
3841 throw new LuckyException("DocumentEditor.EditAnnotationAction1");
3842 }
3843
3844 allOK = true;
3845 }catch(GateException ge){
3846 JOptionPane.showMessageDialog(
3847 DocumentEditor.this,
3848 "There was an error:\n" +
3849 ge.toString(),
3850 "GATE", JOptionPane.ERROR_MESSAGE);
3851 ge.printStackTrace(Err.getPrintWriter());
3852 allOK = false;
3853 }
3854 }else{
3855 if (OkCancelDialog.userHasPressedCancel)
3856 try{
3857 Component comp = tabbedPane.getSelectedComponent();
3858 if(comp instanceof AnnotationVisualResource){
3859 ((AnnotationVisualResource)comp).cancelAction();
3860 }else if(comp instanceof JScrollPane){
3861 ((AnnotationVisualResource)
3862 ((JScrollPane)comp).getViewport().getView()).cancelAction();
3863 }else{
3864 throw new LuckyException("DocumentEditor.EditAnnotationAction");
3865 }
3866 allOK = true;
3867 } catch(GateException ge){
3868 JOptionPane.showMessageDialog(
3869 DocumentEditor.this,
3870 "There was an error:\n" +
3871 ge.toString(),
3872 "GATE", JOptionPane.ERROR_MESSAGE);
3873 allOK = false;
3874 }
3875 allOK = true;
3876 }
3877 }//while(!allOK)
3878 }//public void actionPerformed(ActionEvent e)
3879
3880 protected AnnotationSet set;
3881 protected Annotation annotation;
3882 }//class EditAnnotationAction
3883
3884 /**
3885 * The action that is fired when the user wants to create a new annotation.
3886 * It will build a dialog containing all the valid annotation editors.
3887 */
3888 class NewAnnotationAction extends AbstractAction{
3889 public NewAnnotationAction(AnnotationSet set,
3890 Long startOffset,
3891 Long endOffset){
3892 super("New annotation");
3893 putValue(SHORT_DESCRIPTION, "Creates a new annotation");
3894 this.set = set;
3895 this.startOffset = startOffset;
3896 this.endOffset = endOffset;
3897 this.type = null;
3898 }
3899
3900 public NewAnnotationAction(AnnotationSet set, String type,
3901 Long startOffset, Long endOffset){
3902 super("New \"" + type + "\" annotation");
3903 putValue(SHORT_DESCRIPTION, "Creates a new annotation of type \"" +
3904 type + "\"");
3905 this.set = set;
3906 this.startOffset = startOffset;
3907 this.endOffset = endOffset;
3908 this.type = type;
3909 }
3910
3911 public void actionPerformed(ActionEvent e){
3912 if(set == null){
3913 //get the name from the user
3914 String setName = JOptionPane.showInputDialog(
3915 DocumentEditor.this,
3916 "Please provide a name for the new annotation set",
3917 "GATE", JOptionPane.QUESTION_MESSAGE);
3918 if(setName == null) return;
3919 this.set = document.getAnnotations(setName);
3920 }
3921 try{
3922 //create the new annotation
3923 Integer annId = set.add(startOffset, endOffset,
3924 (type == null ? "_New_" : type),
3925 Factory.newFeatureMap());
3926 Annotation ann = set.get(annId);
3927 new EditAnnotationAction(set, ann).actionPerformed(e);
3928 }catch (GateException ge) {
3929 JOptionPane.showMessageDialog(
3930 DocumentEditor.this,
3931 "There was an error:\n" +
3932 ge.toString(),
3933 "GATE", JOptionPane.ERROR_MESSAGE);
3934 }
3935 //
3936 // //get the lists of editors
3937 // java.util.List specificEditors;
3938 // if(type != null) specificEditors = Gate.getCreoleRegister().
3939 // getAnnotationVRs(type);
3940 // else specificEditors = new ArrayList();
3941 //
3942 // java.util.List genericEditors = Gate.getCreoleRegister().
3943 // getAnnotationVRs();
3944 // //create the GUI
3945 // JTabbedPane tabbedPane = new JTabbedPane(JTabbedPane.BOTTOM);
3946 // //add all the specific editors
3947 // Iterator editorIter = specificEditors.iterator();
3948 // while(editorIter.hasNext()){
3949 // String editorType = (String)editorIter.next();
3950 // //create the editor
3951 // AnnotationVisualResource editor;
3952 // try{
3953 // editor = (AnnotationVisualResource)
3954 // Factory.createResource(editorType);
3955 // tabbedPane.add(new JScrollPane((Component)editor),
3956 // ((ResourceData)Gate.getCreoleRegister().get(editorType)).
3957 // getName());
3958 // editor.setTarget(set);
3959 // editor.setSpan(startOffset, endOffset, type);
3960 //
3961 // }catch(ResourceInstantiationException rie){
3962 // rie.printStackTrace(Err.getPrintWriter());
3963 // }
3964 // }
3965 //
3966 // //add all the generic editors
3967 // editorIter = genericEditors.iterator();
3968 // while(editorIter.hasNext()){
3969 // String editorType = (String)editorIter.next();
3970 // //create the editor
3971 // AnnotationVisualResource editor;
3972 // try{
3973 // editor = (AnnotationVisualResource)
3974 // Factory.createResource(editorType);
3975 //
3976 // if(type == null ||
3977 // (type != null && editor.canDisplayAnnotationType(type))){
3978 // editor.setTarget(set);
3979 // editor.setSpan(startOffset, endOffset, type);
3980 // tabbedPane.add(new JScrollPane((Component)editor),
3981 // ((ResourceData)Gate.getCreoleRegister().
3982 // get(editorType)).getName());
3983 // }
3984 // }catch(ResourceInstantiationException rie){
3985 // rie.printStackTrace(Err.getPrintWriter());
3986 // }
3987 //
3988 // }
3989 //
3990 // //show the modal dialog until the data is OK or the user cancels
3991 // boolean allOK = false;
3992 // while(!allOK){
3993 // if(OkCancelDialog.showDialog(DocumentEditor.this,
3994 // tabbedPane, "Edit Annotation")){
3995 // try{
3996 // ((AnnotationVisualResource)((JScrollPane)tabbedPane.
3997 // getSelectedComponent()).getViewport().
3998 // getView()
3999 // ).okAction();
4000 // allOK = true;
4001 // }catch(GateException ge){
4002 // JOptionPane.showMessageDialog(
4003 // DocumentEditor.this,
4004 // "There was an error:\n" +
4005 // ge.toString(),
4006 // "GATE", JOptionPane.ERROR_MESSAGE);
4007 //// ge.printStackTrace(Err.getPrintWriter());
4008 // allOK = false;
4009 // }
4010 // }else{
4011 // allOK = true;
4012 // }
4013 // }//while(!allOK)
4014 //
4015 //
4016 }//public void actionPerformed(ActionEvent e)
4017
4018 AnnotationSet set;
4019 Long startOffset;
4020 Long endOffset;
4021 String type;
4022 }//class NewAnnotationAction extends AbstractAction
4023
4024 /**
4025 * Fixes the <a
4026 * href="http://developer.java.sun.com/developer/bugParade/bugs/4406598.html">
4027 * 4406598 bug</a> in swing text components.
4028 * The bug consists in the fact that the Background attribute is ignored by
4029 * the text component whent it is defined in a style from which the current
4030 * style inherits.
4031 */
4032 public class CustomStyledEditorKit extends StyledEditorKit{
4033 private final ViewFactory defaultFactory = new CustomStyledViewFactory();
4034 public ViewFactory getViewFactory() {
4035 return defaultFactory;
4036 }
4037
4038 /**
4039 * Inserts content from the given stream, which will be
4040 * treated as plain text.
4041 * This insertion is done without checking \r or \r \n sequence.
4042 * It takes the text from the Reader and place it into Document at position
4043 * pos
4044 */
4045 public void read(Reader in, javax.swing.text.Document doc, int pos)
4046 throws IOException, BadLocationException {
4047
4048 char[] buff = new char[65536];
4049 int charsRead = 0;
4050 while ((charsRead = in.read(buff, 0, buff.length)) != -1) {
4051 doc.insertString(pos, new String(buff, 0, charsRead), null);
4052 pos += charsRead;
4053 }// while
4054 }// read
4055 }
4056
4057 /**
4058 * Fixes the <a
4059 * href="http://developer.java.sun.com/developer/bugParade/bugs/4406598.html">
4060 * 4406598 bug</a> in swing text components.
4061 * The bug consists in the fact that the Background attribute is ignored by
4062 * the text component whent it is defined in a style from which the current
4063 * style inherits.
4064 */
4065 public class CustomStyledViewFactory implements ViewFactory{
4066 public View create(Element elem) {
4067 String kind = elem.getName();
4068 if (kind != null) {
4069 if (kind.equals(AbstractDocument.ContentElementName)) {
4070 return new CustomLabelView(elem);
4071 }else if (kind.equals(AbstractDocument.ParagraphElementName)) {
4072 return new ParagraphView(elem);
4073 }else if (kind.equals(AbstractDocument.SectionElementName)) {
4074 return new BoxView(elem, View.Y_AXIS);
4075 }else if (kind.equals(StyleConstants.ComponentElementName)) {
4076 return new ComponentView(elem);
4077 }else if (kind.equals(StyleConstants.IconElementName)) {
4078 return new IconView(elem);
4079 }
4080 }
4081 // default to text display
4082 return new CustomLabelView(elem);
4083 }
4084 }
4085 }//class AnnotationEditor
|