diff --git a/.github/workflows/automerge.yml b/.github/workflows/automerge.yml
index 3f8ca317ea1..9783e2a9aaa 100644
--- a/.github/workflows/automerge.yml
+++ b/.github/workflows/automerge.yml
@@ -16,7 +16,7 @@ jobs:
env:
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
- name: Auto approve
- uses: hmarr/auto-approve-action@v2.2.1
+ uses: hmarr/auto-approve-action@v2.4.0
if: steps.waitforstatuschecks.outputs.status == 'success'
with:
github-token: "${{ secrets.GITHUB_TOKEN }}"
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b394a70a676..8bef8a1d12b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -11,15 +11,39 @@ Note that this project **does not** adhere to [Semantic Versioning](http://semve
### Added
+- In case a backup is found, the filename of the backup is shown.
+- On startup, JabRef notifies the user if there were parsing errors during opening.
+- We integrated a new three-way merge UI for merging entries in the Entries Merger Dialog, the Duplicate Resolver Dialog, the Entry Importer Dialog, and the External Changes Resolver Dialog. [#8945](https://github.com/JabRef/jabref/pull/8945)
+- We added the ability to merge groups, keywords, comments and files when merging entries. [#9022](https://github.com/JabRef/jabref/pull/9022)
+- We added a warning message next to the authors field in the merge dialog to warn users when the authors are the same but formatted differently. [#8745](https://github.com/JabRef/jabref/issues/8745)
+
### Changed
- We improved the Citavi Importer to also import so called Knowledge-items into the field `comment` of the corresponding entry [#9025](https://github.com/JabRef/jabref/issues/9025)
- We removed wrapping of string constants when writing to a `.bib` file.
+- We call backup files `.bak` and temporary writing files now `.sav`.
+- JabRef keeps 10 older versions of a `.bib` file in the [user data dir](https://github.com/harawata/appdirs#supported-directories) (instead of a single `.sav` (now: `.bak`) file in the directory of the `.bib` file)
- We changed the button label from "Return to JabRef" to "Return to library" to better indicate the purpose of the action.
+- We removed "last-search-date" from the SLR feature, because the last-search-date can be deducted from the git logs. [#9116](https://github.com/JabRef/jabref/pull/9116)
+- A user can now add arbitrary data into `study.yml`. JabRef just ignores this data. [#9124](https://github.com/JabRef/jabref/pull/9124)
+- We reworked the External Changes Resolver dialog. [#9021](https://github.com/JabRef/jabref/pull/9021)
+- We reworked the Define study parameters dialog. [#9123](https://github.com/JabRef/jabref/pull/9123)
### Fixed
+- We fixed an issue where author names with tilde accents (for example ñ) were marked as "Names are not in the standard BibTex format" [#8071](https://github.com/JabRef/jabref/issues/8071)
+- We fixed an issue where the possibility to generate a subdatabase from an aux file was writing empty files when called from the commandline [#9115](https://github.com/JabRef/jabref/issues/9115), [forum#3516](https://discourse.jabref.org/t/export-subdatabase-from-aux-file-on-macos-command-line/3516)
+- We fixed the display of issue, number, eid and pages fields in the entry preview. [#8607](https://github.com/JabRef/jabref/pull/8607), [#8372](https://github.com/JabRef/jabref/issues/8372), [Koppor#514](https://github.com/koppor/jabref/issues/514), [forum#2390](https://discourse.jabref.org/t/unable-to-edit-my-bibtex-file-that-i-used-before-vers-5-1/2390), [forum#3462](https://discourse.jabref.org/t/jabref-5-6-need-help-with-export-from-jabref-to-microsoft-word-entry-preview-of-apa-7-not-rendering-correctly/3462)
+- We fixed the page ranges checker to detect article numbers in the pages field (used at [Check Integrity](https://docs.jabref.org/finding-sorting-and-cleaning-entries/checkintegrity)). [#8607](https://github.com/JabRef/jabref/pull/8607)
+- The [HtmlToLaTeXFormatter](https://docs.jabref.org/finding-sorting-and-cleaning-entries/saveactions#html-to-latex) keeps single `<` characters.
- We fixed a performance regression when opening large libraries [#9041](https://github.com/JabRef/jabref/issues/9041)
+- We fixed a bug where spaces are trimmed when highlighting differences in the Entries merge dialog. [koppor#371](https://github.com/koppor/jabref/issues/371)
+- We fixed some visual glitches with the linked files editor field in the entry editor and increased its height. [#8823](https://github.com/JabRef/jabref/issues/8823)
+- We fixed several bugs regarding the manual and the autosave of library files that sometimes lead to exceptions or data loss. [#9067](https://github.com/JabRef/jabref/pull/9067), [#8448](https://github.com/JabRef/jabref/issues/8484), [#8746](https://github.com/JabRef/jabref/issues/8746), [#6684](https://github.com/JabRef/jabref/issues/6684), [#6644](https://github.com/JabRef/jabref/issues/6644), [#6102](https://github.com/JabRef/jabref/issues/6102), [#6002](https://github.com/JabRef/jabref/issues/6000)
+- We fixed an issue where applied save actions on saving the library file would lead to the dialog "The libary has been modified by another program" popping up [#4877](https://github.com/JabRef/jabref/issues/4877)
+- We fixed issues with save actions not correctly loaded when opening the library. [#9122](https://github.com/JabRef/jabref/pull/9122)
+- We fixed an issue where title case didn't capitalize words after en-dash characters. [#9068](https://github.com/JabRef/jabref/pull/9068)
+- We fixed an issue where JabRef would not exit when a connection to a LibreOffice document was established previously and the document is still open. [#9075](https://github.com/JabRef/jabref/issues/9075)
### Removed
@@ -29,7 +53,6 @@ Note that this project **does not** adhere to [Semantic Versioning](http://semve
-
## [5.7] - 2022-08-05
### Added
@@ -88,6 +111,7 @@ Note that this project **does not** adhere to [Semantic Versioning](http://semve
- We fixed a bug where updating group view mode (intersection or union) requires re-selecting groups to take effect. [#6998](https://github.com/JabRef/jabref/issues/6998)
- We fixed a bug that prevented external group metadata changes from being merged. [#8873](https://github.com/JabRef/jabref/issues/8873)
- We fixed the shared database opening dialog to remember autosave folder and tick. [#7516](https://github.com/JabRef/jabref/issues/7516)
+- We fixed an issue where name formatter could not be saved. [#9120](https://github.com/JabRef/jabref/issues/9120)
### Removed
diff --git a/build.gradle b/build.gradle
index 7fa96605dfb..2fb490c2cf4 100644
--- a/build.gradle
+++ b/build.gradle
@@ -128,12 +128,12 @@ dependencies {
implementation group: 'org.apache.tika', name: 'tika-core', version: '2.4.1'
// required for reading write-protected PDFs - see https://github.com/JabRef/jabref/pull/942#issuecomment-209252635
- implementation 'org.bouncycastle:bcprov-jdk18on:1.71'
+ implementation 'org.bouncycastle:bcprov-jdk18on:1.71.1'
implementation 'commons-cli:commons-cli:1.5.0'
- implementation 'org.libreoffice:libreoffice:7.3.5'
- implementation 'org.libreoffice:unoloader:7.3.5'
+ implementation 'org.libreoffice:libreoffice:7.4.0'
+ implementation 'org.libreoffice:unoloader:7.4.0'
implementation 'io.github.java-diff-utils:java-diff-utils:4.12'
implementation 'info.debatty:java-string-similarity:2.0.0'
@@ -148,7 +148,7 @@ dependencies {
implementation group: 'org.mariadb.jdbc', name: 'mariadb-java-client', version: '2.7.6'
- implementation 'org.postgresql:postgresql:42.4.1'
+ implementation 'org.postgresql:postgresql:42.5.0'
implementation ('com.oracle.ojdbc:ojdbc10:19.3.0.0') {
// causing module issues
@@ -174,13 +174,13 @@ dependencies {
implementation 'com.jfoenix:jfoenix:9.0.10'
implementation 'org.controlsfx:controlsfx:11.1.1'
- implementation 'org.jsoup:jsoup:1.15.1'
- implementation 'com.konghq:unirest-java:3.13.10'
+ implementation 'org.jsoup:jsoup:1.15.3'
+ implementation 'com.konghq:unirest-java:3.13.11'
- implementation 'org.slf4j:slf4j-api:2.0.0-beta1'
- implementation "org.tinylog:tinylog-api:2.4.1"
- implementation "org.tinylog:slf4j-tinylog:2.4.1"
- implementation "org.tinylog:tinylog-impl:2.4.1"
+ implementation 'org.slf4j:slf4j-api:2.0.0'
+ implementation "org.tinylog:tinylog-api:2.5.0"
+ implementation "org.tinylog:slf4j-tinylog:2.5.0"
+ implementation "org.tinylog:tinylog-impl:2.5.0"
implementation 'de.undercouch:citeproc-java:3.0.0-alpha.6'
@@ -207,7 +207,7 @@ dependencies {
testImplementation 'org.junit.jupiter:junit-jupiter:5.9.0'
testImplementation 'org.junit.platform:junit-platform-launcher:1.9.0'
- testImplementation 'org.mockito:mockito-core:4.6.1'
+ testImplementation 'org.mockito:mockito-core:4.7.0'
testImplementation 'org.xmlunit:xmlunit-core:2.9.0'
testImplementation 'org.xmlunit:xmlunit-matchers:2.9.0'
testRuntimeOnly 'com.tngtech.archunit:archunit-junit5-engine:0.23.1'
@@ -216,7 +216,7 @@ dependencies {
testImplementation "org.testfx:testfx-junit5:4.0.16-alpha"
testImplementation "org.hamcrest:hamcrest-library:2.2"
- checkstyle 'com.puppycrawl.tools:checkstyle:10.3.2'
+ checkstyle 'com.puppycrawl.tools:checkstyle:10.3.3'
// xjc needs the runtime as well for the ant task, otherwise it fails
xjc group: 'org.glassfish.jaxb', name: 'jaxb-xjc', version: '3.0.2'
xjc group: 'org.glassfish.jaxb', name: 'jaxb-runtime', version: '3.0.2'
diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle
index 7a8603ed300..a8c68783372 100644
--- a/buildSrc/build.gradle
+++ b/buildSrc/build.gradle
@@ -8,7 +8,7 @@ repositories {
dependencies {
implementation 'com.h2database:h2-mvstore:2.1.214'
implementation 'org.apache.commons:commons-csv:1.9.0'
- implementation 'org.slf4j:slf4j-api:2.0.0-beta1'
+ implementation 'org.slf4j:slf4j-api:2.0.0'
}
sourceSets{
diff --git a/buildres/csl/csl-locales/.github/workflows/merge.yaml b/buildres/csl/csl-locales/.github/workflows/merge.yaml
index 7e5736cc960..f52a6ba4110 100644
--- a/buildres/csl/csl-locales/.github/workflows/merge.yaml
+++ b/buildres/csl/csl-locales/.github/workflows/merge.yaml
@@ -136,14 +136,14 @@ jobs:
- name: Bump version and push tag
id: tag_version
- uses: mathieudutour/github-tag-action@v5.6
+ uses: mathieudutour/github-tag-action@v6.0
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
default_bump: patch
if: github.event_name == 'push' && hashFiles('composer.json') != '' && (steps.update.outputs.updated == 'true' || steps.update.outputs.deleted == 'true')
- name: Create a GitHub release
- uses: softprops/action-gh-release@v0.1.14
+ uses: softprops/action-gh-release@v1
env:
github_token: ${{ secrets.GITHUB_TOKEN }}
with:
diff --git a/buildres/csl/csl-styles/.github/workflows/merge.yaml b/buildres/csl/csl-styles/.github/workflows/merge.yaml
index 7e5736cc960..f52a6ba4110 100644
--- a/buildres/csl/csl-styles/.github/workflows/merge.yaml
+++ b/buildres/csl/csl-styles/.github/workflows/merge.yaml
@@ -136,14 +136,14 @@ jobs:
- name: Bump version and push tag
id: tag_version
- uses: mathieudutour/github-tag-action@v5.6
+ uses: mathieudutour/github-tag-action@v6.0
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
default_bump: patch
if: github.event_name == 'push' && hashFiles('composer.json') != '' && (steps.update.outputs.updated == 'true' || steps.update.outputs.deleted == 'true')
- name: Create a GitHub release
- uses: softprops/action-gh-release@v0.1.14
+ uses: softprops/action-gh-release@v1
env:
github_token: ${{ secrets.GITHUB_TOKEN }}
with:
diff --git a/buildres/csl/csl-styles/acta-universitatis-agriculturae-sueciae.csl b/buildres/csl/csl-styles/acta-universitatis-agriculturae-sueciae.csl
index 8a0830b3456..1c921f4204b 100644
--- a/buildres/csl/csl-styles/acta-universitatis-agriculturae-sueciae.csl
+++ b/buildres/csl/csl-styles/acta-universitatis-agriculturae-sueciae.csl
@@ -15,12 +15,16 @@
Style as per the May 2019 guidelines.
- 2021-11-10T10:39:01+00:00
+ 2022-07-13T09:26:37+00:00This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Licenseed
+
+ ed.
+ eds
+
@@ -34,15 +38,10 @@
-
-
-
-
-
-
-
-
-
+
+
+
+
@@ -93,8 +92,14 @@
-
+
+
+
+
+
+
+
@@ -244,20 +249,26 @@
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -313,7 +324,7 @@
-
+
@@ -325,7 +336,6 @@
-
@@ -338,7 +348,6 @@
-
diff --git a/buildres/csl/csl-styles/anais-da-academia-brasileira-de-ciencias.csl b/buildres/csl/csl-styles/anais-da-academia-brasileira-de-ciencias.csl
new file mode 100644
index 00000000000..d3c14f3c89c
--- /dev/null
+++ b/buildres/csl/csl-styles/anais-da-academia-brasileira-de-ciencias.csl
@@ -0,0 +1,159 @@
+
+
diff --git a/buildres/csl/csl-styles/apa-6th-edition-no-ampersand.csl b/buildres/csl/csl-styles/apa-6th-edition-no-ampersand.csl
index ff55b46b98d..70e5db1f6ab 100644
--- a/buildres/csl/csl-styles/apa-6th-edition-no-ampersand.csl
+++ b/buildres/csl/csl-styles/apa-6th-edition-no-ampersand.csl
@@ -897,7 +897,7 @@
-
+
diff --git a/buildres/csl/csl-styles/apa-6th-edition.csl b/buildres/csl/csl-styles/apa-6th-edition.csl
index 851a3096ab0..82ad9ae3084 100644
--- a/buildres/csl/csl-styles/apa-6th-edition.csl
+++ b/buildres/csl/csl-styles/apa-6th-edition.csl
@@ -896,7 +896,7 @@
-
+
diff --git a/buildres/csl/csl-styles/apa-annotated-bibliography.csl b/buildres/csl/csl-styles/apa-annotated-bibliography.csl
index 0636d93db58..055541d0d4f 100644
--- a/buildres/csl/csl-styles/apa-annotated-bibliography.csl
+++ b/buildres/csl/csl-styles/apa-annotated-bibliography.csl
@@ -1552,7 +1552,7 @@
-
+
diff --git a/buildres/csl/csl-styles/apa-cv.csl b/buildres/csl/csl-styles/apa-cv.csl
index 228ad3ee882..44888982952 100644
--- a/buildres/csl/csl-styles/apa-cv.csl
+++ b/buildres/csl/csl-styles/apa-cv.csl
@@ -1299,7 +1299,7 @@
-
+
diff --git a/buildres/csl/csl-styles/apa-no-ampersand.csl b/buildres/csl/csl-styles/apa-no-ampersand.csl
index 5759e34ec4a..bf9f189455b 100644
--- a/buildres/csl/csl-styles/apa-no-ampersand.csl
+++ b/buildres/csl/csl-styles/apa-no-ampersand.csl
@@ -1552,7 +1552,7 @@
-
+
diff --git a/buildres/csl/csl-styles/apa-no-doi-no-issue.csl b/buildres/csl/csl-styles/apa-no-doi-no-issue.csl
index c72c8bdf126..5c314996729 100644
--- a/buildres/csl/csl-styles/apa-no-doi-no-issue.csl
+++ b/buildres/csl/csl-styles/apa-no-doi-no-issue.csl
@@ -903,7 +903,7 @@
-
+
diff --git a/buildres/csl/csl-styles/apa-no-initials.csl b/buildres/csl/csl-styles/apa-no-initials.csl
index 4695cd9b6fb..910de1713e0 100644
--- a/buildres/csl/csl-styles/apa-no-initials.csl
+++ b/buildres/csl/csl-styles/apa-no-initials.csl
@@ -1552,7 +1552,7 @@
-
+
diff --git a/buildres/csl/csl-styles/apa-numeric-superscript-brackets.csl b/buildres/csl/csl-styles/apa-numeric-superscript-brackets.csl
index ba3dadf6d20..6f26b02e4c8 100644
--- a/buildres/csl/csl-styles/apa-numeric-superscript-brackets.csl
+++ b/buildres/csl/csl-styles/apa-numeric-superscript-brackets.csl
@@ -1355,7 +1355,7 @@
-
+
diff --git a/buildres/csl/csl-styles/apa-numeric-superscript.csl b/buildres/csl/csl-styles/apa-numeric-superscript.csl
index 1ed0ae88b7c..641bb4f02c4 100644
--- a/buildres/csl/csl-styles/apa-numeric-superscript.csl
+++ b/buildres/csl/csl-styles/apa-numeric-superscript.csl
@@ -1355,7 +1355,7 @@
-
+
diff --git a/buildres/csl/csl-styles/apa-old-doi-prefix.csl b/buildres/csl/csl-styles/apa-old-doi-prefix.csl
index cdfaa73a451..2ede590912d 100644
--- a/buildres/csl/csl-styles/apa-old-doi-prefix.csl
+++ b/buildres/csl/csl-styles/apa-old-doi-prefix.csl
@@ -897,7 +897,7 @@
-
+
diff --git a/buildres/csl/csl-styles/apa-single-spaced.csl b/buildres/csl/csl-styles/apa-single-spaced.csl
index ff224f6a405..ad7890fa539 100644
--- a/buildres/csl/csl-styles/apa-single-spaced.csl
+++ b/buildres/csl/csl-styles/apa-single-spaced.csl
@@ -1552,7 +1552,7 @@
-
+
diff --git a/buildres/csl/csl-styles/apa-with-abstract.csl b/buildres/csl/csl-styles/apa-with-abstract.csl
index 25d185866bf..694b609f754 100644
--- a/buildres/csl/csl-styles/apa-with-abstract.csl
+++ b/buildres/csl/csl-styles/apa-with-abstract.csl
@@ -1552,7 +1552,7 @@
-
+
diff --git a/buildres/csl/csl-styles/apa.csl b/buildres/csl/csl-styles/apa.csl
index 0fd6bd71a5e..4d965a02d24 100644
--- a/buildres/csl/csl-styles/apa.csl
+++ b/buildres/csl/csl-styles/apa.csl
@@ -1553,7 +1553,7 @@
-
+
diff --git a/buildres/csl/csl-styles/avian-conservation-and-ecology.csl b/buildres/csl/csl-styles/avian-conservation-and-ecology.csl
index e509aebd02d..c1bccf809f6 100644
--- a/buildres/csl/csl-styles/avian-conservation-and-ecology.csl
+++ b/buildres/csl/csl-styles/avian-conservation-and-ecology.csl
@@ -1,7 +1,8 @@
-
diff --git a/buildres/csl/csl-styles/cardiff-university-harvard.csl b/buildres/csl/csl-styles/cardiff-university-harvard.csl
index a3810c23159..401e2abd744 100644
--- a/buildres/csl/csl-styles/cardiff-university-harvard.csl
+++ b/buildres/csl/csl-styles/cardiff-university-harvard.csl
@@ -1,6 +1,5 @@
-
diff --git a/buildres/csl/csl-styles/dependent/oxford-brookes-university-harvard-no-et-al.csl b/buildres/csl/csl-styles/dependent/oxford-brookes-university-harvard-no-et-al.csl
new file mode 100644
index 00000000000..fa36a8b2460
--- /dev/null
+++ b/buildres/csl/csl-styles/dependent/oxford-brookes-university-harvard-no-et-al.csl
@@ -0,0 +1,14 @@
+
+
diff --git a/buildres/csl/csl-styles/dependent/oxford-brookes-university-harvard.csl b/buildres/csl/csl-styles/dependent/oxford-brookes-university-harvard.csl
new file mode 100644
index 00000000000..61143eead54
--- /dev/null
+++ b/buildres/csl/csl-styles/dependent/oxford-brookes-university-harvard.csl
@@ -0,0 +1,14 @@
+
+
diff --git a/buildres/csl/csl-styles/dependent/prifysgol-caerdydd-harvard.csl b/buildres/csl/csl-styles/dependent/prifysgol-caerdydd-harvard.csl
deleted file mode 100644
index f5e6a1ea157..00000000000
--- a/buildres/csl/csl-styles/dependent/prifysgol-caerdydd-harvard.csl
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
diff --git a/buildres/csl/csl-styles/economic-geology.csl b/buildres/csl/csl-styles/economic-geology.csl
index ec400f65765..e2395e92814 100644
--- a/buildres/csl/csl-styles/economic-geology.csl
+++ b/buildres/csl/csl-styles/economic-geology.csl
@@ -6,6 +6,7 @@
+
Patrick O'Brien, PhDobrienpat86@gmail.com
@@ -14,7 +15,7 @@
0361-01281554-0774
- 2018-02-19T15:05:37+00:00
+ 2022-07-06T08:50:29+00:00This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 License
@@ -24,13 +25,13 @@
-
+
-
+
@@ -155,7 +156,7 @@
-
+
@@ -203,6 +204,10 @@
+
+
+
+
diff --git a/buildres/csl/csl-styles/food-science-and-biotechnology.csl b/buildres/csl/csl-styles/food-science-and-biotechnology.csl
new file mode 100644
index 00000000000..e980c7cd026
--- /dev/null
+++ b/buildres/csl/csl-styles/food-science-and-biotechnology.csl
@@ -0,0 +1,194 @@
+
+
diff --git a/buildres/csl/csl-styles/harvard-anglia-ruskin-university.csl b/buildres/csl/csl-styles/harvard-anglia-ruskin-university.csl
index 7def55a6922..a482dbfd51f 100644
--- a/buildres/csl/csl-styles/harvard-anglia-ruskin-university.csl
+++ b/buildres/csl/csl-styles/harvard-anglia-ruskin-university.csl
@@ -16,7 +16,7 @@
Anglia Ruskin University Harvard style
- 2022-01-13T10:42:51+00:00
+ 2022-08-22T08:06:04+00:00This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 License
@@ -294,7 +294,7 @@
-
+
@@ -325,7 +325,7 @@
-
+
diff --git a/buildres/csl/csl-styles/harvard-cardiff-university-old.csl b/buildres/csl/csl-styles/harvard-cardiff-university-old.csl
deleted file mode 100644
index 31cc6f69ea3..00000000000
--- a/buildres/csl/csl-styles/harvard-cardiff-university-old.csl
+++ /dev/null
@@ -1,257 +0,0 @@
-
-
diff --git a/buildres/csl/csl-styles/harvard-oxford-brookes-university-faculty-of-health-and-life-sciences.csl b/buildres/csl/csl-styles/harvard-oxford-brookes-university-faculty-of-health-and-life-sciences.csl
deleted file mode 100644
index abd139694c5..00000000000
--- a/buildres/csl/csl-styles/harvard-oxford-brookes-university-faculty-of-health-and-life-sciences.csl
+++ /dev/null
@@ -1,196 +0,0 @@
-
-
diff --git a/buildres/csl/csl-styles/harvard-university-for-the-creative-arts.csl b/buildres/csl/csl-styles/harvard-university-for-the-creative-arts.csl
index d84f8d4e6d9..dade8feb696 100644
--- a/buildres/csl/csl-styles/harvard-university-for-the-creative-arts.csl
+++ b/buildres/csl/csl-styles/harvard-university-for-the-creative-arts.csl
@@ -1,5 +1,5 @@
-
diff --git a/buildres/csl/csl-styles/renamed-styles.json b/buildres/csl/csl-styles/renamed-styles.json
index e30246291be..a8127010827 100644
--- a/buildres/csl/csl-styles/renamed-styles.json
+++ b/buildres/csl/csl-styles/renamed-styles.json
@@ -438,7 +438,7 @@
"ophthalmic-surgery-lasers-and-imaging": "ophthalmic-surgery-lasers-and-imaging-retina",
"optical-society-of-america": "the-optical-society",
"organic-and-medicinal-chemistry-letters": "chemistry-central-journal",
- "oxford-brookes-university-faculty-of-health-and-life-sciences": "harvard-oxford-brookes-university-faculty-of-health-and-life-sciences",
+ "oxford-brookes-university-faculty-of-health-and-life-sciences": "harvard-cite-them-right",
"oxford-university-new-south-wales": "oxford-the-university-of-new-south-wales",
"pedobiologia-international-journal-of-soil-biology": "pedobiologia-journal-of-soil-ecology",
"pharmacoeconomics-german-research-articles": "springer-vancouver-brackets",
@@ -530,5 +530,9 @@
"reach-reviews-in-human-space-exploration": "reach",
"journal-of-oncological-science": "journal-of-oncological-sciences",
"harvard-dublin-city-university": "harvard-cite-them-right",
- "technische-universitat-dresden-medizin": "apa"
+ "technische-universitat-dresden-medizin": "apa",
+ "prifysgol-caerdydd-harvard": "cardiff-university-harvard",
+ "harvard-cardiff-university-old":"cardiff-university-harvard",
+ "harvard-oxford-brookes-university-faculty-of-health-and-life-sciences": "harvard-cite-them-right",
+ "bluebook2": "bluebook-law-review"
}
diff --git a/buildres/csl/csl-styles/sociologia-urbana-e-rurale.csl b/buildres/csl/csl-styles/sociologia-urbana-e-rurale.csl
new file mode 100644
index 00000000000..c59e6cc5b24
--- /dev/null
+++ b/buildres/csl/csl-styles/sociologia-urbana-e-rurale.csl
@@ -0,0 +1,237 @@
+
+
diff --git a/buildres/csl/csl-styles/the-journal-of-hellenic-studies.csl b/buildres/csl/csl-styles/the-journal-of-hellenic-studies.csl
index 9ce9f5531ba..bfd40f29bfe 100644
--- a/buildres/csl/csl-styles/the-journal-of-hellenic-studies.csl
+++ b/buildres/csl/csl-styles/the-journal-of-hellenic-studies.csl
@@ -4,7 +4,7 @@
The Journal of Hellenic Studieshttp://www.zotero.org/styles/the-journal-of-hellenic-studies
-
+
Polly Low
diff --git a/buildres/csl/csl-styles/universite-du-quebec-a-montreal-prenoms.csl b/buildres/csl/csl-styles/universite-du-quebec-a-montreal-prenoms.csl
new file mode 100644
index 00000000000..eb0ea68d9b9
--- /dev/null
+++ b/buildres/csl/csl-styles/universite-du-quebec-a-montreal-prenoms.csl
@@ -0,0 +1,935 @@
+
+
diff --git a/buildres/csl/csl-styles/university-of-hull-harvard.csl b/buildres/csl/csl-styles/university-of-hull-harvard.csl
new file mode 100644
index 00000000000..bf4022edecb
--- /dev/null
+++ b/buildres/csl/csl-styles/university-of-hull-harvard.csl
@@ -0,0 +1,315 @@
+
+
diff --git a/docs/advanced-reading/index.md b/docs/advanced-reading/index.md
deleted file mode 100644
index bb31c245120..00000000000
--- a/docs/advanced-reading/index.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-nav_order: 5
-has_children: true
----
-# Advanced Reading
diff --git a/docs/advanced-reading/code-quality.md b/docs/code-howtos/code-quality.md
similarity index 98%
rename from docs/advanced-reading/code-quality.md
rename to docs/code-howtos/code-quality.md
index 0cd041920cf..30d55d56f72 100644
--- a/docs/advanced-reading/code-quality.md
+++ b/docs/code-howtos/code-quality.md
@@ -1,6 +1,6 @@
---
+parent: Code Howtos
nav_order: 1
-parent: Advanced Reading
---
# Code Quality
diff --git a/docs/advanced-reading/custom-svg-icons.md b/docs/code-howtos/custom-svg-icons.md
similarity index 98%
rename from docs/advanced-reading/custom-svg-icons.md
rename to docs/code-howtos/custom-svg-icons.md
index a0bccc0622c..6e94b1dbee3 100644
--- a/docs/advanced-reading/custom-svg-icons.md
+++ b/docs/code-howtos/custom-svg-icons.md
@@ -1,6 +1,6 @@
---
-nav_order: 3
-parent: Advanced Reading
+parent: Code Howtos
+nav_order: 2
---
# Custom SVG icons
diff --git a/docs/code-howtos/error-handling.md b/docs/code-howtos/error-handling.md
new file mode 100644
index 00000000000..2df80219ed0
--- /dev/null
+++ b/docs/code-howtos/error-handling.md
@@ -0,0 +1,40 @@
+---
+parent: Code Howtos
+nav_order: 3
+---
+# Error Handling in JabRef
+
+## Throwing and Catching Exceptions
+
+Principles:
+
+* All exceptions we throw should be or extend `JabRefException`; This is especially important if the message stored in the Exception should be shown to the user. `JabRefException` has already implemented the `getLocalizedMessage()` method which should be used for such cases (see details below!).
+* Catch and wrap all API exceptions (such as `IOExceptions`) and rethrow them
+ * Example:
+
+ ```java
+ try {
+ // ...
+ } catch (IOException ioe) {
+ throw new JabRefException("Something went wrong...",
+ 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`).
+
+_(Rationale and further reading:_ [https://www.baeldung.com/java-exceptions](https://www.baeldung.com/java-exceptions)_)_
+
+## Outputting Errors in the UI
+
+Principle: Error messages shown to the User should not contain technical details (e.g., underlying exceptions, or even stack traces). Instead, the message should be concise, understandable for non-programmers and localized. The technical reasons (and stack traces) for a failure should only be logged.
+
+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
+```
diff --git a/docs/code-howtos/eventbus.md b/docs/code-howtos/eventbus.md
new file mode 100644
index 00000000000..5d90d47b22c
--- /dev/null
+++ b/docs/code-howtos/eventbus.md
@@ -0,0 +1,71 @@
+---
+parent: Code Howtos
+nav_order: 4
+---
+# Event Bus and Event System
+
+## What the EventSystem is used for
+
+Many times there is a need to provide an object on many locations simultaneously. This design pattern is quite similar to Java's Observer, but it is much simpler and readable while having the same functional sense.
+
+## Main principle
+
+`EventBus` represents a communication line between multiple components. Objects can be passed through the bus and reach the listening method of another object which is registered on that `EventBus` instance. Hence, the passed object is available as a parameter in the listening method.
+
+## Register to the `EventBus`
+
+Any listening method has to be annotated with `@Subscribe` keyword and must have only one accepting parameter. Furthermore, the object which contains such listening method(s) has to be registered using the `register(Object)` method provided by `EventBus`. The listening methods can be overloaded by using different parameter types.
+
+## Posting an object
+
+`post(object)` posts an object through the `EventBus` which has been used to register the listening/subscribing methods.
+
+## Short example
+
+```java
+/* Listener.java */
+
+import com.google.common.eventbus.Subscribe;
+
+public class Listener {
+
+ private int value = 0;
+
+ @Subscribe
+ public void listen(int value) {
+ this.value = value;
+ }
+
+ public int getValue() {
+ return this.value;
+ }
+}
+```
+
+```java
+/* Main.java */
+
+import com.google.common.eventbus.EventBus;
+
+public class Main {
+ private static EventBus eventBus = new EventBus();
+
+ public static void main(String[] args) {
+ Main main = new Main();
+ Listener listener = new Listener();
+ eventBus.register(listener);
+ eventBus.post(1); // 1 represents the passed event
+
+ // Output should be 1
+ System.out.println(listener.getValue());
+ }
+}
+```
+
+## Event handling in JabRef
+
+The `event` package contains some specific events which occur in JabRef.
+
+For example: Every time an entry was added to the database a new `EntryAddedEvent` is sent through the `eventBus` which is located in `BibDatabase`.
+
+If you want to catch the event you'll have to register your listener class with the `registerListener(Object listener)` method in `BibDatabase`. `EntryAddedEvent` provides also methods to get the inserted `BibEntry`.
diff --git a/docs/advanced-reading/fetchers.md b/docs/code-howtos/fetchers.md
similarity index 99%
rename from docs/advanced-reading/fetchers.md
rename to docs/code-howtos/fetchers.md
index b1dc9cb4ca5..5c4edf7225f 100644
--- a/docs/advanced-reading/fetchers.md
+++ b/docs/code-howtos/fetchers.md
@@ -1,8 +1,8 @@
---
-nav_order: 7
-parent: Advanced Reading
+parent: Code Howtos
+nav_order: 5
---
-# Working on fetchers
+# Fetchers
Fetchers are the implementation of the [search using online services](https://docs.jabref.org/collect/import-using-online-bibliographic-database). Some fetchers require API keys to get them working. To get the fetchers running in a JabRef development setup, the keys need to be placed in the respective environment variable. The following table lists the respective fetchers, where to get the key from and the environment variable where the key has to be placed.
diff --git a/docs/code-howtos/index.md b/docs/code-howtos/index.md
new file mode 100644
index 00000000000..94ca60ff51a
--- /dev/null
+++ b/docs/code-howtos/index.md
@@ -0,0 +1,106 @@
+---
+nav_order: 6
+has_children: true
+---
+# Code Howtos
+
+This page provides some development support in the form of howtos.
+See also [High Level Documentation](../getting-into-thecode/high-level-documentation.md).
+
+## Generic code how tos
+
+We really recommend reading the book [Java by Comparison](http://java.by-comparison.com).
+
+Please read [https://github.com/cxxr/better-java](https://github.com/cxxr/better-java)
+
+* try not to abbreviate names of variables, classes or methods
+* use lowerCamelCase instead of snake\_case
+* name enums in singular, e.g. `Weekday` instead of `Weekdays` (except if they represent flags)
+
+## Cleanup and Formatters
+
+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 themselves are independent of any logic and therefore easy to test.
+
+Example: [NormalizePagesFormatter](https://github.com/JabRef/jabref/blob/master/src/main/java/org/jabref/logic/formatter/bibtexfields/NormalizePagesFormatter.java)
+
+## Drag and Drop
+
+Drag and Drop makes usage of the Dragboard. For JavaFX the following [tutorial](https://docs.oracle.com/javafx/2/drag\_drop/jfxpub-drag\_drop.htm) is helpful. Note that the data has to be serializable which is put on the dragboard. For drag and drop of Bib-entries between the maintable and the groups panel, a custom Dragboard is used, `CustomLocalDragboard` which is a generic alternative to the system one.
+
+For accessing or putting data into the Clipboard use the `ClipboardManager`.
+
+## Get the JabRef frame panel
+
+`JabRefFrame` and `BasePanel` are the two main classes. You should never directly call them, instead pass them as parameters to the class.
+
+## Get Absolute Filename or Path for file in File directory
+
+```java
+Optional file = FileHelper.expandFilename(database, fileText, preferences.getFilePreferences());
+```
+
+`String path` Can be the files name or a relative path to it. The Preferences should only be directly accessed in the GUI. For the usage in logic pass them as parameter
+
+## Setting a Database Directory for a .bib File
+
+* `@comment{jabref-meta: fileDirectory:`
+* “fileDirectory” is determined by Globals.pref.get(“userFileDir”) (which defaults to “fileDirectory”
+* There is also “fileDirectory-\”, which is determined by Globals.prefs.get(“userFileDirIndividual”)
+* Used at DatabasePropertiesDialog
+
+## How to work with Preferences
+
+`model` and `logic` must not know `JabRefPreferences`. See `ProxyPreferences` for encapsulated preferences and [https://github.com/JabRef/jabref/pull/658](https://github.com/JabRef/jabref/pull/658) for a detailed discussion.
+
+See [https://github.com/JabRef/jabref/blob/master/src/main/java/org/jabref/logic/preferences/TimestampPreferences.java](https://github.com/JabRef/jabref/blob/master/src/main/java/org/jabref/logic/preferences/TimestampPreferences.java) (via [https://github.com/JabRef/jabref/pull/3092](https://github.com/JabRef/jabref/pull/3092)) for the current way how to deal with preferences.
+
+Defaults should go into the model package. See [Comments in this Commit](https://github.com/JabRef/jabref/commit/2f553e6557bddf7753b618b0f4edcaa6e873f719#commitcomment-15779484)
+
+## UI
+
+Global variables should be avoided. Try to pass them as dependency.
+
+## "Special Fields"
+
+### keywords sync
+
+Database.addDatabaseChangeListener does not work as the DatabaseChangedEvent does not provide the field information. Therefore, we have to use BibtexEntry.addPropertyChangeListener(VetoableChangeListener listener)
+
+## Working with BibTeX data
+
+### Working with authors
+
+You can normalize the authors using `org.jabref.model.entry.AuthorList.fixAuthor_firstNameFirst(String)`. Then the authors always look nice. The only alternative containing all data of the names is `org.jabref.model.entry.AuthorList.fixAuthor_lastNameFirst(String)`. The other `fix...` methods omit data (like the von parts or the junior information).
+
+## Benchmarks
+
+* Benchmarks can be executed by running the `jmh` gradle task (this functionality uses the [JMH Gradle plugin](https://github.com/melix/jmh-gradle-plugin))
+* Best practices:
+ * Read test input from `@State` objects
+ * Return result of calculations (either explicitly or via a `BlackHole` object)
+* [List of examples](https://github.com/melix/jmh-gradle-example/tree/master/src/jmh/java/org/openjdk/jmh/samples)
+
+## Measure performance
+
+Try out the [YourKit JAva Profiler](https://www.yourkit.com).
+
+## equals
+
+When creating an `equals` method follow:
+
+1. Use the `==` operator to check if the argument is a reference to this object. If so, return `true`.
+2. Use the `instanceof` operator to check if the argument has the correct type. If not, return `false`.
+3. Cast the argument to the correct type.
+4. For each “significant” field in the class, check if that field of the argument matches the corresponding field of this object. If all these tests succeed, return `true` otherwise, return `false`.
+5. When you are finished writing your equals method, ask yourself three questions: Is it symmetric? Is it transitive? Is it consistent?
+
+Also, note:
+
+* Always override `hashCode` when you override equals (`hashCode` also has very strict rules) (Item 9 of[Effective Java](https://www.oreilly.com/library/view/effective-java-3rd/9780134686097/))
+* Don’t try to be too clever
+* Don’t substitute another type for `Object` in the equals declaration
+
+## Files and Paths
+
+Always try to use the methods from the nio-package. For interoperability, they provide methods to convert between file and path. [https://docs.oracle.com/javase/tutorial/essential/io/path.html](https://docs.oracle.com/javase/tutorial/essential/io/path.html) Mapping between old methods and new methods [https://docs.oracle.com/javase/tutorial/essential/io/legacy.html#mapping](https://docs.oracle.com/javase/tutorial/essential/io/legacy.html#mapping)
+
diff --git a/docs/readings-on-coding/javafx.md b/docs/code-howtos/javafx.md
similarity index 61%
rename from docs/readings-on-coding/javafx.md
rename to docs/code-howtos/javafx.md
index 99d83c6982b..b67e04cafea 100644
--- a/docs/readings-on-coding/javafx.md
+++ b/docs/code-howtos/javafx.md
@@ -1,10 +1,56 @@
---
-nav_order: 1
-parent: Readings on Coding
+parent: Code Howtos
+nav_order: 6
---
-# Readings on JavaFX
+# JavaFX
+
+## FXML
+
+The following expressions can be used in FXML attributes, according to the [official documentation](https://docs.oracle.com/javase/8/javafx/api/javafx/fxml/doc-files/introduction\_to\_fxml.html#attributes)
+
+| Type | Expression | Value point to | Remark |
+| -------------------------------- | ------------------------------------------------ | ---------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
+| Location | `@image.png` | path relative to the current FXML file | |
+| Resource | `%textToBeTranslated` | key in ResourceBundle | |
+| Attribute variable | `$idOfControl` or `$variable` | named control or variable in controller (may be path in the namespace) | resolved only once at load time |
+| Expression binding | `${expression}` | expression, for example `textField.text` | changes to source are propagated |
+| Bidirectional expression binding | `#{expression}` | expression | changes are propagated in both directions (not yet implemented in JavaFX, see [feature request](https://bugs.openjdk.java.net/browse/JDK-8090665)) |
+| Event handler | `#nameOfEventHandler` | name of the event handler method in the controller | |
+| Constant | `` | constant (here `MYSTRING` in the `Strings` class) | |
+
+## JavaFX Radio Buttons example
+
+All radio buttons that should be grouped together need to have a ToggleGroup defined in the FXML code Example:
+
+```markup
+
+
+
+
+
+
+
+
+
+
+
+```
+
+## JavaFX Dialogs
+
+All dialogs should be displayed to the user via `DialogService` interface methods. `DialogService` provides methods to display various dialogs (including custom ones) to the user. It also ensures the displayed dialog opens on the correct window via `initOwner()` (for cases where the user has multiple screens). The following code snippet demonstrates how a custom dialog is displayed to the user:
-JabRef's recommendations about JavaFX
+```java
+dialogService.showCustomDialog(new DocumentViewerView());
+```
+
+If an instance of `DialogService` is unavailable within current class/scope in which the dialog needs to be displayed, `DialogService` can be instantiated via the code snippet shown as follows:
+
+```java
+DialogService dialogService = Injector.instantiateModelOrService(DialogService.class);
+```
## Architecture: Model - View - (Controller) - ViewModel (MV(C)VM)
diff --git a/docs/advanced-reading/jpackage.md b/docs/code-howtos/jpackage.md
similarity index 95%
rename from docs/advanced-reading/jpackage.md
rename to docs/code-howtos/jpackage.md
index e3a83411e95..8a527bcd70e 100644
--- a/docs/advanced-reading/jpackage.md
+++ b/docs/code-howtos/jpackage.md
@@ -1,8 +1,8 @@
---
-nav_order: 2
-parent: Advanced Reading
+parent: Code Howtos
+nav_order: 7
---
-# Creating a binary and debug it
+# JPackage: Creating a binary and debug it
JabRef uses [jpackage](https://docs.oracle.com/en/java/javase/14/jpackage/) to build binary application bundles and installers for Windows, Linux, and macOS. For Gradle, we use the [Badass JLink Plugin](https://badass-jlink-plugin.beryx.org/releases/latest/).
diff --git a/docs/code-howtos/localization.md b/docs/code-howtos/localization.md
new file mode 100644
index 00000000000..ed0b3567641
--- /dev/null
+++ b/docs/code-howtos/localization.md
@@ -0,0 +1,52 @@
+---
+parent: Code Howtos
+nav_order: 8
+---
+# Localization
+
+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.
+
+JabRef uses ResourceBundles ([see Oracle Tutorial](https://docs.oracle.com/javase/tutorial/i18n/resbundle/concept.html)) to store `key=value` pairs for each String to be localized.
+
+To show an localized String the following `org.jabref.logic.l10n.Localization` has to be used. The Class currently provides three methods to obtain translated strings:
+
+```java
+ public static String lang(String key);
+
+ public static String lang(String key, String... params);
+
+ public static String menuTitle(String key, String... params);
+```
+
+The actual usage might look like:
+
+```java
+ Localization.lang("Get me a translated String");
+ Localization.lang("Using %0 or more %1 is also possible", "one", "parameter");
+ Localization.menuTitle("Used for Menus only");
+```
+
+General hints:
+
+* Use the String you want to localize directly, do not use members or local variables: `Localization.lang("Translate me");` instead of `Localization.lang(someVariable)` (possibly in the form `someVariable = Localization.lang("Translate me")`
+* Use `%x`-variables where appropriate: `Localization.lang("Exported %0 entries.", number)` instead of `Localization.lang("Exported ") + number + Localization.lang(" entries.");`
+* Use a full stop/period (".") to end full sentences
+
+The tests check whether translation strings appear correctly in the resource bundles.
+
+1. Add new `Localization.lang("KEY")` to Java file. Run the `LocalizationConsistencyTest`under (src/test/org.jabref.logic.
+
+ )
+2. Tests fail. In the test output a snippet is generated which must be added to the English translation file.
+3. Add snippet to English translation file located at `src/main/resources/l10n/JabRef_en.properties`
+4. Please do not add translations for other languages directly in the properties. They will be overwritten by [Crowdin](https://crowdin.com/project/jabref)
+
+## Adding a new Language
+
+1. Add the new Language to the Language enum in [https://github.com/JabRef/jabref/blob/master/src/main/java/org/jabref/logic/l10n/Language.java](https://github.com/JabRef/jabref/blob/master/src/main/java/org/jabref/logic/l10n/Language.java)
+2. Create an empty \.properties file
+3. Configure the new language in [Crowdin](https://crowdin.com/project/jabref)
+
+If the language is a variant of a language `zh_CN` or `pt_BR` it is necessary to add a language mapping for Crowdin to the crowdin.yml file in the root. Of course the properties file also has to be named according to the language code and locale.
diff --git a/docs/code-howtos/logging.md b/docs/code-howtos/logging.md
new file mode 100644
index 00000000000..ca16897502e
--- /dev/null
+++ b/docs/code-howtos/logging.md
@@ -0,0 +1,24 @@
+---
+parent: Code Howtos
+nav_order: 9
+---
+## Logging
+
+JabRef uses the logging facade [SLF4j](https://www.slf4j.org). All log messages are passed internally to [tinylog](https://tinylog.org/v2/) which handles any filtering, formatting and writing of log messages.
+
+* Obtaining a logger for a class:
+
+ ```java
+ private static final Logger LOGGER = LoggerFactory.getLogger(.class);
+ ```
+* If the logging event is caused by an exception, please add the exception to the log message as:
+
+ ```java
+ catch (SomeException e) {
+ LOGGER.warn("Warning text.", e);
+ ...
+ }
+ ```
+* SLF4J also support parameterized logging, e.g. if you want to print out multiple arguments in a log statement use a pair of curly braces. [Examples](https://www.slf4j.org/faq.html#logging\_performance)
+* When running tests, `tinylog-test.properties` is used. It is located under `src/test/resources`. As default, only `info` is logged. When developing, it makes sense to use `debug` as log level. One can change the log level per class using the pattern `level@class=debug` is set to `debug`. In the `.properties` file, this is done for `org.jabref.model.entry.BibEntry`.
+
diff --git a/docs/openoffice/code-reorganization.md b/docs/code-howtos/openoffice/code-reorganization.md
similarity index 99%
rename from docs/openoffice/code-reorganization.md
rename to docs/code-howtos/openoffice/code-reorganization.md
index 9b9999b4e43..c9c37fe47d8 100644
--- a/docs/openoffice/code-reorganization.md
+++ b/docs/code-howtos/openoffice/code-reorganization.md
@@ -1,6 +1,7 @@
---
nav_order: 4
parent: The LibreOffice Panel
+grand_parent: Code Howtos
---
# Code reorganization
diff --git a/docs/openoffice/index.md b/docs/code-howtos/openoffice/index.md
similarity index 60%
rename from docs/openoffice/index.md
rename to docs/code-howtos/openoffice/index.md
index 2fbb9dab491..a36a86fd2b5 100644
--- a/docs/openoffice/index.md
+++ b/docs/code-howtos/openoffice/index.md
@@ -1,5 +1,6 @@
---
-nav_order: 7
+parent: Code Howtos
+nav_order: 12
has_children: true
---
# The LibreOffice Panel
diff --git a/docs/openoffice/layers-v1.svg b/docs/code-howtos/openoffice/layers-v1.svg
similarity index 100%
rename from docs/openoffice/layers-v1.svg
rename to docs/code-howtos/openoffice/layers-v1.svg
diff --git a/docs/openoffice/ooresult-ooerror/index.md b/docs/code-howtos/openoffice/ooresult-ooerror/index.md
similarity index 100%
rename from docs/openoffice/ooresult-ooerror/index.md
rename to docs/code-howtos/openoffice/ooresult-ooerror/index.md
diff --git a/docs/openoffice/ooresult-ooerror/ooresult-alternatives.md b/docs/code-howtos/openoffice/ooresult-ooerror/ooresult-alternatives.md
similarity index 100%
rename from docs/openoffice/ooresult-ooerror/ooresult-alternatives.md
rename to docs/code-howtos/openoffice/ooresult-ooerror/ooresult-alternatives.md
diff --git a/docs/openoffice/order-of-appearance.md b/docs/code-howtos/openoffice/order-of-appearance.md
similarity index 99%
rename from docs/openoffice/order-of-appearance.md
rename to docs/code-howtos/openoffice/order-of-appearance.md
index fa2b529f374..2848b89c0e5 100644
--- a/docs/openoffice/order-of-appearance.md
+++ b/docs/code-howtos/openoffice/order-of-appearance.md
@@ -1,6 +1,7 @@
---
nav_order: 2
parent: The LibreOffice Panel
+grand_parent: Code Howtos
---
# Order of appearance of citation groups
diff --git a/docs/openoffice/overview.md b/docs/code-howtos/openoffice/overview.md
similarity index 99%
rename from docs/openoffice/overview.md
rename to docs/code-howtos/openoffice/overview.md
index 315c7a62989..56ec4d857ce 100644
--- a/docs/openoffice/overview.md
+++ b/docs/code-howtos/openoffice/overview.md
@@ -1,6 +1,7 @@
---
nav_order: 1
parent: The LibreOffice Panel
+grand_parent: Code Howtos
---
# Overview
diff --git a/docs/openoffice/problems.md b/docs/code-howtos/openoffice/problems.md
similarity index 99%
rename from docs/openoffice/problems.md
rename to docs/code-howtos/openoffice/problems.md
index 12cbbcb9957..4d11dc62966 100644
--- a/docs/openoffice/problems.md
+++ b/docs/code-howtos/openoffice/problems.md
@@ -1,6 +1,7 @@
---
nav_order: 3
parent: The LibreOffice Panel
+grand_parent: Code Howtos
---
# Problems
diff --git a/docs/advanced-reading/remote-storage.md b/docs/code-howtos/remote-storage.md
similarity index 96%
rename from docs/advanced-reading/remote-storage.md
rename to docs/code-howtos/remote-storage.md
index 80f1d4d8cd1..2f01a096452 100644
--- a/docs/advanced-reading/remote-storage.md
+++ b/docs/code-howtos/remote-storage.md
@@ -1,6 +1,6 @@
---
-nav_order: 5
-parent: Advanced Reading
+parent: Code Howtos
+nav_order: 11
---
# Remote Storage
diff --git a/docs/advanced-reading/telemetry.md b/docs/code-howtos/telemetry.md
similarity index 95%
rename from docs/advanced-reading/telemetry.md
rename to docs/code-howtos/telemetry.md
index 3a8ee5525a8..d070ee2d813 100644
--- a/docs/advanced-reading/telemetry.md
+++ b/docs/code-howtos/telemetry.md
@@ -1,6 +1,6 @@
---
-nav_order: 6
-parent: Advanced Reading
+parent: Code Howtos
+nav_order: 13
---
# Telemetry
diff --git a/docs/code-howtos/testing.md b/docs/code-howtos/testing.md
new file mode 100644
index 00000000000..fe3493f6a3d
--- /dev/null
+++ b/docs/code-howtos/testing.md
@@ -0,0 +1,132 @@
+---
+parent: Code Howtos
+nav_order: 14
+---
+# Testing JabRef
+
+## Background on Java testing
+
+In JabRef, we mainly rely on basic JUnit tests to increase code coverage. There are other ways to test:
+
+| Type | Techniques | Tool (Java) | Kind of tests | Used In JabRef |
+| -------------- | ------------------------------------------ | ----------------------------------------------------------------------- | -------------------------------------------------------------- | ------------------------------------------------------------- |
+| Functional | Dynamics, black box, positive and negative | [JUnit-QuickCheck](https://github.com/pholser/junit-quickcheck) | Random data generation | No, not intended, because other test kinds seem more helpful. |
+| Functional | Dynamics, black box, positive and negative | [GraphWalker](https://graphwalker.github.io) | Model-based | No, because the BibDatabase doesn't need to be tests |
+| Functional | Dynamics, black box, positive and negative | [TestFX](https://github.com/TestFX/TestFX) | GUI Tests | Yes |
+| Functional | Dynamics, white box, negative | [PIT](https://pitest.org) | Mutation | No |
+| Functional | Dynamics, white box, positive and negative | [Mockito](https://site.mockito.org) | Mocking | Yes |
+| Non-functional | Dynamics, black box, positive and negative | [JETM](http://jetm.void.fm), [Apache JMeter](https://jmeter.apache.org) | Performance (performance testing vs load testing respectively) | No |
+| Structural | Static, white box | [CheckStyle](https://checkstyle.sourceforge.io) | Constient formatting of the source code | Yes |
+| Structural | Dynamics, white box | [SpotBugs](https://spotbugs.github.io) | Reocurreing bugs (based on experience of other projects) | No |
+
+## General hints on tests
+
+Imagine you want to test the method `format(String value)` in the class `BracesFormatter` which removes double braces in a given string.
+
+* _Placing:_ all tests should be placed in a class named `classTest`, e.g. `BracesFormatterTest`.
+* _Naming:_ the name should be descriptive enough to describe the whole test. Use the format `methodUnderTest_ expectedBehavior_context` (without the dashes). So for example `formatRemovesDoubleBracesAtBeginning`. Try to avoid naming the tests with a `test` prefix since this information is already contained in the class name. Moreover, starting the name with `test` leads often to inferior test names (see also the [Stackoverflow discussion about naming](http://stackoverflow.com/questions/155436/unit-test-naming-best-practices)).
+* _Test only one thing per test:_ tests should be short and test only one small part of the method. So instead of
+
+ ```java
+ testFormat() {
+ assertEqual("test", format("test"));
+ assertEqual("{test", format("{test"));
+ assertEqual("test", format("test}}"));
+ }
+ ```
+
+ we would have five tests containing a single `assert` statement and named accordingly (`formatDoesNotChangeStringWithoutBraces`, `formatDoesNotRemoveSingleBrace`, , etc.). See [JUnit AntiPattern](https://exubero.com/junit/anti-patterns/#Multiple\_Assertions) for background.
+* Do _not just test happy paths_, but also wrong/weird input.
+* It is recommend to write tests _before_ you actually implement the functionality (test driven development).
+* _Bug fixing:_ write a test case covering the bug and then fix it, leaving the test as a security that the bug will never reappear.
+* Do not catch exceptions in tests, instead use the `assertThrows(Exception.class, ()->doSomethingThrowsEx())` feature of [junit-jupiter](https://junit.org/junit5/docs/current/user-guide/) to the test method.
+
+## Lists in tests
+
+* Use `assertEquals(Collections.emptyList(), actualList);` instead of `assertEquals(0, actualList.size());` to test whether a list is empty.
+* Similarly, use `assertEquals(Arrays.asList("a", "b"), actualList);` to compare lists instead of
+
+ ```java
+ assertEquals(2, actualList.size());
+ assertEquals("a", actualList.get(0));
+ assertEquals("b", actualList.get(1));
+ ```
+
+## BibEntries in tests
+
+* Use the `assertEquals` methods in `BibtexEntryAssert` to check that the correct BibEntry is returned.
+
+## Files and folders in tests
+
+* If you need a temporary file in tests, then add the following Annotation before the class:
+
+ ```java
+ @ExtendWith(TempDirectory.class)
+ class TestClass{
+
+ @BeforeEach
+ void setUp(@TempDirectory.TempDir Path temporaryFolder){
+ }
+ }
+ ```
+
+ to the test class. A temporary file is now created by `Files.createFile(path)`. Using this pattern automatically ensures that the test folder is deleted after the tests are run. See the [junit-pioneer doc](https://junit-pioneer.org/docs/temp-directory/) for more details.
+
+## Loading Files from Resources
+
+Sometimes it is necessary to load a specific resource or to access the resource directory
+
+```java
+Path resourceDir = Paths.get(MSBibExportFormatTestFiles.class.getResource("MsBibExportFormatTest1.bib").toURI()).getParent();
+```
+
+When the directory is needed, it is important to first point to an actual existing file. Otherwise the wrong directory will be returned.
+
+## Preferences in tests
+
+If you modify preference, use following pattern to ensure that the stored preferences of a developer are not affected:
+
+Or even better, try to mock the preferences and insert them via dependency injection.
+
+```java
+@Test
+public void getTypeReturnsBibLatexArticleInBibLatexMode() {
+ // Mock preferences
+ PreferencesService mockedPrefs = mock(PreferencesService.class);
+ GeneralPreferences mockedGeneralPrefs = mock(GeneralPReferences.class);
+ // Switch to BibLatex mode
+ when(mockedPrefs.getGeneralPrefs()).thenReturn(mockedGeneralPrefs);
+ when(mockedGeneralPrefs.getDefaultBibDatabaseMode())
+ .thenReturn(BibDatabaseMode.BIBLATEX);
+
+ // Now test
+ EntryTypes biblatexentrytypes = new EntryTypes(mockedPrefs);
+ assertEquals(BibLatexEntryTypes.ARTICLE, biblatexentrytypes.getType("article"));
+}
+```
+
+To test that a preferences migration works successfully, use the mockito method `verify`. See `PreferencesMigrationsTest` for an example.
+
+## Database tests
+
+### PostgreSQL
+
+To quickly host a local PostgreSQL database, execute following statement:
+
+```
+docker run -d -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=postgres -e POSTGRES_DB=postgres -p 5432:5432 --name db postgres:10 postgres -c log_statement=all
+```
+
+Set the environment variable `DBMS` to `postgres` (or leave it unset)
+
+Then, all DBMS Tests (annotated with `@org.jabref.testutils.category.DatabaseTest`) run properly.
+
+### MySQL
+
+A MySQL DBMS can be started using following command:
+
+```
+docker run -e MYSQL_ROOT_PASSWORD=root -e MYSQL_DATABASE=jabref -p 3800:3307 mysql:8.0 --port=3307
+```
+
+Set the environment variable `DBMS` to `mysql`.
diff --git a/docs/readings-on-coding/tools.md b/docs/code-howtos/tools.md
similarity index 98%
rename from docs/readings-on-coding/tools.md
rename to docs/code-howtos/tools.md
index d2a378c52b7..1bb55d88feb 100644
--- a/docs/readings-on-coding/tools.md
+++ b/docs/code-howtos/tools.md
@@ -1,6 +1,6 @@
---
-nav_order: 2
-parent: Readings on Coding
+parent: Code Howtos
+nav_order: 100
---
# Useful development tooling
diff --git a/docs/advanced-reading/ui-recommendations.md b/docs/code-howtos/ui-recommendations.md
similarity index 95%
rename from docs/advanced-reading/ui-recommendations.md
rename to docs/code-howtos/ui-recommendations.md
index b2bc0c07cf4..e81ea116f4a 100644
--- a/docs/advanced-reading/ui-recommendations.md
+++ b/docs/code-howtos/ui-recommendations.md
@@ -1,8 +1,8 @@
---
-nav_order: 4
-parent: Advanced Reading
+parent: Code Howtos
+nav_order: 15
---
-# Recommendations for UI design
+# UI Design Recommendations
* [Designing More Efficient Forms: Structure, Inputs, Labels and Actions](https://uxplanet.org/designing-more-efficient-forms-structure-inputs-labels-and-actions-e3a47007114f)
* [Input form label alignment top or left?](https://ux.stackexchange.com/questions/8480/input-form-label-alignment-top-or-left)
diff --git a/docs/decisions/0001-use-crowdin-for-translations.md b/docs/decisions/0001-use-crowdin-for-translations.md
index 5ded77562e0..af8ba405b1b 100644
--- a/docs/decisions/0001-use-crowdin-for-translations.md
+++ b/docs/decisions/0001-use-crowdin-for-translations.md
@@ -13,7 +13,7 @@ The JabRef UI is offered in multiple languages. It should be easy for translator
* Use [Crowdin](http://crowdin.com/)
* Use [popeye](https://github.com/JabRef/popeye)
* Use [Lingohub](https://lingohub.com/)
-* Keep current GitHub flow. See the [Step-by-step guide](https://docs.jabref.org/faq/how-to-translate-the-ui).
+* Keep current GitHub flow. See the [Step-by-step guide](https://docs.jabref.org/contributing/how-to-translate-the-ui).
## Decision Outcome
diff --git a/docs/decisions/index.md b/docs/decisions/index.md
index f6f60378dd3..bab7c46aec3 100644
--- a/docs/decisions/index.md
+++ b/docs/decisions/index.md
@@ -1,5 +1,5 @@
---
-nav_order: 3
+nav_order: 4
has_children: true
---
# Decision Records
diff --git a/docs/getting-into-the-code/code-howtos.md b/docs/getting-into-the-code/code-howtos.md
deleted file mode 100644
index cee1d7a2b3d..00000000000
--- a/docs/getting-into-the-code/code-howtos.md
+++ /dev/null
@@ -1,431 +0,0 @@
----
-parent: Getting into the code
-nav_order: 4
----
-# Code Howtos
-
-This page provides some development support in the form of howtos. See also [High Level Documentation](high-level-documentation.md).
-
-## Generic code how tos
-
-We really recommend reading the book [Java by Comparison](http://java.by-comparison.com).
-
-Please read [https://github.com/cxxr/better-java](https://github.com/cxxr/better-java)
-
-* try not to abbreviate names of variables, classes or methods
-* use lowerCamelCase instead of snake\_case
-* name enums in singular, e.g. `Weekday` instead of `Weekdays` (except if they represent flags)
-
-## Error Handling in JabRef
-
-### Throwing and Catching Exceptions
-
-Principles:
-
-* All exceptions we throw should be or extend `JabRefException`; This is especially important if the message stored in the Exception should be shown to the user. `JabRefException` has already implemented the `getLocalizedMessage()` method which should be used for such cases (see details below!).
-* Catch and wrap all API exceptions (such as `IOExceptions`) and rethrow them
- * Example:
-
- ```java
- try {
- // ...
- } catch (IOException ioe) {
- throw new JabRefException("Something went wrong...",
- 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`).
-
-_(Rationale and further reading:_ [https://www.baeldung.com/java-exceptions](https://www.baeldung.com/java-exceptions)_)_
-
-### Outputting Errors in the UI
-
-Principle: Error messages shown to the User should not contain technical details (e.g., underlying exceptions, or even stack traces). Instead, the message should be concise, understandable for non-programmers and localized. The technical reasons (and stack traces) for a failure should only be logged.
-
-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
-```
-
-## Using the EventSystem
-
-### What the EventSystem is used for
-
-Many times there is a need to provide an object on many locations simultaneously. This design pattern is quite similar to Java's Observer, but it is much simpler and readable while having the same functional sense.
-
-### Main principle
-
-`EventBus` represents a communication line between multiple components. Objects can be passed through the bus and reach the listening method of another object which is registered on that `EventBus` instance. Hence, the passed object is available as a parameter in the listening method.
-
-### Register to the `EventBus`
-
-Any listening method has to be annotated with `@Subscribe` keyword and must have only one accepting parameter. Furthermore, the object which contains such listening method(s) has to be registered using the `register(Object)` method provided by `EventBus`. The listening methods can be overloaded by using different parameter types.
-
-### Posting an object
-
-`post(object)` posts an object through the `EventBus` which has been used to register the listening/subscribing methods.
-
-### Short example
-
-```java
-/* Listener.java */
-
-import com.google.common.eventbus.Subscribe;
-
-public class Listener {
-
- private int value = 0;
-
- @Subscribe
- public void listen(int value) {
- this.value = value;
- }
-
- public int getValue() {
- return this.value;
- }
-}
-```
-
-```java
-/* Main.java */
-
-import com.google.common.eventbus.EventBus;
-
-public class Main {
- private static EventBus eventBus = new EventBus();
-
- public static void main(String[] args) {
- Main main = new Main();
- Listener listener = new Listener();
- eventBus.register(listener);
- eventBus.post(1); // 1 represents the passed event
-
- // Output should be 1
- System.out.println(listener.getValue());
- }
-}
-```
-
-### Event handling in JabRef
-
-The `event` package contains some specific events which occur in JabRef.
-
-For example: Every time an entry was added to the database a new `EntryAddedEvent` is sent through the `eventBus` which is located in `BibDatabase`.
-
-If you want to catch the event you'll have to register your listener class with the `registerListener(Object listener)` method in `BibDatabase`. `EntryAddedEvent` provides also methods to get the inserted `BibEntry`.
-
-## Logging
-
-JabRef uses the logging facade [SLF4j](https://www.slf4j.org). All log messages are passed internally to [tinylog](https://tinylog.org/v2/) which handles any filtering, formatting and writing of log messages.
-
-* Obtaining a logger for a class:
-
- ```java
- private static final Logger LOGGER = LoggerFactory.getLogger(.class);
- ```
-* If the logging event is caused by an exception, please add the exception to the log message as:
-
- ```java
- catch (SomeException e) {
- LOGGER.warn("Warning text.", e);
- ...
- }
- ```
-* SLF4J also support parameterized logging, e.g. if you want to print out multiple arguments in a log statement use a pair of curly braces. [Examples](https://www.slf4j.org/faq.html#logging\_performance)
-* When running tests, `tinylog-test.properties` is used. It is located under `src/test/resources`. As default, only `info` is logged. When developing, it makes sense to use `debug` as log level. One can change the log level per class using the pattern `level@class=debug` is set to `debug`. In the `.properties` file, this is done for `org.jabref.model.entry.BibEntry`.
-
-## Using Localization correctly
-
-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.
-
-JabRef uses ResourceBundles ([see Oracle Tutorial](https://docs.oracle.com/javase/tutorial/i18n/resbundle/concept.html)) to store `key=value` pairs for each String to be localized.
-
-To show an localized String the following `org.jabref.logic.l10n.Localization` has to be used. The Class currently provides three methods to obtain translated strings:
-
-```java
- public static String lang(String key);
-
- public static String lang(String key, String... params);
-
- public static String menuTitle(String key, String... params);
-```
-
-The actual usage might look like:
-
-```java
- Localization.lang("Get me a translated String");
- Localization.lang("Using %0 or more %1 is also possible", "one", "parameter");
- Localization.menuTitle("Used for Menus only");
-```
-
-General hints:
-
-* Use the String you want to localize directly, do not use members or local variables: `Localization.lang("Translate me");` instead of `Localization.lang(someVariable)` (possibly in the form `someVariable = Localization.lang("Translate me")`
-* Use `%x`-variables where appropriate: `Localization.lang("Exported %0 entries.", number)` instead of `Localization.lang("Exported ") + number + Localization.lang(" entries.");`
-* Use a full stop/period (".") to end full sentences
-
-The tests check whether translation strings appear correctly in the resource bundles.
-
-1. Add new `Localization.lang("KEY")` to Java file. Run the `LocalizationConsistencyTest`under (src/test/org.jabref.logic.
-
- )
-2. Tests fail. In the test output a snippet is generated which must be added to the English translation file.
-3. Add snippet to English translation file located at `src/main/resources/l10n/JabRef_en.properties`
-4. Please do not add translations for other languages directly in the properties. They will be overwritten by [Crowdin](https://crowdin.com/project/jabref)
-
-## Adding a new Language
-
-1. Add the new Language to the Language enum in [https://github.com/JabRef/jabref/blob/master/src/main/java/org/jabref/logic/l10n/Language.java](https://github.com/JabRef/jabref/blob/master/src/main/java/org/jabref/logic/l10n/Language.java)
-2. Create an empty \.properties file
-3. Configure the new language in [Crowdin](https://crowdin.com/project/jabref)
-
-If the language is a variant of a language `zh_CN` or `pt_BR` it is necessary to add a language mapping for Crowdin to the crowdin.yml file in the root. Of course the properties file also has to be named according to the language code and locale.
-
-## Cleanup and Formatters
-
-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 themselves are independent of any logic and therefore easy to test.
-
-Example: [NormalizePagesFormatter](https://github.com/JabRef/jabref/blob/master/src/main/java/org/jabref/logic/formatter/bibtexfields/NormalizePagesFormatter.java)
-
-## Drag and Drop
-
-Drag and Drop makes usage of the Dragboard. For JavaFX the following [tutorial](https://docs.oracle.com/javafx/2/drag\_drop/jfxpub-drag\_drop.htm) is helpful. Note that the data has to be serializable which is put on the dragboard. For drag and drop of Bib-entries between the maintable and the groups panel, a custom Dragboard is used, `CustomLocalDragboard` which is a generic alternative to the system one.
-
-For accessing or putting data into the Clipboard use the `ClipboardManager`.
-
-## Get the JabRef frame panel
-
-`JabRefFrame` and `BasePanel` are the two main classes. You should never directly call them, instead pass them as parameters to the class.
-
-## Get Absolute Filename or Path for file in File directory
-
-```java
-Optional file = FileHelper.expandFilename(database, fileText, preferences.getFilePreferences());
-```
-
-`String path` Can be the files name or a relative path to it. The Preferences should only be directly accessed in the GUI. For the usage in logic pass them as parameter
-
-## Setting a Database Directory for a .bib File
-
-* `@comment{jabref-meta: fileDirectory:`
-* “fileDirectory” is determined by Globals.pref.get(“userFileDir”) (which defaults to “fileDirectory”
-* There is also “fileDirectory-\”, which is determined by Globals.prefs.get(“userFileDirIndividual”)
-* Used at DatabasePropertiesDialog
-
-## How to work with Preferences
-
-`model` and `logic` must not know `JabRefPreferences`. See `ProxyPreferences` for encapsulated preferences and [https://github.com/JabRef/jabref/pull/658](https://github.com/JabRef/jabref/pull/658) for a detailed discussion.
-
-See [https://github.com/JabRef/jabref/blob/master/src/main/java/org/jabref/logic/preferences/TimestampPreferences.java](https://github.com/JabRef/jabref/blob/master/src/main/java/org/jabref/logic/preferences/TimestampPreferences.java) (via [https://github.com/JabRef/jabref/pull/3092](https://github.com/JabRef/jabref/pull/3092)) for the current way how to deal with preferences.
-
-Defaults should go into the model package. See [Comments in this Commit](https://github.com/JabRef/jabref/commit/2f553e6557bddf7753b618b0f4edcaa6e873f719#commitcomment-15779484)
-
-## Test Cases
-
-### General hints on tests
-
-Imagine you want to test the method `format(String value)` in the class `BracesFormatter` which removes double braces in a given string.
-
-* _Placing:_ all tests should be placed in a class named `classTest`, e.g. `BracesFormatterTest`.
-* _Naming:_ the name should be descriptive enough to describe the whole test. Use the format `methodUnderTest_ expectedBehavior_context` (without the dashes). So for example `formatRemovesDoubleBracesAtBeginning`. Try to avoid naming the tests with a `test` prefix since this information is already contained in the class name. Moreover, starting the name with `test` leads often to inferior test names (see also the [Stackoverflow discussion about naming](http://stackoverflow.com/questions/155436/unit-test-naming-best-practices)).
-* _Test only one thing per test:_ tests should be short and test only one small part of the method. So instead of
-
- ```java
- testFormat() {
- assertEqual("test", format("test"));
- assertEqual("{test", format("{test"));
- assertEqual("test", format("test}}"));
- }
- ```
-
- we would have five tests containing a single `assert` statement and named accordingly (`formatDoesNotChangeStringWithoutBraces`, `formatDoesNotRemoveSingleBrace`, , etc.). See [JUnit AntiPattern](https://exubero.com/junit/anti-patterns/#Multiple\_Assertions) for background.
-* Do _not just test happy paths_, but also wrong/weird input.
-* It is recommend to write tests _before_ you actually implement the functionality (test driven development).
-* _Bug fixing:_ write a test case covering the bug and then fix it, leaving the test as a security that the bug will never reappear.
-* Do not catch exceptions in tests, instead use the `assertThrows(Exception.class, ()->doSomethingThrowsEx())` feature of [junit-jupiter](https://junit.org/junit5/docs/current/user-guide/) to the test method.
-
-### Lists in tests
-
-* Use `assertEquals(Collections.emptyList(), actualList);` instead of `assertEquals(0, actualList.size());` to test whether a list is empty.
-* Similarly, use `assertEquals(Arrays.asList("a", "b"), actualList);` to compare lists instead of
-
- ```java
- assertEquals(2, actualList.size());
- assertEquals("a", actualList.get(0));
- assertEquals("b", actualList.get(1));
- ```
-
-### BibEntries in tests
-
-* Use the `assertEquals` methods in `BibtexEntryAssert` to check that the correct BibEntry is returned.
-
-### Files and folders in tests
-
-* If you need a temporary file in tests, then add the following Annotation before the class:
-
- ```java
- @ExtendWith(TempDirectory.class)
- class TestClass{
-
- @BeforeEach
- void setUp(@TempDirectory.TempDir Path temporaryFolder){
- }
- }
- ```
-
- to the test class. A temporary file is now created by `Files.createFile(path)`. Using this pattern automatically ensures that the test folder is deleted after the tests are run. See the [junit-pioneer doc](https://junit-pioneer.org/docs/temp-directory/) for more details.
-
-### Loading Files from Resources
-
-Sometimes it is necessary to load a specific resource or to access the resource directory
-
-```java
-Path resourceDir = Paths.get(MSBibExportFormatTestFiles.class.getResource("MsBibExportFormatTest1.bib").toURI()).getParent();
-```
-
-When the directory is needed, it is important to first point to an actual existing file. Otherwise the wrong directory will be returned.
-
-### Preferences in tests
-
-If you modify preference, use following pattern to ensure that the stored preferences of a developer are not affected:
-
-Or even better, try to mock the preferences and insert them via dependency injection.
-
-```java
-@Test
-public void getTypeReturnsBibLatexArticleInBibLatexMode() {
- // Mock preferences
- PreferencesService mockedPrefs = mock(PreferencesService.class);
- GeneralPreferences mockedGeneralPrefs = mock(GeneralPReferences.class);
- // Switch to BibLatex mode
- when(mockedPrefs.getGeneralPrefs()).thenReturn(mockedGeneralPrefs);
- when(mockedGeneralPrefs.getDefaultBibDatabaseMode())
- .thenReturn(BibDatabaseMode.BIBLATEX);
-
- // Now test
- EntryTypes biblatexentrytypes = new EntryTypes(mockedPrefs);
- assertEquals(BibLatexEntryTypes.ARTICLE, biblatexentrytypes.getType("article"));
-}
-```
-
-To test that a preferences migration works successfully, use the mockito method `verify`. See `PreferencesMigrationsTest` for an example.
-
-### Background on Java testing
-
-In JabRef, we mainly rely to basic JUnit tests to increase code coverage. There are other ways to test:
-
-| Type | Techniques | Tool (Java) | Kind of tests | Used In JabRef |
-| -------------- | ------------------------------------------ | ----------------------------------------------------------------------- | -------------------------------------------------------------- | ------------------------------------------------------------- |
-| Functional | Dynamics, black box, positive and negative | [JUnit-QuickCheck](https://github.com/pholser/junit-quickcheck) | Random data generation | No, not intended, because other test kinds seem more helpful. |
-| Functional | Dynamics, black box, positive and negative | [GraphWalker](https://graphwalker.github.io) | Model-based | No, because the BibDatabase doesn't need to be tests |
-| Functional | Dynamics, black box, positive and negative | [TestFX](https://github.com/TestFX/TestFX) | GUI Tests | Yes |
-| Functional | Dynamics, white box, negative | [PIT](https://pitest.org) | Mutation | No |
-| Functional | Dynamics, white box, positive and negative | [Mockito](https://site.mockito.org) | Mocking | Yes |
-| Non-functional | Dynamics, black box, positive and negative | [JETM](http://jetm.void.fm), [Apache JMeter](https://jmeter.apache.org) | Performance (performance testing vs load testing respectively) | No |
-| Structural | Static, white box | [CheckStyle](https://checkstyle.sourceforge.io) | Constient formatting of the source code | Yes |
-| Structural | Dynamics, white box | [SpotBugs](https://spotbugs.github.io) | Reocurreing bugs (based on experience of other projects) | No |
-
-## UI
-
-Global variables should be avoided. Try to pass them as dependency.
-
-## "Special Fields"
-
-### keywords sync
-
-Database.addDatabaseChangeListener does not work as the DatabaseChangedEvent does not provide the field information. Therefore, we have to use BibtexEntry.addPropertyChangeListener(VetoableChangeListener listener)
-
-## Working with BibTeX data
-
-### Working with authors
-
-You can normalize the authors using `org.jabref.model.entry.AuthorList.fixAuthor_firstNameFirst(String)`. Then the authors always look nice. The only alternative containing all data of the names is `org.jabref.model.entry.AuthorList.fixAuthor_lastNameFirst(String)`. The other `fix...` methods omit data (like the von parts or the junior information).
-
-## Benchmarks
-
-* Benchmarks can be executed by running the `jmh` gradle task (this functionality uses the [JMH Gradle plugin](https://github.com/melix/jmh-gradle-plugin))
-* Best practices:
- * Read test input from `@State` objects
- * Return result of calculations (either explicitly or via a `BlackHole` object)
-* [List of examples](https://github.com/melix/jmh-gradle-example/tree/master/src/jmh/java/org/openjdk/jmh/samples)
-
-## Measure performance
-
-Try out the [YourKit JAva Profiler](https://www.yourkit.com).
-
-## equals
-
-When creating an `equals` method follow:
-
-1. Use the `==` operator to check if the argument is a reference to this object. If so, return `true`.
-2. Use the `instanceof` operator to check if the argument has the correct type. If not, return `false`.
-3. Cast the argument to the correct type.
-4. For each “significant” field in the class, check if that field of the argument matches the corresponding field of this object. If all these tests succeed, return `true` otherwise, return `false`.
-5. When you are finished writing your equals method, ask yourself three questions: Is it symmetric? Is it transitive? Is it consistent?
-
-Also, note:
-
-* Always override `hashCode` when you override equals (`hashCode` also has very strict rules) (Item 9 of[Effective Java](https://www.oreilly.com/library/view/effective-java-3rd/9780134686097/))
-* Don’t try to be too clever
-* Don’t substitute another type for `Object` in the equals declaration
-
-## Files and Paths
-
-Always try to use the methods from the nio-package. For interoperability, they provide methods to convert between file and path. [https://docs.oracle.com/javase/tutorial/essential/io/path.html](https://docs.oracle.com/javase/tutorial/essential/io/path.html) Mapping between old methods and new methods [https://docs.oracle.com/javase/tutorial/essential/io/legacy.html#mapping](https://docs.oracle.com/javase/tutorial/essential/io/legacy.html#mapping)
-
-## JavaFX
-
-The following expressions can be used in FXML attributes, according to the [official documentation](https://docs.oracle.com/javase/8/javafx/api/javafx/fxml/doc-files/introduction\_to\_fxml.html#attributes)
-
-| Type | Expression | Value point to | Remark |
-| -------------------------------- | ------------------------------------------------ | ---------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
-| Location | `@image.png` | path relative to the current FXML file | |
-| Resource | `%textToBeTranslated` | key in ResourceBundle | |
-| Attribute variable | `$idOfControl` or `$variable` | named control or variable in controller (may be path in the namespace) | resolved only once at load time |
-| Expression binding | `${expression}` | expression, for example `textField.text` | changes to source are propagated |
-| Bidirectional expression binding | `#{expression}` | expression | changes are propagated in both directions (not yet implemented in JavaFX, see [feature request](https://bugs.openjdk.java.net/browse/JDK-8090665)) |
-| Event handler | `#nameOfEventHandler` | name of the event handler method in the controller | |
-| Constant | `` | constant (here `MYSTRING` in the `Strings` class) | |
-
-### JavaFX Radio Buttons example
-
-All radio buttons that should be grouped together need to have a ToggleGroup defined in the FXML code Example:
-
-```markup
-
-
-
-
-
-
-
-
-
-
-
-```
-
-### JavaFX Dialogs
-
-All dialogs should be displayed to the user via `DialogService` interface methods. `DialogService` provides methods to display various dialogs (including custom ones) to the user. It also ensures the displayed dialog opens on the correct window via `initOwner()` (for cases where the user has multiple screens). The following code snippet demonstrates how a custom dialog is displayed to the user:
-
-```java
-dialogService.showCustomDialog(new DocumentViewerView());
-```
-
-If an instance of `DialogService` is unavailable within current class/scope in which the dialog needs to be displayed, `DialogService` can be instantiated via the code snippet shown as follows:
-
-```java
-DialogService dialogService = Injector.instantiateModelOrService(DialogService.class);
-```
diff --git a/docs/getting-into-the-code/development-strategy.md b/docs/getting-into-the-code/development-strategy.md
index d9ba466b484..84a6fea798b 100644
--- a/docs/getting-into-the-code/development-strategy.md
+++ b/docs/getting-into-the-code/development-strategy.md
@@ -1,6 +1,6 @@
---
parent: Getting into the code
-nav_order: 5
+nav_order: 1
---
# JabRef's development strategy
@@ -14,7 +14,7 @@ To ensure high code-quality,
* We document our design decisions using the lightweight architectural decision records [MADR](https://adr.github.io/madr/).
* We review each external pull request by at least two [JabRef Core Developers](https://github.com/JabRef/jabref/blob/main/MAINTAINERS/README.md).
-Read on about our automated quality checks at [Code Quality](../advanced-reading/code-quality.md).
+Read on about our automated quality checks at [Code Quality](../code-howtos/code-quality.md).
## Continuous integration
diff --git a/docs/getting-into-the-code/guidelines-for-setting-up-a-local-workspace.md b/docs/getting-into-the-code/guidelines-for-setting-up-a-local-workspace.md
index dd8ff6ee03c..409416b9e9b 100644
--- a/docs/getting-into-the-code/guidelines-for-setting-up-a-local-workspace.md
+++ b/docs/getting-into-the-code/guidelines-for-setting-up-a-local-workspace.md
@@ -1,6 +1,6 @@
---
parent: Getting into the code
-nav_order: 1
+nav_order: 2
---
# Set up a local workspace
@@ -70,7 +70,7 @@ Eclipse JEE 2022-03 or newer is required.
### Other Tooling
-See our [tool recommendations](../readings-on-coding/tools.md).
+See our [tool recommendations](../code-howtos/tools.md).
## Get the code
diff --git a/docs/getting-into-the-code/high-level-documentation.md b/docs/getting-into-the-code/high-level-documentation.md
index 4539b9c33d8..315246f062d 100644
--- a/docs/getting-into-the-code/high-level-documentation.md
+++ b/docs/getting-into-the-code/high-level-documentation.md
@@ -1,6 +1,6 @@
---
parent: Getting into the code
-nav_order: 2
+nav_order: 3
---
# High-level documentation
diff --git a/docs/getting-into-the-code/index.md b/docs/getting-into-the-code/index.md
index b6e28461c59..7104747e918 100644
--- a/docs/getting-into-the-code/index.md
+++ b/docs/getting-into-the-code/index.md
@@ -1,5 +1,5 @@
---
-nav_order: 4
+nav_order: 5
has_children: true
---
# Getting into the code
diff --git a/docs/getting-into-the-code/testing.md b/docs/getting-into-the-code/testing.md
deleted file mode 100644
index 488f77a4d7a..00000000000
--- a/docs/getting-into-the-code/testing.md
+++ /dev/null
@@ -1,31 +0,0 @@
----
-parent: Getting into the code
-nav_order: 3
----
-# How to test
-
-For details on unit testing see [https://devdocs.jabref.org/getting-into-the-code/code-howtos#test-cases](https://devdocs.jabref.org/getting-into-the-code/code-howtos#test-cases).
-
-## Database tests
-
-### PostgreSQL
-
-To quickly host a local PostgreSQL database, execute following statement:
-
-```
-docker run -d -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=postgres -e POSTGRES_DB=postgres -p 5432:5432 --name db postgres:10 postgres -c log_statement=all
-```
-
-Set the environment variable `DBMS` to `postgres` (or leave it unset)
-
-Then, all DBMS Tests (annotated with `@org.jabref.testutils.category.DatabaseTest`) run properly.
-
-### MySQL
-
-A MySQL DBMS can be started using following command:
-
-```
-docker run -e MYSQL_ROOT_PASSWORD=root -e MYSQL_DATABASE=jabref -p 3800:3307 mysql:8.0 --port=3307
-```
-
-Set the environment variable `DBMS` to `mysql`.
diff --git a/docs/readings-on-coding/index.md b/docs/readings-on-coding/index.md
deleted file mode 100644
index b612d0df6f3..00000000000
--- a/docs/readings-on-coding/index.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-nav_order: 6
-has_children: true
----
-# Readings on Coding
diff --git a/docs/teaching.md b/docs/teaching.md
index fdf628d7073..097110ce58e 100644
--- a/docs/teaching.md
+++ b/docs/teaching.md
@@ -1,5 +1,5 @@
---
-nav_order: 6
+nav_order: 3
---
# JabRef and Software Engineering Training
diff --git a/src/main/java/org/jabref/cli/ArgumentProcessor.java b/src/main/java/org/jabref/cli/ArgumentProcessor.java
index f1aaa0653d7..fad33ed0d0d 100644
--- a/src/main/java/org/jabref/cli/ArgumentProcessor.java
+++ b/src/main/java/org/jabref/cli/ArgumentProcessor.java
@@ -15,7 +15,6 @@
import org.jabref.gui.Globals;
import org.jabref.gui.externalfiles.AutoSetFileLinksUtil;
-import org.jabref.gui.externalfiletype.ExternalFileTypes;
import org.jabref.gui.undo.NamedCompound;
import org.jabref.logic.JabRefException;
import org.jabref.logic.bibtex.FieldWriterPreferences;
@@ -525,19 +524,21 @@ private void saveDatabase(BibDatabase newBase, String subName) {
System.out.println(Localization.lang("Saving") + ": " + subName);
GeneralPreferences generalPreferences = preferencesService.getGeneralPreferences();
SavePreferences savePreferences = preferencesService.getSavePreferences();
- AtomicFileWriter fileWriter = new AtomicFileWriter(Path.of(subName), StandardCharsets.UTF_8);
- BibWriter bibWriter = new BibWriter(fileWriter, OS.NEWLINE);
- BibDatabaseWriter databaseWriter = new BibtexDatabaseWriter(
- bibWriter,
- generalPreferences,
- savePreferences,
- Globals.entryTypesManager);
- databaseWriter.saveDatabase(new BibDatabaseContext(newBase));
-
- // Show just a warning message if encoding did not work for all characters:
- if (fileWriter.hasEncodingProblems()) {
- System.err.println(Localization.lang("Warning") + ": "
- + Localization.lang("UTF-8 could not be used to encode the following characters: %0", fileWriter.getEncodingProblems()));
+ try (AtomicFileWriter fileWriter = new AtomicFileWriter(Path.of(subName), StandardCharsets.UTF_8)) {
+ BibWriter bibWriter = new BibWriter(fileWriter, OS.NEWLINE);
+
+ BibDatabaseWriter databaseWriter = new BibtexDatabaseWriter(
+ bibWriter,
+ generalPreferences,
+ savePreferences,
+ Globals.entryTypesManager);
+ databaseWriter.saveDatabase(new BibDatabaseContext(newBase));
+
+ // Show just a warning message if encoding did not work for all characters:
+ if (fileWriter.hasEncodingProblems()) {
+ System.err.println(Localization.lang("Warning") + ": "
+ + Localization.lang("UTF-8 could not be used to encode the following characters: %0", fileWriter.getEncodingProblems()));
+ }
}
} catch (IOException ex) {
System.err.println(Localization.lang("Could not save file.") + "\n" + ex.getLocalizedMessage());
@@ -640,8 +641,7 @@ private void automaticallySetFileLinks(List loaded) {
AutoSetFileLinksUtil util = new AutoSetFileLinksUtil(
parserResult.getDatabaseContext(),
preferencesService.getFilePreferences(),
- preferencesService.getAutoLinkPreferences(),
- ExternalFileTypes.getInstance());
+ preferencesService.getAutoLinkPreferences());
util.linkAssociatedFiles(database.getEntries(), new NamedCompound(""));
}
}
diff --git a/src/main/java/org/jabref/gui/EntryTypeViewModel.java b/src/main/java/org/jabref/gui/EntryTypeViewModel.java
index 4e18e4d87c0..7e08b6857e2 100644
--- a/src/main/java/org/jabref/gui/EntryTypeViewModel.java
+++ b/src/main/java/org/jabref/gui/EntryTypeViewModel.java
@@ -15,7 +15,6 @@
import javafx.concurrent.Worker;
import org.jabref.gui.externalfiles.ImportHandler;
-import org.jabref.gui.externalfiletype.ExternalFileTypes;
import org.jabref.gui.importer.NewEntryAction;
import org.jabref.logic.importer.FetcherClientException;
import org.jabref.logic.importer.FetcherException;
@@ -169,7 +168,6 @@ public void runFetcherWorker() {
ImportHandler handler = new ImportHandler(
libraryTab.getBibDatabaseContext(),
- ExternalFileTypes.getInstance(),
preferencesService,
Globals.getFileUpdateMonitor(),
libraryTab.getUndoManager(),
diff --git a/src/main/java/org/jabref/gui/JabRefFrame.java b/src/main/java/org/jabref/gui/JabRefFrame.java
index 638a7761f10..718516f669e 100644
--- a/src/main/java/org/jabref/gui/JabRefFrame.java
+++ b/src/main/java/org/jabref/gui/JabRefFrame.java
@@ -61,7 +61,6 @@
import org.jabref.gui.copyfiles.CopyFilesAction;
import org.jabref.gui.customentrytypes.CustomizeEntryAction;
import org.jabref.gui.desktop.JabRefDesktop;
-import org.jabref.gui.dialogs.AutosaveUiManager;
import org.jabref.gui.documentviewer.ShowDocumentViewerAction;
import org.jabref.gui.duplicationFinder.DuplicateSearch;
import org.jabref.gui.edit.CopyMoreAction;
@@ -81,7 +80,6 @@
import org.jabref.gui.externalfiles.AutoLinkFilesAction;
import org.jabref.gui.externalfiles.DownloadFullTextAction;
import org.jabref.gui.externalfiles.FindUnlinkedFilesAction;
-import org.jabref.gui.externalfiletype.ExternalFileTypes;
import org.jabref.gui.help.AboutAction;
import org.jabref.gui.help.ErrorConsoleAction;
import org.jabref.gui.help.HelpAction;
@@ -258,7 +256,7 @@ private void initDragAndDrop() {
tabbedPane.getTabs().remove(dndIndicator);
List bibFiles = DragAndDropHelper.getBibFiles(tabDragEvent.getDragboard());
OpenDatabaseAction openDatabaseAction = this.getOpenDatabaseAction();
- openDatabaseAction.openFiles(bibFiles, true);
+ openDatabaseAction.openFiles(bibFiles);
tabDragEvent.setDropCompleted(true);
tabDragEvent.consume();
} else {
@@ -281,7 +279,7 @@ private void initDragAndDrop() {
tabbedPane.getTabs().remove(dndIndicator);
List bibFiles = DragAndDropHelper.getBibFiles(event.getDragboard());
OpenDatabaseAction openDatabaseAction = this.getOpenDatabaseAction();
- openDatabaseAction.openFiles(bibFiles, true);
+ openDatabaseAction.openFiles(bibFiles);
event.setDropCompleted(true);
event.consume();
});
@@ -381,8 +379,7 @@ private void showTrackingNotification() {
*/
public void openAction(String filePath) {
Path file = Path.of(filePath);
- // all the logic is done in openIt. Even raising an existing panel
- getOpenDatabaseAction().openFile(file, true);
+ getOpenDatabaseAction().openFile(file);
}
/**
@@ -830,7 +827,7 @@ private MenuBar createMenu() {
quality.getItems().addAll(
factory.createMenuItem(StandardActions.FIND_DUPLICATES, new DuplicateSearch(this, dialogService, stateManager)),
- factory.createMenuItem(StandardActions.MERGE_ENTRIES, new MergeEntriesAction(this, dialogService, stateManager)),
+ factory.createMenuItem(StandardActions.MERGE_ENTRIES, new MergeEntriesAction(dialogService, stateManager)),
factory.createMenuItem(StandardActions.CHECK_INTEGRITY, new IntegrityCheckAction(this, stateManager, Globals.TASK_EXECUTOR)),
factory.createMenuItem(StandardActions.CLEANUP_ENTRIES, new CleanupAction(this, this.prefs, dialogService, stateManager)),
@@ -1049,6 +1046,11 @@ hide it and clip it to a square of (width x width) each time width is updated.
return new Group(indicator);
}
+ /**
+ * Might be called when a user asks JabRef at the command line
+ * i) to import a file or
+ * ii) to open a .bib file
+ */
public void addParserResult(ParserResult parserResult, boolean focusPanel) {
if (parserResult.toOpenTab()) {
// Add the entries to the open tab.
@@ -1060,7 +1062,7 @@ public void addParserResult(ParserResult parserResult, boolean focusPanel) {
addImportedEntries(libraryTab, parserResult);
}
} else {
- // only add tab if DB is not already open
+ // only add tab if library is not already open
Optional libraryTab = getLibraryTabs().stream()
.filter(p -> p.getBibDatabaseContext()
.getDatabasePath()
@@ -1120,17 +1122,6 @@ public void addTab(LibraryTab libraryTab, boolean raisePanel) {
}
libraryTab.getUndoManager().registerListener(new UndoRedoEventManager());
-
- BibDatabaseContext context = libraryTab.getBibDatabaseContext();
-
- if (readyForAutosave(context)) {
- AutosaveManager autosaver = AutosaveManager.start(context);
- autosaver.registerListener(new AutosaveUiManager(libraryTab));
- }
-
- BackupManager.start(context, Globals.entryTypesManager, prefs);
-
- trackOpenNewDatabase(libraryTab);
}
private void trackOpenNewDatabase(LibraryTab libraryTab) {
@@ -1143,18 +1134,11 @@ private void trackOpenNewDatabase(LibraryTab libraryTab) {
public LibraryTab addTab(BibDatabaseContext databaseContext, boolean raisePanel) {
Objects.requireNonNull(databaseContext);
- LibraryTab libraryTab = new LibraryTab(this, prefs, stateManager, themeManager, databaseContext, ExternalFileTypes.getInstance(), importFormatReader);
+ LibraryTab libraryTab = new LibraryTab(this, prefs, stateManager, themeManager, databaseContext, importFormatReader);
addTab(libraryTab, raisePanel);
return libraryTab;
}
- private boolean readyForAutosave(BibDatabaseContext context) {
- return ((context.getLocation() == DatabaseLocation.SHARED)
- || ((context.getLocation() == DatabaseLocation.LOCAL)
- && prefs.getImportExportPreferences().shouldAutoSave()))
- && context.getDatabasePath().isPresent();
- }
-
/**
* Opens the import inspection dialog to let the user decide which of the given entries to import.
*
diff --git a/src/main/java/org/jabref/gui/JabRefGUI.java b/src/main/java/org/jabref/gui/JabRefGUI.java
index 3df6414a972..2acb97939c1 100644
--- a/src/main/java/org/jabref/gui/JabRefGUI.java
+++ b/src/main/java/org/jabref/gui/JabRefGUI.java
@@ -1,7 +1,5 @@
package org.jabref.gui;
-import java.io.IOException;
-import java.nio.file.Files;
import java.nio.file.Path;
import java.sql.SQLException;
import java.util.ArrayList;
@@ -14,15 +12,12 @@
import javafx.stage.Screen;
import javafx.stage.Stage;
-import org.jabref.gui.dialogs.BackupUIManager;
import org.jabref.gui.help.VersionWorker;
import org.jabref.gui.icon.IconTheme;
import org.jabref.gui.importer.ParserResultWarningDialog;
import org.jabref.gui.importer.actions.OpenDatabaseAction;
import org.jabref.gui.keyboard.TextInputKeyBindings;
import org.jabref.gui.shared.SharedDatabaseUIManager;
-import org.jabref.logic.autosaveandbackup.BackupManager;
-import org.jabref.logic.importer.OpenDatabase;
import org.jabref.logic.importer.ParserResult;
import org.jabref.logic.l10n.Localization;
import org.jabref.logic.shared.DatabaseNotSupportedException;
@@ -77,8 +72,8 @@ private void openWindow(Stage mainStage) {
if (guiPreferences.isWindowMaximised()) {
mainStage.setMaximized(true);
} else if ((Screen.getScreens().size() == 1) && isWindowPositionOutOfBounds()) {
- // corrects the Window, if its outside of the mainscreen
- LOGGER.debug("The Jabref Window is outside the Main Monitor\n");
+ // corrects the Window, if it is outside the mainscreen
+ LOGGER.debug("The Jabref window is outside the main screen\n");
mainStage.setX(0);
mainStage.setY(0);
mainStage.setWidth(1024);
@@ -135,6 +130,8 @@ private void openDatabases() {
openLastEditedDatabases();
}
+ // From here on, the libraries provided by command line arguments are treated
+
// Remove invalid databases
List invalidDatabases = bibDatabases.stream()
.filter(ParserResult::isInvalid)
@@ -266,34 +263,8 @@ private void openLastEditedDatabases() {
return;
}
- for (String fileName : lastFiles) {
- Path dbFile = Path.of(fileName);
-
- // Already parsed via command line parameter, e.g., "jabref.jar somefile.bib"
- if (isLoaded(dbFile) || !Files.exists(dbFile)) {
- continue;
- }
-
- if (BackupManager.backupFileDiffers(dbFile)) {
- BackupUIManager.showRestoreBackupDialog(mainFrame.getDialogService(), dbFile);
- }
-
- ParserResult parsedDatabase;
- try {
- parsedDatabase = OpenDatabase.loadDatabase(
- dbFile,
- preferencesService.getImportFormatPreferences(),
- Globals.getFileUpdateMonitor());
- } catch (IOException ex) {
- LOGGER.error("Error opening file '{}'", dbFile, ex);
- parsedDatabase = ParserResult.fromError(ex);
- }
- bibDatabases.add(parsedDatabase);
- }
- }
-
- private boolean isLoaded(Path fileToOpen) {
- return bibDatabases.stream().anyMatch(pr -> pr.getPath().isPresent() && pr.getPath().get().equals(fileToOpen));
+ List filesToOpen = lastFiles.stream().map(file -> Path.of(file)).collect(Collectors.toList());
+ getMainFrame().getOpenDatabaseAction().openFiles(filesToOpen);
}
public static JabRefFrame getMainFrame() {
diff --git a/src/main/java/org/jabref/gui/JabRefMain.java b/src/main/java/org/jabref/gui/JabRefMain.java
index e26b1286c9b..a9812e591ec 100644
--- a/src/main/java/org/jabref/gui/JabRefMain.java
+++ b/src/main/java/org/jabref/gui/JabRefMain.java
@@ -15,6 +15,7 @@
import org.jabref.cli.ArgumentProcessor;
import org.jabref.cli.JabRefCLI;
+import org.jabref.gui.openoffice.OOBibBaseConnect;
import org.jabref.gui.remote.JabRefMessageHandler;
import org.jabref.logic.exporter.ExporterFactory;
import org.jabref.logic.journals.JournalAbbreviationLoader;
@@ -131,6 +132,7 @@ public void start(Stage mainStage) {
@Override
public void stop() {
+ OOBibBaseConnect.closeOfficeConnection();
Globals.stopBackgroundTasks();
Globals.shutdownThreadPools();
}
@@ -162,10 +164,12 @@ private static void applyPreferences(PreferencesService preferences) {
Globals.journalAbbreviationRepository = JournalAbbreviationLoader.loadRepository(preferences.getJournalAbbreviationPreferences());
// Build list of Import and Export formats
- Globals.IMPORT_FORMAT_READER.resetImportFormats(preferences.getImporterPreferences(),
+ Globals.IMPORT_FORMAT_READER.resetImportFormats(
+ preferences.getImporterPreferences(),
preferences.getImportFormatPreferences(),
- preferences.getXmpPreferences(), Globals.getFileUpdateMonitor());
- Globals.entryTypesManager.addCustomOrModifiedTypes(preferences.getBibEntryTypes(BibDatabaseMode.BIBTEX),
+ Globals.getFileUpdateMonitor());
+ Globals.entryTypesManager.addCustomOrModifiedTypes(
+ preferences.getBibEntryTypes(BibDatabaseMode.BIBTEX),
preferences.getBibEntryTypes(BibDatabaseMode.BIBLATEX));
Globals.exportFactory = ExporterFactory.create(
preferences.getCustomExportFormats(Globals.journalAbbreviationRepository),
diff --git a/src/main/java/org/jabref/gui/LibraryTab.java b/src/main/java/org/jabref/gui/LibraryTab.java
index f037135f6d9..246b9d554ab 100644
--- a/src/main/java/org/jabref/gui/LibraryTab.java
+++ b/src/main/java/org/jabref/gui/LibraryTab.java
@@ -29,7 +29,6 @@
import org.jabref.gui.collab.DatabaseChangeMonitor;
import org.jabref.gui.dialogs.AutosaveUiManager;
import org.jabref.gui.entryeditor.EntryEditor;
-import org.jabref.gui.externalfiletype.ExternalFileTypes;
import org.jabref.gui.importer.actions.OpenDatabaseAction;
import org.jabref.gui.maintable.MainTable;
import org.jabref.gui.maintable.MainTableDataModel;
@@ -84,7 +83,6 @@ public class LibraryTab extends Tab {
private static final Logger LOGGER = LoggerFactory.getLogger(LibraryTab.class);
private final JabRefFrame frame;
private final CountingUndoManager undoManager;
- private final ExternalFileTypes externalFileTypes;
private final DialogService dialogService;
private final PreferencesService preferencesService;
private final StateManager stateManager;
@@ -122,11 +120,9 @@ public LibraryTab(JabRefFrame frame,
StateManager stateManager,
ThemeManager themeManager,
BibDatabaseContext bibDatabaseContext,
- ExternalFileTypes externalFileTypes,
ImportFormatReader importFormatReader) {
this.frame = Objects.requireNonNull(frame);
this.bibDatabaseContext = Objects.requireNonNull(bibDatabaseContext);
- this.externalFileTypes = Objects.requireNonNull(externalFileTypes);
this.undoManager = frame.getUndoManager();
this.dialogService = frame.getDialogService();
this.preferencesService = Objects.requireNonNull(preferencesService);
@@ -156,7 +152,7 @@ public LibraryTab(JabRefFrame frame,
this.getDatabase().registerListener(new UpdateTimestampListener(preferencesService));
- this.entryEditor = new EntryEditor(this, externalFileTypes);
+ this.entryEditor = new EntryEditor(this);
// set LibraryTab ID for drag'n'drop
// ID content doesn't matter, we only need different tabs to have different ID
@@ -216,6 +212,7 @@ public void onDatabaseLoadingSucceed(ParserResult result) {
OpenDatabaseAction.performPostOpenActions(this, result);
feedData(context);
+
// a temporary workaround to update groups pane
stateManager.activeDatabaseProperty().bind(
EasyBind.map(frame.getTabbedPane().getSelectionModel().selectedItemProperty(),
@@ -257,7 +254,7 @@ public void feedData(BibDatabaseContext bibDatabaseContext) {
this.getDatabase().registerListener(new UpdateTimestampListener(preferencesService));
- this.entryEditor = new EntryEditor(this, externalFileTypes);
+ this.entryEditor = new EntryEditor(this);
Platform.runLater(() -> {
EasyBind.subscribe(changedProperty, this::updateTabTitle);
@@ -265,12 +262,17 @@ public void feedData(BibDatabaseContext bibDatabaseContext) {
updateTabTitle(changedProperty.getValue()));
});
+ installAutosaveManagerAndBackupManager();
+ }
+
+ public void installAutosaveManagerAndBackupManager() {
if (isDatabaseReadyForAutoSave(bibDatabaseContext)) {
- AutosaveManager autoSaver = AutosaveManager.start(bibDatabaseContext);
- autoSaver.registerListener(new AutosaveUiManager(this));
+ AutosaveManager autosaveManager = AutosaveManager.start(bibDatabaseContext);
+ autosaveManager.registerListener(new AutosaveUiManager(this));
+ }
+ if (isDatabaseReadyForBackup(bibDatabaseContext)) {
+ BackupManager.start(bibDatabaseContext, Globals.entryTypesManager, preferencesService);
}
-
- BackupManager.start(this.bibDatabaseContext, Globals.entryTypesManager, preferencesService);
}
private boolean isDatabaseReadyForAutoSave(BibDatabaseContext context) {
@@ -280,6 +282,10 @@ private boolean isDatabaseReadyForAutoSave(BibDatabaseContext context) {
&& context.getDatabasePath().isPresent();
}
+ private boolean isDatabaseReadyForBackup(BibDatabaseContext context) {
+ return (context.getLocation() == DatabaseLocation.LOCAL) && context.getDatabasePath().isPresent();
+ }
+
/**
* Sets the title of the tab modification-asterisk filename – path-fragment
*
@@ -488,7 +494,6 @@ private void createMainTable() {
preferencesService,
dialogService,
stateManager,
- externalFileTypes,
Globals.getKeyPrefs(),
Globals.getClipboardManager(),
Globals.IMPORT_FORMAT_READER);
@@ -808,7 +813,7 @@ public LibraryTab createLibraryTab(JabRefFrame frame, PreferencesService prefere
BibDatabaseContext context = new BibDatabaseContext();
context.setDatabasePath(file);
- LibraryTab newTab = new LibraryTab(frame, preferencesService, stateManager, themeManager, context, ExternalFileTypes.getInstance(), importFormatReader);
+ LibraryTab newTab = new LibraryTab(frame, preferencesService, stateManager, themeManager, context, importFormatReader);
newTab.setDataLoadingTask(dataLoadingTask);
dataLoadingTask.onRunning(newTab::onDatabaseLoadingStarted)
diff --git a/src/main/java/org/jabref/gui/bibtexextractor/BibtexExtractorViewModel.java b/src/main/java/org/jabref/gui/bibtexextractor/BibtexExtractorViewModel.java
index 8224873ad70..a8e007dda62 100644
--- a/src/main/java/org/jabref/gui/bibtexextractor/BibtexExtractorViewModel.java
+++ b/src/main/java/org/jabref/gui/bibtexextractor/BibtexExtractorViewModel.java
@@ -13,7 +13,6 @@
import org.jabref.gui.Globals;
import org.jabref.gui.StateManager;
import org.jabref.gui.externalfiles.ImportHandler;
-import org.jabref.gui.externalfiletype.ExternalFileTypes;
import org.jabref.gui.util.BackgroundTask;
import org.jabref.gui.util.TaskExecutor;
import org.jabref.logic.importer.FetcherException;
@@ -52,7 +51,6 @@ public BibtexExtractorViewModel(BibDatabaseContext bibdatabaseContext,
this.taskExecutor = taskExecutor;
this.importHandler = new ImportHandler(
bibdatabaseContext,
- ExternalFileTypes.getInstance(),
preferencesService,
fileUpdateMonitor,
undoManager,
@@ -66,7 +64,7 @@ public StringProperty inputTextProperty() {
}
public void startParsing() {
- if (preferencesService.getImporterPreferences().isGrobidEnabled()) {
+ if (preferencesService.getGrobidPreferences().isGrobidEnabled()) {
parseUsingGrobid();
} else {
parseUsingBibtexExtractor();
@@ -80,7 +78,7 @@ private void parseUsingBibtexExtractor() {
}
private void parseUsingGrobid() {
- GrobidCitationFetcher grobidCitationFetcher = new GrobidCitationFetcher(preferencesService.getImporterPreferences(), preferencesService.getImportFormatPreferences());
+ GrobidCitationFetcher grobidCitationFetcher = new GrobidCitationFetcher(preferencesService.getGrobidPreferences(), preferencesService.getImportFormatPreferences());
BackgroundTask.wrap(() -> grobidCitationFetcher.performSearch(inputTextProperty.getValue()))
.onRunning(() -> dialogService.notify(Localization.lang("Your text is being parsed...")))
.onFailure((e) -> {
diff --git a/src/main/java/org/jabref/gui/bibtexextractor/ExtractBibtexAction.java b/src/main/java/org/jabref/gui/bibtexextractor/ExtractBibtexAction.java
index ce198561af4..70c693a5fe9 100644
--- a/src/main/java/org/jabref/gui/bibtexextractor/ExtractBibtexAction.java
+++ b/src/main/java/org/jabref/gui/bibtexextractor/ExtractBibtexAction.java
@@ -24,7 +24,7 @@ public ExtractBibtexAction(DialogService dialogService, PreferencesService prefe
@Override
public void execute() {
DialogService dialogService = Injector.instantiateModelOrService(DialogService.class);
- GrobidOptInDialogHelper.showAndWaitIfUserIsUndecided(dialogService, preferencesService.getImporterPreferences());
+ GrobidOptInDialogHelper.showAndWaitIfUserIsUndecided(dialogService, preferencesService.getGrobidPreferences());
dialogService.showCustomDialogAndWait(new ExtractBibtexDialog());
}
}
diff --git a/src/main/java/org/jabref/gui/collab/ChangeDisplayDialog.java b/src/main/java/org/jabref/gui/collab/ChangeDisplayDialog.java
deleted file mode 100644
index e9f0751cc07..00000000000
--- a/src/main/java/org/jabref/gui/collab/ChangeDisplayDialog.java
+++ /dev/null
@@ -1,114 +0,0 @@
-package org.jabref.gui.collab;
-
-import java.util.List;
-
-import javafx.collections.FXCollections;
-import javafx.scene.control.Button;
-import javafx.scene.control.ButtonBar;
-import javafx.scene.control.ButtonBar.ButtonData;
-import javafx.scene.control.ButtonType;
-import javafx.scene.control.CheckBox;
-import javafx.scene.control.Label;
-import javafx.scene.control.ListView;
-import javafx.scene.control.ScrollPane;
-import javafx.scene.control.SplitPane;
-import javafx.scene.layout.BorderPane;
-import javafx.scene.layout.Region;
-import javafx.scene.layout.VBox;
-
-import org.jabref.gui.undo.NamedCompound;
-import org.jabref.gui.util.BaseDialog;
-import org.jabref.logic.l10n.Localization;
-import org.jabref.model.database.BibDatabaseContext;
-
-import com.tobiasdiez.easybind.EasyBind;
-
-class ChangeDisplayDialog extends BaseDialog {
-
- private final ListView changesList;
- private final BorderPane infoPanel = new BorderPane();
- private final CheckBox cb = new CheckBox(Localization.lang("Accept change"));
-
- public ChangeDisplayDialog(BibDatabaseContext database, List changes) {
- this.setTitle(Localization.lang("External changes"));
- this.getDialogPane().setPrefSize(800, 600);
-
- changesList = new ListView<>(FXCollections.observableArrayList(changes));
- changesList.setPrefWidth(200);
- EasyBind.subscribe(changesList.getSelectionModel().selectedItemProperty(), this::selectedChangeChanged);
-
- SplitPane pane = new SplitPane();
- pane.setDividerPositions(0.2);
-
- Button selectAllChangesFromDisk = new Button(Localization.lang("Mark all changes as accepted"));
- selectAllChangesFromDisk.setMinWidth(Region.USE_PREF_SIZE);
- selectAllChangesFromDisk.setOnAction(evt -> {
- for (DatabaseChangeViewModel change : changes) {
- change.setAccepted(true);
- }
- });
- Button unselectAllAcceptChanges = new Button(Localization.lang("Unmark all changes"));
- unselectAllAcceptChanges.setOnAction(evt -> {
- for (DatabaseChangeViewModel change : changes) {
- change.setAccepted(false);
- }
- });
-
- VBox leftContent = new VBox(changesList,
- selectAllChangesFromDisk,
- unselectAllAcceptChanges);
-
- ScrollPane leftScroll = new ScrollPane(leftContent);
- leftScroll.setFitToHeight(true);
- leftScroll.setFitToWidth(true);
-
- pane.getItems().addAll(leftScroll, infoPanel);
- SplitPane.setResizableWithParent(leftScroll, false);
-
- getDialogPane().setContent(pane);
-
- Label rootInfo = new Label(Localization.lang("Select the tree nodes to view and accept or reject changes") + '.');
- infoPanel.setCenter(rootInfo);
- changesList.getSelectionModel().selectFirst();
-
- ButtonType dismissChanges = new ButtonType(Localization.lang("Dismiss"), ButtonData.CANCEL_CLOSE);
-
- getDialogPane().getButtonTypes().setAll(new ButtonType(Localization.lang("Accept changes"), ButtonBar.ButtonData.APPLY),
- dismissChanges);
-
- setResultConverter(button -> {
- if (button == dismissChanges) {
- return false;
- } else {
- // Perform all accepted changes
- NamedCompound ce = new NamedCompound(Localization.lang("Merged external changes"));
- for (DatabaseChangeViewModel change : changes) {
- if (change instanceof EntryChangeViewModel) {
- // We don't have a checkbox for accept and always get the correct merged entry, the accept property in this special case only controls the radio buttons selection
- change.makeChange(database, ce);
- } else if (change.isAccepted()) {
- change.makeChange(database, ce);
- }
- }
- ce.end();
- // TODO: panel.getUndoManager().addEdit(ce);
-
- return true;
- }
- });
- }
-
- private void selectedChangeChanged(DatabaseChangeViewModel currentChange) {
- if (currentChange != null) {
- infoPanel.setCenter(currentChange.description());
-
- if (!(currentChange instanceof EntryChangeViewModel)) {
- cb.setManaged(true);
- infoPanel.setBottom(cb);
- cb.selectedProperty().bindBidirectional(currentChange.acceptedProperty());
- } else {
- cb.setManaged(false);
- }
- }
- }
-}
diff --git a/src/main/java/org/jabref/gui/collab/ChangeScanner.java b/src/main/java/org/jabref/gui/collab/ChangeScanner.java
index f19f406e18f..74e9bf2b88b 100644
--- a/src/main/java/org/jabref/gui/collab/ChangeScanner.java
+++ b/src/main/java/org/jabref/gui/collab/ChangeScanner.java
@@ -7,6 +7,16 @@
import org.jabref.gui.DialogService;
import org.jabref.gui.StateManager;
+import org.jabref.gui.collab.entryadd.EntryAdd;
+import org.jabref.gui.collab.entrychange.EntryChange;
+import org.jabref.gui.collab.entrydelete.EntryDelete;
+import org.jabref.gui.collab.groupchange.GroupChange;
+import org.jabref.gui.collab.metedatachange.MetadataChange;
+import org.jabref.gui.collab.preamblechange.PreambleChange;
+import org.jabref.gui.collab.stringadd.BibTexStringAdd;
+import org.jabref.gui.collab.stringchange.BibTexStringChange;
+import org.jabref.gui.collab.stringdelete.BibTexStringDelete;
+import org.jabref.gui.collab.stringrename.BibTexStringRename;
import org.jabref.gui.theme.ThemeManager;
import org.jabref.logic.bibtex.comparator.BibDatabaseDiff;
import org.jabref.logic.bibtex.comparator.BibEntryDiff;
@@ -31,6 +41,8 @@ public class ChangeScanner {
private final StateManager stateManager;
private final ThemeManager themeManager;
+ private final ExternalChangeResolverFactory externalChangeResolverFactory;
+
public ChangeScanner(BibDatabaseContext database,
DialogService dialogService,
PreferencesService preferencesService,
@@ -41,15 +53,16 @@ public ChangeScanner(BibDatabaseContext database,
this.preferencesService = preferencesService;
this.stateManager = stateManager;
this.themeManager = themeManager;
+ this.externalChangeResolverFactory = new ExternalChangeResolverFactory(dialogService, database);
}
- public List scanForChanges() {
+ public List scanForChanges() {
if (database.getDatabasePath().isEmpty()) {
return Collections.emptyList();
}
try {
- List changes = new ArrayList<>();
+ List changes = new ArrayList<>();
// Parse the modified file
// Important: apply all post-load actions
@@ -61,10 +74,12 @@ public List scanForChanges() {
// Start looking at changes.
BibDatabaseDiff differences = BibDatabaseDiff.compare(database, databaseOnDisk);
differences.getMetaDataDifferences().ifPresent(diff -> {
- changes.add(new MetaDataChangeViewModel(diff, preferencesService));
- diff.getGroupDifferences().ifPresent(groupDiff -> changes.add(new GroupChangeViewModel(groupDiff)));
+ changes.add(new MetadataChange(diff, database, externalChangeResolverFactory));
+ diff.getGroupDifferences().ifPresent(groupDiff -> changes.add(new GroupChange(
+ groupDiff, database, externalChangeResolverFactory
+ )));
});
- differences.getPreambleDifferences().ifPresent(diff -> changes.add(new PreambleChangeViewModel(diff)));
+ differences.getPreambleDifferences().ifPresent(diff -> changes.add(new PreambleChange(diff, database, externalChangeResolverFactory)));
differences.getBibStringDifferences().forEach(diff -> changes.add(createBibStringDiff(diff)));
differences.getEntryDifferences().forEach(diff -> changes.add(createBibEntryDiff(diff)));
return changes;
@@ -74,31 +89,31 @@ public List scanForChanges() {
}
}
- private DatabaseChangeViewModel createBibStringDiff(BibStringDiff diff) {
- if (diff.getOriginalString() == null) {
- return new StringAddChangeViewModel(diff.getNewString());
+ private ExternalChange createBibStringDiff(BibStringDiff diff) {
+ if (diff.getOriginalString() == null) {
+ return new BibTexStringAdd(diff.getNewString(), database, externalChangeResolverFactory);
}
if (diff.getNewString() == null) {
- return new StringRemoveChangeViewModel(diff.getOriginalString());
+ return new BibTexStringDelete(diff.getOriginalString(), database, externalChangeResolverFactory);
}
if (diff.getOriginalString().getName().equals(diff.getNewString().getName())) {
- return new StringChangeViewModel(diff.getOriginalString(), diff.getNewString());
+ return new BibTexStringChange(diff.getOriginalString(), diff.getNewString(), database, externalChangeResolverFactory);
}
- return new StringNameChangeViewModel(diff.getOriginalString(), diff.getNewString());
+ return new BibTexStringRename(diff.getOriginalString(), diff.getNewString(), database, externalChangeResolverFactory);
}
- private DatabaseChangeViewModel createBibEntryDiff(BibEntryDiff diff) {
+ private ExternalChange createBibEntryDiff(BibEntryDiff diff) {
if (diff.getOriginalEntry() == null) {
- return new EntryAddChangeViewModel(diff.getNewEntry(), preferencesService, dialogService, stateManager, themeManager);
+ return new EntryAdd(diff.getNewEntry(), database, externalChangeResolverFactory);
}
if (diff.getNewEntry() == null) {
- return new EntryDeleteChangeViewModel(diff.getOriginalEntry());
+ return new EntryDelete(diff.getOriginalEntry(), database, externalChangeResolverFactory);
}
- return new EntryChangeViewModel(diff.getOriginalEntry(), diff.getNewEntry());
+ return new EntryChange(diff.getOriginalEntry(), diff.getNewEntry(), database, externalChangeResolverFactory);
}
}
diff --git a/src/main/java/org/jabref/gui/collab/DatabaseChangeListener.java b/src/main/java/org/jabref/gui/collab/DatabaseChangeListener.java
index cfb9ac2e1ff..b26a511ecf1 100644
--- a/src/main/java/org/jabref/gui/collab/DatabaseChangeListener.java
+++ b/src/main/java/org/jabref/gui/collab/DatabaseChangeListener.java
@@ -3,5 +3,5 @@
import java.util.List;
public interface DatabaseChangeListener {
- void databaseChanged(List changes);
+ void databaseChanged(List changes);
}
diff --git a/src/main/java/org/jabref/gui/collab/DatabaseChangeMonitor.java b/src/main/java/org/jabref/gui/collab/DatabaseChangeMonitor.java
index 575307bcda8..3f1b3b4fb5e 100644
--- a/src/main/java/org/jabref/gui/collab/DatabaseChangeMonitor.java
+++ b/src/main/java/org/jabref/gui/collab/DatabaseChangeMonitor.java
@@ -58,7 +58,7 @@ public DatabaseChangeMonitor(BibDatabaseContext database,
try {
fileMonitor.addListenerForFile(path, this);
} catch (IOException e) {
- LOGGER.error("Error while trying to monitor " + path, e);
+ LOGGER.error("Error while trying to monitor {}", path, e);
}
});
@@ -67,7 +67,7 @@ public DatabaseChangeMonitor(BibDatabaseContext database,
Localization.lang("The library has been modified by another program."),
List.of(new Action(Localization.lang("Dismiss changes"), event -> notificationPane.hide()),
new Action(Localization.lang("Review changes"), event -> {
- dialogService.showCustomDialogAndWait(new ChangeDisplayDialog(database, changes));
+ dialogService.showCustomDialogAndWait(new ExternalChangesResolverDialog(changes, database, dialogService, stateManager, themeManager, preferencesService));
notificationPane.hide();
})),
Duration.ZERO));
@@ -75,16 +75,18 @@ public DatabaseChangeMonitor(BibDatabaseContext database,
@Override
public void fileUpdated() {
- // File on disk has changed, thus look for notable changes and notify listeners in case there are such changes
- ChangeScanner scanner = new ChangeScanner(database, dialogService, preferencesService, stateManager, themeManager);
- BackgroundTask.wrap(scanner::scanForChanges)
- .onSuccess(changes -> {
- if (!changes.isEmpty()) {
- listeners.forEach(listener -> listener.databaseChanged(changes));
- }
- })
- .onFailure(e -> LOGGER.error("Error while watching for changes", e))
- .executeWith(taskExecutor);
+ synchronized (database) {
+ // File on disk has changed, thus look for notable changes and notify listeners in case there are such changes
+ ChangeScanner scanner = new ChangeScanner(database, dialogService, preferencesService, stateManager, themeManager);
+ BackgroundTask.wrap(scanner::scanForChanges)
+ .onSuccess(changes -> {
+ if (!changes.isEmpty()) {
+ listeners.forEach(listener -> listener.databaseChanged(changes));
+ }
+ })
+ .onFailure(e -> LOGGER.error("Error while watching for changes", e))
+ .executeWith(taskExecutor);
+ }
}
public void addListener(DatabaseChangeListener listener) {
diff --git a/src/main/java/org/jabref/gui/collab/DatabaseChangeViewModel.java b/src/main/java/org/jabref/gui/collab/DatabaseChangeViewModel.java
deleted file mode 100644
index bdc7dead217..00000000000
--- a/src/main/java/org/jabref/gui/collab/DatabaseChangeViewModel.java
+++ /dev/null
@@ -1,54 +0,0 @@
-package org.jabref.gui.collab;
-
-import javafx.beans.property.BooleanProperty;
-import javafx.beans.property.SimpleBooleanProperty;
-import javafx.scene.Node;
-
-import org.jabref.gui.undo.NamedCompound;
-import org.jabref.model.database.BibDatabaseContext;
-
-abstract class DatabaseChangeViewModel {
-
- protected String name;
- private BooleanProperty acceptedProperty = new SimpleBooleanProperty(true);
-
- DatabaseChangeViewModel() {
- name = "";
- }
-
- DatabaseChangeViewModel(String name) {
- this.name = name;
- }
-
- @Override
- public String toString() {
- return name;
- }
-
- public boolean isAccepted() {
- return acceptedProperty.getValue();
- }
-
- public BooleanProperty acceptedProperty() {
- return this.acceptedProperty;
- }
-
- public void setAccepted(boolean accepted) {
- this.acceptedProperty.setValue(accepted);
- }
-
- /**
- * This method returns a {@link Node} detailing the nature and look of the change, e.g. how it is displayed
- *
- * @return Node the Node with the layout of the change
- */
- public abstract Node description();
-
- /**
- * Performs the change. This method is responsible for adding a proper undo edit to the NamedCompound, so the change can be undone.
- *
- * @param database the database that should be modified accordingly.
- * @param undoEdit NamedCompound The compound to hold the undo edits.
- */
- public abstract void makeChange(BibDatabaseContext database, NamedCompound undoEdit);
-}
diff --git a/src/main/java/org/jabref/gui/collab/EntryAddChangeViewModel.java b/src/main/java/org/jabref/gui/collab/EntryAddChangeViewModel.java
deleted file mode 100644
index 56d763be3c6..00000000000
--- a/src/main/java/org/jabref/gui/collab/EntryAddChangeViewModel.java
+++ /dev/null
@@ -1,53 +0,0 @@
-package org.jabref.gui.collab;
-
-import javafx.scene.Node;
-
-import org.jabref.gui.DialogService;
-import org.jabref.gui.StateManager;
-import org.jabref.gui.preview.PreviewViewer;
-import org.jabref.gui.theme.ThemeManager;
-import org.jabref.gui.undo.NamedCompound;
-import org.jabref.gui.undo.UndoableInsertEntries;
-import org.jabref.logic.l10n.Localization;
-import org.jabref.model.database.BibDatabaseContext;
-import org.jabref.model.entry.BibEntry;
-import org.jabref.preferences.PreferencesService;
-
-class EntryAddChangeViewModel extends DatabaseChangeViewModel {
-
- private final BibEntry entry;
- private final PreferencesService preferencesService;
- private final DialogService dialogService;
- private final StateManager stateManager;
- private final ThemeManager themeManager;
-
- public EntryAddChangeViewModel(BibEntry entry,
- PreferencesService preferencesService,
- DialogService dialogService,
- StateManager stateManager,
- ThemeManager themeManager) {
- super();
- this.dialogService = dialogService;
- this.preferencesService = preferencesService;
- this.themeManager = themeManager;
- this.stateManager = stateManager;
- this.name = entry.getCitationKey()
- .map(key -> Localization.lang("Added entry") + ": '" + key + '\'')
- .orElse(Localization.lang("Added entry"));
- this.entry = entry;
- }
-
- @Override
- public void makeChange(BibDatabaseContext database, NamedCompound undoEdit) {
- database.getDatabase().insertEntry(entry);
- undoEdit.addEdit(new UndoableInsertEntries(database.getDatabase(), entry));
- }
-
- @Override
- public Node description() {
- PreviewViewer previewViewer = new PreviewViewer(new BibDatabaseContext(), dialogService, stateManager, themeManager);
- previewViewer.setLayout(preferencesService.getPreviewPreferences().getSelectedPreviewLayout());
- previewViewer.setEntry(entry);
- return previewViewer;
- }
-}
diff --git a/src/main/java/org/jabref/gui/collab/EntryChangeViewModel.java b/src/main/java/org/jabref/gui/collab/EntryChangeViewModel.java
deleted file mode 100644
index 421e4c1fcd9..00000000000
--- a/src/main/java/org/jabref/gui/collab/EntryChangeViewModel.java
+++ /dev/null
@@ -1,72 +0,0 @@
-package org.jabref.gui.collab;
-
-import javafx.geometry.Insets;
-import javafx.scene.Node;
-import javafx.scene.control.Label;
-import javafx.scene.layout.VBox;
-
-import org.jabref.gui.mergeentries.newmergedialog.ShowDiffConfig;
-import org.jabref.gui.mergeentries.newmergedialog.ThreeWayMergeView;
-import org.jabref.gui.mergeentries.newmergedialog.diffhighlighter.DiffHighlighter;
-import org.jabref.gui.mergeentries.newmergedialog.toolbar.ThreeWayMergeToolbar;
-import org.jabref.gui.undo.NamedCompound;
-import org.jabref.gui.undo.UndoableInsertEntries;
-import org.jabref.logic.l10n.Localization;
-import org.jabref.model.database.BibDatabaseContext;
-import org.jabref.model.entry.BibEntry;
-
-class EntryChangeViewModel extends DatabaseChangeViewModel {
-
- private final BibEntry oldEntry;
- private final BibEntry newEntry;
- private ThreeWayMergeView threeWayMergeView;
-
- public EntryChangeViewModel(BibEntry entry, BibEntry newEntry) {
- super();
-
- this.oldEntry = entry;
- this.newEntry = newEntry;
-
- name = entry.getCitationKey()
- .map(key -> Localization.lang("Modified entry") + ": '" + key + '\'')
- .orElse(Localization.lang("Modified entry"));
- }
-
- /**
- * We override this here to select the radio buttons accordingly
- */
- @Override
- public void setAccepted(boolean accepted) {
- super.setAccepted(accepted);
- if (accepted) {
- threeWayMergeView.selectRightEntryValues();
- } else {
- threeWayMergeView.selectLeftEntryValues();
- }
- }
-
- @Override
- public void makeChange(BibDatabaseContext database, NamedCompound undoEdit) {
- this.description(); // Init dialog to prevent NPE
- database.getDatabase().removeEntry(oldEntry);
- BibEntry mergedEntry = threeWayMergeView.getMergedEntry();
- mergedEntry.setId(oldEntry.getId()); // Keep ID
- database.getDatabase().insertEntry(mergedEntry);
- undoEdit.addEdit(new UndoableInsertEntries(database.getDatabase(), oldEntry));
- undoEdit.addEdit(new UndoableInsertEntries(database.getDatabase(), mergedEntry));
- }
-
- @Override
- public Node description() {
- threeWayMergeView = new ThreeWayMergeView(oldEntry, newEntry, Localization.lang("In JabRef"), Localization.lang("On disk"));
- threeWayMergeView.selectLeftEntryValues();
- threeWayMergeView.showDiff(new ShowDiffConfig(ThreeWayMergeToolbar.DiffView.SPLIT, DiffHighlighter.DiffMethod.WORDS));
- VBox container = new VBox(10);
- Label header = new Label(name);
- header.getStyleClass().add("sectionHeader");
- container.getChildren().add(header);
- container.getChildren().add(threeWayMergeView);
- VBox.setMargin(threeWayMergeView, new Insets(5, 5, 5, 5));
- return container;
- }
-}
diff --git a/src/main/java/org/jabref/gui/collab/EntryDeleteChangeViewModel.java b/src/main/java/org/jabref/gui/collab/EntryDeleteChangeViewModel.java
deleted file mode 100644
index f30b6bda14a..00000000000
--- a/src/main/java/org/jabref/gui/collab/EntryDeleteChangeViewModel.java
+++ /dev/null
@@ -1,43 +0,0 @@
-package org.jabref.gui.collab;
-
-import javafx.scene.Node;
-
-import org.jabref.gui.Globals;
-import org.jabref.gui.JabRefGUI;
-import org.jabref.gui.preview.PreviewViewer;
-import org.jabref.gui.undo.NamedCompound;
-import org.jabref.gui.undo.UndoableRemoveEntries;
-import org.jabref.logic.l10n.Localization;
-import org.jabref.model.database.BibDatabaseContext;
-import org.jabref.model.entry.BibEntry;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-class EntryDeleteChangeViewModel extends DatabaseChangeViewModel {
-
- private static final Logger LOGGER = LoggerFactory.getLogger(EntryDeleteChangeViewModel.class);
- private final BibEntry entry;
-
- public EntryDeleteChangeViewModel(BibEntry entry) {
- super(Localization.lang("Deleted entry"));
-
- this.name = entry.getCitationKey()
- .map(key -> Localization.lang("Deleted entry") + ": '" + key + '\'')
- .orElse(Localization.lang("Deleted entry"));
- this.entry = entry;
- }
-
- @Override
- public void makeChange(BibDatabaseContext database, NamedCompound undoEdit) {
- database.getDatabase().removeEntry(entry);
- undoEdit.addEdit(new UndoableRemoveEntries(database.getDatabase(), entry));
- }
-
- @Override
- public Node description() {
- PreviewViewer previewViewer = new PreviewViewer(new BibDatabaseContext(), JabRefGUI.getMainFrame().getDialogService(), Globals.stateManager, Globals.getThemeManager());
- previewViewer.setEntry(entry);
- return previewViewer;
- }
-}
diff --git a/src/main/java/org/jabref/gui/collab/ExternalChange.java b/src/main/java/org/jabref/gui/collab/ExternalChange.java
new file mode 100644
index 00000000000..00d13c92d29
--- /dev/null
+++ b/src/main/java/org/jabref/gui/collab/ExternalChange.java
@@ -0,0 +1,71 @@
+package org.jabref.gui.collab;
+
+import java.util.Optional;
+
+import javafx.beans.property.BooleanProperty;
+import javafx.beans.property.SimpleBooleanProperty;
+import javafx.beans.property.SimpleStringProperty;
+import javafx.beans.property.StringProperty;
+
+import org.jabref.gui.collab.entryadd.EntryAdd;
+import org.jabref.gui.collab.entrychange.EntryChange;
+import org.jabref.gui.collab.entrydelete.EntryDelete;
+import org.jabref.gui.collab.groupchange.GroupChange;
+import org.jabref.gui.collab.metedatachange.MetadataChange;
+import org.jabref.gui.collab.preamblechange.PreambleChange;
+import org.jabref.gui.collab.stringadd.BibTexStringAdd;
+import org.jabref.gui.collab.stringchange.BibTexStringChange;
+import org.jabref.gui.collab.stringdelete.BibTexStringDelete;
+import org.jabref.gui.collab.stringrename.BibTexStringRename;
+import org.jabref.gui.undo.NamedCompound;
+import org.jabref.gui.util.OptionalObjectProperty;
+import org.jabref.model.database.BibDatabaseContext;
+
+public sealed abstract class ExternalChange permits EntryAdd, EntryChange, EntryDelete, GroupChange, MetadataChange, PreambleChange, BibTexStringAdd, BibTexStringChange, BibTexStringDelete, BibTexStringRename {
+ protected final BibDatabaseContext databaseContext;
+ protected final OptionalObjectProperty externalChangeResolver = OptionalObjectProperty.empty();
+ private final BooleanProperty accepted = new SimpleBooleanProperty();
+ private final StringProperty name = new SimpleStringProperty();
+
+ protected ExternalChange(BibDatabaseContext databaseContext, ExternalChangeResolverFactory externalChangeResolverFactory) {
+ this.databaseContext = databaseContext;
+ setChangeName("Unnamed Change!");
+
+ if (externalChangeResolverFactory != null) {
+ externalChangeResolver.set(externalChangeResolverFactory.create(this));
+ }
+ }
+
+ public boolean isAccepted() {
+ return accepted.get();
+ }
+
+ public BooleanProperty acceptedProperty() {
+ return accepted;
+ }
+
+ public void setAccepted(boolean accepted) {
+ this.accepted.set(accepted);
+ }
+
+ /**
+ * Convinience method for accepting changes
+ * */
+ public void accept() {
+ setAccepted(true);
+ }
+
+ public String getName() {
+ return name.get();
+ }
+
+ protected void setChangeName(String changeName) {
+ name.set(changeName);
+ }
+
+ public Optional getExternalChangeResolver() {
+ return externalChangeResolver.get();
+ }
+
+ public abstract void applyChange(NamedCompound undoEdit);
+}
diff --git a/src/main/java/org/jabref/gui/collab/ExternalChangeDetailsView.java b/src/main/java/org/jabref/gui/collab/ExternalChangeDetailsView.java
new file mode 100644
index 00000000000..d83940060da
--- /dev/null
+++ b/src/main/java/org/jabref/gui/collab/ExternalChangeDetailsView.java
@@ -0,0 +1,17 @@
+package org.jabref.gui.collab;
+
+import javafx.scene.layout.AnchorPane;
+
+import org.jabref.gui.collab.entryadd.EntryAddDetailsView;
+import org.jabref.gui.collab.entrychange.EntryChangeDetailsView;
+import org.jabref.gui.collab.entrydelete.EntryDeleteDetailsView;
+import org.jabref.gui.collab.groupchange.GroupChangeDetailsView;
+import org.jabref.gui.collab.metedatachange.MetadataChangeDetailsView;
+import org.jabref.gui.collab.preamblechange.PreambleChangeDetailsView;
+import org.jabref.gui.collab.stringadd.BibTexStringAddDetailsView;
+import org.jabref.gui.collab.stringchange.BibTexStringChangeDetailsView;
+import org.jabref.gui.collab.stringdelete.BibTexStringDeleteDetailsView;
+import org.jabref.gui.collab.stringrename.BibTexStringRenameDetailsView;
+
+public sealed abstract class ExternalChangeDetailsView extends AnchorPane permits EntryAddDetailsView, EntryChangeDetailsView, EntryDeleteDetailsView, GroupChangeDetailsView, MetadataChangeDetailsView, PreambleChangeDetailsView, BibTexStringAddDetailsView, BibTexStringChangeDetailsView, BibTexStringDeleteDetailsView, BibTexStringRenameDetailsView {
+}
diff --git a/src/main/java/org/jabref/gui/collab/ExternalChangeDetailsViewFactory.java b/src/main/java/org/jabref/gui/collab/ExternalChangeDetailsViewFactory.java
new file mode 100644
index 00000000000..1935d72a898
--- /dev/null
+++ b/src/main/java/org/jabref/gui/collab/ExternalChangeDetailsViewFactory.java
@@ -0,0 +1,69 @@
+package org.jabref.gui.collab;
+
+import org.jabref.gui.DialogService;
+import org.jabref.gui.StateManager;
+import org.jabref.gui.collab.entryadd.EntryAdd;
+import org.jabref.gui.collab.entryadd.EntryAddDetailsView;
+import org.jabref.gui.collab.entrychange.EntryChange;
+import org.jabref.gui.collab.entrychange.EntryChangeDetailsView;
+import org.jabref.gui.collab.entrydelete.EntryDelete;
+import org.jabref.gui.collab.entrydelete.EntryDeleteDetailsView;
+import org.jabref.gui.collab.groupchange.GroupChange;
+import org.jabref.gui.collab.groupchange.GroupChangeDetailsView;
+import org.jabref.gui.collab.metedatachange.MetadataChange;
+import org.jabref.gui.collab.metedatachange.MetadataChangeDetailsView;
+import org.jabref.gui.collab.preamblechange.PreambleChange;
+import org.jabref.gui.collab.preamblechange.PreambleChangeDetailsView;
+import org.jabref.gui.collab.stringadd.BibTexStringAdd;
+import org.jabref.gui.collab.stringadd.BibTexStringAddDetailsView;
+import org.jabref.gui.collab.stringchange.BibTexStringChange;
+import org.jabref.gui.collab.stringchange.BibTexStringChangeDetailsView;
+import org.jabref.gui.collab.stringdelete.BibTexStringDelete;
+import org.jabref.gui.collab.stringdelete.BibTexStringDeleteDetailsView;
+import org.jabref.gui.collab.stringrename.BibTexStringRename;
+import org.jabref.gui.collab.stringrename.BibTexStringRenameDetailsView;
+import org.jabref.gui.theme.ThemeManager;
+import org.jabref.model.database.BibDatabaseContext;
+import org.jabref.preferences.PreferencesService;
+
+public class ExternalChangeDetailsViewFactory {
+ private final BibDatabaseContext databaseContext;
+ private final DialogService dialogService;
+ private final StateManager stateManager;
+ private final ThemeManager themeManager;
+ private final PreferencesService preferencesService;
+
+ public ExternalChangeDetailsViewFactory(BibDatabaseContext databaseContext, DialogService dialogService, StateManager stateManager, ThemeManager themeManager, PreferencesService preferencesService) {
+ this.databaseContext = databaseContext;
+ this.dialogService = dialogService;
+ this.stateManager = stateManager;
+ this.themeManager = themeManager;
+ this.preferencesService = preferencesService;
+ }
+
+ public ExternalChangeDetailsView create(ExternalChange externalChange) {
+ // TODO: Use Pattern Matching for switch once it's out of preview
+ if (externalChange instanceof EntryChange entryChange) {
+ return new EntryChangeDetailsView(entryChange, databaseContext, dialogService, stateManager, themeManager, preferencesService);
+ } else if (externalChange instanceof EntryAdd entryAdd) {
+ return new EntryAddDetailsView(entryAdd, databaseContext, dialogService, stateManager, themeManager, preferencesService);
+ } else if (externalChange instanceof EntryDelete entryDelete) {
+ return new EntryDeleteDetailsView(entryDelete, databaseContext, dialogService, stateManager, themeManager, preferencesService);
+ } else if (externalChange instanceof BibTexStringAdd stringAdd) {
+ return new BibTexStringAddDetailsView(stringAdd);
+ } else if (externalChange instanceof BibTexStringDelete stringDelete) {
+ return new BibTexStringDeleteDetailsView(stringDelete);
+ } else if (externalChange instanceof BibTexStringChange stringChange) {
+ return new BibTexStringChangeDetailsView(stringChange);
+ } else if (externalChange instanceof BibTexStringRename stringRename) {
+ return new BibTexStringRenameDetailsView(stringRename);
+ } else if (externalChange instanceof MetadataChange metadataChange) {
+ return new MetadataChangeDetailsView(metadataChange, preferencesService);
+ } else if (externalChange instanceof GroupChange groupChange) {
+ return new GroupChangeDetailsView(groupChange);
+ } else if (externalChange instanceof PreambleChange preambleChange) {
+ return new PreambleChangeDetailsView(preambleChange);
+ }
+ throw new UnsupportedOperationException("Cannot preview the given change: " + externalChange.getName());
+ }
+}
diff --git a/src/main/java/org/jabref/gui/collab/ExternalChangeResolver.java b/src/main/java/org/jabref/gui/collab/ExternalChangeResolver.java
new file mode 100644
index 00000000000..17b53e704d3
--- /dev/null
+++ b/src/main/java/org/jabref/gui/collab/ExternalChangeResolver.java
@@ -0,0 +1,16 @@
+package org.jabref.gui.collab;
+
+import java.util.Optional;
+
+import org.jabref.gui.DialogService;
+import org.jabref.gui.collab.entrychange.EntryChangeResolver;
+
+public sealed abstract class ExternalChangeResolver permits EntryChangeResolver {
+ protected final DialogService dialogService;
+
+ protected ExternalChangeResolver(DialogService dialogService) {
+ this.dialogService = dialogService;
+ }
+
+ public abstract Optional askUserToResolveChange();
+}
diff --git a/src/main/java/org/jabref/gui/collab/ExternalChangeResolverFactory.java b/src/main/java/org/jabref/gui/collab/ExternalChangeResolverFactory.java
new file mode 100644
index 00000000000..937fd486e9d
--- /dev/null
+++ b/src/main/java/org/jabref/gui/collab/ExternalChangeResolverFactory.java
@@ -0,0 +1,26 @@
+package org.jabref.gui.collab;
+
+import java.util.Optional;
+
+import org.jabref.gui.DialogService;
+import org.jabref.gui.collab.entrychange.EntryChange;
+import org.jabref.gui.collab.entrychange.EntryChangeResolver;
+import org.jabref.model.database.BibDatabaseContext;
+
+public class ExternalChangeResolverFactory {
+ private final DialogService dialogService;
+ private final BibDatabaseContext databaseContext;
+
+ public ExternalChangeResolverFactory(DialogService dialogService, BibDatabaseContext databaseContext) {
+ this.dialogService = dialogService;
+ this.databaseContext = databaseContext;
+ }
+
+ public Optional create(ExternalChange change) {
+ if (change instanceof EntryChange entryChange) {
+ return Optional.of(new EntryChangeResolver(entryChange, dialogService, databaseContext));
+ }
+
+ return Optional.empty();
+ }
+}
diff --git a/src/main/java/org/jabref/gui/collab/ExternalChangesResolverDialog.fxml b/src/main/java/org/jabref/gui/collab/ExternalChangesResolverDialog.fxml
new file mode 100644
index 00000000000..27197eb571a
--- /dev/null
+++ b/src/main/java/org/jabref/gui/collab/ExternalChangesResolverDialog.fxml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/java/org/jabref/gui/collab/ExternalChangesResolverDialog.java b/src/main/java/org/jabref/gui/collab/ExternalChangesResolverDialog.java
new file mode 100644
index 00000000000..744bf914f1f
--- /dev/null
+++ b/src/main/java/org/jabref/gui/collab/ExternalChangesResolverDialog.java
@@ -0,0 +1,122 @@
+package org.jabref.gui.collab;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.inject.Inject;
+import javax.swing.undo.UndoManager;
+
+import javafx.application.Platform;
+import javafx.beans.property.SimpleStringProperty;
+import javafx.fxml.FXML;
+import javafx.scene.control.Button;
+import javafx.scene.control.SelectionMode;
+import javafx.scene.control.TableColumn;
+import javafx.scene.control.TableView;
+import javafx.scene.layout.BorderPane;
+
+import org.jabref.gui.DialogService;
+import org.jabref.gui.StateManager;
+import org.jabref.gui.theme.ThemeManager;
+import org.jabref.gui.util.BaseDialog;
+import org.jabref.logic.l10n.Localization;
+import org.jabref.model.database.BibDatabaseContext;
+import org.jabref.preferences.PreferencesService;
+
+import com.airhacks.afterburner.views.ViewLoader;
+import com.tobiasdiez.easybind.EasyBind;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ExternalChangesResolverDialog extends BaseDialog {
+ private final static Logger LOGGER = LoggerFactory.getLogger(ExternalChangesResolverDialog.class);
+ /**
+ * Reconstructing the details view to preview an {@link ExternalChange} every time it's selected is a heavy operation.
+ * It is also useless because changes are static and if the change data is static then the view doesn't have to change
+ * either. This cache is used to ensure that we only create the detail view instance once for each {@link ExternalChange}.
+ */
+ private final Map DETAILS_VIEW_CACHE = new HashMap<>();
+
+ @FXML
+ private TableView changesTableView;
+ @FXML
+ private TableColumn changeName;
+ @FXML
+ private Button askUserToResolveChangeButton;
+ @FXML
+ private BorderPane changeInfoPane;
+
+ private final List changes;
+
+ private ExternalChangesResolverViewModel viewModel;
+
+ private final ExternalChangeDetailsViewFactory externalChangeDetailsViewFactory;
+
+ @Inject private UndoManager undoManager;
+
+ public ExternalChangesResolverDialog(List changes, BibDatabaseContext database, DialogService dialogService, StateManager stateManager, ThemeManager themeManager, PreferencesService preferencesService) {
+ this.changes = changes;
+ this.externalChangeDetailsViewFactory = new ExternalChangeDetailsViewFactory(database, dialogService, stateManager, themeManager, preferencesService);
+
+ this.setTitle(Localization.lang("External Changes Resolver"));
+ ViewLoader.view(this)
+ .load()
+ .setAsDialogPane(this);
+
+ this.setResultConverter(button -> {
+ if (viewModel.areAllChangesResolved()) {
+ LOGGER.info("External changes are resolved successfully");
+ return true;
+ } else {
+ LOGGER.info("External changes aren't resolved");
+ return false;
+ }
+ });
+ }
+
+ @FXML
+ private void initialize() {
+ viewModel = new ExternalChangesResolverViewModel(changes, undoManager);
+
+ changeName.setCellValueFactory(data -> new SimpleStringProperty(data.getValue().getName()));
+ askUserToResolveChangeButton.disableProperty().bind(viewModel.canAskUserToResolveChangeProperty().not());
+
+ changesTableView.setItems(viewModel.getVisibleChanges());
+ // Think twice before setting this to MULTIPLE...
+ changesTableView.getSelectionModel().setSelectionMode(SelectionMode.SINGLE);
+ changesTableView.getSelectionModel().selectFirst();
+
+ viewModel.selectedChangeProperty().bind(changesTableView.getSelectionModel().selectedItemProperty());
+ EasyBind.subscribe(viewModel.selectedChangeProperty(), selectedChange -> {
+ if (selectedChange != null) {
+ ExternalChangeDetailsView detailsView = DETAILS_VIEW_CACHE.computeIfAbsent(selectedChange, externalChangeDetailsViewFactory::create);
+ changeInfoPane.setCenter(detailsView);
+ }
+ });
+
+ EasyBind.subscribe(viewModel.areAllChangesResolvedProperty(), isResolved -> {
+ if (isResolved) {
+ Platform.runLater(viewModel::applyChanges);
+ close();
+ LOGGER.debug("Closing ExternalChangesResolverDialog");
+ }
+ });
+ }
+
+ @FXML
+ public void denyChanges() {
+ viewModel.denyChange();
+ }
+
+ @FXML
+ public void acceptChanges() {
+ viewModel.acceptChange();
+ }
+
+ @FXML
+ public void askUserToResolveChange() {
+ viewModel.getSelectedChange().flatMap(ExternalChange::getExternalChangeResolver)
+ .flatMap(ExternalChangeResolver::askUserToResolveChange).ifPresent(viewModel::acceptMergedChange);
+ }
+}
diff --git a/src/main/java/org/jabref/gui/collab/ExternalChangesResolverViewModel.java b/src/main/java/org/jabref/gui/collab/ExternalChangesResolverViewModel.java
new file mode 100644
index 00000000000..efa06a6dd06
--- /dev/null
+++ b/src/main/java/org/jabref/gui/collab/ExternalChangesResolverViewModel.java
@@ -0,0 +1,107 @@
+package org.jabref.gui.collab;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+
+import javax.swing.undo.UndoManager;
+
+import javafx.beans.binding.Bindings;
+import javafx.beans.binding.BooleanBinding;
+import javafx.beans.property.ObjectProperty;
+import javafx.beans.property.SimpleObjectProperty;
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableList;
+
+import org.jabref.gui.AbstractViewModel;
+import org.jabref.gui.undo.NamedCompound;
+import org.jabref.logic.l10n.Localization;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ExternalChangesResolverViewModel extends AbstractViewModel {
+
+ private final static Logger LOGGER = LoggerFactory.getLogger(ExternalChangesResolverViewModel.class);
+
+ private final NamedCompound ce = new NamedCompound(Localization.lang("Merged external changes"));
+ private final ObservableList visibleChanges = FXCollections.observableArrayList();
+
+ /**
+ * Because visible changes list will be bound to the UI, certain changes can be removed. This list is used to keep
+ * track of changes even when they're removed from the UI.
+ */
+ private final ObservableList changes = FXCollections.observableArrayList();
+
+ private final ObjectProperty selectedChange = new SimpleObjectProperty<>();
+
+ private final BooleanBinding areAllChangesResolved;
+
+ private final BooleanBinding canAskUserToResolveChange;
+
+ private final UndoManager undoManager;
+
+ public ExternalChangesResolverViewModel(List externalChanges, UndoManager undoManager) {
+ Objects.requireNonNull(externalChanges);
+ assert !externalChanges.isEmpty();
+
+ this.visibleChanges.addAll(externalChanges);
+ this.changes.addAll(externalChanges);
+ this.undoManager = undoManager;
+
+ areAllChangesResolved = Bindings.createBooleanBinding(visibleChanges::isEmpty, visibleChanges);
+ canAskUserToResolveChange = Bindings.createBooleanBinding(() -> selectedChange.isNotNull().get() && selectedChange.get().getExternalChangeResolver().isPresent(), selectedChange);
+ }
+
+ public ObservableList getVisibleChanges() {
+ return visibleChanges;
+ }
+
+ public ObjectProperty selectedChangeProperty() {
+ return selectedChange;
+ }
+
+ public Optional getSelectedChange() {
+ return Optional.ofNullable(selectedChangeProperty().get());
+ }
+
+ public BooleanBinding areAllChangesResolvedProperty() {
+ return areAllChangesResolved;
+ }
+
+ public boolean areAllChangesResolved() {
+ return areAllChangesResolvedProperty().get();
+ }
+
+ public BooleanBinding canAskUserToResolveChangeProperty() {
+ return canAskUserToResolveChange;
+ }
+
+ public void acceptChange() {
+ getSelectedChange().ifPresent(selectedChange -> {
+ selectedChange.accept();
+ getVisibleChanges().remove(selectedChange);
+ });
+ }
+
+ public void denyChange() {
+ getSelectedChange().ifPresent(getVisibleChanges()::remove);
+ }
+
+ public void acceptMergedChange(ExternalChange externalChange) {
+ Objects.requireNonNull(externalChange);
+
+ getSelectedChange().ifPresent(oldChange -> {
+ changes.remove(oldChange);
+ changes.add(externalChange);
+ getVisibleChanges().remove(oldChange);
+ externalChange.accept();
+ });
+ }
+
+ public void applyChanges() {
+ changes.stream().filter(ExternalChange::isAccepted).forEach(change -> change.applyChange(ce));
+ ce.end();
+ undoManager.addEdit(ce);
+ }
+}
diff --git a/src/main/java/org/jabref/gui/collab/GroupChangeViewModel.java b/src/main/java/org/jabref/gui/collab/GroupChangeViewModel.java
deleted file mode 100644
index 92a08c39884..00000000000
--- a/src/main/java/org/jabref/gui/collab/GroupChangeViewModel.java
+++ /dev/null
@@ -1,57 +0,0 @@
-package org.jabref.gui.collab;
-
-import javafx.scene.Node;
-import javafx.scene.control.Label;
-
-import org.jabref.gui.groups.GroupTreeNodeViewModel;
-import org.jabref.gui.groups.UndoableModifySubtree;
-import org.jabref.gui.undo.NamedCompound;
-import org.jabref.logic.bibtex.comparator.GroupDiff;
-import org.jabref.logic.groups.DefaultGroupsFactory;
-import org.jabref.logic.l10n.Localization;
-import org.jabref.model.database.BibDatabaseContext;
-import org.jabref.model.groups.GroupTreeNode;
-
-class GroupChangeViewModel extends DatabaseChangeViewModel {
-
- private final GroupTreeNode changedGroups;
-
- public GroupChangeViewModel(GroupDiff diff) {
- super(diff.getOriginalGroupRoot() == null ? Localization.lang("Removed all groups") : Localization
- .lang("Modified groups tree"));
- this.changedGroups = diff.getNewGroupRoot();
- }
-
- @Override
- public void makeChange(BibDatabaseContext database, NamedCompound undoEdit) {
- GroupTreeNode root = database.getMetaData().getGroups().orElseGet(() -> {
- GroupTreeNode groupTreeNode = new GroupTreeNode(DefaultGroupsFactory.getAllEntriesGroup());
- database.getMetaData().setGroups(groupTreeNode);
- return groupTreeNode;
- });
-
- final UndoableModifySubtree undo = new UndoableModifySubtree(
- new GroupTreeNodeViewModel(database.getMetaData().getGroups().orElse(null)),
- new GroupTreeNodeViewModel(root), Localization.lang("Modified groups"));
- root.removeAllChildren();
- if (changedGroups == null) {
- // I think setting root to null is not possible
- root.setGroup(DefaultGroupsFactory.getAllEntriesGroup(), false, false, null);
- } else {
- // change root group, even though it'll be AllEntries anyway
- root.setGroup(changedGroups.getGroup(), false, false, null);
- for (GroupTreeNode child : changedGroups.getChildren()) {
- child.copySubtree().moveTo(root);
- }
- }
-
- undoEdit.addEdit(undo);
- }
-
- @Override
- public Node description() {
- return new Label(toString() + '.'
- + (changedGroups == null ? "" : ' ' + Localization
- .lang("Accepting the change replaces the complete groups tree with the externally modified groups tree.")));
- }
-}
diff --git a/src/main/java/org/jabref/gui/collab/MetaDataChangeViewModel.java b/src/main/java/org/jabref/gui/collab/MetaDataChangeViewModel.java
deleted file mode 100644
index a60bccaa6e9..00000000000
--- a/src/main/java/org/jabref/gui/collab/MetaDataChangeViewModel.java
+++ /dev/null
@@ -1,46 +0,0 @@
-package org.jabref.gui.collab;
-
-import javafx.scene.Node;
-import javafx.scene.control.Label;
-import javafx.scene.layout.VBox;
-
-import org.jabref.gui.undo.NamedCompound;
-import org.jabref.logic.bibtex.comparator.MetaDataDiff;
-import org.jabref.logic.l10n.Localization;
-import org.jabref.model.database.BibDatabaseContext;
-import org.jabref.preferences.PreferencesService;
-
-class MetaDataChangeViewModel extends DatabaseChangeViewModel {
-
- private final MetaDataDiff metaDataDiff;
- private final PreferencesService preferences;
-
- public MetaDataChangeViewModel(MetaDataDiff metaDataDiff, PreferencesService preferences) {
- super(Localization.lang("Metadata change"));
- this.metaDataDiff = metaDataDiff;
- this.preferences = preferences;
- }
-
- @Override
- public Node description() {
- VBox container = new VBox(15);
-
- Label header = new Label(Localization.lang("The following metadata changed:"));
- header.getStyleClass().add("sectionHeader");
- container.getChildren().add(header);
-
- for (String change : metaDataDiff.getDifferences(preferences)) {
- container.getChildren().add(new Label(change));
- }
-
- return container;
- }
-
- @Override
- public void makeChange(BibDatabaseContext database, NamedCompound undoEdit) {
- database.setMetaData(metaDataDiff.getNewMetaData());
- // group change is handled by GroupChangeViewModel, so we set the groups root to the original value
- // to prevent any inconsistency
- metaDataDiff.getGroupDifferences().ifPresent(groupDiff -> database.getMetaData().setGroups(groupDiff.getOriginalGroupRoot()));
- }
-}
diff --git a/src/main/java/org/jabref/gui/collab/PreambleChangeViewModel.java b/src/main/java/org/jabref/gui/collab/PreambleChangeViewModel.java
deleted file mode 100644
index b8a17ccb840..00000000000
--- a/src/main/java/org/jabref/gui/collab/PreambleChangeViewModel.java
+++ /dev/null
@@ -1,48 +0,0 @@
-package org.jabref.gui.collab;
-
-import javafx.scene.Node;
-import javafx.scene.control.Label;
-import javafx.scene.layout.VBox;
-
-import org.jabref.gui.undo.NamedCompound;
-import org.jabref.gui.undo.UndoablePreambleChange;
-import org.jabref.logic.bibtex.comparator.PreambleDiff;
-import org.jabref.logic.l10n.Localization;
-import org.jabref.model.database.BibDatabaseContext;
-import org.jabref.model.strings.StringUtil;
-
-class PreambleChangeViewModel extends DatabaseChangeViewModel {
-
- private final PreambleDiff change;
-
- public PreambleChangeViewModel(PreambleDiff change) {
- super(Localization.lang("Changed preamble"));
- this.change = change;
- }
-
- @Override
- public void makeChange(BibDatabaseContext database, NamedCompound undoEdit) {
- database.getDatabase().setPreamble(change.getNewPreamble());
- undoEdit.addEdit(new UndoablePreambleChange(database.getDatabase(), change.getOriginalPreamble(), change.getNewPreamble()));
- }
-
- @Override
- public Node description() {
- VBox container = new VBox();
- Label header = new Label(Localization.lang("Changed preamble"));
- header.getStyleClass().add("sectionHeader");
- container.getChildren().add(header);
-
- if (StringUtil.isNotBlank(change.getOriginalPreamble())) {
- container.getChildren().add(new Label(Localization.lang("Current value") + ": " + change.getOriginalPreamble()));
- }
-
- if (StringUtil.isNotBlank(change.getNewPreamble())) {
- container.getChildren().add(new Label(Localization.lang("Value set externally") + ": " + change.getNewPreamble()));
- } else {
- container.getChildren().add(new Label(Localization.lang("Value cleared externally")));
- }
-
- return container;
- }
-}
diff --git a/src/main/java/org/jabref/gui/collab/StringAddChangeViewModel.java b/src/main/java/org/jabref/gui/collab/StringAddChangeViewModel.java
deleted file mode 100644
index 8ad0c7f4e2b..00000000000
--- a/src/main/java/org/jabref/gui/collab/StringAddChangeViewModel.java
+++ /dev/null
@@ -1,49 +0,0 @@
-package org.jabref.gui.collab;
-
-import javafx.scene.Node;
-import javafx.scene.control.Label;
-import javafx.scene.layout.VBox;
-
-import org.jabref.gui.undo.NamedCompound;
-import org.jabref.gui.undo.UndoableInsertString;
-import org.jabref.logic.l10n.Localization;
-import org.jabref.model.database.BibDatabaseContext;
-import org.jabref.model.database.KeyCollisionException;
-import org.jabref.model.entry.BibtexString;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-class StringAddChangeViewModel extends DatabaseChangeViewModel {
-
- private static final Logger LOGGER = LoggerFactory.getLogger(StringAddChangeViewModel.class);
- private final BibtexString string;
-
- public StringAddChangeViewModel(BibtexString string) {
- super(Localization.lang("Added string") + ": '" + string.getName() + '\'');
- this.string = string;
- }
-
- @Override
- public void makeChange(BibDatabaseContext database, NamedCompound undoEdit) {
- try {
- database.getDatabase().addString(string);
- undoEdit.addEdit(new UndoableInsertString(database.getDatabase(), string));
- } catch (KeyCollisionException ex) {
- LOGGER.warn("Error: could not add string '" + string.getName() + "': " + ex.getMessage(), ex);
- }
- }
-
- @Override
- public Node description() {
- VBox container = new VBox();
- Label header = new Label(Localization.lang("Added string"));
- header.getStyleClass().add("sectionHeader");
- container.getChildren().addAll(
- header,
- new Label(Localization.lang("Label") + ": " + string.getName()),
- new Label(Localization.lang("Content") + ": " + string.getContent())
- );
- return container;
- }
-}
diff --git a/src/main/java/org/jabref/gui/collab/StringChangeViewModel.java b/src/main/java/org/jabref/gui/collab/StringChangeViewModel.java
deleted file mode 100644
index 5747a815a47..00000000000
--- a/src/main/java/org/jabref/gui/collab/StringChangeViewModel.java
+++ /dev/null
@@ -1,53 +0,0 @@
-package org.jabref.gui.collab;
-
-import java.util.Objects;
-
-import javafx.scene.Node;
-import javafx.scene.control.Label;
-import javafx.scene.layout.VBox;
-
-import org.jabref.gui.undo.NamedCompound;
-import org.jabref.gui.undo.UndoableStringChange;
-import org.jabref.logic.l10n.Localization;
-import org.jabref.model.database.BibDatabaseContext;
-import org.jabref.model.entry.BibtexString;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-class StringChangeViewModel extends DatabaseChangeViewModel {
-
- private static final Logger LOGGER = LoggerFactory.getLogger(StringChangeViewModel.class);
- private final BibtexString string;
- private final BibtexString newString;
-
- public StringChangeViewModel(BibtexString string, BibtexString newString) {
- super(Localization.lang("Modified string") + ": '" + string.getName() + '\'');
- this.string = Objects.requireNonNull(string);
- this.newString = Objects.requireNonNull(newString);
- }
-
- @Override
- public void makeChange(BibDatabaseContext database, NamedCompound undoEdit) {
- String currentValue = string.getContent();
- String newValue = newString.getContent();
- string.setContent(newValue);
- undoEdit.addEdit(new UndoableStringChange(string, false, currentValue, newValue));
- }
-
- @Override
- public Node description() {
- VBox container = new VBox();
- Label header = new Label(Localization.lang("Modified string"));
- header.getStyleClass().add("sectionHeader");
- container.getChildren().addAll(
- header,
- new Label(Localization.lang("Label") + ": " + newString.getName()),
- new Label(Localization.lang("Content") + ": " + newString.getContent())
- );
-
- container.getChildren().add(new Label(Localization.lang("Current content") + ": " + string.getContent()));
-
- return container;
- }
-}
diff --git a/src/main/java/org/jabref/gui/collab/StringNameChangeViewModel.java b/src/main/java/org/jabref/gui/collab/StringNameChangeViewModel.java
deleted file mode 100644
index 48a1c473322..00000000000
--- a/src/main/java/org/jabref/gui/collab/StringNameChangeViewModel.java
+++ /dev/null
@@ -1,47 +0,0 @@
-package org.jabref.gui.collab;
-
-import java.util.Objects;
-
-import javafx.scene.Node;
-import javafx.scene.control.Label;
-
-import org.jabref.gui.undo.NamedCompound;
-import org.jabref.gui.undo.UndoableStringChange;
-import org.jabref.logic.l10n.Localization;
-import org.jabref.model.database.BibDatabaseContext;
-import org.jabref.model.entry.BibtexString;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-class StringNameChangeViewModel extends DatabaseChangeViewModel {
-
- private static final Logger LOGGER = LoggerFactory.getLogger(StringNameChangeViewModel.class);
- private final BibtexString string;
- private final BibtexString newString;
-
- public StringNameChangeViewModel(BibtexString string, BibtexString newString) {
- super(Localization.lang("Renamed string") + ": '" + string.getName() + '\'');
- this.string = Objects.requireNonNull(string);
- this.newString = Objects.requireNonNull(newString);
- }
-
- @Override
- public void makeChange(BibDatabaseContext database, NamedCompound undoEdit) {
- if (database.getDatabase().hasStringByName(newString.getName())) {
- // The name to change to is already in the database, so we can't comply.
- LOGGER.info("Cannot rename string '" + string.getName() + "' to '" + newString.getName() + "' because the name "
- + "is already in use.");
- }
-
- String currentName = string.getName();
- String newName = newString.getName();
- string.setName(newName);
- undoEdit.addEdit(new UndoableStringChange(string, true, currentName, newName));
- }
-
- @Override
- public Node description() {
- return new Label(newString.getName() + " : " + string.getContent());
- }
-}
diff --git a/src/main/java/org/jabref/gui/collab/StringRemoveChangeViewModel.java b/src/main/java/org/jabref/gui/collab/StringRemoveChangeViewModel.java
deleted file mode 100644
index 7352de26182..00000000000
--- a/src/main/java/org/jabref/gui/collab/StringRemoveChangeViewModel.java
+++ /dev/null
@@ -1,47 +0,0 @@
-package org.jabref.gui.collab;
-
-import javafx.scene.Node;
-import javafx.scene.control.Label;
-import javafx.scene.layout.VBox;
-
-import org.jabref.gui.undo.NamedCompound;
-import org.jabref.gui.undo.UndoableRemoveString;
-import org.jabref.logic.l10n.Localization;
-import org.jabref.model.database.BibDatabaseContext;
-import org.jabref.model.entry.BibtexString;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-class StringRemoveChangeViewModel extends DatabaseChangeViewModel {
- private static final Logger LOGGER = LoggerFactory.getLogger(StringRemoveChangeViewModel.class);
- private final BibtexString string;
-
- public StringRemoveChangeViewModel(BibtexString string) {
- super(Localization.lang("Removed string") + ": '" + string.getName() + '\'');
- this.string = string;
- }
-
- @Override
- public void makeChange(BibDatabaseContext database, NamedCompound undoEdit) {
- try {
- database.getDatabase().removeString(string.getId());
- undoEdit.addEdit(new UndoableRemoveString(database.getDatabase(), string));
- } catch (Exception ex) {
- LOGGER.warn("Error: could not remove string '" + string.getName() + "': " + ex.getMessage(), ex);
- }
- }
-
- @Override
- public Node description() {
- VBox container = new VBox();
- Label header = new Label(Localization.lang("Removed string"));
- header.getStyleClass().add("sectionHeader");
- container.getChildren().addAll(
- header,
- new Label(Localization.lang("Label") + ": " + string.getName()),
- new Label(Localization.lang("Content") + ": " + string.getContent())
- );
- return container;
- }
-}
diff --git a/src/main/java/org/jabref/gui/collab/entryadd/EntryAdd.java b/src/main/java/org/jabref/gui/collab/entryadd/EntryAdd.java
new file mode 100644
index 00000000000..c9be0167535
--- /dev/null
+++ b/src/main/java/org/jabref/gui/collab/entryadd/EntryAdd.java
@@ -0,0 +1,31 @@
+package org.jabref.gui.collab.entryadd;
+
+import org.jabref.gui.collab.ExternalChange;
+import org.jabref.gui.collab.ExternalChangeResolverFactory;
+import org.jabref.gui.undo.NamedCompound;
+import org.jabref.gui.undo.UndoableInsertEntries;
+import org.jabref.logic.l10n.Localization;
+import org.jabref.model.database.BibDatabaseContext;
+import org.jabref.model.entry.BibEntry;
+
+public final class EntryAdd extends ExternalChange {
+ private final BibEntry addedEntry;
+
+ public EntryAdd(BibEntry addedEntry, BibDatabaseContext databaseContext, ExternalChangeResolverFactory externalChangeResolverFactory) {
+ super(databaseContext, externalChangeResolverFactory);
+ this.addedEntry = addedEntry;
+ setChangeName(addedEntry.getCitationKey()
+ .map(key -> Localization.lang("Added entry '%0'", key))
+ .orElse(Localization.lang("Added entry")));
+ }
+
+ @Override
+ public void applyChange(NamedCompound undoEdit) {
+ databaseContext.getDatabase().insertEntry(addedEntry);
+ undoEdit.addEdit(new UndoableInsertEntries(databaseContext.getDatabase(), addedEntry));
+ }
+
+ public BibEntry getAddedEntry() {
+ return addedEntry;
+ }
+}
diff --git a/src/main/java/org/jabref/gui/collab/entryadd/EntryAddDetailsView.java b/src/main/java/org/jabref/gui/collab/entryadd/EntryAddDetailsView.java
new file mode 100644
index 00000000000..a967d2a1381
--- /dev/null
+++ b/src/main/java/org/jabref/gui/collab/entryadd/EntryAddDetailsView.java
@@ -0,0 +1,25 @@
+package org.jabref.gui.collab.entryadd;
+
+import org.jabref.gui.DialogService;
+import org.jabref.gui.StateManager;
+import org.jabref.gui.collab.ExternalChangeDetailsView;
+import org.jabref.gui.preview.PreviewViewer;
+import org.jabref.gui.theme.ThemeManager;
+import org.jabref.model.database.BibDatabaseContext;
+import org.jabref.preferences.PreferencesService;
+
+public final class EntryAddDetailsView extends ExternalChangeDetailsView {
+
+ public EntryAddDetailsView(EntryAdd entryAdd, BibDatabaseContext bibDatabaseContext, DialogService dialogService, StateManager stateManager, ThemeManager themeManager, PreferencesService preferencesService) {
+ PreviewViewer previewViewer = new PreviewViewer(bibDatabaseContext, dialogService, stateManager, themeManager);
+ previewViewer.setLayout(preferencesService.getPreviewPreferences().getSelectedPreviewLayout());
+ previewViewer.setEntry(entryAdd.getAddedEntry());
+
+ setLeftAnchor(previewViewer, 8d);
+ setTopAnchor(previewViewer, 8d);
+ setRightAnchor(previewViewer, 8d);
+ setBottomAnchor(previewViewer, 8d);
+
+ getChildren().setAll(previewViewer);
+ }
+}
diff --git a/src/main/java/org/jabref/gui/collab/entrychange/EntryChange.java b/src/main/java/org/jabref/gui/collab/entrychange/EntryChange.java
new file mode 100644
index 00000000000..34a70872c50
--- /dev/null
+++ b/src/main/java/org/jabref/gui/collab/entrychange/EntryChange.java
@@ -0,0 +1,49 @@
+package org.jabref.gui.collab.entrychange;
+
+import javax.swing.undo.CompoundEdit;
+
+import org.jabref.gui.collab.ExternalChange;
+import org.jabref.gui.collab.ExternalChangeResolverFactory;
+import org.jabref.gui.undo.NamedCompound;
+import org.jabref.gui.undo.UndoableInsertEntries;
+import org.jabref.gui.undo.UndoableRemoveEntries;
+import org.jabref.logic.l10n.Localization;
+import org.jabref.model.database.BibDatabaseContext;
+import org.jabref.model.entry.BibEntry;
+
+public final class EntryChange extends ExternalChange {
+ private final BibEntry oldEntry;
+ private final BibEntry newEntry;
+
+ public EntryChange(BibEntry oldEntry, BibEntry newEntry, BibDatabaseContext databaseContext, ExternalChangeResolverFactory externalChangeResolverFactory) {
+ super(databaseContext, externalChangeResolverFactory);
+ this.oldEntry = oldEntry;
+ this.newEntry = newEntry;
+ setChangeName(oldEntry.getCitationKey().map(key -> Localization.lang("Modified entry '%0'", key))
+ .orElse(Localization.lang("Modified entry")));
+ }
+
+ public EntryChange(BibEntry oldEntry, BibEntry newEntry, BibDatabaseContext databaseContext) {
+ this(oldEntry, newEntry, databaseContext, null);
+ }
+
+ public BibEntry getOldEntry() {
+ return oldEntry;
+ }
+
+ public BibEntry getNewEntry() {
+ return newEntry;
+ }
+
+ @Override
+ public void applyChange(NamedCompound undoEdit) {
+ databaseContext.getDatabase().removeEntry(oldEntry);
+ databaseContext.getDatabase().insertEntry(newEntry);
+ CompoundEdit changeEntryEdit = new CompoundEdit();
+ changeEntryEdit.addEdit(new UndoableRemoveEntries(databaseContext.getDatabase(), oldEntry));
+ changeEntryEdit.addEdit(new UndoableInsertEntries(databaseContext.getDatabase(), newEntry));
+ changeEntryEdit.end();
+
+ undoEdit.addEdit(changeEntryEdit);
+ }
+}
diff --git a/src/main/java/org/jabref/gui/collab/entrychange/EntryChangeDetailsView.java b/src/main/java/org/jabref/gui/collab/entrychange/EntryChangeDetailsView.java
new file mode 100644
index 00000000000..18286e5fc99
--- /dev/null
+++ b/src/main/java/org/jabref/gui/collab/entrychange/EntryChangeDetailsView.java
@@ -0,0 +1,24 @@
+package org.jabref.gui.collab.entrychange;
+
+import org.jabref.gui.DialogService;
+import org.jabref.gui.StateManager;
+import org.jabref.gui.collab.ExternalChangeDetailsView;
+import org.jabref.gui.preview.PreviewViewer;
+import org.jabref.gui.theme.ThemeManager;
+import org.jabref.model.database.BibDatabaseContext;
+import org.jabref.preferences.PreferencesService;
+
+public final class EntryChangeDetailsView extends ExternalChangeDetailsView {
+ public EntryChangeDetailsView(EntryChange entryChange, BibDatabaseContext bibDatabaseContext, DialogService dialogService, StateManager stateManager, ThemeManager themeManager, PreferencesService preferencesService) {
+ PreviewViewer previewViewer = new PreviewViewer(bibDatabaseContext, dialogService, stateManager, themeManager);
+ previewViewer.setLayout(preferencesService.getPreviewPreferences().getSelectedPreviewLayout());
+ previewViewer.setEntry(entryChange.getNewEntry());
+
+ setLeftAnchor(previewViewer, 8d);
+ setTopAnchor(previewViewer, 8d);
+ setRightAnchor(previewViewer, 8d);
+ setBottomAnchor(previewViewer, 8d);
+
+ getChildren().setAll(previewViewer);
+ }
+}
diff --git a/src/main/java/org/jabref/gui/collab/entrychange/EntryChangeResolver.java b/src/main/java/org/jabref/gui/collab/entrychange/EntryChangeResolver.java
new file mode 100644
index 00000000000..dfb3a1a1470
--- /dev/null
+++ b/src/main/java/org/jabref/gui/collab/entrychange/EntryChangeResolver.java
@@ -0,0 +1,44 @@
+package org.jabref.gui.collab.entrychange;
+
+import java.util.Optional;
+
+import org.jabref.gui.DialogService;
+import org.jabref.gui.collab.ExternalChange;
+import org.jabref.gui.collab.ExternalChangeResolver;
+import org.jabref.gui.mergeentries.EntriesMergeResult;
+import org.jabref.gui.mergeentries.MergeEntriesDialog;
+import org.jabref.gui.mergeentries.newmergedialog.ShowDiffConfig;
+import org.jabref.gui.mergeentries.newmergedialog.diffhighlighter.DiffHighlighter;
+import org.jabref.gui.mergeentries.newmergedialog.toolbar.ThreeWayMergeToolbar;
+import org.jabref.logic.l10n.Localization;
+import org.jabref.model.database.BibDatabaseContext;
+
+public final class EntryChangeResolver extends ExternalChangeResolver {
+ private final EntryChange entryChange;
+ private final BibDatabaseContext databaseContext;
+
+ public EntryChangeResolver(EntryChange entryChange, DialogService dialogService, BibDatabaseContext databaseContext) {
+ super(dialogService);
+ this.entryChange = entryChange;
+ this.databaseContext = databaseContext;
+ }
+
+ @Override
+ public Optional askUserToResolveChange() {
+ MergeEntriesDialog mergeEntriesDialog = new MergeEntriesDialog(entryChange.getOldEntry(), entryChange.getNewEntry());
+ mergeEntriesDialog.setLeftHeaderText(Localization.lang("On JabRef"));
+ mergeEntriesDialog.setRightHeaderText(Localization.lang("On disk"));
+ mergeEntriesDialog.configureDiff(new ShowDiffConfig(ThreeWayMergeToolbar.DiffView.SPLIT, DiffHighlighter.DiffMethod.WORDS));
+
+ return dialogService.showCustomDialogAndWait(mergeEntriesDialog)
+ .map(this::mapMergeResultToExternalChange);
+ }
+
+ private EntryChange mapMergeResultToExternalChange(EntriesMergeResult entriesMergeResult) {
+ return new EntryChange(
+ entryChange.getOldEntry(),
+ entriesMergeResult.mergedEntry(),
+ databaseContext
+ );
+ }
+}
diff --git a/src/main/java/org/jabref/gui/collab/entrydelete/EntryDelete.java b/src/main/java/org/jabref/gui/collab/entrydelete/EntryDelete.java
new file mode 100644
index 00000000000..13913a7974d
--- /dev/null
+++ b/src/main/java/org/jabref/gui/collab/entrydelete/EntryDelete.java
@@ -0,0 +1,31 @@
+package org.jabref.gui.collab.entrydelete;
+
+import org.jabref.gui.collab.ExternalChange;
+import org.jabref.gui.collab.ExternalChangeResolverFactory;
+import org.jabref.gui.undo.NamedCompound;
+import org.jabref.gui.undo.UndoableRemoveEntries;
+import org.jabref.logic.l10n.Localization;
+import org.jabref.model.database.BibDatabaseContext;
+import org.jabref.model.entry.BibEntry;
+
+public final class EntryDelete extends ExternalChange {
+ private final BibEntry deletedEntry;
+
+ public EntryDelete(BibEntry deletedEntry, BibDatabaseContext databaseContext, ExternalChangeResolverFactory externalChangeResolverFactory) {
+ super(databaseContext, externalChangeResolverFactory);
+ this.deletedEntry = deletedEntry;
+ setChangeName(deletedEntry.getCitationKey()
+ .map(key -> Localization.lang("Deleted entry '%0'", key))
+ .orElse(Localization.lang("Deleted entry")));
+ }
+
+ @Override
+ public void applyChange(NamedCompound undoEdit) {
+ databaseContext.getDatabase().removeEntry(deletedEntry);
+ undoEdit.addEdit(new UndoableRemoveEntries(databaseContext.getDatabase(), deletedEntry));
+ }
+
+ public BibEntry getDeletedEntry() {
+ return deletedEntry;
+ }
+}
diff --git a/src/main/java/org/jabref/gui/collab/entrydelete/EntryDeleteDetailsView.java b/src/main/java/org/jabref/gui/collab/entrydelete/EntryDeleteDetailsView.java
new file mode 100644
index 00000000000..e60051af592
--- /dev/null
+++ b/src/main/java/org/jabref/gui/collab/entrydelete/EntryDeleteDetailsView.java
@@ -0,0 +1,25 @@
+package org.jabref.gui.collab.entrydelete;
+
+import org.jabref.gui.DialogService;
+import org.jabref.gui.StateManager;
+import org.jabref.gui.collab.ExternalChangeDetailsView;
+import org.jabref.gui.preview.PreviewViewer;
+import org.jabref.gui.theme.ThemeManager;
+import org.jabref.model.database.BibDatabaseContext;
+import org.jabref.preferences.PreferencesService;
+
+public final class EntryDeleteDetailsView extends ExternalChangeDetailsView {
+
+ public EntryDeleteDetailsView(EntryDelete entryDelete, BibDatabaseContext bibDatabaseContext, DialogService dialogService, StateManager stateManager, ThemeManager themeManager, PreferencesService preferencesService) {
+ PreviewViewer previewViewer = new PreviewViewer(bibDatabaseContext, dialogService, stateManager, themeManager);
+ previewViewer.setLayout(preferencesService.getPreviewPreferences().getSelectedPreviewLayout());
+ previewViewer.setEntry(entryDelete.getDeletedEntry());
+
+ setLeftAnchor(previewViewer, 8d);
+ setTopAnchor(previewViewer, 8d);
+ setRightAnchor(previewViewer, 8d);
+ setBottomAnchor(previewViewer, 8d);
+
+ getChildren().setAll(previewViewer);
+ }
+}
diff --git a/src/main/java/org/jabref/gui/collab/groupchange/GroupChange.java b/src/main/java/org/jabref/gui/collab/groupchange/GroupChange.java
new file mode 100644
index 00000000000..e6259766614
--- /dev/null
+++ b/src/main/java/org/jabref/gui/collab/groupchange/GroupChange.java
@@ -0,0 +1,56 @@
+package org.jabref.gui.collab.groupchange;
+
+import org.jabref.gui.collab.ExternalChange;
+import org.jabref.gui.collab.ExternalChangeResolverFactory;
+import org.jabref.gui.groups.GroupTreeNodeViewModel;
+import org.jabref.gui.groups.UndoableModifySubtree;
+import org.jabref.gui.undo.NamedCompound;
+import org.jabref.logic.bibtex.comparator.GroupDiff;
+import org.jabref.logic.groups.DefaultGroupsFactory;
+import org.jabref.logic.l10n.Localization;
+import org.jabref.model.database.BibDatabaseContext;
+import org.jabref.model.groups.GroupTreeNode;
+
+public final class GroupChange extends ExternalChange {
+ private final GroupDiff groupDiff;
+
+ public GroupChange(GroupDiff groupDiff, BibDatabaseContext databaseContext, ExternalChangeResolverFactory externalChangeResolverFactory) {
+ super(databaseContext, externalChangeResolverFactory);
+ this.groupDiff = groupDiff;
+ setChangeName(groupDiff.getOriginalGroupRoot() == null ? Localization.lang("Removed all groups") : Localization
+ .lang("Modified groups tree"));
+ }
+
+ @Override
+ public void applyChange(NamedCompound undoEdit) {
+ GroupTreeNode oldRoot = groupDiff.getOriginalGroupRoot();
+ GroupTreeNode newRoot = groupDiff.getNewGroupRoot();
+
+ GroupTreeNode root = databaseContext.getMetaData().getGroups().orElseGet(() -> {
+ GroupTreeNode groupTreeNode = new GroupTreeNode(DefaultGroupsFactory.getAllEntriesGroup());
+ databaseContext.getMetaData().setGroups(groupTreeNode);
+ return groupTreeNode;
+ });
+
+ final UndoableModifySubtree undo = new UndoableModifySubtree(
+ new GroupTreeNodeViewModel(databaseContext.getMetaData().getGroups().orElse(null)),
+ new GroupTreeNodeViewModel(root), Localization.lang("Modified groups"));
+ root.removeAllChildren();
+ if (newRoot == null) {
+ // I think setting root to null is not possible
+ root.setGroup(DefaultGroupsFactory.getAllEntriesGroup(), false, false, null);
+ } else {
+ // change root group, even though it'll be AllEntries anyway
+ root.setGroup(newRoot.getGroup(), false, false, null);
+ for (GroupTreeNode child : newRoot.getChildren()) {
+ child.copySubtree().moveTo(root);
+ }
+ }
+
+ undoEdit.addEdit(undo);
+ }
+
+ public GroupDiff getGroupDiff() {
+ return groupDiff;
+ }
+}
diff --git a/src/main/java/org/jabref/gui/collab/groupchange/GroupChangeDetailsView.java b/src/main/java/org/jabref/gui/collab/groupchange/GroupChangeDetailsView.java
new file mode 100644
index 00000000000..0c3f6eda15a
--- /dev/null
+++ b/src/main/java/org/jabref/gui/collab/groupchange/GroupChangeDetailsView.java
@@ -0,0 +1,25 @@
+package org.jabref.gui.collab.groupchange;
+
+import javafx.scene.control.Label;
+
+import org.jabref.gui.collab.ExternalChangeDetailsView;
+import org.jabref.logic.l10n.Localization;
+
+public final class GroupChangeDetailsView extends ExternalChangeDetailsView {
+
+ public GroupChangeDetailsView(GroupChange groupChange) {
+ String labelValue = "";
+ if (groupChange.getGroupDiff().getNewGroupRoot() == null) {
+ labelValue = groupChange.getName() + '.';
+ } else {
+ labelValue = Localization.lang("%0. Accepting the change replaces the complete groups tree with the externally modified groups tree.", groupChange.getName());
+ }
+ Label label = new Label(labelValue);
+ setLeftAnchor(label, 8d);
+ setTopAnchor(label, 8d);
+ setRightAnchor(label, 8d);
+ setBottomAnchor(label, 8d);
+
+ getChildren().setAll(label);
+ }
+}
diff --git a/src/main/java/org/jabref/gui/collab/metedatachange/MetadataChange.java b/src/main/java/org/jabref/gui/collab/metedatachange/MetadataChange.java
new file mode 100644
index 00000000000..ae1340df6c6
--- /dev/null
+++ b/src/main/java/org/jabref/gui/collab/metedatachange/MetadataChange.java
@@ -0,0 +1,32 @@
+package org.jabref.gui.collab.metedatachange;
+
+import org.jabref.gui.collab.ExternalChange;
+import org.jabref.gui.collab.ExternalChangeResolverFactory;
+import org.jabref.gui.undo.NamedCompound;
+import org.jabref.logic.bibtex.comparator.MetaDataDiff;
+import org.jabref.logic.l10n.Localization;
+import org.jabref.model.database.BibDatabaseContext;
+
+public final class MetadataChange extends ExternalChange {
+ private final MetaDataDiff metaDataDiff;
+
+ public MetadataChange(MetaDataDiff metaDataDiff, BibDatabaseContext databaseContext, ExternalChangeResolverFactory externalChangeResolverFactory) {
+ super(databaseContext, externalChangeResolverFactory);
+ this.metaDataDiff = metaDataDiff;
+ setChangeName(Localization.lang("Metadata change"));
+ }
+
+ @Override
+ public void applyChange(NamedCompound undoEdit) {
+ // TODO: Metadata edit should be undoable
+ databaseContext.setMetaData(metaDataDiff.getNewMetaData());
+ // group change is handled by GroupChange, so we set the groups root to the original value
+ // to prevent any inconsistency
+ metaDataDiff.getGroupDifferences()
+ .ifPresent(groupDiff -> databaseContext.getMetaData().setGroups(groupDiff.getOriginalGroupRoot()));
+ }
+
+ public MetaDataDiff getMetaDataDiff() {
+ return metaDataDiff;
+ }
+}
diff --git a/src/main/java/org/jabref/gui/collab/metedatachange/MetadataChangeDetailsView.java b/src/main/java/org/jabref/gui/collab/metedatachange/MetadataChangeDetailsView.java
new file mode 100644
index 00000000000..43023cab438
--- /dev/null
+++ b/src/main/java/org/jabref/gui/collab/metedatachange/MetadataChangeDetailsView.java
@@ -0,0 +1,30 @@
+package org.jabref.gui.collab.metedatachange;
+
+import javafx.scene.control.Label;
+import javafx.scene.layout.VBox;
+
+import org.jabref.gui.collab.ExternalChangeDetailsView;
+import org.jabref.logic.l10n.Localization;
+import org.jabref.preferences.PreferencesService;
+
+public final class MetadataChangeDetailsView extends ExternalChangeDetailsView {
+
+ public MetadataChangeDetailsView(MetadataChange metadataChange, PreferencesService preferencesService) {
+ VBox container = new VBox(15);
+
+ Label header = new Label(Localization.lang("The following metadata changed:"));
+ header.getStyleClass().add("sectionHeader");
+ container.getChildren().add(header);
+
+ for (String change : metadataChange.getMetaDataDiff().getDifferences(preferencesService)) {
+ container.getChildren().add(new Label(change));
+ }
+
+ setLeftAnchor(container, 8d);
+ setTopAnchor(container, 8d);
+ setRightAnchor(container, 8d);
+ setBottomAnchor(container, 8d);
+
+ getChildren().setAll(container);
+ }
+}
diff --git a/src/main/java/org/jabref/gui/collab/preamblechange/PreambleChange.java b/src/main/java/org/jabref/gui/collab/preamblechange/PreambleChange.java
new file mode 100644
index 00000000000..50922e67d35
--- /dev/null
+++ b/src/main/java/org/jabref/gui/collab/preamblechange/PreambleChange.java
@@ -0,0 +1,35 @@
+package org.jabref.gui.collab.preamblechange;
+
+import org.jabref.gui.collab.ExternalChange;
+import org.jabref.gui.collab.ExternalChangeResolverFactory;
+import org.jabref.gui.undo.NamedCompound;
+import org.jabref.gui.undo.UndoablePreambleChange;
+import org.jabref.logic.bibtex.comparator.PreambleDiff;
+import org.jabref.logic.l10n.Localization;
+import org.jabref.model.database.BibDatabaseContext;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public final class PreambleChange extends ExternalChange {
+ private static final Logger LOGGER = LoggerFactory.getLogger(PreambleChange.class);
+
+ private final PreambleDiff preambleDiff;
+
+ public PreambleChange(PreambleDiff preambleDiff, BibDatabaseContext databaseContext, ExternalChangeResolverFactory externalChangeResolverFactory) {
+ super(databaseContext, externalChangeResolverFactory);
+ this.preambleDiff = preambleDiff;
+
+ setChangeName(Localization.lang("Changed preamble"));
+ }
+
+ @Override
+ public void applyChange(NamedCompound undoEdit) {
+ databaseContext.getDatabase().setPreamble(preambleDiff.getNewPreamble());
+ undoEdit.addEdit(new UndoablePreambleChange(databaseContext.getDatabase(), preambleDiff.getOriginalPreamble(), preambleDiff.getNewPreamble()));
+ }
+
+ public PreambleDiff getPreambleDiff() {
+ return preambleDiff;
+ }
+}
diff --git a/src/main/java/org/jabref/gui/collab/preamblechange/PreambleChangeDetailsView.java b/src/main/java/org/jabref/gui/collab/preamblechange/PreambleChangeDetailsView.java
new file mode 100644
index 00000000000..5d621403478
--- /dev/null
+++ b/src/main/java/org/jabref/gui/collab/preamblechange/PreambleChangeDetailsView.java
@@ -0,0 +1,37 @@
+package org.jabref.gui.collab.preamblechange;
+
+import javafx.scene.control.Label;
+import javafx.scene.layout.VBox;
+
+import org.jabref.gui.collab.ExternalChangeDetailsView;
+import org.jabref.logic.bibtex.comparator.PreambleDiff;
+import org.jabref.logic.l10n.Localization;
+import org.jabref.model.strings.StringUtil;
+
+public final class PreambleChangeDetailsView extends ExternalChangeDetailsView {
+
+ public PreambleChangeDetailsView(PreambleChange preambleChange) {
+ PreambleDiff preambleDiff = preambleChange.getPreambleDiff();
+
+ VBox container = new VBox();
+ Label header = new Label(Localization.lang("Changed preamble"));
+ header.getStyleClass().add("sectionHeader");
+ container.getChildren().add(header);
+
+ if (StringUtil.isNotBlank(preambleDiff.getOriginalPreamble())) {
+ container.getChildren().add(new Label(Localization.lang("Current value: %0", preambleDiff.getOriginalPreamble())));
+ }
+
+ if (StringUtil.isNotBlank(preambleDiff.getNewPreamble())) {
+ container.getChildren().add(new Label(Localization.lang("Value set externally: %0", preambleDiff.getNewPreamble())));
+ } else {
+ container.getChildren().add(new Label(Localization.lang("Value cleared externally")));
+ }
+ setLeftAnchor(container, 8d);
+ setTopAnchor(container, 8d);
+ setRightAnchor(container, 8d);
+ setBottomAnchor(container, 8d);
+
+ getChildren().setAll(container);
+ }
+}
diff --git a/src/main/java/org/jabref/gui/collab/stringadd/BibTexStringAdd.java b/src/main/java/org/jabref/gui/collab/stringadd/BibTexStringAdd.java
new file mode 100644
index 00000000000..38a2a8a0168
--- /dev/null
+++ b/src/main/java/org/jabref/gui/collab/stringadd/BibTexStringAdd.java
@@ -0,0 +1,39 @@
+package org.jabref.gui.collab.stringadd;
+
+import org.jabref.gui.collab.ExternalChange;
+import org.jabref.gui.collab.ExternalChangeResolverFactory;
+import org.jabref.gui.undo.NamedCompound;
+import org.jabref.gui.undo.UndoableInsertString;
+import org.jabref.logic.l10n.Localization;
+import org.jabref.model.database.BibDatabaseContext;
+import org.jabref.model.database.KeyCollisionException;
+import org.jabref.model.entry.BibtexString;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public final class BibTexStringAdd extends ExternalChange {
+ private static final Logger LOGGER = LoggerFactory.getLogger(BibTexStringAdd.class);
+
+ private final BibtexString addedString;
+
+ public BibTexStringAdd(BibtexString addedString, BibDatabaseContext databaseContext, ExternalChangeResolverFactory externalChangeResolverFactory) {
+ super(databaseContext, externalChangeResolverFactory);
+ this.addedString = addedString;
+ setChangeName(Localization.lang("Added string: '%0'", addedString.getName()));
+ }
+
+ @Override
+ public void applyChange(NamedCompound undoEdit) {
+ try {
+ databaseContext.getDatabase().addString(addedString);
+ undoEdit.addEdit(new UndoableInsertString(databaseContext.getDatabase(), addedString));
+ } catch (KeyCollisionException ex) {
+ LOGGER.warn("Error: could not add string '{}': {}", addedString.getName(), ex.getMessage(), ex);
+ }
+ }
+
+ public BibtexString getAddedString() {
+ return addedString;
+ }
+}
diff --git a/src/main/java/org/jabref/gui/collab/stringadd/BibTexStringAddDetailsView.java b/src/main/java/org/jabref/gui/collab/stringadd/BibTexStringAddDetailsView.java
new file mode 100644
index 00000000000..4097f03c343
--- /dev/null
+++ b/src/main/java/org/jabref/gui/collab/stringadd/BibTexStringAddDetailsView.java
@@ -0,0 +1,27 @@
+package org.jabref.gui.collab.stringadd;
+
+import javafx.scene.control.Label;
+import javafx.scene.layout.VBox;
+
+import org.jabref.gui.collab.ExternalChangeDetailsView;
+import org.jabref.logic.l10n.Localization;
+
+public final class BibTexStringAddDetailsView extends ExternalChangeDetailsView {
+
+ public BibTexStringAddDetailsView(BibTexStringAdd stringAdd) {
+ VBox container = new VBox();
+ Label header = new Label(Localization.lang("Added string"));
+ header.getStyleClass().add("sectionHeader");
+ container.getChildren().addAll(
+ header,
+ new Label(Localization.lang("Label: %0", stringAdd.getAddedString().getName())),
+ new Label(Localization.lang("Content: %0", stringAdd.getAddedString().getContent()))
+ );
+ setLeftAnchor(container, 8d);
+ setTopAnchor(container, 8d);
+ setRightAnchor(container, 8d);
+ setBottomAnchor(container, 8d);
+
+ getChildren().setAll(container);
+ }
+}
diff --git a/src/main/java/org/jabref/gui/collab/stringchange/BibTexStringChange.java b/src/main/java/org/jabref/gui/collab/stringchange/BibTexStringChange.java
new file mode 100644
index 00000000000..928a15bf73c
--- /dev/null
+++ b/src/main/java/org/jabref/gui/collab/stringchange/BibTexStringChange.java
@@ -0,0 +1,43 @@
+package org.jabref.gui.collab.stringchange;
+
+import org.jabref.gui.collab.ExternalChange;
+import org.jabref.gui.collab.ExternalChangeResolverFactory;
+import org.jabref.gui.undo.NamedCompound;
+import org.jabref.gui.undo.UndoableStringChange;
+import org.jabref.logic.l10n.Localization;
+import org.jabref.model.database.BibDatabaseContext;
+import org.jabref.model.entry.BibtexString;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public final class BibTexStringChange extends ExternalChange {
+ private static final Logger LOGGER = LoggerFactory.getLogger(BibTexStringChange.class);
+
+ private final BibtexString oldString;
+ private final BibtexString newString;
+
+ public BibTexStringChange(BibtexString oldString, BibtexString newString, BibDatabaseContext databaseContext, ExternalChangeResolverFactory externalChangeResolverFactory) {
+ super(databaseContext, externalChangeResolverFactory);
+ this.oldString = oldString;
+ this.newString = newString;
+
+ setChangeName(Localization.lang("Modified string: '%0'", oldString.getName()));
+ }
+
+ @Override
+ public void applyChange(NamedCompound undoEdit) {
+ String oldContent = oldString.getContent();
+ String newContent = newString.getContent();
+ oldString.setContent(newContent);
+ undoEdit.addEdit(new UndoableStringChange(oldString, false, oldContent, newContent));
+ }
+
+ public BibtexString getOldString() {
+ return oldString;
+ }
+
+ public BibtexString getNewString() {
+ return newString;
+ }
+}
diff --git a/src/main/java/org/jabref/gui/collab/stringchange/BibTexStringChangeDetailsView.java b/src/main/java/org/jabref/gui/collab/stringchange/BibTexStringChangeDetailsView.java
new file mode 100644
index 00000000000..f0775409f61
--- /dev/null
+++ b/src/main/java/org/jabref/gui/collab/stringchange/BibTexStringChangeDetailsView.java
@@ -0,0 +1,29 @@
+package org.jabref.gui.collab.stringchange;
+
+import javafx.scene.control.Label;
+import javafx.scene.layout.VBox;
+
+import org.jabref.gui.collab.ExternalChangeDetailsView;
+import org.jabref.logic.l10n.Localization;
+
+public final class BibTexStringChangeDetailsView extends ExternalChangeDetailsView {
+
+ public BibTexStringChangeDetailsView(BibTexStringChange stringChange) {
+ VBox container = new VBox();
+ Label header = new Label(Localization.lang("Modified string"));
+ header.getStyleClass().add("sectionHeader");
+ container.getChildren().addAll(
+ header,
+ new Label(Localization.lang("Label: %0", stringChange.getOldString().getName())),
+ new Label(Localization.lang("Content: %0", stringChange.getNewString().getContent()))
+ );
+
+ container.getChildren().add(new Label(Localization.lang("Current content: %0", stringChange.getOldString().getContent())));
+ setLeftAnchor(container, 8d);
+ setTopAnchor(container, 8d);
+ setRightAnchor(container, 8d);
+ setBottomAnchor(container, 8d);
+
+ getChildren().setAll(container);
+ }
+}
diff --git a/src/main/java/org/jabref/gui/collab/stringdelete/BibTexStringDelete.java b/src/main/java/org/jabref/gui/collab/stringdelete/BibTexStringDelete.java
new file mode 100644
index 00000000000..1f7ca9e16ff
--- /dev/null
+++ b/src/main/java/org/jabref/gui/collab/stringdelete/BibTexStringDelete.java
@@ -0,0 +1,38 @@
+package org.jabref.gui.collab.stringdelete;
+
+import org.jabref.gui.collab.ExternalChange;
+import org.jabref.gui.collab.ExternalChangeResolverFactory;
+import org.jabref.gui.undo.NamedCompound;
+import org.jabref.gui.undo.UndoableRemoveString;
+import org.jabref.logic.l10n.Localization;
+import org.jabref.model.database.BibDatabaseContext;
+import org.jabref.model.entry.BibtexString;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public final class BibTexStringDelete extends ExternalChange {
+ private static final Logger LOGGER = LoggerFactory.getLogger(BibTexStringDelete.class);
+
+ private final BibtexString deletedString;
+
+ public BibTexStringDelete(BibtexString deletedString, BibDatabaseContext databaseContext, ExternalChangeResolverFactory externalChangeResolverFactory) {
+ super(databaseContext, externalChangeResolverFactory);
+ this.deletedString = deletedString;
+ setChangeName(Localization.lang("Deleted string: '%0'", deletedString.getName()));
+ }
+
+ @Override
+ public void applyChange(NamedCompound undoEdit) {
+ try {
+ databaseContext.getDatabase().removeString(deletedString.getId());
+ undoEdit.addEdit(new UndoableRemoveString(databaseContext.getDatabase(), deletedString));
+ } catch (Exception ex) {
+ LOGGER.warn("Error: could not remove string '{}': {}", deletedString.getName(), ex.getMessage(), ex);
+ }
+ }
+
+ public BibtexString getDeletedString() {
+ return deletedString;
+ }
+}
diff --git a/src/main/java/org/jabref/gui/collab/stringdelete/BibTexStringDeleteDetailsView.java b/src/main/java/org/jabref/gui/collab/stringdelete/BibTexStringDeleteDetailsView.java
new file mode 100644
index 00000000000..d57af155a2b
--- /dev/null
+++ b/src/main/java/org/jabref/gui/collab/stringdelete/BibTexStringDeleteDetailsView.java
@@ -0,0 +1,27 @@
+package org.jabref.gui.collab.stringdelete;
+
+import javafx.scene.control.Label;
+import javafx.scene.layout.VBox;
+
+import org.jabref.gui.collab.ExternalChangeDetailsView;
+import org.jabref.logic.l10n.Localization;
+
+public final class BibTexStringDeleteDetailsView extends ExternalChangeDetailsView {
+
+ public BibTexStringDeleteDetailsView(BibTexStringDelete stringDelete) {
+ VBox container = new VBox();
+ Label header = new Label(Localization.lang("Deleted string"));
+ header.getStyleClass().add("sectionHeader");
+ container.getChildren().addAll(
+ header,
+ new Label(Localization.lang("Label: %0", stringDelete.getDeletedString().getName())),
+ new Label(Localization.lang("Content: %0", stringDelete.getDeletedString().getContent()))
+ );
+ setLeftAnchor(container, 8d);
+ setTopAnchor(container, 8d);
+ setRightAnchor(container, 8d);
+ setBottomAnchor(container, 8d);
+
+ getChildren().setAll(container);
+ }
+}
diff --git a/src/main/java/org/jabref/gui/collab/stringrename/BibTexStringRename.java b/src/main/java/org/jabref/gui/collab/stringrename/BibTexStringRename.java
new file mode 100644
index 00000000000..5c58cf11362
--- /dev/null
+++ b/src/main/java/org/jabref/gui/collab/stringrename/BibTexStringRename.java
@@ -0,0 +1,48 @@
+package org.jabref.gui.collab.stringrename;
+
+import org.jabref.gui.collab.ExternalChange;
+import org.jabref.gui.collab.ExternalChangeResolverFactory;
+import org.jabref.gui.undo.NamedCompound;
+import org.jabref.gui.undo.UndoableStringChange;
+import org.jabref.logic.l10n.Localization;
+import org.jabref.model.database.BibDatabaseContext;
+import org.jabref.model.entry.BibtexString;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public final class BibTexStringRename extends ExternalChange {
+ private static final Logger LOGGER = LoggerFactory.getLogger(BibTexStringRename.class);
+
+ private final BibtexString oldString;
+ private final BibtexString newString;
+
+ public BibTexStringRename(BibtexString oldString, BibtexString newString, BibDatabaseContext databaseContext, ExternalChangeResolverFactory externalChangeResolverFactory) {
+ super(databaseContext, externalChangeResolverFactory);
+ this.oldString = oldString;
+ this.newString = newString;
+
+ setChangeName(Localization.lang("Renamed string: '%0'", oldString.getName()));
+ }
+
+ @Override
+ public void applyChange(NamedCompound undoEdit) {
+ if (databaseContext.getDatabase().hasStringByName(newString.getName())) {
+ // The name to change to is already in the database, so we can't comply.
+ LOGGER.info("Cannot rename string '{}' to '{}' because the name is already in use", oldString.getName(), newString.getName());
+ }
+
+ String currentName = oldString.getName();
+ String newName = newString.getName();
+ oldString.setName(newName);
+ undoEdit.addEdit(new UndoableStringChange(oldString, true, currentName, newName));
+ }
+
+ public BibtexString getOldString() {
+ return oldString;
+ }
+
+ public BibtexString getNewString() {
+ return newString;
+ }
+}
diff --git a/src/main/java/org/jabref/gui/collab/stringrename/BibTexStringRenameDetailsView.java b/src/main/java/org/jabref/gui/collab/stringrename/BibTexStringRenameDetailsView.java
new file mode 100644
index 00000000000..693feaf5def
--- /dev/null
+++ b/src/main/java/org/jabref/gui/collab/stringrename/BibTexStringRenameDetailsView.java
@@ -0,0 +1,18 @@
+package org.jabref.gui.collab.stringrename;
+
+import javafx.scene.control.Label;
+
+import org.jabref.gui.collab.ExternalChangeDetailsView;
+
+public final class BibTexStringRenameDetailsView extends ExternalChangeDetailsView {
+
+ public BibTexStringRenameDetailsView(BibTexStringRename stringRename) {
+ Label label = new Label(stringRename.getNewString().getName() + " : " + stringRename.getOldString().getContent());
+ setLeftAnchor(label, 8d);
+ setTopAnchor(label, 8d);
+ setRightAnchor(label, 8d);
+ setBottomAnchor(label, 8d);
+
+ getChildren().setAll(label);
+ }
+}
diff --git a/src/main/java/org/jabref/gui/desktop/JabRefDesktop.java b/src/main/java/org/jabref/gui/desktop/JabRefDesktop.java
index 96bc258e134..517ffc1689d 100644
--- a/src/main/java/org/jabref/gui/desktop/JabRefDesktop.java
+++ b/src/main/java/org/jabref/gui/desktop/JabRefDesktop.java
@@ -219,7 +219,7 @@ public static void openFolderAndSelectFile(Path fileLink, PreferencesService pre
* @param url the URL to open
*/
public static void openBrowser(String url) throws IOException {
- Optional fileType = ExternalFileTypes.getInstance().getExternalFileTypeByExt("html");
+ Optional fileType = ExternalFileTypes.getExternalFileTypeByExt("html", Globals.prefs.getFilePreferences());
openExternalFilePlatformIndependent(fileType, url);
}
diff --git a/src/main/java/org/jabref/gui/desktop/os/Linux.java b/src/main/java/org/jabref/gui/desktop/os/Linux.java
index c522f172c55..289efb4f576 100644
--- a/src/main/java/org/jabref/gui/desktop/os/Linux.java
+++ b/src/main/java/org/jabref/gui/desktop/os/Linux.java
@@ -12,6 +12,7 @@
import org.jabref.architecture.AllowedToUseAwt;
import org.jabref.gui.DialogService;
+import org.jabref.gui.Globals;
import org.jabref.gui.JabRefExecutorService;
import org.jabref.gui.externalfiletype.ExternalFileType;
import org.jabref.gui.externalfiletype.ExternalFileTypes;
@@ -48,7 +49,7 @@ private void nativeOpenFile(String filePath) {
@Override
public void openFile(String filePath, String fileType) throws IOException {
- Optional type = ExternalFileTypes.getInstance().getExternalFileTypeByExt(fileType);
+ Optional type = ExternalFileTypes.getExternalFileTypeByExt(fileType, Globals.prefs.getFilePreferences());
String viewer;
if (type.isPresent() && !type.get().getOpenWithApplication().isEmpty()) {
diff --git a/src/main/java/org/jabref/gui/desktop/os/OSX.java b/src/main/java/org/jabref/gui/desktop/os/OSX.java
index 2bb731619fa..d7a3c6cd710 100644
--- a/src/main/java/org/jabref/gui/desktop/os/OSX.java
+++ b/src/main/java/org/jabref/gui/desktop/os/OSX.java
@@ -6,6 +6,7 @@
import org.jabref.architecture.AllowedToUseAwt;
import org.jabref.gui.DialogService;
+import org.jabref.gui.Globals;
import org.jabref.gui.externalfiletype.ExternalFileType;
import org.jabref.gui.externalfiletype.ExternalFileTypes;
@@ -14,7 +15,7 @@ public class OSX implements NativeDesktop {
@Override
public void openFile(String filePath, String fileType) throws IOException {
- Optional type = ExternalFileTypes.getInstance().getExternalFileTypeByExt(fileType);
+ Optional type = ExternalFileTypes.getExternalFileTypeByExt(fileType, Globals.prefs.getFilePreferences());
if (type.isPresent() && !type.get().getOpenWithApplication().isEmpty()) {
openFileWithApplication(filePath, type.get().getOpenWithApplication());
} else {
diff --git a/src/main/java/org/jabref/gui/desktop/os/Windows.java b/src/main/java/org/jabref/gui/desktop/os/Windows.java
index a5a6acfe79a..d85722740d6 100644
--- a/src/main/java/org/jabref/gui/desktop/os/Windows.java
+++ b/src/main/java/org/jabref/gui/desktop/os/Windows.java
@@ -7,6 +7,7 @@
import java.util.Optional;
import org.jabref.gui.DialogService;
+import org.jabref.gui.Globals;
import org.jabref.gui.externalfiletype.ExternalFileType;
import org.jabref.gui.externalfiletype.ExternalFileTypes;
@@ -15,7 +16,7 @@ public class Windows implements NativeDesktop {
@Override
public void openFile(String filePath, String fileType) throws IOException {
- Optional type = ExternalFileTypes.getInstance().getExternalFileTypeByExt(fileType);
+ Optional type = ExternalFileTypes.getExternalFileTypeByExt(fileType, Globals.prefs.getFilePreferences());
if (type.isPresent() && !type.get().getOpenWithApplication().isEmpty()) {
openFileWithApplication(filePath, type.get().getOpenWithApplication());
diff --git a/src/main/java/org/jabref/gui/dialogs/AutosaveUiManager.java b/src/main/java/org/jabref/gui/dialogs/AutosaveUiManager.java
index 7aee99be695..a7bc85890ee 100644
--- a/src/main/java/org/jabref/gui/dialogs/AutosaveUiManager.java
+++ b/src/main/java/org/jabref/gui/dialogs/AutosaveUiManager.java
@@ -16,16 +16,16 @@
public class AutosaveUiManager {
private static final Logger LOGGER = LoggerFactory.getLogger(AutosaveUiManager.class);
- private final LibraryTab libraryTab;
+ private SaveDatabaseAction saveDatabaseAction;
public AutosaveUiManager(LibraryTab libraryTab) {
- this.libraryTab = libraryTab;
+ this.saveDatabaseAction = new SaveDatabaseAction(libraryTab, Globals.prefs, Globals.entryTypesManager);
}
@Subscribe
public void listen(AutosaveEvent event) {
try {
- new SaveDatabaseAction(libraryTab, Globals.prefs, Globals.entryTypesManager).save(SaveDatabaseAction.SaveDatabaseMode.SILENT);
+ this.saveDatabaseAction.save(SaveDatabaseAction.SaveDatabaseMode.SILENT);
} catch (Throwable e) {
LOGGER.error("Problem occurred while saving.", e);
}
diff --git a/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java b/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java
index 8a344ee8bfb..c6f7cab9c5d 100644
--- a/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java
+++ b/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java
@@ -6,6 +6,8 @@
import org.jabref.gui.util.DefaultTaskExecutor;
import org.jabref.logic.autosaveandbackup.BackupManager;
import org.jabref.logic.l10n.Localization;
+import org.jabref.logic.util.BackupFileType;
+import org.jabref.logic.util.io.BackupFileUtil;
/**
* Stores all user dialogs related to {@link BackupManager}.
@@ -17,7 +19,10 @@ private BackupUIManager() {
public static void showRestoreBackupDialog(DialogService dialogService, Path originalPath) {
String content = new StringBuilder()
- .append(Localization.lang("A backup file for '%0' was found.", originalPath.getFileName().toString()))
+ .append(Localization.lang("A backup file for '%0' was found at '%1'.",
+ originalPath.getFileName().toString(),
+ // We need to determine the path "manually" as the path does not get passed through when a diff is detected.
+ BackupFileUtil.getPathOfLatestExisingBackupFile(originalPath, BackupFileType.BACKUP).map(Path::toString).orElse(Localization.lang("File not found"))))
.append("\n")
.append(Localization.lang("This could indicate that JabRef did not shut down cleanly last time the file was used."))
.append("\n\n")
diff --git a/src/main/java/org/jabref/gui/entryeditor/DeprecatedFieldsTab.java b/src/main/java/org/jabref/gui/entryeditor/DeprecatedFieldsTab.java
index 7127c0af919..d5bc20da9e6 100644
--- a/src/main/java/org/jabref/gui/entryeditor/DeprecatedFieldsTab.java
+++ b/src/main/java/org/jabref/gui/entryeditor/DeprecatedFieldsTab.java
@@ -12,7 +12,6 @@
import org.jabref.gui.DialogService;
import org.jabref.gui.StateManager;
import org.jabref.gui.autocompleter.SuggestionProviders;
-import org.jabref.gui.externalfiletype.ExternalFileTypes;
import org.jabref.gui.icon.IconTheme;
import org.jabref.gui.theme.ThemeManager;
import org.jabref.gui.util.TaskExecutor;
@@ -39,10 +38,9 @@ public DeprecatedFieldsTab(BibDatabaseContext databaseContext,
ThemeManager themeManager,
IndexingTaskManager indexingTaskManager,
BibEntryTypesManager entryTypesManager,
- ExternalFileTypes externalFileTypes,
TaskExecutor taskExecutor,
JournalAbbreviationRepository journalAbbreviationRepository) {
- super(false, databaseContext, suggestionProviders, undoManager, dialogService, preferences, stateManager, themeManager, externalFileTypes, taskExecutor, journalAbbreviationRepository, indexingTaskManager);
+ super(false, databaseContext, suggestionProviders, undoManager, dialogService, preferences, stateManager, themeManager, taskExecutor, journalAbbreviationRepository, indexingTaskManager);
this.entryTypesManager = entryTypesManager;
setText(Localization.lang("Deprecated fields"));
diff --git a/src/main/java/org/jabref/gui/entryeditor/EntryEditor.java b/src/main/java/org/jabref/gui/entryeditor/EntryEditor.java
index 5066e762b1c..6e3d662bd6e 100644
--- a/src/main/java/org/jabref/gui/entryeditor/EntryEditor.java
+++ b/src/main/java/org/jabref/gui/entryeditor/EntryEditor.java
@@ -34,7 +34,6 @@
import org.jabref.gui.entryeditor.fileannotationtab.FileAnnotationTab;
import org.jabref.gui.entryeditor.fileannotationtab.FulltextSearchResultsTab;
import org.jabref.gui.externalfiles.ExternalFilesEntryLinker;
-import org.jabref.gui.externalfiletype.ExternalFileTypes;
import org.jabref.gui.help.HelpAction;
import org.jabref.gui.importer.GrobidOptInDialogHelper;
import org.jabref.gui.keyboard.KeyBinding;
@@ -113,7 +112,7 @@ public class EntryEditor extends BorderPane {
private final List entryEditorTabs = new LinkedList<>();
- public EntryEditor(LibraryTab libraryTab, ExternalFileTypes externalFileTypes) {
+ public EntryEditor(LibraryTab libraryTab) {
this.libraryTab = libraryTab;
this.databaseContext = libraryTab.getBibDatabaseContext();
@@ -122,8 +121,7 @@ public EntryEditor(LibraryTab libraryTab, ExternalFileTypes externalFileTypes) {
.load();
this.entryEditorPreferences = preferencesService.getEntryEditorPreferences();
- this.fileLinker = new ExternalFilesEntryLinker(externalFileTypes, preferencesService.getFilePreferences(),
- databaseContext);
+ this.fileLinker = new ExternalFilesEntryLinker(preferencesService.getFilePreferences(), databaseContext);
EasyBind.subscribe(tabbed.getSelectionModel().selectedItemProperty(), tab -> {
EntryEditorTab activeTab = (EntryEditorTab) tab;
@@ -242,22 +240,22 @@ private void navigateToNextEntry() {
private List createTabs() {
// Preview tab
- entryEditorTabs.add(new PreviewTab(databaseContext, dialogService, preferencesService, stateManager, themeManager, libraryTab.getIndexingTaskManager(), ExternalFileTypes.getInstance()));
+ entryEditorTabs.add(new PreviewTab(databaseContext, dialogService, preferencesService, stateManager, themeManager, libraryTab.getIndexingTaskManager()));
// Required fields
- entryEditorTabs.add(new RequiredFieldsTab(databaseContext, libraryTab.getSuggestionProviders(), undoManager, dialogService, preferencesService, stateManager, themeManager, libraryTab.getIndexingTaskManager(), bibEntryTypesManager, ExternalFileTypes.getInstance(), taskExecutor, journalAbbreviationRepository));
+ entryEditorTabs.add(new RequiredFieldsTab(databaseContext, libraryTab.getSuggestionProviders(), undoManager, dialogService, preferencesService, stateManager, themeManager, libraryTab.getIndexingTaskManager(), bibEntryTypesManager, taskExecutor, journalAbbreviationRepository));
// Optional fields
- entryEditorTabs.add(new OptionalFieldsTab(databaseContext, libraryTab.getSuggestionProviders(), undoManager, dialogService, preferencesService, stateManager, themeManager, libraryTab.getIndexingTaskManager(), bibEntryTypesManager, ExternalFileTypes.getInstance(), taskExecutor, journalAbbreviationRepository));
- entryEditorTabs.add(new OptionalFields2Tab(databaseContext, libraryTab.getSuggestionProviders(), undoManager, dialogService, preferencesService, stateManager, themeManager, libraryTab.getIndexingTaskManager(), bibEntryTypesManager, ExternalFileTypes.getInstance(), taskExecutor, journalAbbreviationRepository));
- entryEditorTabs.add(new DeprecatedFieldsTab(databaseContext, libraryTab.getSuggestionProviders(), undoManager, dialogService, preferencesService, stateManager, themeManager, libraryTab.getIndexingTaskManager(), bibEntryTypesManager, ExternalFileTypes.getInstance(), taskExecutor, journalAbbreviationRepository));
+ entryEditorTabs.add(new OptionalFieldsTab(databaseContext, libraryTab.getSuggestionProviders(), undoManager, dialogService, preferencesService, stateManager, themeManager, libraryTab.getIndexingTaskManager(), bibEntryTypesManager, taskExecutor, journalAbbreviationRepository));
+ entryEditorTabs.add(new OptionalFields2Tab(databaseContext, libraryTab.getSuggestionProviders(), undoManager, dialogService, preferencesService, stateManager, themeManager, libraryTab.getIndexingTaskManager(), bibEntryTypesManager, taskExecutor, journalAbbreviationRepository));
+ entryEditorTabs.add(new DeprecatedFieldsTab(databaseContext, libraryTab.getSuggestionProviders(), undoManager, dialogService, preferencesService, stateManager, themeManager, libraryTab.getIndexingTaskManager(), bibEntryTypesManager, taskExecutor, journalAbbreviationRepository));
// Other fields
- entryEditorTabs.add(new OtherFieldsTab(databaseContext, libraryTab.getSuggestionProviders(), undoManager, dialogService, preferencesService, stateManager, themeManager, libraryTab.getIndexingTaskManager(), bibEntryTypesManager, ExternalFileTypes.getInstance(), taskExecutor, journalAbbreviationRepository));
+ entryEditorTabs.add(new OtherFieldsTab(databaseContext, libraryTab.getSuggestionProviders(), undoManager, dialogService, preferencesService, stateManager, themeManager, libraryTab.getIndexingTaskManager(), bibEntryTypesManager, taskExecutor, journalAbbreviationRepository));
// General fields from preferences
for (Map.Entry> tab : entryEditorPreferences.getEntryEditorTabList().entrySet()) {
- entryEditorTabs.add(new UserDefinedFieldsTab(tab.getKey(), tab.getValue(), databaseContext, libraryTab.getSuggestionProviders(), undoManager, dialogService, preferencesService, stateManager, themeManager, libraryTab.getIndexingTaskManager(), bibEntryTypesManager, ExternalFileTypes.getInstance(), taskExecutor, journalAbbreviationRepository));
+ entryEditorTabs.add(new UserDefinedFieldsTab(tab.getKey(), tab.getValue(), databaseContext, libraryTab.getSuggestionProviders(), undoManager, dialogService, preferencesService, stateManager, themeManager, libraryTab.getIndexingTaskManager(), taskExecutor, journalAbbreviationRepository));
}
// Special tabs
@@ -380,10 +378,9 @@ private void setupToolBar() {
if (fetcher instanceof PdfMergeMetadataImporter.EntryBasedFetcherWrapper) {
// Handle Grobid Opt-In in case of the PdfMergeMetadataImporter
fetcherMenuItem.setOnAction(event -> {
- GrobidOptInDialogHelper.showAndWaitIfUserIsUndecided(dialogService, preferencesService.getImporterPreferences());
+ GrobidOptInDialogHelper.showAndWaitIfUserIsUndecided(dialogService, preferencesService.getGrobidPreferences());
PdfMergeMetadataImporter.EntryBasedFetcherWrapper pdfMergeMetadataImporter =
new PdfMergeMetadataImporter.EntryBasedFetcherWrapper(
- preferencesService.getImporterPreferences(),
preferencesService.getImportFormatPreferences(),
preferencesService.getFilePreferences(),
databaseContext);
diff --git a/src/main/java/org/jabref/gui/entryeditor/FieldsEditorTab.java b/src/main/java/org/jabref/gui/entryeditor/FieldsEditorTab.java
index 342a519d6b7..49fc1d8715b 100644
--- a/src/main/java/org/jabref/gui/entryeditor/FieldsEditorTab.java
+++ b/src/main/java/org/jabref/gui/entryeditor/FieldsEditorTab.java
@@ -26,7 +26,6 @@
import org.jabref.gui.DialogService;
import org.jabref.gui.StateManager;
import org.jabref.gui.autocompleter.SuggestionProviders;
-import org.jabref.gui.externalfiletype.ExternalFileTypes;
import org.jabref.gui.fieldeditors.FieldEditorFX;
import org.jabref.gui.fieldeditors.FieldEditors;
import org.jabref.gui.fieldeditors.FieldNameLabel;
@@ -53,7 +52,6 @@ abstract class FieldsEditorTab extends EntryEditorTab {
private final DialogService dialogService;
private final PreferencesService preferences;
private final ThemeManager themeManager;
- private final ExternalFileTypes externalFileTypes;
private final TaskExecutor taskExecutor;
private final JournalAbbreviationRepository journalAbbreviationRepository;
private final StateManager stateManager;
@@ -71,7 +69,6 @@ public FieldsEditorTab(boolean compressed,
PreferencesService preferences,
StateManager stateManager,
ThemeManager themeManager,
- ExternalFileTypes externalFileTypes,
TaskExecutor taskExecutor,
JournalAbbreviationRepository journalAbbreviationRepository,
IndexingTaskManager indexingTaskManager) {
@@ -82,7 +79,6 @@ public FieldsEditorTab(boolean compressed,
this.dialogService = Objects.requireNonNull(dialogService);
this.preferences = Objects.requireNonNull(preferences);
this.themeManager = themeManager;
- this.externalFileTypes = Objects.requireNonNull(externalFileTypes);
this.taskExecutor = Objects.requireNonNull(taskExecutor);
this.journalAbbreviationRepository = Objects.requireNonNull(journalAbbreviationRepository);
this.stateManager = stateManager;
@@ -246,7 +242,7 @@ private void initPanel() {
scrollPane.setFitToHeight(true);
SplitPane container = new SplitPane(scrollPane);
- previewPanel = new PreviewPanel(databaseContext, dialogService, externalFileTypes, preferences.getKeyBindingRepository(), preferences, stateManager, themeManager, indexingTaskManager);
+ previewPanel = new PreviewPanel(databaseContext, dialogService, preferences.getKeyBindingRepository(), preferences, stateManager, themeManager, indexingTaskManager);
EasyBind.subscribe(preferences.getPreviewPreferences().showPreviewAsExtraTabProperty(), show -> {
if (show) {
container.getItems().remove(previewPanel);
diff --git a/src/main/java/org/jabref/gui/entryeditor/OptionalFields2Tab.java b/src/main/java/org/jabref/gui/entryeditor/OptionalFields2Tab.java
index 07090e543ee..97c38da58dd 100644
--- a/src/main/java/org/jabref/gui/entryeditor/OptionalFields2Tab.java
+++ b/src/main/java/org/jabref/gui/entryeditor/OptionalFields2Tab.java
@@ -5,7 +5,6 @@
import org.jabref.gui.DialogService;
import org.jabref.gui.StateManager;
import org.jabref.gui.autocompleter.SuggestionProviders;
-import org.jabref.gui.externalfiletype.ExternalFileTypes;
import org.jabref.gui.theme.ThemeManager;
import org.jabref.gui.util.TaskExecutor;
import org.jabref.logic.journals.JournalAbbreviationRepository;
@@ -25,7 +24,6 @@ public OptionalFields2Tab(BibDatabaseContext databaseContext,
ThemeManager themeManager,
IndexingTaskManager indexingTaskManager,
BibEntryTypesManager entryTypesManager,
- ExternalFileTypes externalFileTypes,
TaskExecutor taskExecutor,
JournalAbbreviationRepository journalAbbreviationRepository) {
super(
@@ -40,7 +38,6 @@ public OptionalFields2Tab(BibDatabaseContext databaseContext,
themeManager,
indexingTaskManager,
entryTypesManager,
- externalFileTypes,
taskExecutor,
journalAbbreviationRepository
);
diff --git a/src/main/java/org/jabref/gui/entryeditor/OptionalFieldsTab.java b/src/main/java/org/jabref/gui/entryeditor/OptionalFieldsTab.java
index 0035e806258..93a6ff4b6f1 100644
--- a/src/main/java/org/jabref/gui/entryeditor/OptionalFieldsTab.java
+++ b/src/main/java/org/jabref/gui/entryeditor/OptionalFieldsTab.java
@@ -5,7 +5,6 @@
import org.jabref.gui.DialogService;
import org.jabref.gui.StateManager;
import org.jabref.gui.autocompleter.SuggestionProviders;
-import org.jabref.gui.externalfiletype.ExternalFileTypes;
import org.jabref.gui.theme.ThemeManager;
import org.jabref.gui.util.TaskExecutor;
import org.jabref.logic.journals.JournalAbbreviationRepository;
@@ -25,7 +24,6 @@ public OptionalFieldsTab(BibDatabaseContext databaseContext,
ThemeManager themeManager,
IndexingTaskManager indexingTaskManager,
BibEntryTypesManager entryTypesManager,
- ExternalFileTypes externalFileTypes,
TaskExecutor taskExecutor,
JournalAbbreviationRepository journalAbbreviationRepository) {
super(
@@ -40,7 +38,6 @@ public OptionalFieldsTab(BibDatabaseContext databaseContext,
themeManager,
indexingTaskManager,
entryTypesManager,
- externalFileTypes,
taskExecutor,
journalAbbreviationRepository
);
diff --git a/src/main/java/org/jabref/gui/entryeditor/OptionalFieldsTabBase.java b/src/main/java/org/jabref/gui/entryeditor/OptionalFieldsTabBase.java
index 52c88a8c809..af1a5f9f0b6 100644
--- a/src/main/java/org/jabref/gui/entryeditor/OptionalFieldsTabBase.java
+++ b/src/main/java/org/jabref/gui/entryeditor/OptionalFieldsTabBase.java
@@ -11,7 +11,6 @@
import org.jabref.gui.DialogService;
import org.jabref.gui.StateManager;
import org.jabref.gui.autocompleter.SuggestionProviders;
-import org.jabref.gui.externalfiletype.ExternalFileTypes;
import org.jabref.gui.icon.IconTheme;
import org.jabref.gui.theme.ThemeManager;
import org.jabref.gui.util.TaskExecutor;
@@ -40,7 +39,6 @@ public OptionalFieldsTabBase(String title,
ThemeManager themeManager,
IndexingTaskManager indexingTaskManager,
BibEntryTypesManager entryTypesManager,
- ExternalFileTypes externalFileTypes,
TaskExecutor taskExecutor,
JournalAbbreviationRepository journalAbbreviationRepository) {
super(true,
@@ -51,7 +49,6 @@ public OptionalFieldsTabBase(String title,
preferences,
stateManager,
themeManager,
- externalFileTypes,
taskExecutor,
journalAbbreviationRepository,
indexingTaskManager);
diff --git a/src/main/java/org/jabref/gui/entryeditor/OtherFieldsTab.java b/src/main/java/org/jabref/gui/entryeditor/OtherFieldsTab.java
index 08802ceb27c..0fe1281c077 100644
--- a/src/main/java/org/jabref/gui/entryeditor/OtherFieldsTab.java
+++ b/src/main/java/org/jabref/gui/entryeditor/OtherFieldsTab.java
@@ -14,7 +14,6 @@
import org.jabref.gui.DialogService;
import org.jabref.gui.StateManager;
import org.jabref.gui.autocompleter.SuggestionProviders;
-import org.jabref.gui.externalfiletype.ExternalFileTypes;
import org.jabref.gui.icon.IconTheme;
import org.jabref.gui.theme.ThemeManager;
import org.jabref.gui.util.TaskExecutor;
@@ -44,7 +43,6 @@ public OtherFieldsTab(BibDatabaseContext databaseContext,
ThemeManager themeManager,
IndexingTaskManager indexingTaskManager,
BibEntryTypesManager entryTypesManager,
- ExternalFileTypes externalFileTypes,
TaskExecutor taskExecutor,
JournalAbbreviationRepository journalAbbreviationRepository) {
super(false,
@@ -55,7 +53,6 @@ public OtherFieldsTab(BibDatabaseContext databaseContext,
preferences,
stateManager,
themeManager,
- externalFileTypes,
taskExecutor,
journalAbbreviationRepository,
indexingTaskManager);
diff --git a/src/main/java/org/jabref/gui/entryeditor/PreviewTab.java b/src/main/java/org/jabref/gui/entryeditor/PreviewTab.java
index b66b3c49a18..4212506b8f6 100644
--- a/src/main/java/org/jabref/gui/entryeditor/PreviewTab.java
+++ b/src/main/java/org/jabref/gui/entryeditor/PreviewTab.java
@@ -2,7 +2,6 @@
import org.jabref.gui.DialogService;
import org.jabref.gui.StateManager;
-import org.jabref.gui.externalfiletype.ExternalFileTypes;
import org.jabref.gui.icon.IconTheme;
import org.jabref.gui.preview.PreviewPanel;
import org.jabref.gui.theme.ThemeManager;
@@ -19,7 +18,6 @@ public class PreviewTab extends EntryEditorTab {
private final StateManager stateManager;
private final ThemeManager themeManager;
private final IndexingTaskManager indexingTaskManager;
- private final ExternalFileTypes externalFileTypes;
private PreviewPanel previewPanel;
public PreviewTab(BibDatabaseContext databaseContext,
@@ -27,15 +25,13 @@ public PreviewTab(BibDatabaseContext databaseContext,
PreferencesService preferences,
StateManager stateManager,
ThemeManager themeManager,
- IndexingTaskManager indexingTaskManager,
- ExternalFileTypes externalFileTypes) {
+ IndexingTaskManager indexingTaskManager) {
this.databaseContext = databaseContext;
this.dialogService = dialogService;
this.preferences = preferences;
this.stateManager = stateManager;
this.themeManager = themeManager;
this.indexingTaskManager = indexingTaskManager;
- this.externalFileTypes = externalFileTypes;
setGraphic(IconTheme.JabRefIcons.TOGGLE_ENTRY_PREVIEW.getGraphicNode());
setText(Localization.lang("Preview"));
@@ -63,7 +59,7 @@ public boolean shouldShow(BibEntry entry) {
@Override
protected void bindToEntry(BibEntry entry) {
if (previewPanel == null) {
- previewPanel = new PreviewPanel(databaseContext, dialogService, externalFileTypes, preferences.getKeyBindingRepository(), preferences, stateManager, themeManager, indexingTaskManager);
+ previewPanel = new PreviewPanel(databaseContext, dialogService, preferences.getKeyBindingRepository(), preferences, stateManager, themeManager, indexingTaskManager);
setContent(previewPanel);
}
diff --git a/src/main/java/org/jabref/gui/entryeditor/RequiredFieldsTab.java b/src/main/java/org/jabref/gui/entryeditor/RequiredFieldsTab.java
index 5c078dea5b5..3451f2c8f53 100644
--- a/src/main/java/org/jabref/gui/entryeditor/RequiredFieldsTab.java
+++ b/src/main/java/org/jabref/gui/entryeditor/RequiredFieldsTab.java
@@ -11,7 +11,6 @@
import org.jabref.gui.DialogService;
import org.jabref.gui.StateManager;
import org.jabref.gui.autocompleter.SuggestionProviders;
-import org.jabref.gui.externalfiletype.ExternalFileTypes;
import org.jabref.gui.icon.IconTheme;
import org.jabref.gui.theme.ThemeManager;
import org.jabref.gui.util.TaskExecutor;
@@ -40,11 +39,10 @@ public RequiredFieldsTab(BibDatabaseContext databaseContext,
ThemeManager themeManager,
IndexingTaskManager indexingTaskManager,
BibEntryTypesManager entryTypesManager,
- ExternalFileTypes externalFileTypes,
TaskExecutor taskExecutor,
JournalAbbreviationRepository journalAbbreviationRepository) {
super(false, databaseContext, suggestionProviders, undoManager, dialogService,
- preferences, stateManager, themeManager, externalFileTypes, taskExecutor, journalAbbreviationRepository, indexingTaskManager);
+ preferences, stateManager, themeManager, taskExecutor, journalAbbreviationRepository, indexingTaskManager);
this.entryTypesManager = entryTypesManager;
setText(Localization.lang("Required fields"));
diff --git a/src/main/java/org/jabref/gui/entryeditor/UserDefinedFieldsTab.java b/src/main/java/org/jabref/gui/entryeditor/UserDefinedFieldsTab.java
index bc084a8e77b..aa4e65b48e3 100644
--- a/src/main/java/org/jabref/gui/entryeditor/UserDefinedFieldsTab.java
+++ b/src/main/java/org/jabref/gui/entryeditor/UserDefinedFieldsTab.java
@@ -8,7 +8,6 @@
import org.jabref.gui.DialogService;
import org.jabref.gui.StateManager;
import org.jabref.gui.autocompleter.SuggestionProviders;
-import org.jabref.gui.externalfiletype.ExternalFileTypes;
import org.jabref.gui.icon.IconTheme;
import org.jabref.gui.theme.ThemeManager;
import org.jabref.gui.util.TaskExecutor;
@@ -16,7 +15,6 @@
import org.jabref.logic.pdf.search.indexing.IndexingTaskManager;
import org.jabref.model.database.BibDatabaseContext;
import org.jabref.model.entry.BibEntry;
-import org.jabref.model.entry.BibEntryTypesManager;
import org.jabref.model.entry.field.Field;
import org.jabref.preferences.PreferencesService;
@@ -33,11 +31,9 @@ public UserDefinedFieldsTab(String name,
StateManager stateManager,
ThemeManager themeManager,
IndexingTaskManager indexingTaskManager,
- BibEntryTypesManager entryTypesManager,
- ExternalFileTypes externalFileTypes,
TaskExecutor taskExecutor,
JournalAbbreviationRepository journalAbbreviationRepository) {
- super(false, databaseContext, suggestionProviders, undoManager, dialogService, preferences, stateManager, themeManager, externalFileTypes, taskExecutor, journalAbbreviationRepository, indexingTaskManager);
+ super(false, databaseContext, suggestionProviders, undoManager, dialogService, preferences, stateManager, themeManager, taskExecutor, journalAbbreviationRepository, indexingTaskManager);
this.fields = new LinkedHashSet<>(fields);
diff --git a/src/main/java/org/jabref/gui/exporter/SaveDatabaseAction.java b/src/main/java/org/jabref/gui/exporter/SaveDatabaseAction.java
index 685e1c0d28d..84bf9346e23 100644
--- a/src/main/java/org/jabref/gui/exporter/SaveDatabaseAction.java
+++ b/src/main/java/org/jabref/gui/exporter/SaveDatabaseAction.java
@@ -19,7 +19,6 @@
import org.jabref.gui.DialogService;
import org.jabref.gui.JabRefFrame;
import org.jabref.gui.LibraryTab;
-import org.jabref.gui.dialogs.AutosaveUiManager;
import org.jabref.gui.util.BackgroundTask;
import org.jabref.gui.util.FileDialogConfiguration;
import org.jabref.logic.autosaveandbackup.AutosaveManager;
@@ -104,7 +103,7 @@ public void saveSelectedAsPlain() {
}
/**
- * @param file the new file name to save the data base to. This is stored in the database context of the panel upon
+ * @param file the new file name to save the database to. This is stored in the database context of the panel upon
* successful save.
* @return true on successful save
*/
@@ -131,19 +130,13 @@ boolean saveAs(Path file, SaveDatabaseMode mode) {
if (saveResult) {
// we managed to successfully save the file
- // thus, we can store the store the path into the context
+ // thus, we can store the path into the context
context.setDatabasePath(file);
libraryTab.updateTabTitle(false);
// Reset (here: uninstall and install again) AutosaveManager and BackupManager for the new file name
libraryTab.resetChangeMonitor();
- if (readyForAutosave(context)) {
- AutosaveManager autosaver = AutosaveManager.start(context);
- autosaver.registerListener(new AutosaveUiManager(libraryTab));
- }
- if (readyForBackup(context)) {
- BackupManager.start(context, entryTypesManager, preferences);
- }
+ libraryTab.installAutosaveManagerAndBackupManager();
frame.getFileHistory().newFile(file);
}
@@ -199,13 +192,21 @@ private boolean save(Path targetPath, SaveDatabaseMode mode) {
dialogService.notify(String.format("%s...", Localization.lang("Saving library")));
}
- libraryTab.setSaving(true);
+ synchronized (libraryTab) {
+ if (libraryTab.isSaving()) {
+ // if another thread is saving, we do not need to save
+ return true;
+ }
+ libraryTab.setSaving(true);
+ }
+
try {
Charset encoding = libraryTab.getBibDatabaseContext()
.getMetaData()
.getEncoding()
.orElse(StandardCharsets.UTF_8);
- // Make sure to remember which encoding we used.
+
+ // Make sure to remember which encoding we used
libraryTab.getBibDatabaseContext().getMetaData().setEncoding(encoding, ChangePropagation.DO_NOT_POST_EVENT);
// Save the database
@@ -228,32 +229,35 @@ private boolean save(Path targetPath, SaveDatabaseMode mode) {
}
private boolean saveDatabase(Path file, boolean selectedOnly, Charset encoding, SavePreferences.DatabaseSaveType saveType) throws SaveException {
+ // if this code is adapted, please also adapt org.jabref.logic.autosaveandbackup.BackupManager.performBackup
+
GeneralPreferences generalPreferences = this.preferences.getGeneralPreferences();
SavePreferences savePreferences = this.preferences.getSavePreferences()
.withSaveType(saveType);
- try (AtomicFileWriter fileWriter = new AtomicFileWriter(file, encoding, savePreferences.shouldMakeBackup())) {
- BibDatabaseContext bibDatabaseContext = libraryTab.getBibDatabaseContext();
- BibWriter bibWriter = new BibWriter(fileWriter, bibDatabaseContext.getDatabase().getNewLineSeparator());
- BibtexDatabaseWriter databaseWriter = new BibtexDatabaseWriter(bibWriter, generalPreferences, savePreferences, entryTypesManager);
-
- if (selectedOnly) {
- databaseWriter.savePartOfDatabase(bibDatabaseContext, libraryTab.getSelectedEntries());
- } else {
- databaseWriter.saveDatabase(bibDatabaseContext);
- }
+ BibDatabaseContext bibDatabaseContext = libraryTab.getBibDatabaseContext();
+ synchronized (bibDatabaseContext) {
+ try (AtomicFileWriter fileWriter = new AtomicFileWriter(file, encoding, savePreferences.shouldMakeBackup())) {
+ BibWriter bibWriter = new BibWriter(fileWriter, bibDatabaseContext.getDatabase().getNewLineSeparator());
+ BibtexDatabaseWriter databaseWriter = new BibtexDatabaseWriter(bibWriter, generalPreferences, savePreferences, entryTypesManager);
+
+ if (selectedOnly) {
+ databaseWriter.savePartOfDatabase(bibDatabaseContext, libraryTab.getSelectedEntries());
+ } else {
+ databaseWriter.saveDatabase(bibDatabaseContext);
+ }
- libraryTab.registerUndoableChanges(databaseWriter.getSaveActionsFieldChanges());
+ libraryTab.registerUndoableChanges(databaseWriter.getSaveActionsFieldChanges());
- if (fileWriter.hasEncodingProblems()) {
- saveWithDifferentEncoding(file, selectedOnly, encoding, fileWriter.getEncodingProblems(), saveType);
+ if (fileWriter.hasEncodingProblems()) {
+ saveWithDifferentEncoding(file, selectedOnly, encoding, fileWriter.getEncodingProblems(), saveType);
+ }
+ } catch (UnsupportedCharsetException ex) {
+ throw new SaveException(Localization.lang("Character encoding '%0' is not supported.", encoding.displayName()), ex);
+ } catch (IOException ex) {
+ throw new SaveException("Problems saving: " + ex, ex);
}
- } catch (UnsupportedCharsetException ex) {
- throw new SaveException(Localization.lang("Character encoding '%0' is not supported.", encoding.displayName()), ex);
- } catch (IOException ex) {
- throw new SaveException("Problems saving: " + ex, ex);
+ return true;
}
-
- return true;
}
private void saveWithDifferentEncoding(Path file, boolean selectedOnly, Charset encoding, Set encodingProblems, SavePreferences.DatabaseSaveType saveType) throws SaveException {
@@ -282,15 +286,4 @@ private void saveWithDifferentEncoding(Path file, boolean selectedOnly, Charset
}
}
}
-
- private boolean readyForAutosave(BibDatabaseContext context) {
- return ((context.getLocation() == DatabaseLocation.SHARED)
- || ((context.getLocation() == DatabaseLocation.LOCAL)
- && preferences.getImportExportPreferences().shouldAutoSave()))
- && context.getDatabasePath().isPresent();
- }
-
- private boolean readyForBackup(BibDatabaseContext context) {
- return (context.getLocation() == DatabaseLocation.LOCAL) && context.getDatabasePath().isPresent();
- }
}
diff --git a/src/main/java/org/jabref/gui/externalfiles/AutoLinkFilesAction.java b/src/main/java/org/jabref/gui/externalfiles/AutoLinkFilesAction.java
index 6c51f8889dd..689d79dca69 100644
--- a/src/main/java/org/jabref/gui/externalfiles/AutoLinkFilesAction.java
+++ b/src/main/java/org/jabref/gui/externalfiles/AutoLinkFilesAction.java
@@ -9,7 +9,6 @@
import org.jabref.gui.DialogService;
import org.jabref.gui.StateManager;
import org.jabref.gui.actions.SimpleCommand;
-import org.jabref.gui.externalfiletype.ExternalFileTypes;
import org.jabref.gui.undo.NamedCompound;
import org.jabref.gui.util.BindingsHelper;
import org.jabref.gui.util.TaskExecutor;
@@ -51,8 +50,7 @@ public void execute() {
final AutoSetFileLinksUtil util = new AutoSetFileLinksUtil(
database,
preferences.getFilePreferences(),
- preferences.getAutoLinkPreferences(),
- ExternalFileTypes.getInstance());
+ preferences.getAutoLinkPreferences());
final NamedCompound nc = new NamedCompound(Localization.lang("Automatically set file links"));
Task linkFilesTask = new Task<>() {
diff --git a/src/main/java/org/jabref/gui/externalfiles/AutoSetFileLinksUtil.java b/src/main/java/org/jabref/gui/externalfiles/AutoSetFileLinksUtil.java
index e783ee0aed3..4d1f35e3b40 100644
--- a/src/main/java/org/jabref/gui/externalfiles/AutoSetFileLinksUtil.java
+++ b/src/main/java/org/jabref/gui/externalfiles/AutoSetFileLinksUtil.java
@@ -55,16 +55,16 @@ public List getFileExceptions() {
private static final Logger LOGGER = LoggerFactory.getLogger(AutoSetFileLinksUtil.class);
private final List directories;
private final AutoLinkPreferences autoLinkPreferences;
- private final ExternalFileTypes externalFileTypes;
+ private final FilePreferences filePreferences;
- public AutoSetFileLinksUtil(BibDatabaseContext databaseContext, FilePreferences filePreferences, AutoLinkPreferences autoLinkPreferences, ExternalFileTypes externalFileTypes) {
- this(databaseContext.getFileDirectories(filePreferences), autoLinkPreferences, externalFileTypes);
+ public AutoSetFileLinksUtil(BibDatabaseContext databaseContext, FilePreferences filePreferences, AutoLinkPreferences autoLinkPreferences) {
+ this(databaseContext.getFileDirectories(filePreferences), filePreferences, autoLinkPreferences);
}
- private AutoSetFileLinksUtil(List directories, AutoLinkPreferences autoLinkPreferences, ExternalFileTypes externalFileTypes) {
+ private AutoSetFileLinksUtil(List directories, FilePreferences filePreferences, AutoLinkPreferences autoLinkPreferences) {
this.directories = directories;
this.autoLinkPreferences = autoLinkPreferences;
- this.externalFileTypes = externalFileTypes;
+ this.filePreferences = filePreferences;
}
public LinkFilesResult linkAssociatedFiles(List entries, NamedCompound ce) {
@@ -107,7 +107,7 @@ public LinkFilesResult linkAssociatedFiles(List entries, NamedCompound
public List findAssociatedNotLinkedFiles(BibEntry entry) throws IOException {
List linkedFiles = new ArrayList<>();
- List extensions = externalFileTypes.getExternalFileTypeSelection().stream().map(ExternalFileType::getExtension).collect(Collectors.toList());
+ List extensions = filePreferences.getExternalFileTypes().stream().map(ExternalFileType::getExtension).collect(Collectors.toList());
// Run the search operation
FileFinder fileFinder = FileFinders.constructFromConfiguration(autoLinkPreferences);
@@ -128,7 +128,7 @@ public List findAssociatedNotLinkedFiles(BibEntry entry) throws IOEx
if (!fileAlreadyLinked) {
Optional type = FileHelper.getFileExtension(foundFile)
- .map(externalFileTypes::getExternalFileTypeByExt)
+ .map(extension -> ExternalFileTypes.getExternalFileTypeByExt(extension, filePreferences))
.orElse(Optional.of(new UnknownExternalFileType("")));
String strType = type.isPresent() ? type.get().getName() : "";
diff --git a/src/main/java/org/jabref/gui/externalfiles/DownloadFullTextAction.java b/src/main/java/org/jabref/gui/externalfiles/DownloadFullTextAction.java
index 53be917cea1..be8b74ede44 100644
--- a/src/main/java/org/jabref/gui/externalfiles/DownloadFullTextAction.java
+++ b/src/main/java/org/jabref/gui/externalfiles/DownloadFullTextAction.java
@@ -14,7 +14,6 @@
import org.jabref.gui.StateManager;
import org.jabref.gui.actions.ActionHelper;
import org.jabref.gui.actions.SimpleCommand;
-import org.jabref.gui.externalfiletype.ExternalFileTypes;
import org.jabref.gui.fieldeditors.LinkedFileViewModel;
import org.jabref.logic.importer.FulltextFetchers;
import org.jabref.logic.l10n.Localization;
@@ -146,8 +145,7 @@ private void addLinkedFileFromURL(BibDatabaseContext databaseContext, URL url, B
databaseContext,
Globals.TASK_EXECUTOR,
dialogService,
- preferences,
- ExternalFileTypes.getInstance());
+ preferences);
onlineFile.download();
} else {
diff --git a/src/main/java/org/jabref/gui/externalfiles/ExternalFilesEntryLinker.java b/src/main/java/org/jabref/gui/externalfiles/ExternalFilesEntryLinker.java
index b62748a4959..7f9591c60f4 100644
--- a/src/main/java/org/jabref/gui/externalfiles/ExternalFilesEntryLinker.java
+++ b/src/main/java/org/jabref/gui/externalfiles/ExternalFilesEntryLinker.java
@@ -26,14 +26,12 @@ public class ExternalFilesEntryLinker {
private static final Logger LOGGER = LoggerFactory.getLogger(ExternalFilesEntryLinker.class);
- private final ExternalFileTypes externalFileTypes;
private final FilePreferences filePreferences;
private final BibDatabaseContext bibDatabaseContext;
private final MoveFilesCleanup moveFilesCleanup;
private final RenamePdfCleanup renameFilesCleanup;
- public ExternalFilesEntryLinker(ExternalFileTypes externalFileTypes, FilePreferences filePreferences, BibDatabaseContext bibDatabaseContext) {
- this.externalFileTypes = externalFileTypes;
+ public ExternalFilesEntryLinker(FilePreferences filePreferences, BibDatabaseContext bibDatabaseContext) {
this.filePreferences = filePreferences;
this.bibDatabaseContext = bibDatabaseContext;
this.moveFilesCleanup = new MoveFilesCleanup(bibDatabaseContext, filePreferences);
@@ -62,7 +60,7 @@ public void moveLinkedFilesToFileDir(BibEntry entry) {
public void addFilesToEntry(BibEntry entry, List files) {
for (Path file : files) {
FileUtil.getFileExtension(file).ifPresent(ext -> {
- ExternalFileType type = externalFileTypes.getExternalFileTypeByExt(ext)
+ ExternalFileType type = ExternalFileTypes.getExternalFileTypeByExt(ext, filePreferences)
.orElse(new UnknownExternalFileType(ext));
Path relativePath = FileUtil.relativize(file, bibDatabaseContext.getFileDirectories(filePreferences));
LinkedFile linkedfile = new LinkedFile("", relativePath, type.getName());
diff --git a/src/main/java/org/jabref/gui/externalfiles/FileExtensionViewModel.java b/src/main/java/org/jabref/gui/externalfiles/FileExtensionViewModel.java
index 121517bf5bc..edab9aa8644 100644
--- a/src/main/java/org/jabref/gui/externalfiles/FileExtensionViewModel.java
+++ b/src/main/java/org/jabref/gui/externalfiles/FileExtensionViewModel.java
@@ -11,17 +11,18 @@
import org.jabref.gui.util.FileFilterConverter;
import org.jabref.logic.l10n.Localization;
import org.jabref.logic.util.FileType;
+import org.jabref.preferences.FilePreferences;
public class FileExtensionViewModel {
private final String description;
private final List extensions;
- private final ExternalFileTypes externalFileTypes;
+ private final FilePreferences filePreferences;
- FileExtensionViewModel(FileType fileType, ExternalFileTypes externalFileTypes) {
+ FileExtensionViewModel(FileType fileType, FilePreferences filePreferences) {
this.description = Localization.lang("%0 file", fileType.getName());
- this.extensions = fileType.getExtensionsWithDot();
- this.externalFileTypes = externalFileTypes;
+ this.extensions = fileType.getExtensionsWithAsteriskAndDot();
+ this.filePreferences = filePreferences;
}
public String getDescription() {
@@ -29,7 +30,7 @@ public String getDescription() {
}
public JabRefIcon getIcon() {
- return externalFileTypes.getExternalFileTypeByExt(extensions.get(0))
+ return ExternalFileTypes.getExternalFileTypeByExt(extensions.get(0), filePreferences)
.map(ExternalFileType::getIcon)
.orElse(null);
}
diff --git a/src/main/java/org/jabref/gui/externalfiles/ImportHandler.java b/src/main/java/org/jabref/gui/externalfiles/ImportHandler.java
index 61cc7afd2e8..b417e49fc3e 100644
--- a/src/main/java/org/jabref/gui/externalfiles/ImportHandler.java
+++ b/src/main/java/org/jabref/gui/externalfiles/ImportHandler.java
@@ -17,7 +17,6 @@
import org.jabref.gui.Globals;
import org.jabref.gui.StateManager;
import org.jabref.gui.duplicationFinder.DuplicateResolverDialog;
-import org.jabref.gui.externalfiletype.ExternalFileTypes;
import org.jabref.gui.undo.UndoableInsertEntries;
import org.jabref.gui.util.BackgroundTask;
import org.jabref.gui.util.DefaultTaskExecutor;
@@ -65,7 +64,6 @@ public class ImportHandler {
private final ImportFormatReader importFormatReader;
public ImportHandler(BibDatabaseContext database,
- ExternalFileTypes externalFileTypes,
PreferencesService preferencesService,
FileUpdateMonitor fileupdateMonitor,
UndoManager undoManager,
@@ -80,11 +78,8 @@ public ImportHandler(BibDatabaseContext database,
this.dialogService = dialogService;
this.importFormatReader = importFormatReader;
- this.linker = new ExternalFilesEntryLinker(externalFileTypes, preferencesService.getFilePreferences(), database);
- this.contentImporter = new ExternalFilesContentImporter(
- preferencesService.getGeneralPreferences(),
- preferencesService.getImporterPreferences(),
- preferencesService.getImportFormatPreferences());
+ this.linker = new ExternalFilesEntryLinker(preferencesService.getFilePreferences(), database);
+ this.contentImporter = new ExternalFilesContentImporter(preferencesService.getImportFormatPreferences());
this.undoManager = undoManager;
}
diff --git a/src/main/java/org/jabref/gui/externalfiles/UnlinkedFilesDialogView.java b/src/main/java/org/jabref/gui/externalfiles/UnlinkedFilesDialogView.java
index 0805c8a1f22..9ccdfa8ec82 100644
--- a/src/main/java/org/jabref/gui/externalfiles/UnlinkedFilesDialogView.java
+++ b/src/main/java/org/jabref/gui/externalfiles/UnlinkedFilesDialogView.java
@@ -31,7 +31,6 @@
import org.jabref.gui.actions.ActionFactory;
import org.jabref.gui.actions.SimpleCommand;
import org.jabref.gui.actions.StandardActions;
-import org.jabref.gui.externalfiletype.ExternalFileTypes;
import org.jabref.gui.icon.JabRefIcon;
import org.jabref.gui.theme.ThemeManager;
import org.jabref.gui.util.BaseDialog;
@@ -108,7 +107,7 @@ public UnlinkedFilesDialogView() {
@FXML
private void initialize() {
- viewModel = new UnlinkedFilesDialogViewModel(dialogService, ExternalFileTypes.getInstance(), undoManager, fileUpdateMonitor, preferencesService, stateManager, taskExecutor, importFormatReader);
+ viewModel = new UnlinkedFilesDialogViewModel(dialogService, undoManager, fileUpdateMonitor, preferencesService, stateManager, taskExecutor, importFormatReader);
progressDisplay.progressProperty().bind(viewModel.progressValueProperty());
progressText.textProperty().bind(viewModel.progressTextProperty());
diff --git a/src/main/java/org/jabref/gui/externalfiles/UnlinkedFilesDialogViewModel.java b/src/main/java/org/jabref/gui/externalfiles/UnlinkedFilesDialogViewModel.java
index fd942a61508..2129e473c4a 100644
--- a/src/main/java/org/jabref/gui/externalfiles/UnlinkedFilesDialogViewModel.java
+++ b/src/main/java/org/jabref/gui/externalfiles/UnlinkedFilesDialogViewModel.java
@@ -30,7 +30,6 @@
import org.jabref.gui.DialogService;
import org.jabref.gui.StateManager;
-import org.jabref.gui.externalfiletype.ExternalFileTypes;
import org.jabref.gui.util.BackgroundTask;
import org.jabref.gui.util.DirectoryDialogConfiguration;
import org.jabref.gui.util.FileDialogConfiguration;
@@ -82,7 +81,6 @@ public class UnlinkedFilesDialogViewModel {
private final FunctionBasedValidator scanDirectoryValidator;
public UnlinkedFilesDialogViewModel(DialogService dialogService,
- ExternalFileTypes externalFileTypes,
UndoManager undoManager,
FileUpdateMonitor fileUpdateMonitor,
PreferencesService preferences,
@@ -95,7 +93,6 @@ public UnlinkedFilesDialogViewModel(DialogService dialogService,
this.bibDatabase = stateManager.getActiveDatabase().orElseThrow(() -> new NullPointerException("Database null"));
importHandler = new ImportHandler(
bibDatabase,
- externalFileTypes,
preferences,
fileUpdateMonitor,
undoManager,
@@ -104,9 +101,9 @@ public UnlinkedFilesDialogViewModel(DialogService dialogService,
importFormatReader);
this.fileFilterList = FXCollections.observableArrayList(
- new FileExtensionViewModel(StandardFileType.ANY_FILE, externalFileTypes),
- new FileExtensionViewModel(StandardFileType.BIBTEX_DB, externalFileTypes),
- new FileExtensionViewModel(StandardFileType.PDF, externalFileTypes));
+ new FileExtensionViewModel(StandardFileType.ANY_FILE, preferences.getFilePreferences()),
+ new FileExtensionViewModel(StandardFileType.BIBTEX_DB, preferences.getFilePreferences()),
+ new FileExtensionViewModel(StandardFileType.PDF, preferences.getFilePreferences()));
this.dateFilterList = FXCollections.observableArrayList(DateRange.values());
diff --git a/src/main/java/org/jabref/gui/externalfiletype/ExternalFileType.java b/src/main/java/org/jabref/gui/externalfiletype/ExternalFileType.java
index 15e4a869b51..0a5a44259cf 100644
--- a/src/main/java/org/jabref/gui/externalfiletype/ExternalFileType.java
+++ b/src/main/java/org/jabref/gui/externalfiletype/ExternalFileType.java
@@ -11,6 +11,10 @@ public interface ExternalFileType {
String getMimeType();
+ String getOpenWithApplication();
+
+ JabRefIcon getIcon();
+
/**
* Get the bibtex field name used for this file type. Currently we assume that field name equals filename extension.
*
@@ -20,7 +24,14 @@ default Field getField() {
return FieldFactory.parseField(getExtension());
}
- String getOpenWithApplication();
-
- JabRefIcon getIcon();
+ /**
+ * Return a String array representing this file type. This is used for storage into
+ * Preferences, and the same array can be used to construct the file type later,
+ * using the String[] constructor.
+ *
+ * @return A String[] containing all information about this file type.
+ */
+ default String[] toStringArray() {
+ return new String[]{getName(), getExtension(), getMimeType(), getOpenWithApplication(), getIcon().name()};
+ }
}
diff --git a/src/main/java/org/jabref/gui/externalfiletype/ExternalFileTypes.java b/src/main/java/org/jabref/gui/externalfiletype/ExternalFileTypes.java
index 89209f2cffa..3cba8ebd4fa 100644
--- a/src/main/java/org/jabref/gui/externalfiletype/ExternalFileTypes.java
+++ b/src/main/java/org/jabref/gui/externalfiletype/ExternalFileTypes.java
@@ -3,18 +3,18 @@
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Comparator;
+import java.util.Collection;
+import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Optional;
import java.util.Set;
-import java.util.TreeSet;
-import org.jabref.gui.Globals;
import org.jabref.logic.bibtex.FileFieldWriter;
import org.jabref.model.entry.LinkedFile;
import org.jabref.model.strings.StringUtil;
import org.jabref.model.util.FileHelper;
+import org.jabref.preferences.FilePreferences;
// Do not make this class final, as it otherwise can't be mocked for tests
public class ExternalFileTypes {
@@ -22,40 +22,23 @@ public class ExternalFileTypes {
// This String is used in the encoded list in prefs of external file type
// modifications, in order to indicate a removed default file type:
private static final String FILE_TYPE_REMOVED_FLAG = "REMOVED";
- // The only instance of this class:
- private static ExternalFileTypes singleton;
- // Map containing all registered external file types:
- private final Set externalFileTypes = new TreeSet<>(Comparator.comparing(ExternalFileType::getName));
-
- private final ExternalFileType HTML_FALLBACK_TYPE = StandardExternalFileType.URL;
+ private static final ExternalFileType HTML_FALLBACK_TYPE = StandardExternalFileType.URL;
private ExternalFileTypes() {
- updateExternalFileTypes();
- }
-
- public static ExternalFileTypes getInstance() {
- if (ExternalFileTypes.singleton == null) {
- ExternalFileTypes.singleton = new ExternalFileTypes();
- }
- return ExternalFileTypes.singleton;
}
public static List getDefaultExternalFileTypes() {
return Arrays.asList(StandardExternalFileType.values());
}
- public Set getExternalFileTypeSelection() {
- return externalFileTypes;
- }
-
/**
* Look up the external file type registered with this name, if any.
*
* @param name The file type name.
* @return The ExternalFileType registered, or null if none.
*/
- public Optional getExternalFileTypeByName(String name) {
- Optional externalFileType = externalFileTypes.stream().filter(type -> type.getName().equals(name)).findFirst();
+ public static Optional getExternalFileTypeByName(String name, FilePreferences filePreferences) {
+ Optional externalFileType = filePreferences.getExternalFileTypes().stream().filter(type -> type.getName().equals(name)).findFirst();
if (externalFileType.isPresent()) {
return externalFileType;
}
@@ -69,9 +52,9 @@ public Optional getExternalFileTypeByName(String name) {
* @param extension The file extension.
* @return The ExternalFileType registered, or null if none.
*/
- public Optional getExternalFileTypeByExt(String extension) {
+ public static Optional getExternalFileTypeByExt(String extension, FilePreferences filePreferences) {
String extensionCleaned = extension.replace(".", "").replace("*", "");
- return externalFileTypes.stream().filter(type -> type.getExtension().equalsIgnoreCase(extensionCleaned)).findFirst();
+ return filePreferences.getExternalFileTypes().stream().filter(type -> type.getExtension().equalsIgnoreCase(extensionCleaned)).findFirst();
}
/**
@@ -80,8 +63,8 @@ public Optional getExternalFileTypeByExt(String extension) {
* @param extension The file extension.
* @return true if an ExternalFileType with the extension exists, false otherwise
*/
- public boolean isExternalFileTypeByExt(String extension) {
- return externalFileTypes.stream().anyMatch(type -> type.getExtension().equalsIgnoreCase(extension));
+ public static boolean isExternalFileTypeByExt(String extension, FilePreferences filePreferences) {
+ return filePreferences.getExternalFileTypes().stream().anyMatch(type -> type.getExtension().equalsIgnoreCase(extension));
}
/**
@@ -90,10 +73,10 @@ public boolean isExternalFileTypeByExt(String extension) {
* @param filename The name of the file whose type to look up.
* @return The ExternalFileType registered, or null if none.
*/
- public Optional getExternalFileTypeForName(String filename) {
+ public static Optional getExternalFileTypeForName(String filename, FilePreferences filePreferences) {
int longestFound = -1;
ExternalFileType foundType = null;
- for (ExternalFileType type : externalFileTypes) {
+ for (ExternalFileType type : filePreferences.getExternalFileTypes()) {
if (!type.getExtension().isEmpty() && filename.toLowerCase(Locale.ROOT).endsWith(type.getExtension().toLowerCase(Locale.ROOT))
&& (type.getExtension().length() > longestFound)) {
longestFound = type.getExtension().length();
@@ -110,12 +93,12 @@ public Optional getExternalFileTypeForName(String filename) {
* @return The ExternalFileType registered, or null if none. For the mime type "text/html", a valid file type is
* guaranteed to be returned.
*/
- public Optional getExternalFileTypeByMimeType(String mimeType) {
+ public static Optional getExternalFileTypeByMimeType(String mimeType, FilePreferences filePreferences) {
// Ignores parameters according to link: (https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types)
if (mimeType.indexOf(';') != -1) {
mimeType = mimeType.substring(0, mimeType.indexOf(';')).trim();
}
- for (ExternalFileType type : externalFileTypes) {
+ for (ExternalFileType type : filePreferences.getExternalFileTypes()) {
if (type.getMimeType().equalsIgnoreCase(mimeType)) {
return Optional.of(type);
}
@@ -127,21 +110,44 @@ public Optional getExternalFileTypeByMimeType(String mimeType)
}
}
+ public static Optional getExternalFileTypeByFile(Path file, FilePreferences filePreferences) {
+ final String filePath = file.toString();
+ final Optional extension = FileHelper.getFileExtension(filePath);
+ return extension.flatMap(ext -> getExternalFileTypeByExt(ext, filePreferences));
+ }
+
+ public static Optional getExternalFileTypeByLinkedFile(LinkedFile linkedFile, boolean deduceUnknownType, FilePreferences filePreferences) {
+ Optional type = getExternalFileTypeByName(linkedFile.getFileType(), filePreferences);
+ boolean isUnknownType = type.isEmpty() || (type.get() instanceof UnknownExternalFileType);
+
+ if (isUnknownType && deduceUnknownType) {
+ // No file type was recognized. Try to find a usable file type based on mime type:
+ Optional mimeType = getExternalFileTypeByMimeType(linkedFile.getFileType(), filePreferences);
+ if (mimeType.isPresent()) {
+ return mimeType;
+ }
+
+ // No type could be found from mime type. Try based on the extension:
+ return FileHelper.getFileExtension(linkedFile.getLink())
+ .flatMap(extension -> getExternalFileTypeByExt(extension, filePreferences));
+ } else {
+ return type;
+ }
+ }
+
/**
- * Reset the List of external file types after user customization.
- *
- * @param types The new List of external file types. This is the complete list, not just new entries.
+ * @return A StringList of customized and removed file types compared to the default list of external file types for storing
*/
- public void setExternalFileTypes(List types) {
+ public static String toStringList(Collection fileTypes) {
// First find a list of the default types:
List defTypes = new ArrayList<>(getDefaultExternalFileTypes());
// Make a list of types that are unchanged:
List unchanged = new ArrayList<>();
+ // Create a result list
+ List results = new ArrayList<>();
- externalFileTypes.clear();
- for (ExternalFileType type : types) {
- externalFileTypes.add(type);
-
+ for (ExternalFileType type : fileTypes) {
+ results.add(type);
// See if we can find a type with matching name in the default type list:
ExternalFileType found = null;
for (ExternalFileType defType : defTypes) {
@@ -166,52 +172,38 @@ public void setExternalFileTypes(List types) {
// and from the list of defaults, since we don't need to mention these in prefs:
for (ExternalFileType type : unchanged) {
defTypes.remove(type);
- types.remove(type);
+ results.remove(type);
}
// Now set up the array to write to prefs, containing all new types, all modified
// types, and a flag denoting each default type that has been removed:
- String[][] array = new String[types.size() + defTypes.size()][];
+ String[][] array = new String[results.size() + defTypes.size()][];
int i = 0;
- for (ExternalFileType type : types) {
- array[i] = getStringArrayRepresentation(type);
+ for (ExternalFileType type : results) {
+ array[i] = type.toStringArray();
i++;
}
for (ExternalFileType type : defTypes) {
array[i] = new String[] {type.getName(), FILE_TYPE_REMOVED_FLAG};
i++;
}
- Globals.prefs.storeExternalFileTypes(FileFieldWriter.encodeStringArray(array));
+ return FileFieldWriter.encodeStringArray(array);
}
/**
- * Return a String array representing this file type. This is used for storage into
- * Preferences, and the same array can be used to construct the file type later,
- * using the String[] constructor.
- *
- * @return A String[] containing all information about this file type.
+ * Set up the list of external file types, either from default values, or from values recorded in PreferencesService.
*/
- private String[] getStringArrayRepresentation(ExternalFileType type) {
- return new String[] {type.getName(), type.getExtension(), type.getMimeType(), type.getOpenWithApplication(), type.getIcon().name()};
- }
-
- /**
- * Set up the list of external file types, either from default values, or from values recorded in Preferences.
- */
- private void updateExternalFileTypes() {
+ public static Set fromString(String storedFileTypes) {
// First get a list of the default file types as a starting point:
- List types = new ArrayList<>(getDefaultExternalFileTypes());
+ Set types = new HashSet<>(getDefaultExternalFileTypes());
+
// If no changes have been stored, simply use the defaults:
- Optional storedFileTypes = Globals.prefs.getExternalFileTypes();
- if (storedFileTypes.isEmpty()) {
- externalFileTypes.clear();
- externalFileTypes.addAll(types);
- return;
+ if (StringUtil.isBlank(storedFileTypes)) {
+ return types;
}
// Read the prefs information for file types:
- String[][] vals = StringUtil
- .decodeStringDoubleArray(storedFileTypes.orElse(""));
+ String[][] vals = StringUtil.decodeStringDoubleArray(storedFileTypes);
for (String[] val : vals) {
if ((val.length == 2) && val[1].equals(FILE_TYPE_REMOVED_FLAG)) {
// This entry indicates that a default entry type should be removed:
@@ -248,32 +240,6 @@ private void updateExternalFileTypes() {
}
}
- // Finally, build the list of types based on the modified defaults list:
- externalFileTypes.addAll(types);
- }
-
- public Optional getExternalFileTypeByFile(Path file) {
- final String filePath = file.toString();
- final Optional extension = FileHelper.getFileExtension(filePath);
- return extension.flatMap(this::getExternalFileTypeByExt);
- }
-
- public Optional fromLinkedFile(LinkedFile linkedFile, boolean deduceUnknownType) {
- Optional type = getExternalFileTypeByName(linkedFile.getFileType());
- boolean isUnknownType = type.isEmpty() || (type.get() instanceof UnknownExternalFileType);
-
- if (isUnknownType && deduceUnknownType) {
- // No file type was recognized. Try to find a usable file type based on mime type:
- Optional mimeType = getExternalFileTypeByMimeType(linkedFile.getFileType());
- if (mimeType.isPresent()) {
- return mimeType;
- }
-
- // No type could be found from mime type. Try based on the extension:
- return FileHelper.getFileExtension(linkedFile.getLink())
- .flatMap(this::getExternalFileTypeByExt);
- } else {
- return type;
- }
+ return types;
}
}
diff --git a/src/main/java/org/jabref/gui/fieldeditors/LinkedFileViewModel.java b/src/main/java/org/jabref/gui/fieldeditors/LinkedFileViewModel.java
index 89a943d9e1f..65d5c505f75 100644
--- a/src/main/java/org/jabref/gui/fieldeditors/LinkedFileViewModel.java
+++ b/src/main/java/org/jabref/gui/fieldeditors/LinkedFileViewModel.java
@@ -16,11 +16,14 @@
import javax.net.ssl.SSLSocketFactory;
import javafx.beans.Observable;
+import javafx.beans.binding.Bindings;
+import javafx.beans.binding.ObjectBinding;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.StringProperty;
+import javafx.scene.Node;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.ButtonBar.ButtonData;
import javafx.scene.control.ButtonType;
@@ -82,7 +85,8 @@ public class LinkedFileViewModel extends AbstractViewModel {
private final TaskExecutor taskExecutor;
private final PreferencesService preferences;
private final LinkedFileHandler linkedFileHandler;
- private final ExternalFileTypes externalFileTypes;
+
+ private ObjectBinding linkedFileIconBinding;
private final Validator fileExistsValidator;
@@ -91,8 +95,7 @@ public LinkedFileViewModel(LinkedFile linkedFile,
BibDatabaseContext databaseContext,
TaskExecutor taskExecutor,
DialogService dialogService,
- PreferencesService preferences,
- ExternalFileTypes externalFileTypes) {
+ PreferencesService preferences) {
this.linkedFile = linkedFile;
this.preferences = preferences;
@@ -101,7 +104,6 @@ public LinkedFileViewModel(LinkedFile linkedFile,
this.entry = entry;
this.dialogService = dialogService;
this.taskExecutor = taskExecutor;
- this.externalFileTypes = externalFileTypes;
fileExistsValidator = new FunctionBasedValidator<>(
linkedFile.linkProperty(),
@@ -176,11 +178,19 @@ public Optional findIn(List directories) {
}
public JabRefIcon getTypeIcon() {
- return externalFileTypes.fromLinkedFile(linkedFile, false)
+ return ExternalFileTypes.getExternalFileTypeByLinkedFile(linkedFile, false, preferences.getFilePreferences())
.map(ExternalFileType::getIcon)
.orElse(IconTheme.JabRefIcons.FILE);
}
+ public ObjectBinding typeIconProperty() {
+ if (linkedFileIconBinding == null) {
+ linkedFileIconBinding = Bindings.createObjectBinding(() -> this.getTypeIcon().getGraphicNode(), linkedFile.fileTypeProperty());
+ }
+
+ return linkedFileIconBinding;
+ }
+
public void markAsAutomaticallyFound() {
isAutomaticallyFound.setValue(true);
}
@@ -199,7 +209,7 @@ public Observable[] getObservables() {
public void open() {
try {
- Optional type = ExternalFileTypes.getInstance().fromLinkedFile(linkedFile, true);
+ Optional type = ExternalFileTypes.getExternalFileTypeByLinkedFile(linkedFile, true, preferences.getFilePreferences());
boolean successful = JabRefDesktop.openExternalFileAnyFormat(databaseContext, preferences, linkedFile.getLink(), type);
if (!successful) {
dialogService.showErrorDialogAndWait(Localization.lang("File not found"), Localization.lang("Could not find file '%0'.", linkedFile.getLink()));
@@ -444,10 +454,12 @@ public void download() {
}
if (!isDuplicate) {
- LinkedFile newLinkedFile = LinkedFilesEditorViewModel.fromFile(destination, databaseContext.getFileDirectories(preferences.getFilePreferences()), externalFileTypes);
- List linkedFiles = entry.getFiles();
-
- entry.addLinkedFile(entry, linkedFile, newLinkedFile, linkedFiles);
+ // we need to call LinkedFileViewModel#fromFile, because we need to make the path relative to the configured directories
+ LinkedFile newLinkedFile = LinkedFilesEditorViewModel.fromFile(
+ destination,
+ databaseContext.getFileDirectories(preferences.getFilePreferences()),
+ preferences.getFilePreferences());
+ entry.replaceDownloadedFile(linkedFile.getLink(), newLinkedFile);
// Notify in bar when the file type is HTML.
if (newLinkedFile.getFileType().equals(StandardExternalFileType.URL.getName())) {
@@ -523,15 +535,15 @@ private Optional inferFileTypeFromMimeType(URLDownload urlDown
if (mimeType != null) {
LOGGER.debug("MIME Type suggested: " + mimeType);
- return externalFileTypes.getExternalFileTypeByMimeType(mimeType);
+ return ExternalFileTypes.getExternalFileTypeByMimeType(mimeType, preferences.getFilePreferences());
} else {
return Optional.empty();
}
}
private Optional inferFileTypeFromURL(String url) {
- return URLUtil.getSuffix(url)
- .flatMap(externalFileTypes::getExternalFileTypeByExt);
+ return URLUtil.getSuffix(url, preferences.getFilePreferences())
+ .flatMap(extension -> ExternalFileTypes.getExternalFileTypeByExt(extension, preferences.getFilePreferences()));
}
public LinkedFile getFile() {
@@ -548,8 +560,8 @@ public void parsePdfMetadataAndShowMergeDialog() {
dialog.addSource(Localization.lang("Entry"), entry);
dialog.addSource(Localization.lang("Verbatim"), wrapImporterToSupplier(new PdfVerbatimBibTextImporter(preferences.getImportFormatPreferences()), filePath));
dialog.addSource(Localization.lang("Embedded"), wrapImporterToSupplier(new PdfEmbeddedBibFileImporter(preferences.getImportFormatPreferences()), filePath));
- if (preferences.getImporterPreferences().isGrobidEnabled()) {
- dialog.addSource("Grobid", wrapImporterToSupplier(new PdfGrobidImporter(preferences.getImporterPreferences(), preferences.getImportFormatPreferences()), filePath));
+ if (preferences.getGrobidPreferences().isGrobidEnabled()) {
+ dialog.addSource("Grobid", wrapImporterToSupplier(new PdfGrobidImporter(preferences.getImportFormatPreferences()), filePath));
}
dialog.addSource(Localization.lang("XMP metadata"), wrapImporterToSupplier(new PdfXmpImporter(preferences.getXmpPreferences()), filePath));
dialog.addSource(Localization.lang("Content"), wrapImporterToSupplier(new PdfContentImporter(preferences.getImportFormatPreferences()), filePath));
diff --git a/src/main/java/org/jabref/gui/fieldeditors/LinkedFilesEditor.fxml b/src/main/java/org/jabref/gui/fieldeditors/LinkedFilesEditor.fxml
index 7d34c27164e..5f61cfd109c 100644
--- a/src/main/java/org/jabref/gui/fieldeditors/LinkedFilesEditor.fxml
+++ b/src/main/java/org/jabref/gui/fieldeditors/LinkedFilesEditor.fxml
@@ -6,40 +6,40 @@
+
-
-
-
+
+
+
+
+
diff --git a/src/main/java/org/jabref/gui/fieldeditors/LinkedFilesEditor.java b/src/main/java/org/jabref/gui/fieldeditors/LinkedFilesEditor.java
index 08a329d508d..e93c3952858 100644
--- a/src/main/java/org/jabref/gui/fieldeditors/LinkedFilesEditor.java
+++ b/src/main/java/org/jabref/gui/fieldeditors/LinkedFilesEditor.java
@@ -12,7 +12,9 @@
import javafx.scene.Parent;
import javafx.scene.control.Button;
import javafx.scene.control.ContextMenu;
+import javafx.scene.control.Label;
import javafx.scene.control.ListView;
+import javafx.scene.control.OverrunStyle;
import javafx.scene.control.ProgressBar;
import javafx.scene.control.SelectionMode;
import javafx.scene.control.SeparatorMenuItem;
@@ -84,7 +86,7 @@ public LinkedFilesEditor(Field field,
.load();
ViewModelListCellFactory cellFactory = new ViewModelListCellFactory()
- .withStringTooltip(LinkedFileViewModel::getDescription)
+ .withStringTooltip(LinkedFileViewModel::getDescriptionAndLink)
.withGraphic(this::createFileDisplay)
.withContextMenu(this::createContextMenuForFile)
.withOnMouseClickedEvent(this::handleItemMouseClick)
@@ -165,10 +167,17 @@ private Node createFileDisplay(LinkedFileViewModel linkedFile) {
progressIndicator.progressProperty().bind(linkedFile.downloadProgressProperty());
progressIndicator.visibleProperty().bind(linkedFile.downloadOngoingProperty());
+ Label label = new Label();
+ label.graphicProperty().bind(linkedFile.typeIconProperty());
+ label.textProperty().bind(linkedFile.linkProperty());
+ label.getStyleClass().setAll("file-row-text");
+ label.textOverrunProperty().setValue(OverrunStyle.LEADING_ELLIPSIS);
+ EasyBind.subscribe(linkedFile.isAutomaticallyFoundProperty(), found -> label.pseudoClassStateChanged(opacity, found));
+
HBox info = new HBox(8);
HBox.setHgrow(info, Priority.ALWAYS);
info.setStyle("-fx-padding: 0.5em 0 0.5em 0;"); // To align with buttons below which also have 0.5em padding
- info.getChildren().setAll(icon, link, desc, progressIndicator);
+ info.getChildren().setAll(label, progressIndicator);
Button acceptAutoLinkedFile = IconTheme.JabRefIcons.AUTO_LINKED_FILE.asButton();
acceptAutoLinkedFile.setTooltip(new Tooltip(Localization.lang("This file was found automatically. Do you want to link it to this entry?")));
@@ -177,7 +186,7 @@ private Node createFileDisplay(LinkedFileViewModel linkedFile) {
acceptAutoLinkedFile.setOnAction(event -> linkedFile.acceptAsLinked());
acceptAutoLinkedFile.getStyleClass().setAll("icon-button");
- Button writeMetadataToPdf = IconTheme.JabRefIcons.IMPORT.asButton();
+ Button writeMetadataToPdf = IconTheme.JabRefIcons.PDF_METADATA_WRITE.asButton();
writeMetadataToPdf.setTooltip(new Tooltip(Localization.lang("Write BibTeXEntry metadata to PDF.")));
writeMetadataToPdf.visibleProperty().bind(linkedFile.isOfflinePdfProperty());
writeMetadataToPdf.getStyleClass().setAll("icon-button");
@@ -186,18 +195,18 @@ private Node createFileDisplay(LinkedFileViewModel linkedFile) {
writeMetadataToPdf.disableProperty().bind(writeMetadataToPdfCommand.executableProperty().not());
writeMetadataToPdf.setOnAction(event -> writeMetadataToPdfCommand.execute());
- Button parsePdfMetadata = IconTheme.JabRefIcons.FILE_SEARCH.asButton();
+ Button parsePdfMetadata = IconTheme.JabRefIcons.PDF_METADATA_READ.asButton();
parsePdfMetadata.setTooltip(new Tooltip(Localization.lang("Parse Metadata from PDF.")));
parsePdfMetadata.visibleProperty().bind(linkedFile.isOfflinePdfProperty());
parsePdfMetadata.setOnAction(event -> {
- GrobidOptInDialogHelper.showAndWaitIfUserIsUndecided(dialogService, preferencesService.getImporterPreferences());
+ GrobidOptInDialogHelper.showAndWaitIfUserIsUndecided(dialogService, preferencesService.getGrobidPreferences());
linkedFile.parsePdfMetadataAndShowMergeDialog();
});
parsePdfMetadata.getStyleClass().setAll("icon-button");
- HBox container = new HBox(10);
+ HBox container = new HBox(2);
container.setPrefHeight(Double.NEGATIVE_INFINITY);
-
+ container.maxWidthProperty().bind(listView.widthProperty().subtract(20d));
container.getChildren().addAll(acceptAutoLinkedFile, info, writeMetadataToPdf, parsePdfMetadata);
return container;
@@ -261,7 +270,7 @@ private void handleItemMouseClick(LinkedFileViewModel linkedFile, MouseEvent eve
@Override
public double getWeight() {
- return 2;
+ return 3;
}
private ContextMenu createContextMenuForFile(LinkedFileViewModel linkedFile) {
diff --git a/src/main/java/org/jabref/gui/fieldeditors/LinkedFilesEditorViewModel.java b/src/main/java/org/jabref/gui/fieldeditors/LinkedFilesEditorViewModel.java
index 4525c7f050c..44a3f15d538 100644
--- a/src/main/java/org/jabref/gui/fieldeditors/LinkedFilesEditorViewModel.java
+++ b/src/main/java/org/jabref/gui/fieldeditors/LinkedFilesEditorViewModel.java
@@ -38,6 +38,7 @@
import org.jabref.model.entry.LinkedFile;
import org.jabref.model.entry.field.Field;
import org.jabref.model.util.FileHelper;
+import org.jabref.preferences.FilePreferences;
import org.jabref.preferences.PreferencesService;
public class LinkedFilesEditorViewModel extends AbstractEditorViewModel {
@@ -48,7 +49,6 @@ public class LinkedFilesEditorViewModel extends AbstractEditorViewModel {
private final BibDatabaseContext databaseContext;
private final TaskExecutor taskExecutor;
private final PreferencesService preferences;
- private final ExternalFileTypes externalFileTypes = ExternalFileTypes.getInstance();
public LinkedFilesEditorViewModel(Field field, SuggestionProvider> suggestionProvider,
DialogService dialogService,
@@ -87,27 +87,25 @@ private static String getStringRepresentation(List files) {
*
* TODO: Move this method to {@link LinkedFile} as soon as {@link CustomExternalFileType} lives in model.
*/
- public static LinkedFile fromFile(Path file, List fileDirectories, ExternalFileTypes externalFileTypesFile) {
+ public static LinkedFile fromFile(Path file, List fileDirectories, FilePreferences filePreferences) {
String fileExtension = FileHelper.getFileExtension(file).orElse("");
- ExternalFileType suggestedFileType = externalFileTypesFile
- .getExternalFileTypeByExt(fileExtension)
- .orElse(new UnknownExternalFileType(fileExtension));
+ ExternalFileType suggestedFileType = ExternalFileTypes.getExternalFileTypeByExt(fileExtension, filePreferences)
+ .orElse(new UnknownExternalFileType(fileExtension));
Path relativePath = FileUtil.relativize(file, fileDirectories);
return new LinkedFile("", relativePath, suggestedFileType.getName());
}
- public LinkedFileViewModel fromFile(Path file) {
+ public LinkedFileViewModel fromFile(Path file, FilePreferences filePreferences) {
List fileDirectories = databaseContext.getFileDirectories(preferences.getFilePreferences());
- LinkedFile linkedFile = fromFile(file, fileDirectories, externalFileTypes);
+ LinkedFile linkedFile = fromFile(file, fileDirectories, filePreferences);
return new LinkedFileViewModel(
linkedFile,
entry,
databaseContext,
taskExecutor,
dialogService,
- preferences,
- externalFileTypes);
+ preferences);
}
public boolean isFulltextLookupInProgress() {
@@ -122,8 +120,7 @@ private List parseToFileViewModel(String stringValue) {
databaseContext,
taskExecutor,
dialogService,
- preferences,
- externalFileTypes))
+ preferences))
.collect(Collectors.toList());
}
@@ -145,15 +142,14 @@ public void addNewFile() {
List fileDirectories = databaseContext.getFileDirectories(preferences.getFilePreferences());
dialogService.showFileOpenDialogAndGetMultipleFiles(fileDialogConfiguration).forEach(newFile -> {
- LinkedFile newLinkedFile = fromFile(newFile, fileDirectories, externalFileTypes);
+ LinkedFile newLinkedFile = fromFile(newFile, fileDirectories, preferences.getFilePreferences());
files.add(new LinkedFileViewModel(
newLinkedFile,
entry,
databaseContext,
taskExecutor,
dialogService,
- preferences,
- externalFileTypes));
+ preferences));
});
}
@@ -178,8 +174,7 @@ private List findAssociatedNotLinkedFiles(BibEntry entry) {
AutoSetFileLinksUtil util = new AutoSetFileLinksUtil(
databaseContext,
preferences.getFilePreferences(),
- preferences.getAutoLinkPreferences(),
- ExternalFileTypes.getInstance());
+ preferences.getAutoLinkPreferences());
try {
List linkedFiles = util.findAssociatedNotLinkedFiles(entry);
for (LinkedFile linkedFile : linkedFiles) {
@@ -189,8 +184,7 @@ private List findAssociatedNotLinkedFiles(BibEntry entry) {
databaseContext,
taskExecutor,
dialogService,
- preferences,
- externalFileTypes);
+ preferences);
newLinkedFile.markAsAutomaticallyFound();
result.add(newLinkedFile);
}
@@ -241,8 +235,7 @@ private void addFromURL(URL url) {
databaseContext,
taskExecutor,
dialogService,
- preferences,
- externalFileTypes);
+ preferences);
files.add(onlineFile);
onlineFile.download();
}
diff --git a/src/main/java/org/jabref/gui/fieldeditors/URLUtil.java b/src/main/java/org/jabref/gui/fieldeditors/URLUtil.java
index 1ee8e8b2652..d7f58db7c00 100644
--- a/src/main/java/org/jabref/gui/fieldeditors/URLUtil.java
+++ b/src/main/java/org/jabref/gui/fieldeditors/URLUtil.java
@@ -8,6 +8,7 @@
import java.util.Optional;
import org.jabref.gui.externalfiletype.ExternalFileTypes;
+import org.jabref.preferences.FilePreferences;
public class URLUtil {
private static final String URL_EXP = "^(https?|ftp)://.+";
@@ -90,7 +91,7 @@ public static boolean isURL(String url) {
* @param link The link
* @return The suffix, excluding the dot (e.g. "pdf")
*/
- public static Optional getSuffix(final String link) {
+ public static Optional getSuffix(final String link, FilePreferences filePreferences) {
String strippedLink = link;
try {
// Try to strip the query string, if any, to get the correct suffix:
@@ -110,7 +111,7 @@ public static Optional getSuffix(final String link) {
} else {
suffix = strippedLink.substring(strippedLinkIndex + 1);
}
- if (!ExternalFileTypes.getInstance().isExternalFileTypeByExt(suffix)) {
+ if (!ExternalFileTypes.isExternalFileTypeByExt(suffix, filePreferences)) {
// If the suffix doesn't seem to give any reasonable file type, try
// with the non-stripped link:
int index = link.lastIndexOf('.');
diff --git a/src/main/java/org/jabref/gui/icon/IconTheme.java b/src/main/java/org/jabref/gui/icon/IconTheme.java
index 80eb1824a9b..a573bf6c74c 100644
--- a/src/main/java/org/jabref/gui/icon/IconTheme.java
+++ b/src/main/java/org/jabref/gui/icon/IconTheme.java
@@ -192,6 +192,8 @@ public enum JabRefIcons implements JabRefIcon {
DELETE_ENTRY(MaterialDesignD.DELETE),
SEARCH(MaterialDesignM.MAGNIFY),
FILE_SEARCH(MaterialDesignF.FILE_FIND),
+ PDF_METADATA_READ(MaterialDesignF.FORMAT_ALIGN_TOP),
+ PDF_METADATA_WRITE(MaterialDesignF.FORMAT_ALIGN_BOTTOM),
ADVANCED_SEARCH(Color.CYAN, MaterialDesignM.MAGNIFY),
PREFERENCES(MaterialDesignC.COG),
SELECTORS(MaterialDesignS.STAR_SETTINGS),
@@ -309,6 +311,7 @@ public enum JabRefIcons implements JabRefIcon {
NEW_GROUP(MaterialDesignP.PLUS),
OPEN_LINK(MaterialDesignO.OPEN_IN_NEW),
LOOKUP_IDENTIFIER(MaterialDesignS.SEARCH_WEB),
+ LINKED_FILE_ADD(MaterialDesignP.PLUS),
FETCH_FULLTEXT(MaterialDesignS.SEARCH_WEB),
FETCH_BY_IDENTIFIER(MaterialDesignC.CLIPBOARD_ARROW_DOWN),
TOGGLE_ABBREVIATION(MaterialDesignF.FORMAT_ALIGN_CENTER),
diff --git a/src/main/java/org/jabref/gui/importer/GenerateEntryFromIdAction.java b/src/main/java/org/jabref/gui/importer/GenerateEntryFromIdAction.java
index 619ec5a8e35..95e2eb99d53 100644
--- a/src/main/java/org/jabref/gui/importer/GenerateEntryFromIdAction.java
+++ b/src/main/java/org/jabref/gui/importer/GenerateEntryFromIdAction.java
@@ -8,7 +8,6 @@
import org.jabref.gui.StateManager;
import org.jabref.gui.actions.SimpleCommand;
import org.jabref.gui.externalfiles.ImportHandler;
-import org.jabref.gui.externalfiletype.ExternalFileTypes;
import org.jabref.gui.util.BackgroundTask;
import org.jabref.gui.util.TaskExecutor;
import org.jabref.logic.importer.CompositeIdFetcher;
@@ -32,7 +31,13 @@ public class GenerateEntryFromIdAction extends SimpleCommand {
private final PopOver entryFromIdPopOver;
private final StateManager stateManager;
- public GenerateEntryFromIdAction(LibraryTab libraryTab, DialogService dialogService, PreferencesService preferencesService, TaskExecutor taskExecutor, PopOver entryFromIdPopOver, String identifier, StateManager stateManager) {
+ public GenerateEntryFromIdAction(LibraryTab libraryTab,
+ DialogService dialogService,
+ PreferencesService preferencesService,
+ TaskExecutor taskExecutor,
+ PopOver entryFromIdPopOver,
+ String identifier,
+ StateManager stateManager) {
this.libraryTab = libraryTab;
this.dialogService = dialogService;
this.preferencesService = preferencesService;
@@ -69,7 +74,7 @@ public void execute() {
Optional result = bibEntry;
if (result.isPresent()) {
final BibEntry entry = result.get();
- ImportHandler handler = new ImportHandler(libraryTab.getBibDatabaseContext(), ExternalFileTypes.getInstance(), preferencesService, Globals.getFileUpdateMonitor(), libraryTab.getUndoManager(), stateManager, dialogService, null);
+ ImportHandler handler = new ImportHandler(libraryTab.getBibDatabaseContext(), preferencesService, Globals.getFileUpdateMonitor(), libraryTab.getUndoManager(), stateManager, dialogService, null);
handler.importEntryWithDuplicateCheck(libraryTab.getBibDatabaseContext(), entry);
} else {
dialogService.notify("No entry found or import canceled");
diff --git a/src/main/java/org/jabref/gui/importer/GrobidOptInDialogHelper.java b/src/main/java/org/jabref/gui/importer/GrobidOptInDialogHelper.java
index d58d6c8d5d4..cf21a38a30c 100644
--- a/src/main/java/org/jabref/gui/importer/GrobidOptInDialogHelper.java
+++ b/src/main/java/org/jabref/gui/importer/GrobidOptInDialogHelper.java
@@ -1,7 +1,7 @@
package org.jabref.gui.importer;
import org.jabref.gui.DialogService;
-import org.jabref.logic.importer.ImporterPreferences;
+import org.jabref.logic.importer.fetcher.GrobidPreferences;
import org.jabref.logic.l10n.Localization;
/**
@@ -18,7 +18,7 @@ public class GrobidOptInDialogHelper {
* @param dialogService the DialogService to use
* @return if the user enabled Grobid, either in the past or after being asked by the dialog.
*/
- public static boolean showAndWaitIfUserIsUndecided(DialogService dialogService, ImporterPreferences preferences) {
+ public static boolean showAndWaitIfUserIsUndecided(DialogService dialogService, GrobidPreferences preferences) {
if (preferences.isGrobidEnabled()) {
return true;
}
diff --git a/src/main/java/org/jabref/gui/importer/ImportAction.java b/src/main/java/org/jabref/gui/importer/ImportAction.java
index 3b08767886d..4b32d0d74db 100644
--- a/src/main/java/org/jabref/gui/importer/ImportAction.java
+++ b/src/main/java/org/jabref/gui/importer/ImportAction.java
@@ -121,8 +121,11 @@ private List doImport(List files)
if (importer.isEmpty()) {
// Unknown format:
DefaultTaskExecutor.runAndWaitInJavaFXThread(() -> {
- if (fileIsPdf(filename) && GrobidOptInDialogHelper.showAndWaitIfUserIsUndecided(frame.getDialogService(), prefs.getImporterPreferences())) {
- Globals.IMPORT_FORMAT_READER.resetImportFormats(prefs.getImporterPreferences(), prefs.getImportFormatPreferences(), prefs.getXmpPreferences(), Globals.getFileUpdateMonitor());
+ if (fileIsPdf(filename) && GrobidOptInDialogHelper.showAndWaitIfUserIsUndecided(frame.getDialogService(), prefs.getGrobidPreferences())) {
+ Globals.IMPORT_FORMAT_READER.resetImportFormats(
+ prefs.getImporterPreferences(),
+ prefs.getImportFormatPreferences(),
+ Globals.getFileUpdateMonitor());
}
frame.getDialogService().notify(Localization.lang("Importing in unknown format") + "...");
});
@@ -130,8 +133,13 @@ private List doImport(List files)
imports.add(Globals.IMPORT_FORMAT_READER.importUnknownFormat(filename, Globals.getFileUpdateMonitor()));
} else {
DefaultTaskExecutor.runAndWaitInJavaFXThread(() -> {
- if ((importer.get() instanceof PdfGrobidImporter) || ((importer.get() instanceof PdfMergeMetadataImporter) && GrobidOptInDialogHelper.showAndWaitIfUserIsUndecided(frame.getDialogService(), prefs.getImporterPreferences()))) {
- Globals.IMPORT_FORMAT_READER.resetImportFormats(prefs.getImporterPreferences(), prefs.getImportFormatPreferences(), prefs.getXmpPreferences(), Globals.getFileUpdateMonitor());
+ if ((importer.get() instanceof PdfGrobidImporter) || (
+ (importer.get() instanceof PdfMergeMetadataImporter)
+ && GrobidOptInDialogHelper.showAndWaitIfUserIsUndecided(frame.getDialogService(), prefs.getGrobidPreferences()))) {
+ Globals.IMPORT_FORMAT_READER.resetImportFormats(
+ prefs.getImporterPreferences(),
+ prefs.getImportFormatPreferences(),
+ Globals.getFileUpdateMonitor());
}
frame.getDialogService().notify(Localization.lang("Importing in %0 format", importer.get().getName()) + "...");
});
diff --git a/src/main/java/org/jabref/gui/importer/ImportEntriesViewModel.java b/src/main/java/org/jabref/gui/importer/ImportEntriesViewModel.java
index 877a677234c..0f75519e910 100644
--- a/src/main/java/org/jabref/gui/importer/ImportEntriesViewModel.java
+++ b/src/main/java/org/jabref/gui/importer/ImportEntriesViewModel.java
@@ -17,7 +17,6 @@
import org.jabref.gui.StateManager;
import org.jabref.gui.duplicationFinder.DuplicateResolverDialog;
import org.jabref.gui.externalfiles.ImportHandler;
-import org.jabref.gui.externalfiletype.ExternalFileTypes;
import org.jabref.gui.fieldeditors.LinkedFileViewModel;
import org.jabref.gui.util.BackgroundTask;
import org.jabref.gui.util.TaskExecutor;
@@ -149,8 +148,7 @@ public void importEntries(List entriesToImport, boolean shouldDownload
databaseContext,
taskExecutor,
dialogService,
- preferences,
- ExternalFileTypes.getInstance()).download());
+ preferences).download());
}
}
@@ -166,7 +164,6 @@ public void importEntries(List entriesToImport, boolean shouldDownload
private void buildImportHandlerThenImportEntries(List entriesToImport) {
ImportHandler importHandler = new ImportHandler(
databaseContext,
- ExternalFileTypes.getInstance(),
preferences,
fileUpdateMonitor,
undoManager,
diff --git a/src/main/java/org/jabref/gui/importer/actions/OpenDatabaseAction.java b/src/main/java/org/jabref/gui/importer/actions/OpenDatabaseAction.java
index 052dba2ccac..45e95158e29 100644
--- a/src/main/java/org/jabref/gui/importer/actions/OpenDatabaseAction.java
+++ b/src/main/java/org/jabref/gui/importer/actions/OpenDatabaseAction.java
@@ -5,8 +5,6 @@
import java.nio.file.Path;
import java.sql.SQLException;
import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
@@ -21,6 +19,7 @@
import org.jabref.gui.StateManager;
import org.jabref.gui.actions.SimpleCommand;
import org.jabref.gui.dialogs.BackupUIManager;
+import org.jabref.gui.menus.FileHistoryMenu;
import org.jabref.gui.shared.SharedDatabaseUIManager;
import org.jabref.gui.theme.ThemeManager;
import org.jabref.gui.util.BackgroundTask;
@@ -46,7 +45,7 @@ public class OpenDatabaseAction extends SimpleCommand {
// List of actions that may need to be called after opening the file. Such as
// upgrade actions etc. that may depend on the JabRef version that wrote the file:
- private static final List POST_OPEN_ACTIONS = Arrays.asList(
+ private static final List POST_OPEN_ACTIONS = List.of(
// Migrations:
// Warning for migrating the Review into the Comment field
new MergeReviewIntoCommentAction(),
@@ -95,7 +94,7 @@ public void execute() {
.build();
List filesToOpen = dialogService.showFileOpenDialogAndGetMultipleFiles(fileDialogConfiguration);
- openFiles(filesToOpen, true);
+ openFiles(filesToOpen);
}
/**
@@ -111,20 +110,22 @@ private Path getInitialDirectory() {
}
/**
- * Opens the given file. If null or 404, nothing happens
+ * Opens the given file. If null or 404, nothing happens.
+ * In case the file is already opened, that panel is raised.
*
* @param file the file, may be null or not existing
*/
- public void openFile(Path file, boolean raisePanel) {
- openFiles(new ArrayList<>(List.of(file)), raisePanel);
+ public void openFile(Path file) {
+ openFiles(new ArrayList<>(List.of(file)));
}
/**
- * Opens the given files. If one of it is null or 404, nothing happens
+ * Opens the given files. If one of it is null or 404, nothing happens.
+ * In case the file is already opened, that panel is raised.
*
* @param filesToOpen the filesToOpen, may be null or not existing
*/
- public void openFiles(List filesToOpen, boolean raisePanel) {
+ public void openFiles(List filesToOpen) {
LibraryTab toRaise = null;
int initialCount = filesToOpen.size();
int removed = 0;
@@ -152,16 +153,12 @@ public void openFiles(List filesToOpen, boolean raisePanel) {
// Run the actual open in a thread to prevent the program
// locking until the file is loaded.
if (!filesToOpen.isEmpty()) {
- final List theFiles = Collections.unmodifiableList(filesToOpen);
-
- for (Path theFile : theFiles) {
+ FileHistoryMenu fileHistory = frame.getFileHistory();
+ filesToOpen.forEach(theFile -> {
// This method will execute the concrete file opening and loading in a background thread
- openTheFile(theFile, raisePanel);
- }
-
- for (Path theFile : theFiles) {
- frame.getFileHistory().newFile(theFile);
- }
+ openTheFile(theFile);
+ fileHistory.newFile(theFile);
+ });
} else if (toRaise != null) {
// If no files are remaining to open, this could mean that a file was
// already open. If so, we may have to raise the correct tab:
@@ -170,9 +167,11 @@ public void openFiles(List filesToOpen, boolean raisePanel) {
}
/**
- * @param file the file, may be null or not existing
+ * This is the real file opening. Should be called via {@link #openFile(Path)}
+ *
+ * @param file the file, may be NOT null, but may not be existing
*/
- private void openTheFile(Path file, boolean raisePanel) {
+ private void openTheFile(Path file) {
Objects.requireNonNull(file);
if (!Files.exists(file)) {
return;
@@ -185,6 +184,10 @@ private void openTheFile(Path file, boolean raisePanel) {
backgroundTask.onFinished(() -> trackOpenNewDatabase(newTab));
}
+ /**
+ * This method is similar to {@link org.jabref.gui.JabRefGUI#openLastEditedDatabases()}.
+ * This method also has the capability to open remote shared databases
+ */
private ParserResult loadDatabase(Path file) throws Exception {
Path fileToLoad = file.toAbsolutePath();
diff --git a/src/main/java/org/jabref/gui/linkedfile/AttachFileAction.java b/src/main/java/org/jabref/gui/linkedfile/AttachFileAction.java
index 00e0c1f3b4b..8dad9f0399c 100644
--- a/src/main/java/org/jabref/gui/linkedfile/AttachFileAction.java
+++ b/src/main/java/org/jabref/gui/linkedfile/AttachFileAction.java
@@ -8,7 +8,6 @@
import org.jabref.gui.StateManager;
import org.jabref.gui.actions.ActionHelper;
import org.jabref.gui.actions.SimpleCommand;
-import org.jabref.gui.externalfiletype.ExternalFileTypes;
import org.jabref.gui.fieldeditors.LinkedFilesEditorViewModel;
import org.jabref.gui.undo.UndoableFieldChange;
import org.jabref.gui.util.FileDialogConfiguration;
@@ -62,9 +61,10 @@ public void execute() {
.build();
dialogService.showFileOpenDialog(fileDialogConfiguration).ifPresent(newFile -> {
- LinkedFile linkedFile = LinkedFilesEditorViewModel.fromFile(newFile,
+ LinkedFile linkedFile = LinkedFilesEditorViewModel.fromFile(
+ newFile,
databaseContext.getFileDirectories(filePreferences),
- ExternalFileTypes.getInstance());
+ filePreferences);
LinkedFileEditDialogView dialog = new LinkedFileEditDialogView(linkedFile);
diff --git a/src/main/java/org/jabref/gui/linkedfile/LinkedFileEditDialog.fxml b/src/main/java/org/jabref/gui/linkedfile/LinkedFileEditDialog.fxml
index 592a99d1567..364f3da9214 100644
--- a/src/main/java/org/jabref/gui/linkedfile/LinkedFileEditDialog.fxml
+++ b/src/main/java/org/jabref/gui/linkedfile/LinkedFileEditDialog.fxml
@@ -1,42 +1,52 @@
-
+
-
+
+
-
+
-
-
+
+
-
-
-
+
+
+
-
-
+
-
-
-
-
diff --git a/src/main/java/org/jabref/gui/linkedfile/LinkedFileEditDialogView.java b/src/main/java/org/jabref/gui/linkedfile/LinkedFileEditDialogView.java
index f8ad3e1f7bc..3d6149a203b 100644
--- a/src/main/java/org/jabref/gui/linkedfile/LinkedFileEditDialogView.java
+++ b/src/main/java/org/jabref/gui/linkedfile/LinkedFileEditDialogView.java
@@ -11,8 +11,9 @@
import org.jabref.gui.DialogService;
import org.jabref.gui.StateManager;
import org.jabref.gui.externalfiletype.ExternalFileType;
-import org.jabref.gui.externalfiletype.ExternalFileTypes;
import org.jabref.gui.util.BaseDialog;
+import org.jabref.gui.util.ViewModelListCellFactory;
+import org.jabref.logic.l10n.Localization;
import org.jabref.model.entry.LinkedFile;
import org.jabref.preferences.PreferencesService;
@@ -32,17 +33,17 @@ public class LinkedFileEditDialogView extends BaseDialog {
private LinkedFilesEditDialogViewModel viewModel;
private final LinkedFile linkedFile;
- private final ExternalFileTypes externalFileTypes;
public LinkedFileEditDialogView(LinkedFile linkedFile) {
this.linkedFile = linkedFile;
- this.externalFileTypes = ExternalFileTypes.getInstance();
ViewLoader.view(this)
.load()
.setAsContent(this.getDialogPane());
this.getDialogPane().getButtonTypes().addAll(ButtonType.APPLY, ButtonType.CANCEL);
+ this.setResizable(false);
+ this.setTitle(Localization.lang("Edit file link"));
this.setResultConverter(button -> {
if (button == ButtonType.APPLY) {
@@ -55,8 +56,13 @@ public LinkedFileEditDialogView(LinkedFile linkedFile) {
@FXML
private void initialize() {
- viewModel = new LinkedFilesEditDialogViewModel(linkedFile, stateManager.getActiveDatabase().get(), dialogService, preferences.getFilePreferences(), externalFileTypes);
+ viewModel = new LinkedFilesEditDialogViewModel(linkedFile, stateManager.getActiveDatabase().get(), dialogService, preferences.getFilePreferences());
fileType.itemsProperty().bindBidirectional(viewModel.externalFileTypeProperty());
+ new ViewModelListCellFactory()
+ .withIcon(ExternalFileType::getIcon)
+ .withText(ExternalFileType::getName)
+ .install(fileType);
+
description.textProperty().bindBidirectional(viewModel.descriptionProperty());
link.textProperty().bindBidirectional(viewModel.linkProperty());
fileType.valueProperty().bindBidirectional(viewModel.selectedExternalFileTypeProperty());
diff --git a/src/main/java/org/jabref/gui/linkedfile/LinkedFilesEditDialogViewModel.java b/src/main/java/org/jabref/gui/linkedfile/LinkedFilesEditDialogViewModel.java
index 12d3f3f45ce..a46e908c358 100644
--- a/src/main/java/org/jabref/gui/linkedfile/LinkedFilesEditDialogViewModel.java
+++ b/src/main/java/org/jabref/gui/linkedfile/LinkedFilesEditDialogViewModel.java
@@ -41,14 +41,15 @@ public class LinkedFilesEditDialogViewModel extends AbstractViewModel {
private final BibDatabaseContext database;
private final DialogService dialogService;
private final FilePreferences filePreferences;
- private final ExternalFileTypes externalFileTypes;
- public LinkedFilesEditDialogViewModel(LinkedFile linkedFile, BibDatabaseContext database, DialogService dialogService, FilePreferences filePreferences, ExternalFileTypes externalFileTypes) {
+ public LinkedFilesEditDialogViewModel(LinkedFile linkedFile,
+ BibDatabaseContext database,
+ DialogService dialogService,
+ FilePreferences filePreferences) {
this.database = database;
this.dialogService = dialogService;
this.filePreferences = filePreferences;
- this.externalFileTypes = externalFileTypes;
- allExternalFileTypes.set(FXCollections.observableArrayList(externalFileTypes.getExternalFileTypeSelection()));
+ allExternalFileTypes.set(FXCollections.observableArrayList(filePreferences.getExternalFileTypes()));
monadicSelectedExternalFileType = EasyBind.wrapNullable(selectedExternalFileType);
setValues(linkedFile);
@@ -58,12 +59,14 @@ private void setExternalFileTypeByExtension(String link) {
if (!link.isEmpty()) {
// Check if this looks like a remote link:
if (REMOTE_LINK_PATTERN.matcher(link).matches()) {
- externalFileTypes.getExternalFileTypeByExt("html").ifPresent(selectedExternalFileType::setValue);
+ ExternalFileTypes.getExternalFileTypeByExt("html", filePreferences)
+ .ifPresent(selectedExternalFileType::setValue);
}
// Try to guess the file type:
String theLink = link.trim();
- externalFileTypes.getExternalFileTypeForName(theLink).ifPresent(selectedExternalFileType::setValue);
+ ExternalFileTypes.getExternalFileTypeForName(theLink, filePreferences)
+ .ifPresent(selectedExternalFileType::setValue);
}
}
@@ -101,7 +104,7 @@ public void setValues(LinkedFile linkedFile) {
selectedExternalFileType.setValue(null);
// See what is a reasonable selection for the type combobox:
- Optional fileType = externalFileTypes.fromLinkedFile(linkedFile, false);
+ Optional fileType = ExternalFileTypes.getExternalFileTypeByLinkedFile(linkedFile, false, filePreferences);
if (fileType.isPresent() && !(fileType.get() instanceof UnknownExternalFileType)) {
selectedExternalFileType.setValue(fileType.get());
} else if ((linkedFile.getLink() != null) && (!linkedFile.getLink().isEmpty())) {
diff --git a/src/main/java/org/jabref/gui/maintable/CellFactory.java b/src/main/java/org/jabref/gui/maintable/CellFactory.java
index 9f0940831d4..13c65f6a336 100644
--- a/src/main/java/org/jabref/gui/maintable/CellFactory.java
+++ b/src/main/java/org/jabref/gui/maintable/CellFactory.java
@@ -8,7 +8,6 @@
import javafx.scene.Node;
import org.jabref.gui.externalfiletype.ExternalFileType;
-import org.jabref.gui.externalfiletype.ExternalFileTypes;
import org.jabref.gui.icon.IconTheme;
import org.jabref.gui.icon.JabRefIcon;
import org.jabref.gui.specialfields.SpecialFieldViewModel;
@@ -22,7 +21,7 @@ public class CellFactory {
private final Map TABLE_ICONS = new HashMap<>();
- public CellFactory(ExternalFileTypes externalFileTypes, PreferencesService preferencesService, UndoManager undoManager) {
+ public CellFactory(PreferencesService preferencesService, UndoManager undoManager) {
JabRefIcon icon;
icon = IconTheme.JabRefIcons.PDF_FILE;
// icon.setToo(Localization.lang("Open") + " PDF");
@@ -56,7 +55,7 @@ public CellFactory(ExternalFileTypes externalFileTypes, PreferencesService prefe
// icon.setToolTipText(Localization.lang("Open file"));
TABLE_ICONS.put(StandardField.FILE, icon);
- for (ExternalFileType fileType : externalFileTypes.getExternalFileTypeSelection()) {
+ for (ExternalFileType fileType : preferencesService.getFilePreferences().getExternalFileTypes()) {
icon = fileType.getIcon();
// icon.setToolTipText(Localization.lang("Open %0 file", fileType.getName()));
TABLE_ICONS.put(fileType.getField(), icon);
diff --git a/src/main/java/org/jabref/gui/maintable/MainTable.java b/src/main/java/org/jabref/gui/maintable/MainTable.java
index d37f3bb09c4..8f7fff99355 100644
--- a/src/main/java/org/jabref/gui/maintable/MainTable.java
+++ b/src/main/java/org/jabref/gui/maintable/MainTable.java
@@ -34,7 +34,6 @@
import org.jabref.gui.actions.StandardActions;
import org.jabref.gui.edit.EditAction;
import org.jabref.gui.externalfiles.ImportHandler;
-import org.jabref.gui.externalfiletype.ExternalFileTypes;
import org.jabref.gui.keyboard.KeyBinding;
import org.jabref.gui.keyboard.KeyBindingRepository;
import org.jabref.gui.maintable.columns.LibraryColumn;
@@ -79,7 +78,6 @@ public MainTable(MainTableDataModel model,
PreferencesService preferencesService,
DialogService dialogService,
StateManager stateManager,
- ExternalFileTypes externalFileTypes,
KeyBindingRepository keyBindingRepository,
ClipBoardManager clipBoardManager,
ImportFormatReader importFormatReader) {
@@ -95,7 +93,7 @@ public MainTable(MainTableDataModel model,
MainTablePreferences mainTablePreferences = preferencesService.getMainTablePreferences();
importHandler = new ImportHandler(
- database, externalFileTypes,
+ database,
preferencesService,
Globals.getFileUpdateMonitor(),
undoManager,
@@ -113,7 +111,6 @@ public MainTable(MainTableDataModel model,
database,
preferencesService,
preferencesService.getColumnPreferences(),
- externalFileTypes,
libraryTab.getUndoManager(),
dialogService,
stateManager).createColumns());
diff --git a/src/main/java/org/jabref/gui/maintable/MainTableColumnFactory.java b/src/main/java/org/jabref/gui/maintable/MainTableColumnFactory.java
index ece234f9ff4..52cf6a2f04f 100644
--- a/src/main/java/org/jabref/gui/maintable/MainTableColumnFactory.java
+++ b/src/main/java/org/jabref/gui/maintable/MainTableColumnFactory.java
@@ -23,7 +23,6 @@
import org.jabref.gui.DialogService;
import org.jabref.gui.StateManager;
-import org.jabref.gui.externalfiletype.ExternalFileTypes;
import org.jabref.gui.icon.IconTheme;
import org.jabref.gui.maintable.columns.FieldColumn;
import org.jabref.gui.maintable.columns.FileColumn;
@@ -54,7 +53,6 @@ public class MainTableColumnFactory {
private final PreferencesService preferencesService;
private final ColumnPreferences columnPreferences;
- private final ExternalFileTypes externalFileTypes;
private final BibDatabaseContext database;
private final CellFactory cellFactory;
private final UndoManager undoManager;
@@ -64,16 +62,14 @@ public class MainTableColumnFactory {
public MainTableColumnFactory(BibDatabaseContext database,
PreferencesService preferencesService,
ColumnPreferences abstractColumnPrefs,
- ExternalFileTypes externalFileTypes,
UndoManager undoManager,
DialogService dialogService,
StateManager stateManager) {
this.database = Objects.requireNonNull(database);
this.preferencesService = Objects.requireNonNull(preferencesService);
this.columnPreferences = abstractColumnPrefs;
- this.externalFileTypes = Objects.requireNonNull(externalFileTypes);
this.dialogService = dialogService;
- this.cellFactory = new CellFactory(externalFileTypes, preferencesService, undoManager);
+ this.cellFactory = new CellFactory(preferencesService, undoManager);
this.undoManager = undoManager;
this.stateManager = stateManager;
}
@@ -233,7 +229,6 @@ private TableColumn
private TableColumn> createFilesColumn(MainTableColumnModel columnModel) {
return new FileColumn(columnModel,
database,
- externalFileTypes,
dialogService,
preferencesService);
}
@@ -244,7 +239,6 @@ private TableColumn> createFilesColumn(
private TableColumn> createExtraFileColumn(MainTableColumnModel columnModel) {
return new FileColumn(columnModel,
database,
- externalFileTypes,
dialogService,
preferencesService,
columnModel.getQualifier());
diff --git a/src/main/java/org/jabref/gui/maintable/OpenExternalFileAction.java b/src/main/java/org/jabref/gui/maintable/OpenExternalFileAction.java
index 9f669a18431..499f07a3526 100644
--- a/src/main/java/org/jabref/gui/maintable/OpenExternalFileAction.java
+++ b/src/main/java/org/jabref/gui/maintable/OpenExternalFileAction.java
@@ -8,7 +8,6 @@
import org.jabref.gui.StateManager;
import org.jabref.gui.actions.ActionHelper;
import org.jabref.gui.actions.SimpleCommand;
-import org.jabref.gui.externalfiletype.ExternalFileTypes;
import org.jabref.gui.fieldeditors.LinkedFileViewModel;
import org.jabref.logic.l10n.Localization;
import org.jabref.model.entry.BibEntry;
@@ -68,8 +67,7 @@ public void execute() {
databaseContext,
Globals.TASK_EXECUTOR,
dialogService,
- preferencesService,
- ExternalFileTypes.getInstance());
+ preferencesService);
linkedFileViewModelList.add(linkedFileViewModel);
}
@@ -93,8 +91,7 @@ public void execute() {
databaseContext,
Globals.TASK_EXECUTOR,
dialogService,
- preferencesService,
- ExternalFileTypes.getInstance());
+ preferencesService);
linkedFileViewModel.open();
}
});
diff --git a/src/main/java/org/jabref/gui/maintable/OpenFolderAction.java b/src/main/java/org/jabref/gui/maintable/OpenFolderAction.java
index 8ce16f62f1b..92c8404ca39 100644
--- a/src/main/java/org/jabref/gui/maintable/OpenFolderAction.java
+++ b/src/main/java/org/jabref/gui/maintable/OpenFolderAction.java
@@ -5,7 +5,6 @@
import org.jabref.gui.StateManager;
import org.jabref.gui.actions.ActionHelper;
import org.jabref.gui.actions.SimpleCommand;
-import org.jabref.gui.externalfiletype.ExternalFileTypes;
import org.jabref.gui.fieldeditors.LinkedFileViewModel;
import org.jabref.model.entry.BibEntry;
import org.jabref.model.entry.LinkedFile;
@@ -49,9 +48,7 @@ public void execute() {
databaseContext,
Globals.TASK_EXECUTOR,
dialogService,
- preferencesService,
- ExternalFileTypes.getInstance()
- );
+ preferencesService);
linkedFileViewModel.openFolder();
});
} else {
@@ -61,9 +58,7 @@ public void execute() {
databaseContext,
Globals.TASK_EXECUTOR,
dialogService,
- preferencesService,
- ExternalFileTypes.getInstance()
- );
+ preferencesService);
linkedFileViewModel.openFolder();
}
});
diff --git a/src/main/java/org/jabref/gui/maintable/RightClickMenu.java b/src/main/java/org/jabref/gui/maintable/RightClickMenu.java
index 15ff1e113e0..79b2a95006c 100644
--- a/src/main/java/org/jabref/gui/maintable/RightClickMenu.java
+++ b/src/main/java/org/jabref/gui/maintable/RightClickMenu.java
@@ -52,7 +52,7 @@ public static ContextMenu create(BibEntryTableViewModel entry,
createCopySubMenu(factory, dialogService, stateManager, preferencesService, clipBoardManager, taskExecutor),
factory.createMenuItem(StandardActions.PASTE, new EditAction(StandardActions.PASTE, libraryTab.frame(), stateManager)),
factory.createMenuItem(StandardActions.CUT, new EditAction(StandardActions.CUT, libraryTab.frame(), stateManager)),
- factory.createMenuItem(StandardActions.MERGE_ENTRIES, new MergeEntriesAction(libraryTab.frame(), dialogService, stateManager)),
+ factory.createMenuItem(StandardActions.MERGE_ENTRIES, new MergeEntriesAction(dialogService, stateManager)),
factory.createMenuItem(StandardActions.DELETE_ENTRY, new EditAction(StandardActions.DELETE_ENTRY, libraryTab.frame(), stateManager)),
new SeparatorMenuItem(),
diff --git a/src/main/java/org/jabref/gui/maintable/columns/FileColumn.java b/src/main/java/org/jabref/gui/maintable/columns/FileColumn.java
index c325637af32..5aaeeb207a0 100644
--- a/src/main/java/org/jabref/gui/maintable/columns/FileColumn.java
+++ b/src/main/java/org/jabref/gui/maintable/columns/FileColumn.java
@@ -32,7 +32,6 @@
*/
public class FileColumn extends MainTableColumn> {
- private final ExternalFileTypes externalFileTypes;
private final DialogService dialogService;
private final BibDatabaseContext database;
private final PreferencesService preferencesService;
@@ -42,11 +41,9 @@ public class FileColumn extends MainTableColumn> {
*/
public FileColumn(MainTableColumnModel model,
BibDatabaseContext database,
- ExternalFileTypes externalFileTypes,
DialogService dialogService,
PreferencesService preferencesService) {
super(model);
- this.externalFileTypes = Objects.requireNonNull(externalFileTypes);
this.database = Objects.requireNonNull(database);
this.dialogService = dialogService;
this.preferencesService = preferencesService;
@@ -68,8 +65,7 @@ public FileColumn(MainTableColumnModel model,
entry.getEntry(),
database, Globals.TASK_EXECUTOR,
dialogService,
- preferencesService,
- externalFileTypes);
+ preferencesService);
linkedFileViewModel.open();
}
})
@@ -81,22 +77,19 @@ public FileColumn(MainTableColumnModel model,
*/
public FileColumn(MainTableColumnModel model,
BibDatabaseContext database,
- ExternalFileTypes externalFileTypes,
DialogService dialogService,
PreferencesService preferencesService,
String fileType) {
super(model);
- this.externalFileTypes = Objects.requireNonNull(externalFileTypes);
this.database = Objects.requireNonNull(database);
this.dialogService = dialogService;
this.preferencesService = preferencesService;
setCommonSettings();
- this.setGraphic(externalFileTypes
- .getExternalFileTypeByName(fileType)
- .map(ExternalFileType::getIcon).orElse(IconTheme.JabRefIcons.FILE)
- .getGraphicNode());
+ this.setGraphic(ExternalFileTypes.getExternalFileTypeByName(fileType, preferencesService.getFilePreferences())
+ .map(ExternalFileType::getIcon).orElse(IconTheme.JabRefIcons.FILE)
+ .getGraphicNode());
new ValueTableCellFactory>()
.withGraphic(linkedFiles -> createFileIcon(linkedFiles.stream().filter(linkedFile ->
@@ -131,8 +124,7 @@ private ContextMenu createFileMenu(BibEntryTableViewModel entry, List linkedFiles) {
if (linkedFiles.size() > 1) {
return IconTheme.JabRefIcons.FILE_MULTIPLE.getGraphicNode();
} else if (linkedFiles.size() == 1) {
- return externalFileTypes.fromLinkedFile(linkedFiles.get(0), true)
+ return ExternalFileTypes.getExternalFileTypeByLinkedFile(linkedFiles.get(0), true, preferencesService.getFilePreferences())
.map(ExternalFileType::getIcon)
.orElse(IconTheme.JabRefIcons.FILE)
.getGraphicNode();
diff --git a/src/main/java/org/jabref/gui/menus/FileHistoryMenu.java b/src/main/java/org/jabref/gui/menus/FileHistoryMenu.java
index 7a748f6d838..43a80641bf1 100644
--- a/src/main/java/org/jabref/gui/menus/FileHistoryMenu.java
+++ b/src/main/java/org/jabref/gui/menus/FileHistoryMenu.java
@@ -92,6 +92,6 @@ public void openFile(Path file) {
setItems();
return;
}
- openDatabaseAction.openFile(file, true);
+ openDatabaseAction.openFile(file);
}
}
diff --git a/src/main/java/org/jabref/gui/mergeentries/EntriesMergeResult.java b/src/main/java/org/jabref/gui/mergeentries/EntriesMergeResult.java
new file mode 100644
index 00000000000..b134b11688a
--- /dev/null
+++ b/src/main/java/org/jabref/gui/mergeentries/EntriesMergeResult.java
@@ -0,0 +1,8 @@
+package org.jabref.gui.mergeentries;
+
+import org.jabref.model.entry.BibEntry;
+
+public record EntriesMergeResult(
+ BibEntry originalLeftEntry, BibEntry originalRightEntry, BibEntry newLeftEntry, BibEntry newRightEntry, BibEntry mergedEntry
+) {
+}
diff --git a/src/main/java/org/jabref/gui/mergeentries/FetchAndMergeEntry.java b/src/main/java/org/jabref/gui/mergeentries/FetchAndMergeEntry.java
index 37d3bbf0750..e6aff3d4ea2 100644
--- a/src/main/java/org/jabref/gui/mergeentries/FetchAndMergeEntry.java
+++ b/src/main/java/org/jabref/gui/mergeentries/FetchAndMergeEntry.java
@@ -99,7 +99,8 @@ private void showMergeDialog(BibEntry originalEntry, BibEntry fetchedEntry, WebF
dialog.setTitle(Localization.lang("Merge entry with %0 information", fetcher.getName()));
dialog.setLeftHeaderText(Localization.lang("Original entry"));
dialog.setRightHeaderText(Localization.lang("Entry from %0", fetcher.getName()));
- Optional mergedEntry = dialogService.showCustomDialogAndWait(dialog).map(MergeResult::mergedEntry);
+ Optional mergedEntry = dialogService.showCustomDialogAndWait(dialog).map(EntriesMergeResult::mergedEntry);
+
if (mergedEntry.isPresent()) {
NamedCompound ce = new NamedCompound(Localization.lang("Merge entry with %0 information", fetcher.getName()));
diff --git a/src/main/java/org/jabref/gui/mergeentries/MergeEntriesAction.java b/src/main/java/org/jabref/gui/mergeentries/MergeEntriesAction.java
index 7820cd95c92..a4c04975906 100644
--- a/src/main/java/org/jabref/gui/mergeentries/MergeEntriesAction.java
+++ b/src/main/java/org/jabref/gui/mergeentries/MergeEntriesAction.java
@@ -1,32 +1,22 @@
package org.jabref.gui.mergeentries;
-import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import org.jabref.gui.DialogService;
-import org.jabref.gui.Globals;
-import org.jabref.gui.JabRefFrame;
import org.jabref.gui.StateManager;
import org.jabref.gui.actions.ActionHelper;
import org.jabref.gui.actions.SimpleCommand;
-import org.jabref.gui.undo.NamedCompound;
-import org.jabref.gui.undo.UndoableInsertEntries;
-import org.jabref.gui.undo.UndoableRemoveEntries;
import org.jabref.logic.bibtex.comparator.EntryComparator;
import org.jabref.logic.l10n.Localization;
-import org.jabref.model.database.BibDatabaseContext;
import org.jabref.model.entry.BibEntry;
import org.jabref.model.entry.field.InternalField;
public class MergeEntriesAction extends SimpleCommand {
-
- private final JabRefFrame frame;
private final DialogService dialogService;
private final StateManager stateManager;
- public MergeEntriesAction(JabRefFrame frame, DialogService dialogService, StateManager stateManager) {
- this.frame = frame;
+ public MergeEntriesAction(DialogService dialogService, StateManager stateManager) {
this.dialogService = dialogService;
this.stateManager = stateManager;
@@ -38,7 +28,6 @@ public void execute() {
if (stateManager.getActiveDatabase().isEmpty()) {
return;
}
- BibDatabaseContext databaseContext = stateManager.getActiveDatabase().get();
// Check if there are two entries selected
List selectedEntries = stateManager.getSelectedEntries();
@@ -68,20 +57,10 @@ public void execute() {
MergeEntriesDialog dialog = new MergeEntriesDialog(first, second);
dialog.setTitle(Localization.lang("Merge entries"));
- Optional mergeResultOpt = dialogService.showCustomDialogAndWait(dialog);
- mergeResultOpt.ifPresentOrElse(mergeResult -> {
- // TODO: BibDatabase::insertEntry does not contain logic to mark the BasePanel as changed and to mark
- // entries with a timestamp, only BasePanel::insertEntry does. Workaround for the moment is to get the
- // BasePanel from the constructor injected JabRefFrame. Should be refactored and extracted!
- frame.getCurrentLibraryTab().insertEntry(mergeResult.mergedEntry());
-
- NamedCompound ce = new NamedCompound(Localization.lang("Merge entries"));
- ce.addEdit(new UndoableInsertEntries(databaseContext.getDatabase(), mergeResult.mergedEntry()));
- List entriesToRemove = Arrays.asList(one, two);
- ce.addEdit(new UndoableRemoveEntries(databaseContext.getDatabase(), entriesToRemove));
- databaseContext.getDatabase().removeEntries(entriesToRemove);
- ce.end();
- Globals.undoManager.addEdit(ce);
+
+ Optional mergeResultOpt = dialogService.showCustomDialogAndWait(dialog);
+ mergeResultOpt.ifPresentOrElse(entriesMergeResult -> {
+ new MergeTwoEntriesAction(entriesMergeResult, stateManager).execute();
dialogService.notify(Localization.lang("Merged entries"));
}, () -> dialogService.notify(Localization.lang("Canceled merging entries")));
diff --git a/src/main/java/org/jabref/gui/mergeentries/MergeEntriesDialog.java b/src/main/java/org/jabref/gui/mergeentries/MergeEntriesDialog.java
index a93598494b0..ce4149a6bb1 100644
--- a/src/main/java/org/jabref/gui/mergeentries/MergeEntriesDialog.java
+++ b/src/main/java/org/jabref/gui/mergeentries/MergeEntriesDialog.java
@@ -3,16 +3,21 @@
import javafx.scene.control.ButtonBar;
import javafx.scene.control.ButtonType;
+import org.jabref.gui.mergeentries.newmergedialog.ShowDiffConfig;
import org.jabref.gui.mergeentries.newmergedialog.ThreeWayMergeView;
import org.jabref.gui.util.BaseDialog;
import org.jabref.logic.l10n.Localization;
import org.jabref.model.entry.BibEntry;
-public class MergeEntriesDialog extends BaseDialog {
+public class MergeEntriesDialog extends BaseDialog {
private final ThreeWayMergeView threeWayMergeView;
+ private final BibEntry one;
+ private final BibEntry two;
public MergeEntriesDialog(BibEntry one, BibEntry two) {
threeWayMergeView = new ThreeWayMergeView(one, two);
+ this.one = one;
+ this.two = two;
init();
}
@@ -31,7 +36,7 @@ private void init() {
this.getDialogPane().getButtonTypes().setAll(ButtonType.CANCEL, replaceEntries);
this.setResultConverter(buttonType -> {
if (buttonType.equals(replaceEntries)) {
- return new MergeResult(threeWayMergeView.getLeftEntry(), threeWayMergeView.getRightEntry(), threeWayMergeView.getMergedEntry());
+ return new EntriesMergeResult(one, two, threeWayMergeView.getLeftEntry(), threeWayMergeView.getRightEntry(), threeWayMergeView.getMergedEntry());
} else {
return null;
}
@@ -45,4 +50,8 @@ public void setLeftHeaderText(String leftHeaderText) {
public void setRightHeaderText(String rightHeaderText) {
threeWayMergeView.setRightHeader(rightHeaderText);
}
+
+ public void configureDiff(ShowDiffConfig diffConfig) {
+ threeWayMergeView.showDiff(diffConfig);
+ }
}
diff --git a/src/main/java/org/jabref/gui/mergeentries/MergeTwoEntriesAction.java b/src/main/java/org/jabref/gui/mergeentries/MergeTwoEntriesAction.java
new file mode 100644
index 00000000000..d5e695b6810
--- /dev/null
+++ b/src/main/java/org/jabref/gui/mergeentries/MergeTwoEntriesAction.java
@@ -0,0 +1,44 @@
+package org.jabref.gui.mergeentries;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.jabref.gui.Globals;
+import org.jabref.gui.StateManager;
+import org.jabref.gui.actions.SimpleCommand;
+import org.jabref.gui.undo.NamedCompound;
+import org.jabref.gui.undo.UndoableInsertEntries;
+import org.jabref.gui.undo.UndoableRemoveEntries;
+import org.jabref.logic.l10n.Localization;
+import org.jabref.model.database.BibDatabase;
+import org.jabref.model.entry.BibEntry;
+
+public class MergeTwoEntriesAction extends SimpleCommand {
+ private final EntriesMergeResult entriesMergeResult;
+ private final StateManager stateManager;
+
+ public MergeTwoEntriesAction(EntriesMergeResult entriesMergeResult, StateManager stateManager) {
+ this.entriesMergeResult = entriesMergeResult;
+ this.stateManager = stateManager;
+ }
+
+ @Override
+ public void execute() {
+ if (stateManager.getActiveDatabase().isEmpty()) {
+ return;
+ }
+
+ BibDatabase database = stateManager.getActiveDatabase().get().getDatabase();
+ List entriesToRemove = Arrays.asList(entriesMergeResult.originalLeftEntry(), entriesMergeResult.originalRightEntry());
+
+ database.insertEntry(entriesMergeResult.mergedEntry());
+ database.removeEntries(entriesToRemove);
+
+ NamedCompound ce = new NamedCompound(Localization.lang("Merge entries"));
+ ce.addEdit(new UndoableInsertEntries(stateManager.getActiveDatabase().get().getDatabase(), entriesMergeResult.mergedEntry()));
+ ce.addEdit(new UndoableRemoveEntries(database, entriesToRemove));
+ ce.end();
+
+ Globals.undoManager.addEdit(ce);
+ }
+}
diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/FieldRowController.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/FieldRowController.java
deleted file mode 100644
index 8f2411d89cf..00000000000
--- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/FieldRowController.java
+++ /dev/null
@@ -1,139 +0,0 @@
-package org.jabref.gui.mergeentries.newmergedialog;
-
-import javafx.beans.property.ReadOnlyStringProperty;
-import javafx.scene.control.ToggleGroup;
-
-import org.jabref.gui.mergeentries.newmergedialog.cell.FieldNameCell;
-import org.jabref.gui.mergeentries.newmergedialog.cell.FieldValueCell;
-import org.jabref.gui.mergeentries.newmergedialog.cell.MergedFieldCell;
-import org.jabref.gui.mergeentries.newmergedialog.diffhighlighter.SplitDiffHighlighter;
-import org.jabref.gui.mergeentries.newmergedialog.diffhighlighter.UnifiedDiffHighlighter;
-import org.jabref.gui.mergeentries.newmergedialog.toolbar.ThreeWayMergeToolbar;
-import org.jabref.model.strings.StringUtil;
-
-import org.fxmisc.richtext.StyleClassedTextArea;
-
-public class FieldRowController {
- private final FieldNameCell fieldNameCell;
- private final FieldValueCell leftValueCell;
- private FieldValueCell rightValueCell;
- private final MergedFieldCell mergedValueCell;
-
- private final String leftValue;
-
- private final String rightValue;
-
- private final ToggleGroup toggleGroup = new ToggleGroup();
-
- public FieldRowController(String fieldName, String leftValue, String rightValue, int rowIndex) {
- fieldNameCell = new FieldNameCell(fieldName, rowIndex);
- leftValueCell = new FieldValueCell(leftValue, rowIndex);
- rightValueCell = new FieldValueCell(rightValue, rowIndex);
- mergedValueCell = new MergedFieldCell(StringUtil.isNullOrEmpty(leftValue) ? rightValue : leftValue, rowIndex);
-
- this.leftValue = leftValue;
- this.rightValue = rightValue;
-
- toggleGroup.getToggles().addAll(leftValueCell, rightValueCell);
- toggleGroup.selectToggle(StringUtil.isNullOrEmpty(leftValue) ? rightValueCell : leftValueCell);
- toggleGroup.selectedToggleProperty().addListener(invalidated -> {
- if (toggleGroup.getSelectedToggle() != null) {
- mergedValueCell.setText((String) toggleGroup.getSelectedToggle().getUserData());
- }
- });
-
- mergedValueCell.textProperty().addListener((observable, old, mergedValue) -> {
- if (mergedValue.equals(leftValue)) {
- toggleGroup.selectToggle(leftValueCell);
- } else if (mergedValue.equals(rightValue)) {
- toggleGroup.selectToggle(rightValueCell);
- } else {
- // deselect all toggles because left and right values don't equal the merged value
- toggleGroup.selectToggle(null);
- }
- });
-
- // When both the left and right cells have the same value, only the left value is displayed,
- // making it unnecessary to keep allocating memory for the right cell.
- if (hasEqualLeftAndRightValues()) {
- // Setting this to null so the GC release the memory allocated to the right cell.
- this.rightValueCell = null;
- }
- }
-
- public void selectLeftValue() {
- toggleGroup.selectToggle(leftValueCell);
- }
-
- public void selectRightValue() {
- if (isRightValueCellHidden()) {
- selectLeftValue();
- } else {
- toggleGroup.selectToggle(rightValueCell);
- }
- }
-
- public String getMergedValue() {
- return mergedValueProperty().getValue();
- }
-
- public ReadOnlyStringProperty mergedValueProperty() {
- return mergedValueCell.textProperty();
- }
-
- public FieldNameCell getFieldNameCell() {
- return fieldNameCell;
- }
-
- public FieldValueCell getLeftValueCell() {
- return leftValueCell;
- }
-
- public FieldValueCell getRightValueCell() {
- return rightValueCell;
- }
-
- public MergedFieldCell getMergedValueCell() {
- return mergedValueCell;
- }
-
- public boolean hasEqualLeftAndRightValues() {
- return isRightValueCellHidden() || (!StringUtil.isNullOrEmpty(leftValueCell.getText()) &&
- !StringUtil.isNullOrEmpty(rightValueCell.getText()) &&
- leftValueCell.getText().equals(rightValueCell.getText()));
- }
-
- public void showDiff(ShowDiffConfig diffConfig) {
- if (isRightValueCellHidden()) {
- return;
- }
-
- StyleClassedTextArea leftLabel = leftValueCell.getStyleClassedLabel();
- StyleClassedTextArea rightLabel = rightValueCell.getStyleClassedLabel();
- // Clearing old diff styles based on previous diffConfig
- hideDiff();
- if (diffConfig.diffView() == ThreeWayMergeToolbar.DiffView.UNIFIED) {
- new UnifiedDiffHighlighter(leftLabel, rightLabel, diffConfig.diffHighlightingMethod()).highlight();
- } else {
- new SplitDiffHighlighter(leftLabel, rightLabel, diffConfig.diffHighlightingMethod()).highlight();
- }
- }
-
- public void hideDiff() {
- if (isRightValueCellHidden()) {
- return;
- }
-
- int leftValueLength = getLeftValueCell().getStyleClassedLabel().getLength();
- getLeftValueCell().getStyleClassedLabel().clearStyle(0, leftValueLength);
- getLeftValueCell().getStyleClassedLabel().replaceText(leftValue);
-
- int rightValueLength = getRightValueCell().getStyleClassedLabel().getLength();
- getRightValueCell().getStyleClassedLabel().clearStyle(0, rightValueLength);
- getRightValueCell().getStyleClassedLabel().replaceText(rightValue);
- }
-
- private boolean isRightValueCellHidden() {
- return rightValueCell == null;
- }
-}
diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/FieldRowView.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/FieldRowView.java
index 78888ba787c..9b50e95817d 100644
--- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/FieldRowView.java
+++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/FieldRowView.java
@@ -5,7 +5,9 @@
import javax.swing.undo.CannotUndoException;
import javax.swing.undo.CompoundEdit;
+import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ReadOnlyStringProperty;
+import javafx.beans.property.SimpleBooleanProperty;
import javafx.scene.control.ToggleGroup;
import javafx.scene.layout.GridPane;
@@ -21,6 +23,7 @@
import org.jabref.gui.mergeentries.newmergedialog.toolbar.ThreeWayMergeToolbar;
import org.jabref.model.entry.BibEntry;
import org.jabref.model.entry.field.Field;
+import org.jabref.model.strings.StringUtil;
import com.tobiasdiez.easybind.EasyBind;
import org.fxmisc.richtext.StyleClassedTextArea;
@@ -34,6 +37,10 @@
*/
public class FieldRowView {
private static final Logger LOGGER = LoggerFactory.getLogger(FieldRowView.class);
+
+ protected final FieldRowViewModel viewModel;
+
+ protected final BooleanProperty shouldShowDiffs = new SimpleBooleanProperty(true);
private final FieldNameCell fieldNameCell;
private final FieldValueCell leftValueCell;
private final FieldValueCell rightValueCell;
@@ -41,8 +48,6 @@ public class FieldRowView {
private final ToggleGroup toggleGroup = new ToggleGroup();
- private final FieldRowViewModel viewModel;
-
private final CompoundEdit fieldsMergedEdit = new CompoundEdit();
public FieldRowView(Field field, BibEntry leftEntry, BibEntry rightEntry, BibEntry mergedEntry, FieldMergerFactory fieldMergerFactory, int rowIndex) {
@@ -150,7 +155,7 @@ public MergedFieldCell getMergedValueCell() {
}
public void showDiff(ShowDiffConfig diffConfig) {
- if (!rightValueCell.isVisible()) {
+ if (!rightValueCell.isVisible() || StringUtil.isNullOrEmpty(viewModel.getLeftFieldValue()) || StringUtil.isNullOrEmpty(viewModel.getRightFieldValue())) {
return;
}
LOGGER.debug("Showing diffs...");
@@ -159,10 +164,12 @@ public void showDiff(ShowDiffConfig diffConfig) {
StyleClassedTextArea rightLabel = rightValueCell.getStyleClassedLabel();
// Clearing old diff styles based on previous diffConfig
hideDiff();
- if (diffConfig.diffView() == ThreeWayMergeToolbar.DiffView.UNIFIED) {
- new UnifiedDiffHighlighter(leftLabel, rightLabel, diffConfig.diffHighlightingMethod()).highlight();
- } else {
- new SplitDiffHighlighter(leftLabel, rightLabel, diffConfig.diffHighlightingMethod()).highlight();
+ if (shouldShowDiffs.get()) {
+ if (diffConfig.diffView() == ThreeWayMergeToolbar.DiffView.UNIFIED) {
+ new UnifiedDiffHighlighter(leftLabel, rightLabel, diffConfig.diffHighlightingMethod()).highlight();
+ } else {
+ new SplitDiffHighlighter(leftLabel, rightLabel, diffConfig.diffHighlightingMethod()).highlight();
+ }
}
}
@@ -212,6 +219,9 @@ public void execute() {
}
public class UnmergeCommand extends SimpleCommand {
+
+ public UnmergeCommand() { }
+
@Override
public void execute() {
if (fieldsMergedEdit.canUndo()) {
diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/PersonsNameFieldRowView.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/PersonsNameFieldRowView.java
new file mode 100644
index 00000000000..8d707f34b0f
--- /dev/null
+++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/PersonsNameFieldRowView.java
@@ -0,0 +1,34 @@
+package org.jabref.gui.mergeentries.newmergedialog;
+
+import org.jabref.gui.mergeentries.newmergedialog.cell.sidebuttons.InfoButton;
+import org.jabref.gui.mergeentries.newmergedialog.fieldsmerger.FieldMergerFactory;
+import org.jabref.logic.importer.AuthorListParser;
+import org.jabref.logic.l10n.Localization;
+import org.jabref.model.entry.AuthorList;
+import org.jabref.model.entry.BibEntry;
+import org.jabref.model.entry.field.Field;
+import org.jabref.model.entry.field.FieldProperty;
+
+public class PersonsNameFieldRowView extends FieldRowView {
+ private final AuthorList leftEntryNames;
+ private final AuthorList rightEntryNames;
+
+ public PersonsNameFieldRowView(Field field, BibEntry leftEntry, BibEntry rightEntry, BibEntry mergedEntry, FieldMergerFactory fieldMergerFactory, int rowIndex) {
+ super(field, leftEntry, rightEntry, mergedEntry, fieldMergerFactory, rowIndex);
+ assert field.getProperties().contains(FieldProperty.PERSON_NAMES);
+
+ var authorsParser = new AuthorListParser();
+ leftEntryNames = authorsParser.parse(viewModel.getLeftFieldValue());
+ rightEntryNames = authorsParser.parse(viewModel.getRightFieldValue());
+
+ if (!viewModel.hasEqualLeftAndRightValues() && leftEntryNames.equals(rightEntryNames)) {
+ showPersonsNamesAreTheSameInfo();
+ shouldShowDiffs.set(false);
+ }
+ }
+
+ private void showPersonsNamesAreTheSameInfo() {
+ InfoButton infoButton = new InfoButton(Localization.lang("The %0s are the same. However, the order of field content differs", viewModel.getField().getName()));
+ getFieldNameCell().addSideButton(infoButton);
+ }
+}
diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java
index a61833456f8..06f6861d751 100644
--- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java
+++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java
@@ -17,6 +17,7 @@
import org.jabref.logic.l10n.Localization;
import org.jabref.model.entry.BibEntry;
import org.jabref.model.entry.field.Field;
+import org.jabref.model.entry.field.FieldProperty;
public class ThreeWayMergeView extends VBox {
public static final int GRID_COLUMN_MIN_WIDTH = 250;
@@ -119,7 +120,12 @@ private Field getFieldAtIndex(int index) {
private void addRow(int fieldIndex) {
Field field = getFieldAtIndex(fieldIndex);
- FieldRowView fieldRow = new FieldRowView(field, getLeftEntry(), getRightEntry(), getMergedEntry(), fieldMergerFactory, fieldIndex);
+ FieldRowView fieldRow;
+ if (field.getProperties().contains(FieldProperty.PERSON_NAMES)) {
+ fieldRow = new PersonsNameFieldRowView(field, getLeftEntry(), getRightEntry(), getMergedEntry(), fieldMergerFactory, fieldIndex);
+ } else {
+ fieldRow = new FieldRowView(field, getLeftEntry(), getRightEntry(), getMergedEntry(), fieldMergerFactory, fieldIndex);
+ }
fieldRows.add(fieldIndex, fieldRow);
diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/sidebuttons/InfoButton.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/sidebuttons/InfoButton.java
new file mode 100644
index 00000000000..da37e2267ce
--- /dev/null
+++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/sidebuttons/InfoButton.java
@@ -0,0 +1,51 @@
+package org.jabref.gui.mergeentries.newmergedialog.cell.sidebuttons;
+
+import javafx.beans.property.SimpleStringProperty;
+import javafx.beans.property.StringProperty;
+import javafx.scene.control.Button;
+
+import org.jabref.gui.Globals;
+import org.jabref.gui.actions.Action;
+import org.jabref.gui.actions.ActionFactory;
+import org.jabref.gui.actions.SimpleCommand;
+import org.jabref.gui.icon.IconTheme;
+
+import com.tobiasdiez.easybind.EasyBind;
+
+public class InfoButton extends Button {
+ private final StringProperty infoMessage = new SimpleStringProperty();
+ private final ActionFactory actionFactory = new ActionFactory(Globals.getKeyPrefs());
+
+ public InfoButton(String infoMessage) {
+ setInfoMessage(infoMessage);
+ configureButton();
+ EasyBind.subscribe(infoMessageProperty(), newWarningMessage -> {
+ configureButton();
+ });
+ }
+
+ private void configureButton() {
+ setMaxHeight(Double.MAX_VALUE);
+ setFocusTraversable(false);
+ Action mergeAction = new Action.Builder(getInfoMessage()).setIcon(IconTheme.JabRefIcons.INTEGRITY_INFO);
+
+ actionFactory.configureIconButton(mergeAction, new SimpleCommand() {
+ @Override
+ public void execute() {
+ // The info button is not meant to be clickable that's why this is empty
+ }
+ }, this);
+ }
+
+ private void setInfoMessage(String infoMessage) {
+ infoMessageProperty().set(infoMessage);
+ }
+
+ public StringProperty infoMessageProperty() {
+ return infoMessage;
+ }
+
+ public String getInfoMessage() {
+ return infoMessage.get();
+ }
+}
diff --git a/src/main/java/org/jabref/gui/openoffice/OOBibBaseConnect.java b/src/main/java/org/jabref/gui/openoffice/OOBibBaseConnect.java
index 24660410fc3..8a12722a17b 100644
--- a/src/main/java/org/jabref/gui/openoffice/OOBibBaseConnect.java
+++ b/src/main/java/org/jabref/gui/openoffice/OOBibBaseConnect.java
@@ -15,6 +15,8 @@
import org.jabref.model.openoffice.uno.UnoTextDocument;
import org.jabref.model.openoffice.util.OOResult;
+import com.sun.star.bridge.XBridge;
+import com.sun.star.bridge.XBridgeFactory;
import com.sun.star.comp.helper.BootstrapException;
import com.sun.star.container.NoSuchElementException;
import com.sun.star.container.XEnumeration;
@@ -25,11 +27,17 @@
import com.sun.star.lang.XMultiComponentFactory;
import com.sun.star.text.XTextDocument;
import com.sun.star.uno.XComponentContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static com.sun.star.uno.UnoRuntime.queryInterface;
/**
* Establish connection to a document opened in OpenOffice or LibreOffice.
*/
-class OOBibBaseConnect {
+public class OOBibBaseConnect {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(OOBibBaseConnect.class);
private final DialogService dialogService;
private final XDesktop xDesktop;
@@ -70,6 +78,27 @@ private XDesktop simpleBootstrap(Path loPath)
return UnoCast.cast(XDesktop.class, desktop).get();
}
+ /**
+ * Close any open office connection, if none exists does nothing
+ */
+ public static void closeOfficeConnection() {
+ try {
+ // get the bridge factory from the local service manager
+ XBridgeFactory bridgeFactory = queryInterface(XBridgeFactory.class,
+ org.jabref.gui.openoffice.Bootstrap.createSimpleServiceManager()
+ .createInstance("com.sun.star.bridge.BridgeFactory"));
+
+ if (bridgeFactory != null) {
+ for (XBridge bridge : bridgeFactory.getExistingBridges()) {
+ // dispose of this bridge after closing its connection
+ queryInterface(XComponent.class, bridge).dispose();
+ }
+ }
+ } catch (Exception ex) {
+ LOGGER.error("Exception disposing office process connection bridge", ex);
+ }
+ }
+
private static List getTextDocuments(XDesktop desktop)
throws
NoSuchElementException,
@@ -125,15 +154,15 @@ public String toString() {
// auto-detecting instances, so we need to show dialog in FX
// thread
Optional selectedDocument =
- (dialogService
+ dialogService
.showChoiceDialogAndWait(Localization.lang("Select document"),
Localization.lang("Found documents:"),
Localization.lang("Use selected document"),
- viewModel));
+ viewModel);
- return (selectedDocument
+ return selectedDocument
.map(DocumentTitleViewModel::getXtextDocument)
- .orElse(null));
+ .orElse(null);
}
/**
@@ -157,7 +186,7 @@ public void selectDocument(boolean autoSelectForSingle)
List textDocumentList = getTextDocuments(this.xDesktop);
if (textDocumentList.isEmpty()) {
throw new NoDocumentFoundException("No Writer documents found");
- } else if (textDocumentList.size() == 1 && autoSelectForSingle) {
+ } else if ((textDocumentList.size() == 1) && autoSelectForSingle) {
selected = textDocumentList.get(0); // Get the only one
} else { // Bring up a dialog
selected = OOBibBaseConnect.selectDocumentDialog(textDocumentList,
@@ -233,4 +262,4 @@ public Optional getCurrentDocumentTitle() {
return UnoTextDocument.getFrameTitle(this.xTextDocument);
}
}
-} // end of OOBibBaseConnect
+}
diff --git a/src/main/java/org/jabref/gui/openoffice/StyleSelectDialogViewModel.java b/src/main/java/org/jabref/gui/openoffice/StyleSelectDialogViewModel.java
index 92bd6dd607f..b78e69aeba9 100644
--- a/src/main/java/org/jabref/gui/openoffice/StyleSelectDialogViewModel.java
+++ b/src/main/java/org/jabref/gui/openoffice/StyleSelectDialogViewModel.java
@@ -93,7 +93,7 @@ public void deleteStyle() {
public void editStyle() {
OOBibStyle style = selectedItem.getValue().getStyle();
- Optional type = ExternalFileTypes.getInstance().getExternalFileTypeByExt("jstyle");
+ Optional type = ExternalFileTypes.getExternalFileTypeByExt("jstyle", preferencesService.getFilePreferences());
try {
JabRefDesktop.openExternalFileAnyFormat(new BibDatabaseContext(), preferencesService, style.getPath(), type);
} catch (IOException e) {
diff --git a/src/main/java/org/jabref/gui/preferences/customimporter/CustomImporterTabViewModel.java b/src/main/java/org/jabref/gui/preferences/customimporter/CustomImporterTabViewModel.java
index 08012693858..2f8065d5615 100644
--- a/src/main/java/org/jabref/gui/preferences/customimporter/CustomImporterTabViewModel.java
+++ b/src/main/java/org/jabref/gui/preferences/customimporter/CustomImporterTabViewModel.java
@@ -42,7 +42,7 @@ public CustomImporterTabViewModel(PreferencesService preferences, DialogService
@Override
public void setValues() {
- Set importersLogic = preferences.getCustomImportFormats();
+ Set importersLogic = preferences.getImporterPreferences().getCustomImportList();
for (CustomImporter importer : importersLogic) {
importers.add(new ImporterViewModel(importer));
}
@@ -50,13 +50,13 @@ public void setValues() {
@Override
public void storeSettings() {
- preferences.storeCustomImportFormats(importers.stream()
- .map(ImporterViewModel::getLogic)
- .collect(Collectors.toSet()));
+ preferences.getImporterPreferences().getCustomImportList().clear();
+ preferences.getImporterPreferences().getCustomImportList().addAll(importers.stream()
+ .map(ImporterViewModel::getLogic)
+ .collect(Collectors.toSet()));
Globals.IMPORT_FORMAT_READER.resetImportFormats(
preferences.getImporterPreferences(),
preferences.getImportFormatPreferences(),
- preferences.getXmpPreferences(),
Globals.getFileUpdateMonitor());
}
diff --git a/src/main/java/org/jabref/gui/externalfiletype/EditExternalFileTypeEntryDialog.fxml b/src/main/java/org/jabref/gui/preferences/externalfiletypes/EditExternalFileTypeEntryDialog.fxml
similarity index 51%
rename from src/main/java/org/jabref/gui/externalfiletype/EditExternalFileTypeEntryDialog.fxml
rename to src/main/java/org/jabref/gui/preferences/externalfiletypes/EditExternalFileTypeEntryDialog.fxml
index 2e885d60011..d197a829648 100644
--- a/src/main/java/org/jabref/gui/externalfiletype/EditExternalFileTypeEntryDialog.fxml
+++ b/src/main/java/org/jabref/gui/preferences/externalfiletypes/EditExternalFileTypeEntryDialog.fxml
@@ -10,7 +10,7 @@
-
+
@@ -27,25 +27,23 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/java/org/jabref/gui/externalfiletype/EditExternalFileTypeEntryDialog.java b/src/main/java/org/jabref/gui/preferences/externalfiletypes/EditExternalFileTypeEntryDialog.java
similarity index 90%
rename from src/main/java/org/jabref/gui/externalfiletype/EditExternalFileTypeEntryDialog.java
rename to src/main/java/org/jabref/gui/preferences/externalfiletypes/EditExternalFileTypeEntryDialog.java
index 622ab07fb97..377291e96e4 100644
--- a/src/main/java/org/jabref/gui/externalfiletype/EditExternalFileTypeEntryDialog.java
+++ b/src/main/java/org/jabref/gui/preferences/externalfiletypes/EditExternalFileTypeEntryDialog.java
@@ -1,4 +1,4 @@
-package org.jabref.gui.externalfiletype;
+package org.jabref.gui.preferences.externalfiletypes;
import javax.inject.Inject;
@@ -35,11 +35,12 @@ public class EditExternalFileTypeEntryDialog extends BaseDialog {
private final NativeDesktop nativeDesktop = JabRefDesktop.getNativeDesktop();
private final FileDialogConfiguration fileDialogConfiguration = new FileDialogConfiguration.Builder().withInitialDirectory(nativeDesktop.getApplicationDirectory()).build();
+ private final ExternalFileTypeItemViewModel item;
+
private EditExternalFileTypeViewModel viewModel;
- private CustomExternalFileType entry;
- public EditExternalFileTypeEntryDialog(CustomExternalFileType entry, String dialogTitle) {
- this.entry = entry;
+ public EditExternalFileTypeEntryDialog(ExternalFileTypeItemViewModel item, String dialogTitle) {
+ this.item = item;
this.setTitle(dialogTitle);
@@ -57,7 +58,7 @@ public EditExternalFileTypeEntryDialog(CustomExternalFileType entry, String dial
@FXML
public void initialize() {
- viewModel = new EditExternalFileTypeViewModel(entry);
+ viewModel = new EditExternalFileTypeViewModel(item);
icon.setGraphic(viewModel.getIcon());
diff --git a/src/main/java/org/jabref/gui/externalfiletype/EditExternalFileTypeViewModel.java b/src/main/java/org/jabref/gui/preferences/externalfiletypes/EditExternalFileTypeViewModel.java
similarity index 65%
rename from src/main/java/org/jabref/gui/externalfiletype/EditExternalFileTypeViewModel.java
rename to src/main/java/org/jabref/gui/preferences/externalfiletypes/EditExternalFileTypeViewModel.java
index ed124bb4194..693f6eab412 100644
--- a/src/main/java/org/jabref/gui/externalfiletype/EditExternalFileTypeViewModel.java
+++ b/src/main/java/org/jabref/gui/preferences/externalfiletypes/EditExternalFileTypeViewModel.java
@@ -1,4 +1,4 @@
-package org.jabref.gui.externalfiletype;
+package org.jabref.gui.preferences.externalfiletypes;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
@@ -7,39 +7,41 @@
import javafx.scene.Node;
public class EditExternalFileTypeViewModel {
+ private final ExternalFileTypeItemViewModel fileTypeViewModel;
- private final StringProperty extensionProperty = new SimpleStringProperty("");
private final StringProperty nameProperty = new SimpleStringProperty("");
private final StringProperty mimeTypeProperty = new SimpleStringProperty("");
+ private final StringProperty extensionProperty = new SimpleStringProperty("");
private final StringProperty selectedApplicationProperty = new SimpleStringProperty("");
private final BooleanProperty defaultApplicationSelectedProperty = new SimpleBooleanProperty(false);
private final BooleanProperty customApplicationSelectedProperty = new SimpleBooleanProperty(false);
- private final Node icon;
- private final CustomExternalFileType fileType;
-
- public EditExternalFileTypeViewModel(CustomExternalFileType fileType) {
- this.fileType = fileType;
- extensionProperty.setValue(fileType.getExtension());
- nameProperty.setValue(fileType.getName());
- mimeTypeProperty.setValue(fileType.getMimeType());
- selectedApplicationProperty.setValue(fileType.getOpenWithApplication());
- icon = fileType.getIcon().getGraphicNode();
-
- if (fileType.getOpenWithApplication().isEmpty()) {
+
+ public EditExternalFileTypeViewModel(ExternalFileTypeItemViewModel fileTypeViewModel) {
+ this.fileTypeViewModel = fileTypeViewModel;
+
+ extensionProperty.setValue(fileTypeViewModel.extensionProperty().getValue());
+ nameProperty.setValue(fileTypeViewModel.nameProperty().getValue());
+ mimeTypeProperty.setValue(fileTypeViewModel.mimetypeProperty().getValue());
+
+ if (fileTypeViewModel.applicationProperty().getValue().isEmpty()) {
defaultApplicationSelectedProperty.setValue(true);
} else {
customApplicationSelectedProperty.setValue(true);
}
}
- public StringProperty extensionProperty() {
- return extensionProperty;
+ public Node getIcon() {
+ return fileTypeViewModel.iconProperty().getValue().getGraphicNode();
}
public StringProperty nameProperty() {
return nameProperty;
}
+ public StringProperty extensionProperty() {
+ return extensionProperty;
+ }
+
public StringProperty mimeTypeProperty() {
return mimeTypeProperty;
}
@@ -56,29 +58,25 @@ public BooleanProperty customApplicationSelectedProperty() {
return customApplicationSelectedProperty;
}
- public Node getIcon() {
- return icon;
- }
-
public void storeSettings() {
- fileType.setName(nameProperty.getValue().trim());
- fileType.setMimeType(mimeTypeProperty.getValue().trim());
+ fileTypeViewModel.nameProperty().setValue(nameProperty.getValue().trim());
+ fileTypeViewModel.mimetypeProperty().setValue(mimeTypeProperty.getValue().trim());
String ext = extensionProperty.getValue().trim();
if (!ext.isEmpty() && (ext.charAt(0) == '.')) {
- fileType.setExtension(ext.substring(1));
+ fileTypeViewModel.extensionProperty().setValue(ext.substring(1));
} else {
- fileType.setExtension(ext);
+ fileTypeViewModel.extensionProperty().setValue(ext);
}
String application = selectedApplicationProperty.getValue().trim();
// store application as empty if the "Default" option is selected, or if the application name is empty:
if (defaultApplicationSelectedProperty.getValue() || application.isEmpty()) {
- fileType.setOpenWith("");
+ fileTypeViewModel.applicationProperty().setValue("");
selectedApplicationProperty.setValue("");
} else {
- fileType.setOpenWith(application);
+ fileTypeViewModel.applicationProperty().setValue(application);
}
}
}
diff --git a/src/main/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypeItemViewModel.java b/src/main/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypeItemViewModel.java
new file mode 100644
index 00000000000..da149d6e516
--- /dev/null
+++ b/src/main/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypeItemViewModel.java
@@ -0,0 +1,68 @@
+package org.jabref.gui.preferences.externalfiletypes;
+
+import javafx.beans.property.ObjectProperty;
+import javafx.beans.property.SimpleObjectProperty;
+import javafx.beans.property.SimpleStringProperty;
+import javafx.beans.property.StringProperty;
+
+import org.jabref.gui.externalfiletype.CustomExternalFileType;
+import org.jabref.gui.externalfiletype.ExternalFileType;
+import org.jabref.gui.icon.IconTheme;
+import org.jabref.gui.icon.JabRefIcon;
+
+public class ExternalFileTypeItemViewModel {
+ private final ObjectProperty icon = new SimpleObjectProperty<>();
+ private final StringProperty name = new SimpleStringProperty();
+ private final StringProperty extension = new SimpleStringProperty();
+ private final StringProperty mimetype = new SimpleStringProperty();
+ private final StringProperty application = new SimpleStringProperty();
+
+ public ExternalFileTypeItemViewModel(ExternalFileType fileType) {
+ this.icon.setValue(fileType.getIcon());
+ this.name.setValue(fileType.getName());
+ this.extension.setValue(fileType.getExtension());
+ this.mimetype.setValue(fileType.getMimeType());
+ this.application.setValue(fileType.getOpenWithApplication());
+ }
+
+ public ExternalFileTypeItemViewModel() {
+ this(new CustomExternalFileType("", "", "", "", "new", IconTheme.JabRefIcons.FILE));
+ }
+
+ public ObjectProperty iconProperty() {
+ return icon;
+ }
+
+ /**
+ * Used for sorting in the table
+ */
+ public String getName() {
+ return name.get();
+ }
+
+ public StringProperty nameProperty() {
+ return name;
+ }
+
+ public StringProperty extensionProperty() {
+ return extension;
+ }
+
+ public StringProperty mimetypeProperty() {
+ return mimetype;
+ }
+
+ public StringProperty applicationProperty() {
+ return application;
+ }
+
+ public ExternalFileType toExternalFileType() {
+ return new CustomExternalFileType(
+ this.name.get(),
+ this.extension.get(),
+ this.mimetype.get(),
+ this.application.get(),
+ this.icon.get().name(),
+ this.icon.get());
+ }
+}
diff --git a/src/main/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTab.fxml b/src/main/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTab.fxml
index 5d8b4556c70..040bb446dc7 100644
--- a/src/main/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTab.fxml
+++ b/src/main/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTab.fxml
@@ -16,7 +16,7 @@
-
+
diff --git a/src/main/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTab.java b/src/main/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTab.java
index 41998975660..b131484ddc3 100644
--- a/src/main/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTab.java
+++ b/src/main/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTab.java
@@ -4,8 +4,6 @@
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
-import org.jabref.gui.externalfiletype.ExternalFileType;
-import org.jabref.gui.externalfiletype.ExternalFileTypes;
import org.jabref.gui.icon.IconTheme;
import org.jabref.gui.icon.JabRefIcon;
import org.jabref.gui.preferences.AbstractPreferenceTabView;
@@ -21,14 +19,14 @@
*/
public class ExternalFileTypesTab extends AbstractPreferenceTabView implements PreferencesTab {
- @FXML private TableColumn fileTypesTableIconColumn;
- @FXML private TableColumn fileTypesTableNameColumn;
- @FXML private TableColumn fileTypesTableExtensionColumn;
- @FXML private TableColumn fileTypesTableTypeColumn;
- @FXML private TableColumn fileTypesTableApplicationColumn;
- @FXML private TableColumn fileTypesTableEditColumn;
- @FXML private TableColumn fileTypesTableDeleteColumn;
- @FXML private TableView fileTypesTable;
+ @FXML private TableColumn fileTypesTableIconColumn;
+ @FXML private TableColumn fileTypesTableNameColumn;
+ @FXML private TableColumn fileTypesTableExtensionColumn;
+ @FXML private TableColumn fileTypesTableMimeTypeColumn;
+ @FXML private TableColumn fileTypesTableApplicationColumn;
+ @FXML private TableColumn fileTypesTableEditColumn;
+ @FXML private TableColumn fileTypesTableDeleteColumn;
+ @FXML private TableView fileTypesTable;
public ExternalFileTypesTab() {
ViewLoader.view(this)
@@ -43,26 +41,46 @@ public String getTabName() {
@FXML
public void initialize() {
- viewModel = new ExternalFileTypesTabViewModel(ExternalFileTypes.getInstance());
+ viewModel = new ExternalFileTypesTabViewModel(preferencesService.getFilePreferences(), dialogService);
fileTypesTable.setItems(viewModel.getFileTypes());
- fileTypesTableIconColumn.setCellValueFactory(data -> BindingsHelper.constantOf(data.getValue().getIcon()));
- fileTypesTableNameColumn.setCellValueFactory(data -> BindingsHelper.constantOf(data.getValue().getName()));
- fileTypesTableExtensionColumn.setCellValueFactory(data -> BindingsHelper.constantOf(data.getValue().getExtension()));
- fileTypesTableTypeColumn.setCellValueFactory(data -> BindingsHelper.constantOf(data.getValue().getMimeType()));
- fileTypesTableApplicationColumn.setCellValueFactory(data -> BindingsHelper.constantOf(data.getValue().getOpenWithApplication()));
+ fileTypesTableIconColumn.setCellValueFactory(cellData -> cellData.getValue().iconProperty());
+ new ValueTableCellFactory()
+ .withGraphic(JabRefIcon::getGraphicNode)
+ .install(fileTypesTableIconColumn);
+
+ fileTypesTableNameColumn.setCellValueFactory(cellData -> cellData.getValue().nameProperty());
+ new ValueTableCellFactory()
+ .withText(name -> name)
+ .install(fileTypesTableNameColumn);
+
+ fileTypesTableExtensionColumn.setCellValueFactory(cellData -> cellData.getValue().extensionProperty());
+ new ValueTableCellFactory()
+ .withText(extension -> extension)
+ .install(fileTypesTableExtensionColumn);
+
+ fileTypesTableMimeTypeColumn.setCellValueFactory(cellData -> cellData.getValue().mimetypeProperty());
+ new ValueTableCellFactory()
+ .withText(mimetype -> mimetype)
+ .install(fileTypesTableMimeTypeColumn);
+
+ fileTypesTableApplicationColumn.setCellValueFactory(cellData -> cellData.getValue().applicationProperty());
+ new ValueTableCellFactory()
+ .withText(extension -> extension)
+ .install(fileTypesTableApplicationColumn);
+
fileTypesTableEditColumn.setCellValueFactory(data -> BindingsHelper.constantOf(true));
fileTypesTableDeleteColumn.setCellValueFactory(data -> BindingsHelper.constantOf(true));
- new ValueTableCellFactory()
+ new ValueTableCellFactory()
.withGraphic(JabRefIcon::getGraphicNode)
.install(fileTypesTableIconColumn);
- new ValueTableCellFactory()
+ new ValueTableCellFactory()
.withGraphic(none -> IconTheme.JabRefIcons.EDIT.getGraphicNode())
.withOnMouseClickedEvent((type, none) -> event -> viewModel.edit(type))
.install(fileTypesTableEditColumn);
- new ValueTableCellFactory()
+ new ValueTableCellFactory()
.withGraphic(none -> IconTheme.JabRefIcons.DELETE_ENTRY.getGraphicNode())
.withOnMouseClickedEvent((type, none) -> event -> viewModel.remove(type))
.install(fileTypesTableDeleteColumn);
diff --git a/src/main/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModel.java b/src/main/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModel.java
index ee669177d6d..8a1faf6f8f4 100644
--- a/src/main/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModel.java
+++ b/src/main/java/org/jabref/gui/preferences/externalfiletypes/ExternalFileTypesTabViewModel.java
@@ -1,75 +1,77 @@
package org.jabref.gui.preferences.externalfiletypes;
import java.util.Comparator;
-import java.util.List;
+import java.util.HashSet;
+import java.util.Set;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import org.jabref.gui.DialogService;
-import org.jabref.gui.externalfiletype.CustomExternalFileType;
-import org.jabref.gui.externalfiletype.EditExternalFileTypeEntryDialog;
import org.jabref.gui.externalfiletype.ExternalFileType;
import org.jabref.gui.externalfiletype.ExternalFileTypes;
-import org.jabref.gui.icon.IconTheme;
import org.jabref.gui.preferences.PreferenceTabViewModel;
import org.jabref.logic.l10n.Localization;
-
-import com.airhacks.afterburner.injection.Injector;
+import org.jabref.preferences.FilePreferences;
public class ExternalFileTypesTabViewModel implements PreferenceTabViewModel {
- private final ExternalFileTypes externalFileTypes;
- private final ObservableList fileTypes = FXCollections.observableArrayList();
+ private final ObservableList fileTypes = FXCollections.observableArrayList();
+
+ private final FilePreferences filePreferences;
+ private final DialogService dialogService;
- public ExternalFileTypesTabViewModel(ExternalFileTypes externalFileTypes) {
- this.externalFileTypes = externalFileTypes;
+ public ExternalFileTypesTabViewModel(FilePreferences filePreferences, DialogService dialogService) {
+ this.filePreferences = filePreferences;
+ this.dialogService = dialogService;
}
@Override
public void setValues() {
- fileTypes.setAll(externalFileTypes.getExternalFileTypeSelection());
- fileTypes.sort(Comparator.comparing(ExternalFileType::getName));
+ fileTypes.addAll(filePreferences.getExternalFileTypes().stream()
+ .map(ExternalFileTypeItemViewModel::new)
+ .toList());
+ fileTypes.sort(Comparator.comparing(ExternalFileTypeItemViewModel::getName));
}
public void storeSettings() {
- externalFileTypes.setExternalFileTypes(fileTypes);
+ Set saveList = new HashSet<>();
+
+ fileTypes.stream().map(ExternalFileTypeItemViewModel::toExternalFileType)
+ .forEach(type -> ExternalFileTypes.getDefaultExternalFileTypes().stream()
+ .filter(type::equals).findAny()
+ .ifPresentOrElse(saveList::add, () -> saveList.add(type)));
+
+ filePreferences.getExternalFileTypes().clear();
+ filePreferences.getExternalFileTypes().addAll(saveList);
}
public void resetToDefaults() {
- List list = ExternalFileTypes.getDefaultExternalFileTypes();
- fileTypes.setAll(list);
- fileTypes.sort(Comparator.comparing(ExternalFileType::getName));
+ fileTypes.setAll(ExternalFileTypes.getDefaultExternalFileTypes().stream()
+ .map(ExternalFileTypeItemViewModel::new)
+ .toList());
+ fileTypes.sort(Comparator.comparing(ExternalFileTypeItemViewModel::getName));
}
public void addNewType() {
- CustomExternalFileType type = new CustomExternalFileType("", "", "", "", "new", IconTheme.JabRefIcons.FILE);
- fileTypes.add(type);
- showEditDialog(type, Localization.lang("Add new file type"));
+ ExternalFileTypeItemViewModel item = new ExternalFileTypeItemViewModel();
+ fileTypes.add(item);
+ showEditDialog(item, Localization.lang("Add new file type"));
}
- public ObservableList getFileTypes() {
+ public ObservableList getFileTypes() {
return fileTypes;
}
- private void showEditDialog(ExternalFileType type, String dialogTitle) {
- CustomExternalFileType typeForEdit;
- if (type instanceof CustomExternalFileType) {
- typeForEdit = (CustomExternalFileType) type;
- } else {
- typeForEdit = new CustomExternalFileType(type);
- fileTypes.add(fileTypes.indexOf(type), typeForEdit);
- fileTypes.remove(type);
- }
- DialogService dialogService = Injector.instantiateModelOrService(DialogService.class);
- dialogService.showCustomDialogAndWait(new EditExternalFileTypeEntryDialog(typeForEdit, dialogTitle));
+ private void showEditDialog(ExternalFileTypeItemViewModel item, String dialogTitle) {
+ dialogService.showCustomDialogAndWait(new EditExternalFileTypeEntryDialog(item, dialogTitle));
}
- public void edit(ExternalFileType type) {
+ public void edit(ExternalFileTypeItemViewModel type) {
showEditDialog(type, Localization.lang("Edit file type"));
}
- public void remove(ExternalFileType type) {
+ public void remove(ExternalFileTypeItemViewModel type) {
fileTypes.remove(type);
}
}
diff --git a/src/main/java/org/jabref/gui/preferences/importexport/ImportExportTab.java b/src/main/java/org/jabref/gui/preferences/importexport/ImportExportTab.java
index 86fc0ba223a..49c7fd20563 100644
--- a/src/main/java/org/jabref/gui/preferences/importexport/ImportExportTab.java
+++ b/src/main/java/org/jabref/gui/preferences/importexport/ImportExportTab.java
@@ -46,7 +46,7 @@ public String getTabName() {
}
public void initialize() {
- this.viewModel = new ImportExportTabViewModel(preferencesService, preferencesService.getDOIPreferences(), dialogService, preferencesService.getImportExportPreferences());
+ this.viewModel = new ImportExportTabViewModel(preferencesService, dialogService);
useCustomDOI.selectedProperty().bindBidirectional(viewModel.useCustomDOIProperty());
useCustomDOIName.textProperty().bindBidirectional(viewModel.useCustomDOINameProperty());
diff --git a/src/main/java/org/jabref/gui/preferences/importexport/ImportExportTabViewModel.java b/src/main/java/org/jabref/gui/preferences/importexport/ImportExportTabViewModel.java
index 3c3fe3472bd..c3e1edef304 100644
--- a/src/main/java/org/jabref/gui/preferences/importexport/ImportExportTabViewModel.java
+++ b/src/main/java/org/jabref/gui/preferences/importexport/ImportExportTabViewModel.java
@@ -27,6 +27,7 @@
import org.jabref.logic.importer.ImporterPreferences;
import org.jabref.logic.importer.WebFetchers;
import org.jabref.logic.importer.fetcher.CustomizableKeyFetcher;
+import org.jabref.logic.importer.fetcher.GrobidPreferences;
import org.jabref.logic.l10n.Localization;
import org.jabref.logic.net.URLDownload;
import org.jabref.logic.preferences.DOIPreferences;
@@ -61,17 +62,19 @@ public class ImportExportTabViewModel implements PreferenceTabViewModel {
private final DialogService dialogService;
private final PreferencesService preferencesService;
private final DOIPreferences doiPreferences;
+ private final GrobidPreferences grobidPreferences;
private final ImporterPreferences importerPreferences;
private final SaveOrderConfig initialExportOrder;
private final ImportExportPreferences importExportPreferences;
- public ImportExportTabViewModel(PreferencesService preferencesService, DOIPreferences doiPreferences, DialogService dialogService, ImportExportPreferences importExportPreferences) {
+ public ImportExportTabViewModel(PreferencesService preferencesService, DialogService dialogService) {
this.dialogService = dialogService;
this.preferencesService = preferencesService;
this.importerPreferences = preferencesService.getImporterPreferences();
- this.doiPreferences = doiPreferences;
+ this.grobidPreferences = preferencesService.getGrobidPreferences();
+ this.doiPreferences = preferencesService.getDOIPreferences();
this.initialExportOrder = preferencesService.getExportSaveOrder();
- this.importExportPreferences = importExportPreferences;
+ this.importExportPreferences = preferencesService.getImportExportPreferences();
}
@Override
@@ -95,8 +98,8 @@ public void setValues() {
.map(SortCriterionViewModel::new)
.toList());
- grobidEnabledProperty.setValue(importerPreferences.isGrobidEnabled());
- grobidURLProperty.setValue(importerPreferences.getGrobidURL());
+ grobidEnabledProperty.setValue(grobidPreferences.isGrobidEnabled());
+ grobidURLProperty.setValue(grobidPreferences.getGrobidURL());
apiKeys.setValue(FXCollections.observableArrayList(preferencesService.getImporterPreferences().getApiKeys()));
}
@@ -104,9 +107,9 @@ public void setValues() {
@Override
public void storeSettings() {
importerPreferences.setGenerateNewKeyOnImport(generateKeyOnImportProperty.getValue());
- importerPreferences.setGrobidEnabled(grobidEnabledProperty.getValue());
- importerPreferences.setGrobidOptOut(importerPreferences.isGrobidOptOut());
- importerPreferences.setGrobidURL(grobidURLProperty.getValue());
+ grobidPreferences.setGrobidEnabled(grobidEnabledProperty.getValue());
+ grobidPreferences.setGrobidOptOut(grobidPreferences.isGrobidOptOut());
+ grobidPreferences.setGrobidURL(grobidURLProperty.getValue());
doiPreferences.setUseCustom(useCustomDOIProperty.get());
doiPreferences.setDefaultBaseURI(useCustomDOINameProperty.getValue().trim());
diff --git a/src/main/java/org/jabref/gui/preferences/protectedterms/ProtectedTermsTabViewModel.java b/src/main/java/org/jabref/gui/preferences/protectedterms/ProtectedTermsTabViewModel.java
index ca6b668ab5d..62c09375df6 100644
--- a/src/main/java/org/jabref/gui/preferences/protectedterms/ProtectedTermsTabViewModel.java
+++ b/src/main/java/org/jabref/gui/preferences/protectedterms/ProtectedTermsTabViewModel.java
@@ -125,8 +125,8 @@ public void createNewFile() {
public void edit(ProtectedTermsListItemModel file) {
Optional termsFileType = OptionalUtil.orElse(
- ExternalFileTypes.getInstance().getExternalFileTypeByExt("terms"),
- ExternalFileTypes.getInstance().getExternalFileTypeByExt("txt")
+ ExternalFileTypes.getExternalFileTypeByExt("terms", preferences.getFilePreferences()),
+ ExternalFileTypes.getExternalFileTypeByExt("txt", preferences.getFilePreferences())
);
String fileName = file.getTermsList().getLocation();
diff --git a/src/main/java/org/jabref/gui/preferences/table/TableTabViewModel.java b/src/main/java/org/jabref/gui/preferences/table/TableTabViewModel.java
index 2184469c933..ecae4a7d091 100644
--- a/src/main/java/org/jabref/gui/preferences/table/TableTabViewModel.java
+++ b/src/main/java/org/jabref/gui/preferences/table/TableTabViewModel.java
@@ -14,7 +14,6 @@
import org.jabref.gui.DialogService;
import org.jabref.gui.externalfiletype.ExternalFileType;
-import org.jabref.gui.externalfiletype.ExternalFileTypes;
import org.jabref.gui.maintable.ColumnPreferences;
import org.jabref.gui.maintable.MainTableColumnModel;
import org.jabref.gui.maintable.MainTableNameFormatPreferences;
@@ -180,10 +179,10 @@ private void removeSpecialFieldColumns() {
}
private void insertExtraFileColumns() {
- ExternalFileTypes.getInstance().getExternalFileTypeSelection().stream()
- .map(ExternalFileType::getName)
- .map(name -> new MainTableColumnModel(MainTableColumnModel.Type.EXTRAFILE, name))
- .forEach(item -> availableColumnsProperty.getValue().add(item));
+ preferences.getFilePreferences().getExternalFileTypes().stream()
+ .map(ExternalFileType::getName)
+ .map(name -> new MainTableColumnModel(MainTableColumnModel.Type.EXTRAFILE, name))
+ .forEach(item -> availableColumnsProperty.getValue().add(item));
}
private void removeExtraFileColumns() {
diff --git a/src/main/java/org/jabref/gui/preview/PreviewPanel.java b/src/main/java/org/jabref/gui/preview/PreviewPanel.java
index f16b27d8b13..94cf57cd1e3 100644
--- a/src/main/java/org/jabref/gui/preview/PreviewPanel.java
+++ b/src/main/java/org/jabref/gui/preview/PreviewPanel.java
@@ -19,7 +19,6 @@
import org.jabref.gui.DialogService;
import org.jabref.gui.StateManager;
import org.jabref.gui.externalfiles.ExternalFilesEntryLinker;
-import org.jabref.gui.externalfiletype.ExternalFileTypes;
import org.jabref.gui.icon.IconTheme;
import org.jabref.gui.keyboard.KeyBinding;
import org.jabref.gui.keyboard.KeyBindingRepository;
@@ -50,7 +49,6 @@ public class PreviewPanel extends VBox {
public PreviewPanel(BibDatabaseContext database,
DialogService dialogService,
- ExternalFileTypes externalFileTypes,
KeyBindingRepository keyBindingRepository,
PreferencesService preferences,
StateManager stateManager,
@@ -61,7 +59,7 @@ public PreviewPanel(BibDatabaseContext database,
this.stateManager = stateManager;
this.previewPreferences = preferences.getPreviewPreferences();
this.indexingTaskManager = indexingTaskManager;
- this.fileLinker = new ExternalFilesEntryLinker(externalFileTypes, preferences.getFilePreferences(), database);
+ this.fileLinker = new ExternalFilesEntryLinker(preferences.getFilePreferences(), database);
PreviewPreferences previewPreferences = preferences.getPreviewPreferences();
previewView = new PreviewViewer(database, dialogService, stateManager, themeManager);
diff --git a/src/main/java/org/jabref/gui/search/GlobalSearchBar.java b/src/main/java/org/jabref/gui/search/GlobalSearchBar.java
index d6b3bfc0364..d42bcd34cb7 100644
--- a/src/main/java/org/jabref/gui/search/GlobalSearchBar.java
+++ b/src/main/java/org/jabref/gui/search/GlobalSearchBar.java
@@ -47,7 +47,6 @@
import org.jabref.gui.autocompleter.AutoCompletionTextInputBinding;
import org.jabref.gui.autocompleter.PersonNameStringConverter;
import org.jabref.gui.autocompleter.SuggestionProvider;
-import org.jabref.gui.externalfiletype.ExternalFileTypes;
import org.jabref.gui.icon.IconTheme;
import org.jabref.gui.keyboard.KeyBinding;
import org.jabref.gui.keyboard.KeyBindingRepository;
@@ -239,7 +238,7 @@ private void initSearchModifierButtons() {
initSearchModifierButton(openGlobalSearchButton);
openGlobalSearchButton.setOnAction(evt -> {
globalSearchActive.setValue(true);
- globalSearchResultDialog = new GlobalSearchResultDialog(ExternalFileTypes.getInstance(), undoManager);
+ globalSearchResultDialog = new GlobalSearchResultDialog(undoManager);
performSearch();
globalSearchResultDialog.showAndWait();
globalSearchActive.setValue(false);
diff --git a/src/main/java/org/jabref/gui/search/GlobalSearchResultDialog.java b/src/main/java/org/jabref/gui/search/GlobalSearchResultDialog.java
index 59843b2e4cb..b23dc8ab2df 100644
--- a/src/main/java/org/jabref/gui/search/GlobalSearchResultDialog.java
+++ b/src/main/java/org/jabref/gui/search/GlobalSearchResultDialog.java
@@ -11,7 +11,6 @@
import org.jabref.gui.DialogService;
import org.jabref.gui.StateManager;
-import org.jabref.gui.externalfiletype.ExternalFileTypes;
import org.jabref.gui.icon.IconTheme;
import org.jabref.gui.maintable.columns.SpecialFieldColumn;
import org.jabref.gui.preview.PreviewViewer;
@@ -28,7 +27,6 @@ public class GlobalSearchResultDialog extends BaseDialog {
@FXML private SplitPane container;
@FXML private ToggleButton keepOnTop;
- private final ExternalFileTypes externalFileTypes;
private final UndoManager undoManager;
@Inject private PreferencesService preferencesService;
@@ -38,9 +36,8 @@ public class GlobalSearchResultDialog extends BaseDialog {
private GlobalSearchResultDialogViewModel viewModel;
- public GlobalSearchResultDialog(ExternalFileTypes externalFileTypes, UndoManager undoManager) {
+ public GlobalSearchResultDialog(UndoManager undoManager) {
this.undoManager = undoManager;
- this.externalFileTypes = externalFileTypes;
setTitle(Localization.lang("Search results from open libraries"));
ViewLoader.view(this)
@@ -57,7 +54,7 @@ private void initialize() {
previewViewer.setLayout(preferencesService.getPreviewPreferences().getSelectedPreviewLayout());
SearchResultsTableDataModel model = new SearchResultsTableDataModel(viewModel.getSearchDatabaseContext(), preferencesService, stateManager);
- SearchResultsTable resultsTable = new SearchResultsTable(model, viewModel.getSearchDatabaseContext(), preferencesService, undoManager, dialogService, stateManager, externalFileTypes);
+ SearchResultsTable resultsTable = new SearchResultsTable(model, viewModel.getSearchDatabaseContext(), preferencesService, undoManager, dialogService, stateManager);
resultsTable.getColumns().removeIf(col -> col instanceof SpecialFieldColumn);
resultsTable.getSelectionModel().selectFirst();
diff --git a/src/main/java/org/jabref/gui/search/SearchResultsTable.java b/src/main/java/org/jabref/gui/search/SearchResultsTable.java
index 09083921f7a..a0f3ac20ea6 100644
--- a/src/main/java/org/jabref/gui/search/SearchResultsTable.java
+++ b/src/main/java/org/jabref/gui/search/SearchResultsTable.java
@@ -10,7 +10,6 @@
import org.jabref.gui.DialogService;
import org.jabref.gui.StateManager;
-import org.jabref.gui.externalfiletype.ExternalFileTypes;
import org.jabref.gui.maintable.BibEntryTableViewModel;
import org.jabref.gui.maintable.MainTable;
import org.jabref.gui.maintable.MainTableColumnFactory;
@@ -28,8 +27,7 @@ public SearchResultsTable(SearchResultsTableDataModel model,
PreferencesService preferencesService,
UndoManager undoManager,
DialogService dialogService,
- StateManager stateManager,
- ExternalFileTypes externalFileTypes) {
+ StateManager stateManager) {
super();
MainTablePreferences mainTablePreferences = preferencesService.getMainTablePreferences();
@@ -38,7 +36,6 @@ public SearchResultsTable(SearchResultsTableDataModel model,
database,
preferencesService,
preferencesService.getSearchDialogColumnPreferences(),
- externalFileTypes,
undoManager,
dialogService,
stateManager).createColumns();
diff --git a/src/main/java/org/jabref/gui/shared/SharedDatabaseUIManager.java b/src/main/java/org/jabref/gui/shared/SharedDatabaseUIManager.java
index e5c2a0e9732..6b87d7c620a 100644
--- a/src/main/java/org/jabref/gui/shared/SharedDatabaseUIManager.java
+++ b/src/main/java/org/jabref/gui/shared/SharedDatabaseUIManager.java
@@ -15,8 +15,8 @@
import org.jabref.gui.LibraryTab;
import org.jabref.gui.entryeditor.EntryEditor;
import org.jabref.gui.exporter.SaveDatabaseAction;
+import org.jabref.gui.mergeentries.EntriesMergeResult;
import org.jabref.gui.mergeentries.MergeEntriesDialog;
-import org.jabref.gui.mergeentries.MergeResult;
import org.jabref.gui.undo.UndoableRemoveEntries;
import org.jabref.logic.importer.ParserResult;
import org.jabref.logic.l10n.Localization;
@@ -100,7 +100,7 @@ public void listen(UpdateRefusedEvent updateRefusedEvent) {
if (response.isPresent() && response.get().equals(merge)) {
MergeEntriesDialog dialog = new MergeEntriesDialog(localBibEntry, sharedBibEntry);
- Optional mergedEntry = dialogService.showCustomDialogAndWait(dialog).map(MergeResult::mergedEntry);
+ Optional mergedEntry = dialogService.showCustomDialogAndWait(dialog).map(EntriesMergeResult::mergedEntry);
mergedEntry.ifPresent(mergedBibEntry -> {
mergedBibEntry.getSharedBibEntryData().setSharedID(sharedBibEntry.getSharedBibEntryData().getSharedID());
diff --git a/src/main/java/org/jabref/gui/slr/ExistingStudySearchAction.java b/src/main/java/org/jabref/gui/slr/ExistingStudySearchAction.java
index c45b7f2fc1c..284e8c9a487 100644
--- a/src/main/java/org/jabref/gui/slr/ExistingStudySearchAction.java
+++ b/src/main/java/org/jabref/gui/slr/ExistingStudySearchAction.java
@@ -130,7 +130,7 @@ public void crawl() {
dialogService.showErrorDialogAndWait(Localization.lang("Error during persistence of crawling results."), e);
})
.onSuccess(unused -> {
- new OpenDatabaseAction(frame, preferencesService, dialogService, stateManager, themeManager).openFile(Path.of(studyDirectory.toString(), "studyResult.bib"), true);
+ new OpenDatabaseAction(frame, preferencesService, dialogService, stateManager, themeManager).openFile(Path.of(studyDirectory.toString(), "studyResult.bib"));
// If finished reset command object for next use
studyDirectory = null;
})
diff --git a/src/main/java/org/jabref/gui/slr/ManageStudyDefinition.fxml b/src/main/java/org/jabref/gui/slr/ManageStudyDefinition.fxml
index b6ea1d41f7a..297ee2c1318 100644
--- a/src/main/java/org/jabref/gui/slr/ManageStudyDefinition.fxml
+++ b/src/main/java/org/jabref/gui/slr/ManageStudyDefinition.fxml
@@ -2,7 +2,6 @@
-
@@ -19,50 +18,86 @@
-
+