/*  Copyright (C) 2003-2011 JabRef contributors.
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along
    with this program; if not, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package net.sf.jabref;

import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.*;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Vector;

import javax.swing.*;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import net.sf.jabref.gui.AutoCompleteListener;
import net.sf.jabref.search.BasicSearch;
import net.sf.jabref.search.SearchExpression;
import net.sf.jabref.search.SearchExpressionParser;
import net.sf.jabref.search.SearchMatcher;
import net.sf.jabref.gui.SearchResultsDialog;
import net.sf.jabref.help.HelpAction;

public class SearchManager2 extends SidePaneComponent
    implements ActionListener, KeyListener, ItemListener, CaretListener, ErrorMessageDisplay {

    private JabRefFrame frame;

    GridBagLayout gbl = new GridBagLayout() ;
    GridBagConstraints con = new GridBagConstraints() ;

    IncrementalSearcher incSearcher;
    SearchResultsDialog searchDialog = null;

    AutoCompleteListener autoCompleteListener = null;
    
	/**
	 * subscribed Objects
	 */
	private Vector<SearchTextListener> listeners = new Vector<SearchTextListener>();

    //private JabRefFrame frame;
    private JTextField searchField = new JTextField("", 12);
    private JPopupMenu settings = new JPopupMenu();
    private JButton openset = new JButton(Globals.lang("Settings"));
    private JButton escape = new JButton(Globals.lang("Clear"));
    private JButton help = new JButton(GUIGlobals.getImage("help"));
    /** This button's text will be set later. */
    private JButton search = new JButton();
    private JCheckBoxMenuItem searchReq, searchOpt, searchGen,
    searchAll, caseSensitive, regExpSearch, highLightWords, searchAutoComplete;

    private JRadioButton increment, floatSearch, hideSearch, showResultsInDialog,
        searchAllBases;
    private JCheckBoxMenuItem select;
    private ButtonGroup types = new ButtonGroup();
    private boolean incSearch = false, startedFloatSearch=false, startedFilterSearch=false;

    private int incSearchPos = -1; // To keep track of where we are in
                   // an incremental search. -1 means
                   // that the search is inactive.


    public SearchManager2(JabRefFrame frame, SidePaneManager manager) {
    super(manager, GUIGlobals.getIconUrl("search"), Globals.lang("Search"));

        this.frame = frame;
    incSearcher = new IncrementalSearcher(Globals.prefs);



    //setBorder(BorderFactory.createMatteBorder(1,1,1,1,Color.magenta));

        searchReq = new JCheckBoxMenuItem
        (Globals.lang("Search required fields"),
         Globals.prefs.getBoolean("searchReq"));
    searchOpt = new JCheckBoxMenuItem
        (Globals.lang("Search optional fields"),
         Globals.prefs.getBoolean("searchOpt"));
    searchGen = new JCheckBoxMenuItem
        (Globals.lang("Search general fields"),
         Globals.prefs.getBoolean("searchGen"));
        searchAll = new JCheckBoxMenuItem
        (Globals.lang("Search all fields"),
         Globals.prefs.getBoolean("searchAll"));
        regExpSearch = new JCheckBoxMenuItem
        (Globals.lang("Use regular expressions"),
         Globals.prefs.getBoolean("regExpSearch"));

    increment = new JRadioButton(Globals.lang("Incremental"), false);
    floatSearch = new JRadioButton(Globals.lang("Float"), true);
    hideSearch = new JRadioButton(Globals.lang("Filter"), true);
    showResultsInDialog = new JRadioButton(Globals.lang("Show results in dialog"), true);
    searchAllBases = new JRadioButton(Globals.lang("Global search"),
            Globals.prefs.getBoolean("searchAllBases"));
    types.add(increment);
    types.add(floatSearch);
    types.add(hideSearch);
    types.add(showResultsInDialog);
    types.add(searchAllBases);

        select = new JCheckBoxMenuItem(Globals.lang("Select matches"), false);
        increment.setToolTipText(Globals.lang("Incremental search"));
        floatSearch.setToolTipText(Globals.lang("Gray out non-matching entries"));
        hideSearch.setToolTipText(Globals.lang("Hide non-matching entries"));
        showResultsInDialog.setToolTipText(Globals.lang("Show search results in a window"));

    // Add an item listener that makes sure we only listen for key events
    // when incremental search is turned on.
    increment.addItemListener(this);
    floatSearch.addItemListener(this);
    hideSearch.addItemListener(this);
    showResultsInDialog.addItemListener(this);
        // Add the global focus listener, so a menu item can see if this field was focused when
        // an action was called.
        searchField.addFocusListener(Globals.focusListener);


    if (searchAll.isSelected()) {
        searchReq.setEnabled(false);
        searchOpt.setEnabled(false);
        searchGen.setEnabled(false);
    }
    searchAll.addChangeListener(new ChangeListener() {
        public void stateChanged(ChangeEvent event) {
            boolean state = !searchAll.isSelected();
            searchReq.setEnabled(state);
            searchOpt.setEnabled(state);
            searchGen.setEnabled(state);
        }
    });

        caseSensitive = new JCheckBoxMenuItem(Globals.lang("Case sensitive"),
                      Globals.prefs.getBoolean("caseSensitiveSearch"));
        
        highLightWords = new JCheckBoxMenuItem(Globals.lang("Highlight Words"),
        		Globals.prefs.getBoolean("highLightWords"));

        searchAutoComplete = new JCheckBoxMenuItem(Globals.lang("Autocomplete names"),
                Globals.prefs.getBoolean("searchAutoComplete"));
    settings.add(select);

    // 2005.03.29, trying to remove field category searches, to simplify
        // search usability.
    //settings.addSeparator();
    //settings.add(searchReq);
    //settings.add(searchOpt);
    //settings.add(searchGen);
    //settings.addSeparator();
    //settings.add(searchAll);
    // ---------------------------------------------------------------
    settings.addSeparator();
        settings.add(caseSensitive);
    settings.add(regExpSearch);
    settings.addSeparator();
    settings.add(highLightWords);
    settings.addSeparator();
    settings.add(searchAutoComplete);

    searchField.addActionListener(this);
    searchField.addCaretListener(this);
        search.addActionListener(this);
    searchField.addFocusListener(new FocusAdapter() {
          public void focusGained(FocusEvent e) {
            if (increment.isSelected())
              searchField.setText("");
          }
        public void focusLost(FocusEvent e) {
            incSearch = false;
            incSearchPos = -1; // Reset incremental
                       // search. This makes the
                       // incremental search reset
                       // once the user moves focus to
                       // somewhere else.
                    if (increment.isSelected()) {
                      //searchField.setText("");
                      //System.out.println("focuslistener");
                    }
        }
        });
    escape.addActionListener(this);
    escape.setEnabled(false); // enabled after searching

    openset.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
                  if (settings.isVisible()) {
                    //System.out.println("oee");
                    //settings.setVisible(false);
                  }
                  else {
                    JButton src = (JButton) e.getSource();
                    settings.show(src, 0, openset.getHeight());
                  }
        }
        });

    searchAutoComplete.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent actionEvent) {
            Globals.prefs.putBoolean("searchAutoComplete", searchAutoComplete.isSelected());
            if (SearchManager2.this.frame.basePanel() != null) {
                SearchManager2.this.frame.basePanel().updateSearchManager();
            }

        }
    });
            Insets margin = new Insets(0, 2, 0, 2);
            //search.setMargin(margin);
            escape.setMargin(margin);
            openset.setMargin(margin);
            int butSize = help.getIcon().getIconHeight() + 5;
            Dimension butDim = new Dimension(butSize, butSize);
            help.setPreferredSize(butDim);
            help.setMinimumSize(butDim);
            help.setMargin(margin);
            help.addActionListener(new HelpAction(Globals.helpDiag, GUIGlobals.searchHelp, "Help"));

    // Select the last used mode of search:
    if (Globals.prefs.getBoolean("incrementS"))
        increment.setSelected(true);
    else if (Globals.prefs.getBoolean("floatSearch"))
        floatSearch.setSelected(true);
    else if (Globals.prefs.getBoolean("showSearchInDialog"))
        showResultsInDialog.setSelected(true);
    else if (Globals.prefs.getBoolean("searchAllBases"))
        searchAllBases.setSelected(true);
    else
        hideSearch.setSelected(true);

    JPanel main = new JPanel();
    main.setLayout(gbl);
    con.gridwidth = GridBagConstraints.REMAINDER;
    con.fill = GridBagConstraints.BOTH;
        con.weightx = 1;

        gbl.setConstraints(searchField,con);
        main.add(searchField) ;
        //con.gridwidth = 1;
        gbl.setConstraints(search,con);
        main.add(search) ;
        con.gridwidth = GridBagConstraints.REMAINDER;
        gbl.setConstraints(escape,con);
        main.add(escape) ;
        con.insets = new Insets(0, 2, 0,  0);
        gbl.setConstraints(increment, con);
        main.add(increment);
        gbl.setConstraints(floatSearch, con);
        main.add(floatSearch);
        gbl.setConstraints(hideSearch, con);
        main.add(hideSearch);
        gbl.setConstraints(showResultsInDialog, con);
        main.add(showResultsInDialog);
        gbl.setConstraints(searchAllBases, con);
        main.add(searchAllBases);
    con.insets = new Insets(0, 0, 0,  0);
        JPanel pan = new JPanel();
        GridBagLayout gb = new GridBagLayout();
        gbl.setConstraints(pan, con);
        pan.setLayout(gb);
        con.weightx = 1;
        con.gridwidth = 1;
        gb.setConstraints(openset, con);
        pan.add(openset);
        con.weightx = 0;
        gb.setConstraints(help, con);
        pan.add(help);
        main.add(pan);
        main.setBorder(BorderFactory.createEmptyBorder(1,1,1,1));
        
        setContent(main);

    searchField.getInputMap().put(Globals.prefs.getKey("Repeat incremental search"),
                      "repeat");

    searchField.getActionMap().put("repeat", new AbstractAction() {
        public void actionPerformed(ActionEvent e) {
            if (increment.isSelected())
            repeatIncremental();
        }
        });
    searchField.getInputMap().put(Globals.prefs.getKey("Clear search"), "escape");
    searchField.getActionMap().put("escape", new AbstractAction() {
        public void actionPerformed(ActionEvent e) {
            hideAway();
            //SearchManager2.this.actionPerformed(new ActionEvent(escape, 0, ""));
        }
        });
    setSearchButtonSizes();
    updateSearchButtonText();
    }

    public void setAutoCompleteListener(AutoCompleteListener listener) {
        this.autoCompleteListener = listener;
        updateKeyListeners();
    }

    /**
     * Add the correct key listeners to the search text field, depending on whether
     * and autocomplete listener has been set and whether incremental search
     * is selected.
     */
    protected void updateKeyListeners() {
        KeyListener[] listeners = searchField.getKeyListeners();
        for (int i = 0; i < listeners.length; i++) {
            searchField.removeKeyListener(listeners[i]);
        }
        if (increment.isSelected()) {
            searchField.addKeyListener(this);
        }
        else {
            if (searchAutoComplete.isSelected() && autoCompleteListener != null)
                searchField.addKeyListener(autoCompleteListener);
        }
    }

	/**
	 * Subscribe to the SearchListener and receive events, if the user searches for some thing. You
	 * will receive a list of words
	 * 
	 * @param l
	 */
	public void addSearchListener(SearchTextListener l) {
		if (listeners.contains(l))
			return;
		else
			listeners.add(l);
		//fire event for the new subscriber
		l.searchText(getSearchwords(searchField.getText()));
	}

	/**
	 * Remove object from the SearchListener
	 * @param l
	 */
	public void removeSearchListener(SearchTextListener l) {
		listeners.remove(l);
	}

	/**
	 * parse the search string for valid words and return a list of words
	 * Like "The great Vikinger" will be ["The","great","Vikinger"] 
	 * 
	 * @param t
	 * @return
	 */
	private ArrayList<String> getSearchwords(String t) {
		// for now ... just seperate words by whitespace
		String[] strings = t.split(" ");
		ArrayList<String> words = new ArrayList<String>(strings.length);
		for (int i = 0; i < strings.length; i++) {
			words.add(strings[i]);
		}
		return words;
	}
    /**
     * Fires an event if a search was started / canceled
     * @param t
     */
    private void fireSearchlistenerEvent(String t){
    	// parse the Search string to words    	
    	ArrayList<String> words;
    	if ((t == null) || (t.isEmpty())) {
    		words = null;
    	} else { 
    		words = getSearchwords(t);
    	}
    	
    	//fire an event for every listener
    	for(SearchTextListener s: listeners)s.searchText(words);
    }
    
    
    /** force the search button to be large enough for
     * the longer of the two texts */
    private void setSearchButtonSizes() {
        search.setText(Globals.lang("Search specified field(s)"));
        Dimension size1 = search.getPreferredSize();
        search.setText(Globals.lang("Search all fields"));
        Dimension size2 = search.getPreferredSize();
        size2.width = Math.max(size1.width,size2.width);
        search.setMinimumSize(size2);
        search.setPreferredSize(size2);
    }

    /**
     * Instantiate the search dialog, unless it has already been instantiated:
     */
    protected void instantiateSearchDialog() {
        if (searchDialog == null)
            searchDialog = new SearchResultsDialog(frame, Globals.lang("Search results"));
    }

    public void updatePrefs() {
        Globals.prefs.putBoolean("searchReq", searchReq.isSelected());
        Globals.prefs.putBoolean("searchOpt", searchOpt.isSelected());
        Globals.prefs.putBoolean("searchGen", searchGen.isSelected());
        Globals.prefs.putBoolean("searchAll", searchAll.isSelected());
        Globals.prefs.putBoolean("incrementS", increment.isSelected());
        Globals.prefs.putBoolean("selectS", select.isSelected());
        Globals.prefs.putBoolean("floatSearch", floatSearch.isSelected());
        Globals.prefs.putBoolean("caseSensitiveSearch",
                 caseSensitive.isSelected());
        Globals.prefs.putBoolean("regExpSearch", regExpSearch.isSelected());
        Globals.prefs.putBoolean("highLightWords", highLightWords.isSelected());
        Globals.prefs.putBoolean("showSearchInDialog", showResultsInDialog.isSelected());
        Globals.prefs.putBoolean("searchAllBases", searchAllBases.isSelected());
    }

    public void startIncrementalSearch() {
    increment.setSelected(true);
    searchField.setText("");
        //System.out.println("startIncrementalSearch");
    searchField.requestFocus();
    }

    /**
     * Clears and focuses the search field if it is not
     * focused. Otherwise, cycles to the next search type.
     */
    public void startSearch() {
    if (increment.isSelected() && incSearch) {
        repeatIncremental();
        return;
    }
    if (!searchField.hasFocus()) {
        //searchField.setText("");
            searchField.selectAll();
        searchField.requestFocus();
    } else {
        if (increment.isSelected())
            floatSearch.setSelected(true);
        else if (floatSearch.isSelected())
            hideSearch.setSelected(true);
        else if (hideSearch.isSelected())
            showResultsInDialog.setSelected(true);
        else if (showResultsInDialog.isSelected())
            searchAllBases.setSelected(true);
        else {
            increment.setSelected(true);
        }
        increment.revalidate();
        increment.repaint();

        searchField.requestFocus();

    }
    }
    
    private void clearSearchLater() {
        if (panel != null) {
            Thread t = new Thread() {
                public void run() {
                    clearSearch();
                }
            };
            // do this after the button action is over
            SwingUtilities.invokeLater(t);
        }
    }

    public void actionPerformed(ActionEvent e) {

    if (e.getSource() == escape) {
        incSearch = false;
        clearSearchLater();
    }
    else if (((e.getSource() == searchField) || (e.getSource() == search))
         && !increment.isSelected()
         && (panel != null)) {

        updatePrefs(); // Make sure the user's choices are recorded.
        if (searchField.getText().equals("")) {
            // An empty search field should cause the search to be cleared.
            clearSearchLater();
            return;
        }

        fireSearchlistenerEvent(searchField.getText());
            
        // Setup search parameters common to both normal and float.
        Hashtable<String, String> searchOptions = new Hashtable<String, String>();
        searchOptions.put("option",searchField.getText()) ;
        SearchRuleSet searchRules = new SearchRuleSet() ;
        SearchRule rule1;

        rule1 = new BasicSearch(Globals.prefs.getBoolean("caseSensitiveSearch"),
                Globals.prefs.getBoolean("regExpSearch"));

        try {
            // this searches specified fields if specified,
            // and all fields otherwise
            rule1 = new SearchExpression(Globals.prefs,searchOptions);
        } catch (Exception ex) {
            // we'll do a search in all fields
        }

        searchRules.addRule(rule1) ;

        if (!searchRules.validateSearchStrings(searchOptions)) {
            panel.output(Globals.lang("Search failed: illegal search expression"));
            panel.stopShowingSearchResults();
            return;
        }
        SearchWorker worker = new SearchWorker(searchRules, searchOptions);
        worker.getWorker().run();
        worker.getCallBack().update();
        escape.setEnabled(true);

        frame.basePanel().mainTable.setSelected(0);
    }
    }

    class SearchWorker extends AbstractWorker {
        private SearchRuleSet rules;
        Hashtable<String, String> searchTerm;
        int hits = 0;
        public SearchWorker(SearchRuleSet rules, Hashtable<String, String> searchTerm) {
            this.rules = rules;
            this.searchTerm = searchTerm;
        }

        public void run() {
            if (!searchAllBases.isSelected()) {
                // Search only the current database:
                for (BibtexEntry entry : panel.getDatabase().getEntries()){

                    boolean hit = rules.applyRule(searchTerm, entry) > 0;
                    entry.setSearchHit(hit);
                    if (hit) hits++;
                }
            }
            else {
                // Search all databases:
                for (int i=0; i<frame.getTabbedPane().getTabCount(); i++) {
                    BasePanel p = frame.baseAt(i);
                    for (BibtexEntry entry : p.getDatabase().getEntries()){

                        boolean hit = rules.applyRule(searchTerm, entry) > 0;
                        entry.setSearchHit(hit);
                        if (hit) hits++;
                    }
                }
            }
        }

        public void update() {
            panel.output(Globals.lang("Searched database. Number of hits")
                    + ": " + hits);

            // Show the result in the chosen way:
            if (searchAllBases.isSelected()) {
                // Search all databases. This means we need to use the search results dialog.
                // Turn off other search mode, if activated:
                if (startedFloatSearch) {
                    panel.mainTable.stopShowingFloatSearch();
                    startedFloatSearch = false;
                }
                if (startedFilterSearch) {
                    panel.stopShowingSearchResults();
                    startedFilterSearch = false;
                }
                // Make sure the search dialog is instantiated and cleared:
                instantiateSearchDialog();
                searchDialog.clear();
                for (int i=0; i<frame.getTabbedPane().getTabCount(); i++) {
                    BasePanel p = frame.baseAt(i);
                    for (BibtexEntry entry : p.getDatabase().getEntries()){
                        if (entry.isSearchHit())
                            searchDialog.addEntry(entry, p);
                    }
                }
                searchDialog.selectFirstEntry();
                searchDialog.setVisible(true);
            }

            else if (showResultsInDialog.isSelected()) {
                // Turn off other search mode, if activated:
                if (startedFloatSearch) {
                    panel.mainTable.stopShowingFloatSearch();
                    startedFloatSearch = false;
                }
                if (startedFilterSearch) {
                    panel.stopShowingSearchResults();
                    startedFilterSearch = false;
                }
                // Make sure the search dialog is instantiated and cleared:
                instantiateSearchDialog();
                searchDialog.clear();
                for (BibtexEntry entry : panel.getDatabase().getEntries()) {
                    if (entry.isSearchHit())
                        searchDialog.addEntry(entry, panel);
                }
                searchDialog.selectFirstEntry();
                searchDialog.setVisible(true);
            }
            else if (hideSearch.isSelected()) {
                // Filtering search - removes non-hits from the table:
                if (startedFloatSearch) {
                    panel.mainTable.stopShowingFloatSearch();
                    startedFloatSearch = false;
                }
                startedFilterSearch = true;
                panel.setSearchMatcher(SearchMatcher.INSTANCE);

            } else {
                // Float search - floats hits to the top of the table:
                if (startedFilterSearch) {
                    panel.stopShowingSearchResults();
                    startedFilterSearch = false;
                }
                startedFloatSearch = true;
                panel.mainTable.showFloatSearch(SearchMatcher.INSTANCE);

            }

            // Afterwards, select all text in the search field.
            searchField.select(0, searchField.getText().length());

        }
    }

    public void clearSearch() {
        
        if (panel.isShowingFloatSearch()) {
            startedFloatSearch = false;
            panel.mainTable.stopShowingFloatSearch();
        } else if (panel.isShowingFilterSearch()) {
            startedFilterSearch = false;
            panel.stopShowingSearchResults();
        }
        
        // clear search means that nothing is searched for
        // even if a word is written in the text field,
        // nothing should be highlighted
        fireSearchlistenerEvent(null);
        
        // disable "Cancel" button to signal this to the user
        escape.setEnabled(false);
    }
    public void itemStateChanged(ItemEvent e) {
    if (e.getSource() == increment) {
        if (startedFilterSearch || startedFloatSearch) {
            clearSearch();
        }
        updateSearchButtonText();

        // Make sure the correct key listener is activated:
        updateKeyListeners();

    } else /*if (e.getSource() == normal)*/ {
        updateSearchButtonText();

        // If this search type is disabled, remove reordering from
        // all databases.
        /*if ((panel != null) && increment.isSelected()) {
            clearSearch();
        } */
    }
    }

    private void repeatIncremental() {
    incSearchPos++;
    if (panel != null)
        goIncremental();
    }

    /**
     * Used for incremental search. Only activated when incremental
     * is selected.
     *
     * The variable incSearchPos keeps track of which entry was last
     * checked.
     */
    public void keyTyped(KeyEvent e) {
    if (e.isControlDown()) {
        return;
    }
    if (panel != null)
        goIncremental();
    }

    private void goIncremental() {
    incSearch = true;
    escape.setEnabled(true);
    SwingUtilities.invokeLater(new Thread() {
        public void run() {
            String text = searchField.getText();


            if (incSearchPos >= panel.getDatabase().getEntryCount()) {
            panel.output("'"+text+"' : "+
                    Globals.lang("Incremental search failed. Repeat to search from top.")+".");
            incSearchPos = -1;
            return;
            }

            if (searchField.getText().equals("")) return;
            if (incSearchPos < 0)
            incSearchPos = 0;
            BibtexEntry be = panel.mainTable.getEntryAt(incSearchPos);
            while (!incSearcher.search(text, be)) {
                incSearchPos++;
                if (incSearchPos < panel.getDatabase().getEntryCount())
                    be = panel.mainTable.getEntryAt(incSearchPos);
            else {
                panel.output("'"+text+"' : "+
                     Globals.lang("Incremental search failed. Repeat to search from top."));
                incSearchPos = -1;
                return;
            }
            }
            if (incSearchPos >= 0) {

            panel.selectSingleEntry(incSearchPos);
            panel.output("'"+text+"' "+
                     Globals.lang("found")+".");

            }
        }
        });
    }

    public void componentClosing() {
    frame.searchToggle.setSelected(false);
        if (panel != null) {
            if (startedFilterSearch || startedFloatSearch)
                clearSearch();
        }
    }


    public void keyPressed(KeyEvent e) {}
    public void keyReleased(KeyEvent e) {}

    public void caretUpdate(CaretEvent e) {
        if (e.getSource() == searchField) {
            updateSearchButtonText();
        }
    }

    /** Updates the text on the search button to reflect
      * the type of search that will happen on click. */
    private void updateSearchButtonText() {
        search.setText(!increment.isSelected()
                && SearchExpressionParser.checkSyntax(
                searchField.getText(),
                caseSensitive.isSelected(),
                regExpSearch.isSelected()) != null
                ? Globals.lang("Search specified field(s)")
                : Globals.lang("Search all fields"));
    }

    /**
     * This method is required by the ErrorMessageDisplay interface, and lets this class
     * serve as a callback for regular expression exceptions happening in DatabaseSearch.
     * @param errorMessage
     */
    public void reportError(String errorMessage) {
        JOptionPane.showMessageDialog(panel, errorMessage, Globals.lang("Search error"),
                JOptionPane.ERROR_MESSAGE);
    }

    /**
     * This method is required by the ErrorMessageDisplay interface, and lets this class
     * serve as a callback for regular expression exceptions happening in DatabaseSearch.
     * @param errorMessage
     */
    public void reportError(String errorMessage, Exception exception) {
        reportError(errorMessage);
    }


    public void setActiveBasePanel(BasePanel panel) {
        super.setActiveBasePanel(panel);
        if (panel != null)
            escape.setEnabled(panel.isShowingFloatSearch()
                    || panel.isShowingFilterSearch());
        else
            escape.setEnabled(false);
    }
}
