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