Logo Search packages:      
Sourcecode: jabref version File versions  Download package

SearchManager2.java

/*
Copyright (C) 2003 JabRef team

All programs in this directory and
subdirectories are published under the GNU General Public License as
described below.

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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA

Further information about the GNU GPL is available at:
http://www.gnu.org/copyleft/gpl.ja.html

*/
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.Hashtable;

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.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;

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;

    //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;

    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"));

    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();


    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());
                  }
        }
        });

            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);
    //SidePaneHeader header = new SidePaneHeader("Search", GUIGlobals.searchIconFile, this);
    con.gridwidth = GridBagConstraints.REMAINDER;
    con.fill = GridBagConstraints.BOTH;
        con.weightx = 1;
    //con.insets = new Insets(0, 0, 2,  0);
    //gbl.setConstraints(header, con);
    //add(header);
        //con.insets = new Insets(0, 0, 0,  0);
        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();
    }

    /** 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("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();

    }
    }

    public void actionPerformed(ActionEvent e) {
    if (e.getSource() == escape) {
        incSearch = false;
        if (panel != null) {
            Thread t = new Thread() {
                public void run() {
                    clearSearch();
                }
            };
            // do this after the button action is over
            SwingUtilities.invokeLater(t);
        }
    }
    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.
              panel.stopShowingSearchResults();
              return;
            }
        // 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) ;
        SearchWorker worker = new SearchWorker(searchRules, searchOptions);
        worker.getWorker().run();
        worker.getCallBack().update();
        escape.setEnabled(true);
    }
    }

    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();
        }
        // 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();
        if (increment.isSelected())
        searchField.addKeyListener(this);
        else
        searchField.removeKeyListener(this);
    } 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);
    }
}

Generated by  Doxygen 1.6.0   Back to index