From 4b8d66863cdbb9d54bea569ab36ed4b1b3498cd0 Mon Sep 17 00:00:00 2001 From: James Christian Ruiz <125330341+plvzfq-rit@users.noreply.github.com> Date: Thu, 3 Oct 2024 18:27:25 +0800 Subject: [PATCH 1/6] Add fetcher test info to developer documentation (#11873) * Add fetcher test info to developer documentation Information pertaining to (automatic) fetcher tests are added to fetchers.md. Information pertaining to how fetcher tests could be done locally are added to testing.md. * Fixed spacing in testing.md * Update docs/code-howtos/fetchers.md Removed rendundancy in line 90 (tests -> of them) Co-authored-by: Subhramit Basu Bhowmick * Update docs/code-howtos/fetchers.md More clarificatory refinements in line 89 (description of fetcher tests) Co-authored-by: Subhramit Basu Bhowmick * Update docs/code-howtos/testing.md Made line 145 more confident sounding + other clarificatory refinements Co-authored-by: Subhramit Basu Bhowmick * Update docs/code-howtos/testing.md Refinements in line 151 Co-authored-by: Subhramit Basu Bhowmick * Add section on Failing Fetcher tests in faq.md Create a small section in the FAQs to briefly discuss the process of dealing with failed fetcher tests. The discussion also links to other pertinent sections within the documentation * Update docs/code-howtos/faq.md --------- Co-authored-by: Subhramit Basu Bhowmick --- docs/code-howtos/faq.md | 5 +++++ docs/code-howtos/fetchers.md | 7 +++++++ docs/code-howtos/testing.md | 10 ++++++++++ 3 files changed, 22 insertions(+) diff --git a/docs/code-howtos/faq.md b/docs/code-howtos/faq.md index c75f168b093..e92bb62daa4 100644 --- a/docs/code-howtos/faq.md +++ b/docs/code-howtos/faq.md @@ -65,6 +65,11 @@ More information on the architecture can be found at [../getting-into-the-code/h This test is triggered when any kind of documentation is touched (be it the JabRef docs, or JavaDoc in code). If you changed something in the documentation, and particularly added/changed any links (to external files or websites), check if the links are correct and working. If you didn't change/add any link, or added correct links, the test is most probably failing due to any of the existing links being broken, and thus can be ignored (in the context of your contribution). +### Failing Fetcher tests + +Fetcher tests are run when any file in the `.../fetcher` directory has been touched. If you have changed any fetcher logic, check if the changes are correct. You can look for more details on how to locally run fetcher tests [here](https://devdocs.jabref.org/code-howtos/testing.html#fetchers-in-tests). +Otherwise, since these tests depend on remote services, their failure can also be caused by the network or an external server, and thus can be ignored in the context of your contribution. For more information, you can look [here](https://devdocs.jabref.org/code-howtos/fetchers.html#committing-and-pushing-changes-to-fetcher-files). + ## Gradle outputs ### `ANTLR Tool version 4.12.0 used for code generation does not match the current runtime version 4.13.1` diff --git a/docs/code-howtos/fetchers.md b/docs/code-howtos/fetchers.md index a0be55e912d..b9520cb56be 100644 --- a/docs/code-howtos/fetchers.md +++ b/docs/code-howtos/fetchers.md @@ -83,3 +83,10 @@ new BuildInfo().springerNatureAPIKey ``` When executing `./gradlew run`, gradle executes `processResources` and populates `build/build.properties` accordingly. However, when working directly in the IDE, Eclipse keeps reading `build.properties` from `src/main/resources`. In IntelliJ, the task `JabRef Main` is executing `./gradlew processResources` before running JabRef from the IDE to ensure the `build.properties` is properly populated. + +## Committing and pushing changes to fetcher files + +Fetcher tests are run when a PR contains changes touching any file in the `src/main/java/org/jabref/logic/importer/fetcher/` directory. +Since these tests rely on remote services, some of them may fail due to the network or the external server. + +To learn more about doing fetcher tests locally, see Fetchers in tests in [Testing](https://devdocs.jabref.org/code-howtos/testing.html). diff --git a/docs/code-howtos/testing.md b/docs/code-howtos/testing.md index 084a3350de7..d32bf51b62c 100644 --- a/docs/code-howtos/testing.md +++ b/docs/code-howtos/testing.md @@ -140,6 +140,16 @@ docker run -e MYSQL_ROOT_PASSWORD=root -e MYSQL_DATABASE=jabref -p 3800:3307 mys Set the environment variable `DBMS` to `mysql`. +## Fetchers in tests + +Fetcher tests can be run locally by executing the Gradle task `fetcherTest`. This can be done by running the following command in the command line: + +```shell +./gradlew fetcherTest +``` + +Alternatively, if one is using IntelliJ, this can also be done by double-clicking the `fetcherTest` task under the `other` group in the Gradle Tool window (`JabRef > Tasks > other > fetcherTest`). + ## Advanced testing and further reading On top of basic unit testing, there are more ways to test a software: From d3c7e51255ce34dd8b8362f7ced51d9fde8ea294 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Mochnack=C3=BD?= <48349316+JamseBonde007@users.noreply.github.com> Date: Thu, 3 Oct 2024 22:04:59 +0200 Subject: [PATCH 2/6] =?UTF-8?q?Add=20a=20"view=20as=20BibTeX"=20option=20b?= =?UTF-8?q?efore=20importing=20an=20entry=20from=20the=20cita=E2=80=A6=20(?= =?UTF-8?q?#11847)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add a "view as BibTeX" option before importing an entry from the citation relation tab #11826 * Correction of created errors #11826 * Fix problems #11826 * Fix: Issuance of an instance #11826 --------- Co-authored-by: Martin Mochnacký --- CHANGELOG.md | 1 + .../jabref/gui/entryeditor/EntryEditor.java | 2 +- .../CitationRelationsTab.java | 66 ++++++++++++++++++- 3 files changed, 66 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 52105585c0f..40acfb9ab75 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv ### Added +- We added a "view as BibTeX" option before importing an entry from the citation relation tab. [#11826](https://github.com/JabRef/jabref/issues/11826) - We added probable search hits instead of exact matches. Sorting by hit score can be done by the new score table column. [#11542](https://github.com/JabRef/jabref/pull/11542) - We added support finding LaTeX-encoded special characters based on plain Unicode and vice versa. [#11542](https://github.com/JabRef/jabref/pull/11542) - When a search hits a file, the file icon of that entry is changed accordingly. [#11542](https://github.com/JabRef/jabref/pull/11542) diff --git a/src/main/java/org/jabref/gui/entryeditor/EntryEditor.java b/src/main/java/org/jabref/gui/entryeditor/EntryEditor.java index 7e99897cadb..88c282366f2 100644 --- a/src/main/java/org/jabref/gui/entryeditor/EntryEditor.java +++ b/src/main/java/org/jabref/gui/entryeditor/EntryEditor.java @@ -299,7 +299,7 @@ private List createTabs() { tabs.add(new FileAnnotationTab(libraryTab.getAnnotationCache())); tabs.add(new SciteTab(preferences, taskExecutor, dialogService)); tabs.add(new CitationRelationsTab(dialogService, databaseContext, - undoManager, stateManager, fileMonitor, preferences, libraryTab, taskExecutor)); + undoManager, stateManager, fileMonitor, preferences, libraryTab, taskExecutor, bibEntryTypesManager)); tabs.add(new RelatedArticlesTab(buildInfo, databaseContext, preferences, dialogService, taskExecutor)); sourceTab = new SourceTab( databaseContext, diff --git a/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/CitationRelationsTab.java b/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/CitationRelationsTab.java index f6f8e9ca111..3ab2a167b9c 100644 --- a/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/CitationRelationsTab.java +++ b/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/CitationRelationsTab.java @@ -1,6 +1,7 @@ package org.jabref.gui.entryeditor.citationrelationtab; import java.io.IOException; +import java.io.StringWriter; import java.net.URI; import java.util.Arrays; import java.util.List; @@ -12,11 +13,15 @@ import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.css.PseudoClass; +import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.Node; import javafx.scene.control.Button; +import javafx.scene.control.ButtonType; +import javafx.scene.control.DialogPane; import javafx.scene.control.Label; import javafx.scene.control.ProgressIndicator; +import javafx.scene.control.ScrollPane; import javafx.scene.control.SplitPane; import javafx.scene.control.ToggleButton; import javafx.scene.control.Tooltip; @@ -28,6 +33,7 @@ import org.jabref.gui.DialogService; import org.jabref.gui.LibraryTab; import org.jabref.gui.StateManager; +import org.jabref.gui.collab.entrychange.PreviewWithSourceTab; import org.jabref.gui.desktop.os.NativeDesktop; import org.jabref.gui.entryeditor.EntryEditorTab; import org.jabref.gui.entryeditor.citationrelationtab.semanticscholar.CitationFetcher; @@ -36,11 +42,17 @@ import org.jabref.gui.preferences.GuiPreferences; import org.jabref.gui.util.NoSelectionModel; import org.jabref.gui.util.ViewModelListCellFactory; +import org.jabref.logic.bibtex.BibEntryWriter; +import org.jabref.logic.bibtex.FieldPreferences; +import org.jabref.logic.bibtex.FieldWriter; import org.jabref.logic.database.DuplicateCheck; +import org.jabref.logic.exporter.BibWriter; import org.jabref.logic.l10n.Localization; +import org.jabref.logic.os.OS; import org.jabref.logic.util.BackgroundTask; import org.jabref.logic.util.TaskExecutor; import org.jabref.model.database.BibDatabaseContext; +import org.jabref.model.database.BibDatabaseMode; import org.jabref.model.database.BibDatabaseModeDetection; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.BibEntryTypesManager; @@ -51,6 +63,8 @@ import com.tobiasdiez.easybind.EasyBind; import org.controlsfx.control.CheckListView; +import org.fxmisc.flowless.VirtualizedScrollPane; +import org.fxmisc.richtext.CodeArea; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -74,6 +88,7 @@ public class CitationRelationsTab extends EntryEditorTab { private final BibEntryRelationsRepository bibEntryRelationsRepository; private final CitationsRelationsTabViewModel citationsRelationsTabViewModel; private final DuplicateCheck duplicateCheck; + private final BibEntryTypesManager entryTypesManager; public CitationRelationsTab(DialogService dialogService, BibDatabaseContext databaseContext, @@ -82,7 +97,8 @@ public CitationRelationsTab(DialogService dialogService, FileUpdateMonitor fileUpdateMonitor, GuiPreferences preferences, LibraryTab libraryTab, - TaskExecutor taskExecutor) { + TaskExecutor taskExecutor, + BibEntryTypesManager bibEntryTypesManager) { this.dialogService = dialogService; this.databaseContext = databaseContext; this.preferences = preferences; @@ -91,7 +107,8 @@ public CitationRelationsTab(DialogService dialogService, setText(Localization.lang("Citation relations")); setTooltip(new Tooltip(Localization.lang("Show articles related by citation"))); - this.duplicateCheck = new DuplicateCheck(new BibEntryTypesManager()); + this.entryTypesManager = bibEntryTypesManager; + this.duplicateCheck = new DuplicateCheck(entryTypesManager); this.bibEntryRelationsRepository = new BibEntryRelationsRepository(new SemanticScholarFetcher(preferences.getImporterPreferences()), new BibEntryRelationsCache()); citationsRelationsTabViewModel = new CitationsRelationsTabViewModel(databaseContext, preferences, undoManager, stateManager, dialogService, fileUpdateMonitor, taskExecutor); @@ -254,6 +271,14 @@ private void styleFetchedListView(CheckListView listView) vContainer.getChildren().addLast(openWeb); } + Button showEntrySource = IconTheme.JabRefIcons.SOURCE.asButton(); + showEntrySource.setTooltip(new Tooltip(Localization.lang("%0 source", "BibTeX"))); + showEntrySource.setOnMouseClicked(event -> { + showEntrySourceDialog(entry.entry()); + }); + + vContainer.getChildren().addLast(showEntrySource); + hContainer.getChildren().addAll(entryNode, separator, vContainer); hContainer.getStyleClass().add("entry-container"); @@ -270,6 +295,43 @@ private void styleFetchedListView(CheckListView listView) listView.setSelectionModel(new NoSelectionModel<>()); } + /** + * @implNote This code is similar to {@link PreviewWithSourceTab#getSourceString(BibEntry, BibDatabaseMode, FieldPreferences, BibEntryTypesManager)}. + */ + private String getSourceString(BibEntry entry, BibDatabaseMode type, FieldPreferences fieldPreferences, BibEntryTypesManager entryTypesManager) throws IOException { + StringWriter writer = new StringWriter(); + BibWriter bibWriter = new BibWriter(writer, OS.NEWLINE); + FieldWriter fieldWriter = FieldWriter.buildIgnoreHashes(fieldPreferences); + new BibEntryWriter(fieldWriter, entryTypesManager).write(entry, bibWriter, type); + return writer.toString(); + } + + private void showEntrySourceDialog(BibEntry entry) { + CodeArea ca = new CodeArea(); + try { + ca.appendText(getSourceString(entry, databaseContext.getMode(), preferences.getFieldPreferences(), this.entryTypesManager)); + } catch (IOException e) { + LOGGER.warn("Incorrect entry, could not load source:", e); + return; + } + + ca.setWrapText(true); + ca.setPadding(new Insets(0, 10, 0, 10)); + ca.showParagraphAtTop(0); + + ScrollPane scrollPane = new ScrollPane(); + scrollPane.setFitToWidth(true); + scrollPane.setFitToHeight(true); + scrollPane.setContent(new VirtualizedScrollPane<>(ca)); + + DialogPane dialogPane = new DialogPane(); + dialogPane.setPrefSize(800, 400); + dialogPane.setContent(scrollPane); + String title = Localization.lang("Show BibTeX source"); + + dialogService.showCustomDialogAndWait(title, dialogPane, ButtonType.OK); + } + /** * Method to style heading labels * From 03f7a494161b401ee222c419468bfe9306f88ff2 Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Fri, 4 Oct 2024 18:14:57 +0200 Subject: [PATCH 3/6] Fix sort of requires (#11880) --- src/main/java/module-info.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index cd22dd41ad4..852af1ba396 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -149,6 +149,7 @@ requires jvm.openai; requires langchain4j; requires langchain4j.core; + requires langchain4j.google.ai.gemini; requires langchain4j.hugging.face; requires langchain4j.mistral.ai; requires langchain4j.open.ai; @@ -188,6 +189,5 @@ requires mslinks; requires org.antlr.antlr4.runtime; requires org.libreoffice.uno; - requires langchain4j.google.ai.gemini; // endregion } From ecd78de9ca02bcfdbe5817e702a94b7d7b782a75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Melis=20=C3=96lmez?= <77929541+melisolmez@users.noreply.github.com> Date: Fri, 4 Oct 2024 19:19:18 +0300 Subject: [PATCH 4/6] Remove listeners in UndoAction and RedoAction for memory efficiency (#11839) * fix remove listeners in RedoAction for memory efficiency, used WeakChangeListeners * fix checkstyle error * fix checkstyle error * revert javadoc and fix catch indent * Remove listeners in UndoAction for memory efficiency * Use the fact that there is only one UndoManager instance shared between libraries * Update CHANGELOG.md --------- Co-authored-by: HoussemNasri Co-authored-by: Houssem Nasri --- CHANGELOG.md | 2 ++ src/main/java/org/jabref/gui/LibraryTab.java | 4 ++-- .../java/org/jabref/gui/frame/MainMenu.java | 11 +++++----- .../org/jabref/gui/frame/MainToolBar.java | 4 ++-- .../java/org/jabref/gui/undo/RedoAction.java | 22 ++++++++++++------- .../java/org/jabref/gui/undo/UndoAction.java | 21 ++++++++++++------ 6 files changed, 39 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 40acfb9ab75..3df79eca574 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -82,6 +82,8 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv - We fixed an issue where unescaped braces in the arXiv fetcher were not treated. [#11704](https://github.com/JabRef/jabref/issues/11704) - We fixed an issue where HTML instead of the fulltext pdf was downloaded when importing arXiv entries. [#4913](https://github.com/JabRef/jabref/issues/4913) - We fixed an issue where the keywords and crossref fields were not properly focused. [#11177](https://github.com/JabRef/jabref/issues/11177) +- We fixed an issue where listeners were being attached without being released later leading to a memory leak. [#11809](https://github.com/JabRef/jabref/issues/11809) +- We fixed an issue where the Undo/Redo buttons were active even when all libraries are closed. [#11837](https://github.com/JabRef/jabref/issues/11837) - We fixed an issue where recently opened files were not displayed in the main menu properly. [#9042](https://github.com/JabRef/jabref/issues/9042) - We fixed an issue where the DOI lookup would show an error when a DOI was found for an entry. [#11850](https://github.com/JabRef/jabref/issues/11850) diff --git a/src/main/java/org/jabref/gui/LibraryTab.java b/src/main/java/org/jabref/gui/LibraryTab.java index 93901045848..791eec0dee1 100644 --- a/src/main/java/org/jabref/gui/LibraryTab.java +++ b/src/main/java/org/jabref/gui/LibraryTab.java @@ -260,8 +260,8 @@ private EntryEditor createEntryEditor() { Supplier tabSupplier = () -> this; return new EntryEditor(this, // Actions are recreated here since this avoids passing more parameters and the amount of additional memory consumption is neglegtable. - new UndoAction(tabSupplier, dialogService, stateManager), - new RedoAction(tabSupplier, dialogService, stateManager)); + new UndoAction(tabSupplier, undoManager, dialogService, stateManager), + new RedoAction(tabSupplier, undoManager, dialogService, stateManager)); } private static void addChangedInformation(StringBuilder text, String fileName) { diff --git a/src/main/java/org/jabref/gui/frame/MainMenu.java b/src/main/java/org/jabref/gui/frame/MainMenu.java index 7500cf72e03..96ab72d70d4 100644 --- a/src/main/java/org/jabref/gui/frame/MainMenu.java +++ b/src/main/java/org/jabref/gui/frame/MainMenu.java @@ -2,8 +2,6 @@ import java.util.function.Supplier; -import javax.swing.undo.UndoManager; - import javafx.event.ActionEvent; import javafx.scene.control.Menu; import javafx.scene.control.MenuBar; @@ -70,6 +68,7 @@ import org.jabref.gui.slr.StartNewStudyAction; import org.jabref.gui.specialfields.SpecialFieldMenuItemFactory; import org.jabref.gui.texparser.ParseLatexAction; +import org.jabref.gui.undo.CountingUndoManager; import org.jabref.gui.undo.RedoAction; import org.jabref.gui.undo.UndoAction; import org.jabref.gui.util.UiTaskExecutor; @@ -98,7 +97,7 @@ public class MainMenu extends MenuBar { private final DialogService dialogService; private final JournalAbbreviationRepository abbreviationRepository; private final BibEntryTypesManager entryTypesManager; - private final UndoManager undoManager; + private final CountingUndoManager undoManager; private final ClipBoardManager clipBoardManager; private final Supplier openDatabaseActionSupplier; private final AiService aiService; @@ -114,7 +113,7 @@ public MainMenu(JabRefFrame frame, DialogService dialogService, JournalAbbreviationRepository abbreviationRepository, BibEntryTypesManager entryTypesManager, - UndoManager undoManager, + CountingUndoManager undoManager, ClipBoardManager clipBoardManager, Supplier openDatabaseActionSupplier, AiService aiService) { @@ -184,8 +183,8 @@ private void createMenu() { ); edit.getItems().addAll( - factory.createMenuItem(StandardActions.UNDO, new UndoAction(frame::getCurrentLibraryTab, dialogService, stateManager)), - factory.createMenuItem(StandardActions.REDO, new RedoAction(frame::getCurrentLibraryTab, dialogService, stateManager)), + factory.createMenuItem(StandardActions.UNDO, new UndoAction(frame::getCurrentLibraryTab, undoManager, dialogService, stateManager)), + factory.createMenuItem(StandardActions.REDO, new RedoAction(frame::getCurrentLibraryTab, undoManager, dialogService, stateManager)), new SeparatorMenuItem(), diff --git a/src/main/java/org/jabref/gui/frame/MainToolBar.java b/src/main/java/org/jabref/gui/frame/MainToolBar.java index 03bbc72e3b0..e07467e2c3d 100644 --- a/src/main/java/org/jabref/gui/frame/MainToolBar.java +++ b/src/main/java/org/jabref/gui/frame/MainToolBar.java @@ -133,8 +133,8 @@ private void createToolBar() { new Separator(Orientation.VERTICAL), new HBox( - factory.createIconButton(StandardActions.UNDO, new UndoAction(frame::getCurrentLibraryTab, dialogService, stateManager)), - factory.createIconButton(StandardActions.REDO, new RedoAction(frame::getCurrentLibraryTab, dialogService, stateManager)), + factory.createIconButton(StandardActions.UNDO, new UndoAction(frame::getCurrentLibraryTab, undoManager, dialogService, stateManager)), + factory.createIconButton(StandardActions.REDO, new RedoAction(frame::getCurrentLibraryTab, undoManager, dialogService, stateManager)), factory.createIconButton(StandardActions.CUT, new EditAction(StandardActions.CUT, frame::getCurrentLibraryTab, stateManager, undoManager)), factory.createIconButton(StandardActions.COPY, new EditAction(StandardActions.COPY, frame::getCurrentLibraryTab, stateManager, undoManager)), factory.createIconButton(StandardActions.PASTE, new EditAction(StandardActions.PASTE, frame::getCurrentLibraryTab, stateManager, undoManager))), diff --git a/src/main/java/org/jabref/gui/undo/RedoAction.java b/src/main/java/org/jabref/gui/undo/RedoAction.java index 4ec944731e7..d5d3170a993 100644 --- a/src/main/java/org/jabref/gui/undo/RedoAction.java +++ b/src/main/java/org/jabref/gui/undo/RedoAction.java @@ -4,36 +4,42 @@ import javax.swing.undo.CannotRedoException; +import javafx.beans.binding.Bindings; + import org.jabref.gui.DialogService; import org.jabref.gui.LibraryTab; import org.jabref.gui.StateManager; import org.jabref.gui.actions.SimpleCommand; import org.jabref.logic.l10n.Localization; +import static org.jabref.gui.actions.ActionHelper.needsDatabase; + /** * @implNote See also {@link UndoAction} */ public class RedoAction extends SimpleCommand { private final Supplier tabSupplier; private final DialogService dialogService; + private final CountingUndoManager undoManager; - public RedoAction(Supplier tabSupplier, DialogService dialogService, StateManager stateManager) { + public RedoAction(Supplier tabSupplier, CountingUndoManager undoManager, DialogService dialogService, StateManager stateManager) { this.tabSupplier = tabSupplier; this.dialogService = dialogService; + this.undoManager = undoManager; - // TODO: The old listener should be removed. Otherwise, memory consumption will increase. - stateManager.activeTabProperty().addListener((observable, oldValue, activeLibraryTab) -> { - activeLibraryTab.ifPresent(libraryTab -> - this.executable.bind(libraryTab.getUndoManager().getRedoableProperty())); - }); + this.executable.bind(Bindings.and(needsDatabase(stateManager), undoManager.getRedoableProperty())); } @Override public void execute() { LibraryTab libraryTab = this.tabSupplier.get(); try { - libraryTab.getUndoManager().redo(); - dialogService.notify(Localization.lang("Redo")); + if (undoManager.canRedo()) { + undoManager.redo(); + dialogService.notify(Localization.lang("Redo")); + } else { + throw new CannotRedoException(); + } } catch (CannotRedoException ex) { dialogService.notify(Localization.lang("Nothing to redo") + '.'); } diff --git a/src/main/java/org/jabref/gui/undo/UndoAction.java b/src/main/java/org/jabref/gui/undo/UndoAction.java index a61551bcf6b..5eab905a9f8 100644 --- a/src/main/java/org/jabref/gui/undo/UndoAction.java +++ b/src/main/java/org/jabref/gui/undo/UndoAction.java @@ -4,35 +4,42 @@ import javax.swing.undo.CannotUndoException; +import javafx.beans.binding.Bindings; + import org.jabref.gui.DialogService; import org.jabref.gui.LibraryTab; import org.jabref.gui.StateManager; import org.jabref.gui.actions.SimpleCommand; import org.jabref.logic.l10n.Localization; +import static org.jabref.gui.actions.ActionHelper.needsDatabase; + /** * @implNote See also {@link RedoAction} */ public class UndoAction extends SimpleCommand { private final Supplier tabSupplier; private final DialogService dialogService; + private final CountingUndoManager undoManager; - public UndoAction(Supplier tabSupplier, DialogService dialogService, StateManager stateManager) { + public UndoAction(Supplier tabSupplier, CountingUndoManager undoManager, DialogService dialogService, StateManager stateManager) { this.tabSupplier = tabSupplier; this.dialogService = dialogService; + this.undoManager = undoManager; - stateManager.activeTabProperty().addListener((observable, oldValue, activeLibraryTab) -> { - activeLibraryTab.ifPresent(libraryTab -> - this.executable.bind(libraryTab.getUndoManager().getUndoableProperty())); - }); + this.executable.bind(Bindings.and(needsDatabase(stateManager), undoManager.getUndoableProperty())); } @Override public void execute() { LibraryTab libraryTab = this.tabSupplier.get(); try { - libraryTab.getUndoManager().undo(); - dialogService.notify(Localization.lang("Undo")); + if (undoManager.canUndo()) { + undoManager.undo(); + dialogService.notify(Localization.lang("Undo")); + } else { + throw new CannotUndoException(); + } } catch (CannotUndoException ex) { dialogService.notify(Localization.lang("Nothing to undo") + '.'); } From 3502ba617a17c3ffe7065acb20e1eac509594655 Mon Sep 17 00:00:00 2001 From: Christoph Date: Fri, 4 Oct 2024 22:52:33 +0200 Subject: [PATCH 5/6] migrate maintable.css to base css (#11881) * migrate maintable.css to base css add style class for main table * remove debug * changelog * remove old css * Fix indent --------- Co-authored-by: Oliver Kopp --- CHANGELOG.md | 1 + src/main/java/org/jabref/gui/Base.css | 218 +++++++++++++++++- .../org/jabref/gui/maintable/MainTable.css | 210 ----------------- .../org/jabref/gui/maintable/MainTable.java | 4 +- .../jabref/gui/search/SearchResultsTable.java | 5 +- 5 files changed, 222 insertions(+), 216 deletions(-) delete mode 100644 src/main/java/org/jabref/gui/maintable/MainTable.css diff --git a/CHANGELOG.md b/CHANGELOG.md index 3df79eca574..f1bfa3434b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -55,6 +55,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv - We improved the performance when pasting and importing entries in an existing library. [#11843](https://github.com/JabRef/jabref/pull/11843) - When fulltext search is selected but indexing is deactivated, a dialog is now shown asking if the user wants to enable indexing now [#9491](https://github.com/JabRef/jabref/issues/9491) - We changed instances of 'Search Selected' to 'Search Pre-configured' in Web Search Preferences UI. [#11871](https://github.com/JabRef/jabref/pull/11871) +- We added a new CSS style class `main-table` for the main table. [#11881](https://github.com/JabRef/jabref/pull/11881) ### Fixed diff --git a/src/main/java/org/jabref/gui/Base.css b/src/main/java/org/jabref/gui/Base.css index 14ae1873d4a..49d581717c7 100644 --- a/src/main/java/org/jabref/gui/Base.css +++ b/src/main/java/org/jabref/gui/Base.css @@ -340,7 +340,6 @@ TextFlow > .hyperlink:visited, -fx-underline: true; } - .glyph-icon { /* This adjusts text alignment within the bounds of text nodes so that the text is always vertically centered within the bounds. Based on @@ -1517,4 +1516,221 @@ We want to have a look that matches our icons in the tool-bar */ -fx-background-color: transparent; } +/* endregion */ + +/* region: maintable css */ + + +.main-table .column-icon { + -fx-alignment: baseline-center; + -fx-padding: 0; +} + +.main-table .column-header.column-icon > .label { + -fx-padding: 0; + -fx-alignment: baseline-center; +} + +.main-table .empty-special-field { + visibility: hidden; +} + +.main-table .table-row-cell:hover .empty-special-field { + visibility: visible; + -fx-icon-color: -jr-gray-2; + -fx-fill: -jr-gray-2; +} + +.main-table .table-row-cell:dragOver-bottom { + -fx-border-color: -jr-drag-target; + -fx-border-width: 0 0 2 0; + -fx-padding: 0 0 -2 0; +} + +.main-table .table-row-cell:dragOver-center { + -fx-border-color: -jr-drag-target; + -fx-border-width: 1 1 1 1; + -fx-padding: -1 -1 -1 -1; + -fx-background-color: -jr-drag-target-hover; +} + +.main-table .table-row-cell:dragOver-top { + -fx-border-color: -jr-drag-target; + -fx-border-width: 2 0 0 0; + -fx-padding: -2 0 0 0; +} + +/** even and odd are swapped around somehow. Below "odd" matches lines 2, 4, ... **/ + +.main-table .table-row-cell:matching-search-and-groups { + -fx-background-color: -jr-match-1-even; +} +.main-table .table-row-cell:matching-search-and-groups > .table-cell { + -fx-text-fill: -jr-match-1-text-color; +} +.main-table .table-row-cell:matching-search-and-groups:focused > .table-cell { + -fx-text-fill: -fx-focused-text-base-color; +} +.main-table .table-row-cell:matching-search-and-groups:focused:hover > .table-cell { + -fx-text-fill: -jr-maintable-focused-hover-text; +} +.main-table .table-row-cell:matching-search-and-groups:focused:hover > .table-cell > .ikonli-font-icon { + -fx-icon-color: -jr-maintable-focused-hover-text; +} +.main-table .table-row-cell:matching-search-and-groups:hover > .table-cell { + -fx-text-fill: -jr-hover-text; +} +.main-table .table-row-cell:matching-search-and-groups > .table-cell > .ikonli-font-icon { + -fx-icon-color: -jr-match-1-text-color; +} +.main-table .table-row-cell:matching-search-and-groups:hover > .table-cell > .ikonli-font-icon { + -fx-icon-color: -jr-hover-text; +} +.main-table .table-row-cell:matching-search-and-groups:odd { + -fx-background-color: -jr-match-1-odd; +} +.main-table .table-row-cell:matching-search-and-groups:odd:selected, +.main-table .table-row-cell:matching-search-and-groups:odd:focused, +.main-table .table-row-cell:matching-search-and-groups:odd:focused:hover, +.main-table .table-row-cell:matching-search-and-groups:focused:hover { + -fx-background-color: -jr-selected; +} +.main-table .table-row-cell:matching-search-and-groups:odd:hover { + -fx-background-color: -jr-hover; +} + +.main-table .table-row-cell:matching-search-not-groups { + -fx-background-color: -jr-match-2-even; +} +.main-table .table-row-cell:matching-search-not-groups > .table-cell { + -fx-text-fill: -jr-match-2-text-color; +} +.main-table .table-row-cell:matching-search-not-groups:focused > .table-cell { + -fx-text-fill: -fx-focused-text-base-color; +} +.main-table .table-row-cell:matching-search-not-groups:focused:hover > .table-cell { + -fx-text-fill: -jr-maintable-focused-hover-text; +} +.main-table .table-row-cell:matching-search-not-groups:focused:hover > .table-cell > .ikonli-font-icon { + -fx-icon-color: -jr-maintable-focused-hover-text; +} +.main-table .table-row-cell:matching-search-not-groups:hover > .table-cell { + -fx-text-fill: -jr-hover-text; +} +.main-table .table-row-cell:matching-search-not-groups > .table-cell > .ikonli-font-icon { + -fx-icon-color: -jr-match-2-text-color; +} +.main-table .table-row-cell:matching-search-not-groups:hover > .table-cell > .ikonli-font-icon { + -fx-icon-color: -jr-hover-text; +} +.main-table .table-row-cell:matching-search-not-groups:odd { + -fx-background-color: -jr-match-2-odd; +} +.main-table .table-row-cell:matching-search-not-groups:odd:selected, +.main-table .table-row-cell:matching-search-not-groups:odd:focused, +.main-table .table-row-cell:matching-search-not-groups:odd:focused:hover, +.main-table .table-row-cell:matching-search-not-groups:focused:hover { + -fx-background-color: -jr-selected; +} +.main-table .table-row-cell:matching-search-not-groups:odd:hover { + -fx-background-color: -jr-hover; +} + +.main-table .table-row-cell:matching-groups-not-search { + -fx-background-color: -jr-match-3-even; +} +.main-table .table-row-cell:matching-groups-not-search > .table-cell { + -fx-text-fill: -jr-match-3-text-color; +} +.main-table .table-row-cell:matching-groups-not-search:focused > .table-cell { + -fx-text-fill: -fx-focused-text-base-color; +} +.main-table .table-row-cell:matching-groups-not-search:focused:hover > .table-cell { + -fx-text-fill: -jr-maintable-focused-hover-text; +} +.main-table .table-row-cell:matching-groups-not-search:focused:hover > .table-cell > .ikonli-font-icon { + -fx-icon-color: -jr-maintable-focused-hover-text; +} +.main-table .table-row-cell:matching-groups-not-search:hover > .table-cell { + -fx-text-fill: -jr-hover-text; +} +.main-table .table-row-cell:matching-groups-not-search > .table-cell > .ikonli-font-icon { + -fx-icon-color: -jr-match-3-text-color; +} +.main-table .table-row-cell:matching-groups-not-search:hover > .table-cell > .ikonli-font-icon { + -fx-icon-color: -jr-hover-text; +} +.main-table .table-row-cell:matching-groups-not-search:odd { + -fx-background-color: -jr-match-3-odd; +} +.main-table .table-row-cell:matching-groups-not-search:odd:selected, +.main-table .table-row-cell:matching-groups-not-search:odd:focused, +.main-table .table-row-cell:matching-groups-not-search:odd:focused:hover, +.main-table .table-row-cell:matching-groups-not-search:focused:hover { + -fx-background-color: -jr-selected; +} +.main-table .table-row-cell:matching-groups-not-search:odd:hover { + -fx-background-color: -jr-hover; +} + +.main-table .table-row-cell:not-matching-search-and-groups { + -fx-background-color: -jr-match-4-even; +} +.main-table .table-row-cell:not-matching-search-and-groups > .table-cell { + -fx-text-fill: -jr-match-4-text-color; +} +.main-table .table-row-cell:not-matching-search-and-groups:focused > .table-cell { + -fx-text-fill: -fx-focused-text-base-color; +} +.main-table .table-row-cell:not-matching-search-and-groups:focused:hover > .table-cell { + -fx-text-fill: -jr-maintable-focused-hover-text; +} +.main-table .table-row-cell:not-matching-search-and-groups:focused:hover > .table-cell > .ikonli-font-icon { + -fx-icon-color: -jr-maintable-focused-hover-text; +} +.main-table .table-row-cell:not-matching-search-and-groups:hover > .table-cell { + -fx-text-fill: -jr-hover-text; +} +.main-table .table-row-cell:not-matching-search-and-groups > .table-cell > .ikonli-font-icon { + -fx-icon-color: -jr-match-4-text-color; +} +.main-table.table-row-cell:not-matching-search-and-groups:hover > .table-cell > .ikonli-font-icon { + -fx-icon-color: -jr-hover-text; +} +.main-table .table-row-cell:not-matching-search-and-groups:odd { + -fx-background-color: -jr-match-4-odd; +} +.main-table .table-row-cell:not-matching-search-and-groups:odd:selected, +.main-table .table-row-cell:not-matching-search-and-groups:odd:focused, +.main-table .table-row-cell:not-matching-search-and-groups:odd:focused:hover, +.main-table .table-row-cell:not-matching-search-and-groups:focused:hover { + -fx-background-color: -jr-selected; +} +.main-table .table-row-cell:not-matching-search-and-groups:odd:hover { + -fx-background-color: -jr-hover; +} + +.rating > .container { + -fx-spacing: 2; +} + +.rating > .container > .button { + -fx-pref-width: 16; + -fx-pref-height: 10; + -fx-background-repeat: no-repeat no-repeat; + -fx-background-size: 16 16; + -fx-border-style: none; + -fx-border-width: 0; + -fx-padding: 0; +} + +.rating > .container > .button.strong { + +} + +.rating > .container > .button:hover { + -fx-effect: dropshadow(three-pass-box, rgba(0, 0, 0, 0.6), 8, 0.0, 0, 0); +} + + /* endregion */ diff --git a/src/main/java/org/jabref/gui/maintable/MainTable.css b/src/main/java/org/jabref/gui/maintable/MainTable.css deleted file mode 100644 index f468033b854..00000000000 --- a/src/main/java/org/jabref/gui/maintable/MainTable.css +++ /dev/null @@ -1,210 +0,0 @@ -.column-icon { - -fx-alignment: baseline-center; - -fx-padding: 0; -} - -.column-header.column-icon > .label { - -fx-padding: 0; - -fx-alignment: baseline-center; -} - -.empty-special-field { - visibility: hidden; -} - -.table-row-cell:hover .empty-special-field { - visibility: visible; - -fx-icon-color: -jr-gray-2; - -fx-fill: -jr-gray-2; -} - -.table-row-cell:dragOver-bottom { - -fx-border-color: -jr-drag-target; - -fx-border-width: 0 0 2 0; - -fx-padding: 0 0 -2 0; -} - -.table-row-cell:dragOver-center { - -fx-border-color: -jr-drag-target; - -fx-border-width: 1 1 1 1; - -fx-padding: -1 -1 -1 -1; - -fx-background-color: -jr-drag-target-hover; -} - -.table-row-cell:dragOver-top { - -fx-border-color: -jr-drag-target; - -fx-border-width: 2 0 0 0; - -fx-padding: -2 0 0 0; -} - -/** even and odd are swapped around somehow. Below "odd" matches lines 2, 4, ... **/ - -.table-row-cell:matching-search-and-groups { - -fx-background-color: -jr-match-1-even; -} -.table-row-cell:matching-search-and-groups > .table-cell { - -fx-text-fill: -jr-match-1-text-color; -} -.table-row-cell:matching-search-and-groups:focused > .table-cell { - -fx-text-fill: -fx-focused-text-base-color; -} -.table-row-cell:matching-search-and-groups:focused:hover > .table-cell { - -fx-text-fill: -jr-maintable-focused-hover-text; -} -.table-row-cell:matching-search-and-groups:focused:hover > .table-cell > .ikonli-font-icon { - -fx-icon-color: -jr-maintable-focused-hover-text; -} -.table-row-cell:matching-search-and-groups:hover > .table-cell { - -fx-text-fill: -jr-hover-text; -} -.table-row-cell:matching-search-and-groups > .table-cell > .ikonli-font-icon { - -fx-icon-color: -jr-match-1-text-color; -} -.table-row-cell:matching-search-and-groups:hover > .table-cell > .ikonli-font-icon { - -fx-icon-color: -jr-hover-text; -} -.table-row-cell:matching-search-and-groups:odd { - -fx-background-color: -jr-match-1-odd; -} -.table-row-cell:matching-search-and-groups:odd:selected, -.table-row-cell:matching-search-and-groups:odd:focused, -.table-row-cell:matching-search-and-groups:odd:focused:hover, -.table-row-cell:matching-search-and-groups:focused:hover { - -fx-background-color: -jr-selected; -} -.table-row-cell:matching-search-and-groups:odd:hover { - -fx-background-color: -jr-hover; -} - -.table-row-cell:matching-search-not-groups { - -fx-background-color: -jr-match-2-even; -} -.table-row-cell:matching-search-not-groups > .table-cell { - -fx-text-fill: -jr-match-2-text-color; -} -.table-row-cell:matching-search-not-groups:focused > .table-cell { - -fx-text-fill: -fx-focused-text-base-color; -} -.table-row-cell:matching-search-not-groups:focused:hover > .table-cell { - -fx-text-fill: -jr-maintable-focused-hover-text; -} -.table-row-cell:matching-search-not-groups:focused:hover > .table-cell > .ikonli-font-icon { - -fx-icon-color: -jr-maintable-focused-hover-text; -} -.table-row-cell:matching-search-not-groups:hover > .table-cell { - -fx-text-fill: -jr-hover-text; -} -.table-row-cell:matching-search-not-groups > .table-cell > .ikonli-font-icon { - -fx-icon-color: -jr-match-2-text-color; -} -.table-row-cell:matching-search-not-groups:hover > .table-cell > .ikonli-font-icon { - -fx-icon-color: -jr-hover-text; -} -.table-row-cell:matching-search-not-groups:odd { - -fx-background-color: -jr-match-2-odd; -} -.table-row-cell:matching-search-not-groups:odd:selected, -.table-row-cell:matching-search-not-groups:odd:focused, -.table-row-cell:matching-search-not-groups:odd:focused:hover, -.table-row-cell:matching-search-not-groups:focused:hover { - -fx-background-color: -jr-selected; -} -.table-row-cell:matching-search-not-groups:odd:hover { - -fx-background-color: -jr-hover; -} - -.table-row-cell:matching-groups-not-search { - -fx-background-color: -jr-match-3-even; -} -.table-row-cell:matching-groups-not-search > .table-cell { - -fx-text-fill: -jr-match-3-text-color; -} -.table-row-cell:matching-groups-not-search:focused > .table-cell { - -fx-text-fill: -fx-focused-text-base-color; -} -.table-row-cell:matching-groups-not-search:focused:hover > .table-cell { - -fx-text-fill: -jr-maintable-focused-hover-text; -} -.table-row-cell:matching-groups-not-search:focused:hover > .table-cell > .ikonli-font-icon { - -fx-icon-color: -jr-maintable-focused-hover-text; -} -.table-row-cell:matching-groups-not-search:hover > .table-cell { - -fx-text-fill: -jr-hover-text; -} -.table-row-cell:matching-groups-not-search > .table-cell > .ikonli-font-icon { - -fx-icon-color: -jr-match-3-text-color; -} -.table-row-cell:matching-groups-not-search:hover > .table-cell > .ikonli-font-icon { - -fx-icon-color: -jr-hover-text; -} -.table-row-cell:matching-groups-not-search:odd { - -fx-background-color: -jr-match-3-odd; -} -.table-row-cell:matching-groups-not-search:odd:selected, -.table-row-cell:matching-groups-not-search:odd:focused, -.table-row-cell:matching-groups-not-search:odd:focused:hover, -.table-row-cell:matching-groups-not-search:focused:hover { - -fx-background-color: -jr-selected; -} -.table-row-cell:matching-groups-not-search:odd:hover { - -fx-background-color: -jr-hover; -} - -.table-row-cell:not-matching-search-and-groups { - -fx-background-color: -jr-match-4-even; -} -.table-row-cell:not-matching-search-and-groups > .table-cell { - -fx-text-fill: -jr-match-4-text-color; -} -.table-row-cell:not-matching-search-and-groups:focused > .table-cell { - -fx-text-fill: -fx-focused-text-base-color; -} -.table-row-cell:not-matching-search-and-groups:focused:hover > .table-cell { - -fx-text-fill: -jr-maintable-focused-hover-text; -} -.table-row-cell:not-matching-search-and-groups:focused:hover > .table-cell > .ikonli-font-icon { - -fx-icon-color: -jr-maintable-focused-hover-text; -} -.table-row-cell:not-matching-search-and-groups:hover > .table-cell { - -fx-text-fill: -jr-hover-text; -} -.table-row-cell:not-matching-search-and-groups > .table-cell > .ikonli-font-icon { - -fx-icon-color: -jr-match-4-text-color; -} -.table-row-cell:not-matching-search-and-groups:hover > .table-cell > .ikonli-font-icon { - -fx-icon-color: -jr-hover-text; -} -.table-row-cell:not-matching-search-and-groups:odd { - -fx-background-color: -jr-match-4-odd; -} -.table-row-cell:not-matching-search-and-groups:odd:selected, -.table-row-cell:not-matching-search-and-groups:odd:focused, -.table-row-cell:not-matching-search-and-groups:odd:focused:hover, -.table-row-cell:not-matching-search-and-groups:focused:hover { - -fx-background-color: -jr-selected; -} -.table-row-cell:not-matching-search-and-groups:odd:hover { - -fx-background-color: -jr-hover; -} - -.rating > .container { - -fx-spacing: 2; -} - -.rating > .container > .button { - -fx-pref-width: 16; - -fx-pref-height: 10; - -fx-background-repeat: no-repeat no-repeat; - -fx-background-size: 16 16; - -fx-border-style: none; - -fx-border-width: 0; - -fx-padding: 0; -} - -.rating > .container > .button.strong { - -} - -.rating > .container > .button:hover { - -fx-effect: dropshadow(three-pass-box, rgba(0, 0, 0, 0.6), 8, 0.0, 0, 0); -} diff --git a/src/main/java/org/jabref/gui/maintable/MainTable.java b/src/main/java/org/jabref/gui/maintable/MainTable.java index e8939f9aec8..4a94fd56b5f 100644 --- a/src/main/java/org/jabref/gui/maintable/MainTable.java +++ b/src/main/java/org/jabref/gui/maintable/MainTable.java @@ -114,6 +114,8 @@ public MainTable(MainTableDataModel model, this.setOnDragOver(this::handleOnDragOverTableView); this.setOnDragDropped(this::handleOnDragDroppedTableView); + this.getStyleClass().add("main-table"); + MainTableColumnFactory mainTableColumnFactory = new MainTableColumnFactory( database, preferences, @@ -185,8 +187,6 @@ public MainTable(MainTableDataModel model, // Enable sorting model.getEntriesFilteredAndSorted().comparatorProperty().bind(this.comparatorProperty()); - this.getStylesheets().add(Objects.requireNonNull(MainTable.class.getResource("MainTable.css")).toExternalForm()); - // Store visual state new PersistenceVisualStateTable(this, mainTablePreferences.getColumnPreferences()).addListeners(); diff --git a/src/main/java/org/jabref/gui/search/SearchResultsTable.java b/src/main/java/org/jabref/gui/search/SearchResultsTable.java index 5bb4049ecae..76ebb2cff26 100644 --- a/src/main/java/org/jabref/gui/search/SearchResultsTable.java +++ b/src/main/java/org/jabref/gui/search/SearchResultsTable.java @@ -12,7 +12,6 @@ import org.jabref.gui.DialogService; import org.jabref.gui.StateManager; import org.jabref.gui.maintable.BibEntryTableViewModel; -import org.jabref.gui.maintable.MainTable; import org.jabref.gui.maintable.MainTableColumnFactory; import org.jabref.gui.maintable.MainTablePreferences; import org.jabref.gui.maintable.PersistenceVisualStateTable; @@ -35,6 +34,8 @@ public SearchResultsTable(SearchResultsTableDataModel model, TaskExecutor taskExecutor) { super(); + this.getStyleClass().add("main-table"); + MainTablePreferences mainTablePreferences = preferences.getMainTablePreferences(); List> allCols = new MainTableColumnFactory( @@ -68,8 +69,6 @@ public SearchResultsTable(SearchResultsTableDataModel model, // Enable sorting model.getEntriesFilteredAndSorted().comparatorProperty().bind(this.comparatorProperty()); - this.getStylesheets().add(MainTable.class.getResource("MainTable.css").toExternalForm()); - // Store visual state new PersistenceVisualStateTable(this, preferences.getSearchDialogColumnPreferences()).addListeners(); From ebca33e74ee674515e171036d11edc36e8ddc34e Mon Sep 17 00:00:00 2001 From: Subhramit Basu Bhowmick Date: Sat, 5 Oct 2024 12:37:20 +0530 Subject: [PATCH 6/6] Remove entry (#11882) --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f1bfa3434b5..5760240dfdd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -83,7 +83,6 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv - We fixed an issue where unescaped braces in the arXiv fetcher were not treated. [#11704](https://github.com/JabRef/jabref/issues/11704) - We fixed an issue where HTML instead of the fulltext pdf was downloaded when importing arXiv entries. [#4913](https://github.com/JabRef/jabref/issues/4913) - We fixed an issue where the keywords and crossref fields were not properly focused. [#11177](https://github.com/JabRef/jabref/issues/11177) -- We fixed an issue where listeners were being attached without being released later leading to a memory leak. [#11809](https://github.com/JabRef/jabref/issues/11809) - We fixed an issue where the Undo/Redo buttons were active even when all libraries are closed. [#11837](https://github.com/JabRef/jabref/issues/11837) - We fixed an issue where recently opened files were not displayed in the main menu properly. [#9042](https://github.com/JabRef/jabref/issues/9042) - We fixed an issue where the DOI lookup would show an error when a DOI was found for an entry. [#11850](https://github.com/JabRef/jabref/issues/11850)