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