CorpusEditor.java
001 /*
002  *  Copyright (c) 1995-2010, The University of Sheffield. See the file
003  *  COPYRIGHT.txt in the software or at http://gate.ac.uk/gate/COPYRIGHT.txt
004  *
005  *  This file is part of GATE (see http://gate.ac.uk/), and is free
006  *  software, licenced under the GNU Library General Public License,
007  *  Version 2, June 1991 (in the distribution as file licence.html,
008  *  and also available at http://gate.ac.uk/gate/licence.html).
009  *
010  *  Valentin Tablan 12/07/2001
011  *
012  *  $Id: CorpusEditor.java 12511 2010-04-16 21:41:44Z thomas_heitz $
013  *
014  */
015 package gate.gui;
016 
017 import java.awt.BorderLayout;
018 import java.awt.Component;
019 import java.awt.Dimension;
020 import java.awt.datatransfer.Transferable;
021 import java.awt.datatransfer.StringSelection;
022 import java.awt.datatransfer.DataFlavor;
023 import java.awt.datatransfer.UnsupportedFlavorException;
024 import java.awt.event.*;
025 import java.util.*;
026 import java.io.IOException;
027 
028 import javax.swing.*;
029 import javax.swing.event.ListSelectionListener;
030 import javax.swing.event.ListSelectionEvent;
031 import javax.swing.table.*;
032 
033 import gate.*;
034 import gate.creole.AbstractVisualResource;
035 import gate.event.CorpusEvent;
036 import gate.creole.metadata.CreoleResource;
037 import gate.creole.metadata.GuiType;
038 import gate.event.CorpusListener;
039 import gate.event.CreoleListener;
040 import gate.event.CreoleEvent;
041 import gate.swing.XJTable;
042 import gate.swing.XJPopupMenu;
043 import gate.util.GateException;
044 import gate.util.GateRuntimeException;
045 
046 /**
047  * A simple viewer/editor for corpora. It will allow the visualisation of the
048  * list of documents inside a corpus along with their features.
049  * It will also allow addition and removal of documents.
050  */
051 @CreoleResource(name = "Corpus editor", guiType = GuiType.LARGE,
052     resourceDisplayed = "gate.Corpus", mainViewer = true)
053 public class CorpusEditor extends AbstractVisualResource
054   implements CorpusListener {
055 
056   public Resource init(){
057     initLocalData();
058     initGuiComponents();
059     initListeners();
060     return this;
061   }
062 
063 
064   protected void initLocalData(){
065     docTableModel = new DocumentTableModel();
066     try {
067       documentsLoadedCount = Gate.getCreoleRegister()
068         .getAllInstances("gate.Document").size();
069     catch (GateException exception) {
070       exception.printStackTrace();
071     }
072   }
073 
074   protected void initGuiComponents(){
075     setLayout(new BorderLayout());
076     renderer = new DocumentNameRenderer();
077     
078     docTable = new XJTable(docTableModel);
079     docTable.setSortable(true);
080     docTable.setSortedColumn(DocumentTableModel.COL_INDEX);
081     docTable.setAutoResizeMode(XJTable.AUTO_RESIZE_LAST_COLUMN);
082     docTable.getColumnModel().getColumn(DocumentTableModel.COL_NAME).
083         setCellRenderer(renderer);
084     docTable.setDragEnabled(true);
085     docTable.setTransferHandler(new TransferHandler() {
086       // drag and drop to move up and down the table rows
087       // import selected documents from the resources tree
088       String source = "";
089       public int getSourceActions(JComponent c) {
090         return MOVE;
091       }
092       protected Transferable createTransferable(JComponent c) {
093         int selectedRows[] = docTable.getSelectedRows();
094         Arrays.sort(selectedRows);
095         return new StringSelection("CorpusEditor"
096           + Arrays.toString(selectedRows));
097       }
098       protected void exportDone(JComponent c, Transferable data, int action) {
099       }
100       public boolean canImport(JComponent c, DataFlavor[] flavors) {
101         for(DataFlavor flavor : flavors) {
102           if(DataFlavor.stringFlavor.equals(flavor)) {
103             return true;
104           }
105         }
106         return false;
107       }
108       public boolean importData(JComponent c, Transferable t) {
109         if (!canImport(c, t.getTransferDataFlavors())) {
110           return false;
111         }
112         try {
113           source = (String)t.getTransferData(DataFlavor.stringFlavor);
114           if (source.startsWith("ResourcesTree")) {
115             int insertion = docTable.getSelectedRow();
116             List<Document> documents = new ArrayList<Document>();
117             source = source.replaceFirst("^ResourcesTree\\[""");
118             source = source.replaceFirst("\\]$""");
119             final String documentsNames[] = source.split(", ");
120             List<Resource> loadedDocuments;
121             try {
122               loadedDocuments =
123                 Gate.getCreoleRegister().getAllInstances("gate.Document");
124             catch(GateException e) {
125               e.printStackTrace();
126               return false;
127             }
128             // get the list of documents selected when dragging started
129             for(String documentName : documentsNames) {
130               for (Resource loadedDocument : loadedDocuments) {
131                 if (loadedDocument.getName().equals(documentName)
132                  && !corpus.contains(loadedDocument)) {
133                   documents.add((DocumentloadedDocument);
134                 }
135               }
136             }
137             // add the documents at the insertion point
138             for (Document document : documents) {
139               if (insertion != -1) {
140                 corpus.add(docTable.rowViewToModel(insertion), document);
141                 if (insertion == docTable.getRowCount()) { insertion++; }
142               else {
143                 corpus.add(document);
144               }
145             }
146             // select the moved/already existing documents
147             SwingUtilities.invokeLater(new Runnable() {
148               public void run() {
149                 docTable.clearSelection();
150                 for (String documentName : documentsNames) {
151                   for (int row = 0; row < docTable.getRowCount(); row++) {
152                     if (docTable.getValueAt(
153                           row, docTable.convertColumnIndexToView(1))
154                         .equals(documentName)) {
155                       docTable.addRowSelectionInterval(row, row);
156                     }
157                   }
158                 }
159               }
160             });
161             changeMessage();
162             return true;
163 
164           else if (source.startsWith("CorpusEditor")) {
165             int insertion = docTable.getSelectedRow();
166             int initialInsertion = insertion;
167             List<Document> documents = new ArrayList<Document>();
168             source = source.replaceFirst("^CorpusEditor\\[""");
169             source = source.replaceFirst("\\]$""");
170             String selectedRows[] = source.split(", ");
171             if (Integer.valueOf(selectedRows[0]) < insertion) { insertion++; }
172             // get the list of documents selected when dragging started
173             for(String row : selectedRows) {
174               if (Integer.valueOf(row== initialInsertion) {
175                 // the user dragged the selected rows on themselves, do nothing
176                 return false;
177               }
178               documents.add((Documentcorpus.get(
179                 docTable.rowViewToModel(Integer.valueOf(row))));
180               if (Integer.valueOf(row< initialInsertion) { insertion--; }
181             }
182             // remove the documents selected when dragging started
183             for(Document document : documents) {
184               corpus.remove(document);
185             }
186             // add the documents at the insertion point
187             for (Document document : documents) {
188               corpus.add(docTable.rowViewToModel(insertion), document);
189               insertion++;
190             }
191             // select the moved documents
192             docTable.addRowSelectionInterval(
193               insertion - selectedRows.length, insertion - 1);
194             return true;
195 
196           else {
197             return false;
198           }
199 
200         catch (UnsupportedFlavorException ufe) {
201           return false;
202         catch (IOException ioe) {
203           return false;
204         }
205       }
206     });
207 
208     JScrollPane scroller = new JScrollPane(docTable);
209     scroller.setHorizontalScrollBarPolicy(
210             JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
211     scroller.getViewport().setBackground(docTable.getBackground());
212     add(scroller, BorderLayout.CENTER);
213 
214     toolbar = new JToolBar();
215     toolbar.setFloatable(false);
216     toolbar.add(newDocumentAction = new NewDocumentAction());
217     toolbar.add(removeDocumentsAction = new RemoveDocumentsAction());
218     toolbar.addSeparator();
219     toolbar.add(moveUpAction = new MoveUpAction());
220     toolbar.add(moveDownAction = new MoveDownAction());
221     toolbar.addSeparator();
222     toolbar.add(openDocumentsAction = new OpenDocumentsAction());
223 
224     removeDocumentsAction.setEnabled(false);
225     moveUpAction.setEnabled(false);
226     moveDownAction.setEnabled(false);
227     openDocumentsAction.setEnabled(false);
228 
229     JPanel topPanel = new JPanel(new BorderLayout());
230     topPanel.add(toolbar, BorderLayout.NORTH);
231 
232     messageLabel = new JLabel();
233     changeMessage();
234     topPanel.add(messageLabel, BorderLayout.SOUTH);
235 
236     add(topPanel, BorderLayout.NORTH);
237   }
238 
239   protected void initListeners(){
240 
241     // mouse double-click to open the document
242     // context menu to get the actions for the selection
243     docTable.addMouseListener(new MouseAdapter() {
244       public void mouseClicked(MouseEvent e) {
245         processMouseEvent(e);
246       }
247       public void mousePressed(MouseEvent e) {
248         if(e.isPopupTrigger()) { processMouseEvent(e)}
249       }
250       public void mouseReleased(MouseEvent e) {
251         if(e.isPopupTrigger()) { processMouseEvent(e)}
252       }
253       private void processMouseEvent(MouseEvent e) {
254         int row = docTable.rowAtPoint(e.getPoint());
255         if(row == -1) { return}
256 
257         if(e.isPopupTrigger()) {
258           // context menu
259           if(!docTable.isRowSelected(row)) {
260             // if right click outside the selection then reset selection
261             docTable.getSelectionModel().setSelectionInterval(row, row);
262           }
263           JPopupMenu popup = new XJPopupMenu();
264           popup.add(openDocumentsAction);
265           popup.add(removeDocumentsAction);
266           popup.show(docTable, e.getPoint().x, e.getPoint().y);
267 
268         else if(e.getID() == MouseEvent.MOUSE_CLICKED
269                && e.getClickCount() == 2) {
270           // open document on double-click
271           openDocumentsAction.actionPerformed(null);
272         }
273       }
274     });
275 
276     // Enter key opens the selected documents
277     docTable.addKeyListener(new KeyAdapter() {
278       public void keyPressed(KeyEvent e) {
279         if (e.getKeyCode() == KeyEvent.VK_ENTER) {
280           openDocumentsAction.actionPerformed(null);
281         }
282       }
283     });
284 
285     docTable.getSelectionModel().addListSelectionListener(
286       new ListSelectionListener() {
287         public void valueChanged(ListSelectionEvent e) {
288           // enable/disable buttons according to the selection
289           removeDocumentsAction.setEnabled(docTable.getSelectedRowCount() 0);
290           openDocumentsAction.setEnabled(docTable.getSelectedRowCount() 0);
291           moveUpAction.setEnabled(docTable.getSelectedRowCount() 0
292             && !docTable.isRowSelected(0));
293           moveDownAction.setEnabled(docTable.getSelectedRowCount() 0
294             && !docTable.isRowSelected(docTable.getRowCount() 1));
295         }
296       });
297 
298     Gate.getCreoleRegister().addCreoleListener(new CreoleListener() {
299       public void resourceLoaded(CreoleEvent e) {
300         if (e.getResource() instanceof Document) {
301           documentsLoadedCount++;
302           changeMessage();
303         }
304       }
305       public void resourceUnloaded(CreoleEvent e) {
306         if (e.getResource() instanceof Document) {
307           documentsLoadedCount--;
308           changeMessage();
309         }
310       }
311       public void datastoreOpened(CreoleEvent e) { /* do nothing */ }
312       public void datastoreCreated(CreoleEvent e) { /* do nothing */ }
313       public void datastoreClosed(CreoleEvent e) { /* do nothing */ }
314       public void resourceRenamed(Resource resource, String oldName,
315                                   String newName) { /* do nothing */ }
316     });
317   }
318 
319   public void cleanup(){
320     super.cleanup();
321     corpus = null;
322   }
323 
324   public void setTarget(Object target){
325     if(corpus != null && corpus != target){
326       //we already had a different corpus
327       corpus.removeCorpusListener(this);
328     }
329     if(!(target instanceof Corpus)){
330       throw new IllegalArgumentException(
331         "The GATE corpus editor can only be used with a GATE corpus!\n" +
332         target.getClass().toString() " is not a GATE corpus!");
333     }
334     this.corpus = (Corpus)target;
335     corpus.addCorpusListener(this);
336     docTableModel.dataChanged();
337     SwingUtilities.invokeLater(new Runnable(){
338       public void run(){
339         docTableModel.fireTableDataChanged();
340       }
341     });
342   }
343 
344   public void documentAdded(final CorpusEvent e) {
345     docTableModel.dataChanged();
346     SwingUtilities.invokeLater(new Runnable(){
347       public void run(){
348         changeMessage();
349         docTableModel.fireTableRowsInserted(e.getDocumentIndex(),
350                 e.getDocumentIndex());
351       }
352     });
353   }
354 
355   public void documentRemoved(final CorpusEvent e) {
356     docTableModel.dataChanged();
357     SwingUtilities.invokeLater(new Runnable(){
358       public void run(){
359         changeMessage();
360         docTableModel.fireTableRowsDeleted(e.getDocumentIndex()
361                 e.getDocumentIndex());
362       }
363     });
364   }
365 
366   class DocumentTableModel extends AbstractTableModel{
367     public DocumentTableModel(){
368       documentNames = new ArrayList<String>();
369     }
370     
371     /**
372      * Called externally when the underlying corpus has changed.
373      */
374     private void dataChanged(){
375       List<String> newDocs = new ArrayList<String>();
376       if(corpus != null){
377         newDocs.addAll(corpus.getDocumentNames());
378      }
379       List<String> oldDocs = documentNames;
380       documentNames = newDocs;
381       oldDocs.clear();
382     }
383     
384     public int getColumnCount() {
385       return COLUMN_COUNT;
386     }
387 
388     public int getRowCount() {
389       return documentNames.size();
390     }
391 
392     public Object getValueAt(int rowIndex, int columnIndex) {
393       //invalid indexes might appear when update events are slow to act 
394       if(rowIndex < || rowIndex >= documentNames.size() || 
395          columnIndex < || columnIndex > COLUMN_COUNTreturn null;
396       switch(columnIndex) {
397         case COL_INDEX:
398           return rowIndex;
399         case COL_NAME:
400           return documentNames.get(rowIndex);
401         default:
402           return null;
403       }
404     }
405 
406     @Override
407     public Class<?> getColumnClass(int columnIndex) {
408       switch(columnIndex) {
409         case COL_INDEX:
410           return Integer.class;
411         case COL_NAME:
412           return String.class;
413         default:
414           return String.class;
415       }
416     }
417 
418     @Override
419     public String getColumnName(int column) {
420       return COLUMN_NAMES[column];
421     }
422 
423     @Override
424     public boolean isCellEditable(int rowIndex, int columnIndex) {
425       return false;
426     }
427     
428     private List<String> documentNames;
429     private final String[] COLUMN_NAMES = {"Index""Document name"}
430     private static final int COL_INDEX = 0;
431     private static final int COL_NAME = 1;
432     private static final int COLUMN_COUNT = 2;
433   }
434 
435   class DocumentNameRenderer extends DefaultTableCellRenderer implements 
436       ListCellRenderer{
437     public DocumentNameRenderer(){
438       super();
439       setIcon(MainFrame.getIcon("document"));
440     }
441     
442     public Component getListCellRendererComponent(JList list, Object value,
443             int index, boolean isSelected, boolean cellHasFocus) {
444       // prepare the renderer
445 
446       return getTableCellRendererComponent(docTable, value, isSelected, 
447               cellHasFocus, index, DocumentTableModel.COL_NAME);
448     }
449 
450     @Override
451     public Dimension getMaximumSize() {
452       //we don't mind being extended horizontally
453       Dimension dim = super.getMaximumSize();
454       if(dim != null){
455         dim.width = Integer.MAX_VALUE;
456         setMaximumSize(dim);
457       }
458       return dim;
459     }
460 
461     @Override
462     public Dimension getMinimumSize() {
463       //we don't like being squashed!
464       return getPreferredSize();
465     }
466     
467     
468   }
469   
470   class MoveUpAction extends AbstractAction{
471     public MoveUpAction(){
472       super("Move up", MainFrame.getIcon("up"));
473       putValue(SHORT_DESCRIPTION, "Moves selected document(s) up");
474       putValue(MNEMONIC_KEY, KeyEvent.VK_UP);
475     }
476 
477     public void actionPerformed(ActionEvent e) {
478       int[] rowsTable = docTable.getSelectedRows();
479       int[] rowsCorpus = new int[rowsTable.length];
480       for(int i = 0; i < rowsTable.length; i++)
481         rowsCorpus[i= docTable.rowViewToModel(rowsTable[i]);
482       Arrays.sort(rowsCorpus);
483       //starting from the smallest one, move each element up
484       for(int i = 0; i < rowsCorpus.length; i++){
485         if(rowsCorpus[i0){
486           //swap the doc with the one before
487           //serial corpus does not load the document on remove, so we need
488           //to load the document explicitly
489           boolean wasLoaded = corpus.isDocumentLoaded(rowsCorpus[i]);
490           Document doc = (Document)corpus.get(rowsCorpus[i]);
491           corpus.remove(rowsCorpus[i]);
492           rowsCorpus[i= rowsCorpus[i1;
493           corpus.add(rowsCorpus[i], doc);
494           if(!wasLoaded){
495             corpus.unloadDocument(doc);
496             Factory.deleteResource(doc);
497           }
498         }
499       }
500       //restore selection
501       //the remove / add events will cause the table to be updated
502       //we need to only restore the selection after that happened
503       final int[] selectedRowsCorpus = new int[rowsCorpus.length];
504       System.arraycopy(rowsCorpus, 0, selectedRowsCorpus, 0, rowsCorpus.length);
505       SwingUtilities.invokeLater(new Runnable(){
506         public void run(){
507           docTable.clearSelection();
508           for(int i = 0; i < selectedRowsCorpus.length; i++){
509             int rowTable = docTable.rowModelToView(selectedRowsCorpus[i]);
510             docTable.getSelectionModel().addSelectionInterval(rowTable, 
511                     rowTable);
512           }                
513         }
514       });
515     }
516   }
517 
518   class MoveDownAction extends AbstractAction{
519     public MoveDownAction(){
520       super("Move down", MainFrame.getIcon("down"));
521       putValue(SHORT_DESCRIPTION, "Moves selected document(s) down");
522       putValue(MNEMONIC_KEY, KeyEvent.VK_DOWN);
523     }
524 
525     public void actionPerformed(ActionEvent e) {
526       int[] rowsTable = docTable.getSelectedRows();
527       int[] rowsCorpus = new int[rowsTable.length];
528       for(int i = 0; i < rowsTable.length; i++)
529         rowsCorpus[i= docTable.rowViewToModel(rowsTable[i]);
530       Arrays.sort(rowsCorpus);
531       //starting from the largest one, move each element down
532       for(int i = rowsCorpus.length -1; i >=0; i--){
533         if(rowsCorpus[i< corpus.size() -1){
534           //swap the doc with the one before
535           //serial corpus does not load the document on remove, so we need
536           //to load the document explicitly
537           boolean wasLoaded = corpus.isDocumentLoaded(rowsCorpus[i]);
538           Document doc = (Document)corpus.get(rowsCorpus[i]);
539           corpus.remove(rowsCorpus[i]);
540           rowsCorpus[i]++;
541           corpus.add(rowsCorpus[i], doc);
542           if(!wasLoaded){
543             corpus.unloadDocument(doc);
544             Factory.deleteResource(doc);
545           }
546         }
547       }
548       //restore selection
549       //the remove / add events will cause the table to be updated
550       //we need to only restore the selection after that happened
551       final int[] selectedRowsCorpus = new int[rowsCorpus.length];
552       System.arraycopy(rowsCorpus, 0, selectedRowsCorpus, 0, rowsCorpus.length);
553       SwingUtilities.invokeLater(new Runnable(){
554         public void run(){
555           docTable.clearSelection();
556           for(int i = 0; i < selectedRowsCorpus.length; i++){
557             int rowTable = docTable.rowModelToView(selectedRowsCorpus[i]);
558             docTable.getSelectionModel().addSelectionInterval(rowTable, 
559                     rowTable);
560           }                
561         }
562       });
563     }
564   }
565 
566   class NewDocumentAction extends AbstractAction{
567     public NewDocumentAction(){
568       super("Add document", MainFrame.getIcon("add-document"));
569       putValue(SHORT_DESCRIPTION, "Add new document(s) to this corpus");
570       putValue(MNEMONIC_KEY, KeyEvent.VK_ENTER);
571     }
572 
573     public void actionPerformed(ActionEvent e){
574       List<Resource> loadedDocuments;
575       try {
576         // get all the documents loaded in the system
577         loadedDocuments = Gate.getCreoleRegister()
578           .getAllInstances("gate.Document");
579       catch(GateException ge) {
580         //gate.Document is not registered in creole.xml....what is!?
581         throw new GateRuntimeException(
582           "gate.Document is not registered in the creole register!\n" +
583           "Something must be terribly wrong...take a vacation!");
584       }
585       Vector<String> docNames = new Vector<String>();
586       for (Resource loadedDocument : new ArrayList<Resource>(loadedDocuments)) {
587         if (corpus.contains(loadedDocument)) {
588           loadedDocuments.remove(loadedDocument);
589         else {
590           docNames.add(loadedDocument.getName());
591         }
592       }
593       JList docList = new JList(docNames);
594       docList.getSelectionModel().setSelectionInterval(0, docNames.size()-1);
595       docList.setCellRenderer(renderer);
596       final JOptionPane optionPane = new JOptionPane(new JScrollPane(docList),
597         JOptionPane.QUESTION_MESSAGE, JOptionPane.OK_CANCEL_OPTION);
598       final JDialog dialog = optionPane.createDialog(CorpusEditor.this,
599         "Add document(s) to this corpus");
600       docList.addMouseListener(new MouseAdapter() {
601         public void mouseClicked(MouseEvent e) {
602           if (e.getClickCount() == 2) {
603             optionPane.setValue(JOptionPane.OK_OPTION);
604             dialog.dispose();
605           }
606         }
607       });
608       dialog.setVisible(true);
609       if(optionPane.getValue().equals(JOptionPane.OK_OPTION)){
610         int[] selectedIndices = docList.getSelectedIndices();
611         for (int selectedIndice : selectedIndices) {
612           corpus.add(loadedDocuments.get(selectedIndice));
613         }
614       }
615       changeMessage();
616     }
617   }
618 
619   class RemoveDocumentsAction extends AbstractAction{
620     public RemoveDocumentsAction(){
621       super("Remove documents", MainFrame.getIcon("remove-document"));
622       putValue(SHORT_DESCRIPTION,
623         "Removes selected document(s) from this corpus");
624       putValue(MNEMONIC_KEY, KeyEvent.VK_DELETE);
625     }
626 
627     public void actionPerformed(ActionEvent e){
628       int[] selectedIndexes = docTable.getSelectedRows();
629       int[] corpusIndexes = new int[selectedIndexes.length];
630       for(int i = 0; i < selectedIndexes.length; i++)
631         corpusIndexes[i= docTable.rowViewToModel(selectedIndexes[i]);
632       Arrays.sort(corpusIndexes);
633       //remove the document starting with the one with the highest index
634       for(int i = corpusIndexes.length-1; i >= 0; i--){
635         corpus.remove(corpusIndexes[i]);
636       }
637       docTable.clearSelection();
638       changeMessage();
639     }
640   }
641 
642   class OpenDocumentsAction extends AbstractAction{
643     public OpenDocumentsAction(){
644       super("Open documents", MainFrame.getIcon("document"));
645       putValue(SHORT_DESCRIPTION,
646         "Opens selected document(s) in a document editor");
647     }
648 
649     public void actionPerformed(ActionEvent e){
650       Component root = SwingUtilities.getRoot(CorpusEditor.this);
651       if (!(root instanceof MainFrame)) { return}
652       final MainFrame mainFrame = (MainFrameroot;
653       final int[] selectedRows = docTable.getSelectedRows();
654       if (selectedRows.length > 10) {
655         Object[] possibleValues =
656           "Open the "+selectedRows.length+" documents""Don't open" };
657         int selectedValue =
658           JOptionPane.showOptionDialog(docTable, "Do you want to open "
659           +selectedRows.length+" documents in the central tabbed pane ?",
660           "Warning", JOptionPane.DEFAULT_OPTION,
661           JOptionPane.QUESTION_MESSAGE, null,
662           possibleValues, possibleValues[1]);
663         if (selectedValue == 1
664          || selectedValue == JOptionPane.CLOSED_OPTION) {
665           return;
666         }
667       }
668       for (int row : selectedRows) {
669         // load the document if inside a datastore
670         corpus.get(docTable.rowViewToModel(row));
671       }
672       SwingUtilities.invokeLater(new Runnable() { public void run() {
673         for (int row : selectedRows) {
674           Document doc = (Documentcorpus.get(docTable.rowViewToModel(row));
675           // show the document in the central view
676           mainFrame.select(doc);
677         }
678       }});
679     }
680   }
681 
682   protected void changeMessage() {
683     SwingUtilities.invokeLater(new Runnable(){ public void run() {
684     if (corpus == null || corpus.size() == 0) {
685       newDocumentAction.setEnabled(true);
686       messageLabel.setText(
687         "<html>To add or remove documents to this corpus:<ul>" +
688         "<li>use the toolbar buttons at the top of this view" +
689         "<li>drag documents from the left resources tree and drop them below" +
690         "<li>right click on the corpus in the resources tree and choose 'Populate'" +
691         "</ul></html>");
692       messageLabel.setVisible(true);
693     else if (documentsLoadedCount > 0
694             && documentsLoadedCount == corpus.size()) {
695       newDocumentAction.setEnabled(false);
696       messageLabel.setText("All the documents loaded in the " +
697         "system are in this corpus.");
698       messageLabel.setVisible(true);
699     else if (documentsLoadedCount == 0) {
700       newDocumentAction.setEnabled(false);
701       if (corpus.getDataStore() == null) {
702         messageLabel.setText(
703           "There are no documents loaded in the system. " +
704           "Press F1 for help.");
705       else {
706         messageLabel.setText("Open a document to load it from the datastore.");
707       }
708       messageLabel.setVisible(true);
709     else {
710       newDocumentAction.setEnabled(true);
711       messageLabel.setVisible(false);
712     }
713     }});
714   }
715 
716   protected XJTable docTable;
717   protected DocumentTableModel docTableModel;
718   protected DocumentNameRenderer renderer;
719   protected JToolBar toolbar;
720   protected Corpus corpus;
721   protected NewDocumentAction newDocumentAction;
722   protected RemoveDocumentsAction removeDocumentsAction;
723   protected MoveUpAction moveUpAction;
724   protected MoveDownAction moveDownAction;
725   protected OpenDocumentsAction openDocumentsAction;
726   protected JLabel messageLabel;
727   protected int documentsLoadedCount;
728 }