diff --git a/CHANGELOG.md b/CHANGELOG.md index 52485dda5ce..c015c7ba552 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv - We added support for import of a Refer/BibIX file format. [#13069](https://github.com/JabRef/jabref/issues/13069) - We added a new `jabkit` command `pseudonymize` to pseudonymize the library. [#13109](https://github.com/JabRef/jabref/issues/13109) - We added functionality to focus running instance when trying to start a second instance. [#13129](https://github.com/JabRef/jabref/issues/13129) +- We added a "Copy Field Content" submenu to the entry context menu, allowing users to quickly copy specific field contents including Author, Journal, Date, Keywords, and Abstract fields from selected entries. - We added a highlighted diff regarding changes to the Group Tree Structure of a bib file, made outside JabRef. [#11221](https://github.com/JabRef/jabref/issues/11221) - We added a new setting in the 'Entry Editor' preferences to hide the 'File Annotations' tab when no annotations are available. [#13143](https://github.com/JabRef/jabref/issues/13143) - We added support for multi-file import across different formats. [#13269](https://github.com/JabRef/jabref/issues/13269) diff --git a/jabgui/src/main/java/org/jabref/gui/actions/StandardActions.java b/jabgui/src/main/java/org/jabref/gui/actions/StandardActions.java index 446511c7857..2899753e599 100644 --- a/jabgui/src/main/java/org/jabref/gui/actions/StandardActions.java +++ b/jabgui/src/main/java/org/jabref/gui/actions/StandardActions.java @@ -20,6 +20,12 @@ public enum StandardActions implements Action { COPY_CITATION_HTML(Localization.lang("Copy citation (html)"), KeyBinding.COPY_PREVIEW), COPY_CITATION_TEXT(Localization.lang("Copy citation (text)")), COPY_CITATION_PREVIEW(Localization.lang("Copy preview"), KeyBinding.COPY_PREVIEW), + COPY_FIELD_CONTENT(Localization.lang("Copy field content")), + COPY_FIELD_AUTHOR(Localization.lang("Author")), + COPY_FIELD_JOURNAL(Localization.lang("Journal")), + COPY_FIELD_DATE(Localization.lang("Date")), + COPY_FIELD_KEYWORDS(Localization.lang("Keywords")), + COPY_FIELD_ABSTRACT(Localization.lang("Abstract")), EXPORT_TO_CLIPBOARD(Localization.lang("Export to clipboard"), IconTheme.JabRefIcons.EXPORT_TO_CLIPBOARD), EXPORT_SELECTED_TO_CLIPBOARD(Localization.lang("Export selected entries to clipboard"), IconTheme.JabRefIcons.EXPORT_TO_CLIPBOARD), COPY(Localization.lang("Copy"), IconTheme.JabRefIcons.COPY, KeyBinding.COPY), diff --git a/jabgui/src/main/java/org/jabref/gui/edit/CopyMoreAction.java b/jabgui/src/main/java/org/jabref/gui/edit/CopyMoreAction.java index 80789f0c5fa..1fe50739ea6 100644 --- a/jabgui/src/main/java/org/jabref/gui/edit/CopyMoreAction.java +++ b/jabgui/src/main/java/org/jabref/gui/edit/CopyMoreAction.java @@ -23,6 +23,7 @@ import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.field.StandardField; +import org.jabref.model.strings.LatexToUnicodeAdapter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -72,6 +73,16 @@ public void execute() { copyKeyAndLink(); case COPY_DOI, COPY_DOI_URL -> copyDoi(); + case COPY_FIELD_AUTHOR -> + copyField(StandardField.AUTHOR, Localization.lang("Author")); + case COPY_FIELD_JOURNAL -> + copyField(StandardField.JOURNAL, Localization.lang("Journal")); + case COPY_FIELD_DATE -> + copyField(StandardField.DATE, Localization.lang("Date")); + case COPY_FIELD_KEYWORDS -> + copyField(StandardField.KEYWORDS, Localization.lang("Keywords")); + case COPY_FIELD_ABSTRACT -> + copyField(StandardField.ABSTRACT, Localization.lang("Abstract")); default -> LOGGER.info("Unknown copy command."); } @@ -82,7 +93,7 @@ private void copyTitle() { List titles = selectedBibEntries.stream() .filter(bibEntry -> bibEntry.getTitle().isPresent()) - .map(bibEntry -> bibEntry.getTitle().get()) + .map(bibEntry -> LatexToUnicodeAdapter.format(bibEntry.getTitle().get())) .collect(Collectors.toList()); if (titles.isEmpty()) { @@ -264,4 +275,32 @@ private void copyKeyAndLink() { Long.toString(entries.size() - entriesWithKey.size()), Integer.toString(entries.size()))); } } + + private void copyField(StandardField field, String fieldDisplayName) { + List selectedBibEntries = stateManager.getSelectedEntries(); + + List fieldValues = selectedBibEntries.stream() + .filter(bibEntry -> bibEntry.getFieldOrAlias(field).isPresent()) + .map(bibEntry -> LatexToUnicodeAdapter.format(bibEntry.getFieldOrAlias(field).orElse(""))) + .filter(value -> !value.isEmpty()) + .collect(Collectors.toList()); + + if (fieldValues.isEmpty()) { + dialogService.notify(Localization.lang("None of the selected entries have %0.", fieldDisplayName)); + return; + } + + final String copiedContent = fieldValues.stream().collect(Collectors.joining("\n")); + clipBoardManager.setContent(copiedContent); + + if (fieldValues.size() == selectedBibEntries.size()) { + dialogService.notify(Localization.lang("Copied '%0' to clipboard.", + JabRefDialogService.shortenDialogMessage(copiedContent))); + } else { + dialogService.notify(Localization.lang("Warning: %0 out of %1 entries have undefined %2.", + Integer.toString(selectedBibEntries.size() - fieldValues.size()), + Integer.toString(selectedBibEntries.size()), + fieldDisplayName)); + } + } } diff --git a/jabgui/src/main/java/org/jabref/gui/maintable/RightClickMenu.java b/jabgui/src/main/java/org/jabref/gui/maintable/RightClickMenu.java index 5a9c93e6cac..da210ccce9e 100644 --- a/jabgui/src/main/java/org/jabref/gui/maintable/RightClickMenu.java +++ b/jabgui/src/main/java/org/jabref/gui/maintable/RightClickMenu.java @@ -178,6 +178,8 @@ private static Menu createCopySubMenu(ActionFactory factory, copySpecialMenu.getItems().addAll( factory.createMenuItem(StandardActions.COPY_TITLE, new CopyMoreAction(StandardActions.COPY_TITLE, dialogService, stateManager, clipBoardManager, preferences, abbreviationRepository)), + createCopyFieldContentSubMenu(factory, dialogService, stateManager, clipBoardManager, preferences, abbreviationRepository), + new SeparatorMenuItem(), factory.createMenuItem(StandardActions.COPY_KEY, new CopyMoreAction(StandardActions.COPY_KEY, dialogService, stateManager, clipBoardManager, preferences, abbreviationRepository)), factory.createMenuItem(StandardActions.COPY_CITE_KEY, new CopyMoreAction(StandardActions.COPY_CITE_KEY, dialogService, stateManager, clipBoardManager, preferences, abbreviationRepository)), factory.createMenuItem(StandardActions.COPY_KEY_AND_TITLE, new CopyMoreAction(StandardActions.COPY_KEY_AND_TITLE, dialogService, stateManager, clipBoardManager, preferences, abbreviationRepository)), @@ -204,6 +206,25 @@ private static Menu createCopySubMenu(ActionFactory factory, return copySpecialMenu; } + private static Menu createCopyFieldContentSubMenu(ActionFactory factory, + DialogService dialogService, + StateManager stateManager, + ClipBoardManager clipBoardManager, + GuiPreferences preferences, + JournalAbbreviationRepository abbreviationRepository) { + Menu copyFieldContentMenu = factory.createMenu(StandardActions.COPY_FIELD_CONTENT); + + copyFieldContentMenu.getItems().addAll( + factory.createMenuItem(StandardActions.COPY_FIELD_AUTHOR, new CopyMoreAction(StandardActions.COPY_FIELD_AUTHOR, dialogService, stateManager, clipBoardManager, preferences, abbreviationRepository)), + factory.createMenuItem(StandardActions.COPY_FIELD_JOURNAL, new CopyMoreAction(StandardActions.COPY_FIELD_JOURNAL, dialogService, stateManager, clipBoardManager, preferences, abbreviationRepository)), + factory.createMenuItem(StandardActions.COPY_FIELD_DATE, new CopyMoreAction(StandardActions.COPY_FIELD_DATE, dialogService, stateManager, clipBoardManager, preferences, abbreviationRepository)), + factory.createMenuItem(StandardActions.COPY_FIELD_KEYWORDS, new CopyMoreAction(StandardActions.COPY_FIELD_KEYWORDS, dialogService, stateManager, clipBoardManager, preferences, abbreviationRepository)), + factory.createMenuItem(StandardActions.COPY_FIELD_ABSTRACT, new CopyMoreAction(StandardActions.COPY_FIELD_ABSTRACT, dialogService, stateManager, clipBoardManager, preferences, abbreviationRepository)) + ); + + return copyFieldContentMenu; + } + private static Menu createSendSubMenu(ActionFactory factory, DialogService dialogService, StateManager stateManager, diff --git a/jabgui/src/test/java/org/jabref/gui/edit/CopyMoreActionTest.java b/jabgui/src/test/java/org/jabref/gui/edit/CopyMoreActionTest.java index 9a5b9743660..977d3862834 100644 --- a/jabgui/src/test/java/org/jabref/gui/edit/CopyMoreActionTest.java +++ b/jabgui/src/test/java/org/jabref/gui/edit/CopyMoreActionTest.java @@ -3,6 +3,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Optional; +import java.util.StringJoiner; import javafx.collections.FXCollections; import javafx.collections.ObservableList; @@ -43,6 +44,11 @@ class CopyMoreActionTest { private final List titles = new ArrayList<>(); private final List keys = new ArrayList<>(); private final List dois = new ArrayList<>(); + private final List authors = new ArrayList<>(); + private final List journals = new ArrayList<>(); + private final List dates = new ArrayList<>(); + private final List keywords = new ArrayList<>(); + private final List abstracts = new ArrayList<>(); private CopyMoreAction copyMoreAction; private BibEntry entry; @@ -50,16 +56,31 @@ class CopyMoreActionTest { @BeforeEach void setUp() { String title = "A tale from the trenches"; + String author = "Souti Chattopadhyay and Nicholas Nelson and Audrey Au and Natalia Morales and Christopher Sanchez and Rahul Pandita and Anita Sarma"; + String journal = "Journal of the American College of Nutrition"; + String date = "2001-10"; + String keyword = "software engineering, cognitive bias, empirical study"; + String abstractText = "This paper presents a study on cognitive biases in software development."; + entry = new BibEntry(StandardEntryType.Misc) - .withField(StandardField.AUTHOR, "Souti Chattopadhyay and Nicholas Nelson and Audrey Au and Natalia Morales and Christopher Sanchez and Rahul Pandita and Anita Sarma") + .withField(StandardField.AUTHOR, author) .withField(StandardField.TITLE, title) .withField(StandardField.YEAR, "2020") + .withField(StandardField.JOURNAL, journal) + .withField(StandardField.DATE, date) + .withField(StandardField.KEYWORDS, keyword) + .withField(StandardField.ABSTRACT, abstractText) .withField(StandardField.DOI, "10.1145/3377811.3380330") .withField(StandardField.SUBTITLE, "cognitive biases and software development") .withCitationKey("abc"); titles.add(title); keys.add("abc"); dois.add("10.1145/3377811.3380330"); + authors.add(author); + journals.add(journal); + dates.add(date); + keywords.add(keyword); + abstracts.add(abstractText); ExternalApplicationsPreferences externalApplicationsPreferences = mock(ExternalApplicationsPreferences.class); when(externalApplicationsPreferences.getCiteCommand()).thenReturn(new CitationCommandString("\\cite{", ",", "}")); @@ -226,4 +247,298 @@ void executeCopyDoiOnSuccess() { verify(dialogService, times(1)).notify(Localization.lang("Copied '%0' to clipboard.", JabRefDialogService.shortenDialogMessage(copiedDois))); } + + @Test + void executeCopyAuthorWithNoAuthor() { + BibEntry entryWithNoAuthor = (BibEntry) entry.clone(); + entryWithNoAuthor.clearField(StandardField.AUTHOR); + ObservableList entriesWithNoAuthors = FXCollections.observableArrayList(entryWithNoAuthor); + BibDatabaseContext databaseContext = new BibDatabaseContext(new BibDatabase(entriesWithNoAuthors)); + + when(stateManager.getActiveDatabase()).thenReturn(Optional.ofNullable(databaseContext)); + when(stateManager.getSelectedEntries()).thenReturn(entriesWithNoAuthors); + copyMoreAction = new CopyMoreAction(StandardActions.COPY_FIELD_AUTHOR, dialogService, stateManager, clipBoardManager, preferences, abbreviationRepository); + copyMoreAction.execute(); + + verify(clipBoardManager, times(0)).setContent(any(String.class)); + verify(dialogService, times(1)).notify(Localization.lang("None of the selected entries have %0.", "Author")); + } + + @Test + void executeCopyAuthorOnPartialSuccess() { + BibEntry entryWithNoAuthor = (BibEntry) entry.clone(); + entryWithNoAuthor.clearField(StandardField.AUTHOR); + ObservableList mixedEntries = FXCollections.observableArrayList(entryWithNoAuthor, entry); + BibDatabaseContext databaseContext = new BibDatabaseContext(new BibDatabase(mixedEntries)); + + when(stateManager.getActiveDatabase()).thenReturn(Optional.ofNullable(databaseContext)); + when(stateManager.getSelectedEntries()).thenReturn(mixedEntries); + copyMoreAction = new CopyMoreAction(StandardActions.COPY_FIELD_AUTHOR, dialogService, stateManager, clipBoardManager, preferences, abbreviationRepository); + copyMoreAction.execute(); + + StringJoiner authorsJoiner = new StringJoiner("\n"); + for (String author : authors) { + authorsJoiner.add(author); + } + String copiedAuthors = authorsJoiner.toString(); + verify(clipBoardManager, times(1)).setContent(copiedAuthors); + verify(dialogService, times(1)).notify(Localization.lang("Warning: %0 out of %1 entries have undefined %2.", + Integer.toString(mixedEntries.size() - authors.size()), Integer.toString(mixedEntries.size()), "Author")); + } + + @Test + void executeCopyAuthorOnSuccess() { + ObservableList entriesWithAuthors = FXCollections.observableArrayList(entry); + BibDatabaseContext databaseContext = new BibDatabaseContext(new BibDatabase(entriesWithAuthors)); + + when(stateManager.getActiveDatabase()).thenReturn(Optional.ofNullable(databaseContext)); + when(stateManager.getSelectedEntries()).thenReturn(entriesWithAuthors); + copyMoreAction = new CopyMoreAction(StandardActions.COPY_FIELD_AUTHOR, dialogService, stateManager, clipBoardManager, preferences, abbreviationRepository); + copyMoreAction.execute(); + + StringJoiner authorsJoiner = new StringJoiner("\n"); + for (String author : authors) { + authorsJoiner.add(author); + } + String copiedAuthors = authorsJoiner.toString(); + verify(clipBoardManager, times(1)).setContent(copiedAuthors); + verify(dialogService, times(1)).notify(Localization.lang("Copied '%0' to clipboard.", + JabRefDialogService.shortenDialogMessage(copiedAuthors))); + } + + @Test + void executeCopyJournalWithNoJournal() { + BibEntry entryWithNoJournal = (BibEntry) entry.clone(); + entryWithNoJournal.clearField(StandardField.JOURNAL); + entryWithNoJournal.clearField(StandardField.JOURNALTITLE); + ObservableList entriesWithNoJournals = FXCollections.observableArrayList(entryWithNoJournal); + BibDatabaseContext databaseContext = new BibDatabaseContext(new BibDatabase(entriesWithNoJournals)); + + when(stateManager.getActiveDatabase()).thenReturn(Optional.ofNullable(databaseContext)); + when(stateManager.getSelectedEntries()).thenReturn(entriesWithNoJournals); + copyMoreAction = new CopyMoreAction(StandardActions.COPY_FIELD_JOURNAL, dialogService, stateManager, clipBoardManager, preferences, abbreviationRepository); + copyMoreAction.execute(); + + verify(clipBoardManager, times(0)).setContent(any(String.class)); + verify(dialogService, times(1)).notify(Localization.lang("None of the selected entries have %0.", "Journal")); + } + + @Test + void executeCopyJournalOnPartialSuccess() { + BibEntry entryWithNoJournal = (BibEntry) entry.clone(); + entryWithNoJournal.clearField(StandardField.JOURNAL); + entryWithNoJournal.clearField(StandardField.JOURNALTITLE); + ObservableList mixedEntries = FXCollections.observableArrayList(entryWithNoJournal, entry); + BibDatabaseContext databaseContext = new BibDatabaseContext(new BibDatabase(mixedEntries)); + + when(stateManager.getActiveDatabase()).thenReturn(Optional.ofNullable(databaseContext)); + when(stateManager.getSelectedEntries()).thenReturn(mixedEntries); + copyMoreAction = new CopyMoreAction(StandardActions.COPY_FIELD_JOURNAL, dialogService, stateManager, clipBoardManager, preferences, abbreviationRepository); + copyMoreAction.execute(); + + StringJoiner journalsJoiner = new StringJoiner("\n"); + for (String journal : journals) { + journalsJoiner.add(journal); + } + String copiedJournals = journalsJoiner.toString(); + verify(clipBoardManager, times(1)).setContent(copiedJournals); + verify(dialogService, times(1)).notify(Localization.lang("Warning: %0 out of %1 entries have undefined %2.", + Integer.toString(mixedEntries.size() - journals.size()), Integer.toString(mixedEntries.size()), "Journal")); + } + + @Test + void executeCopyJournalOnSuccess() { + ObservableList entriesWithJournals = FXCollections.observableArrayList(entry); + BibDatabaseContext databaseContext = new BibDatabaseContext(new BibDatabase(entriesWithJournals)); + + when(stateManager.getActiveDatabase()).thenReturn(Optional.ofNullable(databaseContext)); + when(stateManager.getSelectedEntries()).thenReturn(entriesWithJournals); + copyMoreAction = new CopyMoreAction(StandardActions.COPY_FIELD_JOURNAL, dialogService, stateManager, clipBoardManager, preferences, abbreviationRepository); + copyMoreAction.execute(); + + StringJoiner journalsJoiner = new StringJoiner("\n"); + for (String journal : journals) { + journalsJoiner.add(journal); + } + String copiedJournals = journalsJoiner.toString(); + verify(clipBoardManager, times(1)).setContent(copiedJournals); + verify(dialogService, times(1)).notify(Localization.lang("Copied '%0' to clipboard.", + JabRefDialogService.shortenDialogMessage(copiedJournals))); + } + + @Test + void executeCopyDateWithNoDate() { + BibEntry entryWithNoDate = (BibEntry) entry.clone(); + entryWithNoDate.clearField(StandardField.DATE); + entryWithNoDate.clearField(StandardField.YEAR); + ObservableList entriesWithNoDates = FXCollections.observableArrayList(entryWithNoDate); + BibDatabaseContext databaseContext = new BibDatabaseContext(new BibDatabase(entriesWithNoDates)); + + when(stateManager.getActiveDatabase()).thenReturn(Optional.ofNullable(databaseContext)); + when(stateManager.getSelectedEntries()).thenReturn(entriesWithNoDates); + copyMoreAction = new CopyMoreAction(StandardActions.COPY_FIELD_DATE, dialogService, stateManager, clipBoardManager, preferences, abbreviationRepository); + copyMoreAction.execute(); + + verify(clipBoardManager, times(0)).setContent(any(String.class)); + verify(dialogService, times(1)).notify(Localization.lang("None of the selected entries have %0.", "Date")); + } + + @Test + void executeCopyDateOnPartialSuccess() { + BibEntry entryWithNoDate = (BibEntry) entry.clone(); + entryWithNoDate.clearField(StandardField.DATE); + entryWithNoDate.clearField(StandardField.YEAR); + ObservableList mixedEntries = FXCollections.observableArrayList(entryWithNoDate, entry); + BibDatabaseContext databaseContext = new BibDatabaseContext(new BibDatabase(mixedEntries)); + + when(stateManager.getActiveDatabase()).thenReturn(Optional.ofNullable(databaseContext)); + when(stateManager.getSelectedEntries()).thenReturn(mixedEntries); + copyMoreAction = new CopyMoreAction(StandardActions.COPY_FIELD_DATE, dialogService, stateManager, clipBoardManager, preferences, abbreviationRepository); + copyMoreAction.execute(); + + StringJoiner datesJoiner = new StringJoiner("\n"); + for (String date : dates) { + datesJoiner.add(date); + } + String copiedDates = datesJoiner.toString(); + verify(clipBoardManager, times(1)).setContent(copiedDates); + verify(dialogService, times(1)).notify(Localization.lang("Warning: %0 out of %1 entries have undefined %2.", + Integer.toString(mixedEntries.size() - dates.size()), Integer.toString(mixedEntries.size()), "Date")); + } + + @Test + void executeCopyDateOnSuccess() { + ObservableList entriesWithDates = FXCollections.observableArrayList(entry); + BibDatabaseContext databaseContext = new BibDatabaseContext(new BibDatabase(entriesWithDates)); + + when(stateManager.getActiveDatabase()).thenReturn(Optional.ofNullable(databaseContext)); + when(stateManager.getSelectedEntries()).thenReturn(entriesWithDates); + copyMoreAction = new CopyMoreAction(StandardActions.COPY_FIELD_DATE, dialogService, stateManager, clipBoardManager, preferences, abbreviationRepository); + copyMoreAction.execute(); + + StringJoiner datesJoiner = new StringJoiner("\n"); + for (String date : dates) { + datesJoiner.add(date); + } + String copiedDates = datesJoiner.toString(); + verify(clipBoardManager, times(1)).setContent(copiedDates); + verify(dialogService, times(1)).notify(Localization.lang("Copied '%0' to clipboard.", + JabRefDialogService.shortenDialogMessage(copiedDates))); + } + + @Test + void executeCopyKeywordsWithNoKeywords() { + BibEntry entryWithNoKeywords = (BibEntry) entry.clone(); + entryWithNoKeywords.clearField(StandardField.KEYWORDS); + ObservableList entriesWithNoKeywords = FXCollections.observableArrayList(entryWithNoKeywords); + BibDatabaseContext databaseContext = new BibDatabaseContext(new BibDatabase(entriesWithNoKeywords)); + + when(stateManager.getActiveDatabase()).thenReturn(Optional.ofNullable(databaseContext)); + when(stateManager.getSelectedEntries()).thenReturn(entriesWithNoKeywords); + copyMoreAction = new CopyMoreAction(StandardActions.COPY_FIELD_KEYWORDS, dialogService, stateManager, clipBoardManager, preferences, abbreviationRepository); + copyMoreAction.execute(); + + verify(clipBoardManager, times(0)).setContent(any(String.class)); + verify(dialogService, times(1)).notify(Localization.lang("None of the selected entries have %0.", "Keywords")); + } + + @Test + void executeCopyKeywordsOnPartialSuccess() { + BibEntry entryWithNoKeywords = (BibEntry) entry.clone(); + entryWithNoKeywords.clearField(StandardField.KEYWORDS); + ObservableList mixedEntries = FXCollections.observableArrayList(entryWithNoKeywords, entry); + BibDatabaseContext databaseContext = new BibDatabaseContext(new BibDatabase(mixedEntries)); + + when(stateManager.getActiveDatabase()).thenReturn(Optional.ofNullable(databaseContext)); + when(stateManager.getSelectedEntries()).thenReturn(mixedEntries); + copyMoreAction = new CopyMoreAction(StandardActions.COPY_FIELD_KEYWORDS, dialogService, stateManager, clipBoardManager, preferences, abbreviationRepository); + copyMoreAction.execute(); + + StringJoiner keywordsJoiner = new StringJoiner("\n"); + for (String keyword : keywords) { + keywordsJoiner.add(keyword); + } + String copiedKeywords = keywordsJoiner.toString(); + verify(clipBoardManager, times(1)).setContent(copiedKeywords); + verify(dialogService, times(1)).notify(Localization.lang("Warning: %0 out of %1 entries have undefined %2.", + Integer.toString(mixedEntries.size() - keywords.size()), Integer.toString(mixedEntries.size()), "Keywords")); + } + + @Test + void executeCopyKeywordsOnSuccess() { + ObservableList entriesWithKeywords = FXCollections.observableArrayList(entry); + BibDatabaseContext databaseContext = new BibDatabaseContext(new BibDatabase(entriesWithKeywords)); + + when(stateManager.getActiveDatabase()).thenReturn(Optional.ofNullable(databaseContext)); + when(stateManager.getSelectedEntries()).thenReturn(entriesWithKeywords); + copyMoreAction = new CopyMoreAction(StandardActions.COPY_FIELD_KEYWORDS, dialogService, stateManager, clipBoardManager, preferences, abbreviationRepository); + copyMoreAction.execute(); + + StringJoiner keywordsJoiner = new StringJoiner("\n"); + for (String keyword : keywords) { + keywordsJoiner.add(keyword); + } + String copiedKeywords = keywordsJoiner.toString(); + verify(clipBoardManager, times(1)).setContent(copiedKeywords); + verify(dialogService, times(1)).notify(Localization.lang("Copied '%0' to clipboard.", + JabRefDialogService.shortenDialogMessage(copiedKeywords))); + } + + @Test + void executeCopyAbstractWithNoAbstract() { + BibEntry entryWithNoAbstract = (BibEntry) entry.clone(); + entryWithNoAbstract.clearField(StandardField.ABSTRACT); + ObservableList entriesWithNoAbstracts = FXCollections.observableArrayList(entryWithNoAbstract); + BibDatabaseContext databaseContext = new BibDatabaseContext(new BibDatabase(entriesWithNoAbstracts)); + + when(stateManager.getActiveDatabase()).thenReturn(Optional.ofNullable(databaseContext)); + when(stateManager.getSelectedEntries()).thenReturn(entriesWithNoAbstracts); + copyMoreAction = new CopyMoreAction(StandardActions.COPY_FIELD_ABSTRACT, dialogService, stateManager, clipBoardManager, preferences, abbreviationRepository); + copyMoreAction.execute(); + + verify(clipBoardManager, times(0)).setContent(any(String.class)); + verify(dialogService, times(1)).notify(Localization.lang("None of the selected entries have %0.", "Abstract")); + } + + @Test + void executeCopyAbstractOnPartialSuccess() { + BibEntry entryWithNoAbstract = (BibEntry) entry.clone(); + entryWithNoAbstract.clearField(StandardField.ABSTRACT); + ObservableList mixedEntries = FXCollections.observableArrayList(entryWithNoAbstract, entry); + BibDatabaseContext databaseContext = new BibDatabaseContext(new BibDatabase(mixedEntries)); + + when(stateManager.getActiveDatabase()).thenReturn(Optional.ofNullable(databaseContext)); + when(stateManager.getSelectedEntries()).thenReturn(mixedEntries); + copyMoreAction = new CopyMoreAction(StandardActions.COPY_FIELD_ABSTRACT, dialogService, stateManager, clipBoardManager, preferences, abbreviationRepository); + copyMoreAction.execute(); + + StringJoiner abstractsJoiner = new StringJoiner("\n"); + for (String abstractText : abstracts) { + abstractsJoiner.add(abstractText); + } + String copiedAbstracts = abstractsJoiner.toString(); + verify(clipBoardManager, times(1)).setContent(copiedAbstracts); + verify(dialogService, times(1)).notify(Localization.lang("Warning: %0 out of %1 entries have undefined %2.", + Integer.toString(mixedEntries.size() - abstracts.size()), Integer.toString(mixedEntries.size()), "Abstract")); + } + + @Test + void executeCopyAbstractOnSuccess() { + ObservableList entriesWithAbstracts = FXCollections.observableArrayList(entry); + BibDatabaseContext databaseContext = new BibDatabaseContext(new BibDatabase(entriesWithAbstracts)); + + when(stateManager.getActiveDatabase()).thenReturn(Optional.ofNullable(databaseContext)); + when(stateManager.getSelectedEntries()).thenReturn(entriesWithAbstracts); + copyMoreAction = new CopyMoreAction(StandardActions.COPY_FIELD_ABSTRACT, dialogService, stateManager, clipBoardManager, preferences, abbreviationRepository); + copyMoreAction.execute(); + + StringJoiner abstractsJoiner = new StringJoiner("\n"); + for (String abstractText : abstracts) { + abstractsJoiner.add(abstractText); + } + String copiedAbstracts = abstractsJoiner.toString(); + verify(clipBoardManager, times(1)).setContent(copiedAbstracts); + verify(dialogService, times(1)).notify(Localization.lang("Copied '%0' to clipboard.", + JabRefDialogService.shortenDialogMessage(copiedAbstracts))); + } } diff --git a/jablib/src/main/resources/l10n/JabRef_en.properties b/jablib/src/main/resources/l10n/JabRef_en.properties index 131266ce455..05503f2edaf 100644 --- a/jablib/src/main/resources/l10n/JabRef_en.properties +++ b/jablib/src/main/resources/l10n/JabRef_en.properties @@ -167,6 +167,7 @@ Content=Content Copy=Copy Copy\ title=Copy title +Copy\ field\ content=Copy field content Copy\ citation\ (html)=Copy citation (html) Copy\ citation\ (text)=Copy citation (text) Copy\ citation\ key=Copy citation key @@ -1117,6 +1118,7 @@ Problem\ collecting\ citations=Problem collecting citations Citation=Citation Connecting...=Connecting... Select\ style=Select style +Journal=Journal Journals=Journals Cite=Cite Cite\ in-text=Cite in-text @@ -1379,6 +1381,7 @@ Could\ not\ connect\ to\ %0=Could not connect to %0 Warning\:\ %0\ out\ of\ %1\ entries\ have\ undefined\ title.=Warning: %0 out of %1 entries have undefined title. Warning\:\ %0\ out\ of\ %1\ entries\ have\ undefined\ citation\ key.=Warning: %0 out of %1 entries have undefined citation key. Warning\:\ %0\ out\ of\ %1\ entries\ have\ undefined\ DOIs.=Warning: %0 out of %1 entries have undefined DOIs. +Warning\:\ %0\ out\ of\ %1\ entries\ have\ undefined\ %2.=Warning: %0 out of %1 entries have undefined %2. Really\ delete\ the\ selected\ entry?=Really delete the selected entry? Really\ delete\ the\ %0\ selected\ entries?=Really delete the %0 selected entries? @@ -1469,6 +1472,7 @@ Display\ keywords\ appearing\ in\ ANY\ entry=Display keywords appearing in ANY e None\ of\ the\ selected\ entries\ have\ titles.=None of the selected entries have titles. None\ of\ the\ selected\ entries\ have\ citation\ keys.=None of the selected entries have citation keys. None\ of\ the\ selected\ entries\ have\ DOIs.=None of the selected entries have DOIs. +None\ of\ the\ selected\ entries\ have\ %0.=None of the selected entries have %0. Unabbreviate\ journal\ names=Unabbreviate journal names Unabbreviating...=Unabbreviating...