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, Mar 23, 2004
0011 *
0012 * $Id: AnnotationSetsView.java 13614 2011-04-05 14:24:31Z valyt $
0013 */
0014 package gate.gui.docview;
0015
0016 import java.awt.*;
0017 import java.awt.event.*;
0018 import java.beans.PropertyChangeEvent;
0019 import java.beans.PropertyChangeListener;
0020 import java.io.*;
0021 import java.net.URISyntaxException;
0022 import java.util.*;
0023 import java.util.List;
0024 import java.util.concurrent.BlockingQueue;
0025 import java.util.concurrent.LinkedBlockingQueue;
0026
0027 import javax.swing.*;
0028 import javax.swing.Timer;
0029 import javax.swing.border.Border;
0030 import javax.swing.event.*;
0031 import javax.swing.table.*;
0032 import javax.swing.text.*;
0033
0034 import gate.Annotation;
0035 import gate.AnnotationSet;
0036 import gate.Factory;
0037 import gate.Gate;
0038 import gate.GateConstants;
0039 import gate.TextualDocument;
0040 import gate.creole.ResourceData;
0041 import gate.creole.ResourceInstantiationException;
0042 import gate.event.*;
0043 import gate.event.DocumentEvent;
0044 import gate.event.DocumentListener;
0045 import gate.gui.*;
0046 import gate.gui.annedit.*;
0047 import gate.swing.ColorGenerator;
0048 import gate.swing.XJTable;
0049 import gate.swing.XJFileChooser;
0050 import gate.util.*;
0051
0052 /**
0053 * Display document annotation sets and types in a tree view like with a table.
0054 * Allow the selection of annotation type and modification of their color.
0055 */
0056 public class AnnotationSetsView extends AbstractDocumentView
0057 implements DocumentListener,
0058 AnnotationSetListener,
0059 AnnotationEditorOwner{
0060
0061
0062 /* (non-Javadoc)
0063 * @see gate.gui.annedit.AnnotationEditorOwner#annotationTypeChanged(gate.Annotation, java.lang.String, java.lang.String)
0064 */
0065 public void annotationChanged(Annotation ann, AnnotationSet set,
0066 String oldType) {
0067 lastAnnotationType = ann.getType();
0068 //show new annotation type
0069 setTypeSelected(set.getName(), ann.getType(), true);
0070 //select new annotation
0071 // selectAnnotation(new AnnotationDataImpl(set, ann));
0072 }
0073
0074
0075
0076 /**
0077 * Queues an an action for selecting the provided annotation
0078 */
0079 public void selectAnnotation(final AnnotationData aData) {
0080 Runnable action = new Runnable(){
0081 public void run(){
0082 List<AnnotationData> selAnns = Collections.singletonList(aData);
0083 owner.setSelectedAnnotations(selAnns);
0084 }
0085 };
0086 pendingEvents.offer(new PerformActionEvent(action));
0087 eventMinder.restart();
0088 }
0089
0090
0091
0092 /* (non-Javadoc)
0093 * @see gate.gui.annedit.AnnotationEditorOwner#getNextAnnotation()
0094 */
0095 public Annotation getNextAnnotation() {
0096 return null;
0097 }
0098
0099 /* (non-Javadoc)
0100 * @see gate.gui.annedit.AnnotationEditorOwner#getPreviousAnnotation()
0101 */
0102 public Annotation getPreviousAnnotation() {
0103 return null;
0104 }
0105
0106 /* (non-Javadoc)
0107 * @see gate.gui.annedit.AnnotationEditorOwner#getTextComponent()
0108 */
0109 public JTextComponent getTextComponent() {
0110 return textPane;
0111 }
0112
0113
0114 /* (non-Javadoc)
0115 * @see gate.gui.annedit.AnnotationEditorOwner#getListComponent()
0116 * TODO: delete this obsolete method?
0117 */
0118 public AnnotationList getListComponent() {
0119 return listView;
0120 }
0121
0122 public AnnotationSetsView(){
0123 setHandlers = new ArrayList<SetHandler>();
0124 tableRows = new ArrayList();
0125 visibleAnnotationTypes = new LinkedBlockingQueue<TypeSpec>();
0126 actions = new ArrayList();
0127 actions.add(new SavePreserveFormatAction());
0128 pendingEvents = new LinkedBlockingQueue<GateEvent>();
0129 eventMinder = new Timer(EVENTS_HANDLE_DELAY,
0130 new HandleDocumentEventsAction());
0131 eventMinder.setRepeats(true);
0132 eventMinder.setCoalesce(true);
0133 }
0134
0135 public List getActions() {
0136 return actions;
0137 }
0138
0139 /* (non-Javadoc)
0140 * @see gate.gui.docview.DocumentView#getType()
0141 */
0142 public int getType() {
0143 return VERTICAL;
0144 }
0145
0146 protected void initGUI(){
0147 //get a pointer to the textual view used for highlights
0148 Iterator centralViewsIter = owner.getCentralViews().iterator();
0149 while(textView == null && centralViewsIter.hasNext()){
0150 DocumentView aView = (DocumentView)centralViewsIter.next();
0151 if(aView instanceof TextualDocumentView)
0152 textView = (TextualDocumentView)aView;
0153 }
0154 textPane = (JTextArea)((JScrollPane)textView.getGUI())
0155 .getViewport().getView();
0156
0157 //get a pointer to the list view
0158 Iterator horizontalViewsIter = owner.getHorizontalViews().iterator();
0159 while(listView == null && horizontalViewsIter.hasNext()){
0160 DocumentView aView = (DocumentView)horizontalViewsIter.next();
0161 if(aView instanceof AnnotationListView)
0162 listView = (AnnotationListView)aView;
0163 }
0164 //get a pointer to the stack view
0165 horizontalViewsIter = owner.getHorizontalViews().iterator();
0166 while(stackView == null && horizontalViewsIter.hasNext()){
0167 DocumentView aView = (DocumentView)horizontalViewsIter.next();
0168 if(aView instanceof AnnotationStackView)
0169 stackView = (AnnotationStackView)aView;
0170 }
0171 mainTable = new XJTable();
0172 tableModel = new SetsTableModel();
0173 mainTable.setSortable(false);
0174 mainTable.setModel(tableModel);
0175 mainTable.setRowMargin(0);
0176 mainTable.getColumnModel().setColumnMargin(0);
0177 SetsTableCellRenderer cellRenderer = new SetsTableCellRenderer();
0178 mainTable.getColumnModel().getColumn(NAME_COL).setCellRenderer(cellRenderer);
0179 mainTable.getColumnModel().getColumn(SELECTED_COL).setCellRenderer(cellRenderer);
0180 SetsTableCellEditor cellEditor = new SetsTableCellEditor();
0181 mainTable.getColumnModel().getColumn(SELECTED_COL).setCellEditor(cellEditor);
0182 mainTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
0183 mainTable.setColumnSelectionAllowed(false);
0184 mainTable.setRowSelectionAllowed(true);
0185 mainTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
0186 //block autocreation of new columns from now on
0187 mainTable.setAutoCreateColumnsFromModel(false);
0188 mainTable.setTableHeader(null);
0189 mainTable.setShowGrid(false);
0190 mainTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
0191
0192 //the background colour seems to change somewhere when using the GTK+
0193 //look and feel on Linux, so we copy the value now and set it
0194 Color tableBG = mainTable.getBackground();
0195 //make a copy of the value (as the reference gets changed somewhere)
0196 tableBG = new Color(tableBG.getRGB());
0197 mainTable.setBackground(tableBG);
0198
0199 scroller = new JScrollPane(mainTable);
0200 scroller.getViewport().setOpaque(true);
0201 scroller.getViewport().setBackground(tableBG);
0202
0203 try {
0204 annotationEditor = createAnnotationEditor(textView, this);
0205 }
0206 catch(ResourceInstantiationException e) {
0207 //this should not really happen
0208 throw new GateRuntimeException(
0209 "Could not initialise the annotation editor!", e);
0210 }
0211
0212 mainPanel = new JPanel();
0213 mainPanel.setLayout(new GridBagLayout());
0214 GridBagConstraints constraints = new GridBagConstraints();
0215
0216 constraints.gridy = 0;
0217 constraints.gridx = GridBagConstraints.RELATIVE;
0218 constraints.gridwidth = 2;
0219 constraints.weighty = 1;
0220 constraints.weightx = 1;
0221 constraints.fill = GridBagConstraints.BOTH;
0222 mainPanel.add(scroller, constraints);
0223
0224 constraints.gridy = 1;
0225 constraints.gridwidth = 1;
0226 constraints.weighty = 0;
0227 newSetNameTextField = new JTextField();
0228 mainPanel.add(newSetNameTextField, constraints);
0229 constraints.weightx = 0;
0230 newSetAction = new NewAnnotationSetAction();
0231 mainPanel.add(new JButton(newSetAction), constraints);
0232
0233 populateUI();
0234 tableModel.fireTableDataChanged();
0235
0236
0237 eventMinder.start();
0238 initListeners();
0239 }
0240
0241 /**
0242 * Create the annotation editor (responsible for creating the window
0243 * used to edit individual annotations).
0244 *
0245 * @param textView
0246 * @param asView
0247 * @return
0248 * @throws ResourceInstantiationException
0249 */
0250 protected gate.gui.annedit.OwnedAnnotationEditor createAnnotationEditor(
0251 TextualDocumentView textView, AnnotationSetsView asView)
0252 throws ResourceInstantiationException {
0253 // find the last VR that implements the AnnotationEditor interface
0254 List<String> vrTypes = new ArrayList<String>(Gate.getCreoleRegister()
0255 .getPublicVrTypes());
0256 Collections.reverse(vrTypes);
0257 for(String aVrType : vrTypes) {
0258 ResourceData rData = (ResourceData)Gate.getCreoleRegister().get(aVrType);
0259 try {
0260 Class resClass = rData.getResourceClass();
0261 if(OwnedAnnotationEditor.class.isAssignableFrom(resClass)) {
0262 OwnedAnnotationEditor newEditor = (OwnedAnnotationEditor)resClass
0263 .newInstance();
0264 newEditor.setOwner(this);
0265 newEditor.init();
0266 return newEditor;
0267 }
0268 }
0269 catch(ClassNotFoundException cnfe) {
0270 // ignore
0271 Err.prln("Invalid CREOLE data:");
0272 cnfe.printStackTrace(Err.getPrintWriter());
0273 }
0274 catch(InstantiationException e) {
0275 e.printStackTrace();
0276 }
0277 catch(IllegalAccessException e) {
0278 e.printStackTrace();
0279 }
0280 }
0281 // if we got this far, we couldn't find an editor
0282 Err.prln("Could not find any annotation editors. Editing annotations disabled.");
0283 return null;
0284 }
0285
0286 protected void populateUI(){
0287 setHandlers.add(new SetHandler(document.getAnnotations()));
0288 List setNames = document.getNamedAnnotationSets() == null ?
0289 new ArrayList() :
0290 new ArrayList(document.getNamedAnnotationSets().keySet());
0291 Collections.sort(setNames);
0292 Iterator setsIter = setNames.iterator();
0293 while(setsIter.hasNext()){
0294 setHandlers.add(new SetHandler(document.
0295 getAnnotations((String)setsIter.next())));
0296 }
0297 tableRows.addAll(setHandlers);
0298 }
0299
0300 public Component getGUI(){
0301 return mainPanel;
0302 }
0303
0304 /**
0305 * Get the saved colour for this annotation type or create a new one
0306 * and save it. The colours are saved in the user configuration file.
0307 * @param annotationType type to get a colour for
0308 * @return a colour
0309 */
0310 public static Color getColor(String annotationSet, String annotationType) {
0311 Map<String, String> colourMap = Gate.getUserConfig()
0312 .getMap(AnnotationSetsView.class.getName()+".colours");
0313 String colourValue = colourMap.get(annotationSet+"."+annotationType);
0314 if (colourValue == null) colourValue = colourMap.get(annotationType);
0315
0316 Color colour;
0317 if (colourValue == null) {
0318 float components[] = colourGenerator.getNextColor().getComponents(null);
0319 colour = new Color(components[0], components[1], components[2], 0.5f);
0320 int rgb = colour.getRGB();
0321 int alpha = colour.getAlpha();
0322 int rgba = rgb | (alpha << 24);
0323 colourMap.put(annotationType, String.valueOf(rgba));
0324 Gate.getUserConfig().put(
0325 AnnotationSetsView.class.getName()+".colours", colourMap);
0326 } else {
0327 colour = new Color(Integer.valueOf(colourValue), true);
0328 }
0329
0330 return colour;
0331 }
0332
0333 protected void saveColor(String annotationSet, String annotationType, Color colour){
0334 Map<String, String> colourMap = Gate.getUserConfig()
0335 .getMap(AnnotationSetsView.class.getName()+".colours");
0336 int rgb = colour.getRGB();
0337 int alpha = colour.getAlpha();
0338 int rgba = rgb | (alpha << 24);
0339
0340 String defaultValue = colourMap.get(annotationType);
0341 String newValue = String.valueOf(rgba);
0342
0343 if (newValue.equals(defaultValue)) {
0344 colourMap.remove(annotationSet+"."+annotationType);
0345 }
0346 else {
0347 colourMap.put(annotationSet+"."+annotationType, newValue);
0348 }
0349
0350 Gate.getUserConfig().put(
0351 AnnotationSetsView.class.getName()+".colours", colourMap);
0352 }
0353
0354 /**
0355 * Save type or remove unselected type in the preferences.
0356 * @param setName set name to save/remove or null for the default set
0357 * @param typeName type name to save/remove
0358 * @param selected state of the selection
0359 */
0360 public void saveType(String setName, String typeName, boolean selected) {
0361 List<String> typeList = Gate.getUserConfig().getList(
0362 AnnotationSetsView.class.getName() + ".types");
0363 String prefix = (setName == null) ? "." : setName + ".";
0364 if (selected) {
0365 typeList.add(prefix+typeName);
0366 } else {
0367 typeList.remove(prefix+typeName);
0368 }
0369 Gate.getUserConfig().put(
0370 AnnotationSetsView.class.getName()+".types", typeList);
0371 }
0372
0373 /**
0374 * Restore previously selected types from the preferences.
0375 */
0376 public void restoreSavedSelectedTypes() {
0377 List<String> typeList = Gate.getUserConfig().getList(
0378 AnnotationSetsView.class.getName() + ".types");
0379 for (SetHandler sHandler : setHandlers){
0380 String prefix = (sHandler.set.getName() == null) ?
0381 "." : sHandler.set.getName() + ".";
0382 for (TypeHandler tHandler : sHandler.typeHandlers) {
0383 if (typeList.contains(prefix + tHandler.name)) {
0384 tHandler.setSelected(true);
0385 }
0386 }
0387 }
0388 }
0389
0390 /**
0391 * Enables or disables creation of the new annotation set.
0392 */
0393 public void setNewAnnSetCreationEnabled(boolean b) {
0394 newSetAction.setEnabled(b);
0395 newSetNameTextField.setEnabled(b);
0396 }
0397
0398 /**
0399 * This method will be called whenever the view becomes active. Implementers
0400 * should use this to add hooks (such as mouse listeners) to the other views
0401 * as required by their functionality.
0402 */
0403 protected void registerHooks(){
0404 textPane.addMouseListener(textMouseListener);
0405 textPane.addMouseMotionListener(textMouseListener);
0406 textPane.addPropertyChangeListener("highlighter", textChangeListener);
0407 // textPane.addAncestorListener(textAncestorListener);
0408 restoreSelectedTypes();
0409 }
0410
0411 /**
0412 * This method will be called whenever this view becomes inactive.
0413 * Implementers should use it to unregister whatever hooks they registered
0414 * in {@link #registerHooks()}.
0415 *
0416 */
0417 protected void unregisterHooks(){
0418 textPane.removeMouseListener(textMouseListener);
0419 textPane.removeMouseMotionListener(textMouseListener);
0420 textPane.removePropertyChangeListener("highlighter", textChangeListener);
0421 // textPane.removeAncestorListener(textAncestorListener);
0422 storeSelectedTypes();
0423 }
0424
0425
0426 /**
0427 * Populates the {@link #visibleAnnotationTypes} structure based on the
0428 * current selection
0429 *
0430 */
0431 protected void storeSelectedTypes(){
0432 visibleAnnotationTypes.clear(); // for security
0433 for(SetHandler sHandler:setHandlers){
0434 for(TypeHandler tHandler: sHandler.typeHandlers){
0435 if(tHandler.isSelected()){
0436 visibleAnnotationTypes.add(new TypeSpec(sHandler.set.getName(),
0437 tHandler.name));
0438 tHandler.setSelected(false);
0439 }
0440 }
0441 }
0442 }
0443
0444 /**
0445 * Restores the selected types based on the state saved in the
0446 * {@link #visibleAnnotationTypes} data structure.
0447 */
0448 protected void restoreSelectedTypes(){
0449 TypeSpec typeSpec;
0450 while((typeSpec = visibleAnnotationTypes.poll()) != null){
0451 TypeHandler typeHandler =
0452 getTypeHandler(typeSpec.setName, typeSpec.type);
0453 if (typeHandler != null) { // may have been deleted since
0454 typeHandler.setSelected(true);
0455 }
0456 }
0457 }
0458
0459 protected void initListeners(){
0460
0461 document.addDocumentListener(this);
0462
0463 // popup menu to change the color, select, unselect
0464 // and delete annotation types
0465 mainTable.addMouseListener(new MouseAdapter(){
0466 public void mouseClicked(MouseEvent evt){
0467 processMouseEvent(evt);
0468 }
0469 public void mousePressed(MouseEvent evt){
0470 int row = mainTable.rowAtPoint(evt.getPoint());
0471 if(evt.isPopupTrigger()
0472 && !mainTable.isRowSelected(row)) {
0473 // if right click outside the selection then reset selection
0474 mainTable.getSelectionModel().setSelectionInterval(row, row);
0475 }
0476 processMouseEvent(evt);
0477 }
0478 public void mouseReleased(MouseEvent evt){
0479 processMouseEvent(evt);
0480 }
0481 protected void processMouseEvent(MouseEvent evt){
0482 int row = mainTable.rowAtPoint(evt.getPoint());
0483 int col = mainTable.columnAtPoint(evt.getPoint());
0484
0485 if(row >= 0 && col == NAME_COL){
0486 Object handler = tableRows.get(row);
0487 if(evt.isPopupTrigger()){
0488 // popup menu
0489 JPopupMenu popup = new JPopupMenu();
0490 if(handler instanceof TypeHandler
0491 && mainTable.getSelectedRowCount() == 1){
0492 TypeHandler tHandler = (TypeHandler)handler;
0493 popup.add(tHandler.changeColourAction);
0494 popup.add(new DeleteSelectedAnnotationsAction("Delete"));
0495 } else if (mainTable.getSelectedRowCount() > 1
0496 || handler instanceof SetHandler) {
0497 popup.add(new SetSelectedAnnotationsAction(true));
0498 popup.add(new SetSelectedAnnotationsAction(false));
0499 popup.add(new DeleteSelectedAnnotationsAction("Delete all"));
0500 }
0501 if (popup.getComponentCount() > 0) {
0502 popup.show(mainTable, evt.getX(), evt.getY());
0503 }
0504 } else if(evt.getClickCount() >= 2
0505 && evt.getID() == MouseEvent.MOUSE_CLICKED
0506 && handler instanceof TypeHandler){
0507 //double click
0508 TypeHandler tHandler = (TypeHandler)handler;
0509 tHandler.changeColourAction.actionPerformed(null);
0510 }
0511 }
0512 }
0513 });
0514
0515 // Enter key to change the color of annotation type
0516 // Space key to select/unselect annotation type
0517 // Left/Right keys to close/open an annotation set
0518 mainTable.addKeyListener(new KeyAdapter(){
0519 public void keyPressed(KeyEvent e) {
0520 int row = mainTable.getSelectedRow();
0521 int col = mainTable.getSelectedColumn();
0522 if(row <= 0
0523 || mainTable.getSelectedRowCount() > 1) {
0524 return;
0525 }
0526 Object handler = tableRows.get(row);
0527 if (col == NAME_COL) {
0528 if(e.getKeyCode() == KeyEvent.VK_ENTER
0529 && handler instanceof TypeHandler){
0530 TypeHandler tHandler = (TypeHandler)handler;
0531 tHandler.changeColourAction.actionPerformed(null);
0532 e.consume();
0533 } else if (e.getKeyCode() == KeyEvent.VK_SPACE) {
0534 if (handler instanceof TypeHandler){
0535 TypeHandler tHandler = (TypeHandler)handler;
0536 (new SetSelectedAnnotationsAction(!tHandler.selected))
0537 .actionPerformed(null);
0538 } else if (handler instanceof SetHandler) {
0539 SetHandler sHandler = (SetHandler)handler;
0540 boolean allUnselected = true;
0541 for (TypeHandler tHandler : sHandler.typeHandlers) {
0542 if (tHandler.selected) {
0543 allUnselected = false;
0544 break;
0545 }
0546 }
0547 (new SetSelectedAnnotationsAction(allUnselected))
0548 .actionPerformed(null);
0549 }
0550 } else if (e.getKeyCode() == KeyEvent.VK_LEFT) {
0551 if (handler instanceof SetHandler) {
0552 ((SetHandler)handler).setExpanded(false);
0553 mainTable.setColumnSelectionInterval(col, col);
0554 mainTable.setRowSelectionInterval(row, row);
0555 }
0556 e.consume();
0557
0558 } else if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
0559 if (handler instanceof SetHandler) {
0560 ((SetHandler)handler).setExpanded(true);
0561 mainTable.setColumnSelectionInterval(col, col);
0562 mainTable.setRowSelectionInterval(row, row);
0563 }
0564 e.consume();
0565 }
0566 }
0567 }
0568 });
0569
0570 mouseStoppedMovingAction = new MouseStoppedMovingAction();
0571 mouseMovementTimer = new javax.swing.Timer(MOUSE_MOVEMENT_TIMER_DELAY,
0572 mouseStoppedMovingAction);
0573 mouseMovementTimer.setRepeats(false);
0574 textMouseListener = new TextMouseListener();
0575 textChangeListener = new PropertyChangeListener(){
0576 public void propertyChange(PropertyChangeEvent evt) {
0577 if(evt.getNewValue() != null){
0578 //we have a new highlighter
0579 //we need to re-highlight all selected annotations
0580 for(SetHandler sHandler : setHandlers){
0581 for(TypeHandler tHandler : sHandler.typeHandlers){
0582 if(tHandler.isSelected()){
0583 setTypeSelected(sHandler.set.getName(), tHandler.name, false);
0584 setTypeSelected(sHandler.set.getName(), tHandler.name, true);
0585 }
0586 }
0587 }
0588 }
0589 }
0590 };
0591
0592 mainTable.getInputMap().put(KeyStroke.getKeyStroke("DELETE"), "deleteAll");
0593 mainTable.getInputMap()
0594 .put(KeyStroke.getKeyStroke("shift DELETE"), "deleteAll");
0595 mainTable.getActionMap().put("deleteAll",
0596 new DeleteSelectedAnnotationsAction("Delete"));
0597 newSetNameTextField.getInputMap().put(
0598 KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "newSet");
0599 newSetNameTextField.getActionMap().put("newSet", newSetAction);
0600 textPane.getInputMap()
0601 .put(KeyStroke.getKeyStroke("control E"), "edit annotation");
0602 textPane.getActionMap().put("edit annotation", new AbstractAction() {
0603 public void actionPerformed(ActionEvent e) {
0604 // use the same action as when the mouse stop over a selection
0605 // or annotation but this time for a keyboard shortcut
0606 mouseStoppedMovingAction.setTextLocation(textPane.getCaretPosition());
0607 mouseStoppedMovingAction.actionPerformed(null);
0608 SwingUtilities.invokeLater(new Runnable() { public void run() {
0609 annotationEditor.setPinnedMode(true);
0610 }});
0611 }});
0612
0613 // skip first column when tabbing
0614 InputMap im =
0615 mainTable.getInputMap(JTable.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
0616 KeyStroke tab = KeyStroke.getKeyStroke("TAB");
0617 final Action oldTabAction = mainTable.getActionMap().get(im.get(tab));
0618 Action tabAction = new AbstractAction() {
0619 public void actionPerformed(ActionEvent e) {
0620 oldTabAction.actionPerformed(e);
0621 JTable table = (JTable) e.getSource();
0622 if(table.getSelectedColumn() == SELECTED_COL) {
0623 oldTabAction.actionPerformed(e);
0624 }
0625 }
0626 };
0627 mainTable.getActionMap().put(im.get(tab), tabAction);
0628 KeyStroke shiftTab = KeyStroke.getKeyStroke("shift TAB");
0629 final Action oldShiftTabAction =
0630 mainTable.getActionMap().get(im.get(shiftTab));
0631 Action shiftTabAction = new AbstractAction() {
0632 public void actionPerformed(ActionEvent e) {
0633 oldShiftTabAction.actionPerformed(e);
0634 JTable table = (JTable) e.getSource();
0635 if(table.getSelectedColumn() == SELECTED_COL) {
0636 oldShiftTabAction.actionPerformed(e);
0637 }
0638 }
0639 };
0640 mainTable.getActionMap().put(im.get(shiftTab), shiftTabAction);
0641 }
0642
0643
0644 /* (non-Javadoc)
0645 * @see gate.Resource#cleanup()
0646 */
0647 public void cleanup() {
0648 document.removeDocumentListener(this);
0649 for(SetHandler sHandler : setHandlers){
0650 sHandler.set.removeAnnotationSetListener(AnnotationSetsView.this);
0651 }
0652 eventMinder.stop();
0653 pendingEvents.clear();
0654 super.cleanup();
0655 document = null;
0656 }
0657
0658 public void annotationSetAdded(final DocumentEvent e) {
0659 pendingEvents.offer(e);
0660 eventMinder.restart();
0661 }//public void annotationSetAdded(DocumentEvent e)
0662
0663 public void annotationSetRemoved(final DocumentEvent e) {
0664 pendingEvents.offer(e);
0665 eventMinder.restart();
0666 }//public void annotationSetRemoved(DocumentEvent e)
0667
0668 /**Called when the content of the document has changed through an edit
0669 * operation.
0670 */
0671 public void contentEdited(DocumentEvent e){
0672 //go through all the type handlers and propagate the event
0673 Iterator setIter = setHandlers.iterator();
0674 while(setIter.hasNext()){
0675 SetHandler sHandler = (SetHandler)setIter.next();
0676 Iterator typeIter = sHandler.typeHandlers.iterator();
0677 while(typeIter.hasNext()){
0678 TypeHandler tHandler = (TypeHandler)typeIter.next();
0679 if(tHandler.isSelected())
0680 tHandler.repairHighlights(e.getEditStart().intValue(),
0681 e.getEditEnd().intValue());
0682 }
0683 }
0684 }
0685
0686
0687 public void annotationAdded(final AnnotationSetEvent e) {
0688 pendingEvents.offer(e);
0689 eventMinder.restart();
0690 }
0691
0692 public void annotationRemoved(final AnnotationSetEvent e) {
0693 pendingEvents.offer(e);
0694 eventMinder.restart();
0695 }
0696
0697 /**
0698 * Get an annotation set handler in this annotation set view.
0699 * @param name name of the annotation set or null for the default set
0700 * @return the annotation set handler
0701 */
0702 protected SetHandler getSetHandler(String name){
0703 for (SetHandler setHandler : setHandlers) {
0704 if (name == null) { // default annotation set
0705 if (setHandler.set.getName() == null) return setHandler;
0706 } else {
0707 if (name.equals(setHandler.set.getName())) return setHandler;
0708 }
0709 }
0710 // set handler not found
0711 return null;
0712 }
0713
0714 /**
0715 * Get an annotation type handler in this annotation set view.
0716 * @param set name of the annotation set or null for the default set
0717 * @param type type of the annotation
0718 * @return the type handler
0719 */
0720 public TypeHandler getTypeHandler(String set, String type){
0721 for(TypeHandler typeHandler : getSetHandler(set).typeHandlers){
0722 if(typeHandler.name.equals(type)) {
0723 return typeHandler;
0724 }
0725 }
0726 // type handler not found
0727 return null;
0728 }
0729
0730 /**
0731 * Un/select an annotation type in this annotation set view
0732 * and indirectly highlight it in the document view.
0733 * @param setName name of the annotation set or null for the default set
0734 * @param typeName type of the annotation
0735 * @param selected state of the selection
0736 */
0737 public void setTypeSelected(final String setName,
0738 final String typeName,
0739 final boolean selected){
0740 SwingUtilities.invokeLater(new Runnable(){
0741 public void run(){
0742 TypeHandler tHandler = getTypeHandler(setName, typeName);
0743 if(tHandler != null){
0744 tHandler.setSelected(selected);
0745 int row = tableRows.indexOf(tHandler);
0746 tableModel.fireTableRowsUpdated(row, row);
0747 mainTable.getSelectionModel().setSelectionInterval(row, row);
0748 }else{
0749 //type handler not created yet
0750 visibleAnnotationTypes.add(new TypeSpec(setName, typeName));
0751 }
0752 }
0753 });
0754 }
0755
0756
0757
0758 /* (non-Javadoc)
0759 * @see gate.gui.docview.AbstractDocumentView#setSelectedAnnotations(java.util.List)
0760 */
0761 @Override
0762 public void setSelectedAnnotations(List<AnnotationData> selectedAnnots) {
0763 if(annotationEditor != null && annotationEditor.isActive()){
0764 // editor active - let's update it.
0765 // For annotation editing purposes, only a single selected annotation
0766 // makes sense. Anything else is equivalent to no selection.
0767 PerformActionEvent actionEvent = null;
0768 if(selectedAnnots.size() == 1){
0769 final AnnotationData aData = selectedAnnots.get(0);
0770 //queue the select action to the events minder
0771 actionEvent = new PerformActionEvent(new Runnable(){
0772 public void run(){
0773 //select the annotation for editing, if editing enabled
0774 if(annotationEditor.getAnnotationSetCurrentlyEdited() !=
0775 aData.getAnnotationSet() ||
0776 annotationEditor.getAnnotationCurrentlyEdited() !=
0777 aData.getAnnotation()){
0778 annotationEditor.editAnnotation(aData.getAnnotation(),
0779 aData.getAnnotationSet());
0780 }
0781 }
0782 });
0783 } else {
0784 actionEvent = new PerformActionEvent(new Runnable(){
0785 public void run(){
0786 // un-select the edited annotation
0787 annotationEditor.editAnnotation(null,
0788 annotationEditor.getAnnotationSetCurrentlyEdited());
0789 }
0790 });
0791 }
0792 pendingEvents.offer(actionEvent);
0793 eventMinder.restart();
0794 }
0795 }
0796
0797 /**
0798 * Sets a particular annotation as selected. If the list view is visible
0799 * and active, it makes sure that the same annotation is selected there.
0800 * If the annotation editor exists and is active, it switches it to this
0801 * current annotation.
0802 * @param ann the annotation
0803 * @param annSet the parent set
0804 */
0805 public void selectAnnotation(final Annotation ann,
0806 final AnnotationSet annSet){
0807 selectAnnotation(new AnnotationDataImpl(annSet, ann));
0808 }
0809
0810 protected class SetsTableModel extends AbstractTableModel{
0811 public int getRowCount(){
0812 return tableRows.size();
0813 // //we have at least one row per set
0814 // int rows = setHandlers.size();
0815 // //expanded sets add rows
0816 // for(int i =0; i < setHandlers.size(); i++){
0817 // SetHandler sHandler = (SetHandler)setHandlers.get(i);
0818 // rows += sHandler.expanded ? sHandler.set.getAllTypes().size() : 0;
0819 // }
0820 // return rows;
0821 }
0822
0823 public int getColumnCount(){
0824 return 2;
0825 }
0826
0827 public Object getValueAt(int row, int column){
0828 Object value = tableRows.get(row);
0829 switch(column){
0830 case NAME_COL:
0831 return value;
0832 case SELECTED_COL:
0833 if(value instanceof SetHandler)
0834 return new Boolean(((SetHandler)value).isExpanded());
0835 if(value instanceof TypeHandler)
0836 return new Boolean(((TypeHandler)value).isSelected());
0837 default:
0838 return null;
0839 }
0840 //
0841 // int currentRow = 0;
0842 // Iterator handlerIter = setHandlers.iterator();
0843 // SetHandler sHandler = (SetHandler)handlerIter.next();
0844 //
0845 // while(currentRow < row){
0846 // if(sHandler.expanded){
0847 // if(sHandler.typeHandlers.size() + currentRow >= row){
0848 // //we want a row in current set
0849 // return sHandler.typeHandlers.get(row - currentRow);
0850 // }else{
0851 // currentRow += sHandler.typeHandlers.size();
0852 // sHandler = (SetHandler)handlerIter.next();
0853 // }
0854 // }else{
0855 // //just go to next handler
0856 // currentRow++;
0857 // sHandler = (SetHandler)handlerIter.next();
0858 // }
0859 // if(currentRow == row) return sHandler;
0860 // }
0861 // if(currentRow == row) return sHandler;
0862 //System.out.println("BUG! row: " + row + " col: " + column);
0863 // return null;
0864 }
0865
0866 public boolean isCellEditable(int rowIndex, int columnIndex){
0867 Object value = tableRows.get(rowIndex);
0868 switch(columnIndex){
0869 case NAME_COL: return false;
0870 case SELECTED_COL:
0871 if(value instanceof SetHandler)
0872 return ((SetHandler)value).typeHandlers.size() > 0;
0873 if(value instanceof TypeHandler) return true;
0874 }
0875 return columnIndex == SELECTED_COL;
0876 }
0877
0878 public void setValueAt(Object aValue, int rowIndex, int columnIndex){
0879 Object receiver = tableRows.get(rowIndex);
0880 switch(columnIndex){
0881 case SELECTED_COL:
0882 if(receiver instanceof SetHandler){
0883 ((SetHandler)receiver).setExpanded(((Boolean)aValue).booleanValue());
0884 }else if(receiver instanceof TypeHandler){
0885 ((TypeHandler)receiver).setSelected(((Boolean)aValue).booleanValue());
0886 }
0887
0888 break;
0889 default:
0890 break;
0891 }
0892 }
0893 }//public Object getValueAt(int row, int column)
0894
0895 protected class SetsTableCellRenderer implements TableCellRenderer{
0896 public SetsTableCellRenderer(){
0897 typeLabel = new JLabel(){
0898 public void repaint(long tm, int x, int y, int width, int height){}
0899 public void repaint(Rectangle r){}
0900 public void validate(){}
0901 public void revalidate(){}
0902 protected void firePropertyChange(String propertyName,
0903 Object oldValue,
0904 Object newValue){}
0905 };
0906 typeLabel.setOpaque(true);
0907 typeLabel.setBorder(BorderFactory.createCompoundBorder(
0908 BorderFactory.createMatteBorder(0, 5, 0, 0,
0909 mainTable.getBackground()),
0910 BorderFactory.createEmptyBorder(0, 5, 0, 5)));
0911 // typeLabel.setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 5));
0912
0913
0914 setLabel = new JLabel(){
0915 public void repaint(long tm, int x, int y, int width, int height){}
0916 public void repaint(Rectangle r){}
0917 public void validate(){}
0918 public void revalidate(){}
0919 protected void firePropertyChange(String propertyName,
0920 Object oldValue,
0921 Object newValue){}
0922 };
0923 setLabel.setOpaque(true);
0924 setLabel.setFont(setLabel.getFont().deriveFont(Font.BOLD));
0925 setLabel.setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 5));
0926
0927
0928 typeChk = new JCheckBox(){
0929 public void repaint(long tm, int x, int y, int width, int height){}
0930 public void repaint(Rectangle r){}
0931 public void validate(){}
0932 public void revalidate(){}
0933 protected void firePropertyChange(String propertyName,
0934 Object oldValue,
0935 Object newValue){}
0936 };
0937 typeChk.setOpaque(true);
0938 // typeChk.setBorder(BorderFactory.createEmptyBorder(0, 15, 0, 0));
0939
0940 setChk = new JCheckBox(){
0941 public void repaint(long tm, int x, int y, int width, int height){}
0942 public void repaint(Rectangle r){}
0943 public void validate(){}
0944 public void revalidate(){}
0945 protected void firePropertyChange(String propertyName,
0946 Object oldValue,
0947 Object newValue){}
0948 };
0949 setChk.setSelectedIcon(MainFrame.getIcon("expanded"));
0950 setChk.setIcon(MainFrame.getIcon("closed"));
0951 setChk.setMaximumSize(setChk.getMinimumSize());
0952 setChk.setOpaque(true);
0953
0954 normalBorder = BorderFactory.createLineBorder(
0955 mainTable.getBackground(), 2);
0956 selectedBorder = BorderFactory.createLineBorder(
0957 mainTable.getSelectionBackground(), 2);
0958 }
0959
0960 public Component getTableCellRendererComponent(JTable table,
0961 Object value,
0962 boolean isSelected,
0963 boolean hasFocus,
0964 int row,
0965 int column){
0966
0967 value = tableRows.get(row);
0968 if(value instanceof SetHandler){
0969 SetHandler sHandler = (SetHandler)value;
0970 switch(column){
0971 case NAME_COL:
0972 setLabel.setText(sHandler.set.getName());
0973 setLabel.setBackground(isSelected ?
0974 table.getSelectionBackground() :
0975 table.getBackground());
0976 return setLabel;
0977 case SELECTED_COL:
0978 setChk.setSelected(sHandler.isExpanded());
0979 setChk.setBackground(isSelected ?
0980 table.getSelectionBackground() :
0981 table.getBackground());
0982 setChk.setEnabled(sHandler.typeHandlers.size() > 0);
0983 return setChk;
0984 }
0985 }else if(value instanceof TypeHandler){
0986 TypeHandler tHandler = (TypeHandler)value;
0987 switch(column){
0988 case NAME_COL:
0989 typeLabel.setBackground(tHandler.colour);
0990 typeLabel.setText(tHandler.name);
0991 typeLabel.setBorder(isSelected ? selectedBorder : normalBorder);
0992 return typeLabel;
0993 case SELECTED_COL:
0994 typeChk.setBackground(isSelected ?
0995 table.getSelectionBackground() :
0996 table.getBackground());
0997 typeChk.setSelected(tHandler.isSelected());
0998 return typeChk;
0999 }
1000 }
1001 typeLabel.setText("?");
1002 return typeLabel;
1003 //bugcheck!
1004 }
1005
1006 protected JLabel typeLabel;
1007 protected JLabel setLabel;
1008 protected JCheckBox setChk;
1009 protected JCheckBox typeChk;
1010 protected Border selectedBorder;
1011 protected Border normalBorder;
1012 }
1013
1014 protected class SetsTableCellEditor extends AbstractCellEditor
1015 implements TableCellEditor{
1016 public SetsTableCellEditor(){
1017 setChk = new JCheckBox();
1018 setChk.setSelectedIcon(MainFrame.getIcon("expanded"));
1019 setChk.setIcon(MainFrame.getIcon("closed"));
1020 // setChk.setMaximumSize(setChk.getMinimumSize());
1021 setChk.setOpaque(true);
1022 setChk.addActionListener(new ActionListener(){
1023 public void actionPerformed(ActionEvent evt){
1024 fireEditingStopped();
1025 }
1026 });
1027 typeChk = new JCheckBox();
1028 typeChk.setOpaque(false);
1029 // typeChk.setBorder(BorderFactory.createEmptyBorder(0, 15, 0, 0));
1030 typeChk.addActionListener(new ActionListener(){
1031 public void actionPerformed(ActionEvent evt){
1032 fireEditingStopped();
1033 }
1034 });
1035 }
1036
1037 public Component getTableCellEditorComponent(JTable table,
1038 Object value,
1039 boolean isSelected,
1040 int row,
1041 int column){
1042 value = tableRows.get(row);
1043 if(value instanceof SetHandler){
1044 SetHandler sHandler = (SetHandler)value;
1045 switch(column){
1046 case NAME_COL: return null;
1047 case SELECTED_COL:
1048 setChk.setSelected(sHandler.isExpanded());
1049 setChk.setEnabled(sHandler.typeHandlers.size() > 0);
1050 setChk.setBackground(isSelected ?
1051 table.getSelectionBackground() :
1052 table.getBackground());
1053 currentChk = setChk;
1054 return setChk;
1055 }
1056 }else if(value instanceof TypeHandler){
1057 TypeHandler tHandler = (TypeHandler)value;
1058 switch(column){
1059 case NAME_COL: return null;
1060 case SELECTED_COL:
1061 // typeChk.setBackground(tHandler.colour);
1062 typeChk.setSelected(tHandler.isSelected());
1063 currentChk = typeChk;
1064 return typeChk;
1065 }
1066 }
1067 return null;
1068 }
1069
1070 public boolean stopCellEditing(){
1071 return true;
1072 }
1073
1074 public Object getCellEditorValue(){
1075 return new Boolean(currentChk.isSelected());
1076 }
1077
1078 public boolean shouldSelectCell(EventObject anEvent){
1079 return false;
1080 }
1081
1082 public boolean isCellEditable(EventObject anEvent){
1083 return true;
1084 }
1085
1086 JCheckBox currentChk;
1087 JCheckBox setChk;
1088 JCheckBox typeChk;
1089 }
1090
1091
1092 /**
1093 * Stores the data related to an annotation set
1094 */
1095 public class SetHandler{
1096 SetHandler(AnnotationSet set){
1097 this.set = set;
1098 typeHandlers = new ArrayList<TypeHandler>();
1099 typeHandlersByType = new HashMap();
1100 List typeNames = new ArrayList(set.getAllTypes());
1101 Collections.sort(typeNames);
1102 Iterator typIter = typeNames.iterator();
1103 while(typIter.hasNext()){
1104 String name = (String)typIter.next();
1105 TypeHandler tHandler = new TypeHandler(this, name);
1106 tHandler.annotationCount = set.get(name).size();
1107 typeHandlers.add(tHandler);
1108 typeHandlersByType.put(name, tHandler);
1109 }
1110 set.addAnnotationSetListener(AnnotationSetsView.this);
1111 }
1112
1113 public void cleanup(){
1114 set.removeAnnotationSetListener(AnnotationSetsView.this);
1115 typeHandlers.clear();
1116 }
1117
1118 /**
1119 * Notifies this set handler that a new type of annotations has been created
1120 * @param type the new type of annotations
1121 * @return the new TypeHandler created as a result
1122 */
1123 public TypeHandler newType(String type){
1124 //create a new TypeHandler
1125 TypeHandler tHandler = new TypeHandler(this, type);
1126 //add it to the list at the right position
1127 int pos = 0;
1128 for(;
1129 pos < typeHandlers.size() &&
1130 ((TypeHandler)typeHandlers.get(pos)).name.compareTo(type) <= 0;
1131 pos++);
1132 typeHandlers.add(pos, tHandler);
1133 typeHandlersByType.put(type, tHandler);
1134 //preserve table selection
1135 int row = mainTable.getSelectedRow();
1136 int setRow = tableRows.indexOf(this);
1137 if(typeHandlers.size() == 1)
1138 tableModel.fireTableRowsUpdated(setRow, setRow);
1139 if(expanded){
1140 tableRows.add(setRow + pos + 1, tHandler);
1141 tableModel.fireTableRowsInserted(setRow + pos + 1,
1142 setRow + pos + 1);
1143 }
1144 //restore selection if any
1145 if(row != -1) mainTable.getSelectionModel().setSelectionInterval(row, row);
1146 //select the newly created type if previously requested
1147 TypeSpec typeSpec = new TypeSpec(set.getName(), type);
1148 if(visibleAnnotationTypes.remove(typeSpec)){
1149 tHandler.setSelected(true);
1150 }
1151 return tHandler;
1152 }
1153
1154 public void removeType(TypeHandler tHandler){
1155 int setRow = tableRows.indexOf(this);
1156 int pos = typeHandlers.indexOf(tHandler);
1157 if(setRow != -1 && pos != -1){
1158 typeHandlers.remove(pos);
1159 typeHandlersByType.remove(tHandler.name);
1160 //preserve table selection
1161 int row = mainTable.getSelectedRow();
1162 if(expanded){
1163 tableRows.remove(setRow + pos + 1);
1164 tableModel.fireTableRowsDeleted(setRow + pos + 1, setRow + pos + 1);
1165 // if(row >= (setRow + pos + 1)) row--;
1166 }
1167 if(typeHandlers.isEmpty()){
1168 //the set has no more handlers
1169 setExpanded(false);
1170 tableModel.fireTableRowsUpdated(setRow, setRow);
1171 }
1172 //restore selection if any
1173 if(row != -1){
1174 if(mainTable.getRowCount() <= row){
1175 row = mainTable.getRowCount() -1;
1176 }
1177 mainTable.getSelectionModel().setSelectionInterval(row, row); }
1178 }
1179 }
1180
1181 public void removeType(String type){
1182 removeType((TypeHandler)typeHandlersByType.get(type));
1183 }
1184
1185 public TypeHandler getTypeHandler(String type){
1186 return (TypeHandler)typeHandlersByType.get(type);
1187 }
1188
1189 public void setExpanded(boolean expanded){
1190 if(this.expanded == expanded) return;
1191 this.expanded = expanded;
1192 int myPosition = tableRows.indexOf(this);
1193 if(expanded){
1194 //expand
1195 tableRows.addAll(myPosition + 1, typeHandlers);
1196 tableModel.fireTableRowsInserted(myPosition + 1,
1197 myPosition + 1 + typeHandlers.size());
1198 }else{
1199 //collapse
1200 for(int i = 0; i < typeHandlers.size(); i++){
1201 tableRows.remove(myPosition + 1);
1202 }
1203 tableModel.fireTableRowsDeleted(myPosition + 1,
1204 myPosition + 1 + typeHandlers.size());
1205 }
1206 tableModel.fireTableRowsUpdated(myPosition, myPosition);
1207 }
1208
1209 public boolean isExpanded(){
1210 return expanded;
1211 }
1212
1213
1214 AnnotationSet set;
1215 List<TypeHandler> typeHandlers;
1216 Map typeHandlersByType;
1217 private boolean expanded = false;
1218 }
1219
1220 public class TypeHandler{
1221 TypeHandler (SetHandler setHandler, String name){
1222 this.setHandler = setHandler;
1223 this.name = name;
1224 colour = getColor(setHandler.set.getName(),name);
1225 hghltTagsForAnnId = new HashMap<Integer, Object>();
1226 annListTagsForAnn = new HashMap();
1227 changeColourAction = new ChangeColourAction();
1228 annotationCount = 0;
1229 }
1230
1231 /**
1232 * @return the colour
1233 */
1234 public Color getColour() {
1235 return colour;
1236 }
1237
1238 public void setColour(Color colour){
1239 if(this.colour.equals(colour)) return;
1240 this.colour = colour;
1241 saveColor(setHandler.set.getName(),name, colour);
1242 if(isSelected()){
1243 //redraw the highlights
1244 //hide highlights
1245 textView.removeHighlights(hghltTagsForAnnId.values());
1246 hghltTagsForAnnId.clear();
1247 //show highlights
1248 List<Annotation> annots = new ArrayList<Annotation>(
1249 setHandler.set.get(name));
1250 List<AnnotationData>aDataList = new ArrayList<AnnotationData>();
1251 for(Annotation ann : annots){
1252 aDataList.add(new AnnotationDataImpl(setHandler.set, ann));
1253 }
1254 List tags = textView.addHighlights(aDataList, TypeHandler.this.colour);
1255 for(int i = 0; i < annots.size(); i++){
1256 hghltTagsForAnnId.put(annots.get(i).getId(), tags.get(i));
1257 }
1258 }
1259 //update the table display
1260 int row = tableRows.indexOf(this);
1261 if(row >= 0) tableModel.fireTableRowsUpdated(row, row);
1262
1263 if (stackView.isActive()) stackView.updateStackView();
1264 }
1265
1266 public void setSelected(boolean selected){
1267 if(this.selected == selected) return;
1268 this.selected = selected;
1269 final List<Annotation> annots = new ArrayList<Annotation>(setHandler.set.get(name));
1270 if(selected){
1271 //make sure set is expanded
1272 setHandler.setExpanded(true);
1273 //add to the list view
1274 annListTagsForAnn.clear();
1275 List<AnnotationData> listTags =
1276 listView.addAnnotations(annots, setHandler.set);
1277 for(AnnotationData aData: listTags)
1278 annListTagsForAnn.put(aData.getAnnotation().getId(), aData);
1279 //show highlights
1280 hghltTagsForAnnId.clear();
1281 // List tags = textView.addHighlights(annots, setHandler.set, colour);
1282 List tags = textView.addHighlights(listTags, colour);
1283 for(int i = 0; i < annots.size(); i++){
1284 hghltTagsForAnnId.put(annots.get(i).getId(), tags.get(i));
1285 }
1286 }else{
1287 //hide highlights
1288 try{
1289 listView.removeAnnotations(annListTagsForAnn.values());
1290 textView.removeHighlights(hghltTagsForAnnId.values());
1291 }finally{
1292 hghltTagsForAnnId.clear();
1293 annListTagsForAnn.clear();
1294 }
1295 }
1296 //update the table display
1297 int row = tableRows.indexOf(this);
1298 tableModel.fireTableRowsUpdated(row, row);
1299 saveType(setHandler.set.getName(), name, selected);
1300 //update the stack view
1301 stackView.updateStackView();
1302 }
1303
1304 public boolean isSelected(){
1305 return selected;
1306 }
1307
1308 /**
1309 * Notifies this type handler that a new annotation was created of the
1310 * right type
1311 * @param ann
1312 */
1313 public void annotationAdded(final Annotation ann){
1314 annotationCount++;
1315 if(selected){
1316 //add new highlight
1317 if(!hghltTagsForAnnId.containsKey(ann.getId()))
1318 hghltTagsForAnnId.put(ann.getId(),
1319 textView.addHighlight(
1320 new AnnotationDataImpl(setHandler.set, ann),
1321 colour));
1322 if(!annListTagsForAnn.containsKey(ann.getId())){
1323 annListTagsForAnn.put(ann.getId(),
1324 listView.addAnnotation(ann, setHandler.set));
1325 }
1326 //update the stack view
1327 stackView.updateStackView();
1328 }
1329 }
1330
1331 /**
1332 * Notifies this type handler that an annotation has been removed
1333 * @param ann the removed annotation
1334 */
1335 public void annotationRemoved(Annotation ann){
1336 annotationCount--;
1337 if(selected){
1338 //single annotation removal
1339 Object tag = hghltTagsForAnnId.remove(ann.getId());
1340 if(tag != null) textView.removeHighlight(tag);
1341 AnnotationData listTag = annListTagsForAnn.remove(ann.getId());
1342 if(tag != null) listView.removeAnnotation(listTag);
1343 //update the stack view
1344 stackView.updateStackView();
1345 }
1346 //if this was the last annotation of this type then the handler is no
1347 //longer required
1348 if(annotationCount == 0){
1349 setHandler.removeType(TypeHandler.this);
1350 }
1351 }
1352
1353 protected void repairHighlights(int start, int end){
1354 //map from tag to annotation
1355 List tags = new ArrayList(hghltTagsForAnnId.size());
1356 List<Annotation> annots = new ArrayList<Annotation>(hghltTagsForAnnId.size());
1357 Iterator annIter = hghltTagsForAnnId.keySet().iterator();
1358 while(annIter.hasNext()){
1359 Annotation ann = setHandler.set.get((Integer)annIter.next());
1360 // editing the text sometimes leads to annotations being deleted
1361 if(ann == null) continue;
1362 int annStart = ann.getStartNode().getOffset().intValue();
1363 int annEnd = ann.getEndNode().getOffset().intValue();
1364 if((annStart <= start && start <= annEnd) ||
1365 (start <= annStart && annStart <= end)){
1366 if(!hghltTagsForAnnId.containsKey(ann.getId())){
1367 System.out.println("Error!!!");
1368 }
1369 tags.add(hghltTagsForAnnId.get(ann.getId()));
1370 annots.add(ann);
1371 }
1372 }
1373 for(int i = 0; i < tags.size(); i++){
1374 Object tag = tags.get(i);
1375 Annotation ann = annots.get(i);
1376 try{
1377 textView.moveHighlight(tag,
1378 ann.getStartNode().getOffset().intValue(),
1379 ann.getEndNode().getOffset().intValue());
1380 }catch(BadLocationException ble){
1381 //this should never happen as the offsets come from an annotation
1382 }
1383 }
1384 }
1385
1386
1387 protected class ChangeColourAction extends AbstractAction{
1388 public ChangeColourAction(){
1389 super("Change colour");
1390 putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke("ENTER"));
1391 }
1392
1393 public void actionPerformed(ActionEvent evt){
1394 Color col = JColorChooser.showDialog(mainTable,
1395 "Select colour for \"" + name + "\"",
1396 colour);
1397 if(col != null){
1398 Color colAlpha = new Color(col.getRed(), col.getGreen(),
1399 col.getBlue(), 128);
1400 setColour(colAlpha);
1401 }
1402 }
1403 }
1404
1405 ChangeColourAction changeColourAction;
1406 boolean selected;
1407 /**
1408 * Map from annotation ID (which is immutable) to highlight tag
1409 */
1410 Map<Integer, Object> hghltTagsForAnnId;
1411
1412 /**
1413 * Map from annotation ID (which is immutable) to AnnotationListView tag
1414 */
1415 Map<Integer, AnnotationData> annListTagsForAnn;
1416
1417 String name;
1418 SetHandler setHandler;
1419 Color colour;
1420 int annotationCount;
1421 }
1422
1423 /**
1424 * A class storing the identifying information for an annotation type (i.e.
1425 * the set name and the type).
1426 * @author Valentin Tablan (valyt)
1427 *
1428 */
1429 private static class TypeSpec{
1430 private String setName;
1431
1432 private String type;
1433
1434 public TypeSpec(String setName, String type) {
1435 super();
1436 this.setName = setName;
1437 this.type = type;
1438 }
1439
1440 @Override
1441 public int hashCode() {
1442 final int PRIME = 31;
1443 int result = 1;
1444 result = PRIME * result + ((setName == null) ? 0 : setName.hashCode());
1445 result = PRIME * result + ((type == null) ? 0 : type.hashCode());
1446 return result;
1447 }
1448
1449 @Override
1450 public boolean equals(Object obj) {
1451 if(this == obj) return true;
1452 if(obj == null) return false;
1453 if(getClass() != obj.getClass()) return false;
1454 final TypeSpec other = (TypeSpec)obj;
1455 if(setName == null) {
1456 if(other.setName != null) return false;
1457 }
1458 else if(!setName.equals(other.setName)) return false;
1459 if(type == null) {
1460 if(other.type != null) return false;
1461 }
1462 else if(!type.equals(other.type)) return false;
1463 return true;
1464 }
1465 }
1466
1467
1468 /**
1469 * A mouse listener used for events in the text view.
1470 */
1471 protected class TextMouseListener implements MouseInputListener{
1472 public void mouseDragged(MouseEvent e){
1473 //do not create annotations while dragging
1474 mouseMovementTimer.stop();
1475 }
1476
1477 public void mouseMoved(MouseEvent e){
1478 //this triggers select annotation leading to edit annotation or new
1479 //annotation actions
1480 //ignore movement if CTRL pressed or dragging
1481 int modEx = e.getModifiersEx();
1482 if((modEx & MouseEvent.CTRL_DOWN_MASK) != 0){
1483 mouseMovementTimer.stop();
1484 return;
1485 }
1486 if((modEx & MouseEvent.BUTTON1_DOWN_MASK) != 0){
1487 mouseMovementTimer.stop();
1488 return;
1489 }
1490 //check the text location is real
1491 int textLocation = textPane.viewToModel(e.getPoint());
1492 try {
1493 Rectangle viewLocation = textPane.modelToView(textLocation);
1494 //expand the rectangle a bit
1495 int error = 10;
1496 viewLocation = new Rectangle(viewLocation.x - error,
1497 viewLocation.y - error,
1498 viewLocation.width + 2*error,
1499 viewLocation.height + 2*error);
1500 if(viewLocation.contains(e.getPoint())){
1501 mouseStoppedMovingAction.setTextLocation(textLocation);
1502 }else{
1503 mouseStoppedMovingAction.setTextLocation(-1);
1504 }
1505 }
1506 catch(BadLocationException e1) {
1507 //this should not happen, as the text location comes from the text view
1508 //if it does. we'll just ignore it.
1509 // throw new LuckyException(e1);
1510 }finally{
1511 mouseMovementTimer.restart();
1512 }
1513 }
1514
1515 public void mouseClicked(MouseEvent e){
1516 }
1517
1518 public void mousePressed(MouseEvent e){
1519
1520 }
1521 public void mouseReleased(MouseEvent e){
1522
1523 }
1524
1525 public void mouseEntered(MouseEvent e){
1526
1527 }
1528
1529 public void mouseExited(MouseEvent e){
1530 mouseMovementTimer.stop();
1531 }
1532 }//protected class TextMouseListener implements MouseInputListener
1533
1534
1535
1536 protected class NewAnnotationSetAction extends AbstractAction{
1537 public NewAnnotationSetAction(){
1538 super("New");
1539 putValue(SHORT_DESCRIPTION, "Creates a new annotation set");
1540 }
1541
1542 public void actionPerformed(ActionEvent evt){
1543 String name = newSetNameTextField.getText();
1544 newSetNameTextField.setText("");
1545 if(name != null && name.length() > 0){
1546 AnnotationSet set = document.getAnnotations(name);
1547 //select the newly added set
1548 Iterator rowsIter = tableRows.iterator();
1549 int row = -1;
1550 for(int i = 0; i < tableRows.size() && row < 0; i++){
1551 if(tableRows.get(i) instanceof SetHandler &&
1552 ((SetHandler)tableRows.get(i)).set == set) row = i;
1553 }
1554 if(row >= 0) mainTable.getSelectionModel().setSelectionInterval(row, row);
1555 }
1556 }
1557 }
1558
1559 protected class NewAnnotationAction extends AbstractAction{
1560 public NewAnnotationAction(String selection){
1561 super("Create new annotation");
1562 putValue(SHORT_DESCRIPTION, "Creates a new annotation from the" +
1563 " selection: [" + Strings.crop(selection, 30) + "]");
1564 }
1565 public void actionPerformed(ActionEvent evt){
1566 if(annotationEditor == null) return;
1567 int start = textPane.getSelectionStart();
1568 int end = textPane.getSelectionEnd();
1569 if(start != end){
1570 textPane.setSelectionStart(start);
1571 textPane.setSelectionEnd(start);
1572 //create a new annotation
1573 //find the selected set
1574 int row = mainTable.getSelectedRow();
1575 //select the default annotation set if none selected
1576 if(row < 0) row = 0;
1577 //find the set handler
1578 while(!(tableRows.get(row) instanceof SetHandler)) row --;
1579 AnnotationSet set = ((SetHandler)tableRows.get(row)).set;
1580 try{
1581 Integer annId = set.add(new Long(start), new Long(end),
1582 lastAnnotationType, Factory.newFeatureMap());
1583 Annotation ann = set.get(annId);
1584 //select the annotation set in the tree view and expand it
1585 //to avoid the next annotation to be always in the default set
1586 if (tableRows.get(row) instanceof SetHandler) {
1587 ((SetHandler)tableRows.get(row)).setExpanded(true);
1588 mainTable.getSelectionModel().setSelectionInterval(row, row);
1589 }
1590 //make sure new annotation is visible
1591 setTypeSelected(set.getName(), ann.getType(), true);
1592 //edit the new annotation
1593 pendingEvents.offer(new PerformActionEvent(
1594 new EditAnnotationAction(new AnnotationDataImpl(set, ann))));
1595 eventMinder.restart();
1596 }catch(InvalidOffsetException ioe){
1597 //this should never happen
1598 throw new GateRuntimeException(ioe);
1599 }
1600 }
1601 }
1602 }
1603
1604 /**
1605 * The beginning is the same as {@link NameBearerHandle.SaveAsXmlAction}.
1606 */
1607 protected class SavePreserveFormatAction extends AbstractAction{
1608 public SavePreserveFormatAction(){
1609 super("Save Preserving Format");
1610 putValue(SHORT_DESCRIPTION,
1611 "Saves original markups and highlighted annotations");
1612 }
1613
1614 public void actionPerformed(ActionEvent evt){
1615 Runnable runableAction = new Runnable(){
1616 public void run(){
1617 XJFileChooser fileChooser = MainFrame.getFileChooser();
1618 ExtensionFileFilter filter =
1619 new ExtensionFileFilter("XML files", "xml", "gml");
1620 fileChooser.addChoosableFileFilter(filter);
1621 fileChooser.setMultiSelectionEnabled(false);
1622 fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
1623 fileChooser.setDialogTitle("Select document to save ...");
1624
1625 if (document.getSourceUrl() != null) {
1626 String fileName = "";
1627 try {
1628 fileName = document.getSourceUrl().toURI().getPath().trim();
1629 } catch (URISyntaxException e) {
1630 fileName = document.getSourceUrl().getPath().trim();
1631 }
1632 if (fileName.equals("") || fileName.equals("/")) {
1633 if (document.getNamedAnnotationSets().containsKey("Original markups")
1634 && !document.getAnnotations("Original markups").get("title").isEmpty()) {
1635 // use the title annotation if any
1636 try {
1637 fileName = document.getContent().getContent(
1638 document.getAnnotations("Original markups").get("title").firstNode().getOffset(),
1639 document.getAnnotations("Original markups").get("title").lastNode().getOffset())
1640 .toString();
1641 } catch(InvalidOffsetException e) {
1642 e.printStackTrace();
1643 }
1644 } else {
1645 fileName = document.getSourceUrl().toString();
1646 }
1647 // cleans the file name
1648 fileName = fileName.replaceAll("/", "_");
1649 } else {
1650 // replaces the extension with .xml
1651 fileName = fileName.replaceAll("\\.[a-zA-Z]{1,4}$", ".xml");
1652 }
1653 // cleans the file name
1654 fileName = fileName.replaceAll("[^/a-zA-Z0-9._-]", "_");
1655 fileName = fileName.replaceAll("__+", "_");
1656 // adds a .xml extension if not present
1657 if (!fileName.endsWith(".xml")) { fileName += ".xml"; }
1658 File file = new File(fileName);
1659 fileChooser.ensureFileIsVisible(file);
1660 fileChooser.setSelectedFile(file);
1661 }
1662
1663 if(fileChooser.showSaveDialog(owner) == JFileChooser.APPROVE_OPTION) {
1664 File selectedFile = fileChooser.getSelectedFile();
1665 if(selectedFile == null) return;
1666 StatusListener sListener = (StatusListener)Gate.getListeners().
1667 get("gate.event.StatusListener");
1668 if (sListener != null)
1669 sListener.statusChanged("Please wait while dumping annotations"+
1670 "in the original format to " + selectedFile.toString() + " ...");
1671 // This method construct a set with all annotations that need to be
1672 // dumped as Xml. If the set is null then only the original markups
1673 // are dumped.
1674 Set annotationsToDump = new HashSet();
1675 Iterator setIter = setHandlers.iterator();
1676 while(setIter.hasNext()){
1677 SetHandler sHandler = (SetHandler)setIter.next();
1678 Iterator typeIter = sHandler.typeHandlers.iterator();
1679 while(typeIter.hasNext()){
1680 TypeHandler tHandler = (TypeHandler)typeIter.next();
1681 if(tHandler.isSelected()){
1682 annotationsToDump.addAll(sHandler.set.get(tHandler.name));
1683 }
1684 }
1685 }
1686
1687 try{
1688 // Prepare to write into the xmlFile using the original encoding
1689 String encoding = ((TextualDocument)document).getEncoding();
1690
1691 OutputStreamWriter writer = new OutputStreamWriter(
1692 new FileOutputStream(selectedFile),
1693 encoding);
1694
1695 //determine if the features need to be saved first
1696 Boolean featuresSaved =
1697 Gate.getUserConfig().getBoolean(
1698 GateConstants.SAVE_FEATURES_WHEN_PRESERVING_FORMAT);
1699 boolean saveFeatures = true;
1700 if (featuresSaved != null)
1701 saveFeatures = featuresSaved.booleanValue();
1702 // Write with the toXml() method
1703 writer.write(
1704 document.toXml(annotationsToDump, saveFeatures));
1705 writer.flush();
1706 writer.close();
1707 document.setSourceUrl(selectedFile.toURI().toURL());
1708 } catch (Exception ex){
1709 ex.printStackTrace(Out.getPrintWriter());
1710 }// End try
1711 if (sListener != null)
1712 sListener.statusChanged("Finished dumping into the "+
1713 "file : " + selectedFile.toString());
1714 }// End if
1715 }// End run()
1716 };// End Runnable
1717 Thread thread = new Thread(runableAction, "");
1718 thread.setPriority(Thread.MIN_PRIORITY);
1719 thread.start();
1720 }
1721 }
1722
1723 /**
1724 * A fake GATE Event used to wrap a {@link Runnable} value. This is used for
1725 * queueing actions to the document UI update timer.
1726 */
1727 private class PerformActionEvent extends GateEvent{
1728 public PerformActionEvent(Runnable runnable){
1729 super(AnnotationSetsView.this, 0);
1730 this.runnable = runnable;
1731 this.action = null;
1732 }
1733
1734 public PerformActionEvent(Action action){
1735 super(AnnotationSetsView.this, 0);
1736 this.runnable = null;
1737 this.action = action;
1738 }
1739
1740 /**
1741 * Runs the action (or runnable) enclosed by this event.
1742 */
1743 public void run(){
1744 if(runnable != null){
1745 runnable.run();
1746 }else if(action != null){
1747 action.actionPerformed(null);
1748 }
1749 }
1750
1751 private Action action;
1752
1753 private Runnable runnable;
1754 }
1755
1756 protected class HandleDocumentEventsAction extends AbstractAction{
1757
1758 public void actionPerformed(ActionEvent ev) {
1759 //see if we need to try again to rebuild from scratch
1760 if(uiDirty){
1761 //a previous call to rebuild has failed; try again
1762 rebuildDisplay();
1763 return;
1764 }
1765 //if too many individual events, then rebuild UI from scratch as it's
1766 //faster.
1767 if(pendingEvents.size() > MAX_EVENTS){
1768 rebuildDisplay();
1769 return;
1770 }
1771 //process the individual events
1772 while(!pendingEvents.isEmpty()){
1773 GateEvent event = pendingEvents.remove();
1774 if(event instanceof DocumentEvent){
1775 DocumentEvent e = (DocumentEvent)event;
1776 if(event.getType() == DocumentEvent.ANNOTATION_SET_ADDED){
1777 String newSetName = e.getAnnotationSetName();
1778 SetHandler sHandler = new SetHandler(document.getAnnotations(newSetName));
1779 //find the right location for the new set
1780 //this is a named set and the first one is always the default one
1781 int i = 0;
1782 if(newSetName != null){
1783 for(i = 1;
1784 i < setHandlers.size() &&
1785 ((SetHandler)setHandlers.get(i)).set.
1786 getName().compareTo(newSetName) <= 0;
1787 i++);
1788 }
1789 setHandlers.add(i, sHandler);
1790 //update the tableRows list
1791 int j = 0;
1792 if(i > 0){
1793 SetHandler previousHandler = (SetHandler)setHandlers.get(i -1);
1794 //find the index for the previous handler - which is guaranteed to exist
1795 for(; tableRows.get(j) != previousHandler; j++);
1796 if(previousHandler.isExpanded()){
1797 j += previousHandler.typeHandlers.size();
1798 }
1799 j++;
1800 }
1801 tableRows.add(j, sHandler);
1802 //update the table view
1803 tableModel.fireTableRowsInserted(j, j);
1804 }else if(event.getType() == DocumentEvent.ANNOTATION_SET_REMOVED){
1805 String setName = e.getAnnotationSetName();
1806 //find the handler and remove it from the list of handlers
1807 SetHandler sHandler = getSetHandler(setName);
1808 if(sHandler != null){
1809 sHandler.set.removeAnnotationSetListener(AnnotationSetsView.this);
1810 //remove highlights if any
1811 Iterator typeIter = sHandler.typeHandlers.iterator();
1812 while(typeIter.hasNext()){
1813 TypeHandler tHandler = (TypeHandler)typeIter.next();
1814 tHandler.setSelected(false);
1815 }
1816 setHandlers.remove(sHandler);
1817 //remove the set from the table
1818 int row = tableRows.indexOf(sHandler);
1819 tableRows.remove(row);
1820 int removed = 1;
1821 //remove the type rows as well
1822 if(sHandler.isExpanded())
1823 for(int i = 0; i < sHandler.typeHandlers.size(); i++){
1824 tableRows.remove(row);
1825 removed++;
1826 }
1827 tableModel.fireTableRowsDeleted(row, row + removed -1);
1828 sHandler.cleanup();
1829 }
1830 }else{
1831 //some other kind of event we don't care about
1832 }
1833 }else if(event instanceof AnnotationSetEvent){
1834 AnnotationSetEvent e = (AnnotationSetEvent)event;
1835 AnnotationSet set = (AnnotationSet)e.getSource();
1836 Annotation ann = e.getAnnotation();
1837 if(event.getType() == AnnotationSetEvent.ANNOTATION_ADDED){
1838 TypeHandler tHandler = getTypeHandler(set.getName(), ann.getType());
1839 if(tHandler == null){
1840 //new type for this set
1841 SetHandler sHandler = getSetHandler(set.getName());
1842 tHandler = sHandler.newType(ann.getType());
1843 }
1844 tHandler.annotationAdded(ann);
1845 }else if(event.getType() == AnnotationSetEvent.ANNOTATION_REMOVED){
1846 TypeHandler tHandler = getTypeHandler(set.getName(), ann.getType());
1847 if(tHandler != null) tHandler.annotationRemoved(ann);
1848 }else{
1849 //some other kind of event we don't care about
1850 }
1851 }else if(event instanceof PerformActionEvent){
1852 ((PerformActionEvent)event).run();
1853 }else{
1854 //unknown type of event -> ignore
1855 }
1856 }
1857 }
1858
1859 /**
1860 * This method is used to update the display by reading the associated
1861 * document when it is considered that doing so would be cheaper than
1862 * acting on the events queued
1863 */
1864 protected void rebuildDisplay(){
1865 //if there is a process still running, we may get concurrent modification
1866 //exceptions, in which case we should give up and try again later.
1867 //this method will always run from the UI thread, so no synchronisation
1868 //is necessary
1869 uiDirty = false;
1870 try{
1871 //Ignore all pending events, as we're rebuilding from scratch.
1872 //Rotate once through the whole queue, filtering out events we want
1873 //to ignore.
1874 GateEvent event;
1875 pendingEvents.offer(END_OF_LIST);
1876 while((event = pendingEvents.poll()) != END_OF_LIST){
1877 if(event instanceof DocumentEvent ||
1878 event instanceof AnnotationSetEvent){
1879 //ignore event
1880 }else{
1881 //event of unknown type -> we re-queue it!
1882 pendingEvents.offer(event);
1883 }
1884 }
1885 //store selection state
1886 storeSelectedTypes();
1887 //release all resources
1888 for(SetHandler sHandler : setHandlers){
1889 sHandler.typeHandlers.clear();
1890 sHandler.typeHandlersByType.clear();
1891 sHandler.set.removeAnnotationSetListener(AnnotationSetsView.this);
1892 }
1893 setHandlers.clear();
1894 tableRows.clear();
1895 listView.removeAnnotations(listView.getAllAnnotations());
1896 //update the stack view
1897 stackView.updateStackView();
1898 // textView.removeAllBlinkingHighlights();
1899 //rebuild the UI
1900 populateUI();
1901
1902 //restore the selection
1903 restoreSelectedTypes();
1904 tableModel.fireTableDataChanged();
1905 }catch(Throwable t){
1906 //something happened, we need to give up
1907 uiDirty = true;
1908 // t.printStackTrace();
1909 }
1910 }
1911
1912 boolean uiDirty = false;
1913 /**
1914 * Maximum number of events to treat individually. If we have more pending
1915 * events than this value, the UI will be rebuilt from scratch
1916 */
1917 private static final int MAX_EVENTS = 300;
1918 }
1919
1920 /**
1921 * Used to select an annotation for editing.
1922 *
1923 */
1924 protected class MouseStoppedMovingAction extends AbstractAction{
1925
1926 public void actionPerformed(ActionEvent evt){
1927 if(annotationEditor == null) return;
1928 //this action either creates a new annotation or starts editing an
1929 //existing one. In either case we need first to make sure that the current
1930 //annotation is finished editing.
1931 if(!annotationEditor.editingFinished()) return;
1932 if(textLocation == -1) return;
1933 JPopupMenu popup = new JPopupMenu();
1934
1935 //check for selection hovering
1936 if(textPane.getSelectedText() != null
1937 && textPane.getSelectionStart() <= textLocation
1938 && textPane.getSelectionEnd() >= textLocation){
1939 //add 'New annotation' to the popup menu
1940 popup.add(new NewAnnotationAction(textPane.getSelectedText()));
1941 popup.addSeparator();
1942 }
1943
1944 //check for annotations at location
1945 for(SetHandler setHandler : setHandlers) {
1946 for(Annotation ann : setHandler.set.get(
1947 Math.max(0l, textLocation-1),
1948 Math.min(document.getContent().size(), textLocation+1))) {
1949 if(setHandler.getTypeHandler(ann.getType()).isSelected()) {
1950 AnnotationDataImpl annotAtPoint =
1951 new AnnotationDataImpl(setHandler.set, ann);
1952 //add annotations to edit to the popup menu
1953 popup.add(new HighlightMenuItem(
1954 new EditAnnotationAction(annotAtPoint),
1955 annotAtPoint.getAnnotation().getStartNode().getOffset().intValue(),
1956 annotAtPoint.getAnnotation().getEndNode().getOffset().intValue(),
1957 popup));
1958 }
1959 }
1960 }
1961
1962 if (popup.getComponentCount() == 0) {
1963 // nothing to do
1964 } else if(popup.getComponentCount() == 1
1965 || (popup.getComponentCount() == 2
1966 && popup.getComponent(1) instanceof JSeparator)) {
1967 //only one annotation, start the editing directly
1968 //or only one selection, add new annotation
1969 ((JMenuItem)popup.getComponent(0)).getAction().actionPerformed(evt);
1970 } else { //mouse hover a selection AND annotation(s)
1971 try{
1972 Rectangle rect = textPane.modelToView(textLocation);
1973 //display the popup
1974 popup.show(textPane, rect.x + 10, rect.y);
1975 }catch(BadLocationException ble){
1976 throw new GateRuntimeException(ble);
1977 }
1978 }
1979 }
1980
1981 public void setTextLocation(int textLocation){
1982 this.textLocation = textLocation;
1983 }
1984 int textLocation;
1985 }//protected class SelectAnnotationAction extends AbstractAction{
1986
1987
1988 /**
1989 * The popup menu items used to select annotations
1990 * Apart from the normal {@link javax.swing.JMenuItem} behaviour, this menu
1991 * item also highlights the annotation which it would select if pressed.
1992 */
1993 protected class HighlightMenuItem extends JMenuItem {
1994 public HighlightMenuItem(Action action, int startOffset, int endOffset,
1995 JPopupMenu popup) {
1996 super(action);
1997 this.start = startOffset;
1998 this.end = endOffset;
1999 this.addMouseListener(new MouseAdapter() {
2000 public void mouseEntered(MouseEvent e) {
2001 showHighlight();
2002 }
2003
2004 public void mouseExited(MouseEvent e) {
2005 removeHighlight();
2006 }
2007 });
2008 popup.addPopupMenuListener(new PopupMenuListener(){
2009 public void popupMenuWillBecomeVisible(PopupMenuEvent e){
2010
2011 }
2012 public void popupMenuCanceled(PopupMenuEvent e){
2013 removeHighlight();
2014 }
2015 public void popupMenuWillBecomeInvisible(PopupMenuEvent e){
2016 removeHighlight();
2017 }
2018
2019
2020 });
2021 }
2022
2023 protected void showHighlight(){
2024 try {
2025 highlight = textPane.getHighlighter().addHighlight(start, end,
2026 DefaultHighlighter.DefaultPainter);
2027 }catch(BadLocationException ble){
2028 throw new GateRuntimeException(ble.toString());
2029 }
2030
2031 }
2032
2033 protected void removeHighlight(){
2034 if(highlight != null){
2035 textPane.getHighlighter().removeHighlight(highlight);
2036 highlight = null;
2037 }
2038
2039 }
2040
2041 int start;
2042 int end;
2043 Action action;
2044 Object highlight;
2045 }
2046
2047
2048
2049 protected class EditAnnotationAction extends AbstractAction{
2050 public EditAnnotationAction(AnnotationData aData){
2051 super(aData.getAnnotation().getType() + " [" +
2052 (aData.getAnnotationSet().getName() == null ? " " :
2053 aData.getAnnotationSet().getName()) +
2054 "]");
2055 putValue(SHORT_DESCRIPTION, aData.getAnnotation().getFeatures().toString());
2056 this.aData = aData;
2057 }
2058
2059 public void actionPerformed(ActionEvent evt){
2060 if(annotationEditor == null) return;
2061 //if the editor is done with the current annotation, we can move to the
2062 //next one
2063 if(annotationEditor.editingFinished()){
2064 //queue an event to set the annotation as selected
2065 selectAnnotation(aData);
2066 //queue an event to show the annotation editor
2067 Runnable action = new Runnable() {
2068 public void run() {
2069 annotationEditor.editAnnotation(aData.getAnnotation(),
2070 aData.getAnnotationSet());
2071 }
2072 };
2073 pendingEvents.offer(new PerformActionEvent(action));
2074 eventMinder.restart();
2075 }
2076 }
2077
2078 private AnnotationData aData;
2079 }
2080
2081 protected class SetSelectedAnnotationsAction extends AbstractAction{
2082 public SetSelectedAnnotationsAction(boolean selected){
2083 String title = (selected) ? "Select all" : "Unselect all";
2084 putValue(NAME, title);
2085 putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke("SPACE"));
2086 this.selected = selected;
2087 }
2088 public void actionPerformed(ActionEvent evt){
2089 List<Object> handlersToSelect = new ArrayList<Object>();
2090 int[] selectedRows = mainTable.getSelectedRows();
2091 Arrays.sort(selectedRows);
2092 for (int row : selectedRows) {
2093 Object handler = tableRows.get(row);
2094 if(handler instanceof SetHandler){
2095 // store the set handler
2096 handlersToSelect.add(0, handler);
2097 } else if(handler instanceof TypeHandler
2098 && !handlersToSelect.contains(((TypeHandler)handler).setHandler)){
2099 // store the type handler
2100 // only if not included in a previous selected set handler
2101 handlersToSelect.add(handlersToSelect.size(), handler);
2102 }
2103 }
2104 for (Object handler : handlersToSelect) {
2105 if(handler instanceof TypeHandler){
2106 TypeHandler tHandler = (TypeHandler)handler;
2107 tHandler.setSelected(selected);
2108 }else if(handler instanceof SetHandler){
2109 SetHandler sHandler = (SetHandler)handler;
2110 for (String setName : sHandler.set.getAllTypes()) {
2111 TypeHandler tHandler = sHandler.getTypeHandler(setName);
2112 tHandler.setSelected(selected);
2113 }
2114 }
2115 }
2116 }
2117 boolean selected;
2118 }
2119
2120 protected class DeleteSelectedAnnotationsAction extends AbstractAction{
2121 public DeleteSelectedAnnotationsAction(String name){
2122 putValue(NAME, name);
2123 putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke("DELETE"));
2124 }
2125 public void actionPerformed(ActionEvent event){
2126 // builds the list of type and set handlers to delete
2127 Vector<String> resourcesToDelete = new Vector<String>();
2128 List<Object> handlersToDelete = new ArrayList<Object>();
2129 int[] selectedRows = mainTable.getSelectedRows();
2130 Arrays.sort(selectedRows);
2131 for (int row : selectedRows) {
2132 Object handler = tableRows.get(row);
2133 if(handler instanceof SetHandler){
2134 // store the set handler
2135 handlersToDelete.add(0, handler);
2136 String setName = ((SetHandler) handler).set.getName();
2137 setName = (setName == null)? "Default set" : setName;
2138 resourcesToDelete.add("set: " + setName);
2139 } else if(handler instanceof TypeHandler
2140 && !handlersToDelete.contains(((TypeHandler)handler).setHandler)){
2141 // store the type handler
2142 // only if not included in a previous selected set handler
2143 handlersToDelete.add(handlersToDelete.size(), handler);
2144 String setName = ((TypeHandler) handler).setHandler.set.getName();
2145 setName = (setName == null)? "Default set" : setName;
2146 resourcesToDelete.add("type: " + ((TypeHandler) handler).name
2147 + " in set: " + setName);
2148 }
2149 }
2150 if ((event.getModifiers() & ActionEvent.SHIFT_MASK)
2151 != ActionEvent.SHIFT_MASK
2152 && (event.getModifiers() & InputEvent.BUTTON1_MASK)
2153 != InputEvent.BUTTON1_MASK) {
2154 // shows a confirm dialog to delete types and sets
2155 JList list = new JList(resourcesToDelete);
2156 list.setVisibleRowCount(Math.min(resourcesToDelete.size()+1, 10));
2157 int choice = JOptionPane.showOptionDialog(MainFrame.getInstance(), new
2158 Object[]{"Are you sure you want to delete the following annotations?",
2159 '\n', new JScrollPane(list),
2160 "<html><i>You can use Shift+Delete to bypass this dialog.</i>\n\n"},
2161 "Delete annotations",
2162 JOptionPane.DEFAULT_OPTION, JOptionPane.QUESTION_MESSAGE, null,
2163 new String[]{"Delete annotations", "Cancel"}, "Cancel");
2164 if (choice == JOptionPane.CLOSED_OPTION || choice == 1) { return; }
2165 }
2166 // deletes types and sets
2167 for (Object handler : handlersToDelete) {
2168 if(handler instanceof SetHandler){
2169 SetHandler sHandler = (SetHandler)handler;
2170 if(sHandler.set == document.getAnnotations()){
2171 //the default annotation set - clear
2172 for (Annotation annotation: new HashSet<Annotation>(sHandler.set)) {
2173 sHandler.set.remove(annotation);
2174 }
2175 }else{
2176 document.removeAnnotationSet(sHandler.set.getName());
2177 }
2178 } else if(handler instanceof TypeHandler){
2179 TypeHandler tHandler = (TypeHandler)handler;
2180 AnnotationSet set = tHandler.setHandler.set;
2181 AnnotationSet toDeleteAS = set.get(tHandler.name);
2182 if(toDeleteAS != null && toDeleteAS.size() > 0){
2183 List<Annotation> toDelete = new ArrayList<Annotation>(toDeleteAS);
2184 set.removeAll(toDelete);
2185 }
2186 }
2187 }
2188 }
2189 }
2190
2191 List<SetHandler> setHandlers;
2192 /** Contains the data of the main table. */
2193 List tableRows;
2194 XJTable mainTable;
2195 SetsTableModel tableModel;
2196 JScrollPane scroller;
2197 JPanel mainPanel;
2198 JTextField newSetNameTextField;
2199
2200 TextualDocumentView textView;
2201 AnnotationListView listView;
2202 AnnotationStackView stackView;
2203 JTextArea textPane;
2204 gate.gui.annedit.OwnedAnnotationEditor annotationEditor;
2205 NewAnnotationSetAction newSetAction;
2206
2207 /**
2208 * The listener for mouse and mouse motion events in the text view.
2209 */
2210 protected TextMouseListener textMouseListener;
2211
2212 /**
2213 * Listener for property changes on the text pane.
2214 */
2215 protected PropertyChangeListener textChangeListener;
2216
2217 /**
2218 * Stores the list of visible annotation types when the view is inactivated
2219 * so that the selection can be restored when the view is made active again.
2220 * The values are String[2] pairs of form <set name, type>.
2221 */
2222 protected BlockingQueue<TypeSpec> visibleAnnotationTypes;
2223
2224 protected Timer mouseMovementTimer;
2225 /**
2226 * Timer used to handle events coming from the document
2227 */
2228 protected Timer eventMinder;
2229
2230 protected BlockingQueue<GateEvent> pendingEvents;
2231
2232 private static final int MOUSE_MOVEMENT_TIMER_DELAY = 500;
2233 protected MouseStoppedMovingAction mouseStoppedMovingAction;
2234
2235 protected String lastAnnotationType = "_New_";
2236
2237 protected List actions;
2238
2239 protected final static ColorGenerator colourGenerator = new ColorGenerator();
2240 private static final int NAME_COL = 1;
2241 private static final int SELECTED_COL = 0;
2242
2243 /**
2244 * A special GateEvent used as a flag.
2245 */
2246 private static final GateEvent END_OF_LIST = new GateEvent(
2247 AnnotationSetsView.class,
2248 Integer.MAX_VALUE);
2249
2250 private static final int EVENTS_HANDLE_DELAY = 300;
2251
2252 }
|