11package org .jabref .gui .cleanup ;
22
3+ import java .util .ArrayList ;
4+ import java .util .List ;
5+ import java .util .Optional ;
36import java .util .function .Supplier ;
7+ import java .util .stream .Collectors ;
48
59import javax .swing .undo .UndoManager ;
610
11+ import javafx .application .Platform ;
12+
713import org .jabref .gui .DialogService ;
814import org .jabref .gui .LibraryTab ;
915import org .jabref .gui .StateManager ;
1016import org .jabref .gui .actions .ActionHelper ;
1117import org .jabref .gui .actions .SimpleCommand ;
18+ import org .jabref .gui .undo .NamedCompoundEdit ;
19+ import org .jabref .gui .undo .UndoableFieldChange ;
20+ import org .jabref .logic .JabRefException ;
21+ import org .jabref .logic .cleanup .CleanupPreferences ;
22+ import org .jabref .logic .cleanup .CleanupWorker ;
23+ import org .jabref .logic .l10n .Localization ;
1224import org .jabref .logic .preferences .CliPreferences ;
25+ import org .jabref .logic .util .BackgroundTask ;
1326import org .jabref .logic .util .TaskExecutor ;
27+ import org .jabref .model .FieldChange ;
28+ import org .jabref .model .database .BibDatabaseContext ;
29+ import org .jabref .model .entry .BibEntry ;
1430
1531public class CleanupAction extends SimpleCommand {
1632
@@ -20,6 +36,10 @@ public class CleanupAction extends SimpleCommand {
2036 private final StateManager stateManager ;
2137 private final TaskExecutor taskExecutor ;
2238 private final UndoManager undoManager ;
39+ private final List <JabRefException > failures ;
40+
41+ private boolean isCanceled ;
42+ private int modifiedEntriesCount ;
2343
2444 public CleanupAction (Supplier <LibraryTab > tabSupplier ,
2545 CliPreferences preferences ,
@@ -33,6 +53,7 @@ public CleanupAction(Supplier<LibraryTab> tabSupplier,
3353 this .stateManager = stateManager ;
3454 this .taskExecutor = taskExecutor ;
3555 this .undoManager = undoManager ;
56+ this .failures = new ArrayList <>();
3657
3758 this .executable .bind (ActionHelper .needsEntriesSelected (stateManager ));
3859 }
@@ -43,16 +64,119 @@ public void execute() {
4364 return ;
4465 }
4566
67+ if (stateManager .getSelectedEntries ().isEmpty ()) { // None selected. Inform the user to select entries first.
68+ dialogService .showInformationDialogAndWait (Localization .lang ("Cleanup entry" ), Localization .lang ("First select entries to clean up." ));
69+ return ;
70+ }
71+
72+ isCanceled = false ;
73+ modifiedEntriesCount = 0 ;
74+
4675 CleanupDialog cleanupDialog = new CleanupDialog (
47- tabSupplier ,
4876 stateManager .getActiveDatabase ().get (),
49- preferences ,
50- dialogService ,
51- stateManager ,
52- taskExecutor ,
53- undoManager
77+ preferences .getCleanupPreferences (),
78+ preferences .getFilePreferences ()
79+ );
80+
81+ Optional <CleanupPreferences > chosenPreset = dialogService .showCustomDialogAndWait (cleanupDialog );
82+
83+ chosenPreset .ifPresent (preset -> {
84+ if (preset .isActive (CleanupPreferences .CleanupStep .RENAME_PDF ) && preferences .getAutoLinkPreferences ().shouldAskAutoNamingPdfs ()) {
85+ boolean confirmed = dialogService .showConfirmationDialogWithOptOutAndWait (Localization .lang ("Autogenerate PDF Names" ),
86+ Localization .lang ("Auto-generating PDF-Names does not support undo. Continue?" ),
87+ Localization .lang ("Autogenerate PDF Names" ),
88+ Localization .lang ("Cancel" ),
89+ Localization .lang ("Do not ask again" ),
90+ optOut -> preferences .getAutoLinkPreferences ().setAskAutoNamingPdfs (!optOut ));
91+ if (!confirmed ) {
92+ isCanceled = true ;
93+ return ;
94+ }
95+ }
96+
97+ preferences .getCleanupPreferences ().setActiveJobs (preset .getActiveJobs ());
98+ preferences .getCleanupPreferences ().setFieldFormatterCleanups (preset .getFieldFormatterCleanups ());
99+
100+ BackgroundTask .wrap (() -> cleanup (stateManager .getActiveDatabase ().get (), preset ))
101+ .onSuccess (result -> showResults ())
102+ .onFailure (dialogService ::showErrorDialogAndWait )
103+ .executeWith (taskExecutor );
104+ });
105+ }
106+
107+ /**
108+ * Runs the cleanup on the entry and records the change.
109+ *
110+ * @return true iff entry was modified
111+ */
112+ private boolean doCleanup (BibDatabaseContext databaseContext , CleanupPreferences preset , BibEntry entry , NamedCompoundEdit compoundEdit ) {
113+ // Create and run cleaner
114+ CleanupWorker cleaner = new CleanupWorker (
115+ databaseContext ,
116+ preferences .getFilePreferences (),
117+ preferences .getTimestampPreferences ()
54118 );
55119
56- dialogService .showCustomDialogAndWait (cleanupDialog );
120+ List <FieldChange > changes = cleaner .cleanup (preset , entry );
121+
122+ // Register undo action
123+ for (FieldChange change : changes ) {
124+ compoundEdit .addEdit (new UndoableFieldChange (change ));
125+ }
126+
127+ failures .addAll (cleaner .getFailures ());
128+
129+ return !changes .isEmpty ();
130+ }
131+
132+ private void showResults () {
133+ if (isCanceled ) {
134+ return ;
135+ }
136+
137+ if (modifiedEntriesCount > 0 ) {
138+ tabSupplier .get ().markBaseChanged ();
139+ }
140+
141+ if (modifiedEntriesCount == 0 ) {
142+ dialogService .notify (Localization .lang ("No entry needed a clean up" ));
143+ } else if (modifiedEntriesCount == 1 ) {
144+ dialogService .notify (Localization .lang ("One entry needed a clean up" ));
145+ } else {
146+ dialogService .notify (Localization .lang ("%0 entries needed a clean up" , Integer .toString (modifiedEntriesCount )));
147+ }
148+ }
149+
150+ private void cleanup (BibDatabaseContext databaseContext , CleanupPreferences cleanupPreferences ) {
151+ this .failures .clear ();
152+
153+ // undo granularity is on set of all entries
154+ NamedCompoundEdit compoundEdit = new NamedCompoundEdit (Localization .lang ("Clean up entries" ));
155+
156+ for (BibEntry entry : List .copyOf (stateManager .getSelectedEntries ())) {
157+ if (doCleanup (databaseContext , cleanupPreferences , entry , compoundEdit )) {
158+ modifiedEntriesCount ++;
159+ }
160+ }
161+
162+ compoundEdit .end ();
163+
164+ if (compoundEdit .hasEdits ()) {
165+ undoManager .addEdit (compoundEdit );
166+ }
167+
168+ if (!failures .isEmpty ()) {
169+ showFailures (failures );
170+ }
171+ }
172+
173+ private void showFailures (List <JabRefException > failures ) {
174+ String message = failures .stream ()
175+ .map (exception -> "- " + exception .getLocalizedMessage ())
176+ .collect (Collectors .joining ("\n " ));
177+
178+ Platform .runLater (() ->
179+ dialogService .showErrorDialogAndWait (Localization .lang ("File Move Errors" ), message )
180+ );
57181 }
58182}
0 commit comments