Skip to content

Commit

Permalink
Highlight groups that match any/all of the entries selected in the ma…
Browse files Browse the repository at this point in the history
…in table. (#2515)

* Highlight groups that match any/all of the entries selected in the main table.

* Update language

* Don't highlight all entries group
  • Loading branch information
tobiasdiez committed Feb 6, 2017
1 parent a52d657 commit 2e9e7c8
Show file tree
Hide file tree
Showing 48 changed files with 1,101 additions and 1,321 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ We refer to [GitHub issues](https://github.com/JabRef/jabref/issues) by using `#
- Add tab which shows the MathSciNet review website if the `MRNumber` field is present.
- Partly switched to new UI technology (JavaFX).
- Redesigned group panel.
- Number of matched entries is always shown.
- The background color of the hit counter signals whether the group contains all/any of the entries selected in the main table.
- Redesigned about dialog.
- Redesigned key bindings dialog.
- Redesigned journal abbreviations dialog.
Expand Down
783 changes: 363 additions & 420 deletions src/main/java/net/sf/jabref/gui/BasePanel.java

Large diffs are not rendered by default.

1,025 changes: 472 additions & 553 deletions src/main/java/net/sf/jabref/gui/JabRefFrame.java

Large diffs are not rendered by default.

14 changes: 14 additions & 0 deletions src/main/java/net/sf/jabref/gui/StateManager.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
package net.sf.jabref.gui;

import java.util.List;
import java.util.Optional;

import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;

import net.sf.jabref.model.database.BibDatabaseContext;
import net.sf.jabref.model.entry.BibEntry;
import net.sf.jabref.model.groups.GroupTreeNode;

/**
Expand All @@ -20,6 +24,7 @@ public class StateManager {

private final ObjectProperty<Optional<BibDatabaseContext>> activeDatabase = new SimpleObjectProperty<>(Optional.empty());
private final ObjectProperty<Optional<GroupTreeNode>> activeGroup = new SimpleObjectProperty<>(Optional.empty());
private final ObservableList<BibEntry> selectedEntries = FXCollections.observableArrayList();

public ObjectProperty<Optional<BibDatabaseContext>> activeDatabaseProperty() {
return activeDatabase;
Expand All @@ -28,4 +33,13 @@ public ObjectProperty<Optional<BibDatabaseContext>> activeDatabaseProperty() {
public ObjectProperty<Optional<GroupTreeNode>> activeGroupProperty() {
return activeGroup;
}

public ObservableList<BibEntry> getSelectedEntries() {
return FXCollections.unmodifiableObservableList(selectedEntries);
}

public void setSelectedEntries(List<BibEntry> newSelectedEntries) {
selectedEntries.clear();
selectedEntries.addAll(newSelectedEntries);
}
}
3 changes: 0 additions & 3 deletions src/main/java/net/sf/jabref/gui/actions/Actions.java
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,6 @@ public class Actions {
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 TOGGLE_HIGHLIGHTS_GROUPS_MATCHING_ALL = "toggleHighlightGroupsMatchingAll";
public static final String TOGGLE_HIGHLIGHTS_GROUPS_MATCHING_ANY = "toggleHighlightGroupsMatchingAny";
public static final String TOGGLE_HIGHLIGHTS_GROUPS_MATCHING_DISABLE = "toggleHighlightGroupsMatchingDisable";
public static final String TOGGLE_GROUPS = "toggleGroups";
public static final String TOGGLE_PREVIEW = "togglePreview";
public static final String UNABBREVIATE = "unabbreviate";
Expand Down
28 changes: 22 additions & 6 deletions src/main/java/net/sf/jabref/gui/groups/GroupNodeViewModel.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@

import javafx.application.Platform;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.collections.ObservableList;

import net.sf.jabref.gui.StateManager;
import net.sf.jabref.gui.util.BindingsHelper;
import net.sf.jabref.logic.l10n.Localization;
import net.sf.jabref.model.database.BibDatabaseContext;
import net.sf.jabref.model.entry.event.EntryEvent;
Expand All @@ -28,31 +31,44 @@ public class GroupNodeViewModel {
private final GroupTreeNode groupNode;
private final SimpleIntegerProperty hits;
private final SimpleBooleanProperty hasChildren;
private final BooleanBinding anySelectedEntriesMatched;
private final BooleanBinding allSelectedEntriesMatched;

public GroupNodeViewModel(BibDatabaseContext databaseContext, GroupTreeNode groupNode) {
public GroupNodeViewModel(BibDatabaseContext databaseContext, StateManager stateManager, GroupTreeNode groupNode) {
this.databaseContext = Objects.requireNonNull(databaseContext);
this.groupNode = Objects.requireNonNull(groupNode);

name = groupNode.getName();
isRoot = groupNode.isRoot();
iconCode = "";
children = EasyBind.map(groupNode.getChildren(), child -> new GroupNodeViewModel(databaseContext, child));
children = EasyBind.map(groupNode.getChildren(), child -> new GroupNodeViewModel(databaseContext, stateManager, child));
hasChildren = new SimpleBooleanProperty();
hasChildren.bind(Bindings.isNotEmpty(children));
hits = new SimpleIntegerProperty(0);
calculateNumberOfMatches();

// Register listener
databaseContext.getDatabase().registerListener(this);

ObservableList<Boolean> selectedEntriesMatchStatus = EasyBind.map(stateManager.getSelectedEntries(), groupNode::matches);
anySelectedEntriesMatched = BindingsHelper.any(selectedEntriesMatchStatus, matched -> matched);
allSelectedEntriesMatched = BindingsHelper.all(selectedEntriesMatchStatus, matched -> matched);
}

public GroupNodeViewModel(BibDatabaseContext databaseContext, StateManager stateManager, AbstractGroup group) {
this(databaseContext, stateManager, new GroupTreeNode(group));
}

static GroupNodeViewModel getAllEntriesGroup(BibDatabaseContext newDatabase, StateManager stateManager) {
return new GroupNodeViewModel(newDatabase, stateManager, new AllEntriesGroup(Localization.lang("All entries")));
}

public GroupNodeViewModel(BibDatabaseContext databaseContext, AbstractGroup group) {
this(databaseContext, new GroupTreeNode(group));
public BooleanBinding anySelectedEntriesMatchedProperty() {
return anySelectedEntriesMatched;
}

static GroupNodeViewModel getAllEntriesGroup(BibDatabaseContext newDatabase) {
return new GroupNodeViewModel(newDatabase, new AllEntriesGroup(Localization.lang("All entries")));
public BooleanBinding allSelectedEntriesMatchedProperty() {
return allSelectedEntriesMatched;
}

public SimpleBooleanProperty hasChildrenProperty() {
Expand Down
8 changes: 8 additions & 0 deletions src/main/java/net/sf/jabref/gui/groups/GroupTree.css
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,14 @@
-fx-background-radius: 10px;
}

.numberColumn > .hits:any-selected {
-fx-background-color: #c6c44f;
}

.numberColumn > .hits:all-selected {
-fx-background-color: #4dc64f;
}

.disclosureNodeColumn {
-fx-alignment: top-right;
-fx-font-size: 12px;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import net.sf.jabref.gui.AbstractController;
import net.sf.jabref.gui.DialogService;
import net.sf.jabref.gui.StateManager;
import net.sf.jabref.gui.util.BindingsHelper;
import net.sf.jabref.gui.util.RecursiveTreeItem;
import net.sf.jabref.gui.util.ViewModelTreeTableCellFactory;
import net.sf.jabref.logic.l10n.Localization;
Expand Down Expand Up @@ -59,10 +60,16 @@ public void initialize() {
);

// Number of hits
PseudoClass anySelected = PseudoClass.getPseudoClass("any-selected");
PseudoClass allSelected = PseudoClass.getPseudoClass("all-selected");
numberColumn.setCellFactory(new ViewModelTreeTableCellFactory<GroupNodeViewModel, GroupNodeViewModel>()
.withGraphic(viewModel -> {
final StackPane node = new StackPane();
node.getStyleClass().setAll("hits");
if (!viewModel.isRoot()) {
BindingsHelper.includePseudoClassWhen(node, anySelected, viewModel.anySelectedEntriesMatchedProperty());
BindingsHelper.includePseudoClassWhen(node, allSelected, viewModel.allSelectedEntriesMatchedProperty());
}
Text text = new Text();
text.textProperty().bind(viewModel.getHits().asString());
text.getStyleClass().setAll("text");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,8 @@ private void onActiveDatabaseChanged(Optional<BibDatabaseContext> newDatabase) {
GroupNodeViewModel newRoot = newDatabase
.map(BibDatabaseContext::getMetaData)
.flatMap(MetaData::getGroups)
.map(root -> new GroupNodeViewModel(newDatabase.get(), root))
.orElse(GroupNodeViewModel.getAllEntriesGroup(newDatabase.get()));
.map(root -> new GroupNodeViewModel(newDatabase.get(), stateManager, root))
.orElse(GroupNodeViewModel.getAllEntriesGroup(newDatabase.get(), stateManager));
rootGroup.setValue(newRoot);
}
}
Expand Down
47 changes: 47 additions & 0 deletions src/main/java/net/sf/jabref/gui/util/BindingsHelper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package net.sf.jabref.gui.util;

import java.util.function.Predicate;

import javafx.beans.binding.Bindings;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.BooleanPropertyBase;
import javafx.beans.value.ObservableValue;
import javafx.collections.ObservableList;
import javafx.css.PseudoClass;
import javafx.scene.Node;

/**
* Helper methods for javafx binding.
* Some methods are taken from https://bugs.openjdk.java.net/browse/JDK-8134679
*/
public class BindingsHelper {
public static <T> BooleanBinding any(ObservableList<T> source, Predicate<T> predicate) {
return Bindings.createBooleanBinding(() -> source.stream().anyMatch(predicate), source);
}

public static <T> BooleanBinding all(ObservableList<T> source, Predicate<T> predicate) {
// Stream.allMatch() (in contrast to Stream.anyMatch() returns 'true' for empty streams, so this has to be checked explicitly.
return Bindings.createBooleanBinding(() -> !source.isEmpty() && source.stream().allMatch(predicate), source);
}

public static void includePseudoClassWhen(Node node, PseudoClass pseudoClass, ObservableValue<? extends Boolean> condition) {
BooleanProperty pseudoClassState = new BooleanPropertyBase(false) {
@Override
protected void invalidated() {
node.pseudoClassStateChanged(pseudoClass, get());
}

@Override
public Object getBean() {
return node;
}

@Override
public String getName() {
return pseudoClass.getPseudoClassName();
}
};
pseudoClassState.bind(condition);
}
}
8 changes: 8 additions & 0 deletions src/main/java/net/sf/jabref/model/groups/GroupTreeNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -216,4 +216,12 @@ public int calculateNumberOfMatches(List<BibEntry> entries) {
public int calculateNumberOfMatches(BibDatabase database) {
return calculateNumberOfMatches(database.getEntries());
}

/**
* Returns whether this group matches the specified {@link BibEntry} while taking the hierarchical information
* into account.
*/
public boolean matches(BibEntry entry) {
return getSearchMatcher().isMatch(entry);
}
}

This file was deleted.

Loading

0 comments on commit 2e9e7c8

Please sign in to comment.