diff --git a/src/main/java/org/jabref/gui/DialogService.java b/src/main/java/org/jabref/gui/DialogService.java index 26cf46c00a1..dc70b364a18 100644 --- a/src/main/java/org/jabref/gui/DialogService.java +++ b/src/main/java/org/jabref/gui/DialogService.java @@ -178,6 +178,20 @@ boolean showConfirmationDialogWithOptOutAndWait(String title, String content, Optional showCustomButtonDialogAndWait(Alert.AlertType type, String title, String content, ButtonType... buttonTypes); + // CS427 Issue link: https://github.com/JabRef/jabref/issues/8296 + /** + * This will create and display a new dialog of the specified + * {@link Alert.AlertType} but with user defined buttons as optional + * {@link ButtonType}s. + * Moreover, the dialog contains a opt-out checkbox with the given text to support "Do not ask again"-behaviour. + * + * @return Optional with the pressed Button as ButtonType + */ + Optional showCustomButtonDialogWithOptOutAndWait(Alert.AlertType type, String title, String content, + String optOutMessage, Consumer optOutAction, + ButtonType... buttonTypes); + + /** * This will create and display a new dialog showing a custom {@link DialogPane} * and using custom {@link ButtonType}s. diff --git a/src/main/java/org/jabref/gui/JabRefDialogService.java b/src/main/java/org/jabref/gui/JabRefDialogService.java index a3bf7ea74f0..303ed6b703c 100644 --- a/src/main/java/org/jabref/gui/JabRefDialogService.java +++ b/src/main/java/org/jabref/gui/JabRefDialogService.java @@ -16,6 +16,7 @@ import javafx.print.PrinterJob; import javafx.scene.Group; import javafx.scene.Node; +import javafx.scene.control.Alert; import javafx.scene.control.Alert.AlertType; import javafx.scene.control.Button; import javafx.scene.control.ButtonBar; @@ -259,6 +260,15 @@ public Optional showCustomButtonDialogAndWait(AlertType type, String return alert.showAndWait(); } + @Override + public Optional showCustomButtonDialogWithOptOutAndWait(Alert.AlertType type, String title, String content, + String optOutMessage, Consumer optOutAction, + ButtonType... buttonTypes) { + FXDialog alert = createDialogWithOptOut(AlertType.CONFIRMATION, title, content, optOutMessage, optOutAction); + alert.getButtonTypes().setAll(buttonTypes); + return alert.showAndWait(); + } + @Override public Optional showCustomDialogAndWait(String title, DialogPane contentPane, ButtonType... buttonTypes) { diff --git a/src/main/java/org/jabref/gui/JabRefFrame.java b/src/main/java/org/jabref/gui/JabRefFrame.java index edb26429e3b..fb33cfab66d 100644 --- a/src/main/java/org/jabref/gui/JabRefFrame.java +++ b/src/main/java/org/jabref/gui/JabRefFrame.java @@ -397,8 +397,16 @@ public boolean quit() { final BibDatabaseContext context = libraryTab.getBibDatabaseContext(); if (context.hasEmptyEntries()) { - if (!confirmEmptyEntry(libraryTab, context)) { - return false; + if (prefs.getGeneralPreferences().shouldConfirmEmptyEntries()) { + Optional response = confirmEmptyEntry(libraryTab); + if (!deleteEmptyEntry(libraryTab, context, response)) { + return false; + } + } else if (prefs.getGeneralPreferences().shouldDeleteEmptyEntries()) { + Optional response = Optional.of(new ButtonType(Localization.lang("Delete empty entries"), ButtonBar.ButtonData.YES)); + if (!deleteEmptyEntry(libraryTab, context, response)) { + return false; + } } } @@ -1169,8 +1177,10 @@ private boolean confirmClose(LibraryTab libraryTab) { /** * Ask if the user really wants to remove any empty entries + * + * @return user response from the confirmation dialog */ - private Boolean confirmEmptyEntry(LibraryTab libraryTab, BibDatabaseContext context) { + private Optional confirmEmptyEntry(LibraryTab libraryTab) { String filename = libraryTab.getBibDatabaseContext() .getDatabasePath() .map(Path::toAbsolutePath) @@ -1181,11 +1191,22 @@ private Boolean confirmEmptyEntry(LibraryTab libraryTab, BibDatabaseContext cont ButtonType keepEmptyEntries = new ButtonType(Localization.lang("Keep empty entries"), ButtonBar.ButtonData.NO); ButtonType cancel = new ButtonType(Localization.lang("Return to JabRef"), ButtonBar.ButtonData.CANCEL_CLOSE); - Optional response = dialogService.showCustomButtonDialogAndWait(Alert.AlertType.CONFIRMATION, + Optional response = dialogService.showCustomButtonDialogWithOptOutAndWait(Alert.AlertType.CONFIRMATION, Localization.lang("Empty entries"), Localization.lang("Library '%0' has empty entries. Do you want to delete them?", filename), + Localization.lang("Do not ask again"), + optOut -> prefs.getGeneralPreferences().setConfirmEmptyEntries(!optOut), deleteEmptyEntries, keepEmptyEntries, cancel); - if (response.isPresent() && response.get().equals(deleteEmptyEntries)) { + if (response.isPresent() && response.get().getButtonData().equals(ButtonBar.ButtonData.NO)) { + prefs.getGeneralPreferences().setDeleteEmptyEntries(false); + } else if (response.isPresent() && response.get().getButtonData().equals(ButtonBar.ButtonData.YES)) { + prefs.getGeneralPreferences().setDeleteEmptyEntries(true); + } + return response; + } + + private Boolean deleteEmptyEntry(LibraryTab libraryTab, BibDatabaseContext context, Optional response) { + if (response.isPresent() && response.get().getButtonData() == ButtonBar.ButtonData.YES) { // The user wants to delete. try { for (BibEntry currentEntry : new ArrayList<>(context.getEntries())) { @@ -1206,7 +1227,7 @@ private Boolean confirmEmptyEntry(LibraryTab libraryTab, BibDatabaseContext cont // Save was cancelled or an error occurred. return false; } - return !response.get().equals(cancel); + return response.get().getButtonData() != ButtonBar.ButtonData.CANCEL_CLOSE; } private void closeTab(LibraryTab libraryTab) { @@ -1214,11 +1235,19 @@ private void closeTab(LibraryTab libraryTab) { if (libraryTab == null) { return; } - + prefs.getGeneralPreferences().setConfirmEmptyEntries(true); final BibDatabaseContext context = libraryTab.getBibDatabaseContext(); if (context.hasEmptyEntries()) { - if (!confirmEmptyEntry(libraryTab, context)) { - return; + if (prefs.getGeneralPreferences().shouldConfirmEmptyEntries()) { + Optional response = confirmEmptyEntry(libraryTab); + if (!deleteEmptyEntry(libraryTab, context, response)) { + return; + } + } else if (prefs.getGeneralPreferences().shouldDeleteEmptyEntries()) { + Optional response = Optional.of(new ButtonType(Localization.lang("Delete empty entries"), ButtonBar.ButtonData.YES)); + if (!deleteEmptyEntry(libraryTab, context, response)) { + return; + } } } diff --git a/src/main/java/org/jabref/preferences/GeneralPreferences.java b/src/main/java/org/jabref/preferences/GeneralPreferences.java index 4598c00e392..75c922fefc5 100644 --- a/src/main/java/org/jabref/preferences/GeneralPreferences.java +++ b/src/main/java/org/jabref/preferences/GeneralPreferences.java @@ -14,6 +14,8 @@ public class GeneralPreferences { private final ObjectProperty defaultBibDatabaseMode; private final BooleanProperty warnAboutDuplicatesInInspection; private final BooleanProperty confirmDelete; + private final BooleanProperty confirmEmptyEntries; + private final BooleanProperty deleteEmptyEntries; private final BooleanProperty memoryStickMode; private final BooleanProperty showAdvancedHints; @@ -22,12 +24,16 @@ public GeneralPreferences(Charset defaultEncoding, BibDatabaseMode defaultBibDatabaseMode, boolean warnAboutDuplicatesInInspection, boolean confirmDelete, + boolean confirmEmptyEntries, + boolean deleteEmptyEntries, boolean memoryStickMode, boolean showAdvancedHints) { this.defaultEncoding = new SimpleObjectProperty<>(defaultEncoding); this.defaultBibDatabaseMode = new SimpleObjectProperty<>(defaultBibDatabaseMode); this.warnAboutDuplicatesInInspection = new SimpleBooleanProperty(warnAboutDuplicatesInInspection); this.confirmDelete = new SimpleBooleanProperty(confirmDelete); + this.confirmEmptyEntries = new SimpleBooleanProperty(confirmEmptyEntries); + this.deleteEmptyEntries = new SimpleBooleanProperty(deleteEmptyEntries); this.memoryStickMode = new SimpleBooleanProperty(memoryStickMode); this.showAdvancedHints = new SimpleBooleanProperty(showAdvancedHints); @@ -80,6 +86,30 @@ public BooleanProperty confirmDeleteProperty() { public void setConfirmDelete(boolean confirmDelete) { this.confirmDelete.set(confirmDelete); } + + public boolean shouldConfirmEmptyEntries() { + return confirmEmptyEntries.get(); + } + + public BooleanProperty confirmEmptyEntriesProperty() { + return confirmEmptyEntries; + } + + public void setConfirmEmptyEntries(boolean confirmEmptyEntries) { + this.confirmEmptyEntries.set(confirmEmptyEntries); + } + + public boolean shouldDeleteEmptyEntries() { + return deleteEmptyEntries.get(); + } + + public BooleanProperty deleteEmptyEntriesProperty() { + return deleteEmptyEntries; + } + + public void setDeleteEmptyEntries(boolean deleteEmptyEntries) { + this.deleteEmptyEntries.set(deleteEmptyEntries); + } public boolean isMemoryStickMode() { return memoryStickMode.get(); diff --git a/src/main/java/org/jabref/preferences/JabRefPreferences.java b/src/main/java/org/jabref/preferences/JabRefPreferences.java index 295d3f137a1..45ac76ff46b 100644 --- a/src/main/java/org/jabref/preferences/JabRefPreferences.java +++ b/src/main/java/org/jabref/preferences/JabRefPreferences.java @@ -255,6 +255,8 @@ public class JabRefPreferences implements PreferencesService { public static final String DEFAULT_CITATION_KEY_PATTERN = "defaultBibtexKeyPattern"; public static final String UNWANTED_CITATION_KEY_CHARACTERS = "defaultUnwantedBibtexKeyCharacters"; public static final String CONFIRM_DELETE = "confirmDelete"; + public static final String CONFIRM_EMPTY_ENTRIES = "confirmEmptyEntries"; + public static final String DELETE_EMPTY_ENTRIES = "deleteEmptyEntries"; public static final String WARN_BEFORE_OVERWRITING_KEY = "warnBeforeOverwritingKey"; public static final String AVOID_OVERWRITING_KEY = "avoidOverwritingKey"; public static final String AUTOLINK_EXACT_KEY_ONLY = "autolinkExactKeyOnly"; @@ -626,6 +628,8 @@ private JabRefPreferences() { defaults.put(AVOID_OVERWRITING_KEY, Boolean.FALSE); defaults.put(WARN_BEFORE_OVERWRITING_KEY, Boolean.TRUE); defaults.put(CONFIRM_DELETE, Boolean.TRUE); + defaults.put(CONFIRM_EMPTY_ENTRIES, Boolean.TRUE); + defaults.put(DELETE_EMPTY_ENTRIES, Boolean.TRUE); defaults.put(DEFAULT_CITATION_KEY_PATTERN, "[auth][year]"); defaults.put(UNWANTED_CITATION_KEY_CHARACTERS, "-`สน:!;?^+"); defaults.put(DO_NOT_RESOLVE_STRINGS_FOR, StandardField.URL.getName()); @@ -1330,6 +1334,8 @@ public GeneralPreferences getGeneralPreferences() { getBoolean(BIBLATEX_DEFAULT_MODE) ? BibDatabaseMode.BIBLATEX : BibDatabaseMode.BIBTEX, getBoolean(WARN_ABOUT_DUPLICATES_IN_INSPECTION), getBoolean(CONFIRM_DELETE), + getBoolean(CONFIRM_EMPTY_ENTRIES), + getBoolean(DELETE_EMPTY_ENTRIES), getBoolean(MEMORY_STICK_MODE), getBoolean(SHOW_ADVANCED_HINTS)); @@ -1337,6 +1343,8 @@ public GeneralPreferences getGeneralPreferences() { EasyBind.listen(generalPreferences.defaultBibDatabaseModeProperty(), (obs, oldValue, newValue) -> putBoolean(BIBLATEX_DEFAULT_MODE, (newValue == BibDatabaseMode.BIBLATEX))); EasyBind.listen(generalPreferences.isWarnAboutDuplicatesInInspectionProperty(), (obs, oldValue, newValue) -> putBoolean(WARN_ABOUT_DUPLICATES_IN_INSPECTION, newValue)); EasyBind.listen(generalPreferences.confirmDeleteProperty(), (obs, oldValue, newValue) -> putBoolean(CONFIRM_DELETE, newValue)); + EasyBind.listen(generalPreferences.confirmEmptyEntriesProperty(), (obs, oldValue, newValue) -> putBoolean(CONFIRM_EMPTY_ENTRIES, newValue)); + EasyBind.listen(generalPreferences.deleteEmptyEntriesProperty(), (obs, oldValue, newValue) -> putBoolean(DELETE_EMPTY_ENTRIES, newValue)); EasyBind.listen(generalPreferences.memoryStickModeProperty(), (obs, oldValue, newValue) -> putBoolean(MEMORY_STICK_MODE, newValue)); EasyBind.listen(generalPreferences.showAdvancedHintsProperty(), (obs, oldValue, newValue) -> putBoolean(SHOW_ADVANCED_HINTS, newValue));