diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 8c59721c045..8c9b29c28c2 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -31,9 +31,9 @@ // Install java. // See https://github.com/devcontainers/features/tree/main/src/java#options for details. "ghcr.io/devcontainers/features/java:1": { - "version": "21.0.1-librca", + "version": "23.0.1-tem", "installGradle": false, - "jdkDistro": "librca" + "jdkDistro": "Temurin" } } } diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index c52221619ed..a01fad86a7a 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -9,7 +9,7 @@ Link the issue that will be closed, e.g., "Closes #333". If your PR closes a kop --> - [x] I own the copyright of the code submitted and I licence it under the [MIT license](https://github.com/JabRef/jabref/blob/main/LICENSE) -- [ ] Change in `CHANGELOG.md` described in a way that is understandable for the average user (if applicable) +- [ ] Change in `CHANGELOG.md` described in a way that is understandable for the average user (if change is visible to the user) - [ ] Tests created for changes (if applicable) - [ ] Manually tested changed features in running JabRef (always required) - [ ] Screenshots added in PR description (for UI changes) diff --git a/.github/ghprcomment.yml b/.github/ghprcomment.yml index f42ddc26b33..96ed0801b1f 100644 --- a/.github/ghprcomment.yml +++ b/.github/ghprcomment.yml @@ -2,12 +2,8 @@ message: | Your code currently does not meet [JabRef's code guidelines](https://devdocs.jabref.org/getting-into-the-code/guidelines-for-setting-up-a-local-workspace/intellij-13-code-style.html). We use [Checkstyle](https://checkstyle.sourceforge.io/) to identify issues. - The tool reviewdog already placed comments on GitHub to indicate the places. See the tab "Files" in you PR. Please carefully follow [the setup guide for the codestyle](https://devdocs.jabref.org/getting-into-the-code/guidelines-for-setting-up-a-local-workspace/intellij-13-code-style.html). Afterwards, please [run checkstyle locally](https://devdocs.jabref.org/getting-into-the-code/guidelines-for-setting-up-a-local-workspace/intellij-13-code-style.html#run-checkstyle) and fix the issues. - - - You can check review dog's comments at the tab "Files changed" of your pull request. - jobName: OpenRewrite message: | Your code currently does not meet JabRef's code guidelines. diff --git a/.github/workflows/assign-issue.yml b/.github/workflows/assign-issue.yml index e88f6b762d2..7b1a9ac13bd 100644 --- a/.github/workflows/assign-issue.yml +++ b/.github/workflows/assign-issue.yml @@ -2,13 +2,14 @@ name: Assign Issue on: schedule: - - cron: 0 0 * * * + - cron: 4 12 * * * issue_comment: types: [created] workflow_dispatch: jobs: assign: + if: github.repository_owner == 'JabRef' runs-on: ubuntu-latest permissions: issues: write @@ -18,7 +19,7 @@ jobs: uses: takanome-dev/assign-issue-action@beta with: github_token: '${{ secrets.GITHUB_TOKEN }}' - days_until_unassign: 30 + days_until_unassign: 90 maintainers: koppor, Siedlerchr, ThiloteE, calixtus, HoussemNasri assigned_comment: | 👋 Hey @{{ handle }}, thank you for your interest in this issue! 🎉 diff --git a/.github/workflows/check-links.yml b/.github/workflows/check-links.yml index 67881841c46..3006d476b6c 100644 --- a/.github/workflows/check-links.yml +++ b/.github/workflows/check-links.yml @@ -18,6 +18,7 @@ concurrency: jobs: lychee: + if: github.repository_owner == 'JabRef' runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -31,7 +32,7 @@ jobs: restore-keys: cache-lychee- - name: Link Checker id: lychee - uses: lycheeverse/lychee-action@v2.0.2 + uses: lycheeverse/lychee-action@v2.1.0 with: fail: true args: --accept '200,201,202,203,204,403,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 bd38e11ea16..490443b0460 100644 --- a/.github/workflows/cleanup-pr.yml +++ b/.github/workflows/cleanup-pr.yml @@ -6,6 +6,7 @@ on: jobs: cleanup: + if: github.repository_owner == 'JabRef' runs-on: ubuntu-latest steps: - name: Cancel deployment run @@ -28,7 +29,7 @@ jobs: BUILDJABREFPRIVATEKEY: ${{ secrets.buildJabRefPrivateKey }} - name: Delete folder on builds.jabref.org if: steps.checksecrets.outputs.secretspresent == 'YES' - uses: appleboy/ssh-action@v1.1.0 + uses: appleboy/ssh-action@v1.2.0 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 cd360c1f474..8b26386157d 100644 --- a/.github/workflows/deployment-arm64.yml +++ b/.github/workflows/deployment-arm64.yml @@ -34,6 +34,7 @@ concurrency: jobs: build: + if: github.repository_owner == 'JabRef' strategy: fail-fast: false matrix: @@ -68,16 +69,16 @@ jobs: submodules: 'true' show-progress: 'false' - name: Install GitVersion - uses: gittools/actions/gitversion/setup@v3.0.0 + uses: gittools/actions/gitversion/setup@v3.0.3 with: versionSpec: "5.x" - name: Run GitVersion id: gitversion - uses: gittools/actions/gitversion/execute@v3.0.0 + uses: gittools/actions/gitversion/execute@v3.0.3 - name: Setup JDK uses: actions/setup-java@v4 with: - java-version: 21.0.2 + java-version: 23.0.1 distribution: 'temurin' - name: Clean up keychain run: | diff --git a/.github/workflows/deployment-jdk-ea.yml b/.github/workflows/deployment-jdk-ea.yml index 2bc7390f568..799b6c569c7 100644 --- a/.github/workflows/deployment-jdk-ea.yml +++ b/.github/workflows/deployment-jdk-ea.yml @@ -32,11 +32,12 @@ concurrency: jobs: build: + if: github.repository_owner == 'JabRef' strategy: fail-fast: false matrix: os: [ubuntu-latest, windows-latest, macos-latest, buildjet-8vcpu-ubuntu-2204-arm] - jdk: [22] + jdk: [23] javafx: [24] include: - os: ubuntu-latest @@ -84,12 +85,12 @@ jobs: packages: pigz version: 1.0 - name: Install GitVersion - uses: gittools/actions/gitversion/setup@v3.0.0 + uses: gittools/actions/gitversion/setup@v3.0.3 with: versionSpec: "5.x" - name: Run GitVersion id: gitversion - uses: gittools/actions/gitversion/execute@v3.0.0 + uses: gittools/actions/gitversion/execute@v3.0.3 # JDK - name: 'Set up JDK ${{ matrix.jdk }}' diff --git a/.github/workflows/deployment.yml b/.github/workflows/deployment.yml index e6796073e08..b0411f93902 100644 --- a/.github/workflows/deployment.yml +++ b/.github/workflows/deployment.yml @@ -81,16 +81,16 @@ jobs: packages: pigz version: 1.0 - name: Install GitVersion - uses: gittools/actions/gitversion/setup@v3.0.0 + uses: gittools/actions/gitversion/setup@v3.0.3 with: versionSpec: "5.x" - name: Run GitVersion id: gitversion - uses: gittools/actions/gitversion/execute@v3.0.0 + uses: gittools/actions/gitversion/execute@v3.0.3 - name: Setup JDK uses: actions/setup-java@v4 with: - java-version: 21.0.2 + java-version: 23.0.1 distribution: 'temurin' - name: Setup Gradle uses: gradle/actions/setup-gradle@v4 diff --git a/.github/workflows/gource.yml b/.github/workflows/gource.yml index cfdbc44d6e0..3bccc1f3d14 100644 --- a/.github/workflows/gource.yml +++ b/.github/workflows/gource.yml @@ -15,6 +15,7 @@ concurrency: jobs: action: + if: github.repository_owner == 'JabRef' runs-on: ubuntu-latest steps: - name: 'Checkout' diff --git a/.github/workflows/on-labeled-issue.yml b/.github/workflows/on-labeled-issue.yml index 8839eca84b7..93e65e1cf66 100644 --- a/.github/workflows/on-labeled-issue.yml +++ b/.github/workflows/on-labeled-issue.yml @@ -6,8 +6,36 @@ on: - labeled jobs: + Assigned: + # Triggered when manually assigned the label "📍 Assigned" to trigger the automatic unassignment after 30 days + name: "📍 Assigned" + if: ${{ github.event.label.name == '📍 Assigned' && github.repository_owner == 'JabRef' }} + runs-on: ubuntu-latest + permissions: + issues: write + steps: + - name: Move Issue to "Free to take" Column in "Candidates for University Projects" + uses: m7kvqbe1/github-action-move-issues@feat/skip-if-not-in-project-flag + with: + github-token: ${{ secrets.GH_TOKEN_ACTION_MOVE_ISSUE }} + project-url: "https://github.com/orgs/JabRef/projects/3" + target-labels: "📍 Assigned" + target-column: "Free to take" + ignored-columns: "" + default-column: "Free to take" + skip-if-not-in-project: true + - name: Move Issue to "Free to take" Column in "Good First Issues" + uses: m7kvqbe1/github-action-move-issues@feat/skip-if-not-in-project-flag + with: + github-token: ${{ secrets.GH_TOKEN_ACTION_MOVE_ISSUE }} + project-url: "https://github.com/orgs/JabRef/projects/5" + target-labels: "📍 Assigned" + target-column: "Free to take" + ignored-columns: "" + default-column: "Free to take" + skip-if-not-in-project: true FirstTimeCodeContribution: - if: ${{ github.event.label.name == 'FirstTimeCodeContribution' }} + if: ${{ github.event.label.name == 'FirstTimeCodeContribution' && github.repository_owner == 'JabRef' }} runs-on: ubuntu-latest permissions: issues: write @@ -35,7 +63,7 @@ jobs: with: github-token: ${{ secrets.GH_TOKEN_ACTION_MOVE_ISSUE }} project-url: "https://github.com/orgs/JabRef/projects/3" - target-labels: "📍 Assigned" + target-labels: "FirstTimeCodeContribution" target-column: "Assigned" ignored-columns: "" default-column: "Free to take" @@ -45,14 +73,14 @@ jobs: with: github-token: ${{ secrets.GH_TOKEN_ACTION_MOVE_ISSUE }} project-url: "https://github.com/orgs/JabRef/projects/5" - target-labels: "📍 Assigned" + target-labels: "FirstTimeCodeContribution" target-column: "Assigned" ignored-columns: "" default-column: "Free to take" skip-if-not-in-project: true good-first-issue: name: "good first issue" - if: "${{ github.event.label.name == 'good first issue' }}" + if: "${{ github.event.label.name == 'good first issue' && github.repository_owner == 'JabRef' }}" runs-on: ubuntu-latest steps: - name: "good first issue" @@ -62,7 +90,7 @@ jobs: ISSUE_URL=$(jq --raw-output .issue.html_url "$GITHUB_EVENT_PATH") gh project item-add 5 --owner JabRef --url $ISSUE_URL needs-refinement: - if: github.event.label.name == 'needs-refinement' + if: github.event.label.name == 'needs-refinement' && github.repository_owner == 'JabRef' runs-on: ubuntu-latest steps: - name: needs-refinement @@ -73,7 +101,7 @@ jobs: gh project item-add 15 --owner JabRef --url $ISSUE_URL status-freeze: name: "status: freeze" - if: "${{ github.event.label.name == 'status: freeze' }}" + if: "${{ github.event.label.name == 'status: freeze' && github.repository_owner == 'JabRef' }}" runs-on: ubuntu-latest steps: - name: "status: freeze" @@ -83,7 +111,7 @@ jobs: ISSUE_URL=$(jq --raw-output .issue.html_url "$GITHUB_EVENT_PATH") gh project item-add 9 --owner JabRef --url $ISSUE_URL ui: - if: "${{ github.event.label.name == 'ui' }}" + if: "${{ github.event.label.name == 'ui' && github.repository_owner == 'JabRef' }}" runs-on: ubuntu-latest steps: - name: ui diff --git a/.github/workflows/on-labeled-pr.yml b/.github/workflows/on-labeled-pr.yml index 4ceb3844fe7..2e64d79f1f7 100644 --- a/.github/workflows/on-labeled-pr.yml +++ b/.github/workflows/on-labeled-pr.yml @@ -8,7 +8,7 @@ on: jobs: automerge: name: Auto Merge - if: "${{ github.event.label.name == 'automerge' }}" + if: "${{ github.event.label.name == 'automerge' && github.repository_owner == 'JabRef' }}" runs-on: ubuntu-latest steps: - name: Approve PR diff --git a/.github/workflows/on-unlabeled-issue.yml b/.github/workflows/on-unlabeled-issue.yml index 9c5870066ca..64872289b3d 100644 --- a/.github/workflows/on-unlabeled-issue.yml +++ b/.github/workflows/on-unlabeled-issue.yml @@ -7,7 +7,7 @@ on: jobs: FirstTimeCodeContribution_or_Assigned: - if: ${{ (github.event.label.name == 'FirstTimeCodeContribution') || (github.event.label.name == '📍 Assigned') }} + if: ${{ ((github.event.label.name == 'FirstTimeCodeContribution') || (github.event.label.name == '📍 Assigned')) && github.repository_owner == 'JabRef' }} runs-on: ubuntu-latest permissions: issues: write diff --git a/.github/workflows/pr-comment.yml b/.github/workflows/pr-comment.yml index f8ad213aea3..090adf78352 100644 --- a/.github/workflows/pr-comment.yml +++ b/.github/workflows/pr-comment.yml @@ -14,7 +14,7 @@ on: jobs: comment: # https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#running-a-workflow-based-on-the-conclusion-of-another-workflow - if: ${{ github.event.workflow_run.conclusion == 'failure' }} + if: ${{ github.event.workflow_run.conclusion == 'failure' && (github.repository_owner == 'JabRef') }} runs-on: ubuntu-latest permissions: actions: read @@ -35,24 +35,25 @@ jobs: echo "Read PR number $PR_NUMBER" echo "pr_number=$PR_NUMBER" >> $GITHUB_OUTPUT - uses: actions/checkout@v4 - - name: Determine owner + - name: Is PR from forked? if: ${{ steps.read-pr_number.outputs.pr_number != '' }} - id: owner + id: isCrossRepository run: | - owner=$(gh pr view $pr_number --json headRepositoryOwner --jq '.headRepositoryOwner') - echo "Got owner $owner" - echo owner=$owner >> $GITHUB_OUTPUT + isCrossRepository=$(gh pr view $pr_number --json isCrossRepository --jq '.isCrossRepository') + echo "Got isCrossRepository $isCrossRepository" + echo isCrossRepository=$isCrossRepository >> $GITHUB_OUTPUT env: GH_TOKEN: ${{ github.token }} + pr_number: ${{ steps.read-pr_number.outputs.pr_number }} - name: Checkout - if: ${{ (steps.read-pr_number.outputs.pr_number != '') && (steps.owner.owner != 'JabRef') }} + if: ${{ steps.read-pr_number.outputs.pr_number != '' && steps.isCrossRepository.outputs.isCrossRepository == 'true' }} uses: actions/checkout@v4 with: fetch-depth: '0' show-progress: 'false' token: ${{ secrets.GITHUB_TOKEN }} - name: jbang - if: ${{ (steps.read-pr_number.outputs.pr_number != '') && (steps.owner.owner != 'JabRef') }} + if: ${{ steps.read-pr_number.outputs.pr_number != '' && steps.isCrossRepository.outputs.isCrossRepository == 'true' }} uses: jbangdev/jbang-action@v0.119.0 with: script: ghprcomment@koppor/ghprcomment diff --git a/.github/workflows/tests-fetchers.yml b/.github/workflows/tests-fetchers.yml index 00768f2b03b..e02bfca4916 100644 --- a/.github/workflows/tests-fetchers.yml +++ b/.github/workflows/tests-fetchers.yml @@ -48,7 +48,7 @@ jobs: - name: Set up JDK uses: actions/setup-java@v4 with: - java-version: 21.0.2 + java-version: 23.0.1 distribution: 'temurin' - name: Setup Gradle uses: gradle/actions/setup-gradle@v4 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 90ed601c0fa..78f8bdd5c5f 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -37,7 +37,7 @@ jobs: - name: Set up JDK uses: actions/setup-java@v4 with: - java-version: 21.0.2 + java-version: 23.0.1 distribution: 'temurin' - name: Run checkstyle reporter uses: dbelyaev/action-checkstyle@master @@ -65,7 +65,7 @@ jobs: - name: Set up JDK uses: actions/setup-java@v4 with: - java-version: 21.0.2 + java-version: 23.0.1 distribution: 'temurin' - name: Setup Gradle uses: gradle/actions/setup-gradle@v4 @@ -87,7 +87,7 @@ jobs: - name: Set up JDK uses: actions/setup-java@v4 with: - java-version: 21.0.2 + java-version: 23.0.1 distribution: 'temurin' - name: Setup Gradle uses: gradle/actions/setup-gradle@v4 @@ -109,7 +109,7 @@ jobs: submodules: 'false' show-progress: 'false' - name: markdownlint-cli2-action - uses: DavidAnson/markdownlint-cli2-action@v17 + uses: DavidAnson/markdownlint-cli2-action@v18 with: globs: | *.md @@ -175,7 +175,7 @@ jobs: - name: Set up JDK uses: actions/setup-java@v4 with: - java-version: 21.0.2 + java-version: 23.0.1 distribution: 'temurin' - name: Setup Gradle uses: gradle/actions/setup-gradle@v4 @@ -218,7 +218,7 @@ jobs: - name: Set up JDK uses: actions/setup-java@v4 with: - java-version: 21.0.2 + java-version: 23.0.1 distribution: 'temurin' - name: Setup Gradle uses: gradle/actions/setup-gradle@v4 @@ -259,7 +259,7 @@ jobs: - name: Set up JDK uses: actions/setup-java@v4 with: - java-version: 21.0.2 + java-version: 23.0.1 distribution: 'temurin' - name: Setup Gradle uses: gradle/actions/setup-gradle@v4 @@ -309,7 +309,7 @@ jobs: if: github.ref == 'refs/heads/main' uses: actions/setup-java@v4 with: - java-version: 21.0.2 + java-version: 23.0.1 distribution: 'temurin' - name: Setup Gradle uses: gradle/actions/setup-gradle@v4 @@ -322,7 +322,7 @@ jobs: CI: "true" CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} DBMS: "postgresql" - - uses: codecov/codecov-action@v4 + - uses: codecov/codecov-action@v5 if: (github.ref == 'refs/heads/main') && (steps.checksecrets.outputs.secretspresent == 'YES') with: token: ${{ secrets.CODECOV_TOKEN }} @@ -342,7 +342,7 @@ jobs: - name: Set up JDK uses: actions/setup-java@v4 with: - java-version: 21.0.2 + java-version: 23.0.1 distribution: 'temurin' - name: Setup Gradle uses: gradle/actions/setup-gradle@v4 diff --git a/.github/workflows/update-gradle-wrapper.yml b/.github/workflows/update-gradle-wrapper.yml index 826a211b195..d0081709026 100644 --- a/.github/workflows/update-gradle-wrapper.yml +++ b/.github/workflows/update-gradle-wrapper.yml @@ -7,6 +7,7 @@ on: jobs: update-gradle-wrapper: + if: github.repository_owner == 'JabRef' runs-on: ubuntu-latest steps: @@ -14,7 +15,7 @@ jobs: - name: Setup JDK uses: actions/setup-java@v4 with: - java-version: 21.0.2 + java-version: 23.0.1 distribution: 'temurin' - name: Update Gradle Wrapper diff --git a/.gitpod.Dockerfile b/.gitpod.Dockerfile index 0e80edc3ec8..61c8da1a8e2 100644 --- a/.gitpod.Dockerfile +++ b/.gitpod.Dockerfile @@ -5,4 +5,4 @@ FROM gitpod/workspace-full # All available versions can be listed using sdk ls java # More information about SDKMAN available at https://github.com/sdkman/sdkman-cli#sdkman-cli RUN bash -c ". /home/gitpod/.sdkman/bin/sdkman-init.sh \ - && sdk install java 21-open" + && sdk install java 23-open" diff --git a/.vscode/ltex.dictionary.en-US.txt b/.vscode/ltex.dictionary.en-US.txt index b39ebc75eeb..49996eb095f 100644 --- a/.vscode/ltex.dictionary.en-US.txt +++ b/.vscode/ltex.dictionary.en-US.txt @@ -4,3 +4,4 @@ JabDrive JabRef OpenFastTrace OpenRewrite +Temurin diff --git a/CHANGELOG.md b/CHANGELOG.md index dc911099c31..e33e6ebd9c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,8 +16,11 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv - When a search hits a file, the file icon of that entry is changed accordingly. [#11542](https://github.com/JabRef/jabref/pull/11542) - We added an AI-based chat for entries with linked PDF files. [#11430](https://github.com/JabRef/jabref/pull/11430) - We added an AI-based summarization possibility for entries with linked PDF files. [#11430](https://github.com/JabRef/jabref/pull/11430) +- We added an AI section in JabRef's [preferences](https://docs.jabref.org/ai/preferences). [#11430](https://github.com/JabRef/jabref/pull/11430) +- We added AI providers: OpenAI, Mistral AI, Hugging Face and Google. [#11430](https://github.com/JabRef/jabref/pull/11430), [#11736](https://github.com/JabRef/jabref/pull/11736) +- We added AI providers: [Ollama](https://docs.jabref.org/ai/local-llm#step-by-step-guide-for-ollama) and GPT4All, which add the possibility to use local LLMs privately on your own device. [#11430](https://github.com/JabRef/jabref/pull/11430), [#11870](https://github.com/JabRef/jabref/issues/11870) - We added support for selecting and using CSL Styles in JabRef's OpenOffice/LibreOffice integration for inserting bibliographic and in-text citations into a document. [#2146](https://github.com/JabRef/jabref/issues/2146), [#8893](https://github.com/JabRef/jabref/issues/8893) -- We added Tools > New library based on references in PDF file... to create a new library based on the references section in a PDF file. [#11522](https://github.com/JabRef/jabref/pull/11522) +- We added "Tools > New library based on references in PDF file" ... to create a new library based on the references section in a PDF file. [#11522](https://github.com/JabRef/jabref/pull/11522) - When converting the references section of a paper (PDF file), more than the last page is treated. [#11522](https://github.com/JabRef/jabref/pull/11522) - Added the functionality to invoke offline reference parsing explicitly. [#11565](https://github.com/JabRef/jabref/pull/11565) - The dialog for [adding an entry using reference text](https://docs.jabref.org/collect/newentryfromplaintext) is now filled with the clipboard contents as default. [#11565](https://github.com/JabRef/jabref/pull/11565) @@ -38,8 +41,10 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv - We added an importer for SSRN URLs. [#12021](https://github.com/JabRef/jabref/pull/12021) - We added a compare button to the duplicates in the citation relations tab to open the "Possible duplicate entries" window. [#11192](https://github.com/JabRef/jabref/issues/11192) - We added automatic browser extension install on Windows for Chrome and Edge. [#6076](https://github.com/JabRef/jabref/issues/6076) +- We added support to automatically open a `.bib` file in the current/parent folder if no other library is opened. [koppor#377](https://github.com/koppor/jabref/issues/377) - We added a search bar for filtering keyboard shortcuts. [#11686](https://github.com/JabRef/jabref/issues/11686) - By double clicking on a local citation in the Citation Relations Tab you can now jump the linked entry. [#11955](https://github.com/JabRef/jabref/pull/11955) +- We use the menu icon for background tasks as a progress indicator to visualise an import's progress when dragging and dropping several PDF files into the main table. [#12072](https://github.com/JabRef/jabref/pull/12072) ### Changed @@ -57,6 +62,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv - ⚠️ We relaxed the escaping requirements for [bracketed patterns](https://docs.jabref.org/setup/citationkeypatterns), which are used for the [citaton key generator](https://docs.jabref.org/advanced/entryeditor#autogenerate-citation-key) and [filename and directory patterns](https://docs.jabref.org/finding-sorting-and-cleaning-entries/filelinks#auto-linking-files). One only needs to write `\"` if a quote sign should be escaped. All other escapings are not necessary (and working) any more. [#11967](https://github.com/JabRef/jabref/pull/11967) - When importing BibTeX data starging from on a PDF, the XMP metadata takes precedence over Grobid data. [#11992](https://github.com/JabRef/jabref/pull/11992) - JabRef now uses TLS 1.2 for all HTTPS connections. [#11852](https://github.com/JabRef/jabref/pull/11852) +- We improved the functionality of getting BibTeX data out of PDF files. [#11999](https://github.com/JabRef/jabref/issues/11999) - We improved the display of long messages in the integrity check dialog. [#11619](https://github.com/JabRef/jabref/pull/11619) - We improved the undo/redo buttons in the main toolbar and main menu to be disabled when there is nothing to undo/redo. [#8807](https://github.com/JabRef/jabref/issues/8807) - We improved the DOI detection in PDF imports. [#11782](https://github.com/JabRef/jabref/pull/11782) @@ -65,8 +71,13 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv - We changed instances of 'Search Selected' to 'Search Pre-configured' in Web Search Preferences UI. [#11871](https://github.com/JabRef/jabref/pull/11871) - We added a new CSS style class `main-table` for the main table. [#11881](https://github.com/JabRef/jabref/pull/11881) - When renaming a file, the old extension is now used if there is none provided in the new name. [#11903](https://github.com/JabRef/jabref/issues/11903) -- We changed the name of the library-based file directory from 'General File Directory' to 'Library-specific File Directory' per issue [#571](https://github.com/koppor/jabref/issues/571) +- When importing a file using "Find Unlinked Files", when one or more file directories are available, the file path will be relativized where possible [koppor#549](https://github.com/koppor/jabref/issues/549) +- We added minimum window sizing for windows dedicated to creating new entries [#11944](https://github.com/JabRef/jabref/issues/11944) +- We changed the name of the library-based file directory from 'General File Directory' to 'Library-specific File Directory' per issue. [#571](https://github.com/koppor/jabref/issues/571) +- We changed the defualt [unwanted charachters](https://docs.jabref.org/setup/citationkeypatterns#removing-unwanted-characters) in the citation key generator and allow a dash (`-`) and colon (`:`) being part of a citation key. [#12144](https://github.com/JabRef/jabref/pull/12144) - The CitationKey column is now a default shown column for the entry table. [#10510](https://github.com/JabRef/jabref/issues/10510) +- We disabled the actions "Open Terminal here" and "Reveal in file explorer" for unsaved libraries. [#11920](https://github.com/JabRef/jabref/issues/11920) +- JabRef now opens the corresponding directory in the library properties when "Browse" is clicked. [#12223](https://github.com/JabRef/jabref/pull/12223) ### Fixed @@ -100,10 +111,14 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv - We fixed an issue where recently opened files were not displayed in the main menu properly. [#9042](https://github.com/JabRef/jabref/issues/9042) - We fixed an issue where the DOI lookup would show an error when a DOI was found for an entry. [#11850](https://github.com/JabRef/jabref/issues/11850) - We fixed an issue where Tab cannot be used to jump to next field in some single-line fields. [#11785](https://github.com/JabRef/jabref/issues/11785) +- We fixed an issue where the "Do not ask again" checkbox was not working, when asking for permission to use Grobid [koppor#556](https://github.com/koppor/jabref/issues/566). - We fixed an issue where we display warning message for moving attached open files. [#10121](https://github.com/JabRef/jabref/issues/10121) - We fixed an issue where it was not possible to select selecting content of other user's comments.[#11106](https://github.com/JabRef/jabref/issues/11106) - We fixed an issue where web search preferences "Custom API key" table modifications not discarded. [#11925](https://github.com/JabRef/jabref/issues/11925) +- We fixed an issue when opening attached files in [extra file columns](https://docs.jabref.org/finding-sorting-and-cleaning-entries/filelinks#adding-additional-columns-to-entry-table-for-file-types). [#12005](https://github.com/JabRef/jabref/issues/12005) - We fixed an issue where trying to open a library from a failed mounted directory on Mac would cause an error. [#10548](https://github.com/JabRef/jabref/issues/10548) +- We fixed an issue when the preview was out of sync. [#9172](https://github.com/JabRef/jabref/issues/9172) +- We fixed an issue where identifier paste couldn't work with Unicode REPLACEMENT CHARACTER. [#11986](https://github.com/JabRef/jabref/issues/11986) ### Removed diff --git a/build.gradle b/build.gradle index 116feba772b..03dbafbff48 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ import org.jabref.build.xjc.XjcTask plugins { id 'application' - id 'com.github.andygoossens.modernizer' version '1.9.3' + id 'com.github.andygoossens.modernizer' version '1.10.0' id 'me.champeau.jmh' version '0.7.2' @@ -15,7 +15,7 @@ plugins { id 'org.openjfx.javafxplugin' version '0.1.0' - id 'org.beryx.jlink' version '3.0.1' + id 'org.beryx.jlink' version '3.1.0-rc-1' // nicer test outputs during running and completion // Homepage: https://github.com/radarsh/gradle-test-logger-plugin @@ -29,7 +29,7 @@ plugins { id 'idea' - id 'org.openrewrite.rewrite' version '6.25.1' + id 'org.openrewrite.rewrite' version '6.27.0' id "org.itsallcode.openfasttrace" version "3.0.1" } @@ -45,8 +45,8 @@ group = "org.jabref" version = project.findProperty('projVersion') ?: '100.0.0' java { - sourceCompatibility = JavaVersion.VERSION_21 - targetCompatibility = JavaVersion.VERSION_21 + sourceCompatibility = JavaVersion.VERSION_23 + targetCompatibility = JavaVersion.VERSION_23 // Workaround needed for Eclipse, probably because of https://github.com/gradle/gradle/issues/16922 // Should be removed as soon as Gradle 7.0.1 is released ( https://github.com/gradle/gradle/issues/16922#issuecomment-828217060 ) @@ -56,8 +56,12 @@ java { // If this is updated, also update // - .gitpod.Dockerfile // - .devcontainer/devcontainer.json#L34 and - // - .github/workflows/deployment-jdk-ea.yml#L53 - languageVersion = JavaLanguageVersion.of(21) + // - .github/workflows/deployment*.yml + // - .github/workflows/tests*.yml + // - .github/workflows/update-gradle-wrapper.yml + // - docs/getting-into-the-code/guidelines-for-setting-up-a-local-workspace/intellij-12-build.md + // - build.gradle -> jacoco -> toolVersion (because JaCoCo does not support newest JDK out of the box. Check versions at https://www.jacoco.org/jacoco/trunk/doc/changes.html) + languageVersion = JavaLanguageVersion.of(23) // See https://docs.gradle.org/current/javadoc/org/gradle/jvm/toolchain/JvmVendorSpec.html for a full list // vendor = JvmVendorSpec.AMAZON } @@ -126,6 +130,7 @@ sourceSets { repositories { mavenCentral() maven { url 'https://s01.oss.sonatype.org/content/repositories/snapshots/' } + maven { url 'https://oss.sonatype.org/content/repositories/snapshots' } maven { url 'https://jitpack.io' } maven { url 'https://oss.sonatype.org/content/groups/public' } @@ -147,7 +152,7 @@ javafx { } jacoco { - toolVersion = "0.8.10" + toolVersion = "0.8.13-SNAPSHOT" } dependencies { @@ -173,14 +178,14 @@ dependencies { implementation "org.apache.lucene:lucene-analysis-common:$luceneVersion" implementation "org.apache.lucene:lucene-highlighter:$luceneVersion" - implementation group: 'org.apache.commons', name: 'commons-csv', version: '1.11.0' + implementation group: 'org.apache.commons', name: 'commons-csv', version: '1.12.0' implementation group: 'org.apache.commons', name: 'commons-lang3', version: '3.17.0' implementation group: 'org.apache.commons', name: 'commons-text', version: '1.12.0' implementation 'commons-logging:commons-logging:1.3.4' implementation 'com.h2database:h2-mvstore:2.3.232' // required for reading write-protected PDFs - see https://github.com/JabRef/jabref/pull/942#issuecomment-209252635 - implementation 'org.bouncycastle:bcprov-jdk18on:1.78.1' + implementation 'org.bouncycastle:bcprov-jdk18on:1.79' implementation 'commons-cli:commons-cli:1.9.0' @@ -200,8 +205,8 @@ dependencies { implementation group: 'org.eclipse.jgit', name: 'org.eclipse.jgit', version: '7.0.0.202409031743-r' - implementation group: 'com.fasterxml.jackson.dataformat', name: 'jackson-dataformat-yaml', version: '2.17.2' - implementation group: 'com.fasterxml.jackson.datatype', name: 'jackson-datatype-jsr310', version: '2.18.0' + implementation group: 'com.fasterxml.jackson.dataformat', name: 'jackson-dataformat-yaml', version: '2.18.1' + implementation group: 'com.fasterxml.jackson.datatype', name: 'jackson-datatype-jsr310', version: '2.18.1' implementation 'com.fasterxml:aalto-xml:1.3.3' @@ -238,7 +243,7 @@ dependencies { } implementation 'org.fxmisc.flowless:flowless:0.7.3' implementation 'org.fxmisc.richtext:richtextfx:0.11.3' - implementation (group: 'com.dlsc.gemsfx', name: 'gemsfx', version: '2.60.0') { + implementation (group: 'com.dlsc.gemsfx', name: 'gemsfx', version: '2.64.0') { exclude module: 'javax.inject' // Split package, use only jakarta.inject exclude module: 'commons-lang3' exclude group: 'org.apache.commons.validator' @@ -260,8 +265,8 @@ dependencies { // region HTTP clients implementation 'org.jsoup:jsoup:1.18.1' - implementation 'com.konghq:unirest-java-core:4.4.4' - implementation 'com.konghq:unirest-modules-gson:4.4.4' + implementation 'com.konghq:unirest-java-core:4.4.5' + implementation 'com.konghq:unirest-modules-gson:4.4.5' implementation 'org.apache.httpcomponents.client5:httpclient5:5.4' // endregion @@ -273,7 +278,7 @@ dependencies { // route all requests to java.util.logging to SLF4J (which in turn routes to tinylog) implementation 'org.slf4j:jul-to-slf4j:2.0.16' // route all requests to log4j to SLF4J - implementation 'org.apache.logging.log4j:log4j-to-slf4j:2.24.0' + implementation 'org.apache.logging.log4j:log4j-to-slf4j:2.24.2' implementation('de.undercouch:citeproc-java:3.1.0') { exclude group: 'org.antlr' @@ -312,7 +317,7 @@ dependencies { // 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.9' - testImplementation 'org.glassfish.jersey.test-framework.providers:jersey-test-framework-provider-grizzly2:3.1.8' + testImplementation 'org.glassfish.jersey.test-framework.providers:jersey-test-framework-provider-grizzly2:3.1.9' // Allow objects "magically" to be mapped to JSON using GSON // implementation 'org.glassfish.jersey.media:jersey-media-json-gson:3.1.1' @@ -329,29 +334,29 @@ dependencies { implementation 'org.yaml:snakeyaml:2.3' // region AI - implementation 'dev.langchain4j:langchain4j:0.35.0' + implementation 'dev.langchain4j:langchain4j:0.36.0' // Even though we use jvm-openai for LLM connection, we still need this package for tokenization. - implementation('dev.langchain4j:langchain4j-open-ai:0.35.0') { + implementation('dev.langchain4j:langchain4j-open-ai:0.36.0') { exclude group: 'com.squareup.okhttp3' exclude group: 'com.squareup.retrofit2', module: 'retrofit' exclude group: 'org.jetbrains.kotlin' } - implementation('dev.langchain4j:langchain4j-mistral-ai:0.35.0') { + implementation('dev.langchain4j:langchain4j-mistral-ai:0.36.2') { exclude group: 'com.squareup.okhttp3' exclude group: 'com.squareup.retrofit2', module: 'retrofit' exclude group: 'org.jetbrains.kotlin' } - implementation('dev.langchain4j:langchain4j-google-ai-gemini:0.35.0') { + implementation('dev.langchain4j:langchain4j-google-ai-gemini:0.36.0') { exclude group: 'com.squareup.okhttp3' exclude group: 'com.squareup.retrofit2', module: 'retrofit' } - implementation('dev.langchain4j:langchain4j-hugging-face:0.35.0') { + implementation('dev.langchain4j:langchain4j-hugging-face:0.36.0') { exclude group: 'com.squareup.okhttp3' exclude group: 'com.squareup.retrofit2', module: 'retrofit' exclude group: 'org.jetbrains.kotlin' } - + implementation 'org.apache.velocity:velocity-engine-core:2.4.1' implementation platform('ai.djl:bom:0.30.0') implementation 'ai.djl:api' implementation 'ai.djl.huggingface:tokenizers' @@ -362,20 +367,20 @@ dependencies { exclude group: 'org.jetbrains.kotlin', module: 'kotlin-stdlib-jdk8' } // GemxFX also (transitively) depends on kotlin - implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.0.20' + implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.0.21' // endregion - implementation 'commons-io:commons-io:2.17.0' + implementation 'commons-io:commons-io:2.18.0' // Even if "compileOnly" is used, IntelliJ always adds to module-info.java. To avoid issues during committing, we use "implementation" instead of "compileOnly" implementation 'io.github.adr:e-adr:2.0.0-SNAPSHOT' - implementation 'io.zonky.test:embedded-postgres:2.0.7' + implementation 'io.zonky.test:embedded-postgres:2.1.0' implementation enforcedPlatform('io.zonky.test.postgres:embedded-postgres-binaries-bom:17.0.0') testImplementation 'io.github.classgraph:classgraph:4.8.177' - testImplementation 'org.junit.jupiter:junit-jupiter:5.11.0' - testImplementation 'org.junit.platform:junit-platform-launcher:1.10.3' + testImplementation 'org.junit.jupiter:junit-jupiter:5.11.3' + testImplementation 'org.junit.platform:junit-platform-launcher:1.11.3' testImplementation 'org.mockito:mockito-core:5.14.2' testImplementation 'org.xmlunit:xmlunit-core:2.10.0' @@ -385,11 +390,12 @@ dependencies { testImplementation "org.testfx:testfx-core:4.0.16-alpha" testImplementation "org.testfx:testfx-junit5:4.0.16-alpha" testImplementation "org.hamcrest:hamcrest-library:3.0" + testImplementation "com.github.javaparser:javaparser-symbol-solver-core:3.26.2" // recommended by https://github.com/wiremock/wiremock/issues/2149#issuecomment-1835775954 - testImplementation 'org.wiremock:wiremock-standalone:3.3.1' + testImplementation 'org.wiremock:wiremock-standalone:3.9.2' - checkstyle 'com.puppycrawl.tools:checkstyle:10.18.2' + checkstyle 'com.puppycrawl.tools:checkstyle:10.20.1' // xjc needs the runtime as well for the ant task, otherwise it fails xjc group: 'org.glassfish.jaxb', name: 'jaxb-xjc', version: '3.0.2' xjc group: 'org.glassfish.jaxb', name: 'jaxb-runtime', version: '3.0.2' diff --git a/buildres/abbrv.jabref.org b/buildres/abbrv.jabref.org index 50edbd56165..0fdf99147a8 160000 --- a/buildres/abbrv.jabref.org +++ b/buildres/abbrv.jabref.org @@ -1 +1 @@ -Subproject commit 50edbd56165d8efa27be7d06ff1df5d20aa8d2c7 +Subproject commit 0fdf99147a8a5fc8ae7ccd79ad4e0029e736e4a3 diff --git a/config/IntelliJ Code Style.xml b/config/IntelliJ Code Style.xml index 6a03e104d9d..51aa18498d7 100644 --- a/config/IntelliJ Code Style.xml +++ b/config/IntelliJ Code Style.xml @@ -293,4 +293,4 @@ - \ No newline at end of file + diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml index b40e6b25d37..312e52aca6e 100644 --- a/config/checkstyle/checkstyle.xml +++ b/config/checkstyle/checkstyle.xml @@ -33,6 +33,13 @@ + + + + + + + diff --git a/config/checkstyle/checkstyle_reviewdog.xml b/config/checkstyle/checkstyle_reviewdog.xml index 510baff905f..13f380bad61 100644 --- a/config/checkstyle/checkstyle_reviewdog.xml +++ b/config/checkstyle/checkstyle_reviewdog.xml @@ -33,6 +33,13 @@ + + + + + + + diff --git a/docs/code-howtos/localization.md b/docs/code-howtos/localization.md index 568ad369529..7ef71486d2f 100644 --- a/docs/code-howtos/localization.md +++ b/docs/code-howtos/localization.md @@ -45,8 +45,9 @@ To write a localized string in FXML file, prepend it with `%`, like in this code ## General hints * Use the String you want to localize directly, do not use members or local variables: `Localization.lang("Translate me");` instead of `Localization.lang(someVariable)` (possibly in the form `someVariable = Localization.lang("Translate me")` -* Use `%x`-variables where appropriate: `Localization.lang("Exported %0 entries.", number)` instead of `Localization.lang("Exported ") + number + Localization.lang(" entries.");` +* Use `%x`-variables where appropriate: `Localization.lang("Exported %0 entry(s).", number)` instead of `Localization.lang("Exported ") + number + Localization.lang(" entry(s).");` * Use a full stop/period (".") to end full sentences +* For pluralization, use a combined form. E.g., `Localization.lang("checked %0 entry(s)")`. ## Checking for correctness diff --git a/docs/decisions/0039-use-apache-velocity-as-template-engine.md b/docs/decisions/0039-use-apache-velocity-as-template-engine.md index 5e6ca8d1072..46435540836 100644 --- a/docs/decisions/0039-use-apache-velocity-as-template-engine.md +++ b/docs/decisions/0039-use-apache-velocity-as-template-engine.md @@ -54,6 +54,7 @@ ${CanonicalBibEntry.getCanonicalRepresentation($entry)} * Good, because it has simple syntax, and it is designed for simple template workflows. * Good, because it has a stable syntax ([source](https://stackoverflow.com/a/1984458/10037342)). * Bad, because it is in maintenance mode. +* Bad, because [removed from Spring 5.0.1](https://www.baeldung.com/spring-template-engines#other-template-engines) ### Apache FreeMarker @@ -107,4 +108,7 @@ Here are the papers you are analyzing: As stated in [the template discussion issue](https://github.com/koppor/jabref/issues/392), we should choose a template engine, and then slowly migrate previous code and templates to the chosen engine. +Other template engines are discussed at , especially [`#other-template-engines`](https://www.baeldung.com/spring-template-engines#other-template-engines). +We did not find any other engine there worth switching to. + diff --git a/docs/decisions/0040-display-front-cover-in-preview-tab.md b/docs/decisions/0040-display-front-cover-in-preview-tab.md new file mode 100644 index 00000000000..48b57d644c3 --- /dev/null +++ b/docs/decisions/0040-display-front-cover-in-preview-tab.md @@ -0,0 +1,61 @@ +--- +parent: Decision Records +nav_order: 40 +--- + +# Display front cover for book citations in the Preview tab + +## Context and Problem Statement + +* Users have requested that the front covers of book citations are displayed in JabRef. +* This is discussed on the [JabRef forum](https://discourse.jabref.org/t/display-cover-images-for-books/3647) and raised as a [feature request](https://github.com/JabRef/jabref/issues/10120). +* We need to decide where the book cover should be placed. + +## Decision Drivers + +* It should not be obtrusive or distracting since the main use of JabRef is for articles not books. +* It should not obstruct the view of existing GUI components, specifically the MainTable or the information in the EntryEditor's tabs. + +## Considered Options + +Place the book cover in: + +1. The existing SidePane +2. A new SidePane +3. The Preview panel of the EntryEditor +4. A SplitPane next to the MainTable + +## Decision Outcome + +Chosen option: "The PreviewPanel of the EntryEditor". + +## Pros and Cons of the Options + +### Existing SidePane + +![Image: Placement in SidePane](0040-placement-in-sidepane.png) + +* Good, because it would be unobtrusive +* Bad, because it would crowd other panels in the SidePane +* Bad, because changing the size of the SidePane would affect both the MainTable and the EntryEditor. + +### New right-sided SidePane + +![Image: Placement in the new right-sided SidePane](0040-placement-in-new-sidepane.png) + +* Good, if integrated together with entry preview because it would make it easier to view a citation's preview. +* Bad, because an extra SidePane would make the interface overly complex. + +### The PreviewPanel of the EntryEditor + +![Image: Placement in the Preview Panel](0040-placement-in-preview-panel.png) + +* Good, because it would not be obtrusive or distracting. +* Bad, if the Entry Editor is closed, users will have to open the Entry Editor and navigate to the "Preview" or "Required fields" tab to see the cover. + +### SplitPane next to the MainTable + +![Image: Placement next to the Main Table](0040-placement-in-maintable.png) + +* Good, because changing the size of this SplitPane would [only affect the MainTable](https://github.com/user-attachments/assets/4e458099-ca5c-41bc-a33b-ce4240d7df82). +* Bad, because it would obstruct some columns in the MainTable. diff --git a/docs/decisions/0040-placement-in-maintable.png b/docs/decisions/0040-placement-in-maintable.png new file mode 100644 index 00000000000..37704645e13 Binary files /dev/null and b/docs/decisions/0040-placement-in-maintable.png differ diff --git a/docs/decisions/0040-placement-in-new-sidepane.png b/docs/decisions/0040-placement-in-new-sidepane.png new file mode 100644 index 00000000000..b0a1c42ff20 Binary files /dev/null and b/docs/decisions/0040-placement-in-new-sidepane.png differ diff --git a/docs/decisions/0040-placement-in-preview-panel.png b/docs/decisions/0040-placement-in-preview-panel.png new file mode 100644 index 00000000000..7ddebfdf40b Binary files /dev/null and b/docs/decisions/0040-placement-in-preview-panel.png differ diff --git a/docs/decisions/0040-placement-in-sidepane.png b/docs/decisions/0040-placement-in-sidepane.png new file mode 100644 index 00000000000..a8829abe23a Binary files /dev/null and b/docs/decisions/0040-placement-in-sidepane.png differ diff --git a/docs/decisions/0041-use-one-form-for-singular-and-plural.md b/docs/decisions/0041-use-one-form-for-singular-and-plural.md new file mode 100644 index 00000000000..94c53ca6239 --- /dev/null +++ b/docs/decisions/0041-use-one-form-for-singular-and-plural.md @@ -0,0 +1,95 @@ +--- +nav_order: 41 +parent: Decision Records +--- + + +# Use one language string for pluralization localization + +## Context and Problem Statement + +For user-facing messages, sometimes, it needs to be counted: E.g., 1 entry updated, 2 entries updated, etc. + +In some languages, there is not only "one" and "more than one", but other forms: + +* zero → “لم نزرع أي شجرة حتى الآن” +* one → “لقد زرعنا شجرة ١ حتى الآن” +* two → “لقد زرعنا شجرتين ٢ حتى الآن” +* few → “لقد زرعنا ٣ شجرات حتى الآن” +* many → “لقد زرعنا ١١ شجرة حتى الآن” +* other → “لقد زرعنا ١٠٠ شجرة حتى الآن” + +(Example is from [Pluralization: A Guide to Localizing Plurals](https://phrase.com/blog/posts/pluralization/)) + +How to localize pluralization? + +## Decision Drivers + +* Good English language +* Good localization to other languages + +## Considered Options + +* Use one language string for pluralization (no explicit pluralization) +* Use singular and plural +* Handling of multiple forms + +## Decision Outcome + +Chosen option: "Use one form only (no explicit pluralization)", because it is the most easiest to handle in the code. + +## Pros and Cons of the Options + +### Use one language string for pluralization (no explicit pluralization) + +Example: + +- `Imported 0 entry(s)` +- `Imported 1 entry(s)` +- `Imported 12 entry(s)` + +There are sub alternatives here: + +* `Imported %0 entry(ies)`. +* `Number of entries imported: %0` (always use "other" plural form) + +These arguments are for the general case of using a single text for all kinds of numbers: + +* Good, because easy to handle in the code +* Bad, because reads strange in English UI + +### Use singular and plural + +Example: + +- `Imported 0 entries` +- `Imported 1 entry` +- `Imported 12 entries` + +* Good, because reads well in English +* Bad, because all localizations need to take an `if` check for the count +* Bad, because Arabic not localized properly + +### Handling of multiple forms + +Example: + +- `Imported 0 entries` +- `Imported 1 entry` +- `Imported 12 entries` + +Code: `Localization.lang("Imported %0 entries", "Imported %0 entry.", "Imported %0 entries.", "Imported %0 entries.", "Imported %0 entries.", "Imported %0 entries.", count)` + +* Good, because reads well in English +* Bad, because sophisticated localization handling is required +* Bad, because no Java library for handling pluralization is known +* Bad, because Arabic not localized properly + +## More Information + +- [Language Plural Rules](https://www.unicode.org/cldr/charts/43/supplemental/language_plural_rules.html) +- [Unicode CLDR Project's Plural Rules](https://cldr.unicode.org/index/cldr-spec/plural-rules) +- [Implementation in Mozilla Firefox](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/PluralRules) +- [SX discussion on plural forms](https://english.stackexchange.com/a/90283/66058) + + diff --git a/docs/getting-into-the-code/guidelines-for-setting-up-a-local-workspace/intellij-11-code-into-ide.md b/docs/getting-into-the-code/guidelines-for-setting-up-a-local-workspace/intellij-11-code-into-ide.md index b818be103d7..9e150ae1a74 100644 --- a/docs/getting-into-the-code/guidelines-for-setting-up-a-local-workspace/intellij-11-code-into-ide.md +++ b/docs/getting-into-the-code/guidelines-for-setting-up-a-local-workspace/intellij-11-code-into-ide.md @@ -62,4 +62,19 @@ This will make these directories "Unregistered roots:", which is fine. ![Directory Mappings having three repositories unregsitered](intellij-directory-mappings-unregistered-roots.png) {% endfigure %} +## Ensure that committing with other tools work + +Open a "git bash". +On Windows, navigate to `C:\git-repositories\JabRef`. +Open the context menu of the file explorer (using the right mouse button), choose "Open Git Bash here". + +Execute following command: + +```shell +git update-index --assume-unchanged buildres/abbrv.jabref.org src/main/resources/csl-styles src/main/resources/csl-locales +``` + +{: .tip } +If you do not see the context menu, re-install git following the steps given at [StackOverflow](https://stackoverflow.com/a/50667280/873282). + diff --git a/docs/getting-into-the-code/guidelines-for-setting-up-a-local-workspace/intellij-12-build.md b/docs/getting-into-the-code/guidelines-for-setting-up-a-local-workspace/intellij-12-build.md index b67c0f6fb99..85fd85cf130 100644 --- a/docs/getting-into-the-code/guidelines-for-setting-up-a-local-workspace/intellij-12-build.md +++ b/docs/getting-into-the-code/guidelines-for-setting-up-a-local-workspace/intellij-12-build.md @@ -6,24 +6,24 @@ nav_order: 12 # Step 2: Set up the build system: JDK and Gradle -## Ensure that JDK 21 is available to IntelliJ +## Ensure that JDK 23 is available to IntelliJ -Ensure you have a Java 21 SDK configured by navigating to **File > Project Structure... > Platform Settings > SDKs**.
+Ensure you have a Java 23 SDK configured by navigating to **File > Project Structure... > Platform Settings > SDKs**.
**Note:** In some MacBooks, `Project Structure` can be found at the "IntelliJ" button of the app menu instead of at "File". -{% figure caption:"JDKs 11, 14, and 15 shown in available SDKs. JDK 21 is missing." %} +{% figure caption:"JDKs 11, 14, and 15 shown in available SDKs. JDK 23 is missing." %} ![Plattform Settings - SDKs](intellij-choose-jdk-adoptopenjdk-on-windows-project-settings.png) {% endfigure %} -If there is another JDK than JDK 21 selected, click on the plus button and choose "Download JDK..." +If there is another JDK than JDK 23 selected, click on the plus button and choose "Download JDK..." {% figure caption:"Download JDK..." %} ![Plattform Settings - SDKs - plus button - Download JDK...](guidelines-select-download-jdk.png) {% endfigure %} -Select JDK version 21 and then Eclipse Temurin. +Select JDK version 23 and then Eclipse Temurin. -{% figure caption:"Example for JDK 21 - Choose Eclipse Temurin" %} +{% figure caption:"Example for JDK 23 - Choose Eclipse Temurin" %} ![Download Eclipse Temurin](guidelines-intellij-select-jdk-eclipse-temurin.png) {% endfigure %} @@ -33,10 +33,10 @@ After clicking "Download", IntelliJ installs Eclipse Temurin: ![IntelliJ installs Eclipse Temurin](guidelines-intellij-installs-temurin.png) {% endfigure %} -Navigate to **Project Settings > Project** and ensure that the projects' SDK is Java 21. +Navigate to **Project Settings > Project** and ensure that the projects' SDK is Java 23. -{% figure caption:"Project SDK is pinned to the downloaded SDK (showing JDK 21 as example)" %} -![Project SDK is JDK 21](guidelines-intellij-project-settings-jdk.png) +{% figure caption:"Project SDK is pinned to the downloaded SDK (showing JDK 23 as example)" %} +![Project SDK is JDK 23](guidelines-intellij-project-settings-jdk.png) {% endfigure %} Click "OK" to store the changes. @@ -44,9 +44,9 @@ Click "OK" to store the changes. ## Ensure correct JDK setting for Gradle Navigate to **File > Settings... > Build, Execution, Deployment > Build Tools > Gradle** and select the "Project SDK" as the Gradle JVM at the bottom. -If that does not exist, just select JDK 21. +If that does not exist, just select JDK 23. -{% figure caption:"Gradle JVM is project SDK (showing "Projekt SDK temurin-21" as example)" %} +{% figure caption:"Gradle JVM is project SDK (showing "Projekt SDK temurin-23" as example)" %} ![Gradle JVM is project SDK](guidelines-intellij-settings-gradle-gradlejvm-is-projectjvm.png) {% endfigure %} @@ -128,7 +128,7 @@ After that a new entry called "jabref \[run]" appears in the run configurations. Now you can also select "jabref \[run]" and either run or debug the application from within IntelliJ. {: .note } -You can run any other development task in a similar way. +You can run any other development task similarly. ## Using IntelliJ's internal build system for tests diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index fb602ee2af0..eb1a55be0e1 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=31c55713e40233a8303827ceb42ca48a47267a0ad4bab9177123121e71524c26 -distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip +distributionSha256Sum=f397b287023acdba1e9f6fc5ea72d22dd63669d59ed4a289a29b1a76eee151c6 +distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 49906ad2625..f0151b8988e 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -160,6 +160,7 @@ uses ai.djl.repository.RepositoryFactory; uses ai.djl.repository.zoo.ZooProvider; uses dev.langchain4j.spi.prompt.PromptTemplateFactory; + requires velocity.engine.core; // endregion // region: Lucene diff --git a/src/main/java/org/jabref/cli/ArgumentProcessor.java b/src/main/java/org/jabref/cli/ArgumentProcessor.java index 3df190f9816..bc7cb7d3ed7 100644 --- a/src/main/java/org/jabref/cli/ArgumentProcessor.java +++ b/src/main/java/org/jabref/cli/ArgumentProcessor.java @@ -99,7 +99,7 @@ public ArgumentProcessor(String[] args, } /** - * Will open a file (like {@link #importFile(String)}, but will also request JabRef to focus on this database. + * Will open a file (like {@link #importFile(String)}, but will also request JabRef to focus on this library. * * @return ParserResult with setToOpenTab(true) */ diff --git a/src/main/java/org/jabref/gui/Base.css b/src/main/java/org/jabref/gui/Base.css index 15b3b920a3e..6c32faf0094 100644 --- a/src/main/java/org/jabref/gui/Base.css +++ b/src/main/java/org/jabref/gui/Base.css @@ -1,4 +1,5 @@ .root { + /* Note that counting for odd/even starts at 0, thus the first displayed row is even */ -jr-row-odd-background: -fx-control-inner-background-alt; -jr-row-even-background: -fx-control-inner-background; /* @@ -1589,13 +1590,12 @@ We want to have a look that matches our icons in the tool-bar */ .main-table .table-row-cell:matching-search-and-groups:odd { -fx-background-color: -jr-match-1-odd; } -.main-table .table-row-cell:matching-search-and-groups:odd:selected, -.main-table .table-row-cell:matching-search-and-groups:odd:focused, -.main-table .table-row-cell:matching-search-and-groups:odd:focused:hover, +.main-table .table-row-cell:matching-search-and-groups:selected, +.main-table .table-row-cell:matching-search-and-groups:focused, .main-table .table-row-cell:matching-search-and-groups:focused:hover { -fx-background-color: -jr-selected; } -.main-table .table-row-cell:matching-search-and-groups:odd:hover { +.main-table .table-row-cell:matching-search-and-groups:hover { -fx-background-color: -jr-hover; } @@ -1626,13 +1626,13 @@ We want to have a look that matches our icons in the tool-bar */ .main-table .table-row-cell:matching-search-not-groups:odd { -fx-background-color: -jr-match-2-odd; } -.main-table .table-row-cell:matching-search-not-groups:odd:selected, -.main-table .table-row-cell:matching-search-not-groups:odd:focused, -.main-table .table-row-cell:matching-search-not-groups:odd:focused:hover, -.main-table .table-row-cell:matching-search-not-groups:focused:hover { +.main-table .table-row-cell:matching-search-not-groups:selected, +.main-table .table-row-cell:matching-search-not-groups:focused, +.main-table .table-row-cell:matching-search-not-groups:focused:hover, +.main-table .table-row-cell:matching-search-not-groups:hover { -fx-background-color: -jr-selected; } -.main-table .table-row-cell:matching-search-not-groups:odd:hover { +.main-table .table-row-cell:matching-search-not-groups:hover { -fx-background-color: -jr-hover; } @@ -1663,13 +1663,13 @@ We want to have a look that matches our icons in the tool-bar */ .main-table .table-row-cell:matching-groups-not-search:odd { -fx-background-color: -jr-match-3-odd; } -.main-table .table-row-cell:matching-groups-not-search:odd:selected, -.main-table .table-row-cell:matching-groups-not-search:odd:focused, -.main-table .table-row-cell:matching-groups-not-search:odd:focused:hover, -.main-table .table-row-cell:matching-groups-not-search:focused:hover { +.main-table .table-row-cell:matching-groups-not-search:selected, +.main-table .table-row-cell:matching-groups-not-search:focused, +.main-table .table-row-cell:matching-groups-not-search:focused:hover, +.main-table .table-row-cell:matching-groups-not-search:hover { -fx-background-color: -jr-selected; } -.main-table .table-row-cell:matching-groups-not-search:odd:hover { +.main-table .table-row-cell:matching-groups-not-search:hover { -fx-background-color: -jr-hover; } @@ -1700,13 +1700,12 @@ We want to have a look that matches our icons in the tool-bar */ .main-table .table-row-cell:not-matching-search-and-groups:odd { -fx-background-color: -jr-match-4-odd; } -.main-table .table-row-cell:not-matching-search-and-groups:odd:selected, -.main-table .table-row-cell:not-matching-search-and-groups:odd:focused, -.main-table .table-row-cell:not-matching-search-and-groups:odd:focused:hover, +.main-table .table-row-cell:not-matching-search-and-groups:selected, +.main-table .table-row-cell:not-matching-search-and-groups:focused, .main-table .table-row-cell:not-matching-search-and-groups:focused:hover { -fx-background-color: -jr-selected; } -.main-table .table-row-cell:not-matching-search-and-groups:odd:hover { +.main-table .table-row-cell:not-matching-search-and-groups:hover { -fx-background-color: -jr-hover; } diff --git a/src/main/java/org/jabref/gui/LibraryTab.java b/src/main/java/org/jabref/gui/LibraryTab.java index 190ab74b520..f93ef8c6101 100644 --- a/src/main/java/org/jabref/gui/LibraryTab.java +++ b/src/main/java/org/jabref/gui/LibraryTab.java @@ -482,7 +482,6 @@ public void editEntryAndFocusField(BibEntry entry, Field field) { Platform.runLater(() -> { // Focus field and entry in main table (async to give entry editor time to load) entryEditor.setFocusToField(field); - clearAndSelect(entry); }); } @@ -567,11 +566,15 @@ public EntryEditor getEntryEditor() { } /** - * Sets the entry editor as the bottom component in the split pane. If an entry editor already was shown, makes sure that the divider doesn't move. Updates the mode to SHOWING_EDITOR. Then shows the given entry. + * Sets the entry editor as the bottom component in the split pane. If an entry editor already was shown, makes sure that the divider doesn't move. Updates the mode to {@link PanelMode#MAIN_TABLE_AND_ENTRY_EDITOR}. + * Then shows the given entry. + * + * Additionally, selects the entry in the main table - so that the selected entry in the main table always corresponds to the edited entry. * * @param entry The entry to edit. */ public void showAndEdit(BibEntry entry) { + this.clearAndSelect(entry); if (!splitPane.getItems().contains(entryEditor)) { splitPane.getItems().addLast(entryEditor); mode = PanelMode.MAIN_TABLE_AND_ENTRY_EDITOR; @@ -586,9 +589,6 @@ public void showAndEdit(BibEntry entry) { entryEditor.requestFocus(); } - /** - * Removes the bottom component. - */ public void closeBottomPane() { mode = PanelMode.MAIN_TABLE; splitPane.getItems().remove(entryEditor); @@ -628,13 +628,6 @@ private void ensureNotShowingBottomPanel(List entriesToCheck) { } } - public void updateEntryEditorIfShowing() { - if (mode == PanelMode.MAIN_TABLE_AND_ENTRY_EDITOR) { - BibEntry currentEntry = entryEditor.getCurrentlyEditedEntry(); - showAndEdit(currentEntry); - } - } - /** * Put an asterisk behind the filename to indicate the database has changed. */ @@ -889,16 +882,16 @@ public void insertEntry(final BibEntry bibEntry) { } public void insertEntries(final List entries) { - if (!entries.isEmpty()) { - importHandler.importCleanedEntries(entries); - - // Create an UndoableInsertEntries object. - getUndoManager().addEdit(new UndoableInsertEntries(bibDatabaseContext.getDatabase(), entries)); + if (entries.isEmpty()) { + return; + } - markBaseChanged(); - if (preferences.getEntryEditorPreferences().shouldOpenOnNewEntry()) { - showAndEdit(entries.getFirst()); - } + importHandler.importCleanedEntries(entries); + getUndoManager().addEdit(new UndoableInsertEntries(bibDatabaseContext.getDatabase(), entries)); + markBaseChanged(); + if (preferences.getEntryEditorPreferences().shouldOpenOnNewEntry()) { + showAndEdit(entries.getFirst()); + } else { clearAndSelect(entries.getFirst()); } } @@ -906,29 +899,29 @@ public void insertEntries(final List entries) { public void copyEntry() { int entriesCopied = doCopyEntry(getSelectedEntries()); if (entriesCopied >= 0) { - dialogService.notify(Localization.lang("Copied %0 entry(ies)", entriesCopied)); + dialogService.notify(Localization.lang("Copied %0 entry(s)", entriesCopied)); } else { dialogService.notify(Localization.lang("Copy failed", entriesCopied)); } } private int doCopyEntry(List selectedEntries) { - if (!selectedEntries.isEmpty()) { - List stringConstants = bibDatabaseContext.getDatabase().getUsedStrings(selectedEntries); - try { - if (stringConstants.isEmpty()) { - clipBoardManager.setContent(selectedEntries, entryTypesManager); - } else { - clipBoardManager.setContent(selectedEntries, entryTypesManager, stringConstants); - } - return selectedEntries.size(); - } catch (IOException e) { - LOGGER.error("Error while copying selected entries to clipboard.", e); - return -1; - } + if (selectedEntries.isEmpty()) { + return 0; } - return 0; + List stringConstants = bibDatabaseContext.getDatabase().getUsedStrings(selectedEntries); + try { + if (stringConstants.isEmpty()) { + clipBoardManager.setContent(selectedEntries, entryTypesManager); + } else { + clipBoardManager.setContent(selectedEntries, entryTypesManager, stringConstants); + } + return selectedEntries.size(); + } catch (IOException e) { + LOGGER.error("Error while copying selected entries to clipboard.", e); + return -1; + } } public void pasteEntry() { @@ -948,8 +941,7 @@ public void pasteEntry() { private List handleNonBibTeXStringData(String data) { try { return this.importHandler.handleStringData(data); - } catch ( - FetcherException exception) { + } catch (FetcherException exception) { if (exception instanceof FetcherClientException) { dialogService.showInformationDialogAndWait(Localization.lang("Look up identifier"), Localization.lang("No data was found for the identifier")); } else if (exception instanceof FetcherServerException) { @@ -970,7 +962,7 @@ public void cutEntry() { int entriesDeleted = doDeleteEntry(StandardActions.CUT, mainTable.getSelectedEntries()); if (entriesCopied == entriesDeleted) { - dialogService.notify(Localization.lang("Cut %0 entry(ies)", entriesCopied)); + dialogService.notify(Localization.lang("Cut %0 entry(s)", entriesCopied)); } else { dialogService.notify(Localization.lang("Cut failed", entriesCopied)); undoManager.undo(); @@ -983,7 +975,7 @@ public void cutEntry() { */ public void deleteEntry() { int entriesDeleted = doDeleteEntry(StandardActions.DELETE_ENTRY, mainTable.getSelectedEntries()); - dialogService.notify(Localization.lang("Deleted %0 entry(ies)", entriesDeleted)); + dialogService.notify(Localization.lang("Deleted %0 entry(s)", entriesDeleted)); } public void deleteEntry(BibEntry entry) { diff --git a/src/main/java/org/jabref/gui/actions/ActionHelper.java b/src/main/java/org/jabref/gui/actions/ActionHelper.java index 437d38d9bfe..16ffd8050c5 100644 --- a/src/main/java/org/jabref/gui/actions/ActionHelper.java +++ b/src/main/java/org/jabref/gui/actions/ActionHelper.java @@ -29,6 +29,11 @@ public static BooleanExpression needsDatabase(StateManager stateManager) { return stateManager.activeDatabaseProperty().isPresent(); } + public static BooleanExpression needsSavedLocalDatabase(StateManager stateManager) { + EasyBinding binding = EasyBind.map(stateManager.activeDatabaseProperty(), context -> context.filter(c -> c.getLocation() == DatabaseLocation.LOCAL && c.getDatabasePath().isPresent()).isPresent()); + return BooleanExpression.booleanExpression(binding); + } + public static BooleanExpression needsSharedDatabase(StateManager stateManager) { EasyBinding binding = EasyBind.map(stateManager.activeDatabaseProperty(), context -> context.filter(c -> c.getLocation() == DatabaseLocation.SHARED).isPresent()); return BooleanExpression.booleanExpression(binding); @@ -49,7 +54,7 @@ public static BooleanExpression needsEntriesSelected(StateManager stateManager) public static BooleanExpression needsEntriesSelected(int numberOfEntries, StateManager stateManager) { return Bindings.createBooleanBinding(() -> stateManager.getSelectedEntries().size() == numberOfEntries, - stateManager.getSelectedEntries()); + stateManager.getSelectedEntries()); } public static BooleanExpression isFieldSetForSelectedEntry(Field field, StateManager stateManager) { diff --git a/src/main/java/org/jabref/gui/ai/components/privacynotice/PrivacyNoticeComponent.fxml b/src/main/java/org/jabref/gui/ai/components/privacynotice/PrivacyNoticeComponent.fxml index 2f439beac31..a4f4da47cc2 100644 --- a/src/main/java/org/jabref/gui/ai/components/privacynotice/PrivacyNoticeComponent.fxml +++ b/src/main/java/org/jabref/gui/ai/components/privacynotice/PrivacyNoticeComponent.fxml @@ -66,6 +66,15 @@ + + + + + + + + + diff --git a/src/main/java/org/jabref/gui/ai/components/privacynotice/PrivacyNoticeComponent.java b/src/main/java/org/jabref/gui/ai/components/privacynotice/PrivacyNoticeComponent.java index 12dad69d580..4ef78da8c36 100644 --- a/src/main/java/org/jabref/gui/ai/components/privacynotice/PrivacyNoticeComponent.java +++ b/src/main/java/org/jabref/gui/ai/components/privacynotice/PrivacyNoticeComponent.java @@ -25,6 +25,7 @@ public class PrivacyNoticeComponent extends ScrollPane { @FXML private TextFlow mistralAiPrivacyTextFlow; @FXML private TextFlow geminiPrivacyTextFlow; @FXML private TextFlow huggingFacePrivacyTextFlow; + @FXML private TextFlow gpt4AllTextFlow; @FXML private Text embeddingModelText; private final AiPreferences aiPreferences; @@ -49,6 +50,7 @@ private void initialize() { initPrivacyHyperlink(mistralAiPrivacyTextFlow, AiProvider.MISTRAL_AI); initPrivacyHyperlink(geminiPrivacyTextFlow, AiProvider.GEMINI); initPrivacyHyperlink(huggingFacePrivacyTextFlow, AiProvider.HUGGING_FACE); + initPrivacyHyperlink(gpt4AllTextFlow, AiProvider.GPT4ALL); String newEmbeddingModelText = embeddingModelText.getText().replaceAll("%0", aiPreferences.getEmbeddingModel().sizeInfo()); embeddingModelText.setText(newEmbeddingModelText); diff --git a/src/main/java/org/jabref/gui/cleanup/CleanupAction.java b/src/main/java/org/jabref/gui/cleanup/CleanupAction.java index b09d82e6f5f..3be3143ab58 100644 --- a/src/main/java/org/jabref/gui/cleanup/CleanupAction.java +++ b/src/main/java/org/jabref/gui/cleanup/CleanupAction.java @@ -131,7 +131,6 @@ private void showResults() { } if (modifiedEntriesCount > 0) { - tabSupplier.get().updateEntryEditorIfShowing(); tabSupplier.get().markBaseChanged(); } diff --git a/src/main/java/org/jabref/gui/collab/DatabaseChangesResolverDialog.java b/src/main/java/org/jabref/gui/collab/DatabaseChangesResolverDialog.java index 7c86ee9950b..3c0be42e6ed 100644 --- a/src/main/java/org/jabref/gui/collab/DatabaseChangesResolverDialog.java +++ b/src/main/java/org/jabref/gui/collab/DatabaseChangesResolverDialog.java @@ -99,7 +99,8 @@ public boolean areAllChangesDenied() { @FXML private void initialize() { - PreviewViewer previewViewer = new PreviewViewer(database, dialogService, preferences, themeManager, taskExecutor); + PreviewViewer previewViewer = new PreviewViewer(dialogService, preferences, themeManager, taskExecutor); + previewViewer.setDatabaseContext(database); DatabaseChangeDetailsViewFactory databaseChangeDetailsViewFactory = new DatabaseChangeDetailsViewFactory(database, dialogService, themeManager, preferences, entryTypesManager, previewViewer, taskExecutor); viewModel = new ExternalChangesResolverViewModel(changes, undoManager); diff --git a/src/main/java/org/jabref/gui/collab/entrychange/EntryChangeDetailsView.java b/src/main/java/org/jabref/gui/collab/entrychange/EntryChangeDetailsView.java index 8cdabe824ef..f62934e7732 100644 --- a/src/main/java/org/jabref/gui/collab/entrychange/EntryChangeDetailsView.java +++ b/src/main/java/org/jabref/gui/collab/entrychange/EntryChangeDetailsView.java @@ -40,7 +40,8 @@ public EntryChangeDetailsView(BibEntry oldEntry, onDisk.getStyleClass().add("lib-change-header"); // we need a copy here as we otherwise would set the same entry twice - PreviewViewer previewClone = new PreviewViewer(databaseContext, dialogService, preferences, themeManager, taskExecutor); + PreviewViewer previewClone = new PreviewViewer(dialogService, preferences, themeManager, taskExecutor); + previewClone.setDatabaseContext(databaseContext); // The scroll bar used is not part of ScrollPane, but the attached WebView. WebView previewCloneView = (WebView) previewClone.getContent(); diff --git a/src/main/java/org/jabref/gui/entryeditor/CommentsTab.java b/src/main/java/org/jabref/gui/entryeditor/CommentsTab.java index 1930d618d9c..c9053f0a4bf 100644 --- a/src/main/java/org/jabref/gui/entryeditor/CommentsTab.java +++ b/src/main/java/org/jabref/gui/entryeditor/CommentsTab.java @@ -16,26 +16,22 @@ import javafx.scene.layout.Priority; import javafx.scene.layout.RowConstraints; -import org.jabref.gui.DialogService; import org.jabref.gui.autocompleter.SuggestionProviders; import org.jabref.gui.fieldeditors.FieldEditorFX; import org.jabref.gui.fieldeditors.FieldNameLabel; import org.jabref.gui.fieldeditors.MarkdownEditor; import org.jabref.gui.icon.IconTheme; import org.jabref.gui.preferences.GuiPreferences; -import org.jabref.gui.theme.ThemeManager; +import org.jabref.gui.preview.PreviewPanel; import org.jabref.gui.undo.RedoAction; import org.jabref.gui.undo.UndoAction; -import org.jabref.gui.util.OptionalObjectProperty; import org.jabref.logic.journals.JournalAbbreviationRepository; import org.jabref.logic.l10n.Localization; -import org.jabref.logic.util.TaskExecutor; import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.field.Field; import org.jabref.model.entry.field.StandardField; import org.jabref.model.entry.field.UserSpecificCommentField; -import org.jabref.model.search.query.SearchQuery; public class CommentsTab extends FieldsEditorTab { public static final String NAME = "Comments"; @@ -51,25 +47,17 @@ public CommentsTab(GuiPreferences preferences, UndoManager undoManager, UndoAction undoAction, RedoAction redoAction, - DialogService dialogService, - ThemeManager themeManager, - TaskExecutor taskExecutor, JournalAbbreviationRepository journalAbbreviationRepository, - OptionalObjectProperty searchQueryProperty) { - super( - false, + PreviewPanel previewPanel) { + super(false, databaseContext, suggestionProviders, undoManager, undoAction, redoAction, - dialogService, preferences, - themeManager, - taskExecutor, journalAbbreviationRepository, - searchQueryProperty - ); + previewPanel); this.defaultOwner = preferences.getOwnerPreferences().getDefaultOwner().toLowerCase(Locale.ROOT).replaceAll("[^a-z0-9]", "-"); setText(Localization.lang("Comments")); setGraphic(IconTheme.JabRefIcons.COMMENT.getGraphicNode()); diff --git a/src/main/java/org/jabref/gui/entryeditor/DeprecatedFieldsTab.java b/src/main/java/org/jabref/gui/entryeditor/DeprecatedFieldsTab.java index c4f75a09893..6bef4bf0dd8 100644 --- a/src/main/java/org/jabref/gui/entryeditor/DeprecatedFieldsTab.java +++ b/src/main/java/org/jabref/gui/entryeditor/DeprecatedFieldsTab.java @@ -9,24 +9,20 @@ import javafx.scene.control.Tooltip; -import org.jabref.gui.DialogService; import org.jabref.gui.autocompleter.SuggestionProviders; import org.jabref.gui.icon.IconTheme; import org.jabref.gui.preferences.GuiPreferences; -import org.jabref.gui.theme.ThemeManager; +import org.jabref.gui.preview.PreviewPanel; import org.jabref.gui.undo.RedoAction; import org.jabref.gui.undo.UndoAction; -import org.jabref.gui.util.OptionalObjectProperty; import org.jabref.logic.journals.JournalAbbreviationRepository; import org.jabref.logic.l10n.Localization; -import org.jabref.logic.util.TaskExecutor; import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.database.BibDatabaseMode; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.BibEntryType; import org.jabref.model.entry.BibEntryTypesManager; import org.jabref.model.entry.field.Field; -import org.jabref.model.search.query.SearchQuery; import com.tobiasdiez.easybind.EasyBind; @@ -40,14 +36,21 @@ public DeprecatedFieldsTab(BibDatabaseContext databaseContext, UndoManager undoManager, UndoAction undoAction, RedoAction redoAction, - DialogService dialogService, GuiPreferences preferences, - ThemeManager themeManager, BibEntryTypesManager entryTypesManager, - TaskExecutor taskExecutor, JournalAbbreviationRepository journalAbbreviationRepository, - OptionalObjectProperty searchQueryProperty) { - super(false, databaseContext, suggestionProviders, undoManager, undoAction, redoAction, dialogService, preferences, themeManager, taskExecutor, journalAbbreviationRepository, searchQueryProperty); + PreviewPanel previewPanel) { + super( + false, + databaseContext, + suggestionProviders, + undoManager, + undoAction, + redoAction, + preferences, + journalAbbreviationRepository, + previewPanel + ); this.entryTypesManager = entryTypesManager; setText(Localization.lang("Deprecated fields")); diff --git a/src/main/java/org/jabref/gui/entryeditor/DetailOptionalFieldsTab.java b/src/main/java/org/jabref/gui/entryeditor/DetailOptionalFieldsTab.java index a10d6aff476..48fb2ef017f 100644 --- a/src/main/java/org/jabref/gui/entryeditor/DetailOptionalFieldsTab.java +++ b/src/main/java/org/jabref/gui/entryeditor/DetailOptionalFieldsTab.java @@ -2,19 +2,15 @@ import javax.swing.undo.UndoManager; -import org.jabref.gui.DialogService; import org.jabref.gui.autocompleter.SuggestionProviders; import org.jabref.gui.preferences.GuiPreferences; -import org.jabref.gui.theme.ThemeManager; +import org.jabref.gui.preview.PreviewPanel; import org.jabref.gui.undo.RedoAction; import org.jabref.gui.undo.UndoAction; -import org.jabref.gui.util.OptionalObjectProperty; import org.jabref.logic.journals.JournalAbbreviationRepository; import org.jabref.logic.l10n.Localization; -import org.jabref.logic.util.TaskExecutor; import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.entry.BibEntryTypesManager; -import org.jabref.model.search.query.SearchQuery; public class DetailOptionalFieldsTab extends OptionalFieldsTabBase { @@ -25,13 +21,10 @@ public DetailOptionalFieldsTab(BibDatabaseContext databaseContext, UndoManager undoManager, UndoAction undoAction, RedoAction redoAction, - DialogService dialogService, GuiPreferences preferences, - ThemeManager themeManager, BibEntryTypesManager entryTypesManager, - TaskExecutor taskExecutor, JournalAbbreviationRepository journalAbbreviationRepository, - OptionalObjectProperty searchQueryProperty) { + PreviewPanel previewPanel) { super( Localization.lang("Optional fields 2"), false, @@ -40,13 +33,10 @@ public DetailOptionalFieldsTab(BibDatabaseContext databaseContext, undoManager, undoAction, redoAction, - dialogService, preferences, - themeManager, entryTypesManager, - taskExecutor, journalAbbreviationRepository, - searchQueryProperty + previewPanel ); } } diff --git a/src/main/java/org/jabref/gui/entryeditor/EntryEditor.java b/src/main/java/org/jabref/gui/entryeditor/EntryEditor.java index d2689b2e8b7..3a2bd6f2258 100644 --- a/src/main/java/org/jabref/gui/entryeditor/EntryEditor.java +++ b/src/main/java/org/jabref/gui/entryeditor/EntryEditor.java @@ -2,7 +2,6 @@ import java.io.File; import java.nio.file.Path; -import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.LinkedList; @@ -37,12 +36,13 @@ import org.jabref.gui.entryeditor.fileannotationtab.FulltextSearchResultsTab; import org.jabref.gui.externalfiles.ExternalFilesEntryLinker; import org.jabref.gui.help.HelpAction; -import org.jabref.gui.importer.GrobidOptInDialogHelper; +import org.jabref.gui.importer.GrobidUseDialogHelper; import org.jabref.gui.keyboard.KeyBinding; import org.jabref.gui.keyboard.KeyBindingRepository; import org.jabref.gui.menus.ChangeEntryTypeMenu; import org.jabref.gui.mergeentries.FetchAndMergeEntry; import org.jabref.gui.preferences.GuiPreferences; +import org.jabref.gui.preview.PreviewPanel; import org.jabref.gui.theme.ThemeManager; import org.jabref.gui.undo.CountingUndoManager; import org.jabref.gui.undo.RedoAction; @@ -69,6 +69,7 @@ import com.tobiasdiez.easybind.EasyBind; import com.tobiasdiez.easybind.Subscription; import jakarta.inject.Inject; +import org.jspecify.annotations.NonNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -91,6 +92,7 @@ public class EntryEditor extends BorderPane { private final BibDatabaseContext databaseContext; private final EntryEditorPreferences entryEditorPreferences; private final ExternalFilesEntryLinker fileLinker; + private final PreviewPanel previewPanel; private final DirectoryMonitorManager directoryMonitorManager; private final UndoAction undoAction; private final RedoAction redoAction; @@ -121,7 +123,6 @@ public class EntryEditor extends BorderPane { @Inject private AiService aiService; private final List allPossibleTabs; - private final Collection previewTabs; public EntryEditor(LibraryTab libraryTab, UndoAction undoAction, RedoAction redoAction) { this.libraryTab = libraryTab; @@ -135,14 +136,22 @@ public EntryEditor(LibraryTab libraryTab, UndoAction undoAction, RedoAction redo .load(); this.entryEditorPreferences = preferences.getEntryEditorPreferences(); - this.fileLinker = new ExternalFilesEntryLinker(preferences.getExternalApplicationsPreferences(), preferences.getFilePreferences(), databaseContext, dialogService); + this.fileLinker = new ExternalFilesEntryLinker(preferences.getExternalApplicationsPreferences(), preferences.getFilePreferences(), dialogService, stateManager); + this.previewPanel = new PreviewPanel( + dialogService, + preferences.getKeyBindingRepository(), + preferences, + themeManager, + taskExecutor, + stateManager, + libraryTab.searchQueryProperty()); + this.previewPanel.setDatabase(databaseContext); setupKeyBindings(); this.allPossibleTabs = createTabs(); - this.previewTabs = this.allPossibleTabs.stream().filter(OffersPreview.class::isInstance).map(OffersPreview.class::cast).toList(); - setupDragAndDrop(libraryTab); + setupDragAndDrop(); EasyBind.subscribe(tabbed.getSelectionModel().selectedItemProperty(), tab -> { EntryEditorTab activeTab = (EntryEditorTab) tab; @@ -155,11 +164,18 @@ public EntryEditor(LibraryTab libraryTab, UndoAction undoAction, RedoAction redo (obs, oldValue, newValue) -> { if (currentlyEditedEntry != null) { adaptVisibleTabs(); + Tab tab = tabbed.getSelectionModel().selectedItemProperty().get(); + if (newValue && tab instanceof FieldsEditorTab fieldsEditorTab) { + fieldsEditorTab.removePreviewPanelFromThisTab(); + } + if (tab instanceof TabWithPreviewPanel previewTab) { + previewTab.handleFocus(); + } } }); } - private void setupDragAndDrop(LibraryTab libraryTab) { + private void setupDragAndDrop() { this.setOnDragOver(event -> { if (event.getDragboard().hasFiles()) { event.acceptTransferModes(TransferMode.COPY, TransferMode.MOVE, TransferMode.LINK); @@ -267,21 +283,21 @@ private void navigateToNextEntry() { private List createTabs() { List tabs = new LinkedList<>(); - tabs.add(new PreviewTab(databaseContext, dialogService, preferences, themeManager, taskExecutor, libraryTab.searchQueryProperty())); + tabs.add(new PreviewTab(databaseContext, preferences, previewPanel)); // Required, optional (important+detail), deprecated, and "other" fields - tabs.add(new RequiredFieldsTab(databaseContext, libraryTab.getSuggestionProviders(), undoManager, undoAction, redoAction, dialogService, preferences, themeManager, bibEntryTypesManager, taskExecutor, journalAbbreviationRepository, libraryTab.searchQueryProperty())); - tabs.add(new ImportantOptionalFieldsTab(databaseContext, libraryTab.getSuggestionProviders(), undoManager, undoAction, redoAction, dialogService, preferences, themeManager, bibEntryTypesManager, taskExecutor, journalAbbreviationRepository, libraryTab.searchQueryProperty())); - tabs.add(new DetailOptionalFieldsTab(databaseContext, libraryTab.getSuggestionProviders(), undoManager, undoAction, redoAction, dialogService, preferences, themeManager, bibEntryTypesManager, taskExecutor, journalAbbreviationRepository, libraryTab.searchQueryProperty())); - tabs.add(new DeprecatedFieldsTab(databaseContext, libraryTab.getSuggestionProviders(), undoManager, undoAction, redoAction, dialogService, preferences, themeManager, bibEntryTypesManager, taskExecutor, journalAbbreviationRepository, libraryTab.searchQueryProperty())); - tabs.add(new OtherFieldsTab(databaseContext, libraryTab.getSuggestionProviders(), undoManager, undoAction, redoAction, dialogService, preferences, themeManager, bibEntryTypesManager, taskExecutor, journalAbbreviationRepository, libraryTab.searchQueryProperty())); + tabs.add(new RequiredFieldsTab(databaseContext, libraryTab.getSuggestionProviders(), undoManager, undoAction, redoAction, preferences, bibEntryTypesManager, journalAbbreviationRepository, previewPanel)); + tabs.add(new ImportantOptionalFieldsTab(databaseContext, libraryTab.getSuggestionProviders(), undoManager, undoAction, redoAction, preferences, bibEntryTypesManager, journalAbbreviationRepository, previewPanel)); + tabs.add(new DetailOptionalFieldsTab(databaseContext, libraryTab.getSuggestionProviders(), undoManager, undoAction, redoAction, preferences, bibEntryTypesManager, journalAbbreviationRepository, previewPanel)); + tabs.add(new DeprecatedFieldsTab(databaseContext, libraryTab.getSuggestionProviders(), undoManager, undoAction, redoAction, preferences, bibEntryTypesManager, journalAbbreviationRepository, previewPanel)); + tabs.add(new OtherFieldsTab(databaseContext, libraryTab.getSuggestionProviders(), undoManager, undoAction, redoAction, preferences, bibEntryTypesManager, journalAbbreviationRepository, previewPanel)); // Comment Tab: Tab for general and user-specific comments - tabs.add(new CommentsTab(preferences, databaseContext, libraryTab.getSuggestionProviders(), undoManager, undoAction, redoAction, dialogService, themeManager, taskExecutor, journalAbbreviationRepository, libraryTab.searchQueryProperty())); + tabs.add(new CommentsTab(preferences, databaseContext, libraryTab.getSuggestionProviders(), undoManager, undoAction, redoAction, journalAbbreviationRepository, previewPanel)); Map> entryEditorTabList = getAdditionalUserConfiguredTabs(); for (Map.Entry> tab : entryEditorTabList.entrySet()) { - tabs.add(new UserDefinedFieldsTab(tab.getKey(), tab.getValue(), databaseContext, libraryTab.getSuggestionProviders(), undoManager, undoAction, redoAction, dialogService, preferences, themeManager, taskExecutor, journalAbbreviationRepository, libraryTab.searchQueryProperty())); + tabs.add(new UserDefinedFieldsTab(tab.getKey(), tab.getValue(), databaseContext, libraryTab.getSuggestionProviders(), undoManager, undoAction, redoAction, preferences, journalAbbreviationRepository, previewPanel)); } tabs.add(new MathSciNetTab()); @@ -374,8 +390,12 @@ public BibEntry getCurrentlyEditedEntry() { return currentlyEditedEntry; } - public void setCurrentlyEditedEntry(BibEntry currentlyEditedEntry) { - this.currentlyEditedEntry = Objects.requireNonNull(currentlyEditedEntry); + public void setCurrentlyEditedEntry(@NonNull BibEntry currentlyEditedEntry) { + if (Objects.equals(this.currentlyEditedEntry, currentlyEditedEntry)) { + return; + } + + this.currentlyEditedEntry = currentlyEditedEntry; // Subscribe to type changes for rebuilding the currently visible tab if (typeSubscription != null) { @@ -385,16 +405,13 @@ public void setCurrentlyEditedEntry(BibEntry currentlyEditedEntry) { typeSubscription = EasyBind.subscribe(this.currentlyEditedEntry.typeProperty(), type -> { typeLabel.setText(new TypedBibEntry(currentlyEditedEntry, databaseContext.getMode()).getTypeForDisplay()); adaptVisibleTabs(); + setupToolBar(); getSelectedTab().notifyAboutFocus(currentlyEditedEntry); }); - adaptVisibleTabs(); - setupToolBar(); - if (entryEditorPreferences.showSourceTabByDefault()) { tabbed.getSelectionModel().select(sourceTab); } - getSelectedTab().notifyAboutFocus(currentlyEditedEntry); } private EntryEditorTab getSelectedTab() { @@ -423,7 +440,7 @@ private void setupToolBar() { if (fetcher instanceof PdfMergeMetadataImporter.EntryBasedFetcherWrapper) { // Handle Grobid Opt-In in case of the PdfMergeMetadataImporter fetcherMenuItem.setOnAction(event -> { - GrobidOptInDialogHelper.showAndWaitIfUserIsUndecided(dialogService, preferences.getGrobidPreferences()); + GrobidUseDialogHelper.showAndWaitIfUserIsUndecided(dialogService, preferences.getGrobidPreferences()); PdfMergeMetadataImporter.EntryBasedFetcherWrapper pdfMergeMetadataImporter = new PdfMergeMetadataImporter.EntryBasedFetcherWrapper( preferences.getImportFormatPreferences(), @@ -457,10 +474,10 @@ public void setFocusToField(Field field) { } public void nextPreviewStyle() { - this.previewTabs.forEach(OffersPreview::nextPreviewStyle); + this.previewPanel.nextPreviewStyle(); } public void previousPreviewStyle() { - this.previewTabs.forEach(OffersPreview::previousPreviewStyle); + this.previewPanel.previousPreviewStyle(); } } diff --git a/src/main/java/org/jabref/gui/entryeditor/EntryEditorTab.java b/src/main/java/org/jabref/gui/entryeditor/EntryEditorTab.java index 1ac5ec4149b..61a89bd055b 100644 --- a/src/main/java/org/jabref/gui/entryeditor/EntryEditorTab.java +++ b/src/main/java/org/jabref/gui/entryeditor/EntryEditorTab.java @@ -5,8 +5,13 @@ import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.types.EntryType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + public abstract class EntryEditorTab extends Tab { + private static final Logger LOGGER = LoggerFactory.getLogger(EntryEditorTab.class); + protected BibEntry currentEntry; /** @@ -37,6 +42,10 @@ protected void handleFocus() { */ public void notifyAboutFocus(BibEntry entry) { if (!entry.equals(currentEntry) || !entry.getType().equals(currentEntryType)) { + // TODO: Shouldn't "bindToEntry" called when changing the entry? + LOGGER.trace("Tab got focus with different entry (or entry type) {}", entry); + LOGGER.trace("Different entry: {}", entry.equals(currentEntry)); + LOGGER.trace("Different entry type: {}", !entry.getType().equals(currentEntryType)); currentEntry = entry; currentEntryType = entry.getType(); bindToEntry(entry); diff --git a/src/main/java/org/jabref/gui/entryeditor/FieldsEditorTab.java b/src/main/java/org/jabref/gui/entryeditor/FieldsEditorTab.java index 4814dbb2776..39cfdf8ff89 100644 --- a/src/main/java/org/jabref/gui/entryeditor/FieldsEditorTab.java +++ b/src/main/java/org/jabref/gui/entryeditor/FieldsEditorTab.java @@ -24,47 +24,43 @@ import javafx.scene.layout.Region; import javafx.scene.layout.RowConstraints; -import org.jabref.gui.DialogService; import org.jabref.gui.autocompleter.SuggestionProviders; import org.jabref.gui.fieldeditors.FieldEditorFX; import org.jabref.gui.fieldeditors.FieldEditors; import org.jabref.gui.fieldeditors.FieldNameLabel; import org.jabref.gui.preferences.GuiPreferences; import org.jabref.gui.preview.PreviewPanel; -import org.jabref.gui.theme.ThemeManager; import org.jabref.gui.undo.RedoAction; import org.jabref.gui.undo.UndoAction; -import org.jabref.gui.util.OptionalObjectProperty; import org.jabref.logic.journals.JournalAbbreviationRepository; -import org.jabref.logic.util.TaskExecutor; import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.field.Field; -import org.jabref.model.search.query.SearchQuery; import com.tobiasdiez.easybind.EasyBind; import com.tobiasdiez.easybind.Subscription; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * A single tab displayed in the EntryEditor holding several FieldEditors. */ -abstract class FieldsEditorTab extends EntryEditorTab implements OffersPreview { - protected final BibDatabaseContext databaseContext; +abstract class FieldsEditorTab extends TabWithPreviewPanel { + + private static final Logger LOGGER = LoggerFactory.getLogger(FieldsEditorTab.class); + protected final Map editors = new LinkedHashMap<>(); protected GridPane gridPane; private final boolean isCompressed; private final SuggestionProviders suggestionProviders; private final UndoAction undoAction; private final RedoAction redoAction; - private final DialogService dialogService; private final GuiPreferences preferences; - private final ThemeManager themeManager; - private final TaskExecutor taskExecutor; private final JournalAbbreviationRepository journalAbbreviationRepository; - private PreviewPanel previewPanel; private final UndoManager undoManager; - private final OptionalObjectProperty searchQueryProperty; + private Collection fields = new ArrayList<>(); + @SuppressWarnings("FieldCanBeLocal") private Subscription dividerPositionSubscription; @@ -74,24 +70,17 @@ public FieldsEditorTab(boolean compressed, UndoManager undoManager, UndoAction undoAction, RedoAction redoAction, - DialogService dialogService, GuiPreferences preferences, - ThemeManager themeManager, - TaskExecutor taskExecutor, JournalAbbreviationRepository journalAbbreviationRepository, - OptionalObjectProperty searchQueryProperty) { + PreviewPanel previewPanel) { + super(databaseContext, previewPanel); this.isCompressed = compressed; - this.databaseContext = Objects.requireNonNull(databaseContext); this.suggestionProviders = Objects.requireNonNull(suggestionProviders); this.undoManager = Objects.requireNonNull(undoManager); this.undoAction = undoAction; this.redoAction = redoAction; - this.dialogService = Objects.requireNonNull(dialogService); this.preferences = Objects.requireNonNull(preferences); - this.themeManager = themeManager; - this.taskExecutor = Objects.requireNonNull(taskExecutor); this.journalAbbreviationRepository = Objects.requireNonNull(journalAbbreviationRepository); - this.searchQueryProperty = searchQueryProperty; } private static void addColumn(GridPane gridPane, int columnIndex, List