diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ab36e459dc..1fc1c554033 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv - We added [LOBID](https://lobid.org/) as an alternative ISBN-Fetcher. [#13076](https://github.com/JabRef/jabref/issues/13076) - We added a success dialog when using the "Copy to" option, indicating whether the entry was successfully copied and specifying if a cross-reference entry was included. [#12486](https://github.com/JabRef/jabref/issues/12486) - We added a new button to toggle the file path between an absolute and relative formats in context of library properties. [#13031](https://github.com/JabRef/jabref/issues/13031) +- We introduced user-configurable group 'Imported entries' for automatic import of entries from web search, PDF import and web fetchers. [#12548](https://github.com/JabRef/jabref/issues/12548) - We added automatic selection of the “Enter Identifier” tab with pre-filled clipboard content if the clipboard contains a valid identifier when opening the “Create New Entry” dialog. [#13087](https://github.com/JabRef/jabref/issues/13087) - We added an "Open example library" button to Welcome Tab. [#13014](https://github.com/JabRef/jabref/issues/13014) - We added automatic detection and selection of the identifier type (e.g., DOI, ISBN, arXiv) based on clipboard content when opening the "New Entry" dialog [#13111](https://github.com/JabRef/jabref/pull/13111) diff --git a/jabgui/src/main/java/org/jabref/gui/externalfiles/ImportHandler.java b/jabgui/src/main/java/org/jabref/gui/externalfiles/ImportHandler.java index ff02eed566a..c623ae3b929 100644 --- a/jabgui/src/main/java/org/jabref/gui/externalfiles/ImportHandler.java +++ b/jabgui/src/main/java/org/jabref/gui/externalfiles/ImportHandler.java @@ -56,6 +56,7 @@ import org.jabref.model.entry.field.StandardField; import org.jabref.model.groups.GroupEntryChanger; import org.jabref.model.groups.GroupTreeNode; +import org.jabref.model.groups.SmartGroup; import org.jabref.model.util.FileUpdateMonitor; import org.jabref.model.util.OptionalUtil; @@ -164,6 +165,7 @@ public List call() { // Modifiers do not work on macOS: https://bugs.openjdk.org/browse/JDK-8264172 // Similar code as org.jabref.gui.preview.PreviewPanel.PreviewPanel DragDrop.handleDropOfFiles(List.of(file), transferMode, fileLinker, entry); + addToImportEntriesGroup(pdfEntriesInFile); entriesToAdd.addAll(pdfEntriesInFile); addResultToList(file, true, Localization.lang("File was successfully imported as a new entry")); }); @@ -267,6 +269,7 @@ private void importEntryWithDuplicateCheck(BibDatabaseContext bibDatabaseContext finalEntry = duplicateHandledEntry.get(); } importCleanedEntries(bibDatabaseContext, List.of(finalEntry)); + addToImportEntriesGroup(List.of(finalEntry)); downloadLinkedFiles(finalEntry); BibEntry entryToFocus = finalEntry; stateManager.activeTabProperty().get().ifPresent(tab -> tab.clearAndSelect(entryToFocus)); @@ -510,4 +513,17 @@ private List handlePdfUrl(String pdfUrl) throws IOException { return List.of(); } } + + private void addToImportEntriesGroup(List entriesToInsert) { + if (preferences.getLibraryPreferences().isAddImportedEntriesEnabled()) { + // Only one SmartGroup + this.bibDatabaseContext.getMetaData() + .getGroups() + .flatMap(grp -> grp.getChildren() + .stream() + .filter(node -> node.getGroup() instanceof SmartGroup) + .findFirst()) + .ifPresent(smtGrp -> smtGrp.addEntriesToGroup(entriesToInsert)); + } + } } diff --git a/jabgui/src/main/java/org/jabref/gui/groups/GroupDescriptions.java b/jabgui/src/main/java/org/jabref/gui/groups/GroupDescriptions.java index 5023f77e5f0..606f7a586a9 100644 --- a/jabgui/src/main/java/org/jabref/gui/groups/GroupDescriptions.java +++ b/jabgui/src/main/java/org/jabref/gui/groups/GroupDescriptions.java @@ -4,6 +4,7 @@ import org.jabref.model.groups.ExplicitGroup; import org.jabref.model.groups.KeywordGroup; import org.jabref.model.groups.SearchGroup; +import org.jabref.model.groups.SmartGroup; import org.jabref.model.strings.StringUtil; public class GroupDescriptions { @@ -61,6 +62,10 @@ public static String getShortDescriptionAllEntriesGroup() { return Localization.lang("All Entries (this group cannot be edited or removed)"); } + public static String getShortDescriptionSmartGroup(SmartGroup smartGroup) { + return Localization.lang("Smart Group (Import Entries)"); + } + public static String getShortDescription(SearchGroup searchGroup, boolean showDynamic) { StringBuilder sb = new StringBuilder(); sb.append(""); diff --git a/jabgui/src/main/java/org/jabref/gui/groups/GroupNodeViewModel.java b/jabgui/src/main/java/org/jabref/gui/groups/GroupNodeViewModel.java index 40187f0be76..66ff70471a0 100644 --- a/jabgui/src/main/java/org/jabref/gui/groups/GroupNodeViewModel.java +++ b/jabgui/src/main/java/org/jabref/gui/groups/GroupNodeViewModel.java @@ -46,6 +46,7 @@ import org.jabref.model.groups.LastNameGroup; import org.jabref.model.groups.RegexKeywordGroup; import org.jabref.model.groups.SearchGroup; +import org.jabref.model.groups.SmartGroup; import org.jabref.model.groups.TexGroup; import org.jabref.model.search.event.IndexAddedOrUpdatedEvent; import org.jabref.model.search.event.IndexClosedEvent; @@ -432,6 +433,8 @@ public boolean canAddEntriesIn() { AbstractGroup group = groupNode.getGroup(); if (group instanceof AllEntriesGroup) { return false; + } else if (group instanceof SmartGroup) { + return false; } else if (group instanceof ExplicitGroup) { return true; } else if (group instanceof LastNameGroup || group instanceof RegexKeywordGroup) { @@ -458,7 +461,7 @@ public boolean canAddEntriesIn() { public boolean canBeDragged() { AbstractGroup group = groupNode.getGroup(); return switch (group) { - case AllEntriesGroup _ -> false; + case AllEntriesGroup _, SmartGroup _ -> false; case ExplicitGroup _, SearchGroup _, AutomaticKeywordGroup _, AutomaticPersonsGroup _, TexGroup _ -> true; case KeywordGroup _ -> // KeywordGroup is parent of LastNameGroup, RegexKeywordGroup and WordKeywordGroup @@ -477,13 +480,13 @@ public boolean canAddGroupsIn() { AbstractGroup group = groupNode.getGroup(); return switch (group) { case AllEntriesGroup _, ExplicitGroup _, SearchGroup _, TexGroup _ -> true; + case AutomaticKeywordGroup _, AutomaticPersonsGroup _, SmartGroup _ -> false; case KeywordGroup _ -> // KeywordGroup is parent of LastNameGroup, RegexKeywordGroup and WordKeywordGroup groupNode.getParent() .map(GroupTreeNode::getGroup) .map(groupParent -> !(groupParent instanceof AutomaticKeywordGroup || groupParent instanceof AutomaticPersonsGroup)) .orElse(false); - case AutomaticKeywordGroup _, AutomaticPersonsGroup _ -> false; case null -> throw new IllegalArgumentException("Group cannot be null"); default -> throw new UnsupportedOperationException("canAddGroupsIn method not yet implemented in group: " + group.getClass().getName()); }; @@ -492,7 +495,7 @@ public boolean canAddGroupsIn() { public boolean canRemove() { AbstractGroup group = groupNode.getGroup(); return switch (group) { - case AllEntriesGroup _ -> false; + case AllEntriesGroup _, SmartGroup _ -> false; case ExplicitGroup _, SearchGroup _, AutomaticKeywordGroup _, AutomaticPersonsGroup _, TexGroup _ -> true; case KeywordGroup _ -> // KeywordGroup is parent of LastNameGroup, RegexKeywordGroup and WordKeywordGroup @@ -508,7 +511,7 @@ public boolean canRemove() { public boolean isEditable() { AbstractGroup group = groupNode.getGroup(); return switch (group) { - case AllEntriesGroup _ -> false; + case AllEntriesGroup _, SmartGroup _ -> false; case ExplicitGroup _, SearchGroup _, AutomaticKeywordGroup _, AutomaticPersonsGroup _, TexGroup _ -> true; case KeywordGroup _ -> // KeywordGroup is parent of LastNameGroup, RegexKeywordGroup and WordKeywordGroup diff --git a/jabgui/src/main/java/org/jabref/gui/groups/GroupTreeNodeViewModel.java b/jabgui/src/main/java/org/jabref/gui/groups/GroupTreeNodeViewModel.java index 38f9c532988..d8f454931da 100644 --- a/jabgui/src/main/java/org/jabref/gui/groups/GroupTreeNodeViewModel.java +++ b/jabgui/src/main/java/org/jabref/gui/groups/GroupTreeNodeViewModel.java @@ -17,6 +17,7 @@ import org.jabref.model.groups.GroupTreeNode; import org.jabref.model.groups.KeywordGroup; import org.jabref.model.groups.SearchGroup; +import org.jabref.model.groups.SmartGroup; public class GroupTreeNodeViewModel { private final GroupTreeNode node; @@ -51,6 +52,8 @@ public String getDescription() { String shortDescription = ""; boolean showDynamic = true; shortDescription = switch (group) { + case SmartGroup smartGroup -> + GroupDescriptions.getShortDescriptionSmartGroup(smartGroup); case ExplicitGroup explicitGroup -> GroupDescriptions.getShortDescriptionExplicitGroup(explicitGroup); case KeywordGroup keywordGroup -> diff --git a/jabgui/src/main/java/org/jabref/gui/groups/GroupTreeViewModel.java b/jabgui/src/main/java/org/jabref/gui/groups/GroupTreeViewModel.java index 8f621dc11b5..1977ea34c24 100644 --- a/jabgui/src/main/java/org/jabref/gui/groups/GroupTreeViewModel.java +++ b/jabgui/src/main/java/org/jabref/gui/groups/GroupTreeViewModel.java @@ -37,9 +37,11 @@ import org.jabref.model.groups.AutomaticKeywordGroup; import org.jabref.model.groups.AutomaticPersonsGroup; import org.jabref.model.groups.ExplicitGroup; +import org.jabref.model.groups.GroupHierarchyType; import org.jabref.model.groups.GroupTreeNode; import org.jabref.model.groups.RegexKeywordGroup; import org.jabref.model.groups.SearchGroup; +import org.jabref.model.groups.SmartGroup; import org.jabref.model.groups.TexGroup; import org.jabref.model.groups.WordKeywordGroup; import org.jabref.model.metadata.MetaData; @@ -176,6 +178,29 @@ private void onActiveDatabaseChanged(Optional newDatabase) { rootGroup.setValue(null); } currentDatabase = newDatabase; + newDatabase.ifPresent(db -> addGroupImportEntries(rootGroup.get())); + } + + private void addGroupImportEntries(GroupNodeViewModel parent) { + if (!preferences.getLibraryPreferences().isAddImportedEntriesEnabled()) { + return; + } + + String grpName = preferences.getLibraryPreferences().getAddImportedEntriesGroupName(); + AbstractGroup importEntriesGroup = new SmartGroup(grpName, GroupHierarchyType.INDEPENDENT, ','); + boolean isGrpExist = parent.getGroupNode() + .getChildren() + .stream() + .map(GroupTreeNode::getGroup) + .anyMatch(grp -> grp instanceof SmartGroup); + if (!isGrpExist) { + currentDatabase.ifPresent(db -> { + GroupTreeNode newSubgroup = parent.addSubgroup(importEntriesGroup); + newSubgroup.moveTo(parent.getGroupNode(), 0); + selectedGroups.setAll(new GroupNodeViewModel(db, stateManager, taskExecutor, newSubgroup, localDragboard, preferences)); + writeGroupChangesToMetaData(); + }); + } } /** diff --git a/jabgui/src/main/java/org/jabref/gui/preferences/websearch/WebSearchTab.java b/jabgui/src/main/java/org/jabref/gui/preferences/websearch/WebSearchTab.java index f842ba1233e..2a9073041d0 100644 --- a/jabgui/src/main/java/org/jabref/gui/preferences/websearch/WebSearchTab.java +++ b/jabgui/src/main/java/org/jabref/gui/preferences/websearch/WebSearchTab.java @@ -33,6 +33,8 @@ public class WebSearchTab extends AbstractPreferenceTabView defaultPlainCitationParser; @FXML private CheckBox useCustomDOI; @@ -76,6 +78,10 @@ public void initialize() { downloadLinkedOnlineFiles.selectedProperty().bindBidirectional(viewModel.shouldDownloadLinkedOnlineFiles()); keepDownloadUrl.selectedProperty().bindBidirectional(viewModel.shouldKeepDownloadUrl()); + addImportedEntries.selectedProperty().bindBidirectional(viewModel.getAddImportedEntries()); + addImportedEntriesGroupName.textProperty().bindBidirectional(viewModel.getAddImportedEntriesGroupName()); + addImportedEntriesGroupName.disableProperty().bind(addImportedEntries.selectedProperty().not()); + new ViewModelListCellFactory() .withText(PlainCitationParserChoice::getLocalizedName) .install(defaultPlainCitationParser); diff --git a/jabgui/src/main/java/org/jabref/gui/preferences/websearch/WebSearchTabViewModel.java b/jabgui/src/main/java/org/jabref/gui/preferences/websearch/WebSearchTabViewModel.java index ced3fc6a146..e22198286b4 100644 --- a/jabgui/src/main/java/org/jabref/gui/preferences/websearch/WebSearchTabViewModel.java +++ b/jabgui/src/main/java/org/jabref/gui/preferences/websearch/WebSearchTabViewModel.java @@ -21,6 +21,7 @@ import org.jabref.gui.preferences.PreferenceTabViewModel; import org.jabref.gui.slr.StudyCatalogItem; import org.jabref.logic.FilePreferences; +import org.jabref.logic.LibraryPreferences; import org.jabref.logic.importer.ImportFormatPreferences; import org.jabref.logic.importer.ImporterPreferences; import org.jabref.logic.importer.SearchBasedFetcher; @@ -48,6 +49,9 @@ public class WebSearchTabViewModel implements PreferenceTabViewModel { new SimpleListProperty<>(FXCollections.observableArrayList(PlainCitationParserChoice.values())); private final ObjectProperty defaultPlainCitationParser = new SimpleObjectProperty<>(); + private final BooleanProperty addImportedEntries = new SimpleBooleanProperty(); + private final StringProperty addImportedEntriesGroupName = new SimpleStringProperty(""); + private final BooleanProperty useCustomDOIProperty = new SimpleBooleanProperty(); private final StringProperty useCustomDOINameProperty = new SimpleStringProperty(""); @@ -67,6 +71,7 @@ public class WebSearchTabViewModel implements PreferenceTabViewModel { private final ImporterPreferences importerPreferences; private final FilePreferences filePreferences; private final ImportFormatPreferences importFormatPreferences; + private final LibraryPreferences libraryPreferences; private final ReadOnlyBooleanProperty refAiEnabled; @@ -78,6 +83,7 @@ public WebSearchTabViewModel(CliPreferences preferences, DialogService dialogSer this.doiPreferences = preferences.getDOIPreferences(); this.filePreferences = preferences.getFilePreferences(); this.importFormatPreferences = preferences.getImportFormatPreferences(); + this.libraryPreferences = preferences.getLibraryPreferences(); this.refAiEnabled = refAiEnabled; @@ -128,6 +134,8 @@ public void setValues() { warnAboutDuplicatesOnImportProperty.setValue(importerPreferences.shouldWarnAboutDuplicatesOnImport()); shouldDownloadLinkedOnlineFiles.setValue(filePreferences.shouldDownloadLinkedFiles()); shouldkeepDownloadUrl.setValue(filePreferences.shouldKeepDownloadUrl()); + addImportedEntries.setValue(libraryPreferences.isAddImportedEntriesEnabled()); + addImportedEntriesGroupName.setValue(libraryPreferences.getAddImportedEntriesGroupName()); defaultPlainCitationParser.setValue(importerPreferences.getDefaultPlainCitationParser()); useCustomDOIProperty.setValue(doiPreferences.isUseCustom()); @@ -159,7 +167,14 @@ public void storeSettings() { importerPreferences.setWarnAboutDuplicatesOnImport(warnAboutDuplicatesOnImportProperty.getValue()); filePreferences.setDownloadLinkedFiles(shouldDownloadLinkedOnlineFiles.getValue()); filePreferences.setKeepDownloadUrl(shouldkeepDownloadUrl.getValue()); + libraryPreferences.setAddImportedEntries(addImportedEntries.getValue()); + if (addImportedEntriesGroupName.getValue().isEmpty() || addImportedEntriesGroupName.getValue().startsWith(" ")) { + libraryPreferences.setAddImportedEntriesGroupName(Localization.lang("Imported entries")); + } else { + libraryPreferences.setAddImportedEntriesGroupName(addImportedEntriesGroupName.getValue()); + } importerPreferences.setDefaultPlainCitationParser(defaultPlainCitationParser.getValue()); + grobidPreferences.setGrobidEnabled(grobidEnabledProperty.getValue()); grobidPreferences.setGrobidUseAsked(grobidPreferences.isGrobidUseAsked()); grobidPreferences.setGrobidURL(grobidURLProperty.getValue()); @@ -189,6 +204,14 @@ public ObjectProperty defaultPlainCitationParserPrope return defaultPlainCitationParser; } + public BooleanProperty getAddImportedEntries() { + return addImportedEntries; + } + + public StringProperty getAddImportedEntriesGroupName() { + return addImportedEntriesGroupName; + } + public BooleanProperty useCustomDOIProperty() { return this.useCustomDOIProperty; } diff --git a/jabgui/src/main/resources/org/jabref/gui/preferences/websearch/WebSearchTab.fxml b/jabgui/src/main/resources/org/jabref/gui/preferences/websearch/WebSearchTab.fxml index 574112bc8ab..6675f474304 100644 --- a/jabgui/src/main/resources/org/jabref/gui/preferences/websearch/WebSearchTab.fxml +++ b/jabgui/src/main/resources/org/jabref/gui/preferences/websearch/WebSearchTab.fxml @@ -21,6 +21,10 @@ + + + +