diff --git a/src/main/java/org/jabref/gui/DuplicateResolverDialog.java b/src/main/java/org/jabref/gui/DuplicateResolverDialog.java index 362333ebfa0..cb34df3d202 100644 --- a/src/main/java/org/jabref/gui/DuplicateResolverDialog.java +++ b/src/main/java/org/jabref/gui/DuplicateResolverDialog.java @@ -1,26 +1,20 @@ package org.jabref.gui; -import java.awt.BorderLayout; -import java.awt.event.WindowAdapter; -import java.awt.event.WindowEvent; +import javafx.scene.control.Button; +import javafx.scene.control.ButtonBar; +import javafx.scene.control.ButtonBar.ButtonData; +import javafx.scene.control.ButtonType; +import javafx.scene.layout.BorderPane; -import javax.swing.Box; -import javax.swing.JButton; -import javax.swing.JPanel; - -import javafx.scene.Scene; - -import org.jabref.gui.customjfx.CustomJFXPanel; +import org.jabref.gui.DuplicateResolverDialog.DuplicateResolverResult; import org.jabref.gui.help.HelpAction; -import org.jabref.gui.importer.ImportInspectionDialog; import org.jabref.gui.mergeentries.MergeEntries; -import org.jabref.gui.util.WindowLocation; +import org.jabref.gui.util.BaseDialog; import org.jabref.logic.help.HelpFile; import org.jabref.logic.l10n.Localization; import org.jabref.model.entry.BibEntry; -import org.jabref.preferences.JabRefPreferences; -public class DuplicateResolverDialog extends JabRefDialog { +public class DuplicateResolverDialog extends BaseDialog { public enum DuplicateResolverType { DUPLICATE_SEARCH, @@ -30,7 +24,6 @@ public enum DuplicateResolverType { } public enum DuplicateResolverResult { - NOT_CHOSEN, KEEP_BOTH, KEEP_LEFT, KEEP_RIGHT, @@ -39,108 +32,89 @@ public enum DuplicateResolverResult { BREAK } - JButton helpButton = new HelpAction(Localization.lang("Help"), HelpFile.FIND_DUPLICATES).getHelpButton(); - private final JButton cancel = new JButton(Localization.lang("Cancel")); - private final JButton merge = new JButton(Localization.lang("Keep merged entry only")); private final JabRefFrame frame; - private final JPanel options = new JPanel(); - private DuplicateResolverResult status = DuplicateResolverResult.NOT_CHOSEN; private MergeEntries me; public DuplicateResolverDialog(JabRefFrame frame, BibEntry one, BibEntry two, DuplicateResolverType type) { - super(Localization.lang("Possible duplicate entries"), true, DuplicateResolverDialog.class); this.frame = frame; - init(one, two, type); - } - - public DuplicateResolverDialog(ImportInspectionDialog dialog, BibEntry one, BibEntry two, - DuplicateResolverType type) { - super(dialog, Localization.lang("Possible duplicate entries"), true, DuplicateResolverDialog.class); - this.frame = dialog.getFrame(); + this.setTitle(Localization.lang("Possible duplicate entries")); init(one, two, type); } private void init(BibEntry one, BibEntry two, DuplicateResolverType type) { - JButton both; - JButton second; - JButton first; - JButton removeExact = null; - switch (type) { - case DUPLICATE_SEARCH: - first = new JButton(Localization.lang("Keep left")); - second = new JButton(Localization.lang("Keep right")); - both = new JButton(Localization.lang("Keep both")); - me = new MergeEntries(one, two, frame.getCurrentBasePanel().getBibDatabaseContext().getMode()); - break; - case INSPECTION: - first = new JButton(Localization.lang("Remove old entry")); - second = new JButton(Localization.lang("Remove entry from import")); - both = new JButton(Localization.lang("Keep both")); - me = new MergeEntries(one, two, Localization.lang("Old entry"), - Localization.lang("From import"), frame.getCurrentBasePanel().getBibDatabaseContext().getMode()); - break; - case DUPLICATE_SEARCH_WITH_EXACT: - first = new JButton(Localization.lang("Keep left")); - second = new JButton(Localization.lang("Keep right")); - both = new JButton(Localization.lang("Keep both")); - removeExact = new JButton(Localization.lang("Automatically remove exact duplicates")); - me = new MergeEntries(one, two, frame.getCurrentBasePanel().getBibDatabaseContext().getMode()); - break; - default: - first = new JButton(Localization.lang("Import and remove old entry")); - second = new JButton(Localization.lang("Do not import entry")); - both = new JButton(Localization.lang("Import and keep old entry")); - me = new MergeEntries(one, two, Localization.lang("Old entry"), - Localization.lang("From import"), frame.getCurrentBasePanel().getBibDatabaseContext().getMode()); - break; - } - if (removeExact != null) { - options.add(removeExact); + HelpAction helpCommand = new HelpAction(HelpFile.FIND_DUPLICATES); + ButtonType help = new ButtonType(Localization.lang("Help"), ButtonData.HELP); + + ButtonType cancel = ButtonType.CANCEL; + ButtonType merge = new ButtonType(Localization.lang("Keep merged entry only"), ButtonData.APPLY); + + ButtonBar options = new ButtonBar(); + ButtonType both; + ButtonType second; + ButtonType first; + ButtonType removeExact = new ButtonType(Localization.lang("Automatically remove exact duplicates"), ButtonData.APPLY); + boolean removeExactVisible = false; + + switch (type) { + case DUPLICATE_SEARCH: + first = new ButtonType(Localization.lang("Keep left"), ButtonData.APPLY); + second = new ButtonType(Localization.lang("Keep right"), ButtonData.APPLY); + both = new ButtonType(Localization.lang("Keep both"), ButtonData.APPLY); + me = new MergeEntries(one, two, frame.getCurrentBasePanel().getBibDatabaseContext().getMode()); + break; + case INSPECTION: + first = new ButtonType(Localization.lang("Remove old entry"), ButtonData.APPLY); + second = new ButtonType(Localization.lang("Remove entry from import"), ButtonData.APPLY); + both = new ButtonType(Localization.lang("Keep both"), ButtonData.APPLY); + me = new MergeEntries(one, two, Localization.lang("Old entry"), + Localization.lang("From import"), frame.getCurrentBasePanel().getBibDatabaseContext().getMode()); + break; + case DUPLICATE_SEARCH_WITH_EXACT: + first = new ButtonType(Localization.lang("Keep left"), ButtonData.APPLY); + second = new ButtonType(Localization.lang("Keep right"), ButtonData.APPLY); + both = new ButtonType(Localization.lang("Keep both"), ButtonData.APPLY); + + removeExactVisible = true; + + me = new MergeEntries(one, two, frame.getCurrentBasePanel().getBibDatabaseContext().getMode()); + break; + default: + first = new ButtonType(Localization.lang("Import and remove old entry"), ButtonData.APPLY); + second = new ButtonType(Localization.lang("Do not import entry"), ButtonData.APPLY); + both = new ButtonType(Localization.lang("Import and keep old entry"), ButtonData.APPLY); + me = new MergeEntries(one, two, Localization.lang("Old entry"), + Localization.lang("From import"), frame.getCurrentBasePanel().getBibDatabaseContext().getMode()); + break; } - options.add(first); - options.add(second); - options.add(both); - options.add(merge); - options.add(Box.createHorizontalStrut(5)); - options.add(cancel); - options.add(helpButton); - - first.addActionListener(e -> buttonPressed(DuplicateResolverResult.KEEP_LEFT)); - second.addActionListener(e -> buttonPressed(DuplicateResolverResult.KEEP_RIGHT)); - both.addActionListener(e -> buttonPressed(DuplicateResolverResult.KEEP_BOTH)); - merge.addActionListener(e -> buttonPressed(DuplicateResolverResult.KEEP_MERGE)); - if (removeExact != null) { - removeExact.addActionListener(e -> buttonPressed(DuplicateResolverResult.AUTOREMOVE_EXACT)); + if (removeExactVisible) { + this.getDialogPane().getButtonTypes().add(removeExact); } - cancel.addActionListener(e -> buttonPressed(DuplicateResolverResult.BREAK)); - addWindowListener(new WindowAdapter() { - @Override - public void windowClosing(WindowEvent e) { - buttonPressed(DuplicateResolverResult.BREAK); - } - }); + this.getDialogPane().getButtonTypes().addAll(first, second, both, merge, cancel, help); - getContentPane().add(CustomJFXPanel.wrap(new Scene(me))); - getContentPane().add(options, BorderLayout.SOUTH); - pack(); + BorderPane borderPane = new BorderPane(me); + borderPane.setBottom(options); - WindowLocation pw = new WindowLocation(this, JabRefPreferences.DUPLICATES_POS_X, - JabRefPreferences.DUPLICATES_POS_Y, JabRefPreferences.DUPLICATES_SIZE_X, - JabRefPreferences.DUPLICATES_SIZE_Y); - pw.displayWindowAtStoredLocation(); + this.setResultConverter(button -> { - both.requestFocus(); - } - - private void buttonPressed(DuplicateResolverResult result) { - status = result; - dispose(); - } + if (button.equals(first)) { + return DuplicateResolverResult.KEEP_LEFT; + } else if (button.equals(second)) { + return DuplicateResolverResult.KEEP_RIGHT; + } else if (button.equals(both)) { + return DuplicateResolverResult.KEEP_BOTH; + } else if (button.equals(merge)) { + return DuplicateResolverResult.KEEP_MERGE; + } else if (button.equals(removeExact)) { + return DuplicateResolverResult.AUTOREMOVE_EXACT; + } + return null; + }); - public DuplicateResolverResult getSelected() { - return status; + getDialogPane().setContent(borderPane); + Button helpButton = (Button) this.getDialogPane().lookupButton(help); + helpButton.setOnAction(evt -> helpCommand.getCommand().execute()); } public BibEntry getMergedEntry() { diff --git a/src/main/java/org/jabref/gui/DuplicateSearch.java b/src/main/java/org/jabref/gui/DuplicateSearch.java index 8331483d01f..476b3f70276 100644 --- a/src/main/java/org/jabref/gui/DuplicateSearch.java +++ b/src/main/java/org/jabref/gui/DuplicateSearch.java @@ -12,8 +12,6 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; -import javax.swing.SwingUtilities; - import org.jabref.Globals; import org.jabref.JabRefExecutorService; import org.jabref.gui.DuplicateResolverDialog.DuplicateResolverResult; @@ -37,15 +35,17 @@ public class DuplicateSearch extends SimpleCommand { private final AtomicBoolean libraryAnalyzed = new AtomicBoolean(); private final AtomicBoolean autoRemoveExactDuplicates = new AtomicBoolean(); private final AtomicInteger duplicateCount = new AtomicInteger(); + private final DialogService dialogService; - public DuplicateSearch(JabRefFrame frame) { + public DuplicateSearch(JabRefFrame frame, DialogService dialogService) { this.frame = frame; + this.dialogService = dialogService; } @Override public void execute() { BasePanel panel = frame.getCurrentBasePanel(); - panel.output(Localization.lang("Searching for duplicates...")); + dialogService.notify(Localization.lang("Searching for duplicates...")); List entries = panel.getDatabase().getEntries(); duplicates.clear(); @@ -57,8 +57,7 @@ public void execute() { return; } - JabRefExecutorService.INSTANCE - .executeInterruptableTask(() -> searchPossibleDuplicates(entries, panel.getBibDatabaseContext().getMode()), "DuplicateSearcher"); + JabRefExecutorService.INSTANCE.executeInterruptableTask(() -> searchPossibleDuplicates(entries, panel.getBibDatabaseContext().getMode()), "DuplicateSearcher"); BackgroundTask.wrap(this::verifyDuplicates) .onSuccess(this::handleDuplicates) .executeWith(Globals.TASK_EXECUTOR); @@ -124,13 +123,11 @@ private DuplicateSearchResult verifyDuplicates() { private void askResolveStrategy(DuplicateSearchResult result, BibEntry first, BibEntry second, DuplicateResolverType resolverType) { DuplicateResolverDialog dialog = new DuplicateResolverDialog(frame, first, second, resolverType); - dialog.setVisible(true); - dialog.dispose(); - DuplicateResolverResult resolverResult = dialog.getSelected(); + DuplicateResolverResult resolverResult = dialog.showAndWait().orElse(DuplicateResolverResult.BREAK); if ((resolverResult == DuplicateResolverResult.KEEP_LEFT) - || (resolverResult == DuplicateResolverResult.AUTOREMOVE_EXACT)) { + || (resolverResult == DuplicateResolverResult.AUTOREMOVE_EXACT)) { result.remove(second); if (resolverResult == DuplicateResolverResult.AUTOREMOVE_EXACT) { autoRemoveExactDuplicates.set(true); // Remember choice @@ -150,31 +147,30 @@ private void handleDuplicates(DuplicateSearchResult result) { return; } - SwingUtilities.invokeLater(() -> { - BasePanel panel = frame.getCurrentBasePanel(); - final NamedCompound compoundEdit = new NamedCompound(Localization.lang("duplicate removal")); - // Now, do the actual removal: - if (!result.getToRemove().isEmpty()) { - for (BibEntry entry : result.getToRemove()) { - panel.getDatabase().removeEntry(entry); - compoundEdit.addEdit(new UndoableRemoveEntry(panel.getDatabase(), entry, panel)); - } - panel.markBaseChanged(); + BasePanel panel = frame.getCurrentBasePanel(); + final NamedCompound compoundEdit = new NamedCompound(Localization.lang("duplicate removal")); + // Now, do the actual removal: + if (!result.getToRemove().isEmpty()) { + for (BibEntry entry : result.getToRemove()) { + panel.getDatabase().removeEntry(entry); + compoundEdit.addEdit(new UndoableRemoveEntry(panel.getDatabase(), entry, panel)); } - // and adding merged entries: - if (!result.getToAdd().isEmpty()) { - for (BibEntry entry : result.getToAdd()) { - panel.getDatabase().insertEntry(entry); - compoundEdit.addEdit(new UndoableInsertEntry(panel.getDatabase(), entry)); - } - panel.markBaseChanged(); + panel.markBaseChanged(); + } + // and adding merged entries: + if (!result.getToAdd().isEmpty()) { + for (BibEntry entry : result.getToAdd()) { + panel.getDatabase().insertEntry(entry); + compoundEdit.addEdit(new UndoableInsertEntry(panel.getDatabase(), entry)); } + panel.markBaseChanged(); + } + + dialogService.notify(Localization.lang("Duplicates found") + ": " + duplicateCount.get() + ' ' + + Localization.lang("pairs processed") + ": " + result.getDuplicateCount()); + compoundEdit.end(); + panel.getUndoManager().addEdit(compoundEdit); - panel.output(Localization.lang("Duplicates found") + ": " + duplicateCount.get() + ' ' - + Localization.lang("pairs processed") + ": " + result.getDuplicateCount()); - compoundEdit.end(); - panel.getUndoManager().addEdit(compoundEdit); - }); } /** diff --git a/src/main/java/org/jabref/gui/JabRefFrame.java b/src/main/java/org/jabref/gui/JabRefFrame.java index d8fd07cb6f2..b1872641c36 100644 --- a/src/main/java/org/jabref/gui/JabRefFrame.java +++ b/src/main/java/org/jabref/gui/JabRefFrame.java @@ -858,7 +858,7 @@ private MenuBar createMenu() { } quality.getItems().addAll( - factory.createMenuItem(StandardActions.FIND_DUPLICATES, new DuplicateSearch(this)), + factory.createMenuItem(StandardActions.FIND_DUPLICATES, new DuplicateSearch(this, dialogService)), factory.createMenuItem(StandardActions.MERGE_ENTRIES, new MergeEntriesAction(this)), new SeparatorMenuItem(), diff --git a/src/main/java/org/jabref/gui/bibtexkeypattern/ResolveDuplicateLabelDialog.java b/src/main/java/org/jabref/gui/bibtexkeypattern/ResolveDuplicateLabelDialog.java index 61434841141..68006125528 100644 --- a/src/main/java/org/jabref/gui/bibtexkeypattern/ResolveDuplicateLabelDialog.java +++ b/src/main/java/org/jabref/gui/bibtexkeypattern/ResolveDuplicateLabelDialog.java @@ -44,12 +44,11 @@ class ResolveDuplicateLabelDialog { private boolean okPressed; private boolean cancelPressed; - public ResolveDuplicateLabelDialog(BasePanel panel, String key, List entries) { diag = new JDialog((JFrame) null, Localization.lang("Duplicate BibTeX key"), true); FormBuilder b = FormBuilder.create().layout(new FormLayout( - "left:pref, 4dlu, fill:pref", "p")); + "left:pref, 4dlu, fill:pref", "p")); b.add(new JLabel(Localization.lang("Duplicate BibTeX key") + ": " + key)).xyw(1, 1, 3); b.getPanel().setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); @@ -59,7 +58,7 @@ public ResolveDuplicateLabelDialog(BasePanel panel, String key, List e JCheckBox cb = new JCheckBox(Localization.lang("Generate BibTeX key"), !first); b.appendRows("1dlu, p"); b.add(cb).xy(1, row); - PreviewPanel previewPanel = new PreviewPanel(null, null, Globals.getKeyPrefs(), Globals.prefs.getPreviewPreferences(), new FXDialogService(), ExternalFileTypes.getInstance()); + PreviewPanel previewPanel = new PreviewPanel(null, panel.getBibDatabaseContext(), Globals.getKeyPrefs(), Globals.prefs.getPreviewPreferences(), new FXDialogService(), ExternalFileTypes.getInstance()); previewPanel.setEntry(entry); JFXPanel container = CustomJFXPanel.wrap(new Scene(previewPanel)); container.setPreferredSize(new Dimension(800, 90)); @@ -86,8 +85,8 @@ public ResolveDuplicateLabelDialog(BasePanel panel, String key, List e diag.pack(); ok.addActionListener(e -> { - okPressed = true; - diag.dispose(); + okPressed = true; + diag.dispose(); }); ignore.addActionListener(e -> diag.dispose()); diff --git a/src/main/java/org/jabref/gui/importer/ImportInspectionDialog.java b/src/main/java/org/jabref/gui/importer/ImportInspectionDialog.java index ae3dc7c5b4b..2ce86d731c4 100644 --- a/src/main/java/org/jabref/gui/importer/ImportInspectionDialog.java +++ b/src/main/java/org/jabref/gui/importer/ImportInspectionDialog.java @@ -695,14 +695,12 @@ public void actionPerformed(ActionEvent event) { // is indicated by the entry's group hit status: if (entry.isGroupHit()) { - boolean continuePressed = - DefaultTaskExecutor.runInJavaFXThread(() -> - frame.getDialogService().showConfirmationDialogWithOptOutAndWait(Localization.lang("Duplicates found"), - Localization.lang("There are possible duplicates (marked with an icon) that haven't been resolved. Continue?"), - Localization.lang("Continue"), - Localization.lang("Cancel"), - Localization.lang("Disable this confirmation dialog"), - optOut -> Globals.prefs.putBoolean(JabRefPreferences.WARN_ABOUT_DUPLICATES_IN_INSPECTION, !optOut))); + boolean continuePressed = DefaultTaskExecutor.runInJavaFXThread(() -> frame.getDialogService().showConfirmationDialogWithOptOutAndWait(Localization.lang("Duplicates found"), + Localization.lang("There are possible duplicates (marked with an icon) that haven't been resolved. Continue?"), + Localization.lang("Continue"), + Localization.lang("Cancel"), + Localization.lang("Disable this confirmation dialog"), + optOut -> Globals.prefs.putBoolean(JabRefPreferences.WARN_ABOUT_DUPLICATES_IN_INSPECTION, !optOut))); if (!continuePressed) { return; @@ -1053,12 +1051,13 @@ public void mousePressed(MouseEvent e) { if (other.isPresent()) { // This will be true if the duplicate is in the existing // database. - DuplicateResolverDialog diag = new DuplicateResolverDialog(ImportInspectionDialog.this, other.get(), + DuplicateResolverDialog diag = new DuplicateResolverDialog(getFrame(), other.get(), first, DuplicateResolverDialog.DuplicateResolverType.INSPECTION); - diag.setLocationRelativeTo(ImportInspectionDialog.this); - diag.setVisible(true); + + DuplicateResolverResult result = diag.showAndWait().orElse(DuplicateResolverResult.BREAK); + ImportInspectionDialog.this.toFront(); - if (diag.getSelected() == DuplicateResolverResult.KEEP_LEFT) { + if (result == DuplicateResolverResult.KEEP_LEFT) { // Remove old entry. Or... add it to a list of entries // to be deleted. We only delete // it after Ok is clicked. @@ -1074,7 +1073,7 @@ public void mousePressed(MouseEvent e) { entries.getReadWriteLock().writeLock().unlock(); } - } else if (diag.getSelected() == DuplicateResolverResult.KEEP_RIGHT) { + } else if (result == DuplicateResolverResult.KEEP_RIGHT) { // Remove the entry from the import inspection dialog. entries.getReadWriteLock().writeLock().lock(); try { @@ -1082,7 +1081,7 @@ public void mousePressed(MouseEvent e) { } finally { entries.getReadWriteLock().writeLock().unlock(); } - } else if (diag.getSelected() == DuplicateResolverResult.KEEP_BOTH) { + } else if (result == DuplicateResolverResult.KEEP_BOTH) { // Do nothing. entries.getReadWriteLock().writeLock().lock(); try { @@ -1090,7 +1089,7 @@ public void mousePressed(MouseEvent e) { } finally { entries.getReadWriteLock().writeLock().unlock(); } - } else if (diag.getSelected() == DuplicateResolverResult.KEEP_MERGE) { + } else if (result == DuplicateResolverResult.KEEP_MERGE) { // Remove old entry. Or... add it to a list of entries // to be deleted. We only delete // it after Ok is clicked. @@ -1114,12 +1113,11 @@ public void mousePressed(MouseEvent e) { // Check if the duplicate is of another entry in the import: other = internalDuplicate(entries, first); if (other.isPresent()) { - DuplicateResolverDialog diag = new DuplicateResolverDialog(ImportInspectionDialog.this, first, + DuplicateResolverDialog diag = new DuplicateResolverDialog(getFrame(), first, other.get(), DuplicateResolverDialog.DuplicateResolverType.DUPLICATE_SEARCH); - diag.setLocationRelativeTo(ImportInspectionDialog.this); - diag.setVisible(true); + ImportInspectionDialog.this.toFront(); - DuplicateResolverResult answer = diag.getSelected(); + DuplicateResolverResult answer = diag.showAndWait().get(); if (answer == DuplicateResolverResult.KEEP_LEFT) { entries.remove(other.get()); first.setGroupHit(false);