From c3453121a28cf2b5e95add2acbd98b30061f970c Mon Sep 17 00:00:00 2001 From: jayvardhanghildiyal Date: Sun, 22 Jun 2025 01:38:19 +0930 Subject: [PATCH 01/10] Added warning label that allows user to jump to already existing entry --- CHANGELOG.md | 1 + jabgui/src/main/java/module-info.java | 1 + .../org/jabref/gui/newentry/NewEntryView.java | 63 ++++++++++++++++++- .../gui/newentry/NewEntryViewModel.java | 5 ++ .../org/jabref/gui/newentry/NewEntry.fxml | 5 ++ .../jabref/logic/database/DuplicateCheck.java | 4 +- 6 files changed, 76 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 30029a698af..43ae35bedf2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv - We added support for import of a Refer/BibIX file format. [#13069](https://github.com/JabRef/jabref/issues/13069) - We added a new `jabkit` command `pseudonymize` to pseudonymize the library. [#13109](https://github.com/JabRef/jabref/issues/13109) - We added functionality to focus running instance when trying to start a second instance. [#13129](https://github.com/JabRef/jabref/issues/13129) +- When adding a duplicate entry, display warning label that allows user to jump to it instead. [#13261](https://github.com/JabRef/jabref/issues/13261) ### Changed diff --git a/jabgui/src/main/java/module-info.java b/jabgui/src/main/java/module-info.java index fc93769b06d..6a3d16b5efc 100644 --- a/jabgui/src/main/java/module-info.java +++ b/jabgui/src/main/java/module-info.java @@ -191,5 +191,6 @@ requires org.antlr.antlr4.runtime; requires org.libreoffice.uno; requires com.dlsc.pdfviewfx; + requires jbibtex; // endregion } diff --git a/jabgui/src/main/java/org/jabref/gui/newentry/NewEntryView.java b/jabgui/src/main/java/org/jabref/gui/newentry/NewEntryView.java index e3c057a17cd..5053ba2f5f9 100644 --- a/jabgui/src/main/java/org/jabref/gui/newentry/NewEntryView.java +++ b/jabgui/src/main/java/org/jabref/gui/newentry/NewEntryView.java @@ -2,6 +2,7 @@ import java.util.ArrayList; import java.util.Collection; +import java.util.HashMap; import java.util.List; import java.util.Optional; @@ -10,6 +11,7 @@ import javafx.scene.control.Button; import javafx.scene.control.ButtonType; import javafx.scene.control.ComboBox; +import javafx.scene.control.Hyperlink; import javafx.scene.control.Label; import javafx.scene.control.RadioButton; import javafx.scene.control.Tab; @@ -19,6 +21,7 @@ import javafx.scene.control.TitledPane; import javafx.scene.control.ToggleGroup; import javafx.scene.control.Tooltip; +import javafx.scene.layout.HBox; import javafx.scene.layout.TilePane; import javafx.stage.Screen; import javafx.stage.Stage; @@ -43,11 +46,15 @@ import org.jabref.logic.importer.fetcher.isbntobibtex.IsbnFetcher; import org.jabref.logic.importer.plaincitation.PlainCitationParserChoice; import org.jabref.logic.l10n.Localization; +import org.jabref.logic.layout.LayoutFormatter; +import org.jabref.logic.layout.format.DOIStrip; import org.jabref.logic.util.TaskExecutor; +import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.database.BibDatabaseMode; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.BibEntryType; import org.jabref.model.entry.BibEntryTypesManager; +import org.jabref.model.entry.field.StandardField; import org.jabref.model.entry.identifier.ArXivIdentifier; import org.jabref.model.entry.identifier.DOI; import org.jabref.model.entry.identifier.ISBN; @@ -86,6 +93,9 @@ public class NewEntryView extends BaseDialog { @Inject private FileUpdateMonitor fileUpdateMonitor; private final ControlsFxVisualizer visualizer; + private BibEntry duplicateEntry; + private final HashMap doiCache = new HashMap(); + private boolean isCacheInitialized = false; @FXML private ButtonType generateButtonType; private Button generateButton; @@ -115,6 +125,8 @@ public class NewEntryView extends BaseDialog { @FXML private TextArea bibtexText; + @FXML private HBox duplicateWarningBox; + @FXML private Hyperlink duplicateSelectLink; private BibEntry result; public NewEntryView(NewEntryDialogTab initialApproach, GuiPreferences preferences, LibraryTab libraryTab, DialogService dialogService) { @@ -212,6 +224,40 @@ public void initialize() { initializeSpecifyBibTeX(); } + private void checkDOI(String doiInput) { + if (doiInput == null || doiInput.isBlank()) { + viewModel.isDuplicateEntryProperty().set(false); + duplicateEntry = null; + return; + } + + LayoutFormatter doiStrip = new DOIStrip(); + String normalizedInput = doiStrip.format(doiInput); + + if (!isCacheInitialized) { + doiCache.clear(); + BibDatabaseContext databaseContext = stateManager.getActiveDatabase().orElseThrow(() -> new NullPointerException("no active library found !")); + + for (BibEntry entry : databaseContext.getEntries()) { + entry.getField(StandardField.DOI) + .map(doiStrip::format) + .ifPresent(strippedDoi -> doiCache.put(strippedDoi.toLowerCase(), entry)); + } + + isCacheInitialized = true; + } + + BibEntry matchedEntry = doiCache.get(normalizedInput.toLowerCase()); + + if (matchedEntry != null) { + duplicateEntry = matchedEntry; + viewModel.isDuplicateEntryProperty().set(true); + } else { + duplicateEntry = null; + viewModel.isDuplicateEntryProperty().set(false); + } + } + private void initializeCreateEntry() { entryRecommendedTitle.managedProperty().bind(entryRecommendedTitle.visibleProperty()); entryRecommendedTitle.expandedProperty().bindBidirectional(preferences.typesRecommendedExpandedProperty()); @@ -260,7 +306,9 @@ private void initializeLookupIdentifier() { // method (each automatically independently, or all through the same fetcher). idText.setPromptText(Localization.lang("Enter the reference identifier to search for.")); idText.textProperty().bindBidirectional(viewModel.idTextProperty()); - final String clipboardText = ClipBoardManager.getContents().trim(); + + duplicateWarningBox.visibleProperty().bind(viewModel.isDuplicateEntryProperty()); + duplicateWarningBox.managedProperty().bind(viewModel.isDuplicateEntryProperty()); ToggleGroup toggleGroup = new ToggleGroup(); idLookupGuess.setToggleGroup(toggleGroup); @@ -278,6 +326,9 @@ private void initializeLookupIdentifier() { idText.setText(ClipBoardManager.getContents().trim()); idText.selectAll(); + // since the clipboard automatically pastes the DOI in the textfield, a check here is also required ! + checkDOI(ClipBoardManager.getContents().trim()); + Identifier id = validClipboardId.get(); Platform.runLater(() -> { idLookupSpecify.setSelected(true); @@ -303,6 +354,16 @@ private void initializeLookupIdentifier() { idErrorInvalidText.visibleProperty().bind(viewModel.idTextValidatorProperty().not()); idErrorInvalidFetcher.visibleProperty().bind(idLookupSpecify.selectedProperty().and(viewModel.idFetcherValidatorProperty().not())); + + idText.textProperty().addListener((_, _, newText) -> { + checkDOI(newText); + }); + + duplicateSelectLink.setOnAction(_ -> { + if (duplicateEntry != null) { + libraryTab.showAndEdit(duplicateEntry); + } + }); } private void initializeInterpretCitations() { diff --git a/jabgui/src/main/java/org/jabref/gui/newentry/NewEntryViewModel.java b/jabgui/src/main/java/org/jabref/gui/newentry/NewEntryViewModel.java index 601c3314638..a06f74e5478 100644 --- a/jabgui/src/main/java/org/jabref/gui/newentry/NewEntryViewModel.java +++ b/jabgui/src/main/java/org/jabref/gui/newentry/NewEntryViewModel.java @@ -62,6 +62,7 @@ public class NewEntryViewModel { private final BooleanProperty executing; private final BooleanProperty executedSuccessfully; + private final BooleanProperty isDuplicateEntry = new SimpleBooleanProperty(false); private final StringProperty idText; private final Validator idTextValidator; @@ -130,6 +131,10 @@ public NewEntryViewModel(GuiPreferences preferences, bibtexWorker = null; } + public BooleanProperty isDuplicateEntryProperty() { + return isDuplicateEntry; + } + public ReadOnlyBooleanProperty executingProperty() { return executing; } diff --git a/jabgui/src/main/resources/org/jabref/gui/newentry/NewEntry.fxml b/jabgui/src/main/resources/org/jabref/gui/newentry/NewEntry.fxml index 57f484527c3..18bf6b2bb06 100644 --- a/jabgui/src/main/resources/org/jabref/gui/newentry/NewEntry.fxml +++ b/jabgui/src/main/resources/org/jabref/gui/newentry/NewEntry.fxml @@ -4,6 +4,7 @@ + @@ -64,6 +65,10 @@ + + diff --git a/jablib/src/main/java/org/jabref/logic/database/DuplicateCheck.java b/jablib/src/main/java/org/jabref/logic/database/DuplicateCheck.java index 24555a5a158..0812b9c021e 100644 --- a/jablib/src/main/java/org/jabref/logic/database/DuplicateCheck.java +++ b/jablib/src/main/java/org/jabref/logic/database/DuplicateCheck.java @@ -76,8 +76,8 @@ public DuplicateCheck(BibEntryTypesManager entryTypesManager) { private static boolean haveSameIdentifier(final BibEntry one, final BibEntry two) { return one.getFields().stream() - .filter(field -> field.getProperties().contains(FieldProperty.IDENTIFIER)) - .anyMatch(field -> two.getField(field).map(content -> one.getField(field).orElseThrow().equals(content)).orElse(false)); + .filter(field -> field.getProperties().contains(FieldProperty.IDENTIFIER)) + .anyMatch(field -> two.getField(field).map(content -> one.getField(field).orElseThrow().equals(content)).orElse(false)); } private static boolean haveDifferentEntryType(final BibEntry one, final BibEntry two) { From 46f52ec5d4b5e8aa1dc106cfb8b0e67462054f3b Mon Sep 17 00:00:00 2001 From: jayvardhanghildiyal Date: Sun, 22 Jun 2025 03:13:54 +0930 Subject: [PATCH 02/10] removed comment and changed hashmap definition --- .../src/main/java/org/jabref/gui/newentry/NewEntryView.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/jabgui/src/main/java/org/jabref/gui/newentry/NewEntryView.java b/jabgui/src/main/java/org/jabref/gui/newentry/NewEntryView.java index 5053ba2f5f9..930385d7100 100644 --- a/jabgui/src/main/java/org/jabref/gui/newentry/NewEntryView.java +++ b/jabgui/src/main/java/org/jabref/gui/newentry/NewEntryView.java @@ -4,6 +4,7 @@ import java.util.Collection; import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Optional; import javafx.application.Platform; @@ -94,7 +95,7 @@ public class NewEntryView extends BaseDialog { private final ControlsFxVisualizer visualizer; private BibEntry duplicateEntry; - private final HashMap doiCache = new HashMap(); + private final Map doiCache = new HashMap<>(); private boolean isCacheInitialized = false; @FXML private ButtonType generateButtonType; @@ -326,7 +327,6 @@ private void initializeLookupIdentifier() { idText.setText(ClipBoardManager.getContents().trim()); idText.selectAll(); - // since the clipboard automatically pastes the DOI in the textfield, a check here is also required ! checkDOI(ClipBoardManager.getContents().trim()); Identifier id = validClipboardId.get(); From f453a05b799cbc8a7aa2730e8b486c23c6fceb69 Mon Sep 17 00:00:00 2001 From: jayvardhanghildiyal Date: Sun, 22 Jun 2025 03:20:51 +0930 Subject: [PATCH 03/10] pulled from main and updated CHANGELOG.md --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 54c5f6667c8..a81ef61ecdc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,7 +40,6 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv - We added a new setting in the 'Entry Editor' preferences to hide the 'File Annotations' tab when no annotations are available. [#13143](https://github.com/JabRef/jabref/issues/13143) - We added support for multi-file import across different formats. [#13269](https://github.com/JabRef/jabref/issues/13269) - ### Changed - We moved some functionality from the graphical application `jabref` with new command verbs `generate-citation-keys`, `check-consistency`, `fetch`, `search`, `convert`, `generate-bib-from-aux`, `preferences` and `pdf` to the new toolkit. [#13012](https://github.com/JabRef/jabref/pull/13012) [#110](https://github.com/JabRef/jabref/issues/110) From 7ef3dc95c2e9e7018bf0e5a98ba41c880c3d0b2e Mon Sep 17 00:00:00 2001 From: jayvardhanghildiyal Date: Sun, 22 Jun 2025 03:40:25 +0930 Subject: [PATCH 04/10] removed line 'requires jbibtex;' from the end of module-info.java --- jabgui/src/main/java/module-info.java | 1 - 1 file changed, 1 deletion(-) diff --git a/jabgui/src/main/java/module-info.java b/jabgui/src/main/java/module-info.java index 0b85ab906d1..0a529f52082 100644 --- a/jabgui/src/main/java/module-info.java +++ b/jabgui/src/main/java/module-info.java @@ -188,6 +188,5 @@ requires org.antlr.antlr4.runtime; requires org.libreoffice.uno; requires com.dlsc.pdfviewfx; - requires jbibtex; // endregion } From ddff1b27ce2505ed2d229610bdfde7b4cfc7cb22 Mon Sep 17 00:00:00 2001 From: jayvardhanghildiyal Date: Sun, 22 Jun 2025 21:15:02 +0930 Subject: [PATCH 05/10] shifted most code to viewmodel from view, removed unused initializeCreateEntry function --- .../org/jabref/gui/newentry/NewEntryView.java | 108 ++---------------- .../gui/newentry/NewEntryViewModel.java | 47 +++++++- .../org/jabref/gui/newentry/NewEntry.fxml | 12 +- 3 files changed, 61 insertions(+), 106 deletions(-) diff --git a/jabgui/src/main/java/org/jabref/gui/newentry/NewEntryView.java b/jabgui/src/main/java/org/jabref/gui/newentry/NewEntryView.java index eaa549c4a07..093ba3331c2 100644 --- a/jabgui/src/main/java/org/jabref/gui/newentry/NewEntryView.java +++ b/jabgui/src/main/java/org/jabref/gui/newentry/NewEntryView.java @@ -2,9 +2,7 @@ import java.util.ArrayList; import java.util.Collection; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.Optional; import javafx.application.Platform; @@ -22,7 +20,6 @@ import javafx.scene.control.TitledPane; import javafx.scene.control.ToggleGroup; import javafx.scene.control.Tooltip; -import javafx.scene.layout.HBox; import javafx.scene.layout.TilePane; import javafx.stage.Screen; import javafx.stage.Stage; @@ -47,15 +44,11 @@ import org.jabref.logic.importer.fetcher.isbntobibtex.IsbnFetcher; import org.jabref.logic.importer.plaincitation.PlainCitationParserChoice; import org.jabref.logic.l10n.Localization; -import org.jabref.logic.layout.LayoutFormatter; -import org.jabref.logic.layout.format.DOIStrip; import org.jabref.logic.util.TaskExecutor; -import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.database.BibDatabaseMode; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.BibEntryType; import org.jabref.model.entry.BibEntryTypesManager; -import org.jabref.model.entry.field.StandardField; import org.jabref.model.entry.identifier.ArXivIdentifier; import org.jabref.model.entry.identifier.DOI; import org.jabref.model.entry.identifier.ISBN; @@ -94,9 +87,6 @@ public class NewEntryView extends BaseDialog { @Inject private FileUpdateMonitor fileUpdateMonitor; private final ControlsFxVisualizer visualizer; - private BibEntry duplicateEntry; - private final Map doiCache = new HashMap<>(); - private boolean isCacheInitialized = false; @FXML private ButtonType generateButtonType; private Button generateButton; @@ -120,14 +110,14 @@ public class NewEntryView extends BaseDialog { @FXML private ComboBox idFetcher; @FXML private Label idErrorInvalidText; @FXML private Label idErrorInvalidFetcher; + @FXML private Label duplicateSelectLabel; + @FXML private Hyperlink duplicateSelectLink; @FXML private TextArea interpretText; @FXML private ComboBox interpretParser; @FXML private TextArea bibtexText; - @FXML private HBox duplicateWarningBox; - @FXML private Hyperlink duplicateSelectLink; private BibEntry result; public NewEntryView(NewEntryDialogTab initialApproach, GuiPreferences preferences, LibraryTab libraryTab, DialogService dialogService) { @@ -225,82 +215,6 @@ public void initialize() { initializeSpecifyBibTeX(); } - private void checkDOI(String doiInput) { - if (doiInput == null || doiInput.isBlank()) { - viewModel.isDuplicateEntryProperty().set(false); - duplicateEntry = null; - return; - } - - LayoutFormatter doiStrip = new DOIStrip(); - String normalizedInput = doiStrip.format(doiInput); - - if (!isCacheInitialized) { - doiCache.clear(); - BibDatabaseContext databaseContext = stateManager.getActiveDatabase().orElseThrow(() -> new NullPointerException("no active library found !")); - - for (BibEntry entry : databaseContext.getEntries()) { - entry.getField(StandardField.DOI) - .map(doiStrip::format) - .ifPresent(strippedDoi -> doiCache.put(strippedDoi.toLowerCase(), entry)); - } - - isCacheInitialized = true; - } - - BibEntry matchedEntry = doiCache.get(normalizedInput.toLowerCase()); - - if (matchedEntry != null) { - duplicateEntry = matchedEntry; - viewModel.isDuplicateEntryProperty().set(true); - } else { - duplicateEntry = null; - viewModel.isDuplicateEntryProperty().set(false); - } - } - - private void initializeCreateEntry() { - entryRecommendedTitle.managedProperty().bind(entryRecommendedTitle.visibleProperty()); - entryRecommendedTitle.expandedProperty().bindBidirectional(preferences.typesRecommendedExpandedProperty()); - entryRecommended.managedProperty().bind(entryRecommended.visibleProperty()); - - entryOtherTitle.managedProperty().bind(entryOtherTitle.visibleProperty()); - entryOtherTitle.expandedProperty().bindBidirectional(preferences.typesOtherExpandedProperty()); - entryOther.managedProperty().bind(entryOther.visibleProperty()); - - entryCustomTitle.managedProperty().bind(entryCustomTitle.visibleProperty()); - entryCustomTitle.expandedProperty().bindBidirectional(preferences.typesCustomExpandedProperty()); - entryCustom.managedProperty().bind(entryCustom.visibleProperty()); - - final boolean isBiblatexMode = libraryTab.getBibDatabaseContext().isBiblatexMode(); - - List recommendedEntries; - List otherEntries; - if (isBiblatexMode) { - recommendedEntries = BiblatexEntryTypeDefinitions.RECOMMENDED; - otherEntries = new ArrayList<>(BiblatexEntryTypeDefinitions.ALL); - otherEntries.removeAll(recommendedEntries); - otherEntries.addAll(BiblatexSoftwareEntryTypeDefinitions.ALL); - otherEntries.addAll(BiblatexAPAEntryTypeDefinitions.ALL); - } else { - recommendedEntries = BibtexEntryTypeDefinitions.RECOMMENDED; - otherEntries = new ArrayList<>(BiblatexEntryTypeDefinitions.ALL); - otherEntries.removeAll(recommendedEntries); - otherEntries.addAll(IEEETranEntryTypeDefinitions.ALL); - } - addEntriesToPane(entryRecommended, recommendedEntries); - addEntriesToPane(entryOther, otherEntries); - - final BibEntryTypesManager entryTypesManager = Injector.instantiateModelOrService(BibEntryTypesManager.class); - final BibDatabaseMode customTypesDatabaseMode = isBiblatexMode ? BibDatabaseMode.BIBLATEX : BibDatabaseMode.BIBTEX; - final List customEntries = entryTypesManager.getAllCustomTypes(customTypesDatabaseMode); - if (customEntries.isEmpty()) { - entryCustomTitle.setVisible(false); - } else { - addEntriesToPane(entryCustom, customEntries); - } - } - private void initializeAddEntry() { entryRecommendedTitle.managedProperty().bind(entryRecommendedTitle.visibleProperty()); entryRecommendedTitle.expandedProperty().bindBidirectional(preferences.typesRecommendedExpandedProperty()); @@ -350,9 +264,6 @@ private void initializeLookupIdentifier() { idText.setPromptText(Localization.lang("Enter the reference identifier to search for.")); idText.textProperty().bindBidirectional(viewModel.idTextProperty()); - duplicateWarningBox.visibleProperty().bind(viewModel.isDuplicateEntryProperty()); - duplicateWarningBox.managedProperty().bind(viewModel.isDuplicateEntryProperty()); - ToggleGroup toggleGroup = new ToggleGroup(); idLookupGuess.setToggleGroup(toggleGroup); idLookupSpecify.setToggleGroup(toggleGroup); @@ -363,14 +274,14 @@ private void initializeLookupIdentifier() { idLookupSpecify.selectedProperty().set(true); } + viewModel.populateDOICache(); + // [impl->req~newentry.clipboard.autofocus~1] Optional validClipboardId = extractValidIdentifierFromClipboard(); if (validClipboardId.isPresent()) { idText.setText(ClipBoardManager.getContents().trim()); idText.selectAll(); - checkDOI(ClipBoardManager.getContents().trim()); - Identifier id = validClipboardId.get(); Platform.runLater(() -> { idLookupSpecify.setSelected(true); @@ -395,15 +306,14 @@ private void initializeLookupIdentifier() { idFetcher.setOnAction(_ -> preferences.setLatestIdFetcher(idFetcher.getValue().getName())); idErrorInvalidText.visibleProperty().bind(viewModel.idTextValidatorProperty().not()); + idErrorInvalidText.managedProperty().bind(viewModel.idTextValidatorProperty().not()); + duplicateSelectLabel.visibleProperty().bind(viewModel.duplicateDoiValidatorProperty()); + duplicateSelectLink.visibleProperty().bind(viewModel.duplicateDoiValidatorProperty()); idErrorInvalidFetcher.visibleProperty().bind(idLookupSpecify.selectedProperty().and(viewModel.idFetcherValidatorProperty().not())); - idText.textProperty().addListener((_, _, newText) -> { - checkDOI(newText); - }); - duplicateSelectLink.setOnAction(_ -> { - if (duplicateEntry != null) { - libraryTab.showAndEdit(duplicateEntry); + if (viewModel.duplicateDoiValidatorProperty() != null) { + libraryTab.showAndEdit(viewModel.getDuplicateEntry()); } }); } diff --git a/jabgui/src/main/java/org/jabref/gui/newentry/NewEntryViewModel.java b/jabgui/src/main/java/org/jabref/gui/newentry/NewEntryViewModel.java index a06f74e5478..ca2b298d626 100644 --- a/jabgui/src/main/java/org/jabref/gui/newentry/NewEntryViewModel.java +++ b/jabgui/src/main/java/org/jabref/gui/newentry/NewEntryViewModel.java @@ -1,6 +1,8 @@ package org.jabref.gui.newentry; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.Optional; @@ -38,7 +40,10 @@ import org.jabref.logic.importer.plaincitation.RuleBasedPlainCitationParser; import org.jabref.logic.importer.plaincitation.SeveralPlainCitationParser; import org.jabref.logic.l10n.Localization; +import org.jabref.logic.layout.LayoutFormatter; +import org.jabref.logic.layout.format.DOIStrip; import org.jabref.model.entry.BibEntry; +import org.jabref.model.entry.field.StandardField; import org.jabref.model.strings.StringUtil; import org.jabref.model.util.FileUpdateMonitor; @@ -62,10 +67,10 @@ public class NewEntryViewModel { private final BooleanProperty executing; private final BooleanProperty executedSuccessfully; - private final BooleanProperty isDuplicateEntry = new SimpleBooleanProperty(false); private final StringProperty idText; private final Validator idTextValidator; + private final Validator duplicateDoiValidator; private final ListProperty idFetchers; private final ObjectProperty idFetcher; private final Validator idFetcherValidator; @@ -80,6 +85,8 @@ public class NewEntryViewModel { private final StringProperty bibtexText; private final Validator bibtexTextValidator; private Task>> bibtexWorker; + private BibEntry duplicateEntry; + private final Map doiCache; public NewEntryViewModel(GuiPreferences preferences, LibraryTab libraryTab, @@ -98,12 +105,17 @@ public NewEntryViewModel(GuiPreferences preferences, executing = new SimpleBooleanProperty(false); executedSuccessfully = new SimpleBooleanProperty(false); + doiCache = new HashMap<>(); idText = new SimpleStringProperty(); idTextValidator = new FunctionBasedValidator<>( idText, StringUtil::isNotBlank, ValidationMessage.error(Localization.lang("You must specify an identifier."))); + duplicateDoiValidator = new FunctionBasedValidator<>( + idText, + this::checkDOI, + ValidationMessage.error(Localization.lang("DOI already exists in a library."))); idFetchers = new SimpleListProperty<>(FXCollections.observableArrayList()); idFetchers.addAll(WebFetchers.getIdBasedFetchers(preferences.getImportFormatPreferences(), preferences.getImporterPreferences())); idFetcher = new SimpleObjectProperty<>(); @@ -131,8 +143,33 @@ public NewEntryViewModel(GuiPreferences preferences, bibtexWorker = null; } - public BooleanProperty isDuplicateEntryProperty() { - return isDuplicateEntry; + public void populateDOICache() { + LayoutFormatter doiStrip = new DOIStrip(); + doiCache.clear(); + stateManager.getOpenDatabases().stream() + .flatMap(context -> context.getEntries().stream()) + .forEach(entry -> entry.getField(StandardField.DOI).ifPresent(doi -> { + String stripped = doiStrip.format(doi); + doiCache.put(stripped, entry); + })); + } + + public boolean checkDOI(String doiInput) { + if (doiInput == null || doiInput.isBlank()) { + return false; + } + + LayoutFormatter doiStrip = new DOIStrip(); + String normalized = doiStrip.format(doiInput); + if (doiCache.containsKey(normalized)) { + duplicateEntry = doiCache.get(normalized); + return true; + } + return false; + } + + public BibEntry getDuplicateEntry() { + return duplicateEntry; } public ReadOnlyBooleanProperty executingProperty() { @@ -151,6 +188,10 @@ public ReadOnlyBooleanProperty idTextValidatorProperty() { return idTextValidator.getValidationStatus().validProperty(); } + public ReadOnlyBooleanProperty duplicateDoiValidatorProperty() { + return duplicateDoiValidator.getValidationStatus().validProperty(); + } + public ListProperty idFetchersProperty() { return idFetchers; } diff --git a/jabgui/src/main/resources/org/jabref/gui/newentry/NewEntry.fxml b/jabgui/src/main/resources/org/jabref/gui/newentry/NewEntry.fxml index 3ea9a9b596d..459f0b15817 100644 --- a/jabgui/src/main/resources/org/jabref/gui/newentry/NewEntry.fxml +++ b/jabgui/src/main/resources/org/jabref/gui/newentry/NewEntry.fxml @@ -65,10 +65,6 @@ - - @@ -93,6 +89,14 @@ + + + + + + From 619458b7cf88be90dda386c1941026c9e1c31a02 Mon Sep 17 00:00:00 2001 From: jayvardhanghildiyal Date: Mon, 23 Jun 2025 19:24:00 +0930 Subject: [PATCH 06/10] removing and updating some logic. the functionality requested should now be functional --- .../gui/newentry/NewEntryViewModel.java | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/jabgui/src/main/java/org/jabref/gui/newentry/NewEntryViewModel.java b/jabgui/src/main/java/org/jabref/gui/newentry/NewEntryViewModel.java index ca2b298d626..da508430246 100644 --- a/jabgui/src/main/java/org/jabref/gui/newentry/NewEntryViewModel.java +++ b/jabgui/src/main/java/org/jabref/gui/newentry/NewEntryViewModel.java @@ -42,6 +42,7 @@ import org.jabref.logic.l10n.Localization; import org.jabref.logic.layout.LayoutFormatter; import org.jabref.logic.layout.format.DOIStrip; +import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.field.StandardField; import org.jabref.model.strings.StringUtil; @@ -115,7 +116,7 @@ public NewEntryViewModel(GuiPreferences preferences, duplicateDoiValidator = new FunctionBasedValidator<>( idText, this::checkDOI, - ValidationMessage.error(Localization.lang("DOI already exists in a library."))); + ValidationMessage.error(Localization.lang("DOI already exists in a library"))); idFetchers = new SimpleListProperty<>(FXCollections.observableArrayList()); idFetchers.addAll(WebFetchers.getIdBasedFetchers(preferences.getImportFormatPreferences(), preferences.getImporterPreferences())); idFetcher = new SimpleObjectProperty<>(); @@ -144,14 +145,13 @@ public NewEntryViewModel(GuiPreferences preferences, } public void populateDOICache() { - LayoutFormatter doiStrip = new DOIStrip(); doiCache.clear(); - stateManager.getOpenDatabases().stream() - .flatMap(context -> context.getEntries().stream()) - .forEach(entry -> entry.getField(StandardField.DOI).ifPresent(doi -> { - String stripped = doiStrip.format(doi); - doiCache.put(stripped, entry); - })); + Optional activeDatabase = stateManager.getActiveDatabase(); + + activeDatabase.ifPresent(bibDatabaseContext -> bibDatabaseContext.getEntries() + .forEach(entry -> entry.getField(StandardField.DOI).ifPresent(doi -> { + doiCache.put(doi, entry); + }))); } public boolean checkDOI(String doiInput) { @@ -160,7 +160,8 @@ public boolean checkDOI(String doiInput) { } LayoutFormatter doiStrip = new DOIStrip(); - String normalized = doiStrip.format(doiInput); + String normalized = doiStrip.format(doiInput.toLowerCase()); + if (doiCache.containsKey(normalized)) { duplicateEntry = doiCache.get(normalized); return true; From 3fee7ee82ac270d385204eb1fc1362bd113f6ee4 Mon Sep 17 00:00:00 2001 From: jayvardhanghildiyal Date: Wed, 25 Jun 2025 22:30:29 +0930 Subject: [PATCH 07/10] removing some existing code and replacing it with validation logic. also added a new key in jabref_en.properties --- .../org/jabref/gui/newentry/NewEntryView.java | 18 +++++++------ .../gui/newentry/NewEntryViewModel.java | 25 +++++++++---------- .../org/jabref/gui/newentry/NewEntry.fxml | 16 ++++++------ .../main/resources/l10n/JabRef_en.properties | 1 + 4 files changed, 32 insertions(+), 28 deletions(-) diff --git a/jabgui/src/main/java/org/jabref/gui/newentry/NewEntryView.java b/jabgui/src/main/java/org/jabref/gui/newentry/NewEntryView.java index 093ba3331c2..b9f3ce78b97 100644 --- a/jabgui/src/main/java/org/jabref/gui/newentry/NewEntryView.java +++ b/jabgui/src/main/java/org/jabref/gui/newentry/NewEntryView.java @@ -17,6 +17,7 @@ import javafx.scene.control.TabPane; import javafx.scene.control.TextArea; import javafx.scene.control.TextField; +import javafx.scene.control.TextInputControl; import javafx.scene.control.TitledPane; import javafx.scene.control.ToggleGroup; import javafx.scene.control.Tooltip; @@ -28,6 +29,7 @@ import org.jabref.gui.DialogService; import org.jabref.gui.LibraryTab; import org.jabref.gui.StateManager; +import org.jabref.gui.fieldeditors.EditorValidator; import org.jabref.gui.preferences.GuiPreferences; import org.jabref.gui.search.SearchType; import org.jabref.gui.util.BaseDialog; @@ -307,15 +309,17 @@ private void initializeLookupIdentifier() { idErrorInvalidText.visibleProperty().bind(viewModel.idTextValidatorProperty().not()); idErrorInvalidText.managedProperty().bind(viewModel.idTextValidatorProperty().not()); - duplicateSelectLabel.visibleProperty().bind(viewModel.duplicateDoiValidatorProperty()); - duplicateSelectLink.visibleProperty().bind(viewModel.duplicateDoiValidatorProperty()); idErrorInvalidFetcher.visibleProperty().bind(idLookupSpecify.selectedProperty().and(viewModel.idFetcherValidatorProperty().not())); - duplicateSelectLink.setOnAction(_ -> { - if (viewModel.duplicateDoiValidatorProperty() != null) { - libraryTab.showAndEdit(viewModel.getDuplicateEntry()); - } - }); +// duplicateSelectLink.setOnAction(_ -> { +// if (viewModel.duplicateDoiValidatorProperty() != null) { +// libraryTab.showAndEdit(viewModel.getDuplicateEntry()); +// } +// }); + + TextInputControl textInput = idText; + EditorValidator validator = new EditorValidator(this.guiPreferences); + validator.configureValidation(viewModel.duplicateDoiValidatorStatus(), textInput); } private void initializeInterpretCitations() { diff --git a/jabgui/src/main/java/org/jabref/gui/newentry/NewEntryViewModel.java b/jabgui/src/main/java/org/jabref/gui/newentry/NewEntryViewModel.java index da508430246..e0c9258cb6d 100644 --- a/jabgui/src/main/java/org/jabref/gui/newentry/NewEntryViewModel.java +++ b/jabgui/src/main/java/org/jabref/gui/newentry/NewEntryViewModel.java @@ -50,6 +50,7 @@ import de.saxsys.mvvmfx.utils.validation.FunctionBasedValidator; import de.saxsys.mvvmfx.utils.validation.ValidationMessage; +import de.saxsys.mvvmfx.utils.validation.ValidationStatus; import de.saxsys.mvvmfx.utils.validation.Validator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -115,8 +116,7 @@ public NewEntryViewModel(GuiPreferences preferences, ValidationMessage.error(Localization.lang("You must specify an identifier."))); duplicateDoiValidator = new FunctionBasedValidator<>( idText, - this::checkDOI, - ValidationMessage.error(Localization.lang("DOI already exists in a library"))); + this::checkDOI); idFetchers = new SimpleListProperty<>(FXCollections.observableArrayList()); idFetchers.addAll(WebFetchers.getIdBasedFetchers(preferences.getImportFormatPreferences(), preferences.getImporterPreferences())); idFetcher = new SimpleObjectProperty<>(); @@ -154,24 +154,23 @@ public void populateDOICache() { }))); } - public boolean checkDOI(String doiInput) { - if (doiInput == null || doiInput.isBlank()) { - return false; + public ValidationMessage checkDOI(String doiInput) { + if (doiInput == null) { + return null; } - LayoutFormatter doiStrip = new DOIStrip(); String normalized = doiStrip.format(doiInput.toLowerCase()); if (doiCache.containsKey(normalized)) { duplicateEntry = doiCache.get(normalized); - return true; + return ValidationMessage.warning(Localization.lang("Entry already exists in a library")); } - return false; + return null; } - public BibEntry getDuplicateEntry() { - return duplicateEntry; - } +// public BibEntry getDuplicateEntry() { +// return duplicateEntry; +// } public ReadOnlyBooleanProperty executingProperty() { return executing; @@ -189,8 +188,8 @@ public ReadOnlyBooleanProperty idTextValidatorProperty() { return idTextValidator.getValidationStatus().validProperty(); } - public ReadOnlyBooleanProperty duplicateDoiValidatorProperty() { - return duplicateDoiValidator.getValidationStatus().validProperty(); + public ValidationStatus duplicateDoiValidatorStatus() { + return duplicateDoiValidator.getValidationStatus(); } public ListProperty idFetchersProperty() { diff --git a/jabgui/src/main/resources/org/jabref/gui/newentry/NewEntry.fxml b/jabgui/src/main/resources/org/jabref/gui/newentry/NewEntry.fxml index 459f0b15817..41b61bed640 100644 --- a/jabgui/src/main/resources/org/jabref/gui/newentry/NewEntry.fxml +++ b/jabgui/src/main/resources/org/jabref/gui/newentry/NewEntry.fxml @@ -89,14 +89,14 @@ - - - - - - + + + + + + + + diff --git a/jablib/src/main/resources/l10n/JabRef_en.properties b/jablib/src/main/resources/l10n/JabRef_en.properties index 131266ce455..2d9d0f6d6e8 100644 --- a/jablib/src/main/resources/l10n/JabRef_en.properties +++ b/jablib/src/main/resources/l10n/JabRef_en.properties @@ -2916,6 +2916,7 @@ Enter\ identifier...=Enter identifier... Enter\ identifier=Enter identifier Enter\ plain\ citations\ to\ parse,\ separated\ by\ blank\ lines.=Enter plain citations to parse, separated by blank lines. Enter\ the\ reference\ identifier\ to\ search\ for.=Enter the reference identifier to search for. +Entry\ already\ exists\ in\ a\ library=Entry already exists in a library Failed\ to\ interpret\ citations.\nThe\ following\ error\ was\ encountered\:\n%0=Failed to interpret citations.\nThe following error was encountered:\n%0 Failed\ to\ interpret\ citations=Failed to interpret citations Failed\ to\ lookup\ identifier=Failed to lookup identifier From 6cba540e75acfc45c7c60b146d13f08ecc07326d Mon Sep 17 00:00:00 2001 From: jayvardhanghildiyal Date: Thu, 26 Jun 2025 15:01:11 +0930 Subject: [PATCH 08/10] warning now displays if entry present in any active library. removed commented code and unused variables. --- CHANGELOG.md | 2 +- .../org/jabref/gui/newentry/NewEntryView.java | 9 -------- .../gui/newentry/NewEntryViewModel.java | 23 +++++++++---------- .../org/jabref/gui/newentry/NewEntry.fxml | 8 ------- 4 files changed, 12 insertions(+), 30 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f5d77a77365..44df8cf9b96 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv - We introduced a settings parameter to manage citations' relations local storage time-to-live with a default value set to 30 days. [#11189](https://github.com/JabRef/jabref/issues/11189) - We distribute arm64 images for Linux. [#10842](https://github.com/JabRef/jabref/issues/10842) -- When adding a duplicate entry, display warning label that allows user to jump to it instead. [#13261](https://github.com/JabRef/jabref/issues/13261) +- When adding an entry to a library, a warning is displayed if said entry already exists in an active library. [#13261](https://github.com/JabRef/jabref/issues/13261) - We added the field `monthfiled` to the default list of fields to resolve BibTeX-Strings for [#13375](https://github.com/JabRef/jabref/issues/13375) - We added a new ID based fetcher for [EuropePMC](https://europepmc.org/). [#13389](https://github.com/JabRef/jabref/pull/13389) diff --git a/jabgui/src/main/java/org/jabref/gui/newentry/NewEntryView.java b/jabgui/src/main/java/org/jabref/gui/newentry/NewEntryView.java index b9f3ce78b97..4fe4e95536b 100644 --- a/jabgui/src/main/java/org/jabref/gui/newentry/NewEntryView.java +++ b/jabgui/src/main/java/org/jabref/gui/newentry/NewEntryView.java @@ -10,7 +10,6 @@ import javafx.scene.control.Button; import javafx.scene.control.ButtonType; import javafx.scene.control.ComboBox; -import javafx.scene.control.Hyperlink; import javafx.scene.control.Label; import javafx.scene.control.RadioButton; import javafx.scene.control.Tab; @@ -112,8 +111,6 @@ public class NewEntryView extends BaseDialog { @FXML private ComboBox idFetcher; @FXML private Label idErrorInvalidText; @FXML private Label idErrorInvalidFetcher; - @FXML private Label duplicateSelectLabel; - @FXML private Hyperlink duplicateSelectLink; @FXML private TextArea interpretText; @FXML private ComboBox interpretParser; @@ -311,12 +308,6 @@ private void initializeLookupIdentifier() { idErrorInvalidText.managedProperty().bind(viewModel.idTextValidatorProperty().not()); idErrorInvalidFetcher.visibleProperty().bind(idLookupSpecify.selectedProperty().and(viewModel.idFetcherValidatorProperty().not())); -// duplicateSelectLink.setOnAction(_ -> { -// if (viewModel.duplicateDoiValidatorProperty() != null) { -// libraryTab.showAndEdit(viewModel.getDuplicateEntry()); -// } -// }); - TextInputControl textInput = idText; EditorValidator validator = new EditorValidator(this.guiPreferences); validator.configureValidation(viewModel.duplicateDoiValidatorStatus(), textInput); diff --git a/jabgui/src/main/java/org/jabref/gui/newentry/NewEntryViewModel.java b/jabgui/src/main/java/org/jabref/gui/newentry/NewEntryViewModel.java index e0c9258cb6d..d65fdc30f68 100644 --- a/jabgui/src/main/java/org/jabref/gui/newentry/NewEntryViewModel.java +++ b/jabgui/src/main/java/org/jabref/gui/newentry/NewEntryViewModel.java @@ -1,5 +1,6 @@ package org.jabref.gui.newentry; +import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -16,6 +17,7 @@ import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; import javafx.collections.FXCollections; +import javafx.collections.ObservableList; import javafx.concurrent.Task; import org.jabref.gui.DialogService; @@ -87,7 +89,6 @@ public class NewEntryViewModel { private final StringProperty bibtexText; private final Validator bibtexTextValidator; private Task>> bibtexWorker; - private BibEntry duplicateEntry; private final Map doiCache; public NewEntryViewModel(GuiPreferences preferences, @@ -146,12 +147,15 @@ public NewEntryViewModel(GuiPreferences preferences, public void populateDOICache() { doiCache.clear(); - Optional activeDatabase = stateManager.getActiveDatabase(); - - activeDatabase.ifPresent(bibDatabaseContext -> bibDatabaseContext.getEntries() - .forEach(entry -> entry.getField(StandardField.DOI).ifPresent(doi -> { - doiCache.put(doi, entry); - }))); + ObservableList activeDatabase = stateManager.getOpenDatabases(); + + activeDatabase.stream() + .map(BibDatabaseContext::getEntries) + .flatMap(Collection::stream) + .forEach(bibEntry -> bibEntry.getField(StandardField.DOI) + .ifPresent(doi -> + doiCache.put(doi, bibEntry) + )); } public ValidationMessage checkDOI(String doiInput) { @@ -162,16 +166,11 @@ public ValidationMessage checkDOI(String doiInput) { String normalized = doiStrip.format(doiInput.toLowerCase()); if (doiCache.containsKey(normalized)) { - duplicateEntry = doiCache.get(normalized); return ValidationMessage.warning(Localization.lang("Entry already exists in a library")); } return null; } -// public BibEntry getDuplicateEntry() { -// return duplicateEntry; -// } - public ReadOnlyBooleanProperty executingProperty() { return executing; } diff --git a/jabgui/src/main/resources/org/jabref/gui/newentry/NewEntry.fxml b/jabgui/src/main/resources/org/jabref/gui/newentry/NewEntry.fxml index 41b61bed640..c7f224d2473 100644 --- a/jabgui/src/main/resources/org/jabref/gui/newentry/NewEntry.fxml +++ b/jabgui/src/main/resources/org/jabref/gui/newentry/NewEntry.fxml @@ -89,14 +89,6 @@ - - - - - - - - From c844bbd4571edb03bf264580b77a0d93db8837c9 Mon Sep 17 00:00:00 2001 From: jayvardhanghildiyal Date: Thu, 26 Jun 2025 16:18:25 +0930 Subject: [PATCH 09/10] changed return type of checkDOI function for handling null values safely. --- .../org/jabref/gui/newentry/NewEntryViewModel.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/jabgui/src/main/java/org/jabref/gui/newentry/NewEntryViewModel.java b/jabgui/src/main/java/org/jabref/gui/newentry/NewEntryViewModel.java index d65fdc30f68..f33ba31c02b 100644 --- a/jabgui/src/main/java/org/jabref/gui/newentry/NewEntryViewModel.java +++ b/jabgui/src/main/java/org/jabref/gui/newentry/NewEntryViewModel.java @@ -117,7 +117,7 @@ public NewEntryViewModel(GuiPreferences preferences, ValidationMessage.error(Localization.lang("You must specify an identifier."))); duplicateDoiValidator = new FunctionBasedValidator<>( idText, - this::checkDOI); + input -> checkDOI(input).orElse(null)); idFetchers = new SimpleListProperty<>(FXCollections.observableArrayList()); idFetchers.addAll(WebFetchers.getIdBasedFetchers(preferences.getImportFormatPreferences(), preferences.getImporterPreferences())); idFetcher = new SimpleObjectProperty<>(); @@ -158,17 +158,19 @@ public void populateDOICache() { )); } - public ValidationMessage checkDOI(String doiInput) { - if (doiInput == null) { - return null; + public Optional checkDOI(String doiInput) { + if (doiInput == null || doiInput.isBlank()) { + return Optional.empty(); } + LayoutFormatter doiStrip = new DOIStrip(); String normalized = doiStrip.format(doiInput.toLowerCase()); if (doiCache.containsKey(normalized)) { - return ValidationMessage.warning(Localization.lang("Entry already exists in a library")); + return Optional.of(ValidationMessage.warning(Localization.lang("Entry already exists in a library"))); } - return null; + + return Optional.empty(); } public ReadOnlyBooleanProperty executingProperty() { From 87ee01c1a638e090bb7638614f6490898f8bf61e Mon Sep 17 00:00:00 2001 From: jayvardhanghildiyal Date: Sun, 29 Jun 2025 23:13:01 +0930 Subject: [PATCH 10/10] duplicate checks now happen in currently active library only. solved tooltip issue. --- .../java/org/jabref/gui/newentry/NewEntryView.java | 14 ++++++++++++++ .../org/jabref/gui/newentry/NewEntryViewModel.java | 9 +++++++-- .../org/jabref/gui/newentry/NewEntry.fxml | 14 +++++++++----- .../src/main/resources/l10n/JabRef_en.properties | 1 + 4 files changed, 31 insertions(+), 7 deletions(-) diff --git a/jabgui/src/main/java/org/jabref/gui/newentry/NewEntryView.java b/jabgui/src/main/java/org/jabref/gui/newentry/NewEntryView.java index 4fe4e95536b..fe6e6e7a4ad 100644 --- a/jabgui/src/main/java/org/jabref/gui/newentry/NewEntryView.java +++ b/jabgui/src/main/java/org/jabref/gui/newentry/NewEntryView.java @@ -10,6 +10,7 @@ import javafx.scene.control.Button; import javafx.scene.control.ButtonType; import javafx.scene.control.ComboBox; +import javafx.scene.control.Hyperlink; import javafx.scene.control.Label; import javafx.scene.control.RadioButton; import javafx.scene.control.Tab; @@ -106,6 +107,8 @@ public class NewEntryView extends BaseDialog { @FXML private TilePane entryCustom; @FXML private TextField idText; + @FXML private Tooltip idTextTooltip; + @FXML private Hyperlink idJumpLink; @FXML private RadioButton idLookupGuess; @FXML private RadioButton idLookupSpecify; @FXML private ComboBox idFetcher; @@ -304,10 +307,21 @@ private void initializeLookupIdentifier() { idFetcher.setValue(initialFetcher); idFetcher.setOnAction(_ -> preferences.setLatestIdFetcher(idFetcher.getValue().getName())); + idJumpLink.visibleProperty().bind(viewModel.duplicateDoiValidatorStatus().validProperty().not()); idErrorInvalidText.visibleProperty().bind(viewModel.idTextValidatorProperty().not()); idErrorInvalidText.managedProperty().bind(viewModel.idTextValidatorProperty().not()); idErrorInvalidFetcher.visibleProperty().bind(idLookupSpecify.selectedProperty().and(viewModel.idFetcherValidatorProperty().not())); + idJumpLink.setOnAction(_ -> libraryTab.showAndEdit(viewModel.getDuplicateEntry())); + + viewModel.duplicateDoiValidatorStatus().validProperty().addListener((_, _, isValid) -> { + if (isValid) { + Tooltip.install(idText, idTextTooltip); + } else { + Tooltip.uninstall(idText, idTextTooltip); + } + }); + TextInputControl textInput = idText; EditorValidator validator = new EditorValidator(this.guiPreferences); validator.configureValidation(viewModel.duplicateDoiValidatorStatus(), textInput); diff --git a/jabgui/src/main/java/org/jabref/gui/newentry/NewEntryViewModel.java b/jabgui/src/main/java/org/jabref/gui/newentry/NewEntryViewModel.java index f33ba31c02b..65f71a5c8c6 100644 --- a/jabgui/src/main/java/org/jabref/gui/newentry/NewEntryViewModel.java +++ b/jabgui/src/main/java/org/jabref/gui/newentry/NewEntryViewModel.java @@ -17,7 +17,6 @@ import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; import javafx.collections.FXCollections; -import javafx.collections.ObservableList; import javafx.concurrent.Task; import org.jabref.gui.DialogService; @@ -90,6 +89,7 @@ public class NewEntryViewModel { private final Validator bibtexTextValidator; private Task>> bibtexWorker; private final Map doiCache; + private BibEntry duplicateEntry; public NewEntryViewModel(GuiPreferences preferences, LibraryTab libraryTab, @@ -147,7 +147,7 @@ public NewEntryViewModel(GuiPreferences preferences, public void populateDOICache() { doiCache.clear(); - ObservableList activeDatabase = stateManager.getOpenDatabases(); + Optional activeDatabase = stateManager.getActiveDatabase(); activeDatabase.stream() .map(BibDatabaseContext::getEntries) @@ -167,12 +167,17 @@ public Optional checkDOI(String doiInput) { String normalized = doiStrip.format(doiInput.toLowerCase()); if (doiCache.containsKey(normalized)) { + duplicateEntry = doiCache.get(normalized); return Optional.of(ValidationMessage.warning(Localization.lang("Entry already exists in a library"))); } return Optional.empty(); } + public BibEntry getDuplicateEntry() { + return duplicateEntry; + } + public ReadOnlyBooleanProperty executingProperty() { return executing; } diff --git a/jabgui/src/main/resources/org/jabref/gui/newentry/NewEntry.fxml b/jabgui/src/main/resources/org/jabref/gui/newentry/NewEntry.fxml index c7f224d2473..a9721da734b 100644 --- a/jabgui/src/main/resources/org/jabref/gui/newentry/NewEntry.fxml +++ b/jabgui/src/main/resources/org/jabref/gui/newentry/NewEntry.fxml @@ -17,6 +17,7 @@ + @@ -59,11 +60,14 @@ diff --git a/jablib/src/main/resources/l10n/JabRef_en.properties b/jablib/src/main/resources/l10n/JabRef_en.properties index 2d9d0f6d6e8..3aabdd08595 100644 --- a/jablib/src/main/resources/l10n/JabRef_en.properties +++ b/jablib/src/main/resources/l10n/JabRef_en.properties @@ -2931,6 +2931,7 @@ Interpret\ citations...=Interpret citations... Interpret\ citations=Interpret citations Invalid\ result=Invalid result Invalid\ result\ returned=Invalid result returned +Jump\ to\ entry=Jump to entry New\ Entry=New Entry Other\ types=Other types Parser=Parser