1
15
16 package gate.gui.docview;
17
18 import java.awt.*;
19 import java.awt.event.*;
20 import java.util.*;
21
22 import javax.swing.*;
23 import javax.swing.Timer;
24 import javax.swing.text.BadLocationException;
25
26 import gate.*;
27 import gate.creole.AnnotationSchema;
28 import gate.creole.ResourceInstantiationException;
29 import gate.event.CreoleEvent;
30 import gate.event.CreoleListener;
31 import gate.gui.FeaturesSchemaEditor;
32 import gate.gui.MainFrame;
33 import gate.util.*;
34 import gate.util.GateException;
35 import gate.util.GateRuntimeException;
36
37
38
42 public class AnnotationEditor{
43
46 public AnnotationEditor(TextualDocumentView textView,
47 AnnotationSetsView setsView){
48 this.textView = textView;
49 textPane = (JEditorPane)((JScrollPane)textView.getGUI())
50 .getViewport().getView();
51 this.setsView = setsView;
52 initGUI();
53 }
54
55 protected void initData(){
56 schemasByType = new HashMap();
57 try{
58 java.util.List schemas = Gate.getCreoleRegister().
59 getAllInstances("gate.creole.AnnotationSchema");
60 for(Iterator schIter = schemas.iterator();
61 schIter.hasNext();){
62 AnnotationSchema aSchema = (AnnotationSchema)schIter.next();
63 schemasByType.put(aSchema.getAnnotationName(), aSchema);
64 }
65 }catch(GateException ge){
66 throw new GateRuntimeException(ge);
67 }
68
69 CreoleListener creoleListener = new CreoleListener(){
70 public void resourceLoaded(CreoleEvent e){
71 Resource newResource = e.getResource();
72 if(newResource instanceof AnnotationSchema){
73 AnnotationSchema aSchema = (AnnotationSchema)newResource;
74 schemasByType.put(aSchema.getAnnotationName(), aSchema);
75 }
76 }
77
78 public void resourceUnloaded(CreoleEvent e){
79 Resource newResource = e.getResource();
80 if(newResource instanceof AnnotationSchema){
81 AnnotationSchema aSchema = (AnnotationSchema)newResource;
82 if(schemasByType.containsValue(aSchema)){
83 schemasByType.remove(aSchema.getAnnotationName());
84 }
85 }
86 }
87
88 public void datastoreOpened(CreoleEvent e){
89
90 }
91 public void datastoreCreated(CreoleEvent e){
92
93 }
94 public void datastoreClosed(CreoleEvent e){
95
96 }
97 public void resourceRenamed(Resource resource,
98 String oldName,
99 String newName){
100 }
101 };
102 Gate.getCreoleRegister().addCreoleListener(creoleListener);
103 }
104
105 protected void initBottomWindow(Window parent){
106 bottomWindow = new JWindow(parent);
107 JPanel pane = new JPanel();
108 pane.setBorder(BorderFactory.createLineBorder(Color.BLACK, 1));
109 pane.setLayout(new GridBagLayout());
110 pane.setBackground(UIManager.getLookAndFeelDefaults().
111 getColor("ToolTip.background"));
112 bottomWindow.setContentPane(pane);
113
114 Insets insets0 = new Insets(0, 0, 0, 0);
115 GridBagConstraints constraints = new GridBagConstraints();
116 constraints.fill = GridBagConstraints.NONE;
117 constraints.anchor = GridBagConstraints.CENTER;
118 constraints.gridwidth = 1;
119 constraints.gridy = 0;
120 constraints.gridx = GridBagConstraints.RELATIVE;
121 constraints.weightx = 0;
122 constraints.weighty= 0;
123 constraints.insets = insets0;
124
125 JButton btn = new JButton(solAction);
126 btn.setContentAreaFilled(false);
127 btn.setBorderPainted(false);
128 btn.setMargin(insets0);
129 pane.add(btn, constraints);
130
131 btn = new JButton(sorAction);
132 btn.setContentAreaFilled(false);
133 btn.setBorderPainted(false);
134 btn.setMargin(insets0);
135 pane.add(btn, constraints);
136
137 btn = new JButton(delAction);
138 btn.setContentAreaFilled(false);
139 btn.setBorderPainted(false);
140 btn.setMargin(insets0);
141 constraints.insets = new Insets(0, 20, 0, 20);
142 pane.add(btn, constraints);
143 constraints.insets = insets0;
144
145 btn = new JButton(eolAction);
146 btn.setContentAreaFilled(false);
147 btn.setBorderPainted(false);
148 btn.setMargin(insets0);
149 pane.add(btn, constraints);
150
151 btn = new JButton(eorAction);
152 btn.setContentAreaFilled(false);
153 btn.setBorderPainted(false);
154 btn.setMargin(insets0);
155 pane.add(btn, constraints);
156
157 dismissAction = new DismissAction();
158 btn = new JButton(dismissAction);
159 constraints.insets = new Insets(0, 10, 0, 0);
160 constraints.anchor = GridBagConstraints.NORTHEAST;
161 constraints.weightx = 1;
162 btn.setMargin(new Insets(3,3,3,3));
163 pane.add(btn, constraints);
164 constraints.anchor = GridBagConstraints.CENTER;
165 constraints.insets = insets0;
166
167
168 typeCombo = new JComboBox();
169 typeCombo.setEditable(true);
170 typeCombo.setBackground(UIManager.getLookAndFeelDefaults().
171 getColor("ToolTip.background"));
172 constraints.fill = GridBagConstraints.HORIZONTAL;
173 constraints.gridy = 1;
174 constraints.gridwidth = 6;
175 constraints.weightx = 1;
176 constraints.insets = new Insets(3, 2, 2, 2);
177 pane.add(typeCombo, constraints);
178
179 featuresEditor = new FeaturesSchemaEditor();
180 featuresEditor.setBackground(UIManager.getLookAndFeelDefaults().
181 getColor("ToolTip.background"));
182 try{
183 featuresEditor.init();
184 }catch(ResourceInstantiationException rie){
185 throw new GateRuntimeException(rie);
186 }
187 scroller = new JScrollPane(featuresEditor.getTable());
188
189 constraints.gridy = 2;
190 constraints.weighty = 1;
191 constraints.fill = GridBagConstraints.BOTH;
192 pane.add(scroller, constraints);
193 }
194
195
196 protected void initListeners(){
197 MouseListener windowMouseListener = new MouseAdapter(){
198 public void mouseEntered(MouseEvent evt){
199 hideTimer.stop();
200 }
201 };
202
203 bottomWindow.getRootPane().addMouseListener(windowMouseListener);
204
206 ((JComponent)bottomWindow.getContentPane()).
207 getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).
208 put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "dismiss");
209 ((JComponent)bottomWindow.getContentPane()).
210 getActionMap().put("dismiss", dismissAction);
211
212 typeCombo.addActionListener(new ActionListener(){
213 public void actionPerformed(ActionEvent evt){
214 String newType = typeCombo.getSelectedItem().toString();
215 if(ann != null && ann.getType().equals(newType)) return;
216 Integer oldId = ann.getId();
218 Annotation oldAnn = ann;
219 set.remove(ann);
220 try{
221 set.add(oldId, oldAnn.getStartNode().getOffset(),
222 oldAnn.getEndNode().getOffset(),
223 newType, oldAnn.getFeatures());
224 setAnnotation(set.get(oldId), set);
225
226 setsView.setTypeSelected(set.getName(), newType, true);
227 setsView.setLastAnnotationType(newType);
228 }catch(InvalidOffsetException ioe){
229 throw new GateRuntimeException(ioe);
230 }
231 }
232 });
233 }
234
235 protected void initGUI(){
236 solAction = new StartOffsetLeftAction();
237 sorAction = new StartOffsetRightAction();
238 eolAction = new EndOffsetLeftAction();
239 eorAction = new EndOffsetRightAction();
240 delAction = new DeleteAnnotationAction();
241
242 initData();
243 initBottomWindow(SwingUtilities.getWindowAncestor(textView.getGUI()));
244 initListeners();
245
246 hideTimer = new Timer(HIDE_DELAY, new ActionListener(){
247 public void actionPerformed(ActionEvent evt){
248 hide();
249 }
250 });
251 hideTimer.setRepeats(false);
252
253 }
254
255 public void setAnnotation(Annotation ann, AnnotationSet set){
256 this.ann = ann;
257 this.set = set;
258 String annType = ann.getType();
260 Set types = new HashSet(schemasByType.keySet());
261 types.add(annType);
262 types.addAll(set.getAllTypes());
263 java.util.List typeList = new ArrayList(types);
264 Collections.sort(typeList);
265 typeCombo.setModel(new DefaultComboBoxModel(typeList.toArray()));
266 typeCombo.setSelectedItem(annType);
267
268 featuresEditor.setSchema((AnnotationSchema)schemasByType.get(annType));
269 featuresEditor.setTargetFeatures(ann.getFeatures());
270 }
271
272 public boolean isShowing(){
273 return bottomWindow.isShowing();
274 }
275
276
280 public void show(boolean autohide){
281 placeWindows();
282 bottomWindow.setVisible(true);
283 if(autohide) hideTimer.restart();
284 }
285
286 protected void placeWindows(){
287 try{
289 Rectangle startRect = textPane.modelToView(ann.getStartNode().
290 getOffset().intValue());
291 Rectangle endRect = textPane.modelToView(ann.getEndNode().
292 getOffset().intValue());
293 Point topLeft = textPane.getLocationOnScreen();
294 int x = topLeft.x + startRect.x;
295 int y = topLeft.y + endRect.y + endRect.height;
296 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
298 bottomWindow.pack();
299
300 boolean widthReduced = false;
301 if(x + bottomWindow.getSize().width > screenSize.width){
302 int newWidth = screenSize.width - x;
303 bottomWindow.setSize(newWidth,
304 bottomWindow.getSize().height +
305 scroller.getHorizontalScrollBar().getPreferredSize().height);
306 widthReduced = true;
307 }
308 if(y + bottomWindow.getSize().height > screenSize.height){
309 int newHeight = screenSize.height - y;
310 bottomWindow.setSize(bottomWindow.getSize().width +
311 (widthReduced ? 0 :
312 scroller.getVerticalScrollBar().getPreferredSize().width),
313 newHeight);
314 }
315 bottomWindow.validate();
316 bottomWindow.setLocation(x, y);
317
318 }catch(BadLocationException ble){
319 throw new GateRuntimeException(ble);
321 }
322 }
323
324
332 protected void moveAnnotation(AnnotationSet set, Annotation oldAnnotation,
333 Long newStartOffset, Long newEndOffset) throws InvalidOffsetException{
334 AnnotationSetsView.TypeHandler oldHandler = setsView.getTypeHandler(
341 set.getName(), oldAnnotation.getType());
342
343 Integer oldID = oldAnnotation.getId();
344 set.remove(oldAnnotation);
345 set.add(oldID, newStartOffset, newEndOffset,
346 oldAnnotation.getType(), oldAnnotation.getFeatures());
347 setAnnotation(set.get(oldID), set);
348 AnnotationSetsView.TypeHandler newHandler = setsView.getTypeHandler(
349 set.getName(), oldAnnotation.getType());
350
351 if(newHandler != oldHandler){
352 newHandler.setSelected(false);
354 newHandler.colour = oldHandler.colour;
355 newHandler.setSelected(oldHandler.isSelected());
356 }
357 }
358
359 public void hide(){
360 bottomWindow.setVisible(false);
362 }
363
364
367 protected abstract class AnnotationAction extends AbstractAction{
368 public AnnotationAction(String name, Icon icon){
369 super("", icon);
370 putValue(SHORT_DESCRIPTION, name);
371
372 }
373 }
374
375 protected class StartOffsetLeftAction extends AnnotationAction{
376 public StartOffsetLeftAction(){
377 super("<html><b>Extend</b><br><small>SHIFT = 5 characters, CTRL-SHIFT = 10 characters</small></html>",
378 MainFrame.getIcon("extend-left.gif"));
379 }
380
381 public void actionPerformed(ActionEvent evt){
382 Annotation oldAnn = ann;
383 int increment = 1;
384 if((evt.getModifiers() & ActionEvent.SHIFT_MASK) > 0){
385 increment = SHIFT_INCREMENT;
387 if((evt.getModifiers() & ActionEvent.CTRL_MASK) > 0){
388 increment = CTRL_SHIFT_INCREMENT;
389 }
390 }
391 long newValue = ann.getStartNode().getOffset().longValue() - increment;
392 if(newValue < 0) newValue = 0;
393 try{
394 moveAnnotation(set, ann, new Long(newValue),
395 ann.getEndNode().getOffset());
396 }catch(InvalidOffsetException ioe){
397 throw new GateRuntimeException(ioe);
398 }
399 }
400 }
401
402 protected class StartOffsetRightAction extends AnnotationAction{
403 public StartOffsetRightAction(){
404 super("<html><b>Shrink</b><br><small>SHIFT = 5 characters, " +
405 "CTRL-SHIFT = 10 characters</small></html>",
406 MainFrame.getIcon("extend-right.gif"));
407 }
408
409 public void actionPerformed(ActionEvent evt){
410 long endOffset = ann.getEndNode().getOffset().longValue();
411 int increment = 1;
412 if((evt.getModifiers() & ActionEvent.SHIFT_MASK) > 0){
413 increment = SHIFT_INCREMENT;
415 if((evt.getModifiers() & ActionEvent.CTRL_MASK) > 0){
416 increment = CTRL_SHIFT_INCREMENT;
417 }
418 }
419
420 long newValue = ann.getStartNode().getOffset().longValue() + increment;
421 if(newValue > endOffset) newValue = endOffset;
422 try{
423 moveAnnotation(set, ann, new Long(newValue),
424 ann.getEndNode().getOffset());
425 }catch(InvalidOffsetException ioe){
426 throw new GateRuntimeException(ioe);
427 }
428 }
429 }
430
431 protected class EndOffsetLeftAction extends AnnotationAction{
432 public EndOffsetLeftAction(){
433 super("<html><b>Shrink</b><br><small>SHIFT = 5 characters, " +
434 "CTRL-SHIFT = 10 characters</small></html>",
435 MainFrame.getIcon("extend-left.gif"));
436 }
437
438 public void actionPerformed(ActionEvent evt){
439 long startOffset = ann.getStartNode().getOffset().longValue();
440 int increment = 1;
441 if((evt.getModifiers() & ActionEvent.SHIFT_MASK) > 0){
442 increment = SHIFT_INCREMENT;
444 if((evt.getModifiers() & ActionEvent.CTRL_MASK) > 0){
445 increment =CTRL_SHIFT_INCREMENT;
446 }
447 }
448
449 long newValue = ann.getEndNode().getOffset().longValue() - increment;
450 if(newValue < startOffset) newValue = startOffset;
451 try{
452 moveAnnotation(set, ann, ann.getStartNode().getOffset(),
453 new Long(newValue));
454 }catch(InvalidOffsetException ioe){
455 throw new GateRuntimeException(ioe);
456 }
457 }
458 }
459
460 protected class EndOffsetRightAction extends AnnotationAction{
461 public EndOffsetRightAction(){
462 super("<html><b>Extend</b><br><small>SHIFT = 5 characters, " +
463 "CTRL-SHIFT = 10 characters</small></html>",
464 MainFrame.getIcon("extend-right.gif"));
465 }
466
467 public void actionPerformed(ActionEvent evt){
468 long maxOffset = textView.getDocument().
469 getContent().size().longValue() -1;
470 int increment = 1;
472 if((evt.getModifiers() & ActionEvent.SHIFT_MASK) > 0){
473 increment = SHIFT_INCREMENT;
475 if((evt.getModifiers() & ActionEvent.CTRL_MASK) > 0){
476 increment = CTRL_SHIFT_INCREMENT;
477 }
478 }
479 long newValue = ann.getEndNode().getOffset().longValue() + increment;
480 if(newValue > maxOffset) newValue = maxOffset;
481 try{
482 moveAnnotation(set, ann, ann.getStartNode().getOffset(),
483 new Long(newValue));
484 }catch(InvalidOffsetException ioe){
485 throw new GateRuntimeException(ioe);
486 }
487 }
488 }
489
490
491 protected class DeleteAnnotationAction extends AnnotationAction{
492 public DeleteAnnotationAction(){
493 super("Delete", MainFrame.getIcon("delete.gif"));
494 }
495
496 public void actionPerformed(ActionEvent evt){
497 set.remove(ann);
498 hide();
499 }
500 }
501
502 protected class DismissAction extends AbstractAction{
503 public DismissAction(){
504 super("", MainFrame.getIcon("exit.gif"));
505 putValue(SHORT_DESCRIPTION, "Dismiss");
506 }
507
508 public void actionPerformed(ActionEvent evt){
509 hide();
510 }
511 }
512
513 protected class ApplyAction extends AbstractAction{
514 public ApplyAction(){
515 super("Apply");
516 }
518
519 public void actionPerformed(ActionEvent evt){
520 hide();
521 }
522 }
523
524 protected JWindow bottomWindow;
525
526 protected JComboBox typeCombo;
527 protected FeaturesSchemaEditor featuresEditor;
528 protected JScrollPane scroller;
529
530 protected StartOffsetLeftAction solAction;
531 protected StartOffsetRightAction sorAction;
532 protected EndOffsetLeftAction eolAction;
533 protected EndOffsetRightAction eorAction;
534 protected DismissAction dismissAction;
535
536 protected DeleteAnnotationAction delAction;
537 protected Timer hideTimer;
538 protected static final int HIDE_DELAY = 1500;
539 protected static final int SHIFT_INCREMENT = 5;
540 protected static final int CTRL_SHIFT_INCREMENT = 10;
541
542 protected Object highlight;
543
544
548 protected Map schemasByType;
549
550
551 protected TextualDocumentView textView;
552 protected AnnotationSetsView setsView;
553 protected JEditorPane textPane;
554 protected Annotation ann;
555 protected AnnotationSet set;
556 }
557