diff --git a/src/main/java/org/jabref/gui/BasePanel.java b/src/main/java/org/jabref/gui/BasePanel.java index 7055194e48f..a6b527cde4d 100644 --- a/src/main/java/org/jabref/gui/BasePanel.java +++ b/src/main/java/org/jabref/gui/BasePanel.java @@ -7,7 +7,6 @@ import java.util.Optional; import javafx.application.Platform; -import javafx.beans.binding.Bindings; import javafx.geometry.Orientation; import javafx.scene.Node; import javafx.scene.control.SplitPane; @@ -372,7 +371,7 @@ public void setupMainPanel() { // Saves the divider position as soon as it changes // We need to keep a reference to the subscription, otherwise the binding gets garbage collected - dividerPositionSubscription = EasyBind.wrapNullable(Bindings.valueAt(splitPane.getDividers(), 0)) + dividerPositionSubscription = EasyBind.valueAt(splitPane.getDividers(), 0) .mapObservable(SplitPane.Divider::positionProperty) .subscribeToValues(this::saveDividerLocation); diff --git a/src/main/java/org/jabref/gui/JabRefFrame.java b/src/main/java/org/jabref/gui/JabRefFrame.java index 72b200214bf..7c203c5859a 100644 --- a/src/main/java/org/jabref/gui/JabRefFrame.java +++ b/src/main/java/org/jabref/gui/JabRefFrame.java @@ -457,7 +457,7 @@ private void initLayout() { setTop(head); splitPane.getItems().addAll(sidePane, tabbedPane); - splitPane.setResizableWithParent(sidePane, false); + SplitPane.setResizableWithParent(sidePane, false); // We need to wait with setting the divider since it gets reset a few times during the initial set-up mainStage.showingProperty().addListener(new ChangeListener<>() { @@ -954,14 +954,11 @@ private Group createTaskIndicator() { Tooltip someTasksRunning = new Tooltip(Localization.lang("Background Tasks are running")); Tooltip noTasksRunning = new Tooltip(Localization.lang("Background Tasks are done")); indicator.setTooltip(noTasksRunning); - stateManager.getAnyTaskRunning().addListener(new ChangeListener() { - @Override - public void changed(ObservableValue observable, Boolean oldValue, Boolean newValue) { - if (newValue.booleanValue()) { - indicator.setTooltip(someTasksRunning); - } else { - indicator.setTooltip(noTasksRunning); - } + stateManager.getAnyTaskRunning().addListener((observable, oldValue, newValue) -> { + if (newValue) { + indicator.setTooltip(someTasksRunning); + } else { + indicator.setTooltip(noTasksRunning); } }); diff --git a/src/main/java/org/jabref/gui/StateManager.java b/src/main/java/org/jabref/gui/StateManager.java index 2fc7fc532b0..8509adc5928 100644 --- a/src/main/java/org/jabref/gui/StateManager.java +++ b/src/main/java/org/jabref/gui/StateManager.java @@ -7,8 +7,6 @@ import javafx.beans.Observable; import javafx.beans.binding.Bindings; -import javafx.beans.binding.BooleanBinding; -import javafx.beans.binding.DoubleBinding; import javafx.beans.property.IntegerProperty; import javafx.beans.property.ReadOnlyListProperty; import javafx.beans.property.ReadOnlyListWrapper; @@ -27,6 +25,9 @@ import org.jabref.model.groups.GroupTreeNode; import org.jabref.model.util.OptionalUtil; +import com.tobiasdiez.easybind.EasyBind; +import com.tobiasdiez.easybind.EasyBinding; + /** * This class manages the GUI-state of JabRef, including: * @@ -48,17 +49,13 @@ public class StateManager { private final OptionalObjectProperty activeSearchQuery = OptionalObjectProperty.empty(); private final ObservableMap searchResultMap = FXCollections.observableHashMap(); private final OptionalObjectProperty focusOwner = OptionalObjectProperty.empty(); - private final ObservableList> backgroundTasks = FXCollections.observableArrayList(taskProperty -> { - return new Observable[] {taskProperty.progressProperty(), taskProperty.runningProperty()}; + private final ObservableList> backgroundTasks = FXCollections.observableArrayList(task -> { + return new Observable[]{task.progressProperty(), task.runningProperty()}; }); - private BooleanBinding anyTaskRunning = Bindings.createBooleanBinding( - () -> backgroundTasks.stream().anyMatch(Task::isRunning), backgroundTasks - ); + private final EasyBinding anyTaskRunning = EasyBind.reduce(backgroundTasks, tasks -> tasks.anyMatch(Task::isRunning)); - private DoubleBinding tasksProgress = Bindings.createDoubleBinding( - () -> backgroundTasks.stream().filter(Task::isRunning).mapToDouble(Task::getProgress).average().orElse(1), backgroundTasks - ); + private final EasyBinding tasksProgress = EasyBind.reduce(backgroundTasks, tasks -> tasks.filter(Task::isRunning).mapToDouble(Task::getProgress).average().orElse(1)); public StateManager() { activeGroups.bind(Bindings.valueAt(selectedGroups, activeDatabase.orElse(null))); @@ -143,11 +140,11 @@ public void addBackgroundTask(Task backgroundTask) { this.backgroundTasks.add(0, backgroundTask); } - public BooleanBinding getAnyTaskRunning() { + public EasyBinding getAnyTaskRunning() { return anyTaskRunning; } - public DoubleBinding getTasksProgress() { + public EasyBinding getTasksProgress() { return tasksProgress; } } diff --git a/src/main/java/org/jabref/gui/actions/ActionHelper.java b/src/main/java/org/jabref/gui/actions/ActionHelper.java index 89f9b4e67ad..bbb22b6022f 100644 --- a/src/main/java/org/jabref/gui/actions/ActionHelper.java +++ b/src/main/java/org/jabref/gui/actions/ActionHelper.java @@ -40,10 +40,10 @@ public static BooleanExpression isFieldSetForSelectedEntry(Field field, StateMan public static BooleanExpression isAnyFieldSetForSelectedEntry(List fields, StateManager stateManager) { ObservableList selectedEntries = stateManager.getSelectedEntries(); - Binding fieldsAreSet = EasyBind.wrapNullable(Bindings.valueAt(selectedEntries, 0)) + Binding fieldsAreSet = EasyBind.valueAt(selectedEntries, 0) .mapObservable(entry -> Bindings.createBooleanBinding(() -> { - return entry.getFields().stream().anyMatch(fields::contains); - }, entry.getFieldsObservable())) + return entry.getFields().stream().anyMatch(fields::contains); + }, entry.getFieldsObservable())) .orElse(false); return BooleanExpression.booleanExpression(fieldsAreSet); } @@ -51,14 +51,14 @@ public static BooleanExpression isAnyFieldSetForSelectedEntry(List fields public static BooleanExpression isFilePresentForSelectedEntry(StateManager stateManager, PreferencesService preferencesService) { ObservableList selectedEntries = stateManager.getSelectedEntries(); - Binding fileIsPresent = EasyBind.wrapNullable(Bindings.valueAt(selectedEntries, 0)).map(entry -> { + Binding fileIsPresent = EasyBind.valueAt(selectedEntries, 0).map(entry -> { List files = entry.getFiles(); if ((entry.getFiles().size() > 0) && stateManager.getActiveDatabase().isPresent()) { Optional filename = FileHelper.find( - stateManager.getActiveDatabase().get(), - files.get(0).getLink(), - preferencesService.getFilePreferences()); + stateManager.getActiveDatabase().get(), + files.get(0).getLink(), + preferencesService.getFilePreferences()); return filename.isPresent(); } else { return false; diff --git a/src/main/java/org/jabref/gui/bibtexkeypattern/GenerateBibtexKeySingleAction.java b/src/main/java/org/jabref/gui/bibtexkeypattern/GenerateBibtexKeySingleAction.java index 90789e32990..25c5196ac37 100644 --- a/src/main/java/org/jabref/gui/bibtexkeypattern/GenerateBibtexKeySingleAction.java +++ b/src/main/java/org/jabref/gui/bibtexkeypattern/GenerateBibtexKeySingleAction.java @@ -26,7 +26,7 @@ public GenerateBibtexKeySingleAction(BibEntry entry, BibDatabaseContext database this.undoManager = undoManager; if (preferencesService.getBibtexKeyPatternPreferences().avoidOverwritingCiteKey()) { - this.executable.bind(entry.getCiteKeyBinding().isNull()); + this.executable.bind(entry.getCiteKeyBinding().isEmpty()); } } diff --git a/src/main/java/org/jabref/gui/fieldeditors/AbstractEditorViewModel.java b/src/main/java/org/jabref/gui/fieldeditors/AbstractEditorViewModel.java index eb21c30ea53..167392c8515 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/AbstractEditorViewModel.java +++ b/src/main/java/org/jabref/gui/fieldeditors/AbstractEditorViewModel.java @@ -4,7 +4,6 @@ import javax.swing.undo.UndoManager; -import javafx.beans.binding.ObjectBinding; import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; @@ -18,6 +17,7 @@ import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.field.Field; +import com.tobiasdiez.easybind.EasyObservableValue; import de.saxsys.mvvmfx.utils.validation.CompositeValidator; import de.saxsys.mvvmfx.utils.validation.FunctionBasedValidator; import de.saxsys.mvvmfx.utils.validation.ValidationMessage; @@ -30,7 +30,7 @@ public class AbstractEditorViewModel extends AbstractViewModel { protected BibEntry entry; private final SuggestionProvider suggestionProvider; private final CompositeValidator fieldValidator; - private ObjectBinding fieldBinding; + private EasyObservableValue fieldBinding; public AbstractEditorViewModel(Field field, SuggestionProvider suggestionProvider, FieldCheckers fieldCheckers) { this.field = field; @@ -56,7 +56,7 @@ public void bindToEntry(BibEntry entry) { this.entry = entry; // We need to keep a reference to the binding since it otherwise gets discarded - fieldBinding = entry.getFieldBinding(field); + fieldBinding = entry.getFieldBinding(field).asOrdinary(); BindingsHelper.bindBidirectional( this.textProperty(), diff --git a/src/main/java/org/jabref/gui/groups/GroupNodeViewModel.java b/src/main/java/org/jabref/gui/groups/GroupNodeViewModel.java index a091683722f..8b53deaca03 100644 --- a/src/main/java/org/jabref/gui/groups/GroupNodeViewModel.java +++ b/src/main/java/org/jabref/gui/groups/GroupNodeViewModel.java @@ -22,7 +22,6 @@ import org.jabref.gui.icon.InternalMaterialDesignIcon; import org.jabref.gui.icon.JabRefIcon; import org.jabref.gui.util.BackgroundTask; -import org.jabref.gui.util.BindingsHelper; import org.jabref.gui.util.CustomLocalDragboard; import org.jabref.gui.util.DroppingMouseLocation; import org.jabref.gui.util.TaskExecutor; @@ -39,6 +38,7 @@ import com.google.common.base.Enums; import com.tobiasdiez.easybind.EasyBind; +import com.tobiasdiez.easybind.EasyObservableList; import de.jensd.fx.glyphs.materialdesignicons.MaterialDesignIcon; public class GroupNodeViewModel { @@ -76,7 +76,7 @@ public GroupNodeViewModel(BibDatabaseContext databaseContext, StateManager state .sorted((group1, group2) -> group1.getDisplayName().compareToIgnoreCase(group2.getDisplayName())) .collect(Collectors.toCollection(FXCollections::observableArrayList)); } else { - children = BindingsHelper.mapBacked(groupNode.getChildren(), this::toViewModel); + children = EasyBind.mapBacked(groupNode.getChildren(), this::toViewModel); } hasChildren = new SimpleBooleanProperty(); hasChildren.bind(Bindings.isNotEmpty(children)); @@ -89,9 +89,10 @@ public GroupNodeViewModel(BibDatabaseContext databaseContext, StateManager state entriesList = databaseContext.getDatabase().getEntries(); entriesList.addListener(this::onDatabaseChanged); - ObservableList selectedEntriesMatchStatus = EasyBind.map(stateManager.getSelectedEntries(), groupNode::matches); - anySelectedEntriesMatched = BindingsHelper.any(selectedEntriesMatchStatus, matched -> matched); - allSelectedEntriesMatched = BindingsHelper.all(selectedEntriesMatchStatus, matched -> matched); + EasyObservableList selectedEntriesMatchStatus = EasyBind.map(stateManager.getSelectedEntries(), groupNode::matches); + anySelectedEntriesMatched = selectedEntriesMatchStatus.anyMatch(matched -> matched); + // 'all' returns 'true' for empty streams, so this has to be checked explicitly + allSelectedEntriesMatched = selectedEntriesMatchStatus.isEmptyBinding().not().and(selectedEntriesMatchStatus.allMatch(matched -> matched)); } public GroupNodeViewModel(BibDatabaseContext databaseContext, StateManager stateManager, TaskExecutor taskExecutor, AbstractGroup group, CustomLocalDragboard localDragboard) { diff --git a/src/main/java/org/jabref/gui/groups/GroupTreeViewModel.java b/src/main/java/org/jabref/gui/groups/GroupTreeViewModel.java index 4ea073c297d..8d58b990143 100644 --- a/src/main/java/org/jabref/gui/groups/GroupTreeViewModel.java +++ b/src/main/java/org/jabref/gui/groups/GroupTreeViewModel.java @@ -7,7 +7,6 @@ import java.util.function.Predicate; import java.util.stream.Collectors; -import javafx.beans.binding.Bindings; import javafx.beans.property.ListProperty; import javafx.beans.property.ObjectProperty; import javafx.beans.property.SimpleListProperty; @@ -61,7 +60,7 @@ public GroupTreeViewModel(StateManager stateManager, DialogService dialogService EasyBind.subscribe(selectedGroups, this::onSelectedGroupChanged); // Set-up bindings - filterPredicate.bind(Bindings.createObjectBinding(() -> group -> group.isMatchedBy(filterText.get()), filterText)); + filterPredicate.bind(EasyBind.map(filterText, text -> group -> group.isMatchedBy(text))); // Init refresh(); diff --git a/src/main/java/org/jabref/gui/maintable/BibEntryTableViewModel.java b/src/main/java/org/jabref/gui/maintable/BibEntryTableViewModel.java index fd011a8cfdf..c09c540ef39 100644 --- a/src/main/java/org/jabref/gui/maintable/BibEntryTableViewModel.java +++ b/src/main/java/org/jabref/gui/maintable/BibEntryTableViewModel.java @@ -8,7 +8,6 @@ import java.util.stream.Collectors; import javafx.beans.binding.Bindings; -import javafx.beans.binding.ObjectBinding; import javafx.beans.property.SimpleObjectProperty; import javafx.beans.value.ObservableValue; @@ -28,15 +27,16 @@ import com.tobiasdiez.easybind.EasyBind; import com.tobiasdiez.easybind.EasyBinding; +import com.tobiasdiez.easybind.optional.OptionalBinding; public class BibEntryTableViewModel { private final BibEntry entry; private final BibDatabase database; private final MainTableNameFormatter nameFormatter; private final Map> fieldValues = new HashMap<>(); - private final Map>> specialFieldValues = new HashMap<>(); + private final Map> specialFieldValues = new HashMap<>(); private final EasyBinding> linkedFiles; - private final ObjectBinding> linkedIdentifiers; + private final EasyBinding> linkedIdentifiers; private final ObservableValue> matchedGroups; public BibEntryTableViewModel(BibEntry entry, BibDatabaseContext database, MainTableNameFormatter nameFormatter) { @@ -44,43 +44,48 @@ public BibEntryTableViewModel(BibEntry entry, BibDatabaseContext database, MainT this.database = database.getDatabase(); this.nameFormatter = nameFormatter; - this.linkedFiles = EasyBind.map(getField(StandardField.FILE), FileFieldParser::parse); + this.linkedFiles = getField(StandardField.FILE).map(FileFieldParser::parse).orElse(Collections.emptyList()); this.linkedIdentifiers = createLinkedIdentifiersBinding(entry); - this.matchedGroups = createMatchedGroupsBinding(database); + this.matchedGroups = createMatchedGroupsBinding(database, entry); } - private ObjectBinding> createLinkedIdentifiersBinding(BibEntry entry) { - return Bindings.createObjectBinding(() -> { + private static EasyBinding> createLinkedIdentifiersBinding(BibEntry entry) { + return EasyBind.combine( + entry.getFieldBinding(StandardField.URL), + entry.getFieldBinding(StandardField.DOI), + entry.getFieldBinding(StandardField.URI), + entry.getFieldBinding(StandardField.EPRINT), + (url, doi, uri, eprint) -> { Map identifiers = new HashMap<>(); - entry.getField(StandardField.URL).ifPresent(value -> identifiers.put(StandardField.URL, value)); - entry.getField(StandardField.DOI).ifPresent(value -> identifiers.put(StandardField.DOI, value)); - entry.getField(StandardField.URI).ifPresent(value -> identifiers.put(StandardField.URI, value)); - entry.getField(StandardField.EPRINT).ifPresent(value -> identifiers.put(StandardField.EPRINT, value)); + url.ifPresent(value -> identifiers.put(StandardField.URL, value)); + doi.ifPresent(value -> identifiers.put(StandardField.DOI, value)); + uri.ifPresent(value -> identifiers.put(StandardField.URI, value)); + eprint.ifPresent(value -> identifiers.put(StandardField.EPRINT, value)); return identifiers; - }, - getEntry().getFieldBinding(StandardField.URL), - getEntry().getFieldBinding(StandardField.DOI), - getEntry().getFieldBinding(StandardField.URI), - getEntry().getFieldBinding(StandardField.EPRINT)); + }); } public BibEntry getEntry() { return entry; } - public ObjectBinding getField(Field field) { - return entry.getFieldBinding(field); + private static ObservableValue> createMatchedGroupsBinding(BibDatabaseContext database, BibEntry entry) { + Optional root = database.getMetaData().getGroups(); + if (root.isPresent()) { + return EasyBind.map(entry.getFieldBinding(StandardField.GROUPS), field -> { + List groups = root.get().getMatchingGroups(entry) + .stream() + .map(GroupTreeNode::getGroup) + .collect(Collectors.toList()); + groups.remove(root.get().getGroup()); + return groups; + }); + } + return new SimpleObjectProperty<>(Collections.emptyList()); } - public ObservableValue> getSpecialField(SpecialField field) { - ObservableValue> value = specialFieldValues.get(field); - if (value != null) { - return value; - } else { - value = EasyBind.map(getField(field), fieldValue -> field.parseValue(fieldValue).map(SpecialFieldValueViewModel::new)); - specialFieldValues.put(field, value); - return value; - } + public OptionalBinding getField(Field field) { + return entry.getFieldBinding(field); } public ObservableValue> getLinkedFiles() { @@ -95,19 +100,15 @@ public ObservableValue> getMatchedGroups() { return matchedGroups; } - private ObservableValue> createMatchedGroupsBinding(BibDatabaseContext database) { - Optional root = database.getMetaData().getGroups(); - if (root.isPresent()) { - return EasyBind.map(entry.getFieldBinding(StandardField.GROUPS), field -> { - List groups = root.get().getMatchingGroups(entry) - .stream() - .map(GroupTreeNode::getGroup) - .collect(Collectors.toList()); - groups.remove(root.get().getGroup()); - return groups; - }); + public ObservableValue> getSpecialField(SpecialField field) { + OptionalBinding value = specialFieldValues.get(field); + if (value != null) { + return value; + } else { + value = getField(field).flatMap(fieldValue -> field.parseValue(fieldValue).map(SpecialFieldValueViewModel::new)); + specialFieldValues.put(field, value); + return value; } - return new SimpleObjectProperty<>(Collections.emptyList()); } public ObservableValue getFields(OrFields fields) { diff --git a/src/main/java/org/jabref/gui/maintable/MainTableDataModel.java b/src/main/java/org/jabref/gui/maintable/MainTableDataModel.java index d4bd4d2603d..7076a0adf4a 100644 --- a/src/main/java/org/jabref/gui/maintable/MainTableDataModel.java +++ b/src/main/java/org/jabref/gui/maintable/MainTableDataModel.java @@ -13,12 +13,15 @@ import org.jabref.Globals; import org.jabref.gui.groups.GroupViewMode; import org.jabref.gui.util.BindingsHelper; +import org.jabref.logic.search.SearchQuery; import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.entry.BibEntry; import org.jabref.model.groups.GroupTreeNode; import org.jabref.model.search.matchers.MatcherSet; import org.jabref.model.search.matchers.MatcherSets; +import com.tobiasdiez.easybind.EasyBind; + public class MainTableDataModel { private final FilteredList entriesFiltered; private final SortedList entriesSorted; @@ -28,13 +31,11 @@ public MainTableDataModel(BibDatabaseContext context) { ObservableList allEntries = BindingsHelper.forUI(context.getDatabase().getEntries()); MainTableNameFormatter nameFormatter = new MainTableNameFormatter(Globals.prefs); - ObservableList entriesViewModel = BindingsHelper.mapBacked(allEntries, entry -> new BibEntryTableViewModel(entry, context, nameFormatter)); + ObservableList entriesViewModel = EasyBind.mapBacked(allEntries, entry -> new BibEntryTableViewModel(entry, context, nameFormatter)); entriesFiltered = new FilteredList<>(entriesViewModel); entriesFiltered.predicateProperty().bind( - Bindings.createObjectBinding(() -> this::isMatched, - Globals.stateManager.activeGroupProperty(), Globals.stateManager.activeSearchQueryProperty()) - + EasyBind.combine(Globals.stateManager.activeGroupProperty(), Globals.stateManager.activeSearchQueryProperty(), (groups, query) -> entry -> isMatched(groups, query, entry)) ); IntegerProperty resultSize = new SimpleIntegerProperty(); @@ -45,19 +46,17 @@ public MainTableDataModel(BibDatabaseContext context) { groupViewMode = Globals.prefs.getGroupViewMode(); } - private boolean isMatched(BibEntryTableViewModel entry) { - return isMatchedByGroup(entry) && isMatchedBySearch(entry); + private boolean isMatched(ObservableList groups, Optional query, BibEntryTableViewModel entry) { + return isMatchedByGroup(groups, entry) && isMatchedBySearch(query, entry); } - private boolean isMatchedBySearch(BibEntryTableViewModel entry) { - return Globals.stateManager - .activeSearchQueryProperty().getValue() - .map(matcher -> matcher.isMatch(entry.getEntry())) - .orElse(true); + private boolean isMatchedBySearch(Optional query, BibEntryTableViewModel entry) { + return query.map(matcher -> matcher.isMatch(entry.getEntry())) + .orElse(true); } - private boolean isMatchedByGroup(BibEntryTableViewModel entry) { - return createGroupMatcher(Globals.stateManager.activeGroupProperty().getValue()) + private boolean isMatchedByGroup(ObservableList groups, BibEntryTableViewModel entry) { + return createGroupMatcher(groups) .map(matcher -> matcher.isMatch(entry.getEntry())) .orElse(true); } diff --git a/src/main/java/org/jabref/gui/preferences/PreferencesFilterDialog.java b/src/main/java/org/jabref/gui/preferences/PreferencesFilterDialog.java index dce46746f56..19b3fbfb5b0 100644 --- a/src/main/java/org/jabref/gui/preferences/PreferencesFilterDialog.java +++ b/src/main/java/org/jabref/gui/preferences/PreferencesFilterDialog.java @@ -21,6 +21,7 @@ import org.jabref.preferences.JabRefPreferencesFilter; import com.airhacks.afterburner.views.ViewLoader; +import com.tobiasdiez.easybind.EasyBind; public class PreferencesFilterDialog extends BaseDialog { @@ -52,14 +53,13 @@ public PreferencesFilterDialog(JabRefPreferencesFilter preferencesFilter) { @FXML private void initialize() { showOnlyDeviatingPreferenceOptions.setOnAction(event -> updateModel()); - filteredOptions.predicateProperty().bind(Bindings.createObjectBinding(() -> { - String searchText = searchField.getText(); + filteredOptions.predicateProperty().bind(EasyBind.map(searchField.textProperty(), searchText -> { if ((searchText == null) || searchText.isEmpty()) { return null; } String lowerCaseSearchText = searchText.toLowerCase(Locale.ROOT); return (option) -> option.getKey().toLowerCase(Locale.ROOT).contains(lowerCaseSearchText); - }, searchField.textProperty())); + })); columnType.setCellValueFactory(data -> new ReadOnlyObjectWrapper<>(data.getValue().getType())); columnKey.setCellValueFactory(data -> new ReadOnlyStringWrapper(data.getValue().getKey())); columnValue.setCellValueFactory(data -> new ReadOnlyObjectWrapper<>(data.getValue().getValue())); diff --git a/src/main/java/org/jabref/gui/util/BindingsHelper.java b/src/main/java/org/jabref/gui/util/BindingsHelper.java index d144bccff65..d63f848e893 100644 --- a/src/main/java/org/jabref/gui/util/BindingsHelper.java +++ b/src/main/java/org/jabref/gui/util/BindingsHelper.java @@ -4,9 +4,7 @@ import java.util.Map; import java.util.function.Consumer; import java.util.function.Function; -import java.util.function.Predicate; -import javafx.beans.binding.Bindings; import javafx.beans.binding.BooleanBinding; import javafx.beans.binding.ObjectBinding; import javafx.beans.binding.StringBinding; @@ -35,15 +33,6 @@ public class BindingsHelper { private BindingsHelper() { } - public static BooleanBinding any(ObservableList source, Predicate predicate) { - return Bindings.createBooleanBinding(() -> source.stream().anyMatch(predicate), source); - } - - public static BooleanBinding all(ObservableList source, Predicate 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 Subscription includePseudoClassWhen(Node node, PseudoClass pseudoClass, ObservableValue condition) { Consumer changePseudoClass = value -> node.pseudoClassStateChanged(pseudoClass, value); Subscription subscription = EasyBind.subscribe(condition, changePseudoClass); @@ -53,18 +42,6 @@ public static Subscription includePseudoClassWhen(Node node, PseudoClass pseudoC return subscription; } - /** - * Creates a new list in which each element is converted using the provided mapping. - * All changes to the underlying list are propagated to the converted list. - * - * In contrast to {@link com.tobiasdiez.easybind.EasyBind#map(ObservableList, Function)}, - * the items are converted when the are inserted (and at the initialization) instead of when they are accessed. - * Thus the initial CPU overhead and memory consumption is higher but the access to list items is quicker. - */ - public static MappedList mapBacked(ObservableList source, Function mapper) { - return new MappedList<>(source, mapper); - } - public static ObservableList map(ObservableValue source, Function> mapper) { PreboundBinding> binding = new PreboundBinding>(source) { diff --git a/src/main/java/org/jabref/gui/util/MappedList.java b/src/main/java/org/jabref/gui/util/MappedList.java deleted file mode 100644 index 9870e556ebe..00000000000 --- a/src/main/java/org/jabref/gui/util/MappedList.java +++ /dev/null @@ -1,95 +0,0 @@ -package org.jabref.gui.util; - -import java.util.ArrayList; -import java.util.List; -import java.util.function.Function; - -import javafx.collections.ListChangeListener; -import javafx.collections.ObservableList; -import javafx.collections.transformation.TransformationList; - -/** - * MappedList implementation based on https://github.com/corda/corda/blob/master/client/jfx/src/main/kotlin/net/corda/client/jfx/utils/MappedList.kt - */ -public final class MappedList extends TransformationList { - - private final Function mapper; - private final List backingList; - - public MappedList(ObservableList sourceList, Function mapper) { - super(sourceList); - this.mapper = mapper; - this.backingList = new ArrayList<>(sourceList.size()); - sourceList.stream().map(mapper::apply).forEach(backingList::add); - } - - @Override - protected void sourceChanged(ListChangeListener.Change change) { - beginChange(); - while (change.next()) { - if (change.wasPermutated()) { - int from = change.getFrom(); - int to = change.getTo(); - - // get permutation array - int[] permutation = new int[to - from]; - for (int i = 0; i < to - from; i++) { - permutation[i] = change.getPermutation(i); - } - - // perform permutation - Object[] permutedPart = new Object[to - from]; - for (int i = from; i < to; i++) { - permutedPart[permutation[i]] = backingList.get(i); - } - - // update backingList - for (int i = 0; i < to; i++) { - backingList.set(i + from, (A) permutedPart[i]); - } - nextPermutation(from, to, permutation); - } else if (change.wasUpdated()) { - backingList.set(change.getFrom(), mapper.apply(getSource().get(change.getFrom()))); - nextUpdate(change.getFrom()); - } else { - if (change.wasRemoved()) { - int removePosition = change.getFrom(); - List removed = new ArrayList<>(change.getRemovedSize()); - for (int i = 0; i < change.getRemovedSize(); i++) { - removed.add(backingList.remove(removePosition)); - } - nextRemove(change.getFrom(), removed); - } - if (change.wasAdded()) { - int addStart = change.getFrom(); - int addEnd = change.getTo(); - for (int i = addStart; i < addEnd; i++) { - backingList.add(i, mapper.apply(change.getList().get(i))); - } - nextAdd(addStart, addEnd); - } - } - } - endChange(); - } - - @Override - public int getSourceIndex(int index) { - return index; - } - - @Override - public int getViewIndex(int index) { - return index; - } - - @Override - public A get(int index) { - return backingList.get(index); - } - - @Override - public int size() { - return backingList.size(); - } -} diff --git a/src/main/java/org/jabref/gui/util/RecursiveTreeItem.java b/src/main/java/org/jabref/gui/util/RecursiveTreeItem.java index 4926453ea7c..6bf002c59d0 100644 --- a/src/main/java/org/jabref/gui/util/RecursiveTreeItem.java +++ b/src/main/java/org/jabref/gui/util/RecursiveTreeItem.java @@ -7,13 +7,14 @@ import javafx.beans.property.ObjectProperty; import javafx.beans.property.SimpleObjectProperty; import javafx.beans.value.ObservableValue; -import javafx.collections.ListChangeListener; import javafx.collections.ObservableList; import javafx.collections.transformation.FilteredList; import javafx.scene.Node; import javafx.scene.control.CheckBoxTreeItem; import javafx.util.Callback; +import com.tobiasdiez.easybind.EasyBind; + /** * @implNote Taken from https://gist.github.com/lestard/011e9ed4433f9eb791a8 * @implNote As CheckBoxTreeItem extends TreeItem, this class will work for both. @@ -21,8 +22,8 @@ public class RecursiveTreeItem extends CheckBoxTreeItem { private final Callback expandedProperty; - private Callback> childrenFactory; - private ObjectProperty> filter = new SimpleObjectProperty<>(); + private final Callback> childrenFactory; + private final ObjectProperty> filter = new SimpleObjectProperty<>(); private FilteredList> children; public RecursiveTreeItem(final T value, Callback> func) { @@ -66,25 +67,10 @@ private void bindExpandedProperty(T value, Callback expanded } private void addChildrenListener(T value) { - children = new FilteredList<>( - BindingsHelper.mapBacked(childrenFactory.call(value), - child -> new RecursiveTreeItem<>(child, getGraphic(), childrenFactory, expandedProperty, filter))); - children.predicateProperty().bind(Bindings.createObjectBinding(() -> this::showNode, filter)); - - getChildren().addAll(0, children); - - children.addListener((ListChangeListener>) change -> { - while (change.next()) { + children = EasyBind.mapBacked(childrenFactory.call(value), child -> new RecursiveTreeItem<>(child, getGraphic(), childrenFactory, expandedProperty, filter)) + .filtered(Bindings.createObjectBinding(() -> this::showNode, filter)); - if (change.wasRemoved()) { - getChildren().removeAll(change.getRemoved()); - } - - if (change.wasAdded()) { - getChildren().addAll(change.getFrom(), change.getAddedSubList()); - } - } - }); + Bindings.bindContent(getChildren(), children); } private boolean showNode(RecursiveTreeItem node) { diff --git a/src/main/java/org/jabref/model/entry/BibEntry.java b/src/main/java/org/jabref/model/entry/BibEntry.java index 703a38dcd30..aa9482831b6 100644 --- a/src/main/java/org/jabref/model/entry/BibEntry.java +++ b/src/main/java/org/jabref/model/entry/BibEntry.java @@ -15,8 +15,6 @@ import java.util.regex.Pattern; import javafx.beans.Observable; -import javafx.beans.binding.Bindings; -import javafx.beans.binding.ObjectBinding; import javafx.beans.property.ObjectProperty; import javafx.beans.property.SimpleObjectProperty; import javafx.collections.FXCollections; @@ -42,6 +40,7 @@ import com.google.common.base.Strings; import com.google.common.eventbus.EventBus; import com.tobiasdiez.easybind.EasyBind; +import com.tobiasdiez.easybind.optional.OptionalBinding; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -929,14 +928,14 @@ public Optional getMonth() { return getFieldOrAlias(StandardField.MONTH).flatMap(Month::parse); } - public ObjectBinding getFieldBinding(Field field) { + public OptionalBinding getFieldBinding(Field field) { if ((field == InternalField.TYPE_HEADER) || (field == InternalField.OBSOLETE_TYPE_HEADER)) { - return (ObjectBinding) EasyBind.map(type, EntryType::getDisplayName); + return EasyBind.wrapNullable(type).map(EntryType::getDisplayName); } - return Bindings.valueAt(fields, field); + return EasyBind.valueAt(fields, field); } - public ObjectBinding getCiteKeyBinding() { + public OptionalBinding getCiteKeyBinding() { return getFieldBinding(InternalField.KEY_FIELD); }