Skip to content

Commit

Permalink
New global search (#8045)
Browse files Browse the repository at this point in the history
Co-authored-by: Carl Christian Snethlage <50491877+calixtus@users.noreply.github.com>
  • Loading branch information
Siedlerchr and calixtus authored Sep 2, 2021
1 parent 7257e18 commit 9c5b8ab
Show file tree
Hide file tree
Showing 18 changed files with 397 additions and 58 deletions.
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ Note that this project **does not** adhere to [Semantic Versioning](http://semve
- We added unprotect_terms to the list of bracketed pattern modifiers [#7826](https://github.com/JabRef/jabref/pull/7960)
- We added a dialog that allows to parse metadata from linked pdfs. [#7929](https://github.com/JabRef/jabref/pull/7929)
- We added an icon picker in group edit dialog. [#6142](https://github.com/JabRef/jabref/issues/6142)
- We added a preference to Opt-In to JabRef's online metadata extraction service (Grobid) usage. [8002](https://github.com/JabRef/jabref/pull/8002)
- We added a preference to Opt-In to JabRef's online metadata extraction service (Grobid) usage. [#8002](https://github.com/JabRef/jabref/pull/8002)
- We readded the possibility to display the search results of all databases ("Global Search"). It is shown in a separate window. [#4096](https://github.com/JabRef/jabref/issues/4096)
- We readded the possibility to keep the search string when switching tabs. It is implemented by a toggle button. [#4096](https://github.com/JabRef/jabref/issues/4096#issuecomment-575986882)

### Changed

Expand Down
6 changes: 6 additions & 0 deletions src/main/java/org/jabref/gui/Base.css
Original file line number Diff line number Diff line change
Expand Up @@ -960,6 +960,12 @@ TextFlow > .tooltip-text-monospaced {
-fx-fill: -jr-search-text;
}

.mainToolbar .search-field .button .glyph-icon {
-fx-fill: -jr-search-text;
-fx-text-fill: -jr-search-text;
-fx-icon-color: -jr-search-text;
}

/* magnifier glass */
.mainToolbar .search-field .glyph-icon {
-fx-fill: -jr-search-text;
Expand Down
5 changes: 5 additions & 0 deletions src/main/java/org/jabref/gui/Dark.css
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,11 @@
-fx-background-color: -jr-background;
}

.mainToolbar .search-field .button .glyph-icon {
-fx-fill: derive(-fx-light-text-color, 80%);
-fx-text-fill: derive(-fx-light-text-color, 80%);
-fx-icon-color: derive(-fx-light-text-color, 80%);
}

.mainToolbar .search-field .toggle-button .glyph-icon {
-fx-fill: -jr-search-text;
Expand Down
12 changes: 9 additions & 3 deletions src/main/java/org/jabref/gui/JabRefFrame.java
Original file line number Diff line number Diff line change
Expand Up @@ -180,8 +180,8 @@ public JabRefFrame(Stage mainStage) {
this.dialogService = new JabRefDialogService(mainStage, this, prefs);
this.stateManager = Globals.stateManager;
this.pushToApplicationsManager = new PushToApplicationsManager(dialogService, stateManager, prefs);
this.globalSearchBar = new GlobalSearchBar(this, stateManager, prefs);
this.undoManager = Globals.undoManager;
this.globalSearchBar = new GlobalSearchBar(this, stateManager, prefs, undoManager);
this.fileHistory = new FileHistoryMenu(prefs, dialogService, getOpenDatabaseAction());
this.taskExecutor = Globals.TASK_EXECUTOR;
this.setOnKeyTyped(key -> {
Expand Down Expand Up @@ -594,8 +594,14 @@ public void init() {
// Subscribe to the search
EasyBind.subscribe(stateManager.activeSearchQueryProperty(),
query -> {
if (getCurrentLibraryTab() != null) {
getCurrentLibraryTab().setCurrentSearchQuery(query);
if (prefs.getSearchPreferences().isKeepSearchString()) {
for (LibraryTab tab : getLibraryTabs()) {
tab.setCurrentSearchQuery(query);
}
} else {
if (getCurrentLibraryTab() != null) {
getCurrentLibraryTab().setCurrentSearchQuery(query);
}
}
});

Expand Down
17 changes: 4 additions & 13 deletions src/main/java/org/jabref/gui/LibraryTab.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package org.jabref.gui;

import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.util.ArrayList;
Expand Down Expand Up @@ -109,7 +108,7 @@ public class LibraryTab extends Tab {
// initializing it so we prevent NullPointerException
private BackgroundTask<ParserResult> dataLoadingTask = BackgroundTask.wrap(() -> null);

private IndexingTaskManager indexingTaskManager = new IndexingTaskManager(Globals.TASK_EXECUTOR);
private final IndexingTaskManager indexingTaskManager = new IndexingTaskManager(Globals.TASK_EXECUTOR);

public LibraryTab(JabRefFrame frame,
PreferencesService preferencesService,
Expand Down Expand Up @@ -311,17 +310,9 @@ public void updateTabTitle(boolean isChanged) {
}

// Unique path fragment
List<String> uniquePathParts = FileUtil.uniquePathSubstrings(collectAllDatabasePaths());
Optional<String> uniquePathPart = uniquePathParts.stream()
.filter(part -> databasePath.toString().contains(part)
&& !part.equals(fileName) && part.contains(File.separator))
.findFirst();
if (uniquePathPart.isPresent()) {
String uniquePath = uniquePathPart.get();
// remove filename
uniquePath = uniquePath.substring(0, uniquePath.lastIndexOf(File.separator));
tabTitle.append(" \u2013 ").append(uniquePath);
}
Optional<String> uniquePathPart = FileUtil.getUniquePathFragment(collectAllDatabasePaths(), databasePath);
uniquePathPart.ifPresent(part -> tabTitle.append(" \u2013 ").append(part));

} else {
if (databaseLocation == DatabaseLocation.LOCAL) {
tabTitle.append(Localization.lang("untitled"));
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/org/jabref/gui/StateManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public class StateManager {
private final OptionalObjectProperty<SearchQuery> activeSearchQuery = OptionalObjectProperty.empty();
private final ObservableMap<BibDatabaseContext, IntegerProperty> searchResultMap = FXCollections.observableHashMap();
private final OptionalObjectProperty<Node> focusOwner = OptionalObjectProperty.empty();
private final ObservableList<Task<?>> backgroundTasks = FXCollections.observableArrayList(task -> new Observable[]{task.progressProperty(), task.runningProperty()});
private final ObservableList<Task<?>> backgroundTasks = FXCollections.observableArrayList(task -> new Observable[] {task.progressProperty(), task.runningProperty()});
private final EasyBinding<Boolean> anyTaskRunning = EasyBind.reduce(backgroundTasks, tasks -> tasks.anyMatch(Task::isRunning));
private final EasyBinding<Double> tasksProgress = EasyBind.reduce(backgroundTasks, tasks -> tasks.filter(Task::isRunning).mapToDouble(Task::getProgress).average().orElse(1));
private final ObservableMap<String, DialogWindowState> dialogWindowStates = FXCollections.observableHashMap();
Expand Down
6 changes: 5 additions & 1 deletion src/main/java/org/jabref/gui/icon/IconTheme.java
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,11 @@ public enum JabRefIcons implements JabRefIcon {
LINK(MaterialDesignL.LINK),
LINK_VARIANT(MaterialDesignL.LINK_VARIANT),
PROTECT_STRING(MaterialDesignC.CODE_BRACES),
SELECT_ICONS(MaterialDesignA.APPS);
SELECT_ICONS(MaterialDesignA.APPS),
KEEP_SEARCH_STRING(MaterialDesignE.EARTH),
KEEP_ON_TOP(MaterialDesignP.PIN),
KEEP_ON_TOP_OFF(MaterialDesignP.PIN_OFF_OUTLINE),
OPEN_GLOBAL_SEARCH(MaterialDesignO.OPEN_IN_NEW);

private final JabRefIcon icon;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ public MainTableDataModel(BibDatabaseContext context, PreferencesService prefere
// We need to wrap the list since otherwise sorting in the table does not work
entriesSorted = new SortedList<>(entriesFiltered);
groupViewMode = preferencesService.getGroupViewMode();

}

private boolean isMatched(ObservableList<GroupTreeNode> groups, Optional<SearchQuery> query, BibEntryTableViewModel entry) {
Expand Down
85 changes: 55 additions & 30 deletions src/main/java/org/jabref/gui/search/GlobalSearchBar.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,21 @@
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

import javax.swing.undo.UndoManager;

import javafx.application.Platform;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.css.PseudoClass;
import javafx.event.Event;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Cursor;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonBase;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.Label;
import javafx.scene.control.ListView;
Expand All @@ -40,10 +46,12 @@
import org.jabref.gui.autocompleter.AutoCompletionTextInputBinding;
import org.jabref.gui.autocompleter.PersonNameStringConverter;
import org.jabref.gui.autocompleter.SuggestionProvider;
import org.jabref.gui.externalfiletype.ExternalFileTypes;
import org.jabref.gui.icon.IconTheme;
import org.jabref.gui.keyboard.KeyBinding;
import org.jabref.gui.keyboard.KeyBindingRepository;
import org.jabref.gui.search.rules.describer.SearchDescribers;
import org.jabref.gui.undo.CountingUndoManager;
import org.jabref.gui.util.BindingsHelper;
import org.jabref.gui.util.DefaultTaskExecutor;
import org.jabref.gui.util.IconValidationDecorator;
Expand All @@ -54,7 +62,6 @@
import org.jabref.preferences.PreferencesService;
import org.jabref.preferences.SearchPreferences;

import com.tobiasdiez.easybind.EasyBind;
import de.saxsys.mvvmfx.utils.validation.FunctionBasedValidator;
import de.saxsys.mvvmfx.utils.validation.ValidationMessage;
import de.saxsys.mvvmfx.utils.validation.Validator;
Expand All @@ -81,23 +88,30 @@ public class GlobalSearchBar extends HBox {
private final ToggleButton caseSensitiveButton;
private final ToggleButton regularExpressionButton;
private final ToggleButton fulltextButton;
private final Button openGlobalSearchButton;
private final ToggleButton keepSearchString;
// private final Button searchModeButton;
private final Tooltip searchFieldTooltip = new Tooltip();
private final Label currentResults = new Label("");

private final StateManager stateManager;
private final PreferencesService preferencesService;
private final Validator regexValidator;
private final UndoManager undoManager;

private SearchPreferences searchPreferences;

public GlobalSearchBar(JabRefFrame frame, StateManager stateManager, PreferencesService preferencesService) {
private final BooleanProperty globalSearchActive = new SimpleBooleanProperty(false);
private GlobalSearchResultDialog globalSearchResultDialog;

public GlobalSearchBar(JabRefFrame frame, StateManager stateManager, PreferencesService preferencesService, CountingUndoManager undoManager) {
super();
this.stateManager = stateManager;
this.preferencesService = preferencesService;
this.searchPreferences = preferencesService.getSearchPreferences();
this.undoManager = undoManager;

this.searchField.disableProperty().bind(needsDatabase(stateManager).not());
searchField.disableProperty().bind(needsDatabase(stateManager).not());

// fits the standard "found x entries"-message thus hinders the searchbar to jump around while searching if the frame width is too small
currentResults.setPrefWidth(150);
Expand Down Expand Up @@ -125,13 +139,16 @@ public GlobalSearchBar(JabRefFrame frame, StateManager stateManager, Preferences
regularExpressionButton = IconTheme.JabRefIcons.REG_EX.asToggleButton();
caseSensitiveButton = IconTheme.JabRefIcons.CASE_SENSITIVE.asToggleButton();
fulltextButton = IconTheme.JabRefIcons.FULLTEXT.asToggleButton();
// searchModeButton = new Button();
openGlobalSearchButton = IconTheme.JabRefIcons.OPEN_GLOBAL_SEARCH.asButton();
keepSearchString = IconTheme.JabRefIcons.KEEP_SEARCH_STRING.asToggleButton();

initSearchModifierButtons();

BooleanBinding focusedOrActive = searchField.focusedProperty()
.or(regularExpressionButton.focusedProperty())
.or(caseSensitiveButton.focusedProperty())
.or(fulltextButton.focusedProperty())
.or(keepSearchString.focusedProperty())
.or(searchField.textProperty()
.isNotEmpty());

Expand All @@ -141,8 +158,10 @@ public GlobalSearchBar(JabRefFrame frame, StateManager stateManager, Preferences
caseSensitiveButton.visibleProperty().bind(focusedOrActive);
fulltextButton.visibleProperty().unbind();
fulltextButton.visibleProperty().bind(focusedOrActive);
keepSearchString.visibleProperty().unbind();
keepSearchString.visibleProperty().bind(focusedOrActive);

StackPane modifierButtons = new StackPane(new HBox(regularExpressionButton, caseSensitiveButton, fulltextButton));
StackPane modifierButtons = new StackPane(new HBox(regularExpressionButton, caseSensitiveButton, fulltextButton, keepSearchString));
modifierButtons.setAlignment(Pos.CENTER);
searchField.setRight(new HBox(searchField.getRight(), modifierButtons));
searchField.getStyleClass().add("search-field");
Expand All @@ -152,13 +171,12 @@ public GlobalSearchBar(JabRefFrame frame, StateManager stateManager, Preferences
regexValidator = new FunctionBasedValidator<>(
searchField.textProperty(),
query -> !(regularExpressionButton.isSelected() && !validRegex()),
ValidationMessage.error(Localization.lang("Invalid regular expression"))
);
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));

this.getChildren().addAll(searchField, currentResults);
this.getChildren().addAll(searchField, openGlobalSearchButton, currentResults);
this.setSpacing(4.0);
this.setAlignment(Pos.CENTER_LEFT);

Expand All @@ -170,15 +188,18 @@ public GlobalSearchBar(JabRefFrame frame, StateManager stateManager, Preferences
// Async update
searchTask.restart();
},
query -> setSearchTerm(query.map(SearchQuery::getQuery).orElse(""))
);
query -> setSearchTerm(query.map(SearchQuery::getQuery).orElse("")));

EasyBind.subscribe(this.stateManager.activeSearchQueryProperty(), searchQuery -> {
searchQuery.ifPresent(query -> {
updateResults(this.stateManager.getSearchResultSize().intValue(), SearchDescribers.getSearchDescriberFor(query).getDescription(),
query.isGrammarBasedSearch());
});
});
this.stateManager.activeSearchQueryProperty().addListener((obs, oldvalue, newValue) -> newValue.ifPresent(this::updateSearchResultsForQuery));
this.stateManager.activeDatabaseProperty().addListener((obs, oldValue, newValue) -> stateManager.activeSearchQueryProperty().get().ifPresent(this::updateSearchResultsForQuery));
}

private void updateSearchResultsForQuery(SearchQuery query) {
updateResults(this.stateManager.getSearchResultSize().intValue(), SearchDescribers.getSearchDescriberFor(query).getDescription(),
query.isGrammarBasedSearch());
if ((globalSearchResultDialog != null) && globalSearchActive.getValue()) {
globalSearchResultDialog.updateSearch();
}
}

private void initSearchModifierButtons() {
Expand Down Expand Up @@ -209,25 +230,29 @@ private void initSearchModifierButtons() {
performSearch();
});

// ToDo: Reimplement searchMode (searchModeButton)
/* searchModeButton.setText(searchPreferences.getSearchDisplayMode().getDisplayName());
searchModeButton.setTooltip(new Tooltip(searchPreferences.getSearchDisplayMode().getToolTipText()));
searchModeButton.setOnAction(event -> {
SearchDisplayMode searchDisplayMode = searchPreferences.getSearchDisplayMode();
int nextSearchMode = (searchDisplayMode.ordinal() + 1) % SearchDisplayMode.values().length;
searchDisplayMode = SearchDisplayMode.values()[nextSearchMode];
searchPreferences = searchPreferences..withSearchDisplayMode(searchDisplayMode);
keepSearchString.setSelected(searchPreferences.isKeepSearchString());
keepSearchString.setTooltip(new Tooltip(Localization.lang("Keep search string across libraries")));
initSearchModifierButton(keepSearchString);
keepSearchString.setOnAction(evt -> {
searchPreferences = searchPreferences.withKeepSearchString(keepSearchString.isSelected());
preferencesService.storeSearchPreferences(searchPreferences);
performSearch();
});

searchModeButton.setText(searchDisplayMode.getDisplayName());
searchModeButton.setTooltip(new Tooltip(searchDisplayMode.getToolTipText()));
openGlobalSearchButton.disableProperty().bindBidirectional(globalSearchActive);
openGlobalSearchButton.setTooltip(new Tooltip(Localization.lang("Search across libraries in a new window")));
initSearchModifierButton(openGlobalSearchButton);
openGlobalSearchButton.setOnAction(evt -> {
globalSearchActive.setValue(true);
globalSearchResultDialog = new GlobalSearchResultDialog(ExternalFileTypes.getInstance(), undoManager);
performSearch();
}); */
globalSearchResultDialog.updateSearch();
globalSearchResultDialog.showAndWait();
globalSearchActive.setValue(false);
});
}

private void initSearchModifierButton(ToggleButton searchButton) {
private void initSearchModifierButton(ButtonBase searchButton) {
searchButton.setCursor(Cursor.DEFAULT);
searchButton.setMinHeight(28);
searchButton.setMaxHeight(28);
Expand Down
28 changes: 28 additions & 0 deletions src/main/java/org/jabref/gui/search/GlobalSearchResultDialog.fxml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.ButtonType?>
<?import javafx.scene.control.DialogPane?>
<?import javafx.scene.control.SplitPane?>
<?import javafx.scene.control.ToggleButton?>
<?import javafx.scene.control.Tooltip?>
<?import javafx.scene.layout.VBox?>
<?import org.jabref.gui.icon.JabRefIconView?>
<DialogPane xmlns="http://javafx.com/javafx/16" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="org.jabref.gui.search.GlobalSearchResultDialog"
prefHeight="400.0" prefWidth="600.0">
<content>
<VBox>
<SplitPane fx:id="container" orientation="VERTICAL"/>
<ToggleButton fx:id="keepOnTop" styleClass="icon-button,narrow" prefHeight="20.0" prefWidth="20.0">
<graphic>
<JabRefIconView glyph="KEEP_ON_TOP"/>
</graphic>
<tooltip>
<Tooltip text="%Keep dialog always on top"/>
</tooltip>
</ToggleButton>

</VBox>
</content>
<ButtonType fx:constant="OK"/>
</DialogPane>
Loading

0 comments on commit 9c5b8ab

Please sign in to comment.