diff --git a/CHANGELOG.md b/CHANGELOG.md index aa487a1574a..6ca4820613e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv - We added a different background color to the search bar to indicate when the search syntax is wrong. [#11658](https://github.com/JabRef/jabref/pull/11658) - We added a setting which always adds the literal "Cited on pages" text before each JStyle citation. [#11691](https://github.com/JabRef/jabref/pull/11732) - We added a new plain citation parser that uses LLMs. [#11825](https://github.com/JabRef/jabref/issues/11825) +- We added a compare button to the duplicates in the citation relations tab to open the "Possible duplicate entries" window. [#11192](https://github.com/JabRef/jabref/issues/11192) - We added automatic browser extension install on Windows for Chrome and Edge. [#6076](https://github.com/JabRef/jabref/issues/6076) - We added a search bar for filtering keyboard shortcuts. [#11686](https://github.com/JabRef/jabref/issues/11686) - By double clicking on a local citation in the Citation Relations Tab you can now jump the the linked entry. [#11955](https://github.com/JabRef/jabref/pull/11955) 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 48403a3d16d..a550a0098a7 100644 --- a/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/CitationRelationsTab.java +++ b/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/CitationRelationsTab.java @@ -5,6 +5,7 @@ import java.net.URI; import java.util.Arrays; import java.util.List; +import java.util.Optional; import javax.swing.undo.UndoManager; @@ -39,7 +40,12 @@ import org.jabref.gui.entryeditor.citationrelationtab.semanticscholar.CitationFetcher; import org.jabref.gui.entryeditor.citationrelationtab.semanticscholar.SemanticScholarFetcher; import org.jabref.gui.icon.IconTheme; +import org.jabref.gui.mergeentries.EntriesMergeResult; +import org.jabref.gui.mergeentries.MergeEntriesDialog; import org.jabref.gui.preferences.GuiPreferences; +import org.jabref.gui.undo.NamedCompound; +import org.jabref.gui.undo.UndoableInsertEntries; +import org.jabref.gui.undo.UndoableRemoveEntries; import org.jabref.gui.util.NoSelectionModel; import org.jabref.gui.util.ViewModelListCellFactory; import org.jabref.logic.bibtex.BibEntryWriter; @@ -51,6 +57,7 @@ import org.jabref.logic.os.OS; import org.jabref.logic.util.BackgroundTask; import org.jabref.logic.util.TaskExecutor; +import org.jabref.model.database.BibDatabase; import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.database.BibDatabaseMode; import org.jabref.model.database.BibDatabaseModeDetection; @@ -89,6 +96,8 @@ public class CitationRelationsTab extends EntryEditorTab { private final CitationsRelationsTabViewModel citationsRelationsTabViewModel; private final DuplicateCheck duplicateCheck; private final BibEntryTypesManager entryTypesManager; + private final StateManager stateManager; + private final UndoManager undoManager; public CitationRelationsTab(DialogService dialogService, BibDatabaseContext databaseContext, @@ -104,6 +113,8 @@ public CitationRelationsTab(DialogService dialogService, this.preferences = preferences; this.libraryTab = libraryTab; this.taskExecutor = taskExecutor; + this.undoManager = undoManager; + this.stateManager = stateManager; setText(Localization.lang("Citation relations")); setTooltip(new Tooltip(Localization.lang("Show articles related by citation"))); @@ -238,6 +249,13 @@ private void styleFetchedListView(CheckListView listView) } }); vContainer.getChildren().add(jumpTo); + + Button compareButton = IconTheme.JabRefIcons.MERGE_ENTRIES.asButton(); + compareButton.setTooltip(new Tooltip(Localization.lang("Compare with existing entry"))); + compareButton.setOnMouseClicked(event -> { + openPossibleDuplicateEntriesWindow(entry, listView); + }); + vContainer.getChildren().add(compareButton); } else { ToggleButton addToggle = IconTheme.JabRefIcons.ADD.asToggleButton(); addToggle.setTooltip(new Tooltip(Localization.lang("Select entry"))); @@ -511,4 +529,43 @@ private void importEntries(List entriesToImport, CitationF dialogService.notify(Localization.lang("Number of entries successfully imported") + ": " + entriesToImport.size()); } + + /** + * Function to open possible duplicate entries window to compare duplicate entries + * + * @param citationRelationItem duplicate in the citation relations tab + * @param listView CheckListView to display citations + */ + private void openPossibleDuplicateEntriesWindow(CitationRelationItem citationRelationItem, CheckListView listView) { + BibEntry libraryEntry = citationRelationItem.localEntry(); + BibEntry citationEntry = citationRelationItem.entry(); + String leftHeader = Localization.lang("Library Entry"); + String rightHeader = Localization.lang("Citation Entry"); + + MergeEntriesDialog dialog = new MergeEntriesDialog(libraryEntry, citationEntry, leftHeader, rightHeader, preferences); + dialog.setTitle(Localization.lang("Possible duplicate entries")); + + Optional entriesMergeResult = dialogService.showCustomDialogAndWait(dialog); + entriesMergeResult.ifPresentOrElse(mergeResult -> { + + BibEntry mergedEntry = mergeResult.mergedEntry(); + // update local entry of selected citation relation item + listView.getItems().set(listView.getItems().indexOf(citationRelationItem), new CitationRelationItem(citationRelationItem.entry(), mergedEntry, true)); + + // Merge method is similar to MergeTwoEntriesAction#execute + BibDatabase database = stateManager.getActiveDatabase().get().getDatabase(); + database.removeEntry(mergeResult.originalLeftEntry()); + libraryTab.getMainTable().setCitationMergeMode(true); + database.insertEntry(mergedEntry); + + NamedCompound ce = new NamedCompound(Localization.lang("Merge entries")); + ce.addEdit(new UndoableRemoveEntries(database, mergeResult.originalLeftEntry())); + ce.addEdit(new UndoableInsertEntries(stateManager.getActiveDatabase().get().getDatabase(), mergedEntry)); + ce.end(); + + undoManager.addEdit(ce); + + dialogService.notify(Localization.lang("Merged entries")); + }, () -> dialogService.notify(Localization.lang("Canceled merging entries"))); + } } diff --git a/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/CitationsRelationsTabViewModel.java b/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/CitationsRelationsTabViewModel.java index 7ae52965b8f..630ee2e387e 100644 --- a/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/CitationsRelationsTabViewModel.java +++ b/src/main/java/org/jabref/gui/entryeditor/citationrelationtab/CitationsRelationsTabViewModel.java @@ -64,6 +64,7 @@ private void importCites(List entries, BibEntry existingEntry, ImportH List citeKeys = getExistingEntriesFromCiteField(existingEntry); citeKeys.removeIf(String::isEmpty); + for (BibEntry entryToCite : entries) { if (generateNewKeyOnImport || entryToCite.getCitationKey().isEmpty()) { String key = generator.generateKey(entryToCite); diff --git a/src/main/java/org/jabref/gui/externalfiles/ImportHandler.java b/src/main/java/org/jabref/gui/externalfiles/ImportHandler.java index 669aa9a21bc..8e89ab5026f 100644 --- a/src/main/java/org/jabref/gui/externalfiles/ImportHandler.java +++ b/src/main/java/org/jabref/gui/externalfiles/ImportHandler.java @@ -194,6 +194,7 @@ public void importEntries(List entries) { } public void importCleanedEntries(List entries) { + entries = entries.stream().map(entry -> (BibEntry) entry.clone()).toList(); bibDatabaseContext.getDatabase().insertEntries(entries); generateKeys(entries); setAutomaticFields(entries); diff --git a/src/main/java/org/jabref/gui/maintable/MainTable.java b/src/main/java/org/jabref/gui/maintable/MainTable.java index 4a94fd56b5f..70285478536 100644 --- a/src/main/java/org/jabref/gui/maintable/MainTable.java +++ b/src/main/java/org/jabref/gui/maintable/MainTable.java @@ -82,6 +82,7 @@ public class MainTable extends TableView { private long lastKeyPressTime; private String columnSearchTerm; + private boolean citationMergeMode = false; public MainTable(MainTableDataModel model, LibraryTab libraryTab, @@ -249,11 +250,18 @@ public void listen(EntriesAddedEvent event) { } public void clearAndSelect(BibEntry bibEntry) { - getSelectionModel().clearSelection(); - findEntry(bibEntry).ifPresent(entry -> { - getSelectionModel().select(entry); - scrollTo(entry); - }); + // check if entries merged from citation relations tab + if (citationMergeMode) { + // keep original entry selected and reset citation merge mode + this.citationMergeMode = false; + } else { + // select new entry + getSelectionModel().clearSelection(); + findEntry(bibEntry).ifPresent(entry -> { + getSelectionModel().select(entry); + scrollTo(entry); + }); + } } private void scrollToNextMatchCategory() { @@ -492,4 +500,8 @@ private Optional findEntry(BibEntry entry) { .filter(viewModel -> viewModel.getEntry().equals(entry)) .findFirst(); } + + public void setCitationMergeMode(boolean citationMerge) { + this.citationMergeMode = citationMerge; + } } diff --git a/src/main/java/org/jabref/gui/mergeentries/MergeEntriesDialog.java b/src/main/java/org/jabref/gui/mergeentries/MergeEntriesDialog.java index 9613b457991..dc485757be0 100644 --- a/src/main/java/org/jabref/gui/mergeentries/MergeEntriesDialog.java +++ b/src/main/java/org/jabref/gui/mergeentries/MergeEntriesDialog.java @@ -23,6 +23,14 @@ public MergeEntriesDialog(BibEntry one, BibEntry two, GuiPreferences preferences init(); } + public MergeEntriesDialog(BibEntry one, BibEntry two, String leftHeader, String rightHeader, GuiPreferences preferences) { + threeWayMergeView = new ThreeWayMergeView(one, two, leftHeader, rightHeader, preferences); + this.one = one; + this.two = two; + + init(); + } + /** * Sets up the dialog */ diff --git a/src/main/resources/l10n/JabRef_en.properties b/src/main/resources/l10n/JabRef_en.properties index 0d980636e73..cd741f10878 100644 --- a/src/main/resources/l10n/JabRef_en.properties +++ b/src/main/resources/l10n/JabRef_en.properties @@ -2797,3 +2797,7 @@ Warning\:\ The\ selected\ directory\ is\ not\ a\ valid\ directory.=Warning: The Currently\ selected\ JStyle\:\ '%0' = Currently selected JStyle: '%0' Currently\ selected\ CSL\ Style\:\ '%0' = Currently selected CSL Style: '%0' Store\ url\ for\ downloaded\ file=Store url for downloaded file + +Compare\ with\ existing\ entry=Compare with existing entry +Library\ Entry=Library Entry +Citation\ Entry=Citation Entry