diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index e754e4fd43e..dbc1b61775b 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -12,7 +12,7 @@ body: attributes: label: JabRef version options: - - "5.11 (latest release)" + - "5.12 (latest release)" - Latest development branch build (please note build date below) - Other (please describe below) description: The version as shown in the about dialog. diff --git a/.github/workflows/check-links.yml b/.github/workflows/check-links.yml index a121d295643..0fa9a5f1670 100644 --- a/.github/workflows/check-links.yml +++ b/.github/workflows/check-links.yml @@ -11,7 +11,7 @@ on: workflow_dispatch: concurrency: - group: "${{ github.workflow }}-${{ github.head_ref }}" + group: "${{ github.workflow }}-${{ github.head_ref || github.ref }}" cancel-in-progress: true jobs: @@ -29,7 +29,7 @@ jobs: restore-keys: cache-lychee- - name: Link Checker id: lychee - uses: lycheeverse/lychee-action@v1.8.0 + uses: lycheeverse/lychee-action@v1.9.0 with: fail: true - args: --max-concurrency 1 --cache --no-progress --exclude-all-private './**/*.md' + args: --accept '200,201,202,203,204,429,500' --max-concurrency 1 --cache --no-progress --exclude-all-private './**/*.md' diff --git a/.github/workflows/cleanup-pr.yml b/.github/workflows/cleanup-pr.yml index 77a8874bc6b..9444fd882b6 100644 --- a/.github/workflows/cleanup-pr.yml +++ b/.github/workflows/cleanup-pr.yml @@ -26,7 +26,7 @@ jobs: BUILDJABREFPRIVATEKEY: ${{ secrets.buildJabRefPrivateKey }} - name: Delete folder on builds.jabref.org if: steps.checksecrets.outputs.secretspresent == 'YES' - uses: appleboy/ssh-action@v1.0.0 + uses: appleboy/ssh-action@v1.0.3 with: script: rm -rf /var/www/builds.jabref.org/www/pull/${{ github.event.pull_request.number }} || true host: build-upload.jabref.org diff --git a/.github/workflows/deployment-arm64.yml b/.github/workflows/deployment-arm64.yml index b5cb0cf4bc0..02967c123ea 100644 --- a/.github/workflows/deployment-arm64.yml +++ b/.github/workflows/deployment-arm64.yml @@ -22,7 +22,7 @@ env: JAVA_OPTS: -Xmx4g concurrency: - group: ${{ github.workflow }}-${{ github.ref }} + group: "${{ github.workflow }}-${{ github.head_ref || github.ref }}" cancel-in-progress: true jobs: @@ -59,11 +59,10 @@ jobs: id: gitversion uses: gittools/actions/gitversion/execute@v0.10.2 - name: Setup JDK - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: 21.0.1 distribution: 'liberica' - cache: 'gradle' - name: Clean up keychain run: | security delete-keychain signing_temp.keychain ${{runner.temp}}/keychain/notarization.keychain || true @@ -85,6 +84,8 @@ jobs: mkdir ${{runner.temp}}/keychain security create-keychain -p jabref ${{runner.temp}}/keychain/notarization.keychain security set-keychain-settings ${{runner.temp}}/keychain/notarization.keychain + - name: Setup Gradle + uses: gradle/gradle-build-action@v2 - name: Prepare merged jars and modules dir (macOS) run: ./gradlew -i -PprojVersion="${{ steps.gitversion.outputs.AssemblySemVer }}" -PprojVersionInfo="${{ steps.gitversion.outputs.InformationalVersion }}" prepareModulesDir - name: Build dmg (macOS) @@ -161,7 +162,7 @@ jobs: rsync -Pavz --itemize-changes --stats --partial-dir=/tmp/partial --rsync-path="mkdir -p /var/www/builds.jabref.org/www/${{ steps.gitversion.outputs.branchName }} && rsync" -e 'ssh -p 9922 -i ~/.ssh/id_rsa' build/distribution/ jrrsync@build-upload.jabref.org:/var/www/builds.jabref.org/www/${{ steps.gitversion.outputs.branchName }}/ - name: Upload to GitHub workflow artifacts store if: ${{ !startsWith(github.ref, 'refs/heads/gh-readonly-queue') }} - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: JabRef-${{ matrix.displayName }} path: build/distribution diff --git a/.github/workflows/deployment.yml b/.github/workflows/deployment.yml index c23f0ccf516..d4e176743f6 100644 --- a/.github/workflows/deployment.yml +++ b/.github/workflows/deployment.yml @@ -31,7 +31,7 @@ env: JDK21: "" concurrency: - group: ${{ github.workflow }}-${{ github.ref }} + group: "${{ github.workflow }}-${{ github.head_ref || github.ref }}" cancel-in-progress: true jobs: @@ -90,11 +90,12 @@ jobs: id: gitversion uses: gittools/actions/gitversion/execute@v0.10.2 - name: Setup JDK - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: 21.0.1 distribution: 'liberica' - cache: 'gradle' + - name: Setup Gradle + uses: gradle/gradle-build-action@v2 - name: Prepare merged jars and modules dir (macOS) if: (matrix.os == 'macos-latest') || (steps.checksecrets.outputs.secretspresent == 'NO') run: ./gradlew -i -PprojVersion="${{ steps.gitversion.outputs.AssemblySemVer }}" -PprojVersionInfo="${{ steps.gitversion.outputs.InformationalVersion }}" prepareModulesDir @@ -219,14 +220,14 @@ jobs: rsync -rt --chmod=Du=rwx,Dg=rx,Do=rx,Fu=rw,Fg=r,Fo=r --itemize-changes --stats --rsync-path="mkdir -p /var/www/builds.jabref.org/www/${{ steps.gitversion.outputs.branchName }} && rsync" -e 'ssh -p 9922 -i sshkey -o StrictHostKeyChecking=no' build/distribution/ jrrsync@build-upload.jabref.org:/var/www/builds.jabref.org/www/${{ steps.gitversion.outputs.branchName }}/ - name: Upload to GitHub workflow artifacts store (macOS) if: (matrix.os == 'macos-latest') && (steps.checksecrets.outputs.secretspresent == 'YES') && (startsWith(github.ref, 'refs/tags/') || inputs.notarization == true) - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: # tbn = to-be-notarized name: JabRef-macOS-tbn path: build/distribution - name: Upload to GitHub workflow artifacts store if: (steps.checksecrets.outputs.secretspresent != 'YES') - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: # tbn = to-be-notarized name: JabRef-${{ matrix.os }} diff --git a/.github/workflows/gource.yml b/.github/workflows/gource.yml index ccde09af4f9..a1cb073f6f8 100644 --- a/.github/workflows/gource.yml +++ b/.github/workflows/gource.yml @@ -64,7 +64,7 @@ jobs: run: | mv gource/gource.mp4 gource-videos/jabref-complete.mp4 - name: 'Upload gource video' - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: Gource path: gource-videos/ diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml index cf662647b3e..761626a85ce 100644 --- a/.github/workflows/pages.yml +++ b/.github/workflows/pages.yml @@ -39,13 +39,13 @@ jobs: working-directory: docs/ - name: Setup Pages id: pages - uses: actions/configure-pages@v3 + uses: actions/configure-pages@v4 - name: Build with Jekyll run: | cd docs bundle exec jekyll build - name: Upload artifact - uses: actions/upload-pages-artifact@v2 + uses: actions/upload-pages-artifact@v3 with: path: docs/_site/ @@ -59,4 +59,4 @@ jobs: steps: - name: Deploy to GitHub Pages id: deployment - uses: actions/deploy-pages@v2 + uses: actions/deploy-pages@v4 diff --git a/.github/workflows/refresh-csl-subtrees.yml b/.github/workflows/refresh-csl-subtrees.yml index 3e86ac97267..789bd4b6ba0 100644 --- a/.github/workflows/refresh-csl-subtrees.yml +++ b/.github/workflows/refresh-csl-subtrees.yml @@ -31,6 +31,11 @@ jobs: cd src/main/resources/csl-locales git checkout master git pull + - name: Persist changes + run: | + git add -f src/main/resources/csl-styles + git add -f src/main/resources/csl-locales + git diff-index --quiet HEAD || git commit -m 'Update CSL styles' - uses: peter-evans/create-pull-request@v5 with: token: ${{ secrets.GH_TOKEN_UPDATE_GRADLE_WRAPPER }} diff --git a/.github/workflows/refresh-journal-lists.yml b/.github/workflows/refresh-journal-lists.yml index 88f43f673c1..367c905031c 100644 --- a/.github/workflows/refresh-journal-lists.yml +++ b/.github/workflows/refresh-journal-lists.yml @@ -28,14 +28,19 @@ jobs: git checkout main git pull - name: Set up JDK - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: 21.0.1 distribution: 'liberica' - cache: 'gradle' + - name: Setup Gradle + uses: gradle/gradle-build-action@v2 - name: Check whether journal-list.mv can be generated (the "real" generation is done inside JabRef's build process) run: | ./gradlew generateJournalListMV + - name: Persist changes + run: | + git add -f buildres/abbrv.jabref.org + git diff-index --quiet HEAD || git commit -m 'Update journal abbreviation lists' - uses: peter-evans/create-pull-request@v5 with: token: ${{ secrets.GH_TOKEN_UPDATE_GRADLE_WRAPPER }} diff --git a/.github/workflows/tests-fetchers.yml b/.github/workflows/tests-fetchers.yml index ee155b0c243..f107721e328 100644 --- a/.github/workflows/tests-fetchers.yml +++ b/.github/workflows/tests-fetchers.yml @@ -29,7 +29,7 @@ env: BiodiversityHeritageApiKey: ${{ secrets.BiodiversityHeritageApiKey_FOR_TESTS}} concurrency: - group: ${{ github.workflow }}-${{ github.ref }} + group: "${{ github.workflow }}-${{ github.head_ref || github.ref }}" cancel-in-progress: true permissions: @@ -46,11 +46,12 @@ jobs: submodules: 'true' show-progress: 'false' - name: Set up JDK - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: 21.0.1 distribution: 'liberica' - cache: 'gradle' + - name: Setup Gradle + uses: gradle/gradle-build-action@v2 - name: Run fetcher tests run: ./gradlew fetcherTest env: diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 86ae1543a1a..af7f28c8fc0 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -18,7 +18,7 @@ env: JAVA_OPTS: -Xmx4g concurrency: - group: tests-${{ github.head_ref }} + group: "${{ github.workflow }}-${{ github.head_ref || github.ref }}" cancel-in-progress: true permissions: @@ -35,11 +35,10 @@ jobs: submodules: 'true' show-progress: 'false' - name: Set up JDK - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: 21.0.1 distribution: 'liberica' - cache: 'gradle' - name: Run checkstyle reporter uses: nikitasavinov/checkstyle-action@master with: @@ -47,6 +46,8 @@ jobs: github_token: ${{ secrets.GITHUB_TOKEN }} checkstyle_config: 'config/checkstyle/checkstyle_reviewdog.xml' checkstyle_version: '10.3' + - name: Setup Gradle + uses: gradle/gradle-build-action@v2 - name: Run checkstyle using gradle run: ./gradlew checkstyleMain checkstyleTest checkstyleJmh openrewrite: @@ -59,11 +60,12 @@ jobs: submodules: 'true' show-progress: 'false' - name: Set up JDK - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: 21.0.1 distribution: 'liberica' - cache: 'gradle' + - name: Setup Gradle + uses: gradle/gradle-build-action@v2 - name: Run OpenRewrite run: | ./gradlew rewriteDryRun @@ -77,11 +79,12 @@ jobs: submodules: 'true' show-progress: 'false' - name: Set up JDK - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: 21.0.1 distribution: 'liberica' - cache: 'gradle' + - name: Setup Gradle + uses: gradle/gradle-build-action@v2 - name: Run modernizer run: | # enable failing of this task if modernizer complains @@ -97,7 +100,7 @@ jobs: submodules: 'false' show-progress: 'false' - name: markdownlint-cli2-action - uses: DavidAnson/markdownlint-cli2-action@v13 + uses: DavidAnson/markdownlint-cli2-action@v14 with: globs: | *.md @@ -158,11 +161,12 @@ jobs: submodules: 'true' show-progress: 'false' - name: Set up JDK - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: 21.0.1 distribution: 'liberica' - cache: 'gradle' + - name: Setup Gradle + uses: gradle/gradle-build-action@v2 - name: Run tests run: xvfb-run --auto-servernum ./gradlew check -x checkstyleJmh -x checkstyleMain -x checkstyleTest -x modernizer env: @@ -193,11 +197,12 @@ jobs: submodules: 'true' show-progress: 'false' - name: Set up JDK - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: 21.0.1 distribution: 'liberica' - cache: 'gradle' + - name: Setup Gradle + uses: gradle/gradle-build-action@v2 - name: Run tests on PostgreSQL run: ./gradlew databaseTest --rerun-tasks env: @@ -230,11 +235,12 @@ jobs: submodules: 'true' show-progress: 'false' - name: Set up JDK - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: 21.0.1 distribution: 'liberica' - cache: 'gradle' + - name: Setup Gradle + uses: gradle/gradle-build-action@v2 - name: Run GUI tests run: xvfb-run --auto-servernum ./gradlew guiTest env: @@ -274,11 +280,12 @@ jobs: show-progress: 'false' - name: Set up JDK if: github.ref == 'refs/heads/main' - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: 21.0.1 distribution: 'liberica' - cache: 'gradle' + - name: Setup Gradle + uses: gradle/gradle-build-action@v2 - name: Update test coverage metrics if: (github.ref == 'refs/heads/main') && (steps.checksecrets.outputs.secretspresent == 'YES') run: xvfb-run --auto-servernum ./gradlew jacocoTestReport diff --git a/.github/workflows/update-gradle-wrapper.yml b/.github/workflows/update-gradle-wrapper.yml index afa016cb262..1cce0bab119 100644 --- a/.github/workflows/update-gradle-wrapper.yml +++ b/.github/workflows/update-gradle-wrapper.yml @@ -8,9 +8,14 @@ on: jobs: update-gradle-wrapper: runs-on: ubuntu-latest - + steps: - uses: actions/checkout@v4 + - name: Setup JDK + uses: actions/setup-java@v4 + with: + java-version: 21.0.1 + distribution: 'liberica' - name: Update Gradle Wrapper uses: gradle-update/update-gradle-wrapper-action@v1 diff --git a/.gitmodules b/.gitmodules index 39f867d9511..10944a3d5a0 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,9 +1,15 @@ -[submodule "buildres/abbrv.jabref.org"] +[submodule "abbrv.jabref.org"] path = buildres/abbrv.jabref.org url = https://github.com/JabRef/abbrv.jabref.org.git -[submodule "buildres/csl-styles"] + ignore = all + shallow = true +[submodule "csl-styles"] path = src/main/resources/csl-styles url = https://github.com/citation-style-language/styles.git -[submodule "buildres/csl-locales"] + ignore = all + shallow = true +[submodule "csl-locales"] path = src/main/resources/csl-locales url = https://github.com/citation-style-language/locales.git + ignore = all + shallow = true diff --git a/.lycheeignore b/.lycheeignore index 8d3f2072f17..801ddc56e23 100644 --- a/.lycheeignore +++ b/.lycheeignore @@ -2,6 +2,7 @@ https://arxiv.org/ https://contribute.jabref.org/ https://donations.jabref.org/ https://pubs.acs.org/ +https://scholar.archive.org/ https://web.archive.org/ https://www.researchgate.net/privacy-policy https://www.sciencedirect.com/ diff --git a/CHANGELOG.md b/CHANGELOG.md index ffaa0b80b05..744c6d801b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,11 +11,38 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv ### Added +- When importing entries form the "Citation relations" tab, the field [cites](https://docs.jabref.org/advanced/entryeditor/entrylinks) is now filled according to the relationship between the entries. [#10572](https://github.com/JabRef/jabref/pull/10752) + +### Changed + +- The "Automatically open folders of attached files" preference default status has been changed to enabled on Windows. [koppor#56](https://github.com/koppor/jabref/issues/56) +- The Custom export format now uses the custom DOI base URI in the preferences for the `DOICheck`, if activated [forum#4084](https://discourse.jabref.org/t/export-html-disregards-custom-doi-base-uri/4084) +- The index directories for full text search have now more readable names to increase debugging possibilities using Apache Lucense's Lurk. [#10193](https://github.com/JabRef/jabref/issues/10193) +- The fulltext search also indexes files ending with .pdf (but do not having an explicit file type set). [#10193](https://github.com/JabRef/jabref/issues/10193) +- We changed the order of the lists in the "Citation relations" tab. `Cites` are now on the left and `Cited by` on the right [#10572](https://github.com/JabRef/jabref/pull/10752) + +### Fixed + +- We fixed an issue where attempting to cancel the importing/generation of an entry from id is ignored. [#10508](https://github.com/JabRef/jabref/issues/10508) +- We fixed an issue where the preview panel showing the wrong entry (an entry that is not selected in the entry table). [#9172](https://github.com/JabRef/jabref/issues/9172) +- The last page of a PDF is now indexed by the full text search. [#10193](https://github.com/JabRef/jabref/issues/10193) +- We fixed an issue where the duplicate check did not take umlauts or other LaTeX-encoded characters into account. [#10744](https://github.com/JabRef/jabref/pull/10744) +- We fixed the colors of the icon on hover for unset special fields. [#10431](https://github.com/JabRef/jabref/issues/10431) + +### Removed + +## [5.12] – 2023-12-24 + +### Added + - We added a scite.ai tab in the entry editor that retrieves 'Smart Citation' tallies for citations that have a DOI. [koppor#375](https://github.com/koppor/jabref/issues/375) - We added a dropdown menu to let users change the reference library during AUX file import. [#10472](https://github.com/JabRef/jabref/issues/10472) - We added a button to let users reset the cite command to the default value. [#10569](https://github.com/JabRef/jabref/issues/10569) - We added the option to use System Preference for Light/Dark Theme [#8729](https://github.com/JabRef/jabref/issues/8729). - We added [scholar.archive.org](https://scholar.archive.org/) as a new fetcher. [#10498](https://github.com/JabRef/jabref/issues/10498) +- We integrated predatory journal checking as part of the Integrity Checker based on the [check-bib-for-predatory](https://github.com/CfKu/check-bib-for-predatory). [koppor#348](https://github.com/koppor/jabref/issues/348) +- We added a 'More options' section in the main table right click menu opening the preferences dialog. [#9432](https://github.com/JabRef/jabref/issues/9432) +- When creating a new group, it inherits the icon of the parent group. [#10521](https://github.com/JabRef/jabref/pull/10521) ### Changed @@ -25,21 +52,23 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv - We replaced "SearchAll" in Web Search by "Search Selected". [#10556](https://github.com/JabRef/jabref/issues/10556) - Short DOI formatter now checks, if the value is already formatted. If so, it returns the value instead of calling the ShortDOIService again. [#10589](https://github.com/JabRef/jabref/issues/10589) - We upgraded to JavaFX 21.0.1. As a consequence JabRef requires now macOS 11 or later and GTK 3.8 or later on Linux [10627](https://github.com/JabRef/jabref/pull/10627). +- A user-specific comment fields is not enabled by default, but can be enabled using the "Add" button. [#10424](https://github.com/JabRef/jabref/issues/10424) +- We upgraded to Lucene 9.9 for the fulltext search. The search index will be rebuild. [#10686](https://github.com/JabRef/jabref/pull/10686) +- When using "Copy..." -> "Copy citation key", the delimiter configured at "Push applications" is respected. [#10707](https://github.com/JabRef/jabref/pull/10707) ### Fixed - We fixed an issue where the added protected term has unwanted leading and trailing whitespaces, where the formatted text has unwanted empty brackets and where the word at the cursor in the textbox can be added to the list. [#10415](https://github.com/JabRef/jabref/issues/10415) - We fixed an issue where in the merge dialog the file field of entries was not correctly merged when the first and second entry both contained values inside the file field. [#10572](https://github.com/JabRef/jabref/issues/10572) -- We fixed some small inconsistencies in the user interface. [#10507](https://github.com/JabRef/jabref/issues/10507) [#10458](https://github.com/JabRef/jabref/issues/10458) +- We fixed some small inconsistencies in the user interface. [#10507](https://github.com/JabRef/jabref/issues/10507) [#10458](https://github.com/JabRef/jabref/issues/10458) [#10660](https://github.com/JabRef/jabref/issues/10660) - We fixed the issue where the Hayagriva YAML exporter would not include a parent field for the publisher/series. [#10596](https://github.com/JabRef/jabref/issues/10596) +- We fixed issues in the external file type dialog w.r.t. duplicate entries in the case of a language switch. [#10271](https://github.com/JabRef/jabref/issues/10271) +- We fixed an issue where the right-click action "Copy cite..." did not respect the configured citation command under "External Programs" -> "[Push Applications](https://docs.jabref.org/cite/pushtoapplications)" [#10615](https://github.com/JabRef/jabref/issues/10615) ### Removed - We removed duplicate filtering and sorting operations in the MainTable when editing BibEntries. [#10619](https://github.com/JabRef/jabref/pull/10619) - - - ## [5.11] – 2023-10-22 ### Added @@ -1194,7 +1223,8 @@ The changelog of JabRef 4.x is available at the [v4.3.1 tag](https://github.com/ The changelog of JabRef 3.x is available at the [v3.8.2 tag](https://github.com/JabRef/jabref/blob/v3.8.2/CHANGELOG.md). The changelog of JabRef 2.11 and all previous versions is available as [text file in the v2.11.1 tag](https://github.com/JabRef/jabref/blob/v2.11.1/CHANGELOG). -[Unreleased]: https://github.com/JabRef/jabref/compare/v5.11...HEAD +[Unreleased]: https://github.com/JabRef/jabref/compare/v5.12...HEAD +[5.12]: https://github.com/JabRef/jabref/compare/v5.11...v5.12 [5.11]: https://github.com/JabRef/jabref/compare/v5.10...v5.11 [5.10]: https://github.com/JabRef/jabref/compare/v5.9...v5.10 [5.9]: https://github.com/JabRef/jabref/compare/v5.8...v5.9 diff --git a/CITATION.cff b/CITATION.cff new file mode 100644 index 00000000000..570a4e57b0c --- /dev/null +++ b/CITATION.cff @@ -0,0 +1,58 @@ +# This CITATION.cff file was generated with cffinit. +# Visit https://bit.ly/cffinit to generate yours today! + +cff-version: 1.2.0 +title: JabRef +message: >- + If you use this software, please cite it using the + metadata from this file. +type: software +authors: + - given-names: Oliver + family-names: Kopp + orcid: 'https://orcid.org/0000-0001-6962-4290' + - given-names: Tobias + family-names: Diez + orcid: 'https://orcid.org/0000-0002-1407-7696' + - given-names: Christoph + family-names: Schwentker + - given-names: Carl Christian + family-names: Snethlage + - given-names: Jonatan + family-names: Asketorp + - given-names: Benedikt + family-names: Tutzer + - given-names: Thilo + family-names: Ertel + - given-names: Houssem + family-names: Nasri +repository-code: 'https://github.com/jabref/jabref/' +url: 'https://www.jabref.org' +abstract: >- + JabRef is an open-source, cross-platform citation and + reference management tool. +keywords: + - reference manager + - bibtex + - biblatex +license: MIT +preferred-citation: + type: article + authors: + - family-names: "Kopp" + given-names: "Oliver" + orcid: "https://orcid.org/0000-0001-6962-4290" + - family-names: "Snethlage" + given-names: "Carl Christian" + - family-names: "Schwentker" + given-names: "Christoph" + doi: "10.47397/tb/44-3/tb138kopp-jabref" + journal: "TUGboat" + month: 11 + start: 441 + end: 447 + title: "JabRef: BibTeX-based literature management software" + issue: 138 + volume: 44 + number: 3 + year: 2023 diff --git a/build.gradle b/build.gradle index 800fa9fe067..256b3d85bee 100644 --- a/build.gradle +++ b/build.gradle @@ -27,7 +27,7 @@ plugins { id 'idea' - id 'org.openrewrite.rewrite' version '6.5.4' + id 'org.openrewrite.rewrite' version '6.6.3' } // Enable following for debugging @@ -101,7 +101,7 @@ dependencyLocking { } javafx { - version = "21.0.1" + version = "20" modules = [ 'javafx.controls', 'javafx.fxml', 'javafx.web', 'javafx.swing' ] } @@ -113,27 +113,29 @@ dependencies { // Include all jar-files in the 'lib' folder as dependencies implementation fileTree(dir: 'lib', includes: ['*.jar']) - implementation 'org.apache.pdfbox:pdfbox:3.0.0' - implementation 'org.apache.pdfbox:fontbox:3.0.0' - implementation 'org.apache.pdfbox:xmpbox:3.0.0' + implementation 'org.apache.pdfbox:pdfbox:3.0.1' + implementation 'org.apache.pdfbox:fontbox:3.0.1' + implementation ('org.apache.pdfbox:xmpbox:3.0.1') { + exclude group: 'org.junit.jupiter' + } - implementation 'org.apache.lucene:lucene-core:9.8.0' - implementation 'org.apache.lucene:lucene-queryparser:9.8.0' - implementation 'org.apache.lucene:lucene-queries:9.8.0' - implementation 'org.apache.lucene:lucene-analysis-common:9.8.0' - implementation 'org.apache.lucene:lucene-highlighter:9.8.0' + implementation 'org.apache.lucene:lucene-core:9.9.1' + implementation 'org.apache.lucene:lucene-queryparser:9.9.1' + implementation 'org.apache.lucene:lucene-queries:9.9.0' + implementation 'org.apache.lucene:lucene-analysis-common:9.9.1' + implementation 'org.apache.lucene:lucene-highlighter:9.9.1' implementation group: 'org.apache.commons', name: 'commons-csv', version: '1.10.0' - implementation group: 'org.apache.commons', name: 'commons-lang3', version: '3.13.0' + implementation group: 'org.apache.commons', name: 'commons-lang3', version: '3.14.0' implementation 'com.h2database:h2-mvstore:2.2.224' // required for reading write-protected PDFs - see https://github.com/JabRef/jabref/pull/942#issuecomment-209252635 - implementation 'org.bouncycastle:bcprov-jdk18on:1.76' + implementation 'org.bouncycastle:bcprov-jdk18on:1.77' implementation 'commons-cli:commons-cli:1.6.0' - implementation 'org.libreoffice:unoloader:7.6.1' - implementation 'org.libreoffice:libreoffice:7.6.1' + implementation 'org.libreoffice:unoloader:7.6.4' + implementation 'org.libreoffice:libreoffice:7.6.4' implementation 'io.github.java-diff-utils:java-diff-utils:4.12' implementation 'info.debatty:java-string-similarity:2.0.0' @@ -142,16 +144,16 @@ dependencies { antlr4 'org.antlr:antlr4:4.13.1' implementation 'org.antlr:antlr4-runtime:4.13.1' - implementation group: 'org.eclipse.jgit', name: 'org.eclipse.jgit', version: '6.7.0.202309050840-r' + implementation group: 'org.eclipse.jgit', name: 'org.eclipse.jgit', version: '6.8.0.202311291450-r' - implementation group: 'com.fasterxml.jackson.dataformat', name: 'jackson-dataformat-yaml', version: '2.16.0' - implementation group: 'com.fasterxml.jackson.datatype', name: 'jackson-datatype-jsr310', version: '2.15.3' + implementation group: 'com.fasterxml.jackson.dataformat', name: 'jackson-dataformat-yaml', version: '2.16.1' + implementation group: 'com.fasterxml.jackson.datatype', name: 'jackson-datatype-jsr310', version: '2.16.1' implementation 'com.fasterxml:aalto-xml:1.3.2' implementation group: 'org.mariadb.jdbc', name: 'mariadb-java-client', version: '2.7.9' - implementation 'org.postgresql:postgresql:42.6.0' + implementation 'org.postgresql:postgresql:42.7.1' implementation ('com.oracle.ojdbc:ojdbc10:19.3.0.0') { // causing module issues @@ -182,7 +184,7 @@ dependencies { implementation 'org.controlsfx:controlsfx:11.2.0' implementation 'com.github.Dansoftowner:jSystemThemeDetector:3.8' - implementation 'org.jsoup:jsoup:1.16.2' + implementation 'org.jsoup:jsoup:1.17.2' implementation 'com.konghq:unirest-java:3.14.5' implementation 'org.slf4j:slf4j-api:2.0.9' @@ -221,29 +223,32 @@ dependencies { // API implementation 'jakarta.ws.rs:jakarta.ws.rs-api:3.1.0' // Implementation of the API - implementation 'org.glassfish.jersey.core:jersey-server:3.1.3' + implementation 'org.glassfish.jersey.core:jersey-server:3.1.5' // injection framework - implementation 'org.glassfish.jersey.inject:jersey-hk2:3.1.3' + implementation 'org.glassfish.jersey.inject:jersey-hk2:3.1.5' implementation 'org.glassfish.hk2:hk2-api:3.0.5' // testImplementation 'org.glassfish.hk2:hk2-testing:3.0.4' // implementation 'org.glassfish.hk2:hk2-testing-jersey:3.0.4' // testImplementation 'org.glassfish.hk2:hk2-junitrunner:3.0.4' // HTTP server // implementation 'org.glassfish.jersey.containers:jersey-container-netty-http:3.1.1' - implementation 'org.glassfish.jersey.containers:jersey-container-grizzly2-http:3.1.3' - testImplementation 'org.glassfish.jersey.test-framework.providers:jersey-test-framework-provider-grizzly2:3.1.3' + implementation 'org.glassfish.jersey.containers:jersey-container-grizzly2-http:3.1.5' + testImplementation 'org.glassfish.jersey.test-framework.providers:jersey-test-framework-provider-grizzly2:3.1.5' // Allow objects "magically" to be mapped to JSON using GSON // implementation 'org.glassfish.jersey.media:jersey-media-json-gson:3.1.1' - testImplementation 'io.github.classgraph:classgraph:4.8.164' + // Because of GraalVM quirks, we need to ship that. See https://github.com/jspecify/jspecify/issues/389#issuecomment-1661130973 for details + implementation 'org.jspecify:jspecify:0.3.0' + + testImplementation 'io.github.classgraph:classgraph:4.8.165' testImplementation 'org.junit.jupiter:junit-jupiter:5.10.1' testImplementation 'org.junit.platform:junit-platform-launcher:1.10.1' - testImplementation 'org.mockito:mockito-core:5.7.0' + testImplementation 'org.mockito:mockito-core:5.8.0' testImplementation 'org.xmlunit:xmlunit-core:2.9.1' testImplementation 'org.xmlunit:xmlunit-matchers:2.9.1' - testRuntimeOnly 'com.tngtech.archunit:archunit-junit5-engine:1.2.0' - testImplementation 'com.tngtech.archunit:archunit-junit5-api:1.2.0' + testRuntimeOnly 'com.tngtech.archunit:archunit-junit5-engine:1.2.1' + testImplementation 'com.tngtech.archunit:archunit-junit5-api:1.2.1' testImplementation "org.testfx:testfx-core:4.0.16-alpha" testImplementation "org.testfx:testfx-junit5:4.0.16-alpha" testImplementation "org.hamcrest:hamcrest-library:2.2" @@ -253,7 +258,7 @@ dependencies { xjc group: 'org.glassfish.jaxb', name: 'jaxb-xjc', version: '3.0.2' xjc group: 'org.glassfish.jaxb', name: 'jaxb-runtime', version: '3.0.2' - rewrite(platform("org.openrewrite.recipe:rewrite-recipe-bom:2.5.0")) + rewrite(platform("org.openrewrite.recipe:rewrite-recipe-bom:2.5.4")) rewrite("org.openrewrite.recipe:rewrite-static-analysis") rewrite("org.openrewrite.recipe:rewrite-logging-frameworks") rewrite("org.openrewrite.recipe:rewrite-testing-frameworks") @@ -277,7 +282,8 @@ processResources { "astrophysicsDataSystemAPIKey": System.getenv('AstrophysicsDataSystemAPIKey') ? System.getenv('AstrophysicsDataSystemAPIKey') : '', "ieeeAPIKey": System.getenv('IEEEAPIKey') ? System.getenv('IEEEAPIKey') : '', "scienceDirectApiKey": System.getenv('SCIENCEDIRECTAPIKEY') ? System.getenv('SCIENCEDIRECTAPIKEY') : '', - "biodiversityHeritageApiKey": System.getenv('BiodiversityHeritageApiKey') ? System.getenv('BiodiversityHeritageApiKey') : '' + "biodiversityHeritageApiKey": System.getenv('BiodiversityHeritageApiKey') ? System.getenv('BiodiversityHeritageApiKey') : '', + "semanticScholarApiKey": System.getenv('SemanticScholarApiKey') ? System.getenv("SemanticScholarApiKey") : '' ) filteringCharset = 'UTF-8' } @@ -326,8 +332,19 @@ tasks.register("generateJournalListMV", JavaExec) { !file("build/resources/main/journals/journal-list.mv").exists() } } -jar.dependsOn "generateJournalListMV" -compileTestJava.dependsOn "generateJournalListMV" + +tasks.register("generatePredatoryJournalListMV", JavaExec) { + group = "JabRef" + description = "Load predatory journal information from online sources to a H2 MVStore" + classpath = sourceSets.main.runtimeClasspath + mainClass = "org.jabref.cli.PredatoryJournalsMvGenerator" + onlyIf { + !file("build/resources/main/journals/predatory-journals.mv").exists() + } +} + +jar.dependsOn("generateJournalListMV", "generatePredatoryJournalListMV") +compileTestJava.dependsOn("generateJournalListMV","generatePredatoryJournalListMV") tasks.register('generateCitaviSource', XjcTask) { group = 'JabRef' @@ -591,6 +608,7 @@ jlink { requires 'com.google.gson' requires 'org.slf4j' requires 'jakarta.xml.bind' + requires 'org.apache.commons.lang3' uses 'org.mariadb.jdbc.credential.CredentialPlugin' uses 'org.mariadb.jdbc.authentication.AuthenticationPlugin' uses 'org.mariadb.jdbc.tls.TlsSocketPlugin' diff --git a/docs/code-howtos/fetchers.md b/docs/code-howtos/fetchers.md index 3c540014bf7..a0be55e912d 100644 --- a/docs/code-howtos/fetchers.md +++ b/docs/code-howtos/fetchers.md @@ -5,15 +5,16 @@ parent: Code Howtos 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. -| Service | Key Source | Environment Variable | Rate Limit | -|:-----------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------|--------------------------------|----------------------------------| -| [IEEEXplore](https://docs.jabref.org/collect/import-using-online-bibliographic-database#ieeexplore) | [IEEE Xplore API portal](https://developer.ieee.org) | `IEEEAPIKey` | 200 calls/day | -| [MathSciNet](http://www.ams.org/mathscinet) | (none) | (none) | Depending on the current network | -| [SAO/NASA Astrophysics Data System](https://docs.jabref.org/collect/import-using-online-bibliographic-database#sao-nasa-astrophysics-data-system) | [ADS UI](https://ui.adsabs.harvard.edu/user/settings/token) | `AstrophysicsDataSystemAPIKey` | 5000 calls/day | -| [ScienceDirect](https://www.sciencedirect.com) | | `ScienceDirectApiKey` | | -| [Springer Nature](https://docs.jabref.org/collect/import-using-online-bibliographic-database#springer) | [Springer Nature API Portal](https://dev.springernature.com) | `SpringerNatureAPIKey` | 5000 calls/day | -| [Zentralblatt Math](https://www.zbmath.org) | (none) | (none) | Depending on the current network | -| [Biodiversity Heritage Library](https://www.biodiversitylibrary.org/) | [Biodiversitylibrary](https://about.biodiversitylibrary.org/tools-and-services/developer-and-data-tools/#APIs) | `BiodiversityHeritageApiKey` | - | +| Service | Key Source | Environment Variable | Rate Limit | +|:--------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------|--------------------------------|----------------------------------| +| [IEEEXplore](https://docs.jabref.org/collect/import-using-online-bibliographic-database#ieeexplore) | [IEEE Xplore API portal](https://developer.ieee.org) | `IEEEAPIKey` | 200 calls/day | +| [MathSciNet](http://www.ams.org/mathscinet) | (none) | (none) | Depending on the current network | +| [SAO/NASA Astrophysics Data System](https://docs.jabref.org/collect/import-using-online-bibliographic-database#sao-nasa-astrophysics-data-system) | [ADS UI](https://ui.adsabs.harvard.edu/user/settings/token) | `AstrophysicsDataSystemAPIKey` | 5000 calls/day | +| [ScienceDirect](https://www.sciencedirect.com) | | `ScienceDirectApiKey` | | +| [SemanticScholar](https://www.semanticscholar.org/) | | `SemanticScholarApiKey` | | +| [Springer Nature](https://docs.jabref.org/collect/import-using-online-bibliographic-database#springer) | [Springer Nature API Portal](https://dev.springernature.com) | `SpringerNatureAPIKey` | 5000 calls/day | +| [Zentralblatt Math](https://www.zbmath.org) | (none) | (none) | Depending on the current network | +| [Biodiversity Heritage Library](https://www.biodiversitylibrary.org/) | [Biodiversitylibrary](https://about.biodiversitylibrary.org/tools-and-services/developer-and-data-tools/#APIs) | `BiodiversityHeritageApiKey` | - | "Depending on the current network" means that it depends on whether your request is routed through a network having paid access. For instance, some universities have subscriptions to MathSciNet. @@ -72,7 +73,7 @@ springerNatureAPIKey=${springerNatureAPIKey} In `build.gradle`, these variables are filled: ```groovy -"springerNatureAPIKey": System.getenv('SpringerNatureAPIKey') +"springerNatureAPIKey" : System.getenv('SpringerNatureAPIKey') ``` The `BuildInfo` class reads from that file. diff --git a/docs/code-howtos/tools.md b/docs/code-howtos/tools.md index f2b344da05a..a21bf399add 100644 --- a/docs/code-howtos/tools.md +++ b/docs/code-howtos/tools.md @@ -70,10 +70,3 @@ Then, each weak do `choco upgrade all` to ensure all tooling is kept updated. ### Tools for working with XMP * Validate XMP: [https://www.pdflib.com/pdf-knowledge-base/xmp/free-xmp-validator/](https://www.pdflib.com/pdf-knowledge-base/xmp/free-xmp-validator/) - -### Some useful keyboard shortcuts - -* [AutoHotkey](http://autohotkey.com) - Preparation for the next step -* [https://github.com/koppor/autohotkey-scripts](https://github.com/koppor/autohotkey-scripts) - Aim: Have Win+C opening ConEmu - 1. Clone the repository locally. - 2. Then link `ConEmu.ahk` and `WindowsExplorer.ahk` at the startup menu (Link creation works with drag'n'drop using the right mouse key and then choosing "Create link" when dropping). Hint: Startup is in the folder `Startup` (German: `Autostart`) at `%APPDATA%\Microsoft\Windows\Start Menu\Programs\` - accessible via Win+R: `shell:startup` diff --git a/docs/getting-into-the-code/guidelines-for-setting-up-a-local-workspace/intellij-89-run-with-intellij.md b/docs/getting-into-the-code/guidelines-for-setting-up-a-local-workspace/intellij-89-run-with-intellij.md index b0897e95a65..67240227082 100644 --- a/docs/getting-into-the-code/guidelines-for-setting-up-a-local-workspace/intellij-89-run-with-intellij.md +++ b/docs/getting-into-the-code/guidelines-for-setting-up-a-local-workspace/intellij-89-run-with-intellij.md @@ -11,17 +11,19 @@ It is also possible to use IntelliJ's internal build and run system to launch Ja Due to [IDEA-119280](https://youtrack.jetbrains.com/issue/IDEA-119280), it is a bit more work. 1. Navigate to **File > Settings... > Build, Execution, Deployment > Build Tools > Gradle**. -2. Change the setting "Build an run using:" to "IntelliJ IDEA". +2. Change the setting "Build and run using:" to "IntelliJ IDEA". 3. Navigate to **File > Settings... > Build, Execution, Deployment > Compiler > Java Compiler**. 4. Uncheck `--Use 'release' option for cross-compilation`. 5. **Build > Build Project** 6. Open the project view (Alt+1 , on mac cmd>+1) 7. Copy all build resources to the folder of the build classes - 1. Navigate to the folder `out/production/resources` - 2. Select all folders below (`bst`, `csl-locales`, ...) - 3. Press Ctrl+C to mark them for copying - 4. Select the folder `classes` - 5. Press Ctrl+V to start the copy process + 1. Navigate to the folder `build/resoruces/main` + 1. Right click -> "Open In" -> "Explorer (Finder on macOS)" + 1. Navigate into directory "main" + 1. Select the folder `out/production/classes` + 1. Right click -> "Open In" -> "Explorer (Finder on macOS)" + 1. Navigate into directory "classes" + 1. Now you have two Explorer windows opened. Copy all files and directories from the first one to the second one. 8. Locate the class `Launcher` (e.g., by ctrl+N and then typing `Launcher`). Press Enter to jump to that class.
IntelliJ search for class “Launcher” diff --git a/external-libraries.md b/external-libraries.md index 2f06c5cdcf1..d5d4867024b 100644 --- a/external-libraries.md +++ b/external-libraries.md @@ -84,6 +84,13 @@ URL: https://github.com/FasterXML/jackson License: Apache-2.0 ``` +```yaml +Id: com.github.Dansoftowner:jSystemThemeDetector +Project: jSystemThemeDetector +URL: https://github.com/Dansoftowner/jSystemThemeDetector +License: Apache-2.0 +``` + ```yaml Id: com.github.hypfvieh.dbus-java Project: dbus-java @@ -119,6 +126,13 @@ URL: https://github.com/tomtung/latex2unicode License: Apache-2.0 ``` +```yaml +Id: com.github.weisj:jsvg +Project: JSVG - A Java SVG implementation +URL: https://github.com/weisJ/jsvg +License: MIT +``` + ```yaml Id: com.google.code.gson:gson Project: Google Guava @@ -514,6 +528,13 @@ URL: https://github.com/jhy/jsoup/ License: MIT ``` +```yaml +Id: org.jspecify:jspecify +Project: jspecify +URL: https://jspecify.dev/ +License: Apache-2.0 +``` + ```yaml Id: org.kordamp.ikonli Project: Ikonli @@ -647,22 +668,26 @@ License: BSD-3-Clause 3. (on WSL) `sed 's/[^a-z]*//' < build/dependencies.txt | sed "s/\(.*\) .*/\1/" | grep -v "\->" | sort | uniq > build/dependencies-for-external-libraries.txt` ```text + at.favre.lib:hkdf:1.1.0 -com.dlsc.gemsfx:gemsfx:1.82.0 +com.dlsc.gemsfx:gemsfx:1.90.0 com.dlsc.pickerfx:pickerfx:1.2.0 com.dlsc.unitfx:unitfx:1.0.10 -com.fasterxml.jackson.core:jackson-annotations:2.15.3 -com.fasterxml.jackson.core:jackson-core:2.15.3 -com.fasterxml.jackson.core:jackson-databind:2.15.3 -com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.15.3 +com.fasterxml.jackson.core:jackson-annotations:2.16.0 +com.fasterxml.jackson.core:jackson-core:2.16.0 +com.fasterxml.jackson.core:jackson-databind:2.16.0 +com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.16.0 com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.15.3 -com.fasterxml.jackson:jackson-bom:2.15.3 -com.fasterxml:aalto-xml:1.3.1 +com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.16.0 +com.fasterxml.jackson:jackson-bom:2.16.0 +com.fasterxml:aalto-xml:1.3.2 +com.github.Dansoftowner:jSystemThemeDetector:3.8 com.github.hypfvieh:dbus-java-core:4.2.1 com.github.hypfvieh:dbus-java-transport-native-unixsocket:4.2.1 com.github.javakeyring:java-keyring:1.0.4 com.github.sialcasa.mvvmFX:mvvmfx-validation:f195849ca9 com.github.tomtung:latex2unicode_2.13:0.3.2 +com.github.weisj:jsvg:1.2.0 com.google.code.gson:gson:2.10 com.google.errorprone:error_prone_annotations:2.21.1 com.google.guava:failureaccess:1.0.1 @@ -695,7 +720,7 @@ com.vladsch.flexmark:flexmark-util-sequence:0.64.8 com.vladsch.flexmark:flexmark-util-visitor:0.64.8 com.vladsch.flexmark:flexmark:0.64.8 commons-beanutils:commons-beanutils:1.9.4 -commons-cli:commons-cli:1.5.0 +commons-cli:commons-cli:1.6.0 commons-codec:commons-codec:1.16.0 commons-collections:commons-collections:3.2.2 commons-digester:commons-digester:2.1 @@ -726,71 +751,74 @@ net.jodah:typetools:0.6.1 one.jpro.jproutils:tree-showing:0.2.2 org.antlr:antlr4-runtime:4.13.1 org.apache.commons:commons-csv:1.10.0 -org.apache.commons:commons-lang3:3.13.0 +org.apache.commons:commons-lang3:3.14.0 org.apache.httpcomponents:httpasyncclient:4.1.5 org.apache.httpcomponents:httpclient:4.5.13 org.apache.httpcomponents:httpcore-nio:4.4.13 org.apache.httpcomponents:httpcore:4.4.13 org.apache.httpcomponents:httpmime:4.5.13 -org.apache.logging.log4j:log4j-api:2.20.0 -org.apache.logging.log4j:log4j-to-slf4j:2.20.0 -org.apache.lucene:lucene-analysis-common:9.8.0 -org.apache.lucene:lucene-core:9.8.0 -org.apache.lucene:lucene-highlighter:9.8.0 -org.apache.lucene:lucene-queries:9.8.0 -org.apache.lucene:lucene-queryparser:9.8.0 -org.apache.lucene:lucene-sandbox:9.8.0 +org.apache.logging.log4j:log4j-api:2.21.1 +org.apache.logging.log4j:log4j-to-slf4j:2.21.1 +org.apache.lucene:lucene-analysis-common:9.9.1 +org.apache.lucene:lucene-core:9.9.0 +org.apache.lucene:lucene-core:9.9.1 +org.apache.lucene:lucene-highlighter:9.9.0 +org.apache.lucene:lucene-queries:9.9.0 +org.apache.lucene:lucene-queries:9.9.1 +org.apache.lucene:lucene-queryparser:9.9.1 +org.apache.lucene:lucene-sandbox:9.9.1 org.apache.pdfbox:fontbox:3.0.0 org.apache.pdfbox:pdfbox-io:3.0.0 org.apache.pdfbox:pdfbox:3.0.0 -org.apache.pdfbox:xmpbox:3.0.0 -org.bouncycastle:bcprov-jdk18on:1.76 +org.apache.pdfbox:xmpbox:3.0.1 +org.bouncycastle:bcprov-jdk18on:1.77 org.checkerframework:checker-qual:3.37.0 org.codehaus.woodstox:stax2-api:4.2 -org.controlsfx:controlsfx:11.1.2 +org.controlsfx:controlsfx:11.2.0 org.eclipse.jgit:org.eclipse.jgit:6.7.0.202309050840-r -org.fxmisc.flowless:flowless:0.7.1 -org.fxmisc.richtext:richtextfx:0.11.1 +org.fxmisc.flowless:flowless:0.7.2 +org.fxmisc.richtext:richtextfx:0.11.2 org.fxmisc.undo:undofx:2.1.1 org.fxmisc.wellbehaved:wellbehavedfx:0.3.3 -org.glassfish.grizzly:grizzly-framework:4.0.0 -org.glassfish.grizzly:grizzly-http-server:4.0.0 -org.glassfish.grizzly:grizzly-http:4.0.0 -org.glassfish.hk2.external:aopalliance-repackaged:3.0.4 -org.glassfish.hk2:hk2-api:3.0.4 -org.glassfish.hk2:hk2-locator:3.0.4 -org.glassfish.hk2:hk2-utils:3.0.4 +org.glassfish.grizzly:grizzly-framework:4.0.1 +org.glassfish.grizzly:grizzly-http-server:4.0.1 +org.glassfish.grizzly:grizzly-http:4.0.1 +org.glassfish.hk2.external:aopalliance-repackaged:3.0.5 +org.glassfish.hk2:hk2-api:3.0.5 +org.glassfish.hk2:hk2-locator:3.0.5 +org.glassfish.hk2:hk2-utils:3.0.5 org.glassfish.hk2:osgi-resource-locator:1.0.3 org.glassfish.jaxb:jaxb-core:4.0.3 org.glassfish.jaxb:jaxb-runtime:4.0.3 org.glassfish.jaxb:txw2:4.0.3 -org.glassfish.jersey.containers:jersey-container-grizzly2-http:3.1.3 -org.glassfish.jersey.core:jersey-client:3.1.3 -org.glassfish.jersey.core:jersey-common:3.1.3 -org.glassfish.jersey.core:jersey-server:3.1.3 -org.glassfish.jersey.inject:jersey-hk2:3.1.3 +org.glassfish.jersey.containers:jersey-container-grizzly2-http:3.1.4 +org.glassfish.jersey.core:jersey-client:3.1.4 +org.glassfish.jersey.core:jersey-common:3.1.5 +org.glassfish.jersey.core:jersey-server:3.1.4 +org.glassfish.jersey.inject:jersey-hk2:3.1.5 org.jabref:afterburner.fx:2.0.0 org.javassist:javassist:3.29.2-GA org.jbibtex:jbibtex:1.0.20 org.jetbrains:annotations:24.0.1 org.jooq:jool:0.9.15 -org.jsoup:jsoup:1.16.1 +org.jsoup:jsoup:1.16.2 +org.jspecify:jspecify:0.3.0 org.kordamp.ikonli:ikonli-bootstrapicons-pack:12.3.1 org.kordamp.ikonli:ikonli-core:12.3.1 org.kordamp.ikonli:ikonli-javafx:12.3.1 org.kordamp.ikonli:ikonli-material-pack:12.3.1 org.kordamp.ikonli:ikonli-materialdesign-pack:12.3.1 org.kordamp.ikonli:ikonli-materialdesign2-pack:12.3.1 -org.libreoffice:libreoffice:7.6.1 -org.libreoffice:unoloader:7.6.1 +org.libreoffice:libreoffice:7.6.4 +org.libreoffice:unoloader:7.6.4 org.mariadb.jdbc:mariadb-java-client:2.7.9 -org.openjfx:javafx-base:20.0.2 -org.openjfx:javafx-controls:20.0.2 -org.openjfx:javafx-fxml:20.0.2 -org.openjfx:javafx-graphics:20.0.2 -org.openjfx:javafx-media:20.0.2 -org.openjfx:javafx-swing:20.0.2 -org.openjfx:javafx-web:20.0.2 +org.openjfx:javafx-base:21.0.1 +org.openjfx:javafx-controls:21.0.1 +org.openjfx:javafx-fxml:21.0.1 +org.openjfx:javafx-graphics:21.0.1 +org.openjfx:javafx-media:21.0.1 +org.openjfx:javafx-swing:21.0.1 +org.openjfx:javafx-web:21.0.1 org.postgresql:postgresql:42.6.0 org.reactfx:reactfx:2.0-M5 org.scala-lang:scala-library:2.13.8 @@ -799,7 +827,7 @@ org.slf4j:slf4j-api:2.0.9 org.tinylog:slf4j-tinylog:2.6.2 org.tinylog:tinylog-api:2.6.2 org.tinylog:tinylog-impl:2.6.2 -org.yaml:snakeyaml:2.1 +org.yaml:snakeyaml:2.2 pt.davidafsilva.apple:jkeychain:1.1.0 tech.units:indriya:2.1.2 tech.uom.lib:uom-lib-common:2.1 diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 7f93135c49b..d64cd491770 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 46671acb6e1..db8c3baafe3 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=3e1af3ae886920c3ac87f7a91f816c0c7c436f276a6eefdb3da152100fef72ae -distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip +distributionSha256Sum=9d926787066a081739e8200858338b4a69e837c3a821a33aca9db09dd4a41026 +distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/rewrite.yml b/rewrite.yml index 9d0c5502679..a5be80970e3 100644 --- a/rewrite.yml +++ b/rewrite.yml @@ -195,7 +195,5 @@ recipeList: - org.openrewrite.staticanalysis.UseSystemLineSeparator - org.openrewrite.staticanalysis.WhileInsteadOfFor # - org.openrewrite.staticanalysis.WriteOctalValuesAsDecimal - -# - org.openrewrite.java.testing.junit5.JUnit5BestPractices + - org.openrewrite.java.testing.junit5.CleanupAssertions - org.openrewrite.java.testing.junit5.JUnit5BestPractices - diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index 98e805bf7c0..3f094b255fe 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -58,7 +58,7 @@ environment: parts: jabref: plugin: dump - source: https://builds.jabref.org/main/JabRef-5.12-portable_linux.tar.gz + source: https://builds.jabref.org/main/JabRef-5.13-portable_linux.tar.gz stage-packages: - x11-utils override-build: | diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 70eb10b2914..48267ada078 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -61,7 +61,7 @@ // http server and client exchange requires java.net.http; requires jakarta.ws.rs; - requires grizzly.framework; + requires org.glassfish.grizzly; // data mapping requires jakarta.xml.bind; @@ -121,7 +121,7 @@ // fulltext search requires org.apache.lucene.core; // In case the version is updated, please also adapt SearchFieldConstants#VERSION to the newly used version - uses org.apache.lucene.codecs.lucene95.Lucene95Codec; + uses org.apache.lucene.codecs.lucene99.Lucene99Codec; requires org.apache.lucene.queryparser; uses org.apache.lucene.queryparser.classic.MultiFieldQueryParser; @@ -136,6 +136,8 @@ uses org.eclipse.jgit.transport.SshSessionFactory; uses org.eclipse.jgit.lib.GpgSigner; + requires transitive org.jspecify; + // other libraries requires org.antlr.antlr4.runtime; requires org.libreoffice.uno; diff --git a/src/main/java/org/jabref/cli/JournalListMvGenerator.java b/src/main/java/org/jabref/cli/JournalListMvGenerator.java index 9a4fd3d67c6..dad187dc81a 100644 --- a/src/main/java/org/jabref/cli/JournalListMvGenerator.java +++ b/src/main/java/org/jabref/cli/JournalListMvGenerator.java @@ -37,15 +37,15 @@ public static void main(String[] args) throws IOException { // we currently do not have good support for BibTeX strings "journal_abbreviations_ieee_strings.csv" - ); + ); Files.createDirectories(journalListMvFile.getParent()); try (DirectoryStream stream = Files.newDirectoryStream(abbreviationsDirectory, "*.csv"); MVStore store = new MVStore.Builder(). - fileName(journalListMvFile.toString()). - compressHigh(). - open()) { + fileName(journalListMvFile.toString()). + compressHigh(). + open()) { MVMap fullToAbbreviation = store.openMap("FullToAbbreviation"); stream.forEach(Unchecked.consumer(path -> { String fileName = path.getFileName().toString(); diff --git a/src/main/java/org/jabref/cli/Launcher.java b/src/main/java/org/jabref/cli/Launcher.java index a49ea92e865..2e8033267d6 100644 --- a/src/main/java/org/jabref/cli/Launcher.java +++ b/src/main/java/org/jabref/cli/Launcher.java @@ -12,6 +12,7 @@ import org.jabref.gui.Globals; import org.jabref.gui.MainApplication; import org.jabref.logic.journals.JournalAbbreviationLoader; +import org.jabref.logic.journals.predatory.PredatoryJournalListLoader; import org.jabref.logic.l10n.Localization; import org.jabref.logic.net.ProxyAuthenticator; import org.jabref.logic.net.ProxyPreferences; @@ -169,6 +170,8 @@ private static void initGlobals(PreferencesService preferences) { // Read list(s) of journal names and abbreviations Globals.journalAbbreviationRepository = JournalAbbreviationLoader .loadRepository(preferences.getJournalAbbreviationPreferences()); + Globals.predatoryJournalRepository = PredatoryJournalListLoader + .loadRepository(); Globals.entryTypesManager = preferences.getCustomEntryTypesRepository(); Globals.protectedTermsLoader = new ProtectedTermsLoader(preferences.getProtectedTermsPreferences()); diff --git a/src/main/java/org/jabref/cli/PredatoryJournalsMvGenerator.java b/src/main/java/org/jabref/cli/PredatoryJournalsMvGenerator.java new file mode 100644 index 00000000000..574e287abf8 --- /dev/null +++ b/src/main/java/org/jabref/cli/PredatoryJournalsMvGenerator.java @@ -0,0 +1,47 @@ +package org.jabref.cli; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; + +import org.jabref.logic.journals.predatory.PredatoryJournalInformation; +import org.jabref.logic.journals.predatory.PredatoryJournalListCrawler; + +import org.h2.mvstore.MVMap; +import org.h2.mvstore.MVStore; + +public class PredatoryJournalsMvGenerator { + public static void main(String[] args) throws IOException { + boolean verbose = (args.length == 1) && ("--verbose".equals(args[0])); + + Path predatoryJournalsMvFile = Path.of("build", "resources", "main", "journals", "predatory-journals.mv"); + Files.createDirectories(predatoryJournalsMvFile.getParent()); + + try (MVStore store = new MVStore.Builder() + .fileName(predatoryJournalsMvFile.toString()) + .compressHigh() + .backgroundExceptionHandler((t, e) -> { + System.err.println("Exception occurred in Thread " + t + "with exception " + e); + e.printStackTrace(); + }) + .open()) { + MVMap predatoryJournalsMap = store.openMap("PredatoryJournals"); + + PredatoryJournalListCrawler loader = new PredatoryJournalListCrawler(); + Set predatoryJournals = loader.loadFromOnlineSources(); + + var resultMap = predatoryJournals.stream().collect(Collectors.toMap(PredatoryJournalInformation::name, Function.identity(), + (predatoryJournalInformation, predatoryJournalInformation2) -> { + if (verbose) { + System.out.println("Double entry " + predatoryJournalInformation.name()); + } + return predatoryJournalInformation2; + })); + + predatoryJournalsMap.putAll(resultMap); + } + } +} diff --git a/src/main/java/org/jabref/gui/EntryTypeView.java b/src/main/java/org/jabref/gui/EntryTypeView.java index 238d80d7d17..3b10205a547 100644 --- a/src/main/java/org/jabref/gui/EntryTypeView.java +++ b/src/main/java/org/jabref/gui/EntryTypeView.java @@ -81,6 +81,7 @@ public EntryTypeView(LibraryTab libraryTab, DialogService dialogService, Prefere .setAsDialogPane(this); ControlHelper.setAction(generateButton, this.getDialogPane(), event -> viewModel.runFetcherWorker()); + setOnCloseRequest(e -> viewModel.cancelFetcherWorker()); setResultConverter(button -> { // The buttonType will always be "cancel", even if we pressed one of the entry type buttons diff --git a/src/main/java/org/jabref/gui/EntryTypeViewModel.java b/src/main/java/org/jabref/gui/EntryTypeViewModel.java index a197348997a..b63718edeaf 100644 --- a/src/main/java/org/jabref/gui/EntryTypeViewModel.java +++ b/src/main/java/org/jabref/gui/EntryTypeViewModel.java @@ -196,7 +196,7 @@ public void runFetcherWorker() { Localization.lang("Return to dialog")); if (addEntryFlag) { new NewEntryAction( - libraryTab.frame(), + () -> libraryTab, StandardEntryType.Article, dialogService, preferencesService, @@ -211,4 +211,8 @@ public void runFetcherWorker() { }); taskExecutor.execute(fetcherWorker); } + + public void cancelFetcherWorker() { + fetcherWorker.cancel(); + } } diff --git a/src/main/java/org/jabref/gui/Globals.java b/src/main/java/org/jabref/gui/Globals.java index 04ead50bf63..06437d7f6c9 100644 --- a/src/main/java/org/jabref/gui/Globals.java +++ b/src/main/java/org/jabref/gui/Globals.java @@ -9,6 +9,7 @@ import org.jabref.gui.util.DefaultTaskExecutor; import org.jabref.gui.util.TaskExecutor; import org.jabref.logic.journals.JournalAbbreviationRepository; +import org.jabref.logic.journals.predatory.PredatoryJournalRepository; import org.jabref.logic.protectedterms.ProtectedTermsLoader; import org.jabref.logic.remote.RemotePreferences; import org.jabref.logic.remote.server.RemoteListenerServerManager; @@ -51,6 +52,7 @@ public class Globals { * Only GUI code is allowed to access it, logic code should use dependency injection. */ public static JournalAbbreviationRepository journalAbbreviationRepository; + public static PredatoryJournalRepository predatoryJournalRepository; /** * This field is initialized upon startup. diff --git a/src/main/java/org/jabref/gui/JabRefDialogService.java b/src/main/java/org/jabref/gui/JabRefDialogService.java index 6f3c333db13..6b595654d31 100644 --- a/src/main/java/org/jabref/gui/JabRefDialogService.java +++ b/src/main/java/org/jabref/gui/JabRefDialogService.java @@ -25,6 +25,7 @@ import javafx.scene.control.ChoiceDialog; import javafx.scene.control.DialogPane; import javafx.scene.control.Label; +import javafx.scene.control.TextArea; import javafx.scene.control.TextInputDialog; import javafx.scene.layout.HBox; import javafx.scene.layout.Region; @@ -79,8 +80,12 @@ public JabRefDialogService(Window mainWindow) { private FXDialog createDialog(AlertType type, String title, String content) { FXDialog alert = new FXDialog(type, title, true); alert.setHeaderText(null); - alert.setContentText(content); alert.getDialogPane().setMinHeight(Region.USE_PREF_SIZE); + alert.setResizable(true); + + TextArea area = new TextArea(content); + + alert.getDialogPane().setContent(area); alert.initOwner(mainWindow); return alert; } diff --git a/src/main/java/org/jabref/gui/JabRefExecutorService.java b/src/main/java/org/jabref/gui/JabRefExecutorService.java index b23beeb8d2c..90c3b31bdae 100644 --- a/src/main/java/org/jabref/gui/JabRefExecutorService.java +++ b/src/main/java/org/jabref/gui/JabRefExecutorService.java @@ -13,6 +13,8 @@ import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; +import org.jabref.logic.pdf.search.PdfIndexerManager; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -146,6 +148,8 @@ public void shutdownEverything() { gracefullyShutdown(this.executorService); gracefullyShutdown(this.lowPriorityExecutorService); + PdfIndexerManager.shutdownAllIndexers(); + timer.cancel(); } diff --git a/src/main/java/org/jabref/gui/JabRefFrame.java b/src/main/java/org/jabref/gui/JabRefFrame.java index 8a829134b96..0631c7fedc5 100644 --- a/src/main/java/org/jabref/gui/JabRefFrame.java +++ b/src/main/java/org/jabref/gui/JabRefFrame.java @@ -61,6 +61,7 @@ import org.jabref.logic.importer.ImportCleanup; import org.jabref.logic.importer.ParserResult; import org.jabref.logic.l10n.Localization; +import org.jabref.logic.pdf.search.PdfIndexerManager; import org.jabref.logic.shared.DatabaseLocation; import org.jabref.logic.undo.AddUndoableActionEvent; import org.jabref.logic.undo.UndoChangeEvent; @@ -267,31 +268,31 @@ private void initKeyBindings() { getGlobalSearchBar().focus(); break; case NEW_ARTICLE: - new NewEntryAction(this, StandardEntryType.Article, dialogService, prefs, stateManager).execute(); + new NewEntryAction(this::getCurrentLibraryTab, StandardEntryType.Article, dialogService, prefs, stateManager).execute(); break; case NEW_BOOK: - new NewEntryAction(this, StandardEntryType.Book, dialogService, prefs, stateManager).execute(); + new NewEntryAction(this::getCurrentLibraryTab, StandardEntryType.Book, dialogService, prefs, stateManager).execute(); break; case NEW_INBOOK: - new NewEntryAction(this, StandardEntryType.InBook, dialogService, prefs, stateManager).execute(); + new NewEntryAction(this::getCurrentLibraryTab, StandardEntryType.InBook, dialogService, prefs, stateManager).execute(); break; case NEW_MASTERSTHESIS: - new NewEntryAction(this, StandardEntryType.MastersThesis, dialogService, prefs, stateManager).execute(); + new NewEntryAction(this::getCurrentLibraryTab, StandardEntryType.MastersThesis, dialogService, prefs, stateManager).execute(); break; case NEW_PHDTHESIS: - new NewEntryAction(this, StandardEntryType.PhdThesis, dialogService, prefs, stateManager).execute(); + new NewEntryAction(this::getCurrentLibraryTab, StandardEntryType.PhdThesis, dialogService, prefs, stateManager).execute(); break; case NEW_PROCEEDINGS: - new NewEntryAction(this, StandardEntryType.Proceedings, dialogService, prefs, stateManager).execute(); + new NewEntryAction(this::getCurrentLibraryTab, StandardEntryType.Proceedings, dialogService, prefs, stateManager).execute(); break; case NEW_TECHREPORT: - new NewEntryAction(this, StandardEntryType.TechReport, dialogService, prefs, stateManager).execute(); + new NewEntryAction(this::getCurrentLibraryTab, StandardEntryType.TechReport, dialogService, prefs, stateManager).execute(); break; case NEW_UNPUBLISHED: - new NewEntryAction(this, StandardEntryType.Unpublished, dialogService, prefs, stateManager).execute(); + new NewEntryAction(this::getCurrentLibraryTab, StandardEntryType.Unpublished, dialogService, prefs, stateManager).execute(); break; case NEW_INPROCEEDINGS: - new NewEntryAction(this, StandardEntryType.InProceedings, dialogService, prefs, stateManager).execute(); + new NewEntryAction(this::getCurrentLibraryTab, StandardEntryType.InProceedings, dialogService, prefs, stateManager).execute(); break; case PASTE: if (OS.OS_X) { // Workaround for a jdk issue that executes paste twice when using cmd+v in a TextField @@ -406,11 +407,11 @@ public boolean quit() { // Then ask if the user really wants to close, if the library has not been saved since last save. List filenames = new ArrayList<>(); - for (int i = 0; i < tabbedPane.getTabs().size(); i++) { - LibraryTab libraryTab = getLibraryTabAt(i); + for (LibraryTab libraryTab : getLibraryTabs()) { final BibDatabaseContext context = libraryTab.getBibDatabaseContext(); + if (libraryTab.isModified() && (context.getLocation() == DatabaseLocation.LOCAL)) { - tabbedPane.getSelectionModel().select(i); + showLibraryTab(libraryTab); if (!confirmClose(libraryTab)) { return false; } @@ -419,6 +420,7 @@ public boolean quit() { context.getDBMSSynchronizer().closeSharedDatabase(); context.clearDBMSSynchronizer(); } + AutosaveManager.shutdown(context); BackupManager.shutdown(context, prefs.getFilePreferences().getBackupDirectory(), prefs.getFilePreferences().shouldCreateBackup()); context.getDatabasePath().map(Path::toAbsolutePath).map(Path::toString).ifPresent(filenames::add); @@ -488,6 +490,7 @@ private void initLayout() { taskExecutor, dialogService, Globals.journalAbbreviationRepository, + Globals.predatoryJournalRepository, entryTypesManager, undoManager, Globals.getClipboardManager()); @@ -539,15 +542,6 @@ private void setDividerPosition() { } } - /** - * Returns the indexed LibraryTab. - * - * @param i Index of base - */ - public LibraryTab getLibraryTabAt(int i) { - return (LibraryTab) tabbedPane.getTabs().get(i); - } - /** * Returns a list of all LibraryTabs in this frame. */ @@ -558,6 +552,7 @@ public List getLibraryTabs() { .collect(Collectors.toList()); } + @Deprecated public void showLibraryTabAt(int i) { tabbedPane.getSelectionModel().select(i); } @@ -646,7 +641,7 @@ public void init() { } // Update search autocompleter with information for the correct database: - libraryTab.updateSearchManager(); + globalSearchBar.setAutoCompleter(libraryTab.getAutoCompleter()); libraryTab.getUndoManager().postUndoRedoEvent(); libraryTab.getMainTable().requestFocus(); @@ -670,21 +665,6 @@ public LibraryTab getCurrentLibraryTab() { return (LibraryTab) tabbedPane.getSelectionModel().getSelectedItem(); } - /** - * @return the BasePanel count. - */ - public int getBasePanelCount() { - return tabbedPane.getTabs().size(); - } - - /** - * @deprecated do not operate on tabs but on BibDatabaseContexts - */ - @Deprecated - public TabPane getTabbedPane() { - return tabbedPane; - } - /** * This method causes all open LibraryTabs to set up their tables anew. When called from PreferencesDialogViewModel, * this updates to the new settings. We need to notify all tabs about the changes to avoid problems when changing @@ -883,6 +863,7 @@ public void closeTab(LibraryTab libraryTab) { } AutosaveManager.shutdown(context); BackupManager.shutdown(context, prefs.getFilePreferences().getBackupDirectory(), prefs.getFilePreferences().shouldCreateBackup()); + PdfIndexerManager.shutdownAllIndexers(); } private void removeTab(LibraryTab libraryTab) { @@ -960,6 +941,15 @@ public Stage getMainStage() { return mainStage; } + /** + * Refreshes the ui after preferences changes + */ + public void refresh() { + globalSearchBar.updateHintVisibility(); + setupAllTables(); + getLibraryTabs().forEach(panel -> panel.getMainTable().getTableModel().refresh()); + } + /** * The action concerned with closing the window. */ diff --git a/src/main/java/org/jabref/gui/JabRefGUI.java b/src/main/java/org/jabref/gui/JabRefGUI.java index 9a402703619..657429b2e3c 100644 --- a/src/main/java/org/jabref/gui/JabRefGUI.java +++ b/src/main/java/org/jabref/gui/JabRefGUI.java @@ -51,11 +51,11 @@ public class JabRefGUI { private final List toOpenTab = new ArrayList<>(); public JabRefGUI(Stage mainStage, - List databases, + List parserResults, boolean isBlank, PreferencesService preferencesService, FileUpdateMonitor fileUpdateMonitor) { - this.parserResults = databases; + this.parserResults = parserResults; this.isBlank = isBlank; this.preferencesService = preferencesService; this.fileUpdateMonitor = fileUpdateMonitor; @@ -259,9 +259,15 @@ private void openDatabases() { } // Display warnings, if any - int tabNumber = 0; - for (ParserResult pr : parserResults) { - ParserResultWarningDialog.showParserResultWarningDialog(pr, mainFrame, tabNumber++); + for (int tabNumber = 0; tabNumber < parserResults.size(); tabNumber++) { + // ToDo: Method needs to be rewritten, because the index of the parser result and of the libraryTab may not + // be identical, if there are also other tabs opened, that are not libraryTabs. Currently there are none, + // therefore for now this ok. + ParserResult pr = parserResults.get(tabNumber); + if (pr.hasWarnings()) { + ParserResultWarningDialog.showParserResultWarningDialog(pr, mainFrame.getDialogService()); + mainFrame.showLibraryTabAt(tabNumber); + } } // After adding the databases, go through each and see if @@ -269,16 +275,7 @@ private void openDatabases() { // if we found new entry types that can be imported, or checking // if the database contents should be modified due to new features // in this version of JabRef. - // Note that we have to check whether i does not go over getBasePanelCount(). - // This is because importToOpen might have been used, which adds to - // loadedDatabases, but not to getBasePanelCount() - - for (int i = 0; (i < parserResults.size()) && (i < mainFrame.getBasePanelCount()); i++) { - ParserResult pr = parserResults.get(i); - LibraryTab libraryTab = mainFrame.getLibraryTabAt(i); - - OpenDatabaseAction.performPostOpenActions(libraryTab, pr); - } + parserResults.forEach(pr -> OpenDatabaseAction.performPostOpenActions(pr, mainFrame.getDialogService())); LOGGER.debug("Finished adding panels"); } diff --git a/src/main/java/org/jabref/gui/LibraryTab.java b/src/main/java/org/jabref/gui/LibraryTab.java index 137a9548678..658117d8483 100644 --- a/src/main/java/org/jabref/gui/LibraryTab.java +++ b/src/main/java/org/jabref/gui/LibraryTab.java @@ -27,6 +27,7 @@ import org.jabref.gui.autocompleter.AutoCompletePreferences; import org.jabref.gui.autocompleter.PersonNameSuggestionProvider; +import org.jabref.gui.autocompleter.SuggestionProvider; import org.jabref.gui.autocompleter.SuggestionProviders; import org.jabref.gui.autosaveandbackup.AutosaveManager; import org.jabref.gui.autosaveandbackup.BackupManager; @@ -50,8 +51,9 @@ import org.jabref.logic.importer.util.FileFieldParser; import org.jabref.logic.l10n.Localization; import org.jabref.logic.pdf.FileAnnotationCache; -import org.jabref.logic.pdf.search.indexing.IndexingTaskManager; -import org.jabref.logic.pdf.search.indexing.PdfIndexer; +import org.jabref.logic.pdf.search.IndexingTaskManager; +import org.jabref.logic.pdf.search.PdfIndexer; +import org.jabref.logic.pdf.search.PdfIndexerManager; import org.jabref.logic.search.SearchQuery; import org.jabref.logic.shared.DatabaseLocation; import org.jabref.logic.util.UpdateField; @@ -62,6 +64,7 @@ import org.jabref.model.database.event.BibDatabaseContextChangedEvent; import org.jabref.model.database.event.EntriesAddedEvent; import org.jabref.model.database.event.EntriesRemovedEvent; +import org.jabref.model.entry.Author; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.BibEntryTypesManager; import org.jabref.model.entry.LinkedFile; @@ -84,7 +87,7 @@ public class LibraryTab extends Tab { private static final Logger LOGGER = LoggerFactory.getLogger(LibraryTab.class); - private final JabRefFrame frame; + private final LibraryTabContainer tabContainer; private final CountingUndoManager undoManager; private final DialogService dialogService; private final PreferencesService preferencesService; @@ -125,7 +128,7 @@ public class LibraryTab extends Tab { private final TaskExecutor taskExecutor; public LibraryTab(BibDatabaseContext bibDatabaseContext, - JabRefFrame frame, + LibraryTabContainer tabContainer, DialogService dialogService, PreferencesService preferencesService, StateManager stateManager, @@ -133,7 +136,7 @@ public LibraryTab(BibDatabaseContext bibDatabaseContext, BibEntryTypesManager entryTypesManager, CountingUndoManager undoManager, TaskExecutor taskExecutor) { - this.frame = Objects.requireNonNull(frame); + this.tabContainer = Objects.requireNonNull(tabContainer); this.bibDatabaseContext = Objects.requireNonNull(bibDatabaseContext); this.undoManager = undoManager; this.dialogService = dialogService; @@ -220,18 +223,18 @@ public Node createLoadingAnimationLayout() { public void onDatabaseLoadingStarted() { Node loadingLayout = createLoadingAnimationLayout(); getMainTable().placeholderProperty().setValue(loadingLayout); - frame.addTab(this, true); + tabContainer.addTab(this, true); } public void onDatabaseLoadingSucceed(ParserResult result) { BibDatabaseContext context = result.getDatabaseContext(); - OpenDatabaseAction.performPostOpenActions(this, result); + OpenDatabaseAction.performPostOpenActions(result, dialogService); feedData(context); if (preferencesService.getFilePreferences().shouldFulltextIndexLinkedFiles()) { try { - indexingTaskManager.updateIndex(PdfIndexer.of(bibDatabaseContext, preferencesService.getFilePreferences()), bibDatabaseContext); + indexingTaskManager.updateIndex(PdfIndexerManager.getIndexer(bibDatabaseContext, preferencesService.getFilePreferences()), bibDatabaseContext); } catch (IOException e) { LOGGER.error("Cannot access lucene index", e); } @@ -411,10 +414,6 @@ public void setMode(BasePanelMode mode) { this.mode = mode; } - public JabRefFrame frame() { - return frame; - } - /** * Removes the selected entries from the database * @@ -469,12 +468,6 @@ public void insertEntry(final BibEntry bibEntry) { } } - /** - * This method is called from JabRefFrame when the user wants to create a new entry or entries. It is necessary when the user would expect the added entry or one of the added entries to be selected in the entry editor - * - * @param entries The new entries. - */ - public void insertEntries(final List entries) { if (!entries.isEmpty()) { bibDatabaseContext.getDatabase().insertEntries(entries); @@ -488,9 +481,9 @@ public void insertEntries(final List entries) { this.changedProperty.setValue(true); // The database just changed. if (preferencesService.getEntryEditorPreferences().shouldOpenOnNewEntry()) { - showAndEdit(entries.get(0)); + showAndEdit(entries.getFirst()); } - clearAndSelect(entries.get(0)); + clearAndSelect(entries.getFirst()); } } @@ -506,6 +499,7 @@ public void editEntryAndFocusField(BibEntry entry, Field field) { private void createMainTable() { mainTable = new MainTable(tableModel, this, + tabContainer, bibDatabaseContext, preferencesService, dialogService, @@ -558,21 +552,21 @@ public void setupMainPanel() { } /** - * Set up auto completion for this database + * Set up autocompletion for this database */ private void setupAutoCompletion() { AutoCompletePreferences autoCompletePreferences = preferencesService.getAutoCompletePreferences(); if (autoCompletePreferences.shouldAutoComplete()) { suggestionProviders = new SuggestionProviders(getDatabase(), Globals.journalAbbreviationRepository, autoCompletePreferences); } else { - // Create empty suggestion providers if auto completion is deactivated + // Create empty suggestion providers if auto-completion is deactivated suggestionProviders = new SuggestionProviders(); } searchAutoCompleter = new PersonNameSuggestionProvider(FieldFactory.getPersonNameFields(), getDatabase()); } - public void updateSearchManager() { - frame.getGlobalSearchBar().setAutoCompleter(searchAutoCompleter); + public SuggestionProvider getAutoCompleter() { + return searchAutoCompleter; } public EntryEditor getEntryEditor() { @@ -837,7 +831,7 @@ public static LibraryTab createLibraryTab(BackgroundTask dataLoadi DialogService dialogService, PreferencesService preferencesService, StateManager stateManager, - JabRefFrame frame, + LibraryTabContainer tabContainer, FileUpdateMonitor fileUpdateMonitor, BibEntryTypesManager entryTypesManager, CountingUndoManager undoManager, @@ -847,7 +841,7 @@ public static LibraryTab createLibraryTab(BackgroundTask dataLoadi LibraryTab newTab = new LibraryTab( context, - frame, + tabContainer, dialogService, preferencesService, stateManager, @@ -866,7 +860,7 @@ public static LibraryTab createLibraryTab(BackgroundTask dataLoadi } public static LibraryTab createLibraryTab(BibDatabaseContext databaseContext, - JabRefFrame frame, + LibraryTabContainer tabContainer, DialogService dialogService, PreferencesService preferencesService, StateManager stateManager, @@ -876,9 +870,9 @@ public static LibraryTab createLibraryTab(BibDatabaseContext databaseContext, TaskExecutor taskExecutor) { Objects.requireNonNull(databaseContext); - LibraryTab libraryTab = new LibraryTab( + return new LibraryTab( databaseContext, - frame, + tabContainer, dialogService, preferencesService, stateManager, @@ -886,8 +880,6 @@ public static LibraryTab createLibraryTab(BibDatabaseContext databaseContext, entryTypesManager, (CountingUndoManager) undoManager, taskExecutor); - - return libraryTab; } private class GroupTreeListener { @@ -921,10 +913,8 @@ private class IndexUpdateListener { public void listen(EntriesAddedEvent addedEntryEvent) { if (preferencesService.getFilePreferences().shouldFulltextIndexLinkedFiles()) { try { - PdfIndexer pdfIndexer = PdfIndexer.of(bibDatabaseContext, preferencesService.getFilePreferences()); - for (BibEntry addedEntry : addedEntryEvent.getBibEntries()) { - indexingTaskManager.addToIndex(pdfIndexer, addedEntry, bibDatabaseContext); - } + PdfIndexer pdfIndexer = PdfIndexerManager.getIndexer(bibDatabaseContext, preferencesService.getFilePreferences()); + indexingTaskManager.addToIndex(pdfIndexer, addedEntryEvent.getBibEntries()); } catch (IOException e) { LOGGER.error("Cannot access lucene index", e); } @@ -935,7 +925,7 @@ public void listen(EntriesAddedEvent addedEntryEvent) { public void listen(EntriesRemovedEvent removedEntriesEvent) { if (preferencesService.getFilePreferences().shouldFulltextIndexLinkedFiles()) { try { - PdfIndexer pdfIndexer = PdfIndexer.of(bibDatabaseContext, preferencesService.getFilePreferences()); + PdfIndexer pdfIndexer = PdfIndexerManager.getIndexer(bibDatabaseContext, preferencesService.getFilePreferences()); for (BibEntry removedEntry : removedEntriesEvent.getBibEntries()) { indexingTaskManager.removeFromIndex(pdfIndexer, removedEntry); } @@ -958,8 +948,9 @@ public void listen(FieldChangedEvent fieldChangedEvent) { removedFiles.remove(newFileList); try { - indexingTaskManager.addToIndex(PdfIndexer.of(bibDatabaseContext, preferencesService.getFilePreferences()), fieldChangedEvent.getBibEntry(), addedFiles, bibDatabaseContext); - indexingTaskManager.removeFromIndex(PdfIndexer.of(bibDatabaseContext, preferencesService.getFilePreferences()), fieldChangedEvent.getBibEntry(), removedFiles); + PdfIndexer indexer = PdfIndexerManager.getIndexer(bibDatabaseContext, preferencesService.getFilePreferences()); + indexingTaskManager.addToIndex(indexer, fieldChangedEvent.getBibEntry(), addedFiles); + indexingTaskManager.removeFromIndex(indexer, removedFiles); } catch (IOException e) { LOGGER.warn("I/O error when writing lucene index", e); } diff --git a/src/main/java/org/jabref/gui/LibraryTabContainer.java b/src/main/java/org/jabref/gui/LibraryTabContainer.java index 4d3c504d503..c823e5c8892 100644 --- a/src/main/java/org/jabref/gui/LibraryTabContainer.java +++ b/src/main/java/org/jabref/gui/LibraryTabContainer.java @@ -5,8 +5,6 @@ import org.jabref.model.database.BibDatabaseContext; public interface LibraryTabContainer { - LibraryTab getLibraryTabAt(int i); - List getLibraryTabs(); LibraryTab getCurrentLibraryTab(); @@ -20,4 +18,9 @@ public interface LibraryTabContainer { void closeTab(LibraryTab libraryTab); void closeCurrentTab(); + + /** + * Refreshes the ui after changes to the preferences + */ + void refresh(); } diff --git a/src/main/java/org/jabref/gui/MainApplication.java b/src/main/java/org/jabref/gui/MainApplication.java index a187843977d..76a33eb6f24 100644 --- a/src/main/java/org/jabref/gui/MainApplication.java +++ b/src/main/java/org/jabref/gui/MainApplication.java @@ -10,10 +10,15 @@ import org.jabref.model.util.FileUpdateMonitor; import org.jabref.preferences.JabRefPreferences; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * JabRef's main class to process command line options and to start the UI */ public class MainApplication extends Application { + private static final Logger LOGGER = LoggerFactory.getLogger(MainApplication.class); + private static List parserResults; private static boolean isBlank; private static JabRefPreferences preferences; @@ -43,5 +48,10 @@ public void stop() { OOBibBaseConnect.closeOfficeConnection(); Globals.stopBackgroundTasks(); Globals.shutdownThreadPools(); + try { + Globals.predatoryJournalRepository.close(); + } catch (Exception e) { + LOGGER.warn("Cloud not shut down predatoryJournalRepository", e); + } } } diff --git a/src/main/java/org/jabref/gui/MainMenu.java b/src/main/java/org/jabref/gui/MainMenu.java index 28a08a31a0d..c898417b927 100644 --- a/src/main/java/org/jabref/gui/MainMenu.java +++ b/src/main/java/org/jabref/gui/MainMenu.java @@ -65,6 +65,7 @@ import org.jabref.logic.importer.IdFetcher; import org.jabref.logic.importer.WebFetchers; import org.jabref.logic.journals.JournalAbbreviationRepository; +import org.jabref.logic.journals.predatory.PredatoryJournalRepository; import org.jabref.logic.l10n.Localization; import org.jabref.logic.util.OS; import org.jabref.model.entry.BibEntryTypesManager; @@ -82,6 +83,7 @@ public class MainMenu extends MenuBar { private final TaskExecutor taskExecutor; private final DialogService dialogService; private final JournalAbbreviationRepository abbreviationRepository; + private final PredatoryJournalRepository predatoryJournalRepository; private final BibEntryTypesManager entryTypesManager; private final UndoManager undoManager; private final ClipBoardManager clipBoardManager; @@ -95,6 +97,7 @@ public MainMenu(JabRefFrame frame, TaskExecutor taskExecutor, DialogService dialogService, JournalAbbreviationRepository abbreviationRepository, + PredatoryJournalRepository predatoryJournalRepository, BibEntryTypesManager entryTypesManager, UndoManager undoManager, ClipBoardManager clipBoardManager) { @@ -107,6 +110,7 @@ public MainMenu(JabRefFrame frame, this.taskExecutor = taskExecutor; this.dialogService = dialogService; this.abbreviationRepository = abbreviationRepository; + this.predatoryJournalRepository = predatoryJournalRepository; this.entryTypesManager = entryTypesManager; this.undoManager = undoManager; this.clipBoardManager = clipBoardManager; @@ -129,9 +133,9 @@ private void createMenu() { factory.createMenuItem(StandardActions.NEW_LIBRARY, new NewDatabaseAction(frame, preferencesService)), factory.createMenuItem(StandardActions.OPEN_LIBRARY, frame.getOpenDatabaseAction()), frame.getFileHistory(), - factory.createMenuItem(StandardActions.SAVE_LIBRARY, new SaveAction(SaveAction.SaveMethod.SAVE, frame, dialogService, preferencesService, stateManager)), - factory.createMenuItem(StandardActions.SAVE_LIBRARY_AS, new SaveAction(SaveAction.SaveMethod.SAVE_AS, frame, dialogService, preferencesService, stateManager)), - factory.createMenuItem(StandardActions.SAVE_ALL, new SaveAllAction(frame, preferencesService)), + factory.createMenuItem(StandardActions.SAVE_LIBRARY, new SaveAction(SaveAction.SaveMethod.SAVE, frame::getCurrentLibraryTab, dialogService, preferencesService, stateManager)), + factory.createMenuItem(StandardActions.SAVE_LIBRARY_AS, new SaveAction(SaveAction.SaveMethod.SAVE_AS, frame::getCurrentLibraryTab, dialogService, preferencesService, stateManager)), + factory.createMenuItem(StandardActions.SAVE_ALL, new SaveAllAction(frame::getLibraryTabs, preferencesService, dialogService)), factory.createMenuItem(StandardActions.CLOSE_LIBRARY, new JabRefFrame.CloseDatabaseAction(frame)), new SeparatorMenuItem(), @@ -141,19 +145,19 @@ private void createMenu() { factory.createMenuItem(StandardActions.IMPORT_INTO_NEW_LIBRARY, new ImportCommand(frame, ImportCommand.ImportMethod.AS_NEW, preferencesService, stateManager, fileUpdateMonitor, taskExecutor, dialogService))), factory.createSubMenu(StandardActions.EXPORT, - factory.createMenuItem(StandardActions.EXPORT_ALL, new ExportCommand(ExportCommand.ExportMethod.EXPORT_ALL, frame, stateManager, dialogService, preferencesService, entryTypesManager, abbreviationRepository, taskExecutor)), - factory.createMenuItem(StandardActions.EXPORT_SELECTED, new ExportCommand(ExportCommand.ExportMethod.EXPORT_SELECTED, frame, stateManager, dialogService, preferencesService, entryTypesManager, abbreviationRepository, taskExecutor)), - factory.createMenuItem(StandardActions.SAVE_SELECTED_AS_PLAIN_BIBTEX, new SaveAction(SaveAction.SaveMethod.SAVE_SELECTED, frame, dialogService, preferencesService, stateManager))), + factory.createMenuItem(StandardActions.EXPORT_ALL, new ExportCommand(ExportCommand.ExportMethod.EXPORT_ALL, frame::getCurrentLibraryTab, stateManager, dialogService, preferencesService, entryTypesManager, abbreviationRepository, taskExecutor)), + factory.createMenuItem(StandardActions.EXPORT_SELECTED, new ExportCommand(ExportCommand.ExportMethod.EXPORT_SELECTED, frame::getCurrentLibraryTab, stateManager, dialogService, preferencesService, entryTypesManager, abbreviationRepository, taskExecutor)), + factory.createMenuItem(StandardActions.SAVE_SELECTED_AS_PLAIN_BIBTEX, new SaveAction(SaveAction.SaveMethod.SAVE_SELECTED, frame::getCurrentLibraryTab, dialogService, preferencesService, stateManager))), new SeparatorMenuItem(), factory.createSubMenu(StandardActions.REMOTE_DB, - factory.createMenuItem(StandardActions.CONNECT_TO_SHARED_DB, new ConnectToSharedDatabaseCommand(frame)), + factory.createMenuItem(StandardActions.CONNECT_TO_SHARED_DB, new ConnectToSharedDatabaseCommand(frame, dialogService)), factory.createMenuItem(StandardActions.PULL_CHANGES_FROM_SHARED_DB, new PullChangesFromSharedAction(stateManager))), new SeparatorMenuItem(), - factory.createMenuItem(StandardActions.SHOW_PREFS, new ShowPreferencesAction(frame, taskExecutor)), + factory.createMenuItem(StandardActions.SHOW_PREFS, new ShowPreferencesAction(frame, dialogService)), new SeparatorMenuItem(), @@ -161,14 +165,14 @@ private void createMenu() { ); edit.getItems().addAll( - factory.createMenuItem(StandardActions.UNDO, new UndoRedoAction(StandardActions.UNDO, frame, dialogService, stateManager)), - factory.createMenuItem(StandardActions.REDO, new UndoRedoAction(StandardActions.REDO, frame, dialogService, stateManager)), + factory.createMenuItem(StandardActions.UNDO, new UndoRedoAction(StandardActions.UNDO, frame::getCurrentLibraryTab, dialogService, stateManager)), + factory.createMenuItem(StandardActions.REDO, new UndoRedoAction(StandardActions.REDO, frame::getCurrentLibraryTab, dialogService, stateManager)), new SeparatorMenuItem(), - factory.createMenuItem(StandardActions.CUT, new EditAction(StandardActions.CUT, frame, stateManager)), + factory.createMenuItem(StandardActions.CUT, new EditAction(StandardActions.CUT, frame::getCurrentLibraryTab, stateManager, undoManager)), - factory.createMenuItem(StandardActions.COPY, new EditAction(StandardActions.COPY, frame, stateManager)), + factory.createMenuItem(StandardActions.COPY, new EditAction(StandardActions.COPY, frame::getCurrentLibraryTab, stateManager, undoManager)), factory.createSubMenu(StandardActions.COPY_MORE, factory.createMenuItem(StandardActions.COPY_TITLE, new CopyMoreAction(StandardActions.COPY_TITLE, dialogService, stateManager, clipBoardManager, preferencesService, abbreviationRepository)), factory.createMenuItem(StandardActions.COPY_KEY, new CopyMoreAction(StandardActions.COPY_KEY, dialogService, stateManager, clipBoardManager, preferencesService, abbreviationRepository)), @@ -178,12 +182,12 @@ private void createMenu() { factory.createMenuItem(StandardActions.COPY_CITATION_PREVIEW, new CopyCitationAction(CitationStyleOutputFormat.HTML, dialogService, stateManager, clipBoardManager, taskExecutor, preferencesService, abbreviationRepository)), factory.createMenuItem(StandardActions.EXPORT_SELECTED_TO_CLIPBOARD, new ExportToClipboardAction(dialogService, stateManager, clipBoardManager, taskExecutor, preferencesService))), - factory.createMenuItem(StandardActions.PASTE, new EditAction(StandardActions.PASTE, frame, stateManager)), + factory.createMenuItem(StandardActions.PASTE, new EditAction(StandardActions.PASTE, frame::getCurrentLibraryTab, stateManager, undoManager)), new SeparatorMenuItem(), - factory.createMenuItem(StandardActions.REPLACE_ALL, new ReplaceStringAction(frame, stateManager)), - factory.createMenuItem(StandardActions.GENERATE_CITE_KEYS, new GenerateCitationKeyAction(frame, dialogService, stateManager, taskExecutor, preferencesService)), + factory.createMenuItem(StandardActions.REPLACE_ALL, new ReplaceStringAction(frame::getCurrentLibraryTab, stateManager, dialogService)), + factory.createMenuItem(StandardActions.GENERATE_CITE_KEYS, new GenerateCitationKeyAction(frame::getCurrentLibraryTab, dialogService, stateManager, taskExecutor, preferencesService, undoManager)), new SeparatorMenuItem(), @@ -196,12 +200,12 @@ private void createMenu() { specialFieldsSeparator, // ToDo: SpecialField needs the active BasePanel to mark it as changed. // Refactor BasePanel, should mark the BibDatabaseContext or the UndoManager as dirty instead! - SpecialFieldMenuItemFactory.createSpecialFieldMenu(SpecialField.RANKING, factory, frame, dialogService, preferencesService, undoManager, stateManager), - SpecialFieldMenuItemFactory.getSpecialFieldSingleItem(SpecialField.RELEVANCE, factory, frame, dialogService, preferencesService, undoManager, stateManager), - SpecialFieldMenuItemFactory.getSpecialFieldSingleItem(SpecialField.QUALITY, factory, frame, dialogService, preferencesService, undoManager, stateManager), - SpecialFieldMenuItemFactory.getSpecialFieldSingleItem(SpecialField.PRINTED, factory, frame, dialogService, preferencesService, undoManager, stateManager), - SpecialFieldMenuItemFactory.createSpecialFieldMenu(SpecialField.PRIORITY, factory, frame, dialogService, preferencesService, undoManager, stateManager), - SpecialFieldMenuItemFactory.createSpecialFieldMenu(SpecialField.READ_STATUS, factory, frame, dialogService, preferencesService, undoManager, stateManager)); + SpecialFieldMenuItemFactory.createSpecialFieldMenu(SpecialField.RANKING, factory, frame::getCurrentLibraryTab, dialogService, preferencesService, undoManager, stateManager), + SpecialFieldMenuItemFactory.getSpecialFieldSingleItem(SpecialField.RELEVANCE, factory, frame::getCurrentLibraryTab, dialogService, preferencesService, undoManager, stateManager), + SpecialFieldMenuItemFactory.getSpecialFieldSingleItem(SpecialField.QUALITY, factory, frame::getCurrentLibraryTab, dialogService, preferencesService, undoManager, stateManager), + SpecialFieldMenuItemFactory.getSpecialFieldSingleItem(SpecialField.PRINTED, factory, frame::getCurrentLibraryTab, dialogService, preferencesService, undoManager, stateManager), + SpecialFieldMenuItemFactory.createSpecialFieldMenu(SpecialField.PRIORITY, factory, frame::getCurrentLibraryTab, dialogService, preferencesService, undoManager, stateManager), + SpecialFieldMenuItemFactory.createSpecialFieldMenu(SpecialField.READ_STATUS, factory, frame::getCurrentLibraryTab, dialogService, preferencesService, undoManager, stateManager)); edit.addEventHandler(ActionEvent.ACTION, event -> { // Work around for mac only issue, where cmd+v on a dialogue triggers the paste action of menu item, resulting in addition of the pasted content in the MainTable. // If the mainscreen is not focused, the actions captured by menu are consumed. @@ -212,9 +216,9 @@ private void createMenu() { // @formatter:off library.getItems().addAll( - factory.createMenuItem(StandardActions.NEW_ENTRY, new NewEntryAction(frame, dialogService, preferencesService, stateManager)), + factory.createMenuItem(StandardActions.NEW_ENTRY, new NewEntryAction(frame::getCurrentLibraryTab, dialogService, preferencesService, stateManager)), factory.createMenuItem(StandardActions.NEW_ENTRY_FROM_PLAIN_TEXT, new ExtractBibtexAction(dialogService, preferencesService, stateManager)), - factory.createMenuItem(StandardActions.DELETE_ENTRY, new EditAction(StandardActions.DELETE_ENTRY, frame, stateManager)), + factory.createMenuItem(StandardActions.DELETE_ENTRY, new EditAction(StandardActions.DELETE_ENTRY, frame::getCurrentLibraryTab, stateManager, undoManager)), new SeparatorMenuItem(), @@ -222,10 +226,10 @@ private void createMenu() { ); quality.getItems().addAll( - factory.createMenuItem(StandardActions.FIND_DUPLICATES, new DuplicateSearch(frame, dialogService, stateManager, preferencesService, entryTypesManager, taskExecutor)), + factory.createMenuItem(StandardActions.FIND_DUPLICATES, new DuplicateSearch(frame::getCurrentLibraryTab, dialogService, stateManager, preferencesService, entryTypesManager, taskExecutor)), factory.createMenuItem(StandardActions.MERGE_ENTRIES, new MergeEntriesAction(dialogService, stateManager, preferencesService)), - factory.createMenuItem(StandardActions.CHECK_INTEGRITY, new IntegrityCheckAction(frame, preferencesService, dialogService, stateManager, taskExecutor, abbreviationRepository)), - factory.createMenuItem(StandardActions.CLEANUP_ENTRIES, new CleanupAction(frame, preferencesService, dialogService, stateManager, taskExecutor)), + factory.createMenuItem(StandardActions.CHECK_INTEGRITY, new IntegrityCheckAction(frame::getCurrentLibraryTab, preferencesService, dialogService, stateManager, taskExecutor, abbreviationRepository, predatoryJournalRepository)), + factory.createMenuItem(StandardActions.CLEANUP_ENTRIES, new CleanupAction(frame::getCurrentLibraryTab, preferencesService, dialogService, stateManager, taskExecutor, undoManager)), new SeparatorMenuItem(), @@ -234,16 +238,16 @@ private void createMenu() { new SeparatorMenuItem(), factory.createSubMenu(StandardActions.ABBREVIATE, - factory.createMenuItem(StandardActions.ABBREVIATE_DEFAULT, new AbbreviateAction(StandardActions.ABBREVIATE_DEFAULT, frame, dialogService, stateManager, preferencesService.getJournalAbbreviationPreferences(), abbreviationRepository, taskExecutor)), - factory.createMenuItem(StandardActions.ABBREVIATE_DOTLESS, new AbbreviateAction(StandardActions.ABBREVIATE_DOTLESS, frame, dialogService, stateManager, preferencesService.getJournalAbbreviationPreferences(), abbreviationRepository, taskExecutor)), - factory.createMenuItem(StandardActions.ABBREVIATE_SHORTEST_UNIQUE, new AbbreviateAction(StandardActions.ABBREVIATE_SHORTEST_UNIQUE, frame, dialogService, stateManager, preferencesService.getJournalAbbreviationPreferences(), abbreviationRepository, taskExecutor))), + factory.createMenuItem(StandardActions.ABBREVIATE_DEFAULT, new AbbreviateAction(StandardActions.ABBREVIATE_DEFAULT, frame::getCurrentLibraryTab, dialogService, stateManager, preferencesService.getJournalAbbreviationPreferences(), abbreviationRepository, taskExecutor, undoManager)), + factory.createMenuItem(StandardActions.ABBREVIATE_DOTLESS, new AbbreviateAction(StandardActions.ABBREVIATE_DOTLESS, frame::getCurrentLibraryTab, dialogService, stateManager, preferencesService.getJournalAbbreviationPreferences(), abbreviationRepository, taskExecutor, undoManager)), + factory.createMenuItem(StandardActions.ABBREVIATE_SHORTEST_UNIQUE, new AbbreviateAction(StandardActions.ABBREVIATE_SHORTEST_UNIQUE, frame::getCurrentLibraryTab, dialogService, stateManager, preferencesService.getJournalAbbreviationPreferences(), abbreviationRepository, taskExecutor, undoManager))), - factory.createMenuItem(StandardActions.UNABBREVIATE, new AbbreviateAction(StandardActions.UNABBREVIATE, frame, dialogService, stateManager, preferencesService.getJournalAbbreviationPreferences(), abbreviationRepository, taskExecutor)) + factory.createMenuItem(StandardActions.UNABBREVIATE, new AbbreviateAction(StandardActions.UNABBREVIATE, frame::getCurrentLibraryTab, dialogService, stateManager, preferencesService.getJournalAbbreviationPreferences(), abbreviationRepository, taskExecutor, undoManager)) ); Menu lookupIdentifiers = factory.createSubMenu(StandardActions.LOOKUP_DOC_IDENTIFIER); for (IdFetcher fetcher : WebFetchers.getIdFetchers(preferencesService.getImportFormatPreferences())) { - LookupIdentifierAction identifierAction = new LookupIdentifierAction<>(frame, fetcher, stateManager, undoManager, taskExecutor); + LookupIdentifierAction identifierAction = new LookupIdentifierAction<>(fetcher, stateManager, undoManager, dialogService, taskExecutor); lookupIdentifiers.getItems().add(factory.createMenuItem(identifierAction.getAction(), identifierAction)); } @@ -261,7 +265,7 @@ private void createMenu() { tools.getItems().addAll( factory.createMenuItem(StandardActions.PARSE_LATEX, new ParseLatexAction(stateManager)), - factory.createMenuItem(StandardActions.NEW_SUB_LIBRARY_FROM_AUX, new NewSubLibraryAction(frame, stateManager)), + factory.createMenuItem(StandardActions.NEW_SUB_LIBRARY_FROM_AUX, new NewSubLibraryAction(frame, stateManager, dialogService)), new SeparatorMenuItem(), @@ -277,7 +281,7 @@ private void createMenu() { new SeparatorMenuItem(), // Systematic Literature Review (SLR) - factory.createMenuItem(StandardActions.START_NEW_STUDY, new StartNewStudyAction(frame, fileUpdateMonitor, taskExecutor, preferencesService, stateManager)), + factory.createMenuItem(StandardActions.START_NEW_STUDY, new StartNewStudyAction(frame, frame::getOpenDatabaseAction, fileUpdateMonitor, taskExecutor, preferencesService, stateManager, dialogService)), factory.createMenuItem(StandardActions.EDIT_EXISTING_STUDY, new EditExistingStudyAction(dialogService, stateManager)), factory.createMenuItem(StandardActions.UPDATE_SEARCH_RESULTS_OF_STUDY, new ExistingStudySearchAction(frame, frame.getOpenDatabaseAction(), dialogService, fileUpdateMonitor, taskExecutor, preferencesService, stateManager)), @@ -295,13 +299,13 @@ private void createMenu() { new SeparatorMenuItem(), - factory.createMenuItem(StandardActions.NEXT_PREVIEW_STYLE, new PreviewSwitchAction(PreviewSwitchAction.Direction.NEXT, frame, stateManager)), - factory.createMenuItem(StandardActions.PREVIOUS_PREVIEW_STYLE, new PreviewSwitchAction(PreviewSwitchAction.Direction.PREVIOUS, frame, stateManager)), + factory.createMenuItem(StandardActions.NEXT_PREVIEW_STYLE, new PreviewSwitchAction(PreviewSwitchAction.Direction.NEXT, frame::getCurrentLibraryTab, stateManager)), + factory.createMenuItem(StandardActions.PREVIOUS_PREVIEW_STYLE, new PreviewSwitchAction(PreviewSwitchAction.Direction.PREVIOUS, frame::getCurrentLibraryTab, stateManager)), new SeparatorMenuItem(), factory.createMenuItem(StandardActions.SHOW_PDF_VIEWER, new ShowDocumentViewerAction(stateManager, preferencesService)), - factory.createMenuItem(StandardActions.EDIT_ENTRY, new OpenEntryEditorAction(frame, stateManager)), + factory.createMenuItem(StandardActions.EDIT_ENTRY, new OpenEntryEditorAction(frame::getCurrentLibraryTab, stateManager)), factory.createMenuItem(StandardActions.OPEN_CONSOLE, new OpenConsoleAction(stateManager, preferencesService, dialogService)) ); diff --git a/src/main/java/org/jabref/gui/MainToolBar.java b/src/main/java/org/jabref/gui/MainToolBar.java index 1e89db1b724..1882b6a4ecf 100644 --- a/src/main/java/org/jabref/gui/MainToolBar.java +++ b/src/main/java/org/jabref/gui/MainToolBar.java @@ -44,7 +44,7 @@ import org.controlsfx.control.TaskProgressView; public class MainToolBar extends ToolBar { - private final JabRefFrame frame; + private final LibraryTabContainer frame; private final PushToApplicationCommand pushToApplicationCommand; private final GlobalSearchBar globalSearchBar; private final DialogService dialogService; @@ -58,7 +58,7 @@ public class MainToolBar extends ToolBar { private PopOver entryFromIdPopOver; private PopOver progressViewPopOver; - public MainToolBar(JabRefFrame frame, + public MainToolBar(LibraryTabContainer tabContainer, PushToApplicationCommand pushToApplicationCommand, GlobalSearchBar globalSearchBar, DialogService dialogService, @@ -68,7 +68,7 @@ public MainToolBar(JabRefFrame frame, TaskExecutor taskExecutor, BibEntryTypesManager entryTypesManager, CountingUndoManager undoManager) { - this.frame = frame; + this.frame = tabContainer; this.pushToApplicationCommand = pushToApplicationCommand; this.globalSearchBar = globalSearchBar; this.dialogService = dialogService; @@ -97,7 +97,7 @@ private void createToolBar() { new HBox( factory.createIconButton(StandardActions.NEW_LIBRARY, new NewDatabaseAction(frame, preferencesService)), factory.createIconButton(StandardActions.OPEN_LIBRARY, new OpenDatabaseAction(frame, preferencesService, dialogService, stateManager, fileUpdateMonitor, entryTypesManager, undoManager, taskExecutor)), - factory.createIconButton(StandardActions.SAVE_LIBRARY, new SaveAction(SaveAction.SaveMethod.SAVE, frame, dialogService, preferencesService, stateManager))), + factory.createIconButton(StandardActions.SAVE_LIBRARY, new SaveAction(SaveAction.SaveMethod.SAVE, frame::getCurrentLibraryTab, dialogService, preferencesService, stateManager))), leftSpacer, @@ -106,27 +106,27 @@ private void createToolBar() { rightSpacer, new HBox( - factory.createIconButton(StandardActions.NEW_ARTICLE, new NewEntryAction(frame, StandardEntryType.Article, dialogService, preferencesService, stateManager)), - factory.createIconButton(StandardActions.NEW_ENTRY, new NewEntryAction(frame, dialogService, preferencesService, stateManager)), + factory.createIconButton(StandardActions.NEW_ARTICLE, new NewEntryAction(frame::getCurrentLibraryTab, StandardEntryType.Article, dialogService, preferencesService, stateManager)), + factory.createIconButton(StandardActions.NEW_ENTRY, new NewEntryAction(frame::getCurrentLibraryTab, dialogService, preferencesService, stateManager)), createNewEntryFromIdButton(), factory.createIconButton(StandardActions.NEW_ENTRY_FROM_PLAIN_TEXT, new ExtractBibtexAction(dialogService, preferencesService, stateManager)), - factory.createIconButton(StandardActions.DELETE_ENTRY, new EditAction(StandardActions.DELETE_ENTRY, frame, stateManager))), + factory.createIconButton(StandardActions.DELETE_ENTRY, new EditAction(StandardActions.DELETE_ENTRY, frame::getCurrentLibraryTab, stateManager, undoManager))), new Separator(Orientation.VERTICAL), new HBox( - factory.createIconButton(StandardActions.UNDO, new UndoRedoAction(StandardActions.UNDO, frame, dialogService, stateManager)), - factory.createIconButton(StandardActions.REDO, new UndoRedoAction(StandardActions.REDO, frame, dialogService, stateManager)), - factory.createIconButton(StandardActions.CUT, new EditAction(StandardActions.CUT, frame, stateManager)), - factory.createIconButton(StandardActions.COPY, new EditAction(StandardActions.COPY, frame, stateManager)), - factory.createIconButton(StandardActions.PASTE, new EditAction(StandardActions.PASTE, frame, stateManager))), + factory.createIconButton(StandardActions.UNDO, new UndoRedoAction(StandardActions.UNDO, frame::getCurrentLibraryTab, dialogService, stateManager)), + factory.createIconButton(StandardActions.REDO, new UndoRedoAction(StandardActions.REDO, frame::getCurrentLibraryTab, dialogService, stateManager)), + factory.createIconButton(StandardActions.CUT, new EditAction(StandardActions.CUT, frame::getCurrentLibraryTab, stateManager, undoManager)), + factory.createIconButton(StandardActions.COPY, new EditAction(StandardActions.COPY, frame::getCurrentLibraryTab, stateManager, undoManager)), + factory.createIconButton(StandardActions.PASTE, new EditAction(StandardActions.PASTE, frame::getCurrentLibraryTab, stateManager, undoManager))), new Separator(Orientation.VERTICAL), new HBox( pushToApplicationButton, - factory.createIconButton(StandardActions.GENERATE_CITE_KEYS, new GenerateCitationKeyAction(frame, dialogService, stateManager, taskExecutor, preferencesService)), - factory.createIconButton(StandardActions.CLEANUP_ENTRIES, new CleanupAction(frame, preferencesService, dialogService, stateManager, taskExecutor))), + factory.createIconButton(StandardActions.GENERATE_CITE_KEYS, new GenerateCitationKeyAction(frame::getCurrentLibraryTab, dialogService, stateManager, taskExecutor, preferencesService, undoManager)), + factory.createIconButton(StandardActions.CLEANUP_ENTRIES, new CleanupAction(frame::getCurrentLibraryTab, preferencesService, dialogService, stateManager, taskExecutor, undoManager))), new Separator(Orientation.VERTICAL), diff --git a/src/main/java/org/jabref/gui/StateManager.java b/src/main/java/org/jabref/gui/StateManager.java index f1ff42c5785..743ad0ef473 100644 --- a/src/main/java/org/jabref/gui/StateManager.java +++ b/src/main/java/org/jabref/gui/StateManager.java @@ -4,7 +4,6 @@ import java.util.List; import java.util.Objects; import java.util.Optional; -import java.util.stream.Collectors; import javafx.beans.Observable; import javafx.beans.binding.Bindings; @@ -31,7 +30,6 @@ import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.entry.BibEntry; import org.jabref.model.groups.GroupTreeNode; -import org.jabref.model.util.OptionalUtil; import com.tobiasdiez.easybind.EasyBind; import com.tobiasdiez.easybind.EasyBinding; @@ -144,11 +142,6 @@ public void setActiveDatabase(BibDatabaseContext database) { } } - public List getEntriesInCurrentDatabase() { - return OptionalUtil.flatMap(activeDatabase.get(), BibDatabaseContext::getEntries) - .collect(Collectors.toList()); - } - public void clearSearchQuery() { activeSearchQuery.setValue(Optional.empty()); } diff --git a/src/main/java/org/jabref/gui/actions/StandardActions.java b/src/main/java/org/jabref/gui/actions/StandardActions.java index 4053cdc7ea1..7c3429d2a4e 100644 --- a/src/main/java/org/jabref/gui/actions/StandardActions.java +++ b/src/main/java/org/jabref/gui/actions/StandardActions.java @@ -1,5 +1,6 @@ package org.jabref.gui.actions; +import java.util.Objects; import java.util.Optional; import org.jabref.gui.icon.IconTheme; @@ -12,7 +13,7 @@ public enum StandardActions implements Action { COPY_MORE(Localization.lang("Copy") + "..."), COPY_TITLE(Localization.lang("Copy title"), KeyBinding.COPY_TITLE), COPY_KEY(Localization.lang("Copy citation key"), KeyBinding.COPY_CITATION_KEY), - COPY_CITE_KEY(Localization.lang("Copy \\cite{citation key}"), KeyBinding.COPY_CITE_CITATION_KEY), + COPY_CITE_KEY(Localization.lang("Copy citation key with configured cite command"), KeyBinding.COPY_CITE_CITATION_KEY), COPY_KEY_AND_TITLE(Localization.lang("Copy citation key and title"), KeyBinding.COPY_CITATION_KEY_AND_TITLE), COPY_KEY_AND_LINK(Localization.lang("Copy citation key and link"), KeyBinding.COPY_CITATION_KEY_AND_LINK), COPY_CITATION_HTML(Localization.lang("Copy citation (html)"), KeyBinding.COPY_PREVIEW), @@ -84,7 +85,7 @@ public enum StandardActions implements Action { AUTOMATIC_FIELD_EDITOR(Localization.lang("Automatic field editor")), TOGGLE_GROUPS(Localization.lang("Groups"), IconTheme.JabRefIcons.TOGGLE_GROUPS, KeyBinding.TOGGLE_GROUPS_INTERFACE), - TOOGLE_OO(Localization.lang("OpenOffice/LibreOffice"), IconTheme.JabRefIcons.FILE_OPENOFFICE, KeyBinding.OPEN_OPEN_OFFICE_LIBRE_OFFICE_CONNECTION), + TOGGLE_OO(Localization.lang("OpenOffice/LibreOffice"), IconTheme.JabRefIcons.FILE_OPENOFFICE, KeyBinding.OPEN_OPEN_OFFICE_LIBRE_OFFICE_CONNECTION), TOGGLE_WEB_SEARCH(Localization.lang("Web search"), Localization.lang("Toggle web search interface"), IconTheme.JabRefIcons.WWW, KeyBinding.WEB_SEARCH), PARSE_LATEX(Localization.lang("Search for citations in LaTeX files..."), IconTheme.JabRefIcons.LATEX_CITATIONS), @@ -157,6 +158,7 @@ public enum StandardActions implements Action { DELETE_FILE(Localization.lang("Permanently delete local file"), IconTheme.JabRefIcons.DELETE_FILE, KeyBinding.DELETE_ENTRY), HELP(Localization.lang("Online help"), IconTheme.JabRefIcons.HELP, KeyBinding.HELP), + HELP_GROUPS(Localization.lang("Open Help page"), IconTheme.JabRefIcons.HELP, KeyBinding.HELP), HELP_KEY_PATTERNS(Localization.lang("Help on key patterns"), IconTheme.JabRefIcons.HELP, KeyBinding.HELP), HELP_REGEX_SEARCH(Localization.lang("Help on regular expression search"), IconTheme.JabRefIcons.HELP, KeyBinding.HELP), HELP_NAME_FORMATTER(Localization.lang("Help on Name Formatting"), IconTheme.JabRefIcons.HELP, KeyBinding.HELP), @@ -194,7 +196,7 @@ public enum StandardActions implements Action { GROUP_ENTRIES_ADD(Localization.lang("Add selected entries to this group")), GROUP_ENTRIES_REMOVE(Localization.lang("Remove selected entries from this group")); - private final String text; + private String text; private final String description; private final Optional icon; private final Optional keyBinding; @@ -271,4 +273,9 @@ public String getText() { public String getDescription() { return description; } + + public Action withText(String text) { + this.text = Objects.requireNonNull(text); + return this; + } } diff --git a/src/main/java/org/jabref/gui/auximport/NewSubLibraryAction.java b/src/main/java/org/jabref/gui/auximport/NewSubLibraryAction.java index 65cc37abfd9..2af0171b5da 100644 --- a/src/main/java/org/jabref/gui/auximport/NewSubLibraryAction.java +++ b/src/main/java/org/jabref/gui/auximport/NewSubLibraryAction.java @@ -1,12 +1,10 @@ package org.jabref.gui.auximport; import org.jabref.gui.DialogService; -import org.jabref.gui.JabRefFrame; +import org.jabref.gui.LibraryTabContainer; import org.jabref.gui.StateManager; import org.jabref.gui.actions.SimpleCommand; -import com.airhacks.afterburner.injection.Injector; - import static org.jabref.gui.actions.ActionHelper.needsDatabase; /** @@ -16,17 +14,18 @@ */ public class NewSubLibraryAction extends SimpleCommand { - private final JabRefFrame jabRefFrame; + private final LibraryTabContainer tabContainer; + private final DialogService dialogService; - public NewSubLibraryAction(JabRefFrame jabRefFrame, StateManager stateManager) { - this.jabRefFrame = jabRefFrame; + public NewSubLibraryAction(LibraryTabContainer tabContainer, StateManager stateManager, DialogService dialogService) { + this.tabContainer = tabContainer; + this.dialogService = dialogService; this.executable.bind(needsDatabase(stateManager)); } @Override public void execute() { - DialogService dialogService = Injector.instantiateModelOrService(DialogService.class); - dialogService.showCustomDialogAndWait(new FromAuxDialog(jabRefFrame)); + dialogService.showCustomDialogAndWait(new FromAuxDialog(tabContainer)); } } diff --git a/src/main/java/org/jabref/gui/citationkeypattern/GenerateCitationKeyAction.java b/src/main/java/org/jabref/gui/citationkeypattern/GenerateCitationKeyAction.java index 5cc07a31c71..b5fd088e9d1 100644 --- a/src/main/java/org/jabref/gui/citationkeypattern/GenerateCitationKeyAction.java +++ b/src/main/java/org/jabref/gui/citationkeypattern/GenerateCitationKeyAction.java @@ -2,9 +2,12 @@ import java.util.List; import java.util.function.Consumer; +import java.util.function.Supplier; + +import javax.swing.undo.UndoManager; import org.jabref.gui.DialogService; -import org.jabref.gui.JabRefFrame; +import org.jabref.gui.LibraryTab; import org.jabref.gui.StateManager; import org.jabref.gui.actions.ActionHelper; import org.jabref.gui.actions.SimpleCommand; @@ -20,7 +23,7 @@ public class GenerateCitationKeyAction extends SimpleCommand { - private final JabRefFrame frame; + private final Supplier tabSupplier; private final DialogService dialogService; private final StateManager stateManager; @@ -29,13 +32,20 @@ public class GenerateCitationKeyAction extends SimpleCommand { private final TaskExecutor taskExecutor; private final PreferencesService preferencesService; - - public GenerateCitationKeyAction(JabRefFrame frame, DialogService dialogService, StateManager stateManager, TaskExecutor taskExecutor, PreferencesService preferencesService) { - this.frame = frame; + private final UndoManager undoManager; + + public GenerateCitationKeyAction(Supplier tabSupplier, + DialogService dialogService, + StateManager stateManager, + TaskExecutor taskExecutor, + PreferencesService preferencesService, + UndoManager undoManager) { + this.tabSupplier = tabSupplier; this.dialogService = dialogService; this.stateManager = stateManager; this.taskExecutor = taskExecutor; this.preferencesService = preferencesService; + this.undoManager = undoManager; this.executable.bind(ActionHelper.needsEntriesSelected(stateManager)); } @@ -131,10 +141,10 @@ protected Void call() { public BackgroundTask onSuccess(Consumer onSuccess) { // register the undo event only if new citation keys were generated if (compound.hasEdits()) { - frame.getUndoManager().addEdit(compound); + undoManager.addEdit(compound); } - frame.getCurrentLibraryTab().markBaseChanged(); + tabSupplier.get().markBaseChanged(); dialogService.notify(formatOutputMessage(Localization.lang("Generated citation key for"), entries.size())); return super.onSuccess(onSuccess); } diff --git a/src/main/java/org/jabref/gui/cleanup/CleanupAction.java b/src/main/java/org/jabref/gui/cleanup/CleanupAction.java index 5cf61dceaee..f4f75a2ad74 100644 --- a/src/main/java/org/jabref/gui/cleanup/CleanupAction.java +++ b/src/main/java/org/jabref/gui/cleanup/CleanupAction.java @@ -2,9 +2,12 @@ import java.util.List; import java.util.Optional; +import java.util.function.Supplier; + +import javax.swing.undo.UndoManager; import org.jabref.gui.DialogService; -import org.jabref.gui.JabRefFrame; +import org.jabref.gui.LibraryTab; import org.jabref.gui.StateManager; import org.jabref.gui.actions.ActionHelper; import org.jabref.gui.actions.SimpleCommand; @@ -22,25 +25,28 @@ public class CleanupAction extends SimpleCommand { - private final JabRefFrame frame; + private final Supplier tabSupplier; private final PreferencesService preferences; private final DialogService dialogService; private final StateManager stateManager; private final TaskExecutor taskExecutor; + private final UndoManager undoManager; private boolean isCanceled; private int modifiedEntriesCount; - public CleanupAction(JabRefFrame frame, + public CleanupAction(Supplier tabSupplier, PreferencesService preferences, DialogService dialogService, StateManager stateManager, - TaskExecutor taskExecutor) { - this.frame = frame; + TaskExecutor taskExecutor, + UndoManager undoManager) { + this.tabSupplier = tabSupplier; this.preferences = preferences; this.dialogService = dialogService; this.stateManager = stateManager; this.taskExecutor = taskExecutor; + this.undoManager = undoManager; this.executable.bind(ActionHelper.needsEntriesSelected(stateManager)); } @@ -118,8 +124,8 @@ private void showResults() { } if (modifiedEntriesCount > 0) { - frame.getCurrentLibraryTab().updateEntryEditorIfShowing(); - frame.getCurrentLibraryTab().markBaseChanged(); + tabSupplier.get().updateEntryEditorIfShowing(); + tabSupplier.get().markBaseChanged(); } if (modifiedEntriesCount == 0) { @@ -141,7 +147,7 @@ private void cleanup(BibDatabaseContext databaseContext, CleanupPreferences clea ce.end(); if (ce.hasEdits()) { modifiedEntriesCount++; - frame.getUndoManager().addEdit(ce); + undoManager.addEdit(ce); } } } diff --git a/src/main/java/org/jabref/gui/duplicationFinder/DuplicateSearch.java b/src/main/java/org/jabref/gui/duplicationFinder/DuplicateSearch.java index 0beda09fdf5..a6bb39a936f 100644 --- a/src/main/java/org/jabref/gui/duplicationFinder/DuplicateSearch.java +++ b/src/main/java/org/jabref/gui/duplicationFinder/DuplicateSearch.java @@ -10,6 +10,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; import javafx.beans.binding.Bindings; import javafx.beans.property.SimpleIntegerProperty; @@ -17,7 +18,6 @@ import org.jabref.gui.DialogService; import org.jabref.gui.JabRefExecutorService; -import org.jabref.gui.JabRefFrame; import org.jabref.gui.LibraryTab; import org.jabref.gui.StateManager; import org.jabref.gui.actions.SimpleCommand; @@ -41,7 +41,7 @@ public class DuplicateSearch extends SimpleCommand { - private final JabRefFrame frame; + private final Supplier tabSupplier; private final BlockingQueue> duplicates = new LinkedBlockingQueue<>(); private final AtomicBoolean libraryAnalyzed = new AtomicBoolean(); @@ -57,13 +57,13 @@ public class DuplicateSearch extends SimpleCommand { private final BibEntryTypesManager entryTypesManager; private final TaskExecutor taskExecutor; - public DuplicateSearch(JabRefFrame frame, + public DuplicateSearch(Supplier tabSupplier, DialogService dialogService, StateManager stateManager, PreferencesService prefs, BibEntryTypesManager entryTypesManager, TaskExecutor taskExecutor) { - this.frame = frame; + this.tabSupplier = tabSupplier; this.dialogService = dialogService; this.stateManager = stateManager; this.prefs = prefs; @@ -156,7 +156,7 @@ private DuplicateSearchResult verifyDuplicates() { } private void askResolveStrategy(DuplicateSearchResult result, BibEntry first, BibEntry second, DuplicateResolverType resolverType) { - DuplicateResolverDialog dialog = new DuplicateResolverDialog(first, second, resolverType, frame.getCurrentLibraryTab().getBibDatabaseContext(), stateManager, dialogService, prefs); + DuplicateResolverDialog dialog = new DuplicateResolverDialog(first, second, resolverType, tabSupplier.get().getBibDatabaseContext(), stateManager, dialogService, prefs); dialog.titleProperty().bind(Bindings.concat(dialog.getTitle()).concat(" (").concat(duplicateProgress.getValue()).concat("/").concat(duplicateTotal).concat(")")); @@ -189,7 +189,7 @@ private void handleDuplicates(DuplicateSearchResult result) { return; } - LibraryTab libraryTab = frame.getCurrentLibraryTab(); + LibraryTab libraryTab = tabSupplier.get(); final NamedCompound compoundEdit = new NamedCompound(Localization.lang("duplicate removal")); // Now, do the actual removal: if (!result.getToRemove().isEmpty()) { diff --git a/src/main/java/org/jabref/gui/edit/CopyMoreAction.java b/src/main/java/org/jabref/gui/edit/CopyMoreAction.java index 0f9bbe3c103..36a770b4422 100644 --- a/src/main/java/org/jabref/gui/edit/CopyMoreAction.java +++ b/src/main/java/org/jabref/gui/edit/CopyMoreAction.java @@ -3,7 +3,7 @@ import java.io.IOException; import java.io.StringReader; import java.util.List; -import java.util.Optional; +import java.util.function.Function; import java.util.stream.Collectors; import org.jabref.gui.ClipBoardManager; @@ -17,7 +17,9 @@ import org.jabref.logic.l10n.Localization; import org.jabref.logic.layout.Layout; import org.jabref.logic.layout.LayoutHelper; +import org.jabref.logic.push.CitationCommandString; import org.jabref.logic.util.OS; +import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.field.StandardField; import org.jabref.preferences.PreferencesService; @@ -58,13 +60,20 @@ public void execute() { } switch (action) { - case COPY_TITLE -> copyTitle(); - case COPY_KEY -> copyKey(); - case COPY_CITE_KEY -> copyCiteKey(); - case COPY_KEY_AND_TITLE -> copyKeyAndTitle(); - case COPY_KEY_AND_LINK -> copyKeyAndLink(); - case COPY_DOI, COPY_DOI_URL -> copyDoi(); - default -> LOGGER.info("Unknown copy command."); + case COPY_TITLE -> + copyTitle(); + case COPY_KEY -> + copyKey(); + case COPY_CITE_KEY -> + copyCiteKey(); + case COPY_KEY_AND_TITLE -> + copyKeyAndTitle(); + case COPY_KEY_AND_LINK -> + copyKeyAndLink(); + case COPY_DOI, COPY_DOI_URL -> + copyDoi(); + default -> + LOGGER.info("Unknown copy command."); } } @@ -94,47 +103,20 @@ private void copyTitle() { } } - private void copyKey() { - List entries = stateManager.getSelectedEntries(); - - // Collect all non-null keys. - List keys = entries.stream() - .filter(entry -> entry.getCitationKey().isPresent()) - .map(entry -> entry.getCitationKey().get()) - .collect(Collectors.toList()); - - if (keys.isEmpty()) { - dialogService.notify(Localization.lang("None of the selected entries have citation keys.")); - return; - } - - final String copiedKeys = String.join(",", keys); - clipBoardManager.setContent(copiedKeys); - - if (keys.size() == entries.size()) { - // All entries had keys. - dialogService.notify(Localization.lang("Copied '%0' to clipboard.", - JabRefDialogService.shortenDialogMessage(copiedKeys))); - } else { - dialogService.notify(Localization.lang("Warning: %0 out of %1 entries have undefined citation key.", - Integer.toString(entries.size() - keys.size()), Integer.toString(entries.size()))); - } - } - private void copyDoi() { List entries = stateManager.getSelectedEntries(); // Collect all non-null DOI or DOI urls if (action == StandardActions.COPY_DOI_URL) { copyDoiList(entries.stream() - .filter(entry -> entry.getDOI().isPresent()) - .map(entry -> entry.getDOI().get().getURIAsASCIIString()) - .collect(Collectors.toList()), entries.size()); + .filter(entry -> entry.getDOI().isPresent()) + .map(entry -> entry.getDOI().get().getURIAsASCIIString()) + .collect(Collectors.toList()), entries.size()); } else { copyDoiList(entries.stream() - .filter(entry -> entry.getDOI().isPresent()) - .map(entry -> entry.getDOI().get().getDOI()) - .collect(Collectors.toList()), entries.size()); + .filter(entry -> entry.getDOI().isPresent()) + .map(entry -> entry.getDOI().get().getDOI()) + .collect(Collectors.toList()), entries.size()); } } @@ -157,7 +139,7 @@ private void copyDoiList(List dois, int size) { } } - private void copyCiteKey() { + private void doCopyKey(Function, String> mapKeyList) { List entries = stateManager.getSelectedEntries(); // Collect all non-null keys. @@ -171,23 +153,31 @@ private void copyCiteKey() { return; } - String citeCommand = Optional.ofNullable(preferencesService.getExternalApplicationsPreferences().getCiteCommand()) - .filter(cite -> cite.contains("\\")) // must contain \ - .orElse("\\cite"); + String clipBoardContent = mapKeyList.apply(keys); - final String copiedCiteCommand = citeCommand + "{" + String.join(",", keys) + '}'; - clipBoardManager.setContent(copiedCiteCommand); + clipBoardManager.setContent(clipBoardContent); if (keys.size() == entries.size()) { // All entries had keys. dialogService.notify(Localization.lang("Copied '%0' to clipboard.", - JabRefDialogService.shortenDialogMessage(copiedCiteCommand))); + JabRefDialogService.shortenDialogMessage(clipBoardContent))); } else { dialogService.notify(Localization.lang("Warning: %0 out of %1 entries have undefined citation key.", Integer.toString(entries.size() - keys.size()), Integer.toString(entries.size()))); } } + private void copyCiteKey() { + doCopyKey(keys -> { + CitationCommandString citeCommand = preferencesService.getExternalApplicationsPreferences().getCiteCommand(); + return citeCommand.prefix() + String.join(citeCommand.delimiter(), keys) + citeCommand.suffix(); + }); + } + + private void copyKey() { + doCopyKey(keys -> String.join(preferencesService.getExternalApplicationsPreferences().getCiteCommand().delimiter(), keys)); + } + private void copyKeyAndTitle() { List entries = stateManager.getSelectedEntries(); @@ -208,7 +198,9 @@ private void copyKeyAndTitle() { for (BibEntry entry : entries) { if (entry.hasCitationKey()) { entriesWithKeys++; - keyAndTitle.append(layout.doLayout(entry, stateManager.getActiveDatabase().get().getDatabase())); + stateManager.getActiveDatabase() + .map(BibDatabaseContext::getDatabase) + .ifPresent(bibDatabase -> keyAndTitle.append(layout.doLayout(entry, bibDatabase))); } } @@ -242,7 +234,7 @@ private void copyKeyAndLink() { List entriesWithKey = entries.stream() .filter(BibEntry::hasCitationKey) - .collect(Collectors.toList()); + .toList(); if (entriesWithKey.isEmpty()) { dialogService.notify(Localization.lang("None of the selected entries have citation keys.")); @@ -250,7 +242,10 @@ private void copyKeyAndLink() { } for (BibEntry entry : entriesWithKey) { - String key = entry.getCitationKey().get(); + String key = entry.getCitationKey().orElse(""); + if (LOGGER.isDebugEnabled() && key.isEmpty()) { + LOGGER.debug("entry {} had no citation key, but it should have had one", entry); + } String url = entry.getField(StandardField.URL).orElse(""); keyAndLink.append(url.isEmpty() ? key : String.format("%s", url, key)); keyAndLink.append(OS.NEWLINE); diff --git a/src/main/java/org/jabref/gui/edit/EditAction.java b/src/main/java/org/jabref/gui/edit/EditAction.java index b0daa482cb0..be8472a1585 100644 --- a/src/main/java/org/jabref/gui/edit/EditAction.java +++ b/src/main/java/org/jabref/gui/edit/EditAction.java @@ -1,9 +1,13 @@ package org.jabref.gui.edit; +import java.util.function.Supplier; + +import javax.swing.undo.UndoManager; + import javafx.scene.control.TextInputControl; import javafx.scene.web.WebView; -import org.jabref.gui.JabRefFrame; +import org.jabref.gui.LibraryTab; import org.jabref.gui.StateManager; import org.jabref.gui.actions.ActionHelper; import org.jabref.gui.actions.SimpleCommand; @@ -21,14 +25,16 @@ public class EditAction extends SimpleCommand { private static final Logger LOGGER = LoggerFactory.getLogger(EditAction.class); - private final JabRefFrame frame; + private final Supplier tabSupplier; private final StandardActions action; private final StateManager stateManager; + private final UndoManager undoManager; - public EditAction(StandardActions action, JabRefFrame frame, StateManager stateManager) { + public EditAction(StandardActions action, Supplier tabSupplier, StateManager stateManager, UndoManager undoManager) { this.action = action; - this.frame = frame; + this.tabSupplier = tabSupplier; this.stateManager = stateManager; + this.undoManager = undoManager; if (action == StandardActions.PASTE) { this.executable.bind(ActionHelper.needsDatabase(stateManager)); @@ -72,18 +78,18 @@ public void execute() { // Not sure what is selected -> copy/paste/cut selected entries except for Preview and CodeArea switch (action) { - case COPY -> frame.getCurrentLibraryTab().copy(); - case CUT -> frame.getCurrentLibraryTab().cut(); - case PASTE -> frame.getCurrentLibraryTab().paste(); - case DELETE_ENTRY -> frame.getCurrentLibraryTab().delete(false); + case COPY -> tabSupplier.get().copy(); + case CUT -> tabSupplier.get().cut(); + case PASTE -> tabSupplier.get().paste(); + case DELETE_ENTRY -> tabSupplier.get().delete(false); case UNDO -> { - if (frame.getUndoManager().canUndo()) { - frame.getUndoManager().undo(); + if (undoManager.canUndo()) { + undoManager.undo(); } } case REDO -> { - if (frame.getUndoManager().canRedo()) { - frame.getUndoManager().redo(); + if (undoManager.canRedo()) { + undoManager.redo(); } } default -> LOGGER.debug("Only cut/copy/paste/deleteEntry supported but got: {} and focus owner {}", action, focusOwner); diff --git a/src/main/java/org/jabref/gui/edit/ReplaceStringAction.java b/src/main/java/org/jabref/gui/edit/ReplaceStringAction.java index bf7b7e579d0..8339b5db7f3 100644 --- a/src/main/java/org/jabref/gui/edit/ReplaceStringAction.java +++ b/src/main/java/org/jabref/gui/edit/ReplaceStringAction.java @@ -1,25 +1,26 @@ package org.jabref.gui.edit; +import java.util.function.Supplier; + import org.jabref.gui.DialogService; -import org.jabref.gui.JabRefFrame; +import org.jabref.gui.LibraryTab; import org.jabref.gui.StateManager; import org.jabref.gui.actions.ActionHelper; import org.jabref.gui.actions.SimpleCommand; -import com.airhacks.afterburner.injection.Injector; - public class ReplaceStringAction extends SimpleCommand { - private final JabRefFrame frame; + private final Supplier tabSupplier; + private final DialogService dialogService; - public ReplaceStringAction(JabRefFrame frame, StateManager stateManager) { - this.frame = frame; + public ReplaceStringAction(Supplier tabSupplier, StateManager stateManager, DialogService dialogService) { + this.tabSupplier = tabSupplier; + this.dialogService = dialogService; this.executable.bind(ActionHelper.needsDatabase(stateManager)); } @Override public void execute() { - DialogService dialogService = Injector.instantiateModelOrService(DialogService.class); - dialogService.showCustomDialogAndWait(new ReplaceStringView(frame.getCurrentLibraryTab())); + dialogService.showCustomDialogAndWait(new ReplaceStringView(tabSupplier.get())); } } diff --git a/src/main/java/org/jabref/gui/entryeditor/CommentsTab.java b/src/main/java/org/jabref/gui/entryeditor/CommentsTab.java index 269ab381a83..4aa92a3520d 100644 --- a/src/main/java/org/jabref/gui/entryeditor/CommentsTab.java +++ b/src/main/java/org/jabref/gui/entryeditor/CommentsTab.java @@ -1,22 +1,31 @@ package org.jabref.gui.entryeditor; +import java.util.Comparator; import java.util.LinkedHashSet; import java.util.Map; -import java.util.Set; +import java.util.Optional; +import java.util.SequencedSet; import java.util.stream.Collectors; import javax.swing.undo.UndoManager; +import javafx.collections.ObservableList; +import javafx.geometry.VPos; +import javafx.scene.control.Button; +import javafx.scene.layout.Priority; +import javafx.scene.layout.RowConstraints; + import org.jabref.gui.DialogService; import org.jabref.gui.StateManager; import org.jabref.gui.autocompleter.SuggestionProviders; import org.jabref.gui.fieldeditors.FieldEditorFX; +import org.jabref.gui.fieldeditors.FieldNameLabel; import org.jabref.gui.icon.IconTheme; import org.jabref.gui.theme.ThemeManager; import org.jabref.gui.util.TaskExecutor; import org.jabref.logic.journals.JournalAbbreviationRepository; import org.jabref.logic.l10n.Localization; -import org.jabref.logic.pdf.search.indexing.IndexingTaskManager; +import org.jabref.logic.pdf.search.IndexingTaskManager; import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.field.Field; @@ -28,6 +37,10 @@ public class CommentsTab extends FieldsEditorTab { public static final String NAME = "Comments"; private final String defaultOwner; + private final UserSpecificCommentField userSpecificCommentField; + + private final EntryEditorPreferences entryEditorPreferences; + public CommentsTab(PreferencesService preferences, BibDatabaseContext databaseContext, SuggestionProviders suggestionProviders, @@ -54,38 +67,89 @@ public CommentsTab(PreferencesService preferences, this.defaultOwner = preferences.getOwnerPreferences().getDefaultOwner(); setText(Localization.lang("Comments")); setGraphic(IconTheme.JabRefIcons.COMMENT.getGraphicNode()); + + userSpecificCommentField = new UserSpecificCommentField(defaultOwner); + entryEditorPreferences = preferences.getEntryEditorPreferences(); } @Override - protected Set determineFieldsToShow(BibEntry entry) { - UserSpecificCommentField defaultCommentField = new UserSpecificCommentField(defaultOwner); + protected SequencedSet determineFieldsToShow(BibEntry entry) { + SequencedSet comments = new LinkedHashSet<>(); - // As default: Show BibTeX comment field and the user-specific comment field of the default owner - Set comments = new LinkedHashSet<>(Set.of(defaultCommentField, StandardField.COMMENT)); + // First comes the standard comment field + comments.add(StandardField.COMMENT); - comments.addAll(entry.getFields().stream() - .filter(field -> field instanceof UserSpecificCommentField || - field.getName().toLowerCase().contains("comment")) - .collect(Collectors.toSet())); + // Also show comment field of the current user (if enabled in the preferences) + if (entry.hasField(userSpecificCommentField) || entryEditorPreferences.shouldShowUserCommentsFields()) { + comments.add(userSpecificCommentField); + } + // Show all non-empty comment fields (otherwise, they are completely hidden) + comments.addAll(entry.getFields().stream() + .filter(field -> (field instanceof UserSpecificCommentField && !field.equals(userSpecificCommentField)) + || field.getName().toLowerCase().contains("comment")) + .sorted(Comparator.comparing(Field::getName)) + .collect(Collectors.toCollection(LinkedHashSet::new))); return comments; } + /** + * Comment editors: three times size of button + */ + private void setCompressedRowLayout() { + int numberOfComments = gridPane.getRowCount() - 1; + double totalWeight = numberOfComments * 3 + 1; + + RowConstraints commentConstraint = new RowConstraints(); + commentConstraint.setVgrow(Priority.ALWAYS); + commentConstraint.setValignment(VPos.TOP); + double commentHeightPercent = 3.0 / totalWeight * 100.0; + commentConstraint.setPercentHeight(commentHeightPercent); + + RowConstraints buttonConstraint = new RowConstraints(); + buttonConstraint.setVgrow(Priority.ALWAYS); + buttonConstraint.setValignment(VPos.TOP); + double addButtonHeightPercent = 1.0 / totalWeight * 100.0; + buttonConstraint.setPercentHeight(addButtonHeightPercent); + + ObservableList rowConstraints = gridPane.getRowConstraints(); + rowConstraints.clear(); + for (int i = 1; i <= numberOfComments; i++) { + rowConstraints.add(commentConstraint); + } + rowConstraints.add(buttonConstraint); + } + @Override protected void setupPanel(BibEntry entry, boolean compressed) { super.setupPanel(entry, compressed); + Optional fieldEditorForUserDefinedComment = editors.entrySet().stream().filter(f -> f.getKey().getName().contains(defaultOwner)).map(Map.Entry::getValue).findFirst(); for (Map.Entry fieldEditorEntry : editors.entrySet()) { Field field = fieldEditorEntry.getKey(); FieldEditorFX editor = fieldEditorEntry.getValue(); - if (field instanceof UserSpecificCommentField) { - if (field.getName().contains(defaultOwner)) { - editor.getNode().setDisable(false); - } - } else { - editor.getNode().setDisable(!field.getName().equals(StandardField.COMMENT.getName())); - } + boolean isStandardBibtexComment = field == StandardField.COMMENT; + boolean isDefaultOwnerComment = field.equals(userSpecificCommentField); + boolean shouldBeEnabled = isStandardBibtexComment || isDefaultOwnerComment; + editor.getNode().setDisable(!shouldBeEnabled); + } + + // Show "Hide" button only if user-specific comment field is empty. Otherwise, it is a strange UI, because the + // button would just disappear and no change **in the current** editor would be made + if (entryEditorPreferences.shouldShowUserCommentsFields() && !entry.hasField(userSpecificCommentField)) { + Button hideDefaultOwnerCommentButton = new Button(Localization.lang("Hide user comments")); + hideDefaultOwnerCommentButton.setOnAction(e -> { + var labelForField = gridPane.getChildren().stream().filter(s -> s instanceof FieldNameLabel).filter(x -> ((FieldNameLabel) x).getText().equals(userSpecificCommentField.getDisplayName())).findFirst(); + labelForField.ifPresent(label -> gridPane.getChildren().remove(label)); + fieldEditorForUserDefinedComment.ifPresent(f -> gridPane.getChildren().remove(f.getNode())); + editors.remove(userSpecificCommentField); + + entryEditorPreferences.setShowUserCommentsFields(false); + setupPanel(entry, false); + }); + gridPane.add(hideDefaultOwnerCommentButton, 1, gridPane.getRowCount(), 2, 1); + setCompressedRowLayout(); } } } diff --git a/src/main/java/org/jabref/gui/entryeditor/DeprecatedFieldsTab.java b/src/main/java/org/jabref/gui/entryeditor/DeprecatedFieldsTab.java index a40302d54c6..a66339ea7b6 100644 --- a/src/main/java/org/jabref/gui/entryeditor/DeprecatedFieldsTab.java +++ b/src/main/java/org/jabref/gui/entryeditor/DeprecatedFieldsTab.java @@ -1,8 +1,8 @@ package org.jabref.gui.entryeditor; -import java.util.Collections; +import java.util.LinkedHashSet; import java.util.Optional; -import java.util.Set; +import java.util.SequencedSet; import java.util.stream.Collectors; import javax.swing.undo.UndoManager; @@ -17,7 +17,7 @@ import org.jabref.gui.util.TaskExecutor; import org.jabref.logic.journals.JournalAbbreviationRepository; import org.jabref.logic.l10n.Localization; -import org.jabref.logic.pdf.search.indexing.IndexingTaskManager; +import org.jabref.logic.pdf.search.IndexingTaskManager; import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.database.BibDatabaseMode; import org.jabref.model.entry.BibEntry; @@ -59,14 +59,14 @@ public DeprecatedFieldsTab(BibDatabaseContext databaseContext, } @Override - protected Set determineFieldsToShow(BibEntry entry) { + protected SequencedSet determineFieldsToShow(BibEntry entry) { BibDatabaseMode mode = databaseContext.getMode(); Optional entryType = entryTypesManager.enrich(entry.getType(), mode); if (entryType.isPresent()) { - return entryType.get().getDeprecatedFields(mode).stream().filter(field -> !entry.getField(field).isEmpty()).collect(Collectors.toSet()); + return entryType.get().getDeprecatedFields(mode).stream().filter(field -> !entry.getField(field).isEmpty()).collect(Collectors.toCollection(LinkedHashSet::new)); } else { - // Entry type unknown -> treat all fields as required - return Collections.emptySet(); + // Entry type unknown -> treat all fields as required (thus no optional fields) + return new LinkedHashSet<>(); } } } diff --git a/src/main/java/org/jabref/gui/entryeditor/EntryEditor.java b/src/main/java/org/jabref/gui/entryeditor/EntryEditor.java index 7ee6f1b6968..802c04194b4 100644 --- a/src/main/java/org/jabref/gui/entryeditor/EntryEditor.java +++ b/src/main/java/org/jabref/gui/entryeditor/EntryEditor.java @@ -285,7 +285,7 @@ private List createTabs() { entryEditorTabs.add(new FileAnnotationTab(libraryTab.getAnnotationCache())); entryEditorTabs.add(new RelatedArticlesTab(entryEditorPreferences, preferencesService, dialogService, taskExecutor)); entryEditorTabs.add(new CitationRelationsTab(entryEditorPreferences, dialogService, databaseContext, - undoManager, stateManager, fileMonitor, preferencesService, libraryTab)); + undoManager, stateManager, fileMonitor, preferencesService, libraryTab, taskExecutor)); sourceTab = new SourceTab( databaseContext, diff --git a/src/main/java/org/jabref/gui/entryeditor/EntryEditorPreferences.java b/src/main/java/org/jabref/gui/entryeditor/EntryEditorPreferences.java index c2afea3b0af..ae0fda0194c 100644 --- a/src/main/java/org/jabref/gui/entryeditor/EntryEditorPreferences.java +++ b/src/main/java/org/jabref/gui/entryeditor/EntryEditorPreferences.java @@ -48,6 +48,7 @@ public static JournalPopupEnabled fromString(String status) { private final BooleanProperty autoLinkFiles; private final ObjectProperty enablementStatus; private final BooleanProperty shouldShowSciteTab; + private final BooleanProperty showUserCommentsFields; public EntryEditorPreferences(Map> entryEditorTabList, Map> defaultEntryEditorTabList, @@ -60,7 +61,8 @@ public EntryEditorPreferences(Map> entryEditorTabList, double dividerPosition, boolean autolinkFilesEnabled, JournalPopupEnabled journalPopupEnabled, - boolean showSciteTab) { + boolean showSciteTab, + boolean showUserCommentsFields) { this.entryEditorTabList = new SimpleMapProperty<>(FXCollections.observableMap(entryEditorTabList)); this.defaultEntryEditorTabList = new SimpleMapProperty<>(FXCollections.observableMap(defaultEntryEditorTabList)); @@ -74,6 +76,7 @@ public EntryEditorPreferences(Map> entryEditorTabList, this.autoLinkFiles = new SimpleBooleanProperty(autolinkFilesEnabled); this.enablementStatus = new SimpleObjectProperty<>(journalPopupEnabled); this.shouldShowSciteTab = new SimpleBooleanProperty(showSciteTab); + this.showUserCommentsFields = new SimpleBooleanProperty(showUserCommentsFields); } public ObservableMap> getEntryEditorTabs() { @@ -211,4 +214,16 @@ public BooleanProperty shouldShowLSciteTabProperty() { public void setShouldShowSciteTab(boolean shouldShowSciteTab) { this.shouldShowSciteTab.set(shouldShowSciteTab); } + + public boolean shouldShowUserCommentsFields() { + return showUserCommentsFields.get(); + } + + public BooleanProperty showUserCommentsFieldsProperty() { + return showUserCommentsFields; + } + + public void setShowUserCommentsFields(boolean showUserCommentsFields) { + this.showUserCommentsFields.set(showUserCommentsFields); + } } diff --git a/src/main/java/org/jabref/gui/entryeditor/FieldsEditorTab.java b/src/main/java/org/jabref/gui/entryeditor/FieldsEditorTab.java index 780af05c3c4..20e54396bfe 100644 --- a/src/main/java/org/jabref/gui/entryeditor/FieldsEditorTab.java +++ b/src/main/java/org/jabref/gui/entryeditor/FieldsEditorTab.java @@ -6,11 +6,12 @@ import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.Set; +import java.util.SequencedSet; import java.util.stream.Stream; import javax.swing.undo.UndoManager; +import javafx.collections.ObservableList; import javafx.geometry.VPos; import javafx.scene.Node; import javafx.scene.Parent; @@ -33,7 +34,7 @@ import org.jabref.gui.theme.ThemeManager; import org.jabref.gui.util.TaskExecutor; import org.jabref.logic.journals.JournalAbbreviationRepository; -import org.jabref.logic.pdf.search.indexing.IndexingTaskManager; +import org.jabref.logic.pdf.search.IndexingTaskManager; import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.field.Field; @@ -47,6 +48,7 @@ abstract class FieldsEditorTab extends EntryEditorTab { protected final BibDatabaseContext databaseContext; protected final Map editors = new LinkedHashMap<>(); + protected GridPane gridPane; private final boolean isCompressed; private final SuggestionProviders suggestionProviders; private final DialogService dialogService; @@ -59,7 +61,6 @@ abstract class FieldsEditorTab extends EntryEditorTab { private PreviewPanel previewPanel; private final UndoManager undoManager; private Collection fields = new ArrayList<>(); - private GridPane gridPane; public FieldsEditorTab(boolean compressed, BibDatabaseContext databaseContext, @@ -107,36 +108,22 @@ protected void setupPanel(BibEntry entry, boolean compressed) { fields = determineFieldsToShow(entry); - List