diff --git a/CHANGELOG.md b/CHANGELOG.md index bb9108a09a2..ced3f440ff3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv ### Changed +- We separated the "Clean up entries" dialog into three tabs for clarity [#13819](https://github.com/JabRef/jabref/issues/13819) - Ctrl + Shift + L now opens the terminal in the active library directory. [#14130](https://github.com/JabRef/jabref/issues/14130) ### Fixed diff --git a/jabgui/src/main/java/org/jabref/gui/cleanup/CleanupAction.java b/jabgui/src/main/java/org/jabref/gui/cleanup/CleanupAction.java index b623ae39bd7..ad0281df5db 100644 --- a/jabgui/src/main/java/org/jabref/gui/cleanup/CleanupAction.java +++ b/jabgui/src/main/java/org/jabref/gui/cleanup/CleanupAction.java @@ -1,32 +1,16 @@ package org.jabref.gui.cleanup; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; import java.util.function.Supplier; -import java.util.stream.Collectors; import javax.swing.undo.UndoManager; -import javafx.application.Platform; - import org.jabref.gui.DialogService; import org.jabref.gui.LibraryTab; import org.jabref.gui.StateManager; import org.jabref.gui.actions.ActionHelper; import org.jabref.gui.actions.SimpleCommand; -import org.jabref.gui.undo.NamedCompoundEdit; -import org.jabref.gui.undo.UndoableFieldChange; -import org.jabref.logic.JabRefException; -import org.jabref.logic.cleanup.CleanupPreferences; -import org.jabref.logic.cleanup.CleanupWorker; -import org.jabref.logic.l10n.Localization; import org.jabref.logic.preferences.CliPreferences; -import org.jabref.logic.util.BackgroundTask; import org.jabref.logic.util.TaskExecutor; -import org.jabref.model.FieldChange; -import org.jabref.model.database.BibDatabaseContext; -import org.jabref.model.entry.BibEntry; public class CleanupAction extends SimpleCommand { @@ -36,10 +20,6 @@ public class CleanupAction extends SimpleCommand { private final StateManager stateManager; private final TaskExecutor taskExecutor; private final UndoManager undoManager; - private final List failures; - - private boolean isCanceled; - private int modifiedEntriesCount; public CleanupAction(Supplier tabSupplier, CliPreferences preferences, @@ -53,7 +33,6 @@ public CleanupAction(Supplier tabSupplier, this.stateManager = stateManager; this.taskExecutor = taskExecutor; this.undoManager = undoManager; - this.failures = new ArrayList<>(); this.executable.bind(ActionHelper.needsEntriesSelected(stateManager)); } @@ -64,119 +43,16 @@ public void execute() { return; } - if (stateManager.getSelectedEntries().isEmpty()) { // None selected. Inform the user to select entries first. - dialogService.showInformationDialogAndWait(Localization.lang("Cleanup entry"), Localization.lang("First select entries to clean up.")); - return; - } - - isCanceled = false; - modifiedEntriesCount = 0; - CleanupDialog cleanupDialog = new CleanupDialog( stateManager.getActiveDatabase().get(), - preferences.getCleanupPreferences(), - preferences.getFilePreferences() - ); - - Optional chosenPreset = dialogService.showCustomDialogAndWait(cleanupDialog); - - chosenPreset.ifPresent(preset -> { - if (preset.isActive(CleanupPreferences.CleanupStep.RENAME_PDF) && preferences.getAutoLinkPreferences().shouldAskAutoNamingPdfs()) { - boolean confirmed = dialogService.showConfirmationDialogWithOptOutAndWait(Localization.lang("Autogenerate PDF Names"), - Localization.lang("Auto-generating PDF-Names does not support undo. Continue?"), - Localization.lang("Autogenerate PDF Names"), - Localization.lang("Cancel"), - Localization.lang("Do not ask again"), - optOut -> preferences.getAutoLinkPreferences().setAskAutoNamingPdfs(!optOut)); - if (!confirmed) { - isCanceled = true; - return; - } - } - - preferences.getCleanupPreferences().setActiveJobs(preset.getActiveJobs()); - preferences.getCleanupPreferences().setFieldFormatterCleanups(preset.getFieldFormatterCleanups()); - - BackgroundTask.wrap(() -> cleanup(stateManager.getActiveDatabase().get(), preset)) - .onSuccess(result -> showResults()) - .onFailure(dialogService::showErrorDialogAndWait) - .executeWith(taskExecutor); - }); - } - - /** - * Runs the cleanup on the entry and records the change. - * - * @return true iff entry was modified - */ - private boolean doCleanup(BibDatabaseContext databaseContext, CleanupPreferences preset, BibEntry entry, NamedCompoundEdit compoundEdit) { - // Create and run cleaner - CleanupWorker cleaner = new CleanupWorker( - databaseContext, - preferences.getFilePreferences(), - preferences.getTimestampPreferences() + preferences, + dialogService, + stateManager, + undoManager, + tabSupplier, + taskExecutor ); - List changes = cleaner.cleanup(preset, entry); - - // Register undo action - for (FieldChange change : changes) { - compoundEdit.addEdit(new UndoableFieldChange(change)); - } - - failures.addAll(cleaner.getFailures()); - - return !changes.isEmpty(); - } - - private void showResults() { - if (isCanceled) { - return; - } - - if (modifiedEntriesCount > 0) { - tabSupplier.get().markBaseChanged(); - } - - if (modifiedEntriesCount == 0) { - dialogService.notify(Localization.lang("No entry needed a clean up")); - } else if (modifiedEntriesCount == 1) { - dialogService.notify(Localization.lang("One entry needed a clean up")); - } else { - dialogService.notify(Localization.lang("%0 entries needed a clean up", Integer.toString(modifiedEntriesCount))); - } - } - - private void cleanup(BibDatabaseContext databaseContext, CleanupPreferences cleanupPreferences) { - this.failures.clear(); - - // undo granularity is on set of all entries - NamedCompoundEdit compoundEdit = new NamedCompoundEdit(Localization.lang("Clean up entries")); - - for (BibEntry entry : List.copyOf(stateManager.getSelectedEntries())) { - if (doCleanup(databaseContext, cleanupPreferences, entry, compoundEdit)) { - modifiedEntriesCount++; - } - } - - compoundEdit.end(); - - if (compoundEdit.hasEdits()) { - undoManager.addEdit(compoundEdit); - } - - if (!failures.isEmpty()) { - showFailures(failures); - } - } - - private void showFailures(List failures) { - String message = failures.stream() - .map(exception -> "- " + exception.getLocalizedMessage()) - .collect(Collectors.joining("\n")); - - Platform.runLater(() -> - dialogService.showErrorDialogAndWait(Localization.lang("File Move Errors"), message) - ); + dialogService.showCustomDialogAndWait(cleanupDialog); } } diff --git a/jabgui/src/main/java/org/jabref/gui/cleanup/CleanupDialog.java b/jabgui/src/main/java/org/jabref/gui/cleanup/CleanupDialog.java index 752b9309dc6..134714be4a4 100644 --- a/jabgui/src/main/java/org/jabref/gui/cleanup/CleanupDialog.java +++ b/jabgui/src/main/java/org/jabref/gui/cleanup/CleanupDialog.java @@ -1,35 +1,87 @@ package org.jabref.gui.cleanup; -import javafx.scene.control.ButtonType; -import javafx.scene.control.ScrollPane; +import java.util.List; +import java.util.function.Supplier; +import javax.swing.undo.UndoManager; + +import javafx.fxml.FXML; +import javafx.scene.control.Tab; +import javafx.scene.control.TabPane; + +import org.jabref.gui.DialogService; +import org.jabref.gui.LibraryTab; +import org.jabref.gui.StateManager; import org.jabref.gui.util.BaseDialog; import org.jabref.logic.FilePreferences; import org.jabref.logic.cleanup.CleanupPreferences; import org.jabref.logic.l10n.Localization; +import org.jabref.logic.preferences.CliPreferences; +import org.jabref.logic.util.TaskExecutor; import org.jabref.model.database.BibDatabaseContext; +import org.jabref.model.entry.BibEntry; + +import com.airhacks.afterburner.views.ViewLoader; + +public class CleanupDialog extends BaseDialog { + + @FXML private TabPane tabPane; + + private final CleanupDialogViewModel viewModel; + + // Constructor for multiple-entry cleanup + public CleanupDialog(BibDatabaseContext databaseContext, + CliPreferences preferences, + DialogService dialogService, + StateManager stateManager, + UndoManager undoManager, + Supplier tabSupplier, + TaskExecutor taskExecutor) { + + this.viewModel = new CleanupDialogViewModel( + databaseContext, preferences, dialogService, + stateManager, undoManager, tabSupplier, taskExecutor + ); + + init(databaseContext, preferences); + } -public class CleanupDialog extends BaseDialog { - public CleanupDialog(BibDatabaseContext databaseContext, CleanupPreferences initialPreset, FilePreferences filePreferences) { + // Constructor for single-entry cleanup + public CleanupDialog(BibEntry targetEntry, + BibDatabaseContext databaseContext, + CliPreferences preferences, + DialogService dialogService, + StateManager stateManager, + UndoManager undoManager) { + + this.viewModel = new CleanupDialogViewModel( + databaseContext, preferences, dialogService, + stateManager, undoManager, null, null + ); + + viewModel.setTargetEntries(List.of(targetEntry)); + + init(databaseContext, preferences); + } + + private void init(BibDatabaseContext databaseContext, CliPreferences preferences) { setTitle(Localization.lang("Clean up entries")); - getDialogPane().setPrefSize(600, 650); - getDialogPane().getButtonTypes().setAll(ButtonType.OK, ButtonType.CANCEL); - - CleanupPresetPanel presetPanel = new CleanupPresetPanel(databaseContext, initialPreset, filePreferences); - - // placing the content of the presetPanel in a scroll pane - ScrollPane scrollPane = new ScrollPane(); - scrollPane.setFitToWidth(true); - scrollPane.setFitToHeight(true); - scrollPane.setContent(presetPanel); - - getDialogPane().setContent(scrollPane); - setResultConverter(button -> { - if (button == ButtonType.OK) { - return presetPanel.getCleanupPreset(); - } else { - return null; - } - }); + + ViewLoader.view(this) + .load() + .setAsDialogPane(this); + + CleanupPreferences initialPreset = preferences.getCleanupPreferences(); + FilePreferences filePreferences = preferences.getFilePreferences(); + + CleanupSingleFieldPanel singleFieldPanel = new CleanupSingleFieldPanel(initialPreset, viewModel); + CleanupFileRelatedPanel fileRelatedPanel = new CleanupFileRelatedPanel(databaseContext, initialPreset, filePreferences, viewModel); + CleanupMultiFieldPanel multiFieldPanel = new CleanupMultiFieldPanel(initialPreset, viewModel); + + tabPane.getTabs().setAll( + new Tab(Localization.lang("Single field"), singleFieldPanel), + new Tab(Localization.lang("File-related"), fileRelatedPanel), + new Tab(Localization.lang("Multi-field"), multiFieldPanel) + ); } } diff --git a/jabgui/src/main/java/org/jabref/gui/cleanup/CleanupDialogViewModel.java b/jabgui/src/main/java/org/jabref/gui/cleanup/CleanupDialogViewModel.java new file mode 100644 index 00000000000..5da5cb8ccba --- /dev/null +++ b/jabgui/src/main/java/org/jabref/gui/cleanup/CleanupDialogViewModel.java @@ -0,0 +1,201 @@ +package org.jabref.gui.cleanup; + +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.List; +import java.util.Objects; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +import javax.swing.undo.UndoManager; + +import javafx.application.Platform; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; + +import org.jabref.gui.AbstractViewModel; +import org.jabref.gui.DialogService; +import org.jabref.gui.LibraryTab; +import org.jabref.gui.StateManager; +import org.jabref.gui.undo.NamedCompoundEdit; +import org.jabref.gui.undo.UndoableFieldChange; +import org.jabref.logic.JabRefException; +import org.jabref.logic.cleanup.CleanupPreferences; +import org.jabref.logic.cleanup.CleanupTabSelection; +import org.jabref.logic.cleanup.CleanupWorker; +import org.jabref.logic.l10n.Localization; +import org.jabref.logic.preferences.CliPreferences; +import org.jabref.logic.util.BackgroundTask; +import org.jabref.logic.util.TaskExecutor; +import org.jabref.model.FieldChange; +import org.jabref.model.database.BibDatabaseContext; +import org.jabref.model.entry.BibEntry; + +import org.jspecify.annotations.NonNull; + +public class CleanupDialogViewModel extends AbstractViewModel { + + private final BibDatabaseContext databaseContext; + private final CliPreferences preferences; + private final DialogService dialogService; + private final StateManager stateManager; + private final UndoManager undoManager; + private final Supplier tabSupplier; + private final TaskExecutor taskExecutor; + + private final ObservableList targetEntries = FXCollections.observableArrayList(); + private int modifiedEntriesCount; + + public CleanupDialogViewModel( + @NonNull BibDatabaseContext databaseContext, + @NonNull CliPreferences preferences, + @NonNull DialogService dialogService, + @NonNull StateManager stateManager, + @NonNull UndoManager undoManager, + Supplier tabSupplier, + TaskExecutor taskExecutor + ) { + this.databaseContext = databaseContext; + this.preferences = preferences; + this.dialogService = dialogService; + this.stateManager = stateManager; + this.undoManager = undoManager; + + this.tabSupplier = tabSupplier; // can be null + this.taskExecutor = taskExecutor; // can be null + } + + public void setTargetEntries(List entries) { + targetEntries.setAll(Objects.requireNonNullElse(entries, List.of())); + } + + public void apply(CleanupTabSelection selectedTab) { + if (stateManager.getActiveDatabase().isEmpty()) { + return; + } + + List entriesToProcess = targetEntries.isEmpty() ? List.copyOf(stateManager.getSelectedEntries()) : targetEntries; + + if (entriesToProcess.isEmpty()) { // None selected. Inform the user to select entries first. + dialogService.showInformationDialogAndWait(Localization.lang("Clean up entry"), Localization.lang("First select entries to clean up.") + ); + return; + } + + modifiedEntriesCount = 0; + + if (selectedTab.selectedJobs().contains(CleanupPreferences.CleanupStep.RENAME_PDF) && preferences.getAutoLinkPreferences().shouldAskAutoNamingPdfs()) { + boolean confirmed = dialogService.showConfirmationDialogWithOptOutAndWait( + Localization.lang("Autogenerate PDF Names"), + Localization.lang("Auto-generating PDF-Names does not support undo. Continue?"), + Localization.lang("Autogenerate PDF Names"), + Localization.lang("Cancel"), + Localization.lang("Do not ask again"), + optOut -> preferences.getAutoLinkPreferences().setAskAutoNamingPdfs(!optOut) + ); + if (!confirmed) { + return; + } + } + + CleanupPreferences updatedPreferences = selectedTab.updatePreferences(preferences.getCleanupPreferences()); + + if (selectedTab.isJobTab()) { + preferences.getCleanupPreferences().setActiveJobs(updatedPreferences.getActiveJobs()); + } + + if (selectedTab.isFormatterTab()) { + preferences.getCleanupPreferences().setFieldFormatterCleanups(updatedPreferences.getFieldFormatterCleanups()); + } + + CleanupPreferences cleanupPreset = new CleanupPreferences(EnumSet.copyOf(selectedTab.selectedJobs())); + selectedTab.formatters().ifPresent(cleanupPreset::setFieldFormatterCleanups); + + if (taskExecutor != null) { + BackgroundTask.wrap(() -> cleanup(cleanupPreset, entriesToProcess)) + .onSuccess(result -> showResults()) + .onFailure(dialogService::showErrorDialogAndWait) + .executeWith(taskExecutor); + } else { + cleanup(cleanupPreset, entriesToProcess); + } + } + + /** + * Runs the cleanup on the entry and records the change. + * + * @return true iff entry was modified + */ + private boolean doCleanup(CleanupPreferences preset, + BibEntry entry, + NamedCompoundEdit compoundEdit, + List failures) { + CleanupWorker cleaner = new CleanupWorker( + databaseContext, + preferences.getFilePreferences(), + preferences.getTimestampPreferences() + ); + + List changes = cleaner.cleanup(preset, entry); + + for (FieldChange change : changes) { + compoundEdit.addEdit(new UndoableFieldChange(change)); + } + + failures.addAll(cleaner.getFailures()); + + return !changes.isEmpty(); + } + + private void showResults() { + if (modifiedEntriesCount > 0 && tabSupplier != null) { + tabSupplier.get().markBaseChanged(); + } + + String message = switch (modifiedEntriesCount) { + case 0 -> + Localization.lang("No entry needed a clean up"); + case 1 -> + Localization.lang("One entry needed a clean up"); + default -> + Localization.lang("%0 entries needed a clean up", Integer.toString(modifiedEntriesCount)); + }; + + dialogService.notify(message); + } + + private void cleanup(CleanupPreferences cleanupPreferences, List entries) { + List failures = new ArrayList<>(); + + String editName = Localization.lang("Clean up entry(s)"); + // undo granularity is on a set of all entries + NamedCompoundEdit compoundEdit = new NamedCompoundEdit(editName); + + for (BibEntry entry : entries) { + if (doCleanup(cleanupPreferences, entry, compoundEdit, failures)) { + modifiedEntriesCount++; + } + } + + compoundEdit.end(); + + if (compoundEdit.hasEdits()) { + undoManager.addEdit(compoundEdit); + } + + if (!failures.isEmpty()) { + showFailures(failures); + } + } + + private void showFailures(List failures) { + String message = failures.stream() + .map(exception -> "- " + exception.getLocalizedMessage()) + .collect(Collectors.joining("\n")); + + Platform.runLater(() -> + dialogService.showErrorDialogAndWait(Localization.lang("File Move Errors"), message) + ); + } +} + diff --git a/jabgui/src/main/java/org/jabref/gui/cleanup/CleanupFileRelatedPanel.java b/jabgui/src/main/java/org/jabref/gui/cleanup/CleanupFileRelatedPanel.java new file mode 100644 index 00000000000..4a62ba78a6a --- /dev/null +++ b/jabgui/src/main/java/org/jabref/gui/cleanup/CleanupFileRelatedPanel.java @@ -0,0 +1,88 @@ +package org.jabref.gui.cleanup; + +import java.nio.file.Path; +import java.util.EnumSet; +import java.util.Optional; + +import javafx.fxml.FXML; +import javafx.scene.control.CheckBox; +import javafx.scene.control.Label; +import javafx.scene.layout.VBox; + +import org.jabref.logic.FilePreferences; +import org.jabref.logic.cleanup.CleanupPreferences; +import org.jabref.logic.cleanup.CleanupTabSelection; +import org.jabref.logic.l10n.Localization; +import org.jabref.model.database.BibDatabaseContext; +import org.jabref.model.entry.field.StandardField; + +import com.airhacks.afterburner.views.ViewLoader; +import org.jspecify.annotations.NonNull; + +public class CleanupFileRelatedPanel extends VBox { + + @FXML private Label cleanupRenamePdfLabel; + + @FXML private CheckBox cleanupMovePdf; + @FXML private CheckBox cleanupMakePathsRelative; + @FXML private CheckBox cleanupRenamePdf; + @FXML private CheckBox cleanupRenamePdfOnlyRelativePaths; + @FXML private CheckBox cleanupDeletedFiles; + @FXML private CheckBox cleanupUpgradeExternalLinks; + + private final CleanupFileViewModel viewModel; + private final CleanupDialogViewModel dialogViewModel; + + public CleanupFileRelatedPanel(@NonNull BibDatabaseContext databaseContext, + @NonNull CleanupPreferences cleanupPreferences, + @NonNull FilePreferences filePreferences, + @NonNull CleanupDialogViewModel dialogViewModel) { + + this.dialogViewModel = dialogViewModel; + this.viewModel = new CleanupFileViewModel(cleanupPreferences); + + ViewLoader.view(this) + .root(this) + .load(); + + init(databaseContext, filePreferences); + bindProperties(); + } + + private void init(BibDatabaseContext databaseContext, FilePreferences filePreferences) { + Optional firstExistingDir = databaseContext.getFirstExistingFileDir(filePreferences); + if (firstExistingDir.isPresent()) { + cleanupMovePdf.setText(Localization.lang("Move linked files to default file directory %0", firstExistingDir.get().toString())); + } else { + cleanupMovePdf.setText(Localization.lang("Move linked files to default file directory %0", "...")); + viewModel.movePdfEnabled.set(false); + viewModel.movePdfSelected.set(false); + } + + cleanupRenamePdfOnlyRelativePaths.disableProperty().bind(cleanupRenamePdf.selectedProperty().not()); + + cleanupUpgradeExternalLinks.setText(Localization.lang("Upgrade external PDF/PS links to use the '%0' field.", StandardField.FILE.getName())); + + String currentPattern = Localization.lang("Filename format pattern (from preferences)") + .concat(filePreferences.getFileNamePattern()); + cleanupRenamePdfLabel.setText(currentPattern); + } + + private void bindProperties() { + cleanupMovePdf.selectedProperty().bindBidirectional(viewModel.movePdfSelected); + cleanupMovePdf.disableProperty().bind(viewModel.movePdfEnabled.not()); + cleanupMakePathsRelative.selectedProperty().bindBidirectional(viewModel.makePathsRelativeSelected); + cleanupRenamePdf.selectedProperty().bindBidirectional(viewModel.renamePdfSelected); + cleanupRenamePdfOnlyRelativePaths.selectedProperty().bindBidirectional(viewModel.renamePdfOnlyRelativeSelected); + cleanupDeletedFiles.selectedProperty().bindBidirectional(viewModel.deleteFilesSelected); + cleanupUpgradeExternalLinks.selectedProperty().bindBidirectional(viewModel.upgradeLinksSelected); + } + + @FXML + private void onApply() { + EnumSet selectedJobs = viewModel.getSelectedJobs(); + CleanupTabSelection selectedTab = CleanupTabSelection.ofJobs(CleanupFileViewModel.FILE_RELATED_JOBS, selectedJobs); + dialogViewModel.apply(selectedTab); + getScene().getWindow().hide(); + } +} diff --git a/jabgui/src/main/java/org/jabref/gui/cleanup/CleanupFileViewModel.java b/jabgui/src/main/java/org/jabref/gui/cleanup/CleanupFileViewModel.java new file mode 100644 index 00000000000..91023ab1d3b --- /dev/null +++ b/jabgui/src/main/java/org/jabref/gui/cleanup/CleanupFileViewModel.java @@ -0,0 +1,68 @@ +package org.jabref.gui.cleanup; + +import java.util.EnumSet; + +import javafx.beans.property.BooleanProperty; +import javafx.beans.property.SimpleBooleanProperty; + +import org.jabref.logic.cleanup.CleanupPreferences; + +public class CleanupFileViewModel { + + public static final EnumSet FILE_RELATED_JOBS = EnumSet.of( + CleanupPreferences.CleanupStep.MOVE_PDF, + CleanupPreferences.CleanupStep.MAKE_PATHS_RELATIVE, + CleanupPreferences.CleanupStep.RENAME_PDF, + CleanupPreferences.CleanupStep.RENAME_PDF_ONLY_RELATIVE_PATHS, + CleanupPreferences.CleanupStep.CLEAN_UP_UPGRADE_EXTERNAL_LINKS, + CleanupPreferences.CleanupStep.CLEAN_UP_DELETED_LINKED_FILES + ); + + public final BooleanProperty movePdfSelected = new SimpleBooleanProperty(); + public final BooleanProperty makePathsRelativeSelected = new SimpleBooleanProperty(); + public final BooleanProperty renamePdfSelected = new SimpleBooleanProperty(); + public final BooleanProperty renamePdfOnlyRelativeSelected = new SimpleBooleanProperty(); + public final BooleanProperty upgradeLinksSelected = new SimpleBooleanProperty(); + public final BooleanProperty deleteFilesSelected = new SimpleBooleanProperty(); + + public final BooleanProperty movePdfEnabled = new SimpleBooleanProperty(true); + + public CleanupFileViewModel(CleanupPreferences preferences) { + movePdfSelected.set(preferences.isActive(CleanupPreferences.CleanupStep.MOVE_PDF)); + makePathsRelativeSelected.set(preferences.isActive(CleanupPreferences.CleanupStep.MAKE_PATHS_RELATIVE)); + renamePdfSelected.set(preferences.isActive(CleanupPreferences.CleanupStep.RENAME_PDF)); + renamePdfOnlyRelativeSelected.set(preferences.isActive(CleanupPreferences.CleanupStep.RENAME_PDF_ONLY_RELATIVE_PATHS)); + upgradeLinksSelected.set(preferences.isActive(CleanupPreferences.CleanupStep.CLEAN_UP_UPGRADE_EXTERNAL_LINKS)); + deleteFilesSelected.set(preferences.isActive(CleanupPreferences.CleanupStep.CLEAN_UP_DELETED_LINKED_FILES)); + + renamePdfSelected.addListener((obs, oldVal, newVal) -> { + if (!newVal) { + renamePdfOnlyRelativeSelected.set(false); + } + }); + } + + public EnumSet getSelectedJobs() { + EnumSet activeJobs = EnumSet.noneOf(CleanupPreferences.CleanupStep.class); + if (movePdfSelected.get()) { + activeJobs.add(CleanupPreferences.CleanupStep.MOVE_PDF); + } + if (makePathsRelativeSelected.get()) { + activeJobs.add(CleanupPreferences.CleanupStep.MAKE_PATHS_RELATIVE); + } + if (renamePdfSelected.get()) { + if (renamePdfOnlyRelativeSelected.get()) { + activeJobs.add(CleanupPreferences.CleanupStep.RENAME_PDF_ONLY_RELATIVE_PATHS); + } else { + activeJobs.add(CleanupPreferences.CleanupStep.RENAME_PDF); + } + } + if (upgradeLinksSelected.get()) { + activeJobs.add(CleanupPreferences.CleanupStep.CLEAN_UP_UPGRADE_EXTERNAL_LINKS); + } + if (deleteFilesSelected.get()) { + activeJobs.add(CleanupPreferences.CleanupStep.CLEAN_UP_DELETED_LINKED_FILES); + } + return activeJobs; + } +} diff --git a/jabgui/src/main/java/org/jabref/gui/cleanup/CleanupMultiFieldPanel.java b/jabgui/src/main/java/org/jabref/gui/cleanup/CleanupMultiFieldPanel.java new file mode 100644 index 00000000000..e126ee50a40 --- /dev/null +++ b/jabgui/src/main/java/org/jabref/gui/cleanup/CleanupMultiFieldPanel.java @@ -0,0 +1,57 @@ +package org.jabref.gui.cleanup; + +import java.util.EnumSet; + +import javafx.fxml.FXML; +import javafx.scene.control.CheckBox; +import javafx.scene.layout.VBox; + +import org.jabref.logic.cleanup.CleanupPreferences; +import org.jabref.logic.cleanup.CleanupTabSelection; + +import com.airhacks.afterburner.views.ViewLoader; +import org.jspecify.annotations.NonNull; + +public class CleanupMultiFieldPanel extends VBox { + @FXML private CheckBox cleanupDoi; + @FXML private CheckBox cleanupEprint; + @FXML private CheckBox cleanupUrl; + @FXML private CheckBox cleanupBibLaTeX; + @FXML private CheckBox cleanupBibTeX; + @FXML private CheckBox cleanupTimestampToCreationDate; + @FXML private CheckBox cleanupTimestampToModificationDate; + + private final CleanupMultiFieldViewModel viewModel; + private final CleanupDialogViewModel dialogViewModel; + + public CleanupMultiFieldPanel(@NonNull CleanupPreferences cleanupPreferences, + @NonNull CleanupDialogViewModel dialogViewModel) { + + this.dialogViewModel = dialogViewModel; + this.viewModel = new CleanupMultiFieldViewModel(cleanupPreferences); + + ViewLoader.view(this) + .root(this) + .load(); + + bindProperties(); + } + + private void bindProperties() { + cleanupDoi.selectedProperty().bindBidirectional(viewModel.doiSelected); + cleanupEprint.selectedProperty().bindBidirectional(viewModel.eprintSelected); + cleanupUrl.selectedProperty().bindBidirectional(viewModel.urlSelected); + cleanupBibTeX.selectedProperty().bindBidirectional(viewModel.bibTexSelected); + cleanupBibLaTeX.selectedProperty().bindBidirectional(viewModel.bibLaTexSelected); + cleanupTimestampToCreationDate.selectedProperty().bindBidirectional(viewModel.timestampToCreationSelected); + cleanupTimestampToModificationDate.selectedProperty().bindBidirectional(viewModel.timestampToModificationSelected); + } + + @FXML + private void onApply() { + EnumSet selectedJobs = viewModel.getSelectedJobs(); + CleanupTabSelection selectedTab = CleanupTabSelection.ofJobs(CleanupMultiFieldViewModel.MULTI_FIELD_JOBS, selectedJobs); + dialogViewModel.apply(selectedTab); + getScene().getWindow().hide(); + } +} diff --git a/jabgui/src/main/java/org/jabref/gui/cleanup/CleanupMultiFieldViewModel.java b/jabgui/src/main/java/org/jabref/gui/cleanup/CleanupMultiFieldViewModel.java new file mode 100644 index 00000000000..b401066f4e1 --- /dev/null +++ b/jabgui/src/main/java/org/jabref/gui/cleanup/CleanupMultiFieldViewModel.java @@ -0,0 +1,94 @@ +package org.jabref.gui.cleanup; + +import java.util.EnumSet; + +import javafx.beans.property.BooleanProperty; +import javafx.beans.property.SimpleBooleanProperty; + +import org.jabref.logic.cleanup.CleanupPreferences; + +public class CleanupMultiFieldViewModel { + + public static final EnumSet MULTI_FIELD_JOBS = EnumSet.of( + CleanupPreferences.CleanupStep.CLEAN_UP_DOI, + CleanupPreferences.CleanupStep.CLEANUP_EPRINT, + CleanupPreferences.CleanupStep.CLEAN_UP_URL, + CleanupPreferences.CleanupStep.CONVERT_TO_BIBLATEX, + CleanupPreferences.CleanupStep.CONVERT_TO_BIBTEX, + CleanupPreferences.CleanupStep.CONVERT_TIMESTAMP_TO_CREATIONDATE, + CleanupPreferences.CleanupStep.CONVERT_TIMESTAMP_TO_MODIFICATIONDATE + ); + + public final BooleanProperty doiSelected = new SimpleBooleanProperty(); + public final BooleanProperty eprintSelected = new SimpleBooleanProperty(); + public final BooleanProperty urlSelected = new SimpleBooleanProperty(); + public final BooleanProperty bibTexSelected = new SimpleBooleanProperty(); + public final BooleanProperty bibLaTexSelected = new SimpleBooleanProperty(); + public final BooleanProperty timestampToCreationSelected = new SimpleBooleanProperty(); + public final BooleanProperty timestampToModificationSelected = new SimpleBooleanProperty(); + + public CleanupMultiFieldViewModel(CleanupPreferences preferences) { + doiSelected.set(preferences.isActive(CleanupPreferences.CleanupStep.CLEAN_UP_DOI)); + eprintSelected.set(preferences.isActive(CleanupPreferences.CleanupStep.CLEANUP_EPRINT)); + urlSelected.set(preferences.isActive(CleanupPreferences.CleanupStep.CLEAN_UP_URL)); + bibTexSelected.set(preferences.isActive(CleanupPreferences.CleanupStep.CONVERT_TO_BIBTEX)); + bibLaTexSelected.set(preferences.isActive(CleanupPreferences.CleanupStep.CONVERT_TO_BIBLATEX)); + timestampToCreationSelected.set(preferences.isActive(CleanupPreferences.CleanupStep.CONVERT_TIMESTAMP_TO_CREATIONDATE)); + timestampToModificationSelected.set(preferences.isActive(CleanupPreferences.CleanupStep.CONVERT_TIMESTAMP_TO_MODIFICATIONDATE)); + + bibTexSelected.addListener( + (obs, oldVal, newVal) -> { + if (newVal) { + bibLaTexSelected.set(false); + } + } + ); + bibLaTexSelected.addListener( + (obs, oldVal, newVal) -> { + if (newVal) { + bibTexSelected.set(false); + } + } + ); + timestampToCreationSelected.addListener( + (obs, oldVal, newVal) -> { + if (newVal) { + timestampToModificationSelected.set(false); + } + } + ); + timestampToModificationSelected.addListener( + (obs, oldVal, newVal) -> { + if (newVal) { + timestampToCreationSelected.set(false); + } + } + ); + } + + public EnumSet getSelectedJobs() { + EnumSet activeJobs = EnumSet.noneOf(CleanupPreferences.CleanupStep.class); + if (doiSelected.get()) { + activeJobs.add(CleanupPreferences.CleanupStep.CLEAN_UP_DOI); + } + if (eprintSelected.get()) { + activeJobs.add(CleanupPreferences.CleanupStep.CLEANUP_EPRINT); + } + if (urlSelected.get()) { + activeJobs.add(CleanupPreferences.CleanupStep.CLEAN_UP_URL); + } + if (bibTexSelected.get()) { + activeJobs.add(CleanupPreferences.CleanupStep.CONVERT_TO_BIBTEX); + } + if (bibLaTexSelected.get()) { + activeJobs.add(CleanupPreferences.CleanupStep.CONVERT_TO_BIBLATEX); + } + if (timestampToCreationSelected.get()) { + activeJobs.add(CleanupPreferences.CleanupStep.CONVERT_TIMESTAMP_TO_CREATIONDATE); + } + if (timestampToModificationSelected.get()) { + activeJobs.add(CleanupPreferences.CleanupStep.CONVERT_TIMESTAMP_TO_MODIFICATIONDATE); + } + return activeJobs; + } +} diff --git a/jabgui/src/main/java/org/jabref/gui/cleanup/CleanupPresetPanel.java b/jabgui/src/main/java/org/jabref/gui/cleanup/CleanupPresetPanel.java deleted file mode 100644 index c8c1cae125e..00000000000 --- a/jabgui/src/main/java/org/jabref/gui/cleanup/CleanupPresetPanel.java +++ /dev/null @@ -1,175 +0,0 @@ -package org.jabref.gui.cleanup; - -import java.nio.file.Path; -import java.util.EnumSet; -import java.util.Optional; - -import javafx.collections.FXCollections; -import javafx.fxml.FXML; -import javafx.scene.control.CheckBox; -import javafx.scene.control.Label; -import javafx.scene.layout.VBox; - -import org.jabref.gui.commonfxcontrols.FieldFormatterCleanupsPanel; -import org.jabref.logic.FilePreferences; -import org.jabref.logic.cleanup.CleanupPreferences; -import org.jabref.logic.cleanup.FieldFormatterCleanups; -import org.jabref.logic.l10n.Localization; -import org.jabref.model.database.BibDatabaseContext; -import org.jabref.model.entry.field.FieldTextMapper; -import org.jabref.model.entry.field.StandardField; - -import com.airhacks.afterburner.views.ViewLoader; -import org.jspecify.annotations.NonNull; - -public class CleanupPresetPanel extends VBox { - - private final BibDatabaseContext databaseContext; - @FXML private Label cleanupRenamePDFLabel; - @FXML private CheckBox cleanUpDOI; - @FXML private CheckBox cleanUpEprint; - @FXML private CheckBox cleanUpURL; - @FXML private CheckBox cleanUpMovePDF; - @FXML private CheckBox cleanUpMakePathsRelative; - @FXML private CheckBox cleanUpRenamePDF; - @FXML private CheckBox cleanUpRenamePDFonlyRelativePaths; - @FXML private CheckBox cleanUpDeletedFiles; - @FXML private CheckBox cleanUpUpgradeExternalLinks; - @FXML private CheckBox cleanUpBiblatex; - @FXML private CheckBox cleanUpBibtex; - @FXML private CheckBox cleanUpTimestampToCreationDate; - @FXML private CheckBox cleanUpTimestampToModificationDate; - @FXML private FieldFormatterCleanupsPanel formatterCleanupsPanel; - - public CleanupPresetPanel(@NonNull BibDatabaseContext databaseContext, - CleanupPreferences cleanupPreferences, - FilePreferences filePreferences) { - this.databaseContext = databaseContext; - - ViewLoader.view(this) - .root(this) - .load(); - - init(cleanupPreferences, filePreferences); - } - - private void init(CleanupPreferences cleanupPreferences, FilePreferences filePreferences) { - Optional firstExistingDir = databaseContext.getFirstExistingFileDir(filePreferences); - if (firstExistingDir.isPresent()) { - cleanUpMovePDF.setText(Localization.lang("Move linked files to default file directory %0", firstExistingDir.get().toString())); - } else { - cleanUpMovePDF.setText(Localization.lang("Move linked files to default file directory %0", "...")); - - // Since the directory does not exist, we cannot move it to there. So, this option is not checked - regardless of the presets stored in the preferences. - cleanUpMovePDF.setDisable(true); - cleanUpMovePDF.setSelected(false); - } - - cleanUpRenamePDFonlyRelativePaths.disableProperty().bind(cleanUpRenamePDF.selectedProperty().not()); - - cleanUpUpgradeExternalLinks.setText(Localization.lang("Upgrade external PDF/PS links to use the '%0' field.", FieldTextMapper.getDisplayName(StandardField.FILE))); - - String currentPattern = Localization.lang("Filename format pattern (from preferences)") - .concat(filePreferences.getFileNamePattern()); - cleanupRenamePDFLabel.setText(currentPattern); - - cleanUpBibtex.selectedProperty().addListener( - (observable, oldValue, newValue) -> { - if (newValue) { - cleanUpBiblatex.selectedProperty().setValue(false); - } - }); - cleanUpBiblatex.selectedProperty().addListener( - (observable, oldValue, newValue) -> { - if (newValue) { - cleanUpBibtex.selectedProperty().setValue(false); - } - }); - - cleanUpTimestampToCreationDate.selectedProperty().addListener( - (observable, oldValue, newValue) -> { - if (newValue) { - cleanUpTimestampToModificationDate.selectedProperty().setValue(false); - } - }); - cleanUpTimestampToModificationDate.selectedProperty().addListener( - (observable, oldValue, newValue) -> { - if (newValue) { - cleanUpTimestampToCreationDate.selectedProperty().setValue(false); - } - }); - updateDisplay(cleanupPreferences); - } - - private void updateDisplay(CleanupPreferences preset) { - cleanUpDOI.setSelected(preset.isActive(CleanupPreferences.CleanupStep.CLEAN_UP_DOI)); - cleanUpEprint.setSelected(preset.isActive(CleanupPreferences.CleanupStep.CLEANUP_EPRINT)); - cleanUpURL.setSelected(preset.isActive(CleanupPreferences.CleanupStep.CLEAN_UP_URL)); - if (!cleanUpMovePDF.isDisabled()) { - cleanUpMovePDF.setSelected(preset.isActive(CleanupPreferences.CleanupStep.MOVE_PDF)); - } - cleanUpMakePathsRelative.setSelected(preset.isActive(CleanupPreferences.CleanupStep.MAKE_PATHS_RELATIVE)); - cleanUpRenamePDF.setSelected(preset.isActive(CleanupPreferences.CleanupStep.RENAME_PDF)); - cleanUpRenamePDFonlyRelativePaths.setSelected(preset.isActive(CleanupPreferences.CleanupStep.RENAME_PDF_ONLY_RELATIVE_PATHS)); - cleanUpUpgradeExternalLinks.setSelected(preset.isActive(CleanupPreferences.CleanupStep.CLEAN_UP_UPGRADE_EXTERNAL_LINKS)); - cleanUpDeletedFiles.setSelected(preset.isActive(CleanupPreferences.CleanupStep.CLEAN_UP_DELETED_LINKED_FILES)); - cleanUpBiblatex.setSelected(preset.isActive(CleanupPreferences.CleanupStep.CONVERT_TO_BIBLATEX)); - cleanUpBibtex.setSelected(preset.isActive(CleanupPreferences.CleanupStep.CONVERT_TO_BIBTEX)); - cleanUpTimestampToCreationDate.setSelected(preset.isActive(CleanupPreferences.CleanupStep.CONVERT_TIMESTAMP_TO_CREATIONDATE)); - cleanUpTimestampToModificationDate.setSelected(preset.isActive(CleanupPreferences.CleanupStep.CONVERT_TIMESTAMP_TO_MODIFICATIONDATE)); - cleanUpTimestampToModificationDate.setSelected(preset.isActive(CleanupPreferences.CleanupStep.DO_NOT_CONVERT_TIMESTAMP)); - formatterCleanupsPanel.cleanupsDisableProperty().setValue(!preset.getFieldFormatterCleanups().isEnabled()); - formatterCleanupsPanel.cleanupsProperty().setValue(FXCollections.observableArrayList(preset.getFieldFormatterCleanups().getConfiguredActions())); - } - - public CleanupPreferences getCleanupPreset() { - EnumSet activeJobs = EnumSet.noneOf(CleanupPreferences.CleanupStep.class); - - if (cleanUpMovePDF.isSelected()) { - activeJobs.add(CleanupPreferences.CleanupStep.MOVE_PDF); - } - if (cleanUpDOI.isSelected()) { - activeJobs.add(CleanupPreferences.CleanupStep.CLEAN_UP_DOI); - } - if (cleanUpEprint.isSelected()) { - activeJobs.add(CleanupPreferences.CleanupStep.CLEANUP_EPRINT); - } - if (cleanUpURL.isSelected()) { - activeJobs.add(CleanupPreferences.CleanupStep.CLEAN_UP_URL); - } - if (cleanUpMakePathsRelative.isSelected()) { - activeJobs.add(CleanupPreferences.CleanupStep.MAKE_PATHS_RELATIVE); - } - if (cleanUpRenamePDF.isSelected()) { - if (cleanUpRenamePDFonlyRelativePaths.isSelected()) { - activeJobs.add(CleanupPreferences.CleanupStep.RENAME_PDF_ONLY_RELATIVE_PATHS); - } else { - activeJobs.add(CleanupPreferences.CleanupStep.RENAME_PDF); - } - } - if (cleanUpUpgradeExternalLinks.isSelected()) { - activeJobs.add(CleanupPreferences.CleanupStep.CLEAN_UP_UPGRADE_EXTERNAL_LINKS); - } - if (cleanUpDeletedFiles.isSelected()) { - activeJobs.add(CleanupPreferences.CleanupStep.CLEAN_UP_DELETED_LINKED_FILES); - } - if (cleanUpBiblatex.isSelected()) { - activeJobs.add(CleanupPreferences.CleanupStep.CONVERT_TO_BIBLATEX); - } - if (cleanUpBibtex.isSelected()) { - activeJobs.add(CleanupPreferences.CleanupStep.CONVERT_TO_BIBTEX); - } - if (cleanUpTimestampToCreationDate.isSelected()) { - activeJobs.add(CleanupPreferences.CleanupStep.CONVERT_TIMESTAMP_TO_CREATIONDATE); - } - if (cleanUpTimestampToModificationDate.isSelected()) { - activeJobs.add(CleanupPreferences.CleanupStep.CONVERT_TIMESTAMP_TO_MODIFICATIONDATE); - } - - activeJobs.add(CleanupPreferences.CleanupStep.FIX_FILE_LINKS); - - return new CleanupPreferences(activeJobs, new FieldFormatterCleanups( - !formatterCleanupsPanel.cleanupsDisableProperty().getValue(), - formatterCleanupsPanel.cleanupsProperty())); - } -} diff --git a/jabgui/src/main/java/org/jabref/gui/cleanup/CleanupSingleAction.java b/jabgui/src/main/java/org/jabref/gui/cleanup/CleanupSingleAction.java index ccec9065541..d2834cfd935 100644 --- a/jabgui/src/main/java/org/jabref/gui/cleanup/CleanupSingleAction.java +++ b/jabgui/src/main/java/org/jabref/gui/cleanup/CleanupSingleAction.java @@ -1,25 +1,12 @@ package org.jabref.gui.cleanup; -import java.util.List; -import java.util.Optional; - import javax.swing.undo.UndoManager; -import javafx.application.Platform; - import org.jabref.gui.DialogService; import org.jabref.gui.StateManager; import org.jabref.gui.actions.ActionHelper; import org.jabref.gui.actions.SimpleCommand; -import org.jabref.gui.undo.NamedCompoundEdit; -import org.jabref.gui.undo.UndoableFieldChange; -import org.jabref.logic.JabRefException; -import org.jabref.logic.cleanup.CleanupPreferences; -import org.jabref.logic.cleanup.CleanupWorker; -import org.jabref.logic.l10n.Localization; import org.jabref.logic.preferences.CliPreferences; -import org.jabref.model.FieldChange; -import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.entry.BibEntry; public class CleanupSingleAction extends SimpleCommand { @@ -30,10 +17,11 @@ public class CleanupSingleAction extends SimpleCommand { private final BibEntry entry; private final UndoManager undoManager; - private boolean isCanceled; - private int modifiedEntriesCount; - - public CleanupSingleAction(BibEntry entry, CliPreferences preferences, DialogService dialogService, StateManager stateManager, UndoManager undoManager) { + public CleanupSingleAction(BibEntry entry, + CliPreferences preferences, + DialogService dialogService, + StateManager stateManager, + UndoManager undoManager) { this.entry = entry; this.preferences = preferences; this.dialogService = dialogService; @@ -45,79 +33,19 @@ public CleanupSingleAction(BibEntry entry, CliPreferences preferences, DialogSer @Override public void execute() { - isCanceled = false; + if (stateManager.getActiveDatabase().isEmpty()) { + return; + } CleanupDialog cleanupDialog = new CleanupDialog( + entry, stateManager.getActiveDatabase().get(), - preferences.getCleanupPreferences(), - preferences.getFilePreferences() - ); - - Optional chosenPreset = dialogService.showCustomDialogAndWait(cleanupDialog); - - chosenPreset.ifPresent(preset -> { - if (preset.isActive(CleanupPreferences.CleanupStep.RENAME_PDF) && preferences.getAutoLinkPreferences().shouldAskAutoNamingPdfs()) { - boolean confirmed = dialogService.showConfirmationDialogWithOptOutAndWait(Localization.lang("Autogenerate PDF Names"), - Localization.lang("Auto-generating PDF-Names does not support undo. Continue?"), - Localization.lang("Autogenerate PDF Names"), - Localization.lang("Cancel"), - Localization.lang("Do not ask again"), - optOut -> preferences.getAutoLinkPreferences().setAskAutoNamingPdfs(!optOut)); - if (!confirmed) { - isCanceled = true; - return; - } - } - - preferences.getCleanupPreferences().setActiveJobs(preset.getActiveJobs()); - preferences.getCleanupPreferences().setFieldFormatterCleanups(preset.getFieldFormatterCleanups()); - - cleanup(stateManager.getActiveDatabase().get(), preset); - }); - } - - /** - * Runs the cleanup on the entry and records the change. - */ - private void doCleanup(BibDatabaseContext databaseContext, CleanupPreferences preset, BibEntry entry, NamedCompoundEdit compoundEdit) { - // Create and run cleaner - CleanupWorker cleaner = new CleanupWorker( - databaseContext, - preferences.getFilePreferences(), - preferences.getTimestampPreferences() + preferences, + dialogService, + stateManager, + undoManager ); - List changes = cleaner.cleanup(preset, entry); - - // Register undo action - for (FieldChange change : changes) { - compoundEdit.addEdit(new UndoableFieldChange(change)); - } - - if (!cleaner.getFailures().isEmpty()) { - this.showFailures(cleaner.getFailures()); - } - } - - private void cleanup(BibDatabaseContext databaseContext, CleanupPreferences cleanupPreferences) { - // undo granularity is on entry level - NamedCompoundEdit compoundEdit = new NamedCompoundEdit(Localization.lang("Cleanup entry")); - - doCleanup(databaseContext, cleanupPreferences, entry, compoundEdit); - - compoundEdit.end(); - if (compoundEdit.hasEdits()) { - undoManager.addEdit(compoundEdit); - } - } - - private void showFailures(List failures) { - StringBuilder sb = new StringBuilder(); - for (JabRefException exception : failures) { - sb.append("- ").append(exception.getLocalizedMessage()).append("\n"); - } - Platform.runLater(() -> - dialogService.showErrorDialogAndWait(Localization.lang("File Move Errors"), sb.toString()) - ); + dialogService.showCustomDialogAndWait(cleanupDialog); } } diff --git a/jabgui/src/main/java/org/jabref/gui/cleanup/CleanupSingleFieldPanel.java b/jabgui/src/main/java/org/jabref/gui/cleanup/CleanupSingleFieldPanel.java new file mode 100644 index 00000000000..cbb26482fd1 --- /dev/null +++ b/jabgui/src/main/java/org/jabref/gui/cleanup/CleanupSingleFieldPanel.java @@ -0,0 +1,46 @@ +package org.jabref.gui.cleanup; + +import javafx.fxml.FXML; +import javafx.scene.layout.VBox; + +import org.jabref.gui.commonfxcontrols.FieldFormatterCleanupsPanel; +import org.jabref.logic.cleanup.CleanupPreferences; +import org.jabref.logic.cleanup.CleanupTabSelection; +import org.jabref.logic.cleanup.FieldFormatterCleanups; + +import com.airhacks.afterburner.views.ViewLoader; +import org.jspecify.annotations.NonNull; + +public class CleanupSingleFieldPanel extends VBox { + + @FXML private FieldFormatterCleanupsPanel formatterCleanupsPanel; + + private final CleanupSingleFieldViewModel viewModel; + private final CleanupDialogViewModel dialogViewModel; + + public CleanupSingleFieldPanel(@NonNull CleanupPreferences cleanupPreferences, + @NonNull CleanupDialogViewModel dialogViewModel) { + + this.dialogViewModel = dialogViewModel; + this.viewModel = new CleanupSingleFieldViewModel(cleanupPreferences.getFieldFormatterCleanups()); + + ViewLoader.view(this) + .root(this) + .load(); + + bindProperties(); + } + + private void bindProperties() { + formatterCleanupsPanel.cleanupsDisableProperty().bind(viewModel.cleanupsEnabled.not()); + formatterCleanupsPanel.cleanupsProperty().bindBidirectional(viewModel.cleanups); + } + + @FXML + private void onApply() { + FieldFormatterCleanups selectedFormatters = viewModel.getSelectedFormatters(); + CleanupTabSelection selectedTab = CleanupTabSelection.ofFormatters(selectedFormatters); + dialogViewModel.apply(selectedTab); + getScene().getWindow().hide(); + } +} diff --git a/jabgui/src/main/java/org/jabref/gui/cleanup/CleanupSingleFieldViewModel.java b/jabgui/src/main/java/org/jabref/gui/cleanup/CleanupSingleFieldViewModel.java new file mode 100644 index 00000000000..f41770cb31e --- /dev/null +++ b/jabgui/src/main/java/org/jabref/gui/cleanup/CleanupSingleFieldViewModel.java @@ -0,0 +1,24 @@ +package org.jabref.gui.cleanup; + +import javafx.beans.property.BooleanProperty; +import javafx.beans.property.ListProperty; +import javafx.beans.property.SimpleBooleanProperty; +import javafx.beans.property.SimpleListProperty; +import javafx.collections.FXCollections; + +import org.jabref.logic.cleanup.FieldFormatterCleanup; +import org.jabref.logic.cleanup.FieldFormatterCleanups; + +public class CleanupSingleFieldViewModel { + public final BooleanProperty cleanupsEnabled = new SimpleBooleanProperty(true); + public final ListProperty cleanups = new SimpleListProperty<>(FXCollections.observableArrayList()); + + public CleanupSingleFieldViewModel(FieldFormatterCleanups initialCleanups) { + cleanupsEnabled.set(initialCleanups.isEnabled()); + cleanups.setAll(initialCleanups.getConfiguredActions()); + } + + public FieldFormatterCleanups getSelectedFormatters() { + return new FieldFormatterCleanups(cleanupsEnabled.get(), cleanups.get()); + } +} diff --git a/jabgui/src/main/resources/org/jabref/gui/cleanup/CleanupDialog.fxml b/jabgui/src/main/resources/org/jabref/gui/cleanup/CleanupDialog.fxml new file mode 100644 index 00000000000..9243c5ee63e --- /dev/null +++ b/jabgui/src/main/resources/org/jabref/gui/cleanup/CleanupDialog.fxml @@ -0,0 +1,12 @@ + + + + + + + + + + diff --git a/jabgui/src/main/resources/org/jabref/gui/cleanup/CleanupFileRelatedPanel.fxml b/jabgui/src/main/resources/org/jabref/gui/cleanup/CleanupFileRelatedPanel.fxml new file mode 100644 index 00000000000..b9c42f66dd6 --- /dev/null +++ b/jabgui/src/main/resources/org/jabref/gui/cleanup/CleanupFileRelatedPanel.fxml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + +