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 bottomWindow.doLayout();
272 }
273
274 public boolean isShowing(){
275 return bottomWindow.isShowing();
276 }
277
278
282 public void show(boolean autohide){
283 placeWindows();
284 bottomWindow.setVisible(true);
285 if(autohide) hideTimer.restart();
286 }
287
288 protected void placeWindows(){
289 try{
291 Rectangle startRect = textPane.modelToView(ann.getStartNode().
292 getOffset().intValue());
293 Rectangle endRect = textPane.modelToView(ann.getEndNode().
294 getOffset().intValue());
295 Point topLeft = textPane.getLocationOnScreen();
296 int x = topLeft.x + startRect.x;
297 int y = topLeft.y + endRect.y + endRect.height;
298
299 Rectangle visRect = textPane.getVisibleRect();
302 int maxY = topLeft.y + visRect.y + visRect.height;
303
304 bottomWindow.pack();
306 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
307 boolean revalidate = false;
308 if(bottomWindow.getSize().width > screenSize.width){
309 bottomWindow.setSize(screenSize.width, bottomWindow.getSize().height);
310 revalidate = true;
311 }
312 if(bottomWindow.getSize().height > screenSize.height){
313 bottomWindow.setSize(bottomWindow.getSize().width, screenSize.height);
314 revalidate = true;
315 }
316
317 if(revalidate) bottomWindow.validate();
318 int maxX = screenSize.width - bottomWindow.getSize().width;
320 if(maxY + bottomWindow.getSize().height > screenSize.height){
322 maxY = screenSize.height - bottomWindow.getSize().height;
323 }
324
325 if(y > maxY) y = maxY;
327 if(x > maxX) x = maxX;
328 bottomWindow.setLocation(x, y);
329
330 }catch(BadLocationException ble){
331 throw new GateRuntimeException(ble);
333 }
334 }
335
336
344 protected void moveAnnotation(AnnotationSet set, Annotation oldAnnotation,
345 Long newStartOffset, Long newEndOffset) throws InvalidOffsetException{
346 AnnotationSetsView.TypeHandler oldHandler = setsView.getTypeHandler(
353 set.getName(), oldAnnotation.getType());
354
355 Integer oldID = oldAnnotation.getId();
356 set.remove(oldAnnotation);
357 set.add(oldID, newStartOffset, newEndOffset,
358 oldAnnotation.getType(), oldAnnotation.getFeatures());
359 setAnnotation(set.get(oldID), set);
360 AnnotationSetsView.TypeHandler newHandler = setsView.getTypeHandler(
361 set.getName(), oldAnnotation.getType());
362
363 if(newHandler != oldHandler){
364 newHandler.setSelected(false);
366 newHandler.colour = oldHandler.colour;
367 newHandler.setSelected(oldHandler.isSelected());
368 }
369 }
370
371 public void hide(){
372 bottomWindow.setVisible(false);
374 }
375
376
379 protected abstract class AnnotationAction extends AbstractAction{
380 public AnnotationAction(String name, Icon icon){
381 super("", icon);
382 putValue(SHORT_DESCRIPTION, name);
383
384 }
385 }
386
387 protected class StartOffsetLeftAction extends AnnotationAction{
388 public StartOffsetLeftAction(){
389 super("<html><b>Extend</b><br><small>SHIFT = 5 characters, CTRL-SHIFT = 10 characters</small></html>",
390 MainFrame.getIcon("extend-left.gif"));
391 }
392
393 public void actionPerformed(ActionEvent evt){
394 Annotation oldAnn = ann;
395 int increment = 1;
396 if((evt.getModifiers() & ActionEvent.SHIFT_MASK) > 0){
397 increment = SHIFT_INCREMENT;
399 if((evt.getModifiers() & ActionEvent.CTRL_MASK) > 0){
400 increment = CTRL_SHIFT_INCREMENT;
401 }
402 }
403 long newValue = ann.getStartNode().getOffset().longValue() - increment;
404 if(newValue < 0) newValue = 0;
405 try{
406 moveAnnotation(set, ann, new Long(newValue),
407 ann.getEndNode().getOffset());
408 }catch(InvalidOffsetException ioe){
409 throw new GateRuntimeException(ioe);
410 }
411 }
412 }
413
414 protected class StartOffsetRightAction extends AnnotationAction{
415 public StartOffsetRightAction(){
416 super("<html><b>Shrink</b><br><small>SHIFT = 5 characters, " +
417 "CTRL-SHIFT = 10 characters</small></html>",
418 MainFrame.getIcon("extend-right.gif"));
419 }
420
421 public void actionPerformed(ActionEvent evt){
422 long endOffset = ann.getEndNode().getOffset().longValue();
423 int increment = 1;
424 if((evt.getModifiers() & ActionEvent.SHIFT_MASK) > 0){
425 increment = SHIFT_INCREMENT;
427 if((evt.getModifiers() & ActionEvent.CTRL_MASK) > 0){
428 increment = CTRL_SHIFT_INCREMENT;
429 }
430 }
431
432 long newValue = ann.getStartNode().getOffset().longValue() + increment;
433 if(newValue > endOffset) newValue = endOffset;
434 try{
435 moveAnnotation(set, ann, new Long(newValue),
436 ann.getEndNode().getOffset());
437 }catch(InvalidOffsetException ioe){
438 throw new GateRuntimeException(ioe);
439 }
440 }
441 }
442
443 protected class EndOffsetLeftAction extends AnnotationAction{
444 public EndOffsetLeftAction(){
445 super("<html><b>Shrink</b><br><small>SHIFT = 5 characters, " +
446 "CTRL-SHIFT = 10 characters</small></html>",
447 MainFrame.getIcon("extend-left.gif"));
448 }
449
450 public void actionPerformed(ActionEvent evt){
451 long startOffset = ann.getStartNode().getOffset().longValue();
452 int increment = 1;
453 if((evt.getModifiers() & ActionEvent.SHIFT_MASK) > 0){
454 increment = SHIFT_INCREMENT;
456 if((evt.getModifiers() & ActionEvent.CTRL_MASK) > 0){
457 increment =CTRL_SHIFT_INCREMENT;
458 }
459 }
460
461 long newValue = ann.getEndNode().getOffset().longValue() - increment;
462 if(newValue < startOffset) newValue = startOffset;
463 try{
464 moveAnnotation(set, ann, ann.getStartNode().getOffset(),
465 new Long(newValue));
466 }catch(InvalidOffsetException ioe){
467 throw new GateRuntimeException(ioe);
468 }
469 }
470 }
471
472 protected class EndOffsetRightAction extends AnnotationAction{
473 public EndOffsetRightAction(){
474 super("<html><b>Extend</b><br><small>SHIFT = 5 characters, " +
475 "CTRL-SHIFT = 10 characters</small></html>",
476 MainFrame.getIcon("extend-right.gif"));
477 }
478
479 public void actionPerformed(ActionEvent evt){
480 long maxOffset = textView.getDocument().
481 getContent().size().longValue() -1;
482 int increment = 1;
484 if((evt.getModifiers() & ActionEvent.SHIFT_MASK) > 0){
485 increment = SHIFT_INCREMENT;
487 if((evt.getModifiers() & ActionEvent.CTRL_MASK) > 0){
488 increment = CTRL_SHIFT_INCREMENT;
489 }
490 }
491 long newValue = ann.getEndNode().getOffset().longValue() + increment;
492 if(newValue > maxOffset) newValue = maxOffset;
493 try{
494 moveAnnotation(set, ann, ann.getStartNode().getOffset(),
495 new Long(newValue));
496 }catch(InvalidOffsetException ioe){
497 throw new GateRuntimeException(ioe);
498 }
499 }
500 }
501
502
503 protected class DeleteAnnotationAction extends AnnotationAction{
504 public DeleteAnnotationAction(){
505 super("Delete", MainFrame.getIcon("delete.gif"));
506 }
507
508 public void actionPerformed(ActionEvent evt){
509 set.remove(ann);
510 hide();
511 }
512 }
513
514 protected class DismissAction extends AbstractAction{
515 public DismissAction(){
516 super("");
517 Icon icon = UIManager.getIcon("InternalFrame.closeIcon");
518 if(icon == null) icon = MainFrame.getIcon("exit.gif");
519 putValue(SMALL_ICON, icon);
520 putValue(SHORT_DESCRIPTION, "Dismiss");
521 }
522
523 public void actionPerformed(ActionEvent evt){
524 hide();
525 }
526 }
527
528 protected class ApplyAction extends AbstractAction{
529 public ApplyAction(){
530 super("Apply");
531 }
533
534 public void actionPerformed(ActionEvent evt){
535 hide();
536 }
537 }
538
539 protected JWindow bottomWindow;
540
541 protected JComboBox typeCombo;
542 protected FeaturesSchemaEditor featuresEditor;
543 protected JScrollPane scroller;
544
545 protected StartOffsetLeftAction solAction;
546 protected StartOffsetRightAction sorAction;
547 protected EndOffsetLeftAction eolAction;
548 protected EndOffsetRightAction eorAction;
549 protected DismissAction dismissAction;
550
551 protected DeleteAnnotationAction delAction;
552 protected Timer hideTimer;
553 protected static final int HIDE_DELAY = 1500;
554 protected static final int SHIFT_INCREMENT = 5;
555 protected static final int CTRL_SHIFT_INCREMENT = 10;
556
557 protected Object highlight;
558
559
563 protected Map schemasByType;
564
565
566 protected TextualDocumentView textView;
567 protected AnnotationSetsView setsView;
568 protected JEditorPane textPane;
569 protected Annotation ann;
570 protected AnnotationSet set;
571 }
572