Skip to content

Added warning label that allows user to jump to already existing entry #13390

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 15 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
c345312
Added warning label that allows user to jump to already existing entry
jayvardhanghildiyal Jun 21, 2025
8070987
Merge branch 'main' into fix-for-issue-13261
jayvardhanghildiyal Jun 21, 2025
46f52ec
removed comment and changed hashmap definition
jayvardhanghildiyal Jun 21, 2025
5bafc59
Merge branch 'fix-for-issue-13261' of https://github.com/jayvardhangh…
jayvardhanghildiyal Jun 21, 2025
f453a05
pulled from main and updated CHANGELOG.md
jayvardhanghildiyal Jun 21, 2025
7ef3dc9
removed line 'requires jbibtex;' from the end of module-info.java
jayvardhanghildiyal Jun 21, 2025
ddff1b2
shifted most code to viewmodel from view, removed unused initializeCr…
jayvardhanghildiyal Jun 22, 2025
e6b23f4
Merge branch 'main' into fix-for-issue-13261
jayvardhanghildiyal Jun 22, 2025
4d0fa79
Merge remote-tracking branch 'upstream' into fix-for-issue-13261
jayvardhanghildiyal Jun 23, 2025
619458b
removing and updating some logic. the functionality requested should …
jayvardhanghildiyal Jun 23, 2025
d6cacca
Merge branch 'fix-for-issue-13261' of https://github.com/jayvardhangh…
jayvardhanghildiyal Jun 23, 2025
3fee7ee
removing some existing code and replacing it with validation logic. a…
jayvardhanghildiyal Jun 25, 2025
6cba540
warning now displays if entry present in any active library. removed …
jayvardhanghildiyal Jun 26, 2025
c844bbd
changed return type of checkDOI function for handling null values saf…
jayvardhanghildiyal Jun 26, 2025
87ee01c
duplicate checks now happen in currently active library only. solved …
jayvardhanghildiyal Jun 29, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +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)
- We added the field `monthfiled` to the default list of fields to resolve BibTeX-Strings for [#13375](https://github.com/JabRef/jabref/issues/13375)

### Changed
Expand Down
1 change: 1 addition & 0 deletions jabgui/src/main/java/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -188,5 +188,6 @@
requires org.antlr.antlr4.runtime;
requires org.libreoffice.uno;
requires com.dlsc.pdfviewfx;
requires jbibtex;
// endregion
}
105 changes: 104 additions & 1 deletion jabgui/src/main/java/org/jabref/gui/newentry/NewEntryView.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,17 @@

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;
import javafx.fxml.FXML;
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;
Expand All @@ -19,6 +22,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;
Expand All @@ -43,11 +47,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;
Expand Down Expand Up @@ -86,6 +94,9 @@ public class NewEntryView extends BaseDialog<BibEntry> {
@Inject private FileUpdateMonitor fileUpdateMonitor;

private final ControlsFxVisualizer visualizer;
private BibEntry duplicateEntry;
private final Map<String, BibEntry> doiCache = new HashMap<>();
private boolean isCacheInitialized = false;

@FXML private ButtonType generateButtonType;
private Button generateButton;
Expand Down Expand Up @@ -115,6 +126,8 @@ public class NewEntryView extends BaseDialog<BibEntry> {

@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) {
Expand Down Expand Up @@ -212,6 +225,82 @@ 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<BibEntryType> recommendedEntries;
List<BibEntryType> 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<BibEntryType> 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());
Expand Down Expand Up @@ -260,7 +349,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);
Expand All @@ -278,6 +369,8 @@ private void initializeLookupIdentifier() {
idText.setText(ClipBoardManager.getContents().trim());
idText.selectAll();

checkDOI(ClipBoardManager.getContents().trim());

Identifier id = validClipboardId.get();
Platform.runLater(() -> {
idLookupSpecify.setSelected(true);
Expand All @@ -303,6 +396,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() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -130,6 +131,10 @@ public NewEntryViewModel(GuiPreferences preferences,
bibtexWorker = null;
}

public BooleanProperty isDuplicateEntryProperty() {
return isDuplicateEntry;
}

public ReadOnlyBooleanProperty executingProperty() {
return executing;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<?import javafx.scene.control.ButtonType?>
<?import javafx.scene.control.ComboBox?>
<?import javafx.scene.control.DialogPane?>
<?import javafx.scene.control.Hyperlink?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.RadioButton?>
<?import javafx.scene.control.Tab?>
Expand Down Expand Up @@ -64,6 +65,10 @@
</tooltip>
</TextField>
</HBox>
<HBox fx:id="duplicateWarningBox" alignment="CENTER_LEFT" spacing="5.0">
<Label fx:id="duplicateSelectLabel" text="Entry already existing in library."/>
<Hyperlink fx:id="duplicateSelectLink" text="%Select entry"/>
</HBox>
<VBox spacing="5.0">
<RadioButton fx:id="idLookupGuess" text="%Automatically determine identifier type">
<tooltip>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down