diff --git a/CHANGELOG.md b/CHANGELOG.md index f5a30459d291..9effa3433d4d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,13 @@ We refer to [GitHub issues](https://github.com/JabRef/jabref/issues) by using `# ## [Unreleased] ### Changed +- Implemented [#825](https://github.com/JabRef/jabref/issues/825): Search Bar across all bib files instead each having its own +- Implemented [#573](https://github.com/JabRef/jabref/issues/573): Add key shortcut for global search (`ctrl+shift+f`, if the searchfield is empty it will be focused instead) +- The search result Window will now show which entry belongs to which bib file +- The search result Window will now remember its location +- The search result Window won't stay on top anymore if the main Window is focused and will be present in the taskbar +- The user can jump from the searchbar to the maintable with `ctrl+enter` +- Implemented [#573 (comment)](https://github.com/JabRef/jabref/issues/573#issuecomment-232284156): Added shortcut: closing the search result window with `ctrl+w` - Added integrity check for fields with BibTeX keys, e.g., `crossref` and `related`, to check that the key exists - [#1496](https://github.com/JabRef/jabref/issues/1496) Keep track of which entry a downloaded file belongs to - Made it possible to download multiple entries in one action @@ -26,6 +33,13 @@ We refer to [GitHub issues](https://github.com/JabRef/jabref/issues) by using `# ### Fixed - Fixed selecting an entry out of multiple duplicates +- Fixed [#617](https://github.com/JabRef/jabref/issues/617): `Enter` in global search opens the selected entry & `Enter` in search dialog window opens the selected entry +- Entries in the SearchResultPanel will be shown correctly (Latex to Unicode) +- Suggestions in the autocomplete will be shown correctly (Latex to Unicode) +- Fixed: When searching the first match will be selected if the current selection is no match +- Selecting an entry in the search result Window will now select the correct entry in the bib file +- Entries in the SearchResultDialog are now converted to Unicode +- Suggestions in the autocomplete (search) are now in Unicode - Fixed NullPointerException when opening search result window for an untitled database - Fixed entry table traversal with Tab (no column traversal thus no double jump) - Fixed [#1757](https://github.com/JabRef/jabref/issues/1757): Crash after saving illegal argument in entry editor diff --git a/src/main/java/net/sf/jabref/cli/ArgumentProcessor.java b/src/main/java/net/sf/jabref/cli/ArgumentProcessor.java index 211e10024041..2b3b24f4763f 100644 --- a/src/main/java/net/sf/jabref/cli/ArgumentProcessor.java +++ b/src/main/java/net/sf/jabref/cli/ArgumentProcessor.java @@ -47,6 +47,7 @@ import net.sf.jabref.model.database.BibDatabaseMode; import net.sf.jabref.model.entry.BibEntry; import net.sf.jabref.preferences.JabRefPreferences; +import net.sf.jabref.preferences.SearchPreferences; import net.sf.jabref.shared.prefs.SharedDatabasePreferences; import org.apache.commons.logging.Log; @@ -166,9 +167,10 @@ private boolean exportMatches(List loaded) { BibDatabaseContext databaseContext = pr.getDatabaseContext(); BibDatabase dataBase = pr.getDatabase(); + SearchPreferences searchPreferences = new SearchPreferences(Globals.prefs); SearchQuery query = new SearchQuery(searchTerm, - Globals.prefs.getBoolean(JabRefPreferences.SEARCH_CASE_SENSITIVE), - Globals.prefs.getBoolean(JabRefPreferences.SEARCH_REG_EXP)); + searchPreferences.isCaseSensitive(), + searchPreferences.isRegularExpression()); List matches = new DatabaseSearcher(query, dataBase).getMatches(); //export matches diff --git a/src/main/java/net/sf/jabref/gui/BasePanel.java b/src/main/java/net/sf/jabref/gui/BasePanel.java index 3ea4886fa82d..0d5abf4bf1a5 100644 --- a/src/main/java/net/sf/jabref/gui/BasePanel.java +++ b/src/main/java/net/sf/jabref/gui/BasePanel.java @@ -78,7 +78,6 @@ import net.sf.jabref.gui.mergeentries.FetchAndMergeEntry; import net.sf.jabref.gui.mergeentries.MergeEntriesDialog; import net.sf.jabref.gui.plaintextimport.TextInputDialog; -import net.sf.jabref.gui.search.SearchBar; import net.sf.jabref.gui.undo.CountingUndoManager; import net.sf.jabref.gui.undo.NamedCompound; import net.sf.jabref.gui.undo.UndoableChangeType; @@ -107,6 +106,7 @@ import net.sf.jabref.logic.l10n.Localization; import net.sf.jabref.logic.layout.Layout; import net.sf.jabref.logic.layout.LayoutHelper; +import net.sf.jabref.logic.search.SearchQuery; import net.sf.jabref.logic.util.FileExtensions; import net.sf.jabref.logic.util.UpdateField; import net.sf.jabref.logic.util.io.FileBasedLock; @@ -205,9 +205,10 @@ public class BasePanel extends JPanel implements ClipboardOwner, FileUpdateListe private final SidePaneManager sidePaneManager; - private final SearchBar searchBar; private ContentAutoCompleters autoCompleters; + private SearchQuery currentSearchQuery; + public BasePanel(JabRefFrame frame, BibDatabaseContext bibDatabaseContext) { Objects.requireNonNull(frame); @@ -219,17 +220,16 @@ public BasePanel(JabRefFrame frame, BibDatabaseContext bibDatabaseContext) { this.frame = frame; this.tableModel = new MainTableDataModel(getBibDatabaseContext()); - searchBar = new SearchBar(this); - setupMainPanel(); setupActions(); - Optional file = bibDatabaseContext.getDatabaseFile(); + this.getDatabase().registerListener(new SearchListener()); // ensure that at each addition of a new entry, the entry is added to the groups interface this.bibDatabaseContext.getDatabase().registerListener(new GroupTreeListener()); + Optional file = bibDatabaseContext.getDatabaseFile(); if (file.isPresent()) { // Register so we get notifications about outside changes to the file. try { @@ -519,7 +519,8 @@ public void update() { actions.put(Actions.MERGE_ENTRIES, (BaseAction) () -> new MergeEntriesDialog(BasePanel.this)); - actions.put(Actions.SEARCH, (BaseAction) searchBar::focus); + actions.put(Actions.SEARCH, (BaseAction) frame.getGlobalSearchBar()::focus); + actions.put(Actions.GLOBAL_SEARCH, (BaseAction) frame.getGlobalSearchBar()::performGlobalSearch); // The action for copying the selected entry's key. actions.put(Actions.COPY_KEY, (BaseAction) () -> copyKey()); @@ -1166,11 +1167,6 @@ public BibEntry newEntry(EntryType type) { return null; } - public SearchBar getSearchBar() { - return searchBar; - } - - private class GroupTreeListener { private final Runnable task = new Runnable() { @@ -1273,6 +1269,21 @@ public void listen(EntryChangedEvent entryChangedEvent) { } } + /** + * Ensures that the results of the current search are updated when a new entry is inserted into the database + */ + private class SearchListener { + @Subscribe + public void listen(EntryAddedEvent addedEntryEvent) { + frame.getGlobalSearchBar().performSearch(); + } + + @Subscribe + public void listen(EntryChangedEvent entryChangedEvent) { + frame.getGlobalSearchBar().performSearch(); + } + } + /** * This method is called from JabRefFrame when the user wants to create a new entry. @@ -1477,7 +1488,6 @@ public void setupMainPanel() { setLayout(new BorderLayout()); removeAll(); - add(searchBar, BorderLayout.NORTH); add(splitPane, BorderLayout.CENTER); // Set up name autocompleter for search: @@ -1511,7 +1521,7 @@ public void setupMainPanel() { } public void updateSearchManager() { - searchBar.setAutoCompleter(searchAutoCompleter); + frame.getGlobalSearchBar().setAutoCompleter(searchAutoCompleter); } private void instantiateSearchAutoCompleter() { @@ -2414,4 +2424,12 @@ public Map getEntryEditors() { public BibDatabaseContext getDatabaseContext() { return bibDatabaseContext; } + + public SearchQuery getCurrentSearchQuery() { + return currentSearchQuery; + } + + public void setCurrentSearchQuery(SearchQuery currentSearchQuery) { + this.currentSearchQuery = currentSearchQuery; + } } diff --git a/src/main/java/net/sf/jabref/gui/IconTheme.java b/src/main/java/net/sf/jabref/gui/IconTheme.java index dbcd2c28855b..38771301c0c3 100644 --- a/src/main/java/net/sf/jabref/gui/IconTheme.java +++ b/src/main/java/net/sf/jabref/gui/IconTheme.java @@ -157,6 +157,8 @@ public enum JabRefIcon { FORUM("\uf28c"), /* css: forum */ FACEBOOK("\uf20c"), /* css: facebook */ BLOG("\uf46b"), /* css: rss */ + GLOBAL_SEARCH_ON("\uF1E7"), /* css: earth */ + GLOBAL_SEARCH_OFF("\uF1E8"), /* css: earth-off */ // STILL MISSING: GROUP_REGULAR("\uF4E6", Color.RED); diff --git a/src/main/java/net/sf/jabref/gui/JabRefFrame.java b/src/main/java/net/sf/jabref/gui/JabRefFrame.java index e0c052412074..a7db048ca8ff 100644 --- a/src/main/java/net/sf/jabref/gui/JabRefFrame.java +++ b/src/main/java/net/sf/jabref/gui/JabRefFrame.java @@ -1,8 +1,10 @@ package net.sf.jabref.gui; +import java.awt.BorderLayout; import java.awt.Component; import java.awt.Container; import java.awt.Cursor; +import java.awt.FlowLayout; import java.awt.Font; import java.awt.Frame; import java.awt.GraphicsEnvironment; @@ -28,7 +30,6 @@ import javax.swing.AbstractAction; import javax.swing.Action; -import javax.swing.Box; import javax.swing.ButtonGroup; import javax.swing.Icon; import javax.swing.ImageIcon; @@ -103,6 +104,7 @@ import net.sf.jabref.gui.protectedterms.ProtectedTermsDialog; import net.sf.jabref.gui.push.PushToApplicationButton; import net.sf.jabref.gui.push.PushToApplications; +import net.sf.jabref.gui.search.GlobalSearchBar; import net.sf.jabref.gui.util.FocusRequester; import net.sf.jabref.gui.util.WindowLocation; import net.sf.jabref.gui.worker.MarkEntriesAction; @@ -112,6 +114,7 @@ import net.sf.jabref.logic.importer.ParserResult; import net.sf.jabref.logic.l10n.Localization; import net.sf.jabref.logic.logging.GuiAppender; +import net.sf.jabref.logic.search.SearchQuery; import net.sf.jabref.logic.undo.AddUndoableActionEvent; import net.sf.jabref.logic.undo.UndoChangeEvent; import net.sf.jabref.logic.undo.UndoRedoEvent; @@ -125,6 +128,7 @@ import net.sf.jabref.preferences.HighlightMatchingGroupPreferences; import net.sf.jabref.preferences.JabRefPreferences; import net.sf.jabref.preferences.LastFocusedTabPreferences; +import net.sf.jabref.preferences.SearchPreferences; import net.sf.jabref.specialfields.Printed; import net.sf.jabref.specialfields.Priority; import net.sf.jabref.specialfields.Quality; @@ -167,12 +171,10 @@ public class JabRefFrame extends JFrame implements OutputPrinter { private final IntegrityCheckAction checkIntegrity = new IntegrityCheckAction(this); private final ToolBar tlb = new ToolBar(); + private final GlobalSearchBar globalSearchBar = new GlobalSearchBar(this); private final JMenuBar mb = new JMenuBar(); - private final GridBagLayout gbl = new GridBagLayout(); - private final GridBagConstraints con = new GridBagConstraints(); - private final JLabel statusLine = new JLabel("", SwingConstants.LEFT); private final JLabel statusLabel = new JLabel( Localization.lang("Status") @@ -306,6 +308,11 @@ public class JabRefFrame extends JFrame implements OutputPrinter { Localization.menuTitle("Manage content selectors")); private final AbstractAction normalSearch = new GeneralAction(Actions.SEARCH, Localization.menuTitle("Search"), Localization.lang("Search"), Globals.getKeyPrefs().getKey(KeyBinding.SEARCH), IconTheme.JabRefIcon.SEARCH.getIcon()); + private final AbstractAction globalSearch = new GeneralAction(Actions.GLOBAL_SEARCH, + Localization.menuTitle("Global Search"), + Localization.lang("Search globally"), + Globals.getKeyPrefs().getKey(KeyBinding.GLOBAL_SEARCH), + IconTheme.JabRefIcon.SEARCH.getIcon()); private final AbstractAction copyKey = new GeneralAction(Actions.COPY_KEY, Localization.menuTitle("Copy BibTeX key"), Globals.getKeyPrefs().getKey(KeyBinding.COPY_BIBTEX_KEY)); @@ -636,23 +643,34 @@ public void windowClosing(WindowEvent e) { tabbedPane.addChangeListener(e -> { markActiveBasePanel(); - BasePanel bp = getCurrentBasePanel(); - if (bp == null) { + BasePanel currentBasePanel = getCurrentBasePanel(); + if (currentBasePanel == null) { return; } + if (new SearchPreferences(Globals.prefs).isGlobalSearch()) { + globalSearchBar.performSearch(); + } else { + String content = ""; + SearchQuery currentSearchQuery = currentBasePanel.getCurrentSearchQuery(); + if (currentSearchQuery != null && !currentSearchQuery.getQuery().trim().isEmpty()) { + content = currentSearchQuery.getQuery(); + } + globalSearchBar.setSearchTerm(content, true); + } + groupToggle.setSelected(sidePaneManager.isComponentVisible("groups")); previewToggle.setSelected(Globals.prefs.getBoolean(JabRefPreferences.PREVIEW_ENABLED)); fetcherToggle.setSelected(sidePaneManager.isComponentVisible(generalFetcher.getTitle())); - Globals.getFocusListener().setFocused(bp.getMainTable()); + Globals.getFocusListener().setFocused(currentBasePanel.getMainTable()); setWindowTitle(); editModeAction.initName(); // Update search autocompleter with information for the correct database: - bp.updateSearchManager(); + currentBasePanel.updateSearchManager(); // Set correct enabled state for Back and Forward actions: - bp.setBackAndForwardEnabledState(); - bp.getUndoManager().postUndoRedoEvent(); - new FocusRequester(bp.getMainTable()); + currentBasePanel.setBackAndForwardEnabledState(); + currentBasePanel.getUndoManager().postUndoRedoEvent(); + new FocusRequester(currentBasePanel.getMainTable()); }); //Note: The registration of Apple event is at the end of initialization, because @@ -886,59 +904,27 @@ private void initLayout() { pushExternalButton = new PushToApplicationButton(this, pushApplications.getApplications()); fillMenu(); createToolBar(); - getContentPane().setLayout(gbl); - splitPane.setDividerSize(2); - splitPane.setBorder(null); - //getContentPane().setBackground(GUIGlobals.lightGray); - con.fill = GridBagConstraints.HORIZONTAL; - con.anchor = GridBagConstraints.WEST; - con.weightx = 1; - con.weighty = 0; - con.gridwidth = GridBagConstraints.REMAINDER; - - //gbl.setConstraints(mb, con); - //getContentPane().add(mb); setJMenuBar(mb); - con.anchor = GridBagConstraints.NORTH; - //con.gridwidth = 1;//GridBagConstraints.REMAINDER;; - gbl.setConstraints(tlb, con); - getContentPane().add(tlb); - - Component lim = Box.createGlue(); - gbl.setConstraints(lim, con); - //getContentPane().add(lim); - /* - JPanel empt = new JPanel(); - empt.setBackground(GUIGlobals.lightGray); - gbl.setConstraints(empt, con); - getContentPane().add(empt); - - con.insets = new Insets(1,0,1,1); - con.anchor = GridBagConstraints.EAST; - con.weightx = 0; - gbl.setConstraints(searchManager, con); - getContentPane().add(searchManager);*/ - con.gridwidth = GridBagConstraints.REMAINDER; - con.weightx = 1; - con.weighty = 0; - con.fill = GridBagConstraints.BOTH; - con.anchor = GridBagConstraints.WEST; - con.insets = new Insets(0, 0, 0, 0); - lim = Box.createGlue(); - gbl.setConstraints(lim, con); - getContentPane().add(lim); - //tabbedPane.setVisible(false); - //tabbedPane.setForeground(GUIGlobals.lightGray); - con.weighty = 1; - gbl.setConstraints(splitPane, con); - getContentPane().add(splitPane); + getContentPane().setLayout(new BorderLayout()); - UIManager.put("TabbedPane.contentBorderInsets", new Insets(0, 0, 0, 0)); + JPanel toolbarPanel = new JPanel(new WrapLayout(FlowLayout.LEFT)); + toolbarPanel.add(tlb); + toolbarPanel.add(globalSearchBar); + getContentPane().add(toolbarPanel, BorderLayout.PAGE_START); + splitPane.setDividerSize(2); + splitPane.setBorder(null); splitPane.setRightComponent(tabbedPane); splitPane.setLeftComponent(sidePaneManager.getPanel()); + getContentPane().add(splitPane, BorderLayout.CENTER); + + UIManager.put("TabbedPane.contentBorderInsets", new Insets(0, 0, 0, 0)); sidePaneManager.updateView(); + GridBagLayout gbl = new GridBagLayout(); + GridBagConstraints con = new GridBagConstraints(); + con.fill = GridBagConstraints.BOTH; + con.anchor = GridBagConstraints.WEST; JPanel status = new JPanel(); status.setLayout(gbl); con.weighty = 0; @@ -957,12 +943,8 @@ private void initLayout() { con.insets = new Insets(2, 4, 2, 2); gbl.setConstraints(progressBar, con); status.add(progressBar); - con.weightx = 1; - con.gridwidth = GridBagConstraints.REMAINDER; statusLabel.setForeground(GUIGlobals.ENTRY_EDITOR_LABEL_COLOR.darker()); - con.insets = new Insets(0, 0, 0, 0); - gbl.setConstraints(status, con); - getContentPane().add(status); + getContentPane().add(status, BorderLayout.PAGE_END); // Drag and drop for tabbedPane: TransferHandler xfer = new EntryTableTransferHandler(null, this, null); @@ -1487,13 +1469,13 @@ private void createToolBar() { tlb.addSeparator(); fetcherToggle = new JToggleButton(generalFetcher.getAction()); - tlb.addJToogleButton(fetcherToggle); + tlb.addJToggleButton(fetcherToggle); previewToggle = new JToggleButton(togglePreview); - tlb.addJToogleButton(previewToggle); + tlb.addJToggleButton(previewToggle); groupToggle = new JToggleButton(toggleGroups); - tlb.addJToogleButton(groupToggle); + tlb.addJToggleButton(groupToggle); tlb.addSeparator(); @@ -1517,7 +1499,7 @@ public void output(final String s) { private void initActions() { openDatabaseOnlyActions.clear(); - openDatabaseOnlyActions.addAll(Arrays.asList(manageSelectors, mergeDatabaseAction, newSubDatabaseAction, save, + openDatabaseOnlyActions.addAll(Arrays.asList(manageSelectors, mergeDatabaseAction, newSubDatabaseAction, save, globalSearch, saveAs, saveSelectedAs, saveSelectedAsPlain, editModeAction, undo, redo, cut, deleteEntry, copy, paste, mark, markSpecific, unmark, unmarkAll, rankSubMenu, editEntry, selectAll, copyKey, copyCiteKey, copyKeyAndTitle, editPreamble, editStrings, toggleGroups, makeKeyAction, normalSearch, generalFetcher.getAction(), mergeEntries, cleanupEntries, exportToClipboard, replaceAll, @@ -2288,7 +2270,7 @@ public void addAction(Action a) { add(b); } - public void addJToogleButton(JToggleButton button) { + public void addJToggleButton(JToggleButton button) { button.setText(null); if (!OS.OS_X) { button.setMargin(marg); @@ -2342,6 +2324,10 @@ public PushToApplications getPushApplications() { return pushApplications; } + public GlobalSearchBar getGlobalSearchBar() { + return globalSearchBar; + } + private class UndoRedoEventManager { diff --git a/src/main/java/net/sf/jabref/gui/actions/Actions.java b/src/main/java/net/sf/jabref/gui/actions/Actions.java index 74e8ecebaeff..8be2aa07949e 100644 --- a/src/main/java/net/sf/jabref/gui/actions/Actions.java +++ b/src/main/java/net/sf/jabref/gui/actions/Actions.java @@ -50,6 +50,7 @@ public class Actions { public static final String SAVE_SELECTED_AS = "saveSelectedAs"; public static final String SAVE_SELECTED_AS_PLAIN = "saveSelectedAsPlain"; public static final String SEARCH = "search"; + public static final String GLOBAL_SEARCH = "globalSearch"; public static final String SELECT_ALL = "selectAll"; public static final String SEND_AS_EMAIL = "sendAsEmail"; public static final String SWITCH_PREVIEW = "switchPreview"; diff --git a/src/main/java/net/sf/jabref/gui/autocompleter/AutoCompleteSupport.java b/src/main/java/net/sf/jabref/gui/autocompleter/AutoCompleteSupport.java index e7a3e19b4825..d508091e4dad 100644 --- a/src/main/java/net/sf/jabref/gui/autocompleter/AutoCompleteSupport.java +++ b/src/main/java/net/sf/jabref/gui/autocompleter/AutoCompleteSupport.java @@ -279,4 +279,9 @@ public void focusLost(FocusEvent e) { public void setAutoCompleter(AutoCompleter autoCompleter) { this.autoCompleter = autoCompleter; } -} \ No newline at end of file + + public void setVisible(boolean visible){ + popup.setVisible(visible); + } + +} diff --git a/src/main/java/net/sf/jabref/gui/entryeditor/EntryEditor.java b/src/main/java/net/sf/jabref/gui/entryeditor/EntryEditor.java index a39301687286..40d869edb3a5 100644 --- a/src/main/java/net/sf/jabref/gui/entryeditor/EntryEditor.java +++ b/src/main/java/net/sf/jabref/gui/entryeditor/EntryEditor.java @@ -528,7 +528,7 @@ public Optional getExtra(final FieldEditor editor) { private void setupSourcePanel() { source = new JTextAreaWithHighlighting(); - panel.getSearchBar().getSearchQueryHighlightObservable().addSearchListener((SearchQueryHighlightListener) source); + panel.frame().getGlobalSearchBar().getSearchQueryHighlightObservable().addSearchListener((SearchQueryHighlightListener) source); source.setEditable(true); source.setLineWrap(true); diff --git a/src/main/java/net/sf/jabref/gui/entryeditor/EntryEditorTab.java b/src/main/java/net/sf/jabref/gui/entryeditor/EntryEditorTab.java index ed99b3405a4c..abc7b76db75a 100644 --- a/src/main/java/net/sf/jabref/gui/entryeditor/EntryEditorTab.java +++ b/src/main/java/net/sf/jabref/gui/entryeditor/EntryEditorTab.java @@ -144,7 +144,7 @@ private void setupPanel(JabRefFrame frame, BasePanel bPanel, boolean addKeyField defaultHeight = 0; } else { fieldEditor = new TextArea(field, null); - bPanel.getSearchBar().getSearchQueryHighlightObservable().addSearchListener((TextArea) fieldEditor); + bPanel.frame().getGlobalSearchBar().getSearchQueryHighlightObservable().addSearchListener((TextArea) fieldEditor); defaultHeight = fieldEditor.getPane().getPreferredSize().height; } diff --git a/src/main/java/net/sf/jabref/gui/keyboard/KeyBinding.java b/src/main/java/net/sf/jabref/gui/keyboard/KeyBinding.java index 6e39a1222f7e..c27a5f67faac 100644 --- a/src/main/java/net/sf/jabref/gui/keyboard/KeyBinding.java +++ b/src/main/java/net/sf/jabref/gui/keyboard/KeyBinding.java @@ -6,6 +6,7 @@ public enum KeyBinding { ABBREVIATE( "Abbreviate", Localization.lang("Abbreviate journal names"), "ctrl alt A"), + ACCEPT("Accept", Localization.lang("Accept"), "ctrl ENTER"), AUTOGENERATE_BIBTEX_KEYS("Autogenerate BibTeX keys", Localization.lang("Autogenerate BibTeX keys"), "ctrl G"), AUTOMATICALLY_LINK_FILES( "Automatically link files", Localization.lang("Automatically set file links"), "F7"), @@ -38,6 +39,7 @@ public enum KeyBinding { FIND_UNLINKED_FILES("Find unlinked files", Localization.lang("Find unlinked files"), "shift F7"), FOCUS_ENTRY_TABLE("Focus entry table", Localization.lang("Focus entry table"), "alt 1"), FORWARD("Forward", Localization.lang("Forward"), "alt RIGHT"), + GLOBAL_SEARCH("Search globally", Localization.lang("Search globally"), "ctrl shift F"), HELP("Help", Localization.lang("Help"), "F1"), IMPORT_INTO_CURRENT_DATABASE("Import into current database", Localization.lang("Import into current database"), "ctrl I"), IMPORT_INTO_NEW_DATABASE("Import into new database", Localization.lang("Import into new database"), "ctrl alt I"), diff --git a/src/main/java/net/sf/jabref/gui/maintable/MainTable.java b/src/main/java/net/sf/jabref/gui/maintable/MainTable.java index 34e96c257c09..ef84ddd65e49 100644 --- a/src/main/java/net/sf/jabref/gui/maintable/MainTable.java +++ b/src/main/java/net/sf/jabref/gui/maintable/MainTable.java @@ -182,6 +182,18 @@ public void actionPerformed(ActionEvent e) { panel.selectPreviousEntry(); } }); + am.put("selectNextRow", new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) { + panel.selectNextEntry(); + } + }); + am.put("selectPreviousRow", new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) { + panel.selectPreviousEntry(); + } + }); } public void addSelectionListener(ListEventListener listener) { diff --git a/src/main/java/net/sf/jabref/gui/maintable/MainTableSelectionListener.java b/src/main/java/net/sf/jabref/gui/maintable/MainTableSelectionListener.java index 780e6c0ef722..d7bd8706556a 100644 --- a/src/main/java/net/sf/jabref/gui/maintable/MainTableSelectionListener.java +++ b/src/main/java/net/sf/jabref/gui/maintable/MainTableSelectionListener.java @@ -85,8 +85,9 @@ public MainTableSelectionListener(BasePanel panel, MainTable table) { new PreviewPanel(panel.getBibDatabaseContext(), null, panel, Globals.prefs.get(JabRefPreferences.PREVIEW_1))}; - panel.getSearchBar().getSearchQueryHighlightObservable().addSearchListener(previewPanel[0]); - panel.getSearchBar().getSearchQueryHighlightObservable().addSearchListener(previewPanel[1]); + panel.frame().getGlobalSearchBar().getSearchQueryHighlightObservable() + .addSearchListener(previewPanel[0]) + .addSearchListener(previewPanel[1]); this.preview = previewPanel[activePreview]; } diff --git a/src/main/java/net/sf/jabref/gui/search/GlobalSearchBar.java b/src/main/java/net/sf/jabref/gui/search/GlobalSearchBar.java new file mode 100644 index 000000000000..eab467f962bd --- /dev/null +++ b/src/main/java/net/sf/jabref/gui/search/GlobalSearchBar.java @@ -0,0 +1,384 @@ +package net.sf.jabref.gui.search; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Font; +import java.awt.event.ActionEvent; +import java.io.File; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +import javax.swing.AbstractAction; +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JToggleButton; +import javax.swing.JToolBar; + +import net.sf.jabref.Globals; +import net.sf.jabref.gui.BasePanel; +import net.sf.jabref.gui.GUIGlobals; +import net.sf.jabref.gui.IconTheme; +import net.sf.jabref.gui.JabRefFrame; +import net.sf.jabref.gui.OSXCompatibleToolbar; +import net.sf.jabref.gui.autocompleter.AutoCompleteSupport; +import net.sf.jabref.gui.help.HelpAction; +import net.sf.jabref.gui.keyboard.KeyBinding; +import net.sf.jabref.gui.maintable.MainTableDataModel; +import net.sf.jabref.gui.util.FocusRequester; +import net.sf.jabref.gui.util.component.JTextFieldWithUnfocusedText; +import net.sf.jabref.logic.autocompleter.AutoCompleter; +import net.sf.jabref.logic.help.HelpFile; +import net.sf.jabref.logic.l10n.Localization; +import net.sf.jabref.logic.search.SearchQuery; +import net.sf.jabref.logic.search.SearchQueryHighlightObservable; +import net.sf.jabref.logic.util.OS; +import net.sf.jabref.model.entry.BibEntry; +import net.sf.jabref.preferences.SearchPreferences; + +public class GlobalSearchBar extends JPanel { + + private static final Color NEUTRAL_COLOR = Color.WHITE; + private static final Color NO_RESULTS_COLOR = new Color(232, 202, 202); + private static final Color RESULTS_FOUND_COLOR = new Color(217, 232, 202); + private static final Color ADVANCED_SEARCH_COLOR = new Color(102, 255, 255); + + private final JabRefFrame frame; + + private final JLabel searchIcon = new JLabel(IconTheme.JabRefIcon.SEARCH.getSmallIcon()); + private final JTextFieldWithUnfocusedText searchField = new JTextFieldWithUnfocusedText(Localization.lang("Search") + "..."); + private JButton openCurrentResultsInDialog = new JButton(IconTheme.JabRefIcon.OPEN_IN_NEW_WINDOW.getSmallIcon()); + + private final JToggleButton caseSensitive; + private final JToggleButton regularExp; + private final JButton searchModeButton = new JButton(); + private final JLabel currentResults = new JLabel(""); + + private AutoCompleteSupport autoCompleteSupport = new AutoCompleteSupport<>(searchField); + private final SearchQueryHighlightObservable searchQueryHighlightObservable = new SearchQueryHighlightObservable(); + + private SearchWorker searchWorker; + private GlobalSearchWorker globalSearchWorker; + + private SearchResultFrame searchResultFrame; + + private SearchDisplayMode searchDisplayMode; + private boolean switchedDatabase; + + + public GlobalSearchBar(JabRefFrame frame) { + super(); + this.frame = Objects.requireNonNull(frame); + SearchPreferences searchPreferences = new SearchPreferences(Globals.prefs); + searchDisplayMode = searchPreferences.getSearchMode(); + + // fits the standard "found x entries"-message thus hinders the searchbar to jump around while searching if the frame width is too small + currentResults.setPreferredSize(new Dimension(150, 5)); + currentResults.setFont(currentResults.getFont().deriveFont(Font.BOLD)); + searchField.setColumns(30); + + JToggleButton globalSearch = new JToggleButton(IconTheme.JabRefIcon.GLOBAL_SEARCH_OFF.getSmallIcon(), searchPreferences.isGlobalSearch()); + globalSearch.setSelectedIcon(IconTheme.JabRefIcon.GLOBAL_SEARCH_ON.getSmallIcon()); + globalSearch.setToolTipText(Localization.lang("Search globally")); + globalSearch.addActionListener(e -> { + searchPreferences.setGlobalSearch(globalSearch.isSelected()); + String localization = globalSearch.isSelected() ? "Search in all open databases" : "Show search results in a window"; + openCurrentResultsInDialog.setToolTipText(Localization.lang(localization)); + }); + + openCurrentResultsInDialog.setDisabledIcon(IconTheme.JabRefIcon.OPEN_IN_NEW_WINDOW.getSmallIcon().createDisabledIcon()); + openCurrentResultsInDialog.setToolTipText(Localization.lang("Show search results in a window")); + openCurrentResultsInDialog.addActionListener(event -> { + if (globalSearch.isSelected()) { + performGlobalSearch(); + } else { + openLocalFindingsInExternalPanel(); + } + }); + openCurrentResultsInDialog.setEnabled(false); + + regularExp = new JToggleButton(IconTheme.JabRefIcon.REG_EX.getSmallIcon(), + searchPreferences.isRegularExpression()); + regularExp.setToolTipText(Localization.lang("regular expression")); + regularExp.addActionListener(event -> { + searchPreferences.setRegularExpression(regularExp.isSelected()); + performSearch(); + }); + + caseSensitive = new JToggleButton(IconTheme.JabRefIcon.CASE_SENSITIVE.getSmallIcon(), + searchPreferences.isCaseSensitive()); + caseSensitive.setToolTipText(Localization.lang("Case sensitive")); + caseSensitive.addActionListener(event -> { + searchPreferences.setCaseSensitive(caseSensitive.isSelected()); + performSearch(); + }); + + updateSearchModeButtonText(); + searchModeButton.addActionListener(event -> toggleSearchModeAndSearch()); + + JButton clearSearchButton = new JButton(IconTheme.JabRefIcon.CLOSE.getSmallIcon()); + clearSearchButton.setToolTipText(Localization.lang("Clear")); + clearSearchButton.addActionListener(event -> endSearch()); + + searchField.addFocusListener(Globals.getFocusListener()); + searchField.addActionListener(event -> performSearch()); + JTextFieldChangeListenerUtil.addChangeListener(searchField, e -> performSearch()); + + String endSearch = "endSearch"; + searchField.getInputMap().put(Globals.getKeyPrefs().getKey(KeyBinding.CLEAR_SEARCH), endSearch); + searchField.getActionMap().put(endSearch, new AbstractAction() { + @Override + public void actionPerformed(ActionEvent event) { + endSearch(); + } + }); + + autoCompleteSupport.install(); + + String acceptSearch = "acceptSearch"; + searchField.getInputMap().put(Globals.getKeyPrefs().getKey(KeyBinding.ACCEPT), acceptSearch); + searchField.getActionMap().put(acceptSearch, new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) { + autoCompleteSupport.setVisible(false); + BasePanel currentBasePanel = frame.getCurrentBasePanel(); + Globals.getFocusListener().setFocused(currentBasePanel.getMainTable()); + new FocusRequester(currentBasePanel.getMainTable()); + } + }); + + setLayout(new FlowLayout(FlowLayout.RIGHT)); + JToolBar toolBar = new OSXCompatibleToolbar(); + toolBar.setFloatable(false); + if (OS.OS_X) { + searchField.putClientProperty("JTextField.variant", "search"); + toolBar.add(searchField); + } else { + toolBar.add(searchIcon); + toolBar.add(searchField); + toolBar.add(clearSearchButton); + } + toolBar.addSeparator(); + toolBar.add(openCurrentResultsInDialog); + toolBar.addSeparator(); + toolBar.add(globalSearch); + toolBar.add(regularExp); + toolBar.add(caseSensitive); + toolBar.add(searchModeButton); + toolBar.addSeparator(); + toolBar.add(new HelpAction(HelpFile.SEARCH)); + toolBar.addSeparator(); + toolBar.add(currentResults); + this.add(toolBar); + } + + public void performGlobalSearch() { + BasePanel currentBasePanel = frame.getCurrentBasePanel(); + if (currentBasePanel == null || validateSearchResultFrame(true)) { + return; + } + + if (globalSearchWorker != null) { + globalSearchWorker.cancel(true); + } + + if (searchField.getText().isEmpty()) { + focus(); + return; + } + + globalSearchWorker = new GlobalSearchWorker(currentBasePanel.frame(), getSearchQuery()); + globalSearchWorker.execute(); + } + + private void openLocalFindingsInExternalPanel() { + BasePanel currentBasePanel = frame.getCurrentBasePanel(); + if (currentBasePanel == null || validateSearchResultFrame(false)) { + return; + } + + if (searchField.getText().isEmpty()) { + focus(); + return; + } + + SearchResultFrame searchDialog = new SearchResultFrame(currentBasePanel.frame(), + Localization.lang("Search results in database %0 for %1", currentBasePanel.getBibDatabaseContext() + .getDatabaseFile().map(File::getName).orElse(GUIGlobals.UNTITLED_TITLE), + this.getSearchQuery().localize()), + getSearchQuery(), false); + List entries = currentBasePanel.getDatabase().getEntries().stream() + .filter(BibEntry::isSearchHit) + .collect(Collectors.toList()); + searchDialog.addEntries(entries, currentBasePanel); + searchDialog.selectFirstEntry(); + searchDialog.setVisible(true); + } + + private boolean validateSearchResultFrame(boolean globalSearch) { + if (searchResultFrame != null) { + if (searchResultFrame.isGlobalSearch() == globalSearch && isStillValidQuery(searchResultFrame.getSearchQuery())) { + searchResultFrame.focus(); + return true; + } else { + searchResultFrame.dispose(); + return false; + } + } + + return false; + } + + private void toggleSearchModeAndSearch() { + int nextSearchMode = (searchDisplayMode.ordinal() + 1) % SearchDisplayMode.values().length; + searchDisplayMode = SearchDisplayMode.values()[nextSearchMode]; + new SearchPreferences(Globals.prefs).setSearchMode(searchDisplayMode); + updateSearchModeButtonText(); + performSearch(); + } + + private void updateSearchModeButtonText() { + searchModeButton.setText(searchDisplayMode.getDisplayName()); + searchModeButton.setToolTipText(searchDisplayMode.getToolTipText()); + } + + private void endSearch() { + BasePanel currentBasePanel = frame.getCurrentBasePanel(); + if (currentBasePanel != null) { + clearSearch(currentBasePanel); + Globals.getFocusListener().setFocused(currentBasePanel.getMainTable()); + new FocusRequester(currentBasePanel.getMainTable()); + } + } + + /** + * Focuses the search field if it is not focused. + */ + public void focus() { + if (!searchField.hasFocus()) { + searchField.requestFocus(); + } + } + + private void clearSearch(BasePanel currentBasePanel) { + SearchQuery searchQuery = getSearchQuery(); + updateResults(0, searchQuery.getDescription(), searchQuery.isGrammarBasedSearch()); + + currentResults.setText(""); + searchField.setText(""); + searchField.setBackground(NEUTRAL_COLOR); + searchIcon.setIcon(IconTheme.JabRefIcon.SEARCH.getSmallIcon()); + searchQueryHighlightObservable.reset(); + openCurrentResultsInDialog.setEnabled(false); + + if (currentBasePanel != null) { + currentBasePanel.getMainTable().getTableModel().updateSearchState(MainTableDataModel.DisplayOption.DISABLED); + } + + if (switchedDatabase){ + switchedDatabase = false; + return; + } + focus(); + } + + public void performSearch() { + BasePanel currentBasePanel = frame.getCurrentBasePanel(); + if (currentBasePanel == null) { + return; + } + + if (searchWorker != null) { + searchWorker.cancel(true); + } + + // An empty search field should cause the search to be cleared. + if (searchField.getText().isEmpty()) { + clearSearch(currentBasePanel); + return; + } + + SearchQuery searchQuery = getSearchQuery(); + if (!searchQuery.isValid()) { + informUserAboutInvalidSearchQuery(); + return; + } + + searchWorker = new SearchWorker(currentBasePanel, searchQuery, searchDisplayMode); + searchWorker.execute(); + } + + private void informUserAboutInvalidSearchQuery() { + searchField.setBackground(NO_RESULTS_COLOR); + + searchQueryHighlightObservable.reset(); + + BasePanel currentBasePanel = frame.getCurrentBasePanel(); + currentBasePanel.getMainTable().getTableModel().updateSearchState(MainTableDataModel.DisplayOption.DISABLED); + + searchIcon.setIcon(IconTheme.JabRefIcon.SEARCH.getSmallIcon().createWithNewColor(NO_RESULTS_COLOR)); + String illegalSearch = Localization.lang("Search failed: illegal search expression"); + searchIcon.setToolTipText(illegalSearch); + currentResults.setText(illegalSearch); + openCurrentResultsInDialog.setEnabled(false); + } + + public void setAutoCompleter(AutoCompleter searchCompleter) { + this.autoCompleteSupport.setAutoCompleter(searchCompleter); + } + + public SearchQueryHighlightObservable getSearchQueryHighlightObservable() { + return searchQueryHighlightObservable; + } + + public boolean isStillValidQuery(SearchQuery query) { + return query.getQuery().equals(this.searchField.getText()) + && (query.isRegularExpression() == regularExp.isSelected()) + && (query.isCaseSensitive() == caseSensitive.isSelected()); + } + + private SearchQuery getSearchQuery() { + SearchQuery searchQuery = new SearchQuery(this.searchField.getText(), this.caseSensitive.isSelected(), this.regularExp.isSelected()); + this.frame.getCurrentBasePanel().setCurrentSearchQuery(searchQuery); + return searchQuery; + } + + public void updateResults(int matched, String description, boolean grammarBasedSearch) { + if (matched == 0) { + currentResults.setText(Localization.lang("No results found.")); + this.searchField.setBackground(NO_RESULTS_COLOR); + } else { + currentResults.setText(Localization.lang("Found %0 results.", String.valueOf(matched))); + this.searchField.setBackground(RESULTS_FOUND_COLOR); + } + this.searchField.setToolTipText("" + description + ""); + + if (grammarBasedSearch) { + searchIcon.setIcon(IconTheme.JabRefIcon.SEARCH.getSmallIcon().createWithNewColor(ADVANCED_SEARCH_COLOR)); + searchIcon.setToolTipText(Localization.lang("Advanced search active.")); + } else { + searchIcon.setIcon(IconTheme.JabRefIcon.SEARCH.getSmallIcon()); + searchIcon.setToolTipText(Localization.lang("Normal search active.")); + } + + openCurrentResultsInDialog.setEnabled(true); + } + + public void setSearchResultFrame(SearchResultFrame searchResultFrame) { + this.searchResultFrame = searchResultFrame; + } + + public void setSearchTerm(String searchTerm, boolean switchedDatabase) { + if (searchTerm.equals(searchField.getText())){ + return; + } + + this.switchedDatabase = switchedDatabase; + searchField.setText(searchTerm); + // to hinder the autocomplete window to popup + autoCompleteSupport.setVisible(false); + } + +} diff --git a/src/main/java/net/sf/jabref/gui/search/GlobalSearchWorker.java b/src/main/java/net/sf/jabref/gui/search/GlobalSearchWorker.java index 31d3aaef3b6a..4fdafec6cef2 100644 --- a/src/main/java/net/sf/jabref/gui/search/GlobalSearchWorker.java +++ b/src/main/java/net/sf/jabref/gui/search/GlobalSearchWorker.java @@ -1,51 +1,69 @@ package net.sf.jabref.gui.search; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Objects; +import java.util.concurrent.ExecutionException; import java.util.stream.Collectors; +import javax.swing.SwingWorker; + import net.sf.jabref.gui.BasePanel; import net.sf.jabref.gui.JabRefFrame; -import net.sf.jabref.gui.worker.AbstractWorker; import net.sf.jabref.logic.l10n.Localization; import net.sf.jabref.logic.search.SearchQuery; import net.sf.jabref.model.entry.BibEntry; -class GlobalSearchWorker extends AbstractWorker { +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + + +class GlobalSearchWorker extends SwingWorker>, Void> { + + private static final Log LOGGER = LogFactory.getLog(GlobalSearchWorker.class); private final JabRefFrame frame; private final SearchQuery searchQuery; - private final SearchResultsDialog dialog; + private final SearchResultFrame dialog; public GlobalSearchWorker(JabRefFrame frame, SearchQuery query) { this.frame = Objects.requireNonNull(frame); this.searchQuery = Objects.requireNonNull(query); - dialog = new SearchResultsDialog(frame, + dialog = new SearchResultFrame(frame, Localization.lang("Search results in all databases for %0", - this.searchQuery.localize())); + this.searchQuery.localize()), + searchQuery, true); + frame.getGlobalSearchBar().setSearchResultFrame(dialog); } - /* (non-Javadoc) - * @see net.sf.jabref.Worker#run() - */ @Override - public void run() { - // Search all databases - for (int i = 0; i < frame.getTabbedPane().getTabCount(); i++) { - BasePanel basePanel = frame.getBasePanelAt(i); - List matches = basePanel.getDatabase().getEntries().stream().filter(searchQuery::isMatch).collect(Collectors.toList()); - dialog.addEntries(matches, basePanel); + protected Map> doInBackground() throws Exception { + Map> matches = new HashMap<>(); + for (BasePanel basePanel : frame.getBasePanelList()) { + matches.put(basePanel, basePanel.getDatabase().getEntries().stream() + .filter(searchQuery::isMatch) + .collect(Collectors.toList())); } + return matches; } - /* (non-Javadoc) - * @see net.sf.jabref.AbstractWorker#update() - */ @Override - public void update() { - dialog.selectFirstEntry(); - dialog.setVisible(true); + protected void done() { + if (isCancelled()) { + return; + } + + try { + for (Map.Entry> match : get().entrySet()) { + dialog.addEntries(match.getValue(), match.getKey()); + } + dialog.selectFirstEntry(); + dialog.setVisible(true); + } catch (InterruptedException | ExecutionException e) { + LOGGER.error("something went wrong during the search", e); + } } -} \ No newline at end of file +} diff --git a/src/main/java/net/sf/jabref/gui/search/SearchBar.java b/src/main/java/net/sf/jabref/gui/search/SearchBar.java deleted file mode 100644 index 202ac48aeca5..000000000000 --- a/src/main/java/net/sf/jabref/gui/search/SearchBar.java +++ /dev/null @@ -1,370 +0,0 @@ -package net.sf.jabref.gui.search; - -import java.awt.Color; -import java.awt.Component; -import java.awt.Container; -import java.awt.FlowLayout; -import java.awt.Font; -import java.awt.event.KeyAdapter; -import java.awt.event.KeyEvent; -import java.io.File; -import java.util.List; -import java.util.Objects; -import java.util.stream.Collectors; - -import javax.swing.JButton; -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.JToggleButton; -import javax.swing.JToolBar; - -import net.sf.jabref.Globals; -import net.sf.jabref.gui.BasePanel; -import net.sf.jabref.gui.GUIGlobals; -import net.sf.jabref.gui.IconTheme; -import net.sf.jabref.gui.OSXCompatibleToolbar; -import net.sf.jabref.gui.WrapLayout; -import net.sf.jabref.gui.autocompleter.AutoCompleteSupport; -import net.sf.jabref.gui.help.HelpAction; -import net.sf.jabref.gui.maintable.MainTableDataModel; -import net.sf.jabref.gui.util.component.JTextFieldWithUnfocusedText; -import net.sf.jabref.gui.worker.AbstractWorker; -import net.sf.jabref.logic.autocompleter.AutoCompleter; -import net.sf.jabref.logic.help.HelpFile; -import net.sf.jabref.logic.l10n.Localization; -import net.sf.jabref.logic.search.SearchQuery; -import net.sf.jabref.logic.search.SearchQueryHighlightObservable; -import net.sf.jabref.logic.util.OS; -import net.sf.jabref.model.entry.BibEntry; -import net.sf.jabref.preferences.JabRefPreferences; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -/** - * The search bar at the top of the screen allowing the user to search his database. - */ -public class SearchBar extends JPanel { - - private static final Log LOGGER = LogFactory.getLog(SearchBar.class); - - private static final Color NO_RESULTS_COLOR = new Color(232, 202, 202); - private static final Color RESULTS_FOUND_COLOR = new Color(217, 232, 202); - private static final Color ADVANCED_SEARCH_COLOR = new Color(102, 255, 255); - - private final JButton openCurrentResultsInDialog; - private final JButton globalSearch; - private final JButton searchModeButton; - - private final BasePanel basePanel; - - private final SearchQueryHighlightObservable searchQueryHighlightObservable; - private final JTextFieldWithUnfocusedText searchField = new JTextFieldWithUnfocusedText( - Localization.lang("Search") + "..."); - - private SearchMode searchMode = getSearchModeFromSettings(); - - private final JToggleButton caseSensitive; - private final JToggleButton regularExp; - - private final JLabel currentResults = new JLabel(""); - - private AutoCompleteSupport autoCompleteSupport; - private final JLabel searchIcon; - private SearchWorker searchWorker; - - - /** - * Initializes the search bar. - * - * @param basePanel the base panel - */ - public SearchBar(BasePanel basePanel) { - super(); - - this.basePanel = Objects.requireNonNull(basePanel); - this.searchQueryHighlightObservable = new SearchQueryHighlightObservable(); - - currentResults.setFont(currentResults.getFont().deriveFont(Font.BOLD)); - - caseSensitive = new JToggleButton(IconTheme.JabRefIcon.CASE_SENSITIVE.getSmallIcon(), - Globals.prefs.getBoolean(JabRefPreferences.SEARCH_CASE_SENSITIVE)); - caseSensitive.setToolTipText(Localization.lang("Case sensitive")); - caseSensitive.addActionListener(e -> { - performSearch(); - updatePreferences(); - }); - - regularExp = new JToggleButton(IconTheme.JabRefIcon.REG_EX.getSmallIcon(), - Globals.prefs.getBoolean(JabRefPreferences.SEARCH_REG_EXP)); - regularExp.setToolTipText(Localization.lang("regular expression")); - regularExp.addActionListener(e -> { - performSearch(); - updatePreferences(); - }); - - openCurrentResultsInDialog = new JButton(IconTheme.JabRefIcon.OPEN_IN_NEW_WINDOW.getSmallIcon()); - openCurrentResultsInDialog.setToolTipText(Localization.lang("Show search results in a window")); - openCurrentResultsInDialog.addActionListener(ae -> { - SearchResultsDialog searchDialog = new SearchResultsDialog(basePanel.frame(), - Localization.lang( - "Search results in database %0 for %1", basePanel.getBibDatabaseContext().getDatabaseFile() - .map(File::getName).orElse(GUIGlobals.UNTITLED_TITLE), - this.getSearchQuery().localize())); - List entries = basePanel.getDatabase().getEntries().stream().filter(BibEntry::isSearchHit) - .collect(Collectors.toList()); - searchDialog.addEntries(entries, basePanel); - searchDialog.selectFirstEntry(); - searchDialog.setVisible(true); - }); - openCurrentResultsInDialog.setEnabled(false); - - // Init controls - setLayout(new WrapLayout(FlowLayout.LEFT)); - - searchIcon = new JLabel(IconTheme.JabRefIcon.SEARCH.getSmallIcon()); - this.add(searchIcon); - initSearchField(); - if (OS.OS_X) { - searchField.putClientProperty("JTextField.variant", "search"); - } - this.add(searchField); - - JButton clearSearchButton = new JButton(IconTheme.JabRefIcon.CLOSE.getSmallIcon()); - clearSearchButton.setToolTipText(Localization.lang("Clear")); - clearSearchButton.addActionListener(l -> endSearch()); - - this.add(clearSearchButton); - - searchModeButton = new JButton(); - updateSearchModeButtonText(); - searchModeButton.addActionListener(l -> toggleSearchModeAndSearch()); - - JToolBar toolBar = new OSXCompatibleToolbar(); - toolBar.setFloatable(false); - toolBar.add(clearSearchButton); - toolBar.addSeparator(); - toolBar.add(regularExp); - toolBar.add(caseSensitive); - toolBar.addSeparator(); - toolBar.add(searchModeButton); - toolBar.addSeparator(); - toolBar.add(openCurrentResultsInDialog); - globalSearch = new JButton(Localization.lang("Search globally")); - globalSearch.setToolTipText(Localization.lang("Search in all open databases")); - globalSearch.addActionListener(l -> { - AbstractWorker worker = new GlobalSearchWorker(basePanel.frame(), getSearchQuery()); - worker.run(); - worker.update(); - }); - globalSearch.setEnabled(false); - toolBar.add(globalSearch); - toolBar.addSeparator(); - toolBar.add(new HelpAction(HelpFile.SEARCH)); - - this.add(toolBar); - this.add(currentResults); - - paintBackgroundWhite(this); - } - - private void paintBackgroundWhite(Container container) { - container.setBackground(Color.WHITE); - for (Component component : container.getComponents()) { - component.setBackground(Color.WHITE); - - if (component instanceof Container) { - paintBackgroundWhite((Container) component); - } - } - } - - private static SearchMode getSearchModeFromSettings() { - if (Globals.prefs.getBoolean(JabRefPreferences.SEARCH_MODE_FILTER)) { - return SearchMode.FILTER; - } else if (Globals.prefs.getBoolean(JabRefPreferences.SEARCH_MODE_FLOAT)) { - return SearchMode.FLOAT; - } else { - return SearchMode.FILTER; - } - } - - private void toggleSearchModeAndSearch() { - this.searchMode = searchMode == SearchMode.FILTER ? SearchMode.FLOAT : SearchMode.FILTER; - updatePreferences(); - updateSearchModeButtonText(); - performSearch(); - } - - private void updateSearchModeButtonText() { - searchModeButton.setText(searchMode.getDisplayName()); - searchModeButton.setToolTipText(searchMode.getToolTipText()); - } - - /** - * Initializes the search text field - */ - private void initSearchField() { - searchField.setColumns(30); - searchField.addKeyListener(new KeyAdapter() { - - @Override - public void keyReleased(KeyEvent e) { - if (e.getExtendedKeyCode() == KeyEvent.VK_ESCAPE) { - endSearch(); - } - } - }); - - // Add autocompleter - autoCompleteSupport = new AutoCompleteSupport<>(searchField); - autoCompleteSupport.install(); - - // Add the global focus listener, so a menu item can see if this field was focused when an action was called. - searchField.addFocusListener(Globals.getFocusListener()); - - // Search if user press enter - searchField.addActionListener(e -> performSearch()); - - // Subscribe to changes to the text in the search field in order to "live search" - JTextFieldChangeListenerUtil.addChangeListener(searchField, e -> performSearch()); - - } - - private void endSearch() { - // first focus request is necessary so that the UI stays nice - basePanel.getMainTable().requestFocus(); - clearSearch(); - basePanel.getMainTable().requestFocus(); - } - - /** - * Save current settings. - */ - private void updatePreferences() { - Globals.prefs.putBoolean(JabRefPreferences.SEARCH_MODE_FLOAT, searchMode == SearchMode.FLOAT); - Globals.prefs.putBoolean(JabRefPreferences.SEARCH_MODE_FILTER, searchMode == SearchMode.FILTER); - - Globals.prefs.putBoolean(JabRefPreferences.SEARCH_CASE_SENSITIVE, caseSensitive.isSelected()); - Globals.prefs.putBoolean(JabRefPreferences.SEARCH_REG_EXP, regularExp.isSelected()); - } - - /** - * Focuses the search field if it is not focused. - */ - public void focus() { - if (!searchField.hasFocus()) { - searchField.requestFocus(); - } - } - - /** - * Clears the current search. This includes resetting the search text. - */ - private void clearSearch() { - searchField.setText(""); - searchField.setBackground(Color.WHITE); - - searchQueryHighlightObservable.reset(); - - this.currentResults.setText(""); - - basePanel.getMainTable().getTableModel().updateSearchState(MainTableDataModel.DisplayOption.DISABLED); - - globalSearch.setEnabled(false); - openCurrentResultsInDialog.setEnabled(false); - - searchIcon.setIcon(IconTheme.JabRefIcon.SEARCH.getSmallIcon()); - } - - /** - * Performs a new search based on the current search query. - */ - private void performSearch() { - if (searchWorker != null) { - searchWorker.cancel(true); - } - - // An empty search field should cause the search to be cleared. - if (searchField.getText().isEmpty()) { - clearSearch(); - return; - } - - SearchQuery searchQuery = getSearchQuery(); - LOGGER.debug("Searching " + searchQuery + " in " + basePanel.getTabTitle()); - - if (!searchQuery.isValid()) { - informUserAboutInvalidSearchQuery(); - - return; - } - - searchWorker = new SearchWorker(basePanel, searchQuery, searchMode); - searchWorker.execute(); - } - - private void informUserAboutInvalidSearchQuery() { - searchField.setBackground(NO_RESULTS_COLOR); - - searchQueryHighlightObservable.reset(); - - globalSearch.setEnabled(false); - openCurrentResultsInDialog.setEnabled(false); - - basePanel.getMainTable().getTableModel().updateSearchState(MainTableDataModel.DisplayOption.DISABLED); - - searchIcon.setIcon(IconTheme.JabRefIcon.SEARCH.getSmallIcon().createWithNewColor(NO_RESULTS_COLOR)); - searchIcon.setToolTipText(Localization.lang("Search failed: illegal search expression")); - - currentResults.setText(Localization.lang("Search failed: illegal search expression")); - } - - /** - * Sets the autocompleter used in the search field. - * - * @param searchCompleter the autocompleter - */ - public void setAutoCompleter(AutoCompleter searchCompleter) { - this.autoCompleteSupport.setAutoCompleter(searchCompleter); - } - - public SearchQueryHighlightObservable getSearchQueryHighlightObservable() { - return searchQueryHighlightObservable; - } - - boolean isStillValidQuery(SearchQuery query) { - return query.getQuery().equals(this.searchField.getText()) - && (query.isRegularExpression() == regularExp.isSelected()) - && (query.isCaseSensitive() == caseSensitive.isSelected()); - } - - private SearchQuery getSearchQuery() { - return new SearchQuery(this.searchField.getText(), this.caseSensitive.isSelected(), - this.regularExp.isSelected()); - } - - void updateResults(int matched, String description, boolean grammarBasedSearch) { - if (matched == 0) { - // nothing found - this.currentResults.setText(Localization.lang("No results found.")); - this.searchField.setBackground(NO_RESULTS_COLOR); - } else { - // specific set found, could be all - this.currentResults.setText(Localization.lang("Found %0 results.", String.valueOf(matched))); - this.searchField.setBackground(RESULTS_FOUND_COLOR); - } - this.searchField.setToolTipText("" + description + ""); - - if (grammarBasedSearch) { - searchIcon.setIcon(IconTheme.JabRefIcon.SEARCH.getSmallIcon().createWithNewColor(ADVANCED_SEARCH_COLOR)); - searchIcon.setToolTipText(Localization.lang("Advanced search active.")); - } else { - searchIcon.setIcon(IconTheme.JabRefIcon.SEARCH.getSmallIcon()); - searchIcon.setToolTipText(Localization.lang("Normal search active.")); - } - - globalSearch.setEnabled(true); - openCurrentResultsInDialog.setEnabled(true); - } -} diff --git a/src/main/java/net/sf/jabref/gui/search/SearchMode.java b/src/main/java/net/sf/jabref/gui/search/SearchDisplayMode.java similarity index 61% rename from src/main/java/net/sf/jabref/gui/search/SearchMode.java rename to src/main/java/net/sf/jabref/gui/search/SearchDisplayMode.java index 3bb5210946df..6ca05cbc6029 100644 --- a/src/main/java/net/sf/jabref/gui/search/SearchMode.java +++ b/src/main/java/net/sf/jabref/gui/search/SearchDisplayMode.java @@ -5,18 +5,15 @@ /** * Collects the possible search modes */ -public enum SearchMode { +public enum SearchDisplayMode { - FLOAT(Localization.lang("Float"), - Localization.lang("Gray out non-hits")), - FILTER(Localization.lang("Filter"), - Localization.lang("Hide non-hits")) - ; + FLOAT(Localization.lang("Float"), Localization.lang("Gray out non-hits")), + FILTER(Localization.lang("Filter"), Localization.lang("Hide non-hits")); private final String displayName; private final String toolTipText; - SearchMode(String displayName, String toolTipText) { + SearchDisplayMode(String displayName, String toolTipText) { this.displayName = displayName; this.toolTipText = toolTipText; } @@ -28,4 +25,5 @@ public String getDisplayName() { public String getToolTipText() { return toolTipText; } -} \ No newline at end of file + +} diff --git a/src/main/java/net/sf/jabref/gui/search/SearchResultsDialog.java b/src/main/java/net/sf/jabref/gui/search/SearchResultFrame.java similarity index 74% rename from src/main/java/net/sf/jabref/gui/search/SearchResultsDialog.java rename to src/main/java/net/sf/jabref/gui/search/SearchResultFrame.java index 9a8de60bcaa3..81f67ac55a99 100644 --- a/src/main/java/net/sf/jabref/gui/search/SearchResultsDialog.java +++ b/src/main/java/net/sf/jabref/gui/search/SearchResultFrame.java @@ -2,10 +2,12 @@ import java.awt.BorderLayout; import java.awt.Color; -import java.awt.Dimension; import java.awt.Rectangle; import java.awt.Toolkit; import java.awt.event.ActionEvent; +import java.awt.event.ComponentAdapter; +import java.awt.event.ComponentEvent; +import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.WindowAdapter; @@ -17,17 +19,19 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; import javax.swing.AbstractAction; import javax.swing.ActionMap; import javax.swing.InputMap; import javax.swing.JComponent; -import javax.swing.JDialog; +import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPopupMenu; import javax.swing.JScrollPane; import javax.swing.JSplitPane; import javax.swing.JTable; +import javax.swing.KeyStroke; import javax.swing.SwingUtilities; import javax.swing.table.TableColumnModel; @@ -50,12 +54,15 @@ import net.sf.jabref.logic.bibtex.comparator.EntryComparator; import net.sf.jabref.logic.bibtex.comparator.FieldComparator; import net.sf.jabref.logic.l10n.Localization; +import net.sf.jabref.logic.layout.format.LatexToUnicodeFormatter; +import net.sf.jabref.logic.search.SearchQuery; import net.sf.jabref.model.entry.BibEntry; import net.sf.jabref.model.entry.EntryUtil; import net.sf.jabref.model.entry.FieldName; import net.sf.jabref.model.entry.FieldProperty; import net.sf.jabref.model.entry.InternalBibtexFields; import net.sf.jabref.preferences.JabRefPreferences; +import net.sf.jabref.preferences.SearchPreferences; import ca.odell.glazedlists.BasicEventList; import ca.odell.glazedlists.EventList; @@ -75,19 +82,20 @@ * Dialog to display search results, potentially from more than one BasePanel, with * possibility to preview and to locate each entry in the main window. */ -public class SearchResultsDialog { +public class SearchResultFrame { - private static final Log LOGGER = LogFactory.getLog(SearchResultsDialog.class); + private static final Log LOGGER = LogFactory.getLog(SearchResultFrame.class); private final JabRefFrame frame; - private JDialog diag; + private JFrame searchResultFrame; private static final String[] FIELDS = new String[] { FieldName.AUTHOR, FieldName.TITLE, FieldName.YEAR, FieldName.JOURNAL }; - private static final int FILE_COL = 0; - private static final int URL_COL = 1; - private static final int PAD = 2; + private static final int DATABASE_COL = 0; + private static final int FILE_COL = 1; + private static final int URL_COL = 2; + private static final int PAD = 3; private final JLabel fileLabel = new JLabel(IconTheme.JabRefIcon.FILE.getSmallIcon()); private final JLabel urlLabel = new JLabel(IconTheme.JabRefIcon.WWW.getSmallIcon()); @@ -103,14 +111,22 @@ public class SearchResultsDialog { private JTable entryTable; private PreviewPanel preview; + private SearchQuery searchQuery; + private boolean globalSearch; - public SearchResultsDialog(JabRefFrame frame, String title) { + + public SearchResultFrame(JabRefFrame frame, String title, SearchQuery searchQuery, boolean globalSearch) { this.frame = Objects.requireNonNull(frame); + this.searchQuery = searchQuery; + this.globalSearch = globalSearch; + frame.getGlobalSearchBar().setSearchResultFrame(this); init(Objects.requireNonNull(title)); } private void init(String title) { - diag = new JDialog(frame, title, false); + searchResultFrame = new JFrame(); + searchResultFrame.setTitle(title); + searchResultFrame.setIconImage(IconTheme.getImage("jabrefIcon48").getImage()); int activePreview = Globals.prefs.getInt(JabRefPreferences.ACTIVE_PREVIEW); String layoutFile = activePreview == 0 ? Globals.prefs.get(JabRefPreferences.PREVIEW_0) : Globals.prefs @@ -144,15 +160,16 @@ private void init(String title) { // Key bindings: AbstractAction closeAction = new AbstractAction() { - @Override public void actionPerformed(ActionEvent e) { - diag.dispose(); + dispose(); } }; + ActionMap am = contentPane.getActionMap(); InputMap im = contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW); im.put(Globals.getKeyPrefs().getKey(KeyBinding.CLOSE_DIALOG), "close"); + im.put(Globals.getKeyPrefs().getKey(KeyBinding.CLOSE_DATABASE), "close"); am.put("close", closeAction); entryTable.getActionMap().put("copy", new AbstractAction() { @@ -172,8 +189,17 @@ public void actionPerformed(ActionEvent e) { } }); - diag.addWindowListener(new WindowAdapter() { + // override standard enter-action; enter opens the selected entry + entryTable.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "Enter"); + entryTable.getActionMap().put("Enter", new AbstractAction() { + @Override + public void actionPerformed(ActionEvent ae) { + BibEntry entry = sortedEntries.get(entryTable.getSelectedRow()); + selectEntryInBasePanel(entry); + } + }); + searchResultFrame.addWindowListener(new WindowAdapter() { @Override public void windowOpened(WindowEvent e) { contentPane.setDividerLocation(0.5f); @@ -181,16 +207,32 @@ public void windowOpened(WindowEvent e) { @Override public void windowClosing(WindowEvent event) { - Globals.prefs.putInt(JabRefPreferences.SEARCH_DIALOG_WIDTH, diag.getSize().width); - Globals.prefs.putInt(JabRefPreferences.SEARCH_DIALOG_HEIGHT, diag.getSize().height); + dispose(); } }); - diag.getContentPane().add(contentPane, BorderLayout.CENTER); + searchResultFrame.getContentPane().add(contentPane, BorderLayout.CENTER); + // Remember and default to last size: - diag.setSize(new Dimension(Globals.prefs.getInt(JabRefPreferences.SEARCH_DIALOG_WIDTH), Globals.prefs - .getInt(JabRefPreferences.SEARCH_DIALOG_HEIGHT))); - diag.setLocationRelativeTo(frame); + SearchPreferences searchPreferences = new SearchPreferences(Globals.prefs); + searchResultFrame.setSize(searchPreferences.getSeachDialogWidth(), searchPreferences.getSeachDialogHeight()); + searchResultFrame.setLocation(searchPreferences.getSearchDialogPosX(), searchPreferences.getSearchDialogPosY()); + + searchResultFrame.addComponentListener(new ComponentAdapter() { + @Override + public void componentResized(ComponentEvent e) { + new SearchPreferences(Globals.prefs) + .setSearchDialogWidth(searchResultFrame.getSize().width) + .setSearchDialogHeight(searchResultFrame.getSize().height); + } + + @Override + public void componentMoved(ComponentEvent e) { + new SearchPreferences(Globals.prefs) + .setSearchDialogPosX(searchResultFrame.getLocation().x) + .setSearchDialogPosY(searchResultFrame.getLocation().y); + } + }); } /** @@ -198,7 +240,7 @@ public void windowClosing(WindowEvent event) { * @param visible true to show dialog, false to hide. */ public void setVisible(boolean visible) { - diag.setVisible(visible); + searchResultFrame.setVisible(visible); } public void selectFirstEntry() { @@ -215,13 +257,7 @@ public void selectFirstEntry() { * @param comparatorChooser The comparator chooser controlling the sort order. */ private void setupComparatorChooser(TableComparatorChooser comparatorChooser) { - // First column: - List comparators = comparatorChooser.getComparatorsForColumn(0); - comparators.clear(); - - comparators = comparatorChooser.getComparatorsForColumn(1); - comparators.clear(); - + List comparators; // Icon columns: for (int i = 0; i < PAD; i++) { comparators = comparatorChooser.getComparatorsForColumn(i); @@ -230,6 +266,12 @@ private void setupComparatorChooser(TableComparatorChooser comparatorC comparators.add(new IconComparator(Collections.singletonList(FieldName.FILE))); } else if (i == URL_COL) { comparators.add(new IconComparator(Collections.singletonList(FieldName.URL))); + } else if (i == DATABASE_COL) { + comparators.add((entry1, entry2) -> { + String databaseTitle1 = entryHome.get(entry1).getTabTitle(); + String databaseTitle2 = entryHome.get(entry2).getTabTitle(); + return databaseTitle1.compareTo(databaseTitle2); + }); } } @@ -252,15 +294,25 @@ private void setupComparatorChooser(TableComparatorChooser comparatorC */ private void setWidths() { TableColumnModel cm = entryTable.getColumnModel(); - for (int i = 0; i < PAD; i++) { - cm.getColumn(i).setPreferredWidth(GUIGlobals.WIDTH_ICON_COL); - cm.getColumn(i).setMinWidth(GUIGlobals.WIDTH_ICON_COL); - cm.getColumn(i).setMaxWidth(GUIGlobals.WIDTH_ICON_COL); - } - - for (int i = 0; i < FIELDS.length; i++) { - int width = InternalBibtexFields.getFieldLength(FIELDS[i]); - cm.getColumn(i + PAD).setPreferredWidth(width); + for (int i = 0; i < PAD + FIELDS.length; i++) { + switch (i) { + case FILE_COL: + case URL_COL: + cm.getColumn(i).setPreferredWidth(GUIGlobals.WIDTH_ICON_COL); + cm.getColumn(i).setMinWidth(GUIGlobals.WIDTH_ICON_COL); + cm.getColumn(i).setMaxWidth(GUIGlobals.WIDTH_ICON_COL); + break; + case DATABASE_COL: { + int width = InternalBibtexFields.getFieldLength(FieldName.AUTHOR); + cm.getColumn(i).setPreferredWidth(width); + break; + } + default: { + int width = InternalBibtexFields.getFieldLength(FIELDS[i - PAD]); + cm.getColumn(i).setPreferredWidth(width); + break; + } + } } } @@ -285,6 +337,31 @@ private void addEntry(BibEntry entry, BasePanel panel) { entryHome.put(entry, panel); } + private void selectEntryInBasePanel(BibEntry entry){ + BasePanel basePanel = entryHome.get(entry); + frame.showBasePanel(basePanel); + basePanel.requestFocus(); + basePanel.highlightEntry(entry); + } + + public void dispose(){ + frame.getGlobalSearchBar().setSearchResultFrame(null); + searchResultFrame.dispose(); + frame.getGlobalSearchBar().focus(); + } + + public void focus(){ + entryTable.requestFocus(); + } + + public SearchQuery getSearchQuery() { + return searchQuery; + } + + public boolean isGlobalSearch() { + return globalSearch; + } + /** * Mouse listener for the entry table. Processes icon clicks to open external * files or urls, as well as the opening of the context menu. @@ -310,14 +387,7 @@ public void mousePressed(MouseEvent e) { // A double click on an entry should highlight the entry in its BasePanel: if (e.getClickCount() == 2) { - // Get the selected entry: - BibEntry toShow = model.getElementAt(row); - // Look up which BasePanel it belongs to: - BasePanel p = entryHome.get(toShow); - // Show the correct tab in the main window: - frame.showBasePanel(p); - // Highlight the entry: - p.highlightEntry(toShow); + selectEntryInBasePanel(model.getElementAt(row)); } } @@ -409,9 +479,9 @@ public void listChanged(ListEvent listEvent) { if (listEvent.getSourceList().size() == 1) { BibEntry entry = listEvent.getSourceList().get(0); // Find out which BasePanel the selected entry belongs to: - BasePanel p = entryHome.get(entry); + BasePanel basePanel = entryHome.get(entry); // Update the preview's database context: - preview.setDatabaseContext(p.getBibDatabaseContext()); + preview.setDatabaseContext(basePanel.getBibDatabaseContext()); // Update the preview's entry: preview.setEntry(entry); contentPane.setDividerLocation(0.5f); @@ -435,6 +505,8 @@ public int getColumnCount() { public String getColumnName(int column) { if (column >= PAD) { return EntryUtil.capitalizeFirst(FIELDS[column - PAD]); + } else if (column == DATABASE_COL){ + return Localization.lang("Database"); } else { return ""; } @@ -444,6 +516,8 @@ public String getColumnName(int column) { public Object getColumnValue(BibEntry entry, int column) { if (column < PAD) { switch (column) { + case DATABASE_COL: + return entryHome.get(entry).getTabTitle(); case FILE_COL: if (entry.hasField(FieldName.FILE)) { FileListTableModel tmpModel = new FileListTableModel(); @@ -460,36 +534,41 @@ public Object getColumnValue(BibEntry entry, int column) { } else { return null; } - case URL_COL: - if (entry.hasField(FieldName.URL)) { - urlLabel.setToolTipText(entry.getField(FieldName.URL).get()); + case URL_COL: { + Optional urlField = entry.getField(FieldName.URL); + if (urlField.isPresent()) { + urlLabel.setToolTipText(urlField.get()); return urlLabel; - } else { - return null; } + return null; + } default: return null; } } else { String field = FIELDS[column - PAD]; + + String fieldContent = entry.getField(field).orElse(""); + fieldContent = new LatexToUnicodeFormatter().format(fieldContent); + if (InternalBibtexFields.getFieldProperties(field).contains(FieldProperty.PERSON_NAMES)) { // For name fields, tap into a MainTableFormat instance and use // the same name formatting as is used in the entry table: - if (frame.getCurrentBasePanel() != null) { - return MainTableNameFormatter.formatName(entry.getField(field).orElse(null)); - } + return MainTableNameFormatter.formatName(fieldContent); } - return entry.getField(field).orElse(null); + return fieldContent; } } @Override public Class getColumnClass(int i) { - if (i < PAD) { - return JLabel.class; - } else { - return String.class; + switch (i) { + case FILE_COL: + case URL_COL: + return JLabel.class; + default: + return String.class; } } @@ -498,4 +577,5 @@ public Comparator getColumnComparator(int i) { return null; } } + } diff --git a/src/main/java/net/sf/jabref/gui/search/SearchWorker.java b/src/main/java/net/sf/jabref/gui/search/SearchWorker.java index 355627dcedb2..0409ddb79ded 100644 --- a/src/main/java/net/sf/jabref/gui/search/SearchWorker.java +++ b/src/main/java/net/sf/jabref/gui/search/SearchWorker.java @@ -1,6 +1,5 @@ package net.sf.jabref.gui.search; -import java.util.LinkedList; import java.util.List; import java.util.Objects; import java.util.concurrent.ExecutionException; @@ -8,6 +7,7 @@ import javax.swing.SwingWorker; +import net.sf.jabref.JabRefGUI; import net.sf.jabref.gui.BasePanel; import net.sf.jabref.gui.maintable.MainTableDataModel; import net.sf.jabref.logic.search.SearchQuery; @@ -17,6 +17,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; + /** * Not reusable. Always create a new instance for each search! */ @@ -28,22 +29,21 @@ class SearchWorker extends SwingWorker, Void> { private final BibDatabase database; private final SearchQuery searchQuery; - private final SearchMode mode; + private final SearchDisplayMode searchDisplayMode; - SearchWorker(BasePanel basePanel, SearchQuery searchQuery, SearchMode mode) { + public SearchWorker(BasePanel basePanel, SearchQuery searchQuery, SearchDisplayMode searchDisplayMode) { this.basePanel = Objects.requireNonNull(basePanel); this.database = Objects.requireNonNull(basePanel.getDatabase()); this.searchQuery = Objects.requireNonNull(searchQuery); - this.mode = Objects.requireNonNull(mode); - LOGGER.debug("Search (" + this.mode.getDisplayName() + "): " + this.searchQuery); + this.searchDisplayMode = Objects.requireNonNull(searchDisplayMode); + LOGGER.debug("Search (" + this.searchDisplayMode.getDisplayName() + "): " + this.searchQuery); } @Override protected List doInBackground() throws Exception { - // Search the current database - List matchedEntries = new LinkedList<>(); - matchedEntries.addAll(database.getEntries().stream().filter(searchQuery::isMatch).collect(Collectors.toList())); - return matchedEntries; + return database.getEntries().stream() + .filter(searchQuery::isMatch) + .collect(Collectors.toList()); } @Override @@ -60,9 +60,10 @@ protected void done() { } private void updateUIWithSearchResult(List matchedEntries) { + GlobalSearchBar globalSearchBar = JabRefGUI.getMainFrame().getGlobalSearchBar(); // check if still the current query - if (!basePanel.getSearchBar().isStillValidQuery(searchQuery)) { + if (!globalSearchBar.isStillValidQuery(searchQuery)) { // do not update - another search was already issued return; } @@ -71,33 +72,40 @@ private void updateUIWithSearchResult(List matchedEntries) { for (BibEntry entry : basePanel.getDatabase().getEntries()) { entry.setSearchHit(false); } - + // and mark for (BibEntry entry : matchedEntries) { entry.setSearchHit(true); } basePanel.getMainTable().getTableModel().updateSearchState(MainTableDataModel.DisplayOption.DISABLED); - // Show the result in the chosen way: - switch (mode) { - case FLOAT: - basePanel.getMainTable().getTableModel().updateSearchState(MainTableDataModel.DisplayOption.FLOAT); - break; - case FILTER: - basePanel.getMainTable().getTableModel().updateSearchState(MainTableDataModel.DisplayOption.FILTER); - break; - default: - break; + switch (searchDisplayMode) { + case FLOAT: + basePanel.getMainTable().getTableModel().updateSearchState(MainTableDataModel.DisplayOption.FLOAT); + break; + case FILTER: + basePanel.getMainTable().getTableModel().updateSearchState(MainTableDataModel.DisplayOption.FILTER); + break; + default: + LOGGER.error("Following searchDisplayMode was not defined: " + searchDisplayMode); + break; } - // select first match (i.e., row) if there is any - int hits = matchedEntries.size(); - if ((hits > 0) && (basePanel.getMainTable().getRowCount() > 0)) { - basePanel.getMainTable().setSelected(0); + // only selects the first match if the selected entries are no hits or no entry is selected + List selectedEntries = basePanel.getSelectedEntries(); + boolean isHitSelected = selectedEntries.stream().anyMatch(BibEntry::isSearchHit); + if (!isHitSelected && !matchedEntries.isEmpty()) { + for (int i = 0; i < basePanel.getMainTable().getRowCount(); i++) { + BibEntry entry = basePanel.getMainTable().getEntryAt(i); + if (entry.isSearchHit()) { + basePanel.getMainTable().setSelected(i); + break; + } + } } - basePanel.getSearchBar().updateResults(hits, searchQuery.getDescription(), searchQuery.isGrammarBasedSearch()); - basePanel.getSearchBar().getSearchQueryHighlightObservable().fireSearchlistenerEvent(searchQuery); + globalSearchBar.updateResults(matchedEntries.size(), searchQuery.getDescription(), searchQuery.isGrammarBasedSearch()); + globalSearchBar.getSearchQueryHighlightObservable().fireSearchlistenerEvent(searchQuery); } } diff --git a/src/main/java/net/sf/jabref/logic/autocompleter/AbstractAutoCompleter.java b/src/main/java/net/sf/jabref/logic/autocompleter/AbstractAutoCompleter.java index 2edb7e9c427d..53498d0affb9 100644 --- a/src/main/java/net/sf/jabref/logic/autocompleter/AbstractAutoCompleter.java +++ b/src/main/java/net/sf/jabref/logic/autocompleter/AbstractAutoCompleter.java @@ -9,6 +9,8 @@ import java.util.SortedSet; import java.util.TreeSet; +import net.sf.jabref.logic.layout.format.LatexToUnicodeFormatter; + /** * Delivers possible completions for a given string. * @@ -103,6 +105,8 @@ public void addItemToIndex(String word) { return; } + word = new LatexToUnicodeFormatter().format(word); + indexCaseSensitive.add(word); // insensitive treatment diff --git a/src/main/java/net/sf/jabref/logic/search/SearchQueryHighlightObservable.java b/src/main/java/net/sf/jabref/logic/search/SearchQueryHighlightObservable.java index 0deafe75b1fe..22768cfa6211 100644 --- a/src/main/java/net/sf/jabref/logic/search/SearchQueryHighlightObservable.java +++ b/src/main/java/net/sf/jabref/logic/search/SearchQueryHighlightObservable.java @@ -20,19 +20,17 @@ public class SearchQueryHighlightObservable { * Adds a SearchQueryHighlightListener to the search bar. The added listener is immediately informed about the current search. * Subscribers will be notified about searches. * - * @param l SearchQueryHighlightListener to be added + * @param newListener SearchQueryHighlightListener to be added */ - public void addSearchListener(SearchQueryHighlightListener l) { - Objects.requireNonNull(l); + public SearchQueryHighlightObservable addSearchListener(SearchQueryHighlightListener newListener) { + Objects.requireNonNull(newListener); - if (listeners.contains(l)) { - return; - } else { - listeners.add(l); + if (!listeners.contains(newListener)) { + listeners.add(newListener); + newListener.highlightPattern(pattern); } - // fire event for the new subscriber - l.highlightPattern(pattern); + return this; } public int getListenerCount() { diff --git a/src/main/java/net/sf/jabref/logic/search/rules/ContainBasedSearchRule.java b/src/main/java/net/sf/jabref/logic/search/rules/ContainBasedSearchRule.java index e35b7b34dc35..b9c7f043917a 100644 --- a/src/main/java/net/sf/jabref/logic/search/rules/ContainBasedSearchRule.java +++ b/src/main/java/net/sf/jabref/logic/search/rules/ContainBasedSearchRule.java @@ -3,7 +3,7 @@ import java.util.Iterator; import java.util.List; -import net.sf.jabref.logic.layout.format.RemoveLatexCommands; +import net.sf.jabref.logic.layout.format.LatexToUnicodeFormatter; import net.sf.jabref.model.entry.BibEntry; /** @@ -11,7 +11,7 @@ */ public class ContainBasedSearchRule implements SearchRule { - private static final RemoveLatexCommands REMOVE_LATEX_COMMANDS = new RemoveLatexCommands(); + private static final LatexToUnicodeFormatter LATEX_TO_UNICODE_FORMATTER = new LatexToUnicodeFormatter(); private final boolean caseSensitive; @@ -39,7 +39,7 @@ public boolean applyRule(String query, BibEntry bibEntry) { List unmatchedWords = new SentenceAnalyzer(searchString).getWords(); for (String fieldContent : bibEntry.getFieldValues()) { - String formattedFieldContent = ContainBasedSearchRule.REMOVE_LATEX_COMMANDS.format(fieldContent); + String formattedFieldContent = ContainBasedSearchRule.LATEX_TO_UNICODE_FORMATTER.format(fieldContent); if (!caseSensitive) { formattedFieldContent = formattedFieldContent.toLowerCase(); } diff --git a/src/main/java/net/sf/jabref/logic/search/rules/RegexBasedSearchRule.java b/src/main/java/net/sf/jabref/logic/search/rules/RegexBasedSearchRule.java index 5a71f63e6df7..ec499f6e2a42 100644 --- a/src/main/java/net/sf/jabref/logic/search/rules/RegexBasedSearchRule.java +++ b/src/main/java/net/sf/jabref/logic/search/rules/RegexBasedSearchRule.java @@ -1,10 +1,11 @@ package net.sf.jabref.logic.search.rules; +import java.util.Optional; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; -import net.sf.jabref.logic.layout.format.RemoveLatexCommands; +import net.sf.jabref.logic.layout.format.LatexToUnicodeFormatter; import net.sf.jabref.model.entry.BibEntry; /** @@ -12,7 +13,7 @@ */ public class RegexBasedSearchRule implements SearchRule { - private static final RemoveLatexCommands REMOVE_LATEX_COMMANDS = new RemoveLatexCommands(); + private static final LatexToUnicodeFormatter LATEX_TO_UNICODE_FORMATTER = new LatexToUnicodeFormatter(); private final boolean caseSensitive; @@ -50,10 +51,10 @@ public boolean applyRule(String query, BibEntry bibEntry) { } for (String field : bibEntry.getFieldNames()) { - if (bibEntry.hasField(field)) { - String fieldContent = RegexBasedSearchRule.REMOVE_LATEX_COMMANDS - .format(bibEntry.getField(field).get()); - String fieldContentNoBrackets = RegexBasedSearchRule.REMOVE_LATEX_COMMANDS.format(fieldContent); + Optional fieldOptional = bibEntry.getField(field); + if (fieldOptional.isPresent()) { + String fieldContent = RegexBasedSearchRule.LATEX_TO_UNICODE_FORMATTER.format(fieldOptional.get()); + String fieldContentNoBrackets = RegexBasedSearchRule.LATEX_TO_UNICODE_FORMATTER.format(fieldContent); Matcher m = pattern.matcher(fieldContentNoBrackets); if (m.find()) { return true; diff --git a/src/main/java/net/sf/jabref/preferences/JabRefPreferences.java b/src/main/java/net/sf/jabref/preferences/JabRefPreferences.java index 0486096004c8..8a3e2f85f076 100644 --- a/src/main/java/net/sf/jabref/preferences/JabRefPreferences.java +++ b/src/main/java/net/sf/jabref/preferences/JabRefPreferences.java @@ -151,8 +151,7 @@ public class JabRefPreferences { public static final String SIDE_PANE_COMPONENT_NAMES = "sidePaneComponentNames"; public static final String XMP_PRIVACY_FILTERS = "xmpPrivacyFilters"; public static final String USE_XMP_PRIVACY_FILTER = "useXmpPrivacyFilter"; - public static final String SEARCH_MODE_FILTER = "searchModeFilter"; - public static final String SEARCH_CASE_SENSITIVE = "caseSensitiveSearch"; + public static final String DEFAULT_AUTO_SORT = "defaultAutoSort"; public static final String DEFAULT_SHOW_SOURCE = "defaultShowSource"; // Window sizes @@ -196,7 +195,6 @@ public class JabRefPreferences { public static final String WORKING_DIRECTORY = "workingDirectory"; public static final String NUMBER_COL_WIDTH = "numberColWidth"; public static final String AUTO_COMPLETE = "autoComplete"; - public static final String SEARCH_REG_EXP = "regExpSearch"; public static final String EDITOR_EMACS_KEYBINDINGS = "editorEMACSkeyBindings"; public static final String EDITOR_EMACS_KEYBINDINGS_REBIND_CA = "editorEMACSkeyBindingsRebindCA"; public static final String EDITOR_EMACS_KEYBINDINGS_REBIND_CF = "editorEMACSkeyBindingsRebindCF"; @@ -290,7 +288,6 @@ public class JabRefPreferences { // When this should be made possible, the code to inspect is net.sf.jabref.gui.preftabs.BibtexKeyPatternPrefTab.storeSettings() -> LabelPattern keypatterns = getBibtexKeyPattern(); etc public static final String DEFAULT_BIBTEX_KEY_PATTERN = "defaultBibtexKeyPattern"; - public static final String SEARCH_MODE_FLOAT = "floatSearch"; public static final String GRAY_OUT_NON_HITS = "grayOutNonHits"; public static final String CONFIRM_DELETE = "confirmDelete"; public static final String WARN_BEFORE_OVERWRITING_KEY = "warnBeforeOverwritingKey"; @@ -497,6 +494,8 @@ private JabRefPreferences() { // load user preferences prefs = Preferences.userNodeForPackage(JabRefMain.class); + SearchPreferences.putDefaults(defaults); + defaults.put(TEXMAKER_PATH, JabRefDesktop.getNativeDesktop().detectProgramPath("texmaker", "Texmaker")); defaults.put(WIN_EDT_PATH, JabRefDesktop.getNativeDesktop().detectProgramPath("WinEdt", "WinEdt Team\\WinEdt")); defaults.put(LATEX_EDITOR_PATH, JabRefDesktop.getNativeDesktop().detectProgramPath("LEd", "LEd")); @@ -627,10 +626,8 @@ private JabRefPreferences() { defaults.put(TERMS_SIZE_X, 500); defaults.put(TERMS_SIZE_Y, 500); defaults.put(DEFAULT_SHOW_SOURCE, Boolean.FALSE); - defaults.put(SEARCH_CASE_SENSITIVE, Boolean.FALSE); - defaults.put(SEARCH_MODE_FILTER, Boolean.TRUE); - defaults.put(SEARCH_REG_EXP, Boolean.FALSE); + defaults.put(DEFAULT_AUTO_SORT, Boolean.FALSE); defaults.put(MERGE_ENTRIES_DIFF_MODE, 2); @@ -747,7 +744,6 @@ private JabRefPreferences() { defaults.put(WARN_BEFORE_OVERWRITING_KEY, Boolean.TRUE); defaults.put(CONFIRM_DELETE, Boolean.TRUE); defaults.put(GRAY_OUT_NON_HITS, Boolean.TRUE); - defaults.put(SEARCH_MODE_FLOAT, Boolean.FALSE); defaults.put(DEFAULT_BIBTEX_KEY_PATTERN, "[auth][year]"); defaults.put(PREVIEW_ENABLED, Boolean.TRUE); defaults.put(ACTIVE_PREVIEW, 0); @@ -821,8 +817,6 @@ private JabRefPreferences() { defaults.put(IMPORT_INSPECTION_DIALOG_WIDTH, 650); defaults.put(IMPORT_INSPECTION_DIALOG_HEIGHT, 650); - defaults.put(SEARCH_DIALOG_WIDTH, 650); - defaults.put(SEARCH_DIALOG_HEIGHT, 500); defaults.put(SHOW_FILE_LINKS_UPGRADE_WARNING, Boolean.TRUE); defaults.put(AUTOLINK_EXACT_KEY_ONLY, Boolean.FALSE); defaults.put(NUMERIC_FIELDS, "mittnum;author"); diff --git a/src/main/java/net/sf/jabref/preferences/SearchPreferences.java b/src/main/java/net/sf/jabref/preferences/SearchPreferences.java new file mode 100644 index 000000000000..1a4dbfae219c --- /dev/null +++ b/src/main/java/net/sf/jabref/preferences/SearchPreferences.java @@ -0,0 +1,117 @@ +package net.sf.jabref.preferences; + +import java.util.Map; +import java.util.Objects; + +import net.sf.jabref.gui.search.SearchDisplayMode; + + +public class SearchPreferences { + + private static final String SEARCH_GLOBAL = "searchGlobal"; + private static final String SEARCH_DISPLAY_MODE = "searchDisplayMode"; + private static final String SEARCH_CASE_SENSITIVE = "caseSensitiveSearch"; + private static final String SEARCH_REG_EXP = "regExpSearch"; + + private static final String SEARCH_DIALOG_HEIGHT = "searchDialogHeight"; + private static final String SEARCH_DIALOG_WIDTH = "searchDialogWidth"; + private static final String SEARCH_DIALOG_POS_X = "searchDialogPosX"; + private static final String SEARCH_DIALOG_POS_Y = "searchDialogPosY"; + + private final JabRefPreferences preferences; + + + public SearchPreferences(JabRefPreferences preferences) { + this.preferences = Objects.requireNonNull(preferences); + } + + public static void putDefaults(Map defaults) { + defaults.put(SEARCH_GLOBAL, Boolean.FALSE); + defaults.put(SEARCH_DISPLAY_MODE, SearchDisplayMode.FILTER.toString()); + defaults.put(SEARCH_CASE_SENSITIVE, Boolean.FALSE); + defaults.put(SEARCH_REG_EXP, Boolean.FALSE); + + defaults.put(SEARCH_DIALOG_WIDTH, 650); + defaults.put(SEARCH_DIALOG_HEIGHT, 500); + defaults.put(SEARCH_DIALOG_POS_X, 0); + defaults.put(SEARCH_DIALOG_POS_Y, 0); + } + + public boolean isGlobalSearch() { + return preferences.getBoolean(SEARCH_GLOBAL); + } + + public SearchPreferences setGlobalSearch(boolean isGlobalSearch) { + preferences.putBoolean(SEARCH_GLOBAL, isGlobalSearch); + return this; + } + + public SearchDisplayMode getSearchMode() { + try { + return SearchDisplayMode.valueOf(preferences.get(SEARCH_DISPLAY_MODE)); + } catch (IllegalArgumentException ex) { + // Should only occur when the searchmode is set directly via preferences.put and the enum was not used + return SearchDisplayMode.valueOf((String) preferences.defaults.get(SEARCH_DISPLAY_MODE)); + } + } + + public SearchPreferences setSearchMode(SearchDisplayMode searchDisplayMode) { + preferences.put(SEARCH_DISPLAY_MODE, Objects.requireNonNull(searchDisplayMode).toString()); + return this; + } + + public boolean isCaseSensitive() { + return preferences.getBoolean(SEARCH_CASE_SENSITIVE); + } + + public SearchPreferences setCaseSensitive(boolean isCaseSensitive) { + preferences.putBoolean(SEARCH_CASE_SENSITIVE, isCaseSensitive); + return this; + } + + public boolean isRegularExpression() { + return preferences.getBoolean(SEARCH_REG_EXP); + } + + public SearchPreferences setRegularExpression(boolean isRegularExpression) { + preferences.putBoolean(SEARCH_REG_EXP, isRegularExpression); + return this; + } + + public int getSeachDialogWidth() { + return preferences.getInt(SEARCH_DIALOG_WIDTH); + } + + public SearchPreferences setSearchDialogWidth(int width) { + preferences.putInt(SEARCH_DIALOG_WIDTH, width); + return this; + } + + public int getSeachDialogHeight() { + return preferences.getInt(SEARCH_DIALOG_HEIGHT); + } + + public SearchPreferences setSearchDialogHeight(int height) { + preferences.putInt(SEARCH_DIALOG_HEIGHT, height); + return this; + } + + public int getSearchDialogPosX() { + return preferences.getInt(SEARCH_DIALOG_POS_X); + } + + public SearchPreferences setSearchDialogPosX(int x) { + preferences.putInt(SEARCH_DIALOG_POS_X, x); + return this; + } + + public int getSearchDialogPosY() { + return preferences.getInt(SEARCH_DIALOG_POS_Y); + } + + public SearchPreferences setSearchDialogPosY(int y) { + preferences.putInt(SEARCH_DIALOG_POS_Y, y); + return this; + } + +} diff --git a/src/main/resources/l10n/JabRef_da.properties b/src/main/resources/l10n/JabRef_da.properties index cf52a210c2cc..30c9d3747f3a 100644 --- a/src/main/resources/l10n/JabRef_da.properties +++ b/src/main/resources/l10n/JabRef_da.properties @@ -1353,7 +1353,6 @@ Found_%0_results.= No_results_found.= Normal_search_active.= Search_globally= -Search_in_all_open_databases= Search_results_in_all_databases_for_%0= Search_results_in_database_%0_for_%1= This_search_contains_entries_in_which= diff --git a/src/main/resources/l10n/JabRef_de.properties b/src/main/resources/l10n/JabRef_de.properties index a73443f1d44e..5227fdc04f4b 100644 --- a/src/main/resources/l10n/JabRef_de.properties +++ b/src/main/resources/l10n/JabRef_de.properties @@ -2061,7 +2061,6 @@ Found_%0_results.=%0_Ergebnisse_gefunden. No_results_found.=Keine_Ergebnisse_gefunden. Normal_search_active.=Normale_Suche_aktiv. Search_globally=Global_suchen -Search_in_all_open_databases=Suche_in_allen_offenen_Datenbanken Search_results_in_all_databases_for_%0=Suchergebnisse_in_allen_Datenbanken_für_%0 Search_results_in_database_%0_for_%1=Suchergebnisse_in_Datenbank_%0_für_%1 This_search_contains_entries_in_which=Diese_Suche_enthält_Einträge,_in_denen diff --git a/src/main/resources/l10n/JabRef_en.properties b/src/main/resources/l10n/JabRef_en.properties index 5a23997950b2..e065e4c2512b 100644 --- a/src/main/resources/l10n/JabRef_en.properties +++ b/src/main/resources/l10n/JabRef_en.properties @@ -1951,7 +1951,6 @@ Search_%0=Search_%0 Search_results_in_all_databases_for_%0=Search_results_in_all_databases_for_%0 Search_results_in_database_%0_for_%1=Search_results_in_database_%0_for_%1 Search_globally=Search_globally -Search_in_all_open_databases=Search_in_all_open_databases No_results_found.=No_results_found. Found_%0_results.=Found_%0_results. Advanced_search_active.=Advanced_search_active. diff --git a/src/main/resources/l10n/JabRef_es.properties b/src/main/resources/l10n/JabRef_es.properties index e070cb0c9bdf..0d169e7e4329 100644 --- a/src/main/resources/l10n/JabRef_es.properties +++ b/src/main/resources/l10n/JabRef_es.properties @@ -1256,7 +1256,6 @@ Found_%0_results.=Se_encontraron_%0_resultados. No_results_found.=No_se_encontraron_resultados. Normal_search_active.=Búsqueda_normal_activa. Search_globally=Buscar_globalmente. -Search_in_all_open_databases=Buscar_en_todas_las_bases_de_datos_abiertas. Search_results_in_all_databases_for_%0=Resultados_para_%0_en_todas_las_bases_de_datos Search_results_in_database_%0_for_%1=Resultados_para_%1_en_la_base_de_datos_&0 This_search_contains_entries_in_which=Esta_búsqueda_contiene_entradas_en_las_cuales diff --git a/src/main/resources/l10n/JabRef_fa.properties b/src/main/resources/l10n/JabRef_fa.properties index 1f671019f292..e38eecb07e0c 100644 --- a/src/main/resources/l10n/JabRef_fa.properties +++ b/src/main/resources/l10n/JabRef_fa.properties @@ -2027,7 +2027,6 @@ Found_%0_results.= No_results_found.= Normal_search_active.= Search_globally= -Search_in_all_open_databases= Search_results_in_all_databases_for_%0= Search_results_in_database_%0_for_%1= This_search_contains_entries_in_which= diff --git a/src/main/resources/l10n/JabRef_fr.properties b/src/main/resources/l10n/JabRef_fr.properties index eb24b092d9fb..6086d16f7313 100644 --- a/src/main/resources/l10n/JabRef_fr.properties +++ b/src/main/resources/l10n/JabRef_fr.properties @@ -1291,7 +1291,6 @@ Found_%0_results.=%0_résultats_trouvés. No_results_found.=Aucun_résultat_trouvé. Normal_search_active.=Recherche_normale_active. Search_globally=Rechercher_globalement -Search_in_all_open_databases=Rechercher_dans_toutes_les_bases_ouvertes Search_results_in_all_databases_for_%0=Résultats_de_recherche_dans_toutes_les_bases_pour_%0 Search_results_in_database_%0_for_%1=Résultats_de_recherche_dans_la_base_%0_pour_%1 This_search_contains_entries_in_which=Cette_recherche_contient_des_entrées_pour_lequelles diff --git a/src/main/resources/l10n/JabRef_in.properties b/src/main/resources/l10n/JabRef_in.properties index fdc77eb85b94..7b314aca1ac9 100644 --- a/src/main/resources/l10n/JabRef_in.properties +++ b/src/main/resources/l10n/JabRef_in.properties @@ -1274,7 +1274,6 @@ Found_%0_results.=Menemukan_%0_hasil_pencarian. No_results_found.=Tidak_menemukan_hasil_pencarian. Normal_search_active.=Pencarian_biasa_aktif. Search_globally=Cari_secara_global -Search_in_all_open_databases=Cari_dalam_semua_basisdata_yang_terbuka Search_results_in_all_databases_for_%0=Hasil_pencarian_untuk_%0_dalam_semua_basisdata Search_results_in_database_%0_for_%1=Hasil_pencarian_untuk_%0_dalam_basisdata_%1 This_search_contains_entries_in_which= diff --git a/src/main/resources/l10n/JabRef_it.properties b/src/main/resources/l10n/JabRef_it.properties index c9259571f16b..511fb2a38880 100644 --- a/src/main/resources/l10n/JabRef_it.properties +++ b/src/main/resources/l10n/JabRef_it.properties @@ -1371,7 +1371,6 @@ Found_%0_results.= No_results_found.= Normal_search_active.= Search_globally= -Search_in_all_open_databases= Search_results_in_all_databases_for_%0= Search_results_in_database_%0_for_%1= This_search_contains_entries_in_which= diff --git a/src/main/resources/l10n/JabRef_ja.properties b/src/main/resources/l10n/JabRef_ja.properties index ee5a27317d8b..afc5442df368 100644 --- a/src/main/resources/l10n/JabRef_ja.properties +++ b/src/main/resources/l10n/JabRef_ja.properties @@ -2039,7 +2039,6 @@ Found_%0_results.=%0件検出しました。 No_results_found.=検出されませんでした。 Normal_search_active.=通常検索が進行中です。 Search_globally=大域検索 -Search_in_all_open_databases=開かれているデータベースをすべて検索 Search_results_in_all_databases_for_%0=%0を全データベースで検索した結果 Search_results_in_database_%0_for_%1=%0をデータベース%1で検索した結果 This_search_contains_entries_in_which=この検索結果は下記のものを表示; diff --git a/src/main/resources/l10n/JabRef_nl.properties b/src/main/resources/l10n/JabRef_nl.properties index e81023947423..1822c0da943d 100644 --- a/src/main/resources/l10n/JabRef_nl.properties +++ b/src/main/resources/l10n/JabRef_nl.properties @@ -2043,7 +2043,6 @@ Found_%0_results.= No_results_found.= Normal_search_active.= Search_globally= -Search_in_all_open_databases= Search_results_in_all_databases_for_%0= Search_results_in_database_%0_for_%1= This_search_contains_entries_in_which= diff --git a/src/main/resources/l10n/JabRef_no.properties b/src/main/resources/l10n/JabRef_no.properties index de0241ef53b1..200d83d60fcf 100644 --- a/src/main/resources/l10n/JabRef_no.properties +++ b/src/main/resources/l10n/JabRef_no.properties @@ -2438,7 +2438,6 @@ Found_%0_results.= No_results_found.= Normal_search_active.= Search_globally= -Search_in_all_open_databases= Search_results_in_all_databases_for_%0= Search_results_in_database_%0_for_%1= This_search_contains_entries_in_which= diff --git a/src/main/resources/l10n/JabRef_pt_BR.properties b/src/main/resources/l10n/JabRef_pt_BR.properties index ab0ee72ec782..4cd09f646d0e 100644 --- a/src/main/resources/l10n/JabRef_pt_BR.properties +++ b/src/main/resources/l10n/JabRef_pt_BR.properties @@ -1268,7 +1268,6 @@ Found_%0_results.=Encontrados_%0_resultados. No_results_found.=Nenhum_resultado_encontrado. Normal_search_active.= Search_globally= -Search_in_all_open_databases= Search_results_in_all_databases_for_%0= Search_results_in_database_%0_for_%1= This_search_contains_entries_in_which= diff --git a/src/main/resources/l10n/JabRef_ru.properties b/src/main/resources/l10n/JabRef_ru.properties index 09a4e1558677..f53238d99e4b 100644 --- a/src/main/resources/l10n/JabRef_ru.properties +++ b/src/main/resources/l10n/JabRef_ru.properties @@ -2012,7 +2012,6 @@ Found_%0_results.= No_results_found.= Normal_search_active.= Search_globally= -Search_in_all_open_databases= Search_results_in_all_databases_for_%0= Search_results_in_database_%0_for_%1= This_search_contains_entries_in_which= diff --git a/src/main/resources/l10n/JabRef_sv.properties b/src/main/resources/l10n/JabRef_sv.properties index 6613057ce112..936eaa498e85 100644 --- a/src/main/resources/l10n/JabRef_sv.properties +++ b/src/main/resources/l10n/JabRef_sv.properties @@ -970,7 +970,6 @@ Search_expression=Sökuttryck Search_failed\:_illegal_search_expression=Sökning_misslyckades\:_ogiltigt_sökuttryck Search_for=Sök_efter Search_globally=Sök_globalt -Search_in_all_open_databases=Sök_i_alla_öppna_databaser Search_results_in_all_databases_for_%0=Söker_efter_%0_i_alla_databaser Search_results_in_database_%0_for_%1=Söker_efter_%1_i_databasen_%0 Searches_for_unlinked_PDF_files_on_the_file_system=Letar_efter_olänkade_PDF-filer_på_filsystemet diff --git a/src/main/resources/l10n/JabRef_tr.properties b/src/main/resources/l10n/JabRef_tr.properties index 50597b9a5f90..f9f02c560b55 100644 --- a/src/main/resources/l10n/JabRef_tr.properties +++ b/src/main/resources/l10n/JabRef_tr.properties @@ -1293,7 +1293,6 @@ Found_%0_results.=%0_sonuç_bulundu. No_results_found.=Hiçbir_sonuç_bulunmadı. Normal_search_active.=Normal_arama_aktif. Search_globally=Küresel_ara -Search_in_all_open_databases=Tüm_açık_veritabanlarında_ara Search_results_in_all_databases_for_%0=Sonuçları_%0_için_tüm_veritabanlarında_ara Search_results_in_database_%0_for_%1=Sonuçları_%1_için_%0_veritabanında_ara This_search_contains_entries_in_which=Bu_arama_içinde_şu_olan_girdileri_içerir diff --git a/src/main/resources/l10n/JabRef_vi.properties b/src/main/resources/l10n/JabRef_vi.properties index f066e2c9c23b..69a467d4259b 100644 --- a/src/main/resources/l10n/JabRef_vi.properties +++ b/src/main/resources/l10n/JabRef_vi.properties @@ -2039,7 +2039,6 @@ Found_%0_results.= No_results_found.= Normal_search_active.= Search_globally= -Search_in_all_open_databases= Search_results_in_all_databases_for_%0= Search_results_in_database_%0_for_%1= This_search_contains_entries_in_which= diff --git a/src/main/resources/l10n/JabRef_zh.properties b/src/main/resources/l10n/JabRef_zh.properties index 0ca263eb486b..4a26f4be714b 100644 --- a/src/main/resources/l10n/JabRef_zh.properties +++ b/src/main/resources/l10n/JabRef_zh.properties @@ -614,7 +614,6 @@ Search=查找 Search_expression=查找表达式 Search_for=查找 Search_globally=全局搜索 -Search_in_all_open_databases=在所有打开的数据库中搜索 Search_results_in_all_databases_for_%0=在所有数据库中搜索_%0_的结果 Search_results_in_database_%0_for_%1=在数据库_%0_中搜索_%1_的结果 Searching_for_duplicates...=正在查找重复记录... diff --git a/src/main/resources/l10n/Menu_da.properties b/src/main/resources/l10n/Menu_da.properties index 7bed3360025c..4d1d4d34aa0e 100644 --- a/src/main/resources/l10n/Menu_da.properties +++ b/src/main/resources/l10n/Menu_da.properties @@ -119,3 +119,5 @@ JabRef_resources= Development_version= View_change_log= + +Global_Search= diff --git a/src/main/resources/l10n/Menu_de.properties b/src/main/resources/l10n/Menu_de.properties index 6226dd6acb21..33721fd9f30b 100644 --- a/src/main/resources/l10n/Menu_de.properties +++ b/src/main/resources/l10n/Menu_de.properties @@ -216,4 +216,7 @@ View_change_log=Changelog_öffnen Website=Webseite Blog=Blog Development_version=Entwicklungsversion -JabRef_resources=Mehr_zu_JabRef \ No newline at end of file +JabRef_resources=Mehr_zu_JabRef + +Global_Search=Globale_Suche + diff --git a/src/main/resources/l10n/Menu_en.properties b/src/main/resources/l10n/Menu_en.properties index 9ee753bff706..dff936e3a0ad 100644 --- a/src/main/resources/l10n/Menu_en.properties +++ b/src/main/resources/l10n/Menu_en.properties @@ -64,6 +64,7 @@ Save_database_as...=S&ave_database_as... Save_selected_as...=Save_se&lected_as... # Tools Search=&Search +Global_Search=Global_Search Select_all=Select_&all Set_up_general_fields=Set_up_&general_fields Show_error_console=Show_error_console diff --git a/src/main/resources/l10n/Menu_es.properties b/src/main/resources/l10n/Menu_es.properties index 7d994cd01693..e6c9addbbd7a 100644 --- a/src/main/resources/l10n/Menu_es.properties +++ b/src/main/resources/l10n/Menu_es.properties @@ -146,3 +146,5 @@ JabRef_resources= Development_version= View_change_log= + +Global_Search= diff --git a/src/main/resources/l10n/Menu_fa.properties b/src/main/resources/l10n/Menu_fa.properties index 03c5c3eb23fb..4afabc1793dd 100644 --- a/src/main/resources/l10n/Menu_fa.properties +++ b/src/main/resources/l10n/Menu_fa.properties @@ -144,3 +144,5 @@ JabRef_resources= Development_version= View_change_log= + +Global_Search= diff --git a/src/main/resources/l10n/Menu_fr.properties b/src/main/resources/l10n/Menu_fr.properties index ab02777d71f2..72e48122ca53 100644 --- a/src/main/resources/l10n/Menu_fr.properties +++ b/src/main/resources/l10n/Menu_fr.properties @@ -132,3 +132,5 @@ JabRef_resources=Plus_sur_JabRef Development_version=Version_en_développement View_change_log=Afficher_le_fichier_des_changements + +Global_Search= diff --git a/src/main/resources/l10n/Menu_in.properties b/src/main/resources/l10n/Menu_in.properties index 53b76dc697c4..8b5bbe991c9b 100644 --- a/src/main/resources/l10n/Menu_in.properties +++ b/src/main/resources/l10n/Menu_in.properties @@ -117,3 +117,5 @@ JabRef_resources= Development_version= View_change_log= + +Global_Search= diff --git a/src/main/resources/l10n/Menu_it.properties b/src/main/resources/l10n/Menu_it.properties index 37757316763a..5b2e09d4c977 100644 --- a/src/main/resources/l10n/Menu_it.properties +++ b/src/main/resources/l10n/Menu_it.properties @@ -138,3 +138,6 @@ JabRef_resources= Development_version= View_change_log= + + +Global_Search= diff --git a/src/main/resources/l10n/Menu_ja.properties b/src/main/resources/l10n/Menu_ja.properties index a6c909a17bcc..6bce914a19d6 100644 --- a/src/main/resources/l10n/Menu_ja.properties +++ b/src/main/resources/l10n/Menu_ja.properties @@ -145,3 +145,5 @@ JabRef_resources= Development_version= View_change_log= + +Global_Search= diff --git a/src/main/resources/l10n/Menu_nl.properties b/src/main/resources/l10n/Menu_nl.properties index 00c50df3994e..608e30e73bdb 100644 --- a/src/main/resources/l10n/Menu_nl.properties +++ b/src/main/resources/l10n/Menu_nl.properties @@ -226,3 +226,5 @@ JabRef_resources= Development_version= View_change_log= + +Global_Search= diff --git a/src/main/resources/l10n/Menu_no.properties b/src/main/resources/l10n/Menu_no.properties index b4c740ceba71..b1eff6fd8bc3 100644 --- a/src/main/resources/l10n/Menu_no.properties +++ b/src/main/resources/l10n/Menu_no.properties @@ -138,3 +138,6 @@ JabRef_resources=JabRef-ressurser Development_version=Utviklingsversjon View_change_log=Vis_endringslogg + + +Global_Search= diff --git a/src/main/resources/l10n/Menu_pt_BR.properties b/src/main/resources/l10n/Menu_pt_BR.properties index d344b994a923..35e19819387e 100644 --- a/src/main/resources/l10n/Menu_pt_BR.properties +++ b/src/main/resources/l10n/Menu_pt_BR.properties @@ -119,3 +119,5 @@ JabRef_resources= Development_version= View_change_log= + +Global_Search= diff --git a/src/main/resources/l10n/Menu_ru.properties b/src/main/resources/l10n/Menu_ru.properties index ec6962126f49..2124665c415e 100644 --- a/src/main/resources/l10n/Menu_ru.properties +++ b/src/main/resources/l10n/Menu_ru.properties @@ -142,3 +142,5 @@ JabRef_resources= Development_version= View_change_log= + +Global_Search= diff --git a/src/main/resources/l10n/Menu_sv.properties b/src/main/resources/l10n/Menu_sv.properties index ab3c392c47f4..a12f89e184b0 100644 --- a/src/main/resources/l10n/Menu_sv.properties +++ b/src/main/resources/l10n/Menu_sv.properties @@ -108,3 +108,5 @@ JabRef_resources=JabRef-resurser Development_version=Utvecklingsversion View_change_log=Visa_ändringar + +Global_Search= diff --git a/src/main/resources/l10n/Menu_tr.properties b/src/main/resources/l10n/Menu_tr.properties index 9c412df3d238..467cdc028a7f 100644 --- a/src/main/resources/l10n/Menu_tr.properties +++ b/src/main/resources/l10n/Menu_tr.properties @@ -121,3 +121,5 @@ JabRef_resources=JabRef_kaynakları Development_version=Geliştirme_sürümü View_change_log=Değişiklik_kütüğünü_göster + +Global_Search= diff --git a/src/main/resources/l10n/Menu_vi.properties b/src/main/resources/l10n/Menu_vi.properties index 1b7434e02957..a51aac0c54d2 100644 --- a/src/main/resources/l10n/Menu_vi.properties +++ b/src/main/resources/l10n/Menu_vi.properties @@ -144,3 +144,5 @@ JabRef_resources= Development_version= View_change_log= + +Global_Search= diff --git a/src/main/resources/l10n/Menu_zh.properties b/src/main/resources/l10n/Menu_zh.properties index 6ac798200fca..8346cb36878c 100644 --- a/src/main/resources/l10n/Menu_zh.properties +++ b/src/main/resources/l10n/Menu_zh.properties @@ -143,3 +143,5 @@ JabRef_resources= Development_version= View_change_log= + +Global_Search=