Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
400c6b0
Add cleanup dialog tabs with individual tab preferences
alexsukhin Sep 10, 2025
dd2f74c
Fixed indentation and added commenting
alexsukhin Sep 10, 2025
033201c
Fix Trag-bot review issues
alexsukhin Sep 11, 2025
14fde53
Fix Trag-bot review issues
alexsukhin Sep 11, 2025
9c44aad
Merge branch 'main' into 13819-add-dialog-tabs
alexsukhin Sep 11, 2025
0c5939e
Fix Trag-bot review issues
alexsukhin Sep 11, 2025
757514e
Avoid nested Optionals, returning Optional<CleanupPreferences> directly
alexsukhin Sep 11, 2025
a60f696
Refactor CleanupPreferences by keeping one assertion per test
alexsukhin Sep 11, 2025
43c981b
Converted tests to assertEquals
alexsukhin Sep 11, 2025
56b6164
Maintain consistent naming conventions
alexsukhin Sep 11, 2025
4f2ed12
Returns CleanupPreferences directly since value is always present
alexsukhin Sep 11, 2025
1d2f991
Initial review refactor draft
alexsukhin Sep 14, 2025
c8c3d42
Merge branch 'main' into 13819-add-dialog-tabs
alexsukhin Sep 14, 2025
7276eb0
fix import error!
alexsukhin Sep 14, 2025
9ed570f
Reformat codebase (more carefully) (#13885)
subhramit Sep 13, 2025
55c02c2
fix import error & merge
alexsukhin Sep 14, 2025
b0c4f6a
Apply OpenRewrite Cleanup
alexsukhin Sep 14, 2025
651f5ef
Refactor Cleanup Tabs
alexsukhin Sep 16, 2025
d280c21
Merge remote-tracking branch 'upstream/main' into 13819-add-dialog-tabs
alexsukhin Sep 16, 2025
161e36f
Fix getDisplayName method
alexsukhin Sep 16, 2025
2dfb7a7
Fix formatting
alexsukhin Sep 16, 2025
84d7716
Trag-bot review and fix en properties
alexsukhin Sep 16, 2025
442a623
fix indentation plssss
alexsukhin Sep 17, 2025
6606e59
format properly and change to observablelist
alexsukhin Sep 17, 2025
f90923a
fix formatting entriestoprocess (please)
alexsukhin Sep 17, 2025
9073361
Updated names and changed optional dependencies back to nullable
alexsukhin Sep 19, 2025
a8d371e
Merge branch 'main' into 13819-add-dialog-tabs
alexsukhin Sep 19, 2025
074242e
Merge branch 'main' into 13819-add-dialog-tabs
Siedlerchr Sep 23, 2025
8714141
Refactored panels to use separate ViewModels
alexsukhin Sep 25, 2025
f35319f
Moved ALL_JOBS to respective ViewModels, small naming changes
alexsukhin Sep 25, 2025
0ee7d35
Merge upstream/main, fix submodules
alexsukhin Sep 25, 2025
b24af6b
Replaced requireNotNull to @NotNull following https://github.com/JabR…
alexsukhin Sep 25, 2025
b7f4594
Merge branch 'main' into 13819-add-dialog-tabs
alexsukhin Oct 1, 2025
9c9f202
Merge branch 'main' into 13819-add-dialog-tabs
alexsukhin Oct 2, 2025
40f6dbf
Merge branch 'main' into 13819-add-dialog-tabs
koppor Nov 5, 2025
25a1151
Merge branch 'main' into 13819-add-dialog-tabs
koppor Nov 6, 2025
cb94ec5
Merge branch 'main' into 13819-add-dialog-tabs
alexsukhin Nov 9, 2025
5adc301
Address review feedback in CleanupDialogViewModel
alexsukhin Nov 9, 2025
576071d
Merge branch 'main' into 13819-add-dialog-tabs
koppor Nov 11, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv
- We improved the event viewer for debugging [#13783](https://github.com/JabRef/jabref/pull/13783).
- We improved "REDACTED" replacement of API key value in web fetcher search URL [#13796](https://github.com/JabRef/jabref/issues/13796)
- When the pin "Keep dialog always on top" in the global search dialog is selected, the search window stays open when double-clicking on an entry. [#13840](https://github.com/JabRef/jabref/issues/13840)
- We separated the "Clean up entries" dialog into three tabs for clarity [#13819](https://github.com/JabRef/jabref/issues/13819)

### Fixed

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,10 @@ public void execute() {
}
}

preferences.getCleanupPreferences().setActiveJobs(preset.getActiveJobs());
preferences.getCleanupPreferences().setFieldFormatterCleanups(preset.getFieldFormatterCleanups());
CleanupPreferences updatedPreferences = preferences.getCleanupPreferences().updateWith(preset);

preferences.getCleanupPreferences().setActiveJobs(updatedPreferences.getActiveJobs());
preferences.getCleanupPreferences().setFieldFormatterCleanups(updatedPreferences.getFieldFormatterCleanups());

BackgroundTask.wrap(() -> cleanup(stateManager.getActiveDatabase().get(), preset))
.onSuccess(result -> showResults())
Expand Down
38 changes: 25 additions & 13 deletions jabgui/src/main/java/org/jabref/gui/cleanup/CleanupDialog.java
Original file line number Diff line number Diff line change
@@ -1,32 +1,44 @@
package org.jabref.gui.cleanup;

import javafx.scene.control.ButtonType;
import javafx.scene.control.ScrollPane;
import javafx.fxml.FXML;
import javafx.scene.control.ButtonBar;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;

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.model.database.BibDatabaseContext;

import com.airhacks.afterburner.views.ViewLoader;

public class CleanupDialog extends BaseDialog<CleanupPreferences> {

@FXML private TabPane tabPane;

public CleanupDialog(BibDatabaseContext databaseContext, CleanupPreferences initialPreset, FilePreferences filePreferences) {
setTitle(Localization.lang("Clean up entries"));
getDialogPane().setPrefSize(600, 650);
getDialogPane().getButtonTypes().setAll(ButtonType.OK, ButtonType.CANCEL);

CleanupPresetPanel presetPanel = new CleanupPresetPanel(databaseContext, initialPreset, filePreferences);
ViewLoader.view(this)
.load()
.setAsDialogPane(this);

CleanupSingleFieldPanel singleFieldPanel = new CleanupSingleFieldPanel(initialPreset);
CleanupFileRelatedPanel fileRelatedPanel = new CleanupFileRelatedPanel(databaseContext, initialPreset, filePreferences);
CleanupMultiFieldPanel multiFieldPanel = new CleanupMultiFieldPanel(initialPreset);

// placing the content of the presetPanel in a scroll pane
ScrollPane scrollPane = new ScrollPane();
scrollPane.setFitToWidth(true);
scrollPane.setFitToHeight(true);
scrollPane.setContent(presetPanel);
tabPane.getTabs().addAll(
new Tab(Localization.lang("Single field"), singleFieldPanel),
new Tab(Localization.lang("File-related"), fileRelatedPanel),
new Tab(Localization.lang("Multi-field"), multiFieldPanel)
);

getDialogPane().setContent(scrollPane);
setResultConverter(button -> {
if (button == ButtonType.OK) {
return presetPanel.getCleanupPreset();
if (button.getButtonData() == ButtonBar.ButtonData.OK_DONE) {
Tab selectedTab = tabPane.getSelectionModel().getSelectedItem();
CleanupPanel panel = (CleanupPanel) selectedTab.getContent();
return panel.getCleanupPreferences().orElse(null);
} else {
return null;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package org.jabref.gui.cleanup;

import java.nio.file.Path;
import java.util.EnumSet;
import java.util.Objects;
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.l10n.Localization;
import org.jabref.model.database.BibDatabaseContext;
import org.jabref.model.entry.field.StandardField;

import com.airhacks.afterburner.views.ViewLoader;

public class CleanupFileRelatedPanel extends VBox implements CleanupPanel {

@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;

public CleanupFileRelatedPanel(BibDatabaseContext databaseContext, CleanupPreferences cleanupPreferences, FilePreferences filePreferences) {
Objects.requireNonNull(databaseContext, "databaseContext must not be null");
Objects.requireNonNull(cleanupPreferences, "cleanupPreferences must not be null");
Objects.requireNonNull(filePreferences, "filePreferences must not be null");

ViewLoader.view(this)
.root(this)
.load();

init(databaseContext, cleanupPreferences, filePreferences);
}

private void init(BibDatabaseContext databaseContext, CleanupPreferences cleanupPreferences, FilePreferences filePreferences) {
Optional<Path> 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", "..."));

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.", StandardField.FILE.getDisplayName()));

String currentPattern = Localization.lang("Filename format pattern (from preferences)")
.concat(filePreferences.getFileNamePattern());
cleanupRenamePdfLabel.setText(currentPattern);

updateDisplay(cleanupPreferences);
}

private void updateDisplay(CleanupPreferences preset) {
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));
}

public Optional<CleanupPreferences> getCleanupPreferences() {
EnumSet<CleanupPreferences.CleanupStep> activeJobs = EnumSet.noneOf(CleanupPreferences.CleanupStep.class);

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);
}

return Optional.of(new CleanupPreferences(activeJobs));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package org.jabref.gui.cleanup;

import java.util.EnumSet;
import java.util.Objects;
import java.util.Optional;

import javafx.fxml.FXML;
import javafx.scene.control.CheckBox;
import javafx.scene.layout.VBox;

import org.jabref.logic.cleanup.CleanupPreferences;

import com.airhacks.afterburner.views.ViewLoader;

public class CleanupMultiFieldPanel extends VBox implements CleanupPanel {
@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;

public CleanupMultiFieldPanel(CleanupPreferences cleanupPreferences) {
Objects.requireNonNull(cleanupPreferences, "cleanupPreferences must not be null");

ViewLoader.view(this)
.root(this)
.load();

init(cleanupPreferences);
}

private void init(CleanupPreferences cleanupPreferences) {
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));
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));
}

@Override
public Optional<CleanupPreferences> getCleanupPreferences() {
EnumSet<CleanupPreferences.CleanupStep> activeJobs = EnumSet.noneOf(CleanupPreferences.CleanupStep.class);

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 (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);
}

return Optional.of(new CleanupPreferences(activeJobs));
}
}
9 changes: 9 additions & 0 deletions jabgui/src/main/java/org/jabref/gui/cleanup/CleanupPanel.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.jabref.gui.cleanup;

import java.util.Optional;

import org.jabref.logic.cleanup.CleanupPreferences;

public interface CleanupPanel {
Optional<CleanupPreferences> getCleanupPreferences();
}
Loading
Loading