DocumentEditor.java
0001 /*
0002  *  Copyright (c) 1995-2010, The University of Sheffield. See the file
0003  *  COPYRIGHT.txt in the software or at http://gate.ac.uk/gate/COPYRIGHT.txt
0004  *
0005  *  This file is part of GATE (see http://gate.ac.uk/), and is free
0006  *  software, licenced under the GNU Library General Public License,
0007  *  Version 2, June 1991 (in the distribution as file licence.html,
0008  *  and also available at http://gate.ac.uk/gate/licence.html).
0009  *
0010  *  Valentin Tablan, 22 March 2004
0011  *
0012  *  $Id: DocumentEditor.java 12723 2010-06-03 21:30:31Z thomas_heitz $
0013  */
0014 package gate.gui.docview;
0015 
0016 import java.awt.*;
0017 import java.awt.event.*;
0018 import java.util.*;
0019 import java.util.List;
0020 import java.util.Timer;
0021 import java.util.regex.*;
0022 
0023 import javax.swing.*;
0024 import javax.swing.text.JTextComponent;
0025 
0026 import gate.Document;
0027 import gate.Factory;
0028 import gate.Gate;
0029 import gate.GateConstants;
0030 import gate.Resource;
0031 import gate.creole.AbstractVisualResource;
0032 import gate.creole.ResourceData;
0033 import gate.creole.ResourceInstantiationException;
0034 import gate.creole.metadata.CreoleResource;
0035 import gate.creole.metadata.GuiType;
0036 import gate.gui.ActionsPublisher;
0037 import gate.gui.MainFrame;
0038 import gate.gui.annedit.AnnotationData;
0039 import gate.gui.annedit.SearchExpressionsAction;
0040 import gate.swing.JMenuButton;
0041 import gate.util.GateRuntimeException;
0042 import gate.util.OptionsMap;
0043 
0044 /**
0045  * This is the GATE Document viewer/editor. This class is only the shell of the
0046  * main document VR, which gets populated with views (objects that implement
0047  * the {@link DocumentView} interface.
0048  *
0049  * Contains a search dialog and an option menu button.
0050  */
0051 
0052 @CreoleResource(name = "Document Editor", guiType = GuiType.LARGE,
0053     resourceDisplayed = "gate.Document", mainViewer = true)
0054 public class DocumentEditor extends AbstractVisualResource
0055                             implements ActionsPublisher {
0056 
0057   private static final long serialVersionUID = 1L;
0058 
0059   /**
0060    * Save and restore the layout of the views and selected annotations.
0061    */
0062   static class Settings {
0063     int rightViewIdx = 0;
0064     int bottomViewIdx = 0;
0065     List<String[]> annotationSetsTypes = new ArrayList<String[]>();
0066     
0067     public void saveSettings(DocumentEditor de) {
0068       rightViewIdx = de.rightViewIdx;
0069       bottomViewIdx = de.bottomViewIdx;
0070       annotationSetsTypes.clear();
0071       DocumentView dv = de.getRightView();
0072       if (dv instanceof AnnotationSetsView) {
0073         AnnotationSetsView av = (AnnotationSetsView)dv;
0074         for (AnnotationSetsView.SetHandler sh : av.setHandlers) {
0075           for (AnnotationSetsView.TypeHandler th : sh.typeHandlers) {
0076             if (th.isSelected()) {
0077               annotationSetsTypes.add(new String[] {sh.set.getName(), th.name});
0078             }
0079           }
0080         }
0081       }
0082     }
0083 
0084     public void restoreSettings(DocumentEditor de) {
0085       if (de.rightViewIdx != rightViewIdxde.setRightView(rightViewIdx);
0086       if (de.bottomViewIdx != bottomViewIdxde.setBottomView(bottomViewIdx);
0087       DocumentView dv = de.getRightView();
0088       if (dv instanceof AnnotationSetsView) {
0089         AnnotationSetsView av = (AnnotationSetsViewdv;
0090         for (AnnotationSetsView.SetHandler sh : av.setHandlers) {
0091           for (AnnotationSetsView.TypeHandler th : sh.typeHandlers) {
0092             th.setSelected(false);
0093             for (String[] annotationSetType : annotationSetsTypes) {
0094               String setName = annotationSetType[0];
0095               String typeName = annotationSetType[1];
0096               if (av.getSetHandler(setName!= null
0097                && av.getSetHandler(setName).getTypeHandler(typeName!= null) {
0098                 av.setTypeSelected(setName, typeName, true);
0099               }
0100             }
0101           }
0102         }
0103       }
0104     }
0105   }
0106   static Settings settings = new Settings();   
0107   /**
0108    * The document view is just an empty shell. This method publishes the actions
0109    * from the contained views. 
0110    */
0111   public List getActions() {
0112     List actions = new ArrayList();
0113     Iterator<DocumentView> viewIter;
0114     if(getCentralViews() != null){
0115       viewIter = getCentralViews().iterator();
0116       while(viewIter.hasNext()){
0117         actions.addAll(viewIter.next().getActions());
0118       }
0119     }
0120     if(getHorizontalViews() != null){
0121       viewIter = getHorizontalViews().iterator();
0122       while(viewIter.hasNext()){
0123         actions.addAll(viewIter.next().getActions());
0124       }
0125     }
0126     if(getVerticalViews() != null){
0127       viewIter = getVerticalViews().iterator();
0128       while(viewIter.hasNext()){
0129         actions.addAll(viewIter.next().getActions());
0130       }
0131     }
0132     return actions;
0133   }
0134 
0135 
0136   /* (non-Javadoc)
0137    * @see gate.Resource#init()
0138    */
0139   public Resource init() throws ResourceInstantiationException {
0140     addComponentListener(new ComponentAdapter() {
0141       public void componentHidden(ComponentEvent e) {
0142       }
0143       public void componentMoved(ComponentEvent e) {
0144       }
0145       public void componentResized(ComponentEvent e) {
0146         if(!viewsInitedinitViews();
0147       }
0148       //lazily build the GUI only when needed
0149       public void componentShown(ComponentEvent e) {
0150         if(!viewsInitedinitViews();
0151       }
0152     });
0153 
0154     return this;
0155   }
0156   
0157   public void cleanup(){
0158     Iterator<DocumentView> viewsIter;
0159     if(centralViews != null){ 
0160       viewsIter= centralViews.iterator();
0161       while(viewsIter.hasNext()) viewsIter.next().cleanup();
0162       centralViews.clear();
0163     }
0164     if(horizontalViews != null){
0165       viewsIter = horizontalViews.iterator();
0166       while(viewsIter.hasNext()) viewsIter.next().cleanup();
0167       horizontalViews.clear();
0168     }
0169     if(verticalViews != null){
0170       viewsIter = verticalViews.iterator();
0171       while(viewsIter.hasNext()) viewsIter.next().cleanup();
0172       verticalViews.clear();
0173     }
0174   }
0175   
0176   protected void initViews(){
0177     viewsInited = true;
0178     //start building the UI
0179     setLayout(new BorderLayout());
0180     JProgressBar progressBar = new JProgressBar();
0181     progressBar.setStringPainted(true);
0182     progressBar.setMaximumSize(new Dimension(Integer.MAX_VALUE, progressBar.getPreferredSize().height));
0183     add(progressBar, BorderLayout.CENTER);
0184 
0185     progressBar.setString("Building views");
0186     progressBar.setValue(10);
0187 
0188     //create the skeleton UI
0189     topSplit = new JSplitPane(JSplitPane.VERTICAL_SPLIT, null, null);
0190     topSplit.setResizeWeight(0.3);
0191     topSplit.setContinuousLayout(true);
0192     topSplit.setOneTouchExpandable(true);
0193     bottomSplit = new JSplitPane(JSplitPane.VERTICAL_SPLIT, topSplit, null);
0194     bottomSplit.setResizeWeight(0.7);
0195     bottomSplit.setContinuousLayout(true);
0196     bottomSplit.setOneTouchExpandable(true);
0197     horizontalSplit = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, bottomSplit, null);
0198     horizontalSplit.setResizeWeight(0.7);
0199     horizontalSplit.setContinuousLayout(true);
0200     horizontalSplit.setOneTouchExpandable(true);
0201 
0202     //create the bars
0203     topBar = new JToolBar(JToolBar.HORIZONTAL);
0204     topBar.setFloatable(false);
0205     add(topBar, BorderLayout.NORTH);
0206 
0207     progressBar.setValue(40);
0208 
0209     centralViews = new ArrayList<DocumentView>();
0210     verticalViews = new ArrayList<DocumentView>();
0211     horizontalViews = new ArrayList<DocumentView>();
0212 
0213     //parse all Creole resources and look for document views
0214     Set<String> vrSet = Gate.getCreoleRegister().getVrTypes();
0215     List<ResourceData> viewTypes = new ArrayList<ResourceData>();
0216     for(String vr : vrSet) {
0217       ResourceData rData = Gate.getCreoleRegister().get(vr);
0218       try {
0219         if(DocumentView.class.isAssignableFrom(rData.getResourceClass())) {
0220           viewTypes.add(rData);
0221         }
0222       }
0223       catch(ClassNotFoundException cnfe) {
0224         cnfe.printStackTrace();
0225       }
0226     }
0227     //sort view types by label
0228     Collections.sort(viewTypes, new Comparator<ResourceData>(){
0229       public int compare(ResourceData rd1, ResourceData rd2){
0230         return rd1.getName().compareTo(rd2.getName());
0231       }
0232     });
0233     for(ResourceData viewType : viewTypes) {
0234       try {
0235         //create the resource
0236         DocumentView aView = (DocumentViewFactory.
0237           createResource(viewType.getClassName());
0238         aView.setTarget(document);
0239         aView.setOwner(this);
0240         //add the view
0241         addView(aView, viewType.getName());
0242       }
0243       catch(ResourceInstantiationException rie) {
0244         rie.printStackTrace();
0245       }
0246     }
0247     //select the main central view only
0248     if(centralViews.size() 0setCentralView(0);
0249     
0250     //populate the main VIEW
0251     remove(progressBar);
0252     add(horizontalSplit, BorderLayout.CENTER);
0253     searchAction = new SearchAction();
0254     JButton searchButton = new JButton(searchAction);
0255     searchButton.setMargin(new Insets(0000));
0256     topBar.add(Box.createHorizontalStrut(5));
0257     topBar.add(searchButton);
0258 
0259     // add a key binding for the search function
0260     getInputMap(WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(
0261       KeyStroke.getKeyStroke("control F")"Search in text");
0262     getActionMap().put("Search in text", searchAction);
0263 
0264     // create menu that contains several options for the document editor
0265     final OptionsMap config = Gate.getUserConfig();
0266     final JPopupMenu optionsMenu = new JPopupMenu("Options menu");
0267     final JMenuItem saveCurrentLayoutMenuItem = new JMenuItem(
0268       new AbstractAction("Save Current Layout") {
0269       public void actionPerformed(ActionEvent evt) {
0270         settings.saveSettings(DocumentEditor.this);
0271       }
0272     });
0273     optionsMenu.add(saveCurrentLayoutMenuItem);
0274     final JCheckBoxMenuItem restoreLayoutAutomaticallyMenuItem =
0275       new JCheckBoxMenuItem("Restore Layout Automatically");
0276     restoreLayoutAutomaticallyMenuItem.setSelected(Gate.getUserConfig()
0277      .getBoolean(DocumentEditor.class.getName()+".restoreLayoutAutomatically"));
0278     restoreLayoutAutomaticallyMenuItem.addItemListener(new ItemListener() {
0279       public void itemStateChanged(ItemEvent e) {
0280         // whenever the user checks/unchecks, update the config
0281         config.put(DocumentEditor.class.getName()+".restoreLayoutAutomatically",
0282         restoreLayoutAutomaticallyMenuItem.isSelected());
0283       }
0284     });
0285     optionsMenu.add(restoreLayoutAutomaticallyMenuItem);
0286     final JCheckBoxMenuItem readOnly = new JCheckBoxMenuItem("Read-only");
0287     readOnly.addItemListener(new ItemListener() {
0288       public void itemStateChanged(ItemEvent e) {
0289         config.put(GateConstants.DOCEDIT_READ_ONLY, readOnly.isSelected());
0290         setEditable(!readOnly.isSelected());
0291       }
0292     });
0293     readOnly.setSelected(config.getBoolean(GateConstants.DOCEDIT_READ_ONLY));
0294     optionsMenu.addSeparator();
0295     optionsMenu.add(readOnly);
0296     ButtonGroup buttonGroup = new ButtonGroup();
0297     final JRadioButtonMenuItem insertAppend =
0298       new JRadioButtonMenuItem("Insert Append");
0299     buttonGroup.add(insertAppend);
0300     insertAppend.setSelected(
0301       config.getBoolean(GateConstants.DOCEDIT_INSERT_APPEND));
0302     insertAppend.addItemListener(new ItemListener() {
0303       public void itemStateChanged(ItemEvent e) {
0304         config.put(GateConstants.DOCEDIT_INSERT_APPEND,
0305           insertAppend.isSelected());
0306       }
0307     });
0308     optionsMenu.addSeparator();
0309     optionsMenu.add(insertAppend);
0310     final JRadioButtonMenuItem insertPrepend =
0311       new JRadioButtonMenuItem("Insert Prepend");
0312     buttonGroup.add(insertPrepend);
0313     insertPrepend.setSelected(
0314       config.getBoolean(GateConstants.DOCEDIT_INSERT_PREPEND));
0315     insertPrepend.addItemListener(new ItemListener() {
0316       public void itemStateChanged(ItemEvent e) {
0317         config.put(GateConstants.DOCEDIT_INSERT_PREPEND,
0318           insertPrepend.isSelected());
0319       }
0320     });
0321     optionsMenu.add(insertPrepend);
0322     // if none set then set the default one
0323     if (!(insertAppend.isSelected()
0324        || insertPrepend.isSelected())) {
0325       insertAppend.setSelected(true);
0326     }
0327     JMenuButton menuButton = new JMenuButton(optionsMenu);
0328     menuButton.setIcon(MainFrame.getIcon("expanded"));
0329     menuButton.setToolTipText("Options for the document editor");
0330     menuButton.setMargin(new Insets(0001))// icon is not centred
0331     topBar.add(Box.createHorizontalGlue());
0332     topBar.add(menuButton);
0333 
0334     // when the editor is shown restore views if layout saving is enable
0335     addFocusListener(new FocusAdapter () {
0336       public void focusGained(FocusEvent e) {
0337         super.focusGained(e);
0338         if(Gate.getUserConfig().getBoolean(
0339           DocumentEditor.class.getName()+".restoreLayoutAutomatically")) {
0340           settings.restoreSettings(DocumentEditor.this);
0341         }
0342       }
0343     });
0344     
0345     validate();
0346   }
0347   
0348   public List<DocumentView> getCentralViews(){
0349     return centralViews == null null 
0350       Collections.unmodifiableList(centralViews);
0351   }
0352   
0353   public List<DocumentView> getHorizontalViews(){
0354     return horizontalViews == null null 
0355       Collections.unmodifiableList(horizontalViews);
0356   }
0357   
0358   public List<DocumentView> getVerticalViews(){
0359     return verticalViews == null null 
0360       Collections.unmodifiableList(verticalViews);
0361   }
0362   
0363 
0364   /**
0365    * Registers a new view by adding it to the right list and creating the 
0366    * activation button for it.
0367    @param view view to add to the GUI as a button
0368    @param name name of the view used in the GUI as a button name
0369    */
0370   protected void addView(DocumentView view, String name){
0371     topBar.add(Box.createHorizontalStrut(5));
0372     final ViewButton viewButton = new ViewButton(view, name);
0373     switch(view.getType()){
0374       case DocumentView.CENTRAL :
0375         centralViews.add(view);
0376 //        leftBar.add(new ViewButton(view, name));
0377         topBar.add(viewButton);
0378         break;
0379       case DocumentView.VERTICAL :
0380         verticalViews.add(view);
0381 //        rightBar.add(new ViewButton(view, name));
0382         topBar.add(viewButton);
0383         break;
0384       case DocumentView.HORIZONTAL :
0385         horizontalViews.add(view);
0386         topBar.add(viewButton);
0387 //        bottomBar.add(new ViewButton(view, name));
0388         break;
0389       default :
0390         throw new GateRuntimeException(
0391           getClass().getName() +  ": Invalid view type");
0392     }
0393 
0394     // binds a F-key to each view toggle button
0395     // avoid the F-Key F1,2,6,8,10 because already used
0396     if ((fKeyNumber == 5|| (fKeyNumber == 7|| (fKeyNumber == 9)) {
0397       fKeyNumber++;
0398     }
0399     getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)
0400       .put(KeyStroke.getKeyStroke("F" (fKeyNumber + 1)),
0401         "Shows view "+ fKeyNumber + 1);
0402     getActionMap().put("Shows view " + fKeyNumber + 1,
0403       new AbstractAction() {
0404         public void actionPerformed(ActionEvent evt) {
0405           viewButton.doClick();
0406         }
0407       }
0408     );
0409     // add a tooltip with the key shortcut
0410     if (view instanceof AnnotationSetsView) {
0411       getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)
0412         .put(KeyStroke.getKeyStroke("shift F" (fKeyNumber + 1)),
0413         "Shows view " + fKeyNumber + 1);
0414 
0415       viewButton.setToolTipText("<html>Toggle the view of " + name
0416         +"&nbsp;&nbsp;<font color=#667799><small>F" (fKeyNumber + 1)
0417         +"&nbsp;&nbsp;</small></font>"
0418         +"<br>Set last selected annotations "
0419         +"&nbsp;&nbsp;<font color=#667799><small>Shift+F" (fKeyNumber + 1)
0420         +"&nbsp;&nbsp;</small></font></html>");
0421 
0422     else {
0423       viewButton.setToolTipText("<html>Toggle the view of "+ name
0424         +"&nbsp;&nbsp;<font color=#667799><small>F" (fKeyNumber + 1)
0425         +"&nbsp;&nbsp;</small></font></html>");
0426     }
0427     fKeyNumber++;
0428   }
0429 
0430   /**
0431    * Gets the currently showing top view
0432    @return {@link DocumentView} object.
0433    */
0434   protected DocumentView getTopView(){
0435     return (topViewIdx == -1null : horizontalViews.get(topViewIdx);
0436   }
0437   
0438   /**
0439    * Shows a new top view based on an index in the {@link #horizontalViews}
0440    * list.
0441    @param index the index in {@link #horizontalViews} list for the new
0442    * view to be shown.
0443    */
0444   public void setTopView(int index){
0445     //deactivate current view
0446     DocumentView oldView = getTopView();
0447     if(oldView != null){
0448       oldView.setActive(false);
0449     }
0450     topViewIdx = index;
0451     if(topViewIdx == -1setTopView(null);
0452     else{
0453       DocumentView newView = horizontalViews.get(topViewIdx);
0454       //hide if shown at the bottom
0455       if(bottomViewIdx == topViewIdx){
0456         setBottomView(null);
0457         bottomViewIdx  = -1;
0458       }
0459       //activate if necessary
0460       if(!newView.isActive()){
0461         newView.setActive(true);
0462       }
0463       //show the new view
0464       setTopView(newView);
0465     }
0466   }
0467 
0468   /**
0469    * Sets a new UI component in the top location. This method is intended to 
0470    * only be called from {@link #setTopView(int)}.
0471    @param view the new view to be shown.
0472    */
0473   protected void setTopView(DocumentView view){
0474     topSplit.setTopComponent(view == null null : view.getGUI());
0475     topSplit.resetToPreferredSizes();
0476     updateBar(topBar);
0477     validate();
0478   }
0479 
0480   /**
0481    * Gets the currently showing central view
0482    @return {@link DocumentView} object.
0483    */
0484   protected DocumentView getCentralView(){
0485     return (centralViewIdx == -1null : centralViews.get(centralViewIdx);
0486   }
0487   
0488   /**
0489    * Shows a new central view based on an index in the {@link #centralViews}
0490    * list.
0491    @param index the index in {@link #centralViews} list for the new
0492    * view to be shown.
0493    */
0494   public void setCentralView(int index){
0495     //deactivate current view
0496     DocumentView oldView = getCentralView();
0497     if(oldView != null){
0498       oldView.setActive(false);
0499     }
0500     centralViewIdx = index;
0501     if(centralViewIdx == -1setCentralView(null);
0502     else{
0503       DocumentView newView = centralViews.get(centralViewIdx);
0504       //activate if necessary
0505       if(!newView.isActive()){
0506         newView.setActive(true);
0507       }
0508       //show the new view
0509       setCentralView(newView);
0510     }
0511   }
0512 
0513   /**
0514    * Sets a new UI component in the central location. This method is intended to 
0515    * only be called from {@link #setCentralView(int)}.
0516    @param view the new view to be shown.
0517    */
0518   protected void setCentralView(DocumentView view){
0519     topSplit.setBottomComponent(view == null null : view.getGUI());
0520     topSplit.resetToPreferredSizes();
0521 //    updateBar(leftBar);
0522     updateBar(topBar);
0523     validate();
0524   }
0525   
0526   
0527   /**
0528    * Gets the currently showing bottom view
0529    @return {@link DocumentView} object.
0530    */
0531   protected DocumentView getBottomView(){
0532     return (bottomViewIdx == -1?  null : horizontalViews.get(bottomViewIdx);
0533   }
0534   
0535   /**
0536    * Shows a new bottom view based on an index in the {@link #horizontalViews}
0537    * list.
0538    @param index the index in {@link #horizontalViews} list for the new
0539    * view to be shown.
0540    */
0541   public void setBottomView(int index){
0542     //deactivate current view
0543     DocumentView oldView = getBottomView();
0544     if(oldView != null){
0545       oldView.setActive(false);
0546     }
0547     bottomViewIdx = index;
0548     if(bottomViewIdx == -1){
0549       setBottomView(null);
0550     }else{
0551       DocumentView newView = horizontalViews.get(bottomViewIdx);
0552       //hide if shown at the top
0553       if(topViewIdx == bottomViewIdx){
0554         setTopView(null);
0555         topViewIdx  = -1;
0556       }
0557       //activate if necessary
0558       if(!newView.isActive()){
0559         newView.setActive(true);
0560       }
0561       //show the new view
0562       setBottomView(newView);
0563     }
0564   }
0565 
0566   /**
0567    * Sets a new UI component in the top location. This method is intended to 
0568    * only be called from {@link #setBottomView(int)}.
0569    @param view the new view to be shown.
0570    */
0571   protected void setBottomView(DocumentView view){
0572     bottomSplit.setBottomComponent(view == null null : view.getGUI());
0573     bottomSplit.resetToPreferredSizes();
0574 //    updateBar(bottomBar);
0575     updateBar(topBar);
0576     validate();
0577   }
0578   
0579   
0580   /**
0581    * Gets the currently showing right view
0582    @return {@link DocumentView} object.
0583    */
0584   protected DocumentView getRightView(){
0585     return (rightViewIdx == -1null : verticalViews.get(rightViewIdx);
0586   }
0587   
0588   /**
0589    * Shows a new right view based on an index in the {@link #verticalViews}
0590    * list.
0591    @param index the index in {@link #verticalViews} list for the new
0592    * view to be shown.
0593    */
0594   public void setRightView(int index){
0595     //deactivate current view
0596     DocumentView oldView = getRightView();
0597     if(oldView != null){
0598       oldView.setActive(false);
0599     }
0600     rightViewIdx = index;
0601     if(rightViewIdx == -1setRightView(null);
0602     else{
0603       DocumentView newView = verticalViews.get(rightViewIdx);
0604       //activate if necessary
0605       if(!newView.isActive()){
0606         newView.setActive(true);
0607       }
0608       //show the new view
0609       setRightView(newView);
0610     }
0611   }
0612 
0613   /**
0614    * Sets a new UI component in the right hand side location. This method is 
0615    * intended to only be called from {@link #setRightView(int)}.
0616    @param view the new view to be shown.
0617    */
0618   protected void setRightView(DocumentView view){
0619     horizontalSplit.setRightComponent(view == null null : view.getGUI());
0620 //    updateBar(rightBar);
0621     updateBar(topBar);
0622     validate();
0623   }  
0624   
0625   /**
0626    * Change the set of selected annotations. This new value will be
0627    * sent to all active constituent views.
0628    @param selectedAnnots list of AnnotationData to select
0629    */
0630   public void setSelectedAnnotations(List<AnnotationData> selectedAnnots){
0631     selectedAnnotations.clear();
0632     selectedAnnotations.addAll(selectedAnnots);
0633     //notify all active views
0634     for(DocumentView aView : centralViews){
0635       if(aView.isActive()) aView.setSelectedAnnotations(selectedAnnotations);
0636     }
0637     for(DocumentView aView : horizontalViews){
0638       if(aView.isActive()) aView.setSelectedAnnotations(selectedAnnotations);
0639     }
0640     for(DocumentView aView : verticalViews){
0641       if(aView.isActive()) aView.setSelectedAnnotations(selectedAnnotations);
0642     }
0643   }
0644   
0645   /**
0646    * Gets the current set of selected annotations.
0647    @return set of selected annotations
0648    */
0649   public List<AnnotationData> getSelectedAnnotations(){
0650     return selectedAnnotations;
0651   }
0652 
0653   /**
0654    * TODO: to remove? doesn't seems to be used anywhere.
0655    */
0656   protected void updateSplitLocation(JSplitPane split, int foo){
0657     Component left = split.getLeftComponent();
0658     Component right = split.getRightComponent();
0659     if(left == null){
0660       split.setDividerLocation(0);
0661       return;
0662     }
0663     if(right == null){ 
0664       split.setDividerLocation(1);
0665       return;
0666     }
0667     Dimension leftPS = left.getPreferredSize();
0668     Dimension rightPS = right.getPreferredSize();
0669     double location = split.getOrientation() == JSplitPane.HORIZONTAL_SPLIT ? 
0670       (double)leftPS.width / (leftPS.width + rightPS.width:
0671       (double)leftPS.height / (leftPS.height + rightPS.height);
0672     split.setDividerLocation(location);
0673   }
0674   
0675   /* (non-Javadoc)
0676    * @see gate.VisualResource#setTarget(java.lang.Object)
0677    */
0678   public void setTarget(Object target) {
0679     this.document = (Document)target;
0680   }
0681 
0682   /**
0683    * Updates the selected state of the buttons on one of the toolbars. 
0684    @param toolbar toolbar to update
0685    */
0686   protected void updateBar(JToolBar toolbar){
0687     Component btns[] = toolbar.getComponents();
0688     if(btns != null){
0689       for(Component btn : btns) {
0690         if(btn instanceof ViewButton)
0691           ((ViewButtonbtn).updateSelected();
0692       }
0693     }
0694   }
0695 
0696   /**
0697    @return the text component associated with this document editor.
0698    */
0699   protected JTextComponent getTextComponent() {
0700     return (JTextComponent) (((JScrollPane)getCentralView()
0701       .getGUI()).getViewport()).getView();
0702   }
0703 
0704   /**
0705    * Set the document as editable or readonly.
0706    * Documents are editable by default.
0707    @param editable true if editable, false if readonly
0708    */
0709   public void setEditable(boolean editable) {
0710     getTextComponent().setEditable(editable);
0711   }
0712 
0713   /**
0714    * Dialog to search an expression in the document.
0715    * Select the current match in the document.
0716    * Options: incremental search, case insensitive, whole word,
0717    * hightlighted annotations, regular expression.
0718    */
0719   protected class SearchAction extends AbstractAction {
0720 
0721     public SearchAction() {
0722       super();
0723       putValue(SHORT_DESCRIPTION, "<html>Search within the document." +
0724         "&nbsp;&nbsp;<font color=#667799><small>Ctrl-F" +
0725         "&nbsp;&nbsp;</small></font></html>");
0726       putValue(SMALL_ICON, MainFrame.getIcon("search"));
0727     }
0728 
0729     public void actionPerformed(ActionEvent evt) {
0730       if (searchDialog == null) {
0731         Window parent =
0732           SwingUtilities.getWindowAncestor(DocumentEditor.this);
0733         searchDialog = (parent instanceof Dialog)?
0734           new SearchDialog((Dialog)parent):new SearchDialog((Frame)parent);
0735         searchDialog.pack();
0736         searchDialog.setLocationRelativeTo(DocumentEditor.this);
0737         searchDialog.setResizable(true);
0738         MainFrame.getGuiRoots().add(searchDialog);
0739       }
0740 
0741       JTextComponent textPane = getTextComponent();
0742 
0743       // if the user never gives the focus to the textPane then
0744       // there will never be any selection in it so we force it
0745       textPane.requestFocusInWindow();
0746 
0747       // put the selection of the document into the search text field
0748       if (textPane.getSelectedText() != null) {
0749         searchDialog.patternTextField.setText(textPane.getSelectedText());
0750       }
0751 
0752       if (searchDialog.isVisible()) {
0753         searchDialog.toFront();
0754       else {
0755         searchDialog.setVisible(true);
0756       }
0757       searchDialog.patternTextField.selectAll();
0758       searchDialog.patternTextField.requestFocusInWindow();
0759     }
0760   }
0761 
0762   protected class SearchDialog extends JDialog {
0763 
0764     SearchDialog(Frame owner) {
0765       super(owner, false);
0766       setTitle("Search in \"" + document.getName() "\"");
0767       initLocalData();
0768       initGuiComponents();
0769       initListeners();
0770     }
0771 
0772     SearchDialog(Dialog owner) {
0773       super(owner, false);
0774       setTitle("Search in \"" + document.getName() "\"");
0775       initLocalData();
0776       initGuiComponents();
0777       initListeners();
0778     }
0779 
0780     protected void initLocalData() {
0781       pattern = null;
0782       nextMatchStartsFrom = 0;
0783       content = document.getContent().toString();
0784 
0785       findFirstAction = new AbstractAction("Find first") {
0786         {
0787           putValue(SHORT_DESCRIPTION, "Finds first match");
0788           putValue(MNEMONIC_KEY, KeyEvent.VK_F);
0789         }
0790         public void actionPerformed(ActionEvent evt) {
0791           refresh();
0792           if(!isValidRegularExpression()) return;
0793           boolean found = false;
0794           int start = -1;
0795           int end = -1;
0796           nextMatchStartsFrom = 0;
0797 
0798           Matcher matcher = pattern.matcher(content);
0799           while (matcher.find(nextMatchStartsFrom&& !found) {
0800             start = matcher.start();
0801             end = matcher.end();
0802             found = false;
0803             if (highlightsChk.isSelected()) {
0804               javax.swing.text.Highlighter.Highlight[] highlights =
0805                 textPane.getHighlighter().getHighlights();
0806               for (javax.swing.text.Highlighter.Highlight h : highlights) {
0807                 if (h.getStartOffset() <= start && h.getEndOffset() >= end) {
0808                   found = true;
0809                   break;
0810                 }
0811               }
0812             else {
0813               found = true;
0814             }
0815             nextMatchStartsFrom = end;
0816           }
0817 
0818           if (found) {
0819             setTitle("Found: \"" +
0820               content.substring(Math.max(0, start-13), start)
0821                 .replaceAll("\\s+"" ""[" +
0822               content.substring(start, end).replaceAll("\\s+"" ""]" +
0823               content.substring(end, Math.min(content.length(), end+13))
0824                 .replaceAll("\\s+"" ""\"");
0825             // select the match in the document
0826             textPane.setCaretPosition(start);
0827             textPane.moveCaretPosition(end);
0828 
0829           else {
0830             setTitle("Expression not found at all in the document.");
0831             findFirstAction.setEnabled(false);
0832             findNextAction.setEnabled(false);
0833           }
0834           patternTextField.requestFocusInWindow();
0835         }};
0836 
0837       findNextAction = new AbstractAction("Find next") {
0838         {
0839           putValue(SHORT_DESCRIPTION, "Finds next match");
0840           putValue(MNEMONIC_KEY, KeyEvent.VK_N);
0841         }
0842         public void actionPerformed(ActionEvent evt) {
0843           refresh();
0844           if(!isValidRegularExpression()) return;
0845           boolean found = false;
0846           int start = -1;
0847           int end = -1;
0848           if (evt == null) {
0849             // incremental search
0850             nextMatchStartsFrom = textPane.getSelectionStart();
0851           else {
0852             nextMatchStartsFrom = textPane.getCaretPosition();
0853           }
0854 
0855           Matcher matcher = pattern.matcher(content);
0856           while (matcher.find(nextMatchStartsFrom&& !found) {
0857             start = matcher.start();
0858             end = matcher.end();
0859             found = false;
0860             if (highlightsChk.isSelected()) {
0861               javax.swing.text.Highlighter.Highlight[] highlights =
0862                 textPane.getHighlighter().getHighlights();
0863               for (javax.swing.text.Highlighter.Highlight h : highlights) {
0864                 if (h.getStartOffset() <= start && h.getEndOffset() >= end) {
0865                   found = true;
0866                   break;
0867                 }
0868               }
0869             else {
0870               found = true;
0871             }
0872             nextMatchStartsFrom = end;
0873           }
0874 
0875           if (found) {
0876             setTitle("Found: \"" +
0877               content.substring(Math.max(0, start-13), start)
0878                 .replaceAll("\\s+"" ""[" +
0879               content.substring(start, end).replaceAll("\\s+"" ""]" +
0880               content.substring(end, Math.min(content.length(), end+13))
0881                 .replaceAll("\\s+"" ""\"");
0882             // select the match in the document
0883             textPane.setCaretPosition(start);
0884             textPane.moveCaretPosition(end);
0885 
0886           else {
0887             setTitle("Expression not found after the document caret.");
0888             findNextAction.setEnabled(false);
0889           }
0890           patternTextField.requestFocusInWindow();
0891         }};
0892 
0893       cancelAction = new AbstractAction("Cancel") {
0894         {
0895           putValue(SHORT_DESCRIPTION, "Cancel");
0896         }
0897         public void actionPerformed(ActionEvent evt){
0898           searchDialog.setVisible(false);
0899         }
0900       };
0901     }
0902 
0903     protected void initGuiComponents() {
0904       getContentPane().setLayout(new BoxLayout(getContentPane(),
0905               BoxLayout.Y_AXIS));
0906 
0907       getContentPane().add(Box.createVerticalStrut(5));
0908 
0909       Box hBox = Box.createHorizontalBox();
0910       hBox.add(Box.createHorizontalStrut(6));
0911       hBox.add(new JLabel("Find:"));
0912       hBox.add(Box.createHorizontalStrut(6));
0913       hBox.add(patternTextField = new JTextField(20));
0914       hBox.add(Box.createHorizontalStrut(3));
0915       JButton helpRegExpButton = new JButton("?");
0916       helpRegExpButton.setMargin(new Insets(0202));
0917       helpRegExpButton.setToolTipText("GATE search expression builder.");
0918 
0919       hBox.add(helpRegExpButton);
0920       hBox.add(Box.createHorizontalGlue());
0921 
0922       hBox.add(Box.createHorizontalStrut(6));
0923       hBox.add(Box.createHorizontalGlue());
0924       getContentPane().add(hBox);
0925 
0926       getContentPane().add(Box.createVerticalStrut(5));
0927 
0928       hBox = Box.createHorizontalBox();
0929       hBox.add(Box.createHorizontalStrut(6));
0930       hBox.add(ignoreCaseChk = new JCheckBox("Ignore case"true));
0931       hBox.add(Box.createHorizontalStrut(6));
0932       hBox.add(wholeWordsChk = new JCheckBox("Whole word"false));
0933       hBox.add(Box.createHorizontalStrut(6));
0934       hBox.add(regularExpressionChk =
0935         new JCheckBox("Regular Exp."false));
0936       regularExpressionChk.setToolTipText("Regular expression search.");
0937       hBox.add(Box.createHorizontalStrut(6));
0938       hBox.add(highlightsChk = new JCheckBox("Highlights"false));
0939       highlightsChk.setToolTipText(
0940         "Restrict the search on the highlighted annotations.");
0941       hBox.add(Box.createHorizontalStrut(6));
0942       hBox.add(Box.createHorizontalGlue());
0943       getContentPane().add(hBox);
0944 
0945       getContentPane().add(Box.createVerticalStrut(5));
0946 
0947       hBox = Box.createHorizontalBox();
0948       hBox.add(Box.createHorizontalGlue());
0949       JButton findFirstButton = new JButton(findFirstAction);
0950       hBox.add(findFirstButton);
0951       hBox.add(Box.createHorizontalStrut(6));
0952       hBox.add(new JButton(findNextAction));
0953       hBox.add(Box.createHorizontalStrut(6));
0954       hBox.add(new JButton(cancelAction));
0955       hBox.add(Box.createHorizontalGlue());
0956       getContentPane().add(hBox);
0957 
0958       getContentPane().add(Box.createVerticalStrut(5));
0959 
0960       getRootPane().setDefaultButton(findFirstButton);
0961 
0962       helpRegExpButton.addActionListener(new SearchExpressionsAction(
0963         patternTextField, this, regularExpressionChk));
0964     }
0965 
0966     protected void initListeners() {
0967 
0968       addComponentListener(new ComponentAdapter() {
0969         public void componentShown(ComponentEvent e) {
0970           refresh();
0971         }
0972       });
0973 
0974      // incremental search
0975       patternTextField.getDocument().addDocumentListener(
0976         new javax.swing.event.DocumentListener() {
0977           private Timer timer = new Timer("Document Editor search timer"true);
0978           private TimerTask timerTask;
0979           public void insertUpdate(javax.swing.event.DocumentEvent e) {
0980             update();
0981           }
0982           public void removeUpdate(javax.swing.event.DocumentEvent e) {
0983             update();
0984           }
0985           public void changedUpdate(javax.swing.event.DocumentEvent e) {
0986             refresh();
0987           }
0988           private void update() {
0989             if (timerTask != null) { timerTask.cancel()}
0990             refresh();
0991             Date timeToRun = new Date(System.currentTimeMillis() 250);
0992             timerTask = new TimerTask() { public void run() {
0993               findNextAction.actionPerformed(null);
0994             }};
0995             // add a delay
0996             timer.schedule(timerTask, timeToRun);
0997           }
0998         });
0999 
1000       wholeWordsChk.addActionListener(new ActionListener() {
1001         public void actionPerformed(ActionEvent e) {
1002           refresh();
1003         }
1004       });
1005       ignoreCaseChk.addActionListener(new ActionListener() {
1006         public void actionPerformed(ActionEvent e) {
1007           refresh();
1008         }
1009       });
1010       regularExpressionChk.addActionListener(new ActionListener() {
1011         public void actionPerformed(ActionEvent e) {
1012           refresh();
1013         }
1014       });
1015       highlightsChk.addActionListener(new ActionListener() {
1016         public void actionPerformed(ActionEvent e) {
1017           refresh();
1018         }
1019       });
1020 
1021       ((JComponent)getContentPane())
1022       .getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).
1023       put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0)"cancelAction");
1024       ((JComponent)getContentPane())
1025       .getActionMap().put("cancelAction", cancelAction);
1026     }
1027 
1028     /**
1029      * Builds and validates the regular expression before use.
1030      @return true if the regular expression is valid, false otherwise.
1031      */
1032     protected boolean isValidRegularExpression() {
1033       String patternText = patternTextField.getText();
1034       boolean valid = true;
1035       //update patternRE
1036       try {
1037         String prefixPattern = wholeWordsChk.isSelected() "\\b":"";
1038         prefixPattern += regularExpressionChk.isSelected() "":"\\Q";
1039         String suffixPattern = regularExpressionChk.isSelected() "":"\\E";
1040         suffixPattern += wholeWordsChk.isSelected() "\\b":"";
1041         patternText = prefixPattern + patternText + suffixPattern;
1042         pattern = ignoreCaseChk.isSelected() ?
1043                   Pattern.compile(patternText, Pattern.CASE_INSENSITIVE:
1044                   Pattern.compile(patternText);
1045 
1046       catch (PatternSyntaxException e) {
1047         setTitle(e.getMessage().replaceFirst("(?s) near index .+$""."));
1048         int index = e.getMessage().indexOf(" near index ");
1049         if (index != -1) {
1050           index += " near index ".length();
1051           patternTextField.setCaretPosition(Integer.valueOf(
1052             e.getMessage().substring(index, index+1)));
1053         }
1054         patternTextField.requestFocusInWindow();
1055         valid = false;
1056       }
1057       return valid;
1058     }
1059 
1060     protected void refresh() {
1061       String patternText = patternTextField.getText();
1062 
1063       if (patternText != null && patternText.length() 0) {
1064         //update actions state
1065         findFirstAction.setEnabled(true);
1066         findNextAction.setEnabled(true);
1067       else {
1068         findFirstAction.setEnabled(false);
1069         findNextAction.setEnabled(false);
1070       }
1071     }
1072 
1073     JTextComponent textPane = getTextComponent();
1074     JTextField patternTextField;
1075     JCheckBox ignoreCaseChk;
1076     JCheckBox wholeWordsChk;
1077     JCheckBox regularExpressionChk;
1078     JCheckBox highlightsChk;
1079     Pattern pattern;
1080     int nextMatchStartsFrom;
1081     String content;
1082     Action findFirstAction;
1083     Action findNextAction;
1084     Action cancelAction;
1085 
1086   // end of class SearchDialog
1087 
1088   protected class ViewButton extends JToggleButton{
1089     public ViewButton(DocumentView aView, String name){
1090       super();
1091       setSelected(false);
1092 //      setBorder(null);
1093       this.view = aView;
1094       setText(name);
1095       
1096 //      if(aView.getType() == DocumentView.HORIZONTAL){
1097 //        setText(name);
1098 //      }else if(aView.getType() == DocumentView.CENTRAL){
1099 //        setIcon(new VerticalTextIcon(this, name, VerticalTextIcon.ROTATE_LEFT));
1100 //      }else if(aView.getType() == DocumentView.VERTICAL){
1101 //        setIcon(new VerticalTextIcon(this, name, 
1102 //                                     VerticalTextIcon.ROTATE_RIGHT));
1103 //      }
1104       
1105       addActionListener(new ActionListener(){
1106         public void actionPerformed(ActionEvent evt){
1107           if(isSelected()){
1108             //show this new view
1109             switch(view.getType()){
1110               case DocumentView.CENTRAL:
1111                 setCentralView(centralViews.indexOf(view));
1112                 break;
1113               case DocumentView.VERTICAL:
1114                 setRightView(verticalViews.indexOf(view));
1115                 break;
1116               case DocumentView.HORIZONTAL:
1117 //                if(ViewButton.this.getParent() == topBar){
1118 //                  setTopView(horizontalViews.indexOf(view));
1119 //                }else{
1120                   setBottomView(horizontalViews.indexOf(view));
1121 //                }
1122                 break;
1123             }
1124 
1125             // the view is an annotation sets view
1126             if (view instanceof AnnotationSetsView
1127             && annotationSetsViewFirstTime) {
1128               annotationSetsViewFirstTime = false;
1129               AnnotationSetsView asv = (AnnotationSetsView)view;
1130 
1131               // shift key was pressed
1132               if (evt.getModifiers() == ActionEvent.SHIFT_MASK
1133               || (evt.getModifiers() == ActionEvent.SHIFT_MASK
1134                   + ActionEvent.MOUSE_EVENT_MASK)) {
1135                 asv.restoreSavedSelectedTypes();
1136 
1137               else {
1138                 // expand default set
1139                 asv.getSetHandler(null).setExpanded(true);
1140                 if (document.getAnnotationSetNames() != null) {
1141                   for (Object setName : document.getAnnotationSetNames()) {
1142                     if (!setName.equals("Original markups")) {
1143                       // expand other annotation sets
1144                       asv.getSetHandler((String)setName).setExpanded(true);
1145                     }
1146                   }
1147                 }
1148               }
1149 
1150               // remove the tooltip for the shift key
1151               setToolTipText(getToolTipText().replaceFirst(
1152                 "<br>.*</html>$""</html>"));
1153             }
1154 
1155           }else{
1156             //hide this view
1157             switch(view.getType()){
1158               case DocumentView.CENTRAL:
1159                 setCentralView(-1);
1160                 break;
1161               case DocumentView.VERTICAL:
1162                 setRightView(-1);
1163                 break;
1164               case DocumentView.HORIZONTAL:
1165 //                if(ViewButton.this.getParent() == topBar){
1166 //                  setTopView(-1);
1167 //                }else{
1168                   setBottomView(-1);
1169 //                }
1170                 break;
1171             }
1172           }
1173           if (view instanceof TextualDocumentView) {
1174             // enable/disable according to text visibility
1175             searchAction.setEnabled(isSelected());
1176           }
1177         }
1178       });
1179     }
1180     
1181     public void updateSelected(){
1182       switch(view.getType()){
1183         case DocumentView.CENTRAL:
1184           setSelected(getCentralView() == view);
1185           break;
1186         case DocumentView.VERTICAL:
1187           setSelected(getRightView() == view);
1188           break;
1189         case DocumentView.HORIZONTAL:
1190 //          if(ViewButton.this.getParent() == topBar){
1191 //            setSelected(getTopView() == view);
1192 //          }else{
1193             setSelected(getBottomView() == view);
1194 //          }
1195           break;
1196       }
1197     }
1198     DocumentView view;
1199     boolean annotationSetsViewFirstTime = true;
1200   }
1201 
1202   protected JSplitPane horizontalSplit;
1203   protected JSplitPane topSplit;
1204   protected JSplitPane bottomSplit;
1205 
1206   /** The dialog used for text search */
1207   private SearchDialog searchDialog;
1208 
1209   protected Action searchAction;
1210   /**
1211    * Cahced value for the selected annotations.
1212    */
1213   private List<AnnotationData> selectedAnnotations = new ArrayList<AnnotationData>();
1214   
1215   protected JToolBar topBar;
1216 //  protected JToolBar rightBar;
1217 //  protected JToolBar leftBar;
1218 //  protected JToolBar bottomBar;
1219 
1220   protected Document document;
1221 
1222 
1223   /**
1224    * A list of {@link DocumentView} objects of type {@link DocumentView#CENTRAL}
1225    */
1226   protected List<DocumentView> centralViews;
1227   
1228   /**
1229    * A list of {@link DocumentView} objects of type 
1230    {@link DocumentView#VERTICAL}
1231    */
1232   protected List<DocumentView> verticalViews;
1233 
1234   /**
1235    * A list of {@link DocumentView} objects of type 
1236    {@link DocumentView#HORIZONTAL}
1237    */
1238   protected List<DocumentView> horizontalViews;
1239 
1240   /**
1241    * The index in {@link #centralViews} of the currently active central view.
1242    <code>-1</code> if none is active.
1243    */
1244   protected int centralViewIdx = -1;
1245 
1246   /**
1247    * The index in {@link #verticalViews} of the currently active right view.
1248    <code>-1</code> if none is active.
1249    */
1250   protected int rightViewIdx = -1;
1251   
1252   /**
1253    * The index in {@link #horizontalViews} of the currently active top view.
1254    <code>-1</code> if none is active.
1255    */
1256   protected int topViewIdx = -1;
1257   
1258   /**
1259    * The index in {@link #horizontalViews} of the currently active bottom view.
1260    <code>-1</code> if none is active.
1261    */
1262   protected int bottomViewIdx = -1;
1263   
1264   protected boolean viewsInited = false;
1265 
1266   /**
1267    * Used to know the last F-key used when adding a new view.
1268    */
1269   protected int fKeyNumber = 2;
1270 }