001: /*
002: * SearchBar.java - Search & replace toolbar
003: * :tabSize=8:indentSize=8:noTabs=false:
004: * :folding=explicit:collapseFolds=1:
005: *
006: * Copyright (C) 2000, 2001, 2002 Slava Pestov
007: *
008: * This program is free software; you can redistribute it and/or
009: * modify it under the terms of the GNU General Public License
010: * as published by the Free Software Foundation; either version 2
011: * of the License, or any later version.
012: *
013: * This program is distributed in the hope that it will be useful,
014: * but WITHOUT ANY WARRANTY; without even the implied warranty of
015: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
016: * GNU General Public License for more details.
017: *
018: * You should have received a copy of the GNU General Public License
019: * along with this program; if not, write to the Free Software
020: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
021: */
022:
023: package org.gjt.sp.jedit.search;
024:
025: //{{{ Imports
026: import java.awt.event.*;
027: import java.awt.*;
028: import javax.swing.event.*;
029: import javax.swing.*;
030: import org.gjt.sp.jedit.*;
031: import org.gjt.sp.jedit.syntax.SyntaxStyle;
032: import org.gjt.sp.jedit.gui.*;
033: import org.gjt.sp.jedit.textarea.*;
034: import org.gjt.sp.util.Log;
035:
036: //}}}
037:
038: /**
039: * Incremental search tool bar.
040: * @version $Id: SearchBar.java 10798 2007-10-04 09:04:25Z kpouer $
041: */
042: public class SearchBar extends JPanel {
043: //{{{ SearchBar constructor
044: public SearchBar(final View view, boolean temp) {
045: setLayout(new BoxLayout(this , BoxLayout.X_AXIS));
046:
047: this .view = view;
048:
049: add(Box.createHorizontalStrut(2));
050:
051: JLabel label = new JLabel(jEdit.getProperty("view.search.find"));
052: add(label);
053: add(Box.createHorizontalStrut(12));
054: add(find = new HistoryTextField("find"));
055: find.setSelectAllOnFocus(true);
056:
057: SyntaxStyle style = GUIUtilities.parseStyle(jEdit
058: .getProperty("view.style.invalid"), "Dialog", 12);
059: errorBackground = style.getBackgroundColor();
060: errorForeground = style.getForegroundColor();
061: defaultBackground = find.getBackground();
062: defaultForeground = find.getForeground();
063: Dimension max = find.getPreferredSize();
064: max.width = Integer.MAX_VALUE;
065: find.setMaximumSize(max);
066: ActionHandler actionHandler = new ActionHandler();
067: find.addKeyListener(new KeyHandler());
068: find.addActionListener(actionHandler);
069: find.getDocument().addDocumentListener(new DocumentHandler());
070:
071: Insets margin = new Insets(1, 1, 1, 1);
072:
073: add(Box.createHorizontalStrut(12));
074: add(ignoreCase = new JCheckBox(jEdit.getProperty("search.case")));
075: ignoreCase.addActionListener(actionHandler);
076: ignoreCase.setMargin(margin);
077: ignoreCase.setRequestFocusEnabled(false);
078: add(Box.createHorizontalStrut(2));
079: add(regexp = new JCheckBox(jEdit.getProperty("search.regexp")));
080: regexp.addActionListener(actionHandler);
081: regexp.setMargin(margin);
082: regexp.setRequestFocusEnabled(false);
083: add(Box.createHorizontalStrut(2));
084: add(hyperSearch = new JCheckBox(jEdit
085: .getProperty("search.hypersearch")));
086: hyperSearch.addActionListener(actionHandler);
087: hyperSearch.setMargin(margin);
088: hyperSearch.setRequestFocusEnabled(false);
089:
090: update();
091:
092: //{{{ Create the timer used by incremental search
093: timer = new Timer(0, new ActionListener() {
094: public void actionPerformed(ActionEvent evt) {
095: if (!incrementalSearch(searchStart, searchReverse)) {
096: if (!incrementalSearch((searchReverse ? view
097: .getBuffer().getLength() : 0),
098: searchReverse)) {
099: // not found at all.
100: view
101: .getStatus()
102: .setMessageAndClear(
103: jEdit
104: .getProperty("view.status.search-not-found"));
105: }
106: }
107: }
108: }); //}}}
109:
110: // if 'temp' is true, hide search bar after user is done with it
111: this .temp = temp;
112:
113: propertiesChanged();
114: } //}}}
115:
116: //{{{ getField() method
117: public HistoryTextField getField() {
118: return find;
119: } //}}}
120:
121: //{{{ setHyperSearch() method
122: public void setHyperSearch(boolean hyperSearch) {
123: jEdit.setBooleanProperty("view.search.hypersearch.toggle",
124: hyperSearch);
125: this .hyperSearch.setSelected(hyperSearch);
126: } //}}}
127:
128: //{{{ update() method
129: public void update() {
130: ignoreCase.setSelected(SearchAndReplace.getIgnoreCase());
131: regexp.setSelected(SearchAndReplace.getRegexp());
132: hyperSearch.setSelected(jEdit
133: .getBooleanProperty("view.search.hypersearch.toggle"));
134: } //}}}
135:
136: //{{{ propertiesChanged() method
137: public void propertiesChanged() {
138: if (temp) {
139: if (close == null) {
140: close = new RolloverButton(GUIUtilities
141: .loadIcon("closebox.gif"));
142: close.addActionListener(new ActionHandler());
143: close.setToolTipText(jEdit
144: .getProperty("view.search.close-tooltip"));
145: }
146: add(close);
147: } else if (close != null)
148: remove(close);
149: } //}}}
150:
151: //{{{ Private members
152:
153: //{{{ Instance variables
154: private View view;
155: private HistoryTextField find;
156: private JCheckBox ignoreCase, regexp, hyperSearch;
157: private Timer timer;
158: private boolean wasError;
159: private Color defaultBackground;
160: private Color defaultForeground;
161: private Color errorForeground;
162: private Color errorBackground;
163: // close button only there if 'temp' is true
164: private RolloverButton close;
165:
166: private int searchStart;
167: private boolean searchReverse;
168: private boolean temp;
169:
170: //}}}
171:
172: //{{{ find() method
173: private void find(boolean reverse) {
174: timer.stop();
175:
176: String text = find.getText();
177: //{{{ If nothing entered, show search and replace dialog box
178: if (text.length() == 0) {
179: jEdit.setBooleanProperty("search.hypersearch.toggle",
180: hyperSearch.isSelected());
181: SearchDialog.showSearchDialog(view, null,
182: SearchDialog.CURRENT_BUFFER);
183: } //}}}
184: //{{{ HyperSearch
185: else if (hyperSearch.isSelected()) {
186: if (temp) {
187: view.removeToolBar(SearchBar.this );
188: } else
189: find.setText(null);
190:
191: SearchAndReplace.setSearchString(text);
192: SearchAndReplace.setSearchFileSet(new CurrentBufferSet());
193: SearchAndReplace.hyperSearch(view);
194: } //}}}
195: //{{{ Incremental search
196: else {
197: if (reverse && SearchAndReplace.getRegexp()) {
198: GUIUtilities.error(view, "regexp-reverse", null);
199: return;
200: }
201:
202: // on enter, start search from end
203: // of current match to find next one
204: int start;
205: JEditTextArea textArea = view.getTextArea();
206: Selection s = textArea.getSelectionAtOffset(textArea
207: .getCaretPosition());
208: if (s == null)
209: start = textArea.getCaretPosition();
210: else if (reverse)
211: start = s.getStart();
212: else
213: start = s.getEnd();
214:
215: if (!incrementalSearch(start, reverse)) {
216: // not found. start from
217: // beginning
218: if (!incrementalSearch(reverse ? view.getBuffer()
219: .getLength() : 0, reverse)) {
220: // not found at all.
221: view
222: .getStatus()
223: .setMessageAndClear(
224: jEdit
225: .getProperty("view.status.search-not-found"));
226: } else {
227: // inform user search restarted
228: view.getStatus().setMessageAndClear(
229: jEdit.getProperty("view.status.auto-wrap"));
230: // beep if beep property set
231: if (jEdit
232: .getBooleanProperty("search.beepOnSearchAutoWrap")) {
233: getToolkit().beep();
234: }
235: }
236: }
237: } //}}}
238: } //}}}
239:
240: //{{{ incrementalSearch() method
241: private boolean incrementalSearch(int start, boolean reverse) {
242: /* For example, if the current fileset is a directory,
243: * C+g will find the next match within that fileset.
244: * This can be annoying if you have just done an
245: * incremental search and want the next occurrence
246: * in the current buffer. */
247: SearchAndReplace.setSearchFileSet(new CurrentBufferSet());
248: SearchAndReplace.setSearchString(find.getText());
249: SearchAndReplace.setReverseSearch(reverse);
250:
251: boolean ret = false;
252: try {
253: if (SearchAndReplace.find(view, view.getBuffer(), start,
254: false, reverse))
255: ret = true;
256: } catch (Exception e) {
257: Log.log(Log.DEBUG, this , e);
258:
259: // invalid regexp, ignore
260: // return true to avoid annoying beeping while
261: // typing a re
262: ret = true;
263: }
264: if (ret) {
265: if (wasError) {
266: find.setForeground(defaultForeground);
267: find.setBackground(defaultBackground);
268: wasError = false;
269: }
270: } else {
271: if (!wasError) {
272: find.setForeground(errorForeground);
273: find.setBackground(errorBackground);
274: wasError = true;
275: }
276: }
277:
278: return ret;
279: } //}}}
280:
281: //{{{ timerIncrementalSearch() method
282: private void timerIncrementalSearch(int start, boolean reverse) {
283: searchStart = start;
284: searchReverse = reverse;
285:
286: timer.stop();
287: timer.setRepeats(false);
288: timer.setInitialDelay(150);
289: timer.start();
290: } //}}}
291:
292: //}}}
293:
294: //{{{ Inner classes
295:
296: //{{{ ActionHandler class
297: class ActionHandler implements ActionListener {
298: //{{{ actionPerformed() method
299: public void actionPerformed(ActionEvent evt) {
300: Object source = evt.getSource();
301: if (source == find)
302: find(false);
303: else if (source == hyperSearch) {
304: jEdit.setBooleanProperty(
305: "view.search.hypersearch.toggle", hyperSearch
306: .isSelected());
307: update();
308: } else if (source == ignoreCase) {
309: SearchAndReplace.setIgnoreCase(ignoreCase.isSelected());
310: } else if (source == regexp) {
311: SearchAndReplace.setRegexp(regexp.isSelected());
312: } else if (source == close) {
313: view.removeToolBar(SearchBar.this );
314: view.getEditPane().focusOnTextArea();
315: }
316: } //}}}
317: } //}}}
318:
319: //{{{ DocumentHandler class
320: class DocumentHandler implements DocumentListener {
321: //{{{ insertUpdate() method
322: public void insertUpdate(DocumentEvent evt) {
323: // on insert, start search from beginning of
324: // current match. This will continue to highlight
325: // the current match until another match is found
326: if (!hyperSearch.isSelected()) {
327: int start;
328: JEditTextArea textArea = view.getTextArea();
329: Selection s = textArea.getSelectionAtOffset(textArea
330: .getCaretPosition());
331: if (s == null)
332: start = textArea.getCaretPosition();
333: else
334: start = s.getStart();
335:
336: timerIncrementalSearch(start, false);
337: }
338: } //}}}
339:
340: //{{{ removeUpdate() method
341: public void removeUpdate(DocumentEvent evt) {
342: // on backspace, restart from beginning
343: if (!hyperSearch.isSelected()) {
344: String text = find.getText();
345: if (text.length() != 0) {
346: // don't beep if not found.
347: // subsequent beeps are very
348: // annoying when backspacing an
349: // invalid search string.
350: if (regexp.isSelected()) {
351: // reverse regexp search
352: // not supported yet, so
353: // 'simulate' with restart
354: timerIncrementalSearch(0, false);
355: } else {
356: int start;
357: JEditTextArea textArea = view.getTextArea();
358: Selection s = textArea
359: .getSelectionAtOffset(textArea
360: .getCaretPosition());
361: if (s == null)
362: start = textArea.getCaretPosition();
363: else
364: start = s.getStart();
365: timerIncrementalSearch(start, true);
366: }
367: }
368: }
369: } //}}}
370:
371: //{{{ changedUpdate() method
372: public void changedUpdate(DocumentEvent evt) {
373: }
374: //}}}
375: } //}}}
376:
377: //{{{ KeyHandler class
378: class KeyHandler extends KeyAdapter {
379: public void keyPressed(KeyEvent evt) {
380: switch (evt.getKeyCode()) {
381: case KeyEvent.VK_ESCAPE:
382: if (temp) {
383: view.removeToolBar(SearchBar.this );
384: }
385: evt.consume();
386: view.getEditPane().focusOnTextArea();
387: break;
388: case KeyEvent.VK_ENTER:
389: if (evt.isShiftDown()) {
390: evt.consume();
391: find(true);
392: }
393: break;
394: }
395: }
396: } //}}}
397:
398: //{{{ FocusHandler class
399: class FocusHandler extends FocusAdapter {
400: public void focusLost(FocusEvent e) {
401: getField().addCurrentToHistory();
402: }
403: } //}}}
404:
405: //}}}
406: }
|