AnnotationListView.java
001 /*
002  *  Copyright (c) 2009-2010, Ontotext AD.
003  *  Copyright (c) 1995-2010, The University of Sheffield. See the file
004  *  COPYRIGHT.txt in the software or at http://gate.ac.uk/gate/COPYRIGHT.txt
005  *
006  *  This file is part of GATE (see http://gate.ac.uk/), and is free
007  *  software, licenced under the GNU Library General Public License,
008  *  Version 2, June 1991 (in the distribution as file licence.html,
009  *  and also available at http://gate.ac.uk/gate/licence.html).
010  *
011  *  AnnotatioListView.java
012  *
013  *  Valentin Tablan, May 25, 2004
014  *
015  *  $Id: AnnotationListView.java 13614 2011-04-05 14:24:31Z valyt $
016  */
017 
018 package gate.gui.docview;
019 
020 import gate.*;
021 import gate.creole.*;
022 import gate.event.AnnotationEvent;
023 import gate.event.AnnotationListener;
024 import gate.gui.MainFrame;
025 import gate.gui.annedit.*;
026 import gate.swing.XJTable;
027 import gate.util.*;
028 import java.awt.*;
029 import java.awt.event.*;
030 import java.util.*;
031 import java.util.List;
032 import java.util.Timer;
033 import javax.swing.*;
034 import javax.swing.event.*;
035 import javax.swing.table.AbstractTableModel;
036 import javax.swing.text.JTextComponent;
037 
038 /**
039  * A tabular view for a list of annotations.
040  * Used as part of the document viewer to display all the annotation currently
041  * highlighted.
042  */
043 public class AnnotationListView extends AbstractDocumentView
044   implements AnnotationListener, AnnotationList, AnnotationEditorOwner{
045 
046   public AnnotationListView(){
047     annDataList = new ArrayList<AnnotationData>();
048     editorsCache = new HashMap<String, AnnotationVisualResource>();
049   }
050 
051 
052    /**
053     *  (non-Javadoc)
054     @see gate.gui.docview.AnnotationList#getAnnotationAtRow(int)
055     */
056    public AnnotationData getAnnotationAtRow(int row) {
057      return annDataList == null null : annDataList.get(
058              table.rowViewToModel(row));
059    }
060 
061 
062    /* (non-Javadoc)
063     * @see gate.gui.docview.AnnotationList#getSelectionModel()
064     */
065    public ListSelectionModel getSelectionModel() {
066      return table == null null : table.getSelectionModel();
067    }
068 
069 
070    @Override
071    public void cleanup() {
072      super.cleanup();
073      for(AnnotationData aData : annDataList){
074        aData.getAnnotation().removeAnnotationListener(this);
075      }
076      annDataList.clear();
077      textView = null;
078    }
079 
080    /* (non-Javadoc)
081     * @see gate.gui.docview.AbstractDocumentView#initGUI()
082     */
083    protected void initGUI() {
084      tableModel = new AnnotationTableModel();
085      table = new XJTable(tableModel);
086      table.setAutoResizeMode(XJTable.AUTO_RESIZE_LAST_COLUMN);
087      table.setSortable(true);
088      table.setSortedColumn(START_COL);
089      table.setIntercellSpacing(new Dimension(20));
090      table.setEnableHidingColumns(true);
091      scroller = new JScrollPane(table);
092 
093      mainPanel = new JPanel();
094      mainPanel.setLayout(new GridBagLayout());
095      GridBagConstraints constraints = new GridBagConstraints();
096 
097      constraints.gridx = 0;
098      constraints.gridwidth = 4;
099      constraints.gridy = 0;
100      constraints.weightx = 1;
101      constraints.weighty = 1;
102      constraints.fill= GridBagConstraints.BOTH;
103      mainPanel.add(scroller, constraints);
104 
105      constraints.gridx = GridBagConstraints.RELATIVE;
106      constraints.gridwidth = 1;
107      constraints.gridy = 1;
108      constraints.weightx = 0;
109      constraints.weighty = 0;
110      constraints.fill= GridBagConstraints.NONE;
111      constraints.anchor = GridBagConstraints.WEST;
112      statusLabel = new JLabel();
113      mainPanel.add(statusLabel, constraints);
114      constraints.fill= GridBagConstraints.HORIZONTAL;
115      constraints.anchor = GridBagConstraints.EAST;
116      mainPanel.add(Box.createHorizontalStrut(10), constraints);
117      mainPanel.add(new JLabel("Select: "), constraints);
118      filterTextField = new JTextField(20);
119      filterTextField.setToolTipText("Select the rows containing this text.");
120      mainPanel.add(filterTextField, constraints);
121 
122      //get a pointer to the text view used to display
123      //the selected annotations
124      Iterator centralViewsIter = owner.getCentralViews().iterator();
125      while(textView == null && centralViewsIter.hasNext()){
126        DocumentView aView = (DocumentView)centralViewsIter.next();
127        if(aView instanceof TextualDocumentView)
128          textView = (TextualDocumentView)aView;
129      }
130 
131      initListeners();
132    }
133 
134    public Component getGUI(){
135      return mainPanel;
136    }
137 
138    protected void initListeners(){
139 
140         tableModel.addTableModelListener(new TableModelListener(){
141           public void tableChanged(TableModelEvent e){
142             statusLabel.setText(
143                     Integer.toString(tableModel.getRowCount()) +
144                     " Annotations (" +
145                     Integer.toString(table.getSelectedRowCount()) +
146                     " selected)");
147           }
148         });
149 
150         table.getSelectionModel().
151           addListSelectionListener(new ListSelectionListener(){
152             public void valueChanged(ListSelectionEvent e){
153               if(!isActive())return;
154               if(e.getValueIsAdjusting()) return;
155               statusLabel.setText(
156                       Integer.toString(tableModel.getRowCount()) +
157                       " Annotations (" +
158                       Integer.toString(table.getSelectedRowCount()) +
159                       " selected)");
160               //if the new selection is already known about, no more work to do 
161               if(localSelectionUpdatingreturn;
162               //update the list of selected annotations globally
163               int[] viewRows = table.getSelectedRows();
164               List<AnnotationData> selAnns = new ArrayList<AnnotationData>();
165               for(int i = 0; i < viewRows.length; i++){
166                 int modelRow = table.rowViewToModel(viewRows[i]);
167                 if(modelRow >= 0){
168                   selAnns.add(annDataList.get(modelRow));
169                 }
170               }
171               owner.setSelectedAnnotations(selAnns);
172               
173               if(table.getSelectedRowCount() >= 1){
174                 int viewRow = table.getSelectionModel().getLeadSelectionIndex();
175                 if(table.getSelectionModel().isSelectedIndex(viewRow)){
176                   int modelRow = table.rowViewToModel(viewRow);
177                   AnnotationData aHandler = annDataList.get(modelRow);
178                   //scroll to show the last highlight
179                   if(aHandler != null && aHandler.getAnnotation() != null)
180                     textView.scrollAnnotationToVisible(aHandler.getAnnotation());
181                 }else{
182                   //last operation was a remove selection
183                   //do nothing
184                 }
185               }
186             }
187         });
188 
189         table.addMouseListener(new MouseAdapter() {
190           public void mouseClicked(MouseEvent me) {
191             processMouseEvent(me);
192           }
193           public void mouseReleased(MouseEvent me) {
194             processMouseEvent(me);
195           }
196           public void mousePressed(MouseEvent me) {
197             int row = table.rowAtPoint(me.getPoint());
198             if(me.isPopupTrigger()
199             && !table.isRowSelected(row)) {
200               // if right click outside the selection then reset selection
201               table.getSelectionModel().setSelectionInterval(row, row);
202             }
203             processMouseEvent(me);
204           }
205           protected void processMouseEvent(MouseEvent me){
206             int viewRow = table.rowAtPoint(me.getPoint());
207             final int modelRow = viewRow == -?
208                                  viewRow :
209                                  table.rowViewToModel(viewRow);
210 
211             // popup menu
212             if(me.isPopupTrigger()) {
213               JPopupMenu popup = new JPopupMenu();
214               popup.add(new DeleteAction());
215 
216               //add the custom edit actions
217               if(modelRow != -1){
218                 AnnotationData aHandler = annDataList.get(modelRow);
219                 popup.addSeparator();
220                 List<Action> specificEditorActions = getSpecificEditorActions(
221                   aHandler.getAnnotationSet(), aHandler.getAnnotation());
222                 for (Action action : specificEditorActions) {
223                   popup.add(action);
224                 }
225                 if (!(popup.getComponent(popup.getComponentCount()-1)
226                     instanceof JSeparator)) {
227                   popup.addSeparator();
228                 }
229                 for (Action action : getGenericEditorActions(
230                   aHandler.getAnnotationSet(), aHandler.getAnnotation())) {
231                   if (specificEditorActions.contains(action)) { continue}
232                   popup.add(action);
233                 }
234                 if ((popup.getComponent(popup.getComponentCount()-1)
235                     instanceof JSeparator)) {
236                   popup.remove(popup.getComponentCount()-1);
237                 }
238               }
239               popup.show(table, me.getX(), me.getY());
240             }
241           }
242         });
243 
244         table.addAncestorListener(new AncestorListener() {
245           public void ancestorAdded(AncestorEvent event) {
246             // force the table to be sorted when the view is shown
247             tableModel.fireTableDataChanged();
248           }
249           public void ancestorMoved(AncestorEvent event) {
250           }
251           public void ancestorRemoved(AncestorEvent event) {
252           }
253         });
254 
255     // select all the rows containing the text from filterTextField
256     filterTextField.getDocument().addDocumentListenernew DocumentListener() {
257       private Timer timer = new Timer("Annotation list selection timer"true);
258       private TimerTask timerTask;
259       public void changedUpdate(DocumentEvent e) { /* do nothing */ }
260       public void insertUpdate(DocumentEvent e) { update()}
261       public void removeUpdate(DocumentEvent e) { update()}
262       private void update() {
263         if (timerTask != null) { timerTask.cancel()}
264         Date timeToRun = new Date(System.currentTimeMillis() 300);
265         timerTask = new TimerTask() { public void run() {
266           selectRows();
267         }};
268         // add a delay
269         timer.schedule(timerTask, timeToRun);
270       }
271       private void selectRows() {
272         table.clearSelection();
273         if (filterTextField.getText().trim().length() 2
274          || table.getRowCount() == 0) {
275           return;
276         }
277         // block upward events
278         localSelectionUpdating = true;
279         for (int row = 0; row < table.getRowCount(); row++) {
280           for (int col = 0; col < table.getColumnCount(); col++) {
281             if (table.getValueAt(row, col!= null
282              && table.getValueAt(row, col).toString()
283                 .contains(filterTextField.getText().trim())) {
284               table.addRowSelectionInterval(row, row);
285               break;
286             }
287           }
288         }
289         localSelectionUpdating = false;
290         // update the highlights in the document
291         if (table.isCellSelected(0,0)) {
292           table.addRowSelectionInterval(00);
293         else {
294           table.removeRowSelectionInterval(00);
295         }
296       }
297     });
298 
299     // Delete key for deleting selected annotations
300     table.addKeyListener(new KeyAdapter() {
301      public void keyPressed(KeyEvent e) {
302        if (e.getKeyCode() == KeyEvent.VK_DELETE) {
303          new DeleteAction().actionPerformed(new ActionEvent(e.getSource(),
304            e.getID()"", e.getWhen(), e.getModifiers()));
305        }
306      }
307     });
308   }
309 
310   public List<Action> getSpecificEditorActions(AnnotationSet set,
311                                                Annotation annotation) {
312     List<Action> actions = new ArrayList<Action>();
313     //add the specific editors
314     List<String> specificEditorClasses =
315       Gate.getCreoleRegister().getAnnotationVRs(annotation.getType());
316     if (specificEditorClasses != null &&
317       specificEditorClasses.size() 0) {
318       for (String editorClass : specificEditorClasses) {
319         AnnotationVisualResource editor =
320           editorsCache.get(editorClass);
321         if (editor == null) {
322           //create the new type of editor
323           try {
324             editor = (AnnotationVisualResource)
325               Factory.createResource(editorClass);
326             editorsCache.put(editorClass, editor);
327           catch (ResourceInstantiationException rie) {
328             rie.printStackTrace(Err.getPrintWriter());
329           }
330         }
331         actions.add(new EditAnnotationAction(set, annotation, editor));
332       }
333     }
334     return actions;
335   }
336 
337   public List<Action> getGenericEditorActions(AnnotationSet set,
338                                               Annotation annotation) {
339     List<Action> actions = new ArrayList<Action>();
340     //add generic editors
341     List<String> genericEditorClasses = Gate.getCreoleRegister().
342       getAnnotationVRs();
343     if (genericEditorClasses != null &&
344       genericEditorClasses.size() 0) {
345       for (String editorClass : genericEditorClasses) {
346         AnnotationVisualResource editor = editorsCache.get(editorClass);
347         if (editor == null) {
348           //create the new type of editor
349           try {
350             ResourceData resData = Gate.getCreoleRegister().get(editorClass);
351             Class<?> resClass = resData.getResourceClass();
352             if (OwnedAnnotationEditor.class.isAssignableFrom(resClass)) {
353               OwnedAnnotationEditor newEditor =
354                 (OwnedAnnotationEditorresClass.newInstance();
355               newEditor.setOwner(AnnotationListView.this);
356               newEditor.init();
357               editor = newEditor;
358             else {
359               editor = (AnnotationVisualResource)
360                 Factory.createResource(editorClass);
361             }
362             editorsCache.put(editorClass, editor);
363           catch (Exception rie) {
364             rie.printStackTrace(Err.getPrintWriter());
365           }
366         }
367         actions.add(new EditAnnotationAction(set, annotation, editor));
368       }
369     }
370     return actions;
371   }
372 
373   protected class DeleteAction extends AbstractAction {
374     public DeleteAction() {
375       super("Delete Annotations");
376       putValue(SHORT_DESCRIPTION, "Delete selected annotations");
377       putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke("DELETE"));
378     }
379     public void actionPerformed(ActionEvent event){
380       if ((event.getModifiers() & ActionEvent.SHIFT_MASK)
381                                != ActionEvent.SHIFT_MASK
382        && (event.getModifiers() & InputEvent.BUTTON1_MASK)
383                                != InputEvent.BUTTON1_MASK) {
384         // shows a confirm dialog before to delete annotations
385         int choice = JOptionPane.showOptionDialog(MainFrame.getInstance()new
386           Object[]{"Are you sure you want to delete the "
387           + table.getSelectedRowCount() " selected annotations?",
388           "<html><i>You can use Shift+Delete to bypass this dialog.</i>\n\n"},
389           "Delete annotations",
390           JOptionPane.DEFAULT_OPTION, JOptionPane.QUESTION_MESSAGE, null,
391           new String[]{"Delete annotations""Cancel"}"Cancel");
392         if (choice == JOptionPane.CLOSED_OPTION || choice == 1)  { return}
393       }
394       List<AnnotationData> annotationsData = new ArrayList<AnnotationData>();
395       for (int selectedRow : table.getSelectedRows()) {
396         annotationsData.add(annDataList.get(table.rowViewToModel(selectedRow)));
397       }
398       for (AnnotationData annotationData : annotationsData) {
399         annotationData.getAnnotationSet().remove(annotationData.getAnnotation());
400       }
401     }
402   }
403 
404    /* (non-Javadoc)
405        * @see gate.gui.docview.AbstractDocumentView#registerHooks()
406        */
407       protected void registerHooks() {
408         //this is called on activation
409     //    showHighlights();
410       }
411 
412    /* (non-Javadoc)
413     * @see gate.gui.docview.AbstractDocumentView#unregisterHooks()
414     */
415    protected void unregisterHooks() {
416      //this is called on de-activation
417      //remove highlights
418  //    textView.removeAllBlinkingHighlights();
419    }
420 
421    /* (non-Javadoc)
422     * @see gate.gui.docview.DocumentView#getType()
423     */
424    public int getType() {
425      return HORIZONTAL;
426    }
427 
428  //  protected void showHighlights(){
429  //    int[] viewRows = table.getSelectedRows();
430  //    AnnotationData aHandler = null;
431  //    for(int i = 0; i < viewRows.length; i++){
432  //      int modelRow = table.rowViewToModel(viewRows[i]);
433  //      if(modelRow >= 0){
434  //        aHandler = annDataList.get(modelRow);
435  //        textView.addBlinkingHighlight(aHandler.getAnnotation());
436  //      }
437  //    }
438  //  }
439 
440 
441    /**
442     * Adds an annotation to be displayed in the list.
443     @param ann the annotation
444     @param set the set containing the annotation
445     @return a tag that can be used to refer to this annotation for future
446     * operations, e.g. when removing the annotation from display.
447     */
448    public AnnotationDataImpl addAnnotation(Annotation ann, AnnotationSet set){
449      AnnotationDataImpl aData = new AnnotationDataImpl(set, ann);
450      annDataList.add(aData);
451      int row = annDataList.size() -1;
452      try{
453        localSelectionUpdating = true;
454        if(tableModel != nulltableModel.fireTableRowsInserted(row, row);
455      }finally{
456        localSelectionUpdating = false;
457      }
458 
459      //listen for the new annotation's events
460      aData.getAnnotation().addAnnotationListener(AnnotationListView.this);
461      return aData;
462    }
463 
464    public void removeAnnotation(AnnotationData tag){
465      int row = annDataList.indexOf(tag);
466      if(row >= 0){
467        AnnotationData aHandler = annDataList.get(row);
468        //remove from selection, if the table is built
469        List<AnnotationData> selAnns = owner.getSelectedAnnotations();
470        if(selAnns.remove(tag)){
471          owner.setSelectedAnnotations(selAnns);
472        }
473        aHandler.getAnnotation().removeAnnotationListener(
474                AnnotationListView.this);
475        annDataList.remove(row);
476        //owner was already notified
477        try{
478          localSelectionUpdating = true;
479          if(tableModel != nulltableModel.fireTableRowsDeleted(row, row);
480        }finally{
481          localSelectionUpdating = false;
482      }
483    }
484    }
485 
486    public void removeAnnotations(Collection<AnnotationData> tags){
487      //to speed-up things, first remove all blinking highlights
488      if(table != null){
489        table.getSelectionModel().clearSelection();
490      }
491      //cache the selected annotations
492      final List<AnnotationData> selAnns = owner.getSelectedAnnotations();
493      boolean selectionChanged = false;
494      //now do the actual removal, in batch mode
495      for(AnnotationData aData : tags){
496        annDataList.remove(aData);
497        if(selAnns.remove(aData)){
498          selectionChanged = true;
499        }
500        aData.getAnnotation().removeAnnotationListener(AnnotationListView.this);
501      }
502      //update the table display
503      if(tableModel != nulltableModel.fireTableDataChanged();
504      //restore selection
505      if(selectionChanged){
506        //this needs to happen after the table has caught up with all the changes
507        //hence we need to queue it to the GUI thread
508        SwingUtilities.invokeLater(new Runnable(){
509        public void run(){
510          owner.setSelectedAnnotations(selAnns);
511        }});
512      }
513    }
514 
515    /**
516     * Adds a batch of annotations in one go.
517     * For each annotation, a tag object will be created. The return value is
518     * list that returns the tags in the same order as the collection used
519     * for the input annotations.
520     * This method does not assume it was called from the UI Thread.
521     @return a collection of tags corresponding to the annotations.
522     @param annotations a collection of annotations
523     @param set the annotation set to which all the annotations belong.
524     */
525    public List<AnnotationData> addAnnotations(List<Annotation> annotations,
526            AnnotationSet set){
527      List<AnnotationData> tags = new ArrayList<AnnotationData>();
528      for(Annotation ann : annotations){
529        AnnotationData aTag = new AnnotationDataImpl(set, ann);
530        tags.add(aTag);
531        annDataList.add(aTag);
532        ann.addAnnotationListener(AnnotationListView.this);
533      }
534      try{
535        //this will cause the selection to change (the actual selection contents
536        //stay the same, but the row numbers may change)
537        //we want to avoid circular notifications.
538        localSelectionUpdating  = true;
539        if(tableModel != nulltableModel.fireTableDataChanged();
540      }finally{
541        localSelectionUpdating = false;
542      }
543      return tags;
544    }
545 
546    /**
547     * Returns the tags for all the annotations currently displayed
548     @return a list of {@link AnnotationDataImpl}.
549     */
550    public List<AnnotationData> getAllAnnotations(){
551      return annDataList;
552    }
553 
554    public void annotationUpdated(AnnotationEvent e){
555      //update all occurrences of this annotation
556     // if annotations tab has not been set to visible state
557      // table will be null.
558      if(table == null)  return;
559      //save selection
560      int[] selection = table.getSelectedRows();
561      if(selection != null){
562        localSelectionUpdating = true;
563      }
564      Annotation ann = (Annotation)e.getSource();
565      if(tableModel != null){
566        for(int i = 0; i < annDataList.size(); i++){
567          AnnotationData aHandler = annDataList.get(i);
568          if(aHandler.getAnnotation() == ann)tableModel.fireTableRowsUpdated(i, i);
569        }
570      }
571      //restore selection
572      table.clearSelection();
573      if(selection != null){
574        localSelectionUpdating = true;
575        for(int i = 0; i < selection.length; i++){
576          table.addRowSelectionInterval(selection[i], selection[i]);
577        }
578        localSelectionUpdating = false;
579      }
580    }
581 
582 
583    /* (non-Javadoc)
584     * @see gate.gui.docview.AbstractDocumentView#setSelectedAnnotations(java.util.List)
585     */
586    @Override
587    public void setSelectedAnnotations(final List<AnnotationData> selectedAnnots) {
588      //if the list of selected annotations differs from the current selection,
589      //update the selection.
590      //otherwise do nothing (to break infinite looping)
591 
592      //first get the local list of selected annotations
593      int[] viewRows = table.getSelectedRows();
594      List<AnnotationData> localSelAnns = new ArrayList<AnnotationData>();
595      for (int viewRow : viewRows) {
596        int modelRow = table.rowViewToModel(viewRow);
597        if (modelRow >= 0) {
598          localSelAnns.add(annDataList.get(modelRow));
599        }
600      }
601      //now compare with the new value
602      if(localSelAnns.size() == selectedAnnots.size()){
603        //same size, we need to actually compare contents
604        localSelAnns.removeAll(selectedAnnots);
605        if(localSelAnns.isEmpty()){
606          //lists are the same -> exit!
607          return;
608        }
609      }
610      //if we got this far, the selection lists were different
611      //we need to change the selection from the UI thread.
612      SwingUtilities.invokeLater(new Runnable(){
613        public void run(){
614          try{
615            //block upward events
616            localSelectionUpdating = true;
617            //update the local selection
618            table.getSelectionModel().clearSelection();
619            int rowToScrollTo = -1;
620            for(AnnotationData aData : selectedAnnots){
621              int modelRow = annDataList.indexOf(aData);
622              if(modelRow != -1){
623                int viewRow = table.rowModelToView(modelRow);
624                table.getSelectionModel().addSelectionInterval(viewRow, viewRow);
625                rowToScrollTo = viewRow;
626              }
627            }
628            if(rowToScrollTo >= 0){
629              table.scrollRectToVisible(table.getCellRect(rowToScrollTo, 0true));
630            }
631          }finally{
632            //re-enable upward events
633            localSelectionUpdating = false;
634          }
635        }
636      });
637    }
638 
639 
640    /**
641     * Selects the annotation for the given tag.
642     @param tag the tag of the annotation to be selected.
643     */
644    public void selectAnnotationForTag(Object tag){
645      int modelPosition = annDataList.indexOf(tag);
646      table.getSelectionModel().clearSelection();
647      if(modelPosition != -1){
648        int tablePosition = table.rowModelToView(modelPosition);
649        table.getSelectionModel().setSelectionInterval(tablePosition,
650                tablePosition);
651        table.scrollRectToVisible(table.getCellRect(tablePosition, 0false));
652      }
653    }
654 
655 
656 
657    /* (non-Javadoc)
658     * @see gate.gui.annedit.AnnotationEditorOwner#annotationChanged(gate.Annotation, gate.AnnotationSet, java.lang.String)
659     */
660    public void annotationChanged(Annotation ann, AnnotationSet set,
661            String oldType) {
662      //do nothing
663    }
664 
665    /* (non-Javadoc)
666     * @see gate.gui.annedit.AnnotationEditorOwner#getNextAnnotation()
667     */
668    public Annotation getNextAnnotation() {
669      return null;
670    }
671 
672 
673    /* (non-Javadoc)
674     * @see gate.gui.annedit.AnnotationEditorOwner#getPreviousAnnotation()
675     */
676    public Annotation getPreviousAnnotation() {
677      return null;
678    }
679 
680 
681    /* (non-Javadoc)
682     * @see gate.gui.annedit.AnnotationEditorOwner#getTextComponent()
683     */
684    public JTextComponent getTextComponent() {
685      //get a pointer to the text view used to display
686      //the selected annotations
687      Iterator centralViewsIter = owner.getCentralViews().iterator();
688      while(textView == null && centralViewsIter.hasNext()){
689        DocumentView aView = (DocumentView)centralViewsIter.next();
690        if(aView instanceof TextualDocumentView)
691          textView = (TextualDocumentView)aView;
692      }
693      return (JTextArea)((JScrollPane)textView.getGUI()).getViewport().getView();
694    }
695 
696    /* (non-Javadoc)
697     * @see gate.gui.annedit.AnnotationEditorOwner#selectAnnotation(gate.gui.annedit.AnnotationData)
698     */
699    public void selectAnnotation(AnnotationData data) {
700    }
701 
702    /* (non-Javadoc)
703     * @see gate.gui.docview.AnnotationList#getRowForAnnotation(gate.gui.annedit.AnnotationData)
704     */
705    public int getRowForAnnotation(AnnotationData data) {
706      return annDataList.indexOf(data);
707    }
708 
709   class AnnotationTableModel extends AbstractTableModel{
710        public int getRowCount(){
711          return annDataList.size();
712        }
713 
714        public int getColumnCount(){
715          return 6;
716        }
717 
718        public String getColumnName(int column){
719          switch(column){
720            case TYPE_COL: return "Type";
721            case SET_COL: return "Set";
722            case START_COL: return "Start";
723            case END_COL: return "End";
724            case ID_COL: return "Id";
725            case FEATURES_COL: return "Features";
726            defaultreturn "?";
727          }
728        }
729 
730        public Class getColumnClass(int column){
731          switch(column){
732            case TYPE_COL: return String.class;
733            case SET_COL: return String.class;
734            case START_COL: return Long.class;
735            case END_COL: return Long.class;
736            case ID_COL: return Integer.class;
737            case FEATURES_COL: return String.class;
738            defaultreturn String.class;
739          }
740        }
741 
742        public boolean isCellEditable(int rowIndex, int columnIndex){
743          return false;
744        }
745 
746        public Object getValueAt(int row, int column){
747          if(row >= annDataList.size()) return null;
748          AnnotationData aData = annDataList.get(row);
749          switch(column){
750            case TYPE_COL: return aData.getAnnotation().getType();
751            case SET_COL: return aData.getAnnotationSet().getName();
752            case START_COL: return aData.getAnnotation().getStartNode().getOffset();
753            case END_COL: return aData.getAnnotation().getEndNode().getOffset();
754            case ID_COL: return aData.getAnnotation().getId();
755            case FEATURES_COL:
756              //sort the features by name
757              FeatureMap features = aData.getAnnotation().getFeatures();
758              List keyList = new ArrayList(features.keySet());
759              Collections.sort(keyList);
760              StringBuffer strBuf = new StringBuffer("{");
761              Iterator keyIter = keyList.iterator();
762              boolean first = true;
763              while(keyIter.hasNext()){
764                Object key = keyIter.next();
765                Object value = features.get(key);
766                if(first){
767                  first = false;
768                }else{
769                  strBuf.append(", ");
770                }
771                strBuf.append(key.toString());
772                strBuf.append("=");
773                strBuf.append(value == null "[null]" : value.toString());
774              }
775              strBuf.append("}");
776              return strBuf.toString();
777            defaultreturn "?";
778          }
779        }
780 
781      }
782 
783 
784   protected class EditAnnotationAction extends AbstractAction{
785        public EditAnnotationAction(AnnotationSet set, Annotation ann,
786                AnnotationVisualResource editor){
787          this.set = set;
788          this.ann = ann;
789          this.editor = editor;
790          ResourceData rData =
791            Gate.getCreoleRegister().get(editor.getClass().getName());
792          if(rData != null){
793            title = rData.getName();
794            putValue(NAME, "Edit with " + title);
795            putValue(SHORT_DESCRIPTION, rData.getComment());
796          }
797        }
798 
799        public void actionPerformed(ActionEvent evt){
800    //      editor.setTarget(set);
801    //      editor.setAnnotation(ann);
802          if(editor instanceof OwnedAnnotationEditor){
803            //we need to unpin the editor so that it actually calculates the
804            //position
805            ((OwnedAnnotationEditor)editor).setPinnedMode(false);
806            ((OwnedAnnotationEditor)editor).placeDialog(
807                    ann.getStartNode().getOffset().intValue(),
808                    ann.getEndNode().getOffset().intValue());
809            //now we need to pin it so that it does not disappear automatically
810            ((OwnedAnnotationEditor)editor).setPinnedMode(true);
811            editor.editAnnotation(ann, set);
812          }else{
813            editor.editAnnotation(ann, set);
814            JScrollPane scroller = new JScrollPane((Component)editor);
815            JOptionPane optionPane = new JOptionPane(scroller,
816                    JOptionPane.QUESTION_MESSAGE,
817                    JOptionPane.OK_CANCEL_OPTION,
818                    null, new String[]{"OK""Cancel"});
819            Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
820            scroller.setMaximumSize(new Dimension((int)(screenSize.width * .75),
821                    (int)(screenSize.height * .75)));
822            JDialog dialog = optionPane.createDialog(AnnotationListView.this.getGUI(),
823                    title);
824            dialog.setModal(true);
825            dialog.setResizable(true);
826            dialog.setVisible(true);
827            try{
828              if(optionPane.getValue().equals("OK")) editor.okAction();
829              else editor.cancelAction();
830            }catch(GateException ge){
831              throw new GateRuntimeException(ge);
832            }
833 
834          }
835 
836        }
837 
838        String title;
839        Annotation ann;
840        AnnotationSet set;
841        AnnotationVisualResource editor;
842      }
843 
844      protected XJTable table;
845      protected AnnotationTableModel tableModel;
846      protected JScrollPane scroller;
847 
848      /**
849       * Stores the {@link AnnotationData} objects representing the annotations
850       * displayed by this view.
851       */
852      protected List<AnnotationData> annDataList;
853 
854      /**
855       * Flag used to mark the fact that the table selection is currently being
856       * updated, to synchronise it with the global selection.
857       * This is used to block update events being sent to the owner, while the
858       * current selection is adjusted.
859       */
860      protected volatile boolean localSelectionUpdating = false;
861 
862      protected JPanel mainPanel;
863      protected JLabel statusLabel;
864      protected JTextField filterTextField;
865      protected TextualDocumentView textView;
866      /**
867       * A map that stores instantiated annotations editors in order to avoid the
868       * delay of building them at each request;
869       */
870      protected Map<String, AnnotationVisualResource> editorsCache;
871 
872      private static final int TYPE_COL = 0;
873      private static final int SET_COL = 1;
874      private static final int START_COL = 2;
875      private static final int END_COL = 3;
876      private static final int ID_COL = 4;
877      private static final int FEATURES_COL = 5;
878 
879    }