
gate.gui.docview.AnnotationEditor Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of gate-core Show documentation
Show all versions of gate-core Show documentation
GATE - general achitecture for text engineering - is
open source software capable of solving almost any text processing problem.
This artifact enables you to embed the core GATE Embedded with its essential dependencies.
You will able to use the GATE Embedded API and load and store GATE XML documents. This
artifact is the perfect dependency for CREOLE plugins or for applications that need to customize
the GATE dependencies due to confict with their own dependencies or for lower footprint.
The newest version!
/*
* Copyright (c) 1995-2012, The University of Sheffield. See the file
* COPYRIGHT.txt in the software or at http://gate.ac.uk/gate/COPYRIGHT.txt
*
* This file is part of GATE (see http://gate.ac.uk/), and is free software,
* licenced under the GNU Library General Public License, Version 2, June 1991
* (in the distribution as file licence.html, and also available at
* http://gate.ac.uk/gate/licence.html).
*
* AnnotationEditor.java
*
* Valentin Tablan, Apr 5, 2004
*
* $Id: AnnotationEditor.java 17901 2014-04-24 12:59:58Z markagreenwood $
*/
package gate.gui.docview;
import gate.Annotation;
import gate.AnnotationSet;
import gate.Gate;
import gate.LanguageResource;
import gate.Resource;
import gate.creole.AbstractVisualResource;
import gate.creole.AnnotationSchema;
import gate.creole.ResourceInstantiationException;
import gate.event.CreoleEvent;
import gate.event.CreoleListener;
import gate.gui.FeaturesSchemaEditor;
import gate.gui.MainFrame;
import gate.gui.annedit.AnnotationDataImpl;
import gate.gui.annedit.AnnotationEditorOwner;
import gate.gui.annedit.OwnedAnnotationEditor;
import gate.gui.annedit.SearchAndAnnotatePanel;
import gate.util.GateException;
import gate.util.GateRuntimeException;
import gate.util.InvalidOffsetException;
import java.awt.Color;
import java.awt.Component;
import java.awt.ComponentOrientation;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionAdapter;
import java.awt.event.MouseMotionListener;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ActionMap;
import javax.swing.BorderFactory;
import javax.swing.DefaultComboBoxModel;
import javax.swing.Icon;
import javax.swing.InputMap;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JToggleButton;
import javax.swing.JWindow;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.event.AncestorEvent;
import javax.swing.event.AncestorListener;
import javax.swing.table.TableCellRenderer;
import javax.swing.text.BadLocationException;
/**
* A generic annotation editor, which uses the known annotation schemas to help
* speed up the annotation process (e.g. by pre-populating sets of choices) but
* does not enforce the schemas, allowing the user full control.
*/
@SuppressWarnings("serial")
public class AnnotationEditor extends AbstractVisualResource implements
OwnedAnnotationEditor {
/*
* (non-Javadoc)
*
* @see gate.creole.AbstractVisualResource#init()
*/
@Override
public Resource init() throws ResourceInstantiationException {
super.init();
initData();
initGUI();
initListeners();
annotationEditorInstance = this;
return this;
}
protected void initData() {
schemasByType = new HashMap();
java.util.List schemas =
Gate.getCreoleRegister().getLrInstances("gate.creole.AnnotationSchema");
for(Iterator schIter = schemas.iterator(); schIter.hasNext();) {
AnnotationSchema aSchema = (AnnotationSchema)schIter.next();
schemasByType.put(aSchema.getAnnotationName(), aSchema);
}
CreoleListener creoleListener = new CreoleListener() {
@Override
public void resourceLoaded(CreoleEvent e) {
Resource newResource = e.getResource();
if(newResource instanceof AnnotationSchema) {
AnnotationSchema aSchema = (AnnotationSchema)newResource;
schemasByType.put(aSchema.getAnnotationName(), aSchema);
}
}
@Override
public void resourceUnloaded(CreoleEvent e) {
Resource newResource = e.getResource();
if(newResource instanceof AnnotationSchema) {
AnnotationSchema aSchema = (AnnotationSchema)newResource;
if(schemasByType.containsValue(aSchema)) {
schemasByType.remove(aSchema.getAnnotationName());
}
}
}
@Override
public void datastoreOpened(CreoleEvent e) {
}
@Override
public void datastoreCreated(CreoleEvent e) {
}
@Override
public void datastoreClosed(CreoleEvent e) {
}
@Override
public void resourceRenamed(Resource resource, String oldName,
String newName) {
}
};
Gate.getCreoleRegister().addCreoleListener(creoleListener);
}
protected void initGUI() {
popupWindow =
new JWindow(SwingUtilities.getWindowAncestor(owner.getTextComponent())) {
@Override
public void pack() {
// increase the feature table size only if not bigger
// than the main frame
if(isVisible()) {
int maxHeight = MainFrame.getInstance().getHeight();
int otherHeight = getHeight() - featuresScroller.getHeight();
maxHeight -= otherHeight;
if(featuresScroller.getPreferredSize().height > maxHeight) {
featuresScroller.setMaximumSize(new Dimension(featuresScroller
.getMaximumSize().width, maxHeight));
featuresScroller.setPreferredSize(new Dimension(
featuresScroller.getPreferredSize().width, maxHeight));
}
}
super.pack();
}
@Override
public void setVisible(boolean b) {
super.setVisible(b);
// when the editor is shown put the focus in the type combo box
if(b) {
typeCombo.requestFocus();
}
}
};
JPanel pane = new JPanel();
pane.setBorder(BorderFactory.createLineBorder(Color.BLACK, 1));
pane.setLayout(new GridBagLayout());
pane.setBackground(UIManager.getLookAndFeelDefaults().getColor(
"ToolTip.background"));
popupWindow.setContentPane(pane);
Insets insets0 = new Insets(0, 0, 0, 0);
GridBagConstraints constraints = new GridBagConstraints();
constraints.fill = GridBagConstraints.NONE;
constraints.anchor = GridBagConstraints.CENTER;
constraints.gridwidth = 1;
constraints.gridy = 0;
constraints.gridx = GridBagConstraints.RELATIVE;
constraints.weightx = 0;
constraints.weighty = 0;
constraints.insets = insets0;
solButton = new JButton();
solButton.setContentAreaFilled(false);
solButton.setBorderPainted(false);
solButton.setMargin(insets0);
pane.add(solButton, constraints);
sorButton = new JButton();
sorButton.setContentAreaFilled(false);
sorButton.setBorderPainted(false);
sorButton.setMargin(insets0);
pane.add(sorButton, constraints);
delButton = new JButton();
delButton.setContentAreaFilled(false);
delButton.setBorderPainted(false);
delButton.setMargin(insets0);
constraints.insets = new Insets(0, 20, 0, 20);
pane.add(delButton, constraints);
constraints.insets = insets0;
eolButton = new JButton();
eolButton.setContentAreaFilled(false);
eolButton.setBorderPainted(false);
eolButton.setMargin(insets0);
pane.add(eolButton, constraints);
eorButton = new JButton();
eorButton.setContentAreaFilled(false);
eorButton.setBorderPainted(false);
eorButton.setMargin(insets0);
pane.add(eorButton, constraints);
pinnedButton = new JToggleButton(MainFrame.getIcon("pin"));
pinnedButton.setSelectedIcon(MainFrame.getIcon("pin-in"));
pinnedButton.setSelected(false);
pinnedButton.setBorderPainted(false);
pinnedButton.setContentAreaFilled(false);
constraints.weightx = 1;
constraints.insets = new Insets(0, 0, 0, 0);
constraints.anchor = GridBagConstraints.EAST;
pane.add(pinnedButton, constraints);
dismissButton = new JButton();
dismissButton.setBorder(null);
constraints.anchor = GridBagConstraints.NORTHEAST;
pane.add(dismissButton, constraints);
constraints.anchor = GridBagConstraints.CENTER;
constraints.insets = insets0;
typeCombo = new JComboBox();
typeCombo.setEditable(true);
typeCombo.setBackground(UIManager.getLookAndFeelDefaults().getColor(
"ToolTip.background"));
constraints.fill = GridBagConstraints.HORIZONTAL;
constraints.gridy = 1;
constraints.gridwidth = 7;
constraints.weightx = 1;
constraints.insets = new Insets(3, 2, 2, 2);
pane.add(typeCombo, constraints);
featuresEditor = new FeaturesSchemaEditor();
featuresEditor.setBackground(UIManager.getLookAndFeelDefaults().getColor(
"ToolTip.background"));
try {
featuresEditor.init();
} catch(ResourceInstantiationException rie) {
throw new GateRuntimeException(rie);
}
constraints.gridy = 2;
constraints.weighty = 1;
constraints.fill = GridBagConstraints.BOTH;
featuresScroller = new JScrollPane(featuresEditor);
pane.add(featuresScroller, constraints);
// add the search and annotate GUI at the bottom of the annotator editor
SearchAndAnnotatePanel searchPanel =
new SearchAndAnnotatePanel(pane.getBackground(), this, popupWindow);
constraints.insets = new Insets(0, 0, 0, 0);
constraints.fill = GridBagConstraints.BOTH;
constraints.anchor = GridBagConstraints.WEST;
constraints.gridx = 0;
constraints.gridy = GridBagConstraints.RELATIVE;
constraints.gridwidth = GridBagConstraints.REMAINDER;
constraints.gridheight = GridBagConstraints.REMAINDER;
constraints.weightx = 0.0;
constraints.weighty = 0.0;
pane.add(searchPanel, constraints);
popupWindow.pack();
}
protected void initListeners() {
// resize the window when the table changes.
featuresEditor.addComponentListener(new ComponentAdapter() {
@Override
public void componentResized(ComponentEvent e) {
// the table has changed size -> resize the window too!
popupWindow.pack();
}
});
KeyAdapter keyAdapter = new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
hideTimer.stop();
}
};
typeCombo.getEditor().getEditorComponent().addKeyListener(keyAdapter);
MouseListener windowMouseListener = new MouseAdapter() {
@Override
public void mouseEntered(MouseEvent evt) {
hideTimer.stop();
}
// allow a JWindow to be dragged with a mouse
@Override
public void mousePressed(MouseEvent me) {
pressed = me;
}
};
MouseMotionListener windowMouseMotionListener = new MouseMotionAdapter() {
Point location;
// allow a JWindow to be dragged with a mouse
@Override
public void mouseDragged(MouseEvent me) {
location = popupWindow.getLocation(location);
int x = location.x - pressed.getX() + me.getX();
int y = location.y - pressed.getY() + me.getY();
popupWindow.setLocation(x, y);
pinnedButton.setSelected(true);
}
};
popupWindow.getRootPane().addMouseListener(windowMouseListener);
popupWindow.getRootPane().addMouseMotionListener(windowMouseMotionListener);
InputMap inputMap =
((JComponent)popupWindow.getContentPane())
.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
actionMap = ((JComponent)popupWindow.getContentPane()).getActionMap();
// add the key-action bindings of this Component to the parent window
solAction =
new StartOffsetLeftAction("", MainFrame.getIcon("extend-left"),
SOL_DESC, KeyEvent.VK_LEFT);
solButton.setAction(solAction);
setShortCuts(inputMap, SOL_KEY_STROKES, "solAction");
actionMap.put("solAction", solAction);
sorAction =
new StartOffsetRightAction("", MainFrame.getIcon("extend-right"),
SOR_DESC, KeyEvent.VK_RIGHT);
sorButton.setAction(sorAction);
setShortCuts(inputMap, SOR_KEY_STROKES, "sorAction");
actionMap.put("sorAction", sorAction);
delAction =
new DeleteAnnotationAction("", MainFrame.getIcon("remove-annotation"),
"Delete the annotation", KeyEvent.VK_DELETE);
delButton.setAction(delAction);
inputMap.put(KeyStroke.getKeyStroke("alt DELETE"), "delAction");
actionMap.put("delAction", delAction);
eolAction =
new EndOffsetLeftAction("", MainFrame.getIcon("extend-left"), EOL_DESC,
KeyEvent.VK_LEFT);
eolButton.setAction(eolAction);
setShortCuts(inputMap, EOL_KEY_STROKES, "eolAction");
actionMap.put("eolAction", eolAction);
eorAction =
new EndOffsetRightAction("", MainFrame.getIcon("extend-right"),
EOR_DESC, KeyEvent.VK_RIGHT);
eorButton.setAction(eorAction);
setShortCuts(inputMap, EOR_KEY_STROKES, "eorAction");
actionMap.put("eorAction", eorAction);
pinnedButton.setToolTipText("Press to pin window in place"
+ " Ctrl-P"
+ " ");
inputMap.put(KeyStroke.getKeyStroke("control P"), "toggle pin");
actionMap.put("toggle pin", new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
pinnedButton.doClick();
}
});
DismissAction dismissAction =
new DismissAction("", null, "Close the window", KeyEvent.VK_ESCAPE);
dismissButton.setAction(dismissAction);
inputMap.put(KeyStroke.getKeyStroke("ESCAPE"), "dismissAction");
inputMap.put(KeyStroke.getKeyStroke("alt ESCAPE"), "dismissAction");
actionMap.put("dismissAction", dismissAction);
ApplyAction applyAction =
new ApplyAction("Apply", null, "", KeyEvent.VK_ENTER);
inputMap.put(KeyStroke.getKeyStroke("alt ENTER"), "applyAction");
actionMap.put("applyAction", applyAction);
typeCombo.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent evt) {
String newType = typeCombo.getSelectedItem().toString();
if(ann == null || ann.getType().equals(newType)) return;
// annotation editing
Integer oldId = ann.getId();
Annotation oldAnn = ann;
set.remove(ann);
try {
set.add(oldId, oldAnn.getStartNode().getOffset(), oldAnn.getEndNode()
.getOffset(), newType, oldAnn.getFeatures());
Annotation newAnn = set.get(oldId);
// update the selection to the new annotation
getOwner().selectAnnotation(new AnnotationDataImpl(set, newAnn));
editAnnotation(newAnn, set);
owner.annotationChanged(newAnn, set, oldAnn.getType());
} catch(InvalidOffsetException ioe) {
throw new GateRuntimeException(ioe);
}
}
});
hideTimer = new Timer(HIDE_DELAY, new ActionListener() {
@Override
public void actionPerformed(ActionEvent evt) {
annotationEditorInstance.setVisible(false);
}
});
hideTimer.setRepeats(false);
AncestorListener textAncestorListener = new AncestorListener() {
@Override
public void ancestorAdded(AncestorEvent event) {
if(wasShowing) {
annotationEditorInstance.setVisible(true);
}
wasShowing = false;
}
@Override
public void ancestorRemoved(AncestorEvent event) {
if(isShowing()) {
wasShowing = true;
popupWindow.dispose();
}
}
@Override
public void ancestorMoved(AncestorEvent event) {
}
private boolean wasShowing = false;
};
owner.getTextComponent().addAncestorListener(textAncestorListener);
}
/*
* (non-Javadoc)
*
* @see gate.gui.annedit.AnnotationEditor#isActive()
*/
@Override
public boolean isActive() {
return popupWindow.isVisible();
}
@Override
public void editAnnotation(Annotation ann, AnnotationSet set) {
this.ann = ann;
this.set = set;
if(ann == null) {
typeCombo.setModel(new DefaultComboBoxModel());
featuresEditor.setSchema(new AnnotationSchema());
// popupWindow.doLayout();
popupWindow.validate();
return;
}
// repopulate the types combo
String annType = ann.getType();
Set types = new HashSet(schemasByType.keySet());
types.add(annType);
types.addAll(set.getAllTypes());
java.util.List typeList = new ArrayList(types);
Collections.sort(typeList);
typeCombo.setModel(new DefaultComboBoxModel(typeList.toArray(new String[typeList.size()])));
typeCombo.setSelectedItem(annType);
featuresEditor.setSchema(schemasByType.get(annType));
featuresEditor.setTargetFeatures(ann.getFeatures());
setEditingEnabled(true);
popupWindow.pack();
setVisible(true);
if(!pinnedButton.isSelected()) {
hideTimer.restart();
}
}
@Override
public Annotation getAnnotationCurrentlyEdited() {
return ann;
}
/*
* (non-Javadoc)
*
* @see gate.gui.annedit.AnnotationEditor#editingFinished()
*/
@Override
public boolean editingFinished() {
// this editor implementation has no special requirements (such as schema
// compliance), so it always returns true.
return true;
}
@Override
public boolean isShowing() {
return popupWindow.isShowing();
}
/**
* Shows/Hides the UI(s) involved in annotation editing.
*/
@Override
public void setVisible(boolean setVisible) {
super.setVisible(setVisible);
if(setVisible) {
placeDialog(ann.getStartNode().getOffset().intValue(), ann.getEndNode()
.getOffset().intValue());
} else {
popupWindow.setVisible(false);
pinnedButton.setSelected(false);
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
// when hiding the editor put back the focus in the document
owner.getTextComponent().requestFocus();
}
});
}
}
/**
* Finds the best location for the editor dialog for a given span of text.
*/
@Override
public void placeDialog(int start, int end) {
if(popupWindow.isVisible() && pinnedButton.isSelected()) {
// just resize
Point where = popupWindow.getLocation();
popupWindow.pack();
if(where != null) {
popupWindow.setLocation(where);
}
} else {
// calculate position
try {
Rectangle startRect = owner.getTextComponent().modelToView(start);
Rectangle endRect = owner.getTextComponent().modelToView(end);
Point topLeft = owner.getTextComponent().getLocationOnScreen();
int x = topLeft.x + startRect.x;
int y = topLeft.y + endRect.y + endRect.height;
// make sure the window doesn't start lower
// than the end of the visible rectangle
Rectangle visRect = owner.getTextComponent().getVisibleRect();
int maxY = topLeft.y + visRect.y + visRect.height;
// make sure window doesn't get off-screen
popupWindow.pack();
// responding to changed orientation
if(currentOrientation == ComponentOrientation.RIGHT_TO_LEFT) {
x = x - popupWindow.getSize().width;
if(x < 0) x = 0;
}
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
boolean revalidate = false;
if(popupWindow.getSize().width > screenSize.width) {
popupWindow.setSize(screenSize.width, popupWindow.getSize().height);
revalidate = true;
}
if(popupWindow.getSize().height > screenSize.height) {
popupWindow.setSize(popupWindow.getSize().width, screenSize.height);
revalidate = true;
}
if(revalidate) popupWindow.validate();
// calculate max X
int maxX = screenSize.width - popupWindow.getSize().width;
// calculate max Y
if(maxY + popupWindow.getSize().height > screenSize.height) {
maxY = screenSize.height - popupWindow.getSize().height;
}
// correct position
if(y > maxY) y = maxY;
if(x > maxX) x = maxX;
popupWindow.setLocation(x, y);
} catch(BadLocationException ble) {
// this should never occur
throw new GateRuntimeException(ble);
}
}
if(!popupWindow.isVisible()) popupWindow.setVisible(true);
}
/**
* Changes the span of an existing annotation by creating a new annotation
* with the same ID, type and features but with the new start and end offsets.
*
* @param set
* the annotation set
* @param oldAnnotation
* the annotation to be moved
* @param newStartOffset
* the new start offset
* @param newEndOffset
* the new end offset
*/
protected void moveAnnotation(AnnotationSet set, Annotation oldAnnotation,
Long newStartOffset, Long newEndOffset) throws InvalidOffsetException {
// Moving is done by deleting the old annotation and creating a new one.
// If this was the last one of one type it would mess up the gui which
// "forgets" about this type and then it recreates it (with a different
// colour and not visible.
// In order to avoid this problem, we'll create a new temporary annotation.
Annotation tempAnn = null;
if(set.get(oldAnnotation.getType()).size() == 1) {
// create a clone of the annotation that will be deleted, to act as a
// placeholder
Integer tempAnnId =
set.add(oldAnnotation.getStartNode(), oldAnnotation.getStartNode(),
oldAnnotation.getType(), oldAnnotation.getFeatures());
tempAnn = set.get(tempAnnId);
}
Integer oldID = oldAnnotation.getId();
set.remove(oldAnnotation);
set.add(oldID, newStartOffset, newEndOffset, oldAnnotation.getType(),
oldAnnotation.getFeatures());
Annotation newAnn = set.get(oldID);
// update the selection to the new annotation
getOwner().selectAnnotation(new AnnotationDataImpl(set, newAnn));
editAnnotation(newAnn, set);
// remove the temporary annotation
if(tempAnn != null) set.remove(tempAnn);
owner.annotationChanged(newAnn, set, null);
}
/**
* Base class for actions on annotations.
*/
protected abstract class AnnotationAction extends AbstractAction {
public AnnotationAction(String text, Icon icon, String desc, int mnemonic) {
super(text, icon);
putValue(SHORT_DESCRIPTION, desc);
putValue(MNEMONIC_KEY, mnemonic);
}
}
protected class StartOffsetLeftAction extends AnnotationAction {
private static final long serialVersionUID = 1L;
public StartOffsetLeftAction(String text, Icon icon, String desc,
int mnemonic) {
super(text, icon, desc, mnemonic);
}
@Override
public void actionPerformed(ActionEvent evt) {
int increment = 1;
if((evt.getModifiers() & ActionEvent.SHIFT_MASK) != 0) {
// CTRL pressed -> use tokens for advancing
increment = SHIFT_INCREMENT;
if((evt.getModifiers() & ActionEvent.CTRL_MASK) != 0) {
increment = CTRL_SHIFT_INCREMENT;
}
}
long newValue = ann.getStartNode().getOffset().longValue() - increment;
if(newValue < 0) newValue = 0;
try {
moveAnnotation(set, ann, new Long(newValue), ann.getEndNode()
.getOffset());
} catch(InvalidOffsetException ioe) {
throw new GateRuntimeException(ioe);
}
}
}
protected class StartOffsetRightAction extends AnnotationAction {
private static final long serialVersionUID = 1L;
public StartOffsetRightAction(String text, Icon icon, String desc,
int mnemonic) {
super(text, icon, desc, mnemonic);
}
@Override
public void actionPerformed(ActionEvent evt) {
long endOffset = ann.getEndNode().getOffset().longValue();
int increment = 1;
if((evt.getModifiers() & ActionEvent.SHIFT_MASK) != 0) {
// CTRL pressed -> use tokens for advancing
increment = SHIFT_INCREMENT;
if((evt.getModifiers() & ActionEvent.CTRL_MASK) != 0) {
increment = CTRL_SHIFT_INCREMENT;
}
}
long newValue = ann.getStartNode().getOffset().longValue() + increment;
if(newValue > endOffset) newValue = endOffset;
try {
moveAnnotation(set, ann, new Long(newValue), ann.getEndNode()
.getOffset());
} catch(InvalidOffsetException ioe) {
throw new GateRuntimeException(ioe);
}
}
}
protected class EndOffsetLeftAction extends AnnotationAction {
private static final long serialVersionUID = 1L;
public EndOffsetLeftAction(String text, Icon icon, String desc, int mnemonic) {
super(text, icon, desc, mnemonic);
}
@Override
public void actionPerformed(ActionEvent evt) {
long startOffset = ann.getStartNode().getOffset().longValue();
int increment = 1;
if((evt.getModifiers() & ActionEvent.SHIFT_MASK) != 0) {
// CTRL pressed -> use tokens for advancing
increment = SHIFT_INCREMENT;
if((evt.getModifiers() & ActionEvent.CTRL_MASK) != 0) {
increment = CTRL_SHIFT_INCREMENT;
}
}
long newValue = ann.getEndNode().getOffset().longValue() - increment;
if(newValue < startOffset) newValue = startOffset;
try {
moveAnnotation(set, ann, ann.getStartNode().getOffset(), new Long(
newValue));
} catch(InvalidOffsetException ioe) {
throw new GateRuntimeException(ioe);
}
}
}
protected class EndOffsetRightAction extends AnnotationAction {
private static final long serialVersionUID = 1L;
public EndOffsetRightAction(String text, Icon icon, String desc,
int mnemonic) {
super(text, icon, desc, mnemonic);
}
@Override
public void actionPerformed(ActionEvent evt) {
long maxOffset = owner.getDocument().getContent().size().longValue();
int increment = 1;
if((evt.getModifiers() & ActionEvent.SHIFT_MASK) != 0) {
// CTRL pressed -> use tokens for advancing
increment = SHIFT_INCREMENT;
if((evt.getModifiers() & ActionEvent.CTRL_MASK) != 0) {
increment = CTRL_SHIFT_INCREMENT;
}
}
long newValue = ann.getEndNode().getOffset().longValue() + increment;
if(newValue > maxOffset) newValue = maxOffset;
try {
moveAnnotation(set, ann, ann.getStartNode().getOffset(), new Long(
newValue));
} catch(InvalidOffsetException ioe) {
throw new GateRuntimeException(ioe);
}
}
}
protected class DeleteAnnotationAction extends AnnotationAction {
private static final long serialVersionUID = 1L;
public DeleteAnnotationAction(String text, Icon icon, String desc,
int mnemonic) {
super(text, icon, desc, mnemonic);
}
@Override
public void actionPerformed(ActionEvent evt) {
set.remove(ann);
// clear the dialog
editAnnotation(null, set);
if(!pinnedButton.isSelected()) {
// if not pinned, hide the dialog.
annotationEditorInstance.setVisible(false);
} else {
setEditingEnabled(false);
}
}
}
protected class DismissAction extends AnnotationAction {
private static final long serialVersionUID = 1L;
public DismissAction(String text, Icon icon, String desc, int mnemonic) {
super(text, icon, desc, mnemonic);
Icon exitIcon = UIManager.getIcon("InternalFrame.closeIcon");
if(exitIcon == null) exitIcon = MainFrame.getIcon("exit");
putValue(SMALL_ICON, exitIcon);
}
@Override
public void actionPerformed(ActionEvent evt) {
annotationEditorInstance.setVisible(false);
}
}
protected class ApplyAction extends AnnotationAction {
private static final long serialVersionUID = 1L;
public ApplyAction(String text, Icon icon, String desc, int mnemonic) {
super(text, icon, desc, mnemonic);
}
@Override
public void actionPerformed(ActionEvent evt) {
annotationEditorInstance.setVisible(false);
}
}
/**
* The popup window used by the editor.
*/
protected JWindow popupWindow;
/**
* Toggle button used to pin down the dialog.
*/
protected JToggleButton pinnedButton;
/**
* Combobox for annotation type.
*/
protected JComboBox typeCombo;
/**
* Component for features editing.
*/
protected FeaturesSchemaEditor featuresEditor;
protected JScrollPane featuresScroller;
protected JButton solButton;
protected JButton sorButton;
protected JButton delButton;
protected JButton eolButton;
protected JButton eorButton;
protected JButton dismissButton;
protected Timer hideTimer;
protected MouseEvent pressed;
/**
* Constant for delay before hiding the popup window (in milliseconds).
*/
protected static final int HIDE_DELAY = 3000;
/**
* Constant for the number of characters when changing annotation boundary
* with Shift key pressed.
*/
protected static final int SHIFT_INCREMENT = 5;
/**
* Constant for the number of characters when changing annotation boundary
* with Ctrl+Shift keys pressed.
*/
protected static final int CTRL_SHIFT_INCREMENT = 10;
/**
* Stores the Annotation schema objects available in the system. The
* annotation types are used as keys for the map.
*/
protected Map schemasByType;
/**
* The controlling object for this editor.
*/
private AnnotationEditorOwner owner;
/**
* The annotation being edited.
*/
protected Annotation ann;
/**
* The parent set of the current annotation.
*/
protected AnnotationSet set;
/**
* Current instance of this class.
*/
protected AnnotationEditor annotationEditorInstance;
/**
* Action bindings for the popup window.
*/
private ActionMap actionMap;
private StartOffsetLeftAction solAction;
private StartOffsetRightAction sorAction;
private DeleteAnnotationAction delAction;
private EndOffsetLeftAction eolAction;
private EndOffsetRightAction eorAction;
/*
* (non-Javadoc)
*
* @see gate.gui.annedit.AnnotationEditor#getAnnotationSetCurrentlyEdited()
*/
@Override
public AnnotationSet getAnnotationSetCurrentlyEdited() {
return set;
}
/**
* @return the owner
*/
@Override
public AnnotationEditorOwner getOwner() {
return owner;
}
/**
* @param owner
* the owner to set
*/
@Override
public void setOwner(AnnotationEditorOwner owner) {
this.owner = owner;
}
@Override
public void setPinnedMode(boolean pinned) {
pinnedButton.setSelected(pinned);
}
@Override
public void setEditingEnabled(boolean isEditingEnabled) {
solButton.setEnabled(isEditingEnabled);
sorButton.setEnabled(isEditingEnabled);
delButton.setEnabled(isEditingEnabled);
eolButton.setEnabled(isEditingEnabled);
eorButton.setEnabled(isEditingEnabled);
typeCombo.setEnabled(isEditingEnabled);
// cancel editing, if any
if(featuresEditor.isEditing()) {
featuresEditor.getColumnModel()
.getColumn(featuresEditor.getEditingColumn()).getCellEditor()
.cancelCellEditing();
}
// en/disable the featuresEditor table, no easy way unfortunately : |
featuresEditor.setEnabled(isEditingEnabled);
if(isEditingEnabled) {
// avoid the background to be incorrectly reset to the default color
Color tableBG = featuresEditor.getBackground();
tableBG = new Color(tableBG.getRGB());
featuresEditor.setBackground(tableBG);
}
final boolean isEditingEnabledF = isEditingEnabled;
for(int col = 0; col < featuresEditor.getColumnCount(); col++) {
final TableCellRenderer previousTcr =
featuresEditor.getColumnModel().getColumn(col).getCellRenderer();
TableCellRenderer tcr = new TableCellRenderer() {
@Override
public Component getTableCellRendererComponent(JTable table,
Object value, boolean isSelected, boolean hasFocus, int row,
int column) {
Component c =
previousTcr.getTableCellRendererComponent(table, value,
isSelected, hasFocus, row, column);
c.setEnabled(isEditingEnabledF);
return c;
}
};
featuresEditor.getColumnModel().getColumn(col).setCellRenderer(tcr);
}
// enable/disable the key binding actions
if(isEditingEnabled) {
actionMap.put("solAction", solAction);
actionMap.put("sorAction", sorAction);
actionMap.put("delAction", delAction);
actionMap.put("eolAction", eolAction);
actionMap.put("eorAction", eorAction);
} else {
actionMap.put("solAction", null);
actionMap.put("sorAction", null);
actionMap.put("delAction", null);
actionMap.put("eolAction", null);
actionMap.put("eorAction", null);
}
// change the orientation
changeOrientation(currentOrientation);
}
/**
* Does nothing, as this editor does not support cancelling and rollbacks.
*/
@Override
public void cancelAction() throws GateException {
}
/**
* Returns true always as this editor is generic and can edit any
* annotation type.
*/
@Override
public boolean canDisplayAnnotationType(String annotationType) {
return true;
}
/**
* Does nothing as this editor works in auto-commit mode (changes are
* implemented immediately).
*/
@Override
public void okAction() throws GateException {
}
/**
* Returns false, as this editor does not support cancel operations.
*/
@Override
public boolean supportsCancel() {
return false;
}
@Override
public void changeOrientation(ComponentOrientation orientation) {
if(orientation == null) return;
// remember the current orientation
this.currentOrientation = orientation;
// input map
InputMap inputMap =
((JComponent)popupWindow.getContentPane())
.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
Action solAction = actionMap.get("solAction");
Action sorAction = actionMap.get("sorAction");
Action eolAction = actionMap.get("eolAction");
Action eorAction = actionMap.get("eorAction");
if(orientation == ComponentOrientation.RIGHT_TO_LEFT) {
// in right to left orientation
// extending start offset is equal to extending end offset
solButton.setAction(eorAction);
solButton.setToolTipText(EOR_DESC);
setShortCuts(inputMap, SOL_KEY_STROKES, "eorAction");
solButton.setIcon(MainFrame.getIcon("extend-left"));
// shrinking start offset is equal to shrinking end offset
sorButton.setAction(eolAction);
sorButton.setToolTipText(EOL_DESC);
setShortCuts(inputMap, SOR_KEY_STROKES, "eolAction");
sorButton.setIcon(MainFrame.getIcon("extend-right"));
// shrinking end offset is equal to shrinking start offset
eolButton.setAction(sorAction);
eolButton.setToolTipText(SOR_DESC);
setShortCuts(inputMap, EOL_KEY_STROKES, "sorAction");
eolButton.setIcon(MainFrame.getIcon("extend-left"));
// extending end offset is extending start offset
eorButton.setAction(solAction);
eorButton.setToolTipText(SOL_DESC);
setShortCuts(inputMap, EOR_KEY_STROKES, "solAction");
eorButton.setIcon(MainFrame.getIcon("extend-right"));
} else {
solButton.setAction(solAction);
solButton.setToolTipText(SOL_DESC);
setShortCuts(inputMap, SOL_KEY_STROKES, "solAction");
solButton.setIcon(MainFrame.getIcon("extend-left"));
sorButton.setAction(sorAction);
sorButton.setToolTipText(SOR_DESC);
setShortCuts(inputMap, SOR_KEY_STROKES, "sorAction");
sorButton.setIcon(MainFrame.getIcon("extend-right"));
eolButton.setAction(eolAction);
eolButton.setToolTipText(EOL_DESC);
setShortCuts(inputMap, EOL_KEY_STROKES, "eolAction");
eolButton.setIcon(MainFrame.getIcon("extend-left"));
eorButton.setAction(eorAction);
eorButton.setToolTipText(EOR_DESC);
setShortCuts(inputMap, EOR_KEY_STROKES, "eorAction");
eorButton.setIcon(MainFrame.getIcon("extend-right"));
}
}
private void setShortCuts(InputMap inputMap, String[] keyStrokes,
String action) {
for(String aKeyStroke : keyStrokes) {
inputMap.put(KeyStroke.getKeyStroke(aKeyStroke), action);
}
}
/**
* current orientation set by the user
*/
private ComponentOrientation currentOrientation = null;
/* various tool tips for the changing offsets buttons */
private final String SOL_DESC = "Extend start"
+ "
LEFT = 1 character" + "
+ SHIFT = 5 characters, "
+ "
+ CTRL + SHIFT = 10 characters";
private final String SOR_DESC = "Shrink start"
+ "
RIGHT = 1 character" + "
+ SHIFT = 5 characters, "
+ "
+ CTRL + SHIFT = 10 characters";
private final String EOL_DESC = "Shrink end"
+ "
ALT + LEFT = 1 character" + "
+ SHIFT = 5 characters, "
+ "
+ CTRL + SHIFT = 10 characters";
private final String EOR_DESC = "Extend end"
+ "
ALT + RIGHT = 1 character" + "
+ SHIFT = 5 characters, "
+ "
+ CTRL + SHIFT = 10 characters";
/* various short cuts we define */
private final String[] SOL_KEY_STROKES = new String[]{"LEFT", "shift LEFT",
"control shift released LEFT"};
private final String[] SOR_KEY_STROKES = new String[]{"RIGHT", "shift RIGHT",
"control shift released RIGHT"};
private final String[] EOL_KEY_STROKES = new String[]{"LEFT", "alt LEFT",
"control alt released LEFT"};
private final String[] EOR_KEY_STROKES = new String[]{"RIGHT", "alt RIGHT",
"control alt released RIGHT"};
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy