001 /*
002 * Copyright (c) 1995-2010, The University of Sheffield. See the file
003 * COPYRIGHT.txt in the software or at http://gate.ac.uk/gate/COPYRIGHT.txt
004 *
005 * This file is part of GATE (see http://gate.ac.uk/), and is free
006 * software, licenced under the GNU Library General Public License,
007 * Version 2, June 1991 (in the distribution as file licence.html,
008 * and also available at http://gate.ac.uk/gate/licence.html).
009 *
010 * Thomas Heitz, Nov 21, 2007
011 *
012 * $Id: SchemaAnnotationEditor.java 9221 2007-11-14 17:46:37Z valyt $
013 */
014
015 package gate.gui.annedit;
016
017 import java.awt.*;
018 import java.awt.event.*;
019 import java.util.*;
020 import java.util.regex.*;
021
022 import javax.swing.*;
023 import javax.swing.event.*;
024 import javax.swing.event.DocumentEvent;
025 import javax.swing.event.DocumentListener;
026
027 import gate.*;
028 import gate.event.*;
029 import gate.gui.MainFrame;
030 import gate.util.*;
031
032 /**
033 * Build a GUI for searching and annotating annotations in a text.
034 * It needs to be called from a {@link gate.gui.annedit.OwnedAnnotationEditor}.
035 *
036 * <p>Here is an example how to add it to a JPanel panel.
037 *
038 * <pre>
039 * SearchAndAnnotatePanel searchPanel =
040 * new SearchAndAnnotatePanel(panel.getBackground(), this, window);
041 * panel.add(searchPanel);
042 * </pre>
043 */
044 public class SearchAndAnnotatePanel extends JPanel {
045
046 private static final long serialVersionUID = 1L;
047
048 /**
049 * The annotation editor that use this search and annotate panel.
050 */
051 private OwnedAnnotationEditor annotationEditor;
052
053 /**
054 * Window that contains the annotation editor.
055 */
056 private Window annotationEditorWindow;
057
058 /**
059 * Listener for updating the list of searched annotations.
060 */
061 protected AnnotationSetListener annotationSetListener;
062
063 /**
064 * The box used to host the search pane.
065 */
066 protected Box searchBox;
067
068 /**
069 * The pane containing the UI for search and annotate functionality.
070 */
071 protected JPanel searchPane;
072
073 /**
074 * Text field for searching
075 */
076 protected JTextField searchTextField;
077
078 /**
079 * Checkbox for enabling RegEx searching.
080 */
081 protected JCheckBox searchRegExpChk;
082
083 /**
084 * Help button that gives predefined regular expressions.
085 */
086 protected JButton helpRegExpButton;
087
088 /**
089 * Checkbox for enabling case sensitive searching.
090 */
091 protected JCheckBox searchCaseSensChk;
092
093 /**
094 * Checkbox for enabling whole word searching.
095 */
096 protected JCheckBox searchWholeWordsChk;
097
098 /**
099 * Checkbox for enabling whole word searching.
100 */
101 protected JCheckBox searchHighlightsChk;
102
103 /**
104 * Checkbox for showing the search UI.
105 */
106 protected JCheckBox searchEnabledCheck;
107
108 /**
109 * Shared instance of the matcher.
110 */
111 protected Matcher matcher;
112
113 protected FindFirstAction findFirstAction;
114
115 protected FindPreviousAction findPreviousAction;
116
117 protected FindNextAction findNextAction;
118
119 protected AnnotateMatchAction annotateMatchAction;
120
121 protected AnnotateAllMatchesAction annotateAllMatchesAction;
122
123 protected UndoAnnotateAllMatchesAction undoAnnotateAllMatchesAction;
124
125 protected int nextMatchStartsFrom;
126
127 protected String content;
128
129 /**
130 * Start and end index of the all the matches.
131 */
132 protected LinkedList<Vector<Integer>> matchedIndexes;
133
134 /**
135 * List of annotations ID that have been created
136 * by the AnnotateAllMatchesAction.
137 */
138 protected LinkedList<Annotation> annotateAllAnnotationsID;
139
140 protected SmallButton firstSmallButton;
141
142 protected SmallButton annotateAllMatchesSmallButton;
143
144
145
146 public SearchAndAnnotatePanel(Color color,
147 OwnedAnnotationEditor annotationEditor, Window window) {
148
149 this.annotationEditor = annotationEditor;
150 annotationEditorWindow = window;
151
152 initGui(color);
153
154 // initially searchBox is collapsed
155 searchBox.remove(searchPane);
156 searchCaseSensChk.setVisible(false);
157 searchRegExpChk.setVisible(false);
158 searchWholeWordsChk.setVisible(false);
159 searchHighlightsChk.setVisible(false);
160
161 // if the user never gives the focus to the textPane then
162 // there will never be any selection in it so we force it
163 getOwner().getTextComponent().requestFocusInWindow();
164
165 initListeners();
166
167 content = getOwner().getDocument().getContent().toString();
168 }
169
170 /**
171 * Build the GUI with JPanels and Boxes.
172 *
173 * @param color Color of the background.
174 * _ _ _ _ _
175 * V Search & Annotate |_| Case |_| Regexp |_| Whole |_| Highlights
176 * _______________________________________________
177 * |V_Searched_Expression__________________________| |?|
178 *
179 * |First| |Prev.| |Next| |Annotate| |Ann. all next|
180 */
181 protected void initGui(Color color) {
182
183 JPanel mainPane = new JPanel();
184 mainPane.setLayout(new BoxLayout(mainPane, BoxLayout.Y_AXIS));
185 mainPane.setBackground(color);
186 mainPane.setBorder(BorderFactory.createEmptyBorder(5, 3, 5, 3));
187
188 setLayout(new BorderLayout());
189 add(mainPane, BorderLayout.CENTER);
190
191 searchBox = Box.createVerticalBox();
192 String aTitle = "Open Search & Annotate tool";
193 JLabel label = new JLabel(aTitle);
194 searchBox.setMinimumSize(
195 new Dimension(label.getPreferredSize().width, 0));
196 searchBox.setAlignmentX(Component.LEFT_ALIGNMENT);
197
198 JPanel firstLinePane = new JPanel();
199 firstLinePane.setAlignmentX(Component.LEFT_ALIGNMENT);
200 firstLinePane.setAlignmentY(Component.TOP_ALIGNMENT);
201 firstLinePane.setLayout(new BoxLayout(firstLinePane, BoxLayout.Y_AXIS));
202 firstLinePane.setBackground(color);
203 Box hBox = Box.createHorizontalBox();
204 searchEnabledCheck = new JCheckBox(aTitle, MainFrame.getIcon("closed"), false);
205 searchEnabledCheck.setSelectedIcon(MainFrame.getIcon("expanded"));
206 searchEnabledCheck.setBackground(color);
207 searchEnabledCheck.setToolTipText("<html>Allows to search for an "
208 +"expression and<br>annotate one or all the matches.</html>");
209 hBox.add(searchEnabledCheck);
210 hBox.add(Box.createHorizontalStrut(5));
211 searchCaseSensChk = new JCheckBox("Case", true);
212 searchCaseSensChk.setToolTipText("Case sensitive search.");
213 searchCaseSensChk.setBackground(color);
214 hBox.add(searchCaseSensChk);
215 hBox.add(Box.createHorizontalStrut(5));
216 searchRegExpChk = new JCheckBox("Regexp", false);
217 searchRegExpChk.setToolTipText("Regular expression search.");
218 searchRegExpChk.setBackground(color);
219 hBox.add(searchRegExpChk);
220 hBox.add(Box.createHorizontalStrut(5));
221 searchWholeWordsChk = new JCheckBox("Whole", false);
222 searchWholeWordsChk.setBackground(color);
223 searchWholeWordsChk.setToolTipText("Whole word search.");
224 hBox.add(searchWholeWordsChk);
225 hBox.add(Box.createHorizontalStrut(5));
226 searchHighlightsChk = new JCheckBox("Highlights", false);
227 searchHighlightsChk.setToolTipText(
228 "Restrict the search on the highlighted annotations.");
229 searchHighlightsChk.setBackground(color);
230 hBox.add(searchHighlightsChk);
231 hBox.add(Box.createHorizontalGlue());
232 firstLinePane.add(hBox);
233 searchBox.add(firstLinePane);
234
235 searchPane = new JPanel();
236 searchPane.setAlignmentX(Component.LEFT_ALIGNMENT);
237 searchPane.setAlignmentY(Component.TOP_ALIGNMENT);
238 searchPane.setLayout(new BoxLayout(searchPane, BoxLayout.Y_AXIS));
239 searchPane.setBackground(color);
240 hBox = Box.createHorizontalBox();
241 hBox.setBorder(BorderFactory.createEmptyBorder(3, 0, 5, 0));
242 hBox.add(Box.createHorizontalStrut(5));
243 searchTextField = new JTextField(10);
244 searchTextField.setToolTipText("Enter an expression to search for.");
245 //disallow vertical expansion
246 searchTextField.setMaximumSize(new Dimension(Integer.MAX_VALUE,
247 searchTextField.getPreferredSize().height));
248 searchTextField.addActionListener(new ActionListener() {
249 public void actionPerformed(ActionEvent arg0) {
250 findFirstAction.actionPerformed(null);
251 }
252 });
253 hBox.add(searchTextField);
254 hBox.add(Box.createHorizontalStrut(2));
255 helpRegExpButton = new JButton("?");
256 helpRegExpButton.setMargin(new Insets(0, 2, 0, 2));
257 helpRegExpButton.setToolTipText("GATE search expression builder.");
258 helpRegExpButton.addActionListener(new SearchExpressionsAction(
259 searchTextField, annotationEditorWindow, searchRegExpChk));
260 hBox.add(helpRegExpButton);
261 hBox.add(Box.createHorizontalGlue());
262 searchPane.add(hBox);
263 hBox = Box.createHorizontalBox();
264 hBox.add(Box.createHorizontalStrut(5));
265 findFirstAction = new FindFirstAction();
266 firstSmallButton = new SmallButton(findFirstAction);
267 hBox.add(firstSmallButton);
268 hBox.add(Box.createHorizontalStrut(5));
269 findPreviousAction = new FindPreviousAction();
270 findPreviousAction.setEnabled(false);
271 hBox.add(new SmallButton(findPreviousAction));
272 hBox.add(Box.createHorizontalStrut(5));
273 findNextAction = new FindNextAction();
274 findNextAction.setEnabled(false);
275 hBox.add(new SmallButton(findNextAction));
276 hBox.add(Box.createHorizontalStrut(5));
277 annotateMatchAction = new AnnotateMatchAction();
278 annotateMatchAction.setEnabled(false);
279 hBox.add(new SmallButton(annotateMatchAction));
280 hBox.add(Box.createHorizontalStrut(5));
281 annotateAllMatchesAction = new AnnotateAllMatchesAction();
282 undoAnnotateAllMatchesAction = new UndoAnnotateAllMatchesAction();
283 annotateAllMatchesSmallButton =
284 new SmallButton(annotateAllMatchesAction);
285 annotateAllMatchesAction.setEnabled(false);
286 undoAnnotateAllMatchesAction.setEnabled(false);
287 hBox.add(annotateAllMatchesSmallButton);
288 hBox.add(Box.createHorizontalStrut(5));
289 searchPane.add(hBox);
290 searchBox.add(searchPane);
291
292 mainPane.add(searchBox);
293 }
294
295 protected void initListeners() {
296
297 searchEnabledCheck.addActionListener(new ActionListener() {
298 public void actionPerformed(ActionEvent e) {
299 if (searchEnabledCheck.isSelected()) {
300 //add the search box if not already there
301 if (!searchBox.isAncestorOf(searchPane)) {
302 // if empty, initialise the search field to the text
303 // of the current annotation
304 String searchText = searchTextField.getText();
305 if (searchText == null || searchText.trim().length() == 0) {
306 if (annotationEditor.getAnnotationCurrentlyEdited() != null
307 && getOwner() != null) {
308 String annText = getOwner().getDocument().getContent().
309 toString().substring(
310 annotationEditor.getAnnotationCurrentlyEdited()
311 .getStartNode().getOffset().intValue(),
312 annotationEditor.getAnnotationCurrentlyEdited()
313 .getEndNode().getOffset().intValue());
314 searchTextField.setText(annText);
315 }
316 }
317 searchBox.add(searchPane);
318 }
319 searchEnabledCheck.setText("");
320 searchCaseSensChk.setVisible(true);
321 searchRegExpChk.setVisible(true);
322 searchWholeWordsChk.setVisible(true);
323 searchHighlightsChk.setVisible(true);
324 searchTextField.requestFocusInWindow();
325 searchTextField.selectAll();
326 annotationEditorWindow.pack();
327 annotationEditor.setPinnedMode(true);
328
329 } else {
330 if(searchBox.isAncestorOf(searchPane)){
331 searchEnabledCheck.setText("Open Search & Annotate tool");
332 searchBox.remove(searchPane);
333 searchCaseSensChk.setVisible(false);
334 searchRegExpChk.setVisible(false);
335 searchWholeWordsChk.setVisible(false);
336 searchHighlightsChk.setVisible(false);
337 if (annotationEditor.getAnnotationCurrentlyEdited() != null) {
338 annotationEditor.setEditingEnabled(true);
339 }
340 annotationEditorWindow.pack();
341 }
342 }
343 }
344 });
345
346 this.addAncestorListener(new AncestorListener() {
347 public void ancestorAdded(AncestorEvent event) {
348 // put the selection of the document into the search text field
349 if (searchTextField.getText().trim().length() == 0
350 && getOwner().getTextComponent().getSelectedText() != null) {
351 searchTextField.setText(getOwner().getTextComponent().getSelectedText());
352 }
353 }
354 public void ancestorRemoved(AncestorEvent event) {
355 // if the editor window is closed
356 enableActions(false);
357 }
358 public void ancestorMoved(AncestorEvent event) {
359 // do nothing
360 }
361 });
362
363 searchTextField.getDocument().addDocumentListener(new DocumentListener(){
364 public void changedUpdate(DocumentEvent e) {
365 enableActions(false);
366 }
367 public void insertUpdate(DocumentEvent e) {
368 enableActions(false);
369 }
370 public void removeUpdate(DocumentEvent e) {
371 enableActions(false);
372 }
373 });
374
375 searchCaseSensChk.addActionListener(new ActionListener() {
376 public void actionPerformed(ActionEvent e) {
377 enableActions(false);
378 }
379 });
380
381 searchRegExpChk.addActionListener(new ActionListener() {
382 public void actionPerformed(ActionEvent e) {
383 enableActions(false);
384 }
385 });
386
387 searchWholeWordsChk.addActionListener(new ActionListener() {
388 public void actionPerformed(ActionEvent e) {
389 enableActions(false);
390 }
391 });
392
393 searchHighlightsChk.addActionListener(new ActionListener() {
394 public void actionPerformed(ActionEvent e) {
395 enableActions(false);
396 }
397 });
398
399 }
400
401 private void enableActions(boolean state){
402 findPreviousAction.setEnabled(state);
403 findNextAction.setEnabled(state);
404 annotateMatchAction.setEnabled(state);
405 annotateAllMatchesAction.setEnabled(state);
406 if (annotateAllMatchesSmallButton.getAction()
407 .equals(undoAnnotateAllMatchesAction)) {
408 annotateAllMatchesSmallButton.setAction(annotateAllMatchesAction);
409 }
410 }
411
412 private boolean isAnnotationEditorReady() {
413 if (!annotationEditor.editingFinished()
414 || getOwner() == null
415 || annotationEditor.getAnnotationCurrentlyEdited() == null
416 || annotationEditor.getAnnotationSetCurrentlyEdited() == null) {
417 annotationEditorWindow.setVisible(false);
418 JOptionPane.showMessageDialog(annotationEditorWindow,
419 (annotationEditor.getAnnotationCurrentlyEdited() == null?
420 "Please select an existing annotation\n"
421 + "or create a new one then select it."
422 :
423 "Please set all required features in the feature table."),
424 "GATE",
425 JOptionPane.INFORMATION_MESSAGE);
426 annotationEditorWindow.setVisible(true);
427 return false;
428 } else {
429 return true;
430 }
431 }
432
433 protected class FindFirstAction extends AbstractAction{
434 private static final long serialVersionUID = 1L;
435
436 public FindFirstAction(){
437 super("First");
438 super.putValue(SHORT_DESCRIPTION, "Finds the first occurrence.");
439 super.putValue(MNEMONIC_KEY, KeyEvent.VK_F);
440 }
441
442 public void actionPerformed(ActionEvent evt){
443 if (!isAnnotationEditorReady()) { return; }
444 annotationEditor.setPinnedMode(true);
445 annotationEditor.setEditingEnabled(false);
446 String patternText = searchTextField.getText();
447 Pattern pattern;
448
449 try {
450 String prefixPattern = searchWholeWordsChk.isSelected() ? "\\b":"";
451 prefixPattern += searchRegExpChk.isSelected() ? "":"\\Q";
452 String suffixPattern = searchRegExpChk.isSelected() ? "":"\\E";
453 suffixPattern += searchWholeWordsChk.isSelected() ? "\\b":"";
454 patternText = prefixPattern + patternText + suffixPattern;
455 // TODO: Pattern.UNICODE_CASE prevent insensitive case to work
456 // for Java 1.5 but works with Java 1.6
457 pattern = searchCaseSensChk.isSelected() ?
458 Pattern.compile(patternText) :
459 Pattern.compile(patternText, Pattern.CASE_INSENSITIVE);
460
461 } catch(PatternSyntaxException e) {
462 // hides the annotator window
463 // to be able to see the dialog window
464 annotationEditorWindow.setVisible(false);
465 JOptionPane.showMessageDialog(annotationEditorWindow,
466 "Invalid regular expression.\n\n"
467 + e.toString().replaceFirst("^.+PatternSyntaxException: ", ""),
468 "GATE",
469 JOptionPane.INFORMATION_MESSAGE);
470 annotationEditorWindow.setVisible(true);
471 return;
472 }
473
474 matcher = pattern.matcher(content);
475 boolean found = false;
476 int start = -1;
477 int end = -1;
478 nextMatchStartsFrom = 0;
479 while (matcher.find(nextMatchStartsFrom) && !found) {
480 start = (matcher.groupCount()>0)?matcher.start(1):matcher.start();
481 end = (matcher.groupCount()>0)?matcher.end(1):matcher.end();
482 found = false;
483 if (searchHighlightsChk.isSelected()) {
484 javax.swing.text.Highlighter.Highlight[] highlights =
485 getOwner().getTextComponent().getHighlighter().getHighlights();
486 for (javax.swing.text.Highlighter.Highlight h : highlights) {
487 if (h.getStartOffset() <= start && h.getEndOffset() >= end) {
488 found = true;
489 break;
490 }
491 }
492 } else {
493 found = true;
494 }
495 nextMatchStartsFrom = end;
496 }
497
498 if (found) {
499 findNextAction.setEnabled(true);
500 annotateMatchAction.setEnabled(true);
501 annotateAllMatchesAction.setEnabled(false);
502 matchedIndexes = new LinkedList<Vector<Integer>>();
503 Vector<Integer> v = new Vector<Integer>(2);
504 v.add(start);
505 v.add(end);
506 matchedIndexes.add(v);
507 getOwner().getTextComponent().select(start, end);
508 annotationEditor.placeDialog(start, end);
509
510 } else {
511 // no match found
512 findNextAction.setEnabled(false);
513 annotateMatchAction.setEnabled(false);
514 }
515 findPreviousAction.setEnabled(false);
516 }
517 }
518
519 protected class FindPreviousAction extends AbstractAction {
520 private static final long serialVersionUID = 1L;
521
522 public FindPreviousAction() {
523 super("Prev.");
524 super.putValue(SHORT_DESCRIPTION, "Finds the previous occurrence.");
525 super.putValue(MNEMONIC_KEY, KeyEvent.VK_P);
526 }
527
528 public void actionPerformed(ActionEvent evt) {
529 if (!isAnnotationEditorReady()) { return; }
530 annotationEditor.setEditingEnabled(false);
531 // the first time we invoke previous action we want to go two
532 // previous matches back not just one
533 matchedIndexes.removeLast();
534
535 Vector<Integer> v;
536 if (matchedIndexes.size() == 1) {
537 // no more previous annotation, disable the action
538 findPreviousAction.setEnabled(false);
539 }
540 v = matchedIndexes.getLast();
541 int start = v.firstElement();
542 int end = v.lastElement();
543 getOwner().getTextComponent().select(start, end);
544 annotationEditor.placeDialog(start, end);
545 // reset the matcher for the next FindNextAction
546 nextMatchStartsFrom = start;
547 findNextAction.setEnabled(true);
548 annotateMatchAction.setEnabled(true);
549 }
550 }
551
552 protected class FindNextAction extends AbstractAction{
553 private static final long serialVersionUID = 1L;
554
555 public FindNextAction(){
556 super("Next");
557 super.putValue(SHORT_DESCRIPTION, "Finds the next occurrence.");
558 super.putValue(MNEMONIC_KEY, KeyEvent.VK_N);
559 }
560
561 public void actionPerformed(ActionEvent evt){
562 if (!isAnnotationEditorReady()) { return; }
563 annotationEditor.setEditingEnabled(false);
564 boolean found = false;
565 int start = -1;
566 int end = -1;
567 nextMatchStartsFrom = getOwner().getTextComponent().getCaretPosition();
568
569 while (matcher.find(nextMatchStartsFrom) && !found) {
570 start = (matcher.groupCount()>0)?matcher.start(1):matcher.start();
571 end = (matcher.groupCount()>0)?matcher.end(1):matcher.end();
572 found = false;
573 if (searchHighlightsChk.isSelected()) {
574 javax.swing.text.Highlighter.Highlight[] highlights =
575 getOwner().getTextComponent().getHighlighter().getHighlights();
576 for (javax.swing.text.Highlighter.Highlight h : highlights) {
577 if (h.getStartOffset() <= start && h.getEndOffset() >= end) {
578 found = true;
579 break;
580 }
581 }
582 } else {
583 found = true;
584 }
585 nextMatchStartsFrom = end;
586 }
587
588 if (found) {
589 Vector<Integer> v = new Vector<Integer>(2);
590 v.add(start);
591 v.add(end);
592 matchedIndexes.add(v);
593 getOwner().getTextComponent().select(start, end);
594 annotationEditor.placeDialog(start, end);
595 findPreviousAction.setEnabled(true);
596 } else {
597 //no more matches possible
598 findNextAction.setEnabled(false);
599 annotateMatchAction.setEnabled(false);
600 }
601 }
602 }
603
604 protected class AnnotateMatchAction extends AbstractAction{
605 private static final long serialVersionUID = 1L;
606
607 public AnnotateMatchAction(){
608 super("Annotate");
609 super.putValue(SHORT_DESCRIPTION, "Annotates the current match.");
610 super.putValue(MNEMONIC_KEY, KeyEvent.VK_A);
611 }
612
613 public void actionPerformed(ActionEvent evt){
614 if (!isAnnotationEditorReady()) { return; }
615 int start = getOwner().getTextComponent().getSelectionStart();
616 int end = getOwner().getTextComponent().getSelectionEnd();
617 FeatureMap features = Factory.newFeatureMap();
618 if(annotationEditor.getAnnotationCurrentlyEdited().getFeatures() != null)
619 features.putAll(annotationEditor.getAnnotationCurrentlyEdited().getFeatures());
620 try {
621 Integer id = annotationEditor.getAnnotationSetCurrentlyEdited().add(
622 new Long(start), new Long(end),
623 annotationEditor.getAnnotationCurrentlyEdited().getType(), features);
624 Annotation newAnn =
625 annotationEditor.getAnnotationSetCurrentlyEdited().get(id);
626 getOwner().getTextComponent().select(end, end);
627 //set the annotation as selected
628 getOwner().selectAnnotation(new AnnotationDataImpl(
629 annotationEditor.getAnnotationSetCurrentlyEdited(), newAnn));
630 annotationEditor.editAnnotation(newAnn,
631 annotationEditor.getAnnotationSetCurrentlyEdited());
632 annotateAllMatchesAction.setEnabled(true);
633 if (annotateAllMatchesSmallButton.getAction()
634 .equals(undoAnnotateAllMatchesAction)) {
635 annotateAllMatchesSmallButton.setAction(annotateAllMatchesAction);
636 }
637 }
638 catch(InvalidOffsetException e) {
639 //the offsets here should always be valid.
640 throw new LuckyException(e);
641 }
642 }
643 }
644
645 protected class AnnotateAllMatchesAction extends AbstractAction{
646 private static final long serialVersionUID = 1L;
647
648 public AnnotateAllMatchesAction(){
649 super("Ann. all next");
650 super.putValue(SHORT_DESCRIPTION, "Annotates all the following matches.");
651 super.putValue(MNEMONIC_KEY, KeyEvent.VK_L);
652 }
653
654 public void actionPerformed(ActionEvent evt){
655 if (!isAnnotationEditorReady()) { return; }
656 annotateAllAnnotationsID = new LinkedList<Annotation>();
657 boolean found = false;
658 int start = -1;
659 int end = -1;
660 nextMatchStartsFrom =
661 getOwner().getTextComponent().getCaretPosition();
662
663 do {
664 found = false;
665 while (matcher.find(nextMatchStartsFrom) && !found) {
666 start = (matcher.groupCount()>0)?matcher.start(1):matcher.start();
667 end = (matcher.groupCount()>0)?matcher.end(1):matcher.end();
668 if (searchHighlightsChk.isSelected()) {
669 javax.swing.text.Highlighter.Highlight[] highlights =
670 getOwner().getTextComponent().getHighlighter().getHighlights();
671 for (javax.swing.text.Highlighter.Highlight h : highlights) {
672 if (h.getStartOffset() <= start && h.getEndOffset() >= end) {
673 found = true;
674 break;
675 }
676 }
677 } else {
678 found = true;
679 }
680 nextMatchStartsFrom = end;
681 }
682 if (found) { annotateCurrentMatch(start, end); }
683 } while (found && !matcher.hitEnd());
684
685 annotateAllMatchesSmallButton.setAction(undoAnnotateAllMatchesAction);
686 undoAnnotateAllMatchesAction.setEnabled(true);
687 }
688
689 private void annotateCurrentMatch(int start, int end){
690 FeatureMap features = Factory.newFeatureMap();
691 features.put("safe.regex", "true");
692 if(annotationEditor.getAnnotationCurrentlyEdited().getFeatures() != null)
693 features.putAll(annotationEditor.getAnnotationCurrentlyEdited().getFeatures());
694 try {
695 Integer id = annotationEditor.getAnnotationSetCurrentlyEdited().add(
696 new Long(start), new Long(end),
697 annotationEditor.getAnnotationCurrentlyEdited().getType(),
698 features);
699 Annotation newAnn =
700 annotationEditor.getAnnotationSetCurrentlyEdited().get(id);
701 annotateAllAnnotationsID.add(newAnn);
702 }
703 catch(InvalidOffsetException e) {
704 //the offsets here should always be valid.
705 throw new LuckyException(e);
706 }
707 }
708 }
709
710 /**
711 * Remove the annotations added by the last action that annotate all matches.
712 */
713 protected class UndoAnnotateAllMatchesAction extends AbstractAction{
714 private static final long serialVersionUID = 1L;
715
716 public UndoAnnotateAllMatchesAction(){
717 super("Undo");
718 super.putValue(SHORT_DESCRIPTION, "Undo previous annotate all action.");
719 super.putValue(MNEMONIC_KEY, KeyEvent.VK_U);
720 }
721
722 public void actionPerformed(ActionEvent evt){
723
724 for(Annotation annotation : annotateAllAnnotationsID) {
725 annotationEditor.getAnnotationSetCurrentlyEdited().remove(annotation);
726 }
727
728 if (annotationEditor.getAnnotationSetCurrentlyEdited() == null) {
729 annotationEditor.setEditingEnabled(false);
730 }
731
732 annotateAllMatchesSmallButton.setAction(annotateAllMatchesAction);
733 annotateAllMatchesAction.setEnabled(false);
734 }
735 }
736
737 /**
738 * A smaller JButton with less margins.
739 */
740 protected class SmallButton extends JButton{
741 private static final long serialVersionUID = 1L;
742
743 public SmallButton(Action a) {
744 super(a);
745 setMargin(new Insets(0, 2, 0, 2));
746 }
747 }
748
749 /**
750 * @return the owner
751 */
752 public AnnotationEditorOwner getOwner() {
753 return annotationEditor.getOwner();
754 }
755
756 }
|