From ba0d0adf3b623ae263e496bfde65f9b7d56da4a4 Mon Sep 17 00:00:00 2001 From: CaptainDaVinci Date: Wed, 2 Oct 2019 22:35:14 +0530 Subject: [PATCH 1/4] Handle regular expressions in global search [WIP] Fixes #5349 Validate regex before setting activesearch. Adds a validator to the search field Fixes #5349 --- .../org/jabref/gui/entryeditor/SourceTab.java | 7 +++- .../org/jabref/gui/preview/PreviewViewer.java | 7 +++- .../jabref/gui/search/GlobalSearchBar.java | 35 ++++++++++++++++++- src/main/resources/l10n/JabRef_en.properties | 1 + 4 files changed, 47 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/jabref/gui/entryeditor/SourceTab.java b/src/main/java/org/jabref/gui/entryeditor/SourceTab.java index 50683c26d81..bf7808c4632 100644 --- a/src/main/java/org/jabref/gui/entryeditor/SourceTab.java +++ b/src/main/java/org/jabref/gui/entryeditor/SourceTab.java @@ -8,6 +8,7 @@ import java.util.Optional; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; import javax.swing.undo.UndoManager; @@ -112,7 +113,11 @@ public SourceTab(BibDatabaseContext bibDatabaseContext, CountingUndoManager undo this.stateManager = stateManager; stateManager.activeSearchQueryProperty().addListener((observable, oldValue, newValue) -> { - searchHighlightPattern = newValue.flatMap(SearchQuery::getPatternForWords); + try { + searchHighlightPattern = newValue.flatMap(SearchQuery::getPatternForWords); + } catch (PatternSyntaxException e) { + LOGGER.error(e.getMessage()); + } highlightSearchPattern(); }); diff --git a/src/main/java/org/jabref/gui/preview/PreviewViewer.java b/src/main/java/org/jabref/gui/preview/PreviewViewer.java index c3d0ec4b405..b2e7b2d2730 100644 --- a/src/main/java/org/jabref/gui/preview/PreviewViewer.java +++ b/src/main/java/org/jabref/gui/preview/PreviewViewer.java @@ -3,6 +3,7 @@ import java.util.Objects; import java.util.Optional; import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; import javafx.beans.InvalidationListener; import javafx.beans.Observable; @@ -62,7 +63,11 @@ public class PreviewViewer extends ScrollPane implements InvalidationListener { private boolean registered; private ChangeListener> listener = (queryObservable, queryOldValue, queryNewValue) -> { - searchHighlightPattern = queryNewValue.flatMap(SearchQuery::getPatternForWords); + try { + searchHighlightPattern = queryNewValue.flatMap(SearchQuery::getPatternForWords); + } catch (PatternSyntaxException e) { + LOGGER.error(e.getMessage()); + } highlightSearchPattern(); }; diff --git a/src/main/java/org/jabref/gui/search/GlobalSearchBar.java b/src/main/java/org/jabref/gui/search/GlobalSearchBar.java index 6938411b4ad..41d4cbdf4dc 100644 --- a/src/main/java/org/jabref/gui/search/GlobalSearchBar.java +++ b/src/main/java/org/jabref/gui/search/GlobalSearchBar.java @@ -4,10 +4,13 @@ import java.util.List; import java.util.Objects; import java.util.Optional; +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; import javafx.animation.KeyFrame; import javafx.animation.KeyValue; import javafx.animation.Timeline; +import javafx.application.Platform; import javafx.beans.binding.Bindings; import javafx.css.PseudoClass; import javafx.event.Event; @@ -48,6 +51,7 @@ import org.jabref.gui.search.rules.describer.SearchDescribers; import org.jabref.gui.util.BindingsHelper; import org.jabref.gui.util.DefaultTaskExecutor; +import org.jabref.gui.util.IconValidationDecorator; import org.jabref.gui.util.TooltipTextUtil; import org.jabref.logic.l10n.Localization; import org.jabref.logic.search.SearchQuery; @@ -55,6 +59,10 @@ import org.jabref.preferences.JabRefPreferences; import org.jabref.preferences.SearchPreferences; +import de.saxsys.mvvmfx.utils.validation.FunctionBasedValidator; +import de.saxsys.mvvmfx.utils.validation.ValidationMessage; +import de.saxsys.mvvmfx.utils.validation.Validator; +import de.saxsys.mvvmfx.utils.validation.visualization.ControlsFxVisualizer; import impl.org.controlsfx.skin.AutoCompletePopup; import org.controlsfx.control.textfield.AutoCompletionBinding; import org.fxmisc.easybind.EasyBind; @@ -81,6 +89,7 @@ public class GlobalSearchBar extends HBox { private final Tooltip tooltip = new Tooltip(); private final StateManager stateManager; private SearchDisplayMode searchDisplayMode; + private Validator regexValidator; public GlobalSearchBar(JabRefFrame frame, StateManager stateManager) { super(); @@ -137,6 +146,15 @@ public GlobalSearchBar(JabRefFrame frame, StateManager stateManager) { searchField.setMaxWidth(initialSize); HBox.setHgrow(searchField, Priority.ALWAYS); + regexValidator = new FunctionBasedValidator<>( + searchField.textProperty(), + query -> !(regularExp.isSelected() && !validRegex()), + ValidationMessage.error(Localization.lang("Invalid regular expression")) + ); + ControlsFxVisualizer visualizer = new ControlsFxVisualizer(); + visualizer.setDecoration(new IconValidationDecorator(Pos.CENTER_LEFT)); + Platform.runLater(() -> { visualizer.initVisualization(regexValidator.getValidationStatus(), searchField); }); + Timer searchTask = FxTimer.create(java.time.Duration.ofMillis(SEARCH_DELAY), this::performSearch); searchField.textProperty().addListener((observable, oldValue, newValue) -> searchTask.restart()); @@ -219,15 +237,30 @@ public void performSearch() { return; } + // Invalid regular expression + if (!regexValidator.getValidationStatus().isValid()) { + currentResults.setText(Localization.lang("Invalid regular expression")); + return; + } + SearchQuery searchQuery = new SearchQuery(this.searchField.getText(), this.caseSensitive.isSelected(), this.regularExp.isSelected()); if (!searchQuery.isValid()) { informUserAboutInvalidSearchQuery(); return; } - stateManager.setSearchQuery(searchQuery); } + private boolean validRegex() { + try { + Pattern.compile(searchField.getText()); + } catch (PatternSyntaxException e) { + LOGGER.debug(e.getMessage()); + return false; + } + return true; + } + private void informUserAboutInvalidSearchQuery() { searchField.pseudoClassStateChanged(CLASS_NO_RESULTS, true); diff --git a/src/main/resources/l10n/JabRef_en.properties b/src/main/resources/l10n/JabRef_en.properties index 283a0fd5ddc..b8fc9524ac0 100644 --- a/src/main/resources/l10n/JabRef_en.properties +++ b/src/main/resources/l10n/JabRef_en.properties @@ -1399,6 +1399,7 @@ should\ contain\ a\ four\ digit\ number=should contain a four digit number should\ contain\ a\ valid\ page\ number\ range=should contain a valid page number range No\ results\ found.=No results found. Found\ %0\ results.=Found %0 results. +Invalid\ Regex=Invalid Regex plain\ text=plain text This\ search\ contains\ entries\ in\ which\ any\ field\ contains\ the\ regular\ expression\ %0=This search contains entries in which any field contains the regular expression %0 This\ search\ contains\ entries\ in\ which\ any\ field\ contains\ the\ term\ %0=This search contains entries in which any field contains the term %0 From 8c8adf04b0f984af72d0c4310a7edd27ad271b7a Mon Sep 17 00:00:00 2001 From: CaptainDaVinci Date: Sat, 5 Oct 2019 17:40:14 +0530 Subject: [PATCH 2/4] Validate regex before setting activesearch. --- src/main/java/org/jabref/gui/entryeditor/SourceTab.java | 7 +------ src/main/java/org/jabref/gui/preview/PreviewViewer.java | 7 +------ 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/src/main/java/org/jabref/gui/entryeditor/SourceTab.java b/src/main/java/org/jabref/gui/entryeditor/SourceTab.java index bf7808c4632..50683c26d81 100644 --- a/src/main/java/org/jabref/gui/entryeditor/SourceTab.java +++ b/src/main/java/org/jabref/gui/entryeditor/SourceTab.java @@ -8,7 +8,6 @@ import java.util.Optional; import java.util.regex.Matcher; import java.util.regex.Pattern; -import java.util.regex.PatternSyntaxException; import javax.swing.undo.UndoManager; @@ -113,11 +112,7 @@ public SourceTab(BibDatabaseContext bibDatabaseContext, CountingUndoManager undo this.stateManager = stateManager; stateManager.activeSearchQueryProperty().addListener((observable, oldValue, newValue) -> { - try { - searchHighlightPattern = newValue.flatMap(SearchQuery::getPatternForWords); - } catch (PatternSyntaxException e) { - LOGGER.error(e.getMessage()); - } + searchHighlightPattern = newValue.flatMap(SearchQuery::getPatternForWords); highlightSearchPattern(); }); diff --git a/src/main/java/org/jabref/gui/preview/PreviewViewer.java b/src/main/java/org/jabref/gui/preview/PreviewViewer.java index b2e7b2d2730..c3d0ec4b405 100644 --- a/src/main/java/org/jabref/gui/preview/PreviewViewer.java +++ b/src/main/java/org/jabref/gui/preview/PreviewViewer.java @@ -3,7 +3,6 @@ import java.util.Objects; import java.util.Optional; import java.util.regex.Pattern; -import java.util.regex.PatternSyntaxException; import javafx.beans.InvalidationListener; import javafx.beans.Observable; @@ -63,11 +62,7 @@ public class PreviewViewer extends ScrollPane implements InvalidationListener { private boolean registered; private ChangeListener> listener = (queryObservable, queryOldValue, queryNewValue) -> { - try { - searchHighlightPattern = queryNewValue.flatMap(SearchQuery::getPatternForWords); - } catch (PatternSyntaxException e) { - LOGGER.error(e.getMessage()); - } + searchHighlightPattern = queryNewValue.flatMap(SearchQuery::getPatternForWords); highlightSearchPattern(); }; From 9c972c49de9c79b7f46a9be3961a7b47aff59df2 Mon Sep 17 00:00:00 2001 From: CaptainDaVinci Date: Tue, 15 Oct 2019 19:27:31 +0530 Subject: [PATCH 3/4] Adds a validator to the search field --- src/main/resources/l10n/JabRef_en.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/l10n/JabRef_en.properties b/src/main/resources/l10n/JabRef_en.properties index b8fc9524ac0..902719b523b 100644 --- a/src/main/resources/l10n/JabRef_en.properties +++ b/src/main/resources/l10n/JabRef_en.properties @@ -1399,7 +1399,7 @@ should\ contain\ a\ four\ digit\ number=should contain a four digit number should\ contain\ a\ valid\ page\ number\ range=should contain a valid page number range No\ results\ found.=No results found. Found\ %0\ results.=Found %0 results. -Invalid\ Regex=Invalid Regex +Invalid\ regular\ expression=Invalid regular expression plain\ text=plain text This\ search\ contains\ entries\ in\ which\ any\ field\ contains\ the\ regular\ expression\ %0=This search contains entries in which any field contains the regular expression %0 This\ search\ contains\ entries\ in\ which\ any\ field\ contains\ the\ term\ %0=This search contains entries in which any field contains the term %0 From b2397be77b14577e63cb31574553b2408390cfbd Mon Sep 17 00:00:00 2001 From: CaptainDaVinci Date: Sun, 20 Oct 2019 20:49:05 +0530 Subject: [PATCH 4/4] Added right padding to error icon --- src/main/java/org/jabref/gui/Base.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/org/jabref/gui/Base.css b/src/main/java/org/jabref/gui/Base.css index 35f32a1c71c..3cef89ee044 100644 --- a/src/main/java/org/jabref/gui/Base.css +++ b/src/main/java/org/jabref/gui/Base.css @@ -871,6 +871,10 @@ -fx-text-fill: -jr-search-text; } +.mainToolbar .search-field .label { + -fx-padding: 0em 1.8em 0em 0em; +} + /* The little arrow that shows up when not all tool-bar icons fit into the tool-bar. We want to have a look that matches our icons in the tool-bar */ .mainToolbar .tool-bar-overflow-button > .arrow {