diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 572c836fe90..1dff31ca5ee 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -37,6 +37,11 @@ jobs: key: ${{ runner.os }}-gradle-${{ hashFiles('gradle/wrapper/gradle-wrapper.properties') }} - name: Run checkstyle run: ./gradlew checkstyleMain checkstyleTest checkstyleJmh + - name: Run markdown-lint + uses: avto-dev/markdown-lint@v1 + with: + args: CHANGELOG.md README.md docs/ + config: '.markdownlint.yml' tests: name: Unit tests runs-on: ubuntu-latest diff --git a/.mailmap b/.mailmap index cb4da018a86..f2fdffb10b9 100644 --- a/.mailmap +++ b/.mailmap @@ -200,3 +200,5 @@ Gennadiy Stakhovskiy Mootez Saad <34676841+MootezSaaD@users.noreply.github.com> Mootez Saad <34676841+MootezSaaD@users.noreply.github.com> Chen Yuheng +Dominik Voigt <43381984+DominikVoigt@users.noreply.github.com> +Carl Christian Snethlage <50491877+calixtus@users.noreply.github.com> diff --git a/.markdownlint.yml b/.markdownlint.yml new file mode 100644 index 00000000000..84a9c96a53a --- /dev/null +++ b/.markdownlint.yml @@ -0,0 +1,18 @@ +default: true + +# Using h2 has side effects in GitBook's toc. Thus, we sometimes switch from h1 to h3 +MD001: false + +MD012: + # 2 are required, because GitBook adss two blank lines at the end of a file + maximum: 2 + +MD013: false + +# The FAQs state questions - we allow them +MD026: + punctuation: ".,;:!" + +MD033: + # we have tags with ids + allowed_elements: ['a'] diff --git a/AUTHORS b/AUTHORS index 40b6b524e3f..7795b1f4c78 100644 --- a/AUTHORS +++ b/AUTHORS @@ -29,6 +29,7 @@ Andreas Buhr Andreas Rudert Andrew Collins Andrew Levit +André Schlichting Andrés Sánchez Anh Nghia Tran Anita Armbruster @@ -40,6 +41,7 @@ August Janse Ayachi Nene Bartosz J. Kaczkowski Bartłomiej Dach +Baruch Oltman Behrouz Javanmardi Benedikt Tutzer Benjamin Köhler @@ -96,6 +98,7 @@ Dimitra Karadima Domenico Cufalo Dominik Schrempf Dominik Traczyk +Dominik Voigt Dominik Waßenhoven Douglas Nassif Roma Junior Eduard Braun @@ -260,6 +263,7 @@ Nick S. Weatherley Nico Schlömer Nicolas Pavillon Nikita Borovikov +Niklas Schmitt nikmilpv NikodemKch Niv Ierushalmi diff --git a/CHANGELOG.md b/CHANGELOG.md index 48e1767b555..d8d7c5e7912 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ Note that this project **does not** adhere to [Semantic Versioning](http://semve ### Added +- We added the Library properties to a context menu on the library tabs [#6485](https://github.com/JabRef/jabref/issues/6485) - We added a new field in the preferences in 'BibTeX key generator' for unwanted characters that can be user-specified. [#6295](https://github.com/JabRef/jabref/issues/6295) - We added support for searching ShortScience for an entry through the user's browser. [#6018](https://github.com/JabRef/jabref/pull/6018) - We updated EditionChecker to permit edition to start with a number. [#6144](https://github.com/JabRef/jabref/issues/6144) @@ -24,6 +25,8 @@ Note that this project **does not** adhere to [Semantic Versioning](http://semve - We fixed the bug when strike the delete key in the text field. [#6421](https://github.com/JabRef/jabref/issues/6421) - We added a BibTex key modifier for truncating strings. [#3915](https://github.com/JabRef/jabref/issues/3915) - We added support for jumping to target entry when typing letter/digit after sorting a column in maintable [#6146](https://github.com/JabRef/jabref/issues/6146) +- We added a new fetcher to enable users to search all available E-Libraries simultaneously. [koppor#369](https://github.com/koppor/jabref/issues/369) +- We added the field "entrytype" to the export sort criteria [#6531](https://github.com/JabRef/jabref/pull/6531) ### Changed @@ -37,9 +40,11 @@ Note that this project **does not** adhere to [Semantic Versioning](http://semve - We improved the error message for invalid jstyles. [#6303](https://github.com/JabRef/jabref/issues/6303) - We changed the section name of 'Advanced' to 'Network' in the preferences and removed some obsolete options.[#6489](https://github.com/JabRef/jabref/pull/6489) - We improved the context menu of the column "Linked identifiers" of the main table, by truncating their texts, if they are too long. [#6499](https://github.com/JabRef/jabref/issues/6499) +- We merged the main table tabs in the preferences dialog. [#6518](https://github.com/JabRef/jabref/pull/6518) ### Fixed +- We fixed to only search file links in the BIB file location directory when preferences has corresponding checkbox checked. [#5891](https://github.com/JabRef/jabref/issues/5891) - We fixed wrong button order (Apply and Cancel) in ManageProtectedTermsDialog. - We fixed an issue with incompatible characters at BibTeX key [#6257](https://github.com/JabRef/jabref/issues/6257) - We fixed an issue where dash (`-`) was reported as illegal BibTeX key [#6295](https://github.com/JabRef/jabref/issues/6295) @@ -63,6 +68,7 @@ Note that this project **does not** adhere to [Semantic Versioning](http://semve - We fixed an issue where JabRef switched to discrete graphics under macOS [#5935](https://github.com/JabRef/jabref/issues/5935) - We fixed an issue where the Preferences entry preview will be unexpected modified leads to Value too long exception [#6198](https://github.com/JabRef/jabref/issues/6198) - We fixed an issue where custom jstyles for Open/LibreOffice would only be valid if a layout line for the entry type `default` was at the end of the layout section [#6303](https://github.com/JabRef/jabref/issues/6303) +- We fixed an issue where a new entry is not shown in the library if a search is active [#6297](https://github.com/JabRef/jabref/issues/6297) - We fixed an issue where long directory names created from patterns could create an exception. [#3915](https://github.com/JabRef/jabref/issues/3915) - We fixed an issue where sort on numeric cases was broken. [#6349](https://github.com/JabRef/jabref/issues/6349) - We fixed an issue where year and month fields were not cleared when converting to biblatex [#6224](https://github.com/JabRef/jabref/issues/6224) @@ -71,6 +77,9 @@ Note that this project **does not** adhere to [Semantic Versioning](http://semve - We fixed an issue where brackets in regular expressions were not working. [6469](https://github.com/JabRef/jabref/pull/6469) - We fixed an issue where LaTeX citations for specific commands (\autocites) of biblatex-mla were not recognized. [#6476](https://github.com/JabRef/jabref/issues/6476) - We fixed an issue where drag and drop was not working on empty database. [#6487](https://github.com/JabRef/jabref/issues/6487) +- We fixed an issue where the name fields were not updated after the preferences changed. [#6515](https://github.com/JabRef/jabref/issues/6515) +- We fixed an issue where "null" appeared in generated BibTeX keys. [#6459](https://github.com/JabRef/jabref/issues/6459) +- We fixed an issue where the authors' names were incorrectly displayed in the authors' column when they were bracketed. [#6465](https://github.com/JabRef/jabref/issues/6465) [#6459](https://github.com/JabRef/jabref/issues/6459) ### Removed @@ -127,6 +136,7 @@ Note that this project **does not** adhere to [Semantic Versioning](http://semve - We fixed an issue where the blue and red text colors in the Merge entries dialog were not quite visible [#6334](https://github.com/JabRef/jabref/issues/6334) - We fixed an issue where underscore character was removed from the file name in the Recent Libraries list in File menu [#6383](https://github.com/JabRef/jabref/issues/6383) - We fixed an issue where few keyboard shortcuts regarding new entries were missing [#6403](https://github.com/JabRef/jabref/issues/6403) + ### Removed - Ampersands are no longer escaped by default in the `bib` file. If you want to keep the current behaviour, you can use the new "Escape Ampersands" formatter as a save action. [#5869](https://github.com/JabRef/jabref/issues/5869) @@ -302,8 +312,8 @@ Note that this project **does not** adhere to [Semantic Versioning](http://semve - We fixed an issue where some journal names were wrongly marked as abbreviated. [#4115](https://github.com/JabRef/jabref/issues/4115) - We fixed an issue where the custom file column were sorted incorrectly. [#3119](https://github.com/JabRef/jabref/issues/3119) - We improved the parsing of author names whose infix is abbreviated without a dot. [#4864](https://github.com/JabRef/jabref/issues/4864) -- We fixed an issues where the entry losses focus when a field is edited and at the same time used for sorting. https://github.com/JabRef/jabref/issues/3373 -- We fixed an issue where the menu on Mac OS was not displayed in the usual Mac-specific way. https://github.com/JabRef/jabref/issues/3146 +- We fixed an issues where the entry losses focus when a field is edited and at the same time used for sorting. [#3373](https://github.com/JabRef/jabref/issues/3373) +- We fixed an issue where the menu on Mac OS was not displayed in the usual Mac-specific way. [#3146](https://github.com/JabRef/jabref/issues/3146) - We improved the integrity check for page numbers. [#4113](https://github.com/JabRef/jabref/issues/4113) and [feature request in the forum](http://discourse.jabref.org/t/pages-field-allow-use-of-en-dash/1199) - We fixed an issue where the order of fields in customized entry types was not saved correctly. [#4033](http://github.com/JabRef/jabref/issues/4033) - We fixed an issue where renaming a group did not change the group name in the interface. [#3189](https://github.com/JabRef/jabref/issues/3189) diff --git a/README.md b/README.md index da480916196..d81ab2b909e 100644 --- a/README.md +++ b/README.md @@ -3,12 +3,12 @@ JabRef is an open-source, cross-platform citation and reference management tool. Stay on top of your literature: JabRef helps you to collect and organize sources, find the paper you need and discover the latest research. -![main table](https://www.jabref.org/img/jabref-5-0-maintable.png) +![main table](http://www.jabref.org/img/jabref-mainscreen.png) ## Features -JabRef is a cross-platform application that works on Windows, Linux and Mac OS X. It is available free of charge and is actively developed. -JabRef supports you in every step of your research work. +JabRef is available free of charge and is actively developed. +It supports you in every step of your research work. ### Collect @@ -33,7 +33,7 @@ JabRef supports you in every step of your research work. ### Cite -- Native [BibTeX] and [Biblatex] support +- Native BibTeX and Biblatex support - Cite-as-you-write functionality for external applications such as Emacs, Kile, LyX, Texmaker, TeXstudio, Vim and WinEdt. - Format references in one of the many thousand built-in citation styles or create your style - Support for Word and LibreOffice/OpenOffice for inserting and formatting citations @@ -47,11 +47,9 @@ JabRef supports you in every step of your research work. ## Installation Fresh development builds are available at [builds.jabref.org](https://builds.jabref.org/master/). -The [latest stable release is available at FossHub](https://www.fosshub.com/JabRef.html). +The [latest stable release is available at FossHub](https://downloads.jabref.org/). -- Windows: JabRef offers an installer, which also adds a shortcut to JabRef to your start menu. Please also see our [Windows FAQ](https://docs.jabref.org/faq/faqwindows) -- Linux: Please see our [Installation Guide](https://docs.jabref.org/general/installation). -- Mac OS X: Please see our [Mac OS X FAQ](https://docs.jabref.org/faq/faqosx). +Please see our [Installation Guide](https://docs.jabref.org/installation). ## Bug Reports, Suggestions, Other Feedback @@ -98,8 +96,4 @@ For IntelliJ IDEA, just import the project via a Gradle Import by pointing at th `gradlew test` executes all tests. We use [Github Actions](https://github.com/JabRef/jabref/actions) for executing the tests after each commit. For developing, it is sufficient to locally only run the associated test for the classes you changed. Github will report any other failure. -[BibTeX]: https://www.ctan.org/pkg/bibtex -[Biblatex]: https://www.ctan.org/pkg/biblatex -[install4j]: https://www.ej-technologies.com/products/install4j/overview.html [JabRef]: https://www.jabref.org -[JavaFX]: https://en.wikipedia.org/wiki/JavaFX diff --git a/build.gradle b/build.gradle index 6c1a4b18087..48be26438d8 100644 --- a/build.gradle +++ b/build.gradle @@ -116,7 +116,7 @@ dependencies { implementation group: 'org.apache.tika', name: 'tika-core', version: '1.24.1' // required for reading write-protected PDFs - see https://github.com/JabRef/jabref/pull/942#issuecomment-209252635 - implementation 'org.bouncycastle:bcprov-jdk15on:1.65' + implementation 'org.bouncycastle:bcprov-jdk15on:1.65.01' implementation 'commons-cli:commons-cli:1.4' @@ -192,9 +192,9 @@ dependencies { exclude module: "log4j-core" } - implementation 'com.vladsch.flexmark:flexmark:0.61.30' - implementation 'com.vladsch.flexmark:flexmark-ext-gfm-strikethrough:0.61.30' - implementation 'com.vladsch.flexmark:flexmark-ext-gfm-tasklist:0.61.30' + implementation 'com.vladsch.flexmark:flexmark:0.62.0' + implementation 'com.vladsch.flexmark:flexmark-ext-gfm-strikethrough:0.62.0' + implementation 'com.vladsch.flexmark:flexmark-ext-gfm-tasklist:0.62.0' testImplementation 'io.github.classgraph:classgraph:4.8.78' testImplementation 'org.junit.jupiter:junit-jupiter:5.6.2' @@ -207,7 +207,7 @@ dependencies { testImplementation 'org.mockito:mockito-core:3.3.3' testImplementation 'org.xmlunit:xmlunit-core:2.7.0' testImplementation 'org.xmlunit:xmlunit-matchers:2.7.0' - testImplementation 'com.tngtech.archunit:archunit-junit5-api:0.13.1' + testImplementation 'com.tngtech.archunit:archunit-junit5-api:0.14.1' testImplementation "org.testfx:testfx-core:4.0.17-alpha-SNAPSHOT" testImplementation "org.testfx:testfx-junit5:4.0.17-alpha-SNAPSHOT" testImplementation "org.hamcrest:hamcrest-library:2.2" diff --git a/docs/README.md b/docs/README.md index 4287ad7e8d9..e65ae522f1d 100644 --- a/docs/README.md +++ b/docs/README.md @@ -42,7 +42,7 @@ This log lists the decisions for JabRef. * [ADR-0007](adr/0007-human-readable-changelog.md) - Provide a human-readable changelog * [ADR-0008](adr/0008-use-public-final-instead-of-getters.md) - Use public final instead of getters to offer access to immutable variables * [ADR-0009](adr/0009-use-plain-junit5-for-testing.md) - Use Plain JUnit5 for advanced test assertions -* [ADR-0010](0010-use-h2-as-internal-database.md) - Use H2 as Internal SQL Database +* [ADR-0010](adr/0010-use-h2-as-internal-database.md) - Use H2 as Internal SQL Database For new ADRs, please use [template.md](adr/template.md) as basis. More information on MADR is available at [https://adr.github.io/madr/](https://adr.github.io/madr/). General information about architectural decision records is available at [https://adr.github.io/](https://adr.github.io/). diff --git a/docs/adr/0004-use-mariadb-connector.md b/docs/adr/0004-use-mariadb-connector.md index 2085113a0a3..31b4b6604d6 100644 --- a/docs/adr/0004-use-mariadb-connector.md +++ b/docs/adr/0004-use-mariadb-connector.md @@ -30,3 +30,4 @@ The [MySQL Connector](https://www.mysql.com/de/products/connector/) is distribut * Good, because it stems from the same development team than MySQL * Bad, because the "Universal FOSS Exception" makes licensing more complicated. + diff --git a/docs/adr/0006-only-translated-strings-in-language-file.md b/docs/adr/0006-only-translated-strings-in-language-file.md index 1aada8966eb..2fd3ccfbc16 100644 --- a/docs/adr/0006-only-translated-strings-in-language-file.md +++ b/docs/adr/0006-only-translated-strings-in-language-file.md @@ -44,3 +44,4 @@ Chosen option: "Only translated strings in language file", because comes out bes * Related to [ADR-0001](0001-use-crowdin-for-translations.md). + diff --git a/docs/adr/template.md b/docs/adr/template.md index 22d77697e71..b07f46e2eb0 100644 --- a/docs/adr/template.md +++ b/docs/adr/template.md @@ -1,10 +1,10 @@ # \[short title of solved problem and solution\] -* Status: \[proposed \| rejected \| accepted \| deprecated \| … \| superseded by [ADR-0005](https://github.com/JabRef/jabref/tree/8c07a88a823a84aebe987cdb717f318ed00a872d/docs/adr/0005-example.md)\] -* Deciders: \[list everyone involved in the decision\] -* Date: \[YYYY-MM-DD when the decision was last updated\] +* Status: \[proposed \| rejected \| accepted \| deprecated \| … \| superseded by [ADR-0005](0005-fully-support-utf8-only-for-latex-files.md)\] +* Deciders: \[list everyone involved in the decision\] +* Date: \[YYYY-MM-DD when the decision was last updated\] -Technical Story: \[description \| ticket/issue URL\] +Technical Story: \[description \| ticket/issue URL\] ## Context and Problem Statement @@ -14,14 +14,14 @@ Technical Story: \[description \| ticket/issue URL\] * \[driver 1, e.g., a force, facing concern, …\] * \[driver 2, e.g., a force, facing concern, …\] -* … +* … ## Considered Options * \[option 1\] * \[option 2\] * \[option 3\] -* … +* … ## Decision Outcome @@ -41,33 +41,33 @@ Chosen option: "\[option 1\]", because \[justification. e.g., only option, which ### \[option 1\] -\[example \| description \| pointer to more information \| …\] +\[example \| description \| pointer to more information \| …\] * Good, because \[argument a\] * Good, because \[argument b\] * Bad, because \[argument c\] -* … +* … ### \[option 2\] -\[example \| description \| pointer to more information \| …\] +\[example \| description \| pointer to more information \| …\] * Good, because \[argument a\] * Good, because \[argument b\] * Bad, because \[argument c\] -* … +* … ### \[option 3\] -\[example \| description \| pointer to more information \| …\] +\[example \| description \| pointer to more information \| …\] * Good, because \[argument a\] * Good, because \[argument b\] * Bad, because \[argument c\] -* … +* … ## Links -* \[Link type\] \[Link to ADR\] -* … +* \[Link type\] \[Link to ADR\] +* … diff --git a/docs/code-howtos.md b/docs/code-howtos.md index 9e747de41ea..182375b4046 100644 --- a/docs/code-howtos.md +++ b/docs/code-howtos.md @@ -30,6 +30,7 @@ Principles: Localization.lang("Something went wrong...", ioe); } ``` + * Never, ever throw and catch `Exception` or `Throwable` * Errors should only be logged when they are finally caught \(i.e., logged only once\). See **Logging** for details. * If the Exception message is intended to be shown to the User in the UI \(see below\) provide also a localizedMessage \(see `JabRefException`\). @@ -45,7 +46,7 @@ To show error message two different ways are usually used in JabRef: * showing an error dialog * updating the status bar at the bottom of the main window -_TODO: Usage of status bar and Swing Dialogs_ +_*TODO: Usage of status bar and Swing Dialogs*_ ## Using the EventSystem @@ -138,7 +139,7 @@ JabRef uses the logging facade [SLF4j](https://www.slf4j.org/). All log messages ## Using Localization correctly -_\(More information about this topic from the translator side is provided under this link:_ [_Translating JabRef Interface_](https://github.com/JabRef/jabref/wiki/Translating-JabRef-Interface) _about the JabRef interface and_ [_Translating JabRef Help_](https://github.com/JabRef/jabref/wiki/Translating-JabRef-Help) _about the JabRef help files\)_ +More information about this topic from the translator side is provided at [Translating JabRef Interface](https://docs.jabref.org/faqcontributing/how-to-translate-the-ui). All labeled UI elements, descriptions and messages shown to the user should be localized, i.e., should be displayed in the chosen language. @@ -179,7 +180,7 @@ The tests check whether translation strings appear correctly in the resource bun We try to build a cleanup mechanism based on formatters. The idea is that we can register these actions in arbitrary places, e.g., onSave, onImport, onExport, cleanup, etc. and apply them to different fields. The formatters themself are independent of any logic and therefore easy to test. -Example: [PageNumbersFormatter](https://github.com/JabRef/jabref/blob/master/src/main/java/net/sf/jabref/logic/formatter/bibtexfields/PageNumbersFormatter.java) +Example: [NormalizePagesFormatter](https://github.com/JabRef/jabref/blob/master/src/main/java/org/jabref/logic/formatter/bibtexfields/NormalizePagesFormatter.java) ## Drag and Drop diff --git a/docs/code-quality.md b/docs/code-quality.md index 5739c3629eb..5504010fd1f 100644 --- a/docs/code-quality.md +++ b/docs/code-quality.md @@ -8,7 +8,7 @@ We monitor the general source code quality at three places: We strongly recommend to read following two books on code quality: -* [Java by Comparison](https://github.com/JabRef/jabref/tree/c81740b3818c7f9311a6d7ff063243e672c821b4/docs/java.by-comparison.com) is a book by three JabRef developers which focuses on code improvements close to single statements. It is fast to read and one gains much information from each recommendation discussed in the book. +* [Java by Comparison](http://java.by-comparison.com) is a book by three JabRef developers which focuses on code improvements close to single statements. It is fast to read and one gains much information from each recommendation discussed in the book. * [Effective Java](https://www.oreilly.com/library/view/effective-java-3rd/9780134686097/) is the standard book for advanced Java programming. Did you know that `enum` is the [recommended way to enforce a singleton instance of a class](https://learning.oreilly.com/library/view/effective-java-3rd/9780134686097/ch2.xhtml#lev3)? Did you know that one should [refer to objects by their interfaces](https://learning.oreilly.com/library/view/effective-java-3rd/9780134686097/ch9.xhtml#lev64)? The principles we follow to ensure high code quality in JabRef is stated at our [Development Strategy](development-strategy.md). diff --git a/docs/guidelines-for-setting-up-a-local-workspace.md b/docs/guidelines-for-setting-up-a-local-workspace.md index 94faa6196e0..e361109b952 100644 --- a/docs/guidelines-for-setting-up-a-local-workspace.md +++ b/docs/guidelines-for-setting-up-a-local-workspace.md @@ -59,7 +59,7 @@ It is strongly recommend that you have git installed. We suggest [IntelliJ IDEA](https://www.jetbrains.com/idea/) or [Eclipse (for advanced users)](https://eclipse.org/) \(`2020-03` or newer\). -Under Ubuntu Linux, you can follow the [documentation from the Ubuntu Community](https://help.ubuntu.com/community/EclipseIDE#Download_Eclipse) or the [step-by-step guideline from Krizna](https://github.com/JabRef/jabref/tree/be9c788de804c2bd9e3abaf76b082b6b2e82e66f/docs/www.krizna.com/ubuntu/install-eclipse-in-ubuntu-12-04/README.md) to install Eclipse. Under Windows, download it from [www.eclipse.org](http://www.eclipse.org/downloads/) and run the installer. +Under Ubuntu Linux, you can follow the [documentation from the Ubuntu Community](https://help.ubuntu.com/community/EclipseIDE#Download_Eclipse) or the [step-by-step guideline from Krizna](https://www.krizna.com/ubuntu/install-eclipse-in-ubuntu-12-04/) to install Eclipse. Under Windows, download it from [www.eclipse.org](http://www.eclipse.org/downloads/) and run the installer. ### Other Tooling @@ -74,7 +74,7 @@ This section explains how you get the JabRef code onto your machine in a form al 1. Log into your GitHub account 2. Go to [https://github.com/JabRef/jabref](https://github.com/JabRef/jabref) 3. Create a fork by clicking at fork button on the right top corner -4. A fork repository will be created under your account \([https://github.com/YOUR\_USERNAME/jabref](https://github.com/YOUR_USERNAME/jabref)\) +4. A fork repository will be created under your account `https://github.com/YOUR\_USERNAME/jabref`. ### Clone your forked repository on your local machine diff --git a/docs/javafx.md b/docs/javafx.md index 06721363efa..232ad8f525e 100644 --- a/docs/javafx.md +++ b/docs/javafx.md @@ -7,7 +7,7 @@ JabRef's recommendations about JavaFX The goal of the MVVM architecture is to separate the state/behavior from the appearance of the ui. This is archived by dividing JabRef into different layers, each having a clear responsibility. * The _Model_ contains the business logic and data structures. These aspects are again encapsulated in the _logic_ and _model_ package, respectively. -* The _View_ controls the appearance and structure of the UI. It is usually defined in a _FXML_ file. +* The _View_ controls the appearance and structure of the UI. It is usually defined in a _FXML_ file. * _View model_ converts the data from logic and model in a form that is easily usable in the gui. Thus it controls the state of the View. Moreover, the ViewModel contains all the logic needed to change the current state of the UI or perform an action. These actions are usually passed down to the _logic_ package, after some data validation. The important aspect is that the ViewModel contains all the ui-related logic but does _not_ have direct access to the controls defined in the View. Hence, the ViewModel can easily be tested by unit tests. * The _Controller_ initializes the view model and binds it to the view. In an ideal world all the binding would already be done directly in the FXML. But JavaFX's binding expressions are not yet powerful enough to accomplish this. It is important to keep in mind that the Controller should be as minimalistic as possible. Especially one should resist the temptation to validate inputs in the controller. The ViewModel should handle data validation! It is often convenient to load the FXML file directly from the controller. @@ -134,14 +134,14 @@ private void openJabrefWebsite() { ### View - FXML -The view consists a FXML file `MyDialog.fxml` which defines the structure and the layout of the UI. Moreover, the FXML file may be accompanied by a style file that should have the same name as the FXML file but with a `css` ending, e.g., `MyDialog.css`. It is recommended to use a graphical design tools like [SceneBuilder](http://gluonhq.com/labs/scene-builder/) to edit the FXML file. The tool [Scenic View](http://fxexperience.com/scenic-view/) is very helpful in debugging styling issues. +The view consists a FXML file `MyDialog.fxml` which defines the structure and the layout of the UI. Moreover, the FXML file may be accompanied by a style file that should have the same name as the FXML file but with a `css` ending, e.g., `MyDialog.css`. It is recommended to use a graphical design tools like [SceneBuilder](http://gluonhq.com/labs/scene-builder/) to edit the FXML file. The tool [Scenic View](https://github.com/JonathanGiles/scenic-view) is very helpful in debugging styling issues. ## Resources * [curated list of awesome JavaFX frameworks, libraries, books and etc...](https://github.com/mhrimaz/AwesomeJavaFX) * [ControlsFX](http://fxexperience.com/controlsfx/features/) amazing collection of controls -* [usage of icon fonts with JavaFX](http://aalmiray.github.io/ikonli/#_javafx) or [jIconFont](https://github.com/jIconFont/jiconfont-google_material_design_icons) or [fontawesomefx](https://bitbucket.org/Jerady/fontawesomefx/) -* [Undo manager](https://github.com/TomasMikula/UndoFX) +* [jIconFont](http://jiconfont.github.io/googlematerialdesignicons) or [fontawesomefx](https://bitbucket.org/Jerady/fontawesomefx/) +* [Undo manager](https://github.com/FXMisc/UndoFX) * [Docking manager](https://github.com/alexbodogit/AnchorFX) [or](https://github.com/RobertBColton/DockFX) * [additional bindings](https://github.com/lestard/advanced-bindings) or [EasyBind](https://github.com/TomasMikula/EasyBind) * [Kubed](https://github.com/hudsonb/kubed): data visualization \(inspired by d3\) diff --git a/docs/jpackage.md b/docs/jpackage.md index 325970b58e9..2a6e005b9ca 100644 --- a/docs/jpackage.md +++ b/docs/jpackage.md @@ -1,6 +1,6 @@ # jpackage -JabRef uses [jpackage](https://jdk.java.net/jpackage/) to build binary distributions for Windows, Linux, and Mac OS X. +JabRef uses [jpackage](https://openjdk.java.net/jeps/343) to build binary distributions for Windows, Linux, and Mac OS X. ## Build Windows binaries locally diff --git a/docs/teaching.md b/docs/teaching.md index a6e9c6726f8..4eda9bacd9e 100644 --- a/docs/teaching.md +++ b/docs/teaching.md @@ -1,8 +1,13 @@ # JabRef as Basis for Teaching Material -JabRef can be used as a training object for software engineering. +With JabRef students can level-up their coding and GitHub skills. +When taking part in JabRef development, one will learn modern Java coding practices, how code reviews work and how to properly address reviewing feedback. -As an instructor, please follow this checklist: +JabRef tries to achieve high code quality. This ultimately leads to improved software engineering knowledge of contributors. After contributing for JabRef, both coding and general software engienering skills will have increased. Our [development strategy](development-strategy.md) provides more details. + +We recommend to start early and constantly, since students wirking earlier and more often, produce projects that are more correct, completed earlier at the same overall invested time [1](#Ayaankazerouni). + +## Notes for instructors 1. Be aware that JabRef is run by volunteers. This implies that the development team cannot ensure to provide feedback on code within hours. 2. Be aware that from the first pull request to the final acceptance the typical time needed is two weeks. @@ -17,7 +22,7 @@ For a near-to-perfect preparation and effect of the course, we ask you to get in It is also possible to just direct students to our [Contribution Guide](https://github.com/JabRef/jabref/blob/master/CONTRIBUTING.md#contributing-guide). The learning effect may be lower as the time of the students has to be spent to a) learn about JabRef and b) select an appropriate issue. -Since a huge fraction of software costs is spend on [software maintenance](https://en.wikipedia.org/wiki/Software_maintenance), adding new features also educates in that aspect: perfective maintenance[1](#LientzSwanson) is trained. When fixing bugs, corrective maintenance[1](#LientzSwanson) is trained. +Since a huge fraction of software costs is spend on [software maintenance](https://en.wikipedia.org/wiki/Software_maintenance), adding new features also educates in that aspect: perfective maintenance1 is trained. When fixing bugs, corrective maintenance[2](#LientzSwanson) is trained. ## Process for contributions @@ -41,7 +46,7 @@ GitHub describes that in their page [Unterstanding the GitHub flow](https://guid ## Process for Java newcomers -Newcomers are invited to follow the process described above. +Newcomers contributing in the context of a university teaching experience are invited to follow the process described above. In case the capacity of the instructing university allows, we propose a three-step approach. First, the contributors prepare their contribution as usual. Then, they submit the pull request *to a separate repository*. @@ -69,7 +74,7 @@ Course: Open Source Software Development Course [CS499 - Open Source Software Development](https://github.com/igorsteinmacher/CS499-OSS) -- Summary: Students experience the process of getting involved in an Open Source project by engaging with a real project. Their goal is to make a "substantial" contribution to a project. +- Summary: Students experience the process of getting involved in an Open Source project by engaging with a real project. Their goal is to make a "substantial" contribution to a project. - Course offered in 2018 ### German @@ -120,17 +125,9 @@ Course [Open Source Software](https://github.com/igorsteinmacher/DSL-UTFPR) - Summary: Students are requested to contribute to an Open Source project to learn about the maintenance and evolution of software projects. This project is the predecessor of NAU's CS499. - Course offered from 2013 to 2016 with different names -## Interesting Read for Students and Advisors - -- [Developing Procrastination Feedback for Student Software Developers](https://medium.com/@ayaankazerouni/developing-procrastination-feedback-for-student-software-developers-1652de60db7f) by [@ayaankazerouni](https://github.com/ayaankazerouni?tab=overview&from=2015-12-01&to=2015-12-31) - > When students worked earlier and more often, they produced projects that: - > - > - were more correct, - > - were completed earlier, - > - took no more or less time to complete - ## References -1: Lientz B., Swanson E., 1980: Software Maintenance Management. Addison Wesley, Reading, MA. +1: [@ayaankazerouni](https://github.com/ayaankazerouni): [Developing Procrastination Feedback for Student Software Developers](https://medium.com/@ayaankazerouni/developing-procrastination-feedback-for-student-software-developers-1652de60db7f) +2: Lientz B., Swanson E., 1980: Software Maintenance Management. Addison Wesley, Reading, MA. diff --git a/mlc_config.json b/mlc_config.json new file mode 100644 index 00000000000..43d1ec18e04 --- /dev/null +++ b/mlc_config.json @@ -0,0 +1,13 @@ +{ + "ignorePatterns": [ + { + "pattern": "^https://dl\\.acm\\.org" + }, + { + "pattern": "^http://purl\\.org/net/bibteXMP" + }, + { + "pattern": "^http://localhost" + } + ] +} diff --git a/src/main/java/org/jabref/gui/BasePanel.java b/src/main/java/org/jabref/gui/BasePanel.java index a6b527cde4d..c0140fbd8b6 100644 --- a/src/main/java/org/jabref/gui/BasePanel.java +++ b/src/main/java/org/jabref/gui/BasePanel.java @@ -101,7 +101,7 @@ public BasePanel(JabRefFrame frame, BasePanelPreferences preferences, BibDatabas bibDatabaseContext.getMetaData().registerListener(this); this.sidePaneManager = frame.getSidePaneManager(); - this.tableModel = new MainTableDataModel(getBibDatabaseContext()); + this.tableModel = new MainTableDataModel(getBibDatabaseContext(), Globals.prefs, Globals.stateManager); citationStyleCache = new CitationStyleCache(bibDatabaseContext); annotationCache = new FileAnnotationCache(bibDatabaseContext, Globals.prefs.getFilePreferences()); diff --git a/src/main/java/org/jabref/gui/EntryTypeView.java b/src/main/java/org/jabref/gui/EntryTypeView.java index 5fe33bb6774..658730b7f1c 100644 --- a/src/main/java/org/jabref/gui/EntryTypeView.java +++ b/src/main/java/org/jabref/gui/EntryTypeView.java @@ -7,6 +7,7 @@ import javafx.application.Platform; import javafx.event.Event; import javafx.fxml.FXML; +import javax.inject.Inject; import javafx.scene.control.Button; import javafx.scene.control.ButtonType; import javafx.scene.control.ComboBox; @@ -56,6 +57,8 @@ public class EntryTypeView extends BaseDialog { @FXML private TitledPane ieeeTranTitlePane; @FXML private TitledPane customTitlePane; + @Inject StateManager stateManager; + private final BasePanel basePanel; private final DialogService dialogService; private final JabRefPreferences prefs; @@ -187,6 +190,7 @@ private void focusTextField(Event event) { private void setEntryTypeForReturnAndClose(Optional entryType) { type = entryType.map(BibEntryType::getType).orElse(null); viewModel.stopFetching(); + this.stateManager.clearSearchQuery(); this.close(); } diff --git a/src/main/java/org/jabref/gui/JabRefFrame.java b/src/main/java/org/jabref/gui/JabRefFrame.java index 3f9126fd63f..68938bb91c4 100644 --- a/src/main/java/org/jabref/gui/JabRefFrame.java +++ b/src/main/java/org/jabref/gui/JabRefFrame.java @@ -22,6 +22,7 @@ import javafx.scene.control.Button; import javafx.scene.control.ButtonBar; import javafx.scene.control.ButtonType; +import javafx.scene.control.ContextMenu; import javafx.scene.control.Menu; import javafx.scene.control.MenuBar; import javafx.scene.control.MenuItem; @@ -97,6 +98,7 @@ import org.jabref.gui.journals.ManageJournalsAction; import org.jabref.gui.keyboard.CustomizeKeyBindingAction; import org.jabref.gui.keyboard.KeyBinding; +import org.jabref.gui.keyboard.KeyBindingRepository; import org.jabref.gui.libraryproperties.LibraryPropertiesAction; import org.jabref.gui.menus.FileHistoryMenu; import org.jabref.gui.mergeentries.MergeEntriesAction; @@ -176,7 +178,7 @@ public JabRefFrame(Stage mainStage) { this.mainStage = mainStage; this.dialogService = new JabRefDialogService(mainStage, this, prefs, themeLoader); this.stateManager = Globals.stateManager; - this.pushToApplicationsManager = new PushToApplicationsManager(dialogService, stateManager); + this.pushToApplicationsManager = new PushToApplicationsManager(dialogService, stateManager, prefs); this.undoManager = Globals.undoManager; this.fileHistory = new FileHistoryMenu(prefs, dialogService, getOpenDatabaseAction()); this.executorService = JabRefExecutorService.INSTANCE; @@ -513,7 +515,7 @@ private Node createToolbar() { final PushToApplicationAction pushToApplicationAction = getPushToApplicationsManager().getPushToApplicationAction(); final Button pushToApplicationButton = factory.createIconButton(pushToApplicationAction.getActionInformation(), pushToApplicationAction); - pushToApplicationsManager.setToolBarButton(pushToApplicationButton); + pushToApplicationsManager.registerReconfigurable(pushToApplicationButton); HBox rightSide = new HBox( factory.createIconButton(StandardActions.NEW_ARTICLE, new NewEntryAction(this, StandardEntryType.Article, dialogService, Globals.prefs, stateManager)), @@ -835,7 +837,7 @@ private MenuBar createMenu() { // PushToApplication final PushToApplicationAction pushToApplicationAction = pushToApplicationsManager.getPushToApplicationAction(); final MenuItem pushToApplicationMenuItem = factory.createMenuItem(pushToApplicationAction.getActionInformation(), pushToApplicationAction); - pushToApplicationsManager.setMenuItem(pushToApplicationMenuItem); + pushToApplicationsManager.registerReconfigurable(pushToApplicationMenuItem); tools.getItems().addAll( factory.createMenuItem(StandardActions.PARSE_LATEX, new ParseLatexAction(stateManager)), @@ -1077,6 +1079,15 @@ public void updateAllTabTitles() { } } + private static ContextMenu createTabContextMenu(KeyBindingRepository keyBindingRepository, BasePanel panel, StateManager stateManager) { + ContextMenu contextMenu = new ContextMenu(); + ActionFactory factory = new ActionFactory(keyBindingRepository); + + contextMenu.getItems().add(factory.createMenuItem(StandardActions.LIBRARY_PROPERTIES, new LibraryPropertiesAction(panel.frame(), stateManager))); + + return contextMenu; + } + public void addTab(BasePanel basePanel, boolean raisePanel) { // add tab Tab newTab = new Tab(basePanel.getTabTitle(), basePanel); @@ -1086,6 +1097,9 @@ public void addTab(BasePanel basePanel, boolean raisePanel) { event.consume(); }); + // add tab context menu + newTab.setContextMenu(createTabContextMenu(Globals.getKeyPrefs(), basePanel, stateManager)); + // update all tab titles updateAllTabTitles(); diff --git a/src/main/java/org/jabref/gui/actions/ActionHelper.java b/src/main/java/org/jabref/gui/actions/ActionHelper.java index bbb22b6022f..dfad438f8c0 100644 --- a/src/main/java/org/jabref/gui/actions/ActionHelper.java +++ b/src/main/java/org/jabref/gui/actions/ActionHelper.java @@ -55,6 +55,10 @@ public static BooleanExpression isFilePresentForSelectedEntry(StateManager state List files = entry.getFiles(); if ((entry.getFiles().size() > 0) && stateManager.getActiveDatabase().isPresent()) { + if (files.get(0).isOnlineLink()) { + return true; + } + Optional filename = FileHelper.find( stateManager.getActiveDatabase().get(), files.get(0).getLink(), diff --git a/src/main/java/org/jabref/gui/entryeditor/EntryEditor.java b/src/main/java/org/jabref/gui/entryeditor/EntryEditor.java index 7aba4446f67..9a391cf2678 100644 --- a/src/main/java/org/jabref/gui/entryeditor/EntryEditor.java +++ b/src/main/java/org/jabref/gui/entryeditor/EntryEditor.java @@ -73,11 +73,22 @@ public class EntryEditor extends BorderPane { private final BibDatabaseContext databaseContext; private final EntryEditorPreferences entryEditorPreferences; private final ExternalFilesEntryLinker fileLinker; + /* + * Tabs which can apply filter, but seems non-sense + * */ private final List tabs; private Subscription typeSubscription; - private BibEntry entry; // A reference to the entry this editor works on. + /* + * A reference to the entry this editor works on. + * */ + private BibEntry entry; private SourceTab sourceTab; + + /* + * tabs to be showed in GUI + * */ @FXML private TabPane tabbed; + @FXML private Button typeChangeButton; @FXML private Button fetcherButton; @FXML private Label typeLabel; diff --git a/src/main/java/org/jabref/gui/groups/GroupNodeViewModel.java b/src/main/java/org/jabref/gui/groups/GroupNodeViewModel.java index 8b53deaca03..e3effaa582e 100644 --- a/src/main/java/org/jabref/gui/groups/GroupNodeViewModel.java +++ b/src/main/java/org/jabref/gui/groups/GroupNodeViewModel.java @@ -35,6 +35,7 @@ import org.jabref.model.groups.GroupEntryChanger; import org.jabref.model.groups.GroupTreeNode; import org.jabref.model.strings.StringUtil; +import org.jabref.preferences.PreferencesService; import com.google.common.base.Enums; import com.tobiasdiez.easybind.EasyBind; @@ -57,13 +58,15 @@ public class GroupNodeViewModel { private final TaskExecutor taskExecutor; private final CustomLocalDragboard localDragBoard; private final ObservableList entriesList; + private final PreferencesService preferencesService; - public GroupNodeViewModel(BibDatabaseContext databaseContext, StateManager stateManager, TaskExecutor taskExecutor, GroupTreeNode groupNode, CustomLocalDragboard localDragBoard) { + public GroupNodeViewModel(BibDatabaseContext databaseContext, StateManager stateManager, TaskExecutor taskExecutor, GroupTreeNode groupNode, CustomLocalDragboard localDragBoard, PreferencesService preferencesService) { this.databaseContext = Objects.requireNonNull(databaseContext); this.taskExecutor = Objects.requireNonNull(taskExecutor); this.stateManager = Objects.requireNonNull(stateManager); this.groupNode = Objects.requireNonNull(groupNode); this.localDragBoard = Objects.requireNonNull(localDragBoard); + this.preferencesService = preferencesService; displayName = new LatexToUnicodeFormatter().format(groupNode.getName()); isRoot = groupNode.isRoot(); @@ -95,16 +98,16 @@ public GroupNodeViewModel(BibDatabaseContext databaseContext, StateManager state allSelectedEntriesMatched = selectedEntriesMatchStatus.isEmptyBinding().not().and(selectedEntriesMatchStatus.allMatch(matched -> matched)); } - public GroupNodeViewModel(BibDatabaseContext databaseContext, StateManager stateManager, TaskExecutor taskExecutor, AbstractGroup group, CustomLocalDragboard localDragboard) { - this(databaseContext, stateManager, taskExecutor, new GroupTreeNode(group), localDragboard); + public GroupNodeViewModel(BibDatabaseContext databaseContext, StateManager stateManager, TaskExecutor taskExecutor, AbstractGroup group, CustomLocalDragboard localDragboard, PreferencesService preferencesService) { + this(databaseContext, stateManager, taskExecutor, new GroupTreeNode(group), localDragboard, preferencesService); } - static GroupNodeViewModel getAllEntriesGroup(BibDatabaseContext newDatabase, StateManager stateManager, TaskExecutor taskExecutor, CustomLocalDragboard localDragBoard) { - return new GroupNodeViewModel(newDatabase, stateManager, taskExecutor, DefaultGroupsFactory.getAllEntriesGroup(), localDragBoard); + static GroupNodeViewModel getAllEntriesGroup(BibDatabaseContext newDatabase, StateManager stateManager, TaskExecutor taskExecutor, CustomLocalDragboard localDragBoard, PreferencesService preferencesService) { + return new GroupNodeViewModel(newDatabase, stateManager, taskExecutor, DefaultGroupsFactory.getAllEntriesGroup(), localDragBoard, preferencesService); } private GroupNodeViewModel toViewModel(GroupTreeNode child) { - return new GroupNodeViewModel(databaseContext, stateManager, taskExecutor, child, localDragBoard); + return new GroupNodeViewModel(databaseContext, stateManager, taskExecutor, child, localDragBoard, preferencesService); } public List addEntriesToGroup(List entries) { @@ -251,13 +254,15 @@ private void updateMatchedEntries() { // We calculate the new hit value // We could be more intelligent and try to figure out the new number of hits based on the entry change // for example, a previously matched entry gets removed -> hits = hits - 1 - BackgroundTask - .wrap(() -> groupNode.findMatches(databaseContext.getDatabase())) - .onSuccess(entries -> { - matchedEntries.clear(); - matchedEntries.addAll(entries); - }) - .executeWith(taskExecutor); + if (preferencesService.getDisplayGroupCount()) { + BackgroundTask + .wrap(() -> groupNode.findMatches(databaseContext.getDatabase())) + .onSuccess(entries -> { + matchedEntries.clear(); + matchedEntries.addAll(entries); + }) + .executeWith(taskExecutor); + } } public GroupTreeNode addSubgroup(AbstractGroup subgroup) { diff --git a/src/main/java/org/jabref/gui/groups/GroupTreeViewModel.java b/src/main/java/org/jabref/gui/groups/GroupTreeViewModel.java index 8d58b990143..a5d51cc7328 100644 --- a/src/main/java/org/jabref/gui/groups/GroupTreeViewModel.java +++ b/src/main/java/org/jabref/gui/groups/GroupTreeViewModel.java @@ -125,13 +125,13 @@ private void onActiveDatabaseChanged(Optional newDatabase) { GroupNodeViewModel newRoot = newDatabase .map(BibDatabaseContext::getMetaData) .flatMap(MetaData::getGroups) - .map(root -> new GroupNodeViewModel(newDatabase.get(), stateManager, taskExecutor, root, localDragboard)) - .orElse(GroupNodeViewModel.getAllEntriesGroup(newDatabase.get(), stateManager, taskExecutor, localDragboard)); + .map(root -> new GroupNodeViewModel(newDatabase.get(), stateManager, taskExecutor, root, localDragboard, preferences)) + .orElse(GroupNodeViewModel.getAllEntriesGroup(newDatabase.get(), stateManager, taskExecutor, localDragboard, preferences)); rootGroup.setValue(newRoot); selectedGroups.setAll( stateManager.getSelectedGroup(newDatabase.get()).stream() - .map(selectedGroup -> new GroupNodeViewModel(newDatabase.get(), stateManager, taskExecutor, selectedGroup, localDragboard)) + .map(selectedGroup -> new GroupNodeViewModel(newDatabase.get(), stateManager, taskExecutor, selectedGroup, localDragboard, preferences)) .collect(Collectors.toList())); } else { rootGroup.setValue(null); diff --git a/src/main/java/org/jabref/gui/libraryproperties/LibraryPropertiesDialogViewModel.java b/src/main/java/org/jabref/gui/libraryproperties/LibraryPropertiesDialogViewModel.java index 9d41d1b8341..3f1c562701d 100644 --- a/src/main/java/org/jabref/gui/libraryproperties/LibraryPropertiesDialogViewModel.java +++ b/src/main/java/org/jabref/gui/libraryproperties/LibraryPropertiesDialogViewModel.java @@ -26,6 +26,7 @@ import org.jabref.model.database.shared.DatabaseLocation; import org.jabref.model.entry.field.Field; import org.jabref.model.entry.field.FieldFactory; +import org.jabref.model.entry.field.InternalField; import org.jabref.model.metadata.MetaData; import org.jabref.model.metadata.SaveOrderConfig; import org.jabref.preferences.PreferencesService; @@ -106,6 +107,8 @@ void setValues() { } Set fieldNames = FieldFactory.getCommonFields(); + // allow entrytype field as sort criterion + fieldNames.add(InternalField.TYPE_HEADER); primarySortFieldsProperty.addAll(fieldNames); secondarySortFieldsProperty.addAll(fieldNames); tertiarySortFieldsProperty.addAll(fieldNames); diff --git a/src/main/java/org/jabref/gui/maintable/BibEntryTableViewModel.java b/src/main/java/org/jabref/gui/maintable/BibEntryTableViewModel.java index c09c540ef39..cbd68c085a3 100644 --- a/src/main/java/org/jabref/gui/maintable/BibEntryTableViewModel.java +++ b/src/main/java/org/jabref/gui/maintable/BibEntryTableViewModel.java @@ -1,5 +1,6 @@ package org.jabref.gui.maintable; +import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -7,6 +8,7 @@ import java.util.Optional; import java.util.stream.Collectors; +import javafx.beans.Observable; import javafx.beans.binding.Bindings; import javafx.beans.property.SimpleObjectProperty; import javafx.beans.value.ObservableValue; @@ -32,14 +34,14 @@ public class BibEntryTableViewModel { private final BibEntry entry; private final BibDatabase database; - private final MainTableNameFormatter nameFormatter; + private final ObservableValue nameFormatter; private final Map> fieldValues = new HashMap<>(); private final Map> specialFieldValues = new HashMap<>(); private final EasyBinding> linkedFiles; private final EasyBinding> linkedIdentifiers; private final ObservableValue> matchedGroups; - public BibEntryTableViewModel(BibEntry entry, BibDatabaseContext database, MainTableNameFormatter nameFormatter) { + public BibEntryTableViewModel(BibEntry entry, BibDatabaseContext database, ObservableValue nameFormatter) { this.entry = entry; this.database = database.getDatabase(); this.nameFormatter = nameFormatter; @@ -115,28 +117,30 @@ public ObservableValue getFields(OrFields fields) { ObservableValue value = fieldValues.get(fields); if (value != null) { return value; - } else { - value = Bindings.createStringBinding(() -> { - boolean isName = false; + } - Optional content = Optional.empty(); - for (Field field : fields) { - content = entry.getResolvedFieldOrAliasLatexFree(field, database); - if (content.isPresent()) { - isName = field.getProperties().contains(FieldProperty.PERSON_NAMES); - break; - } - } + ArrayList observables = new ArrayList<>(List.of(entry.getObservables())); + observables.add(nameFormatter); + + value = Bindings.createStringBinding(() -> { + for (Field field : fields) { + if (field.getProperties().contains(FieldProperty.PERSON_NAMES)) { + Optional name = entry.getResolvedFieldOrAlias(field, database); - String result = content.orElse(null); - if (isName) { - return nameFormatter.formatName(result); + if (name.isPresent()) { + return nameFormatter.getValue().formatNameLatexFree(name.get()); + } } else { - return result; + Optional content = entry.getResolvedFieldOrAliasLatexFree(field, database); + + if (content.isPresent()) { + return content.get(); + } } - }, entry.getObservables()); - fieldValues.put(fields, value); - return value; - } + } + return ""; + }, observables.toArray(Observable[]::new)); + fieldValues.put(fields, value); + return value; } } diff --git a/src/main/java/org/jabref/gui/maintable/MainTableDataModel.java b/src/main/java/org/jabref/gui/maintable/MainTableDataModel.java index 7076a0adf4a..e9f6ecb5b82 100644 --- a/src/main/java/org/jabref/gui/maintable/MainTableDataModel.java +++ b/src/main/java/org/jabref/gui/maintable/MainTableDataModel.java @@ -5,12 +5,14 @@ import javafx.beans.binding.Bindings; import javafx.beans.property.IntegerProperty; +import javafx.beans.property.ObjectProperty; import javafx.beans.property.SimpleIntegerProperty; +import javafx.beans.property.SimpleObjectProperty; import javafx.collections.ObservableList; import javafx.collections.transformation.FilteredList; import javafx.collections.transformation.SortedList; -import org.jabref.Globals; +import org.jabref.gui.StateManager; import org.jabref.gui.groups.GroupViewMode; import org.jabref.gui.util.BindingsHelper; import org.jabref.logic.search.SearchQuery; @@ -19,6 +21,7 @@ import org.jabref.model.groups.GroupTreeNode; import org.jabref.model.search.matchers.MatcherSet; import org.jabref.model.search.matchers.MatcherSets; +import org.jabref.preferences.PreferencesService; import com.tobiasdiez.easybind.EasyBind; @@ -26,24 +29,27 @@ public class MainTableDataModel { private final FilteredList entriesFiltered; private final SortedList entriesSorted; private final GroupViewMode groupViewMode; + private final ObjectProperty nameFormatter; + private final PreferencesService preferencesService; - public MainTableDataModel(BibDatabaseContext context) { - ObservableList allEntries = BindingsHelper.forUI(context.getDatabase().getEntries()); + public MainTableDataModel(BibDatabaseContext context, PreferencesService preferencesService, StateManager stateManager) { + this.nameFormatter = new SimpleObjectProperty<>(new MainTableNameFormatter(preferencesService)); + this.preferencesService = preferencesService; - MainTableNameFormatter nameFormatter = new MainTableNameFormatter(Globals.prefs); + ObservableList allEntries = BindingsHelper.forUI(context.getDatabase().getEntries()); ObservableList entriesViewModel = EasyBind.mapBacked(allEntries, entry -> new BibEntryTableViewModel(entry, context, nameFormatter)); entriesFiltered = new FilteredList<>(entriesViewModel); entriesFiltered.predicateProperty().bind( - EasyBind.combine(Globals.stateManager.activeGroupProperty(), Globals.stateManager.activeSearchQueryProperty(), (groups, query) -> entry -> isMatched(groups, query, entry)) + EasyBind.combine(stateManager.activeGroupProperty(), stateManager.activeSearchQueryProperty(), (groups, query) -> entry -> isMatched(groups, query, entry)) ); IntegerProperty resultSize = new SimpleIntegerProperty(); resultSize.bind(Bindings.size(entriesFiltered)); - Globals.stateManager.setActiveSearchResultSize(context, resultSize); + stateManager.setActiveSearchResultSize(context, resultSize); // We need to wrap the list since otherwise sorting in the table does not work entriesSorted = new SortedList<>(entriesFiltered); - groupViewMode = Globals.prefs.getGroupViewMode(); + groupViewMode = preferencesService.getGroupViewMode(); } private boolean isMatched(ObservableList groups, Optional query, BibEntryTableViewModel entry) { @@ -78,4 +84,8 @@ private Optional createGroupMatcher(List selectedGrou public SortedList getEntriesFilteredAndSorted() { return entriesSorted; } + + public void refresh() { + this.nameFormatter.setValue(new MainTableNameFormatter(preferencesService)); + } } diff --git a/src/main/java/org/jabref/gui/maintable/MainTableNameFormatPreferences.java b/src/main/java/org/jabref/gui/maintable/MainTableNameFormatPreferences.java new file mode 100644 index 00000000000..fde7f1e7b66 --- /dev/null +++ b/src/main/java/org/jabref/gui/maintable/MainTableNameFormatPreferences.java @@ -0,0 +1,30 @@ +package org.jabref.gui.maintable; + +public class MainTableNameFormatPreferences { + + public enum DisplayStyle { + NATBIB, AS_IS, FIRSTNAME_LASTNAME, LASTNAME_FIRSTNAME + } + + public enum AbbreviationStyle { + NONE, LASTNAME_ONLY, FULL + } + + private final DisplayStyle displayStyle; + private final AbbreviationStyle abbreviationStyle; + + public MainTableNameFormatPreferences(DisplayStyle displayStyle, + AbbreviationStyle abbreviationStyle) { + + this.displayStyle = displayStyle; + this.abbreviationStyle = abbreviationStyle; + } + + public DisplayStyle getDisplayStyle() { + return displayStyle; + } + + public AbbreviationStyle getAbbreviationStyle() { + return abbreviationStyle; + } +} diff --git a/src/main/java/org/jabref/gui/maintable/MainTableNameFormatter.java b/src/main/java/org/jabref/gui/maintable/MainTableNameFormatter.java index 741c0027c66..06766cdecbe 100644 --- a/src/main/java/org/jabref/gui/maintable/MainTableNameFormatter.java +++ b/src/main/java/org/jabref/gui/maintable/MainTableNameFormatter.java @@ -1,45 +1,54 @@ package org.jabref.gui.maintable; import org.jabref.model.entry.AuthorList; -import org.jabref.preferences.JabRefPreferences; +import org.jabref.preferences.PreferencesService; public class MainTableNameFormatter { - private final boolean namesNatbib; - private final boolean namesLastOnly; - private final boolean namesAsIs; - private final boolean namesFf; - private final boolean abbrAuthorNames; - - MainTableNameFormatter(JabRefPreferences preferences) { - namesNatbib = preferences.getBoolean(JabRefPreferences.NAMES_NATBIB); - namesLastOnly = preferences.getBoolean(JabRefPreferences.NAMES_LAST_ONLY); - namesAsIs = preferences.getBoolean(JabRefPreferences.NAMES_AS_IS); - namesFf = preferences.getBoolean(JabRefPreferences.NAMES_FIRST_LAST); - abbrAuthorNames = preferences.getBoolean(JabRefPreferences.ABBR_AUTHOR_NAMES); + private final PreferencesService preferencesService; + + MainTableNameFormatter(PreferencesService preferences) { + this.preferencesService = preferences; } /** - * Format a name field for the table, according to user preferences. + * Format a name field for the table, according to user preferences and with latex expressions translated if + * possible. * * @param nameToFormat The contents of the name field. * @return The formatted name field. */ - public String formatName(final String nameToFormat) { + public String formatNameLatexFree(final String nameToFormat) { if (nameToFormat == null) { return null; } - if (namesAsIs) { - return nameToFormat; - } else if (namesNatbib) { - return AuthorList.fixAuthorNatbib(nameToFormat); - } else if (namesLastOnly) { - return AuthorList.fixAuthorLastNameOnlyCommas(nameToFormat, false); - } else if (namesFf) { - return AuthorList.fixAuthorFirstNameFirstCommas(nameToFormat, abbrAuthorNames, false); - } else { - return AuthorList.fixAuthorLastNameFirstCommas(nameToFormat, abbrAuthorNames, false); + MainTableNameFormatPreferences nameFormatPreferences = preferencesService.getMainTableNameFormatPreferences(); + MainTableNameFormatPreferences.DisplayStyle displayStyle = nameFormatPreferences.getDisplayStyle(); + MainTableNameFormatPreferences.AbbreviationStyle abbreviationStyle = nameFormatPreferences.getAbbreviationStyle(); + + AuthorList authors = AuthorList.parse(nameToFormat); + + if (((displayStyle == MainTableNameFormatPreferences.DisplayStyle.FIRSTNAME_LASTNAME) + || (displayStyle == MainTableNameFormatPreferences.DisplayStyle.LASTNAME_FIRSTNAME)) + && abbreviationStyle == MainTableNameFormatPreferences.AbbreviationStyle.LASTNAME_ONLY) { + return authors.getAsLastNamesLatexFree(false); + } + + switch (nameFormatPreferences.getDisplayStyle()) { + case AS_IS: + return nameToFormat; + case NATBIB: + return authors.getAsNatbibLatexFree(); + case FIRSTNAME_LASTNAME: + return authors.getAsFirstLastNamesLatexFree( + abbreviationStyle == MainTableNameFormatPreferences.AbbreviationStyle.FULL, + false); + default: + case LASTNAME_FIRSTNAME: + return authors.getAsLastFirstNamesLatexFree( + abbreviationStyle == MainTableNameFormatPreferences.AbbreviationStyle.FULL, + false); } } } diff --git a/src/main/java/org/jabref/gui/maintable/PersistenceVisualStateTable.java b/src/main/java/org/jabref/gui/maintable/PersistenceVisualStateTable.java index 9e226922dfa..1a9216722b0 100644 --- a/src/main/java/org/jabref/gui/maintable/PersistenceVisualStateTable.java +++ b/src/main/java/org/jabref/gui/maintable/PersistenceVisualStateTable.java @@ -4,7 +4,7 @@ import javafx.beans.InvalidationListener; -import org.jabref.preferences.JabRefPreferences; +import org.jabref.preferences.PreferencesService; /** * Keep track of changes made to the columns (reordering, resorting, resizing). @@ -12,9 +12,9 @@ public class PersistenceVisualStateTable { private final MainTable mainTable; - private final JabRefPreferences preferences; + private final PreferencesService preferences; - public PersistenceVisualStateTable(final MainTable mainTable, JabRefPreferences preferences) { + public PersistenceVisualStateTable(final MainTable mainTable, PreferencesService preferences) { this.mainTable = mainTable; this.preferences = preferences; diff --git a/src/main/java/org/jabref/gui/preferences/ExternalTab.fxml b/src/main/java/org/jabref/gui/preferences/ExternalTab.fxml index fedcd9bdd21..d8ada3599ab 100644 --- a/src/main/java/org/jabref/gui/preferences/ExternalTab.fxml +++ b/src/main/java/org/jabref/gui/preferences/ExternalTab.fxml @@ -4,23 +4,17 @@ - - + - - - - - diff --git a/src/main/java/org/jabref/gui/preferences/ExternalTabView.java b/src/main/java/org/jabref/gui/preferences/ExternalTabView.java index 267d1051035..a1df1709c0d 100644 --- a/src/main/java/org/jabref/gui/preferences/ExternalTabView.java +++ b/src/main/java/org/jabref/gui/preferences/ExternalTabView.java @@ -1,19 +1,21 @@ package org.jabref.gui.preferences; +import javafx.application.Platform; import javafx.fxml.FXML; import javafx.scene.control.Button; import javafx.scene.control.CheckBox; import javafx.scene.control.ComboBox; -import javafx.scene.control.RadioButton; import javafx.scene.control.TextField; -import org.jabref.gui.JabRefFrame; import org.jabref.gui.push.PushToApplication; +import org.jabref.gui.push.PushToApplicationsManager; +import org.jabref.gui.util.IconValidationDecorator; import org.jabref.gui.util.ViewModelListCellFactory; import org.jabref.logic.l10n.Localization; import org.jabref.preferences.JabRefPreferences; import com.airhacks.afterburner.views.ViewLoader; +import de.saxsys.mvvmfx.utils.validation.visualization.ControlsFxVisualizer; public class ExternalTabView extends AbstractPreferenceTabView implements PreferencesTab { @@ -23,21 +25,21 @@ public class ExternalTabView extends AbstractPreferenceTabView pushToApplicationCombo; @FXML private TextField citeCommand; - @FXML private RadioButton useTerminalDefault; - @FXML private RadioButton useTerminalSpecial; - @FXML private TextField useTerminalCommand; - @FXML private Button useTerminalBrowse; + @FXML private CheckBox useCustomTerminal; + @FXML private TextField customTerminalCommand; + @FXML private Button customTerminalBrowse; - @FXML private RadioButton useFileBrowserDefault; - @FXML private RadioButton useFileBrowserSpecial; - @FXML private TextField useFileBrowserSpecialCommand; - @FXML private Button useFileBrowserSpecialBrowse; + @FXML private CheckBox useCustomFileBrowser; + @FXML private TextField customFileBrowserCommand; + @FXML private Button customFileBrowserBrowse; - private final JabRefFrame frame; + private final PushToApplicationsManager pushToApplicationsManager; - public ExternalTabView(JabRefPreferences preferences, JabRefFrame frame) { + private final ControlsFxVisualizer validationVisualizer = new ControlsFxVisualizer(); + + public ExternalTabView(JabRefPreferences preferences, PushToApplicationsManager pushToApplicationsManager) { this.preferences = preferences; - this.frame = frame; + this.pushToApplicationsManager = pushToApplicationsManager; ViewLoader.view(this) .root(this) @@ -50,7 +52,7 @@ public String getTabName() { } public void initialize() { - this.viewModel = new ExternalTabViewModel(dialogService, preferences, frame); + this.viewModel = new ExternalTabViewModel(dialogService, preferences, pushToApplicationsManager); new ViewModelListCellFactory() .withText(PushToApplication::getApplicationName) @@ -64,17 +66,21 @@ public void initialize() { pushToApplicationCombo.valueProperty().bindBidirectional(viewModel.selectedPushToApplication()); citeCommand.textProperty().bindBidirectional(viewModel.citeCommandProperty()); - useTerminalDefault.selectedProperty().bindBidirectional(viewModel.useTerminalDefaultProperty()); - useTerminalSpecial.selectedProperty().bindBidirectional(viewModel.useTerminalSpecialProperty()); - useTerminalCommand.textProperty().bindBidirectional(viewModel.useTerminalCommandProperty()); - useTerminalCommand.disableProperty().bind(useTerminalSpecial.selectedProperty().not()); - useTerminalBrowse.disableProperty().bind(useTerminalSpecial.selectedProperty().not()); - - useFileBrowserDefault.selectedProperty().bindBidirectional(viewModel.useFileBrowserDefaultProperty()); - useFileBrowserSpecial.selectedProperty().bindBidirectional(viewModel.useFileBrowserSpecialProperty()); - useFileBrowserSpecialCommand.textProperty().bindBidirectional(viewModel.useFileBrowserSpecialCommandProperty()); - useFileBrowserSpecialCommand.disableProperty().bind(useFileBrowserSpecial.selectedProperty().not()); - useFileBrowserSpecialBrowse.disableProperty().bind(useFileBrowserSpecial.selectedProperty().not()); + useCustomTerminal.selectedProperty().bindBidirectional(viewModel.useCustomTerminalProperty()); + customTerminalCommand.textProperty().bindBidirectional(viewModel.customTerminalCommandProperty()); + customTerminalCommand.disableProperty().bind(useCustomTerminal.selectedProperty().not()); + customTerminalBrowse.disableProperty().bind(useCustomTerminal.selectedProperty().not()); + + useCustomFileBrowser.selectedProperty().bindBidirectional(viewModel.useCustomFileBrowserProperty()); + customFileBrowserCommand.textProperty().bindBidirectional(viewModel.customFileBrowserCommandProperty()); + customFileBrowserCommand.disableProperty().bind(useCustomFileBrowser.selectedProperty().not()); + customFileBrowserBrowse.disableProperty().bind(useCustomFileBrowser.selectedProperty().not()); + + validationVisualizer.setDecoration(new IconValidationDecorator()); + Platform.runLater(() -> { + validationVisualizer.initVisualization(viewModel.terminalCommandValidationStatus(), customTerminalCommand); + validationVisualizer.initVisualization(viewModel.fileBrowserCommandValidationStatus(), customFileBrowserCommand); + }); } @FXML @@ -89,21 +95,11 @@ void manageExternalFileTypes() { @FXML void useTerminalCommandBrowse() { - viewModel.useTerminalCommandBrowse(); - } - - @FXML - void usePDFAcrobatCommandBrowse() { - viewModel.usePDFAcrobatCommandBrowse(); - } - - @FXML - void usePDFSumatraCommandBrowse() { - viewModel.usePDFSumatraCommandBrowse(); + viewModel.customTerminalBrowse(); } @FXML void useFileBrowserSpecialCommandBrowse() { - viewModel.useFileBrowserSpecialCommandBrowse(); + viewModel.customFileBrowserBrowse(); } } diff --git a/src/main/java/org/jabref/gui/preferences/ExternalTabViewModel.java b/src/main/java/org/jabref/gui/preferences/ExternalTabViewModel.java index 720ac49a21a..21baf20bed3 100644 --- a/src/main/java/org/jabref/gui/preferences/ExternalTabViewModel.java +++ b/src/main/java/org/jabref/gui/preferences/ExternalTabViewModel.java @@ -16,7 +16,6 @@ import javafx.scene.control.DialogPane; import org.jabref.gui.DialogService; -import org.jabref.gui.JabRefFrame; import org.jabref.gui.externalfiletype.EditExternalFileTypesAction; import org.jabref.gui.push.PushToApplication; import org.jabref.gui.push.PushToApplicationSettings; @@ -24,79 +23,116 @@ import org.jabref.gui.util.FileDialogConfiguration; import org.jabref.logic.l10n.Localization; import org.jabref.model.strings.StringUtil; -import org.jabref.preferences.JabRefPreferences; +import org.jabref.preferences.ExternalApplicationsPreferences; +import org.jabref.preferences.PreferencesService; + +import de.saxsys.mvvmfx.utils.validation.CompositeValidator; +import de.saxsys.mvvmfx.utils.validation.FunctionBasedValidator; +import de.saxsys.mvvmfx.utils.validation.ValidationMessage; +import de.saxsys.mvvmfx.utils.validation.ValidationStatus; +import de.saxsys.mvvmfx.utils.validation.Validator; public class ExternalTabViewModel implements PreferenceTabViewModel { private final StringProperty eMailReferenceSubjectProperty = new SimpleStringProperty(""); private final BooleanProperty autoOpenAttachedFoldersProperty = new SimpleBooleanProperty(); - private final ListProperty pushToApplicationsListProperty = new SimpleListProperty<>(); private final ObjectProperty selectedPushToApplicationProperty = new SimpleObjectProperty<>(); private final StringProperty citeCommandProperty = new SimpleStringProperty(""); + private final BooleanProperty useCustomTerminalProperty = new SimpleBooleanProperty(); + private final StringProperty customTerminalCommandProperty = new SimpleStringProperty(""); + private final BooleanProperty useCustomFileBrowserProperty = new SimpleBooleanProperty(); + private final StringProperty customFileBrowserCommandProperty = new SimpleStringProperty(""); - private final BooleanProperty useTerminalDefaultProperty = new SimpleBooleanProperty(); - private final BooleanProperty useTerminalSpecialProperty = new SimpleBooleanProperty(); - private final StringProperty useTerminalCommandProperty = new SimpleStringProperty(""); - - private final BooleanProperty usePDFAcrobatProperty = new SimpleBooleanProperty(); - private final StringProperty usePDFAcrobatCommandProperty = new SimpleStringProperty(""); - private final BooleanProperty usePDFSumatraProperty = new SimpleBooleanProperty(); - private final StringProperty usePDFSumatraCommandProperty = new SimpleStringProperty(""); - - private final BooleanProperty useFileBrowserDefaultProperty = new SimpleBooleanProperty(); - private final BooleanProperty useFileBrowserSpecialProperty = new SimpleBooleanProperty(); - private final StringProperty useFileBrowserSpecialCommandProperty = new SimpleStringProperty(""); + private final Validator terminalCommandValidator; + private final Validator fileBrowserCommandValidator; private final DialogService dialogService; - private final JabRefPreferences preferences; - private final JabRefFrame frame; + private final PreferencesService preferences; + private final PushToApplicationsManager pushToApplicationsManager; private final FileDialogConfiguration fileDialogConfiguration = new FileDialogConfiguration.Builder().build(); - public ExternalTabViewModel(DialogService dialogService, JabRefPreferences preferences, JabRefFrame frame) { + public ExternalTabViewModel(DialogService dialogService, PreferencesService preferencesService, PushToApplicationsManager pushToApplicationsManager) { this.dialogService = dialogService; - this.preferences = preferences; - this.frame = frame; + this.preferences = preferencesService; + this.pushToApplicationsManager = pushToApplicationsManager; + + terminalCommandValidator = new FunctionBasedValidator<>( + customTerminalCommandProperty, + input -> !StringUtil.isNullOrEmpty(input), + ValidationMessage.error(String.format("%s > %s %n %n %s", + Localization.lang("External programs"), + Localization.lang("Custom applications"), + Localization.lang("Please specify a terminal application.")))); + + fileBrowserCommandValidator = new FunctionBasedValidator<>( + customFileBrowserCommandProperty, + input -> !StringUtil.isNullOrEmpty(input), + ValidationMessage.error(String.format("%s > %s %n %n %s", + Localization.lang("External programs"), + Localization.lang("Custom applications"), + Localization.lang("Please specify a file browser.")))); } public void setValues() { + ExternalApplicationsPreferences initialPreferences = + preferences.getExternalApplicationsPreferences(); - eMailReferenceSubjectProperty.setValue(preferences.get(JabRefPreferences.EMAIL_SUBJECT)); - autoOpenAttachedFoldersProperty.setValue(preferences.getBoolean(JabRefPreferences.OPEN_FOLDERS_OF_ATTACHED_FILES)); + eMailReferenceSubjectProperty.setValue(initialPreferences.getEmailSubject()); + autoOpenAttachedFoldersProperty.setValue(initialPreferences.shouldAutoOpenEmailAttachmentsFolder()); - pushToApplicationsListProperty.setValue(FXCollections.observableArrayList(frame.getPushToApplicationsManager().getApplications())); - selectedPushToApplicationProperty.setValue(preferences.getActivePushToApplication(frame.getPushToApplicationsManager())); - citeCommandProperty.setValue(preferences.get(JabRefPreferences.CITE_COMMAND)); + pushToApplicationsListProperty.setValue( + FXCollections.observableArrayList(pushToApplicationsManager.getApplications())); + selectedPushToApplicationProperty.setValue( + pushToApplicationsManager.getApplicationByName(initialPreferences.getPushToApplicationName())); - useTerminalDefaultProperty.setValue(preferences.getBoolean(JabRefPreferences.USE_DEFAULT_CONSOLE_APPLICATION)); - useTerminalCommandProperty.setValue(preferences.get(JabRefPreferences.CONSOLE_COMMAND)); - useTerminalSpecialProperty.setValue(!preferences.getBoolean(JabRefPreferences.USE_DEFAULT_CONSOLE_APPLICATION)); - - useFileBrowserDefaultProperty.setValue(preferences.getBoolean(JabRefPreferences.USE_DEFAULT_FILE_BROWSER_APPLICATION)); - useFileBrowserSpecialProperty.setValue(!preferences.getBoolean(JabRefPreferences.USE_DEFAULT_FILE_BROWSER_APPLICATION)); - useFileBrowserSpecialCommandProperty.setValue(preferences.get(JabRefPreferences.FILE_BROWSER_COMMAND)); + citeCommandProperty.setValue(initialPreferences.getCiteCommand()); + useCustomTerminalProperty.setValue(initialPreferences.useCustomTerminal()); + customTerminalCommandProperty.setValue(initialPreferences.getCustomTerminalCommand()); + useCustomFileBrowserProperty.setValue(initialPreferences.useCustomFileBrowser()); + customFileBrowserCommandProperty.setValue(initialPreferences.getCustomFileBrowserCommand()); } public void storeSettings() { - preferences.put(JabRefPreferences.EMAIL_SUBJECT, eMailReferenceSubjectProperty.getValue()); - preferences.putBoolean(JabRefPreferences.OPEN_FOLDERS_OF_ATTACHED_FILES, autoOpenAttachedFoldersProperty.getValue()); + preferences.storeExternalApplicationsPreferences(new ExternalApplicationsPreferences( + eMailReferenceSubjectProperty.getValue(), + autoOpenAttachedFoldersProperty.getValue(), + selectedPushToApplicationProperty.getValue().getApplicationName(), + citeCommandProperty.getValue(), + useCustomTerminalProperty.getValue(), + customTerminalCommandProperty.getValue(), + useCustomFileBrowserProperty.getValue(), + customFileBrowserCommandProperty.getValue())); - preferences.setActivePushToApplication(selectedPushToApplicationProperty.getValue(), frame.getPushToApplicationsManager()); - preferences.put(JabRefPreferences.CITE_COMMAND, citeCommandProperty.getValue()); + pushToApplicationsManager.updateApplicationAction(selectedPushToApplicationProperty.getValue()); + } - preferences.putBoolean(JabRefPreferences.USE_DEFAULT_CONSOLE_APPLICATION, useTerminalDefaultProperty.getValue()); - preferences.put(JabRefPreferences.CONSOLE_COMMAND, useTerminalCommandProperty.getValue()); + public ValidationStatus terminalCommandValidationStatus() { + return terminalCommandValidator.getValidationStatus(); + } - preferences.putBoolean(JabRefPreferences.USE_DEFAULT_FILE_BROWSER_APPLICATION, useFileBrowserDefaultProperty.getValue()); - if (StringUtil.isNotBlank(useFileBrowserSpecialCommandProperty.getValue())) { - preferences.put(JabRefPreferences.FILE_BROWSER_COMMAND, useFileBrowserSpecialCommandProperty.getValue()); - } else { - preferences.putBoolean(JabRefPreferences.USE_DEFAULT_FILE_BROWSER_APPLICATION, true); // default if no command specified - } + public ValidationStatus fileBrowserCommandValidationStatus() { + return fileBrowserCommandValidator.getValidationStatus(); } public boolean validateSettings() { + CompositeValidator validator = new CompositeValidator(); + + if (useCustomTerminalProperty.getValue()) { + validator.addValidators(terminalCommandValidator); + } + + if (useCustomFileBrowserProperty.getValue()) { + validator.addValidators(fileBrowserCommandValidator); + } + + ValidationStatus validationStatus = validator.getValidationStatus(); + if (!validationStatus.isValid()) { + validationStatus.getHighestMessage().ifPresent(message -> + dialogService.showErrorDialogAndWait(message.getMessage())); + return false; + } return true; } @@ -106,9 +142,8 @@ public List getRestartWarnings() { } public void pushToApplicationSettings() { - PushToApplicationsManager manager = frame.getPushToApplicationsManager(); PushToApplication selectedApplication = selectedPushToApplicationProperty.getValue(); - PushToApplicationSettings settings = manager.getSettings(selectedApplication); + PushToApplicationSettings settings = pushToApplicationsManager.getSettings(selectedApplication); DialogPane dialogPane = new DialogPane(); dialogPane.setContent(settings.getSettingsPane()); @@ -129,20 +164,14 @@ public void manageExternalFileTypes() { new EditExternalFileTypesAction().execute(); } - public void useTerminalCommandBrowse() { - dialogService.showFileOpenDialog(fileDialogConfiguration).ifPresent(file -> useTerminalCommandProperty.setValue(file.toAbsolutePath().toString())); - } - - public void usePDFAcrobatCommandBrowse() { - dialogService.showFileOpenDialog(fileDialogConfiguration).ifPresent(file -> usePDFAcrobatCommandProperty.setValue(file.toAbsolutePath().toString())); + public void customTerminalBrowse() { + dialogService.showFileOpenDialog(fileDialogConfiguration) + .ifPresent(file -> customTerminalCommandProperty.setValue(file.toAbsolutePath().toString())); } - public void usePDFSumatraCommandBrowse() { - dialogService.showFileOpenDialog(fileDialogConfiguration).ifPresent(file -> usePDFSumatraCommandProperty.setValue(file.toAbsolutePath().toString())); - } - - public void useFileBrowserSpecialCommandBrowse() { - dialogService.showFileOpenDialog(fileDialogConfiguration).ifPresent(file -> useFileBrowserSpecialCommandProperty.setValue(file.toAbsolutePath().toString())); + public void customFileBrowserBrowse() { + dialogService.showFileOpenDialog(fileDialogConfiguration) + .ifPresent(file -> customFileBrowserCommandProperty.setValue(file.toAbsolutePath().toString())); } // EMail @@ -171,47 +200,21 @@ public StringProperty citeCommandProperty() { // Open console - public BooleanProperty useTerminalDefaultProperty() { - return this.useTerminalDefaultProperty; - } - - public BooleanProperty useTerminalSpecialProperty() { - return this.useTerminalSpecialProperty; + public BooleanProperty useCustomTerminalProperty() { + return this.useCustomTerminalProperty; } - public StringProperty useTerminalCommandProperty() { - return this.useTerminalCommandProperty; - } - - // Open PDF - - public BooleanProperty usePDFAcrobatProperty() { - return this.usePDFAcrobatProperty; - } - - public StringProperty usePDFAcrobatCommandProperty() { - return this.usePDFAcrobatCommandProperty; - } - - public BooleanProperty usePDFSumatraProperty() { - return this.usePDFSumatraProperty; - } - - public StringProperty usePDFSumatraCommandProperty() { - return this.usePDFSumatraCommandProperty; + public StringProperty customTerminalCommandProperty() { + return this.customTerminalCommandProperty; } // Open File Browser - public BooleanProperty useFileBrowserDefaultProperty() { - return this.useFileBrowserDefaultProperty; - } - - public BooleanProperty useFileBrowserSpecialProperty() { - return this.useFileBrowserSpecialProperty; + public BooleanProperty useCustomFileBrowserProperty() { + return this.useCustomFileBrowserProperty; } - public StringProperty useFileBrowserSpecialCommandProperty() { - return this.useFileBrowserSpecialCommandProperty; + public StringProperty customFileBrowserCommandProperty() { + return this.customFileBrowserCommandProperty; } } diff --git a/src/main/java/org/jabref/gui/preferences/FileTab.fxml b/src/main/java/org/jabref/gui/preferences/FileTab.fxml index dd1f0d4c949..ccc58a72841 100644 --- a/src/main/java/org/jabref/gui/preferences/FileTab.fxml +++ b/src/main/java/org/jabref/gui/preferences/FileTab.fxml @@ -43,7 +43,7 @@ - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + +