diff --git a/CHANGELOG.md b/CHANGELOG.md index 6185098b064..cabfeff6abc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,23 +11,27 @@ We refer to [GitHub issues](https://github.com/JabRef/jabref/issues) by using `# ## [Unreleased] ### Changed -- We moved the `adsurl` field to `url` field when fetching with the ADS fetcher. +- We moved the `adsurl` field to `url` field when fetching with the ADS fetcher. - We continued to improve the new groups interface: - You can now again select multiple groups (and a few related settings were added to the preferences) [#2786](https://github.com/JabRef/jabref/issues/2786). - - We further improved performance of group operations, especially of the new filter feature [#2852](https://github.com/JabRef/jabref/issues/2852). + - We further improved performance of group operations, especially of the new filter feature [#2852](https://github.com/JabRef/jabref/issues/2852). - It is now possible to resort groups using drag & drop [#2785](https://github.com/JabRef/jabref/issues/2785). - The entry editor got a fresh coat of paint: - Homogenize the size of text fields. - The buttons were changed to icons. - Completely new interface to add or modify linked files. - Removed the hidden feature that a double click in the editor inserted the current date. + - Complete new implementation of the the auto complete feature. - All authors and editors are separated using semicolons when exporting to csv. [#2762](https://github.com/JabRef/jabref/issues/2762) - Improved wording of "Show recommendations: into "Show 'Related Articles' tab" in the preferences - We added integration of the Library of Congress catalog as a fetcher based on the [LCCN identifier](https://en.wikipedia.org/wiki/Library_of_Congress_Control_Number). [Feature request 636 in the forum](http://discourse.jabref.org/t/loc-marc-mods-connection/636) - The integrity check for person names now also tests that the names are specified in one of the standard BibTeX formats. - Links in the Recommended Articles tab (Mr.DLib), when clicked, are now opened in the system's default browser. [2931](https://github.com/JabRef/jabref/issues/2931) +- We improved the duplicate checker such that different editions of the same publication are not marked as duplicates. [2960](https://github.com/JabRef/jabref/issues/2960) ### Fixed +- We fixed a bug that leaves .sav file after SaveAs [#2947](https://github.com/JabRef/jabref/issues/2947) +- We fixed the function "Edit - Copy BibTeX key and link" to pass a hyperlink rather than an HTML statement. - We fixed the adding of a new entry from DOI which led to a connection error. The DOI resolution now uses HTTPS to protect the user's privacy.[#2879](https://github.com/JabRef/jabref/issues/2897) - We fixed the IEEE Xplore web search functionality [#2789](https://github.com/JabRef/jabref/issues/2789) - We fixed an error in the CrossRef fetcher that occurred if one of the fetched entries had no title @@ -38,6 +42,10 @@ We refer to [GitHub issues](https://github.com/JabRef/jabref/issues) by using `# - We fixed an error that prevented the FileAnnotation tab to load when the entry had no bibtexkey [#2903](https://github.com/JabRef/jabref/issues/2903). - We fixed a bug which which could result in an exception when opening/saving files from/to a nonexistent directory [#2917](https://github.com/JabRef/jabref/issues/2917). - We fixed a bug where recursive RegExpBased search found a file in a subdirectory multiple times and non-recursive RegExpBased search erroneously found files in subdirectories. +- We fixed a bug where new groups information was not stored on save [#2932](https://github.com/JabRef/jabref/issues/2932) +- We fixed a bug where the language files for Brazilian Portugese could not be loaded and the GUI localization remained in English [#1128](https://github.com/JabRef/jabref/issues/1182) +- We fixed a bug where the database was not marked as dirty when entries or groups were changed [#2787](https://github.com/JabRef/jabref/issues/2787) +- We restored the original functionality that when browsing through the MainTable, the Entry Editor remembers which tab was opened before [#2896](https://github.com/JabRef/jabref/issues/2896) ### Removed @@ -139,7 +147,7 @@ We refer to [GitHub issues](https://github.com/JabRef/jabref/issues) by using `# - JabRef will now no longer delete meta data it does not know, but keeps such entries and tries to keep their formatting as far as possible. - Switch to the [latex2unicode library](https://github.com/tomtung/latex2unicode) for converting LaTeX to unicode - Single underscores are not converted during the LaTeX to unicode conversion, which does not follow the rules of LaTeX, but is what users require. [#2664](https://github.com/JabRef/jabref/issues/2664) -- The bibtexkey field is not converted to unicode +- The bibtexkey field is not converted to unicode ### Fixed - ArXiV fetcher now checks similarity of entry when using DOI retrieval to avoid false positives [#2575](https://github.com/JabRef/jabref/issues/2575) @@ -155,7 +163,7 @@ We refer to [GitHub issues](https://github.com/JabRef/jabref/issues) by using `# - Sciencedirect/Elsevier fetcher is now able to scrape new HTML structure [#2576](https://github.com/JabRef/jabref/issues/2576) - Fixed the synchronization logic of keywords and special fields and vice versa [#2580](https://github.com/JabRef/jabref/issues/2580) - We fixed an exception that prevented JabRef from starting in rare cases [bug report in the forum](http://discourse.jabref.org/t/jabref-not-opening/476). - - We fixed an unhandled exception when saving an entry containing unbalanced braces [#2571](https://github.com/JabRef/jabref/issues/2571) + - We fixed an unhandled exception when saving an entry containing unbalanced braces [#2571](https://github.com/JabRef/jabref/issues/2571) - Fixed a display issue when removing a group with a long name [#1407](https://github.com/JabRef/jabref/issues/1407) - We fixed an issue where the "find unlinked files" functionality threw an error when only one PDF was imported but not assigned to an entry [#2577](https://github.com/JabRef/jabref/issues/2577) - We fixed issue where escaped braces were incorrectly counted when calculating brace balance in a field [#2561](https://github.com/JabRef/jabref/issues/2561) @@ -173,7 +181,7 @@ We refer to [GitHub issues](https://github.com/JabRef/jabref/issues) by using `# - We fixed an issue where the dialog for selecting the main file directory in the preferences opened the wrong folder - OpenOffice text formatting now handles nested tags properly [#2483](https://github.com/JabRef/jabref/issues/#2483) - The group selection is no longer lost when switching tabs [#1104](https://github.com/JabRef/jabref/issues/1104) - + ## [3.8.2] – 2017-01-29 @@ -299,7 +307,7 @@ We refer to [GitHub issues](https://github.com/JabRef/jabref/issues) by using `# - Fields linking to other entries (e.g., `crossref` and `related`) have now specialized editors in the entry editor. Check the tabs "Other fields" and "General". - [#1496](https://github.com/JabRef/jabref/issues/1496) Keep track of which entry a downloaded file belongs to - Made it possible to download multiple entries in one action -- [#1506](https://github.com/JabRef/jabref/issues/1506) It is possible to apply two new key modifier `title_case` for Title Case, `capitalize` for Capitalized first character of each word (difference is that title case will leave prepositions etc in lower case), and `sentence_case` for normal sentence case (first word capitalized). In addition `lower_case` and `upper_case` can be used instead of `lower` and `upper`. +- [#1506](https://github.com/JabRef/jabref/issues/1506) It is possible to apply two new key modifier `title_case` for Title Case, `capitalize` for Capitalized first character of each word (difference is that title case will leave prepositions etc in lower case), and `sentence_case` for normal sentence case (first word capitalized). In addition `lower_case` and `upper_case` can be used instead of `lower` and `upper`. - Added two new pseudo-fields for search: `anykeyword` to search for a specific keyword and `anyfield` to search in all fields (useful in combination with search in specific fields) - [#1813](https://github.com/JabRef/jabref/issues/1813) Import/Export preferences dialog default directory set to working directory - [#1897](https://github.com/JabRef/jabref/issues/1897) Implemented integrity check for `year` field: Last four nonpunctuation characters should be numerals @@ -531,7 +539,7 @@ We refer to [GitHub issues](https://github.com/JabRef/jabref/issues) by using `# - Added \SOFTWARE\Jabref 'Path' registry entry for installation path inside the installer - Added an additional icon to distinguish DOI and URL links ([feature request #696](https://github.com/JabRef/jabref/issues/696)) - Added nbib fields to Medlineplain importer and to MedlineImporter -- Implemented [#1342](https://github.com/JabRef/jabref/issues/1342): show description of case converters as tooltip +- Implemented [#1342](https://github.com/JabRef/jabref/issues/1342): show description of case converters as tooltip - Updated German translation ### Fixed @@ -540,8 +548,8 @@ We refer to [GitHub issues](https://github.com/JabRef/jabref/issues) by using `# - Fixed [#1234](https://github.com/JabRef/jabref/issues/1234): NPE when getting information from retrieved DOI - Fixed [#1245](https://github.com/JabRef/jabref/issues/1245): Empty jstyle properties can now be specified as "" - Fixed [#1259](https://github.com/JabRef/jabref/issues/1259): NPE when sorting tabs -- Fixed display bug in the cleanup dialog: field formatters are now correctly displayed using their name -- Fixed [#1271](https://github.com/JabRef/jabref/issues/1271): Authors with compound first names are displayed properly +- Fixed display bug in the cleanup dialog: field formatters are now correctly displayed using their name +- Fixed [#1271](https://github.com/JabRef/jabref/issues/1271): Authors with compound first names are displayed properly - Fixed: Selecting invalid jstyle causes NPE and prevents opening of style selection dialog - Fixed: Move linked files to default directory works again - Fixed [#1327](https://github.com/JabRef/jabref/issues/1327): PDF cleanup changes order of linked pdfs @@ -591,7 +599,7 @@ We refer to [GitHub issues](https://github.com/JabRef/jabref/issues) by using `# - Add ability to run arbitrary formatters as cleanup actions (some old cleanup jobs are replaced by this functionality) - Add "Move linked files to default file directory" as cleanup procedure - Implemented [#756](https://github.com/JabRef/jabref/issues/756): Add possibility to reformat all entries on save (under Preferences, File) -- All fields in a bib entry are written without any leading and trailing whitespace +- All fields in a bib entry are written without any leading and trailing whitespace - Comments and preamble are serialized with capitalized first letter, i.e. `@Comment` instead of `@comment` and `@Preamble` instead of `@PREAMBLE`. - Global sorting options and preferences are removed. Databases can still be sorted on save, but this is configured locally and stored in the file - OvidImporter now also imports fields: doi, issn, language and keywords @@ -720,7 +728,7 @@ We refer to [GitHub issues](https://github.com/JabRef/jabref/issues) by using `# - Implements [#565](https://github.com/JabRef/jabref/issues/565): Highlighting matches works now also for regular expressions in preview panel and entry editor - IEEEXplore search now downloads the full Bibtex record instead of parsing the fields from the HTML webpage result (fixes [bug 1146](https://sourceforge.net/p/jabref/bugs/1146/) and [bug 1267](https://sourceforge.net/p/jabref/bugs/1267/)) - Christmas color theme (red and green) -- Implements #444: The search is cleared by either clicking the clear-button or by pressing ESC with having focus in the search field. +- Implements #444: The search is cleared by either clicking the clear-button or by pressing ESC with having focus in the search field. - Added command line switch --debug to show more detailed logging messages ### Fixed @@ -780,8 +788,8 @@ We refer to [GitHub issues](https://github.com/JabRef/jabref/issues) by using `# - Search options are available via a drop-down list, this implements [feature request 853](https://sourceforge.net/p/jabref/feature-requests/853/) - "Clear search" button also clears search field, this implements [feature request 601](https://sourceforge.net/p/jabref/feature-requests/601/) - Every search is done automatically (live) as soon as the search text is changed - - Search is local by default. To do a global search, one has to do a local search and then this search can be done globally as well, opening a new window. - - The local search results can be shown in a new window. + - Search is local by default. To do a global search, one has to do a local search and then this search can be done globally as well, opening a new window. + - The local search results can be shown in a new window. - Feature: Merge information from a DOI generated BibTex entry to an entry - Added more characters to HTML/Unicode converter - Feature: Push citations to Texmaker ([bug 318](https://sourceforge.net/p/jabref/bugs/318/), [bug 582](https://sourceforge.net/p/jabref/bugs/582/)) @@ -817,7 +825,7 @@ We refer to [GitHub issues](https://github.com/JabRef/jabref/issues) by using `# - Update supported LookAndFeels - Show replaced journal abbreviations on console - Integrated [GVK-Plugin](http://www.gbv.de/wikis/cls/Jabref-GVK-Plugin) - - The three options to manage file references are moved to their own separated group in the Tools menu. + - The three options to manage file references are moved to their own separated group in the Tools menu. - Default preferences: Remote server (port 6050) always started on first JabRef instance. This prevents JabRef loaded twice when opening a bib file. ### Fixed diff --git a/build.gradle b/build.gradle index ac55b99e8d4..9fa1c4d9b34 100644 --- a/build.gradle +++ b/build.gradle @@ -114,7 +114,10 @@ dependencies { compile 'de.codecentric.centerdevice:javafxsvg:1.2.1' compile 'org.controlsfx:controlsfx:8.40.12' compile 'org.fxmisc.easybind:easybind:1.0.3' - compile 'net.corda:jfx:0.12.1' + compile('net.corda:jfx:0.12.1') { + transitive = false + } + compile 'org.jetbrains.kotlin:kotlin-stdlib:1.1.2' // required by net.corda:jfxc compile 'org.fxmisc.flowless:flowless:0.5.2' compile 'de.jensd:fontawesomefx-materialdesignfont:1.7.22-4' compile 'org.fxmisc.richtext:richtextfx:0.7-M5' @@ -139,7 +142,7 @@ dependencies { compile 'org.citationstyles:locales:1.0.1-SNAPSHOT' compile 'de.undercouch:citeproc-java:1.0.1' - compile 'com.github.tomtung:latex2unicode_2.12:0.2' + compile 'com.github.tomtung:latex2unicode_2.12:0.2.1' compile group: 'com.microsoft.azure', name: 'applicationinsights-core', version: '1.0.+' compile group: 'com.microsoft.azure', name: 'applicationinsights-logging-log4j2', version: '1.0.+' @@ -391,9 +394,12 @@ task media(type: com.install4j.gradle.Install4jTask, dependsOn: "releaseJar") { checkstyle { // do not use other packages for checkstyle, excluding gen(erated) sources checkstyleMain.source = "src/main/java" - toolVersion = '7.6.1' + toolVersion = '8.0' } +checkstyleMain.shouldRunAfter test +checkstyleTest.shouldRunAfter test + task release(dependsOn: ["media", "releaseJar"]) { group = 'JabRef - Release' description 'Creates a release for all target platforms.' diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml index fa31823df20..20cbcee3f60 100644 --- a/config/checkstyle/checkstyle.xml +++ b/config/checkstyle/checkstyle.xml @@ -45,6 +45,10 @@ PLUS, PLUS_ASSIGN, QUESTION, SL, SLIST, SL_ASSIGN, SR, SR_ASSIGN, STAR, STAR_ASSIGN, LITERAL_ASSERT, TYPE_EXTENSION_AND"/> + + + + diff --git a/config/eclipseJabRef.xml b/config/eclipseJabRef.xml new file mode 100644 index 00000000000..55862811cd5 --- /dev/null +++ b/config/eclipseJabRef.xml @@ -0,0 +1,314 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/eclipse.gradle b/eclipse.gradle index ef3791bb12a..771789957fb 100644 --- a/eclipse.gradle +++ b/eclipse.gradle @@ -216,7 +216,7 @@ tasks.eclipse.doFirst { org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true - org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false + org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=true org.eclipse.jdt.core.formatter.indentation.size=4 org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=do not insert org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert @@ -413,6 +413,11 @@ tasks.eclipse.doFirst { org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0 org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1 org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true + org.eclipse.jdt.core.formatter.parentheses_positions_in_for_statment=common_lines + org.eclipse.jdt.core.formatter.parentheses_positions_in_method_invocationc=common_lines + org.eclipse.jdt.core.formatter.parentheses_positions_in_switch_statement=common_lines + org.eclipse.jdt.core.formatter.parentheses_positions_in_enum_constant_declaration=common_lines + org.eclipse.jdt.core.formatter.parentheses_positions_in_if_while_statement=common_lines org.eclipse.jdt.core.formatter.tabulation.char=space org.eclipse.jdt.core.formatter.tabulation.size=4 org.eclipse.jdt.core.formatter.use_on_off_tags=true diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 16209f02c0c..f3ddf67c69f 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 0ca301acdf9..2fab324e0c5 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Thu Jun 29 11:30:01 EEST 2017 +#Sun Jul 09 12:08:20 CEST 2017 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.0-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.0.1-bin.zip diff --git a/gradlew b/gradlew index 4453ccea33d..cccdd3d517f 100755 --- a/gradlew +++ b/gradlew @@ -33,11 +33,11 @@ DEFAULT_JVM_OPTS="" # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" -warn ( ) { +warn () { echo "$*" } -die ( ) { +die () { echo echo "$*" echo @@ -155,7 +155,7 @@ if $cygwin ; then fi # Escape application args -save ( ) { +save () { for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done echo " " } diff --git a/scripts/upload-to-builds.jabref.org.sh b/scripts/upload-to-builds.jabref.org.sh index b2415f440ca..a7297a169f8 100755 --- a/scripts/upload-to-builds.jabref.org.sh +++ b/scripts/upload-to-builds.jabref.org.sh @@ -14,6 +14,12 @@ for buildfile in build/releases/*--snapshot--*; do break; done +for buildfile in build/releases/*--snapshot--*.jar; do + # remove build/releases/ from the filename + jarname=`echo $buildfile | sed "sXbuild/releases/XX"` + break; +done + # now the branch name is in the variable "branch" command="cd www/\n" @@ -26,9 +32,12 @@ if [ "snapshot" != "$branch" ] ; then command="${command}mkdir $branch\ncd $branch\nrm *.dmg\nrm *.jar\nrm *.exe\n" fi -#only upload [Jr]ab[Rr]ef*, not md5sums, updates.xml, etc. -command="${command}mput build/releases/jabref*\n" +# only upload JabRef*, not md5sums, updates.xml, etc. command="${command}mput build/releases/JabRef*\n" + +# create symlink ...--latest.jar to latest version +command="${command}symlink ${jarname} /www/${branch}/JabRef--${branch}--latest.jar\n" + command="${command}exit\n" # now $command is complete diff --git a/src/main/java/org/jabref/Globals.java b/src/main/java/org/jabref/Globals.java index f45b08b3df7..3c5cdad55f0 100644 --- a/src/main/java/org/jabref/Globals.java +++ b/src/main/java/org/jabref/Globals.java @@ -29,7 +29,7 @@ public class Globals { public static final RemoteListenerServerLifecycle REMOTE_LISTENER = new RemoteListenerServerLifecycle(); public static final ImportFormatReader IMPORT_FORMAT_READER = new ImportFormatReader(); - public static final TaskExecutor taskExecutor = new DefaultTaskExecutor(); + public static final TaskExecutor TASK_EXECUTOR = new DefaultTaskExecutor(); // In the main program, this field is initialized in JabRef.java // Each test case initializes this field if required public static JabRefPreferences prefs; @@ -107,7 +107,7 @@ public static FileUpdateMonitor getFileUpdateMonitor() { } public static void shutdownThreadPools() { - taskExecutor.shutdown(); + TASK_EXECUTOR.shutdown(); JabRefExecutorService.INSTANCE.shutdownEverything(); } diff --git a/src/main/java/org/jabref/JabRefMain.java b/src/main/java/org/jabref/JabRefMain.java index 931c640a508..0f98ba7e7d4 100644 --- a/src/main/java/org/jabref/JabRefMain.java +++ b/src/main/java/org/jabref/JabRefMain.java @@ -76,6 +76,7 @@ private static void start(String[] args) { PreferencesMigrations.upgradeFaultyEncodingStrings(); PreferencesMigrations.upgradeLabelPatternToBibtexKeyPattern(); PreferencesMigrations.upgradeStoredCustomEntryTypes(); + PreferencesMigrations.upgradeKeyBindingsToJavaFX(); // Update handling of special fields based on preferences InternalBibtexFields diff --git a/src/main/java/org/jabref/collab/FileUpdateMonitor.java b/src/main/java/org/jabref/collab/FileUpdateMonitor.java index e709321405f..9d009f19848 100644 --- a/src/main/java/org/jabref/collab/FileUpdateMonitor.java +++ b/src/main/java/org/jabref/collab/FileUpdateMonitor.java @@ -20,9 +20,19 @@ public class FileUpdateMonitor implements Runnable { private static final Log LOGGER = LogFactory.getLog(FileUpdateMonitor.class); private static final int WAIT = 4000; - - private int numberOfUpdateListener; private final Map entries = new HashMap<>(); + private int numberOfUpdateListener; + + private static synchronized Path getTempFile() { + Path temporaryFile = null; + try { + temporaryFile = Files.createTempFile("jabref", null); + temporaryFile.toFile().deleteOnExit(); + } catch (IOException ex) { + LOGGER.warn("Could not create temporary file.", ex); + } + return temporaryFile; + } @Override public void run() { @@ -119,7 +129,7 @@ public void updateTimeStamp(String key) { * is used for comparison with the changed on-disk version. * @param key String The handle for this monitor. * @throws IllegalArgumentException If the handle doesn't correspond to an entry. - * @return File The temporary file. + * @return Path The temporary file. */ public Path getTempFile(String key) throws IllegalArgumentException { Entry entry = entries.get(key); @@ -129,7 +139,6 @@ public Path getTempFile(String key) throws IllegalArgumentException { return entry.getTmpFile(); } - /** * A class containing the File, the FileUpdateListener and the current time stamp for one file. */ @@ -209,15 +218,4 @@ public void decreaseTimeStamp() { timeStamp--; } } - - private static synchronized Path getTempFile() { - Path temporaryFile = null; - try { - temporaryFile = Files.createTempFile("jabref", null); - temporaryFile.toFile().deleteOnExit(); - } catch (IOException ex) { - LOGGER.warn("Could not create temporary file.", ex); - } - return temporaryFile; - } } diff --git a/src/main/java/org/jabref/gui/BasePanel.java b/src/main/java/org/jabref/gui/BasePanel.java index 5690497c24a..fa37f93badf 100644 --- a/src/main/java/org/jabref/gui/BasePanel.java +++ b/src/main/java/org/jabref/gui/BasePanel.java @@ -50,6 +50,10 @@ import org.jabref.gui.actions.BaseAction; import org.jabref.gui.actions.CleanupAction; import org.jabref.gui.actions.CopyBibTeXKeyAndLinkAction; +import org.jabref.gui.autocompleter.AutoCompletePreferences; +import org.jabref.gui.autocompleter.AutoCompleteUpdater; +import org.jabref.gui.autocompleter.PersonNameSuggestionProvider; +import org.jabref.gui.autocompleter.SuggestionProviders; import org.jabref.gui.bibtexkeypattern.SearchFixDuplicateLabels; import org.jabref.gui.contentselector.ContentSelectorDialog; import org.jabref.gui.desktop.JabRefDesktop; @@ -96,10 +100,6 @@ import org.jabref.gui.worker.CitationStyleToClipboardWorker; import org.jabref.gui.worker.MarkEntriesAction; import org.jabref.gui.worker.SendAsEMailAction; -import org.jabref.logic.autocompleter.AutoCompletePreferences; -import org.jabref.logic.autocompleter.AutoCompleter; -import org.jabref.logic.autocompleter.AutoCompleterFactory; -import org.jabref.logic.autocompleter.ContentAutoCompleters; import org.jabref.logic.bibtexkeypattern.BibtexKeyPatternUtil; import org.jabref.logic.citationstyle.CitationStyleCache; import org.jabref.logic.citationstyle.CitationStyleOutputFormat; @@ -126,11 +126,13 @@ import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.database.DatabaseLocation; import org.jabref.model.database.KeyCollisionException; +import org.jabref.model.database.event.BibDatabaseContextChangedEvent; import org.jabref.model.database.event.EntryAddedEvent; import org.jabref.model.database.event.EntryRemovedEvent; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.EntryType; import org.jabref.model.entry.FieldName; +import org.jabref.model.entry.InternalBibtexFields; import org.jabref.model.entry.event.EntryChangedEvent; import org.jabref.model.entry.event.EntryEventSource; import org.jabref.model.entry.specialfields.SpecialField; @@ -179,7 +181,7 @@ public class BasePanel extends JPanel implements ClipboardOwner, FileUpdateListe private boolean saving; private boolean updatedExternally; // AutoCompleter used in the search bar - private AutoCompleter searchAutoCompleter; + private PersonNameSuggestionProvider searchAutoCompleter; private boolean baseChanged; private boolean nonUndoableChange; // Used to track whether the base has changed since last save. @@ -193,9 +195,9 @@ public class BasePanel extends JPanel implements ClipboardOwner, FileUpdateListe private PreambleEditor preambleEditor; // Keeps track of the preamble dialog if it is open. private StringDialog stringDialog; - private ContentAutoCompleters autoCompleters; + private SuggestionProviders suggestionProviders; - /** the query the user searches when this basepanel is active */ + // the query the user searches when this BasePanel is active private Optional currentSearchQuery = Optional.empty(); public BasePanel(JabRefFrame frame, BibDatabaseContext bibDatabaseContext) { @@ -203,6 +205,8 @@ public BasePanel(JabRefFrame frame, BibDatabaseContext bibDatabaseContext) { Objects.requireNonNull(bibDatabaseContext); this.bibDatabaseContext = bibDatabaseContext; + bibDatabaseContext.getDatabase().registerListener(this); + bibDatabaseContext.getMetaData().registerListener(this); this.sidePaneManager = frame.getSidePaneManager(); this.frame = frame; @@ -258,9 +262,16 @@ public static void runWorker(AbstractWorker worker) throws Exception { clb.update(); // Runs the update() method on the EDT. } - // Returns a collection of AutoCompleters, which are populated from the current library - public ContentAutoCompleters getAutoCompleters() { - return autoCompleters; + @Subscribe + public void listen(BibDatabaseContextChangedEvent event) { + this.markBaseChanged(); + } + + /** + * Returns a collection of suggestion providers, which are populated from the current library. + */ + public SuggestionProviders getSuggestionProviders() { + return suggestionProviders; } public String getTabTitle() { @@ -368,7 +379,6 @@ private void setupActions() { } else { preambleEditor.setVisible(true); } - }); // The action for opening the string editor @@ -380,7 +390,6 @@ private void setupActions() { } else { stringDialog.setVisible(true); } - }); actions.put(FindUnlinkedFilesDialog.ACTION_COMMAND, (BaseAction) () -> { @@ -711,6 +720,7 @@ public void update() { /** * Generates and copies citations based on the selected entries to the clipboard + * * @param outputFormat the desired {@link CitationStyleOutputFormat} */ private void copyCitationToClipboard(CitationStyleOutputFormat outputFormat) { @@ -751,9 +761,9 @@ private void cut() { /** * Removes the selected entries from the database - * @param cut If false the user will get asked if he really wants to delete the entries, and it will be localized - * as "deleted". - * If true the action will be localized as "cut" + * + * @param cut If false the user will get asked if he really wants to delete the entries, and it will be localized as + * "deleted". If true the action will be localized as "cut" */ private void delete(boolean cut) { List entries = mainTable.getSelectedEntries(); @@ -828,7 +838,6 @@ private void paste() { bibDatabaseContext.getDatabase().insertEntry(be); ce.addEdit(new UndoableInsertEntry(bibDatabaseContext.getDatabase(), be, BasePanel.this)); - } ce.end(); getUndoManager().addEdit(ce); @@ -935,7 +944,7 @@ private void copyKeyAndTitle() { try { layout = new LayoutHelper(sr, Globals.prefs.getLayoutFormatterPreferences(Globals.journalAbbreviationLoader)) - .getLayoutFromText(); + .getLayoutFromText(); } catch (IOException e) { LOGGER.info("Could not get layout", e); return; @@ -1027,7 +1036,7 @@ public void runCommand(final String _command) { } private boolean saveDatabase(File file, boolean selectedOnly, Charset enc, - SavePreferences.DatabaseSaveType saveType) throws SaveException { + SavePreferences.DatabaseSaveType saveType) throws SaveException { SaveSession session; frame.block(); final String SAVE_DATABASE = Localization.lang("Save library"); @@ -1063,7 +1072,6 @@ private boolean saveDatabase(File file, boolean selectedOnly, Charset enc, JOptionPane.showMessageDialog(frame, Localization.lang("Could not save file.") + "\n" + ex.getMessage(), SAVE_DATABASE, JOptionPane.ERROR_MESSAGE); throw new SaveException("rt"); - } finally { frame.unblock(); } @@ -1081,7 +1089,7 @@ private boolean saveDatabase(File file, boolean selectedOnly, Charset enc, String tryDiff = Localization.lang("Try different encoding"); int answer = JOptionPane.showOptionDialog(frame, builder.getPanel(), SAVE_DATABASE, JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE, null, - new String[] {Localization.lang("Save"), tryDiff, Localization.lang("Cancel")}, tryDiff); + new String[]{Localization.lang("Save"), tryDiff, Localization.lang("Cancel")}, tryDiff); if (answer == JOptionPane.NO_OPTION) { // The user wants to use another encoding. @@ -1092,12 +1100,10 @@ private boolean saveDatabase(File file, boolean selectedOnly, Charset enc, } else { Charset newEncoding = Charset.forName((String) choice); return saveDatabase(file, selectedOnly, newEncoding, saveType); - } } else if (answer == JOptionPane.CANCEL_OPTION) { commit = false; } - } if (commit) { @@ -1247,19 +1253,19 @@ private void createMainTable() { public void actionPerformed(ActionEvent e) { // need to close these here, b/c this action overshadows the responsible actions when the main table is selected switch (mode) { - case SHOWING_NOTHING: - frame.getGlobalSearchBar().endSearch(); - break; - case SHOWING_PREVIEW: - getPreviewPanel().close(); - break; - case SHOWING_EDITOR: - case WILL_SHOW_EDITOR: - entryEditorClosing(getCurrentEditor()); - break; - default: - LOGGER.warn("unknown BasePanelMode: '" + mode + "', doing nothing"); - break; + case SHOWING_NOTHING: + frame.getGlobalSearchBar().endSearch(); + break; + case SHOWING_PREVIEW: + getPreviewPanel().close(); + break; + case SHOWING_EDITOR: + case WILL_SHOW_EDITOR: + entryEditorClosing(getCurrentEditor()); + break; + default: + LOGGER.warn("unknown BasePanelMode: '" + mode + "', doing nothing"); + break; } } }); @@ -1306,16 +1312,16 @@ public void keyPressed(KeyEvent e) { if (e.isControlDown()) { switch (keyCode) { - case KeyEvent.VK_PAGE_DOWN: - frame.nextTab.actionPerformed(null); - e.consume(); - break; - case KeyEvent.VK_PAGE_UP: - frame.prevTab.actionPerformed(null); - e.consume(); - break; - default: - break; + case KeyEvent.VK_PAGE_DOWN: + frame.nextTab.actionPerformed(null); + e.consume(); + break; + case KeyEvent.VK_PAGE_UP: + frame.prevTab.actionPerformed(null); + e.consume(); + break; + default: + break; } } else if (keyCode == KeyEvent.VK_ENTER) { e.consume(); @@ -1364,17 +1370,7 @@ public void setupMainPanel() { instantiateSearchAutoCompleter(); this.getDatabase().registerListener(new SearchAutoCompleteListener()); - AutoCompletePreferences autoCompletePreferences = new AutoCompletePreferences(Globals.prefs); - // Set up AutoCompleters for this panel: - if (Globals.prefs.getBoolean(JabRefPreferences.AUTO_COMPLETE)) { - autoCompleters = new ContentAutoCompleters(getDatabase(), bibDatabaseContext.getMetaData(), - autoCompletePreferences, Globals.journalAbbreviationLoader); - // ensure that the autocompleters are in sync with entries - this.getDatabase().registerListener(new AutoCompleteListener()); - } else { - // create empty ContentAutoCompleters() if autoCompletion is deactivated - autoCompleters = new ContentAutoCompleters(); - } + setupAutoCompletion(); // restore floating search result // (needed if preferences have been changed which causes a recreation of the main table) @@ -1390,17 +1386,30 @@ public void setupMainPanel() { splitPane.addPropertyChangeListener(JSplitPane.DIVIDER_LOCATION_PROPERTY, event -> saveDividerLocation()); } + /** + * Set up auto completion for this database + */ + private void setupAutoCompletion() { + AutoCompletePreferences autoCompletePreferences = Globals.prefs.getAutoCompletePreferences(); + if (autoCompletePreferences.shouldAutoComplete()) { + suggestionProviders = new SuggestionProviders(autoCompletePreferences, Globals.journalAbbreviationLoader); + suggestionProviders.indexDatabase(getDatabase()); + // Ensure that the suggestion providers are in sync with entries + this.getDatabase().registerListener(new AutoCompleteUpdater(suggestionProviders)); + } else { + // Create empty suggestion providers if auto completion is deactivated + suggestionProviders = new SuggestionProviders(); + } + } + public void updateSearchManager() { frame.getGlobalSearchBar().setAutoCompleter(searchAutoCompleter); } private void instantiateSearchAutoCompleter() { - AutoCompletePreferences autoCompletePreferences = new AutoCompletePreferences(Globals.prefs); - AutoCompleterFactory autoCompleterFactory = new AutoCompleterFactory(autoCompletePreferences, - Globals.journalAbbreviationLoader); - searchAutoCompleter = autoCompleterFactory.getPersonAutoCompleter(); + searchAutoCompleter = new PersonNameSuggestionProvider(InternalBibtexFields.getPersonNameFields()); for (BibEntry entry : bibDatabaseContext.getDatabase().getEntries()) { - searchAutoCompleter.addBibtexEntry(entry); + searchAutoCompleter.indexEntry(entry); } } @@ -1429,7 +1438,6 @@ public void adjustSplitter() { } else { splitPane.setDividerLocation( splitPane.getHeight() - Globals.prefs.getInt(JabRefPreferences.ENTRY_EDITOR_HEIGHT)); - } } @@ -1437,9 +1445,9 @@ private boolean isShowingEditor() { return (splitPane.getBottomComponent() != null) && (splitPane.getBottomComponent() instanceof EntryEditor); } - public void showEntry(final BibEntry be) { + public void showEntry(final BibEntry bibEntry) { - if (getShowing() == be) { + if (getShowing() == bibEntry) { if (splitPane.getBottomComponent() == null) { // This is the special occasion when showing is set to an // entry, but no entry editor is in fact shown. This happens @@ -1447,10 +1455,9 @@ public void showEntry(final BibEntry be) { // must make sure the same entry is shown again. We do this by // setting showing to null, and recursively calling this method. newEntryShowing(null); - showEntry(be); + showEntry(bibEntry); } return; - } String visName = null; @@ -1459,25 +1466,27 @@ public void showEntry(final BibEntry be) { } // We must instantiate a new editor. - EntryEditor entryEditor = new EntryEditor(frame, BasePanel.this, be); + EntryEditor entryEditor = getEntryEditor(bibEntry); if (visName != null) { entryEditor.setVisibleTab(visName); } showEntryEditor(entryEditor); - newEntryShowing(be); + newEntryShowing(bibEntry); } /** - * Get an entry editor ready to edit the given entry. If an appropriate editor is already cached, it will be updated - * and returned. + * Get an entry editor ready to edit the given entry. * * @param entry The entry to be edited. * @return A suitable entry editor. */ public EntryEditor getEntryEditor(BibEntry entry) { - // Then start the new one: - return new EntryEditor(frame, BasePanel.this, entry); + String lastTabName = ""; + if (currentEditor != null) { + lastTabName = currentEditor.getVisibleTabName(); + } + return new EntryEditor(frame, BasePanel.this, entry, lastTabName); } public EntryEditor getCurrentEditor() { @@ -1607,7 +1616,6 @@ public void markBaseChanged() { LOGGER.info("Problem marking database as changed", e); } } - } private void markBasedChangedInternal() { @@ -1709,7 +1717,6 @@ public boolean showDeleteConfirmationDialog(int numberOfEntries) { } else { return true; } - } /** @@ -1723,7 +1730,7 @@ public void autoGenerateKeysBeforeSaving() { Optional oldKey = bes.getCiteKeyOptional(); if (!(oldKey.isPresent()) || oldKey.get().isEmpty()) { BibtexKeyPatternUtil.makeAndSetLabel(bibDatabaseContext.getMetaData() - .getCiteKeyPattern(Globals.prefs.getBibtexKeyPatternPreferences().getKeyPattern()), + .getCiteKeyPattern(Globals.prefs.getBibtexKeyPatternPreferences().getKeyPattern()), bibDatabaseContext.getDatabase(), bes, Globals.prefs.getBibtexKeyPatternPreferences()); bes.getCiteKeyOptional().ifPresent( @@ -1742,8 +1749,6 @@ public void autoGenerateKeysBeforeSaving() { /** * Activates or deactivates the entry preview, depending on the argument. When deactivating, makes sure that any * visible preview is hidden. - * - * @param enabled */ private void setPreviewActive(boolean enabled) { selectionListener.setPreviewActive(enabled); @@ -1940,7 +1945,6 @@ public void newEntryShowing(BibEntry entry) { showing = entry; setBackAndForwardEnabledState(); } - } /** @@ -1982,15 +1986,6 @@ private String formatOutputMessage(String start, int count) { (count > 1 ? Localization.lang("entries") : Localization.lang("entry"))); } - /** - * This method iterates through all existing entry editors in this BasePanel, telling each to update all its - * instances of FieldContentSelector. This is done to ensure that the list of words in each selector is up-to-date - * after the user has made changes in the Manage dialog. - */ - public void updateAllContentSelectors() { - currentEditor.updateAllContentSelectors(); - } - /** * Set the preview active state for all BasePanel instances. */ @@ -2107,29 +2102,12 @@ private class SearchAutoCompleteListener { @Subscribe public void listen(EntryAddedEvent addedEntryEvent) { - searchAutoCompleter.addBibtexEntry(addedEntryEvent.getBibEntry()); - } - - @Subscribe - public void listen(EntryChangedEvent entryChangedEvent) { - searchAutoCompleter.addBibtexEntry(entryChangedEvent.getBibEntry()); - } - } - - /** - * Ensures that auto completers are up to date when entries are changed AKA Let the auto completer, if any, harvest - * words from the entry - */ - private class AutoCompleteListener { - - @Subscribe - public void listen(EntryAddedEvent addedEntryEvent) { - BasePanel.this.autoCompleters.addEntry(addedEntryEvent.getBibEntry()); + searchAutoCompleter.indexEntry(addedEntryEvent.getBibEntry()); } @Subscribe public void listen(EntryChangedEvent entryChangedEvent) { - BasePanel.this.autoCompleters.addEntry(entryChangedEvent.getBibEntry()); + searchAutoCompleter.indexEntry(entryChangedEvent.getBibEntry()); } } @@ -2145,7 +2123,7 @@ public void listen(EntryAddedEvent addedEntryEvent) { @Subscribe public void listen(EntryChangedEvent entryChangedEvent) { - frame.getGlobalSearchBar().setDontSelectSearchBar(true); + frame.getGlobalSearchBar().setDontSelectSearchBar(); frame.getGlobalSearchBar().performSearch(); } diff --git a/src/main/java/org/jabref/gui/ClipBoardManager.java b/src/main/java/org/jabref/gui/ClipBoardManager.java index e0af977b0ec..e202f51a7fa 100644 --- a/src/main/java/org/jabref/gui/ClipBoardManager.java +++ b/src/main/java/org/jabref/gui/ClipBoardManager.java @@ -73,17 +73,17 @@ public void setClipboardContents(String aString) { StringSelection stringSelection = new StringSelection(aString); CLIPBOARD.setContents(stringSelection, this); } - + public List extractBibEntriesFromClipboard() { // Get clipboard contents, and see if TransferableBibtexEntry is among the content flavors offered Transferable content = CLIPBOARD.getContents(null); List result = new ArrayList<>(); - if (content.isDataFlavorSupported(TransferableBibtexEntry.entryFlavor)) { + if (content.isDataFlavorSupported(TransferableBibtexEntry.ENTRY_FLAVOR)) { // We have determined that the clipboard data is a set of entries. try { @SuppressWarnings("unchecked") - List contents = (List) content.getTransferData(TransferableBibtexEntry.entryFlavor); + List contents = (List) content.getTransferData(TransferableBibtexEntry.ENTRY_FLAVOR); result = contents; } catch (UnsupportedFlavorException | ClassCastException ex) { LOGGER.warn("Could not paste this type", ex); diff --git a/src/main/java/org/jabref/gui/DefaultInjector.java b/src/main/java/org/jabref/gui/DefaultInjector.java index 8e963bf1b15..93845ce5dca 100644 --- a/src/main/java/org/jabref/gui/DefaultInjector.java +++ b/src/main/java/org/jabref/gui/DefaultInjector.java @@ -26,7 +26,7 @@ private static Object createDependency(Class clazz) { if (clazz == DialogService.class) { return new FXDialogService(); } else if (clazz == TaskExecutor.class) { - return Globals.taskExecutor; + return Globals.TASK_EXECUTOR; } else if (clazz == PreferencesService.class) { return Globals.prefs; } else if (clazz == KeyBindingRepository.class) { diff --git a/src/main/java/org/jabref/gui/EntryTypeDialog.java b/src/main/java/org/jabref/gui/EntryTypeDialog.java index d33320340d2..27c0e5f1d55 100644 --- a/src/main/java/org/jabref/gui/EntryTypeDialog.java +++ b/src/main/java/org/jabref/gui/EntryTypeDialog.java @@ -165,7 +165,7 @@ private JPanel createIdFetcherPanel() { WebFetchers.getIdBasedFetchers(Globals.prefs.getImportFormatPreferences()).forEach(fetcher -> comboBox.addItem(fetcher.getName())); // set DOI as default - comboBox.setSelectedItem(DoiFetcher.name); + comboBox.setSelectedItem(DoiFetcher.NAME); generateButton.addActionListener(action -> { fetcherWorker.execute(); diff --git a/src/main/java/org/jabref/gui/FXDialogService.java b/src/main/java/org/jabref/gui/FXDialogService.java index 41209079481..6b474d13a2c 100644 --- a/src/main/java/org/jabref/gui/FXDialogService.java +++ b/src/main/java/org/jabref/gui/FXDialogService.java @@ -12,6 +12,7 @@ import javafx.scene.control.ButtonType; import javafx.scene.control.DialogPane; import javafx.scene.control.TextInputDialog; +import javafx.scene.layout.Region; import javafx.stage.DirectoryChooser; import javafx.stage.FileChooser; @@ -37,6 +38,7 @@ private static FXDialog createDialog(AlertType type, String title, String conten FXDialog alert = new FXDialog(type, title, true); alert.setHeaderText(null); alert.setContentText(content); + alert.getDialogPane().setMinHeight(Region.USE_PREF_SIZE); return alert; } diff --git a/src/main/java/org/jabref/gui/JabRefFrame.java b/src/main/java/org/jabref/gui/JabRefFrame.java index 894f88d4985..1b25660beb2 100644 --- a/src/main/java/org/jabref/gui/JabRefFrame.java +++ b/src/main/java/org/jabref/gui/JabRefFrame.java @@ -624,7 +624,7 @@ public void windowClosing(WindowEvent e) { if (currentSearchQuery.isPresent()) { content = currentSearchQuery.get().getQuery(); } - globalSearchBar.setSearchTerm(content, true); + globalSearchBar.setSearchTerm(content); } currentBasePanel.getPreviewPanel().updateLayout(); diff --git a/src/main/java/org/jabref/gui/TransferableBibtexEntry.java b/src/main/java/org/jabref/gui/TransferableBibtexEntry.java index 1d0188cd1fa..742faea2cc9 100644 --- a/src/main/java/org/jabref/gui/TransferableBibtexEntry.java +++ b/src/main/java/org/jabref/gui/TransferableBibtexEntry.java @@ -22,7 +22,7 @@ */ public class TransferableBibtexEntry implements Transferable { - public static final DataFlavor entryFlavor = new DataFlavor(BibEntry.class, "JabRef entry"); + public static final DataFlavor ENTRY_FLAVOR = new DataFlavor(BibEntry.class, "JabRef entry"); private final List data; @@ -32,19 +32,19 @@ public TransferableBibtexEntry(List bes) { @Override public DataFlavor[] getTransferDataFlavors() { - return new DataFlavor[] {TransferableBibtexEntry.entryFlavor, + return new DataFlavor[]{TransferableBibtexEntry.ENTRY_FLAVOR, DataFlavor.stringFlavor}; } @Override public boolean isDataFlavorSupported(DataFlavor flavor) { - return flavor.equals(TransferableBibtexEntry.entryFlavor) || flavor.equals(DataFlavor.stringFlavor); + return flavor.equals(TransferableBibtexEntry.ENTRY_FLAVOR) || flavor.equals(DataFlavor.stringFlavor); } @Override public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException { - if (flavor.equals(TransferableBibtexEntry.entryFlavor)) { + if (flavor.equals(TransferableBibtexEntry.ENTRY_FLAVOR)) { return data; } else if (flavor.equals(DataFlavor.stringFlavor)) { try { diff --git a/src/main/java/org/jabref/gui/actions/CopyBibTeXKeyAndLinkAction.java b/src/main/java/org/jabref/gui/actions/CopyBibTeXKeyAndLinkAction.java index 8f178d0886c..29d61d68c88 100644 --- a/src/main/java/org/jabref/gui/actions/CopyBibTeXKeyAndLinkAction.java +++ b/src/main/java/org/jabref/gui/actions/CopyBibTeXKeyAndLinkAction.java @@ -3,8 +3,11 @@ import java.util.List; import java.util.stream.Collectors; +import javafx.application.Platform; +import javafx.scene.input.Clipboard; +import javafx.scene.input.ClipboardContent; + import org.jabref.JabRefGUI; -import org.jabref.gui.ClipBoardManager; import org.jabref.gui.maintable.MainTable; import org.jabref.logic.l10n.Localization; import org.jabref.logic.util.OS; @@ -44,8 +47,16 @@ public void action() throws Exception { sb.append(OS.NEWLINE); } - ClipBoardManager clipboard = new ClipBoardManager(); - clipboard.setClipboardContents(sb.toString()); + // This works on Mac and Windows 10, but not on Ubuntu 16.04 + Platform.runLater(new Runnable() { + @Override + public void run() { + final Clipboard clipboard = Clipboard.getSystemClipboard(); + final ClipboardContent content = new ClipboardContent(); + content.putHtml(sb.toString()); + clipboard.setContent(content); + } + }); int copied = entriesWithKey.size(); int toCopy = entries.size(); diff --git a/src/main/java/org/jabref/gui/actions/ManageKeywordsAction.java b/src/main/java/org/jabref/gui/actions/ManageKeywordsAction.java index ba7f6b8db2f..a55f13f56d2 100644 --- a/src/main/java/org/jabref/gui/actions/ManageKeywordsAction.java +++ b/src/main/java/org/jabref/gui/actions/ManageKeywordsAction.java @@ -25,19 +25,15 @@ import javax.swing.JTextField; import org.jabref.Globals; -import org.jabref.JabRefGUI; import org.jabref.gui.BasePanel; import org.jabref.gui.JabRefFrame; -import org.jabref.gui.autocompleter.AutoCompleteListener; import org.jabref.gui.keyboard.KeyBinding; import org.jabref.gui.undo.NamedCompound; import org.jabref.gui.undo.UndoableFieldChange; -import org.jabref.logic.autocompleter.AutoCompleter; import org.jabref.logic.l10n.Localization; import org.jabref.logic.specialfields.SpecialFieldsUtils; import org.jabref.model.FieldChange; import org.jabref.model.entry.BibEntry; -import org.jabref.model.entry.FieldName; import org.jabref.model.entry.Keyword; import org.jabref.model.entry.KeywordList; import org.jabref.model.strings.StringUtil; @@ -53,19 +49,13 @@ public class ManageKeywordsAction extends MnemonicAwareAction { private final JabRefFrame frame; - + private final KeywordList sortedKeywordsOfAllEntriesBeforeUpdateByUser = new KeywordList(); private JDialog diag; - - private DefaultListModel keywordListModel; - private JRadioButton intersectKeywords; private JRadioButton mergeKeywords; - private boolean canceled; - private final KeywordList sortedKeywordsOfAllEntriesBeforeUpdateByUser = new KeywordList(); - public ManageKeywordsAction(JabRefFrame frame) { putValue(Action.NAME, Localization.menuTitle("Manage keywords")); @@ -171,31 +161,6 @@ public void keyPressed(KeyEvent arg0) { } }); - AutoCompleter autoComp = JabRefGUI.getMainFrame().getCurrentBasePanel().getAutoCompleters() - .get(FieldName.KEYWORDS); - AutoCompleteListener acl = new AutoCompleteListener(autoComp); - keyword.addKeyListener(acl); - keyword.addFocusListener(acl); - keyword.addKeyListener(new KeyListener() { - - @Override - public void keyTyped(KeyEvent e) { - // Do nothing - } - - @Override - public void keyReleased(KeyEvent e) { - // Do nothing - } - - @Override - public void keyPressed(KeyEvent e) { - if (e.getKeyCode() == KeyEvent.VK_ENTER) { - addActionListener.actionPerformed(null); - } - } - }); - // Key bindings: ActionMap am = builder.getPanel().getActionMap(); InputMap im = builder.getPanel().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW); diff --git a/src/main/java/org/jabref/gui/autocompleter/AppendPersonNamesStrategy.java b/src/main/java/org/jabref/gui/autocompleter/AppendPersonNamesStrategy.java new file mode 100644 index 00000000000..bae94851ca4 --- /dev/null +++ b/src/main/java/org/jabref/gui/autocompleter/AppendPersonNamesStrategy.java @@ -0,0 +1,27 @@ +package org.jabref.gui.autocompleter; + +public class AppendPersonNamesStrategy extends AppendWordsStrategy { + + /** + * true if the input should be split at a single white space instead of the usual delimiter " and " for names. + * Useful if the input consists of a list of last names. + */ + private final boolean separationBySpace; + + public AppendPersonNamesStrategy() { + this(false); + } + + public AppendPersonNamesStrategy(boolean separationBySpace) { + this.separationBySpace = separationBySpace; + } + + @Override + public String getDelimiter() { + if (this.separationBySpace) { + return " "; + } else { + return " and "; + } + } +} diff --git a/src/main/java/org/jabref/gui/autocompleter/AppendWordsStrategy.java b/src/main/java/org/jabref/gui/autocompleter/AppendWordsStrategy.java new file mode 100644 index 00000000000..1cc0563be0b --- /dev/null +++ b/src/main/java/org/jabref/gui/autocompleter/AppendWordsStrategy.java @@ -0,0 +1,26 @@ +package org.jabref.gui.autocompleter; + +import java.util.Locale; + +public class AppendWordsStrategy implements AutoCompletionStrategy { + + protected String getDelimiter() { + return " "; + } + + @Override + public AutoCompletionInput analyze(String input) { + return determinePrefixAndReturnRemainder(input, getDelimiter()); + } + + private AutoCompletionInput determinePrefixAndReturnRemainder(String input, String delimiter) { + int index = input.toLowerCase(Locale.ROOT).lastIndexOf(delimiter); + if (index >= 0) { + String prefix = input.substring(0, index + delimiter.length()); + String rest = input.substring(index + delimiter.length()); + return new AutoCompletionInput(prefix, rest); + } else { + return new AutoCompletionInput("", input); + } + } +} diff --git a/src/main/java/org/jabref/gui/autocompleter/AutoCompleteFirstNameMode.java b/src/main/java/org/jabref/gui/autocompleter/AutoCompleteFirstNameMode.java new file mode 100644 index 00000000000..30543681808 --- /dev/null +++ b/src/main/java/org/jabref/gui/autocompleter/AutoCompleteFirstNameMode.java @@ -0,0 +1,21 @@ +package org.jabref.gui.autocompleter; + +/** + * For "ONLY_FULL", the auto completer returns the full name, e.g. "Smith, Bob" + * For "ONLY_ABBREVIATED", the auto completer returns the first name abbreviated, e.g. "Smith, B." + * For "BOTH", the auto completer returns both versions. + */ +public enum AutoCompleteFirstNameMode { + ONLY_FULL, + ONLY_ABBREVIATED, + BOTH; + + public static AutoCompleteFirstNameMode parse(String input) { + try { + return AutoCompleteFirstNameMode.valueOf(input); + } catch (IllegalArgumentException ex) { + // Should only occur when preferences are set directly via preferences.put and not via setFirstnameMode + return AutoCompleteFirstNameMode.BOTH; + } + } +} diff --git a/src/main/java/org/jabref/gui/autocompleter/AutoCompleteListener.java b/src/main/java/org/jabref/gui/autocompleter/AutoCompleteListener.java deleted file mode 100644 index 85567638fbe..00000000000 --- a/src/main/java/org/jabref/gui/autocompleter/AutoCompleteListener.java +++ /dev/null @@ -1,480 +0,0 @@ -package org.jabref.gui.autocompleter; - -import java.awt.event.FocusEvent; -import java.awt.event.FocusListener; -import java.awt.event.InputEvent; -import java.awt.event.KeyAdapter; -import java.awt.event.KeyEvent; -import java.util.List; - -import javax.swing.text.BadLocationException; -import javax.swing.text.JTextComponent; - -import org.jabref.logic.autocompleter.AutoCompleter; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -public class AutoCompleteListener extends KeyAdapter implements FocusListener { - - //TODO: The logging behavior in this class is probably too fine-grained and only understandable to its original author - private static final Log LOGGER = LogFactory.getLog(AutoCompleteListener.class); - - private final AutoCompleter completer; - - // These variables keep track of the situation from time to time. - private String toSetIn; // null indicates that there are no completions available - private String lastBeginning; // the letters, the user has typed until know - private int lastCaretPosition = -1; - private List lastCompletions; - private int lastShownCompletion; - private boolean consumeEnterKey = true; - - // This field is set if the focus listener should call another focus listener - // after finishing. This is needed because the autocomplete listener must - // run before the focus listener responsible for storing the current edit. - private FocusListener nextFocusListener; - - public AutoCompleteListener(AutoCompleter completer) { - // if (logger.getHandlers().length == 0) { - // logger.setLevel(Level.FINEST); - // ConsoleHandler ch = new ConsoleHandler(); - // ch.setLevel(Level.FINEST); - // logger.addHandler(ch); - // } - this.completer = completer; - } - - /** - * This method is used if the focus listener should call another focus listener after finishing. This is needed - * because the autocomplete listener must run before the focus listener responsible for storing the current edit. - * - * @param listener The listener to call. - */ - public void setNextFocusListener(FocusListener listener) { - this.nextFocusListener = listener; - } - - /** - * This setting determines whether the autocomplete listener should consume the Enter key stroke when it leads to - * accepting a completion. If set to false, the JTextComponent will receive the Enter key press after the completion - * is done. The default value if true. - * - * @param t true to indicate that the Enter key should be consumed, false that it should be forwarded - */ - public void setConsumeEnterKey(boolean t) { - this.consumeEnterKey = t; - } - - @Override - public void keyPressed(KeyEvent e) { - if ((toSetIn != null) && (e.getKeyCode() == KeyEvent.VK_ENTER)) { - JTextComponent comp = (JTextComponent) e.getSource(); - - // replace typed characters by characters from completion - lastBeginning = lastCompletions.get(lastShownCompletion); - - int end = comp.getSelectionEnd(); - comp.select(end, end); - toSetIn = null; - if (consumeEnterKey) { - e.consume(); - } - } - // Cycle through alternative completions when user presses PGUP/PGDN: - else if ((e.getKeyCode() == KeyEvent.VK_PAGE_DOWN) && (toSetIn != null)) { - cycle((JTextComponent) e.getSource(), 1); - e.consume(); - } else if ((e.getKeyCode() == KeyEvent.VK_PAGE_UP) && (toSetIn != null)) { - cycle((JTextComponent) e.getSource(), -1); - e.consume(); - } - // else if ((e.getKeyCode() == KeyEvent.VK_BACK_SPACE)) { - // StringBuffer currentword = getCurrentWord((JTextComponent) e.getSource()); - // // delete last char to obey semantics of back space - // currentword.deleteCharAt(currentword.length()-1); - // doCompletion(currentword, e); - // } - else if (e.getKeyChar() == KeyEvent.CHAR_UNDEFINED) { - if (e.getKeyCode() == KeyEvent.VK_SHIFT) { - // shift is OK, everything else leads to a reset - LOGGER.debug("Special case: shift pressed. No action."); - } else { - resetAutoCompletion(); - } - } else { - LOGGER.debug("Special case: defined character, but not caught above"); - } - } - - private void cycle(JTextComponent comp, int increment) { - assert (lastCompletions != null); - assert (!lastCompletions.isEmpty()); - lastShownCompletion += increment; - if (lastShownCompletion >= lastCompletions.size()) { - lastShownCompletion = 0; - } else if (lastShownCompletion < 0) { - lastShownCompletion = lastCompletions.size() - 1; - } - String sno = lastCompletions.get(lastShownCompletion); - toSetIn = sno.substring(lastBeginning.length() - 1); - - StringBuilder alltext = new StringBuilder(comp.getText()); - - int oldSelectionStart = comp.getSelectionStart(); - int oldSelectionEnd = comp.getSelectionEnd(); - - // replace prefix with new prefix - int startPos = comp.getSelectionStart() - lastBeginning.length(); - alltext.delete(startPos, oldSelectionStart); - alltext.insert(startPos, sno.subSequence(0, lastBeginning.length())); - - // replace suffix with new suffix - alltext.delete(oldSelectionStart, oldSelectionEnd); - //int cp = oldSelectionEnd - deletedChars; - alltext.insert(oldSelectionStart, toSetIn.substring(1)); - - LOGGER.debug(alltext.toString()); - comp.setText(alltext.toString()); - //comp.setCaretPosition(cp+toSetIn.length()-1); - comp.select(oldSelectionStart, (oldSelectionStart + toSetIn.length()) - 1); - lastCaretPosition = comp.getCaretPosition(); - LOGGER.debug("ToSetIn: '" + toSetIn + "'"); - } - - private boolean atEndOfWord(JTextComponent textField) { - int nextCharPosition = textField.getCaretPosition(); - - // position not at the end of input - if (nextCharPosition < textField.getText().length()) { - char nextChar = textField.getText().charAt(nextCharPosition); - if (!Character.isWhitespace(nextChar)) { - return false; - } - } - return true; - } - - /** - * If user cancels autocompletion by a) entering another letter than the completed word (and there is no other auto - * completion) b) space the casing of the letters has to be kept - * - * Global variable "lastBeginning" keeps track of typed letters. We rely on this variable to reconstruct the text - * - * @param wordSeperatorTyped indicates whether the user has typed a white space character or a - */ - private void setUnmodifiedTypedLetters(JTextComponent comp, boolean lastBeginningContainsTypedCharacter, - boolean wordSeperatorTyped) { - if (lastBeginning == null) { - LOGGER.debug("No last beginning found"); - // There was no previous input (if the user typed a word, where no autocompletion is available) - // Thus, there is nothing to replace - return; - } - LOGGER.debug("lastBeginning: >" + lastBeginning + '<'); - if (comp.getSelectedText() == null) { - // if there is no selection - // the user has typed the complete word, but possibly with a different casing - // we need a replacement - if (wordSeperatorTyped) { - LOGGER.debug("Replacing complete word"); - } else { - // if user did not press a white space character (space, ...), - // then we do not do anything - return; - } - } else { - LOGGER.debug("Selected text " + comp.getSelectedText() + " will be removed"); - // remove completion suggestion - comp.replaceSelection(""); - } - - lastCaretPosition = comp.getCaretPosition(); - - int endIndex = lastCaretPosition - lastBeginning.length(); - if (lastBeginningContainsTypedCharacter) { - // the current letter is NOT contained in comp.getText(), but in lastBeginning - // thus lastBeginning.length() is one too large - endIndex++; - } - String text = comp.getText(); - comp.setText(text.substring(0, endIndex).concat(lastBeginning).concat(text.substring(lastCaretPosition))); - if (lastBeginningContainsTypedCharacter) { - // the current letter is NOT contained in comp.getText() - // Thus, cursor position also did not get updated - lastCaretPosition++; - } - comp.setCaretPosition(lastCaretPosition); - lastBeginning = null; - } - - /** - * Start a new completion attempt (instead of treating a continuation of an existing word or an interrupt of the - * current word) - */ - private void startCompletion(StringBuffer currentword, KeyEvent e) { - JTextComponent comp = (JTextComponent) e.getSource(); - - List completed = findCompletions(currentword.toString()); - String prefix = completer.getPrefix(); - String cWord = (prefix != null) && (!prefix.isEmpty()) ? currentword.toString() - .substring(prefix.length()) : currentword.toString(); - - LOGGER.debug("StartCompletion currentword: >" + currentword + "'<' prefix: >" + prefix + "'<' cword: >" + cWord - + '<'); - - int no = 0; // We use the first word in the array of completions. - if ((completed != null) && (!completed.isEmpty())) { - lastShownCompletion = 0; - lastCompletions = completed; - String sno = completed.get(no); - - // these two lines obey the user's input - //toSetIn = Character.toString(ch); - //toSetIn = toSetIn.concat(sno.substring(cWord.length())); - // BUT we obey the completion - toSetIn = sno.substring(cWord.length() - 1); - - LOGGER.debug("toSetIn: >" + toSetIn + '<'); - - StringBuilder alltext = new StringBuilder(comp.getText()); - int cp = comp.getCaretPosition(); - alltext.insert(cp, toSetIn); - comp.setText(alltext.toString()); - comp.setCaretPosition(cp); - comp.select(cp + 1, (cp + 1 + sno.length()) - cWord.length()); - e.consume(); - lastCaretPosition = comp.getCaretPosition(); - char ch = e.getKeyChar(); - - LOGGER.debug("Appending >" + ch + '<'); - - if (cWord.length() <= 1) { - lastBeginning = Character.toString(ch); - } else { - lastBeginning = cWord.substring(0, cWord.length() - 1).concat(Character.toString(ch)); - } - } - - } - - @Override - public void keyTyped(KeyEvent e) { - LOGGER.debug("key typed event caught " + e.getKeyCode()); - char ch = e.getKeyChar(); - if (ch == '\n') { - // this case is handled at keyPressed(e) - return; - } - - // don't do auto completion inside words - if (!(e.getSource() instanceof JTextComponent) || !atEndOfWord((JTextComponent) e.getSource())) { - return; - } - - if ((e.getModifiers() | InputEvent.SHIFT_MASK) == InputEvent.SHIFT_MASK) { - // plain key or SHIFT + key is pressed, no handling of CTRL+key, META+key, ... - if (Character.isLetter(ch) || Character.isDigit(ch) - || (Character.isWhitespace(ch) && completer.isSingleUnitField())) { - - JTextComponent comp = (JTextComponent) e.getSource(); - - if (toSetIn == null) { - LOGGER.debug("toSetIn is null"); - } else { - LOGGER.debug("toSetIn: >" + toSetIn + '<'); - } - - // The case-insensitive system is a bit tricky here - // If keyword is "TODO" and user types "tO", then this is treated as "continue" as the "O" matches the "O" - // If keyword is "TODO" and user types "To", then this is treated as "discont" as the "o" does NOT match the "O". - - if ((toSetIn != null) && (toSetIn.length() > 1) && (ch == toSetIn.charAt(1))) { - // User continues on the word that was suggested. - LOGGER.debug("cont"); - - toSetIn = toSetIn.substring(1); - if (!toSetIn.isEmpty()) { - int cp = comp.getCaretPosition(); - //comp.setCaretPosition(cp+1-toSetIn.); - comp.select((cp + 1) - toSetIn.length(), cp); - lastBeginning = lastBeginning + ch; - - e.consume(); - lastCaretPosition = comp.getCaretPosition(); - - lastCompletions = findCompletions(lastBeginning); - lastShownCompletion = 0; - for (int i = 0; i < lastCompletions.size(); i++) { - String lastCompletion = lastCompletions.get(i); - if (lastCompletion.endsWith(toSetIn)) { - lastShownCompletion = i; - break; - } - - } - if (toSetIn.length() < 2) { - // User typed the last character of the autocompleted word - // We have to replace the automcompletion word by the typed word. - // This helps if the user presses "space" after the completion - // "space" indicates that the user does NOT want the autocompletion, - // but the typed word - String text = comp.getText(); - comp.setText(text.substring(0, lastCaretPosition - lastBeginning.length()) + lastBeginning - + text.substring(lastCaretPosition)); - // there is no selected text, therefore we are not updating the selection - toSetIn = null; - } - return; - } - } - - if ((toSetIn != null) && ((toSetIn.length() <= 1) || (ch != toSetIn.charAt(1)))) { - // User discontinues the word that was suggested. - lastBeginning = lastBeginning + ch; - - LOGGER.debug("discont toSetIn: >" + toSetIn + "'<' lastBeginning: >" + lastBeginning + '<'); - - List completed = findCompletions(lastBeginning); - if ((completed != null) && (!completed.isEmpty())) { - lastShownCompletion = 0; - lastCompletions = completed; - String sno = completed.get(0); - // toSetIn = string used for autocompletion last time - // this string has to be removed - // lastCaretPosition is the position of the caret after toSetIn. - int lastLen = toSetIn.length() - 1; - toSetIn = sno.substring(lastBeginning.length() - 1); - String text = comp.getText(); - //we do not use toSetIn as we want to obey the casing of "sno" - comp.setText(text.substring(0, (lastCaretPosition - lastLen - lastBeginning.length()) + 1) + sno - + text.substring(lastCaretPosition)); - int startSelect = (lastCaretPosition + 1) - lastLen; - int endSelect = (lastCaretPosition + toSetIn.length()) - lastLen; - comp.select(startSelect, endSelect); - - lastCaretPosition = comp.getCaretPosition(); - e.consume(); - return; - } else { - setUnmodifiedTypedLetters(comp, true, false); - e.consume(); - toSetIn = null; - return; - } - } - - LOGGER.debug("case else"); - - comp.replaceSelection(""); - - StringBuffer currentword = getCurrentWord(comp); - - // only "real characters" end up here - assert (!Character.isISOControl(ch)); - currentword.append(ch); - startCompletion(currentword, e); - return; - } else { - if (Character.isWhitespace(ch)) { - assert (!completer.isSingleUnitField()); - LOGGER.debug("whitespace && !singleUnitField"); - // start a new search if end-of-field is reached - - // replace displayed letters with typed letters - setUnmodifiedTypedLetters((JTextComponent) e.getSource(), false, true); - resetAutoCompletion(); - return; - } - - LOGGER.debug("No letter/digit/whitespace or CHAR_UNDEFINED"); - // replace displayed letters with typed letters - setUnmodifiedTypedLetters((JTextComponent) e.getSource(), false, !Character.isISOControl(ch)); - resetAutoCompletion(); - return; - } - } - resetAutoCompletion(); - } - - /** - * Resets the auto completion data in a way that no leftovers are there - */ - private void resetAutoCompletion() { - LOGGER.debug("Resetting autocompletion"); - toSetIn = null; - lastBeginning = null; - } - - private List findCompletions(String beginning) { - return completer.complete(beginning); - } - - private StringBuffer getCurrentWord(JTextComponent comp) { - StringBuffer res = new StringBuffer(); - String upToCaret; - - try { - upToCaret = comp.getText(0, comp.getCaretPosition()); - // We now have the text from the start of the field up to the caret position. - // In most fields, we are only interested in the currently edited word, so we - // seek from the caret backward to the closest space: - if (!completer.isSingleUnitField()) { - if ((comp.getCaretPosition() < comp.getText().length()) - && Character.isWhitespace(comp.getText().charAt(comp.getCaretPosition()))) { - // caret is in the middle of the text AND current character is a whitespace - // that means: a new word is started and there is no current word - return new StringBuffer(); - } - - int piv = upToCaret.length() - 1; - while ((piv >= 0) && !Character.isWhitespace(upToCaret.charAt(piv))) { - piv--; - } - // piv points to whitespace char or piv is -1 - // copy everything from the next char up to the end of "upToCaret" - res.append(upToCaret.substring(piv + 1)); - } else { - // For fields such as "journal" it is more reasonable to try to complete on the entire - // text field content, so we skip the searching and keep the entire part up to the caret: - res.append(upToCaret); - } - LOGGER.debug("AutoCompListener: " + res); - } catch (BadLocationException ignore) { - // Ignored - } - - return res; - } - - @Override - public void focusGained(FocusEvent event) { - if (nextFocusListener != null) { - nextFocusListener.focusGained(event); - } - } - - @Override - public void focusLost(FocusEvent event) { - if (toSetIn != null) { - JTextComponent comp = (JTextComponent) event.getSource(); - clearCurrentSuggestion(comp); - } - if (nextFocusListener != null) { - nextFocusListener.focusLost(event); - } - } - - public void clearCurrentSuggestion(JTextComponent comp) { - if (toSetIn != null) { - int selStart = comp.getSelectionStart(); - String text = comp.getText(); - comp.setText(text.substring(0, selStart) + text.substring(comp.getSelectionEnd())); - comp.setCaretPosition(selStart); - lastCompletions = null; - lastShownCompletion = 0; - lastCaretPosition = -1; - toSetIn = null; - } - } -} diff --git a/src/main/java/org/jabref/gui/autocompleter/AutoCompletePreferences.java b/src/main/java/org/jabref/gui/autocompleter/AutoCompletePreferences.java new file mode 100644 index 00000000000..48226d3ec33 --- /dev/null +++ b/src/main/java/org/jabref/gui/autocompleter/AutoCompletePreferences.java @@ -0,0 +1,82 @@ +package org.jabref.gui.autocompleter; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import org.jabref.logic.journals.JournalAbbreviationPreferences; + +public class AutoCompletePreferences { + + private static final String DELIMITER = ";"; + private boolean shouldAutoComplete; + private AutoCompleteFirstNameMode firstNameMode; + private boolean onlyCompleteLastFirst; + private boolean onlyCompleteFirstLast; + private List completeNames; + private JournalAbbreviationPreferences journalAbbreviationPreferences; + + public AutoCompletePreferences(boolean shouldAutoComplete, AutoCompleteFirstNameMode firstNameMode, boolean onlyCompleteLastFirst, boolean onlyCompleteFirstLast, List completeNames, JournalAbbreviationPreferences journalAbbreviationPreferences) { + this.shouldAutoComplete = shouldAutoComplete; + this.firstNameMode = firstNameMode; + this.onlyCompleteLastFirst = onlyCompleteLastFirst; + this.onlyCompleteFirstLast = onlyCompleteFirstLast; + this.completeNames = completeNames; + this.journalAbbreviationPreferences = journalAbbreviationPreferences; + } + + public void setShouldAutoComplete(boolean shouldAutoComplete) { + this.shouldAutoComplete = shouldAutoComplete; + } + + public boolean shouldAutoComplete() { + return shouldAutoComplete; + } + + /** + * Returns how the first names are handled. + */ + public AutoCompleteFirstNameMode getFirstNameMode() { + return firstNameMode; + } + + public void setFirstNameMode(AutoCompleteFirstNameMode firstNameMode) { + this.firstNameMode = firstNameMode; + } + + public boolean getOnlyCompleteLastFirst() { + return onlyCompleteLastFirst; + } + + public void setOnlyCompleteLastFirst(boolean onlyCompleteLastFirst) { + this.onlyCompleteLastFirst = onlyCompleteLastFirst; + } + + public boolean getOnlyCompleteFirstLast() { + return onlyCompleteFirstLast; + } + + public void setOnlyCompleteFirstLast(boolean onlyCompleteFirstLast) { + this.onlyCompleteFirstLast = onlyCompleteFirstLast; + } + + public List getCompleteNames() { + return completeNames; + } + + public void setCompleteNames(List completeNames) { + this.completeNames = completeNames; + } + + public void setCompleteNames(String input) { + setCompleteNames(Arrays.asList(input.split(DELIMITER))); + } + + public String getCompleteNamesAsString() { + return completeNames.stream().collect(Collectors.joining(DELIMITER)); + } + + public JournalAbbreviationPreferences getJournalAbbreviationPreferences() { + return journalAbbreviationPreferences; + } +} diff --git a/src/main/java/org/jabref/gui/autocompleter/AutoCompleteRenderer.java b/src/main/java/org/jabref/gui/autocompleter/AutoCompleteRenderer.java deleted file mode 100644 index 319a4b21ab9..00000000000 --- a/src/main/java/org/jabref/gui/autocompleter/AutoCompleteRenderer.java +++ /dev/null @@ -1,60 +0,0 @@ -package org.jabref.gui.autocompleter; - -import java.awt.Component; -import java.awt.event.ActionListener; -import java.util.List; - -/** - * Renders the list of possible autocomplete items. Also takes care of the currently selected item. - * - * @param the type of the items - */ -public abstract class AutoCompleteRenderer { - - /** - * Refreshes the list of possible autocomplete items. Clears the currently selected item. - * - * @param items list of possible autocomplete items - */ - public abstract void update(List items); - - /** - * Creates the control which will be shown in the autocomplete popup. - * - * @param acceptAction the action to be performed if the current selection is chosen as the autocompletion - * @return the control to be added to the autocomplete popup - */ - public abstract Component init(ActionListener acceptAction); - - /** - * Selects the item at the given position. If the specified index is not valid, then the selection will be cleared. - * - * @param index position of the item - */ - public abstract void selectItem(int index); - - /** - * Selects the item relative to the currently selected item. If the specified offset is not valid, then the - * selection will be cleared. - * - * @param offset offset of the item - */ - public void selectItemRelative(int offset) { - int newIndex = getSelectedIndex() + offset; - selectItem(newIndex); - } - - /** - * Returns the index of the currently selected item. - * - * @return index of the selected item - */ - public abstract int getSelectedIndex(); - - /** - * Returns the currently selected item. - * - * @return selected item - */ - public abstract E getSelectedItem(); -} diff --git a/src/main/java/org/jabref/gui/autocompleter/AutoCompleteSuggestionProvider.java b/src/main/java/org/jabref/gui/autocompleter/AutoCompleteSuggestionProvider.java new file mode 100644 index 00000000000..1f188238191 --- /dev/null +++ b/src/main/java/org/jabref/gui/autocompleter/AutoCompleteSuggestionProvider.java @@ -0,0 +1,13 @@ +package org.jabref.gui.autocompleter; + +import java.util.Collection; + +import javafx.util.Callback; + +import org.jabref.model.entry.BibEntry; + +import org.controlsfx.control.textfield.AutoCompletionBinding; + +public interface AutoCompleteSuggestionProvider extends Callback> { + void indexEntry(BibEntry entry); +} diff --git a/src/main/java/org/jabref/gui/autocompleter/AutoCompleteSupport.java b/src/main/java/org/jabref/gui/autocompleter/AutoCompleteSupport.java deleted file mode 100644 index 7f84a264986..00000000000 --- a/src/main/java/org/jabref/gui/autocompleter/AutoCompleteSupport.java +++ /dev/null @@ -1,291 +0,0 @@ -package org.jabref.gui.autocompleter; - -import java.awt.BorderLayout; -import java.awt.Color; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.FocusAdapter; -import java.awt.event.FocusEvent; -import java.awt.event.KeyEvent; -import java.util.List; - -import javax.swing.AbstractAction; -import javax.swing.BorderFactory; -import javax.swing.JComponent; -import javax.swing.JPopupMenu; -import javax.swing.KeyStroke; -import javax.swing.event.DocumentEvent; -import javax.swing.event.DocumentListener; -import javax.swing.event.PopupMenuEvent; -import javax.swing.event.PopupMenuListener; -import javax.swing.text.JTextComponent; - -import org.jabref.logic.autocompleter.AutoCompleter; - -/** - * Endows a textbox with the ability to autocomplete the input. Based on code by Santhosh Kumar - * (http://www.jroller.com/santhosh/date/20050620) James Lemieux (Glazed Lists AutoCompleteSupport) - * - * @param type of items displayed in the autocomplete popup - */ -public class AutoCompleteSupport { - - private final AutoCompleteRenderer renderer; - private AutoCompleter autoCompleter; - private final JTextComponent textComp; - private final JPopupMenu popup = new JPopupMenu(); - private boolean selectsTextOnFocusGain = true; - - - /** - * Constructs a new AutoCompleteSupport for the textbox using the autocompleter and a renderer. - * - * @param textComp the textbox component for which autocompletion should be enabled - * @param autoCompleter the autocompleter providing the data - * @param renderer the renderer displaying the popup - */ - public AutoCompleteSupport(JTextComponent textComp, AutoCompleter autoCompleter, - AutoCompleteRenderer renderer) { - this.renderer = renderer; - this.textComp = textComp; - this.autoCompleter = autoCompleter; - - } - - /** - * Constructs a new AutoCompleteSupport for the textbox. The possible autocomplete items are displayed as a simple - * list. The autocompletion items are provided by an AutoCompleter which has to be specified later using - * {@link setAutoCompleter}. - * - * @param textComp the textbox component for which autocompletion should be enabled - */ - public AutoCompleteSupport(JTextComponent textComp) { - this(textComp, null, new ListAutoCompleteRenderer<>()); - } - - /** - * Constructs a new AutoCompleteSupport for the textbox using the autocompleter and a renderer. The possible - * autocomplete items are displayed as a simple list. - * - * @param textComp the textbox component for which autocompletion should be enabled - * @param autoCompleter the autocompleter providing the data - */ - public AutoCompleteSupport(JTextComponent textComp, AutoCompleter autoCompleter) { - this(textComp, autoCompleter, new ListAutoCompleteRenderer<>()); - } - - /** - * Inits the autocompletion popup. After this method is called, further input in the specified textbox will be - * autocompleted. - */ - public void install() { - // ActionListeners for navigating the suggested autocomplete items with the arrow keys - final ActionListener upAction = new MoveAction(-1); - final ActionListener downAction = new MoveAction(1); - // ActionListener hiding the autocomplete popup - final ActionListener hidePopupAction = e -> popup.setVisible(false); - - // ActionListener accepting the currently selected item as the autocompletion - final ActionListener acceptAction = e -> { - E itemToInsert = renderer.getSelectedItem(); - if (itemToInsert == null) { - return; - } - - String toInsert = autoCompleter.getAutoCompleteText(itemToInsert); - - // TODO: The following should be refactored. For example, the autocompleter shouldn't know whether we want to complete one word or multiple. - // In most fields, we are only interested in the currently edited word, so we - // seek from the caret backward to the closest space: - if (!autoCompleter.isSingleUnitField()) { - // Get position of last word separator (whitespace or comma) - int priv = textComp.getText().length() - 1; - while ((priv >= 0) && !Character.isWhitespace(textComp.getText().charAt(priv)) - && (textComp.getText().charAt(priv) != ',')) { - priv--; - } - // priv points to whitespace char or priv is -1 - // copy everything from the next char up to the end of "upToCaret" - textComp.setText(textComp.getText().substring(0, priv + 1) + toInsert); - } else { - // For fields such as "journal" it is more reasonable to try to complete on the entire - // text field content, so we skip the searching and keep the entire part up to the caret: - textComp.setText(toInsert); - } - textComp.setCaretPosition(textComp.getText().length()); - popup.setVisible(false); - }; - - // Create popup - popup.setBorder(BorderFactory.createMatteBorder(1, 1, 1, 1, Color.LIGHT_GRAY)); - popup.setPopupSize(textComp.getWidth(), 200); - popup.setLayout(new BorderLayout()); - popup.setFocusable(false); - popup.setRequestFocusEnabled(false); - popup.add(renderer.init(acceptAction)); - - // Listen for changes to the text -> update autocomplete suggestions - textComp.getDocument().addDocumentListener(new DocumentListener() { - - @Override - public void insertUpdate(DocumentEvent e) { - postProcessTextChange(); - } - - @Override - public void removeUpdate(DocumentEvent e) { - postProcessTextChange(); - } - - @Override - public void changedUpdate(DocumentEvent e) { - // Do nothing - } - }); - - // Listen for up/down arrow keys -> move currently selected item up or down - // We have to reimplement this function here since we cannot be sure that a simple list will be used to display the items - // So better let the renderer decide what to do. - // (Moreover, the list does not have the focus so probably would not recognize the keystrokes in the first place.) - textComp.registerKeyboardAction(downAction, KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), - JComponent.WHEN_FOCUSED); - - textComp.registerKeyboardAction(upAction, KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), JComponent.WHEN_FOCUSED); - - // Listen for ESC key -> hide popup - textComp.registerKeyboardAction(hidePopupAction, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), - JComponent.WHEN_IN_FOCUSED_WINDOW); - - // Listen to focus events -> select all the text on gaining the focus - this.textComp.addFocusListener(new ComboBoxEditorFocusHandler()); - - // Listen for ENTER key if popup is visible -> accept current autocomplete suggestion - popup.addPopupMenuListener(new PopupMenuListener() { - - @Override - public void popupMenuWillBecomeVisible(PopupMenuEvent e) { - textComp.registerKeyboardAction(acceptAction, KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), - JComponent.WHEN_FOCUSED); - } - - @Override - public void popupMenuWillBecomeInvisible(PopupMenuEvent e) { - textComp.unregisterKeyboardAction(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0)); - } - - @Override - public void popupMenuCanceled(PopupMenuEvent e) { - // Do nothing - } - }); - - } - - /** - * Returns whether the text in the textbox is selected when the textbox gains focus. Defaults to true. - * - * @return - */ - public boolean isSelectsTextOnFocusGain() { - return selectsTextOnFocusGain; - } - - /** - * Sets whether the text in the textbox is selected when the textbox gains focus. Default is true. - * - * @param selectsTextOnFocusGain new value - */ - public void setSelectsTextOnFocusGain(boolean selectsTextOnFocusGain) { - this.selectsTextOnFocusGain = selectsTextOnFocusGain; - } - - /** - * The text changed so update autocomplete suggestions accordingly. - */ - private void postProcessTextChange() { - if (autoCompleter == null) { - popup.setVisible(false); - return; - } - - String text = textComp.getText(); - List candidates = autoCompleter.complete(text); - renderer.update(candidates); - if (textComp.isEnabled() && (!candidates.isEmpty())) { - renderer.selectItem(0); - - popup.setPopupSize(textComp.getWidth(), 200); - popup.show(textComp, 0, textComp.getHeight()); - } else { - popup.setVisible(false); - } - - if (!textComp.hasFocus()) { - textComp.requestFocusInWindow(); - } - } - - - /** - * The action invoked by hitting the up or down arrow key. If the popup is currently shown, that the action is - * relayed to it. Otherwise the arrow keys trigger the popup. - */ - private class MoveAction extends AbstractAction { - - private final int offset; - - - public MoveAction(int offset) { - this.offset = offset; - } - - @Override - public void actionPerformed(ActionEvent e) { - - if (popup.isVisible()) { - renderer.selectItemRelative(offset); - } else { - popup.show(textComp, 0, textComp.getHeight()); - } - - } - } - - /** - * Selects all text when the textbox gains focus. The behavior is controlled by the value returned from - * {@link AutoCompleteSupport#isSelectsTextOnFocusGain()}. - */ - private class ComboBoxEditorFocusHandler extends FocusAdapter { - - @Override - public void focusGained(FocusEvent e) { - if (isSelectsTextOnFocusGain() && !e.isTemporary()) { - textComp.selectAll(); - } - } - - @Override - public void focusLost(FocusEvent e) { - // Do nothing - } - } - - - /** - * Sets the autocompleter used to present autocomplete suggestions. - * - * @param autoCompleter the autocompleter providing the data - */ - public void setAutoCompleter(AutoCompleter autoCompleter) { - this.autoCompleter = autoCompleter; - } - - public void setVisible(boolean visible) { - popup.setVisible(visible); - } - - public boolean isVisible() { - return popup.isVisible(); - } - -} diff --git a/src/main/java/org/jabref/gui/autocompleter/AutoCompleteUpdater.java b/src/main/java/org/jabref/gui/autocompleter/AutoCompleteUpdater.java new file mode 100644 index 00000000000..7fe7fec4aba --- /dev/null +++ b/src/main/java/org/jabref/gui/autocompleter/AutoCompleteUpdater.java @@ -0,0 +1,28 @@ +package org.jabref.gui.autocompleter; + +import org.jabref.model.database.event.EntryAddedEvent; +import org.jabref.model.entry.event.EntryChangedEvent; + +import com.google.common.eventbus.Subscribe; + +/** + * Ensures that suggestion providers are up to date when entries are changed or added. + */ +public class AutoCompleteUpdater { + + private final SuggestionProviders suggestionProviders; + + public AutoCompleteUpdater(SuggestionProviders suggestionProviders) { + this.suggestionProviders = suggestionProviders; + } + + @Subscribe + public void listen(EntryAddedEvent addedEntryEvent) { + suggestionProviders.indexEntry(addedEntryEvent.getBibEntry()); + } + + @Subscribe + public void listen(EntryChangedEvent entryChangedEvent) { + suggestionProviders.indexEntry(entryChangedEvent.getBibEntry()); + } +} diff --git a/src/main/java/org/jabref/gui/autocompleter/AutoCompletionInput.java b/src/main/java/org/jabref/gui/autocompleter/AutoCompletionInput.java new file mode 100644 index 00000000000..e5ebbdbd34e --- /dev/null +++ b/src/main/java/org/jabref/gui/autocompleter/AutoCompletionInput.java @@ -0,0 +1,19 @@ +package org.jabref.gui.autocompleter; + +public class AutoCompletionInput { + private String unfinishedPart; + private String prefix; + + public AutoCompletionInput(String prefix, String unfinishedPart) { + this.prefix = prefix; + this.unfinishedPart = unfinishedPart; + } + + public String getUnfinishedPart() { + return unfinishedPart; + } + + public String getPrefix() { + return prefix; + } +} diff --git a/src/main/java/org/jabref/gui/autocompleter/AutoCompletionStrategy.java b/src/main/java/org/jabref/gui/autocompleter/AutoCompletionStrategy.java new file mode 100644 index 00000000000..50e835a3a9f --- /dev/null +++ b/src/main/java/org/jabref/gui/autocompleter/AutoCompletionStrategy.java @@ -0,0 +1,6 @@ +package org.jabref.gui.autocompleter; + +public interface AutoCompletionStrategy { + + public AutoCompletionInput analyze(String input); +} diff --git a/src/main/java/org/jabref/gui/autocompleter/AutoCompletionTextInputBinding.java b/src/main/java/org/jabref/gui/autocompleter/AutoCompletionTextInputBinding.java new file mode 100644 index 00000000000..8bd97ec3b02 --- /dev/null +++ b/src/main/java/org/jabref/gui/autocompleter/AutoCompletionTextInputBinding.java @@ -0,0 +1,166 @@ +/** + * Copyright (c) 2014, 2015, ControlsFX + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of ControlsFX, any associated website, nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CONTROLSFX BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.jabref.gui.autocompleter; + +import java.util.Collection; + +import javafx.beans.value.ChangeListener; +import javafx.scene.control.TextInputControl; +import javafx.util.Callback; +import javafx.util.StringConverter; + +import org.controlsfx.control.textfield.AutoCompletionBinding; + +/** + * Represents a binding between a text input control and a auto-completion popup + * This class is a slightly modified version of {@link impl.org.controlsfx.autocompletion.AutoCompletionTextFieldBinding} + * that works with general text input controls instead of just text fields. + * @param + * + */ +public class AutoCompletionTextInputBinding extends AutoCompletionBinding { + + /** + * String converter to be used to convert suggestions to strings. + */ + private StringConverter converter; + private AutoCompletionStrategy inputAnalyzer; + private final ChangeListener textChangeListener = (obs, oldText, newText) -> { + if (getCompletionTarget().isFocused()) { + setUserInputText(newText); + } + }; + private boolean showOnFocus; + private final ChangeListener focusChangedListener = (obs, oldFocused, newFocused) -> { + if (newFocused) { + if (showOnFocus) { + setUserInputText(getCompletionTarget().getText()); + } + } else { + hidePopup(); + } + }; + + /** + * Creates a new auto-completion binding between the given textInputControl + * and the given suggestion provider. + */ + private AutoCompletionTextInputBinding(final TextInputControl textInputControl, + Callback> suggestionProvider) { + + this(textInputControl, + suggestionProvider, + AutoCompletionTextInputBinding.defaultStringConverter(), + new ReplaceStrategy()); + } + + private AutoCompletionTextInputBinding(final TextInputControl textInputControl, + final Callback> suggestionProvider, + final StringConverter converter) { + this(textInputControl, suggestionProvider, converter, new ReplaceStrategy()); + } + + private AutoCompletionTextInputBinding(final TextInputControl textInputControl, + final Callback> suggestionProvider, + final StringConverter converter, + final AutoCompletionStrategy inputAnalyzer) { + + super(textInputControl, suggestionProvider, converter); + this.converter = converter; + this.inputAnalyzer = inputAnalyzer; + + getCompletionTarget().textProperty().addListener(textChangeListener); + getCompletionTarget().focusedProperty().addListener(focusChangedListener); + } + + private static StringConverter defaultStringConverter() { + return new StringConverter() { + @Override + public String toString(T t) { + return t == null ? null : t.toString(); + } + + @SuppressWarnings("unchecked") + @Override + public T fromString(String string) { + return (T) string; + } + }; + } + + public static void autoComplete(TextInputControl textArea, Callback> suggestionProvider) { + new AutoCompletionTextInputBinding<>(textArea, suggestionProvider); + } + + public static void autoComplete(TextInputControl textArea, Callback> suggestionProvider, StringConverter converter) { + new AutoCompletionTextInputBinding<>(textArea, suggestionProvider, converter); + } + + public static AutoCompletionTextInputBinding autoComplete(TextInputControl textArea, Callback> suggestionProvider, StringConverter converter, AutoCompletionStrategy inputAnalyzer) { + return new AutoCompletionTextInputBinding<>(textArea, suggestionProvider, converter, inputAnalyzer); + } + + public static AutoCompletionTextInputBinding autoComplete(TextInputControl textArea, Callback> suggestionProvider, AutoCompletionStrategy inputAnalyzer) { + return autoComplete(textArea, suggestionProvider, AutoCompletionTextInputBinding.defaultStringConverter(), inputAnalyzer); + } + + private void setUserInputText(String newText) { + if (newText == null) { + newText = ""; + } + AutoCompletionInput input = inputAnalyzer.analyze(newText); + setUserInput(input.getUnfinishedPart()); + } + + @Override + public TextInputControl getCompletionTarget() { + return (TextInputControl) super.getCompletionTarget(); + } + + @Override + public void dispose() { + getCompletionTarget().textProperty().removeListener(textChangeListener); + getCompletionTarget().focusedProperty().removeListener(focusChangedListener); + } + + @Override + protected void completeUserInput(T completion) { + String completionText = converter.toString(completion); + String inputText = getCompletionTarget().getText(); + if (inputText == null) { + inputText = ""; + } + AutoCompletionInput input = inputAnalyzer.analyze(inputText); + String newText = input.getPrefix() + completionText; + getCompletionTarget().setText(newText); + getCompletionTarget().positionCaret(newText.length()); + } + + public void setShowOnFocus(boolean showOnFocus) { + this.showOnFocus = showOnFocus; + } +} diff --git a/src/main/java/org/jabref/gui/autocompleter/BibEntrySuggestionProvider.java b/src/main/java/org/jabref/gui/autocompleter/BibEntrySuggestionProvider.java new file mode 100644 index 00000000000..568b697996a --- /dev/null +++ b/src/main/java/org/jabref/gui/autocompleter/BibEntrySuggestionProvider.java @@ -0,0 +1,36 @@ +package org.jabref.gui.autocompleter; + +import java.util.Comparator; + +import org.jabref.logic.bibtex.comparator.EntryComparator; +import org.jabref.model.entry.BibEntry; + +import org.controlsfx.control.textfield.AutoCompletionBinding; + +/** + * Delivers possible completions as a list of {@link BibEntry} based on their cite key. + */ +class BibEntrySuggestionProvider extends SuggestionProvider implements AutoCompleteSuggestionProvider { + + @Override + public void indexEntry(BibEntry entry) { + if (entry == null) { + return; + } + + addPossibleSuggestions(entry); + } + + @Override + protected Comparator getComparator() { + return new EntryComparator(false, true, BibEntry.KEY_FIELD); + } + + @Override + protected boolean isMatch(BibEntry suggestion, AutoCompletionBinding.ISuggestionRequest request) { + String userTextLower = request.getUserText().toLowerCase(); + return suggestion.getCiteKeyOptional() + .map(key -> key.toLowerCase().contains(userTextLower)) + .orElse(false); + } +} diff --git a/src/main/java/org/jabref/gui/autocompleter/ContentSelectorSuggestionProvider.java b/src/main/java/org/jabref/gui/autocompleter/ContentSelectorSuggestionProvider.java new file mode 100644 index 00000000000..6c969f2713b --- /dev/null +++ b/src/main/java/org/jabref/gui/autocompleter/ContentSelectorSuggestionProvider.java @@ -0,0 +1,40 @@ +package org.jabref.gui.autocompleter; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.jabref.model.entry.BibEntry; + +import org.controlsfx.control.textfield.AutoCompletionBinding; + +/** + * Enriches a suggestion provider by a given set of content selector values. + */ +public class ContentSelectorSuggestionProvider implements AutoCompleteSuggestionProvider { + + private final AutoCompleteSuggestionProvider suggestionProvider; + private final List contentSelectorValues; + + public ContentSelectorSuggestionProvider(AutoCompleteSuggestionProvider suggestionProvider, + List contentSelectorValues) { + + this.suggestionProvider = suggestionProvider; + this.contentSelectorValues = contentSelectorValues; + } + + @Override + public Collection call(AutoCompletionBinding.ISuggestionRequest request) { + List suggestions = new ArrayList<>(); + if (suggestionProvider != null) { + suggestions.addAll(suggestionProvider.call(request)); + } + suggestions.addAll(contentSelectorValues); + return suggestions; + } + + @Override + public void indexEntry(BibEntry entry) { + suggestionProvider.indexEntry(entry); + } +} diff --git a/src/main/java/org/jabref/gui/autocompleter/FieldValueSuggestionProvider.java b/src/main/java/org/jabref/gui/autocompleter/FieldValueSuggestionProvider.java new file mode 100644 index 00000000000..871723b2cd7 --- /dev/null +++ b/src/main/java/org/jabref/gui/autocompleter/FieldValueSuggestionProvider.java @@ -0,0 +1,26 @@ +package org.jabref.gui.autocompleter; + +import java.util.Objects; + +import org.jabref.model.entry.BibEntry; + +/** + * Stores the full content of one field. + */ +class FieldValueSuggestionProvider extends StringSuggestionProvider implements AutoCompleteSuggestionProvider { + + private final String fieldName; + + FieldValueSuggestionProvider(String fieldName) { + this.fieldName = Objects.requireNonNull(fieldName); + } + + @Override + public void indexEntry(BibEntry entry) { + if (entry == null) { + return; + } + + entry.getField(fieldName).ifPresent(fieldValue -> addPossibleSuggestions(fieldValue.trim())); + } +} diff --git a/src/main/java/org/jabref/gui/autocompleter/JournalsSuggestionProvider.java b/src/main/java/org/jabref/gui/autocompleter/JournalsSuggestionProvider.java new file mode 100644 index 00000000000..f0749e3c7c7 --- /dev/null +++ b/src/main/java/org/jabref/gui/autocompleter/JournalsSuggestionProvider.java @@ -0,0 +1,24 @@ +package org.jabref.gui.autocompleter; + +import java.util.List; +import java.util.stream.Collectors; + +import org.jabref.logic.journals.Abbreviation; +import org.jabref.logic.journals.JournalAbbreviationLoader; +import org.jabref.logic.journals.JournalAbbreviationPreferences; + +public class JournalsSuggestionProvider extends FieldValueSuggestionProvider { + + + JournalsSuggestionProvider(String fieldName, AutoCompletePreferences preferences, + JournalAbbreviationLoader abbreviationLoader) { + super(fieldName); + + JournalAbbreviationPreferences journalAbbreviationPreferences = preferences.getJournalAbbreviationPreferences(); + List journals = abbreviationLoader.getRepository(journalAbbreviationPreferences) + .getAbbreviations().stream() + .map(Abbreviation::getName) + .collect(Collectors.toList()); + addPossibleSuggestions(journals); + } +} diff --git a/src/main/java/org/jabref/gui/autocompleter/ListAutoCompleteRenderer.java b/src/main/java/org/jabref/gui/autocompleter/ListAutoCompleteRenderer.java deleted file mode 100644 index 968ba47190b..00000000000 --- a/src/main/java/org/jabref/gui/autocompleter/ListAutoCompleteRenderer.java +++ /dev/null @@ -1,88 +0,0 @@ -package org.jabref.gui.autocompleter; - -import java.awt.Component; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.util.List; -import java.util.Vector; - -import javax.swing.BorderFactory; -import javax.swing.DefaultListModel; -import javax.swing.JList; -import javax.swing.JScrollPane; -import javax.swing.ListSelectionModel; -import javax.swing.ScrollPaneConstants; - -/** - * Renders possible autocomplete items in form of a simple list. - * - * @param the type of the items - */ -public class ListAutoCompleteRenderer extends AutoCompleteRenderer { - - private final DefaultListModel model = new DefaultListModel<>(); - private final JList list = new JList<>(model); - - /** - * Every selection change by the user is interpreted as accepting the new item as autocompletion. Thus we need this - * helper variable to prevent that also programmatically trigger an autocompletion. - */ - private Boolean interpretSelectionChangeAsAccept = true; - - @Override - public void update(List autoCompletions) { - if (autoCompletions == null) { - model.removeAllElements(); - } else { - list.setListData(new Vector<>(autoCompletions)); - list.clearSelection(); - } - } - - @Override - public Component init(ActionListener newAcceptAction) { - // Init list - list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); - list.setFocusable(false); - list.setRequestFocusEnabled(false); - list.setBorder(BorderFactory.createEmptyBorder(3, 5, 3, 5)); - list.addListSelectionListener(e -> { - if (interpretSelectionChangeAsAccept && (newAcceptAction != null)) { - newAcceptAction.actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, null)); - } - }); - - // Init pane containing the list - JScrollPane scrollPane = new JScrollPane(list); - scrollPane.setFocusable(false); - scrollPane.setRequestFocusEnabled(false); - scrollPane.setBorder(BorderFactory.createEmptyBorder()); - scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER); - scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); - - return scrollPane; - } - - @Override - public E getSelectedItem() { - return list.getSelectedValue(); - } - - @Override - public void selectItem(int index) { - interpretSelectionChangeAsAccept = false; - // Set new index if valid otherwise clean selection - if ((index >= 0) && (index < list.getModel().getSize())) { - list.setSelectedIndex(index); - list.ensureIndexIsVisible(index); - } else { - list.clearSelection(); - } - interpretSelectionChangeAsAccept = true; - } - - @Override - public int getSelectedIndex() { - return list.getSelectedIndex(); - } -} diff --git a/src/main/java/org/jabref/gui/autocompleter/PersonNameStringConverter.java b/src/main/java/org/jabref/gui/autocompleter/PersonNameStringConverter.java new file mode 100644 index 00000000000..f9ff3de842c --- /dev/null +++ b/src/main/java/org/jabref/gui/autocompleter/PersonNameStringConverter.java @@ -0,0 +1,69 @@ +package org.jabref.gui.autocompleter; + +import javafx.util.StringConverter; + +import org.jabref.model.entry.Author; +import org.jabref.model.entry.AuthorList; + +public class PersonNameStringConverter extends StringConverter { + + private final boolean autoCompFF; + private final boolean autoCompLF; + private final AutoCompleteFirstNameMode autoCompleteFirstNameMode; + + public PersonNameStringConverter(boolean autoCompFF, boolean autoCompLF, AutoCompleteFirstNameMode autoCompleteFirstNameMode) { + this.autoCompFF = autoCompFF; + this.autoCompLF = autoCompLF; + this.autoCompleteFirstNameMode = autoCompleteFirstNameMode; + } + + public PersonNameStringConverter(AutoCompletePreferences preferences) { + if (preferences.getOnlyCompleteFirstLast()) { + autoCompFF = true; + autoCompLF = false; + } else if (preferences.getOnlyCompleteLastFirst()) { + autoCompFF = false; + autoCompLF = true; + } else { + autoCompFF = true; + autoCompLF = true; + } + + autoCompleteFirstNameMode = preferences.getFirstNameMode(); + } + + @Override + public String toString(Author author) { + + if (autoCompLF) { + switch (autoCompleteFirstNameMode) { + case ONLY_ABBREVIATED: + return author.getLastFirst(true); + case ONLY_FULL: + return author.getLastFirst(false); + case BOTH: + return author.getLastFirst(true); + default: + break; + } + } + if (autoCompFF) { + switch (autoCompleteFirstNameMode) { + case ONLY_ABBREVIATED: + return author.getFirstLast(true); + case ONLY_FULL: + return author.getFirstLast(false); + case BOTH: + return author.getFirstLast(true); + default: + break; + } + } + return author.getLastFirst(false); + } + + @Override + public Author fromString(String string) { + return AuthorList.parse(string).getAuthor(0); + } +} diff --git a/src/main/java/org/jabref/gui/autocompleter/PersonNameSuggestionProvider.java b/src/main/java/org/jabref/gui/autocompleter/PersonNameSuggestionProvider.java new file mode 100644 index 00000000000..8182be6a33c --- /dev/null +++ b/src/main/java/org/jabref/gui/autocompleter/PersonNameSuggestionProvider.java @@ -0,0 +1,60 @@ +package org.jabref.gui.autocompleter; + +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Objects; + +import org.jabref.model.entry.Author; +import org.jabref.model.entry.AuthorList; +import org.jabref.model.entry.BibEntry; + +import org.controlsfx.control.textfield.AutoCompletionBinding; + +/** + * Delivers possible completions as a list of {@link Author}s. + */ +public class PersonNameSuggestionProvider extends SuggestionProvider implements AutoCompleteSuggestionProvider { + + private final List fieldNames; + private final Comparator authorComparator = Comparator.comparing(Author::getNameForAlphabetization); + + PersonNameSuggestionProvider(String fieldName) { + this(Collections.singletonList(Objects.requireNonNull(fieldName))); + } + + public PersonNameSuggestionProvider(List fieldNames) { + super(); + + this.fieldNames = Objects.requireNonNull(fieldNames); + + } + + @Override + public void indexEntry(BibEntry entry) { + if (entry == null) { + return; + } + + for (String fieldName : fieldNames) { + entry.getField(fieldName).ifPresent(fieldValue -> { + AuthorList authorList = AuthorList.parse(fieldValue); + for (Author author : authorList.getAuthors()) { + addPossibleSuggestions(author); + } + }); + } + } + + @Override + protected Comparator getComparator() { + return authorComparator; + } + + @Override + protected boolean isMatch(Author suggestion, AutoCompletionBinding.ISuggestionRequest request) { + String userTextLower = request.getUserText().toLowerCase(); + String suggestionStr = suggestion.getLastFirst(false).toLowerCase(); + return suggestionStr.contains(userTextLower); + } +} diff --git a/src/main/java/org/jabref/gui/autocompleter/ReplaceStrategy.java b/src/main/java/org/jabref/gui/autocompleter/ReplaceStrategy.java new file mode 100644 index 00000000000..1ba900201b2 --- /dev/null +++ b/src/main/java/org/jabref/gui/autocompleter/ReplaceStrategy.java @@ -0,0 +1,9 @@ +package org.jabref.gui.autocompleter; + +public class ReplaceStrategy implements AutoCompletionStrategy { + + @Override + public AutoCompletionInput analyze(String input) { + return new AutoCompletionInput("", input); + } +} diff --git a/src/main/java/org/jabref/gui/autocompleter/StringSuggestionProvider.java b/src/main/java/org/jabref/gui/autocompleter/StringSuggestionProvider.java new file mode 100644 index 00000000000..1ec2cf0c97d --- /dev/null +++ b/src/main/java/org/jabref/gui/autocompleter/StringSuggestionProvider.java @@ -0,0 +1,26 @@ +package org.jabref.gui.autocompleter; + +import java.util.Comparator; + +import org.controlsfx.control.textfield.AutoCompletionBinding; + +class StringSuggestionProvider extends SuggestionProvider { + + private final Comparator stringComparator = Comparator.naturalOrder(); + + public StringSuggestionProvider() { + + } + + @Override + protected Comparator getComparator() { + return stringComparator; + } + + @Override + protected boolean isMatch(String suggestion, AutoCompletionBinding.ISuggestionRequest request) { + String userTextLower = request.getUserText().toLowerCase(); + String suggestionStr = suggestion.toLowerCase(); + return suggestionStr.contains(userTextLower); + } +} diff --git a/src/main/java/org/jabref/gui/autocompleter/SuggestionProvider.java b/src/main/java/org/jabref/gui/autocompleter/SuggestionProvider.java new file mode 100644 index 00000000000..553b5c681a7 --- /dev/null +++ b/src/main/java/org/jabref/gui/autocompleter/SuggestionProvider.java @@ -0,0 +1,199 @@ +/** + * Copyright (c) 2014, 2016 ControlsFX + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of ControlsFX, any associated website, nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CONTROLSFX BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.jabref.gui.autocompleter; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashSet; +import java.util.List; + +import javafx.util.Callback; + +import org.controlsfx.control.textfield.AutoCompletionBinding.ISuggestionRequest; + +/** + * This is a simple implementation of a generic suggestion provider callback. + * The complexity of suggestion generation is O(n) where n is the number of possible suggestions. + * + * @param Type of suggestions + * + * This class is a copy of {@link impl.org.controlsfx.autocompletion.SuggestionProvider} with the only difference that + * we use a set instead of list to store the suggestions in order to eliminate duplicates. + */ +public abstract class SuggestionProvider implements Callback> { + + private final Collection possibleSuggestions = new HashSet<>(); + private final Object possibleSuggestionsLock = new Object(); + + /** + * Create a default suggestion provider based on the toString() method of the generic objects + * @param possibleSuggestions All possible suggestions + * @return + */ + public static SuggestionProvider create(Collection possibleSuggestions) { + return create(null, possibleSuggestions); + } + + /** + * Create a default suggestion provider based on the toString() method of the generic objects + * using the provided stringConverter + * + * @param stringConverter A stringConverter which converts generic T into a string + * @param possibleSuggestions All possible suggestions + * @return + */ + public static SuggestionProvider create(Callback stringConverter, Collection possibleSuggestions) { + SuggestionProviderString suggestionProvider = new SuggestionProviderString<>(stringConverter); + suggestionProvider.addPossibleSuggestions(possibleSuggestions); + return suggestionProvider; + } + + /** + * Add the given new possible suggestions to this SuggestionProvider + * @param newPossible + */ + public void addPossibleSuggestions(@SuppressWarnings("unchecked") T... newPossible) { + addPossibleSuggestions(Arrays.asList(newPossible)); + } + + /** + * Add the given new possible suggestions to this SuggestionProvider + * @param newPossible + */ + public void addPossibleSuggestions(Collection newPossible) { + synchronized (possibleSuggestionsLock) { + possibleSuggestions.addAll(newPossible); + } + } + + /** + * Remove all current possible suggestions + */ + public void clearSuggestions() { + synchronized (possibleSuggestionsLock) { + possibleSuggestions.clear(); + } + } + + @Override + public final Collection call(final ISuggestionRequest request) { + List suggestions = new ArrayList<>(); + if (!request.getUserText().isEmpty()) { + synchronized (possibleSuggestionsLock) { + for (T possibleSuggestion : possibleSuggestions) { + if (isMatch(possibleSuggestion, request)) { + suggestions.add(possibleSuggestion); + } + } + } + Collections.sort(suggestions, getComparator()); + } + return suggestions; + } + + + /*************************************************************************** + * * + * Static methods * + * * + **************************************************************************/ + + /** + * Get the comparator to order the suggestions + * @return + */ + protected abstract Comparator getComparator(); + + /** + * Check the given possible suggestion is a match (is a valid suggestion) + * @param suggestion + * @param request + * @return + */ + protected abstract boolean isMatch(T suggestion, ISuggestionRequest request); + + + /*************************************************************************** + * * + * Default implementations * + * * + **************************************************************************/ + + /** + * This is a simple string based suggestion provider. + * All generic suggestions T are turned into strings for processing. + * + */ + private static class SuggestionProviderString extends SuggestionProvider { + + private Callback stringConverter; + + private final Comparator stringComparator = new Comparator() { + @Override + public int compare(T o1, T o2) { + String o1str = stringConverter.call(o1); + String o2str = stringConverter.call(o2); + return o1str.compareTo(o2str); + } + }; + + /** + * Create a new SuggestionProviderString + * @param stringConverter + */ + public SuggestionProviderString(Callback stringConverter) { + this.stringConverter = stringConverter; + + // In case no stringConverter was provided, use the default strategy + if (this.stringConverter == null) { + this.stringConverter = new Callback() { + @Override + public String call(T obj) { + return obj != null ? obj.toString() : ""; //$NON-NLS-1$ + } + }; + } + } + + /**{@inheritDoc}*/ + @Override + protected Comparator getComparator() { + return stringComparator; + } + + /**{@inheritDoc}*/ + @Override + protected boolean isMatch(T suggestion, ISuggestionRequest request) { + String userTextLower = request.getUserText().toLowerCase(); + String suggestionStr = suggestion.toString().toLowerCase(); + return suggestionStr.contains(userTextLower); + } + } +} diff --git a/src/main/java/org/jabref/gui/autocompleter/SuggestionProviders.java b/src/main/java/org/jabref/gui/autocompleter/SuggestionProviders.java new file mode 100644 index 00000000000..a2ea193f5ca --- /dev/null +++ b/src/main/java/org/jabref/gui/autocompleter/SuggestionProviders.java @@ -0,0 +1,71 @@ +package org.jabref.gui.autocompleter; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import org.jabref.logic.journals.JournalAbbreviationLoader; +import org.jabref.model.database.BibDatabase; +import org.jabref.model.entry.BibEntry; +import org.jabref.model.entry.FieldName; +import org.jabref.model.entry.FieldProperty; +import org.jabref.model.entry.InternalBibtexFields; + +public class SuggestionProviders { + + /** + * key: field name + */ + private final Map> providers = new HashMap<>(); + + /** + * Empty + */ + public SuggestionProviders() { + + } + + public SuggestionProviders(AutoCompletePreferences preferences, + JournalAbbreviationLoader abbreviationLoader) { + Objects.requireNonNull(preferences); + + List completeFields = preferences.getCompleteNames(); + for (String field : completeFields) { + AutoCompleteSuggestionProvider autoCompleter = initalizeSuggestionProvider(field, preferences, abbreviationLoader); + providers.put(field, autoCompleter); + } + } + + public AutoCompleteSuggestionProvider getForField(String fieldName) { + return providers.get(fieldName); + } + + public void indexDatabase(BibDatabase database) { + for (BibEntry entry : database.getEntries()) { + indexEntry(entry); + } + } + + /** + * This methods assures all information in the given entry is included as suggestions. + */ + public void indexEntry(BibEntry bibEntry) { + for (AutoCompleteSuggestionProvider autoCompleter : providers.values()) { + autoCompleter.indexEntry(bibEntry); + } + } + + private AutoCompleteSuggestionProvider initalizeSuggestionProvider(String fieldName, AutoCompletePreferences preferences, JournalAbbreviationLoader abbreviationLoader) { + if (InternalBibtexFields.getFieldProperties(fieldName).contains(FieldProperty.PERSON_NAMES)) { + return new PersonNameSuggestionProvider(fieldName); + } else if (InternalBibtexFields.getFieldProperties(fieldName).contains(FieldProperty.SINGLE_ENTRY_LINK)) { + return new BibEntrySuggestionProvider(); + } else if (InternalBibtexFields.getFieldProperties(fieldName).contains(FieldProperty.JOURNAL_NAME) + || FieldName.PUBLISHER.equals(fieldName)) { + return new JournalsSuggestionProvider(fieldName, preferences, abbreviationLoader); + } else { + return new WordSuggestionProvider(fieldName); + } + } +} diff --git a/src/main/java/org/jabref/logic/autocompleter/DefaultAutoCompleter.java b/src/main/java/org/jabref/gui/autocompleter/WordSuggestionProvider.java similarity index 50% rename from src/main/java/org/jabref/logic/autocompleter/DefaultAutoCompleter.java rename to src/main/java/org/jabref/gui/autocompleter/WordSuggestionProvider.java index 319f573edce..0f9c2ec56fa 100644 --- a/src/main/java/org/jabref/logic/autocompleter/DefaultAutoCompleter.java +++ b/src/main/java/org/jabref/gui/autocompleter/WordSuggestionProvider.java @@ -1,4 +1,4 @@ -package org.jabref.logic.autocompleter; +package org.jabref.gui.autocompleter; import java.util.Objects; import java.util.StringTokenizer; @@ -6,37 +6,20 @@ import org.jabref.model.entry.BibEntry; /** - * Delivers possible completions for a given string. * Stores all words in the given field which are separated by SEPARATING_CHARS. - * - * @author kahlert, cordes */ -class DefaultAutoCompleter extends AbstractAutoCompleter { +public class WordSuggestionProvider extends StringSuggestionProvider implements AutoCompleteSuggestionProvider { private static final String SEPARATING_CHARS = ";,\n "; private final String fieldName; - /** - * @see AutoCompleterFactory - */ - DefaultAutoCompleter(String fieldName, AutoCompletePreferences preferences) { - super(preferences); - + public WordSuggestionProvider(String fieldName) { this.fieldName = Objects.requireNonNull(fieldName); } @Override - public boolean isSingleUnitField() { - return false; - } - - /** - * {@inheritDoc} - * Stores all words in the given field which are separated by SEPARATING_CHARS. - */ - @Override - public void addBibtexEntry(BibEntry entry) { + public void indexEntry(BibEntry entry) { if (entry == null) { return; } @@ -44,7 +27,7 @@ public void addBibtexEntry(BibEntry entry) { entry.getField(fieldName).ifPresent(fieldValue -> { StringTokenizer tok = new StringTokenizer(fieldValue, SEPARATING_CHARS); while (tok.hasMoreTokens()) { - addItemToIndex(tok.nextToken()); + addPossibleSuggestions(tok.nextToken()); } }); } diff --git a/src/main/java/org/jabref/gui/contentselector/ContentSelectorDialog.java b/src/main/java/org/jabref/gui/contentselector/ContentSelectorDialog.java index 80cc81deef9..2fb061ee6e9 100644 --- a/src/main/java/org/jabref/gui/contentselector/ContentSelectorDialog.java +++ b/src/main/java/org/jabref/gui/contentselector/ContentSelectorDialog.java @@ -73,10 +73,10 @@ public class ContentSelectorDialog extends JabRefDialog { private final JTextField wordEditField = new JTextField("", 20); private final JScrollPane fPane = new JScrollPane(fieldList); private final Map> wordListModels = new HashMap<>(); - private DefaultListModel wordListModel = new DefaultListModel<>(); - private final JList wordList = new JList<>(wordListModel); - private final JScrollPane wPane = new JScrollPane(wordList); private final List removedFields = new ArrayList<>(); + private DefaultListModel wordListModel = new DefaultListModel<>(); + private JList wordList = new JList<>(wordListModel); + private JScrollPane wPane = new JScrollPane(wordList); private String currentField; @@ -262,7 +262,6 @@ private void newWordAction() { } private void applyChanges() { - boolean changedFieldSet = false; // Watch if we need to rebuild entry editors boolean anythingChanged = false; // Watch if we should mark as there is data changed // First remove the mappings for fields that have been deleted. @@ -270,7 +269,6 @@ private void applyChanges() { // cause any harm to remove them here. for (String fieldName : removedFields) { metaData.clearContentSelectors(fieldName); - changedFieldSet = true; anythingChanged = true; } @@ -302,27 +300,19 @@ private void applyChanges() { // Check if there are words to be added and previously there were no content selector for the field if (!data.isEmpty() && metaData.getContentSelectorValuesForField(entry.getKey()).isEmpty()) { - changedFieldSet = true; + anythingChanged = true; } metaData.addContentSelector(new ContentSelector(entry.getKey(), new ArrayList<>(data))); } - // Update all selectors in the current BasePanel. - if (changedFieldSet) { - // TODO: We have added or removed content selectors, update the entry editor - } else if (anythingChanged) { - // Enough to update the content selectors, if anything changed - panel.updateAllContentSelectors(); - } - if (anythingChanged) { + // Update all selectors in the current BasePanel. + panel.setupMainPanel(); + // Mark the database updated so changes are not lost panel.markNonUndoableBaseChanged(); } - - panel.getAutoCompleters().addContentSelectorValuesToAutoCompleters(panel.getBibDatabaseContext().getMetaData()); - } /** diff --git a/src/main/java/org/jabref/gui/contentselector/FieldContentSelector.java b/src/main/java/org/jabref/gui/contentselector/FieldContentSelector.java deleted file mode 100644 index 1501ea7f294..00000000000 --- a/src/main/java/org/jabref/gui/contentselector/FieldContentSelector.java +++ /dev/null @@ -1,200 +0,0 @@ -package org.jabref.gui.contentselector; - -import java.awt.Dimension; -import java.awt.GridBagConstraints; -import java.awt.GridBagLayout; -import java.awt.Window; -import java.awt.event.ActionEvent; - -import javax.swing.AbstractAction; -import javax.swing.Box; -import javax.swing.JButton; -import javax.swing.JComboBox; -import javax.swing.JComponent; -import javax.swing.KeyStroke; - -import org.jabref.gui.BasePanel; -import org.jabref.gui.JabRefFrame; -import org.jabref.gui.fieldeditors.FieldEditor; -import org.jabref.logic.l10n.Localization; -import org.jabref.model.entry.Keyword; -import org.jabref.model.entry.KeywordList; -import org.jabref.model.metadata.MetaData; - -import com.jgoodies.forms.layout.Sizes; -import com.jgoodies.looks.Options; - -/** - * A combo-box and a manage button that will add selected strings to an - * associated entry editor. - * - * Used to manage keywords and authors for instance. - */ -public class FieldContentSelector extends JComponent { - - private static final int MAX_CONTENT_SELECTOR_WIDTH = 240; // The max width of the combobox for content selectors. - private final JComboBox comboBox; - - private final FieldEditor editor; - - private final MetaData metaData; - - private final AbstractAction action; - private final String delimiter; - - - /** - * - * Create a new FieldContentSelector. - * - * @param frame - * The one JabRef-Frame. - * @param panel - * The basepanel the entry-editor is on. - * @param owner - * The window/frame/dialog which should be the owner of the - * content selector dialog. - * @param editor - * The entry editor which will be appended by the text selected - * by the user from the combobox. - * @param action - * The action that will be performed to after an item from the - * combobox has been appended to the text in the entryeditor. - * @param horizontalLayout - * Whether to put a 2 pixel horizontal strut between combobox and - * button. - */ - public FieldContentSelector(JabRefFrame frame, final BasePanel panel, Window owner, final FieldEditor editor, - final AbstractAction action, boolean horizontalLayout, String delimiter) { - - - this.editor = editor; - this.metaData = panel.getBibDatabaseContext().getMetaData(); - this.action = action; - this.delimiter = delimiter; - - comboBox = new JComboBox() { - - @Override - public Dimension getPreferredSize() { - Dimension parents = super.getPreferredSize(); - if (parents.width > MAX_CONTENT_SELECTOR_WIDTH) { - parents.width = MAX_CONTENT_SELECTOR_WIDTH; - } - return parents; - } - }; - - GridBagLayout gbl = new GridBagLayout(); - GridBagConstraints con = new GridBagConstraints(); - - setLayout(gbl); - - // comboBox.setEditable(true); - - comboBox.setMaximumRowCount(35); - - // Set the width of the popup independent of the size of th box itself: - comboBox.putClientProperty(Options.COMBO_POPUP_PROTOTYPE_DISPLAY_VALUE_KEY, - "The longest text in the combo popup menu. And even longer."); - - rebuildComboBox(); - - con.gridwidth = horizontalLayout ? 3 : GridBagConstraints.REMAINDER; - con.fill = GridBagConstraints.HORIZONTAL; - con.weightx = 1; - gbl.setConstraints(comboBox, con); - - comboBox.addActionListener(e -> { - /* - * These conditions signify arrow key navigation in the dropdown - * list, so we should not react to it. I'm not sure if this is - * well defined enough to be guaranteed to work everywhere. - */ - if ("comboBoxChanged".equals(e.getActionCommand()) && (e.getModifiers() == 0)) { - return; - } - - selectionMade(); - }); - // Add an action for the Enter key that signals a selection: - comboBox.getInputMap().put(KeyStroke.getKeyStroke("ENTER"), "enter"); - comboBox.getActionMap().put("enter", new AbstractAction() { - - @Override - public void actionPerformed(ActionEvent actionEvent) { - selectionMade(); - comboBox.setPopupVisible(false); - } - }); - - add(comboBox); - - if (horizontalLayout) { - add(Box.createHorizontalStrut(Sizes.dialogUnitXAsPixel(2, this))); - } - - JButton manage = new JButton(Localization.lang("Manage")); - gbl.setConstraints(manage, con); - add(manage); - - manage.addActionListener(e -> { - ContentSelectorDialog csd = new ContentSelectorDialog(owner, frame, panel, true, editor.getFieldName()); - csd.setLocationRelativeTo(frame); - - // Calling setVisible(true) will open the modal dialog and block - // for the dialog to close. - csd.setVisible(true); - - // So we need to rebuild the ComboBox afterwards - rebuildComboBox(); - }); - } - - private void selectionMade() { - // The first element is empty to avoid a preselection - if (comboBox.getSelectedIndex() == 0) { - return; - } - - String chosen = (String) comboBox.getSelectedItem(); - if ((chosen == null) || chosen.isEmpty()) { - return; - } - - String currentText = editor.getText(); - KeywordList words = KeywordList.parse(currentText, this.delimiter.charAt(0)); - boolean alreadyInList = words.contains(new Keyword(chosen)); - - // not the first word and no duplicate -> we need a comma - if (!"".equals(currentText) && !alreadyInList) { - editor.append(FieldContentSelector.this.delimiter); - } - - // no duplicate -> add it - if (!alreadyInList) { - editor.append(chosen); - } - - comboBox.setSelectedIndex(0); - - // Fire event that we changed the editor - if (action != null) { - action.actionPerformed(new ActionEvent(editor, 0, "")); - } - - // Transfer focus to the editor. - editor.requestFocus(); - } - - public void rebuildComboBox() { - comboBox.removeAllItems(); - - // To have an empty field as the default for the combobox - comboBox.addItem(""); - for (String item : metaData.getContentSelectorValuesForField(editor.getFieldName())) { - comboBox.addItem(item); - } - } - -} diff --git a/src/main/java/org/jabref/gui/entryeditor/EntryEditor.java b/src/main/java/org/jabref/gui/entryeditor/EntryEditor.java index d3e735cd079..fdccb95a3af 100644 --- a/src/main/java/org/jabref/gui/entryeditor/EntryEditor.java +++ b/src/main/java/org/jabref/gui/entryeditor/EntryEditor.java @@ -14,11 +14,9 @@ import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.ArrayList; -import java.util.HashSet; import java.util.List; import java.util.Objects; import java.util.Optional; -import java.util.Set; import javax.swing.AbstractAction; import javax.swing.Action; @@ -49,7 +47,6 @@ import org.jabref.gui.JabRefFrame; import org.jabref.gui.OSXCompatibleToolbar; import org.jabref.gui.actions.Actions; -import org.jabref.gui.contentselector.FieldContentSelector; import org.jabref.gui.externalfiles.WriteXMPEntryEditorAction; import org.jabref.gui.fieldeditors.FieldEditor; import org.jabref.gui.fieldeditors.TextField; @@ -65,7 +62,6 @@ import org.jabref.gui.util.component.CheckBoxMessage; import org.jabref.gui.util.component.VerticalLabelUI; import org.jabref.logic.TypedBibEntry; -import org.jabref.logic.autocompleter.AutoCompleter; import org.jabref.logic.bibtex.InvalidFieldValueException; import org.jabref.logic.bibtex.LatexFieldFormatter; import org.jabref.logic.bibtexkeypattern.BibtexKeyPatternUtil; @@ -136,7 +132,6 @@ public class EntryEditor extends JPanel implements EntryContainer { private final TabPane tabbed = new TabPane(); private final JabRefFrame frame; private final BasePanel panel; - private final Set contentSelectors = new HashSet<>(); private final HelpAction helpAction = new HelpAction(HelpFile.ENTRY_EDITOR, IconTheme.JabRefIcon.HELP.getIcon()); private final UndoAction undoAction = new UndoAction(); private final RedoAction redoAction = new RedoAction(); @@ -147,7 +142,7 @@ public class EntryEditor extends JPanel implements EntryContainer { */ private boolean movingToDifferentEntry; - public EntryEditor(JabRefFrame frame, BasePanel panel, BibEntry entry) { + public EntryEditor(JabRefFrame frame, BasePanel panel, BibEntry entry, String lastTabName) { this.frame = frame; this.panel = panel; this.entry = Objects.requireNonNull(entry); @@ -178,11 +173,25 @@ public void keyReleased(java.awt.event.KeyEvent e) { @Override public void keyPressed(java.awt.event.KeyEvent e) { //We need to consume this event here to prevent the propgation of keybinding events back to the JFrame - e.consume(); + Optional keyBinding = Globals.getKeyPrefs().mapToKeyBinding(e); + if (keyBinding.isPresent()) { + switch (keyBinding.get()) { + case CUT: + case COPY: + case PASTE: + case CLOSE_ENTRY_EDITOR: + case DELETE_ENTRY: + case SELECT_ALL: + e.consume(); + break; + default: + //do nothing + } + } } }); DefaultTaskExecutor.runInJavaFXThread(() -> { - addTabs(); + addTabs(lastTabName); container.setScene(new Scene(tabbed)); }); add(container, BorderLayout.CENTER); @@ -195,7 +204,10 @@ public void keyPressed(java.awt.event.KeyEvent e) { }); setupKeyBindings(); + } + private void selectLastUsedTab(String lastTabName) { + tabbed.getTabs().stream().filter(tab -> lastTabName.equals(tab.getText())).findFirst().ifPresent(tab -> tabbed.getSelectionModel().select(tab)); } /** @@ -206,30 +218,32 @@ private void setupKeyBindings() { Optional keyBinding = Globals.getKeyPrefs().mapToKeyBinding(event); if (keyBinding.isPresent()) { switch (keyBinding.get()) { - case ENTRY_EDITOR_NEXT_PANEL: - case ENTRY_EDITOR_NEXT_PANEL_2: - tabbed.getSelectionModel().selectNext(); - event.consume(); - break; - case ENTRY_EDITOR_PREVIOUS_PANEL: - case ENTRY_EDITOR_PREVIOUS_PANEL_2: - tabbed.getSelectionModel().selectPrevious(); - event.consume(); - break; - case HELP: - helpAction.actionPerformed(null); - event.consume(); - break; - - default: - - // Pass other keys to children + case ENTRY_EDITOR_NEXT_PANEL: + case ENTRY_EDITOR_NEXT_PANEL_2: + tabbed.getSelectionModel().selectNext(); + event.consume(); + break; + case ENTRY_EDITOR_PREVIOUS_PANEL: + case ENTRY_EDITOR_PREVIOUS_PANEL_2: + tabbed.getSelectionModel().selectPrevious(); + event.consume(); + break; + case HELP: + helpAction.actionPerformed(null); + event.consume(); + break; + case CLOSE_ENTRY_EDITOR: + closeAction.actionPerformed(null); + event.consume(); + break; + default: + // Pass other keys to children } } }); } - private void addTabs() { + private void addTabs(String lastTabName) { EntryType type = EntryTypes.getTypeOrDefault(entry.getType(), this.frame.getCurrentBasePanel().getBibDatabaseContext().getMode()); @@ -274,6 +288,8 @@ private void addTabs() { if (Globals.prefs.getBoolean(JabRefPreferences.DEFAULT_SHOW_SOURCE)) { tabbed.getSelectionModel().select(sourceTab); + } else { + selectLastUsedTab(lastTabName); } } @@ -387,28 +403,6 @@ public void mousePressed(MouseEvent e) { add(leftPan, BorderLayout.WEST); } - /** - * getExtra checks the field name against InternalBibtexFields.getFieldExtras(name). - * If the name has an entry, the proper component to be shown is created and - * returned. Otherwise, null is returned. In addition, e.g. listeners can be - * added to the field editor, even if no component is returned. - * - * @return Component to show, or null if none. - */ - /* - public Optional getExtra(final FieldEditor editor) { - final String fieldName = editor.getFieldName(); - - final Set fieldExtras = InternalBibtexFields.getFieldProperties(fieldName); - - if (!panel.getBibDatabaseContext().getMetaData().getContentSelectorValuesForField(fieldName).isEmpty()) { - return FieldExtraComponents.getSelectorExtraComponent(frame, panel, editor, contentSelectors, - storeFieldAction); - } - return Optional.empty(); - } - */ - void addSearchListener(SearchQueryHighlightListener listener) { // TODO: Highlight search text in entry editors searchListeners.add(listener); @@ -467,14 +461,6 @@ public void setFocusToField(String fieldName) { } } - public void updateAllContentSelectors() { - if (!contentSelectors.isEmpty()) { - for (FieldContentSelector contentSelector : contentSelectors) { - contentSelector.rebuildComboBox(); - } - } - } - public void setMovingToDifferentEntry() { movingToDifferentEntry = true; unregisterListeners(); @@ -715,11 +701,13 @@ public void actionPerformed(ActionEvent event) { fieldEditor.setValidBackgroundColor(); - // See if we need to update an AutoCompleter instance: - AutoCompleter aComp = panel.getAutoCompleters().get(fieldEditor.getFieldName()); + //TODO: See if we need to update an AutoCompleter instance: + /* + AutoCompleter aComp = panel.getSuggestionProviders().get(fieldEditor.getFieldName()); if (aComp != null) { aComp.addBibtexEntry(entry); } + */ // Add an UndoableFieldChange to the baseframe's undoManager. UndoableFieldChange undoableFieldChange = new UndoableFieldChange(entry, @@ -832,7 +820,7 @@ public void actionPerformed(ActionEvent e) { } BibtexKeyPatternUtil.makeAndSetLabel(panel.getBibDatabaseContext().getMetaData() - .getCiteKeyPattern(Globals.prefs.getBibtexKeyPatternPreferences().getKeyPattern()), + .getCiteKeyPattern(Globals.prefs.getBibtexKeyPatternPreferences().getKeyPattern()), panel.getDatabase(), entry, Globals.prefs.getBibtexKeyPatternPreferences()); diff --git a/src/main/java/org/jabref/gui/entryeditor/FieldExtraComponents.java b/src/main/java/org/jabref/gui/entryeditor/FieldExtraComponents.java deleted file mode 100644 index 5603aaa0b49..00000000000 --- a/src/main/java/org/jabref/gui/entryeditor/FieldExtraComponents.java +++ /dev/null @@ -1,40 +0,0 @@ -package org.jabref.gui.entryeditor; - -import java.util.Optional; -import java.util.Set; - -import javax.swing.JComponent; - -import org.jabref.gui.BasePanel; -import org.jabref.gui.JabRefFrame; -import org.jabref.gui.contentselector.FieldContentSelector; -import org.jabref.gui.entryeditor.EntryEditor.StoreFieldAction; -import org.jabref.gui.fieldeditors.FieldEditor; -import org.jabref.model.entry.FieldProperty; -import org.jabref.model.entry.InternalBibtexFields; - -public class FieldExtraComponents { - - private FieldExtraComponents() { - } - - - /** - * Return a button opening a content selector for fields where one exists - * - * @param frame - * @param panel - * @param editor - * @param contentSelectors - * @param storeFieldAction - * @return - */ - public static Optional getSelectorExtraComponent(JabRefFrame frame, BasePanel panel, FieldEditor editor, - Set contentSelectors, StoreFieldAction storeFieldAction) { - FieldContentSelector ws = new FieldContentSelector(frame, panel, frame, editor, storeFieldAction, false, - InternalBibtexFields.getFieldProperties(editor.getFieldName()) - .contains(FieldProperty.PERSON_NAMES) ? " and " : ", "); - contentSelectors.add(ws); - return Optional.of(ws); - } -} diff --git a/src/main/java/org/jabref/gui/entryeditor/FieldsEditorTab.java b/src/main/java/org/jabref/gui/entryeditor/FieldsEditorTab.java index e2663763b23..6956ba518d3 100644 --- a/src/main/java/org/jabref/gui/entryeditor/FieldsEditorTab.java +++ b/src/main/java/org/jabref/gui/entryeditor/FieldsEditorTab.java @@ -45,8 +45,8 @@ class FieldsEditorTab extends EntryEditorTab { private final Map editors = new LinkedHashMap<>(); private final JabRefFrame frame; private final BasePanel basePanel; - private FieldEditorFX activeField; private final BibEntry entry; + private FieldEditorFX activeField; public FieldsEditorTab(JabRefFrame frame, BasePanel basePanel, List fields, EntryEditor parent, boolean addKeyField, boolean compressed, BibEntry entry) { this.entry = Objects.requireNonNull(entry); @@ -109,21 +109,12 @@ private Region setupPanel(JabRefFrame frame, BasePanel bPanel, boolean compresse } Optional extra = parent.getExtra(fieldEditor); - - // Add autocompleter listener, if required for this field: - /* - AutoCompleter autoCompleter = bPanel.getAutoCompleters().get(field); - AutoCompleteListener autoCompleteListener = null; - if (autoCompleter != null) { - autoCompleteListener = new AutoCompleteListener(autoCompleter); - } - setupJTextComponent(fieldEditor.getTextComponent(), autoCompleteListener); - fieldEditor.setAutoCompleteListener(autoCompleteListener); */ - FieldEditorFX fieldEditor = FieldEditors.getForField(fieldName, Globals.taskExecutor, new FXDialogService(), + FieldEditorFX fieldEditor = FieldEditors.getForField(fieldName, Globals.TASK_EXECUTOR, new FXDialogService(), Globals.journalAbbreviationLoader, Globals.prefs.getJournalAbbreviationPreferences(), Globals.prefs, - bPanel.getBibDatabaseContext(), entry.getType()); + bPanel.getBibDatabaseContext(), entry.getType(), + bPanel.getSuggestionProviders()); fieldEditor.bindToEntry(entry); editors.put(fieldName, fieldEditor); @@ -141,18 +132,6 @@ private Region setupPanel(JabRefFrame frame, BasePanel bPanel, boolean compresse } */ - /* - // TODO: Reenable content selector - if (!panel.getBibDatabaseContext().getMetaData().getContentSelectorValuesForField(editor.getFieldName()).isEmpty()) { - FieldContentSelector ws = new FieldContentSelector(frame, panel, frame, editor, storeFieldAction, false, - ", "); - contentSelectors.add(ws); - controls.add(ws, BorderLayout.NORTH); - } - //} else if (!panel.getBibDatabaseContext().getMetaData().getContentSelectorValuesForField(fieldName).isEmpty()) { - //return FieldExtraComponents.getSelectorExtraComponent(frame, panel, editor, contentSelectors, storeFieldAction); - */ - labels.add(new FieldNameLabel(fieldName)); } diff --git a/src/main/java/org/jabref/gui/entryeditor/RelatedArticlesTab.java b/src/main/java/org/jabref/gui/entryeditor/RelatedArticlesTab.java index 688498fac79..efda59424b9 100644 --- a/src/main/java/org/jabref/gui/entryeditor/RelatedArticlesTab.java +++ b/src/main/java/org/jabref/gui/entryeditor/RelatedArticlesTab.java @@ -44,7 +44,7 @@ private StackPane getPane(BibEntry entry) { progress.setVisible(false); browser.getEngine().loadContent(convertToHtml(relatedArticles)); }) - .executeWith(Globals.taskExecutor); + .executeWith(Globals.TASK_EXECUTOR); browser.getEngine().getLoadWorker().stateProperty().addListener(new OpenHyperlinksInExternalBrowser(browser)); diff --git a/src/main/java/org/jabref/gui/exporter/SaveDatabaseAction.java b/src/main/java/org/jabref/gui/exporter/SaveDatabaseAction.java index f12e63492a3..76f5aafbb3c 100644 --- a/src/main/java/org/jabref/gui/exporter/SaveDatabaseAction.java +++ b/src/main/java/org/jabref/gui/exporter/SaveDatabaseAction.java @@ -316,7 +316,8 @@ public void saveAs() throws Exception { */ public void saveAs(File file) throws Exception { BibDatabaseContext context = panel.getBibDatabaseContext(); - + Path oldFile = context.getDatabasePath().get(); + if (context.getLocation() == DatabaseLocation.SHARED) { // Save all properties dependent on the ID. This makes it possible to restore them. DBMSConnectionProperties properties = context.getDBMSSynchronizer().getDBProcessor() @@ -334,8 +335,14 @@ public void saveAs(File file) throws Exception { if (!success) { return; } - // Register so we get notifications about outside changes to the file. + //closing AutosaveManager and BackupManager for original library + context.setDatabaseFile(oldFile.toFile()); + AutosaveManager.shutdown(context); + BackupManager.shutdown(context); + context.setDatabaseFile(file); + + // Register so we get notifications about outside changes to the file. try { panel.setFileMonitorHandle(Globals.getFileUpdateMonitor().addUpdateListener(panel, context.getDatabaseFile().orElse(null))); diff --git a/src/main/java/org/jabref/gui/externalfiles/FindFullTextAction.java b/src/main/java/org/jabref/gui/externalfiles/FindFullTextAction.java index 8395a3381dc..5bf278dcd9f 100644 --- a/src/main/java/org/jabref/gui/externalfiles/FindFullTextAction.java +++ b/src/main/java/org/jabref/gui/externalfiles/FindFullTextAction.java @@ -31,7 +31,7 @@ public class FindFullTextAction extends AbstractWorker { private static final Log LOGGER = LogFactory.getLog(FindFullTextAction.class); - private static final int warningLimit = 5; // The minimum number of selected entries to ask the user for confirmation + private static final int WARNING_LIMIT = 5; // The minimum number of selected entries to ask the user for confirmation private final BasePanel basePanel; private final Map, BibEntry> downloads = new ConcurrentHashMap<>(); @@ -51,7 +51,7 @@ public void init() throws Exception { @Override public void run() { - if (basePanel.getSelectedEntries().size() >= warningLimit) { + if (basePanel.getSelectedEntries().size() >= WARNING_LIMIT) { String[] options = new String[] {Localization.lang("Look up full text documents"), Localization.lang("Cancel")}; int answer = JOptionPane.showOptionDialog(basePanel.frame(), diff --git a/src/main/java/org/jabref/gui/fieldeditors/AbstractEditorViewModel.java b/src/main/java/org/jabref/gui/fieldeditors/AbstractEditorViewModel.java index 046e47de7f7..b9443eb6221 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/AbstractEditorViewModel.java +++ b/src/main/java/org/jabref/gui/fieldeditors/AbstractEditorViewModel.java @@ -1,29 +1,51 @@ package org.jabref.gui.fieldeditors; +import java.util.Collection; + +import javafx.beans.binding.ObjectBinding; import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; import org.jabref.gui.AbstractViewModel; +import org.jabref.gui.autocompleter.AutoCompleteSuggestionProvider; import org.jabref.gui.util.BindingsHelper; import org.jabref.model.entry.BibEntry; +import org.controlsfx.control.textfield.AutoCompletionBinding; + public class AbstractEditorViewModel extends AbstractViewModel { + protected final String fieldName; protected StringProperty text = new SimpleStringProperty(""); protected BibEntry entry; + private final AutoCompleteSuggestionProvider suggestionProvider; + private ObjectBinding fieldBinding; + + public AbstractEditorViewModel(String fieldName, AutoCompleteSuggestionProvider suggestionProvider) { + this.fieldName = fieldName; + this.suggestionProvider = suggestionProvider; + } public StringProperty textProperty() { return text; } - public void bindToEntry(String fieldName, BibEntry entry) { + public void bindToEntry(BibEntry entry) { this.entry = entry; + + // We need to keep a reference to the binding since it otherwise gets discarded + fieldBinding = entry.getFieldBinding(fieldName); + BindingsHelper.bindBidirectional( this.textProperty(), - entry.getFieldBinding(fieldName), + fieldBinding, newValue -> { if (newValue != null) { entry.setField(fieldName, newValue); } }); } + + public Collection complete(AutoCompletionBinding.ISuggestionRequest request) { + return suggestionProvider.call(request); + } } diff --git a/src/main/java/org/jabref/gui/fieldeditors/DateEditor.java b/src/main/java/org/jabref/gui/fieldeditors/DateEditor.java index 6f7abfe4145..65a1dbcce55 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/DateEditor.java +++ b/src/main/java/org/jabref/gui/fieldeditors/DateEditor.java @@ -6,19 +6,18 @@ import javafx.scene.Parent; import javafx.scene.layout.HBox; +import org.jabref.gui.autocompleter.AutoCompleteSuggestionProvider; import org.jabref.gui.util.ControlHelper; import org.jabref.gui.util.component.TemporalAccessorPicker; import org.jabref.model.entry.BibEntry; public class DateEditor extends HBox implements FieldEditorFX { - private final String fieldName; @FXML private DateEditorViewModel viewModel; @FXML private TemporalAccessorPicker datePicker; - public DateEditor(String fieldName, DateTimeFormatter dateFormatter) { - this.fieldName = fieldName; - this.viewModel = new DateEditorViewModel(dateFormatter); + public DateEditor(String fieldName, DateTimeFormatter dateFormatter, AutoCompleteSuggestionProvider suggestionProvider) { + this.viewModel = new DateEditorViewModel(fieldName, suggestionProvider, dateFormatter); ControlHelper.loadFXMLForControl(this); @@ -32,7 +31,7 @@ public DateEditorViewModel getViewModel() { @Override public void bindToEntry(BibEntry entry) { - viewModel.bindToEntry(fieldName, entry); + viewModel.bindToEntry(entry); } @Override diff --git a/src/main/java/org/jabref/gui/fieldeditors/DateEditorViewModel.java b/src/main/java/org/jabref/gui/fieldeditors/DateEditorViewModel.java index a9598706f4d..6ea9f8d2237 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/DateEditorViewModel.java +++ b/src/main/java/org/jabref/gui/fieldeditors/DateEditorViewModel.java @@ -6,13 +6,15 @@ import javafx.util.StringConverter; +import org.jabref.gui.autocompleter.AutoCompleteSuggestionProvider; import org.jabref.model.entry.Date; import org.jabref.model.strings.StringUtil; public class DateEditorViewModel extends AbstractEditorViewModel { private final DateTimeFormatter dateFormatter; - public DateEditorViewModel(DateTimeFormatter dateFormatter) { + public DateEditorViewModel(String fieldName, AutoCompleteSuggestionProvider suggestionProvider, DateTimeFormatter dateFormatter) { + super(fieldName, suggestionProvider); this.dateFormatter = dateFormatter; } diff --git a/src/main/java/org/jabref/gui/fieldeditors/EditorTextArea.java b/src/main/java/org/jabref/gui/fieldeditors/EditorTextArea.java index 4ad8b9d2e0e..06a4a2179ed 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/EditorTextArea.java +++ b/src/main/java/org/jabref/gui/fieldeditors/EditorTextArea.java @@ -7,6 +7,8 @@ import javafx.fxml.Initializable; import javafx.scene.control.ContextMenu; import javafx.scene.control.MenuItem; +import javafx.scene.input.KeyCode; +import javafx.scene.input.KeyEvent; import com.sun.javafx.scene.control.skin.TextAreaSkin; @@ -24,6 +26,26 @@ public EditorTextArea(String text) { // Hide horizontal scrollbar and always wrap text setWrapText(true); + + // Should behave as a normal text field with respect to TAB behaviour + addEventFilter(KeyEvent.KEY_PRESSED, event -> { + if (event.getCode() == KeyCode.TAB) { + TextAreaSkin skin = (TextAreaSkin) getSkin(); + if (event.isShiftDown()) { + // Shift + Tab > previous text area + skin.getBehavior().traversePrevious(); + } else { + if (event.isControlDown()) { + // Ctrl + Tab > insert tab + skin.getBehavior().callAction("InsertTab"); + } else { + // Tab > next text area + skin.getBehavior().traverseNext(); + } + } + event.consume(); + } + }); } /** diff --git a/src/main/java/org/jabref/gui/fieldeditors/EditorTypeEditorViewModel.java b/src/main/java/org/jabref/gui/fieldeditors/EditorTypeEditorViewModel.java index ea05ee521ae..118430fea4b 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/EditorTypeEditorViewModel.java +++ b/src/main/java/org/jabref/gui/fieldeditors/EditorTypeEditorViewModel.java @@ -1,5 +1,6 @@ package org.jabref.gui.fieldeditors; +import org.jabref.gui.autocompleter.AutoCompleteSuggestionProvider; import org.jabref.logic.l10n.Localization; import com.google.common.collect.BiMap; @@ -9,7 +10,9 @@ public class EditorTypeEditorViewModel extends MapBasedEditorViewModel { private BiMap itemMap = HashBiMap.create(7); - public EditorTypeEditorViewModel() { + public EditorTypeEditorViewModel(String fieldName, AutoCompleteSuggestionProvider suggestionProvider) { + super(fieldName, suggestionProvider); + itemMap.put("editor", Localization.lang("Editor")); itemMap.put("compiler", Localization.lang("Compiler")); itemMap.put("founder", Localization.lang("Founder")); diff --git a/src/main/java/org/jabref/gui/fieldeditors/FieldEditor.java b/src/main/java/org/jabref/gui/fieldeditors/FieldEditor.java index d80d525a4a1..711b3ffc550 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/FieldEditor.java +++ b/src/main/java/org/jabref/gui/fieldeditors/FieldEditor.java @@ -4,8 +4,6 @@ import javax.swing.JComponent; -import org.jabref.gui.autocompleter.AutoCompleteListener; - /** * FieldEditors is a common interface between the TextField and TextArea. */ @@ -71,8 +69,4 @@ default boolean hasFocus() { void undo(); void redo(); - - void setAutoCompleteListener(AutoCompleteListener listener); - - void clearAutoCompleteSuggestion(); } diff --git a/src/main/java/org/jabref/gui/fieldeditors/FieldEditors.java b/src/main/java/org/jabref/gui/fieldeditors/FieldEditors.java index ffce18ff815..fbc9207d07c 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/FieldEditors.java +++ b/src/main/java/org/jabref/gui/fieldeditors/FieldEditors.java @@ -1,60 +1,92 @@ package org.jabref.gui.fieldeditors; import java.time.format.DateTimeFormatter; +import java.util.List; import java.util.Set; import org.jabref.Globals; import org.jabref.gui.DialogService; +import org.jabref.gui.autocompleter.AutoCompleteSuggestionProvider; +import org.jabref.gui.autocompleter.ContentSelectorSuggestionProvider; +import org.jabref.gui.autocompleter.SuggestionProviders; import org.jabref.gui.util.TaskExecutor; import org.jabref.logic.journals.JournalAbbreviationLoader; import org.jabref.logic.journals.JournalAbbreviationPreferences; import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.entry.FieldProperty; import org.jabref.model.entry.InternalBibtexFields; +import org.jabref.model.metadata.MetaData; import org.jabref.preferences.JabRefPreferences; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + public class FieldEditors { - public static FieldEditorFX getForField(String fieldName, TaskExecutor taskExecutor, DialogService dialogService, JournalAbbreviationLoader journalAbbreviationLoader, JournalAbbreviationPreferences journalAbbreviationPreferences, JabRefPreferences preferences, BibDatabaseContext databaseContext, String entryType) { + private static final Log LOGGER = LogFactory.getLog(FieldEditors.class); + + public static FieldEditorFX getForField(String fieldName, TaskExecutor taskExecutor, DialogService dialogService, JournalAbbreviationLoader journalAbbreviationLoader, JournalAbbreviationPreferences journalAbbreviationPreferences, JabRefPreferences preferences, BibDatabaseContext databaseContext, String entryType, SuggestionProviders suggestionProviders) { final Set fieldExtras = InternalBibtexFields.getFieldProperties(fieldName); + AutoCompleteSuggestionProvider suggestionProvider = getSuggestionProvider(fieldName, suggestionProviders, databaseContext.getMetaData()); + if (Globals.prefs.get(JabRefPreferences.TIME_STAMP_FIELD).equals(fieldName) || fieldExtras.contains(FieldProperty.DATE)) { if (fieldExtras.contains(FieldProperty.ISO_DATE)) { - return new DateEditor(fieldName, DateTimeFormatter.ofPattern("[uuuu][-MM][-dd]")); + return new DateEditor(fieldName, DateTimeFormatter.ofPattern("[uuuu][-MM][-dd]"), suggestionProvider); } else { - return new DateEditor(fieldName, DateTimeFormatter.ofPattern(Globals.prefs.get(JabRefPreferences.TIME_STAMP_FORMAT))); + return new DateEditor(fieldName, DateTimeFormatter.ofPattern(Globals.prefs.get(JabRefPreferences.TIME_STAMP_FORMAT)), suggestionProvider); } } else if (fieldExtras.contains(FieldProperty.EXTERNAL)) { - return new UrlEditor(fieldName, dialogService); + return new UrlEditor(fieldName, dialogService, suggestionProvider); } else if (fieldExtras.contains(FieldProperty.JOURNAL_NAME)) { - return new JournalEditor(fieldName, journalAbbreviationLoader, journalAbbreviationPreferences); + return new JournalEditor(fieldName, journalAbbreviationLoader, journalAbbreviationPreferences, suggestionProvider); } else if (fieldExtras.contains(FieldProperty.DOI) || fieldExtras.contains(FieldProperty.EPRINT) || fieldExtras.contains(FieldProperty.ISBN)) { - return new IdentifierEditor(fieldName, taskExecutor, dialogService); + return new IdentifierEditor(fieldName, taskExecutor, dialogService, suggestionProvider); } else if (fieldExtras.contains(FieldProperty.OWNER)) { - return new OwnerEditor(fieldName, preferences); + return new OwnerEditor(fieldName, preferences, suggestionProvider); } else if (fieldExtras.contains(FieldProperty.FILE_EDITOR)) { - return new LinkedFilesEditor(fieldName, dialogService, databaseContext, taskExecutor); + return new LinkedFilesEditor(fieldName, dialogService, databaseContext, taskExecutor, suggestionProvider); } else if (fieldExtras.contains(FieldProperty.YES_NO)) { - return new OptionEditor<>(fieldName, new YesNoEditorViewModel()); + return new OptionEditor<>(fieldName, new YesNoEditorViewModel(fieldName, suggestionProvider)); } else if (fieldExtras.contains(FieldProperty.MONTH)) { - return new OptionEditor<>(fieldName, new MonthEditorViewModel(databaseContext.getMode())); + return new OptionEditor<>(fieldName, new MonthEditorViewModel(fieldName, suggestionProvider, databaseContext.getMode())); } else if (fieldExtras.contains(FieldProperty.GENDER)) { - return new OptionEditor<>(fieldName, new GenderEditorViewModel()); + return new OptionEditor<>(fieldName, new GenderEditorViewModel(fieldName, suggestionProvider)); } else if (fieldExtras.contains(FieldProperty.EDITOR_TYPE)) { - return new OptionEditor<>(fieldName, new EditorTypeEditorViewModel()); + return new OptionEditor<>(fieldName, new EditorTypeEditorViewModel(fieldName, suggestionProvider)); } else if (fieldExtras.contains(FieldProperty.PAGINATION)) { - return new OptionEditor<>(fieldName, new PaginationEditorViewModel()); + return new OptionEditor<>(fieldName, new PaginationEditorViewModel(fieldName, suggestionProvider)); } else if (fieldExtras.contains(FieldProperty.TYPE)) { if ("patent".equalsIgnoreCase(entryType)) { - return new OptionEditor<>(fieldName, new PatentTypeEditorViewModel()); + return new OptionEditor<>(fieldName, new PatentTypeEditorViewModel(fieldName, suggestionProvider)); } else { - return new OptionEditor<>(fieldName, new TypeEditorViewModel()); + return new OptionEditor<>(fieldName, new TypeEditorViewModel(fieldName, suggestionProvider)); } } else if (fieldExtras.contains(FieldProperty.SINGLE_ENTRY_LINK) || fieldExtras.contains(FieldProperty.MULTIPLE_ENTRY_LINK)) { - return new LinkedEntriesEditor(fieldName, databaseContext); + return new LinkedEntriesEditor(fieldName, databaseContext, suggestionProvider); + } else if (fieldExtras.contains(FieldProperty.PERSON_NAMES)) { + return new PersonsEditor(fieldName, suggestionProvider, preferences.getAutoCompletePreferences()); } // default - return new SimpleEditor(fieldName); + return new SimpleEditor(fieldName, suggestionProvider); + } + + @SuppressWarnings("unchecked") + private static AutoCompleteSuggestionProvider getSuggestionProvider(String fieldName, SuggestionProviders suggestionProviders, MetaData metaData) { + AutoCompleteSuggestionProvider suggestionProvider = suggestionProviders.getForField(fieldName); + + List contentSelectorValues = metaData.getContentSelectorValuesForField(fieldName); + if (!contentSelectorValues.isEmpty()) { + // Enrich auto completion by content selector values + try { + return new ContentSelectorSuggestionProvider((AutoCompleteSuggestionProvider) suggestionProvider, contentSelectorValues); + } catch (ClassCastException exception) { + LOGGER.error("Content selectors are only supported for normal fields with string-based auto completion."); + return suggestionProvider; + } + } else { + return suggestionProvider; + } } } diff --git a/src/main/java/org/jabref/gui/fieldeditors/GenderEditorViewModel.java b/src/main/java/org/jabref/gui/fieldeditors/GenderEditorViewModel.java index 7df18b82bb7..8678807cb0b 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/GenderEditorViewModel.java +++ b/src/main/java/org/jabref/gui/fieldeditors/GenderEditorViewModel.java @@ -1,5 +1,6 @@ package org.jabref.gui.fieldeditors; +import org.jabref.gui.autocompleter.AutoCompleteSuggestionProvider; import org.jabref.logic.l10n.Localization; import com.google.common.collect.BiMap; @@ -9,7 +10,9 @@ public class GenderEditorViewModel extends MapBasedEditorViewModel { private BiMap itemMap = HashBiMap.create(7); - public GenderEditorViewModel() { + public GenderEditorViewModel(String fieldName, AutoCompleteSuggestionProvider suggestionProvider) { + super(fieldName, suggestionProvider); + itemMap.put("sf", Localization.lang("Female name")); itemMap.put("sm", Localization.lang("Male name")); itemMap.put("sn", Localization.lang("Neuter name")); diff --git a/src/main/java/org/jabref/gui/fieldeditors/IdentifierEditor.java b/src/main/java/org/jabref/gui/fieldeditors/IdentifierEditor.java index 69bd79aa4a4..49640668bae 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/IdentifierEditor.java +++ b/src/main/java/org/jabref/gui/fieldeditors/IdentifierEditor.java @@ -13,6 +13,7 @@ import javafx.scene.layout.HBox; import org.jabref.gui.DialogService; +import org.jabref.gui.autocompleter.AutoCompleteSuggestionProvider; import org.jabref.gui.fieldeditors.contextmenu.EditorMenus; import org.jabref.gui.util.ControlHelper; import org.jabref.gui.util.TaskExecutor; @@ -22,16 +23,14 @@ public class IdentifierEditor extends HBox implements FieldEditorFX { - private final String fieldName; @FXML private IdentifierEditorViewModel viewModel; @FXML private EditorTextArea textArea; @FXML private Button fetchInformationByIdentifierButton; @FXML private Button lookupIdentifierButton; private Optional entry; - public IdentifierEditor(String fieldName, TaskExecutor taskExecutor, DialogService dialogService) { - this.fieldName = fieldName; - this.viewModel = new IdentifierEditorViewModel(fieldName, taskExecutor, dialogService); + public IdentifierEditor(String fieldName, TaskExecutor taskExecutor, DialogService dialogService, AutoCompleteSuggestionProvider suggestionProvider) { + this.viewModel = new IdentifierEditorViewModel(fieldName, suggestionProvider, taskExecutor, dialogService); ControlHelper.loadFXMLForControl(this); @@ -57,7 +56,7 @@ public IdentifierEditorViewModel getViewModel() { @Override public void bindToEntry(BibEntry entry) { this.entry = Optional.of(entry); - viewModel.bindToEntry(fieldName, entry); + viewModel.bindToEntry(entry); } @Override diff --git a/src/main/java/org/jabref/gui/fieldeditors/IdentifierEditorViewModel.java b/src/main/java/org/jabref/gui/fieldeditors/IdentifierEditorViewModel.java index 3c3fefe9aa8..850d041c827 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/IdentifierEditorViewModel.java +++ b/src/main/java/org/jabref/gui/fieldeditors/IdentifierEditorViewModel.java @@ -9,6 +9,7 @@ import javafx.beans.property.SimpleObjectProperty; import org.jabref.gui.DialogService; +import org.jabref.gui.autocompleter.AutoCompleteSuggestionProvider; import org.jabref.gui.desktop.JabRefDesktop; import org.jabref.gui.mergeentries.FetchAndMergeEntry; import org.jabref.gui.util.BackgroundTask; @@ -23,7 +24,6 @@ import org.fxmisc.easybind.EasyBind; public class IdentifierEditorViewModel extends AbstractEditorViewModel { - private final String fieldName; private BooleanProperty validIdentifierIsNotPresent = new SimpleBooleanProperty(true); private BooleanProperty identifierLookupInProgress = new SimpleBooleanProperty(false); private BooleanProperty idFetcherAvailable = new SimpleBooleanProperty(true); @@ -31,8 +31,9 @@ public class IdentifierEditorViewModel extends AbstractEditorViewModel { private TaskExecutor taskExecutor; private DialogService dialogService; - public IdentifierEditorViewModel(String fieldName, TaskExecutor taskExecutor, DialogService dialogService) { - this.fieldName = fieldName; + public IdentifierEditorViewModel(String fieldName, AutoCompleteSuggestionProvider suggestionProvider, TaskExecutor taskExecutor, DialogService dialogService) { + super(fieldName, suggestionProvider); + this.taskExecutor = taskExecutor; this.dialogService = dialogService; diff --git a/src/main/java/org/jabref/gui/fieldeditors/JournalEditor.java b/src/main/java/org/jabref/gui/fieldeditors/JournalEditor.java index 920d8a403f3..27a6c823e38 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/JournalEditor.java +++ b/src/main/java/org/jabref/gui/fieldeditors/JournalEditor.java @@ -7,6 +7,8 @@ import javafx.scene.Parent; import javafx.scene.layout.HBox; +import org.jabref.gui.autocompleter.AutoCompleteSuggestionProvider; +import org.jabref.gui.autocompleter.AutoCompletionTextInputBinding; import org.jabref.gui.fieldeditors.contextmenu.EditorMenus; import org.jabref.gui.util.ControlHelper; import org.jabref.logic.journals.JournalAbbreviationLoader; @@ -15,20 +17,19 @@ public class JournalEditor extends HBox implements FieldEditorFX { - private final String fieldName; @FXML private JournalEditorViewModel viewModel; @FXML private EditorTextArea textArea; private Optional entry; - public JournalEditor(String fieldName, JournalAbbreviationLoader journalAbbreviationLoader, JournalAbbreviationPreferences journalAbbreviationPreferences) { - this.fieldName = fieldName; - this.viewModel = new JournalEditorViewModel(journalAbbreviationLoader, journalAbbreviationPreferences); + public JournalEditor(String fieldName, JournalAbbreviationLoader journalAbbreviationLoader, JournalAbbreviationPreferences journalAbbreviationPreferences, AutoCompleteSuggestionProvider suggestionProvider) { + this.viewModel = new JournalEditorViewModel(fieldName, suggestionProvider, journalAbbreviationLoader, journalAbbreviationPreferences); ControlHelper.loadFXMLForControl(this); textArea.textProperty().bindBidirectional(viewModel.textProperty()); - textArea.addToContextMenu(EditorMenus.getDefaultMenu(textArea)); + + AutoCompletionTextInputBinding.autoComplete(textArea, viewModel::complete); } public JournalEditorViewModel getViewModel() { @@ -38,7 +39,7 @@ public JournalEditorViewModel getViewModel() { @Override public void bindToEntry(BibEntry entry) { this.entry = Optional.of(entry); - viewModel.bindToEntry(fieldName, entry); + viewModel.bindToEntry(entry); } @Override diff --git a/src/main/java/org/jabref/gui/fieldeditors/JournalEditorViewModel.java b/src/main/java/org/jabref/gui/fieldeditors/JournalEditorViewModel.java index 18cff62a6f5..278f6c955f2 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/JournalEditorViewModel.java +++ b/src/main/java/org/jabref/gui/fieldeditors/JournalEditorViewModel.java @@ -2,6 +2,7 @@ import java.util.Optional; +import org.jabref.gui.autocompleter.AutoCompleteSuggestionProvider; import org.jabref.logic.journals.JournalAbbreviationLoader; import org.jabref.logic.journals.JournalAbbreviationPreferences; import org.jabref.logic.journals.JournalAbbreviationRepository; @@ -11,7 +12,9 @@ public class JournalEditorViewModel extends AbstractEditorViewModel { private final JournalAbbreviationLoader journalAbbreviationLoader; private final JournalAbbreviationPreferences journalAbbreviationPreferences; - public JournalEditorViewModel(JournalAbbreviationLoader journalAbbreviationLoader, JournalAbbreviationPreferences journalAbbreviationPreferences) { + public JournalEditorViewModel(String fieldName, AutoCompleteSuggestionProvider suggestionProvider, JournalAbbreviationLoader journalAbbreviationLoader, JournalAbbreviationPreferences journalAbbreviationPreferences) { + super(fieldName, suggestionProvider); + this.journalAbbreviationLoader = journalAbbreviationLoader; this.journalAbbreviationPreferences = journalAbbreviationPreferences; } diff --git a/src/main/java/org/jabref/gui/fieldeditors/LinkedEntriesEditor.java b/src/main/java/org/jabref/gui/fieldeditors/LinkedEntriesEditor.java index 1954199372b..f90dc27a89e 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/LinkedEntriesEditor.java +++ b/src/main/java/org/jabref/gui/fieldeditors/LinkedEntriesEditor.java @@ -5,6 +5,7 @@ import javafx.scene.Parent; import javafx.scene.layout.HBox; +import org.jabref.gui.autocompleter.AutoCompleteSuggestionProvider; import org.jabref.gui.util.ControlHelper; import org.jabref.gui.util.component.TagBar; import org.jabref.model.database.BibDatabaseContext; @@ -13,13 +14,11 @@ public class LinkedEntriesEditor extends HBox implements FieldEditorFX { - private final String fieldName; @FXML private LinkedEntriesEditorViewModel viewModel; @FXML private TagBar linkedEntriesBar; - public LinkedEntriesEditor(String fieldName, BibDatabaseContext databaseContext) { - this.fieldName = fieldName; - this.viewModel = new LinkedEntriesEditorViewModel(databaseContext); + public LinkedEntriesEditor(String fieldName, BibDatabaseContext databaseContext, AutoCompleteSuggestionProvider suggestionProvider) { + this.viewModel = new LinkedEntriesEditorViewModel(fieldName, suggestionProvider, databaseContext); ControlHelper.loadFXMLForControl(this); @@ -34,7 +33,7 @@ public LinkedEntriesEditorViewModel getViewModel() { @Override public void bindToEntry(BibEntry entry) { - viewModel.bindToEntry(fieldName, entry); + viewModel.bindToEntry(entry); } @Override diff --git a/src/main/java/org/jabref/gui/fieldeditors/LinkedEntriesEditorViewModel.java b/src/main/java/org/jabref/gui/fieldeditors/LinkedEntriesEditorViewModel.java index 32d6d06315c..dce4a28b009 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/LinkedEntriesEditorViewModel.java +++ b/src/main/java/org/jabref/gui/fieldeditors/LinkedEntriesEditorViewModel.java @@ -5,6 +5,7 @@ import javafx.collections.FXCollections; import javafx.util.StringConverter; +import org.jabref.gui.autocompleter.AutoCompleteSuggestionProvider; import org.jabref.gui.util.BindingsHelper; import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.entry.EntryLinkList; @@ -15,7 +16,9 @@ public class LinkedEntriesEditorViewModel extends AbstractEditorViewModel { private final BibDatabaseContext databaseContext; private final ListProperty linkedEntries; - public LinkedEntriesEditorViewModel(BibDatabaseContext databaseContext) { + public LinkedEntriesEditorViewModel(String fieldName, AutoCompleteSuggestionProvider suggestionProvider, BibDatabaseContext databaseContext) { + super(fieldName, suggestionProvider); + this.databaseContext = databaseContext; linkedEntries = new SimpleListProperty<>(FXCollections.observableArrayList()); BindingsHelper.bindContentBidirectional( diff --git a/src/main/java/org/jabref/gui/fieldeditors/LinkedFileViewModel.java b/src/main/java/org/jabref/gui/fieldeditors/LinkedFileViewModel.java index b11e0bc55bf..ddd3601f16a 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/LinkedFileViewModel.java +++ b/src/main/java/org/jabref/gui/fieldeditors/LinkedFileViewModel.java @@ -16,6 +16,7 @@ import javafx.scene.control.ButtonType; import org.jabref.Globals; +import org.jabref.gui.AbstractViewModel; import org.jabref.gui.DialogService; import org.jabref.gui.FXDialogService; import org.jabref.gui.desktop.JabRefDesktop; @@ -34,7 +35,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -public class LinkedFileViewModel extends AbstractEditorViewModel { +public class LinkedFileViewModel extends AbstractViewModel { private static final Log LOGGER = LogFactory.getLog(LinkedFileViewModel.class); @@ -45,6 +46,7 @@ public class LinkedFileViewModel extends AbstractEditorViewModel { private final BooleanProperty isAutomaticallyFound = new SimpleBooleanProperty(false); private final DialogService dialogService = new FXDialogService(); + private final BibEntry entry; public LinkedFileViewModel(LinkedFile linkedFile, BibEntry entry, BibDatabaseContext databaseContext) { this.linkedFile = linkedFile; @@ -224,6 +226,7 @@ public boolean delete() { if (file.isPresent()) { ButtonType removeFromEntry = new ButtonType(Localization.lang("Remove from entry")); + ButtonType deleteFromEntry = new ButtonType(Localization.lang("Delete from disk")); Optional buttonType = dialogService.showCustomButtonDialogAndWait(AlertType.INFORMATION, Localization.lang("Delete '%0'", file.get().toString()), diff --git a/src/main/java/org/jabref/gui/fieldeditors/LinkedFilesEditor.java b/src/main/java/org/jabref/gui/fieldeditors/LinkedFilesEditor.java index cda3e3383cb..364eac40822 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/LinkedFilesEditor.java +++ b/src/main/java/org/jabref/gui/fieldeditors/LinkedFilesEditor.java @@ -22,6 +22,7 @@ import org.jabref.Globals; import org.jabref.gui.DialogService; +import org.jabref.gui.autocompleter.AutoCompleteSuggestionProvider; import org.jabref.gui.keyboard.KeyBinding; import org.jabref.gui.util.ControlHelper; import org.jabref.gui.util.TaskExecutor; @@ -35,13 +36,11 @@ public class LinkedFilesEditor extends HBox implements FieldEditorFX { - private final String fieldName; - @FXML private LinkedFilesEditorViewModel viewModel; + @FXML private final LinkedFilesEditorViewModel viewModel; @FXML private ListView listView; - public LinkedFilesEditor(String fieldName, DialogService dialogService, BibDatabaseContext databaseContext, TaskExecutor taskExecutor) { - this.fieldName = fieldName; - this.viewModel = new LinkedFilesEditorViewModel(dialogService, databaseContext, taskExecutor); + public LinkedFilesEditor(String fieldName, DialogService dialogService, BibDatabaseContext databaseContext, TaskExecutor taskExecutor, AutoCompleteSuggestionProvider suggestionProvider) { + this.viewModel = new LinkedFilesEditorViewModel(fieldName, suggestionProvider, dialogService, databaseContext, taskExecutor); ControlHelper.loadFXMLForControl(this); @@ -79,15 +78,15 @@ private void setUpKeyBindings() { Optional keyBinding = Globals.getKeyPrefs().mapToKeyBinding(event); if (keyBinding.isPresent()) { switch (keyBinding.get()) { - case DELETE_ENTRY: - LinkedFileViewModel selectedItem = listView.getSelectionModel().getSelectedItem(); - if (selectedItem != null) { - viewModel.deleteFile(selectedItem); - } - event.consume(); - break; - default: - // Pass other keys to children + case DELETE_ENTRY: + LinkedFileViewModel selectedItem = listView.getSelectionModel().getSelectedItem(); + if (selectedItem != null) { + viewModel.deleteFile(selectedItem); + } + event.consume(); + break; + default: + // Pass other keys to children } } }); @@ -99,7 +98,7 @@ public LinkedFilesEditorViewModel getViewModel() { @Override public void bindToEntry(BibEntry entry) { - viewModel.bindToEntry(fieldName, entry); + viewModel.bindToEntry(entry); } @Override @@ -146,11 +145,15 @@ private ContextMenu createContextMenuForFile(LinkedFileViewModel linkedFile) { deleteFile.setOnAction(event -> viewModel.deleteFile(linkedFile)); deleteFile.setDisable(linkedFile.getFile().isOnlineLink()); + MenuItem deleteLink = new MenuItem(Localization.lang("Remove link")); + deleteLink.setOnAction(event -> viewModel.removeFileLink(linkedFile)); + deleteLink.setDisable(linkedFile.getFile().isOnlineLink()); + menu.getItems().add(edit); menu.getItems().add(new SeparatorMenuItem()); menu.getItems().addAll(openFile, openFolder); menu.getItems().add(new SeparatorMenuItem()); - menu.getItems().addAll(renameFile, moveFile, deleteFile); + menu.getItems().addAll(renameFile, moveFile, deleteLink, deleteFile); return menu; } diff --git a/src/main/java/org/jabref/gui/fieldeditors/LinkedFilesEditorViewModel.java b/src/main/java/org/jabref/gui/fieldeditors/LinkedFilesEditorViewModel.java index c5864940515..87a944f430b 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/LinkedFilesEditorViewModel.java +++ b/src/main/java/org/jabref/gui/fieldeditors/LinkedFilesEditorViewModel.java @@ -19,6 +19,7 @@ import org.jabref.Globals; import org.jabref.gui.DialogService; +import org.jabref.gui.autocompleter.AutoCompleteSuggestionProvider; import org.jabref.gui.externalfiles.DownloadExternalFile; import org.jabref.gui.externalfiles.FileDownloadTask; import org.jabref.gui.externalfiletype.ExternalFileType; @@ -47,15 +48,18 @@ import org.apache.commons.logging.LogFactory; public class LinkedFilesEditorViewModel extends AbstractEditorViewModel { + private static final Log LOGGER = LogFactory.getLog(LinkedFilesEditorViewModel.class); - private ListProperty files = new SimpleListProperty<>(FXCollections.observableArrayList(LinkedFileViewModel::getObservables)); - private BooleanProperty fulltextLookupInProgress = new SimpleBooleanProperty(false); - private DialogService dialogService; - private BibDatabaseContext databaseContext; - private TaskExecutor taskExecutor; + private final ListProperty files = new SimpleListProperty<>(FXCollections.observableArrayList(LinkedFileViewModel::getObservables)); + private final BooleanProperty fulltextLookupInProgress = new SimpleBooleanProperty(false); + private final DialogService dialogService; + private final BibDatabaseContext databaseContext; + private final TaskExecutor taskExecutor; + + public LinkedFilesEditorViewModel(String fieldName, AutoCompleteSuggestionProvider suggestionProvider, DialogService dialogService, BibDatabaseContext databaseContext, TaskExecutor taskExecutor) { + super(fieldName, suggestionProvider); - public LinkedFilesEditorViewModel(DialogService dialogService, BibDatabaseContext databaseContext, TaskExecutor taskExecutor) { this.dialogService = dialogService; this.databaseContext = databaseContext; this.taskExecutor = taskExecutor; @@ -63,8 +67,7 @@ public LinkedFilesEditorViewModel(DialogService dialogService, BibDatabaseContex files, text, LinkedFilesEditorViewModel::getStringRepresentation, - this::parseToFileViewModel - ); + this::parseToFileViewModel); } private static String getStringRepresentation(List files) { @@ -126,13 +129,12 @@ public void addNewFile() { newFile -> { LinkedFile newLinkedFile = fromFile(newFile, fileDirectories); files.add(new LinkedFileViewModel(newLinkedFile, entry, databaseContext)); - } - ); + }); } @Override - public void bindToEntry(String fieldName, BibEntry entry) { - super.bindToEntry(fieldName, entry); + public void bindToEntry(BibEntry entry) { + super.bindToEntry(entry); if (entry != null) { BackgroundTask> findAssociatedNotLinkedFiles = BackgroundTask @@ -193,8 +195,7 @@ public void addFromURL() { } catch (MalformedURLException exception) { dialogService.showErrorDialogAndWait( Localization.lang("Invalid URL"), - exception - ); + exception); } } } @@ -217,8 +218,7 @@ private void addFromURL(URL url) { LinkedFile newLinkedFile = fromFile(destination, fileDirectories); files.add(new LinkedFileViewModel(newLinkedFile, entry, databaseContext)); }); - downloadTask.setOnFailed(event -> - dialogService.showErrorDialogAndWait("", downloadTask.getException())); + downloadTask.setOnFailed(event -> dialogService.showErrorDialogAndWait("", downloadTask.getException())); taskExecutor.execute(downloadTask); } @@ -303,4 +303,8 @@ public void deleteFile(LinkedFileViewModel file) { files.remove(file); } } + + public void removeFileLink(LinkedFileViewModel file) { + files.remove(file); + } } diff --git a/src/main/java/org/jabref/gui/fieldeditors/MapBasedEditorViewModel.java b/src/main/java/org/jabref/gui/fieldeditors/MapBasedEditorViewModel.java index 0dd82a758f1..8fd185db2f3 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/MapBasedEditorViewModel.java +++ b/src/main/java/org/jabref/gui/fieldeditors/MapBasedEditorViewModel.java @@ -5,6 +5,8 @@ import javafx.util.StringConverter; +import org.jabref.gui.autocompleter.AutoCompleteSuggestionProvider; + import com.google.common.collect.BiMap; /** @@ -12,6 +14,10 @@ */ public abstract class MapBasedEditorViewModel extends OptionEditorViewModel { + public MapBasedEditorViewModel(String fieldName, AutoCompleteSuggestionProvider suggestionProvider) { + super(fieldName, suggestionProvider); + } + protected abstract BiMap getItemMap(); @Override diff --git a/src/main/java/org/jabref/gui/fieldeditors/MonthEditorViewModel.java b/src/main/java/org/jabref/gui/fieldeditors/MonthEditorViewModel.java index a9c0b3563f1..49c3c1592e5 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/MonthEditorViewModel.java +++ b/src/main/java/org/jabref/gui/fieldeditors/MonthEditorViewModel.java @@ -5,6 +5,7 @@ import javafx.util.StringConverter; +import org.jabref.gui.autocompleter.AutoCompleteSuggestionProvider; import org.jabref.model.database.BibDatabaseMode; import org.jabref.model.entry.Month; import org.jabref.model.strings.StringUtil; @@ -12,7 +13,8 @@ public class MonthEditorViewModel extends OptionEditorViewModel { private BibDatabaseMode databaseMode; - public MonthEditorViewModel(BibDatabaseMode databaseMode) { + public MonthEditorViewModel(String fieldName, AutoCompleteSuggestionProvider suggestionProvider, BibDatabaseMode databaseMode) { + super(fieldName, suggestionProvider); this.databaseMode = databaseMode; } diff --git a/src/main/java/org/jabref/gui/fieldeditors/OptionEditor.java b/src/main/java/org/jabref/gui/fieldeditors/OptionEditor.java index 8718d4b1c49..1b97d7be35f 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/OptionEditor.java +++ b/src/main/java/org/jabref/gui/fieldeditors/OptionEditor.java @@ -14,12 +14,10 @@ */ public class OptionEditor extends HBox implements FieldEditorFX { - private final String fieldName; @FXML private OptionEditorViewModel viewModel; @FXML private ComboBox comboBox; public OptionEditor(String fieldName, OptionEditorViewModel viewModel) { - this.fieldName = fieldName; this.viewModel = viewModel; ControlHelper.loadFXMLForControl(this); @@ -36,7 +34,7 @@ public OptionEditorViewModel getViewModel() { @Override public void bindToEntry(BibEntry entry) { - viewModel.bindToEntry(fieldName, entry); + viewModel.bindToEntry(entry); } @Override diff --git a/src/main/java/org/jabref/gui/fieldeditors/OptionEditorViewModel.java b/src/main/java/org/jabref/gui/fieldeditors/OptionEditorViewModel.java index e899a541d20..4bafb4de7c8 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/OptionEditorViewModel.java +++ b/src/main/java/org/jabref/gui/fieldeditors/OptionEditorViewModel.java @@ -4,8 +4,14 @@ import javafx.util.StringConverter; +import org.jabref.gui.autocompleter.AutoCompleteSuggestionProvider; + public abstract class OptionEditorViewModel extends AbstractEditorViewModel { + public OptionEditorViewModel(String fieldName, AutoCompleteSuggestionProvider suggestionProvider) { + super(fieldName, suggestionProvider); + } + public abstract StringConverter getStringConverter(); public abstract List getItems(); diff --git a/src/main/java/org/jabref/gui/fieldeditors/OwnerEditor.java b/src/main/java/org/jabref/gui/fieldeditors/OwnerEditor.java index a89355c07f3..ea869e1cf9e 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/OwnerEditor.java +++ b/src/main/java/org/jabref/gui/fieldeditors/OwnerEditor.java @@ -1,26 +1,22 @@ package org.jabref.gui.fieldeditors; -import java.util.Optional; - import javafx.event.ActionEvent; import javafx.fxml.FXML; import javafx.scene.Parent; import javafx.scene.layout.HBox; +import org.jabref.gui.autocompleter.AutoCompleteSuggestionProvider; import org.jabref.gui.util.ControlHelper; import org.jabref.model.entry.BibEntry; import org.jabref.preferences.JabRefPreferences; public class OwnerEditor extends HBox implements FieldEditorFX { - private final String fieldName; @FXML private OwnerEditorViewModel viewModel; @FXML private EditorTextArea textArea; - private Optional entry; - public OwnerEditor(String fieldName, JabRefPreferences preferences) { - this.fieldName = fieldName; - this.viewModel = new OwnerEditorViewModel(preferences); + public OwnerEditor(String fieldName, JabRefPreferences preferences, AutoCompleteSuggestionProvider suggestionProvider) { + this.viewModel = new OwnerEditorViewModel(fieldName, suggestionProvider, preferences); ControlHelper.loadFXMLForControl(this); @@ -33,8 +29,7 @@ public OwnerEditorViewModel getViewModel() { @Override public void bindToEntry(BibEntry entry) { - this.entry = Optional.of(entry); - viewModel.bindToEntry(fieldName, entry); + viewModel.bindToEntry(entry); } @Override diff --git a/src/main/java/org/jabref/gui/fieldeditors/OwnerEditorViewModel.java b/src/main/java/org/jabref/gui/fieldeditors/OwnerEditorViewModel.java index 321e3e0011f..7c2bef29fae 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/OwnerEditorViewModel.java +++ b/src/main/java/org/jabref/gui/fieldeditors/OwnerEditorViewModel.java @@ -1,11 +1,13 @@ package org.jabref.gui.fieldeditors; +import org.jabref.gui.autocompleter.AutoCompleteSuggestionProvider; import org.jabref.preferences.JabRefPreferences; public class OwnerEditorViewModel extends AbstractEditorViewModel { private final JabRefPreferences preferences; - public OwnerEditorViewModel(JabRefPreferences preferences) { + public OwnerEditorViewModel(String fieldName, AutoCompleteSuggestionProvider suggestionProvider, JabRefPreferences preferences) { + super(fieldName, suggestionProvider); this.preferences = preferences; } diff --git a/src/main/java/org/jabref/gui/fieldeditors/PaginationEditorViewModel.java b/src/main/java/org/jabref/gui/fieldeditors/PaginationEditorViewModel.java index c30333c2fb9..2b51e82266e 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/PaginationEditorViewModel.java +++ b/src/main/java/org/jabref/gui/fieldeditors/PaginationEditorViewModel.java @@ -1,5 +1,6 @@ package org.jabref.gui.fieldeditors; +import org.jabref.gui.autocompleter.AutoCompleteSuggestionProvider; import org.jabref.logic.l10n.Localization; import com.google.common.collect.BiMap; @@ -9,7 +10,9 @@ public class PaginationEditorViewModel extends MapBasedEditorViewModel { private BiMap itemMap = HashBiMap.create(7); - public PaginationEditorViewModel() { + public PaginationEditorViewModel(String fieldName, AutoCompleteSuggestionProvider suggestionProvider) { + super(fieldName, suggestionProvider); + itemMap.put("page", Localization.lang("Page")); itemMap.put("column", Localization.lang("Column")); itemMap.put("line", Localization.lang("Line")); diff --git a/src/main/java/org/jabref/gui/fieldeditors/PatentTypeEditorViewModel.java b/src/main/java/org/jabref/gui/fieldeditors/PatentTypeEditorViewModel.java index 8cc3c0a1df0..5523efe3cb6 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/PatentTypeEditorViewModel.java +++ b/src/main/java/org/jabref/gui/fieldeditors/PatentTypeEditorViewModel.java @@ -1,5 +1,6 @@ package org.jabref.gui.fieldeditors; +import org.jabref.gui.autocompleter.AutoCompleteSuggestionProvider; import org.jabref.logic.l10n.Localization; import com.google.common.collect.BiMap; @@ -9,7 +10,9 @@ public class PatentTypeEditorViewModel extends MapBasedEditorViewModel { private BiMap itemMap = HashBiMap.create(12); - public PatentTypeEditorViewModel() { + public PatentTypeEditorViewModel(String fieldName, AutoCompleteSuggestionProvider suggestionProvider) { + super(fieldName, suggestionProvider); + itemMap.put("patent", Localization.lang("Patent")); itemMap.put("patentde", Localization.lang("German patent")); itemMap.put("patenteu", Localization.lang("European patent")); diff --git a/src/main/java/org/jabref/gui/fieldeditors/PersonsEditor.java b/src/main/java/org/jabref/gui/fieldeditors/PersonsEditor.java new file mode 100644 index 00000000000..36dd9ed425c --- /dev/null +++ b/src/main/java/org/jabref/gui/fieldeditors/PersonsEditor.java @@ -0,0 +1,39 @@ +package org.jabref.gui.fieldeditors; + +import javafx.fxml.FXML; +import javafx.scene.Parent; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Priority; + +import org.jabref.gui.autocompleter.AutoCompletePreferences; +import org.jabref.gui.autocompleter.AutoCompleteSuggestionProvider; +import org.jabref.gui.autocompleter.AutoCompletionTextInputBinding; +import org.jabref.gui.fieldeditors.contextmenu.EditorMenus; +import org.jabref.model.entry.BibEntry; + +public class PersonsEditor extends HBox implements FieldEditorFX { + + @FXML private final PersonsEditorViewModel viewModel; + + public PersonsEditor(String fieldName, AutoCompleteSuggestionProvider suggestionProvider, AutoCompletePreferences autoCompletePreferences) { + this.viewModel = new PersonsEditorViewModel(fieldName, suggestionProvider, autoCompletePreferences); + + EditorTextArea textArea = new EditorTextArea(); + HBox.setHgrow(textArea, Priority.ALWAYS); + textArea.textProperty().bindBidirectional(viewModel.textProperty()); + textArea.addToContextMenu(EditorMenus.getDefaultMenu(textArea)); + this.getChildren().add(textArea); + + AutoCompletionTextInputBinding.autoComplete(textArea, viewModel::complete, viewModel.getAutoCompletionConverter(), viewModel.getAutoCompletionStrategy()); + } + + @Override + public void bindToEntry(BibEntry entry) { + viewModel.bindToEntry(entry); + } + + @Override + public Parent getNode() { + return this; + } +} diff --git a/src/main/java/org/jabref/gui/fieldeditors/PersonsEditorViewModel.java b/src/main/java/org/jabref/gui/fieldeditors/PersonsEditorViewModel.java new file mode 100644 index 00000000000..c41ac32773e --- /dev/null +++ b/src/main/java/org/jabref/gui/fieldeditors/PersonsEditorViewModel.java @@ -0,0 +1,37 @@ +package org.jabref.gui.fieldeditors; + +import java.util.Collection; + +import javafx.util.StringConverter; + +import org.jabref.gui.autocompleter.AppendPersonNamesStrategy; +import org.jabref.gui.autocompleter.AutoCompletePreferences; +import org.jabref.gui.autocompleter.AutoCompleteSuggestionProvider; +import org.jabref.gui.autocompleter.AutoCompletionStrategy; +import org.jabref.gui.autocompleter.PersonNameStringConverter; +import org.jabref.model.entry.Author; + +import org.controlsfx.control.textfield.AutoCompletionBinding; + +public class PersonsEditorViewModel extends AbstractEditorViewModel { + + private final AutoCompletePreferences preferences; + + public PersonsEditorViewModel(String fieldName, AutoCompleteSuggestionProvider suggestionProvider, AutoCompletePreferences preferences) { + super(fieldName, suggestionProvider); + this.preferences = preferences; + } + + public StringConverter getAutoCompletionConverter() { + return new PersonNameStringConverter(preferences); + } + + @SuppressWarnings("unchecked") + public Collection complete(AutoCompletionBinding.ISuggestionRequest request) { + return (Collection) super.complete(request); + } + + public AutoCompletionStrategy getAutoCompletionStrategy() { + return new AppendPersonNamesStrategy(); + } +} diff --git a/src/main/java/org/jabref/gui/fieldeditors/SimpleEditor.java b/src/main/java/org/jabref/gui/fieldeditors/SimpleEditor.java index 4554a22dd27..261e8a43501 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/SimpleEditor.java +++ b/src/main/java/org/jabref/gui/fieldeditors/SimpleEditor.java @@ -5,28 +5,35 @@ import javafx.scene.layout.HBox; import javafx.scene.layout.Priority; +import org.jabref.gui.autocompleter.AutoCompleteSuggestionProvider; +import org.jabref.gui.autocompleter.AutoCompletionTextInputBinding; +import org.jabref.gui.autocompleter.ContentSelectorSuggestionProvider; import org.jabref.gui.fieldeditors.contextmenu.EditorMenus; import org.jabref.model.entry.BibEntry; public class SimpleEditor extends HBox implements FieldEditorFX { - protected final String fieldName; @FXML private final SimpleEditorViewModel viewModel; - public SimpleEditor(String fieldName) { - this.fieldName = fieldName; - this.viewModel = new SimpleEditorViewModel(); + public SimpleEditor(String fieldName, AutoCompleteSuggestionProvider suggestionProvider) { + this.viewModel = new SimpleEditorViewModel(fieldName, suggestionProvider); EditorTextArea textArea = new EditorTextArea(); HBox.setHgrow(textArea, Priority.ALWAYS); textArea.textProperty().bindBidirectional(viewModel.textProperty()); textArea.addToContextMenu(EditorMenus.getDefaultMenu(textArea)); this.getChildren().add(textArea); + + AutoCompletionTextInputBinding autoCompleter = AutoCompletionTextInputBinding.autoComplete(textArea, viewModel::complete, viewModel.getAutoCompletionStrategy()); + if (suggestionProvider instanceof ContentSelectorSuggestionProvider) { + // If content selector values are present, then we want to show the auto complete suggestions immediately on focus + autoCompleter.setShowOnFocus(true); + } } @Override public void bindToEntry(BibEntry entry) { - viewModel.bindToEntry(fieldName, entry); + viewModel.bindToEntry(entry); } @Override diff --git a/src/main/java/org/jabref/gui/fieldeditors/SimpleEditorViewModel.java b/src/main/java/org/jabref/gui/fieldeditors/SimpleEditorViewModel.java index 2892b5ac730..1cc61e93ab7 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/SimpleEditorViewModel.java +++ b/src/main/java/org/jabref/gui/fieldeditors/SimpleEditorViewModel.java @@ -1,4 +1,16 @@ package org.jabref.gui.fieldeditors; +import org.jabref.gui.autocompleter.AppendWordsStrategy; +import org.jabref.gui.autocompleter.AutoCompleteSuggestionProvider; +import org.jabref.gui.autocompleter.AutoCompletionStrategy; + public class SimpleEditorViewModel extends AbstractEditorViewModel { + + public SimpleEditorViewModel(String fieldName, AutoCompleteSuggestionProvider suggestionProvider) { + super(fieldName, suggestionProvider); + } + + public AutoCompletionStrategy getAutoCompletionStrategy() { + return new AppendWordsStrategy(); + } } diff --git a/src/main/java/org/jabref/gui/fieldeditors/TextArea.java b/src/main/java/org/jabref/gui/fieldeditors/TextArea.java index 4c86c31a318..600d08e7f37 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/TextArea.java +++ b/src/main/java/org/jabref/gui/fieldeditors/TextArea.java @@ -10,7 +10,6 @@ import javafx.scene.Scene; import org.jabref.gui.GUIGlobals; -import org.jabref.gui.autocompleter.AutoCompleteListener; import org.jabref.gui.util.DefaultTaskExecutor; import org.apache.commons.logging.Log; @@ -29,8 +28,6 @@ public class TextArea implements FieldEditor { private final EditorTextArea textArea; private String fieldName; - private AutoCompleteListener autoCompleteListener; - public TextArea(String fieldName, String content) { this(fieldName, content, ""); } @@ -163,20 +160,6 @@ public void redo() { // Nothing } - @Override - public void setAutoCompleteListener(AutoCompleteListener listener) { - autoCompleteListener = listener; - } - - @Override - public void clearAutoCompleteSuggestion() { - /* - if (autoCompleteListener != null) { - autoCompleteListener.clearCurrentSuggestion(this); - } - */ - } - @Override public void requestFocus() { diff --git a/src/main/java/org/jabref/gui/fieldeditors/TextField.java b/src/main/java/org/jabref/gui/fieldeditors/TextField.java index 646e619b39e..6a240db224d 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/TextField.java +++ b/src/main/java/org/jabref/gui/fieldeditors/TextField.java @@ -15,7 +15,6 @@ import org.jabref.Globals; import org.jabref.gui.GUIGlobals; import org.jabref.gui.actions.Actions; -import org.jabref.gui.autocompleter.AutoCompleteListener; import org.jabref.gui.util.component.JTextFieldWithPlaceholder; import org.apache.commons.logging.Log; @@ -31,8 +30,6 @@ public class TextField extends JTextFieldWithPlaceholder implements FieldEditor private final String fieldName; private UndoManager undo; - private AutoCompleteListener autoCompleteListener; - public TextField(String fieldName, String content, boolean changeColorOnFocus) { this(fieldName, content, changeColorOnFocus, ""); @@ -135,18 +132,6 @@ public void redo() { // Nothing } - @Override - public void setAutoCompleteListener(AutoCompleteListener listener) { - autoCompleteListener = listener; - } - - @Override - public void clearAutoCompleteSuggestion() { - if (autoCompleteListener != null) { - autoCompleteListener.clearCurrentSuggestion(this); - } - } - private void setupPasteListener() { // Bind paste command to KeyBinds.PASTE getInputMap().put(Globals.getKeyPrefs().getKey(org.jabref.gui.keyboard.KeyBinding.PASTE), Actions.PASTE); diff --git a/src/main/java/org/jabref/gui/fieldeditors/TypeEditorViewModel.java b/src/main/java/org/jabref/gui/fieldeditors/TypeEditorViewModel.java index 852c8c97c9b..0ad32035de1 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/TypeEditorViewModel.java +++ b/src/main/java/org/jabref/gui/fieldeditors/TypeEditorViewModel.java @@ -1,5 +1,6 @@ package org.jabref.gui.fieldeditors; +import org.jabref.gui.autocompleter.AutoCompleteSuggestionProvider; import org.jabref.logic.l10n.Localization; import com.google.common.collect.BiMap; @@ -9,7 +10,9 @@ public class TypeEditorViewModel extends MapBasedEditorViewModel { private BiMap itemMap = HashBiMap.create(8); - public TypeEditorViewModel() { + public TypeEditorViewModel(String fieldName, AutoCompleteSuggestionProvider suggestionProvider) { + super(fieldName, suggestionProvider); + itemMap.put("mathesis", Localization.lang("Master's thesis")); itemMap.put("phdthesis", Localization.lang("PhD thesis")); itemMap.put("candthesis", Localization.lang("Candidate thesis")); diff --git a/src/main/java/org/jabref/gui/fieldeditors/UrlEditor.java b/src/main/java/org/jabref/gui/fieldeditors/UrlEditor.java index 9b75b39d9f5..6bffe03deee 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/UrlEditor.java +++ b/src/main/java/org/jabref/gui/fieldeditors/UrlEditor.java @@ -1,26 +1,22 @@ package org.jabref.gui.fieldeditors; -import java.util.Optional; - import javafx.event.ActionEvent; import javafx.fxml.FXML; import javafx.scene.Parent; import javafx.scene.layout.HBox; import org.jabref.gui.DialogService; +import org.jabref.gui.autocompleter.AutoCompleteSuggestionProvider; import org.jabref.gui.util.ControlHelper; import org.jabref.model.entry.BibEntry; public class UrlEditor extends HBox implements FieldEditorFX { - private final String fieldName; @FXML private UrlEditorViewModel viewModel; @FXML private EditorTextArea textArea; - private Optional entry; - public UrlEditor(String fieldName, DialogService dialogService) { - this.fieldName = fieldName; - this.viewModel = new UrlEditorViewModel(dialogService); + public UrlEditor(String fieldName, DialogService dialogService, AutoCompleteSuggestionProvider suggestionProvider) { + this.viewModel = new UrlEditorViewModel(fieldName, suggestionProvider, dialogService); ControlHelper.loadFXMLForControl(this); @@ -33,8 +29,7 @@ public UrlEditorViewModel getViewModel() { @Override public void bindToEntry(BibEntry entry) { - this.entry = Optional.of(entry); - viewModel.bindToEntry(fieldName, entry); + viewModel.bindToEntry(entry); } @Override diff --git a/src/main/java/org/jabref/gui/fieldeditors/UrlEditorViewModel.java b/src/main/java/org/jabref/gui/fieldeditors/UrlEditorViewModel.java index 61394dbe8c0..051a5e34b56 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/UrlEditorViewModel.java +++ b/src/main/java/org/jabref/gui/fieldeditors/UrlEditorViewModel.java @@ -6,6 +6,7 @@ import javafx.beans.property.SimpleBooleanProperty; import org.jabref.gui.DialogService; +import org.jabref.gui.autocompleter.AutoCompleteSuggestionProvider; import org.jabref.gui.desktop.JabRefDesktop; import org.jabref.logic.l10n.Localization; import org.jabref.logic.net.URLUtil; @@ -17,7 +18,8 @@ public class UrlEditorViewModel extends AbstractEditorViewModel { private DialogService dialogService; private BooleanProperty validUrlIsNotPresent = new SimpleBooleanProperty(true); - public UrlEditorViewModel(DialogService dialogService) { + public UrlEditorViewModel(String fieldName, AutoCompleteSuggestionProvider suggestionProvider, DialogService dialogService) { + super(fieldName, suggestionProvider); this.dialogService = dialogService; validUrlIsNotPresent.bind( diff --git a/src/main/java/org/jabref/gui/fieldeditors/YesNoEditorViewModel.java b/src/main/java/org/jabref/gui/fieldeditors/YesNoEditorViewModel.java index 3220231982d..7cad3e7e356 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/YesNoEditorViewModel.java +++ b/src/main/java/org/jabref/gui/fieldeditors/YesNoEditorViewModel.java @@ -1,5 +1,7 @@ package org.jabref.gui.fieldeditors; +import org.jabref.gui.autocompleter.AutoCompleteSuggestionProvider; + import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; @@ -7,7 +9,9 @@ public class YesNoEditorViewModel extends MapBasedEditorViewModel { private BiMap itemMap = HashBiMap.create(2); - public YesNoEditorViewModel() { + public YesNoEditorViewModel(String fieldName, AutoCompleteSuggestionProvider suggestionProvider) { + super(fieldName, suggestionProvider); + itemMap.put("yes", "Yes"); itemMap.put("no", "No"); } diff --git a/src/main/java/org/jabref/gui/fieldeditors/contextmenu/ProtectedTermsMenu.java b/src/main/java/org/jabref/gui/fieldeditors/contextmenu/ProtectedTermsMenu.java index 44faba587bc..272af5ca6bb 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/contextmenu/ProtectedTermsMenu.java +++ b/src/main/java/org/jabref/gui/fieldeditors/contextmenu/ProtectedTermsMenu.java @@ -14,7 +14,7 @@ class ProtectedTermsMenu extends Menu { - private static final ProtectTermsFormatter formatter = new ProtectTermsFormatter(Globals.protectedTermsLoader); + private static final ProtectTermsFormatter FORMATTER = new ProtectTermsFormatter(Globals.protectedTermsLoader); private final Menu externalFiles; private final TextArea opener; @@ -30,7 +30,7 @@ public ProtectedTermsMenu(TextArea opener) { }); MenuItem formatItem = new MenuItem(Localization.lang("Format field")); - formatItem.setOnAction(event -> opener.setText(formatter.format(opener.getText()))); + formatItem.setOnAction(event -> opener.setText(FORMATTER.format(opener.getText()))); externalFiles = new Menu(Localization.lang("Add selected text to list")); updateFiles(); @@ -67,7 +67,5 @@ private void updateFiles() { } }); externalFiles.getItems().add(addToNewFileItem); - } - } diff --git a/src/main/java/org/jabref/gui/groups/GroupTreeViewModel.java b/src/main/java/org/jabref/gui/groups/GroupTreeViewModel.java index c505280e705..69f8620888f 100644 --- a/src/main/java/org/jabref/gui/groups/GroupTreeViewModel.java +++ b/src/main/java/org/jabref/gui/groups/GroupTreeViewModel.java @@ -145,10 +145,15 @@ public void addNewSubgroup(GroupNodeViewModel parent) { //parent.expand(); dialogService.notify(Localization.lang("Added group \"%0\".", group.getName())); + writeGroupChangesToMetaData(); }); }); } + private void writeGroupChangesToMetaData() { + currentDatabase.get().getMetaData().setGroups(rootGroup.get().getGroupNode()); + } + /** * Opens "Edit Group Dialog" and changes the given group to the edited one. */ @@ -191,6 +196,7 @@ public void editGroup(GroupNodeViewModel oldGroup) { //} dialogService.notify(Localization.lang("Modified group \"%0\".", group.getName())); + writeGroupChangesToMetaData(); }); }); }); @@ -206,6 +212,7 @@ public void removeSubgroups(GroupNodeViewModel group) { //panel.getUndoManager().addEdit(undo); group.getGroupNode().removeAllChildren(); dialogService.notify(Localization.lang("Removed all subgroups of group \"%0\".", group.getDisplayName())); + writeGroupChangesToMetaData(); } } @@ -224,6 +231,7 @@ public void removeGroupKeepSubgroups(GroupNodeViewModel group) { groupNode.removeFromParent(); dialogService.notify(Localization.lang("Removed group \"%0\".", group.getDisplayName())); + writeGroupChangesToMetaData(); } } @@ -243,6 +251,7 @@ public void removeGroupAndSubgroups(GroupNodeViewModel group) { group.getGroupNode().removeFromParent(); dialogService.notify(Localization.lang("Removed group \"%0\" and its subgroups.", group.getDisplayName())); + writeGroupChangesToMetaData(); } } diff --git a/src/main/java/org/jabref/gui/help/AboutDialogViewModel.java b/src/main/java/org/jabref/gui/help/AboutDialogViewModel.java index d4c05b7ed8a..7833491aecb 100644 --- a/src/main/java/org/jabref/gui/help/AboutDialogViewModel.java +++ b/src/main/java/org/jabref/gui/help/AboutDialogViewModel.java @@ -22,11 +22,11 @@ public class AboutDialogViewModel extends AbstractViewModel { - private static final String homepageUrl = "https://www.jabref.org"; - private static final String donationUrl = "https://donations.jabref.org"; - private static final String librariesUrl = "https://github.com/JabRef/jabref/blob/master/external-libraries.txt"; - private static final String githubUrl = "https://github.com/JabRef/jabref"; - private static final String licenseUrl = "https://github.com/JabRef/jabref/blob/master/LICENSE.md"; + private static final String HOMEPAGE_URL = "https://www.jabref.org"; + private static final String DONATION_URL = "https://donations.jabref.org"; + private static final String LIBRARIES_URL = "https://github.com/JabRef/jabref/blob/master/external-libraries.txt"; + private static final String GITHUB_URL = "https://github.com/JabRef/jabref"; + private static final String LICENSE_URL = "https://github.com/JabRef/jabref/blob/master/LICENSE.md"; private final String changelogUrl; private final String versionInfo; private final Log logger = LogFactory.getLog(AboutDialogViewModel.class); @@ -115,15 +115,15 @@ public void copyVersionToClipboard() { } public void openJabrefWebsite() { - openWebsite(homepageUrl); + openWebsite(HOMEPAGE_URL); } public void openExternalLibrariesWebsite() { - openWebsite(librariesUrl); + openWebsite(LIBRARIES_URL); } public void openGithub() { - openWebsite(githubUrl); + openWebsite(GITHUB_URL); } public void openChangeLog() { @@ -131,11 +131,11 @@ public void openChangeLog() { } public void openLicense() { - openWebsite(licenseUrl); + openWebsite(LICENSE_URL); } public void openDonation() { - openWebsite(donationUrl); + openWebsite(DONATION_URL); } private void openWebsite(String url) { diff --git a/src/main/java/org/jabref/gui/help/HelpAction.java b/src/main/java/org/jabref/gui/help/HelpAction.java index e6bc16f894f..ecb8fe0f311 100644 --- a/src/main/java/org/jabref/gui/help/HelpAction.java +++ b/src/main/java/org/jabref/gui/help/HelpAction.java @@ -34,7 +34,7 @@ public class HelpAction extends MnemonicAwareAction { /** * New languages of the help have to be added here */ - private static final Set avaiableLangFiles = Stream.of("en", "de", "fr", "in", "ja") + private static final Set AVAIABLE_LANG_FILES = Stream.of("en", "de", "fr", "in", "ja") .collect(Collectors.toCollection(HashSet::new)); private HelpFile helpPage; @@ -99,7 +99,7 @@ private void openHelpPage() { String lang = Globals.prefs.get(JabRefPreferences.LANGUAGE); StringBuilder sb = new StringBuilder("https://help.jabref.org/"); - if (avaiableLangFiles.contains(lang)) { + if (AVAIABLE_LANG_FILES.contains(lang)) { sb.append(lang); sb.append("/"); } else { diff --git a/src/main/java/org/jabref/gui/keyboard/KeyBinding.java b/src/main/java/org/jabref/gui/keyboard/KeyBinding.java index 61e663b9700..8181de424a7 100644 --- a/src/main/java/org/jabref/gui/keyboard/KeyBinding.java +++ b/src/main/java/org/jabref/gui/keyboard/KeyBinding.java @@ -14,7 +14,7 @@ public enum KeyBinding { CLEAR_SEARCH("Clear search", Localization.lang("Clear search"), "ESCAPE", KeyBindingCategory.SEARCH), CLOSE_DATABASE("Close library", Localization.lang("Close library"), "ctrl+W", KeyBindingCategory.FILE), CLOSE_DIALOG("Close dialog", Localization.lang("Close dialog"), "ESCAPE", KeyBindingCategory.FILE), - CLOSE_ENTRY_EDITOR("Close entry editor", Localization.lang("Close entry editor"), "ESCAPE", KeyBindingCategory.VIEW), + CLOSE_ENTRY_EDITOR("Close entry editor", Localization.lang("Close entry editor"), "Esc", KeyBindingCategory.VIEW), COPY("Copy", Localization.lang("Copy"), "ctrl+C", KeyBindingCategory.EDIT), COPY_TITLE("Copy title", Localization.lang("Copy title"), "ctrl+shift+alt+T", KeyBindingCategory.EDIT), COPY_CITE_BIBTEX_KEY("Copy \\cite{BibTeX key}", Localization.lang("Copy \\cite{BibTeX key}"), "ctrl+K", KeyBindingCategory.EDIT), @@ -93,27 +93,35 @@ public enum KeyBinding { WEB_SEARCH("Web search", Localization.lang("Web search"), "alt+4", KeyBindingCategory.SEARCH), WRITE_XMP("Write XMP", Localization.lang("Write XMP"), "F6", KeyBindingCategory.TOOLS); - private final String key; + private final String constant; private final String localization; private final String defaultBinding; private final KeyBindingCategory category; - KeyBinding(String key, String localization, String defaultBinding, KeyBindingCategory category) { - this.key = key; + KeyBinding(String constantName, String localization, String defaultKeyBinding, KeyBindingCategory category) { + this.constant = constantName; this.localization = localization; - this.defaultBinding = defaultBinding; + this.defaultBinding = defaultKeyBinding; this.category = category; } - public String getKey() { - return key; + /** + * This method returns the enum constant value + * @return + */ + public String getConstant() { + return constant; } public String getLocalization() { return localization; } - public String getDefaultBinding() { + /** + * This method returns the default key binding, the key(s) which are assigned + * @return The default key binding + */ + public String getDefaultKeyBinding() { return defaultBinding; } diff --git a/src/main/java/org/jabref/gui/keyboard/KeyBindingRepository.java b/src/main/java/org/jabref/gui/keyboard/KeyBindingRepository.java index 8a2053a4b2e..3f7f341b1b5 100644 --- a/src/main/java/org/jabref/gui/keyboard/KeyBindingRepository.java +++ b/src/main/java/org/jabref/gui/keyboard/KeyBindingRepository.java @@ -44,7 +44,7 @@ public KeyBindingRepository(List bindNames, List bindings) { if ((bindNames.isEmpty()) || (bindings.isEmpty()) || (bindNames.size() != bindings.size())) { // Use default key bindings for (KeyBinding keyBinding : KeyBinding.values()) { - put(keyBinding, keyBinding.getDefaultBinding()); + put(keyBinding, keyBinding.getDefaultKeyBinding()); } } else { for (int i = 0; i < bindNames.size(); i++) { @@ -80,7 +80,7 @@ public String get(String key) { if (result.isPresent()) { return result.get(); } else if (keyBinding.isPresent()) { - return keyBinding.get().getDefaultBinding(); + return keyBinding.get().getDefaultKeyBinding(); } else { return "Not associated"; } @@ -102,15 +102,15 @@ public void put(String key, String value) { } private Optional getKeyBinding(String key) { - return Arrays.stream(KeyBinding.values()).filter(b -> b.getKey().equals(key)).findFirst(); + return Arrays.stream(KeyBinding.values()).filter(b -> b.getConstant().equals(key)).findFirst(); } public void resetToDefault(String key) { - getKeyBinding(key).ifPresent(b -> bindings.put(b, b.getDefaultBinding())); + getKeyBinding(key).ifPresent(b -> bindings.put(b, b.getDefaultKeyBinding())); } public void resetToDefault() { - bindings.forEach((b, s) -> bindings.put(b, b.getDefaultBinding())); + bindings.forEach((b, s) -> bindings.put(b, b.getDefaultKeyBinding())); } public int size() { @@ -126,12 +126,25 @@ public Optional mapToKeyBinding(KeyEvent keyEvent) { return Optional.empty(); } + public Optional mapToKeyBinding(java.awt.event.KeyEvent keyEvent) { + Optional keyCode = Arrays.stream(KeyCode.values()).filter(k -> k.getName().equals(keyEvent.getKeyText(keyEvent.getKeyCode()))).findFirst(); + if (keyCode.isPresent()) { + KeyEvent event = new KeyEvent(keyEvent.getSource(), null, KeyEvent.KEY_PRESSED, "", "", keyCode.get(), keyEvent.isShiftDown(), keyEvent.isControlDown(), keyEvent.isAltDown(), keyEvent.isMetaDown()); + return mapToKeyBinding(event); + + } + + return Optional.empty(); + + } + /** * Returns the KeyStroke for this binding, as defined by the defaults, or in the Preferences. */ public KeyStroke getKey(KeyBinding bindName) { - String s = get(bindName.getKey()); + String s = get(bindName.getConstant()); + s = s.replace("+", " "); //swing needs the keys without pluses but whitespace between the modifiers if (OS.OS_X) { return getKeyForMac(KeyStroke.getKeyStroke(s)); @@ -141,7 +154,11 @@ public KeyStroke getKey(KeyBinding bindName) { } private KeyCombination getKeyCombination(KeyBinding bindName) { - String binding = get(bindName.getKey()); + String binding = get(bindName.getConstant()); + if (OS.OS_X) { + binding = binding.replace("ctrl", "meta"); + } + return KeyCombination.valueOf(binding); } @@ -191,7 +208,7 @@ private KeyStroke getKeyForMac(KeyStroke ks) { } public List getBindNames() { - return bindings.keySet().stream().map(KeyBinding::getKey).collect(Collectors.toList()); + return bindings.keySet().stream().map(KeyBinding::getConstant).collect(Collectors.toList()); } public List getBindings() { diff --git a/src/main/java/org/jabref/gui/keyboard/KeyBindingViewModel.java b/src/main/java/org/jabref/gui/keyboard/KeyBindingViewModel.java index b43ccd1cfbc..aa7e86b26bd 100644 --- a/src/main/java/org/jabref/gui/keyboard/KeyBindingViewModel.java +++ b/src/main/java/org/jabref/gui/keyboard/KeyBindingViewModel.java @@ -128,7 +128,7 @@ public boolean setNewBinding(KeyEvent evt) { */ public void resetToDefault() { if (!isCategory()) { - String key = getKeyBinding().getKey(); + String key = getKeyBinding().getConstant(); keyBindingRepository.resetToDefault(key); setBinding(keyBindingRepository.get(key)); } diff --git a/src/main/java/org/jabref/gui/preftabs/AppearancePrefsTab.java b/src/main/java/org/jabref/gui/preftabs/AppearancePrefsTab.java index 60a28465179..1031b1b5450 100644 --- a/src/main/java/org/jabref/gui/preftabs/AppearancePrefsTab.java +++ b/src/main/java/org/jabref/gui/preftabs/AppearancePrefsTab.java @@ -54,7 +54,7 @@ class AppearancePrefsTab extends JPanel implements PrefsTab { private final JCheckBox customLAF; static class LookAndFeel { - private static final List looks = Arrays.asList( + private static final List LOOKS = Arrays.asList( UIManager.getSystemLookAndFeelClassName(), UIManager.getCrossPlatformLookAndFeelClassName(), "com.jgoodies.looks.plastic.Plastic3DLookAndFeel", @@ -63,7 +63,7 @@ static class LookAndFeel { public static List getAvailableLookAndFeels() { List lookAndFeels = new ArrayList<>(); - for (String l : looks) { + for (String l : LOOKS) { try { // Try to find L&F Class.forName(l); @@ -158,16 +158,16 @@ public AppearancePrefsTab(JabRefPreferences prefs) { FormBuilder generalBuilder = FormBuilder.create(); JPanel generalPanel = generalBuilder.columns("left:pref, left:pref, 3dlu, pref, 7dlu, right:pref, 3dlu, pref") .rows("pref, 3dlu, pref, 3dlu, pref") - .columnGroup(2,6) - .columnGroup(4,8) - .add(overrideFonts).xyw(1,1,5) - .add(new JLabel(" ")).xy(1,3) - .add(new JLabel(Localization.lang("Menu and label font size") + ":")).xy(2,3) - .add(fontSize).xy(4,3) + .columnGroup(2, 6) + .columnGroup(4, 8) + .add(overrideFonts).xyw(1, 1, 5) + .add(new JLabel(" ")).xy(1, 3) + .add(new JLabel(Localization.lang("Menu and label font size") + ":")).xy(2, 3) + .add(fontSize).xy(4, 3) .add(new JLabel(Localization.lang("Size of large icons") + ":")).xy(2, 5) - .add(largeIconsTextField).xy(4,5) + .add(largeIconsTextField).xy(4, 5) .add(new JLabel(Localization.lang("Size of small icons") + ":")).xy(6, 5) - .add(smallIconsTextField).xy(8,5) + .add(smallIconsTextField).xy(8, 5) .build(); builder.append(generalPanel); diff --git a/src/main/java/org/jabref/gui/preftabs/EntryEditorPrefsTab.java b/src/main/java/org/jabref/gui/preftabs/EntryEditorPrefsTab.java index 796d45d35a9..28bc7ebce77 100644 --- a/src/main/java/org/jabref/gui/preftabs/EntryEditorPrefsTab.java +++ b/src/main/java/org/jabref/gui/preftabs/EntryEditorPrefsTab.java @@ -9,13 +9,11 @@ import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JRadioButton; -import javax.swing.JSpinner; import javax.swing.JTextField; -import javax.swing.SpinnerNumberModel; +import org.jabref.gui.autocompleter.AutoCompleteFirstNameMode; +import org.jabref.gui.autocompleter.AutoCompletePreferences; import org.jabref.gui.keyboard.EmacsKeyBindings; -import org.jabref.logic.autocompleter.AutoCompleteFirstNameMode; -import org.jabref.logic.autocompleter.AutoCompletePreferences; import org.jabref.logic.l10n.Localization; import org.jabref.preferences.JabRefPreferences; @@ -23,6 +21,9 @@ import com.jgoodies.forms.layout.CellConstraints; import com.jgoodies.forms.layout.FormLayout; +import static org.jabref.gui.autocompleter.AutoCompleteFirstNameMode.ONLY_ABBREVIATED; +import static org.jabref.gui.autocompleter.AutoCompleteFirstNameMode.ONLY_FULL; + class EntryEditorPrefsTab extends JPanel implements PrefsTab { private final JCheckBox autoOpenForm; @@ -38,7 +39,6 @@ class EntryEditorPrefsTab extends JPanel implements PrefsTab { private final JRadioButton firstNameModeFull; private final JRadioButton firstNameModeAbbr; private final JRadioButton firstNameModeBoth; - private final JSpinner shortestToComplete; private final JTextField autoCompFields; private final JabRefPreferences prefs; @@ -47,7 +47,7 @@ class EntryEditorPrefsTab extends JPanel implements PrefsTab { public EntryEditorPrefsTab(JabRefPreferences prefs) { this.prefs = prefs; - autoCompletePreferences = new AutoCompletePreferences(prefs); + autoCompletePreferences = prefs.getAutoCompletePreferences(); setLayout(new BorderLayout()); autoOpenForm = new JCheckBox(Localization.lang("Open editor when a new entry is created")); @@ -58,9 +58,6 @@ public EntryEditorPrefsTab(JabRefPreferences prefs) { autoComplete = new JCheckBox(Localization.lang("Enable word/name autocompletion")); recommendations = new JCheckBox(Localization.lang("Show 'Related_Articles' tab")); - shortestToComplete = new JSpinner( - new SpinnerNumberModel(autoCompletePreferences.getShortestLengthToComplete(), 1, 5, 1)); - // allowed name formats autoCompFF = new JRadioButton(Localization.lang("Autocomplete names in 'Firstname Lastname' format only")); autoCompLF = new JRadioButton(Localization.lang("Autocomplete names in 'Lastname, Firstname' format only")); @@ -122,9 +119,6 @@ public EntryEditorPrefsTab(JabRefPreferences prefs) { builder3.append(label); builder3.append(autoCompFields); - JLabel label2 = new JLabel(Localization.lang("Autocomplete after following number of characters") + ":"); - builder3.append(label2); - builder3.append(shortestToComplete); builder.add(builder3.getPanel(), cc.xyw(2, 19, 3)); builder.addSeparator(Localization.lang("Name format used for autocompletion"), cc.xyw(2, 21, 4)); @@ -150,7 +144,6 @@ private void setAutoCompleteElementsEnabled(boolean enabled) { firstNameModeAbbr.setEnabled(enabled); firstNameModeFull.setEnabled(enabled); firstNameModeBoth.setEnabled(enabled); - shortestToComplete.setEnabled(enabled); } @Override @@ -161,9 +154,8 @@ public void setValues() { emacsRebindCtrlA.setSelected(prefs.getBoolean(JabRefPreferences.EDITOR_EMACS_KEYBINDINGS_REBIND_CA)); emacsRebindCtrlF.setSelected(prefs.getBoolean(JabRefPreferences.EDITOR_EMACS_KEYBINDINGS_REBIND_CF)); recommendations.setSelected(prefs.getBoolean(JabRefPreferences.SHOW_RECOMMENDATIONS)); - autoComplete.setSelected(prefs.getBoolean(JabRefPreferences.AUTO_COMPLETE)); + autoComplete.setSelected(autoCompletePreferences.shouldAutoComplete()); autoCompFields.setText(autoCompletePreferences.getCompleteNamesAsString()); - shortestToComplete.setValue(autoCompletePreferences.getShortestLengthToComplete()); if (autoCompletePreferences.getOnlyCompleteFirstLast()) { autoCompFF.setSelected(true); @@ -173,7 +165,7 @@ public void setValues() { autoCompBoth.setSelected(true); } - switch (autoCompletePreferences.getFirstnameMode()) { + switch (autoCompletePreferences.getFirstNameMode()) { case ONLY_ABBREVIATED: firstNameModeAbbr.setSelected(true); break; @@ -218,8 +210,7 @@ public void storeSettings() { EmacsKeyBindings.load(); } } - autoCompletePreferences.setShortestLengthToComplete((Integer) shortestToComplete.getValue()); - prefs.putBoolean(JabRefPreferences.AUTO_COMPLETE, autoComplete.isSelected()); + autoCompletePreferences.setShouldAutoComplete(autoComplete.isSelected()); autoCompletePreferences.setCompleteNames(autoCompFields.getText()); if (autoCompBoth.isSelected()) { autoCompletePreferences.setOnlyCompleteFirstLast(false); @@ -234,12 +225,13 @@ else if (autoCompFF.isSelected()) { autoCompletePreferences.setOnlyCompleteLastFirst(true); } if (firstNameModeAbbr.isSelected()) { - autoCompletePreferences.setFirstnameMode(AutoCompleteFirstNameMode.ONLY_ABBREVIATED); + autoCompletePreferences.setFirstNameMode(ONLY_ABBREVIATED); } else if (firstNameModeFull.isSelected()) { - autoCompletePreferences.setFirstnameMode(AutoCompleteFirstNameMode.ONLY_FULL); + autoCompletePreferences.setFirstNameMode(ONLY_FULL); } else { - autoCompletePreferences.setFirstnameMode(AutoCompleteFirstNameMode.BOTH); + autoCompletePreferences.setFirstNameMode(AutoCompleteFirstNameMode.BOTH); } + prefs.storeAutoCompletePreferences(autoCompletePreferences); } @Override diff --git a/src/main/java/org/jabref/gui/preftabs/FontSelectorDialog.java b/src/main/java/org/jabref/gui/preftabs/FontSelectorDialog.java index f743e93c9f1..5d9b65a63a9 100644 --- a/src/main/java/org/jabref/gui/preftabs/FontSelectorDialog.java +++ b/src/main/java/org/jabref/gui/preftabs/FontSelectorDialog.java @@ -124,17 +124,17 @@ public class FontSelectorDialog extends JabRefDialog { private static final String ITALIC = "italic"; - private static final String[] styles = {PLAIN, BOLD, ITALIC, BOLD_ITALIC}; + private static final String[] STYLES = {PLAIN, BOLD, ITALIC, BOLD_ITALIC}; - private static final String[] sizes = {"9", "10", "12", "14", "16", "18", "24"}; + private static final String[] SIZES = {"9", "10", "12", "14", "16", "18", "24"}; // private members private boolean isOK; private final JTextField familyField = new JTextField(); private final JList familyList; private final JTextField sizeField = new JTextField(); - private final JList sizeList = new JList<>(sizes); + private final JList sizeList = new JList<>(SIZES); private final JTextField styleField = new JTextField(); - private final JList styleList = new JList<>(styles); + private final JList styleList = new JList<>(STYLES); private final JLabel preview; diff --git a/src/main/java/org/jabref/gui/search/GlobalSearchBar.java b/src/main/java/org/jabref/gui/search/GlobalSearchBar.java index 53ba13620b0..f90cdb743d0 100644 --- a/src/main/java/org/jabref/gui/search/GlobalSearchBar.java +++ b/src/main/java/org/jabref/gui/search/GlobalSearchBar.java @@ -19,27 +19,38 @@ import javax.swing.JToolBar; import javax.swing.SwingUtilities; +import javafx.css.PseudoClass; +import javafx.embed.swing.JFXPanel; +import javafx.scene.Scene; +import javafx.scene.control.TextField; +import javafx.scene.control.Tooltip; + import org.jabref.Globals; import org.jabref.gui.BasePanel; import org.jabref.gui.GUIGlobals; import org.jabref.gui.IconTheme; import org.jabref.gui.JabRefFrame; import org.jabref.gui.OSXCompatibleToolbar; -import org.jabref.gui.autocompleter.AutoCompleteSupport; +import org.jabref.gui.autocompleter.AppendPersonNamesStrategy; +import org.jabref.gui.autocompleter.AutoCompleteFirstNameMode; +import org.jabref.gui.autocompleter.AutoCompleteSuggestionProvider; +import org.jabref.gui.autocompleter.AutoCompletionTextInputBinding; +import org.jabref.gui.autocompleter.PersonNameStringConverter; import org.jabref.gui.help.HelpAction; import org.jabref.gui.keyboard.KeyBinding; import org.jabref.gui.maintable.MainTable; import org.jabref.gui.maintable.MainTableDataModel; -import org.jabref.gui.util.component.JTextFieldWithPlaceholder; -import org.jabref.logic.autocompleter.AutoCompleter; +import org.jabref.gui.util.DefaultTaskExecutor; import org.jabref.logic.help.HelpFile; import org.jabref.logic.l10n.Localization; import org.jabref.logic.search.SearchQuery; import org.jabref.logic.search.SearchQueryHighlightObservable; -import org.jabref.logic.util.OS; +import org.jabref.model.entry.Author; import org.jabref.model.entry.BibEntry; import org.jabref.preferences.SearchPreferences; +import org.fxmisc.easybind.EasyBind; + public class GlobalSearchBar extends JPanel { private static final Color NEUTRAL_COLOR = Color.WHITE; @@ -47,20 +58,19 @@ public class GlobalSearchBar extends JPanel { private static final Color RESULTS_FOUND_COLOR = new Color(217, 232, 202); private static final Color ADVANCED_SEARCH_COLOR = new Color(102, 255, 255); - private final JabRefFrame frame; + private static final PseudoClass CLASS_NO_RESULTS = PseudoClass.getPseudoClass("emptyResult"); + private static final PseudoClass CLASS_RESULTS_FOUND = PseudoClass.getPseudoClass("emptyResult"); - private final JLabel searchIcon = new JLabel(IconTheme.JabRefIcon.SEARCH.getSmallIcon()); - private final JTextFieldWithPlaceholder searchField = new JTextFieldWithPlaceholder(Localization.lang("Search") + "..."); - private JButton openCurrentResultsInDialog = new JButton(IconTheme.JabRefIcon.OPEN_IN_NEW_WINDOW.getSmallIcon()); + private final JabRefFrame frame; + + private final TextField searchField = SearchTextField.create(); private final JToggleButton caseSensitive; private final JToggleButton regularExp; private final JButton searchModeButton = new JButton(); private final JLabel currentResults = new JLabel(""); - - private AutoCompleteSupport autoCompleteSupport = new AutoCompleteSupport<>(searchField); private final SearchQueryHighlightObservable searchQueryHighlightObservable = new SearchQueryHighlightObservable(); - + private JButton openCurrentResultsInDialog = new JButton(IconTheme.JabRefIcon.OPEN_IN_NEW_WINDOW.getSmallIcon()); private SearchWorker searchWorker; private GlobalSearchWorker globalSearchWorker; @@ -83,7 +93,6 @@ public GlobalSearchBar(JabRefFrame frame) { // fits the standard "found x entries"-message thus hinders the searchbar to jump around while searching if the frame width is too small currentResults.setPreferredSize(new Dimension(150, 5)); currentResults.setFont(currentResults.getFont().deriveFont(Font.BOLD)); - searchField.setColumns(30); JToggleButton globalSearch = new JToggleButton(IconTheme.JabRefIcon.GLOBAL_SEARCH.getSmallIcon(), searchPreferences.isGlobalSearch()); globalSearch.setToolTipText(Localization.lang("Search in all open libraries")); @@ -143,14 +152,9 @@ public void actionPerformed(ActionEvent e) { updateSearchModeButtonText(); searchModeButton.addActionListener(event -> toggleSearchModeAndSearch()); - JButton clearSearchButton = new JButton(IconTheme.JabRefIcon.CLOSE.getSmallIcon()); - clearSearchButton.setToolTipText(Localization.lang("Clear")); - clearSearchButton.addActionListener(event -> endSearch()); - - searchField.addFocusListener(Globals.getFocusListener()); - searchField.addActionListener(event -> performSearch()); - JTextFieldChangeListenerUtil.addChangeListener(searchField, e -> performSearch()); + EasyBind.subscribe(searchField.textProperty(), searchText -> performSearch()); + /* String endSearch = "endSearch"; searchField.getInputMap().put(Globals.getKeyPrefs().getKey(KeyBinding.CLEAR_SEARCH), endSearch); searchField.getActionMap().put(endSearch, new AbstractAction() { @@ -163,9 +167,9 @@ public void actionPerformed(ActionEvent event) { } } }); + */ - autoCompleteSupport.install(); - + /* String acceptSearch = "acceptSearch"; searchField.getInputMap().put(Globals.getKeyPrefs().getKey(KeyBinding.ACCEPT), acceptSearch); searchField.getActionMap().put(acceptSearch, new AbstractAction() { @@ -177,18 +181,17 @@ public void actionPerformed(ActionEvent e) { currentBasePanel.getMainTable().requestFocus(); } }); + */ + + JFXPanel container = new JFXPanel(); + DefaultTaskExecutor.runInJavaFXThread(() -> { + container.setScene(new Scene(searchField)); + }); setLayout(new FlowLayout(FlowLayout.RIGHT)); JToolBar toolBar = new OSXCompatibleToolbar(); toolBar.setFloatable(false); - if (OS.OS_X) { - searchField.putClientProperty("JTextField.variant", "search"); - toolBar.add(searchField); - } else { - toolBar.add(searchIcon); - toolBar.add(searchField); - toolBar.add(clearSearchButton); - } + toolBar.add(container); toolBar.addSeparator(); toolBar.add(openCurrentResultsInDialog); toolBar.addSeparator(); @@ -288,7 +291,7 @@ public void endSearch() { * Focuses the search field if it is not focused. */ public void focus() { - if (!searchField.hasFocus()) { + if (!searchField.isFocused()) { searchField.requestFocus(); } searchField.selectAll(); @@ -297,8 +300,6 @@ public void focus() { private void clearSearch(BasePanel currentBasePanel) { currentResults.setText(""); searchField.setText(""); - searchField.setBackground(NEUTRAL_COLOR); - searchIcon.setIcon(IconTheme.JabRefIcon.SEARCH.getSmallIcon()); searchQueryHighlightObservable.reset(); openCurrentResultsInDialog.setEnabled(false); @@ -341,22 +342,23 @@ public void performSearch() { } private void informUserAboutInvalidSearchQuery() { - searchField.setBackground(NO_RESULTS_COLOR); + searchField.pseudoClassStateChanged(CLASS_NO_RESULTS, true); searchQueryHighlightObservable.reset(); BasePanel currentBasePanel = frame.getCurrentBasePanel(); currentBasePanel.getMainTable().getTableModel().updateSearchState(MainTableDataModel.DisplayOption.DISABLED); - searchIcon.setIcon(IconTheme.JabRefIcon.SEARCH.getSmallIcon().createWithNewColor(NO_RESULTS_COLOR)); String illegalSearch = Localization.lang("Search failed: illegal search expression"); - searchIcon.setToolTipText(illegalSearch); currentResults.setText(illegalSearch); openCurrentResultsInDialog.setEnabled(false); } - public void setAutoCompleter(AutoCompleter searchCompleter) { - this.autoCompleteSupport.setAutoCompleter(searchCompleter); + public void setAutoCompleter(AutoCompleteSuggestionProvider searchCompleter) { + AutoCompletionTextInputBinding.autoComplete(searchField, + searchCompleter, + new PersonNameStringConverter(true, true, AutoCompleteFirstNameMode.BOTH), + new AppendPersonNamesStrategy()); } public SearchQueryHighlightObservable getSearchQueryHighlightObservable() { @@ -378,21 +380,12 @@ private SearchQuery getSearchQuery() { public void updateResults(int matched, String description, boolean grammarBasedSearch) { if (matched == 0) { currentResults.setText(Localization.lang("No results found.")); - this.searchField.setBackground(NO_RESULTS_COLOR); + searchField.pseudoClassStateChanged(CLASS_NO_RESULTS, true); } else { currentResults.setText(Localization.lang("Found %0 results.", String.valueOf(matched))); - this.searchField.setBackground(RESULTS_FOUND_COLOR); + searchField.pseudoClassStateChanged(CLASS_RESULTS_FOUND, true); } - this.searchField.setToolTipText("" + description + ""); - - if (grammarBasedSearch) { - searchIcon.setIcon(IconTheme.JabRefIcon.SEARCH.getSmallIcon().createWithNewColor(ADVANCED_SEARCH_COLOR)); - searchIcon.setToolTipText(Localization.lang("Advanced search active.")); - } else { - searchIcon.setIcon(IconTheme.JabRefIcon.SEARCH.getSmallIcon()); - searchIcon.setToolTipText(Localization.lang("Normal search active.")); - } - + searchField.setTooltip(new Tooltip(description)); openCurrentResultsInDialog.setEnabled(true); } @@ -400,19 +393,17 @@ public void setSearchResultFrame(SearchResultFrame searchResultFrame) { this.searchResultFrame = searchResultFrame; } - public void setSearchTerm(String searchTerm, boolean dontSelectSearchBar) { + public void setSearchTerm(String searchTerm) { if (searchTerm.equals(searchField.getText())) { return; } - setDontSelectSearchBar(dontSelectSearchBar); + setDontSelectSearchBar(); searchField.setText(searchTerm); - // to hinder the autocomplete window to popup - autoCompleteSupport.setVisible(false); } - public void setDontSelectSearchBar(boolean dontSelectSearchBar) { - this.dontSelectSearchBar = dontSelectSearchBar; + public void setDontSelectSearchBar() { + this.dontSelectSearchBar = true; } private void updateOpenCurrentResultsTooltip(boolean globalSearchEnabled) { diff --git a/src/main/java/org/jabref/gui/search/JTextFieldChangeListenerUtil.java b/src/main/java/org/jabref/gui/search/JTextFieldChangeListenerUtil.java deleted file mode 100644 index 643e2777d44..00000000000 --- a/src/main/java/org/jabref/gui/search/JTextFieldChangeListenerUtil.java +++ /dev/null @@ -1,87 +0,0 @@ -package org.jabref.gui.search; - -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.util.Objects; - -import javax.swing.JTextArea; -import javax.swing.JTextField; -import javax.swing.SwingUtilities; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; -import javax.swing.event.DocumentEvent; -import javax.swing.event.DocumentListener; -import javax.swing.text.Document; -import javax.swing.text.JTextComponent; - -/** - * Taken from http://stackoverflow.com/questions/3953208/value-change-listener-to-jtextfield - */ -public class JTextFieldChangeListenerUtil { - - private JTextFieldChangeListenerUtil() { - } - - /** - * Installs a listener to receive notification when the text of any - * {@code JTextComponent} is changed. Internally, it installs a - * {@link DocumentListener} on the text component's {@link Document}, - * and a {@link PropertyChangeListener} on the text component to detect - * if the {@code Document} itself is replaced. - * - * Taken from - * - * @param text any text component, such as a {@link JTextField} - * or {@link JTextArea} - * @param changeListener a listener to receive {@link ChangeEvent}s - * when the text is changed; the source object for the events - * will be the text component - * @throws NullPointerException if either parameter is null - */ - public static void addChangeListener(JTextComponent text, ChangeListener changeListener) { - Objects.requireNonNull(text); - Objects.requireNonNull(changeListener); - DocumentListener dl = new DocumentListener() { - - private int lastChange; - private int lastNotifiedChange; - - @Override - public void insertUpdate(DocumentEvent e) { - changedUpdate(e); - } - - @Override - public void removeUpdate(DocumentEvent e) { - changedUpdate(e); - } - - @Override - public void changedUpdate(DocumentEvent e) { - lastChange++; - SwingUtilities.invokeLater(() -> { - if (lastNotifiedChange != lastChange) { - lastNotifiedChange = lastChange; - changeListener.stateChanged(new ChangeEvent(text)); - } - }); - } - }; - text.addPropertyChangeListener("document", (PropertyChangeEvent e) -> { - Document d1 = (Document)e.getOldValue(); - Document d2 = (Document)e.getNewValue(); - if (d1 != null) { - d1.removeDocumentListener(dl); - } - if (d2 != null) { - d2.addDocumentListener(dl); - } - dl.changedUpdate(null); - }); - Document d = text.getDocument(); - if (d != null) { - d.addDocumentListener(dl); - } - } - -} diff --git a/src/main/java/org/jabref/gui/search/SearchTextField.java b/src/main/java/org/jabref/gui/search/SearchTextField.java new file mode 100644 index 00000000000..e018ac2efd9 --- /dev/null +++ b/src/main/java/org/jabref/gui/search/SearchTextField.java @@ -0,0 +1,19 @@ +package org.jabref.gui.search; + +import javafx.scene.control.TextField; + +import org.jabref.gui.IconTheme; +import org.jabref.logic.l10n.Localization; + +import org.controlsfx.control.textfield.CustomTextField; +import org.controlsfx.control.textfield.TextFields; + +public class SearchTextField { + + public static TextField create() { + CustomTextField textField = (CustomTextField) TextFields.createClearableTextField(); + textField.setPromptText(Localization.lang("Search") + "..."); + textField.setLeft(IconTheme.JabRefIcon.SEARCH.getGraphicNode()); + return textField; + } +} diff --git a/src/main/java/org/jabref/logic/autocompleter/AbstractAutoCompleter.java b/src/main/java/org/jabref/logic/autocompleter/AbstractAutoCompleter.java deleted file mode 100644 index 4f4f26ba910..00000000000 --- a/src/main/java/org/jabref/logic/autocompleter/AbstractAutoCompleter.java +++ /dev/null @@ -1,139 +0,0 @@ -package org.jabref.logic.autocompleter; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.SortedSet; -import java.util.TreeSet; - -import org.jabref.logic.layout.format.LatexToUnicodeFormatter; - -/** - * Delivers possible completions for a given string. - * - * @author kahlert, cordes, olly98 - * @see AutoCompleterFactory - */ -public abstract class AbstractAutoCompleter implements AutoCompleter { - - private static final int SHORTEST_WORD_TO_ADD = 4; - private final AutoCompletePreferences preferences; - - /** - * Stores the strings as is. - */ - private final TreeSet indexCaseSensitive = new TreeSet<>(); - - /** - * Stores strings in lowercase. - */ - private final TreeSet indexCaseInsensitive = new TreeSet<>(); - - /** - * Stores for a lowercase string the possible expanded strings. - */ - private final Map> possibleStringsForSearchString = new HashMap<>(); - - - public AbstractAutoCompleter(AutoCompletePreferences preferences) { - this.preferences = Objects.requireNonNull(preferences); - } - - /** - * {@inheritDoc} - * The completion is case sensitive if the string contains upper case letters. - * Otherwise the completion is case insensitive. - */ - @Override - public List complete(String toComplete) { - if (toComplete == null) { - return new ArrayList<>(); - } - if (isTooShortToComplete(toComplete)) { - return new ArrayList<>(); - } - String lowerCase = toComplete.toLowerCase(Locale.ROOT); - - if (lowerCase.equals(toComplete)) { - // user typed in lower case word -> we do an case-insensitive search - String ender = AbstractAutoCompleter.incrementLastCharacter(lowerCase); - SortedSet subset = indexCaseInsensitive.subSet(lowerCase, ender); - - // As subset only contains lower case strings, - // we have to to determine possible strings for each hit - List result = new ArrayList<>(); - for (String s : subset) { - result.addAll(possibleStringsForSearchString.get(s)); - } - return result; - } else { - // user typed in a mix of upper case and lower case, - // we assume user wants to have exact search - String ender = AbstractAutoCompleter.incrementLastCharacter(toComplete); - SortedSet subset = indexCaseSensitive.subSet(toComplete, ender); - return new ArrayList<>(subset); - } - } - - /** - * Increments the last character of a string. - * - * Example: incrementLastCharacter("abc") returns "abd". - */ - private static String incrementLastCharacter(String toIncrement) { - if (toIncrement.isEmpty()) { - return ""; - } - - char lastChar = toIncrement.charAt(toIncrement.length() - 1); - return toIncrement.substring(0, toIncrement.length() - 1) + Character.toString((char) (lastChar + 1)); - } - - /** - * Returns whether the string is to short to be completed. - */ - private boolean isTooShortToComplete(String toCheck) { - return toCheck.length() < preferences.getShortestLengthToComplete(); - } - - @Override - public void addItemToIndex(String word) { - if (word.length() < getLengthOfShortestWordToAdd()) { - return; - } - - word = new LatexToUnicodeFormatter().format(word); - - indexCaseSensitive.add(word); - - // insensitive treatment - // first, add the lower cased word to search index - // second, add a mapping from the lower cased word to the real word - String lowerCase = word.toLowerCase(Locale.ROOT); - indexCaseInsensitive.add(lowerCase); - Set set = possibleStringsForSearchString.get(lowerCase); - if (set == null) { - set = new TreeSet<>(); - } - set.add(word); - possibleStringsForSearchString.put(lowerCase, set); - } - - @Override - public String getPrefix() { - return ""; - } - - @Override - public String getAutoCompleteText(String item) { - return item; - } - - protected int getLengthOfShortestWordToAdd() { - return AbstractAutoCompleter.SHORTEST_WORD_TO_ADD; - } -} diff --git a/src/main/java/org/jabref/logic/autocompleter/AutoCompleteFirstNameMode.java b/src/main/java/org/jabref/logic/autocompleter/AutoCompleteFirstNameMode.java deleted file mode 100644 index b9e4e3e8c18..00000000000 --- a/src/main/java/org/jabref/logic/autocompleter/AutoCompleteFirstNameMode.java +++ /dev/null @@ -1,7 +0,0 @@ -package org.jabref.logic.autocompleter; - -public enum AutoCompleteFirstNameMode { - ONLY_FULL, - ONLY_ABBREVIATED, - BOTH -} diff --git a/src/main/java/org/jabref/logic/autocompleter/AutoCompletePreferences.java b/src/main/java/org/jabref/logic/autocompleter/AutoCompletePreferences.java deleted file mode 100644 index 36c95725f5f..00000000000 --- a/src/main/java/org/jabref/logic/autocompleter/AutoCompletePreferences.java +++ /dev/null @@ -1,90 +0,0 @@ -package org.jabref.logic.autocompleter; - -import java.util.List; -import java.util.Map; -import java.util.Objects; - -import org.jabref.logic.journals.JournalAbbreviationPreferences; -import org.jabref.preferences.JabRefPreferences; - -public class AutoCompletePreferences { - - private static final String AUTOCOMPLETER_SHORTEST_TO_COMPLETE = "shortestToComplete"; - private static final String AUTOCOMPLETER_FIRSTNAME_MODE = "autoCompFirstNameMode"; - private static final String AUTOCOMPLETER_LAST_FIRST = "autoCompLF"; - private static final String AUTOCOMPLETER_FIRST_LAST = "autoCompFF"; - private static final String AUTOCOMPLETER_COMPLETE_FIELDS = "autoCompleteFields"; - - private final JabRefPreferences preferences; - - public AutoCompletePreferences(JabRefPreferences preferences) { - this.preferences = Objects.requireNonNull(preferences); - } - - public static void putDefaults(Map defaults) { - defaults.put(AUTOCOMPLETER_SHORTEST_TO_COMPLETE, 1); - defaults.put(AUTOCOMPLETER_FIRSTNAME_MODE, AutoCompleteFirstNameMode.BOTH.name()); - defaults.put(AUTOCOMPLETER_FIRST_LAST, Boolean.FALSE); // "Autocomplete names in 'Firstname Lastname' format only" - defaults.put(AUTOCOMPLETER_LAST_FIRST, Boolean.FALSE); // "Autocomplete names in 'Lastname, Firstname' format only" - defaults.put(AUTOCOMPLETER_COMPLETE_FIELDS, "author;editor;title;journal;publisher;keywords"); - } - - public int getShortestLengthToComplete() { - return preferences.getInt(AUTOCOMPLETER_SHORTEST_TO_COMPLETE); - } - - public void setShortestLengthToComplete(Integer value) { - preferences.putInt(AUTOCOMPLETER_SHORTEST_TO_COMPLETE, value); - } - - /** - * Returns how the first names are handled. - * For "ONLY_FULL", the auto completer returns the full name, e.g. "Smith, Bob" - * For "ONLY_ABBREVIATED", the auto completer returns the first name abbreviated, e.g. "Smith, B." - * For "BOTH", the auto completer returns both versions. - */ - public AutoCompleteFirstNameMode getFirstnameMode() { - try { - return AutoCompleteFirstNameMode.valueOf(preferences.get(AUTOCOMPLETER_FIRSTNAME_MODE)); - } catch (IllegalArgumentException ex) { - // Should only occur when preferences are set directly via preferences.put and not via setFirstnameMode - return AutoCompleteFirstNameMode.BOTH; - } - } - - public void setFirstnameMode(AutoCompleteFirstNameMode mode) { - preferences.put(AUTOCOMPLETER_FIRSTNAME_MODE, mode.name()); - } - - public boolean getOnlyCompleteLastFirst() { - return preferences.getBoolean(AUTOCOMPLETER_LAST_FIRST); - } - - public void setOnlyCompleteLastFirst(boolean value) { - preferences.putBoolean(AUTOCOMPLETER_LAST_FIRST, value); - } - - public boolean getOnlyCompleteFirstLast() { - return preferences.getBoolean(AUTOCOMPLETER_FIRST_LAST); - } - - public void setOnlyCompleteFirstLast(boolean value) { - preferences.putBoolean(AUTOCOMPLETER_FIRST_LAST, value); - } - - public List getCompleteNames() { - return preferences.getStringList(AUTOCOMPLETER_COMPLETE_FIELDS); - } - - public String getCompleteNamesAsString() { - return preferences.get(AUTOCOMPLETER_COMPLETE_FIELDS); - } - - public void setCompleteNames(String value) { - preferences.put(AUTOCOMPLETER_COMPLETE_FIELDS, value); - } - - public JournalAbbreviationPreferences getJournalAbbreviationPreferences() { - return preferences.getJournalAbbreviationPreferences(); - } -} diff --git a/src/main/java/org/jabref/logic/autocompleter/AutoCompleter.java b/src/main/java/org/jabref/logic/autocompleter/AutoCompleter.java deleted file mode 100644 index ef7f0fc1cf6..00000000000 --- a/src/main/java/org/jabref/logic/autocompleter/AutoCompleter.java +++ /dev/null @@ -1,57 +0,0 @@ -package org.jabref.logic.autocompleter; - -import java.util.List; - -import org.jabref.model.entry.BibEntry; - -/** - * Delivers possible completions for a given string. - */ -public interface AutoCompleter { - - /** - * Formats the specified item. This method is called when an item is selected by the user and we need to determine - * the text to be inserted in the textbox. - * - * @param item the item to format - * @return formated string representation of the item - */ - String getAutoCompleteText(E item); - - /** - * Add a BibEntry to this AutoCompleter. - * @note The AutoCompleter itself decides which information should be stored for later completion. - */ - void addBibtexEntry(BibEntry entry); - - /** - * States whether the field consists of multiple values (false) or of a single value (true) - * - * Symptom: if false, org.jabref.gui.AutoCompleteListener#getCurrentWord(JTextComponent comp) - * returns current word only, if true, it returns the text beginning from the buffer. - */ - boolean isSingleUnitField(); - - /** - * Unclear what this method should do. - * TODO: Remove this method once the AutoCompleteListener is removed. - */ - String getPrefix(); - - /** - * Returns one or more possible completions for a given string. The returned - * completion depends on which informations were stored while adding - * BibtexEntries. If no suggestions for completions are found, then an empty list is returned. - * - * @see AutoCompleter#addBibtexEntry(BibEntry) - */ - List complete(String toComplete); - - /** - * Directly adds an item to the AutoCompleter. - * This method should be called only if the information does not comes directly from a BibEntry. - * Otherwise the {@link #addBibtexEntry(BibEntry)} is preferred. - * @param item item to add - */ - void addItemToIndex(E item); -} diff --git a/src/main/java/org/jabref/logic/autocompleter/AutoCompleterFactory.java b/src/main/java/org/jabref/logic/autocompleter/AutoCompleterFactory.java deleted file mode 100644 index 6fec0fad4ea..00000000000 --- a/src/main/java/org/jabref/logic/autocompleter/AutoCompleterFactory.java +++ /dev/null @@ -1,45 +0,0 @@ -package org.jabref.logic.autocompleter; - -import java.util.Objects; - -import org.jabref.logic.journals.JournalAbbreviationLoader; -import org.jabref.model.entry.FieldName; -import org.jabref.model.entry.FieldProperty; -import org.jabref.model.entry.InternalBibtexFields; - -/** - * Returns an autocompleter to a given fieldname. - * - * @author kahlert, cordes - */ -public class AutoCompleterFactory { - - private final AutoCompletePreferences preferences; - private final JournalAbbreviationLoader abbreviationLoader; - - - public AutoCompleterFactory(AutoCompletePreferences preferences, JournalAbbreviationLoader abbreviationLoader) { - this.preferences = Objects.requireNonNull(preferences); - this.abbreviationLoader = Objects.requireNonNull(abbreviationLoader); - } - - public AutoCompleter getFor(String fieldName) { - Objects.requireNonNull(fieldName); - - if (InternalBibtexFields.getFieldProperties(fieldName).contains(FieldProperty.PERSON_NAMES)) { - return new NameFieldAutoCompleter(fieldName, preferences); - } else if (InternalBibtexFields.getFieldProperties(fieldName).contains(FieldProperty.SINGLE_ENTRY_LINK)) { - return new BibtexKeyAutoCompleter(preferences); - } else if (InternalBibtexFields.getFieldProperties(fieldName).contains(FieldProperty.JOURNAL_NAME) - || FieldName.PUBLISHER.equals(fieldName)) { - return new JournalAutoCompleter(fieldName, preferences, abbreviationLoader); - } else { - return new DefaultAutoCompleter(fieldName, preferences); - } - } - - public AutoCompleter getPersonAutoCompleter() { - return new NameFieldAutoCompleter(InternalBibtexFields.getPersonNameFields(), true, preferences); - } - -} diff --git a/src/main/java/org/jabref/logic/autocompleter/AutoCompleters.java b/src/main/java/org/jabref/logic/autocompleter/AutoCompleters.java deleted file mode 100644 index 93857a84f4c..00000000000 --- a/src/main/java/org/jabref/logic/autocompleter/AutoCompleters.java +++ /dev/null @@ -1,39 +0,0 @@ -package org.jabref.logic.autocompleter; - -import java.util.HashMap; -import java.util.Map; - -import org.jabref.model.database.BibDatabase; -import org.jabref.model.entry.BibEntry; - -class AutoCompleters { - - protected final Map> autoCompleters = new HashMap<>(); - // Hashtable that holds as keys the names of the fields where - // autocomplete is active, and references to the autocompleter objects. - - public AutoCompleter get(String fieldName) { - return autoCompleters.get(fieldName); - } - - protected void addDatabase(BibDatabase database) { - for (BibEntry entry : database.getEntries()) { - addEntry(entry); - } - } - - /** - * This methods assures all words in the given entry are recorded in their - * respective Completers, if any. - */ - public void addEntry(BibEntry bibEntry) { - for (AutoCompleter autoCompleter : autoCompleters.values()) { - autoCompleter.addBibtexEntry(bibEntry); - } - } - - protected void put(String field, AutoCompleter autoCompleter) { - autoCompleters.put(field, autoCompleter); - } - -} diff --git a/src/main/java/org/jabref/logic/autocompleter/BibtexKeyAutoCompleter.java b/src/main/java/org/jabref/logic/autocompleter/BibtexKeyAutoCompleter.java deleted file mode 100644 index 5cad7403180..00000000000 --- a/src/main/java/org/jabref/logic/autocompleter/BibtexKeyAutoCompleter.java +++ /dev/null @@ -1,38 +0,0 @@ -package org.jabref.logic.autocompleter; - -import org.jabref.model.entry.BibEntry; - -/** - * Delivers possible completions for a given string based on the key fields of the added items. - * - * @author kahlert, cordes - */ -class BibtexKeyAutoCompleter extends AbstractAutoCompleter { - - public BibtexKeyAutoCompleter(AutoCompletePreferences preferences) { - super(preferences); - } - - @Override - public boolean isSingleUnitField() { - return false; - } - - /** - * {@inheritDoc} - * The bibtex key of the entry will be added to the index. - */ - @Override - public void addBibtexEntry(BibEntry entry) { - if (entry == null) { - return; - } - - entry.getCiteKeyOptional().ifPresent(key -> addItemToIndex(key.trim())); - } - - @Override - protected int getLengthOfShortestWordToAdd() { - return 1; - } -} diff --git a/src/main/java/org/jabref/logic/autocompleter/ContentAutoCompleters.java b/src/main/java/org/jabref/logic/autocompleter/ContentAutoCompleters.java deleted file mode 100644 index 93ddfaff663..00000000000 --- a/src/main/java/org/jabref/logic/autocompleter/ContentAutoCompleters.java +++ /dev/null @@ -1,41 +0,0 @@ -package org.jabref.logic.autocompleter; - -import java.util.List; -import java.util.Map; -import java.util.Objects; - -import org.jabref.logic.journals.JournalAbbreviationLoader; -import org.jabref.model.database.BibDatabase; -import org.jabref.model.metadata.MetaData; - -public class ContentAutoCompleters extends AutoCompleters { - - public ContentAutoCompleters() { - } - - public ContentAutoCompleters(BibDatabase database, MetaData metaData, AutoCompletePreferences preferences, - JournalAbbreviationLoader abbreviationLoader) { - Objects.requireNonNull(preferences); - - AutoCompleterFactory autoCompleterFactory = new AutoCompleterFactory(preferences, abbreviationLoader); - List completeFields = preferences.getCompleteNames(); - for (String field : completeFields) { - AutoCompleter autoCompleter = autoCompleterFactory.getFor(field); - put(field, autoCompleter); - } - addContentSelectorValuesToAutoCompleters(metaData); - - addDatabase(database); - } - - /** - * For all fields with both autocompletion and content selector, add content selector - * values to the autocompleter list: - */ - public void addContentSelectorValuesToAutoCompleters(MetaData metaData) { - for (Map.Entry> entry : this.autoCompleters.entrySet()) { - AutoCompleter ac = entry.getValue(); - metaData.getContentSelectorValuesForField(entry.getKey()).forEach(ac::addItemToIndex); - } - } -} diff --git a/src/main/java/org/jabref/logic/autocompleter/EntireFieldAutoCompleter.java b/src/main/java/org/jabref/logic/autocompleter/EntireFieldAutoCompleter.java deleted file mode 100644 index 8ef05855e6b..00000000000 --- a/src/main/java/org/jabref/logic/autocompleter/EntireFieldAutoCompleter.java +++ /dev/null @@ -1,43 +0,0 @@ -package org.jabref.logic.autocompleter; - -import java.util.Objects; - -import org.jabref.model.entry.BibEntry; - -/** - * Delivers possible completions for a given string. - * Stores the full original value of one field of the given BibtexEntries. - * - * @author kahlert, cordes - */ -class EntireFieldAutoCompleter extends AbstractAutoCompleter { - - private final String fieldName; - - /** - * @see AutoCompleterFactory - */ - EntireFieldAutoCompleter(String fieldName, AutoCompletePreferences preferences) { - super(preferences); - - this.fieldName = Objects.requireNonNull(fieldName); - } - - @Override - public boolean isSingleUnitField() { - return true; - } - - /** - * {@inheritDoc} - * Stores the full original value of the given field. - */ - @Override - public void addBibtexEntry(BibEntry entry) { - if (entry == null) { - return; - } - - entry.getField(fieldName).ifPresent(fieldValue -> addItemToIndex(fieldValue.trim())); - } -} diff --git a/src/main/java/org/jabref/logic/autocompleter/JournalAutoCompleter.java b/src/main/java/org/jabref/logic/autocompleter/JournalAutoCompleter.java deleted file mode 100644 index 20cee610ea3..00000000000 --- a/src/main/java/org/jabref/logic/autocompleter/JournalAutoCompleter.java +++ /dev/null @@ -1,37 +0,0 @@ -package org.jabref.logic.autocompleter; - -import java.util.List; -import java.util.Objects; - -import org.jabref.logic.journals.Abbreviation; -import org.jabref.logic.journals.JournalAbbreviationLoader; -import org.jabref.logic.journals.JournalAbbreviationPreferences; - -public class JournalAutoCompleter extends EntireFieldAutoCompleter { - - private final JournalAbbreviationLoader abbreviationLoader; - private final JournalAbbreviationPreferences journalAbbreviationPreferences; - - - JournalAutoCompleter(String fieldName, AutoCompletePreferences preferences, - JournalAbbreviationLoader abbreviationLoader) { - super(fieldName, preferences); - this.abbreviationLoader = Objects.requireNonNull(abbreviationLoader); - this.journalAbbreviationPreferences = preferences.getJournalAbbreviationPreferences(); - } - - @Override - public List complete(String toComplete) { - List completions = super.complete(toComplete); - - // Also return journal names in the journal abbreviation list - for (Abbreviation abbreviation : abbreviationLoader - .getRepository(journalAbbreviationPreferences).getAbbreviations()) { - if (abbreviation.getName().startsWith(toComplete)) { - completions.add(abbreviation.getName()); - } - } - - return completions; - } -} diff --git a/src/main/java/org/jabref/logic/autocompleter/NameFieldAutoCompleter.java b/src/main/java/org/jabref/logic/autocompleter/NameFieldAutoCompleter.java deleted file mode 100644 index 95b4ecb09e1..00000000000 --- a/src/main/java/org/jabref/logic/autocompleter/NameFieldAutoCompleter.java +++ /dev/null @@ -1,173 +0,0 @@ -package org.jabref.logic.autocompleter; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Locale; -import java.util.Objects; - -import org.jabref.model.entry.Author; -import org.jabref.model.entry.AuthorList; -import org.jabref.model.entry.BibEntry; - -/** - * Delivers possible completions for a given string. - * Interprets the given values as names and stores them in different - * permutations so we can complete by beginning with last name or first name. - * - * @author kahlert, cordes - */ -class NameFieldAutoCompleter extends AbstractAutoCompleter { - - private final List fieldNames; - /** - * true if only last names should be completed and there is NO separation by " and ", but by " " - */ - private final boolean lastNameOnlyAndSeparationBySpace; - private final boolean autoCompFF; - private final boolean autoCompLF; - private final AutoCompleteFirstNameMode autoCompFirstnameMode; - - private String prefix = ""; - - - /** - * @see AutoCompleterFactory - */ - NameFieldAutoCompleter(String fieldName, AutoCompletePreferences preferences) { - this(Collections.singletonList(Objects.requireNonNull(fieldName)), false, preferences); - } - - public NameFieldAutoCompleter(List fieldNames, boolean lastNameOnlyAndSeparationBySpace, - AutoCompletePreferences preferences) { - super(preferences); - - this.fieldNames = Objects.requireNonNull(fieldNames); - this.lastNameOnlyAndSeparationBySpace = lastNameOnlyAndSeparationBySpace; - if (preferences.getOnlyCompleteFirstLast()) { - autoCompFF = true; - autoCompLF = false; - } else if (preferences.getOnlyCompleteLastFirst()) { - autoCompFF = false; - autoCompLF = true; - } else { - autoCompFF = true; - autoCompLF = true; - } - autoCompFirstnameMode = preferences.getFirstnameMode() == null ? AutoCompleteFirstNameMode.BOTH : preferences - .getFirstnameMode(); - } - - @Override - public boolean isSingleUnitField() { - // quick hack - // when used at entry fields (!this.lastNameOnlyAndSeparationBySpace), this is a single unit field - // when used at the search form (this.lastNameOnlyAndSeparationBySpace), this is NOT a single unit field - // reason: search keywords are separated by space. - // This is OK for last names without prefix. "Lastname" works perfectly. - // querying for "van der Lastname" can be interpreted as - // a) "van" "der" "Lastname" - // b) "van der Lastname" (autocompletion lastname) - return !this.lastNameOnlyAndSeparationBySpace; - } - - @Override - public void addBibtexEntry(BibEntry entry) { - if (entry == null) { - return; - } - for (String fieldName : fieldNames) { - entry.getField(fieldName).ifPresent(fieldValue -> { - AuthorList authorList = AuthorList.parse(fieldValue); - for (Author author : authorList.getAuthors()) { - handleAuthor(author); - } - }); - } - } - - /** - * SIDE EFFECT: sets class variable prefix - * Delimiter: " and " or " " - * - * @return String without prefix - */ - private String determinePrefixAndReturnRemainder(String str, String delimiter) { - String result = str; - int index = result.toLowerCase(Locale.ROOT).lastIndexOf(delimiter); - if (index >= 0) { - prefix = result.substring(0, index + delimiter.length()); - result = result.substring(index + delimiter.length()); - } else { - prefix = ""; - } - return result; - } - - private void handleAuthor(Author author) { - if (lastNameOnlyAndSeparationBySpace) { - addItemToIndex(author.getLastOnly()); - } else { - if (autoCompLF) { - switch (autoCompFirstnameMode) { - case ONLY_ABBREVIATED: - addItemToIndex(author.getLastFirst(true)); - break; - case ONLY_FULL: - addItemToIndex(author.getLastFirst(false)); - break; - case BOTH: - addItemToIndex(author.getLastFirst(true)); - addItemToIndex(author.getLastFirst(false)); - break; - default: - break; - } - } - if (autoCompFF) { - switch (autoCompFirstnameMode) { - case ONLY_ABBREVIATED: - addItemToIndex(author.getFirstLast(true)); - break; - case ONLY_FULL: - addItemToIndex(author.getFirstLast(false)); - break; - case BOTH: - addItemToIndex(author.getFirstLast(true)); - addItemToIndex(author.getFirstLast(false)); - break; - default: - break; - } - } - } - - } - - @Override - public List complete(String toComplete) { - if (toComplete == null) { - return new ArrayList<>(); - } - - String result; - // Normally, one would implement that using - // class inheritance. But this seemed overengineered - if (this.lastNameOnlyAndSeparationBySpace) { - result = determinePrefixAndReturnRemainder(toComplete, " "); - } else { - result = determinePrefixAndReturnRemainder(toComplete, " and "); - } - return super.complete(result); - } - - @Override - public String getPrefix() { - return prefix; - } - - @Override - protected int getLengthOfShortestWordToAdd() { - return 1; - } -} diff --git a/src/main/java/org/jabref/logic/autosaveandbackup/BackupManager.java b/src/main/java/org/jabref/logic/autosaveandbackup/BackupManager.java index 47b98b69296..15d04bcaddd 100644 --- a/src/main/java/org/jabref/logic/autosaveandbackup/BackupManager.java +++ b/src/main/java/org/jabref/logic/autosaveandbackup/BackupManager.java @@ -22,6 +22,7 @@ import org.jabref.logic.util.io.FileUtil; import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.database.event.BibDatabaseContextChangedEvent; +import org.jabref.model.entry.event.FieldChangedEvent; import org.jabref.preferences.JabRefPreferences; import com.google.common.eventbus.Subscribe; @@ -42,12 +43,13 @@ public class BackupManager { private static Set runningInstances = new HashSet<>(); + private String lastFieldChanged; + private final BibDatabaseContext bibDatabaseContext; private final JabRefPreferences preferences; private final ExecutorService executor; private final Runnable backupTask = () -> determineBackupPath().ifPresent(this::performBackup); - private BackupManager(BibDatabaseContext bibDatabaseContext) { this.bibDatabaseContext = bibDatabaseContext; this.preferences = JabRefPreferences.getInstance(); @@ -129,7 +131,18 @@ private void performBackup(Path backupPath) { @Subscribe public synchronized void listen(@SuppressWarnings("unused") BibDatabaseContextChangedEvent event) { - startBackupTask(); + if (!(event instanceof FieldChangedEvent)) { + startBackupTask(); + } else { + // only do a backup if the field changes are more than one character or a new field is edited + FieldChangedEvent fieldChange = (FieldChangedEvent) event; + boolean isEditOnNewField = lastFieldChanged == null || !lastFieldChanged.equals(fieldChange.getFieldName()); + + if (fieldChange.getDelta() > 1 || isEditOnNewField) { + lastFieldChanged = fieldChange.getFieldName(); + startBackupTask(); + } + } } private void startBackupTask() { diff --git a/src/main/java/org/jabref/logic/bibtex/DuplicateCheck.java b/src/main/java/org/jabref/logic/bibtex/DuplicateCheck.java index 1131737d727..57b1c54af16 100644 --- a/src/main/java/org/jabref/logic/bibtex/DuplicateCheck.java +++ b/src/main/java/org/jabref/logic/bibtex/DuplicateCheck.java @@ -26,7 +26,7 @@ * This class contains utility method for duplicate checking of entries. */ public class DuplicateCheck { - public static double duplicateThreshold = 0.75; // The overall threshold to signal a duplicate pair + private static final double DUPLICATE_THRESHOLD = 0.75; // The overall threshold to signal a duplicate pair private static final Log LOGGER = LogFactory.getLog(DuplicateCheck.class); /* @@ -54,7 +54,8 @@ public class DuplicateCheck { DuplicateCheck.FIELD_WEIGHTS.put(FieldName.JOURNAL, 2.); } - private DuplicateCheck() { } + private DuplicateCheck() { + } /** * Checks if the two entries represent the same publication. @@ -64,8 +65,7 @@ private DuplicateCheck() { } * @return boolean */ public static boolean isDuplicate(BibEntry one, BibEntry two, BibDatabaseMode bibDatabaseMode) { - // same identifier - if (hasSameIdentifier(one, two)) { + if (haveSameIdentifier(one, two)) { return true; } @@ -74,6 +74,10 @@ public static boolean isDuplicate(BibEntry one, BibEntry two, BibDatabaseMode bi return false; } + if (haveDifferentEditions(one, two)) { + return false; + } + EntryType type = EntryTypes.getTypeOrDefault(one.getType(), bibDatabaseMode); // The check if they have the same required fields: List var = type.getRequiredFieldsFlat(); @@ -84,21 +88,30 @@ public static boolean isDuplicate(BibEntry one, BibEntry two, BibDatabaseMode bi req = DuplicateCheck.compareFieldSet(var, one, two); } - if (Math.abs(req[0] - DuplicateCheck.duplicateThreshold) > DuplicateCheck.DOUBT_RANGE) { + if (Math.abs(req[0] - DuplicateCheck.DUPLICATE_THRESHOLD) > DuplicateCheck.DOUBT_RANGE) { // Far from the threshold value, so we base our decision on the req. fields only - return req[0] >= DuplicateCheck.duplicateThreshold; + return req[0] >= DuplicateCheck.DUPLICATE_THRESHOLD; } // Close to the threshold value, so we take a look at the optional fields, if any: List optionalFields = type.getOptionalFields(); if (optionalFields != null) { double[] opt = DuplicateCheck.compareFieldSet(optionalFields, one, two); double totValue = ((DuplicateCheck.REQUIRED_WEIGHT * req[0] * req[1]) + (opt[0] * opt[1])) / ((req[1] * DuplicateCheck.REQUIRED_WEIGHT) + opt[1]); - return totValue >= DuplicateCheck.duplicateThreshold; + return totValue >= DuplicateCheck.DUPLICATE_THRESHOLD; } - return req[0] >= DuplicateCheck.duplicateThreshold; + return req[0] >= DuplicateCheck.DUPLICATE_THRESHOLD; } - private static boolean hasSameIdentifier(BibEntry one, BibEntry two) { + private static boolean haveDifferentEditions(BibEntry one, BibEntry two) { + if (one.getField(FieldName.EDITION).isPresent() && two.getField(FieldName.EDITION).isPresent()) { + if (!one.getField(FieldName.EDITION).get().equals(two.getField(FieldName.EDITION).get())) { + return true; + } + } + return false; + } + + private static boolean haveSameIdentifier(BibEntry one, BibEntry two) { for (String name : FieldName.getIdentifierFieldNames()) { if (one.getField(name).isPresent() && one.getField(name).equals(two.getField(name))) { return true; @@ -112,11 +125,7 @@ private static double[] compareFieldSet(List fields, BibEntry one, BibEn double totWeights = 0.; for (String field : fields) { double weight; - if (DuplicateCheck.FIELD_WEIGHTS.containsKey(field)) { - weight = DuplicateCheck.FIELD_WEIGHTS.get(field); - } else { - weight = 1.0; - } + weight = DuplicateCheck.FIELD_WEIGHTS.getOrDefault(field, 1.0); totWeights += weight; int result = DuplicateCheck.compareSingleField(field, one, two); if (result == EQUAL) { @@ -128,7 +137,7 @@ private static double[] compareFieldSet(List fields, BibEntry one, BibEn if (totWeights > 0) { return new double[]{res / totWeights, totWeights}; } - return new double[] {0.5, 0.0}; + return new double[]{0.5, 0.0}; } private static int compareSingleField(String field, BibEntry one, BibEntry two) { @@ -218,7 +227,7 @@ public static double compareEntriesStrictly(BibEntry one, BibEntry two) { * * @param database The database to search. * @param entry The entry of which we are looking for duplicates. - * @return The first duplicate entry found. null if no duplicates are found. + * @return The first duplicate entry found. Empty Optional if no duplicates are found. */ public static Optional containsDuplicate(BibDatabase database, BibEntry entry, BibDatabaseMode bibDatabaseMode) { for (BibEntry other : database.getEntries()) { @@ -232,8 +241,8 @@ public static Optional containsDuplicate(BibDatabase database, BibEntr /** * Compare two strings on the basis of word-by-word correlation analysis. * - * @param s1 The first string - * @param s2 The second string + * @param s1 The first string + * @param s2 The second string * @return a value in the interval [0, 1] indicating the degree of match. */ public static double correlateByWords(String s1, String s2) { @@ -252,17 +261,17 @@ public static double correlateByWords(String s1, String s2) { } - /** + /* * Calculates the similarity (a number within 0 and 1) between two strings. * http://stackoverflow.com/questions/955110/similarity-string-comparison-in-java */ - private static double similarity(String s1, String s2) { - String longer = s1; - String shorter = s2; + private static double similarity(String first, String second) { + String longer = first; + String shorter = second; - if (s1.length() < s2.length()) { - longer = s2; - shorter = s1; + if (first.length() < second.length()) { + longer = second; + shorter = first; } int longerLength = longer.length(); diff --git a/src/main/java/org/jabref/logic/exporter/FileSaveSession.java b/src/main/java/org/jabref/logic/exporter/FileSaveSession.java index 60c1eb00d73..e1b403da460 100644 --- a/src/main/java/org/jabref/logic/exporter/FileSaveSession.java +++ b/src/main/java/org/jabref/logic/exporter/FileSaveSession.java @@ -91,7 +91,7 @@ public void commit(Path file) throws SaveException { PosixFilePermission.GROUP_READ, PosixFilePermission.GROUP_WRITE, PosixFilePermission.OTHERS_READ); - if (FileUtil.isPosixCompilant && Files.exists(file)) { + if (FileUtil.IS_POSIX_COMPILANT && Files.exists(file)) { try { oldFilePermissions = Files.getPosixFilePermissions(file); } catch (IOException exception) { @@ -102,7 +102,7 @@ public void commit(Path file) throws SaveException { FileUtil.copyFile(temporaryFile, file, true); // Restore file permissions - if (FileUtil.isPosixCompilant) { + if (FileUtil.IS_POSIX_COMPILANT) { try { Files.setPosixFilePermissions(file, oldFilePermissions); } catch (IOException exception) { diff --git a/src/main/java/org/jabref/logic/importer/fetcher/DoiFetcher.java b/src/main/java/org/jabref/logic/importer/fetcher/DoiFetcher.java index 40307743a26..1c7a1587395 100644 --- a/src/main/java/org/jabref/logic/importer/fetcher/DoiFetcher.java +++ b/src/main/java/org/jabref/logic/importer/fetcher/DoiFetcher.java @@ -23,7 +23,7 @@ import org.jabref.model.entry.identifier.DOI; public class DoiFetcher implements IdBasedFetcher, EntryBasedFetcher { - public static final String name = "DOI"; + public static final String NAME = "DOI"; private final ImportFormatPreferences preferences; @@ -33,7 +33,7 @@ public DoiFetcher(ImportFormatPreferences preferences) { @Override public String getName() { - return DoiFetcher.name; + return DoiFetcher.NAME; } @Override @@ -80,5 +80,4 @@ public List performSearch(BibEntry entry) throws FetcherException { bibEntry.ifPresent(list::add); return list; } - } diff --git a/src/main/java/org/jabref/logic/importer/fetcher/MathSciNet.java b/src/main/java/org/jabref/logic/importer/fetcher/MathSciNet.java index 4262d289954..eeacd2a0f70 100644 --- a/src/main/java/org/jabref/logic/importer/fetcher/MathSciNet.java +++ b/src/main/java/org/jabref/logic/importer/fetcher/MathSciNet.java @@ -108,5 +108,8 @@ public void doPostCleanup(BibEntry entry) { new MoveFieldCleanup("mrclass", FieldName.KEYWORDS).cleanup(entry); new FieldFormatterCleanup("mrreviewer", new ClearFormatter()).cleanup(entry); new FieldFormatterCleanup(FieldName.URL, new ClearFormatter()).cleanup(entry); + + // Remove comments: MathSciNet prepends a
 html tag
+        entry.setCommentsBeforeEntry("");
     }
 }
diff --git a/src/main/java/org/jabref/logic/journals/AbbreviationParser.java b/src/main/java/org/jabref/logic/journals/AbbreviationParser.java
index c5d8eab569f..589081ab6e0 100644
--- a/src/main/java/org/jabref/logic/journals/AbbreviationParser.java
+++ b/src/main/java/org/jabref/logic/journals/AbbreviationParser.java
@@ -11,9 +11,11 @@
 import java.net.URL;
 import java.nio.charset.Charset;
 import java.nio.charset.StandardCharsets;
+import java.util.HashSet;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Objects;
+import java.util.Set;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
@@ -25,7 +27,7 @@ public class AbbreviationParser {
 
     private static final Log LOGGER = LogFactory.getLog(AbbreviationParser.class);
 
-    private final List abbreviations = new LinkedList<>();
+    private final Set abbreviations = new HashSet<>(5000);
 
     public void readJournalListFromResource(String resourceFileName) {
         URL url = Objects.requireNonNull(JournalAbbreviationRepository.class.getResource(Objects.requireNonNull(resourceFileName)));
@@ -90,9 +92,7 @@ private void addLine(String line) {
             }
 
             Abbreviation abbreviation = new Abbreviation(fullName, abbrName);
-            if (!abbreviations.contains(abbreviation)) {
-                this.abbreviations.add(abbreviation);
-            }
+            this.abbreviations.add(abbreviation);
         }
     }
 
diff --git a/src/main/java/org/jabref/logic/journals/JournalAbbreviationRepository.java b/src/main/java/org/jabref/logic/journals/JournalAbbreviationRepository.java
index 56a26be1b39..08743e3ffe2 100644
--- a/src/main/java/org/jabref/logic/journals/JournalAbbreviationRepository.java
+++ b/src/main/java/org/jabref/logic/journals/JournalAbbreviationRepository.java
@@ -1,14 +1,11 @@
 package org.jabref.logic.journals;
 
+import java.util.Collection;
 import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
+import java.util.HashSet;
 import java.util.Objects;
 import java.util.Optional;
-import java.util.SortedSet;
-import java.util.TreeSet;
+import java.util.Set;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
@@ -19,12 +16,7 @@
 public class JournalAbbreviationRepository {
 
     private static final Log LOGGER = LogFactory.getLog(JournalAbbreviationRepository.class);
-    private final Map fullNameLowerCase2Abbreviation = new HashMap<>();
-    private final Map isoLowerCase2Abbreviation = new HashMap<>();
-
-    private final Map medlineLowerCase2Abbreviation = new HashMap<>();
-
-    private final SortedSet abbreviations = new TreeSet<>();
+    private final Set abbreviations = new HashSet<>(16000); // We have over 15.000 abbreviations in the built-in lists
 
     public JournalAbbreviationRepository(Abbreviation... abbreviations) {
         for (Abbreviation abbreviation : abbreviations) {
@@ -32,19 +24,27 @@ public JournalAbbreviationRepository(Abbreviation... abbreviations) {
         }
     }
 
+    private static boolean isMatched(String name, Abbreviation abbreviation) {
+        return name.equalsIgnoreCase(abbreviation.getName())
+                || name.equalsIgnoreCase(abbreviation.getIsoAbbreviation())
+                || name.equalsIgnoreCase(abbreviation.getMedlineAbbreviation());
+    }
+
+    private static boolean isMatchedAbbreviated(String name, Abbreviation abbreviation) {
+        return name.equalsIgnoreCase(abbreviation.getIsoAbbreviation())
+                || name.equalsIgnoreCase(abbreviation.getMedlineAbbreviation());
+    }
+
     public int size() {
         return abbreviations.size();
     }
 
     public boolean isKnownName(String journalName) {
-        String nameKey = Objects.requireNonNull(journalName).trim().toLowerCase(Locale.ENGLISH);
-        return (fullNameLowerCase2Abbreviation.containsKey(nameKey)) || (isoLowerCase2Abbreviation.containsKey(nameKey))
-                || (medlineLowerCase2Abbreviation.containsKey(nameKey));
+        return abbreviations.stream().anyMatch(abbreviation -> isMatched(journalName.trim(), abbreviation));
     }
 
     public boolean isAbbreviatedName(String journalName) {
-        String nameKey = Objects.requireNonNull(journalName).trim().toLowerCase(Locale.ENGLISH);
-        return (isoLowerCase2Abbreviation.containsKey(nameKey)) || (medlineLowerCase2Abbreviation.containsKey(nameKey));
+        return abbreviations.stream().anyMatch(abbreviation -> isMatchedAbbreviated(journalName.trim(), abbreviation));
     }
 
     /**
@@ -54,23 +54,13 @@ public boolean isAbbreviatedName(String journalName) {
      * @return The abbreviated name
      */
     public Optional getAbbreviation(String journalName) {
-        String nameKey = Objects.requireNonNull(journalName).toLowerCase(Locale.ENGLISH).trim();
-
-        if (fullNameLowerCase2Abbreviation.containsKey(nameKey)) {
-            return Optional.of(fullNameLowerCase2Abbreviation.get(nameKey));
-        } else if (isoLowerCase2Abbreviation.containsKey(nameKey)) {
-            return Optional.of(isoLowerCase2Abbreviation.get(nameKey));
-        } else if (medlineLowerCase2Abbreviation.containsKey(nameKey)) {
-            return Optional.of(medlineLowerCase2Abbreviation.get(nameKey));
-        } else {
-            return Optional.empty();
-        }
+        return abbreviations.stream().filter(abbreviation -> isMatched(journalName.trim(), abbreviation)).findFirst();
     }
 
     public void addEntry(Abbreviation abbreviation) {
         Objects.requireNonNull(abbreviation);
 
-        if (isKnownName(abbreviation.getName())) {
+        if (abbreviations.contains(abbreviation)) {
             Abbreviation previous = getAbbreviation(abbreviation.getName()).get();
             abbreviations.remove(previous);
             LOGGER.info("Duplicate journal abbreviation - old one will be overwritten by new one\nOLD: "
@@ -78,51 +68,25 @@ public void addEntry(Abbreviation abbreviation) {
         }
 
         abbreviations.add(abbreviation);
-
-        fullNameLowerCase2Abbreviation.put(abbreviation.getName().toLowerCase(Locale.ENGLISH), abbreviation);
-        isoLowerCase2Abbreviation.put(abbreviation.getIsoAbbreviation().toLowerCase(Locale.ENGLISH), abbreviation);
-        medlineLowerCase2Abbreviation.put(abbreviation.getMedlineAbbreviation().toLowerCase(Locale.ENGLISH),
-                abbreviation);
     }
 
-    public void addEntries(List abbreviationsToAdd) {
+    public void addEntries(Collection abbreviationsToAdd) {
         abbreviationsToAdd.forEach(this::addEntry);
     }
 
-    public SortedSet getAbbreviations() {
-        return Collections.unmodifiableSortedSet(abbreviations);
+    public Set getAbbreviations() {
+        return Collections.unmodifiableSet(abbreviations);
     }
 
     public Optional getNextAbbreviation(String text) {
-        Optional abbreviation = getAbbreviation(text);
-
-        if (!abbreviation.isPresent()) {
-            return Optional.empty();
-        }
-
-        Abbreviation abbr = abbreviation.get();
-        return Optional.of(abbr.getNext(text));
+        return getAbbreviation(text).map(abbreviation -> abbreviation.getNext(text));
     }
 
     public Optional getMedlineAbbreviation(String text) {
-        Optional abbreviation = getAbbreviation(text);
-
-        if (!abbreviation.isPresent()) {
-            return Optional.empty();
-        }
-
-        Abbreviation abbr = abbreviation.get();
-        return Optional.of(abbr.getMedlineAbbreviation());
+        return getAbbreviation(text).map(Abbreviation::getMedlineAbbreviation);
     }
 
     public Optional getIsoAbbreviation(String text) {
-        Optional abbreviation = getAbbreviation(text);
-
-        if (!abbreviation.isPresent()) {
-            return Optional.empty();
-        }
-
-        Abbreviation abbr = abbreviation.get();
-        return Optional.of(abbr.getIsoAbbreviation());
+        return getAbbreviation(text).map(Abbreviation::getIsoAbbreviation);
     }
 }
diff --git a/src/main/java/org/jabref/logic/l10n/Languages.java b/src/main/java/org/jabref/logic/l10n/Languages.java
index c8429abdec9..8098f4402f7 100644
--- a/src/main/java/org/jabref/logic/l10n/Languages.java
+++ b/src/main/java/org/jabref/logic/l10n/Languages.java
@@ -41,17 +41,22 @@ public static Optional convertToSupportedLocale(String language) {
         Objects.requireNonNull(language);
 
         if (!LANGUAGES.values().contains(language)) {
-            if (!language.contains("_")) {
-                return Optional.empty();
-            }
-
-            String lang = language.split("_")[0];
-            if (!LANGUAGES.values().contains(lang)) {
-                return Optional.empty();
-            }
-            return Optional.of(new Locale(lang));
+            return Optional.empty();
         }
+        //Very important to split languages like pt_BR into two parts, because otherwise the country would be threated lowercase
+        //and create problems in loading
+        String[] languageParts = language.split("_");
+        Locale locale;
+        if (languageParts.length == 1) {
+            locale = new Locale(languageParts[0]);
+        } else if (languageParts.length == 2) {
+            locale = new Locale(languageParts[0], languageParts[1]);
+        } else {
+            locale = Locale.ENGLISH;
+        }
+
+        return Optional.of(locale);
 
-        return Optional.of(new Locale(language));
     }
+
 }
diff --git a/src/main/java/org/jabref/logic/msbib/MSBibMapping.java b/src/main/java/org/jabref/logic/msbib/MSBibMapping.java
index 752484fd116..d7d9e9506b6 100644
--- a/src/main/java/org/jabref/logic/msbib/MSBibMapping.java
+++ b/src/main/java/org/jabref/logic/msbib/MSBibMapping.java
@@ -11,73 +11,70 @@
 import com.google.common.collect.HashBiMap;
 
 /**
- * Mapping between Msbib and biblatex
- * All Fields: List of all MSBib fields
- *
+ * Mapping between Msbib and biblatex All Fields: List
+ * of all MSBib fields
  */
 public class MSBibMapping {
 
     private static final String BIBTEX_PREFIX = "BIBTEX_";
     private static final String MSBIB_PREFIX = "msbib-";
 
-    private static final HashBiMap biblatexToMsBib = HashBiMap.create();
+    private static final HashBiMap BIBLATEX_TO_MS_BIB = HashBiMap.create();
 
     static {
-        biblatexToMsBib.put(BibEntry.KEY_FIELD, "Tag");
-        biblatexToMsBib.put(FieldName.TITLE, "Title");
-        biblatexToMsBib.put(FieldName.YEAR, "Year");
-        biblatexToMsBib.put(FieldName.NOTE, "Comments");
-        biblatexToMsBib.put(FieldName.VOLUME, "Volume");
-        biblatexToMsBib.put(FieldName.LANGUAGE, "LCID");
-        biblatexToMsBib.put(FieldName.EDITION, "Edition");
-        biblatexToMsBib.put(FieldName.PUBLISHER, "Publisher");
-        biblatexToMsBib.put(FieldName.BOOKTITLE, "BookTitle");
-        biblatexToMsBib.put("shorttitle", "ShortTitle");
-        biblatexToMsBib.put(FieldName.NOTE, "Comments");
-        biblatexToMsBib.put(FieldName.VOLUMES, "NumberVolumes");
-
-        //biblatexToMsBib.put(FieldName.BOOKTITLE, "ConferenceName");
-        //biblatexToMsBib.put(FieldName.PAGES, "Pages");
-        biblatexToMsBib.put(FieldName.CHAPTER, "ChapterNumber");
-
-        biblatexToMsBib.put(FieldName.ISSUE, "Issue");
-        biblatexToMsBib.put(FieldName.SCHOOL, "Department");
-        biblatexToMsBib.put(FieldName.INSTITUTION, "Institution");
-        biblatexToMsBib.put(FieldName.DOI, "DOI");
-        biblatexToMsBib.put(FieldName.URL, "URL");
+        BIBLATEX_TO_MS_BIB.put(BibEntry.KEY_FIELD, "Tag");
+        BIBLATEX_TO_MS_BIB.put(FieldName.TITLE, "Title");
+        BIBLATEX_TO_MS_BIB.put(FieldName.YEAR, "Year");
+        BIBLATEX_TO_MS_BIB.put(FieldName.NOTE, "Comments");
+        BIBLATEX_TO_MS_BIB.put(FieldName.VOLUME, "Volume");
+        BIBLATEX_TO_MS_BIB.put(FieldName.LANGUAGE, "LCID");
+        BIBLATEX_TO_MS_BIB.put(FieldName.EDITION, "Edition");
+        BIBLATEX_TO_MS_BIB.put(FieldName.PUBLISHER, "Publisher");
+        BIBLATEX_TO_MS_BIB.put(FieldName.BOOKTITLE, "BookTitle");
+        BIBLATEX_TO_MS_BIB.put("shorttitle", "ShortTitle");
+        BIBLATEX_TO_MS_BIB.put(FieldName.NOTE, "Comments");
+        BIBLATEX_TO_MS_BIB.put(FieldName.VOLUMES, "NumberVolumes");
+
+        BIBLATEX_TO_MS_BIB.put(FieldName.CHAPTER, "ChapterNumber");
+
+        BIBLATEX_TO_MS_BIB.put(FieldName.ISSUE, "Issue");
+        BIBLATEX_TO_MS_BIB.put(FieldName.SCHOOL, "Department");
+        BIBLATEX_TO_MS_BIB.put(FieldName.INSTITUTION, "Institution");
+        BIBLATEX_TO_MS_BIB.put(FieldName.DOI, "DOI");
+        BIBLATEX_TO_MS_BIB.put(FieldName.URL, "URL");
         // BibTeX/Biblatex only fields
 
-        biblatexToMsBib.put(FieldName.SERIES, BIBTEX_PREFIX + "Series");
-        biblatexToMsBib.put(FieldName.ABSTRACT, BIBTEX_PREFIX + "Abstract");
-        biblatexToMsBib.put(FieldName.KEYWORDS, BIBTEX_PREFIX + "KeyWords");
-        biblatexToMsBib.put(FieldName.CROSSREF, BIBTEX_PREFIX + "CrossRef");
-        biblatexToMsBib.put(FieldName.HOWPUBLISHED, BIBTEX_PREFIX + "HowPublished");
-        biblatexToMsBib.put(FieldName.PUBSTATE, BIBTEX_PREFIX + "Pubstate");
-        biblatexToMsBib.put("affiliation", BIBTEX_PREFIX + "Affiliation");
-        biblatexToMsBib.put("contents", BIBTEX_PREFIX + "Contents");
-        biblatexToMsBib.put("copyright", BIBTEX_PREFIX + "Copyright");
-        biblatexToMsBib.put("price", BIBTEX_PREFIX + "Price");
-        biblatexToMsBib.put("size", BIBTEX_PREFIX + "Size");
-        biblatexToMsBib.put("intype", BIBTEX_PREFIX + "InType");
-        biblatexToMsBib.put("paper", BIBTEX_PREFIX + "Paper");
-        biblatexToMsBib.put(FieldName.KEY, BIBTEX_PREFIX + "Key");
+        BIBLATEX_TO_MS_BIB.put(FieldName.SERIES, BIBTEX_PREFIX + "Series");
+        BIBLATEX_TO_MS_BIB.put(FieldName.ABSTRACT, BIBTEX_PREFIX + "Abstract");
+        BIBLATEX_TO_MS_BIB.put(FieldName.KEYWORDS, BIBTEX_PREFIX + "KeyWords");
+        BIBLATEX_TO_MS_BIB.put(FieldName.CROSSREF, BIBTEX_PREFIX + "CrossRef");
+        BIBLATEX_TO_MS_BIB.put(FieldName.HOWPUBLISHED, BIBTEX_PREFIX + "HowPublished");
+        BIBLATEX_TO_MS_BIB.put(FieldName.PUBSTATE, BIBTEX_PREFIX + "Pubstate");
+        BIBLATEX_TO_MS_BIB.put("affiliation", BIBTEX_PREFIX + "Affiliation");
+        BIBLATEX_TO_MS_BIB.put("contents", BIBTEX_PREFIX + "Contents");
+        BIBLATEX_TO_MS_BIB.put("copyright", BIBTEX_PREFIX + "Copyright");
+        BIBLATEX_TO_MS_BIB.put("price", BIBTEX_PREFIX + "Price");
+        BIBLATEX_TO_MS_BIB.put("size", BIBTEX_PREFIX + "Size");
+        BIBLATEX_TO_MS_BIB.put("intype", BIBTEX_PREFIX + "InType");
+        BIBLATEX_TO_MS_BIB.put("paper", BIBTEX_PREFIX + "Paper");
+        BIBLATEX_TO_MS_BIB.put(FieldName.KEY, BIBTEX_PREFIX + "Key");
 
         // MSBib only fields
-        biblatexToMsBib.put(MSBIB_PREFIX + "periodical", "PeriodicalTitle");
-        biblatexToMsBib.put(MSBIB_PREFIX + FieldName.DAY, "Day");
-        biblatexToMsBib.put(MSBIB_PREFIX + "accessed", "Accessed");
-        biblatexToMsBib.put(MSBIB_PREFIX + "medium", "Medium");
-        biblatexToMsBib.put(MSBIB_PREFIX + "recordingnumber", "RecordingNumber");
-        biblatexToMsBib.put(MSBIB_PREFIX + "theater", "Theater");
-        biblatexToMsBib.put(MSBIB_PREFIX + "distributor", "Distributor");
-        biblatexToMsBib.put(MSBIB_PREFIX + "broadcaster", "Broadcaster");
-        biblatexToMsBib.put(MSBIB_PREFIX + "station", "Station");
-        biblatexToMsBib.put(MSBIB_PREFIX + FieldName.TYPE, "Type");
-        biblatexToMsBib.put(MSBIB_PREFIX + "court", "Court");
-        biblatexToMsBib.put(MSBIB_PREFIX + "reporter", "Reporter");
-        biblatexToMsBib.put(MSBIB_PREFIX + "casenumber", "CaseNumber");
-        biblatexToMsBib.put(MSBIB_PREFIX + "abbreviatedcasenumber", "AbbreviatedCaseNumber");
-        biblatexToMsBib.put(MSBIB_PREFIX + "productioncompany", "ProductionCompany");
+        BIBLATEX_TO_MS_BIB.put(MSBIB_PREFIX + "periodical", "PeriodicalTitle");
+        BIBLATEX_TO_MS_BIB.put(MSBIB_PREFIX + FieldName.DAY, "Day");
+        BIBLATEX_TO_MS_BIB.put(MSBIB_PREFIX + "accessed", "Accessed");
+        BIBLATEX_TO_MS_BIB.put(MSBIB_PREFIX + "medium", "Medium");
+        BIBLATEX_TO_MS_BIB.put(MSBIB_PREFIX + "recordingnumber", "RecordingNumber");
+        BIBLATEX_TO_MS_BIB.put(MSBIB_PREFIX + "theater", "Theater");
+        BIBLATEX_TO_MS_BIB.put(MSBIB_PREFIX + "distributor", "Distributor");
+        BIBLATEX_TO_MS_BIB.put(MSBIB_PREFIX + "broadcaster", "Broadcaster");
+        BIBLATEX_TO_MS_BIB.put(MSBIB_PREFIX + "station", "Station");
+        BIBLATEX_TO_MS_BIB.put(MSBIB_PREFIX + FieldName.TYPE, "Type");
+        BIBLATEX_TO_MS_BIB.put(MSBIB_PREFIX + "court", "Court");
+        BIBLATEX_TO_MS_BIB.put(MSBIB_PREFIX + "reporter", "Reporter");
+        BIBLATEX_TO_MS_BIB.put(MSBIB_PREFIX + "casenumber", "CaseNumber");
+        BIBLATEX_TO_MS_BIB.put(MSBIB_PREFIX + "abbreviatedcasenumber", "AbbreviatedCaseNumber");
+        BIBLATEX_TO_MS_BIB.put(MSBIB_PREFIX + "productioncompany", "ProductionCompany");
     }
 
     private MSBibMapping() {
@@ -128,6 +125,7 @@ public static MSBibEntryType getMSBibEntryType(String bibtexType) {
     /**
      * Only English is supported 
* All LCID codes + * * @param language The language to transform * @return Returns 0 for English */ @@ -140,7 +138,7 @@ public static int getLCID(String language) { /** * Only English is supported
* All LCID codes - * @param language + * * @return Returns english */ public static String getLanguage(int LCID) { @@ -149,10 +147,10 @@ public static String getLanguage(int LCID) { } public static String getMSBibField(String bibtexFieldName) { - return biblatexToMsBib.get(bibtexFieldName); + return BIBLATEX_TO_MS_BIB.get(bibtexFieldName); } public static String getBibTeXField(String msbibFieldName) { - return biblatexToMsBib.inverse().get(msbibFieldName); + return BIBLATEX_TO_MS_BIB.inverse().get(msbibFieldName); } } diff --git a/src/main/java/org/jabref/logic/protectedterms/ProtectedTermsLoader.java b/src/main/java/org/jabref/logic/protectedterms/ProtectedTermsLoader.java index f5bf6ab3da3..f83ba552e81 100644 --- a/src/main/java/org/jabref/logic/protectedterms/ProtectedTermsLoader.java +++ b/src/main/java/org/jabref/logic/protectedterms/ProtectedTermsLoader.java @@ -19,17 +19,17 @@ public class ProtectedTermsLoader { - private static final Map internalLists = new HashMap<>(); + private static final Map INTERNAL_LISTS = new HashMap<>(); private static final Log LOGGER = LogFactory.getLog(ProtectedTermsLoader.class); private final List mainList = new ArrayList<>(); static { - internalLists.put("/protectedterms/months_weekdays.terms", Localization.lang("Months and weekdays in English")); - internalLists.put("/protectedterms/countries_territories.terms", + INTERNAL_LISTS.put("/protectedterms/months_weekdays.terms", Localization.lang("Months and weekdays in English")); + INTERNAL_LISTS.put("/protectedterms/countries_territories.terms", Localization.lang("Countries and territories in English")); - internalLists.put("/protectedterms/electrical_engineering.terms", + INTERNAL_LISTS.put("/protectedterms/electrical_engineering.terms", Localization.lang("Electrical engineering terms")); } @@ -38,7 +38,7 @@ public ProtectedTermsLoader(ProtectedTermsPreferences preferences) { } public static List getInternalLists() { - return new ArrayList<>(internalLists.keySet()); + return new ArrayList<>(INTERNAL_LISTS.keySet()); } public void update(ProtectedTermsPreferences preferences) { @@ -46,16 +46,16 @@ public void update(ProtectedTermsPreferences preferences) { // Read internal lists for (String filename : preferences.getEnabledInternalTermLists()) { - if (internalLists.containsKey(filename)) { - mainList.add(readProtectedTermsListFromResource(filename, internalLists.get(filename), true)); + if (INTERNAL_LISTS.containsKey(filename)) { + mainList.add(readProtectedTermsListFromResource(filename, INTERNAL_LISTS.get(filename), true)); } else { LOGGER.warn("Protected terms resource '" + filename + "' is no longer available."); } } for (String filename : preferences.getDisabledInternalTermLists()) { - if (internalLists.containsKey(filename)) { + if (INTERNAL_LISTS.containsKey(filename)) { if (!preferences.getEnabledInternalTermLists().contains(filename)) { - mainList.add(readProtectedTermsListFromResource(filename, internalLists.get(filename), false)); + mainList.add(readProtectedTermsListFromResource(filename, INTERNAL_LISTS.get(filename), false)); } } else { LOGGER.warn("Protected terms resource '" + filename + "' is no longer available."); @@ -63,11 +63,11 @@ public void update(ProtectedTermsPreferences preferences) { } // Check if any new internal lists have emerged - for (String filename : internalLists.keySet()) { + for (String filename : INTERNAL_LISTS.keySet()) { if (!preferences.getEnabledInternalTermLists().contains(filename) && !preferences.getDisabledInternalTermLists().contains(filename)) { // New internal list, add it - mainList.add(readProtectedTermsListFromResource(filename, internalLists.get(filename), true)); + mainList.add(readProtectedTermsListFromResource(filename, INTERNAL_LISTS.get(filename), true)); LOGGER.warn("New protected terms resource '" + filename + "' is available and enabled by default."); } } @@ -106,7 +106,6 @@ public void reloadProtectedTermsList(ProtectedTermsList list) { } catch (IOException e) { LOGGER.warn("Problem with protected terms file '" + list.getLocation() + "'", e); } - } public List getProtectedTermsLists() { diff --git a/src/main/java/org/jabref/logic/util/io/FileUtil.java b/src/main/java/org/jabref/logic/util/io/FileUtil.java index a98a40b47ee..4e01bdeb8f4 100644 --- a/src/main/java/org/jabref/logic/util/io/FileUtil.java +++ b/src/main/java/org/jabref/logic/util/io/FileUtil.java @@ -29,7 +29,7 @@ import org.apache.commons.logging.LogFactory; public class FileUtil { - public static final boolean isPosixCompilant = FileSystems.getDefault().supportedFileAttributeViews().contains("posix"); + public static final boolean IS_POSIX_COMPILANT = FileSystems.getDefault().supportedFileAttributeViews().contains("posix"); private static final Log LOGGER = LogFactory.getLog(FileUtil.class); private FileUtil() { @@ -51,7 +51,7 @@ public static String getFileName(String fileNameWithExtension) { * Adds an extension to the given file name. The original extension is not replaced. That means, * "demo.bib", ".sav" gets "demo.bib.sav" and not "demo.sav" * - * @param path the path to add the extension to + * @param path the path to add the extension to * @param extension the extension to add * @return the with the modified file name */ @@ -108,7 +108,6 @@ public static List uniquePathSubstrings(List paths) { * @param pathToDestinationFile Path Destination file * @param replaceExisting boolean Determines whether the copy goes on even if the file exists. * @return boolean Whether the copy succeeded, or was stopped due to the file already existing. - * @throws IOException */ public static boolean copyFile(Path pathToSourceFile, Path pathToDestinationFile, boolean replaceExisting) { // Check if the file already exists. @@ -142,11 +141,10 @@ public static boolean renameFile(Path fromFile, Path toFile) { /** * Renames a given file * - * @param fromFile The source filename to rename - * @param toFile The target fileName + * @param fromFile The source filename to rename + * @param toFile The target fileName * @param replaceExisting Wether to replace existing files or not * @return True if the rename was successful, false if an exception occurred - * */ public static boolean renameFile(Path fromFile, Path toFile, boolean replaceExisting) { try { @@ -188,9 +186,8 @@ public static Path shortenFileName(Path file, List dirs) { /** * Returns the list of linked files. The files have the absolute filename * - * @param bes list of BibTeX entries + * @param bes list of BibTeX entries * @param fileDirs list of directories to try for expansion - * * @return list of files. May be empty */ public static List getListOfLinkedFiles(List bes, List fileDirs) { @@ -213,7 +210,7 @@ public static List getListOfLinkedFiles(List bes, List fil * @return a suggested fileName */ public static String createFileNameFromPattern(BibDatabase database, BibEntry entry, String fileNamePattern, - LayoutFormatterPreferences prefs) { + LayoutFormatterPreferences prefs) { String targetName = null; StringReader sr = new StringReader(fileNamePattern); @@ -239,7 +236,7 @@ public static String createFileNameFromPattern(BibDatabase database, BibEntry en * Finds a file inside a directory structure. * Will also look for the file inside nested directories. * - * @param filename the name of the file that should be found + * @param filename the name of the file that should be found * @param rootDirectory the rootDirectory that will be searched * @return the path to the first file that matches the defined conditions */ @@ -259,7 +256,7 @@ public static Optional find(String filename, Path rootDirectory) { * Finds a file inside a list of directory structures. * Will also look for the file inside nested directories. * - * @param filename the name of the file that should be found + * @param filename the name of the file that should be found * @param directories the directories that will be searched * @return a list including all found paths to files that match the defined conditions */ diff --git a/src/main/java/org/jabref/migrations/PreferencesMigrations.java b/src/main/java/org/jabref/migrations/PreferencesMigrations.java index 70d8926306c..d5805f75ce6 100644 --- a/src/main/java/org/jabref/migrations/PreferencesMigrations.java +++ b/src/main/java/org/jabref/migrations/PreferencesMigrations.java @@ -1,7 +1,9 @@ package org.jabref.migrations; import java.util.HashMap; +import java.util.List; import java.util.Map; +import java.util.function.UnaryOperator; import java.util.prefs.BackingStoreException; import java.util.prefs.Preferences; @@ -33,7 +35,7 @@ public static void upgradePrefsToOrgJabRef() { // skip further processing as prefs already have been migrated LOGGER.debug("New prefs node already exists with content - skipping migration"); } else { - if ( mainPrefsNode.parent().parent().nodeExists("net/sf/jabref")) { + if (mainPrefsNode.parent().parent().nodeExists("net/sf/jabref")) { LOGGER.info("Migrating old preferences."); Preferences oldNode = mainPrefsNode.parent().parent().node("net/sf/jabref"); copyPrefsRecursively(oldNode, mainPrefsNode); @@ -138,7 +140,7 @@ public static void upgradeStoredCustomEntryTypes() { try { if (mainPrefsNode.nodeExists(JabRefPreferences.CUSTOMIZED_BIBTEX_TYPES) || - mainPrefsNode.nodeExists(JabRefPreferences.CUSTOMIZED_BIBLATEX_TYPES) ) { + mainPrefsNode.nodeExists(JabRefPreferences.CUSTOMIZED_BIBLATEX_TYPES)) { // skip further processing as prefs already have been migrated } else { LOGGER.info("Migrating old custom entry types."); @@ -188,13 +190,29 @@ public static void upgradeLabelPatternToBibtexKeyPattern() { } } + public static void upgradeKeyBindingsToJavaFX() { + UnaryOperator replaceKeys = (str) -> { + String result = str.replace("ctrl ", "ctrl+"); + result = result.replace("shift ", "shift+"); + result = result.replace("alt ", "alt+"); + result = result.replace("meta ", "meta+"); + + return result; + }; + + JabRefPreferences prefs = Globals.prefs; + List keys = prefs.getStringList(JabRefPreferences.BINDINGS); + keys.replaceAll(replaceKeys); + prefs.putStringList(JabRefPreferences.BINDINGS, keys); + + } + private static void migrateTypedKeyPrefs(JabRefPreferences prefs, Preferences oldPatternPrefs) throws BackingStoreException { LOGGER.info("Found old Bibtex Key patterns which will be migrated to new version."); GlobalBibtexKeyPattern keyPattern = GlobalBibtexKeyPattern.fromPattern( - prefs.get(JabRefPreferences.DEFAULT_BIBTEX_KEY_PATTERN) - ); + prefs.get(JabRefPreferences.DEFAULT_BIBTEX_KEY_PATTERN)); for (String key : oldPatternPrefs.keys()) { keyPattern.addBibtexKeyPattern(key, oldPatternPrefs.get(key, null)); } diff --git a/src/main/java/org/jabref/model/entry/AuthorList.java b/src/main/java/org/jabref/model/entry/AuthorList.java index dedf8c1a67e..629fca34501 100644 --- a/src/main/java/org/jabref/model/entry/AuthorList.java +++ b/src/main/java/org/jabref/model/entry/AuthorList.java @@ -122,7 +122,7 @@ public class AuthorList { private static final WeakHashMap AUTHOR_CACHE = new WeakHashMap<>(); // Avoid partition where these values are contained - private final static Collection avoidTermsInLowerCase = Arrays.asList("jr", "sr", "jnr", "snr", "von", "zu", "van", "der"); + private final static Collection AVOID_TERMS_IN_LOWER_CASE = Arrays.asList("jr", "sr", "jnr", "snr", "von", "zu", "van", "der"); private final List authors; private final String[] authorsFirstFirst = new String[4]; private final String[] authorsLastOnly = new String[2]; @@ -196,7 +196,7 @@ public static AuthorList parse(String authors) { Collection avoidIndex = new HashSet<>(); for (int i = 0; i < arrayNameList.size(); i++) { - if (avoidTermsInLowerCase.contains(arrayNameList.get(i).toLowerCase(Locale.ROOT))) { + if (AVOID_TERMS_IN_LOWER_CASE.contains(arrayNameList.get(i).toLowerCase(Locale.ROOT))) { avoidIndex.add(i); valuePartsCount--; } @@ -405,7 +405,8 @@ public String getAsNatbib() { * * @param oxfordComma Whether to put a comma before the and at the end. * @return formatted list of authors. - * @see serial comma for an detailed explaination about the Oxford comma. + * @see serial comma for an detailed explaination about the + * Oxford comma. */ public String getAsLastNames(boolean oxfordComma) { int abbrInt = oxfordComma ? 0 : 1; @@ -455,7 +456,8 @@ public String getAsLastNames(boolean oxfordComma) { * @param abbreviate whether to abbreivate first names. * @param oxfordComma Whether to put a comma before the and at the end. * @return formatted list of authors. - * @see serial comma for an detailed explaination about the Oxford comma. + * @see serial comma for an detailed explaination about the + * Oxford comma. */ public String getAsLastFirstNames(boolean abbreviate, boolean oxfordComma) { int abbrInt = abbreviate ? 0 : 1; @@ -556,7 +558,8 @@ public String getAsLastFirstFirstLastNamesWithAnd(boolean abbreviate) { * @param abbr whether to abbreivate first names. * @param oxfordComma Whether to put a comma before the and at the end. * @return formatted list of authors. - * @see serial comma for an detailed explaination about the Oxford comma. + * @see serial comma for an detailed explaination about the + * Oxford comma. */ public String getAsFirstLastNames(boolean abbr, boolean oxfordComma) { diff --git a/src/main/java/org/jabref/model/entry/BibEntry.java b/src/main/java/org/jabref/model/entry/BibEntry.java index b887754be81..1f5e900a5c4 100644 --- a/src/main/java/org/jabref/model/entry/BibEntry.java +++ b/src/main/java/org/jabref/model/entry/BibEntry.java @@ -605,7 +605,8 @@ public void setParsedSerialization(String parsedSerialization) { } public void setCommentsBeforeEntry(String parsedComments) { - this.commentsBeforeEntry = parsedComments; + // delete trailing whitespaces (between entry and text) + this.commentsBeforeEntry = REMOVE_TRAILING_WHITESPACE.matcher(parsedComments).replaceFirst(""); } public boolean hasChanged() { @@ -700,7 +701,9 @@ public boolean equals(Object o) { return false; } BibEntry entry = (BibEntry) o; - return Objects.equals(type, entry.type) && Objects.equals(fields, entry.fields); + return Objects.equals(type, entry.type) + && Objects.equals(fields, entry.fields) + && Objects.equals(commentsBeforeEntry, entry.commentsBeforeEntry); } @Override @@ -730,8 +733,7 @@ public BibEntry withField(String field, String value) { * Returns user comments (arbitrary text before the entry), if they exist. If not, returns the empty String */ public String getUserComments() { - // delete trailing whitespaces (between entry and text) from stored serialization - return REMOVE_TRAILING_WHITESPACE.matcher(commentsBeforeEntry).replaceFirst(""); + return commentsBeforeEntry; } public List getEntryLinkList(String fieldName, BibDatabase database) { diff --git a/src/main/java/org/jabref/model/entry/CanonicalBibtexEntry.java b/src/main/java/org/jabref/model/entry/CanonicalBibtexEntry.java index 04dbff71414..85fa98f469f 100644 --- a/src/main/java/org/jabref/model/entry/CanonicalBibtexEntry.java +++ b/src/main/java/org/jabref/model/entry/CanonicalBibtexEntry.java @@ -19,19 +19,21 @@ private CanonicalBibtexEntry() { * * Serializes all fields, even the JabRef internal ones. Does NOT serialize "KEY_FIELD" as field, but as key */ - public static String getCanonicalRepresentation(BibEntry e) { + public static String getCanonicalRepresentation(BibEntry entry) { StringBuilder sb = new StringBuilder(); + sb.append(entry.getUserComments()); + // generate first line: type and bibtex key - String citeKey = e.getCiteKeyOptional().orElse(""); - sb.append(String.format("@%s{%s,", e.getType().toLowerCase(Locale.US), citeKey)).append('\n'); + String citeKey = entry.getCiteKeyOptional().orElse(""); + sb.append(String.format("@%s{%s,", entry.getType().toLowerCase(Locale.US), citeKey)).append('\n'); // we have to introduce a new Map as fields are stored case-sensitive in JabRef (see https://github.com/koppor/jabref/issues/45). Map mapFieldToValue = new HashMap<>(); // determine sorted fields -- all fields lower case SortedSet sortedFields = new TreeSet<>(); - for (Entry field : e.getFieldMap().entrySet()) { + for (Entry field : entry.getFieldMap().entrySet()) { String fieldName = field.getKey(); String fieldValue = field.getValue(); // JabRef stores the key in the field KEY_FIELD, which must not be serialized diff --git a/src/main/java/org/jabref/model/entry/FieldName.java b/src/main/java/org/jabref/model/entry/FieldName.java index 78348cafcf5..54fd2a062d3 100644 --- a/src/main/java/org/jabref/model/entry/FieldName.java +++ b/src/main/java/org/jabref/model/entry/FieldName.java @@ -10,7 +10,6 @@ /** * String constants for BibTeX entry field names - * */ public class FieldName { // Character separating field names that are to be used in sequence as @@ -152,7 +151,7 @@ public class FieldName { public static final String MARKED_INTERNAL = "__markedentry"; // Map to hold alternative display names - private static final Map displayNames = new HashMap<>(); + private static final Map DISPLAY_NAMES = new HashMap<>(); private FieldName() { } @@ -166,15 +165,15 @@ public static String orFields(List fields) { } static { - displayNames.put(FieldName.DOI, "DOI"); - displayNames.put(FieldName.ISBN, "ISBN"); - displayNames.put(FieldName.ISRN, "ISRN"); - displayNames.put(FieldName.ISSN, "ISSN"); - displayNames.put(FieldName.PMID, "PMID"); - displayNames.put(FieldName.PS, "PS"); - displayNames.put(FieldName.PDF, "PDF"); - displayNames.put(FieldName.URI, "URI"); - displayNames.put(FieldName.URL, "URL"); + DISPLAY_NAMES.put(FieldName.DOI, "DOI"); + DISPLAY_NAMES.put(FieldName.ISBN, "ISBN"); + DISPLAY_NAMES.put(FieldName.ISRN, "ISRN"); + DISPLAY_NAMES.put(FieldName.ISSN, "ISSN"); + DISPLAY_NAMES.put(FieldName.PMID, "PMID"); + DISPLAY_NAMES.put(FieldName.PS, "PS"); + DISPLAY_NAMES.put(FieldName.PDF, "PDF"); + DISPLAY_NAMES.put(FieldName.URI, "URI"); + DISPLAY_NAMES.put(FieldName.URL, "URL"); } /** @@ -184,8 +183,8 @@ public static String orFields(List fields) { public static String getDisplayName(String field) { String lowercaseField = field.toLowerCase(Locale.ROOT); - if (displayNames.containsKey(lowercaseField)) { - return displayNames.get(lowercaseField); + if (DISPLAY_NAMES.containsKey(lowercaseField)) { + return DISPLAY_NAMES.get(lowercaseField); } return StringUtil.capitalizeFirst(field); } @@ -197,5 +196,4 @@ public static List getNotTextFieldNames() { public static List getIdentifierFieldNames() { return Arrays.asList(FieldName.DOI, FieldName.EPRINT, FieldName.PMID); } - } diff --git a/src/main/java/org/jabref/model/entry/IdGenerator.java b/src/main/java/org/jabref/model/entry/IdGenerator.java index 68a4260bcca..75ba5618c85 100644 --- a/src/main/java/org/jabref/model/entry/IdGenerator.java +++ b/src/main/java/org/jabref/model/entry/IdGenerator.java @@ -9,12 +9,12 @@ */ public class IdGenerator { - private static final NumberFormat idFormat; + private static final NumberFormat ID_FORMAT; static { - idFormat = NumberFormat.getInstance(); - IdGenerator.idFormat.setMinimumIntegerDigits(8); - IdGenerator.idFormat.setGroupingUsed(false); + ID_FORMAT = NumberFormat.getInstance(); + IdGenerator.ID_FORMAT.setMinimumIntegerDigits(8); + IdGenerator.ID_FORMAT.setGroupingUsed(false); } private static int idCounter; @@ -23,7 +23,7 @@ private IdGenerator() { } public static synchronized String next() { - String result = idFormat.format(idCounter); + String result = ID_FORMAT.format(idCounter); idCounter++; return result; } diff --git a/src/main/java/org/jabref/model/entry/event/FieldChangedEvent.java b/src/main/java/org/jabref/model/entry/event/FieldChangedEvent.java index d92d76f262b..56b3b6f106a 100644 --- a/src/main/java/org/jabref/model/entry/event/FieldChangedEvent.java +++ b/src/main/java/org/jabref/model/entry/event/FieldChangedEvent.java @@ -11,52 +11,68 @@ public class FieldChangedEvent extends EntryChangedEvent { private final String fieldName; private final String newValue; private final String oldValue; + private int delta = 0; /** - * @param bibEntry Affected BibEntry object + * @param bibEntry Affected BibEntry object * @param fieldName Name of field which has been changed - * @param newValue new field value - * @param newValue old field value - * @param location location Location affected by this event + * @param newValue new field value + * @param newValue old field value + * @param location location Location affected by this event */ public FieldChangedEvent(BibEntry bibEntry, String fieldName, String newValue, String oldValue, - EntryEventSource location) { + EntryEventSource location) { super(bibEntry, location); this.fieldName = fieldName; this.newValue = newValue; this.oldValue = oldValue; + delta = computeDelta(oldValue, newValue); } /** - * @param bibEntry Affected BibEntry object + * @param bibEntry Affected BibEntry object * @param fieldName Name of field which has been changed - * @param newValue new field value + * @param newValue new field value */ public FieldChangedEvent(BibEntry bibEntry, String fieldName, String newValue, String oldValue) { super(bibEntry); this.fieldName = fieldName; this.newValue = newValue; this.oldValue = oldValue; + delta = computeDelta(oldValue, newValue); } /** - * @param bibEntry Affected BibEntry object + * @param bibEntry Affected BibEntry object * @param fieldName Name of field which has been changed - * @param newValue new field value - * @param location location Location affected by this event + * @param newValue new field value + * @param location location Location affected by this event */ public FieldChangedEvent(FieldChange fieldChange, EntryEventSource location) { super(fieldChange.getEntry(), location); this.fieldName = fieldChange.getField(); this.newValue = fieldChange.getNewValue(); this.oldValue = fieldChange.getOldValue(); + delta = computeDelta(oldValue, newValue); } public FieldChangedEvent(FieldChange fieldChange) { this(fieldChange, EntryEventSource.LOCAL); } + private int computeDelta(String oldValue, String newValue) { + if (oldValue == newValue) { + return 0; + } else if (oldValue == null && newValue != null) { + return newValue.length(); + } else if (newValue == null && oldValue != null) { + return oldValue.length(); + } else { + return Math.abs(newValue.length() - oldValue.length()); + } + } + public String getFieldName() { return fieldName; } @@ -69,4 +85,8 @@ public String getOldValue() { return oldValue; } + public int getDelta() { + return delta; + } + } diff --git a/src/main/java/org/jabref/model/entry/identifier/MathSciNetId.java b/src/main/java/org/jabref/model/entry/identifier/MathSciNetId.java index b85c500347d..255c701978b 100644 --- a/src/main/java/org/jabref/model/entry/identifier/MathSciNetId.java +++ b/src/main/java/org/jabref/model/entry/identifier/MathSciNetId.java @@ -21,7 +21,21 @@ public MathSciNetId(String identifier) { public static Optional parse(String mrNumberRaw) { // Take everything before whitespace or open bracket, so something like `619693 (82j:58046)` gets parsed correctly - return Optional.of(new MathSciNetId(StringUtil.tokenizeToList(mrNumberRaw, " (").get(0))); + String identifier = StringUtil.tokenizeToList(mrNumberRaw, " (").get(0).trim(); + return Optional.of(new MathSciNetId(identifier)); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + MathSciNetId that = (MathSciNetId) o; + return Objects.equals(identifier, that.identifier); + } + + @Override + public int hashCode() { + return Objects.hash(identifier); } @Override diff --git a/src/main/java/org/jabref/preferences/JabRefPreferences.java b/src/main/java/org/jabref/preferences/JabRefPreferences.java index 4ffcccd55cd..dcc3ba1326d 100644 --- a/src/main/java/org/jabref/preferences/JabRefPreferences.java +++ b/src/main/java/org/jabref/preferences/JabRefPreferences.java @@ -36,11 +36,12 @@ import org.jabref.JabRefException; import org.jabref.JabRefMain; +import org.jabref.gui.autocompleter.AutoCompleteFirstNameMode; +import org.jabref.gui.autocompleter.AutoCompletePreferences; import org.jabref.gui.desktop.JabRefDesktop; import org.jabref.gui.entryeditor.EntryEditorTabList; import org.jabref.gui.keyboard.KeyBindingRepository; import org.jabref.gui.preftabs.ImportSettingsTab; -import org.jabref.logic.autocompleter.AutoCompletePreferences; import org.jabref.logic.bibtex.FieldContentParserPreferences; import org.jabref.logic.bibtex.LatexFieldFormatterPreferences; import org.jabref.logic.bibtexkeypattern.BibtexKeyPatternPreferences; @@ -180,7 +181,6 @@ public class JabRefPreferences implements PreferencesService { public static final String PREFS_EXPORT_PATH = "prefsExportPath"; public static final String WORKING_DIRECTORY = "workingDirectory"; public static final String NUMBER_COL_WIDTH = "numberColWidth"; - public static final String AUTO_COMPLETE = "autoComplete"; public static final String EDITOR_EMACS_KEYBINDINGS = "editorEMACSkeyBindings"; public static final String EDITOR_EMACS_KEYBINDINGS_REBIND_CA = "editorEMACSkeyBindingsRebindCA"; public static final String EDITOR_EMACS_KEYBINDINGS_REBIND_CF = "editorEMACSkeyBindingsRebindCF"; @@ -361,16 +361,18 @@ public class JabRefPreferences implements PreferencesService { // Prefs node for customized entry types public static final String CUSTOMIZED_BIBTEX_TYPES = "customizedBibtexTypes"; public static final String CUSTOMIZED_BIBLATEX_TYPES = "customizedBiblatexTypes"; - // Version public static final String VERSION_IGNORED_UPDATE = "versionIgnoreUpdate"; + //KeyBindings - keys - public because needed for pref migration + public static final String BINDINGS = "bindings"; + + private static final String BIND_NAMES = "bindNames"; // User private static final String USER_ID = "userId"; private static final String EXTERNAL_JOURNAL_LISTS = "externalJournalLists"; private static final String PERSONAL_JOURNAL_LIST = "personalJournalList"; private static final String USE_IEEE_ABRV = "useIEEEAbrv"; - private static final String BINDINGS = "bindings"; - private static final String BIND_NAMES = "bindNames"; + // Telemetry collection private static final String COLLECT_TELEMETRY = "collectTelemetry"; private static final String ALREADY_ASKED_TO_COLLECT_TELEMETRY = "askedCollectTelemetry"; @@ -390,6 +392,13 @@ public class JabRefPreferences implements PreferencesService { private static final String PREVIEW_PANEL_HEIGHT = "previewPanelHeight"; private static final String PREVIEW_STYLE = "previewStyle"; private static final String PREVIEW_ENABLED = "previewEnabled"; + // Auto completion + private static final String AUTO_COMPLETE = "autoComplete"; + private static final String AUTOCOMPLETER_FIRSTNAME_MODE = "autoCompFirstNameMode"; + private static final String AUTOCOMPLETER_LAST_FIRST = "autoCompLF"; + private static final String AUTOCOMPLETER_FIRST_LAST = "autoCompFF"; + private static final String AUTOCOMPLETER_COMPLETE_FIELDS = "autoCompleteFields"; + // Helper string private static final String USER_HOME = System.getProperty("user.home"); // solves the issue java.lang.RuntimeException: Internal graphics not initialized yet @@ -564,8 +573,11 @@ private JabRefPreferences() { defaults.put(EDITOR_EMACS_KEYBINDINGS, Boolean.FALSE); defaults.put(EDITOR_EMACS_KEYBINDINGS_REBIND_CA, Boolean.TRUE); defaults.put(EDITOR_EMACS_KEYBINDINGS_REBIND_CF, Boolean.TRUE); - defaults.put(AUTO_COMPLETE, Boolean.FALSE); - AutoCompletePreferences.putDefaults(defaults); + defaults.put(AUTO_COMPLETE, Boolean.TRUE); + defaults.put(AUTOCOMPLETER_FIRSTNAME_MODE, AutoCompleteFirstNameMode.BOTH.name()); + defaults.put(AUTOCOMPLETER_FIRST_LAST, Boolean.FALSE); // "Autocomplete names in 'Firstname Lastname' format only" + defaults.put(AUTOCOMPLETER_LAST_FIRST, Boolean.FALSE); // "Autocomplete names in 'Lastname, Firstname' format only" + defaults.put(AUTOCOMPLETER_COMPLETE_FIELDS, "author;editor;title;journal;publisher;keywords"); defaults.put(GROUP_INTERSECT_SELECTIONS, Boolean.FALSE); defaults.put(GROUPS_DEFAULT_FIELD, FieldName.KEYWORDS); defaults.put(AUTO_ASSIGN_GROUP, Boolean.TRUE); @@ -751,7 +763,6 @@ private JabRefPreferences() { defaults.put(USE_CASE_KEEPER_ON_SEARCH, Boolean.TRUE); defaults.put(USE_UNIT_FORMATTER_ON_SEARCH, Boolean.TRUE); - defaults.put(USE_DEFAULT_CONSOLE_APPLICATION, Boolean.TRUE); if (OS.WINDOWS) { defaults.put(CONSOLE_COMMAND, "C:\\Program Files\\ConEmu\\ConEmu64.exe /single /dir \"%DIR\""); @@ -1220,7 +1231,7 @@ public List loadCustomEntryTypes(BibDatabaseMode bibDatabaseMod } private void clearAllCustomEntryTypes() throws BackingStoreException { - for (BibDatabaseMode mode :BibDatabaseMode.values()) { + for (BibDatabaseMode mode : BibDatabaseMode.values()) { clearCustomEntryTypes(mode); } } @@ -1597,7 +1608,24 @@ public AutoLinkPreferences getAutoLinkPreferences() { getBoolean(JabRefPreferences.AUTOLINK_USE_REG_EXP_SEARCH_KEY), get(JabRefPreferences.AUTOLINK_REG_EXP_SEARCH_EXPRESSION_KEY), getBoolean(JabRefPreferences.AUTOLINK_EXACT_KEY_ONLY), - getKeywordDelimiter() - ); + getKeywordDelimiter()); + } + + public AutoCompletePreferences getAutoCompletePreferences() { + return new AutoCompletePreferences( + getBoolean(AUTO_COMPLETE), + AutoCompleteFirstNameMode.parse(get(AUTOCOMPLETER_FIRSTNAME_MODE)), + getBoolean(AUTOCOMPLETER_LAST_FIRST), + getBoolean(AUTOCOMPLETER_FIRST_LAST), + getStringList(AUTOCOMPLETER_COMPLETE_FIELDS), + getJournalAbbreviationPreferences()); + } + + public void storeAutoCompletePreferences(AutoCompletePreferences autoCompletePreferences) { + putBoolean(AUTO_COMPLETE, autoCompletePreferences.shouldAutoComplete()); + put(AUTOCOMPLETER_FIRSTNAME_MODE, autoCompletePreferences.getFirstNameMode().name()); + putBoolean(AUTOCOMPLETER_LAST_FIRST, autoCompletePreferences.getOnlyCompleteLastFirst()); + putBoolean(AUTOCOMPLETER_FIRST_LAST, autoCompletePreferences.getOnlyCompleteFirstLast()); + putStringList(AUTOCOMPLETER_COMPLETE_FIELDS, autoCompletePreferences.getCompleteNames()); } } diff --git a/src/main/resources/l10n/JabRef_da.properties b/src/main/resources/l10n/JabRef_da.properties index da661c9bbca..c2dc6e20cf0 100644 --- a/src/main/resources/l10n/JabRef_da.properties +++ b/src/main/resources/l10n/JabRef_da.properties @@ -1,3 +1,4 @@ + #! #! created/edited by Popeye version 0.55 (github.com/JabRef/popeye) #! encoding:UTF-8 @@ -708,8 +709,6 @@ Main_file_directory=Hovedbibliotek Main_layout_file=Hoved-layoutfil -Manage=Opsæt - Manage_custom_exports=Opsæt_eksterne_eksportfiltre Manage_custom_imports=Opsæt_eksterne_importfiltre @@ -1554,7 +1553,6 @@ Use_full_firstname_whenever_possible= Use_abbreviated_firstname_whenever_possible= Use_abbreviated_and_full_firstname= Autocompletion_options= -Autocomplete_after_following_number_of_characters= Name_format_used_for_autocompletion= Treatment_of_first_names= Cleanup_entries= @@ -1832,8 +1830,6 @@ Search_results_in_library_%0_for_%1= Search_globally= No_results_found.= Found_%0_results.= -Advanced_search_active.= -Normal_search_active.= plain_text= This_search_contains_entries_in_which_any_field_contains_the_regular_expression_%0= This_search_contains_entries_in_which_any_field_contains_the_term_%0= diff --git a/src/main/resources/l10n/JabRef_de.properties b/src/main/resources/l10n/JabRef_de.properties index 8d44784a4b2..afea570486a 100644 --- a/src/main/resources/l10n/JabRef_de.properties +++ b/src/main/resources/l10n/JabRef_de.properties @@ -1,3 +1,4 @@ + #! #! created/edited by Popeye version 0.55 (github.com/JabRef/popeye) #! encoding:UTF-8 @@ -708,8 +709,6 @@ Main_file_directory=Standard-Verzeichnis_für_Dateien Main_layout_file=Haupt-Layoutdatei -Manage=Verwalten - Manage_custom_exports=Verwalte_externe_Exportfilter Manage_custom_imports=Verwalte_externe_Importfilter @@ -1554,7 +1553,6 @@ Use_full_firstname_whenever_possible=Den_ganzen_Vornamen_nutzen,_wenn_möglich Use_abbreviated_firstname_whenever_possible=Den_abgekürzten_Vornamen_benutzen,_wenn_möglich Use_abbreviated_and_full_firstname=Abgekürzte_und_ganze_Vornamen_verwenden Autocompletion_options=Autovervollständigungs-Optionen -Autocomplete_after_following_number_of_characters=Autovervollständigung_nach_der_folgenden_Anzahl_an_Zeichen Name_format_used_for_autocompletion=Namensformat_für_die_Autovervollständigung Treatment_of_first_names=Behandlung_von_Vornamen Cleanup_entries=Einträge_aufräumen @@ -1832,8 +1830,6 @@ Search_results_in_library_%0_for_%1=Suchergebnisse_in_Bibliothek_%0_für_%1 Search_globally=Global_suchen No_results_found.=Keine_Ergebnisse_gefunden. Found_%0_results.=%0_Ergebnisse_gefunden. -Advanced_search_active.=Erweiterte_Suche_aktiv. -Normal_search_active.=Normale_Suche_aktiv. plain_text=Klartext This_search_contains_entries_in_which_any_field_contains_the_regular_expression_%0=Diese_Suche_enthält_Einträge,_die_in_einem_beliebigen_Feld_den_regulären_Ausdruck_%0_enthalten This_search_contains_entries_in_which_any_field_contains_the_term_%0=Diese_Suche_enthält_Einträge,_die_in_einem_beliebigen_Feld_den_Begriff_%0_enthalten diff --git a/src/main/resources/l10n/JabRef_el.properties b/src/main/resources/l10n/JabRef_el.properties index 6375c68f8d5..bf61e498e18 100644 --- a/src/main/resources/l10n/JabRef_el.properties +++ b/src/main/resources/l10n/JabRef_el.properties @@ -1,3 +1,4 @@ + #! #! created/edited by Popeye version 0.55 (github.com/JabRef/popeye) #! encoding:UTF-8 @@ -708,8 +709,6 @@ Main_file_directory=Main_file_directory Main_layout_file=Main_layout_file -Manage=Manage - Manage_custom_exports=Manage_custom_exports Manage_custom_imports=Manage_custom_imports @@ -1554,7 +1553,6 @@ Use_full_firstname_whenever_possible=Use_full_firstname_whenever_possible Use_abbreviated_firstname_whenever_possible=Use_abbreviated_firstname_whenever_possible Use_abbreviated_and_full_firstname=Use_abbreviated_and_full_firstname Autocompletion_options=Autocompletion_options -Autocomplete_after_following_number_of_characters=Autocomplete_after_following_number_of_characters Name_format_used_for_autocompletion=Name_format_used_for_autocompletion Treatment_of_first_names=Treatment_of_first_names Cleanup_entries=Cleanup_entries @@ -1832,8 +1830,6 @@ Search_results_in_library_%0_for_%1= Search_globally=Search_globally No_results_found.=No_results_found. Found_%0_results.=Found_%0_results. -Advanced_search_active.=Advanced_search_active. -Normal_search_active.=Normal_search_active. plain_text=plain_text This_search_contains_entries_in_which_any_field_contains_the_regular_expression_%0=This_search_contains_entries_in_which_any_field_contains_the_regular_expression_%0 This_search_contains_entries_in_which_any_field_contains_the_term_%0=This_search_contains_entries_in_which_any_field_contains_the_term_%0 diff --git a/src/main/resources/l10n/JabRef_en.properties b/src/main/resources/l10n/JabRef_en.properties index e56631fb3f0..b75a4e5343a 100644 --- a/src/main/resources/l10n/JabRef_en.properties +++ b/src/main/resources/l10n/JabRef_en.properties @@ -1,3 +1,4 @@ + #! #! created/edited by Popeye version 0.55 (github.com/JabRef/popeye) #! encoding:UTF-8 @@ -708,8 +709,6 @@ Main_file_directory=Main_file_directory Main_layout_file=Main_layout_file -Manage=Manage - Manage_custom_exports=Manage_custom_exports Manage_custom_imports=Manage_custom_imports @@ -1554,7 +1553,6 @@ Use_full_firstname_whenever_possible=Use_full_firstname_whenever_possible Use_abbreviated_firstname_whenever_possible=Use_abbreviated_firstname_whenever_possible Use_abbreviated_and_full_firstname=Use_abbreviated_and_full_firstname Autocompletion_options=Autocompletion_options -Autocomplete_after_following_number_of_characters=Autocomplete_after_following_number_of_characters Name_format_used_for_autocompletion=Name_format_used_for_autocompletion Treatment_of_first_names=Treatment_of_first_names Cleanup_entries=Cleanup_entries @@ -1832,8 +1830,6 @@ Search_results_in_library_%0_for_%1=Search_results_in_library_%0_for_%1 Search_globally=Search_globally No_results_found.=No_results_found. Found_%0_results.=Found_%0_results. -Advanced_search_active.=Advanced_search_active. -Normal_search_active.=Normal_search_active. plain_text=plain_text This_search_contains_entries_in_which_any_field_contains_the_regular_expression_%0=This_search_contains_entries_in_which_any_field_contains_the_regular_expression_%0 This_search_contains_entries_in_which_any_field_contains_the_term_%0=This_search_contains_entries_in_which_any_field_contains_the_term_%0 diff --git a/src/main/resources/l10n/JabRef_es.properties b/src/main/resources/l10n/JabRef_es.properties index 4fc710e9cb4..9ec3c4d6036 100644 --- a/src/main/resources/l10n/JabRef_es.properties +++ b/src/main/resources/l10n/JabRef_es.properties @@ -1,3 +1,4 @@ + #! #! created/edited by Popeye version 0.55 (github.com/JabRef/popeye) #! encoding:UTF-8 @@ -708,8 +709,6 @@ Main_file_directory=Carpeta_del_archivo_principal Main_layout_file=Archivo_de_configuración_principal -Manage=Administrar - Manage_custom_exports=Administrar_esportaciones_personalizadas Manage_custom_imports=Administrar_importaciones_personalizadas @@ -1554,7 +1553,6 @@ Use_full_firstname_whenever_possible=Usar_nombre_de_pila_completo_cuando_sea_pos Use_abbreviated_firstname_whenever_possible=Usar_nombre_de_pila_abreviado_cuando_sea_posible Use_abbreviated_and_full_firstname=Usar_apellido_completo_y_abreviado Autocompletion_options=Opciones_de_autocompletar -Autocomplete_after_following_number_of_characters=Autocompletar_después_del_siguiente_número_de_caracteres Name_format_used_for_autocompletion=Formato_de_nombre_usado_para_autocompletar Treatment_of_first_names=Tratamiento_de_nombres_de_pila Cleanup_entries=Limpiar_entradas @@ -1832,8 +1830,6 @@ Search_results_in_library_%0_for_%1=Resultados_para_%1_en_la_biblioteca_&0 Search_globally=Buscar_globalmente. No_results_found.=No_se_encontraron_resultados. Found_%0_results.=Se_encontraron_%0_resultados. -Advanced_search_active.=Búsqueda_avanzada_activa. -Normal_search_active.=Búsqueda_normal_activa. plain_text=texto_plano This_search_contains_entries_in_which_any_field_contains_the_regular_expression_%0=Esta_búsqueda_contiene_entradas_en_las_cuales_cualquier_campo_contiene_la_expresión_regular_%0 This_search_contains_entries_in_which_any_field_contains_the_term_%0=Esta_búsqueda_contiene_entradas_en_las_cuales_cualquier_campo_contiene_el_término_%0 diff --git a/src/main/resources/l10n/JabRef_fa.properties b/src/main/resources/l10n/JabRef_fa.properties index 15ad66d0c45..0e2f434e9ad 100644 --- a/src/main/resources/l10n/JabRef_fa.properties +++ b/src/main/resources/l10n/JabRef_fa.properties @@ -1,3 +1,4 @@ + #! #! created/edited by Popeye version 0.55 (github.com/JabRef/popeye) #! encoding:UTF-8 @@ -708,8 +709,6 @@ Main_file_directory= Main_layout_file= -Manage= - Manage_custom_exports= Manage_custom_imports= @@ -1554,7 +1553,6 @@ Use_full_firstname_whenever_possible= Use_abbreviated_firstname_whenever_possible= Use_abbreviated_and_full_firstname= Autocompletion_options= -Autocomplete_after_following_number_of_characters= Name_format_used_for_autocompletion= Treatment_of_first_names= Cleanup_entries= @@ -1832,8 +1830,6 @@ Search_results_in_library_%0_for_%1= Search_globally= No_results_found.= Found_%0_results.= -Advanced_search_active.= -Normal_search_active.= plain_text= This_search_contains_entries_in_which_any_field_contains_the_regular_expression_%0= This_search_contains_entries_in_which_any_field_contains_the_term_%0= diff --git a/src/main/resources/l10n/JabRef_fr.properties b/src/main/resources/l10n/JabRef_fr.properties index 844ccef84e1..4000cfd9c54 100644 --- a/src/main/resources/l10n/JabRef_fr.properties +++ b/src/main/resources/l10n/JabRef_fr.properties @@ -1,3 +1,4 @@ + #! #! created/edited by Popeye version 0.55 (github.com/JabRef/popeye) #! encoding:UTF-8 @@ -708,8 +709,6 @@ Main_file_directory=Répertoire_de_fichiers_principal Main_layout_file=Principal_fichier_de_mise_en_page -Manage=Gérer - Manage_custom_exports=Gérer_les_exportations_personnalisées Manage_custom_imports=Gérer_les_importations_personnalisées @@ -1554,7 +1553,6 @@ Use_full_firstname_whenever_possible=Utiliser_le_prénom_en_entier_quand_c'est_p Use_abbreviated_firstname_whenever_possible=Utiliser_le_prénom_abrégé_quand_c'est_possible Use_abbreviated_and_full_firstname=Utiliser_le_prénom_abrégé_et_entier Autocompletion_options=Options_de_la_complétion_automatique -Autocomplete_after_following_number_of_characters=Complétion_automatique_après_un_nombre_de_caractères_égal_à Name_format_used_for_autocompletion=Format_de_nom_utilisé_pour_la_complétion_automatique Treatment_of_first_names=Traitement_des_prénoms Cleanup_entries=Nettoyage_des_entrées @@ -1832,8 +1830,6 @@ Search_results_in_library_%0_for_%1=Résultats_de_recherche_dans_le_fichier_%0_p Search_globally=Rechercher_globalement No_results_found.=Aucun_résultat_trouvé. Found_%0_results.=%0_résultats_trouvés. -Advanced_search_active.=Recherche_avancée_active. -Normal_search_active.=Recherche_normale_active. plain_text=texte_brut This_search_contains_entries_in_which_any_field_contains_the_regular_expression_%0=Cette_recherche_contient_des_entrées_pour_lesquelles_au_moins_un_champ_contient_l'expression_régulière_%0 This_search_contains_entries_in_which_any_field_contains_the_term_%0=Cette_recherche_contient_des_entrées_pour_lesquelles_au_moins_un_champ_contient_le_terme_%0 diff --git a/src/main/resources/l10n/JabRef_in.properties b/src/main/resources/l10n/JabRef_in.properties index c52556b7503..46dc97a4a4c 100644 --- a/src/main/resources/l10n/JabRef_in.properties +++ b/src/main/resources/l10n/JabRef_in.properties @@ -1,3 +1,4 @@ + #! #! created/edited by Popeye version 0.55 (github.com/JabRef/popeye) #! encoding:UTF-8 @@ -708,8 +709,6 @@ Main_file_directory=Lokasi_berkas_utama Main_layout_file=Berkas_tataletak_utama -Manage=Mengatur - Manage_custom_exports=Mengatur_ekspor_atursendiri Manage_custom_imports=Mengatur_impor_atursendiri @@ -1554,7 +1553,6 @@ Use_full_firstname_whenever_possible=Gunakan_nama_kecil_penuh_kalau_mungkin Use_abbreviated_firstname_whenever_possible=Gunakan_singkatan_nama_kecil_kalau_mungkin Use_abbreviated_and_full_firstname=Gunakan_nama_kecil_yang_penuh_dan_disingkatkan Autocompletion_options=Opsi_pelengkapian_otomatis -Autocomplete_after_following_number_of_characters=Melengkapi_otomatis_setelah_jumlah_karakter_berikut Name_format_used_for_autocompletion=Format_nama_yang_digunakan_untuk_pelengkapan_otomatis Treatment_of_first_names= Cleanup_entries=Bersihkan_entri @@ -1832,8 +1830,6 @@ Search_results_in_library_%0_for_%1=Hasil_pencarian_untuk_%0_dalam_basisdata_%1 Search_globally=Cari_secara_global No_results_found.=Tidak_menemukan_hasil_pencarian. Found_%0_results.=Menemukan_%0_hasil_pencarian. -Advanced_search_active.=Pencarian_lanjut_aktif. -Normal_search_active.=Pencarian_biasa_aktif. plain_text=teks_biasa This_search_contains_entries_in_which_any_field_contains_the_regular_expression_%0= This_search_contains_entries_in_which_any_field_contains_the_term_%0= diff --git a/src/main/resources/l10n/JabRef_it.properties b/src/main/resources/l10n/JabRef_it.properties index 9d076421dc2..6481b70a219 100644 --- a/src/main/resources/l10n/JabRef_it.properties +++ b/src/main/resources/l10n/JabRef_it.properties @@ -1,3 +1,4 @@ + #! #! created/edited by Popeye version 0.55 (github.com/JabRef/popeye) #! encoding:UTF-8 @@ -708,8 +709,6 @@ Main_file_directory=Cartella_dei_file_principale Main_layout_file=File_di_layout_principale -Manage=Gestione - Manage_custom_exports=Gestione_delle_esportazioni_personalizzate Manage_custom_imports=Gestione_delle_importazioni_personalizzate @@ -1554,7 +1553,6 @@ Use_full_firstname_whenever_possible=Usa_nome_completo_quando_possibile Use_abbreviated_firstname_whenever_possible=Usa_nome_abbreviato_quando_possibile Use_abbreviated_and_full_firstname=Usa_nome_abbreviato_e_completo Autocompletion_options=Opzioni_di_autocompletamento -Autocomplete_after_following_number_of_characters=Attiva_autocompletamento_dopo_il_seguente_numero_di_caratteri Name_format_used_for_autocompletion=Formato_dei_nomi_usato_per_l'autocompletamento Treatment_of_first_names=Gestione_dei_nomi Cleanup_entries=Ripulisci_voci @@ -1832,8 +1830,6 @@ Search_results_in_library_%0_for_%1=Risultati_della_ricerca_di_%1_nella_libreria Search_globally=Ricerca_globale No_results_found.=Nessun_risultato_trovato. Found_%0_results.=Trovati_%0_risultati. -Advanced_search_active.=Ricerca_avanzata_attiva. -Normal_search_active.=Ricerca_normale_attiva. plain_text=Testo_semplice This_search_contains_entries_in_which_any_field_contains_the_regular_expression_%0=Questa_ricerca_contiene_occorrenze_in_cui_qualsiasi_campo_contiene_l'espressione_regolare_%0 This_search_contains_entries_in_which_any_field_contains_the_term_%0=Questa_ricerca_contiene_occorrenze_in_cui_qualsiasi_campo_contiene_il_termine_%0 diff --git a/src/main/resources/l10n/JabRef_ja.properties b/src/main/resources/l10n/JabRef_ja.properties index e2f1df70300..b5df294541c 100644 --- a/src/main/resources/l10n/JabRef_ja.properties +++ b/src/main/resources/l10n/JabRef_ja.properties @@ -1,3 +1,4 @@ + #! #! created/edited by Popeye version 0.55 (github.com/JabRef/popeye) #! encoding:UTF-8 @@ -708,8 +709,6 @@ Main_file_directory=基本ファイルディレクトリ Main_layout_file=基本レイアウトファイル -Manage=管理 - Manage_custom_exports=ユーザー書出の管理 Manage_custom_imports=ユーザー読込の管理 @@ -1554,7 +1553,6 @@ Use_full_firstname_whenever_possible=可能な場合は常に完全なファー Use_abbreviated_firstname_whenever_possible=可能な場合は常に短縮したファーストネームを使用 Use_abbreviated_and_full_firstname=短縮したファーストネームと完全なファーストネームを両方使用 Autocompletion_options=自動補完オプション -Autocomplete_after_following_number_of_characters=右記の文字数以上で自動補完 Name_format_used_for_autocompletion=自動補完に使用される氏名の書式 Treatment_of_first_names=名(first_name)の取り扱い Cleanup_entries=項目の整理 @@ -1832,8 +1830,6 @@ Search_results_in_library_%0_for_%1=%0をデータベース%1で検索した結 Search_globally=大域検索 No_results_found.=検出されませんでした。 Found_%0_results.=%0件検出しました。 -Advanced_search_active.=詳細検索が進行中です。 -Normal_search_active.=通常検索が進行中です。 plain_text=平文 This_search_contains_entries_in_which_any_field_contains_the_regular_expression_%0=この検索結果は、フィールドのうちどれかに正規表現%0が含まれている項目を表示します。 This_search_contains_entries_in_which_any_field_contains_the_term_%0=この検索結果は、フィールドのうちどれかに用語%0が含まれている項目を表示します。 diff --git a/src/main/resources/l10n/JabRef_nl.properties b/src/main/resources/l10n/JabRef_nl.properties index d6792c9a5b0..8e95f177b9d 100644 --- a/src/main/resources/l10n/JabRef_nl.properties +++ b/src/main/resources/l10n/JabRef_nl.properties @@ -1,3 +1,4 @@ + #! #! created/edited by Popeye version 0.55 (github.com/JabRef/popeye) #! encoding:UTF-8 @@ -708,8 +709,6 @@ Main_file_directory= Main_layout_file=Hoofd_layoutbestand -Manage=Beheren - Manage_custom_exports=Beheer_externe_exportfilters Manage_custom_imports=Beheer_externe_importfilters @@ -1554,7 +1553,6 @@ Use_full_firstname_whenever_possible= Use_abbreviated_firstname_whenever_possible= Use_abbreviated_and_full_firstname= Autocompletion_options= -Autocomplete_after_following_number_of_characters= Name_format_used_for_autocompletion= Treatment_of_first_names= Cleanup_entries= @@ -1832,8 +1830,6 @@ Search_results_in_library_%0_for_%1= Search_globally= No_results_found.= Found_%0_results.= -Advanced_search_active.= -Normal_search_active.= plain_text= This_search_contains_entries_in_which_any_field_contains_the_regular_expression_%0= This_search_contains_entries_in_which_any_field_contains_the_term_%0= diff --git a/src/main/resources/l10n/JabRef_no.properties b/src/main/resources/l10n/JabRef_no.properties index 9bd96d50753..288f5992dc6 100644 --- a/src/main/resources/l10n/JabRef_no.properties +++ b/src/main/resources/l10n/JabRef_no.properties @@ -1,3 +1,4 @@ + #! #! created/edited by Popeye version 0.55 (github.com/JabRef/popeye) #! encoding:UTF-8 @@ -708,8 +709,6 @@ Main_file_directory=Hovedkatalog_for_filer Main_layout_file=Hoved-layoutfil -Manage=Sett_opp - Manage_custom_exports=Sett_opp_eksterne_eksportfiltre Manage_custom_imports=Sett_opp_eksterne_importfiltre @@ -1554,7 +1553,6 @@ Use_full_firstname_whenever_possible= Use_abbreviated_firstname_whenever_possible= Use_abbreviated_and_full_firstname= Autocompletion_options= -Autocomplete_after_following_number_of_characters= Name_format_used_for_autocompletion= Treatment_of_first_names= Cleanup_entries= @@ -1832,8 +1830,6 @@ Search_results_in_library_%0_for_%1= Search_globally= No_results_found.= Found_%0_results.= -Advanced_search_active.= -Normal_search_active.= plain_text= This_search_contains_entries_in_which_any_field_contains_the_regular_expression_%0= This_search_contains_entries_in_which_any_field_contains_the_term_%0= diff --git a/src/main/resources/l10n/JabRef_pt_BR.properties b/src/main/resources/l10n/JabRef_pt_BR.properties index 533892661db..905bda993a0 100644 --- a/src/main/resources/l10n/JabRef_pt_BR.properties +++ b/src/main/resources/l10n/JabRef_pt_BR.properties @@ -1,3 +1,4 @@ + #! #! created/edited by Popeye version 0.55 (github.com/JabRef/popeye) #! encoding:UTF-8 @@ -708,8 +709,6 @@ Main_file_directory=Diretório_de_arquivos_principal Main_layout_file=Arquivo_de_leiaute_principal -Manage=Gerenciar - Manage_custom_exports=Gerenciar_exportações_personalizadas Manage_custom_imports=Gerenciar_importações_personalizadas @@ -1554,7 +1553,6 @@ Use_full_firstname_whenever_possible=Usar_primeiro_nome_inteiro_sempre_que_poss Use_abbreviated_firstname_whenever_possible=Usar_primeiro_nome_abreviado_sempre_que_possível Use_abbreviated_and_full_firstname=Usar_primeiro_nome_abreviado_e_inteiro Autocompletion_options=Opções_de_autocompletar -Autocomplete_after_following_number_of_characters=Autocompletar_após_um_número_de_caracteres Name_format_used_for_autocompletion=Formato_de_nome_usado_para_autocompletar Treatment_of_first_names=Tratamento_dos_primeiros_nomes Cleanup_entries=Limpar_entradas @@ -1832,8 +1830,6 @@ Search_results_in_library_%0_for_%1= Search_globally= No_results_found.=Nenhum_resultado_encontrado. Found_%0_results.=Encontrados_%0_resultados. -Advanced_search_active.=Busca_avançada_ativa. -Normal_search_active.= plain_text= This_search_contains_entries_in_which_any_field_contains_the_regular_expression_%0= This_search_contains_entries_in_which_any_field_contains_the_term_%0= diff --git a/src/main/resources/l10n/JabRef_ru.properties b/src/main/resources/l10n/JabRef_ru.properties index aae08dd5db2..24d6f4b01f3 100644 --- a/src/main/resources/l10n/JabRef_ru.properties +++ b/src/main/resources/l10n/JabRef_ru.properties @@ -1,3 +1,4 @@ + #! #! created/edited by Popeye version 0.55 (github.com/JabRef/popeye) #! encoding:UTF-8 @@ -708,8 +709,6 @@ Main_file_directory=Основной_каталог_файлов Main_layout_file=Главный_файл_макета -Manage=Управление - Manage_custom_exports=Управление_пользовательским_экспортом Manage_custom_imports=Управление_пользовательским_импортом @@ -1554,7 +1553,6 @@ Use_full_firstname_whenever_possible=Использовать_полное_им Use_abbreviated_firstname_whenever_possible=Использовать_сокращенное_имя_(если_возможно) Use_abbreviated_and_full_firstname=Использовать_сокращенное_и_полное_имя Autocompletion_options=Параметры_автозавершения -Autocomplete_after_following_number_of_characters=Автозавершение_после_следующего_числа_знаков Name_format_used_for_autocompletion=Формат_имени_для_автозавершения Treatment_of_first_names=Способ_обработки_имен Cleanup_entries=Очистить_записи @@ -1832,8 +1830,6 @@ Search_results_in_library_%0_for_%1=Результаты_поиска_в_БД_%0 Search_globally=Глобальный_поиск No_results_found.=Результаты_не_найдены. Found_%0_results.=Найдено_%0_результатов. -Advanced_search_active.=Включен_расширенный_поиск. -Normal_search_active.=Включен_нормальный_поиск. plain_text=обычный_текст This_search_contains_entries_in_which_any_field_contains_the_regular_expression_%0=Поиск_содержит_записи,_в_которых_любое_поле_содержит_регулярное_выражение_%0 This_search_contains_entries_in_which_any_field_contains_the_term_%0=Поиск_содержит_записи,_в_которых_любое_поле_содержит_условие_%0 diff --git a/src/main/resources/l10n/JabRef_sv.properties b/src/main/resources/l10n/JabRef_sv.properties index dff772667a0..fd32430c335 100644 --- a/src/main/resources/l10n/JabRef_sv.properties +++ b/src/main/resources/l10n/JabRef_sv.properties @@ -1,3 +1,4 @@ + #! #! created/edited by Popeye version 0.55 (github.com/JabRef/popeye) #! encoding:UTF-8 @@ -708,8 +709,6 @@ Main_file_directory=Huvudmapp_för_filer Main_layout_file=Huvudfil_för_layout -Manage=Hantera - Manage_custom_exports=Hantera_egna_exporterare Manage_custom_imports=Hantera_egna_importerare @@ -1554,7 +1553,6 @@ Use_full_firstname_whenever_possible=Använd_hela_förnamnet_om_möjligt Use_abbreviated_firstname_whenever_possible=Använd_om_möjligt_förkortade_förnamn Use_abbreviated_and_full_firstname=Använd_förkortade_och_hela_förnamn Autocompletion_options=Inställningar_för_automatisk_komplettering -Autocomplete_after_following_number_of_characters=Komplettera_automatiskt_efter_följande_antal_bokstäver Name_format_used_for_autocompletion=Namnformat_för_automatisk_komplettering Treatment_of_first_names=Hantering_av_förnamn Cleanup_entries=Städa_upp_poster @@ -1832,8 +1830,6 @@ Search_results_in_library_%0_for_%1=Söker_efter_%1_i_libraryn_%0 Search_globally=Sök_globalt No_results_found.=Inga_resultat_hittades. Found_%0_results.=Hittade_%0_resultat. -Advanced_search_active.=Avancerad_sökning_aktiv. -Normal_search_active.=Normal_sökning_aktiv. plain_text=klartext This_search_contains_entries_in_which_any_field_contains_the_regular_expression_%0=Denna_sökning_innehåller_poster_där_något_fält_innehåller_det_reguljära_uttrycket_%0 This_search_contains_entries_in_which_any_field_contains_the_term_%0=Denna_sökning_innehåller_poster_där_något_fält_innehåller_termen_%0 diff --git a/src/main/resources/l10n/JabRef_tr.properties b/src/main/resources/l10n/JabRef_tr.properties index b16629ed740..0c01bf95cf2 100644 --- a/src/main/resources/l10n/JabRef_tr.properties +++ b/src/main/resources/l10n/JabRef_tr.properties @@ -1,3 +1,4 @@ + #! #! created/edited by Popeye version 0.55 (github.com/JabRef/popeye) #! encoding:UTF-8 @@ -708,8 +709,6 @@ Main_file_directory=Ana_dosya_dizini Main_layout_file=Ana_yerleşim_dosyası -Manage=Yönet - Manage_custom_exports=Özel_dışa_aktarımları_yönet Manage_custom_imports=Özel_içe_aktarımları_yönet @@ -1554,7 +1553,6 @@ Use_full_firstname_whenever_possible=Mümkün_oldukça_tam_ilk_ismi_kullanınız Use_abbreviated_firstname_whenever_possible=Mümkün_oldukça_kısaltılmış_ilk_ismi_kullanınız Use_abbreviated_and_full_firstname=Kısaltılmış_ve_tam_ilk_ismi_kullanınız Autocompletion_options=Otomatik_tamamlama_seçenekleri -Autocomplete_after_following_number_of_characters=Şu_sayıdaki_karakterden_sonra_otomatik_tamamla Name_format_used_for_autocompletion=Otomatik_tamamlama_için_kullanılan_isim_biçemi Treatment_of_first_names=İlk_isimlerin_işlenme_şekli Cleanup_entries=Girdileri_temizle @@ -1832,8 +1830,6 @@ Search_results_in_library_%0_for_%1=Sonuçları_%1_için_%0_veritabanında_ara Search_globally=Küresel_ara No_results_found.=Hiçbir_sonuç_bulunmadı. Found_%0_results.=%0_sonuç_bulundu. -Advanced_search_active.=İleri_düzey_arama_aktif. -Normal_search_active.=Normal_arama_aktif. plain_text=salt_metin This_search_contains_entries_in_which_any_field_contains_the_regular_expression_%0=Bu_arama,_%0_düzenli_ifadesini_içeren_herhangi_bir_alan_bulunan_girdileri_içerir This_search_contains_entries_in_which_any_field_contains_the_term_%0=Bu_arama,_%0_terimini_içeren_herhangi_bir_alan_bulunan_girdileri_içerir diff --git a/src/main/resources/l10n/JabRef_vi.properties b/src/main/resources/l10n/JabRef_vi.properties index b359f585f00..47aed254bd1 100644 --- a/src/main/resources/l10n/JabRef_vi.properties +++ b/src/main/resources/l10n/JabRef_vi.properties @@ -1,3 +1,4 @@ + #! #! created/edited by Popeye version 0.55 (github.com/JabRef/popeye) #! encoding:UTF-8 @@ -708,8 +709,6 @@ Main_file_directory=Thư_mục_tập_tin_chính Main_layout_file=Tập_tin_trình_bày_chính -Manage=Quản_lý - Manage_custom_exports=Quản_lý_các_phép_xuất_tùy_chọn Manage_custom_imports=Quản_lý_các_phép_nhập_tùy_chọn @@ -1554,7 +1553,6 @@ Use_full_firstname_whenever_possible= Use_abbreviated_firstname_whenever_possible= Use_abbreviated_and_full_firstname= Autocompletion_options= -Autocomplete_after_following_number_of_characters= Name_format_used_for_autocompletion= Treatment_of_first_names= Cleanup_entries= @@ -1832,8 +1830,6 @@ Search_results_in_library_%0_for_%1= Search_globally= No_results_found.= Found_%0_results.= -Advanced_search_active.= -Normal_search_active.= plain_text= This_search_contains_entries_in_which_any_field_contains_the_regular_expression_%0= This_search_contains_entries_in_which_any_field_contains_the_term_%0= diff --git a/src/main/resources/l10n/JabRef_zh.properties b/src/main/resources/l10n/JabRef_zh.properties index cef673bfa2c..283d9a1427d 100644 --- a/src/main/resources/l10n/JabRef_zh.properties +++ b/src/main/resources/l10n/JabRef_zh.properties @@ -1,3 +1,4 @@ + #! #! created/edited by Popeye version 0.55 (github.com/JabRef/popeye) #! encoding:UTF-8 @@ -708,8 +709,6 @@ Main_file_directory=文件主目录 Main_layout_file=主_layout_文件 -Manage=管理 - Manage_custom_exports=管理自定义导出器 Manage_custom_imports=管理自定义导入器 @@ -1554,7 +1553,6 @@ Use_full_firstname_whenever_possible=尽可能使用完整的_Firstname Use_abbreviated_firstname_whenever_possible=尽可能使用缩写的_Firstname Use_abbreviated_and_full_firstname=混杂使用缩写和完整的_Firstname Autocompletion_options=自动补全选项 -Autocomplete_after_following_number_of_characters=在键入多少字符时开始自动补全 Name_format_used_for_autocompletion=自动补全的姓名格式 Treatment_of_first_names=Firstname_的处理 Cleanup_entries=清理选中记录 @@ -1832,8 +1830,6 @@ Search_results_in_library_%0_for_%1=在文献库_%0_中搜索_%1_的结果 Search_globally=全局搜索 No_results_found.=没有找到结果. Found_%0_results.=找到_%0_条结果. -Advanced_search_active.=高级搜索行为。 -Normal_search_active.=普通搜索行为。 plain_text=纯文本 This_search_contains_entries_in_which_any_field_contains_the_regular_expression_%0=这次搜索的结果记录符合条件:记录的任意域包含正则表达式_%0 This_search_contains_entries_in_which_any_field_contains_the_term_%0=这次搜索的结果记录符合条件:记录的任意域包含词组_%0 diff --git a/src/test/java/org/jabref/gui/autocompleter/AutoCompleterUtil.java b/src/test/java/org/jabref/gui/autocompleter/AutoCompleterUtil.java new file mode 100644 index 00000000000..c6cf7fce048 --- /dev/null +++ b/src/test/java/org/jabref/gui/autocompleter/AutoCompleterUtil.java @@ -0,0 +1,19 @@ +package org.jabref.gui.autocompleter; + +import org.controlsfx.control.textfield.AutoCompletionBinding; + +public class AutoCompleterUtil { + public static AutoCompletionBinding.ISuggestionRequest getRequest(String text) { + return new AutoCompletionBinding.ISuggestionRequest() { + @Override + public boolean isCancelled() { + return false; + } + + @Override + public String getUserText() { + return text; + } + }; + } +} diff --git a/src/test/java/org/jabref/gui/autocompleter/BibEntrySuggestionProviderTest.java b/src/test/java/org/jabref/gui/autocompleter/BibEntrySuggestionProviderTest.java new file mode 100644 index 00000000000..b66163a940d --- /dev/null +++ b/src/test/java/org/jabref/gui/autocompleter/BibEntrySuggestionProviderTest.java @@ -0,0 +1,117 @@ +package org.jabref.gui.autocompleter; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; + +import org.jabref.model.entry.BibEntry; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import static org.jabref.gui.autocompleter.AutoCompleterUtil.getRequest; + +public class BibEntrySuggestionProviderTest { + private BibEntrySuggestionProvider autoCompleter; + + @Before + public void setUp() throws Exception { + autoCompleter = new BibEntrySuggestionProvider(); + } + + @Test + public void completeWithoutAddingAnythingReturnsNothing() { + Collection result = autoCompleter.call(getRequest(("test"))); + Assert.assertEquals(Collections.emptyList(), result); + } + + @Test + public void completeAfterAddingNullReturnsNothing() { + autoCompleter.indexEntry(null); + + Collection result = autoCompleter.call(getRequest(("test"))); + Assert.assertEquals(Collections.emptyList(), result); + } + + @Test + public void completeAfterAddingEmptyEntryReturnsNothing() { + BibEntry entry = new BibEntry(); + autoCompleter.indexEntry(entry); + + Collection result = autoCompleter.call(getRequest(("test"))); + Assert.assertEquals(Collections.emptyList(), result); + } + + @Test + public void completeKeyReturnsKey() { + BibEntry entry = new BibEntry(); + entry.setCiteKey("testKey"); + autoCompleter.indexEntry(entry); + + Collection result = autoCompleter.call(getRequest(("testKey"))); + Assert.assertEquals(Collections.singletonList(entry), result); + } + + @Test + public void completeBeginnigOfKeyReturnsKey() { + BibEntry entry = new BibEntry(); + entry.setCiteKey("testKey"); + autoCompleter.indexEntry(entry); + + Collection result = autoCompleter.call(getRequest(("test"))); + Assert.assertEquals(Collections.singletonList(entry), result); + } + + @Test + public void completeLowercaseKeyReturnsKey() { + BibEntry entry = new BibEntry(); + entry.setCiteKey("testKey"); + autoCompleter.indexEntry(entry); + + Collection result = autoCompleter.call(getRequest(("testkey"))); + Assert.assertEquals(Collections.singletonList(entry), result); + } + + @Test(expected = NullPointerException.class) + public void completeNullThrowsException() { + BibEntry entry = new BibEntry(); + entry.setCiteKey("testKey"); + autoCompleter.indexEntry(entry); + + autoCompleter.call(getRequest((null))); + } + + @Test + public void completeEmptyStringReturnsNothing() { + BibEntry entry = new BibEntry(); + entry.setCiteKey("testKey"); + autoCompleter.indexEntry(entry); + + Collection result = autoCompleter.call(getRequest((""))); + Assert.assertEquals(Collections.emptyList(), result); + } + + @Test + public void completeReturnsMultipleResults() { + BibEntry entryOne = new BibEntry(); + entryOne.setCiteKey("testKeyOne"); + autoCompleter.indexEntry(entryOne); + BibEntry entryTwo = new BibEntry(); + entryTwo.setCiteKey("testKeyTwo"); + autoCompleter.indexEntry(entryTwo); + + Collection result = autoCompleter.call(getRequest(("testKey"))); + Assert.assertEquals(Arrays.asList(entryTwo, entryOne), result); + } + + @Test + public void completeShortKeyReturnsKey() { + BibEntry entry = new BibEntry(); + entry.setCiteKey("key"); + autoCompleter.indexEntry(entry); + + Collection result = autoCompleter.call(getRequest(("k"))); + Assert.assertEquals(Collections.singletonList(entry), result); + } +} diff --git a/src/test/java/org/jabref/gui/autocompleter/DefaultAutoCompleterTest.java b/src/test/java/org/jabref/gui/autocompleter/DefaultAutoCompleterTest.java new file mode 100644 index 00000000000..5983d43da57 --- /dev/null +++ b/src/test/java/org/jabref/gui/autocompleter/DefaultAutoCompleterTest.java @@ -0,0 +1,154 @@ +package org.jabref.gui.autocompleter; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; + +import org.jabref.model.entry.BibEntry; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import static org.jabref.gui.autocompleter.AutoCompleterUtil.getRequest; + +public class DefaultAutoCompleterTest { + + private WordSuggestionProvider autoCompleter; + + @SuppressWarnings("unused") + @Test(expected = NullPointerException.class) + public void initAutoCompleterWithNullFieldThrowsException() { + new WordSuggestionProvider(null); + } + + @Before + public void setUp() throws Exception { + autoCompleter = new WordSuggestionProvider("field"); + } + + @Test + public void completeWithoutAddingAnythingReturnsNothing() { + Collection result = autoCompleter.call(getRequest(("test"))); + Assert.assertEquals(Collections.emptyList(), result); + } + + @Test + public void completeAfterAddingNullReturnsNothing() { + autoCompleter.indexEntry(null); + + Collection result = autoCompleter.call(getRequest(("test"))); + Assert.assertEquals(Collections.emptyList(), result); + } + + @Test + public void completeAfterAddingEmptyEntryReturnsNothing() { + BibEntry entry = new BibEntry(); + autoCompleter.indexEntry(entry); + + Collection result = autoCompleter.call(getRequest(("test"))); + Assert.assertEquals(Collections.emptyList(), result); + } + + @Test + public void completeAfterAddingEntryWithoutFieldReturnsNothing() { + BibEntry entry = new BibEntry(); + entry.setField("title", "testTitle"); + autoCompleter.indexEntry(entry); + + Collection result = autoCompleter.call(getRequest(("test"))); + Assert.assertEquals(Collections.emptyList(), result); + } + + @Test + public void completeValueReturnsValue() { + BibEntry entry = new BibEntry(); + entry.setField("field", "testValue"); + autoCompleter.indexEntry(entry); + + Collection result = autoCompleter.call(getRequest(("testValue"))); + Assert.assertEquals(Arrays.asList("testValue"), result); + } + + @Test + public void completeBeginnigOfValueReturnsValue() { + BibEntry entry = new BibEntry(); + entry.setField("field", "testValue"); + autoCompleter.indexEntry(entry); + + Collection result = autoCompleter.call(getRequest(("test"))); + Assert.assertEquals(Arrays.asList("testValue"), result); + } + + @Test + public void completeLowercaseValueReturnsValue() { + BibEntry entry = new BibEntry(); + entry.setField("field", "testValue"); + autoCompleter.indexEntry(entry); + + Collection result = autoCompleter.call(getRequest(("testvalue"))); + Assert.assertEquals(Arrays.asList("testValue"), result); + } + + @Test(expected = NullPointerException.class) + public void completeNullThrowsException() { + BibEntry entry = new BibEntry(); + entry.setField("field", "testKey"); + autoCompleter.indexEntry(entry); + + autoCompleter.call(getRequest((null))); + } + + @Test + public void completeEmptyStringReturnsNothing() { + BibEntry entry = new BibEntry(); + entry.setField("field", "testKey"); + autoCompleter.indexEntry(entry); + + Collection result = autoCompleter.call(getRequest((""))); + Assert.assertEquals(Collections.emptyList(), result); + } + + @Test + public void completeReturnsMultipleResults() { + BibEntry entryOne = new BibEntry(); + entryOne.setField("field", "testValueOne"); + autoCompleter.indexEntry(entryOne); + BibEntry entryTwo = new BibEntry(); + entryTwo.setField("field", "testValueTwo"); + autoCompleter.indexEntry(entryTwo); + + Collection result = autoCompleter.call(getRequest(("testValue"))); + Assert.assertEquals(Arrays.asList("testValueOne", "testValueTwo"), result); + } + + @Test + public void completeShortStringReturnsValue() { + BibEntry entry = new BibEntry(); + entry.setField("field", "val"); + autoCompleter.indexEntry(entry); + + Collection result = autoCompleter.call(getRequest(("va"))); + Assert.assertEquals(Collections.singletonList("val"), result); + } + + @Test + public void completeBeginnigOfSecondWordReturnsWord() { + BibEntry entry = new BibEntry(); + entry.setField("field", "test value"); + autoCompleter.indexEntry(entry); + + Collection result = autoCompleter.call(getRequest(("val"))); + Assert.assertEquals(Collections.singletonList("value"), result); + } + + @Test + public void completePartOfWordReturnsValue() { + BibEntry entry = new BibEntry(); + entry.setField("field", "test value"); + autoCompleter.indexEntry(entry); + + Collection result = autoCompleter.call(getRequest(("lue"))); + Assert.assertEquals(Collections.singletonList("value"), result); + } +} diff --git a/src/test/java/org/jabref/gui/autocompleter/FieldValueSuggestionProviderTest.java b/src/test/java/org/jabref/gui/autocompleter/FieldValueSuggestionProviderTest.java new file mode 100644 index 00000000000..1ecf2600084 --- /dev/null +++ b/src/test/java/org/jabref/gui/autocompleter/FieldValueSuggestionProviderTest.java @@ -0,0 +1,163 @@ +package org.jabref.gui.autocompleter; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; + +import org.jabref.model.entry.BibEntry; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import static org.jabref.gui.autocompleter.AutoCompleterUtil.getRequest; + +public class FieldValueSuggestionProviderTest { + private FieldValueSuggestionProvider autoCompleter; + + @Before + public void setUp() throws Exception { + autoCompleter = new FieldValueSuggestionProvider("field"); + } + + @SuppressWarnings("unused") + @Test(expected = NullPointerException.class) + public void initAutoCompleterWithNullFieldThrowsException() { + new FieldValueSuggestionProvider(null); + } + + @Test + public void completeWithoutAddingAnythingReturnsNothing() { + Collection result = autoCompleter.call(getRequest(("test"))); + Assert.assertEquals(Collections.emptyList(), result); + } + + @Test + public void completeAfterAddingNullReturnsNothing() { + autoCompleter.indexEntry(null); + + Collection result = autoCompleter.call(getRequest(("test"))); + Assert.assertEquals(Collections.emptyList(), result); + } + + @Test + public void completeAfterAddingEmptyEntryReturnsNothing() { + BibEntry entry = new BibEntry(); + autoCompleter.indexEntry(entry); + + Collection result = autoCompleter.call(getRequest(("test"))); + Assert.assertEquals(Collections.emptyList(), result); + } + + @Test + public void completeAfterAddingEntryWithoutFieldReturnsNothing() { + BibEntry entry = new BibEntry(); + entry.setField("title", "testTitle"); + autoCompleter.indexEntry(entry); + + Collection result = autoCompleter.call(getRequest(("test"))); + Assert.assertEquals(Collections.emptyList(), result); + } + + @Test + public void completeValueReturnsValue() { + BibEntry entry = new BibEntry(); + entry.setField("field", "testValue"); + autoCompleter.indexEntry(entry); + + Collection result = autoCompleter.call(getRequest(("testValue"))); + Assert.assertEquals(Arrays.asList("testValue"), result); + } + + @Test + public void completeBeginnigOfValueReturnsValue() { + BibEntry entry = new BibEntry(); + entry.setField("field", "testValue"); + autoCompleter.indexEntry(entry); + + Collection result = autoCompleter.call(getRequest(("test"))); + Assert.assertEquals(Arrays.asList("testValue"), result); + } + + @Test + public void completeLowercaseValueReturnsValue() { + BibEntry entry = new BibEntry(); + entry.setField("field", "testValue"); + autoCompleter.indexEntry(entry); + + Collection result = autoCompleter.call(getRequest(("testvalue"))); + Assert.assertEquals(Arrays.asList("testValue"), result); + } + + @Test(expected = NullPointerException.class) + public void completeNullThrowsException() { + BibEntry entry = new BibEntry(); + entry.setField("field", "testKey"); + autoCompleter.indexEntry(entry); + + autoCompleter.call(getRequest((null))); + } + + @Test + public void completeEmptyStringReturnsNothing() { + BibEntry entry = new BibEntry(); + entry.setField("field", "testKey"); + autoCompleter.indexEntry(entry); + + Collection result = autoCompleter.call(getRequest((""))); + Assert.assertEquals(Collections.emptyList(), result); + } + + @Test + public void completeReturnsMultipleResults() { + BibEntry entryOne = new BibEntry(); + entryOne.setField("field", "testValueOne"); + autoCompleter.indexEntry(entryOne); + BibEntry entryTwo = new BibEntry(); + entryTwo.setField("field", "testValueTwo"); + autoCompleter.indexEntry(entryTwo); + + Collection result = autoCompleter.call(getRequest(("testValue"))); + Assert.assertEquals(Arrays.asList("testValueOne", "testValueTwo"), result); + } + + @Test + public void completeShortStringReturnsFieldValue() { + BibEntry entry = new BibEntry(); + entry.setField("field", "val"); + autoCompleter.indexEntry(entry); + + Collection result = autoCompleter.call(getRequest(("va"))); + Assert.assertEquals(Collections.singletonList("val"), result); + } + + @Test + public void completeBeginnigOfSecondWordReturnsWholeFieldValue() { + BibEntry entry = new BibEntry(); + entry.setField("field", "test value"); + autoCompleter.indexEntry(entry); + + Collection result = autoCompleter.call(getRequest(("val"))); + Assert.assertEquals(Collections.singletonList("test value"), result); + } + + @Test + public void completePartOfWordReturnsWholeFieldValue() { + BibEntry entry = new BibEntry(); + entry.setField("field", "test value"); + autoCompleter.indexEntry(entry); + + Collection result = autoCompleter.call(getRequest(("lue"))); + Assert.assertEquals(Collections.singletonList("test value"), result); + } + + @Test + public void completeReturnsWholeFieldValue() { + BibEntry entry = new BibEntry(); + entry.setField("field", "test value"); + autoCompleter.indexEntry(entry); + + Collection result = autoCompleter.call(getRequest(("te"))); + Assert.assertEquals(Collections.singletonList("test value"), result); + } +} diff --git a/src/test/java/org/jabref/gui/autocompleter/PersonNameSuggestionProviderTest.java b/src/test/java/org/jabref/gui/autocompleter/PersonNameSuggestionProviderTest.java new file mode 100644 index 00000000000..9b4365b08dd --- /dev/null +++ b/src/test/java/org/jabref/gui/autocompleter/PersonNameSuggestionProviderTest.java @@ -0,0 +1,186 @@ +package org.jabref.gui.autocompleter; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; + +import org.jabref.model.entry.Author; +import org.jabref.model.entry.BibEntry; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import static org.jabref.gui.autocompleter.AutoCompleterUtil.getRequest; + +public class PersonNameSuggestionProviderTest { + + private static final Author vassilisKostakos = new Author("Vassilis", "V.", "", "Kostakos", ""); + private PersonNameSuggestionProvider autoCompleter; + private BibEntry entry; + + @Test(expected = NullPointerException.class) + public void initAutoCompleterWithNullFieldThrowsException() { + new PersonNameSuggestionProvider((String) null); + } + + @Before + public void setUp() throws Exception { + autoCompleter = new PersonNameSuggestionProvider("field"); + + entry = new BibEntry(); + entry.setField("field", "Vassilis Kostakos"); + } + + @Test + public void completeWithoutAddingAnythingReturnsNothing() { + Collection result = autoCompleter.call(getRequest(("test"))); + Assert.assertEquals(Collections.emptyList(), result); + } + + @Test + public void completeAfterAddingNullReturnsNothing() { + autoCompleter.indexEntry(null); + + Collection result = autoCompleter.call(getRequest(("test"))); + Assert.assertEquals(Collections.emptyList(), result); + } + + @Test + public void completeAfterAddingEmptyEntryReturnsNothing() { + BibEntry entry = new BibEntry(); + autoCompleter.indexEntry(entry); + + Collection result = autoCompleter.call(getRequest(("test"))); + Assert.assertEquals(Collections.emptyList(), result); + } + + @Test + public void completeAfterAddingEntryWithoutFieldReturnsNothing() { + BibEntry entry = new BibEntry(); + entry.setField("title", "testTitle"); + autoCompleter.indexEntry(entry); + + Collection result = autoCompleter.call(getRequest(("test"))); + Assert.assertEquals(Collections.emptyList(), result); + } + + @Test + public void completeNameReturnsName() { + autoCompleter.indexEntry(entry); + + Collection result = autoCompleter.call(getRequest(("Kostakos"))); + Assert.assertEquals(Collections.singletonList(vassilisKostakos), result); + } + + @Test + public void completeBeginningOfNameReturnsName() { + autoCompleter.indexEntry(entry); + + Collection result = autoCompleter.call(getRequest(("Kosta"))); + Assert.assertEquals(Collections.singletonList(vassilisKostakos), result); + } + + @Test + public void completeLowercaseBeginningOfNameReturnsName() { + autoCompleter.indexEntry(entry); + + Collection result = autoCompleter.call(getRequest(("kosta"))); + Assert.assertEquals(Collections.singletonList(vassilisKostakos), result); + } + + @Test(expected = NullPointerException.class) + public void completeNullThrowsException() { + autoCompleter.call(getRequest((null))); + } + + @Test + public void completeEmptyStringReturnsNothing() { + autoCompleter.indexEntry(entry); + + Collection result = autoCompleter.call(getRequest((""))); + Assert.assertEquals(Collections.emptyList(), result); + } + + @Test + public void completeReturnsMultipleResults() { + autoCompleter.indexEntry(entry); + BibEntry entryTwo = new BibEntry(); + entryTwo.setField("field", "Kosta"); + autoCompleter.indexEntry(entryTwo); + Author authorTwo = new Author("", "", "", "Kosta", ""); + + Collection result = autoCompleter.call(getRequest(("Ko"))); + Assert.assertEquals(Arrays.asList(authorTwo, vassilisKostakos), result); + } + + @Test + public void completePartOfNameReturnsName() { + autoCompleter.indexEntry(entry); + + Collection result = autoCompleter.call(getRequest(("osta"))); + Assert.assertEquals(Collections.singletonList(vassilisKostakos), result); + } + + @Test + public void completeBeginningOfFirstNameReturnsName() { + autoCompleter.indexEntry(entry); + + Collection result = autoCompleter.call(getRequest(("Vas"))); + Assert.assertEquals(Collections.singletonList(vassilisKostakos), result); + } + + @Test + public void completeBeginningOfFirstNameReturnsNameWithJr() { + BibEntry entry = new BibEntry(); + entry.setField("field", "Reagle, Jr., Joseph M."); + autoCompleter.indexEntry(entry); + Author author = new Author("Joseph M.", "J. M.", "", "Reagle", "Jr."); + + Collection result = autoCompleter.call(getRequest(("Jos"))); + Assert.assertEquals(Collections.singletonList(author), result); + } + + @Test + public void completeBeginningOfFirstNameReturnsNameWithVon() { + BibEntry entry = new BibEntry(); + entry.setField("field", "Eric von Hippel"); + autoCompleter.indexEntry(entry); + Author author = new Author("Eric", "E.", "von", "Hippel", ""); + + Collection result = autoCompleter.call(getRequest(("Eric"))); + Assert.assertEquals(Collections.singletonList(author), result); + } + + @Test + public void completeBeginningOfLastNameReturnsNameWithUmlauts() { + BibEntry entry = new BibEntry(); + entry.setField("field", "Honig Bär"); + autoCompleter.indexEntry(entry); + Author author = new Author("Honig", "H.", "", "Bär", ""); + + Collection result = autoCompleter.call(getRequest(("Bä"))); + Assert.assertEquals(Collections.singletonList(author), result); + } + + @Test + public void completeVonReturnsName() { + BibEntry entry = new BibEntry(); + entry.setField("field", "Eric von Hippel"); + autoCompleter.indexEntry(entry); + Author author = new Author("Eric", "E.", "von", "Hippel", ""); + + Collection result = autoCompleter.call(getRequest(("von"))); + Assert.assertEquals(Collections.singletonList(author), result); + } + + @Test + public void completeBeginningOfFullNameReturnsName() { + BibEntry entry = new BibEntry(); + entry.setField("field", "Vassilis Kostakos"); + autoCompleter.indexEntry(entry); + + Collection result = autoCompleter.call(getRequest(("Kostakos, Va"))); + Assert.assertEquals(Collections.singletonList(vassilisKostakos), result); + } +} diff --git a/src/test/java/org/jabref/gui/fieldeditors/IdentifierEditorViewModelTest.java b/src/test/java/org/jabref/gui/fieldeditors/IdentifierEditorViewModelTest.java index 3caee9cc192..92c3e63eefa 100644 --- a/src/test/java/org/jabref/gui/fieldeditors/IdentifierEditorViewModelTest.java +++ b/src/test/java/org/jabref/gui/fieldeditors/IdentifierEditorViewModelTest.java @@ -1,6 +1,7 @@ package org.jabref.gui.fieldeditors; import org.jabref.gui.DialogService; +import org.jabref.gui.autocompleter.WordSuggestionProvider; import org.jabref.gui.util.CurrentThreadTaskExecutor; import org.junit.Before; @@ -15,7 +16,7 @@ public class IdentifierEditorViewModelTest { @Before public void setUp() throws Exception { - viewModel = new IdentifierEditorViewModel("DOI", new CurrentThreadTaskExecutor(), mock(DialogService.class)); + viewModel = new IdentifierEditorViewModel("DOI", new WordSuggestionProvider("DOI"), new CurrentThreadTaskExecutor(), mock(DialogService.class)); } @Test diff --git a/src/test/java/org/jabref/gui/journals/ManageJournalAbbreviationsViewModelTest.java b/src/test/java/org/jabref/gui/journals/ManageJournalAbbreviationsViewModelTest.java index 9716581818a..5ed2b5a5545 100644 --- a/src/test/java/org/jabref/gui/journals/ManageJournalAbbreviationsViewModelTest.java +++ b/src/test/java/org/jabref/gui/journals/ManageJournalAbbreviationsViewModelTest.java @@ -76,8 +76,8 @@ public void setUpViewModel() throws Exception { @Test public void testInitialHasNoFilesAndNoAbbreviations() { - Assert.assertEquals(viewModel.journalFilesProperty().size(), 0); - Assert.assertEquals(viewModel.abbreviationsProperty().size(), 0); + Assert.assertEquals(0, viewModel.journalFilesProperty().size()); + Assert.assertEquals(0, viewModel.abbreviationsProperty().size()); } @Test @@ -430,7 +430,7 @@ public void testSaveAbbreviationsToFilesCreatesNewFilesWithWrittenAbbreviations( Assert.assertEquals(expected, actual); - expected = "Abbreviations = Abb" + NEWLINE + "Test Entry = TE" + NEWLINE + "MoreEntries = ME" + NEWLINE + expected = "EntryEntry = EE" + NEWLINE + "Abbreviations = Abb" + NEWLINE + "Test Entry = TE" + NEWLINE + "SomeOtherEntry = SOE" + NEWLINE + ""; actual = Files.contentOf(testFile5EntriesWithDuplicate.toFile(), StandardCharsets.UTF_8); diff --git a/src/test/java/org/jabref/gui/keyboard/KeyBindingsDialogViewModelTest.java b/src/test/java/org/jabref/gui/keyboard/KeyBindingsDialogViewModelTest.java index c3870e64a37..306c7c683e9 100644 --- a/src/test/java/org/jabref/gui/keyboard/KeyBindingsDialogViewModelTest.java +++ b/src/test/java/org/jabref/gui/keyboard/KeyBindingsDialogViewModelTest.java @@ -1,5 +1,10 @@ package org.jabref.gui.keyboard; +import java.awt.event.InputEvent; +import java.util.Optional; + +import javax.swing.JFrame; + import javafx.scene.input.KeyCode; import javafx.scene.input.KeyCombination; import javafx.scene.input.KeyEvent; @@ -9,7 +14,7 @@ import org.junit.Before; import org.junit.Test; - +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; @@ -86,7 +91,7 @@ public void testRandomNewKeyKeyBindingInRepository() { assertTrue(keyBindingRepository.checkKeyCombinationEquality(combination, shortcutKeyEvent)); - assertFalse(keyBindingRepository.checkKeyCombinationEquality(KeyCombination.valueOf(KeyBinding.CLEANUP.getDefaultBinding()), shortcutKeyEvent)); + assertFalse(keyBindingRepository.checkKeyCombinationEquality(KeyCombination.valueOf(KeyBinding.CLEANUP.getDefaultKeyBinding()), shortcutKeyEvent)); } @Test @@ -133,6 +138,21 @@ public void testSetAllKeyBindingsToDefault() { assertFalse(keyBindingRepository.checkKeyCombinationEquality(KeyBinding.ABBREVIATE, shortcutKeyEvent)); } + @Test + public void testCloseEntryEditorCloseEntryKeybinding() { + KeyBindingViewModel viewModel = setKeyBindingViewModel(KeyBinding.CLOSE_ENTRY_EDITOR); + model.selectedKeyBindingProperty().set(viewModel); + KeyEvent closeEditorEvent = new KeyEvent(KeyEvent.KEY_PRESSED, "", "", KeyCode.ESCAPE, false, false, false, false); + + assertEquals(KeyBinding.CLOSE_ENTRY_EDITOR.getDefaultKeyBinding(), KeyCode.ESCAPE.getName()); + + KeyCombination combi = KeyCombination.valueOf(KeyBinding.CLOSE_ENTRY_EDITOR.getDefaultKeyBinding()); + + assertTrue(combi.match(closeEditorEvent)); + assertTrue(keyBindingRepository.checkKeyCombinationEquality(KeyBinding.CLOSE_ENTRY_EDITOR, closeEditorEvent)); + + } + @Test public void testSetSingleKeyBindingToDefault() { KeyBindingViewModel viewModel = setKeyBindingViewModel(KeyBinding.ABBREVIATE); @@ -152,8 +172,16 @@ public void testSetSingleKeyBindingToDefault() { assertFalse(keyBindingRepository.checkKeyCombinationEquality(KeyBinding.ABBREVIATE, shortcutKeyEvent)); } + @Test + public void testConversionAwtKeyEventJavafxKeyEvent() { + java.awt.event.KeyEvent evt = new java.awt.event.KeyEvent(mock(JFrame.class), 0, 0, InputEvent.CTRL_MASK, java.awt.event.KeyEvent.VK_S, java.awt.event.KeyEvent.CHAR_UNDEFINED); + + Optional keyBinding = keyBindingRepository.mapToKeyBinding(evt); + assertEquals(Optional.of(KeyBinding.SAVE_DATABASE), keyBinding); + } + private KeyBindingViewModel setKeyBindingViewModel(KeyBinding binding) { - KeyBindingViewModel bindViewModel = new KeyBindingViewModel(keyBindingRepository, binding, binding.getDefaultBinding()); + KeyBindingViewModel bindViewModel = new KeyBindingViewModel(keyBindingRepository, binding, binding.getDefaultKeyBinding()); model.selectedKeyBindingProperty().set(bindViewModel); return bindViewModel; } diff --git a/src/test/java/org/jabref/logic/autocompleter/AutoCompleterFactoryTest.java b/src/test/java/org/jabref/logic/autocompleter/AutoCompleterFactoryTest.java deleted file mode 100644 index 7da50b60fc1..00000000000 --- a/src/test/java/org/jabref/logic/autocompleter/AutoCompleterFactoryTest.java +++ /dev/null @@ -1,74 +0,0 @@ -package org.jabref.logic.autocompleter; - -import org.jabref.logic.journals.JournalAbbreviationLoader; - -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -import static org.mockito.Mockito.mock; - -public class AutoCompleterFactoryTest { - - private AutoCompleterFactory autoCompleterFactory; - private JournalAbbreviationLoader abbreviationLoader; - - @Before - public void setUp() throws Exception { - AutoCompletePreferences preferences = mock(AutoCompletePreferences.class); - abbreviationLoader = mock(JournalAbbreviationLoader.class); - autoCompleterFactory = new AutoCompleterFactory(preferences, abbreviationLoader); - } - - @Test(expected = NullPointerException.class) - public void initFactoryWithNullPreferenceThrowsException() { - new AutoCompleterFactory(null, abbreviationLoader); - } - - @Test - public void getForUnknownFieldReturnsDefaultAutoCompleter() { - AutoCompleter autoCompleter = autoCompleterFactory.getFor("unknownField"); - Assert.assertTrue(autoCompleter instanceof DefaultAutoCompleter); - } - - @Test(expected = NullPointerException.class) - public void getForNullThrowsException() { - autoCompleterFactory.getFor(null); - } - - @Test - public void getForAuthorReturnsNameFieldAutoCompleter() { - AutoCompleter autoCompleter = autoCompleterFactory.getFor("author"); - Assert.assertTrue(autoCompleter instanceof NameFieldAutoCompleter); - } - - @Test - public void getForEditorReturnsNameFieldAutoCompleter() { - AutoCompleter autoCompleter = autoCompleterFactory.getFor("editor"); - Assert.assertTrue(autoCompleter instanceof NameFieldAutoCompleter); - } - - @Test - public void getForCrossrefReturnsBibtexKeyAutoCompleter() { - AutoCompleter autoCompleter = autoCompleterFactory.getFor("crossref"); - Assert.assertTrue(autoCompleter instanceof BibtexKeyAutoCompleter); - } - - @Test - public void getForJournalReturnsEntireFieldAutoCompleter() { - AutoCompleter autoCompleter = autoCompleterFactory.getFor("journal"); - Assert.assertTrue(autoCompleter instanceof EntireFieldAutoCompleter); - } - - @Test - public void getForPublisherReturnsEntireFieldAutoCompleter() { - AutoCompleter autoCompleter = autoCompleterFactory.getFor("publisher"); - Assert.assertTrue(autoCompleter instanceof EntireFieldAutoCompleter); - } - - @Test - public void getPersonAutoCompleterReturnsNameFieldAutoCompleter() { - AutoCompleter autoCompleter = autoCompleterFactory.getPersonAutoCompleter(); - Assert.assertTrue(autoCompleter instanceof NameFieldAutoCompleter); - } -} diff --git a/src/test/java/org/jabref/logic/autocompleter/BibtexKeyAutoCompleterTest.java b/src/test/java/org/jabref/logic/autocompleter/BibtexKeyAutoCompleterTest.java deleted file mode 100644 index 4c25f1efa99..00000000000 --- a/src/test/java/org/jabref/logic/autocompleter/BibtexKeyAutoCompleterTest.java +++ /dev/null @@ -1,162 +0,0 @@ -package org.jabref.logic.autocompleter; - -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -import org.jabref.model.entry.BibEntry; - -import org.junit.Assert; -import org.junit.Test; - -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -public class BibtexKeyAutoCompleterTest { - - @SuppressWarnings("unused") - @Test(expected = NullPointerException.class) - public void initAutoCompleterWithNullPreferenceThrowsException() { - new BibtexKeyAutoCompleter(null); - } - - @Test - public void completeWithoutAddingAnythingReturnsNothing() { - AutoCompletePreferences preferences = mock(AutoCompletePreferences.class); - BibtexKeyAutoCompleter autoCompleter = new BibtexKeyAutoCompleter(preferences); - - List result = autoCompleter.complete("test"); - Assert.assertEquals(Collections.emptyList(), result); - } - - @Test - public void completeAfterAddingNullReturnsNothing() { - AutoCompletePreferences preferences = mock(AutoCompletePreferences.class); - BibtexKeyAutoCompleter autoCompleter = new BibtexKeyAutoCompleter(preferences); - - autoCompleter.addBibtexEntry(null); - - List result = autoCompleter.complete("test"); - Assert.assertEquals(Collections.emptyList(), result); - } - - @Test - public void completeAfterAddingEmptyEntryReturnsNothing() { - AutoCompletePreferences preferences = mock(AutoCompletePreferences.class); - BibtexKeyAutoCompleter autoCompleter = new BibtexKeyAutoCompleter(preferences); - - BibEntry entry = new BibEntry(); - autoCompleter.addBibtexEntry(entry); - - List result = autoCompleter.complete("test"); - Assert.assertEquals(Collections.emptyList(), result); - } - - @Test - public void completeKeyReturnsKey() { - AutoCompletePreferences preferences = mock(AutoCompletePreferences.class); - BibtexKeyAutoCompleter autoCompleter = new BibtexKeyAutoCompleter(preferences); - - BibEntry entry = new BibEntry(); - entry.setCiteKey("testKey"); - autoCompleter.addBibtexEntry(entry); - - List result = autoCompleter.complete("testKey"); - Assert.assertEquals(Arrays.asList("testKey"), result); - } - - @Test - public void completeBeginnigOfKeyReturnsKey() { - AutoCompletePreferences preferences = mock(AutoCompletePreferences.class); - BibtexKeyAutoCompleter autoCompleter = new BibtexKeyAutoCompleter(preferences); - - BibEntry entry = new BibEntry(); - entry.setCiteKey("testKey"); - autoCompleter.addBibtexEntry(entry); - - List result = autoCompleter.complete("test"); - Assert.assertEquals(Arrays.asList("testKey"), result); - } - - @Test - public void completeLowercaseKeyReturnsKey() { - AutoCompletePreferences preferences = mock(AutoCompletePreferences.class); - BibtexKeyAutoCompleter autoCompleter = new BibtexKeyAutoCompleter(preferences); - - BibEntry entry = new BibEntry(); - entry.setCiteKey("testKey"); - autoCompleter.addBibtexEntry(entry); - - List result = autoCompleter.complete("testkey"); - Assert.assertEquals(Arrays.asList("testKey"), result); - } - - @Test - public void completeNullReturnsNothing() { - AutoCompletePreferences preferences = mock(AutoCompletePreferences.class); - BibtexKeyAutoCompleter autoCompleter = new BibtexKeyAutoCompleter(preferences); - - BibEntry entry = new BibEntry(); - entry.setCiteKey("testKey"); - autoCompleter.addBibtexEntry(entry); - - List result = autoCompleter.complete(null); - Assert.assertEquals(Collections.emptyList(), result); - } - - @Test - public void completeEmptyStringReturnsNothing() { - AutoCompletePreferences preferences = mock(AutoCompletePreferences.class); - BibtexKeyAutoCompleter autoCompleter = new BibtexKeyAutoCompleter(preferences); - - BibEntry entry = new BibEntry(); - entry.setCiteKey("testKey"); - autoCompleter.addBibtexEntry(entry); - - List result = autoCompleter.complete(""); - Assert.assertEquals(Collections.emptyList(), result); - } - - @Test - public void completeReturnsMultipleResults() { - AutoCompletePreferences preferences = mock(AutoCompletePreferences.class); - BibtexKeyAutoCompleter autoCompleter = new BibtexKeyAutoCompleter(preferences); - - BibEntry entryOne = new BibEntry(); - entryOne.setCiteKey("testKeyOne"); - autoCompleter.addBibtexEntry(entryOne); - BibEntry entryTwo = new BibEntry(); - entryTwo.setCiteKey("testKeyTwo"); - autoCompleter.addBibtexEntry(entryTwo); - - List result = autoCompleter.complete("testKey"); - Assert.assertEquals(Arrays.asList("testKeyOne", "testKeyTwo"), result); - } - - @Test - public void completeShortKeyReturnsKey() { - AutoCompletePreferences preferences = mock(AutoCompletePreferences.class); - BibtexKeyAutoCompleter autoCompleter = new BibtexKeyAutoCompleter(preferences); - - BibEntry entry = new BibEntry(); - entry.setCiteKey("key"); - autoCompleter.addBibtexEntry(entry); - - List result = autoCompleter.complete("k"); - Assert.assertEquals(Arrays.asList("key"), result); - } - - @Test - public void completeTooShortInputReturnsNothing() { - AutoCompletePreferences preferences = mock(AutoCompletePreferences.class); - when(preferences.getShortestLengthToComplete()).thenReturn(100); - BibtexKeyAutoCompleter autoCompleter = new BibtexKeyAutoCompleter(preferences); - - BibEntry entry = new BibEntry(); - entry.setCiteKey("testKey"); - autoCompleter.addBibtexEntry(entry); - - List result = autoCompleter.complete("test"); - Assert.assertEquals(Collections.emptyList(), result); - } -} diff --git a/src/test/java/org/jabref/logic/autocompleter/DefaultAutoCompleterTest.java b/src/test/java/org/jabref/logic/autocompleter/DefaultAutoCompleterTest.java deleted file mode 100644 index 95299367cc3..00000000000 --- a/src/test/java/org/jabref/logic/autocompleter/DefaultAutoCompleterTest.java +++ /dev/null @@ -1,207 +0,0 @@ -package org.jabref.logic.autocompleter; - -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -import org.jabref.model.entry.BibEntry; - -import org.junit.Assert; -import org.junit.Test; - -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -public class DefaultAutoCompleterTest { - - @SuppressWarnings("unused") - @Test(expected = NullPointerException.class) - public void initAutoCompleterWithNullPreferenceThrowsException() { - new DefaultAutoCompleter("field", null); - } - - @SuppressWarnings("unused") - @Test(expected = NullPointerException.class) - public void initAutoCompleterWithNullFieldThrowsException() { - new DefaultAutoCompleter(null, mock(AutoCompletePreferences.class)); - } - - @Test - public void completeWithoutAddingAnythingReturnsNothing() { - AutoCompletePreferences preferences = mock(AutoCompletePreferences.class); - DefaultAutoCompleter autoCompleter = new DefaultAutoCompleter("field", preferences); - - List result = autoCompleter.complete("test"); - Assert.assertEquals(Collections.emptyList(), result); - } - - @Test - public void completeAfterAddingNullReturnsNothing() { - AutoCompletePreferences preferences = mock(AutoCompletePreferences.class); - DefaultAutoCompleter autoCompleter = new DefaultAutoCompleter("field", preferences); - - autoCompleter.addBibtexEntry(null); - - List result = autoCompleter.complete("test"); - Assert.assertEquals(Collections.emptyList(), result); - } - - @Test - public void completeAfterAddingEmptyEntryReturnsNothing() { - AutoCompletePreferences preferences = mock(AutoCompletePreferences.class); - DefaultAutoCompleter autoCompleter = new DefaultAutoCompleter("field", preferences); - - BibEntry entry = new BibEntry(); - autoCompleter.addBibtexEntry(entry); - - List result = autoCompleter.complete("test"); - Assert.assertEquals(Collections.emptyList(), result); - } - - @Test - public void completeAfterAddingEntryWithoutFieldReturnsNothing() { - AutoCompletePreferences preferences = mock(AutoCompletePreferences.class); - DefaultAutoCompleter autoCompleter = new DefaultAutoCompleter("field", preferences); - - BibEntry entry = new BibEntry(); - entry.setField("title", "testTitle"); - autoCompleter.addBibtexEntry(entry); - - List result = autoCompleter.complete("test"); - Assert.assertEquals(Collections.emptyList(), result); - } - - @Test - public void completeValueReturnsValue() { - AutoCompletePreferences preferences = mock(AutoCompletePreferences.class); - DefaultAutoCompleter autoCompleter = new DefaultAutoCompleter("field", preferences); - - BibEntry entry = new BibEntry(); - entry.setField("field", "testValue"); - autoCompleter.addBibtexEntry(entry); - - List result = autoCompleter.complete("testValue"); - Assert.assertEquals(Arrays.asList("testValue"), result); - } - - @Test - public void completeBeginnigOfValueReturnsValue() { - AutoCompletePreferences preferences = mock(AutoCompletePreferences.class); - DefaultAutoCompleter autoCompleter = new DefaultAutoCompleter("field", preferences); - - BibEntry entry = new BibEntry(); - entry.setField("field", "testValue"); - autoCompleter.addBibtexEntry(entry); - - List result = autoCompleter.complete("test"); - Assert.assertEquals(Arrays.asList("testValue"), result); - } - - @Test - public void completeLowercaseValueReturnsValue() { - AutoCompletePreferences preferences = mock(AutoCompletePreferences.class); - DefaultAutoCompleter autoCompleter = new DefaultAutoCompleter("field", preferences); - - BibEntry entry = new BibEntry(); - entry.setField("field", "testValue"); - autoCompleter.addBibtexEntry(entry); - - List result = autoCompleter.complete("testvalue"); - Assert.assertEquals(Arrays.asList("testValue"), result); - } - - @Test - public void completeNullReturnsNothing() { - AutoCompletePreferences preferences = mock(AutoCompletePreferences.class); - DefaultAutoCompleter autoCompleter = new DefaultAutoCompleter("field", preferences); - - BibEntry entry = new BibEntry(); - entry.setField("field", "testKey"); - autoCompleter.addBibtexEntry(entry); - - List result = autoCompleter.complete(null); - Assert.assertEquals(Collections.emptyList(), result); - } - - @Test - public void completeEmptyStringReturnsNothing() { - AutoCompletePreferences preferences = mock(AutoCompletePreferences.class); - DefaultAutoCompleter autoCompleter = new DefaultAutoCompleter("field", preferences); - - BibEntry entry = new BibEntry(); - entry.setField("field", "testKey"); - autoCompleter.addBibtexEntry(entry); - - List result = autoCompleter.complete(""); - Assert.assertEquals(Collections.emptyList(), result); - } - - @Test - public void completeReturnsMultipleResults() { - AutoCompletePreferences preferences = mock(AutoCompletePreferences.class); - DefaultAutoCompleter autoCompleter = new DefaultAutoCompleter("field", preferences); - - BibEntry entryOne = new BibEntry(); - entryOne.setField("field", "testValueOne"); - autoCompleter.addBibtexEntry(entryOne); - BibEntry entryTwo = new BibEntry(); - entryTwo.setField("field", "testValueTwo"); - autoCompleter.addBibtexEntry(entryTwo); - - List result = autoCompleter.complete("testValue"); - Assert.assertEquals(Arrays.asList("testValueOne", "testValueTwo"), result); - } - - @Test - public void completeShortStringReturnsNothing() { - AutoCompletePreferences preferences = mock(AutoCompletePreferences.class); - DefaultAutoCompleter autoCompleter = new DefaultAutoCompleter("field", preferences); - - BibEntry entry = new BibEntry(); - entry.setField("field", "val"); - autoCompleter.addBibtexEntry(entry); - - List result = autoCompleter.complete("va"); - Assert.assertEquals(Collections.emptyList(), result); - } - - @Test - public void completeTooShortInputReturnsNothing() { - AutoCompletePreferences preferences = mock(AutoCompletePreferences.class); - when(preferences.getShortestLengthToComplete()).thenReturn(100); - DefaultAutoCompleter autoCompleter = new DefaultAutoCompleter("field", preferences); - - BibEntry entry = new BibEntry(); - entry.setField("field", "testValue"); - autoCompleter.addBibtexEntry(entry); - - List result = autoCompleter.complete("test"); - Assert.assertEquals(Collections.emptyList(), result); - } - - @Test - public void completeBeginnigOfSecondWordReturnsWord() { - AutoCompletePreferences preferences = mock(AutoCompletePreferences.class); - DefaultAutoCompleter autoCompleter = new DefaultAutoCompleter("field", preferences); - - BibEntry entry = new BibEntry(); - entry.setField("field", "test value"); - autoCompleter.addBibtexEntry(entry); - - List result = autoCompleter.complete("val"); - Assert.assertEquals(Arrays.asList("value"), result); - } - - @Test - public void completePartOfWordReturnsNothing() { - AutoCompletePreferences preferences = mock(AutoCompletePreferences.class); - DefaultAutoCompleter autoCompleter = new DefaultAutoCompleter("field", preferences); - - BibEntry entry = new BibEntry(); - entry.setField("field", "test value"); - autoCompleter.addBibtexEntry(entry); - - List result = autoCompleter.complete("lue"); - Assert.assertEquals(Collections.emptyList(), result); - } -} diff --git a/src/test/java/org/jabref/logic/autocompleter/EntireFieldAutoCompleterTest.java b/src/test/java/org/jabref/logic/autocompleter/EntireFieldAutoCompleterTest.java deleted file mode 100644 index 5209d0b0764..00000000000 --- a/src/test/java/org/jabref/logic/autocompleter/EntireFieldAutoCompleterTest.java +++ /dev/null @@ -1,220 +0,0 @@ -package org.jabref.logic.autocompleter; - -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -import org.jabref.model.entry.BibEntry; - -import org.junit.Assert; -import org.junit.Test; - -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -public class EntireFieldAutoCompleterTest { - - @SuppressWarnings("unused") - @Test(expected = NullPointerException.class) - public void initAutoCompleterWithNullPreferenceThrowsException() { - new EntireFieldAutoCompleter("field", null); - } - - @SuppressWarnings("unused") - @Test(expected = NullPointerException.class) - public void initAutoCompleterWithNullFieldThrowsException() { - new EntireFieldAutoCompleter(null, mock(AutoCompletePreferences.class)); - } - - @Test - public void completeWithoutAddingAnythingReturnsNothing() { - AutoCompletePreferences preferences = mock(AutoCompletePreferences.class); - EntireFieldAutoCompleter autoCompleter = new EntireFieldAutoCompleter("field", preferences); - - List result = autoCompleter.complete("test"); - Assert.assertEquals(Collections.emptyList(), result); - } - - @Test - public void completeAfterAddingNullReturnsNothing() { - AutoCompletePreferences preferences = mock(AutoCompletePreferences.class); - EntireFieldAutoCompleter autoCompleter = new EntireFieldAutoCompleter("field", preferences); - - autoCompleter.addBibtexEntry(null); - - List result = autoCompleter.complete("test"); - Assert.assertEquals(Collections.emptyList(), result); - } - - @Test - public void completeAfterAddingEmptyEntryReturnsNothing() { - AutoCompletePreferences preferences = mock(AutoCompletePreferences.class); - EntireFieldAutoCompleter autoCompleter = new EntireFieldAutoCompleter("field", preferences); - - BibEntry entry = new BibEntry(); - autoCompleter.addBibtexEntry(entry); - - List result = autoCompleter.complete("test"); - Assert.assertEquals(Collections.emptyList(), result); - } - - @Test - public void completeAfterAddingEntryWithoutFieldReturnsNothing() { - AutoCompletePreferences preferences = mock(AutoCompletePreferences.class); - EntireFieldAutoCompleter autoCompleter = new EntireFieldAutoCompleter("field", preferences); - - BibEntry entry = new BibEntry(); - entry.setField("title", "testTitle"); - autoCompleter.addBibtexEntry(entry); - - List result = autoCompleter.complete("test"); - Assert.assertEquals(Collections.emptyList(), result); - } - - @Test - public void completeValueReturnsValue() { - AutoCompletePreferences preferences = mock(AutoCompletePreferences.class); - EntireFieldAutoCompleter autoCompleter = new EntireFieldAutoCompleter("field", preferences); - - BibEntry entry = new BibEntry(); - entry.setField("field", "testValue"); - autoCompleter.addBibtexEntry(entry); - - List result = autoCompleter.complete("testValue"); - Assert.assertEquals(Arrays.asList("testValue"), result); - } - - @Test - public void completeBeginnigOfValueReturnsValue() { - AutoCompletePreferences preferences = mock(AutoCompletePreferences.class); - EntireFieldAutoCompleter autoCompleter = new EntireFieldAutoCompleter("field", preferences); - - BibEntry entry = new BibEntry(); - entry.setField("field", "testValue"); - autoCompleter.addBibtexEntry(entry); - - List result = autoCompleter.complete("test"); - Assert.assertEquals(Arrays.asList("testValue"), result); - } - - @Test - public void completeLowercaseValueReturnsValue() { - AutoCompletePreferences preferences = mock(AutoCompletePreferences.class); - EntireFieldAutoCompleter autoCompleter = new EntireFieldAutoCompleter("field", preferences); - - BibEntry entry = new BibEntry(); - entry.setField("field", "testValue"); - autoCompleter.addBibtexEntry(entry); - - List result = autoCompleter.complete("testvalue"); - Assert.assertEquals(Arrays.asList("testValue"), result); - } - - @Test - public void completeNullReturnsNothing() { - AutoCompletePreferences preferences = mock(AutoCompletePreferences.class); - EntireFieldAutoCompleter autoCompleter = new EntireFieldAutoCompleter("field", preferences); - - BibEntry entry = new BibEntry(); - entry.setField("field", "testKey"); - autoCompleter.addBibtexEntry(entry); - - List result = autoCompleter.complete(null); - Assert.assertEquals(Collections.emptyList(), result); - } - - @Test - public void completeEmptyStringReturnsNothing() { - AutoCompletePreferences preferences = mock(AutoCompletePreferences.class); - EntireFieldAutoCompleter autoCompleter = new EntireFieldAutoCompleter("field", preferences); - - BibEntry entry = new BibEntry(); - entry.setField("field", "testKey"); - autoCompleter.addBibtexEntry(entry); - - List result = autoCompleter.complete(""); - Assert.assertEquals(Collections.emptyList(), result); - } - - @Test - public void completeReturnsMultipleResults() { - AutoCompletePreferences preferences = mock(AutoCompletePreferences.class); - EntireFieldAutoCompleter autoCompleter = new EntireFieldAutoCompleter("field", preferences); - - BibEntry entryOne = new BibEntry(); - entryOne.setField("field", "testValueOne"); - autoCompleter.addBibtexEntry(entryOne); - BibEntry entryTwo = new BibEntry(); - entryTwo.setField("field", "testValueTwo"); - autoCompleter.addBibtexEntry(entryTwo); - - List result = autoCompleter.complete("testValue"); - Assert.assertEquals(Arrays.asList("testValueOne", "testValueTwo"), result); - } - - @Test - public void completeShortStringReturnsNothing() { - AutoCompletePreferences preferences = mock(AutoCompletePreferences.class); - EntireFieldAutoCompleter autoCompleter = new EntireFieldAutoCompleter("field", preferences); - - BibEntry entry = new BibEntry(); - entry.setField("field", "val"); - autoCompleter.addBibtexEntry(entry); - - List result = autoCompleter.complete("va"); - Assert.assertEquals(Collections.emptyList(), result); - } - - @Test - public void completeTooShortInputReturnsNothing() { - AutoCompletePreferences preferences = mock(AutoCompletePreferences.class); - when(preferences.getShortestLengthToComplete()).thenReturn(100); - EntireFieldAutoCompleter autoCompleter = new EntireFieldAutoCompleter("field", preferences); - - BibEntry entry = new BibEntry(); - entry.setField("field", "testValue"); - autoCompleter.addBibtexEntry(entry); - - List result = autoCompleter.complete("test"); - Assert.assertEquals(Collections.emptyList(), result); - } - - @Test - public void completeBeginnigOfSecondWordReturnsNothing() { - AutoCompletePreferences preferences = mock(AutoCompletePreferences.class); - EntireFieldAutoCompleter autoCompleter = new EntireFieldAutoCompleter("field", preferences); - - BibEntry entry = new BibEntry(); - entry.setField("field", "test value"); - autoCompleter.addBibtexEntry(entry); - - List result = autoCompleter.complete("val"); - Assert.assertEquals(Collections.emptyList(), result); - } - - @Test - public void completePartOfWordReturnsNothing() { - AutoCompletePreferences preferences = mock(AutoCompletePreferences.class); - EntireFieldAutoCompleter autoCompleter = new EntireFieldAutoCompleter("field", preferences); - - BibEntry entry = new BibEntry(); - entry.setField("field", "test value"); - autoCompleter.addBibtexEntry(entry); - - List result = autoCompleter.complete("lue"); - Assert.assertEquals(Collections.emptyList(), result); - } - - @Test - public void completeReturnsWholeFieldValue() { - AutoCompletePreferences preferences = mock(AutoCompletePreferences.class); - EntireFieldAutoCompleter autoCompleter = new EntireFieldAutoCompleter("field", preferences); - - BibEntry entry = new BibEntry(); - entry.setField("field", "test value"); - autoCompleter.addBibtexEntry(entry); - - List result = autoCompleter.complete("te"); - Assert.assertEquals(Arrays.asList("test value"), result); - } -} diff --git a/src/test/java/org/jabref/logic/autocompleter/NameFieldAutoCompleterTest.java b/src/test/java/org/jabref/logic/autocompleter/NameFieldAutoCompleterTest.java deleted file mode 100644 index 4c7fcb5ab13..00000000000 --- a/src/test/java/org/jabref/logic/autocompleter/NameFieldAutoCompleterTest.java +++ /dev/null @@ -1,357 +0,0 @@ -package org.jabref.logic.autocompleter; - -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -import org.jabref.model.entry.BibEntry; - -import org.junit.Assert; -import org.junit.Test; - -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -public class NameFieldAutoCompleterTest { - - @SuppressWarnings("unused") - @Test(expected = NullPointerException.class) - public void initAutoCompleterWithNullPreferenceThrowsException() { - new NameFieldAutoCompleter("field", null); - } - - @SuppressWarnings("unused") - @Test(expected = NullPointerException.class) - public void initAutoCompleterWithNullFieldThrowsException() { - new NameFieldAutoCompleter(null, mock(AutoCompletePreferences.class)); - } - - @Test - public void completeWithoutAddingAnythingReturnsNothing() { - AutoCompletePreferences preferences = mock(AutoCompletePreferences.class); - NameFieldAutoCompleter autoCompleter = new NameFieldAutoCompleter("field", preferences); - - List result = autoCompleter.complete("test"); - Assert.assertEquals(Collections.emptyList(), result); - } - - @Test - public void completeAfterAddingNullReturnsNothing() { - AutoCompletePreferences preferences = mock(AutoCompletePreferences.class); - NameFieldAutoCompleter autoCompleter = new NameFieldAutoCompleter("field", preferences); - - autoCompleter.addBibtexEntry(null); - - List result = autoCompleter.complete("test"); - Assert.assertEquals(Collections.emptyList(), result); - } - - @Test - public void completeAfterAddingEmptyEntryReturnsNothing() { - AutoCompletePreferences preferences = mock(AutoCompletePreferences.class); - NameFieldAutoCompleter autoCompleter = new NameFieldAutoCompleter("field", preferences); - - BibEntry entry = new BibEntry(); - autoCompleter.addBibtexEntry(entry); - - List result = autoCompleter.complete("test"); - Assert.assertEquals(Collections.emptyList(), result); - } - - @Test - public void completeAfterAddingEntryWithoutFieldReturnsNothing() { - AutoCompletePreferences preferences = mock(AutoCompletePreferences.class); - NameFieldAutoCompleter autoCompleter = new NameFieldAutoCompleter("field", preferences); - - BibEntry entry = new BibEntry(); - entry.setField("title", "testTitle"); - autoCompleter.addBibtexEntry(entry); - - List result = autoCompleter.complete("test"); - Assert.assertEquals(Collections.emptyList(), result); - } - - @Test - public void completeNameReturnsName() { - AutoCompletePreferences preferences = mock(AutoCompletePreferences.class); - NameFieldAutoCompleter autoCompleter = new NameFieldAutoCompleter("field", preferences); - - BibEntry entry = new BibEntry(); - entry.setField("field", "Testname"); - autoCompleter.addBibtexEntry(entry); - - List result = autoCompleter.complete("Testname"); - Assert.assertEquals(Arrays.asList("Testname"), result); - } - - @Test - public void completeBeginnigOfNameReturnsName() { - AutoCompletePreferences preferences = mock(AutoCompletePreferences.class); - NameFieldAutoCompleter autoCompleter = new NameFieldAutoCompleter("field", preferences); - - BibEntry entry = new BibEntry(); - entry.setField("field", "Testname"); - autoCompleter.addBibtexEntry(entry); - - List result = autoCompleter.complete("Test"); - Assert.assertEquals(Arrays.asList("Testname"), result); - } - - @Test - public void completeLowercaseNameReturnsName() { - AutoCompletePreferences preferences = mock(AutoCompletePreferences.class); - NameFieldAutoCompleter autoCompleter = new NameFieldAutoCompleter("field", preferences); - - BibEntry entry = new BibEntry(); - entry.setField("field", "Testname"); - autoCompleter.addBibtexEntry(entry); - - List result = autoCompleter.complete("test"); - Assert.assertEquals(Arrays.asList("Testname"), result); - } - - @Test - public void completeNullReturnsNothing() { - AutoCompletePreferences preferences = mock(AutoCompletePreferences.class); - NameFieldAutoCompleter autoCompleter = new NameFieldAutoCompleter("field", preferences); - - BibEntry entry = new BibEntry(); - entry.setField("field", "testKey"); - autoCompleter.addBibtexEntry(entry); - - List result = autoCompleter.complete(null); - Assert.assertEquals(Collections.emptyList(), result); - } - - @Test - public void completeEmptyStringReturnsNothing() { - AutoCompletePreferences preferences = mock(AutoCompletePreferences.class); - NameFieldAutoCompleter autoCompleter = new NameFieldAutoCompleter("field", preferences); - - BibEntry entry = new BibEntry(); - entry.setField("field", "testKey"); - autoCompleter.addBibtexEntry(entry); - - List result = autoCompleter.complete(""); - Assert.assertEquals(Collections.emptyList(), result); - } - - @Test - public void completeReturnsMultipleResults() { - AutoCompletePreferences preferences = mock(AutoCompletePreferences.class); - NameFieldAutoCompleter autoCompleter = new NameFieldAutoCompleter("field", preferences); - - BibEntry entryOne = new BibEntry(); - entryOne.setField("field", "testNameOne"); - autoCompleter.addBibtexEntry(entryOne); - BibEntry entryTwo = new BibEntry(); - entryTwo.setField("field", "testNameTwo"); - autoCompleter.addBibtexEntry(entryTwo); - - List result = autoCompleter.complete("testName"); - Assert.assertEquals(Arrays.asList("testNameOne", "testNameTwo"), result); - } - - @Test - public void completeTooShortInputReturnsNothing() { - AutoCompletePreferences preferences = mock(AutoCompletePreferences.class); - when(preferences.getShortestLengthToComplete()).thenReturn(100); - NameFieldAutoCompleter autoCompleter = new NameFieldAutoCompleter("field", preferences); - - BibEntry entry = new BibEntry(); - entry.setField("field", "Testname"); - autoCompleter.addBibtexEntry(entry); - - List result = autoCompleter.complete("test"); - Assert.assertEquals(Collections.emptyList(), result); - } - - @Test - public void completePartOfNameReturnsNothing() { - AutoCompletePreferences preferences = mock(AutoCompletePreferences.class); - NameFieldAutoCompleter autoCompleter = new NameFieldAutoCompleter("field", preferences); - - BibEntry entry = new BibEntry(); - entry.setField("field", "Vassilis Kostakos"); - autoCompleter.addBibtexEntry(entry); - - List result = autoCompleter.complete("osta"); - Assert.assertEquals(Collections.emptyList(), result); - } - - @Test - public void completeBeginningOfFirstNameReturnsCompleteName() { - AutoCompletePreferences preferences = mock(AutoCompletePreferences.class); - NameFieldAutoCompleter autoCompleter = new NameFieldAutoCompleter("field", preferences); - - BibEntry entry = new BibEntry(); - entry.setField("field", "Vassilis Kostakos"); - autoCompleter.addBibtexEntry(entry); - - List result = autoCompleter.complete("Vas"); - Assert.assertEquals(Arrays.asList("Vassilis Kostakos"), result); - } - - @Test - public void completeBeginningOfFirstNameReturnsCompleteNameWithJr() { - AutoCompletePreferences preferences = mock(AutoCompletePreferences.class); - NameFieldAutoCompleter autoCompleter = new NameFieldAutoCompleter("field", preferences); - - BibEntry entry = new BibEntry(); - entry.setField("field", "Reagle, Jr., Joseph M."); - autoCompleter.addBibtexEntry(entry); - - List result = autoCompleter.complete("Jos"); - Assert.assertEquals(Arrays.asList("Joseph M. Reagle, Jr."), result); - } - - @Test - public void completeBeginningOfFirstNameReturnsCompleteNameWithVon() { - AutoCompletePreferences preferences = mock(AutoCompletePreferences.class); - NameFieldAutoCompleter autoCompleter = new NameFieldAutoCompleter("field", preferences); - - BibEntry entry = new BibEntry(); - entry.setField("field", "Eric von Hippel"); - autoCompleter.addBibtexEntry(entry); - - List result = autoCompleter.complete("Eric"); - Assert.assertEquals(Arrays.asList("Eric von Hippel"), result); - } - - @Test - public void completeBeginningOfLastNameReturnsNameWithUmlauts() { - AutoCompletePreferences preferences = mock(AutoCompletePreferences.class); - when(preferences.getFirstnameMode()).thenReturn(AutoCompleteFirstNameMode.ONLY_FULL); - NameFieldAutoCompleter autoCompleter = new NameFieldAutoCompleter("field", preferences); - - BibEntry entry = new BibEntry(); - entry.setField("field", "Honig Bär"); - autoCompleter.addBibtexEntry(entry); - - List result = autoCompleter.complete("Bä"); - Assert.assertEquals(Arrays.asList("Bär, Honig"), result); - } - - @Test - public void completeBeginningOfLastNameReturnsNameAndNameWithInitialFirstname() { - AutoCompletePreferences preferences = mock(AutoCompletePreferences.class); - NameFieldAutoCompleter autoCompleter = new NameFieldAutoCompleter("field", preferences); - - BibEntry entry = new BibEntry(); - entry.setField("field", "Vassilis Kostakos"); - autoCompleter.addBibtexEntry(entry); - - List result = autoCompleter.complete("Kosta"); - Assert.assertEquals(Arrays.asList("Kostakos, V.", "Kostakos, Vassilis"), result); - } - - @Test - public void completeBeginningOfLastNameReturnsNameIfPref() { - AutoCompletePreferences preferences = mock(AutoCompletePreferences.class); - when(preferences.getFirstnameMode()).thenReturn(AutoCompleteFirstNameMode.ONLY_FULL); - NameFieldAutoCompleter autoCompleter = new NameFieldAutoCompleter("field", preferences); - - BibEntry entry = new BibEntry(); - entry.setField("field", "Vassilis Kostakos"); - autoCompleter.addBibtexEntry(entry); - - List result = autoCompleter.complete("Kosta"); - Assert.assertEquals(Arrays.asList("Kostakos, Vassilis"), result); - } - - @Test - public void completeBeginningOfLastNameReturnsNameWithJrIfPref() { - AutoCompletePreferences preferences = mock(AutoCompletePreferences.class); - when(preferences.getFirstnameMode()).thenReturn(AutoCompleteFirstNameMode.ONLY_ABBREVIATED); - NameFieldAutoCompleter autoCompleter = new NameFieldAutoCompleter("field", preferences); - - BibEntry entry = new BibEntry(); - entry.setField("field", "Reagle, Jr., Joseph M."); - autoCompleter.addBibtexEntry(entry); - - List result = autoCompleter.complete("Rea"); - Assert.assertEquals(Arrays.asList("Reagle, Jr., J. M."), result); - } - - @Test - public void completeBeginningOfLastNameReturnsNameWithInitialFirstnameIfPref() { - AutoCompletePreferences preferences = mock(AutoCompletePreferences.class); - when(preferences.getFirstnameMode()).thenReturn(AutoCompleteFirstNameMode.ONLY_ABBREVIATED); - NameFieldAutoCompleter autoCompleter = new NameFieldAutoCompleter("field", preferences); - - BibEntry entry = new BibEntry(); - entry.setField("field", "Vassilis Kostakos"); - autoCompleter.addBibtexEntry(entry); - - List result = autoCompleter.complete("Kosta"); - Assert.assertEquals(Arrays.asList("Kostakos, V."), result); - } - - @Test - public void completeVonReturnsNameWithInitialFirstnameIfPref() { - AutoCompletePreferences preferences = mock(AutoCompletePreferences.class); - when(preferences.getFirstnameMode()).thenReturn(AutoCompleteFirstNameMode.ONLY_ABBREVIATED); - NameFieldAutoCompleter autoCompleter = new NameFieldAutoCompleter("field", preferences); - - BibEntry entry = new BibEntry(); - entry.setField("field", "Eric von Hippel"); - autoCompleter.addBibtexEntry(entry); - - List result = autoCompleter.complete("von"); - Assert.assertEquals(Arrays.asList("von Hippel, E."), result); - } - - @Test - public void completeBeginningOfNameReturnsCompleteName() { - AutoCompletePreferences preferences = mock(AutoCompletePreferences.class); - NameFieldAutoCompleter autoCompleter = new NameFieldAutoCompleter("field", preferences); - - BibEntry entry = new BibEntry(); - entry.setField("field", "Vassilis Kostakos"); - autoCompleter.addBibtexEntry(entry); - - List result = autoCompleter.complete("Kostakos, Va"); - Assert.assertEquals(Arrays.asList("Kostakos, Vassilis"), result); - } - - @Test - public void completeBeginningOfLastNameReturnsNothingIfPref() { - AutoCompletePreferences preferences = mock(AutoCompletePreferences.class); - when(preferences.getOnlyCompleteFirstLast()).thenReturn(true); - NameFieldAutoCompleter autoCompleter = new NameFieldAutoCompleter("field", preferences); - - BibEntry entry = new BibEntry(); - entry.setField("field", "Vassilis Kostakos"); - autoCompleter.addBibtexEntry(entry); - - List result = autoCompleter.complete("Kosta"); - Assert.assertEquals(Collections.emptyList(), result); - } - - @Test - public void completeBeginningOfFirstNameReturnsNothingIfPref() { - AutoCompletePreferences preferences = mock(AutoCompletePreferences.class); - when(preferences.getOnlyCompleteLastFirst()).thenReturn(true); - NameFieldAutoCompleter autoCompleter = new NameFieldAutoCompleter("field", preferences); - - BibEntry entry = new BibEntry(); - entry.setField("field", "Vassilis Kostakos"); - autoCompleter.addBibtexEntry(entry); - - List result = autoCompleter.complete("Vas"); - Assert.assertEquals(Collections.emptyList(), result); - } - - @Test - public void completeShortNameReturnsName() { - AutoCompletePreferences preferences = mock(AutoCompletePreferences.class); - NameFieldAutoCompleter autoCompleter = new NameFieldAutoCompleter("field", preferences); - - BibEntry entry = new BibEntry(); - entry.setField("field", "nam"); - autoCompleter.addBibtexEntry(entry); - - List result = autoCompleter.complete("n"); - Assert.assertEquals(Arrays.asList("nam"), result); - } -} diff --git a/src/test/java/org/jabref/logic/bibtex/DuplicateCheckTest.java b/src/test/java/org/jabref/logic/bibtex/DuplicateCheckTest.java index ea4b7d42952..ab802782ddf 100644 --- a/src/test/java/org/jabref/logic/bibtex/DuplicateCheckTest.java +++ b/src/test/java/org/jabref/logic/bibtex/DuplicateCheckTest.java @@ -142,4 +142,83 @@ public void twoEntriesWithSameDoiButDifferentTypesAreDuplicates() { assertTrue(DuplicateCheck.isDuplicate(simpleArticle, duplicateWithDifferentType, BibDatabaseMode.BIBTEX)); } + @Test + public void twoBooksWithDifferentEditionsAreNotDuplicates() { + BibEntry editionOne = new BibEntry(BibtexEntryTypes.BOOK.getName()); + editionOne.setField(FieldName.TITLE, "Effective Java"); + editionOne.setField(FieldName.AUTHOR, "Bloch, Joshua"); + editionOne.setField(FieldName.PUBLISHER, "Prentice Hall"); + editionOne.setField(FieldName.DATE, "2001"); + editionOne.setField(FieldName.EDITION, "1"); + + BibEntry editionTwo = new BibEntry(BibtexEntryTypes.BOOK.getName()); + editionTwo.setField(FieldName.TITLE, "Effective Java"); + editionTwo.setField(FieldName.AUTHOR, "Bloch, Joshua"); + editionTwo.setField(FieldName.PUBLISHER, "Prentice Hall"); + editionTwo.setField(FieldName.DATE, "2008"); + editionTwo.setField(FieldName.EDITION, "2"); + + assertFalse(DuplicateCheck.isDuplicate(editionOne, editionTwo, BibDatabaseMode.BIBTEX)); + } + + @Test + public void sameBooksWithMissingEditionAreDuplicates() { + BibEntry editionOne = new BibEntry(BibtexEntryTypes.BOOK.getName()); + editionOne.setField(FieldName.TITLE, "Effective Java"); + editionOne.setField(FieldName.AUTHOR, "Bloch, Joshua"); + editionOne.setField(FieldName.PUBLISHER, "Prentice Hall"); + editionOne.setField(FieldName.DATE, "2001"); + + BibEntry editionTwo = new BibEntry(BibtexEntryTypes.BOOK.getName()); + editionTwo.setField(FieldName.TITLE, "Effective Java"); + editionTwo.setField(FieldName.AUTHOR, "Bloch, Joshua"); + editionTwo.setField(FieldName.PUBLISHER, "Prentice Hall"); + editionTwo.setField(FieldName.DATE, "2008"); + + assertTrue(DuplicateCheck.isDuplicate(editionOne, editionTwo, BibDatabaseMode.BIBTEX)); + } + + @Test + public void sameBooksWithPartiallyMissingEditionAreDuplicates() { + BibEntry editionOne = new BibEntry(BibtexEntryTypes.BOOK.getName()); + editionOne.setField(FieldName.TITLE, "Effective Java"); + editionOne.setField(FieldName.AUTHOR, "Bloch, Joshua"); + editionOne.setField(FieldName.PUBLISHER, "Prentice Hall"); + editionOne.setField(FieldName.DATE, "2001"); + + BibEntry editionTwo = new BibEntry(BibtexEntryTypes.BOOK.getName()); + editionTwo.setField(FieldName.TITLE, "Effective Java"); + editionTwo.setField(FieldName.AUTHOR, "Bloch, Joshua"); + editionTwo.setField(FieldName.PUBLISHER, "Prentice Hall"); + editionTwo.setField(FieldName.DATE, "2008"); + editionTwo.setField(FieldName.EDITION, "2"); + + assertTrue(DuplicateCheck.isDuplicate(editionOne, editionTwo, BibDatabaseMode.BIBTEX)); + } + + @Test + public void sameBooksWithDifferentEditionsAreNotDuplicates() { + BibEntry editionTwo = new BibEntry(BibtexEntryTypes.BOOK.getName()); + editionTwo.setCiteKey("Sutton17reinfLrnIntroBook"); + editionTwo.setField(FieldName.TITLE, "Reinforcement learning:An introduction"); + editionTwo.setField(FieldName.PUBLISHER, "MIT Press"); + editionTwo.setField(FieldName.YEAR, "2017"); + editionTwo.setField(FieldName.AUTHOR, "Sutton, Richard S and Barto, Andrew G"); + editionTwo.setField(FieldName.ADDRESS, "Cambridge, MA.USA"); + editionTwo.setField(FieldName.EDITION, "Second"); + editionTwo.setField(FieldName.JOURNAL, "MIT Press"); + editionTwo.setField(FieldName.URL, "https://webdocs.cs.ualberta.ca/~sutton/book/the-book-2nd.html"); + + BibEntry editionOne = new BibEntry(BibtexEntryTypes.BOOK.getName()); + editionOne.setCiteKey("Sutton98reinfLrnIntroBook"); + editionOne.setField(FieldName.TITLE, "Reinforcement learning: An introduction"); + editionOne.setField(FieldName.PUBLISHER, "MIT press Cambridge"); + editionOne.setField(FieldName.YEAR, "1998"); + editionOne.setField(FieldName.AUTHOR, "Sutton, Richard S and Barto, Andrew G"); + editionOne.setField(FieldName.VOLUME, "1"); + editionOne.setField(FieldName.NUMBER, "1"); + editionOne.setField(FieldName.EDITION, "First"); + + assertFalse(DuplicateCheck.isDuplicate(editionOne, editionTwo, BibDatabaseMode.BIBTEX)); + } } diff --git a/src/test/java/org/jabref/logic/citationstyle/CitationStyleTest.java b/src/test/java/org/jabref/logic/citationstyle/CitationStyleTest.java index 81948dd8996..79d8f19cf1d 100644 --- a/src/test/java/org/jabref/logic/citationstyle/CitationStyleTest.java +++ b/src/test/java/org/jabref/logic/citationstyle/CitationStyleTest.java @@ -24,7 +24,7 @@ public void testDefaultCitation() { "BibTeX Journal, vol. 34, no. 3, pp. 45–67, Jul. 2016.\n" + " \n"; - Assert.assertEquals(citation, expected); + Assert.assertEquals(expected, citation); } } diff --git a/src/test/java/org/jabref/logic/formatter/bibtexfields/NormalizeNamesFormatterTest.java b/src/test/java/org/jabref/logic/formatter/bibtexfields/NormalizeNamesFormatterTest.java index 85be96b730f..05498b0b9d8 100644 --- a/src/test/java/org/jabref/logic/formatter/bibtexfields/NormalizeNamesFormatterTest.java +++ b/src/test/java/org/jabref/logic/formatter/bibtexfields/NormalizeNamesFormatterTest.java @@ -111,7 +111,7 @@ public void testOddCountOfCommas() { @Test public void formatExample() { - assertEquals(formatter.format(formatter.getExampleInput()), "Einstein, Albert and Turing, Alan"); + assertEquals("Einstein, Albert and Turing, Alan", formatter.format(formatter.getExampleInput())); } @Test diff --git a/src/test/java/org/jabref/logic/importer/fetcher/DiVATest.java b/src/test/java/org/jabref/logic/importer/fetcher/DiVATest.java index ef075780195..60deeaa8544 100644 --- a/src/test/java/org/jabref/logic/importer/fetcher/DiVATest.java +++ b/src/test/java/org/jabref/logic/importer/fetcher/DiVATest.java @@ -51,6 +51,8 @@ public void testPerformSearchById() throws Exception { entry.setField("title", "Lower bounds for constant multiplication problems"); entry.setField("volume", "54"); entry.setField("year", "2007"); + entry.setField("abstract", "Lower bounds for problems related to realizing multiplication by constants with shifts, adders, and subtracters are presented. These lower bounds are straightforwardly calculated and have applications in proving the optimality of solutions obtained by heuristics. "); + entry.setField("doi", "10.1109/TCSII.2007.903212"); assertEquals(Optional.of(entry), fetcher.performSearchById("diva2:260746")); } diff --git a/src/test/java/org/jabref/logic/importer/fetcher/LibraryOfCongressTest.java b/src/test/java/org/jabref/logic/importer/fetcher/LibraryOfCongressTest.java index 4ee2dca2d8c..df7e0fd1748 100644 --- a/src/test/java/org/jabref/logic/importer/fetcher/LibraryOfCongressTest.java +++ b/src/test/java/org/jabref/logic/importer/fetcher/LibraryOfCongressTest.java @@ -3,11 +3,14 @@ import java.util.Optional; import org.jabref.model.entry.BibEntry; +import org.jabref.testutils.category.FetcherTests; import org.junit.Test; +import org.junit.experimental.categories.Category; import static org.junit.Assert.assertEquals; +@Category(FetcherTests.class) public class LibraryOfCongressTest { private LibraryOfCongress fetcher = new LibraryOfCongress(); diff --git a/src/test/java/org/jabref/logic/importer/fileformat/BiblioscapeImporterTest.java b/src/test/java/org/jabref/logic/importer/fileformat/BiblioscapeImporterTest.java index 6ec41409236..0948e0c6c89 100644 --- a/src/test/java/org/jabref/logic/importer/fileformat/BiblioscapeImporterTest.java +++ b/src/test/java/org/jabref/logic/importer/fileformat/BiblioscapeImporterTest.java @@ -23,7 +23,7 @@ public void setUp() throws Exception { @Test public void testGetFormatName() { - Assert.assertEquals(importer.getName(), "Biblioscape"); + Assert.assertEquals("Biblioscape", importer.getName()); } @Test @@ -39,7 +39,7 @@ public void testGetDescription() { @Test public void testGetCLIID() { - Assert.assertEquals(importer.getId(), "biblioscape"); + Assert.assertEquals("biblioscape", importer.getId()); } @Test diff --git a/src/test/java/org/jabref/logic/importer/fileformat/BibtexParserTest.java b/src/test/java/org/jabref/logic/importer/fileformat/BibtexParserTest.java index d2124b9473f..ece99a4723e 100644 --- a/src/test/java/org/jabref/logic/importer/fileformat/BibtexParserTest.java +++ b/src/test/java/org/jabref/logic/importer/fileformat/BibtexParserTest.java @@ -346,7 +346,8 @@ public void parseRecognizesEntryPrecedingComment() throws IOException { List parsed = result.getDatabase().getEntries(); - BibEntry expected = new BibEntry("article").withField(BibEntry.KEY_FIELD, "test") + BibEntry expected = new BibEntry("article") + .withField(BibEntry.KEY_FIELD, "test") .withField("author", "Ed von T@st"); expected.setCommentsBeforeEntry(comment); diff --git a/src/test/java/org/jabref/logic/importer/fileformat/IsiImporterTest.java b/src/test/java/org/jabref/logic/importer/fileformat/IsiImporterTest.java index 3f62db57939..69dc92b07d0 100644 --- a/src/test/java/org/jabref/logic/importer/fileformat/IsiImporterTest.java +++ b/src/test/java/org/jabref/logic/importer/fileformat/IsiImporterTest.java @@ -39,12 +39,12 @@ public void testParseMonthException() { @Test public void testGetFormatName() { - assertEquals(importer.getName(), "ISI"); + assertEquals("ISI", importer.getName()); } @Test public void testGetCLIId() { - assertEquals(importer.getId(), "isi"); + assertEquals("isi", importer.getId()); } @Test diff --git a/src/test/java/org/jabref/logic/importer/fileformat/RISImporterTest.java b/src/test/java/org/jabref/logic/importer/fileformat/RISImporterTest.java index 64d8ff8d03a..6760f058c11 100644 --- a/src/test/java/org/jabref/logic/importer/fileformat/RISImporterTest.java +++ b/src/test/java/org/jabref/logic/importer/fileformat/RISImporterTest.java @@ -24,12 +24,12 @@ public void setUp() { @Test public void testGetFormatName() { - Assert.assertEquals(importer.getName(), "RIS"); + Assert.assertEquals("RIS", importer.getName()); } @Test public void testGetCLIId() { - Assert.assertEquals(importer.getId(), "ris"); + Assert.assertEquals("ris", importer.getId()); } @Test diff --git a/src/test/java/org/jabref/logic/journals/JournalAbbreviationRepositoryTest.java b/src/test/java/org/jabref/logic/journals/JournalAbbreviationRepositoryTest.java index 4df5f2b6896..822ce6d1ce3 100644 --- a/src/test/java/org/jabref/logic/journals/JournalAbbreviationRepositoryTest.java +++ b/src/test/java/org/jabref/logic/journals/JournalAbbreviationRepositoryTest.java @@ -40,15 +40,6 @@ public void oneElement() { } - @Test - public void testSorting() { - JournalAbbreviationRepository repository = new JournalAbbreviationRepository(); - repository.addEntry(new Abbreviation("Long Name", "L. N.")); - repository.addEntry(new Abbreviation("A Long Name", "AL. N.")); - assertEquals("A Long Name", repository.getAbbreviations().first().getName()); - assertEquals("Long Name", repository.getAbbreviations().last().getName()); - } - @Test public void testDuplicates() { JournalAbbreviationRepository repository = new JournalAbbreviationRepository(); @@ -63,9 +54,6 @@ public void testDuplicatesIsoOnly() { repository.addEntry(new Abbreviation("Old Long Name", "L. N.")); repository.addEntry(new Abbreviation("New Long Name", "L. N.")); assertEquals(2, repository.size()); - - assertEquals("L N", repository.getNextAbbreviation("L. N.").orElse("WRONG")); - assertEquals("New Long Name", repository.getNextAbbreviation("L N").orElse("WRONG")); } @Test diff --git a/src/test/java/org/jabref/logic/l10n/LanguagesTest.java b/src/test/java/org/jabref/logic/l10n/LanguagesTest.java index 8d3257dd10b..b5ba7710fc2 100644 --- a/src/test/java/org/jabref/logic/l10n/LanguagesTest.java +++ b/src/test/java/org/jabref/logic/l10n/LanguagesTest.java @@ -6,8 +6,10 @@ import org.junit.Test; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; public class LanguagesTest { + @Test public void convertKnownLanguageOnly() { assertEquals(Optional.of(new Locale("en")), Languages.convertToSupportedLocale("en")); @@ -18,14 +20,26 @@ public void convertUnknownLanguage() { assertEquals(Optional.empty(), Languages.convertToSupportedLocale("This is not a locale")); } + @Test + public void convertKnownLanguageAndCountryCorrect() { + //Language and country code have to be separated see: https://stackoverflow.com/a/3318598 + assertEquals(Optional.of(new Locale("pt", "BR")), Languages.convertToSupportedLocale("pt_BR")); + } + + @Test + public void convertKnownLanguageAndCountryInCorrect() { + //Language and country code have to be separated see: https://stackoverflow.com/a/3318598 + assertFalse(Optional.of(new Locale("pt_BR")).equals(Languages.convertToSupportedLocale("pt_BR"))); + } + @Test public void convertKnownLanguageAndCountryOnly() { - assertEquals(Optional.of(new Locale("en")), Languages.convertToSupportedLocale("en_US")); + assertEquals(Optional.empty(), Languages.convertToSupportedLocale("en_US")); } @Test public void convertKnownLanguageAndUnknownCountry() { - assertEquals(Optional.of(new Locale("en")), Languages.convertToSupportedLocale("en_GB_unknownvariant")); + assertEquals(Optional.empty(), Languages.convertToSupportedLocale("en_GB_unknownvariant")); } @Test diff --git a/src/test/java/org/jabref/logic/util/DevelopmentStageTest.java b/src/test/java/org/jabref/logic/util/DevelopmentStageTest.java index 458e793dfa3..54c93588e4c 100644 --- a/src/test/java/org/jabref/logic/util/DevelopmentStageTest.java +++ b/src/test/java/org/jabref/logic/util/DevelopmentStageTest.java @@ -13,25 +13,25 @@ public void checkStabilityOrder() { assertTrue(Version.DevelopmentStage.BETA.isMoreStableThan(Version.DevelopmentStage.ALPHA)); assertTrue(Version.DevelopmentStage.STABLE.isMoreStableThan(Version.DevelopmentStage.BETA)); - assertEquals("It seems that the development stages have been changed, please adjust the test", - Version.DevelopmentStage.values().length, 4); + assertEquals("It seems that the development stages have been changed, please adjust the test", 4, + Version.DevelopmentStage.values().length); } @Test public void parseStages() { - assertEquals(Version.DevelopmentStage.parse("-alpha"), Version.DevelopmentStage.ALPHA); - assertEquals(Version.DevelopmentStage.parse("-beta"), Version.DevelopmentStage.BETA); - assertEquals(Version.DevelopmentStage.parse(""), Version.DevelopmentStage.STABLE); + assertEquals(Version.DevelopmentStage.ALPHA, Version.DevelopmentStage.parse("-alpha")); + assertEquals(Version.DevelopmentStage.BETA, Version.DevelopmentStage.parse("-beta")); + assertEquals(Version.DevelopmentStage.STABLE, Version.DevelopmentStage.parse("")); } @Test public void parseNull() { - assertEquals(Version.DevelopmentStage.parse(null), Version.DevelopmentStage.UNKNOWN); + assertEquals(Version.DevelopmentStage.UNKNOWN, Version.DevelopmentStage.parse(null)); } @Test public void parseUnknownString() { - assertEquals(Version.DevelopmentStage.parse("asdf"), Version.DevelopmentStage.UNKNOWN); + assertEquals(Version.DevelopmentStage.UNKNOWN, Version.DevelopmentStage.parse("asdf")); } } diff --git a/src/test/java/org/jabref/model/database/BibDatabaseTest.java b/src/test/java/org/jabref/model/database/BibDatabaseTest.java index bcdc5af90ea..a544e7c25fc 100644 --- a/src/test/java/org/jabref/model/database/BibDatabaseTest.java +++ b/src/test/java/org/jabref/model/database/BibDatabaseTest.java @@ -43,8 +43,8 @@ public void setUp() { public void insertEntryAddsEntryToEntriesList() { BibEntry entry = new BibEntry(); database.insertEntry(entry); - assertEquals(database.getEntries().size(), 1); - assertEquals(database.getEntryCount(), 1); + assertEquals(1, database.getEntries().size()); + assertEquals(1, database.getEntryCount()); assertEquals(entry, database.getEntries().get(0)); } @@ -100,8 +100,8 @@ public void insertStringUpdatesStringList() { BibtexString string = new BibtexString("DSP", "Digital Signal Processing"); database.addString(string); assertFalse(database.hasNoStrings()); - assertEquals(database.getStringKeySet().size(), 1); - assertEquals(database.getStringCount(), 1); + assertEquals(1, database.getStringKeySet().size()); + assertEquals(1, database.getStringCount()); assertTrue(database.getStringValues().contains(string)); assertTrue(database.getStringKeySet().contains(string.getId())); assertEquals(string, database.getString(string.getId())); @@ -113,8 +113,8 @@ public void removeStringUpdatesStringList() { database.addString(string); database.removeString(string.getId()); assertTrue(database.hasNoStrings()); - assertEquals(database.getStringKeySet().size(), 0); - assertEquals(database.getStringCount(), 0); + assertEquals(0, database.getStringKeySet().size()); + assertEquals(0, database.getStringCount()); assertFalse(database.getStringValues().contains(string)); assertFalse(database.getStringKeySet().contains(string.getId())); assertNull(database.getString(string.getId())); @@ -186,7 +186,7 @@ public void correctKeyCountOne() { BibEntry entry = new BibEntry(); entry.setCiteKey("AAA"); database.insertEntry(entry); - assertEquals(database.getDuplicationChecker().getNumberOfKeyOccurrences("AAA"), 1); + assertEquals(1, database.getDuplicationChecker().getNumberOfKeyOccurrences("AAA")); } @Test @@ -197,7 +197,7 @@ public void correctKeyCountTwo() { entry = new BibEntry(); entry.setCiteKey("AAA"); database.insertEntry(entry); - assertEquals(database.getDuplicationChecker().getNumberOfKeyOccurrences("AAA"), 2); + assertEquals(2, database.getDuplicationChecker().getNumberOfKeyOccurrences("AAA")); } @Test @@ -209,7 +209,7 @@ public void correctKeyCountAfterRemoving() { entry.setCiteKey("AAA"); database.insertEntry(entry); database.removeEntry(entry); - assertEquals(database.getDuplicationChecker().getNumberOfKeyOccurrences("AAA"), 1); + assertEquals(1, database.getDuplicationChecker().getNumberOfKeyOccurrences("AAA")); } @Test @@ -218,8 +218,8 @@ public void circularStringResolving() { database.addString(string); string = new BibtexString("BBB", "#AAA#"); database.addString(string); - assertEquals(database.resolveForStrings("#AAA#"), "AAA"); - assertEquals(database.resolveForStrings("#BBB#"), "BBB"); + assertEquals("AAA", database.resolveForStrings("#AAA#")); + assertEquals("BBB", database.resolveForStrings("#BBB#")); } @Test @@ -232,29 +232,29 @@ public void circularStringResolvingLongerCycle() { database.addString(string); string = new BibtexString( "DDD", "#AAA#"); database.addString(string); - assertEquals(database.resolveForStrings("#AAA#"), "AAA"); - assertEquals(database.resolveForStrings("#BBB#"), "BBB"); - assertEquals(database.resolveForStrings("#CCC#"), "CCC"); - assertEquals(database.resolveForStrings("#DDD#"), "DDD"); + assertEquals("AAA", database.resolveForStrings("#AAA#")); + assertEquals("BBB", database.resolveForStrings("#BBB#")); + assertEquals("CCC", database.resolveForStrings("#CCC#")); + assertEquals("DDD", database.resolveForStrings("#DDD#")); } @Test public void resolveForStringsMonth() { - assertEquals(database.resolveForStrings("#jan#"), "January"); + assertEquals("January", database.resolveForStrings("#jan#")); } @Test public void resolveForStringsSurroundingContent() { BibtexString string = new BibtexString("AAA", "aaa"); database.addString(string); - assertEquals(database.resolveForStrings("aa#AAA#AAA"), "aaaaaAAA"); + assertEquals("aaaaaAAA", database.resolveForStrings("aa#AAA#AAA")); } @Test public void resolveForStringsOddHashMarkAtTheEnd() { BibtexString string = new BibtexString("AAA", "aaa"); database.addString(string); - assertEquals(database.resolveForStrings("AAA#AAA#AAA#"), "AAAaaaAAA#"); + assertEquals("AAAaaaAAA#", database.resolveForStrings("AAA#AAA#AAA#")); } @Test diff --git a/src/test/java/org/jabref/model/database/DuplicationCheckerTest.java b/src/test/java/org/jabref/model/database/DuplicationCheckerTest.java index 096a446c11b..e9733e9f94e 100644 --- a/src/test/java/org/jabref/model/database/DuplicationCheckerTest.java +++ b/src/test/java/org/jabref/model/database/DuplicationCheckerTest.java @@ -23,7 +23,7 @@ public void addEntry() { BibEntry entry = new BibEntry(); entry.setCiteKey("AAA"); database.insertEntry(entry); - assertEquals(database.getDuplicationChecker().getNumberOfKeyOccurrences("AAA"), 1); + assertEquals(1, database.getDuplicationChecker().getNumberOfKeyOccurrences("AAA")); } @Test @@ -31,9 +31,9 @@ public void addAndRemoveEntry() { BibEntry entry = new BibEntry(); entry.setCiteKey("AAA"); database.insertEntry(entry); - assertEquals(database.getDuplicationChecker().getNumberOfKeyOccurrences("AAA"), 1); + assertEquals(1, database.getDuplicationChecker().getNumberOfKeyOccurrences("AAA")); database.removeEntry(entry); - assertEquals(database.getDuplicationChecker().getNumberOfKeyOccurrences("AAA"), 0); + assertEquals(0, database.getDuplicationChecker().getNumberOfKeyOccurrences("AAA")); } @Test @@ -41,10 +41,10 @@ public void changeCiteKey() { BibEntry entry = new BibEntry(); entry.setCiteKey("AAA"); database.insertEntry(entry); - assertEquals(database.getDuplicationChecker().getNumberOfKeyOccurrences("AAA"), 1); + assertEquals(1, database.getDuplicationChecker().getNumberOfKeyOccurrences("AAA")); entry.setCiteKey("BBB"); - assertEquals(database.getDuplicationChecker().getNumberOfKeyOccurrences("AAA"), 0); - assertEquals(database.getDuplicationChecker().getNumberOfKeyOccurrences("BBB"), 1); + assertEquals(0, database.getDuplicationChecker().getNumberOfKeyOccurrences("AAA")); + assertEquals(1, database.getDuplicationChecker().getNumberOfKeyOccurrences("BBB")); } @@ -56,12 +56,12 @@ public void setCiteKeySameKeyDifferentEntries() { BibEntry entry1 = new BibEntry(); entry1.setCiteKey("BBB"); database.insertEntry(entry1); - assertEquals(database.getDuplicationChecker().getNumberOfKeyOccurrences("AAA"), 1); - assertEquals(database.getDuplicationChecker().getNumberOfKeyOccurrences("BBB"), 1); + assertEquals(1, database.getDuplicationChecker().getNumberOfKeyOccurrences("AAA")); + assertEquals(1, database.getDuplicationChecker().getNumberOfKeyOccurrences("BBB")); entry1.setCiteKey("AAA"); - assertEquals(database.getDuplicationChecker().getNumberOfKeyOccurrences("AAA"), 2); - assertEquals(database.getDuplicationChecker().getNumberOfKeyOccurrences("BBB"), 0); + assertEquals(2, database.getDuplicationChecker().getNumberOfKeyOccurrences("AAA")); + assertEquals(0, database.getDuplicationChecker().getNumberOfKeyOccurrences("BBB")); } @Test @@ -75,16 +75,16 @@ public void removeMultipleCiteKeys(){ BibEntry entry2 = new BibEntry(); entry2.setCiteKey("AAA"); database.insertEntry(entry2); - assertEquals(database.getDuplicationChecker().getNumberOfKeyOccurrences("AAA"), 3); + assertEquals(3, database.getDuplicationChecker().getNumberOfKeyOccurrences("AAA")); database.removeEntry(entry2); - assertEquals(database.getDuplicationChecker().getNumberOfKeyOccurrences("AAA"), 2); + assertEquals(2, database.getDuplicationChecker().getNumberOfKeyOccurrences("AAA")); database.removeEntry(entry1); - assertEquals(database.getDuplicationChecker().getNumberOfKeyOccurrences("AAA"), 1); + assertEquals(1, database.getDuplicationChecker().getNumberOfKeyOccurrences("AAA")); database.removeEntry(entry0); - assertEquals(database.getDuplicationChecker().getNumberOfKeyOccurrences("AAA"), 0); + assertEquals(0, database.getDuplicationChecker().getNumberOfKeyOccurrences("AAA")); } @Test @@ -93,7 +93,7 @@ public void addEmptyCiteKey(){ entry.setCiteKey(""); database.insertEntry(entry); - assertEquals(database.getDuplicationChecker().getNumberOfKeyOccurrences(""), 0); + assertEquals(0, database.getDuplicationChecker().getNumberOfKeyOccurrences("")); } @Test @@ -101,11 +101,11 @@ public void removeEmptyCiteKey(){ BibEntry entry = new BibEntry(); entry.setCiteKey("AAA"); database.insertEntry(entry); - assertEquals(database.getDuplicationChecker().getNumberOfKeyOccurrences("AAA"), 1); + assertEquals(1, database.getDuplicationChecker().getNumberOfKeyOccurrences("AAA")); entry.setCiteKey(""); database.removeEntry(entry); - assertEquals(database.getDuplicationChecker().getNumberOfKeyOccurrences("AAA"), 0); + assertEquals(0, database.getDuplicationChecker().getNumberOfKeyOccurrences("AAA")); } } diff --git a/src/test/java/org/jabref/model/entry/identifier/MathSciNetIdTest.java b/src/test/java/org/jabref/model/entry/identifier/MathSciNetIdTest.java new file mode 100644 index 00000000000..4d4cd6c63f5 --- /dev/null +++ b/src/test/java/org/jabref/model/entry/identifier/MathSciNetIdTest.java @@ -0,0 +1,16 @@ +package org.jabref.model.entry.identifier; + +import java.util.Optional; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class MathSciNetIdTest { + + @Test + public void parseRemovesNewLineCharacterAtEnd() throws Exception { + Optional id = MathSciNetId.parse("3014184\n"); + assertEquals(Optional.of(new MathSciNetId("3014184")), id); + } +} diff --git a/src/test/resources/org/jabref/logic/importer/fileformat/MedlinePlainImporterStringOutOfBounds.bib b/src/test/resources/org/jabref/logic/importer/fileformat/MedlinePlainImporterStringOutOfBounds.bib index 005a90f4969..cf5f8e920e8 100644 --- a/src/test/resources/org/jabref/logic/importer/fileformat/MedlinePlainImporterStringOutOfBounds.bib +++ b/src/test/resources/org/jabref/logic/importer/fileformat/MedlinePlainImporterStringOutOfBounds.bib @@ -1,5 +1,6 @@ @misc{, title = {This is a test title} -}, @misc{, +} +@misc{, title = {This is also a test title} } diff --git a/src/test/resources/org/jabref/logic/importer/fileformat/RepecNepImporterTest2.bib b/src/test/resources/org/jabref/logic/importer/fileformat/RepecNepImporterTest2.bib index 9f24d3e4249..7b5161d9fb5 100644 --- a/src/test/resources/org/jabref/logic/importer/fileformat/RepecNepImporterTest2.bib +++ b/src/test/resources/org/jabref/logic/importer/fileformat/RepecNepImporterTest2.bib @@ -1,5 +1,3 @@ -% Encoding: UTF-8 - @TechReport{ author = {M?ller,Rudolf and Perea,Andr?s and Wolf,Sascha}, title = {Weak Monotonicity and Bayes-Nash Incentive Compatibility}, diff --git a/src/test/resources/org/jabref/logic/importer/fileformat/RepecNepImporterTest3.bib b/src/test/resources/org/jabref/logic/importer/fileformat/RepecNepImporterTest3.bib index 25b69b52242..8d6cbb44118 100644 --- a/src/test/resources/org/jabref/logic/importer/fileformat/RepecNepImporterTest3.bib +++ b/src/test/resources/org/jabref/logic/importer/fileformat/RepecNepImporterTest3.bib @@ -1,5 +1,3 @@ -% Encoding: UTF-8 - @TechReport{, title = {Commercial Television and Voter Information}, url = {http://d.repec.org/n?u=RePEc:cla:levrem:784828000000000363&r=ict} diff --git a/src/test/resources/org/jabref/logic/importer/fileformat/RisImporterTestScopus.bib b/src/test/resources/org/jabref/logic/importer/fileformat/RisImporterTestScopus.bib index ab14c21b29b..fcdd9a0709b 100644 --- a/src/test/resources/org/jabref/logic/importer/fileformat/RisImporterTestScopus.bib +++ b/src/test/resources/org/jabref/logic/importer/fileformat/RisImporterTestScopus.bib @@ -1,5 +1,3 @@ -% Encoding: UTF-8 - @Article{, author = {Federico, S. and Grillo, A. and Herzog, W.}, title = {A transversely isotropic composite with a statistical distribution of spheroidal inclusions: A geometrical approach to overall properties}, diff --git a/src/test/resources/testbib/complex.bib b/src/test/resources/testbib/complex.bib index a1e60fe364c..46c923132a1 100644 --- a/src/test/resources/testbib/complex.bib +++ b/src/test/resources/testbib/complex.bib @@ -304,4 +304,6 @@ @Comment{jabref-meta: @Comment{jabref-meta: saveOrderConfig:specified;author;false;address;false;bibtexkey;false;} +@Comment{jabref-meta: selector_organization:JabRef Developers;JabRef Publisher;} + Some trailing text