Refactorization of the management of the actions in menus, button, shortcuts
[scilab.git] / scilab / modules / scinotes / src / java / org / scilab / modules / scinotes / actions / FindAction.java
1 /* Scilab (http://www.scilab.org/) - This file is part of Scilab
2  * Copyright (C) 2009 - DIGITEO - Sylvestre KOUMAR
3  * Copyright (C) 2009 - DIGITEO - Allan CORNET
4  * Copyright (C) 2009 - DIGITEO - Antoine ELIAS
5  * Copyright (C) 2010 - Calixte DENIZET
6  *
7  * This file must be used under the terms of the CeCILL.
8  * This source file is licensed as described in the file COPYING, which
9  * you should have received as part of this distribution.  The terms
10  * are also available at
11  * http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt
12  *
13  */
14
15 package org.scilab.modules.scinotes.actions;
16
17 import java.awt.Color;
18 import java.awt.GridLayout;
19 import java.awt.event.ActionEvent;
20 import java.awt.event.ActionListener;
21 import java.awt.event.FocusEvent;
22 import java.awt.event.FocusListener;
23 import java.awt.event.ItemEvent;
24 import java.awt.event.ItemListener;
25 import java.awt.event.KeyEvent;
26 import java.awt.event.KeyListener;
27 import java.awt.event.MouseEvent;
28 import java.awt.event.MouseListener;
29 import java.awt.event.WindowEvent;
30 import java.awt.event.WindowListener;
31 import java.util.List;
32 import java.util.regex.Matcher;
33 import java.util.regex.Pattern;
34 import java.util.regex.PatternSyntaxException;
35
36 import javax.swing.BorderFactory;
37 import javax.swing.BoxLayout;
38 import javax.swing.ButtonGroup;
39 import javax.swing.ImageIcon;
40 import javax.swing.JButton;
41 import javax.swing.JCheckBox;
42 import javax.swing.JComboBox;
43 import javax.swing.JFrame;
44 import javax.swing.JLabel;
45 import javax.swing.JPanel;
46 import javax.swing.JRadioButton;
47 import javax.swing.JEditorPane;
48 import javax.swing.KeyStroke;
49 import javax.swing.text.BadLocationException;
50 import javax.swing.text.DefaultHighlighter;
51 import javax.swing.text.Highlighter;
52 import javax.swing.text.Document;
53 import javax.swing.text.Element;
54 import javax.swing.event.CaretEvent;
55 import javax.swing.event.CaretListener;
56
57 import org.scilab.modules.gui.menuitem.MenuItem;
58 import org.scilab.modules.gui.pushbutton.PushButton;
59 import org.scilab.modules.scinotes.SciNotes;
60 import org.scilab.modules.scinotes.ScilabDocument;
61 import org.scilab.modules.scinotes.SearchManager;
62 import org.scilab.modules.scinotes.utils.ConfigSciNotesManager;
63 import org.scilab.modules.scinotes.utils.SciNotesMessages;
64
65
66 /**
67  * FindAction
68  * @author Sylvestre KOUMAR
69  * @author Allan CORNET
70  * @author Antoine ELIAS
71  * @author Vincent COUVERT
72  * @author Calixte DENIZET
73  */
74 public final class FindAction extends DefaultAction {
75
76     /**
77      * serialVersionUID
78      */
79     private static final long serialVersionUID = -5499974793656106222L;
80
81     private static final int GAP = 5;
82     private static final int THREE = 3;
83
84     private static final Color colorSelected = new Color(205, 183, 158);
85     private static final Highlighter.HighlightPainter activePainter = new DefaultHighlighter.DefaultHighlightPainter(Color.green);
86     private static final Highlighter.HighlightPainter inactivePainter = new DefaultHighlighter.DefaultHighlightPainter(Color.yellow);
87     private static final Highlighter.HighlightPainter selectedPainter = new DefaultHighlighter.DefaultHighlightPainter(colorSelected);
88
89     private static String previousSearch;
90
91     private boolean windowAlreadyExist;
92
93     private JFrame frame;
94     private JButton buttonClose;
95     private JButton buttonFind;
96     private ButtonGroup buttonGroup1;
97     private ButtonGroup buttonGroup2;
98     private JButton buttonReplace;
99     private JButton buttonReplaceAll;
100     private JButton buttonReplaceFind;
101     private JCheckBox checkCase;
102     private JCheckBox checkRegular;
103     private JCheckBox checkWarp;
104     private JCheckBox checkWhole;
105     private JComboBox comboFind;
106     private JComboBox comboReplace;
107     private JLabel labelFind;
108     private JLabel labelReplace;
109     private JLabel labelStatus;
110     private JPanel panelButton;
111     private JPanel panelDirection;
112     private JPanel panelFind;
113     private JPanel panelFrame;
114     private JPanel panelOptions;
115     private JPanel panelScope;
116     private JRadioButton radioAll;
117     private JRadioButton radioBackward;
118     private JRadioButton radioForward;
119     private JRadioButton radioSelection;
120
121     private String oldWord;
122     private String newWord;
123     private String wordToFind;
124
125     private int startSelectedLines;
126     private int endSelectedLines;
127
128     private int startFind;
129     private int endFind;
130
131     private Object[] highlighters;
132     private Object selectedHighlight;
133
134     private String previousRegexp;
135     private int previousIndex;
136     private List<Integer[]> foundOffsets;
137     private MyListener myListener = new MyListener();
138
139     /**
140      * Constructor
141      * @param name the name of the action
142      * @param editor SciNotes
143      */
144     public FindAction(String name, SciNotes editor) {
145         super(name, editor);
146         editor.addFindActionWindow(this);
147     }
148
149     /**
150      * doAction
151      */
152     public void doAction() {
153         if (!windowAlreadyExist) {
154             findReplaceBox();
155         } else {
156             frame.setVisible(true);
157             buttonFind.requestFocus();
158         }
159
160         previousRegexp = "";
161         previousIndex = -1;
162         getEditor().getTextPane().removeCaretListener(myListener);
163         startSelectedLines = 0;
164         endSelectedLines = 0;
165
166         try {
167             // If some text is selected, it is used in find.
168             //if more than one line is selected set radio button "selected lines" at true
169             // else find and replace action is applied to the entire document
170             JEditorPane scinotesTextPane = getEditor().getTextPane();
171             int startPos = scinotesTextPane.getSelectionStart();
172             int endPos = scinotesTextPane.getSelectionEnd();
173             Element root = scinotesTextPane.getDocument().getDefaultRootElement();
174             int startLine = root.getElementIndex(startPos);
175             int endLine = root.getElementIndex(endPos);
176
177             if (startPos != endPos) {
178                 if (startLine != endLine) {
179                     radioSelection.doClick();
180                     radioSelection.setSelected(true);
181                     comboFind.setSelectedIndex(-1);
182                     comboReplace.setSelectedIndex(-1);
183                 } else {
184                     radioAll.doClick();
185                     comboFind.getEditor().setItem(scinotesTextPane.getDocument().getText(startPos, endPos - startPos));
186                     comboFind.getEditor().selectAll();
187                 }
188             } else {
189                 radioAll.doClick();
190                 comboFind.setSelectedIndex(-1);
191                 comboReplace.setSelectedIndex(-1);
192             }
193             windowAlreadyExist = true;
194             updateFindReplaceButtonStatus();
195         } catch (BadLocationException e) {
196             e.printStackTrace();
197         }
198     }
199
200     /**
201      * createMenu
202      * @param label label of the menu
203      * @param editor SciNotes
204      * @param key Keystroke
205      * @return MenuItem
206      */
207     public static MenuItem createMenu(String label, SciNotes editor, KeyStroke key) {
208         return createMenu(label, null, new FindAction(label, editor), key);
209     }
210
211     /**
212      * createButton
213      * @param tooltip the tooltip
214      * @param icon an icon name searched in SCI/modules/gui/images/icons/
215      * @param editor SciNotes
216      * @return PushButton
217      */
218     public static PushButton createButton(String tooltip, String icon, SciNotes editor) {
219         return createButton(tooltip, icon, new FindAction(tooltip, editor));
220     }
221
222     /**
223      * findReplaceBox
224      */
225     public void findReplaceBox() {
226
227         //Find & Replace Frame
228         frame = new JFrame();
229         frame.setIconImage(new ImageIcon(System.getenv("SCI") + "/modules/gui/images/icons/scilab.png").getImage());
230         frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
231         frame.setTitle(SciNotesMessages.FIND_REPLACE);
232         frame.setResizable(false);
233
234         buttonGroup1 = new ButtonGroup();
235         buttonGroup2 = new ButtonGroup();
236         panelFrame = new JPanel();
237         panelDirection = new JPanel();
238         radioForward = new JRadioButton();
239         radioBackward = new JRadioButton();
240         panelScope = new JPanel();
241         radioAll = new JRadioButton();
242         radioSelection = new JRadioButton();
243         panelOptions = new JPanel();
244         checkCase = new JCheckBox();
245         checkWhole = new JCheckBox();
246         checkRegular = new JCheckBox();
247         checkWarp = new JCheckBox();
248         panelFind = new JPanel();
249         labelFind = new JLabel();
250         labelReplace = new JLabel();
251         comboFind = new JComboBox();
252         comboReplace = new JComboBox();
253         panelButton = new JPanel();
254         buttonFind = new JButton();
255         buttonReplaceFind = new JButton();
256         buttonReplace = new JButton();
257         buttonReplaceAll = new JButton();
258         buttonClose = new JButton();
259         labelStatus = new JLabel();
260
261         panelFrame.setLayout(new BoxLayout(panelFrame, BoxLayout.PAGE_AXIS));
262         panelFrame.setBorder(BorderFactory.createEmptyBorder(2 * GAP, 2 * GAP, 2 * GAP, 2 * GAP));
263
264         /* Pattern to search and replace*/
265         labelFind.setText(SciNotesMessages.FIND);
266         labelReplace.setText(SciNotesMessages.REPLACE);
267
268         comboFind.setEditable(true);
269         comboReplace.setEditable(true);
270
271         panelFind.setLayout(new GridLayout(2, 2, GAP, GAP));
272         panelFind.add(labelFind);
273         panelFind.add(comboFind);
274         panelFind.add(labelReplace);
275         panelFind.add(comboReplace);
276         panelFrame.add(panelFind);
277
278         /* Search direction selection*/
279         panelDirection.setBorder(BorderFactory.createTitledBorder(SciNotesMessages.DIRECTION));
280
281         buttonGroup1.add(radioForward);
282         radioForward.setText(SciNotesMessages.FORWARD);
283         buttonGroup1.add(radioBackward);
284         radioBackward.setText(SciNotesMessages.BACKWARD);
285
286         panelDirection.setLayout(new GridLayout(2, 1, GAP, GAP));
287         panelDirection.add(radioForward);
288         panelDirection.add(radioBackward);
289
290         /* Scope for search */
291         panelScope.setBorder(BorderFactory.createTitledBorder(SciNotesMessages.SCOPE));
292
293         buttonGroup2.add(radioAll);
294         radioAll.setText(SciNotesMessages.SELECT_ALL);
295
296         buttonGroup2.add(radioSelection);
297         radioSelection.setText(SciNotesMessages.SELECTED_LINES);
298
299         panelScope.setLayout(new GridLayout(2, 1, GAP, GAP));
300         panelScope.add(radioAll);
301         panelScope.add(radioSelection);
302
303         JPanel panelDirectionScope = new JPanel();
304         panelDirectionScope.setLayout(new GridLayout(1, 2, GAP, GAP));
305         panelDirectionScope.add(panelDirection);
306         panelDirectionScope.add(panelScope);
307         panelFrame.add(panelDirectionScope);
308
309         /* Misc Options */
310         panelOptions.setBorder(BorderFactory.createTitledBorder(SciNotesMessages.OPTIONS));
311
312         checkCase.setText(SciNotesMessages.CASE_SENSITIVE);
313         checkWhole.setText(SciNotesMessages.WHOLE_WORD);
314         checkRegular.setText(SciNotesMessages.REGULAR_EXPRESSIONS);
315         checkWarp.setText(SciNotesMessages.WORD_WRAP);
316
317         checkWarp.setSelected(true);
318         panelOptions.setLayout(new GridLayout(2, 2, GAP, GAP));
319         panelOptions.add(checkCase);
320         panelOptions.add(checkRegular);
321         panelOptions.add(checkWhole);
322         panelOptions.add(checkWarp);
323         panelFrame.add(panelOptions);
324
325         buttonFind.setText(SciNotesMessages.FIND_BUTTON);
326         buttonReplaceFind.setText(SciNotesMessages.REPLACE_FIND);
327         buttonReplace.setText(SciNotesMessages.REPLACE);
328         buttonReplaceAll.setText(SciNotesMessages.REPLACE_ALL);
329         buttonClose.setText(SciNotesMessages.CLOSE);
330         labelStatus.setText("");
331
332         panelButton.setBorder(BorderFactory.createEmptyBorder(GAP, GAP, GAP, GAP));
333         panelButton.setLayout(new GridLayout(THREE, THREE, GAP, GAP));
334         panelButton.add(new JLabel());
335         panelButton.add(buttonFind);
336         panelButton.add(buttonReplaceFind);
337         panelButton.add(new JLabel());
338         panelButton.add(buttonReplace);
339         panelButton.add(buttonReplaceAll);
340         panelButton.add(new JLabel());
341         panelButton.add(new JLabel());
342         panelButton.add(buttonClose);
343
344         panelFrame.add(panelButton);
345
346         frame.setContentPane(panelFrame);
347
348         frame.pack();
349         frame.setLocationRelativeTo(getEditor());
350         frame.setVisible(true);
351
352         buttonReplaceFind.setEnabled(false);
353         buttonReplace.setEnabled(false);
354         buttonFind.setEnabled(false);
355         buttonReplaceAll.setEnabled(false);
356
357         radioForward.setSelected(true);
358         radioAll.doClick();
359
360         updateRecentSearch();
361         updateRecentReplace();
362
363         restoreConfiguration();
364
365         /*behaviour of buttons*/
366         radioSelection.addActionListener(new ActionListener() {
367
368                 public void actionPerformed(ActionEvent e) {
369                     JEditorPane scinotesTextPane = getEditor().getTextPane();
370                     Element root = scinotesTextPane.getDocument().getDefaultRootElement();
371                     int startPos = scinotesTextPane.getSelectionStart();
372                     int endPos = scinotesTextPane.getSelectionEnd();
373                     int startLine = root.getElementIndex(startPos);
374                     int endLine = root.getElementIndex(endPos);
375                     startSelectedLines = root.getElement(startLine).getStartOffset();
376                     endSelectedLines = root.getElement(endLine).getEndOffset() - 1;
377
378                     scinotesTextPane.setCaretPosition(startSelectedLines);
379
380                     removeAllHighlights();
381                     setSelectedHighlight();
382
383                     scinotesTextPane.addFocusListener(new FocusListener() {
384
385                             public void focusGained(FocusEvent e) {
386                                 removeAllHighlights();
387                                 previousRegexp = "";
388                                 radioAll.setSelected(true);
389                                 getEditor().getTextPane().removeFocusListener(this);
390                             }
391
392                             public void focusLost(FocusEvent e) { }
393                         });
394                 }
395             });
396
397         radioAll.addActionListener(new ActionListener() {
398
399                 public void actionPerformed(ActionEvent e) {
400                    removeAllHighlights();
401                    previousRegexp = "";
402                 }
403             });
404
405         buttonFind.addActionListener(new ActionListener() {
406
407                 public void actionPerformed(ActionEvent e) {
408                     saveFindReplaceConfiguration();
409                     findText();
410                 }
411             });
412
413         buttonReplace.addActionListener(new ActionListener() {
414
415                 public void actionPerformed(ActionEvent e) {
416                     saveFindReplaceConfiguration();
417                     JEditorPane scinotesTextPane =  getEditor().getTextPane();
418                     ScilabDocument doc = (ScilabDocument) scinotesTextPane.getDocument();
419
420                     doc.mergeEditsBegin();
421                     replaceOnlyText();
422                     doc.mergeEditsEnd();
423                 }
424
425             });
426
427         buttonReplaceFind.addActionListener(new ActionListener() {
428
429                 public void actionPerformed(ActionEvent e) {
430                     saveFindReplaceConfiguration();
431                     JEditorPane scinotesTextPane =  getEditor().getTextPane();
432                     ScilabDocument doc = (ScilabDocument) scinotesTextPane.getDocument();
433
434                     doc.mergeEditsBegin();
435                     if (buttonReplace.isEnabled()) {
436                         replaceText();
437                     }
438                     doc.mergeEditsEnd();
439                     findText();
440                 }
441             });
442
443         buttonReplaceAll.addActionListener(new ActionListener() {
444
445                 public void actionPerformed(ActionEvent e) {
446                     saveFindReplaceConfiguration();
447                     int start = 0;
448                     JEditorPane scinotesTextPane =  getEditor().getTextPane();
449                     ScilabDocument doc = (ScilabDocument) scinotesTextPane.getDocument();
450                     String text = "";
451
452                     boolean wholeWordSelected  = checkWhole.isSelected() &&  checkWhole.isEnabled();
453                     boolean regexpSelected  = checkRegular.isSelected();
454                     boolean caseSelected = checkCase.isSelected();
455
456                     // save current caret position to restore it at the end
457                     int currentCaretPos = scinotesTextPane.getCaretPosition();
458
459                     if (radioSelection.isSelected()) {
460                         ScilabDocument scilabDocument = (ScilabDocument) scinotesTextPane.getDocument();
461                         start = startSelectedLines;
462                         try {
463                             text = doc.getText(startSelectedLines, endSelectedLines - startSelectedLines);
464                         } catch (BadLocationException ex) { }
465                     } else {
466                         text = doc.getText();
467                     }
468
469                     oldWord = (String) comboFind.getEditor().getItem();
470                     newWord = (String) comboReplace.getEditor().getItem();
471                     setPreviousSearch(oldWord);
472
473                     Pattern pattern = SearchManager.generatePattern(oldWord, caseSelected, wholeWordSelected, regexpSelected);
474                     Matcher matcher = pattern.matcher(text);
475                     String replacedText = matcher.replaceAll(Matcher.quoteReplacement(newWord));
476                     if (replacedText.compareTo(text) != 0 && text.length() > 0) {
477                         // only touch document if any replacement took place
478                         try {
479                             doc.mergeEditsBegin();
480                             doc.setFocused(true);
481                             doc.replace(start, text.length(), replacedText, null);
482                             doc.mergeEditsEnd();
483                             previousRegexp = "";
484                             previousIndex = -1;
485                             buttonReplace.setEnabled(false);
486                             buttonReplaceFind.setEnabled(false);
487                             buttonReplaceAll.setEnabled(false);
488                         } catch (BadLocationException e1) {
489                             e1.printStackTrace();
490                         }
491                     }
492                     if (radioSelection.isSelected()) {
493                         removeAllHighlights();
494                         endSelectedLines = startSelectedLines + replacedText.length();
495                         setSelectedHighlight();
496                     }
497                 }
498             });
499
500         buttonClose.addActionListener(new ActionListener() {
501
502                 public void actionPerformed(ActionEvent e) {
503                     closeFindReplaceWindow();
504                 }
505             });
506
507         comboReplace.getEditor().getEditorComponent().addMouseListener(new MouseListener() {
508                 public void mouseReleased(MouseEvent e) { }
509                 public void mousePressed(MouseEvent e) {
510                     closeComboPopUp();
511                 }
512                 public void mouseExited(MouseEvent e) { }
513                 public void mouseEntered(MouseEvent e) { }
514                 public void mouseClicked(MouseEvent e) { }
515             });
516
517         comboReplace.getEditor().getEditorComponent().addKeyListener(new KeyListener() {
518                 public void keyTyped(KeyEvent e) { }
519                 public void keyReleased(KeyEvent e) {
520                     if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
521                         closeFindReplaceWindow();
522                     }
523
524                     if (e.getKeyCode() == KeyEvent.VK_ENTER) {
525                         replaceText();
526                         findText();
527                     }
528
529                     updateFindReplaceButtonStatus();
530                 }
531                 public void keyPressed(KeyEvent e) { }
532             });
533
534         comboReplace.addItemListener(new ItemListener() {
535                 public void itemStateChanged(ItemEvent arg0) {
536                     updateFindReplaceButtonStatus();
537                 }
538             });
539
540         comboReplace.addActionListener(new ActionListener() {
541                 public void actionPerformed(ActionEvent arg0) {
542                     updateFindReplaceButtonStatus();
543                 }
544             });
545
546         comboFind.getEditor().getEditorComponent().addMouseListener(new MouseListener() {
547                 public void mouseReleased(MouseEvent arg0) { }
548                 public void mousePressed(MouseEvent arg0) {
549                     closeComboPopUp();
550                 }
551                 public void mouseExited(MouseEvent arg0) { }
552                 public void mouseEntered(MouseEvent arg0) { }
553                 public void mouseClicked(MouseEvent arg0) { }
554             });
555
556         comboFind.addActionListener(new ActionListener() {
557
558                 public void actionPerformed(ActionEvent arg0) {
559                     updateFindReplaceButtonStatus();
560                 }
561             });
562
563         comboFind.addItemListener(new ItemListener() {
564                 public void itemStateChanged(ItemEvent arg0) {
565                     updateFindReplaceButtonStatus();
566                 }
567             });
568
569         comboFind.getEditor().getEditorComponent().addKeyListener(new KeyListener() {
570
571                 public void keyTyped(KeyEvent e) { }
572                 public void keyReleased(KeyEvent e) {
573                     if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
574                         closeFindReplaceWindow();
575                     }
576
577                     if (e.getKeyCode() == KeyEvent.VK_ENTER) {
578                         findText();
579                     }
580
581                     updateFindReplaceButtonStatus();
582
583                 }
584
585                 public void keyPressed(KeyEvent e) { }
586             });
587
588         frame.addWindowListener(new WindowListener() {
589                 public void windowClosed(WindowEvent e) { }
590                 public void windowDeiconified(WindowEvent e) { }
591                 public void windowActivated(WindowEvent e) { }
592
593                 public void windowClosing(WindowEvent e) {
594                     closeFindReplaceWindow();
595                 }
596
597                 public void windowDeactivated(WindowEvent e) { }
598                 public void windowIconified(WindowEvent e) { }
599                 public void windowOpened(WindowEvent e) { }
600             });
601
602     }
603
604     /**
605      * Close combo pop-up
606      */
607     private void closeComboPopUp() {
608         if (comboFind.isPopupVisible()) {
609             comboFind.hidePopup();
610         }
611
612         if (comboReplace.isPopupVisible()) {
613             comboReplace.hidePopup();
614         }
615     }
616
617     /**
618      * Update recent search
619      */
620     private void updateRecentSearch() {
621         Object old = comboFind.getEditor().getItem();
622         comboFind.removeAllItems();
623         List<String> recentFind = ConfigSciNotesManager.getRecentSearch();
624         for (String item : recentFind) {
625             comboFind.addItem(item);
626         }
627
628         comboFind.getEditor().setItem(old);
629     }
630
631     /**
632      * Update recent replace
633      */
634     private void updateRecentReplace() {
635         Object old = comboReplace.getEditor().getItem();
636         comboReplace.removeAllItems();
637         List<String> recentReaplce = ConfigSciNotesManager.getRecentReplace();
638         for (String item : recentReaplce) {
639             comboReplace.addItem(item);
640         }
641
642         comboReplace.getEditor().setItem(old);
643     }
644
645     /**
646      * Save configuration
647      */
648     public void saveFindReplaceConfiguration() {
649         ConfigSciNotesManager.saveRecentSearch((String) comboFind.getEditor().getItem());
650         ConfigSciNotesManager.saveRecentReplace((String) comboReplace.getEditor().getItem());
651         ConfigSciNotesManager.saveRegularExpression(checkRegular.isSelected());
652         ConfigSciNotesManager.saveWordWarp(checkWarp.isSelected());
653         ConfigSciNotesManager.saveWholeWord(checkWhole.isSelected());
654         ConfigSciNotesManager.saveCaseSensitive(checkCase.isSelected());
655     }
656
657     /**
658      * Restore configuration
659      */
660     private void restoreConfiguration() {
661         checkRegular.setSelected(ConfigSciNotesManager.getRegularExpression());
662         checkWarp.setSelected(ConfigSciNotesManager.getWordWarp());
663         checkWhole.setSelected(ConfigSciNotesManager.getWholeWord());
664         checkCase.setSelected(ConfigSciNotesManager.getCaseSensitive());
665     }
666
667     /**
668      * Update status of buttons
669      */
670     protected void updateFindReplaceButtonStatus() {
671         String textFind = (String) comboFind.getEditor().getItem();
672         String textReplace = (String) comboReplace.getEditor().getItem();
673         if (textFind.compareTo("") != 0) {
674             buttonFind.setEnabled(true);
675             buttonReplace.setEnabled(true);
676             buttonReplaceAll.setEnabled(true);
677             buttonReplaceFind.setEnabled(true);
678         } else {
679             buttonFind.setEnabled(false);
680             buttonReplace.setEnabled(false);
681             buttonReplaceAll.setEnabled(false);
682             buttonReplaceFind.setEnabled(false);
683         }
684
685         // permit to choose "whole word" only if the input is a single word
686         Pattern patternWholeWord = Pattern.compile("\\w*");
687         Matcher matcherWholeWord = patternWholeWord.matcher(textFind);
688
689         checkWhole.setEnabled(false);
690
691         if (matcherWholeWord.find()) {
692             if ((matcherWholeWord.end() - matcherWholeWord.start()) == textFind.length()) {
693                 checkWhole.setEnabled(true);
694             }
695
696         }
697
698         // if we search a regexp, we first need to know if the regexp is valid or not
699         if (checkRegular.isSelected()) {
700             try {
701                 Pattern.compile(textFind);
702                 labelStatus.setText("");
703                 buttonFind.setEnabled(true);
704                 buttonReplaceAll.setEnabled(true);
705             } catch (PatternSyntaxException pse) {
706
707                 labelStatus.setText(String.format(SciNotesMessages.INVALID_REGEXP, textFind));
708
709                 buttonFind.setEnabled(false);
710                 buttonReplaceAll.setEnabled(false);
711             }
712         }
713
714         if (buttonReplace.isEnabled() && oldWord != null && oldWord.compareTo(textFind) != 0) {
715             buttonReplace.setEnabled(false);
716             buttonReplaceFind.setEnabled(false);
717         }
718     }
719
720     /**
721      * Add highlights on the view to see the searched word
722      * @param active to highlight an active word
723      */
724     public void addHighlighters(boolean active) {
725         Highlighter hl = getEditor().getTextPane().getHighlighter();
726         removeAllHighlights();
727         highlighters = new Object[foundOffsets.size()];
728         for (int i = 0; i < foundOffsets.size(); i++) {
729             try {
730                 if (active) {
731                     highlighters[i] = hl.addHighlight(foundOffsets.get(i)[0], foundOffsets.get(i)[1], activePainter);
732                 } else {
733                     highlighters[i] = hl.addHighlight(foundOffsets.get(i)[0], foundOffsets.get(i)[1], inactivePainter);
734                 }
735             } catch (BadLocationException e) {
736                 e.printStackTrace();
737             }
738         }
739
740         if (radioSelection.isSelected()) {
741             setSelectedHighlight();
742         }
743     }
744
745     /**
746      * Change the highlight with index n to active (or not) the word
747      * @param n the index
748      * @param active to highlight an active word
749      */
750     public void changeHighlighter(int n, boolean active) {
751         if (highlighters != null) {
752             Highlighter hl = getEditor().getTextPane().getHighlighter();
753             hl.removeHighlight(highlighters[n]);
754             try {
755                 if (active) {
756                     highlighters[n] = hl.addHighlight(foundOffsets.get(n)[0], foundOffsets.get(n)[1], activePainter);
757                 } else {
758                     highlighters[n] = hl.addHighlight(foundOffsets.get(n)[0], foundOffsets.get(n)[1], inactivePainter);
759                 }
760             } catch (BadLocationException e) {
761                 e.printStackTrace();
762             }
763
764             if (radioSelection.isSelected()) {
765                 setSelectedHighlight();
766             }
767         }
768     }
769
770     /**
771      * Set the selection where to search
772      */
773     public void setSelectedHighlight() {
774         Highlighter hl = getEditor().getTextPane().getHighlighter();
775         try {
776             if (selectedHighlight != null) {
777                 hl.removeHighlight(selectedHighlight);
778             }
779             selectedHighlight = hl.addHighlight(startSelectedLines, endSelectedLines, selectedPainter);
780         } catch (BadLocationException e) {
781             e.printStackTrace();
782         }
783     }
784
785     /**
786      * Remove an highlight
787      * @param n th index of the word
788      **/
789     public void removeHighlighter(int n) {
790         if (highlighters != null) {
791             Highlighter hl = getEditor().getTextPane().getHighlighter();
792             hl.removeHighlight(highlighters[n]);
793         }
794     }
795
796     /**
797      * Remove all Highlights
798      */
799     public void removeAllHighlights() {
800         Highlighter hl = getEditor().getTextPane().getHighlighter();
801         if (highlighters != null) {
802             for (int i = 0; i < highlighters.length; i++) {
803                 hl.removeHighlight(highlighters[i]);
804             }
805         }
806         if (selectedHighlight != null) {
807             hl.removeHighlight(selectedHighlight);
808             selectedHighlight = null;
809         }
810     }
811
812     /**
813      * Get the next keyword after the position pos
814      * @param pos the position of the caret
815      * @param forward for a forward search
816      * @param circular for a circular search
817      * @return the index of the word
818      */
819     private int getSearched(int pos, boolean forward, boolean circular) {
820         int index = -1;
821         for (int i = 0; i < foundOffsets.size(); i++) {
822             if (forward && foundOffsets.get(i)[0] >= pos) {
823                 index = i;
824                 break;
825             } else if (!forward && foundOffsets.get(i)[0] >= pos) {
826                 if (i == 0) {
827                     return -1;
828                 }
829                 index = i - 1;
830                 break;
831             }
832         }
833
834         if (index == -1 && circular) {
835             if (forward) {
836                 return 0;
837             } else {
838                 return foundOffsets.size() - 1;
839             }
840         }
841
842         return index;
843     }
844
845     /**
846      * Generate the offsets of the searched word
847      * @return a boolean if a job has been done
848      */
849     public boolean generateOffsets() {
850         boolean caseSensitive  = checkCase.isSelected();
851         boolean wholeWord  = checkWhole.isSelected() && checkWhole.isEnabled();
852         boolean useRegexp  = checkRegular.isSelected();
853         boolean onlySelectedLines = radioSelection.isSelected();
854         Document doc = getEditor().getTextPane().getDocument();
855         wordToFind = (String) comboFind.getEditor().getItem();
856
857         String strregexp = SearchManager.generatePattern(wordToFind, caseSensitive, wholeWord, useRegexp).toString();
858         if (!previousRegexp.equals(strregexp)) {
859             previousRegexp = strregexp;
860             if (onlySelectedLines) {
861                 foundOffsets = SearchManager.findWord(doc, wordToFind, startSelectedLines, endSelectedLines, caseSensitive, wholeWord, useRegexp);
862             } else {
863                 foundOffsets = SearchManager.findWord(doc, wordToFind, 0, doc.getLength(), caseSensitive, wholeWord, useRegexp);
864             }
865             return true;
866         }
867         return false;
868     }
869
870     /**
871      * findText
872      */
873     private void findText() {
874         boolean wrapSearchSelected = checkWarp.isSelected();
875         boolean forwardSearch = radioForward.isSelected();
876         boolean backwardSearch = radioBackward.isSelected();
877
878         String exp = (String) comboFind.getEditor().getItem();
879         if (exp.compareTo("") == 0) {
880             return;
881         }
882
883         setPreviousSearch(wordToFind);
884         saveFindReplaceConfiguration();
885         updateRecentSearch();
886
887         JEditorPane scinotesTextPane =  getEditor().getTextPane();
888         Document doc = scinotesTextPane.getDocument();
889
890         if (generateOffsets()) {
891             addHighlighters(false);
892             previousIndex = -1;
893         }
894
895         int currentCaretPos = scinotesTextPane.getSelectionStart();
896
897         if (forwardSearch) {
898             currentCaretPos =  scinotesTextPane.getSelectionEnd();
899         } else {
900             currentCaretPos =  scinotesTextPane.getSelectionStart() - 1;
901         }
902
903         labelStatus.setText("");
904
905         int size = foundOffsets.size();
906         if (size > 0) {
907             int nextIndex;
908             if (previousIndex == -1) {
909                 nextIndex = getSearched(currentCaretPos, !backwardSearch, wrapSearchSelected);
910             } else {
911                 if (backwardSearch) {
912                     if (wrapSearchSelected) {
913                         nextIndex = (size + previousIndex - 1) % size;
914                     } else {
915                         nextIndex = Math.max(previousIndex - 1, 0);
916                     }
917                 } else {
918                     if (wrapSearchSelected) {
919                         nextIndex = (previousIndex + 1) % size;
920                     } else {
921                         nextIndex = Math.min(previousIndex + 1, size - 1);
922                     }
923                 }
924                 changeHighlighter(previousIndex, false);
925             }
926
927             if (nextIndex != -1) {
928                 changeHighlighter(nextIndex, true);
929                 previousIndex = nextIndex;
930                 if (backwardSearch) {
931                     scinotesTextPane.setCaretPosition(foundOffsets.get(nextIndex)[0]);
932                 } else {
933                     scinotesTextPane.setCaretPosition(foundOffsets.get(nextIndex)[1]);
934                 }
935
936                 scinotesTextPane.addFocusListener(myListener);
937                 buttonReplace.setEnabled(true);
938                 buttonReplaceFind.setEnabled(true);
939
940                 startFind = foundOffsets.get(nextIndex)[0];
941                 endFind = foundOffsets.get(nextIndex)[1];
942             } else {
943                 startFind = 0;
944                 endFind = 0;
945                 buttonReplace.setEnabled(false);
946                 buttonReplaceFind.setEnabled(false);
947             }
948         } else { // nothing has been found
949             labelStatus.setText(String.format(SciNotesMessages.STRING_NOT_FOUND, wordToFind));
950             buttonReplace.setEnabled(false);
951             buttonReplaceFind.setEnabled(false);
952             buttonReplaceAll.setEnabled(false);
953         }
954     }
955
956     /**
957      * replaceOnlyText
958      */
959     private void replaceOnlyText() {
960         replaceText();
961         buttonReplace.setEnabled(false);
962         buttonReplaceFind.setEnabled(false);
963     }
964
965     /**
966      * replaceText
967      */
968     private void replaceText() {
969         boolean forwardSearch = radioForward.isSelected();
970         boolean backwardSearch = radioBackward.isSelected();
971         boolean caseSensitive  = checkCase.isSelected();
972         boolean wholeWord  = checkWhole.isSelected() && checkWhole.isEnabled();
973         boolean useRegexp  = checkRegular.isSelected();
974
975         String find = (String) comboFind.getEditor().getItem();
976         String replace = (String) comboReplace.getEditor().getItem();
977
978         if (find.compareTo("") == 0 || (startFind == endFind)) {
979             return;
980         }
981
982         saveFindReplaceConfiguration();
983         updateRecentSearch();
984         updateRecentReplace();
985         setPreviousSearch(find);
986
987         JEditorPane scinotesTextPane =  getEditor().getTextPane();
988
989         /*
990          * we replace only the current result and then disable replace and replace find button
991          * same behaviour as find and replace in eclipse
992          */
993
994         try {
995             Pattern pattern = SearchManager.generatePattern(find, caseSensitive, wholeWord, useRegexp);
996             Matcher matcher = pattern.matcher(scinotesTextPane.getText(startFind, endFind - startFind + 1));
997             String replacedText = matcher.replaceAll(Matcher.quoteReplacement(replace));
998             ScilabDocument doc = (ScilabDocument) scinotesTextPane.getDocument();
999             doc.setFocused(true);
1000             doc.replace(startFind, endFind - startFind + 1, replacedText, null);
1001             scinotesTextPane.setCaretPosition(startFind + replacedText.length());
1002             endSelectedLines += replacedText.length() - (endFind - startFind + 1);
1003             previousRegexp = "";
1004             previousIndex = -1;
1005             if (generateOffsets()) {
1006                 addHighlighters(false);
1007             }
1008         } catch (BadLocationException ex) {
1009             ex.printStackTrace();
1010         }
1011     }
1012
1013     /**
1014      * closeFindReplaceWindow
1015      */
1016     public void closeFindReplaceWindow() {
1017         if (windowAlreadyExist) {
1018             JEditorPane scinotesTextPane = getEditor().getTextPane();
1019             if (scinotesTextPane != null) {
1020                 scinotesTextPane.getHighlighter().removeAllHighlights();
1021                 int start = scinotesTextPane.getSelectionStart();
1022                 int end = scinotesTextPane.getSelectionEnd();
1023                 scinotesTextPane.select(start, end);
1024             }
1025             frame.dispose();
1026             windowAlreadyExist = false;
1027         }
1028     }
1029
1030     /**
1031      * Get the previous search
1032      * @return the previuos search
1033      */
1034     public static String getPreviousSearch() {
1035         return previousSearch;
1036     }
1037
1038     /**
1039      * Set the previous search
1040      * @param previousSearch String
1041      */
1042     public static void setPreviousSearch(String previousSearch) {
1043         FindAction.previousSearch = previousSearch;
1044     }
1045
1046     /**
1047      * Inner class to handle events (used as a singleton)
1048      */
1049     private class MyListener implements CaretListener, FocusListener {
1050
1051         /**
1052          * focusGained in interface FocusListener
1053          * @param e FocusEvent
1054          */
1055         public void focusGained(FocusEvent e) {
1056             if (previousIndex != -1) {
1057                 int start = foundOffsets.get(previousIndex)[0];
1058                 int end = foundOffsets.get(previousIndex)[1];
1059                 getEditor().getTextPane().select(start, end);
1060             }
1061             getEditor().getTextPane().addCaretListener(this);
1062         }
1063
1064         /**
1065          * focusLost in interface FocusListener
1066          * @param e FocusEvent
1067          */
1068         public void focusLost(FocusEvent e) { }
1069
1070         /**
1071          * caretUpdate in interface CaretListener
1072          * @param e FocusEvent
1073          */
1074         public void caretUpdate(CaretEvent e) {
1075             removeAllHighlights();
1076             getEditor().getTextPane().removeCaretListener(this);
1077             getEditor().getTextPane().removeFocusListener(this);
1078             previousRegexp = "";
1079         }
1080     }
1081 }