diff --git a/CHANGELOG.md b/CHANGELOG.md index 30029a698af..2dd90147968 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,6 +44,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv - We added a fallback for the "Convert to biblatex" cleanup when it failed to populate the `date` field if `year` contained a full date in ISO format (e.g., `2011-11-11`). [#11868](https://github.com/JabRef/jabref/issues/11868) - We fixed an issue where directory check for relative path was not handled properly under library properties. [#13017](https://github.com/JabRef/jabref/issues/13017) +- We fixed an exception on tab dragging. [#12921](https://github.com/JabRef/jabref/issues/12921) - We fixed an issue where the option for which method to use when parsing plaintext citations was unavailable in the 'Create New Entry' tool. [#8808](https://github.com/JabRef/jabref/issues/8808) - We fixed an issue where the "Make/Sync bibliography" button in the OpenOffice/LibreOffice sidebar was not enabled when a jstyle was selected. [#13055](https://github.com/JabRef/jabref/pull/13055) - We fixed an issue where CSL bibliography title properties would be saved even if the "Modify bibliography title" dialog was closed without pressing the "OK" button. [#13074](https://github.com/JabRef/jabref/pull/13074) diff --git a/jabgui/src/main/java/org/jabref/gui/frame/JabRefFrame.java b/jabgui/src/main/java/org/jabref/gui/frame/JabRefFrame.java index 52ba69e2ca7..4280e9b5ff4 100644 --- a/jabgui/src/main/java/org/jabref/gui/frame/JabRefFrame.java +++ b/jabgui/src/main/java/org/jabref/gui/frame/JabRefFrame.java @@ -15,7 +15,6 @@ import javafx.beans.value.ObservableValue; import javafx.collections.ListChangeListener; import javafx.collections.ObservableList; -import javafx.collections.transformation.FilteredList; import javafx.event.Event; import javafx.geometry.Orientation; import javafx.scene.control.ContextMenu; @@ -374,13 +373,7 @@ private void initKeyBindings() { } private void initBindings() { - // Bind global state - FilteredList filteredTabs = new FilteredList<>(tabbedPane.getTabs()); - filteredTabs.setPredicate(LibraryTab.class::isInstance); - - // This variable cannot be inlined, since otherwise the list created by EasyBind is being garbage collected - openDatabaseList = EasyBind.map(filteredTabs, tab -> ((LibraryTab) tab).getBibDatabaseContext()); - EasyBind.bindContent(stateManager.getOpenDatabases(), openDatabaseList); + BindingsHelper.bindContentFiltered(tabbedPane.getTabs(), stateManager.getOpenDatabases(), LibraryTab.class::isInstance); // the binding for stateManager.activeDatabaseProperty() is at org.jabref.gui.LibraryTab.onDatabaseLoadingSucceed diff --git a/jabgui/src/main/java/org/jabref/gui/util/BindingsHelper.java b/jabgui/src/main/java/org/jabref/gui/util/BindingsHelper.java index 48cef619852..487111614c3 100644 --- a/jabgui/src/main/java/org/jabref/gui/util/BindingsHelper.java +++ b/jabgui/src/main/java/org/jabref/gui/util/BindingsHelper.java @@ -4,6 +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.BooleanBinding; import javafx.beans.binding.ObjectBinding; @@ -19,6 +20,10 @@ import javafx.collections.ObservableMap; import javafx.css.PseudoClass; import javafx.scene.Node; +import javafx.scene.control.Tab; + +import org.jabref.gui.LibraryTab; +import org.jabref.model.database.BibDatabaseContext; import com.tobiasdiez.easybind.EasyBind; import com.tobiasdiez.easybind.PreboundBinding; @@ -189,6 +194,63 @@ public static Subscription subscribeFuture(ObservableValue observable, Co return () -> observable.removeListener(listener); } + public static void bindContentFiltered(ObservableList source, ObservableList target, Predicate filter) { + // FIXME: See https://github.com/JabRef/jabref-koppor/pull/713 - workaround in place until issue is resolved. + // Original code used FilteredList and EasyBind to filter and map tabs directly: + // FilteredList filteredTabs = new FilteredList<>(tabbedPane.getTabs()); + // filteredTabs.setPredicate(LibraryTab.class::isInstance); + // openDatabaseList = EasyBind.map(filteredTabs, tab -> ((LibraryTab) tab).getBibDatabaseContext()); + // EasyBind.bindContent(stateManager.getOpenDatabases(), openDatabaseList); + // Once JabRef#713 is fixed, remove this comment and the bindContentFiltered() method, and restore the original code + + Function tabToContext = tab -> ((LibraryTab) tab).getBibDatabaseContext(); + // Initial sync + target.setAll(source.stream() + .filter(filter) + .map(tabToContext) + .toList()); + + source.addListener((ListChangeListener) change -> { + while (change.next()) { + if (change.wasPermutated()) { + // We need a fresh copy as permutation is much harder to mirror + List reordered = source.stream() + .filter(filter) + .map(tabToContext) + .toList(); + target.setAll(reordered); + } + + if (change.wasRemoved()) { + for (Tab removed : change.getRemoved()) { + if (filter.test(removed)) { + target.remove(tabToContext.apply(removed)); + } + } + } + + if (change.wasAdded()) { + int sourceIndex = change.getFrom(); + int targetIndex = 0; + + // We need to add at the correct place - therefore, we need to find out the correct position + for (int i = 0; i < sourceIndex; i++) { + Tab tab = source.get(i); + if (filter.test(tab)) { + targetIndex++; + } + } + + for (Tab added : change.getAddedSubList()) { + if (filter.test(added)) { + target.add(targetIndex++, tabToContext.apply(added)); + } + } + } + } + }); + } + private static class BidirectionalBinding { private final ObservableValue propertyA;