001: /*
002: * @(#)FirefoxSearchBar.java 10/11/2005
003: *
004: * Copyright 2002 - 2005 JIDE Software Inc. All rights reserved.
005: */
006: package com.jidesoft.swing;
007:
008: import com.jidesoft.plaf.UIDefaultsLookup;
009: import com.jidesoft.swing.event.SearchableEvent;
010: import com.jidesoft.swing.event.SearchableListener;
011:
012: import javax.swing.*;
013: import javax.swing.event.DocumentEvent;
014: import javax.swing.event.DocumentListener;
015: import java.awt.*;
016: import java.awt.event.*;
017: import java.util.Locale;
018:
019: /**
020: * <code>SearchableBar</code> is a convenient component to enable searching feature
021: * for components. As long as the component support <code>Searchable</code> feature,
022: * it can work with <code>SearchableBar</code>.
023: * <p/>
024: * Different from <code>Searchable</code> feature which uses a small popup window to
025: * allow user typing in the searching text, <code>SearchableBar</code> provides a full-size panel.
026: * Although they both pretty provide the same set of features, they should be used in
027: * different cases to achieve the most desirable result.
028: * <p/>
029: * First of all, <code>SearchableBar</code> is a lot bigger than <code>Searchable</code>'s popup
030: * and need more space on the screen. The component that installs <code>SearchableBar</code> should
031: * be large enough. In comparison, <code>Searchable</code> can be installed on components of any size
032: * as it's a floating popup.
033: * <p/>
034: * Secondly, <code>SearchableBar</code> can be set visible all the time or can be set visible by a
035: * keystroke and stay visible unless user explicitly hides it. If your user is not computer savvy,
036: * <code>SearchableBar</code> is more appropriate because user can see searching feature very easily.
037: * <code>SearchableBar</code> can also be a better replacement the traditional "Find" or "Search" dialog
038: * because <code>SearchableBar</code> doesn't block user input like modal dialog. In comparison,
039: * <code>Searchable</code>'s popup is very transient. Mouse clicks outside the popup will hide the popup.
040: * For computer savvy it is very helpful but it could be hard for non-computer savvy to "understand" it.
041: * A good example is IntelliJ IDEA heavily uses Searchable popup because the users are all Java developers. Firefox,
042: * on the other hand, uses SearchableBar because the users are just regular computer users.
043: * <p/>
044: * Although appearence wise, these two are very different, they both based on {@link Searchable} interface. So as developer,
045: * both are almost the same. <code>SearchableBar</code> based on <code>Searchable</code>. So if you have an interface of
046: * <code>Searchable</code>, all you need is to call
047: * <code><pre>
048: * SearchableBar.install(searchable, KeyStroke.getKeyStroke(KeyEvent.VK_F, KeyEvent.CTRL_DOWN_MASK), new SearchableBar.Installer() {
049: * public void openSearchBar(SearchableBar searchableBar) {
050: * // add code to show search bar
051: * }
052: * <p/>
053: * public void closeSearchBar(SearchableBar searchableBar) {
054: * // add code to close search bar
055: * }
056: * });
057: * </pre></code>
058: * Or if you want fully control the SearchableBar, you can create one using one of its constructors
059: * and add to wherever you want.
060: * <p/>
061: * There are a few options you can set on <code>SearchableBar</code>. You can set compact or full mode. Compact mode
062: * will only use icon for buttons v.s. full mode will use both icon and text for buttons. All buttons on the <code>SearchableBar</code>
063: * can be shown/hidden by using {@link #setVisibleButtons(int)} method. You can also set the text field background for mismatch by using
064: * {@link #setMismatchForeground(java.awt.Color)}.
065: * <p/>
066: */
067: public class SearchableBar extends JToolBar implements
068: SearchableProvider {
069: private Searchable _searchable;
070:
071: private JLabel _statusLabel;
072: private JTextField _textField;
073:
074: protected AbstractButton _closeButton;
075: protected AbstractButton _findPrevButton;
076: protected AbstractButton _findNextButton;
077: protected AbstractButton _highlightsButton;
078:
079: private AbstractButton _matchCaseCheckBox;
080: private AbstractButton _repeatCheckBox;
081:
082: public static final int SHOW_CLOSE = 0x1;
083: public static final int SHOW_NAVIGATION = 0x2;
084: public static final int SHOW_HIGHLIGHTS = 0x4;
085: public static final int SHOW_MATCHCASE = 0x8;
086: public static final int SHOW_REPEATS = 0x10;
087: public static final int SHOW_STATUS = 0x20;
088: public static final int SHOW_ALL = 0xFFFFFFFF;
089:
090: private int _visibleButtons = SHOW_ALL & ~SHOW_REPEATS; // default is show all but repeats
091: private boolean _compact;
092:
093: /**
094: * Creates a searchable bar.
095: *
096: * @param searchable
097: */
098: public SearchableBar(Searchable searchable) {
099: this (searchable, "", false);
100: }
101:
102: /**
103: * Creates a searchable bar in compact mode or full mode.
104: *
105: * @param searchable
106: */
107: public SearchableBar(Searchable searchable, boolean compact) {
108: this (searchable, "", compact);
109: }
110:
111: /**
112: * Creates a searchable bar with initial searching text and in compact mode or full mode.
113: *
114: * @param searchable
115: */
116: public SearchableBar(Searchable searchable, String initialText,
117: boolean compact) {
118: setFloatable(false);
119: setRollover(true);
120: _searchable = searchable;
121: _searchable.addSearchableListener(new SearchableListener() {
122: public void searchableEventFired(SearchableEvent e) {
123: if (e.getID() == SearchableEvent.SEARCHABLE_MODEL_CHANGE) {
124: highlightAllOrNext();
125: }
126: }
127: });
128: _searchable.setSearchableProvider(this );
129: _compact = compact;
130: initComponents(initialText);
131: }
132:
133: private void initComponents(String initialText) {
134: AbstractAction closeAction = new AbstractAction() {
135: public void actionPerformed(ActionEvent e) {
136: if (getInstaller() != null) {
137: getInstaller().closeSearchBar(SearchableBar.this );
138: }
139: }
140: };
141:
142: AbstractAction findNextAction = new AbstractAction() {
143: public void actionPerformed(ActionEvent e) {
144: _highlightsButton.setSelected(false);
145: String text = getSearchingText();
146: int cursor = _searchable.getCursor();
147: int found = _searchable.findNext(text);
148: if (found != -1 && _searchable.isRepeats()
149: && found <= cursor) {
150: select(found, text, false);
151: setStatus(
152: getResourceString("SearchableBar.reachedBottomRepeat"),
153: getImageIcon(SearchableBarIconsFactory.Buttons.REPEAT));
154: } else if (!_searchable.isRepeats() && found == -1) {
155: setStatus(
156: getResourceString("SearchableBar.reachedBottom"),
157: getImageIcon(SearchableBarIconsFactory.Buttons.ERROR));
158: } else if (found != -1) {
159: select(found, text, false);
160: clearStatus();
161: }
162: }
163: };
164:
165: AbstractAction findPrevAction = new AbstractAction() {
166: public void actionPerformed(ActionEvent e) {
167: _highlightsButton.setSelected(false);
168: String text = getSearchingText();
169: int cursor = _searchable.getCursor();
170: int found = _searchable.findPrevious(text);
171: if (found != -1 && _searchable.isRepeats()
172: && found >= cursor) {
173: select(found, text, false);
174: setStatus(
175: getResourceString("SearchableBar.reachedTopRepeat"),
176: getImageIcon(SearchableBarIconsFactory.Buttons.REPEAT));
177: } else if (!_searchable.isRepeats() && found == -1) {
178: setStatus(
179: getResourceString("SearchableBar.reachedTop"),
180: getImageIcon(SearchableBarIconsFactory.Buttons.ERROR));
181: } else if (found != -1) {
182: select(found, text, false);
183: clearStatus();
184: }
185: }
186: };
187:
188: _closeButton = createCloseButton(closeAction);
189: _findNextButton = createFindNextButton(findNextAction);
190: _findPrevButton = createFindPrevButton(findPrevAction);
191: _highlightsButton = createHighlightButton();
192: _matchCaseCheckBox = createMatchCaseButton();
193: _repeatCheckBox = createRepeatsButton();
194:
195: _statusLabel = new JLabel();
196:
197: //setup text field
198: _textField = createTextField();
199: _textField.addFocusListener(new FocusAdapter() {
200: @Override
201: public void focusGained(FocusEvent e) {
202: _textField.selectAll();
203: }
204: });
205: _textField.setColumns(13);
206: _textField.getDocument().addDocumentListener(
207: new DocumentListener() {
208: private Timer timer = new Timer(_searchable
209: .getSearchingDelay(), new ActionListener() {
210: public void actionPerformed(ActionEvent e) {
211: highlightAllOrNext();
212: }
213: });
214:
215: public void insertUpdate(DocumentEvent e) {
216: startTimer();
217: }
218:
219: public void removeUpdate(DocumentEvent e) {
220: startTimer();
221: }
222:
223: public void changedUpdate(DocumentEvent e) {
224: startTimer();
225: }
226:
227: void startTimer() {
228: if (_searchable.getSearchingDelay() > 0) {
229: if (timer.isRunning()) {
230: timer.restart();
231: } else {
232: timer.setRepeats(false);
233: timer.start();
234: }
235: } else {
236: highlightAllOrNext();
237: }
238: }
239: });
240: _textField.setText(initialText);
241:
242: _textField.registerKeyboardAction(findNextAction, KeyStroke
243: .getKeyStroke(KeyEvent.VK_DOWN, 0),
244: JComponent.WHEN_FOCUSED);
245: _textField.registerKeyboardAction(findNextAction, KeyStroke
246: .getKeyStroke(KeyEvent.VK_ENTER, 0),
247: JComponent.WHEN_FOCUSED);
248: _textField.registerKeyboardAction(findPrevAction, KeyStroke
249: .getKeyStroke(KeyEvent.VK_UP, 0),
250: JComponent.WHEN_FOCUSED);
251: _textField.registerKeyboardAction(closeAction, KeyStroke
252: .getKeyStroke(KeyEvent.VK_ESCAPE, 0),
253: JComponent.WHEN_FOCUSED);
254:
255: installComponents();
256:
257: int found = _searchable.findFromCursor(getSearchingText());
258: select(found, initialText, false);
259: }
260:
261: /**
262: * Creates the text field where user types the text to be searched.
263: *
264: * @return a text field.
265: */
266: protected JTextField createTextField() {
267: return new JTextField();
268: }
269:
270: /**
271: * Gets the underlying Searchable object.
272: *
273: * @return the Searchable object.
274: */
275: public Searchable getSearchable() {
276: return _searchable;
277: }
278:
279: /**
280: * Creates the close button. Subclass can override it to create your own close button.
281: *
282: * @param closeAction
283: * @return the close button.
284: */
285: protected AbstractButton createCloseButton(
286: AbstractAction closeAction) {
287: AbstractButton button = new JButton(
288: getImageIcon(SearchableBarIconsFactory.Buttons.CLOSE));
289: button.addActionListener(closeAction);
290: button.setRolloverEnabled(true);
291: button.setBorder(BorderFactory.createEmptyBorder());
292: button.setOpaque(false);
293: button.setRequestFocusEnabled(false);
294: button.setFocusable(false);
295: button
296: .setRolloverIcon(getImageIcon(SearchableBarIconsFactory.Buttons.CLOSE_ROLLOVER));
297: return button;
298: }
299:
300: /**
301: * Creates the find next button. Subclass can override it to create your own find next button.
302: *
303: * @param findNextAction
304: * @return the find next button.
305: */
306: protected AbstractButton createFindNextButton(
307: AbstractAction findNextAction) {
308: AbstractButton button = new JButton(_compact ? ""
309: : getResourceString("SearchableBar.findNext"),
310: getImageIcon(SearchableBarIconsFactory.Buttons.NEXT));
311: button
312: .setToolTipText(getResourceString("SearchableBar.findNext.tooltip"));
313: button.setMnemonic(getResourceString(
314: "SearchableBar.findNext.mnemonic").charAt(0));
315: button
316: .setRolloverIcon(getImageIcon(SearchableBarIconsFactory.Buttons.NEXT_ROLLOVER));
317: button
318: .setDisabledIcon(getImageIcon(SearchableBarIconsFactory.Buttons.NEXT_DISABLED));
319: button.setRequestFocusEnabled(false);
320: button.setFocusable(false);
321: button.addActionListener(findNextAction);
322: button.setEnabled(false);
323: return button;
324: }
325:
326: /**
327: * Creates the find prev button. Subclass can override it to create your own find prev button.
328: *
329: * @param findPrevAction
330: * @return the find prev button.
331: */
332: protected AbstractButton createFindPrevButton(
333: AbstractAction findPrevAction) {
334: AbstractButton button = new JButton(
335: _compact ? ""
336: : getResourceString("SearchableBar.findPrevious"),
337: getImageIcon(SearchableBarIconsFactory.Buttons.PREVIOUS));
338: button
339: .setToolTipText(getResourceString("SearchableBar.findPrevious.tooltip"));
340: button.setMnemonic(getResourceString(
341: "SearchableBar.findPrevious.mnemonic").charAt(0));
342: button
343: .setRolloverIcon(getImageIcon(SearchableBarIconsFactory.Buttons.PREVIOUS_ROLLOVER));
344: button
345: .setDisabledIcon(getImageIcon(SearchableBarIconsFactory.Buttons.PREVIOUS_DISABLED));
346: button.setRequestFocusEnabled(false);
347: button.setFocusable(false);
348: button.addActionListener(findPrevAction);
349: button.setEnabled(false);
350: return button;
351: }
352:
353: /**
354: * Creates the highlight button.
355: *
356: * @return the highlight button.
357: */
358: protected AbstractButton createHighlightButton() {
359: AbstractButton button = new JToggleButton(
360: _compact ? ""
361: : getResourceString("SearchableBar.highlights"),
362: getImageIcon(SearchableBarIconsFactory.Buttons.HIGHLIGHTS));
363: button
364: .setToolTipText(getResourceString("SearchableBar.highlights.tooltip"));
365: button.setMnemonic(getResourceString(
366: "SearchableBar.highlights.mnemonic").charAt(0));
367: button
368: .setSelectedIcon(getImageIcon(SearchableBarIconsFactory.Buttons.HIGHLIGHTS_SELECTED));
369: button
370: .setDisabledIcon(getImageIcon(SearchableBarIconsFactory.Buttons.HIGHLIGHTS_DISABLED));
371: button
372: .setRolloverIcon(getImageIcon(SearchableBarIconsFactory.Buttons.HIGHLIGHTS_ROLLOVER));
373: button
374: .setRolloverSelectedIcon(getImageIcon(SearchableBarIconsFactory.Buttons.HIGHLIGHTS_ROLLOVER_SELECTED));
375: button.setRequestFocusEnabled(false);
376: button.setFocusable(false);
377:
378: AbstractAction highlightAllAction = new AbstractAction() {
379: public void actionPerformed(ActionEvent e) {
380: highlightAllOrNext();
381: }
382: };
383:
384: button.addActionListener(highlightAllAction);
385: button.setEnabled(false);
386: return button;
387: }
388:
389: /**
390: * Creates the repeat button. By default it will return a JCheckBox.
391: * Subclass class can override it to return your own button or customize
392: * the button created by default as long as it can set underlying Searchable's repeats property.
393: *
394: * @return the repeat button.
395: */
396: protected AbstractButton createRepeatsButton() {
397: AbstractButton button = new JCheckBox(
398: getResourceString("SearchableBar.repeats"));
399: button.setMnemonic(getResourceString(
400: "SearchableBar.repeats.mnemonic").charAt(0));
401: button.setRequestFocusEnabled(false);
402: button.setFocusable(false);
403: button.setSelected(getSearchable().isRepeats());
404: button.addItemListener(new ItemListener() {
405: public void itemStateChanged(ItemEvent e) {
406: if (e.getSource() instanceof AbstractButton) {
407: getSearchable().setRepeats(
408: ((AbstractButton) e.getSource())
409: .isSelected());
410: }
411: }
412: });
413: return button;
414: }
415:
416: /**
417: * Creates the match case button. By default it will return a JCheckBox.
418: * Subclass class can override it to return your own button or customize
419: * the button created by default as long as it can set underlying Searchable's caseSensitive property.
420: *
421: * @return the match case button.
422: */
423: protected AbstractButton createMatchCaseButton() {
424: JCheckBox checkBox = new JCheckBox(
425: getResourceString("SearchableBar.matchCase"));
426: checkBox.setMnemonic(getResourceString(
427: "SearchableBar.matchCase.mnemonic").charAt(0));
428: checkBox.setRequestFocusEnabled(false);
429: checkBox.setFocusable(false);
430: checkBox.setSelected(getSearchable().isCaseSensitive());
431: checkBox.addItemListener(new ItemListener() {
432: public void itemStateChanged(ItemEvent e) {
433: if (e.getSource() instanceof AbstractButton) {
434: getSearchable().setCaseSensitive(
435: ((AbstractButton) e.getSource())
436: .isSelected());
437: highlightAllOrNext();
438: }
439: }
440: });
441: return checkBox;
442: }
443:
444: /**
445: * Adds the buttons to the SearchableBar. Subclass can override this method to rearrange the layout of those buttons.
446: */
447: protected void installComponents() {
448: setBorder(BorderFactory.createEtchedBorder());
449: setLayout(new JideBoxLayout(this , JideBoxLayout.X_AXIS));
450: add(Box.createHorizontalStrut(4), JideBoxLayout.FIX);
451: if ((_visibleButtons & SHOW_CLOSE) != 0) {
452: add(_closeButton);
453: add(Box.createHorizontalStrut(10));
454: }
455: // setup the label
456: JLabel label = new JLabel(
457: getResourceString("SearchableBar.find"));
458: label.setDisplayedMnemonic(getResourceString(
459: "SearchableBar.find.mnemonic").charAt(0));
460: label.setLabelFor(_textField);
461: add(label);
462: add(Box.createHorizontalStrut(2), JideBoxLayout.FIX);
463: add(JideSwingUtilities.createCenterPanel(_textField),
464: JideBoxLayout.FIX);
465: add(Box.createHorizontalStrut(2), JideBoxLayout.FIX);
466: if ((_visibleButtons & SHOW_NAVIGATION) != 0) {
467: add(_findNextButton);
468: add(_findPrevButton);
469: }
470: if ((_visibleButtons & SHOW_HIGHLIGHTS) != 0) {
471: add(_highlightsButton);
472: }
473: if ((_visibleButtons & SHOW_MATCHCASE) != 0) {
474: add(_matchCaseCheckBox);
475: }
476: if ((_visibleButtons & SHOW_REPEATS) != 0) {
477: add(_repeatCheckBox);
478: }
479: if ((_visibleButtons & SHOW_STATUS) != 0) {
480: add(Box.createHorizontalStrut(24));
481: add(_statusLabel, JideBoxLayout.VARY);
482: }
483: add(Box.createHorizontalStrut(6), JideBoxLayout.FIX);
484: }
485:
486: private void highlightAllOrNext() {
487: if (_highlightsButton.isSelected()) {
488: highlighAll();
489: } else {
490: highlightNext();
491: }
492: }
493:
494: private void highlighAll() {
495: String text = getSearchingText();
496: if (text == null || text.length() == 0) {
497: return;
498: }
499: boolean old = _searchable.isRepeats();
500: _searchable.setRepeats(false);
501: int index = _searchable.findFirst(text);
502: if (index != -1) {
503: _searchable.setSelectedIndex(index, false); // clear side effect of ctrl-a will select all items
504: _searchable.setCursor(index); // as setSelectedIndex is used directly, we have to manually set the cursor value.
505: _findNextButton.setEnabled(true);
506: _findPrevButton.setEnabled(true);
507: _highlightsButton.setEnabled(true);
508: clearStatus();
509: } else {
510: select(-1, text, false);
511: _findNextButton.setEnabled(false);
512: _findPrevButton.setEnabled(false);
513: _highlightsButton.setEnabled(false);
514: setStatus(
515: getResourceString("SearchableBar.notFound"),
516: getImageIcon(SearchableBarIconsFactory.Buttons.ERROR));
517: }
518:
519: int firstIndex = -1;
520:
521: while (index != -1) {
522: int newIndex = _searchable.findNext(text);
523: if (index == newIndex) {
524: index = -1;
525: } else {
526: index = newIndex;
527: }
528: if (index != -1) {
529: if (firstIndex == -1) {
530: firstIndex = index;
531: }
532: select(index, text, true);
533: }
534: }
535: // now select the first one
536: if (firstIndex != -1) {
537: select(firstIndex, text, true);
538: }
539:
540: _searchable.setRepeats(old);
541: _searchable.setCursor(0);
542: }
543:
544: private void highlightNext() {
545: String text = getSearchingText();
546: if (text == null || text.length() == 0) {
547: _findNextButton.setEnabled(false);
548: _findPrevButton.setEnabled(false);
549: _highlightsButton.setEnabled(false);
550: clearStatus();
551: return;
552: }
553: int found = _searchable.findFromCursor(text);
554: if (found == -1) {
555: select(-1, "", false);
556: _findNextButton.setEnabled(false);
557: _findPrevButton.setEnabled(false);
558: _highlightsButton.setEnabled(false);
559: setStatus(
560: getResourceString("SearchableBar.notFound"),
561: getImageIcon(SearchableBarIconsFactory.Buttons.ERROR));
562: } else {
563: select(found, text, false);
564: _findNextButton.setEnabled(true);
565: _findPrevButton.setEnabled(true);
566: _highlightsButton.setEnabled(true);
567: clearStatus();
568: }
569: }
570:
571: private void clearStatus() {
572: _statusLabel.setIcon(null);
573: _statusLabel.setText("");
574: _textField.setBackground(UIDefaultsLookup
575: .getColor("TextField.background"));
576: }
577:
578: private void setStatus(String message, Icon icon) {
579: _statusLabel.setIcon(icon);
580: _statusLabel.setText(message);
581: }
582:
583: /**
584: * Makes the search field having focus.
585: */
586: public void focusSearchField() {
587: if (_textField != null) {
588: _textField.requestFocus();
589: }
590: }
591:
592: protected void select(int index, String searchingText,
593: boolean incremental) {
594: if (index != -1) {
595: _searchable.setSelectedIndex(index, incremental);
596: _searchable.setCursor(index);
597: _textField.setBackground(UIDefaultsLookup
598: .getColor("TextField.background"));
599: } else {
600: _searchable.setSelectedIndex(-1, false);
601: _textField.setBackground(getMismatchBackground());
602: }
603: _searchable.firePropertyChangeEvent(searchingText);
604: if (index != -1) {
605: Object element = _searchable.getElementAt(index);
606: _searchable.fireSearchableEvent(new SearchableEvent(
607: _searchable, SearchableEvent.SEARCHABLE_MATCH,
608: searchingText, element, _searchable
609: .convertElementToString(element)));
610: } else {
611: _searchable.fireSearchableEvent(new SearchableEvent(
612: _searchable, SearchableEvent.SEARCHABLE_NOMATCH,
613: searchingText));
614: }
615: }
616:
617: public String getSearchingText() {
618: return _textField != null ? _textField.getText() : "";
619: }
620:
621: public boolean isPassive() {
622: return false;
623: }
624:
625: final private static Color DEFAULT_MISMATCH_BACKGROUND = new Color(
626: 255, 85, 85);
627: private Color _mismatchBackground;
628:
629: /**
630: * Sets the background for mismatch.
631: *
632: * @param mismatchBackground
633: */
634: public void setMismatchForeground(Color mismatchBackground) {
635: _mismatchBackground = mismatchBackground;
636: }
637:
638: /**
639: * Gets the background color when the searching text doesn't match with any of the elements in the component.
640: *
641: * @return the forground color for mismatch. If you never call
642: * {@link #setMismatchForeground(java.awt.Color)}. red color will be used.
643: */
644: public Color getMismatchBackground() {
645: if (_mismatchBackground == null) {
646: return DEFAULT_MISMATCH_BACKGROUND;
647: } else {
648: return _mismatchBackground;
649: }
650: }
651:
652: private Installer _installer;
653:
654: /**
655: * The installer for SearchableBar.
656: */
657: public interface Installer {
658: /**
659: * Called to show the SearchableBar so that user can see it.
660: * <p/>
661: * For example, if you want to add a SearchableBar to the south of a JTextArea,
662: * you should add JTextArea to the CENTER of a BorderLayout panel. In this method,
663: * you add the SearchableBar to the SOUTH of the same BorderLayout panel.
664: *
665: * @param searchableBar
666: */
667: public void openSearchBar(SearchableBar searchableBar);
668:
669: /**
670: * Called to hide the SearchableBar.
671: *
672: * @param searchableBar
673: */
674: public void closeSearchBar(SearchableBar searchableBar);
675: }
676:
677: public Installer getInstaller() {
678: return _installer;
679: }
680:
681: /**
682: * Sets the installer. Installer is responsible for the installation and uninstallation of SearchableBar.
683: *
684: * @param installer
685: */
686: public void setInstaller(Installer installer) {
687: _installer = installer;
688: }
689:
690: /**
691: * Installs a SearchableBar on a component. This is just a convenient method for you, you can install it in your own code.
692: * See below for the actual code we used in this method.
693: * <p/>
694: * <code><pre>
695: * final SearchableBar searchableBar = new SearchableBar(searchable);
696: * searchableBar.setInstaller(installer);
697: * ((JComponent) searchable.getComponent()).registerKeyboardAction(new AbstractAction() {
698: * public void actionPerformed(ActionEvent e) {
699: * searchableBar.getInstaller().openSearchBar(searchableBar);
700: * searchableBar.focusSearchField();
701: * }
702: * }, keyStroke, JComponent.WHEN_FOCUSED);
703: * return searchableBar;
704: * </pre></code>
705: *
706: * @param searchable
707: * @param keyStroke
708: * @param installer
709: * @return the SearchableBar that is created.
710: */
711: public static SearchableBar install(Searchable searchable,
712: KeyStroke keyStroke, Installer installer) {
713: final SearchableBar searchableBar = new SearchableBar(
714: searchable);
715: searchableBar.setInstaller(installer);
716: ((JComponent) searchable.getComponent())
717: .registerKeyboardAction(new AbstractAction() {
718: public void actionPerformed(ActionEvent e) {
719: searchableBar.getInstaller().openSearchBar(
720: searchableBar);
721: searchableBar.focusSearchField();
722: }
723: }, keyStroke, JComponent.WHEN_FOCUSED);
724: return searchableBar;
725: }
726:
727: @Override
728: public void processKeyEvent(KeyEvent e) {
729: }
730:
731: public int getVisibleButtons() {
732: return _visibleButtons;
733: }
734:
735: /**
736: * Sets visible buttons on <code>SearchableBar</code>.
737: *
738: * @param visibleButtons bit-wise all of several constants. Valid constants are
739: * <ul>
740: * <li> {@link #SHOW_CLOSE} - the close button
741: * <li> {@link #SHOW_NAVIGATION} - the find next and find previous buttons
742: * <li> {@link #SHOW_HIGHLIGHTS} - highlights all button
743: * <li> {@link #SHOW_MATCHCASE} - match case button
744: * <li> {@link #SHOW_REPEATS} - repeats button
745: * <li> {@link #SHOW_STATUS} - status area
746: * <li> {@link #SHOW_ALL} - all buttons
747: * </ul>
748: * For example, if you want to show only close and highlighs all button, call
749: * <code>setVisibleButtons(SearchableBar.SHOW_CLOSE | SearchableBar.SHOW_HIGHLIGHTS)</code>.
750: */
751: public void setVisibleButtons(int visibleButtons) {
752: _visibleButtons = visibleButtons;
753: removeAll();
754: installComponents();
755: revalidate();
756: repaint();
757: }
758:
759: /**
760: * Checks if <code>SearchableBar</code> is in compact mode.
761: *
762: * @return true if in compact. Otherwise, false.
763: */
764: public boolean isCompact() {
765: return _compact;
766: }
767:
768: /**
769: * Sets the <code>SearchableBar</code> to compact or full mode. In compact mode
770: * will only use icon for buttons v.s. full mode will use both icon and text for buttons.
771: *
772: * @param compact
773: */
774: public void setCompact(boolean compact) {
775: _compact = compact;
776: _findNextButton.setText(_compact ? ""
777: : getResourceString("SearchableBar.findNext"));
778: _highlightsButton.setText(_compact ? ""
779: : getResourceString("SearchableBar.highlights"));
780: _findPrevButton.setText(_compact ? ""
781: : getResourceString("SearchableBar.findPrevious"));
782: }
783:
784: /**
785: * Gets the icons from SearchableBarIconsFactory. Subclass can override this method if they want to provide their own icon.
786: *
787: * @param name
788: * @return the icon of the specified name.
789: */
790: protected ImageIcon getImageIcon(String name) {
791: return SearchableBarIconsFactory.getImageIcon(name);
792: }
793:
794: /**
795: * Gets the localized string from resource bundle. Subclass can override it to provide its own string.
796: * Available keys are defined in swing.properties that begin with "SearchableBar.".
797: *
798: * @param key
799: * @return the localized string.
800: */
801: protected String getResourceString(String key) {
802: return Resource.getResourceBundle(Locale.getDefault())
803: .getString(key);
804: }
805: }
|