All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.jidesoft.swing.SearchableBar Maven / Gradle / Ivy

There is a newer version: 3.6.18
Show newest version
/*
 * @(#)SearchableBar.java 10/11/2005
 *
 * Copyright 2002 - 2005 JIDE Software Inc. All rights reserved.
 */
package com.jidesoft.swing;

import com.jidesoft.plaf.UIDefaultsLookup;
import com.jidesoft.popup.JidePopup;
import com.jidesoft.swing.event.SearchableEvent;
import com.jidesoft.swing.event.SearchableListener;

import javax.swing.*;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;

/**
 * SearchableBar is a convenient component to enable searching feature for components. As long as the
 * component support Searchable feature, it can work with SearchableBar.
 * 

* Different from Searchable feature which uses a small popup window to allow user typing in the searching * text, SearchableBar provides a full-size panel. Although they both pretty provide the same set of * features, they should be used in different cases to achieve the most desirable result. *

* First of all, SearchableBar is a lot bigger than Searchable's popup and need more space on * the screen. The component that installs SearchableBar should be large enough. In comparison, * Searchable can be installed on components of any size as it's a floating popup. *

* Secondly, SearchableBar can be set visible all the time or can be set visible by a keystroke and stay * visible unless user explicitly hides it. If your user is not computer savvy, SearchableBar is more * appropriate because user can see searching feature very easily. SearchableBar can also be a better * replacement the traditional "Find" or "Search" dialog because SearchableBar doesn't block user input * like modal dialog. In comparison, Searchable's popup is very transient. Mouse clicks outside the popup * will hide the popup. For computer savvy it is very helpful but it could be hard for non-computer savvy to * "understand" it. A good example is IntelliJ IDEA heavily uses Searchable popup because the users are all Java * developers. Firefox, on the other hand, uses SearchableBar because the users are just regular computer users. *

* Although appearance wise, these two are very different, they both based on {@link Searchable} interface. So as * developer, both are almost the same. SearchableBar based on Searchable. So if you have an * interface of Searchable, all you need is to call *

 * SearchableBar.install(searchable, KeyStroke.getKeyStroke(KeyEvent.VK_F,
 * KeyEvent.CTRL_DOWN_MASK),
 * new SearchableBar.Installer() {
 *     public void openSearchBar(SearchableBar searchableBar) {
 *        // add code to show search bar
 *     }
 * 

* public void closeSearchBar(SearchableBar searchableBar) { * // add code to close search bar * } * }); *

* Or if you want fully control the SearchableBar, you can create one using one of its constructors and add to wherever * you want. *

* There are a few options you can set on SearchableBar. You can set compact or full mode. Compact mode * will only use icon for buttons v.s. full mode will use both icon and text for buttons. All buttons on the * SearchableBar can be shown/hidden by using {@link #setVisibleButtons(int)} method. You can also set the * text field background for mismatch by using {@link #setMismatchForeground(java.awt.Color)}. *

*/ public class SearchableBar extends JToolBar implements SearchableProvider { private Searchable _searchable; protected JLabel _statusLabel; protected JLabel _leadingLabel; protected JTextField _textField; protected JComboBox _comboBox; protected AbstractButton _closeButton; protected AbstractButton _findPrevButton; protected AbstractButton _findNextButton; protected AbstractButton _highlightsButton; protected AbstractButton _matchCaseCheckBox; protected AbstractButton _wholeWordsCheckBox; protected AbstractButton _repeatCheckBox; public static final int SHOW_CLOSE = 0x1; public static final int SHOW_NAVIGATION = 0x2; public static final int SHOW_HIGHLIGHTS = 0x4; public static final int SHOW_MATCHCASE = 0x8; public static final int SHOW_REPEATS = 0x10; public static final int SHOW_STATUS = 0x20; public static final int SHOW_WHOLE_WORDS = 0x40; public static final int SHOW_ALL = 0xFFFFFFFF; public static final String PROPERTY_MAX_HISTORY_LENGTH = "maxHistoryLength"; private int _visibleButtons = ~SHOW_REPEATS; // default is show all but repeats private boolean _compact; private boolean _showMatchCount = false; private JidePopup _messagePopup; private MouseMotionListener _mouseMotionListener; private KeyListener _keyListener; private List _searchHistory; private int _maxHistoryLength = 0; /** * Creates a searchable bar. * * @param searchable the searchable */ public SearchableBar(Searchable searchable) { this(searchable, "", false); } /** * Creates a searchable bar in compact mode or full mode. * * @param searchable the searchable * @param compact the flag indicating compact mode or full mode */ public SearchableBar(Searchable searchable, boolean compact) { this(searchable, "", compact); } /** * Creates a searchable bar with initial searching text and in compact mode or full mode. * * @param searchable the searchable * @param initialText the initial text * @param compact the flag indicating compact mode or full mode */ public SearchableBar(Searchable searchable, String initialText, boolean compact) { setFloatable(false); setRollover(true); _searchable = searchable; _searchable.addSearchableListener(new SearchableListener() { public void searchableEventFired(SearchableEvent e) { if (e.getID() == SearchableEvent.SEARCHABLE_MODEL_CHANGE && _searchable.getSearchingText() != null && _searchable.getSearchingText().length() != 0) { highlightAllOrNext(); } } }); _searchable.setSearchableProvider(this); _compact = compact; initComponents(initialText); } private void initComponents(String initialText) { final AbstractAction closeAction = new AbstractAction() { private static final long serialVersionUID = -2245391247321137224L; public void actionPerformed(ActionEvent e) { if (getInstaller() != null) { getInstaller().closeSearchBar(SearchableBar.this); } } }; final AbstractAction findNextAction = new AbstractAction() { private static final long serialVersionUID = -5263488798121831276L; public void actionPerformed(ActionEvent e) { _highlightsButton.setSelected(false); String text = getSearchingText(); addSearchingTextToHistory(text); int cursor = _searchable.getSelectedIndex(); _searchable.setCursor(cursor); int found = _searchable.findNext(text); if (found == cursor) { select(found, text, false); clearStatus(); } else if (found != -1 && _searchable.isRepeats() && found <= cursor) { select(found, text, false); setStatus(getResourceString("SearchableBar.reachedBottomRepeat"), getImageIcon(SearchableBarIconsFactory.Buttons.REPEAT)); } else if (!_searchable.isRepeats() && found == -1) { setStatus(getResourceString("SearchableBar.reachedBottom"), getImageIcon(SearchableBarIconsFactory.Buttons.ERROR)); } else if (found != -1) { select(found, text, false); clearStatus(); if (_searchable.getSearchingDelay() < 0) { // never updated the count so we update here highlightAllOrNext(); } } } }; final AbstractAction findPrevAction = new AbstractAction() { private static final long serialVersionUID = -2534332227053620232L; public void actionPerformed(ActionEvent e) { _highlightsButton.setSelected(false); String text = getSearchingText(); addSearchingTextToHistory(text); int cursor = _searchable.getSelectedIndex(); _searchable.setCursor(cursor); int found = _searchable.findPrevious(text); if (found == cursor) { select(found, text, false); clearStatus(); } else if (found != -1 && _searchable.isRepeats() && found >= cursor) { select(found, text, false); setStatus(getResourceString("SearchableBar.reachedTopRepeat"), getImageIcon(SearchableBarIconsFactory.Buttons.REPEAT)); } else if (!_searchable.isRepeats() && found == -1) { setStatus(getResourceString("SearchableBar.reachedTop"), getImageIcon(SearchableBarIconsFactory.Buttons.ERROR)); } else if (found != -1) { select(found, text, false); clearStatus(); } } }; _mouseMotionListener = new MouseMotionListener() { public void mouseMoved(MouseEvent e) { hideMessage(); } public void mouseDragged(MouseEvent e) { } }; _keyListener = new KeyListener() { public void keyTyped(KeyEvent e) { hideMessage(); } public void keyPressed(KeyEvent e) { } public void keyReleased(KeyEvent e) { } }; _closeButton = createCloseButton(closeAction); _findNextButton = createFindNextButton(findNextAction); _findPrevButton = createFindPrevButton(findPrevAction); _highlightsButton = createHighlightButton(); _matchCaseCheckBox = createMatchCaseButton(); _wholeWordsCheckBox = createWholeWordsButton(); _repeatCheckBox = createRepeatsButton(); _statusLabel = new JLabel(); //setup text field _textField = createTextField(); _textField.addFocusListener(new FocusAdapter() { @Override public void focusGained(FocusEvent e) { _textField.selectAll(); } }); _textField.setColumns(13); DocumentListener listener = new DocumentListener() { private Timer timer = new Timer(_searchable.getSearchingDelay(), new ActionListener() { public void actionPerformed(ActionEvent e) { highlightAllOrNext(); } }); public void insertUpdate(DocumentEvent e) { startTimer(); } public void removeUpdate(DocumentEvent e) { startTimer(); } public void changedUpdate(DocumentEvent e) { startTimer(); } void startTimer() { if (_searchable.getSearchingDelay() > 0) { if (timer.isRunning()) { timer.restart(); } else { timer.setRepeats(false); timer.start(); } } else if (_searchable.getSearchingDelay() == 0){ highlightAllOrNext(); } } }; _textField.getDocument().addDocumentListener(listener); _textField.setText(initialText); _textField.registerKeyboardAction(findNextAction, KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), JComponent.WHEN_FOCUSED); _textField.registerKeyboardAction(findNextAction, KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), JComponent.WHEN_FOCUSED); _textField.registerKeyboardAction(findPrevAction, KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), JComponent.WHEN_FOCUSED); _textField.registerKeyboardAction(closeAction, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_FOCUSED); _comboBox = createComboBox(); if (_comboBox.getEditor().getEditorComponent() instanceof JTextField) { ((JTextField) _comboBox.getEditor().getEditorComponent()).getDocument().addDocumentListener(listener); registerKeyboardActions(closeAction, findNextAction, findPrevAction); _comboBox.addPopupMenuListener(new PopupMenuListener() { @Override public void popupMenuWillBecomeVisible(PopupMenuEvent e) { unregisterKeyboardActions(); } @Override public void popupMenuWillBecomeInvisible(PopupMenuEvent e) { registerKeyboardActions(closeAction, findNextAction, findPrevAction); } @Override public void popupMenuCanceled(PopupMenuEvent e) { registerKeyboardActions(closeAction, findNextAction, findPrevAction); } }); } _comboBox.setSelectedItem(initialText); _comboBox.setPreferredSize(_textField.getPreferredSize()); installComponents(); int found = _searchable.findFromCursor(getSearchingText()); if (initialText.length() != 0 && found == -1) { select(found, initialText, false); } } private void registerKeyboardActions(AbstractAction closeAction, AbstractAction findNextAction, AbstractAction findPrevAction) { ((JTextField) _comboBox.getEditor().getEditorComponent()).registerKeyboardAction(findNextAction, KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), JComponent.WHEN_FOCUSED); ((JTextField) _comboBox.getEditor().getEditorComponent()).registerKeyboardAction(findNextAction, KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), JComponent.WHEN_FOCUSED); ((JTextField) _comboBox.getEditor().getEditorComponent()).registerKeyboardAction(findPrevAction, KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), JComponent.WHEN_FOCUSED); ((JTextField) _comboBox.getEditor().getEditorComponent()).registerKeyboardAction(closeAction, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_FOCUSED); } private void unregisterKeyboardActions() { ((JTextField) _comboBox.getEditor().getEditorComponent()).unregisterKeyboardAction(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0)); ((JTextField) _comboBox.getEditor().getEditorComponent()).unregisterKeyboardAction(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0)); ((JTextField) _comboBox.getEditor().getEditorComponent()).unregisterKeyboardAction(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0)); ((JTextField) _comboBox.getEditor().getEditorComponent()).unregisterKeyboardAction(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0)); } /** * Creates the text field where user types the text to be searched. * * @return a text field. */ protected JTextField createTextField() { return new JTextField(); } /** * Creates the combo box where user types the text to be searched. * * @return a combo box. * @since 3.4.1 */ protected JComboBox createComboBox() { JComboBox comboBox = new JComboBox(); comboBox.setEditable(true); return comboBox; } /** * Gets the underlying Searchable object. * * @return the Searchable object. */ public Searchable getSearchable() { return _searchable; } /** * Creates the close button. Subclass can override it to create your own close button. * * @param closeAction the close action * * @return the close button. */ protected AbstractButton createCloseButton(AbstractAction closeAction) { AbstractButton button = new JButton(getImageIcon(SearchableBarIconsFactory.Buttons.CLOSE)); button.addActionListener(closeAction); button.setRolloverEnabled(true); button.setBorder(BorderFactory.createEmptyBorder()); button.setOpaque(false); button.setRequestFocusEnabled(false); button.setFocusable(false); button.setRolloverIcon(getImageIcon(SearchableBarIconsFactory.Buttons.CLOSE_ROLLOVER)); return button; } /** * Creates the find next button. Subclass can override it to create your own find next button. * * @param findNextAction the find next action * * @return the find next button. */ protected AbstractButton createFindNextButton(AbstractAction findNextAction) { AbstractButton button = new JButton(_compact ? "" : getResourceString("SearchableBar.findNext"), getImageIcon(SearchableBarIconsFactory.Buttons.NEXT)); button.setToolTipText(getResourceString("SearchableBar.findNext.tooltip")); button.setMnemonic(getResourceString("SearchableBar.findNext.mnemonic").charAt(0)); button.setRolloverIcon(getImageIcon(SearchableBarIconsFactory.Buttons.NEXT_ROLLOVER)); button.setDisabledIcon(getImageIcon(SearchableBarIconsFactory.Buttons.NEXT_DISABLED)); button.setRequestFocusEnabled(false); button.setFocusable(false); button.addActionListener(findNextAction); button.setEnabled(false); return button; } /** * Creates the find prev button. Subclass can override it to create your own find prev button. * * @param findPrevAction the find previous action * * @return the find prev button. */ protected AbstractButton createFindPrevButton(AbstractAction findPrevAction) { AbstractButton button = new JButton(_compact ? "" : getResourceString("SearchableBar.findPrevious"), getImageIcon(SearchableBarIconsFactory.Buttons.PREVIOUS)); button.setToolTipText(getResourceString("SearchableBar.findPrevious.tooltip")); button.setMnemonic(getResourceString("SearchableBar.findPrevious.mnemonic").charAt(0)); button.setRolloverIcon(getImageIcon(SearchableBarIconsFactory.Buttons.PREVIOUS_ROLLOVER)); button.setDisabledIcon(getImageIcon(SearchableBarIconsFactory.Buttons.PREVIOUS_DISABLED)); button.setRequestFocusEnabled(false); button.setFocusable(false); button.addActionListener(findPrevAction); button.setEnabled(false); return button; } /** * Creates the highlight button. * * @return the highlight button. */ protected AbstractButton createHighlightButton() { AbstractButton button = new JToggleButton(_compact ? "" : getResourceString("SearchableBar.highlights"), getImageIcon(SearchableBarIconsFactory.Buttons.HIGHLIGHTS)); button.setToolTipText(getResourceString("SearchableBar.highlights.tooltip")); button.setMnemonic(getResourceString("SearchableBar.highlights.mnemonic").charAt(0)); button.setSelectedIcon(getImageIcon(SearchableBarIconsFactory.Buttons.HIGHLIGHTS_SELECTED)); button.setDisabledIcon(getImageIcon(SearchableBarIconsFactory.Buttons.HIGHLIGHTS_DISABLED)); button.setRolloverIcon(getImageIcon(SearchableBarIconsFactory.Buttons.HIGHLIGHTS_ROLLOVER)); button.setRolloverSelectedIcon(getImageIcon(SearchableBarIconsFactory.Buttons.HIGHLIGHTS_ROLLOVER_SELECTED)); button.setRequestFocusEnabled(false); button.setFocusable(false); AbstractAction highlightAllAction = new AbstractAction() { private static final long serialVersionUID = 5170786863522331175L; public void actionPerformed(ActionEvent e) { addSearchingTextToHistory(getSearchingText()); highlightAllOrNext(); } }; button.addActionListener(highlightAllAction); button.setEnabled(false); return button; } /** * Creates the repeat button. By default it will return a JCheckBox. Subclass class can override it to return your * own button or customize the button created by default as long as it can set underlying Searchable's repeats * property. * * @return the repeat button. */ protected AbstractButton createRepeatsButton() { AbstractButton button = new JCheckBox(getResourceString("SearchableBar.repeats")); button.setMnemonic(getResourceString("SearchableBar.repeats.mnemonic").charAt(0)); button.setRequestFocusEnabled(false); button.setFocusable(false); button.setSelected(getSearchable().isRepeats()); button.addItemListener(new ItemListener() { public void itemStateChanged(ItemEvent e) { if (e.getSource() instanceof AbstractButton) { getSearchable().setRepeats(((AbstractButton) e.getSource()).isSelected()); } } }); button.setOpaque(false); return button; } /** * Creates the match case button. By default it will return a JCheckBox. Subclass class can override it to return * your own button or customize the button created by default as long as it can set underlying Searchable's * caseSensitive property. * * @return the match case button. */ protected AbstractButton createMatchCaseButton() { JCheckBox checkBox = new JCheckBox(getResourceString("SearchableBar.matchCase")); checkBox.setMnemonic(getResourceString("SearchableBar.matchCase.mnemonic").charAt(0)); checkBox.setRequestFocusEnabled(false); checkBox.setFocusable(false); checkBox.setSelected(getSearchable().isCaseSensitive()); checkBox.addItemListener(new ItemListener() { public void itemStateChanged(ItemEvent e) { if (e.getSource() instanceof AbstractButton) { getSearchable().setCaseSensitive(((AbstractButton) e.getSource()).isSelected()); addSearchingTextToHistory(getSearchingText()); highlightAllOrNext(); } } }); checkBox.setOpaque(false); return checkBox; } /** * Creates the whole words button. By default it will return a JCheckBox. Subclass class can override it to return * your own button or customize the button created by default as long as it can set underlying Searchable's * toEnd property. * * @return the whole words button. * @since 3.5.2 */ protected AbstractButton createWholeWordsButton() { JCheckBox checkBox = new JCheckBox(getResourceString("SearchableBar.wholeWords")); checkBox.setMnemonic(getResourceString("SearchableBar.wholeWords.mnemonic").charAt(0)); checkBox.setRequestFocusEnabled(false); checkBox.setFocusable(false); if (getSearchable() instanceof WholeWordsSupport) { checkBox.setSelected(((WholeWordsSupport) getSearchable()).isWholeWords()); } else { checkBox.setSelected(false); checkBox.setEnabled(false); } checkBox.addItemListener(new ItemListener() { public void itemStateChanged(ItemEvent e) { if (e.getSource() instanceof AbstractButton && getSearchable() instanceof WholeWordsSupport) { ((WholeWordsSupport) getSearchable()).setWholeWords(((AbstractButton) e.getSource()).isSelected()); addSearchingTextToHistory(getSearchingText()); highlightAllOrNext(); } } }); checkBox.setOpaque(false); return checkBox; } /** * Adds the buttons to the SearchableBar. Subclass can override this method to rearrange the layout of those * buttons. */ protected void installComponents() { setBorder(BorderFactory.createEtchedBorder()); setLayout(new JideBoxLayout(this, JideBoxLayout.X_AXIS)); add(Box.createHorizontalStrut(4), JideBoxLayout.FIX); if ((_visibleButtons & SHOW_CLOSE) != 0) { add(_closeButton); add(Box.createHorizontalStrut(10)); } // setup the label _leadingLabel = new JLabel(getResourceString("SearchableBar.find")); _leadingLabel.setDisplayedMnemonic(getResourceString("SearchableBar.find.mnemonic").charAt(0)); add(_leadingLabel); add(Box.createHorizontalStrut(2), JideBoxLayout.FIX); add(JideSwingUtilities.createCenterPanel(_textField), JideBoxLayout.FIX); add(JideSwingUtilities.createCenterPanel(_comboBox), JideBoxLayout.FIX); if (getMaxHistoryLength() == 0) { _leadingLabel.setLabelFor(_textField); _textField.setVisible(true); _comboBox.setVisible(false); } else { _leadingLabel.setLabelFor(_comboBox); _comboBox.setVisible(true); _textField.setVisible(false); } add(Box.createHorizontalStrut(2), JideBoxLayout.FIX); if ((_visibleButtons & SHOW_NAVIGATION) != 0) { add(_findNextButton); add(_findPrevButton); } if ((_visibleButtons & SHOW_HIGHLIGHTS) != 0) { add(_highlightsButton); } if ((_visibleButtons & SHOW_MATCHCASE) != 0) { add(_matchCaseCheckBox); add(Box.createHorizontalStrut(2)); } if ((_visibleButtons & SHOW_WHOLE_WORDS) != 0 && getSearchable() instanceof WholeWordsSupport) { add(_wholeWordsCheckBox); add(Box.createHorizontalStrut(2)); } if ((_visibleButtons & SHOW_REPEATS) != 0) { add(_repeatCheckBox); add(Box.createHorizontalStrut(2)); } if ((_visibleButtons & SHOW_STATUS) != 0) { add(Box.createHorizontalStrut(24)); add(_statusLabel, JideBoxLayout.VARY); } add(Box.createHorizontalStrut(6), JideBoxLayout.FIX); } /** * Get if the SearchableBar is highlighting all matches. *

* Even you set it to true, after the customer press previous or next button, this flag will be cleared. * * @return true if all matches are highlighted. Otherwise false. */ public boolean isHighlightAll() { return _highlightsButton.isSelected(); } /** * Set if the SearchableBar will highlight all matches. * * @param highlightAll the flag * * @see #isHighlightAll() */ public void setHighlightAll(boolean highlightAll) { _highlightsButton.setSelected(highlightAll); } private int _previousCursor = -1; private void highlightAllOrNext() { if (_highlightsButton.isSelected()) { _previousCursor = _searchable.getCurrentIndex(); highlightAll(); } else { if (_previousCursor >= 0) { _searchable.setCursor(_previousCursor); _searchable.setSelectedIndex(_previousCursor, false); } highlightNext(); } } private void highlightAll() { String text = getSearchingText(); if (text == null || text.length() == 0) { _findNextButton.setEnabled(false); _findPrevButton.setEnabled(false); _highlightsButton.setEnabled(false); select(-1, "", false); clearStatus(); return; } boolean old = _searchable.isRepeats(); _searchable.setRepeats(false); int index = _searchable.findFirst(text); if (index != -1) { _searchable.setSelectedIndex(index, false); // clear side effect of ctrl-a will select all items _searchable.setCursor(index); // as setSelectedIndex is used directly, we have to manually set the cursor value. _findNextButton.setEnabled(true); _findPrevButton.setEnabled(true); _highlightsButton.setEnabled(true); clearStatus(); } else { select(-1, text, false); _findNextButton.setEnabled(false); _findPrevButton.setEnabled(false); _highlightsButton.setEnabled(false); setStatus(getResourceString("SearchableBar.notFound"), getImageIcon(SearchableBarIconsFactory.Buttons.ERROR)); } _searchable.highlightAll(); _searchable.setRepeats(old); _searchable.setCursor(0); } private void highlightNext() { _searchable.cancelHighlightAll(); String text = getSearchingText(); if (text == null || text.length() == 0) { _findNextButton.setEnabled(false); _findPrevButton.setEnabled(false); _highlightsButton.setEnabled(false); select(-1, "", false); clearStatus(); return; } int found = _searchable.findFromCursor(text); if (found == -1) { select(-1, "", false); _findNextButton.setEnabled(false); _findPrevButton.setEnabled(false); _highlightsButton.setEnabled(false); setStatus(getResourceString("SearchableBar.notFound"), getImageIcon(SearchableBarIconsFactory.Buttons.ERROR)); } else { select(found, text, false); _findNextButton.setEnabled(true); _findPrevButton.setEnabled(true); _highlightsButton.setEnabled(true); clearStatus(); } } private void clearStatus() { _statusLabel.setIcon(null); _textField.setBackground(UIDefaultsLookup.getColor("TextField.background")); _comboBox.getEditor().getEditorComponent().setBackground(UIDefaultsLookup.getColor("TextField.background")); if (isShowMatchCount() && (_textField.getText().length() > 0 || (_comboBox.isVisible() && _comboBox.getEditor().getEditorComponent() instanceof JTextField && ((JTextField) _comboBox.getEditor().getEditorComponent()).getText().length() > 0))) { _statusLabel.setText(getSearchable().getMatchCount() + " " + getResourceString("SearchableBar.matches")); } else { _statusLabel.setText(""); } hideMessage(); } private void setStatus(String message, Icon icon) { _statusLabel.setIcon(icon); _statusLabel.setText(message); _statusLabel.setToolTipText(message); if (!_statusLabel.isShowing() || _statusLabel.getWidth() < 25) { showMessage(message); } } /** * Makes the search field having focus. */ public void focusSearchField() { if (_textField != null && _textField.isVisible()) { _textField.requestFocus(); } if (_comboBox != null && _comboBox.isVisible()) { _comboBox.requestFocus(); } } protected void select(int index, String searchingText, boolean incremental) { if (index != -1) { _searchable.setSelectedIndex(index, incremental); _searchable.setCursor(index, incremental); _textField.setBackground(UIDefaultsLookup.getColor("TextField.background")); _comboBox.getEditor().getEditorComponent().setBackground(UIDefaultsLookup.getColor("TextField.background")); } else { _searchable.setSelectedIndex(-1, false); _textField.setBackground(getMismatchBackground()); _comboBox.getEditor().getEditorComponent().setBackground(UIDefaultsLookup.getColor("TextField.background")); } _searchable.firePropertyChangeEvent(searchingText); if (index != -1) { Object element = _searchable.getElementAt(index); _searchable.fireSearchableEvent(new SearchableEvent(_searchable, SearchableEvent.SEARCHABLE_MATCH, searchingText, element, _searchable.convertElementToString(element))); } else { _searchable.fireSearchableEvent(new SearchableEvent(_searchable, SearchableEvent.SEARCHABLE_NOMATCH, searchingText)); } } /** * Gets the searching text. * * @return the searching text. */ public String getSearchingText() { if (_textField != null && _textField.isVisible()) { return _textField.getText(); } if (_comboBox != null && _comboBox.isVisible()) { Object item = _comboBox.getEditor().getItem(); return item == null ? "" : item.toString(); } return ""; } /** * Sets the searching text. * * @param searchingText the new searching text. */ public void setSearchingText(String searchingText) { if (_textField != null && _textField.isVisible()) { _textField.setText(searchingText); } if (_comboBox != null && _comboBox.isVisible()) { _comboBox.setSelectedItem(searchingText); } } /** * Returns false. * * @return false. */ public boolean isPassive() { return false; } final private static Color DEFAULT_MISMATCH_BACKGROUND = new Color(255, 85, 85); private Color _mismatchBackground; /** * Sets the background for mismatch. * * @param mismatchBackground the mismatch background */ public void setMismatchForeground(Color mismatchBackground) { _mismatchBackground = mismatchBackground; } /** * Gets the background color when the searching text doesn't match with any of the elements in the component. * * @return the foreground color for mismatch. If you never call {@link #setMismatchForeground(java.awt.Color)}. red * color will be used. */ public Color getMismatchBackground() { if (_mismatchBackground == null) { return DEFAULT_MISMATCH_BACKGROUND; } else { return _mismatchBackground; } } private Installer _installer; /** * Gets the search history. * * @return the search history. * @since 3.4.1 */ public String[] getSearchHistory() { return _searchHistory == null ? new String[0] : _searchHistory.toArray(new String[_searchHistory.size()]); } /** * Sets the search history. * * @param searchHistory the search history * @since 3.4.1 */ public void setSearchHistory(String[] searchHistory) { if (searchHistory == null || searchHistory.length == 0) { _searchHistory = null; } else { _searchHistory = new ArrayList(); _searchHistory.addAll(Arrays.asList(searchHistory)); } DefaultComboBoxModel model = new DefaultComboBoxModel(); if (_searchHistory != null) { for (int i = _searchHistory.size() - 1; i >= 0; i--) { model.addElement(_searchHistory.get(i)); } } model.insertElementAt("", 0); _comboBox.setModel(model); _comboBox.setSelectedIndex(0); } /** * Gets the maximum search history length. * * @return the maximum search history length. * @see {@link #setMaxHistoryLength(int)} * @since 3.4.1 */ public int getMaxHistoryLength() { return _maxHistoryLength; } /** * Sets the maximum search history length. *

* By default, it's 0, which means there is no history to shown to keep the behavior backward compatibility. To show * history with a JComboBox, please use this method to set a positive or negative value. Any negative value means * that the history size is unlimited. * * @param maxHistoryLength the maximum history length * @since 3.4.1 */ public void setMaxHistoryLength(int maxHistoryLength) { if (_maxHistoryLength != maxHistoryLength) { int old = _maxHistoryLength; _maxHistoryLength = maxHistoryLength; if (getMaxHistoryLength() == 0) { _leadingLabel.setLabelFor(_textField); _textField.setVisible(true); Object item = _comboBox.getEditor().getItem(); _textField.setText(item == null ? "" : item.toString()); _comboBox.setVisible(false); } else if (!_comboBox.isVisible()) { _leadingLabel.setLabelFor(_comboBox); _comboBox.setVisible(true); _comboBox.getEditor().setItem(_textField.getText()); _textField.setVisible(false); } firePropertyChange(PROPERTY_MAX_HISTORY_LENGTH, old, _maxHistoryLength); } } /** * Gets the flag indicating if the match count should be displayed in the status label. * * @return true if the match count should be displayed. Otherwise false. * @see #setShowMatchCount(boolean) * @since 3.5.2 */ public boolean isShowMatchCount() { return _showMatchCount; } /** * Sets the flag indicating if the match count should be displayed in the status label. *

* By default, the flag is set to false to keep the original behavior. * * @param showMatchCount * @since 3.5.2 */ public void setShowMatchCount(boolean showMatchCount) { _showMatchCount = showMatchCount; if (getSearchable() != null) { getSearchable().setCountMatch(isShowMatchCount()); } } /** * The installer for SearchableBar. */ public interface Installer { /** * Called to show the SearchableBar so that user can see it. *

* For example, if you want to add a SearchableBar to the south of a JTextArea, you should add JTextArea to the * CENTER of a BorderLayout panel. In this method, you add the SearchableBar to the SOUTH of the same * BorderLayout panel. * * @param searchableBar the searchable bar */ public void openSearchBar(SearchableBar searchableBar); /** * Called to hide the SearchableBar. * * @param searchableBar the searchable bar */ public void closeSearchBar(SearchableBar searchableBar); } public Installer getInstaller() { return _installer; } /** * Sets the installer. Installer is responsible for the installation and uninstallation of SearchableBar. * * @param installer the installer */ public void setInstaller(Installer installer) { _installer = installer; } /** * Installs a SearchableBar on a component. This is just a convenient method for you, you can install it in your own * code. See below for the actual code we used in this method. *

*

     * final SearchableBar searchableBar = new SearchableBar(searchable);
     * searchableBar.setInstaller(installer);
     * ((JComponent) searchable.getComponent()).registerKeyboardAction(new AbstractAction() {
     *     public void actionPerformed(ActionEvent e) {
     *         searchableBar.getInstaller().openSearchBar(searchableBar);
     *         searchableBar.focusSearchField();
     *     }
     * }, keyStroke, JComponent.WHEN_FOCUSED);
     * return searchableBar;
     * 
* * @param searchable the searchable * @param keyStroke the key stroke * @param installer the installer * * @return the SearchableBar that is created. */ public static SearchableBar install(Searchable searchable, KeyStroke keyStroke, Installer installer) { final SearchableBar searchableBar = new SearchableBar(searchable); searchableBar.setInstaller(installer); ((JComponent) searchable.getComponent()).registerKeyboardAction(new AbstractAction() { private static final long serialVersionUID = 8328919754409621715L; public void actionPerformed(ActionEvent e) { searchableBar.getInstaller().openSearchBar(searchableBar); searchableBar.focusSearchField(); } }, keyStroke, JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); return searchableBar; } @Override public void processKeyEvent(KeyEvent e) { } public int getVisibleButtons() { return _visibleButtons; } /** * Sets visible buttons on SearchableBar. * * @param visibleButtons bit-wise all of several constants. Valid constants are
  • {@link #SHOW_CLOSE} - the * close button
  • {@link #SHOW_NAVIGATION} - the find next and find previous buttons
  • * {@link #SHOW_HIGHLIGHTS} - highlights all button
  • {@link #SHOW_MATCHCASE} - match case * button
  • {@link #SHOW_WHOLE_WORDS} - word only button
  • {@link #SHOW_REPEATS} - repeats * button
  • {@link #SHOW_STATUS} - status area
  • {@link #SHOW_ALL} - all buttons
* For example, if you want to show only close and highlights all button, call * setVisibleButtons(SearchableBar.SHOW_CLOSE | SearchableBar.SHOW_HIGHLIGHTS). */ public void setVisibleButtons(int visibleButtons) { _visibleButtons = visibleButtons; removeAll(); installComponents(); revalidate(); repaint(); } /** * Checks if SearchableBar is in compact mode. * * @return true if in compact. Otherwise, false. */ public boolean isCompact() { return _compact; } /** * Sets the SearchableBar to compact or full mode. In compact mode will only use icon for buttons v.s. * full mode will use both icon and text for buttons. * * @param compact the flag */ public void setCompact(boolean compact) { _compact = compact; _findNextButton.setText(_compact ? "" : getResourceString("SearchableBar.findNext")); _highlightsButton.setText(_compact ? "" : getResourceString("SearchableBar.highlights")); _findPrevButton.setText(_compact ? "" : getResourceString("SearchableBar.findPrevious")); } /** * Gets the icons from SearchableBarIconsFactory. Subclass can override this method if they want to provide their * own icon. * * @param name the icon name * * @return the icon of the specified name. */ protected ImageIcon getImageIcon(String name) { return SearchableBarIconsFactory.getImageIcon(name); } /** * Gets the localized string from resource bundle. Subclass can override it to provide its own string. Available * keys are defined in swing.properties that begin with "SearchableBar.". * * @param key the resource key * * @return the localized string. */ protected String getResourceString(String key) { return Resource.getResourceBundle(Locale.getDefault()).getString(key); } private void showMessage(String message) { hideMessage(); _messagePopup = com.jidesoft.popup.JidePopupFactory.getSharedInstance().createPopup(); JLabel label = new JLabel(message); label.setOpaque(true); label.setFont(UIDefaultsLookup.getFont("Label.font").deriveFont(Font.BOLD, 11)); label.setBackground(new Color(253, 254, 226)); label.setBorder(BorderFactory.createEmptyBorder(2, 6, 2, 6)); label.setForeground(UIDefaultsLookup.getColor("ToolTip.foreground")); _messagePopup.getContentPane().setLayout(new BorderLayout()); _messagePopup.getContentPane().add(label); if (_textField != null && _textField.isVisible()) { _messagePopup.setOwner(_textField); } if (_comboBox != null && _comboBox.isVisible()) { _messagePopup.setOwner(_comboBox); } _messagePopup.setDefaultMoveOperation(JidePopup.HIDE_ON_MOVED); _messagePopup.setTransient(true); _messagePopup.showPopup(); addMouseMotionListener(_mouseMotionListener); if (_textField != null && _textField.isVisible()) { _textField.addKeyListener(_keyListener); } if (_comboBox != null && _comboBox.isVisible()) { _comboBox.addKeyListener(_keyListener); } } private void hideMessage() { if (_messagePopup != null) { _messagePopup.hidePopupImmediately(); _messagePopup = null; } if (_mouseMotionListener != null) { removeMouseMotionListener(_mouseMotionListener); } if (_keyListener != null) { _textField.removeKeyListener(_keyListener); _comboBox.removeKeyListener(_keyListener); } } private void addSearchingTextToHistory(String searchingText) { if (searchingText == null || searchingText.length() == 0) { return; } if (_searchHistory == null) { _searchHistory = new ArrayList(); } if (_searchHistory.size() <= 0) { _searchHistory.add(searchingText); DefaultComboBoxModel model = new DefaultComboBoxModel(); model.addElement(searchingText); _comboBox.setModel(model); return; } if (JideSwingUtilities.equals(_searchHistory.get(_searchHistory.size() - 1), searchingText)) { return; } _searchHistory.remove(searchingText); // remove existing entry first _searchHistory.add(searchingText); if (getMaxHistoryLength() > 0 && _searchHistory.size() > getMaxHistoryLength()) { _searchHistory.remove(0); } DefaultComboBoxModel model = new DefaultComboBoxModel(); for (int i = _searchHistory.size() - 1; i >= 0; i--) { model.addElement(_searchHistory.get(i)); } _comboBox.setModel(model); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy