diff --git a/CHANGELOG.md b/CHANGELOG.md index d171dc7e6ad..75197bfad9e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv ### Added - We introduced a settings parameters to manage citations' relations local storage time-to-live with a default value set to 30 days. [#11189](https://github.com/JabRef/jabref/issues/11189) +- We introduced an option in Preferences under (under Linked files -> Linked file name conventions) to automatically rename linked files when an entry data changes. [#11316](https://github.com/JabRef/jabref/issues/11316) ### Changed diff --git a/jabgui/build.gradle.kts b/jabgui/build.gradle.kts index 890ea06369f..a4f4d698282 100644 --- a/jabgui/build.gradle.kts +++ b/jabgui/build.gradle.kts @@ -131,6 +131,10 @@ dependencies { testImplementation("io.github.classgraph:classgraph:4.8.179") testImplementation("org.testfx:testfx-core:4.0.16-alpha") testImplementation("org.testfx:testfx-junit5:4.0.16-alpha") + testImplementation("org.junit.jupiter:junit-jupiter-api:5.12.2") + testImplementation("org.junit.jupiter:junit-jupiter:5.12.2") + testImplementation("org.junit.jupiter:junit-jupiter-params:5.12.2") + testImplementation("org.junit.platform:junit-platform-launcher:1.12.2") testImplementation("org.mockito:mockito-core:5.18.0") { exclude(group = "net.bytebuddy", module = "byte-buddy") diff --git a/jabgui/src/main/java/org/jabref/gui/LibraryTab.java b/jabgui/src/main/java/org/jabref/gui/LibraryTab.java index 2afe8e908c1..a924b054fe0 100644 --- a/jabgui/src/main/java/org/jabref/gui/LibraryTab.java +++ b/jabgui/src/main/java/org/jabref/gui/LibraryTab.java @@ -43,6 +43,7 @@ import org.jabref.gui.collab.DatabaseChangeMonitor; import org.jabref.gui.dialogs.AutosaveUiManager; import org.jabref.gui.exporter.SaveDatabaseAction; +import org.jabref.gui.externalfiles.AutoRenameFileOnEntryChange; import org.jabref.gui.externalfiles.ImportHandler; import org.jabref.gui.fieldeditors.LinkedFileViewModel; import org.jabref.gui.importer.actions.OpenDatabaseAction; @@ -203,6 +204,7 @@ private void initializeComponentsAndListeners(boolean isDummyContext) { bibDatabaseContext.getDatabase().registerListener(this); bibDatabaseContext.getMetaData().registerListener(this); + new AutoRenameFileOnEntryChange(bibDatabaseContext, preferences); this.selectedGroupsProperty = new SimpleListProperty<>(stateManager.getSelectedGroups(bibDatabaseContext)); this.tableModel = new MainTableDataModel(getBibDatabaseContext(), preferences, taskExecutor, getIndexManager(), selectedGroupsProperty(), searchQueryProperty, resultSizeProperty()); diff --git a/jabgui/src/main/java/org/jabref/gui/externalfiles/AutoRenameFileOnEntryChange.java b/jabgui/src/main/java/org/jabref/gui/externalfiles/AutoRenameFileOnEntryChange.java new file mode 100644 index 00000000000..c4db6722cd2 --- /dev/null +++ b/jabgui/src/main/java/org/jabref/gui/externalfiles/AutoRenameFileOnEntryChange.java @@ -0,0 +1,66 @@ +package org.jabref.gui.externalfiles; + +import java.util.HashSet; +import java.util.Set; + +import org.jabref.gui.preferences.GuiPreferences; +import org.jabref.logic.FilePreferences; +import org.jabref.logic.citationkeypattern.CitationKeyGenerator; +import org.jabref.logic.cleanup.RenamePdfCleanup; +import org.jabref.model.database.BibDatabaseContext; +import org.jabref.model.entry.BibEntry; +import org.jabref.model.entry.event.FieldChangedEvent; + +import com.google.common.eventbus.Subscribe; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static org.jabref.logic.citationkeypattern.BracketedPattern.expandBrackets; + +public class AutoRenameFileOnEntryChange { + private static final Logger LOGGER = LoggerFactory.getLogger(AutoRenameFileOnEntryChange.class); + + private final GuiPreferences preferences; + private final BibDatabaseContext bibDatabaseContext; + private final RenamePdfCleanup renamePdfCleanup; + + public AutoRenameFileOnEntryChange(BibDatabaseContext bibDatabaseContext, GuiPreferences preferences) { + this.bibDatabaseContext = bibDatabaseContext; + this.preferences = preferences; + bibDatabaseContext.getDatabase().registerListener(this); + renamePdfCleanup = new RenamePdfCleanup(false, () -> bibDatabaseContext, preferences.getFilePreferences()); + } + + @Subscribe + public void listen(FieldChangedEvent event) { + FilePreferences filePreferences = preferences.getFilePreferences(); + + if (!filePreferences.shouldAutoRenameFilesOnChange() + || filePreferences.getFileNamePattern().isEmpty() + || filePreferences.getFileNamePattern() == null + || !relatesToFilePattern(filePreferences.getFileNamePattern(), event)) { + return; + } + + BibEntry entry = event.getBibEntry(); + if (entry.getFiles().isEmpty()) { + return; + } + new CitationKeyGenerator(bibDatabaseContext, preferences.getCitationKeyPatternPreferences()).generateAndSetKey(entry); + renamePdfCleanup.cleanup(entry); + + LOGGER.info("Field changed for entry {}: {}", entry.getCitationKey().orElse("defaultCitationKey"), event.getField().getName()); + } + + private boolean relatesToFilePattern(String fileNamePattern, FieldChangedEvent event) { + Set extractedFields = new HashSet<>(); + + expandBrackets(fileNamePattern, bracketContent -> { + extractedFields.add(bracketContent); + return bracketContent; + }); + + return extractedFields.contains("bibtexkey") + || extractedFields.contains(event.getField().getName()); + } +} diff --git a/jabgui/src/main/java/org/jabref/gui/preferences/linkedfiles/LinkedFilesTab.java b/jabgui/src/main/java/org/jabref/gui/preferences/linkedfiles/LinkedFilesTab.java index f67aba3c420..f4683d4a202 100644 --- a/jabgui/src/main/java/org/jabref/gui/preferences/linkedfiles/LinkedFilesTab.java +++ b/jabgui/src/main/java/org/jabref/gui/preferences/linkedfiles/LinkedFilesTab.java @@ -36,6 +36,7 @@ public class LinkedFilesTab extends AbstractPreferenceTabView fileNamePattern; @FXML private TextField fileDirectoryPattern; @@ -73,6 +74,7 @@ public void initialize() { autolinkRegexKey.textProperty().bindBidirectional(viewModel.autolinkRegexKeyProperty()); autolinkRegexKey.disableProperty().bind(autolinkUseRegex.selectedProperty().not()); fulltextIndex.selectedProperty().bindBidirectional(viewModel.fulltextIndexProperty()); + autoRenameFilesOnChange.selectedProperty().bindBidirectional(viewModel.autoRenameFilesOnChangeProperty()); fileNamePattern.valueProperty().bindBidirectional(viewModel.fileNamePatternProperty()); fileNamePattern.itemsProperty().bind(viewModel.defaultFileNamePatternsProperty()); fileDirectoryPattern.textProperty().bindBidirectional(viewModel.fileDirectoryPatternProperty()); diff --git a/jabgui/src/main/java/org/jabref/gui/preferences/linkedfiles/LinkedFilesTabViewModel.java b/jabgui/src/main/java/org/jabref/gui/preferences/linkedfiles/LinkedFilesTabViewModel.java index 3b83c2c7f0b..313274508f6 100644 --- a/jabgui/src/main/java/org/jabref/gui/preferences/linkedfiles/LinkedFilesTabViewModel.java +++ b/jabgui/src/main/java/org/jabref/gui/preferences/linkedfiles/LinkedFilesTabViewModel.java @@ -37,6 +37,7 @@ public class LinkedFilesTabViewModel implements PreferenceTabViewModel { private final ListProperty defaultFileNamePatternsProperty = new SimpleListProperty<>(FXCollections.observableArrayList(FilePreferences.DEFAULT_FILENAME_PATTERNS)); private final BooleanProperty fulltextIndex = new SimpleBooleanProperty(); + private final BooleanProperty autoRenameFilesOnChangeProperty = new SimpleBooleanProperty(); private final StringProperty fileNamePatternProperty = new SimpleStringProperty(); private final StringProperty fileDirectoryPatternProperty = new SimpleStringProperty(); private final BooleanProperty confirmLinkedFileDeleteProperty = new SimpleBooleanProperty(); @@ -82,6 +83,7 @@ public void setValues() { useMainFileDirectoryProperty.setValue(!filePreferences.shouldStoreFilesRelativeToBibFile()); useBibLocationAsPrimaryProperty.setValue(filePreferences.shouldStoreFilesRelativeToBibFile()); fulltextIndex.setValue(filePreferences.shouldFulltextIndexLinkedFiles()); + autoRenameFilesOnChangeProperty.setValue(filePreferences.shouldAutoRenameFilesOnChange()); fileNamePatternProperty.setValue(filePreferences.getFileNamePattern()); fileDirectoryPatternProperty.setValue(filePreferences.getFileDirectoryPattern()); confirmLinkedFileDeleteProperty.setValue(filePreferences.confirmDeleteLinkedFile()); @@ -104,6 +106,7 @@ public void storeSettings() { // External files preferences / Attached files preferences / File preferences filePreferences.setMainFileDirectory(mainFileDirectoryProperty.getValue()); filePreferences.setStoreFilesRelativeToBibFile(useBibLocationAsPrimaryProperty.getValue()); + filePreferences.setAutoRenameFilesOnChange(autoRenameFilesOnChangeProperty.getValue()); filePreferences.setFileNamePattern(fileNamePatternProperty.getValue()); filePreferences.setFileDirectoryPattern(fileDirectoryPatternProperty.getValue()); filePreferences.setFulltextIndexLinkedFiles(fulltextIndex.getValue()); @@ -179,6 +182,10 @@ public ListProperty defaultFileNamePatternsProperty() { return defaultFileNamePatternsProperty; } + public BooleanProperty autoRenameFilesOnChangeProperty() { + return autoRenameFilesOnChangeProperty; + } + public StringProperty fileNamePatternProperty() { return fileNamePatternProperty; } diff --git a/jabgui/src/main/resources/org/jabref/gui/preferences/linkedfiles/LinkedFilesTab.fxml b/jabgui/src/main/resources/org/jabref/gui/preferences/linkedfiles/LinkedFilesTab.fxml index f71481fbe9b..947d1bdb7ae 100644 --- a/jabgui/src/main/resources/org/jabref/gui/preferences/linkedfiles/LinkedFilesTab.fxml +++ b/jabgui/src/main/resources/org/jabref/gui/preferences/linkedfiles/LinkedFilesTab.fxml @@ -65,6 +65,7 @@