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 != rightViewIdx) de.setRightView(rightViewIdx);
0086 if (de.bottomViewIdx != bottomViewIdx) de.setBottomView(bottomViewIdx);
0087 DocumentView dv = de.getRightView();
0088 if (dv instanceof AnnotationSetsView) {
0089 AnnotationSetsView av = (AnnotationSetsView) dv;
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(!viewsInited) initViews();
0147 }
0148 //lazily build the GUI only when needed
0149 public void componentShown(ComponentEvent e) {
0150 if(!viewsInited) initViews();
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 = (DocumentView) Factory.
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() > 0) setCentralView(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(0, 0, 0, 0));
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(0, 0, 0, 1)); // 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 +" <font color=#667799><small>F" + (fKeyNumber + 1)
0417 +" </small></font>"
0418 +"<br>Set last selected annotations "
0419 +" <font color=#667799><small>Shift+F" + (fKeyNumber + 1)
0420 +" </small></font></html>");
0421
0422 } else {
0423 viewButton.setToolTipText("<html>Toggle the view of "+ name
0424 +" <font color=#667799><small>F" + (fKeyNumber + 1)
0425 +" </small></font></html>");
0426 }
0427 fKeyNumber++;
0428 }
0429
0430 /**
0431 * Gets the currently showing top view
0432 * @return a {@link DocumentView} object.
0433 */
0434 protected DocumentView getTopView(){
0435 return (topViewIdx == -1) ? null : 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 == -1) setTopView(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 a {@link DocumentView} object.
0483 */
0484 protected DocumentView getCentralView(){
0485 return (centralViewIdx == -1) ? null : 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 == -1) setCentralView(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 a {@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 a {@link DocumentView} object.
0583 */
0584 protected DocumentView getRightView(){
0585 return (rightViewIdx == -1) ? null : 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 == -1) setRightView(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 ((ViewButton) btn).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 " <font color=#667799><small>Ctrl-F" +
0725 " </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(0, 2, 0, 2));
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 }
|