diff --git a/CHANGELOG.md b/CHANGELOG.md index ff612b3f49d..300e2fe555e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,6 +40,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv - We added a field for the latest ICORE conference ranking lookup on the General Tab. [#13476](https://github.com/JabRef/jabref/issues/13476) - We added BibLaTeX datamodel validation support in order to improve error message quality in entries' fields validation. [#13318](https://github.com/JabRef/jabref/issues/13318) - We added more supported formats of CAYW endpoint of HTTP server. [#13578](https://github.com/JabRef/jabref/issues/13578) +- We added an integrity checker for the booktitle field. [#12271](https://github.com/JabRef/jabref/issues/12271) - We added chronological navigation for entries in each library. [#6352](https://github.com/JabRef/jabref/issues/6352) - We added support for using Medline/Pubmed fetcher with an API key. [#11296](https://github.com/JabRef/jabref/issues/11296#issuecomment-3289005011) - We added support for using OpenAlex fetcher. [#13940](https://github.com/JabRef/jabref/issues/13940) 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..f48bb7f43fd 100644 --- a/jabgui/src/main/java/org/jabref/gui/cleanup/CleanupAction.java +++ b/jabgui/src/main/java/org/jabref/gui/cleanup/CleanupAction.java @@ -96,6 +96,7 @@ public void execute() { preferences.getCleanupPreferences().setActiveJobs(preset.getActiveJobs()); preferences.getCleanupPreferences().setFieldFormatterCleanups(preset.getFieldFormatterCleanups()); + preferences.getCleanupPreferences().setBooktitleCleanups(preset.getBooktitleCleanups()); BackgroundTask.wrap(() -> cleanup(stateManager.getActiveDatabase().get(), preset)) .onSuccess(result -> showResults()) diff --git a/jabgui/src/main/java/org/jabref/gui/cleanup/CleanupPresetPanel.java b/jabgui/src/main/java/org/jabref/gui/cleanup/CleanupPresetPanel.java index c8c1cae125e..a2130b7f50c 100644 --- a/jabgui/src/main/java/org/jabref/gui/cleanup/CleanupPresetPanel.java +++ b/jabgui/src/main/java/org/jabref/gui/cleanup/CleanupPresetPanel.java @@ -10,6 +10,7 @@ import javafx.scene.control.Label; import javafx.scene.layout.VBox; +import org.jabref.gui.commonfxcontrols.BooktitleCleanupPanel; import org.jabref.gui.commonfxcontrols.FieldFormatterCleanupsPanel; import org.jabref.logic.FilePreferences; import org.jabref.logic.cleanup.CleanupPreferences; @@ -39,6 +40,7 @@ public class CleanupPresetPanel extends VBox { @FXML private CheckBox cleanUpBibtex; @FXML private CheckBox cleanUpTimestampToCreationDate; @FXML private CheckBox cleanUpTimestampToModificationDate; + @FXML private BooktitleCleanupPanel booktitleCleanupPanel; @FXML private FieldFormatterCleanupsPanel formatterCleanupsPanel; public CleanupPresetPanel(@NonNull BibDatabaseContext databaseContext, @@ -118,6 +120,8 @@ private void updateDisplay(CleanupPreferences preset) { 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)); + booktitleCleanupPanel.cleanupsDisableProperty().setValue(!preset.getBooktitleCleanups().isEnabled()); + booktitleCleanupPanel.selectedActionsProperty().setValue(FXCollections.observableMap(preset.getBooktitleCleanups().getConfiguredActions())); formatterCleanupsPanel.cleanupsDisableProperty().setValue(!preset.getFieldFormatterCleanups().isEnabled()); formatterCleanupsPanel.cleanupsProperty().setValue(FXCollections.observableArrayList(preset.getFieldFormatterCleanups().getConfiguredActions())); } @@ -168,8 +172,13 @@ public CleanupPreferences getCleanupPreset() { activeJobs.add(CleanupPreferences.CleanupStep.FIX_FILE_LINKS); - return new CleanupPreferences(activeJobs, new FieldFormatterCleanups( - !formatterCleanupsPanel.cleanupsDisableProperty().getValue(), - formatterCleanupsPanel.cleanupsProperty())); + return new CleanupPreferences( + activeJobs, + new FieldFormatterCleanups( + !formatterCleanupsPanel.cleanupsDisableProperty().getValue(), + formatterCleanupsPanel.cleanupsProperty() + ), + booktitleCleanupPanel.createCleanupAction() + ); } } diff --git a/jabgui/src/main/java/org/jabref/gui/commonfxcontrols/BooktitleCleanupPanel.java b/jabgui/src/main/java/org/jabref/gui/commonfxcontrols/BooktitleCleanupPanel.java new file mode 100644 index 00000000000..b9904633943 --- /dev/null +++ b/jabgui/src/main/java/org/jabref/gui/commonfxcontrols/BooktitleCleanupPanel.java @@ -0,0 +1,166 @@ +package org.jabref.gui.commonfxcontrols; + +import java.util.EnumMap; +import java.util.Map; + +import javafx.beans.property.BooleanProperty; +import javafx.beans.property.MapProperty; +import javafx.beans.value.ObservableValue; +import javafx.collections.MapChangeListener; +import javafx.fxml.FXML; +import javafx.scene.control.CheckBox; +import javafx.scene.control.Label; +import javafx.scene.control.RadioButton; +import javafx.scene.control.Toggle; +import javafx.scene.control.ToggleGroup; +import javafx.scene.layout.GridPane; +import javafx.scene.layout.RowConstraints; +import javafx.scene.layout.VBox; + +import org.jabref.gui.util.BindingsHelper; +import org.jabref.logic.cleanup.BooktitleCleanups; +import org.jabref.logic.util.LocationDetector; +import org.jabref.model.cleanup.BooktitleCleanupAction; +import org.jabref.model.cleanup.BooktitleCleanupField; + +import com.airhacks.afterburner.views.ViewLoader; + +/** + * A JavaFX panel component for configuring book title cleanup operations. + * + *

This panel provides a user interface for managing cleanup actions that extract and move + * embedded fields from booktitles, including months, years, page ranges, and locations. Users can + * configure how each field's cleanup should be handled through radio button groups.

+ * + *

The radio buttons are dynamically generated for each cleanup field based on the + * {@link BooktitleCleanupField} enum and {@link BooktitleCleanupAction} enum. + * + *

Available actions for each metadata type:

+ * + * + * For details on how the cleanups are executed, see {@link BooktitleCleanups} + */ +public class BooktitleCleanupPanel extends VBox { + + public static final double ROW_MIN_HEIGHT = 15.0; + public static final double ROW_PREF_HEIGHT = 20.0; + + private final Map toggleGroups = new EnumMap<>(BooktitleCleanupField.class); + + @FXML private CheckBox cleanupsEnabled; + @FXML private GridPane cleanupGrid; + + private BooktitleCleanupPanelViewModel viewModel; + + public BooktitleCleanupPanel() { + ViewLoader.view(this) + .root(this) + .load(); + } + + @FXML + private void initialize() { + this.viewModel = new BooktitleCleanupPanelViewModel(LocationDetector.getInstance()); + + generateCleanupRows(); + setupBindings(); + } + + /** + * Dynamically generates radio button rows for each cleanup field type. + * + *

For each {@link BooktitleCleanupField}, this method:

+ * + */ + private void generateCleanupRows() { + int rowIndex = 1; + int labelIndex = 0; + + for (BooktitleCleanupField field : BooktitleCleanupField.values()) { + RowConstraints rowConstraint = new RowConstraints(); + rowConstraint.setMinHeight(ROW_MIN_HEIGHT); + rowConstraint.setPrefHeight(ROW_PREF_HEIGHT); + cleanupGrid.getRowConstraints().add(rowConstraint); + + Label fieldLabel = new Label(field.getDisplayName()); + fieldLabel.getStyleClass().add("field-label"); + cleanupGrid.add(fieldLabel, labelIndex, rowIndex); + + ToggleGroup toggleGroup = new ToggleGroup(); + toggleGroups.put(field, toggleGroup); + + int columnIndex = 1; + for (BooktitleCleanupAction cleanupAction : BooktitleCleanupAction.values()) { + RadioButton radioButton = createRadioButton(cleanupAction, toggleGroup); + + if (cleanupAction == field.getDefaultAction()) { + radioButton.setSelected(true); + } + + cleanupGrid.add(radioButton, columnIndex, rowIndex); + columnIndex++; + } + + rowIndex++; + } + } + + private RadioButton createRadioButton(BooktitleCleanupAction cleanupAction, ToggleGroup toggleGroup) { + RadioButton radioButton = new RadioButton(); + radioButton.setToggleGroup(toggleGroup); + radioButton.setUserData(cleanupAction); + return radioButton; + } + + private void setupBindings() { + BindingsHelper.bindBidirectional( + (ObservableValue) cleanupsEnabled.selectedProperty(), + viewModel.cleanupsDisableProperty(), + disabled -> cleanupsEnabled.selectedProperty().setValue(!disabled), + selected -> viewModel.cleanupsDisableProperty().setValue(!selected)); + + // setup bidirectional binding between each toggleGroup and its corresponding field-to-cleanup-action map in the view model + toggleGroups.forEach((field, toggleGroup) -> { + toggleGroup.selectedToggleProperty().addListener((_, _, selectedToggle) -> { + if (selectedToggle != null) { + BooktitleCleanupAction action = (BooktitleCleanupAction) selectedToggle.getUserData(); + viewModel.setSelectedAction(field, action); + } + }); + viewModel.selectedActionsProperty().addListener((MapChangeListener) change -> { + if (change.wasAdded() && change.getKey() == field) { + BooktitleCleanupAction newAction = change.getValueAdded(); + for (Toggle toggle : toggleGroup.getToggles()) { + if (toggle.getUserData() == newAction) { + toggleGroup.selectToggle(toggle); + break; + } + } + } + }); + }); + } + + public BooktitleCleanups createCleanupAction() { + return viewModel.createCleanup(); + } + + public BooleanProperty cleanupsDisableProperty() { + return viewModel.cleanupsDisableProperty(); + } + + public MapProperty selectedActionsProperty() { + return viewModel.selectedActionsProperty(); + } +} diff --git a/jabgui/src/main/java/org/jabref/gui/commonfxcontrols/BooktitleCleanupPanelViewModel.java b/jabgui/src/main/java/org/jabref/gui/commonfxcontrols/BooktitleCleanupPanelViewModel.java new file mode 100644 index 00000000000..8e7c19c5aa9 --- /dev/null +++ b/jabgui/src/main/java/org/jabref/gui/commonfxcontrols/BooktitleCleanupPanelViewModel.java @@ -0,0 +1,58 @@ +package org.jabref.gui.commonfxcontrols; + +import java.util.EnumMap; + +import javafx.beans.property.BooleanProperty; +import javafx.beans.property.MapProperty; +import javafx.beans.property.SimpleBooleanProperty; +import javafx.beans.property.SimpleMapProperty; +import javafx.collections.FXCollections; + +import org.jabref.logic.cleanup.BooktitleCleanups; +import org.jabref.logic.util.LocationDetector; +import org.jabref.model.cleanup.BooktitleCleanupAction; +import org.jabref.model.cleanup.BooktitleCleanupField; + +public class BooktitleCleanupPanelViewModel { + + private final BooleanProperty cleanupsDisableProperty = new SimpleBooleanProperty(); + /* + * Map Property mapping each cleanup field to its corresponding cleanup action. + * Each field corresponds to a ToggleGroup in the Cleanup Panel with each Radio Button representing + * an individual cleanup action. + */ + private final MapProperty selectedActions = + new SimpleMapProperty<>(FXCollections.observableMap(new EnumMap<>(BooktitleCleanupField.class))); + + private final LocationDetector locationDetector; + + public BooktitleCleanupPanelViewModel(LocationDetector locationDetector) { + this.locationDetector = locationDetector; + } + + public BooktitleCleanups createCleanup() { + return new BooktitleCleanups( + getSelectedAction(BooktitleCleanupField.YEAR), + getSelectedAction(BooktitleCleanupField.MONTH), + getSelectedAction(BooktitleCleanupField.PAGE_RANGE), + getSelectedAction(BooktitleCleanupField.LOCATION), + locationDetector + ); + } + + public MapProperty selectedActionsProperty() { + return selectedActions; + } + + public void setSelectedAction(BooktitleCleanupField field, BooktitleCleanupAction action) { + selectedActions.put(field, action); + } + + public BooktitleCleanupAction getSelectedAction(BooktitleCleanupField field) { + return selectedActions.get(field); + } + + public BooleanProperty cleanupsDisableProperty() { + return cleanupsDisableProperty; + } +} diff --git a/jabgui/src/main/resources/org/jabref/gui/cleanup/CleanupPresetPanel.fxml b/jabgui/src/main/resources/org/jabref/gui/cleanup/CleanupPresetPanel.fxml index f6421d75a4d..42e0d2bd2ad 100644 --- a/jabgui/src/main/resources/org/jabref/gui/cleanup/CleanupPresetPanel.fxml +++ b/jabgui/src/main/resources/org/jabref/gui/cleanup/CleanupPresetPanel.fxml @@ -6,6 +6,7 @@ + @@ -24,8 +25,10 @@ + +