diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
index ef4a1dd6b23..4f75fd48baf 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.7 (latest release)"
+ - "5.8 (latest release)"
- "3.8.2"
- Latest development branch build (please note build date below)
- Other (please describe below)
diff --git a/.github/workflows/GreetingFirstTimeCodeContribution.yml b/.github/workflows/GreetingFirstTimeCodeContribution.yml
index fb0ed3164f4..0f2dc091fd0 100644
--- a/.github/workflows/GreetingFirstTimeCodeContribution.yml
+++ b/.github/workflows/GreetingFirstTimeCodeContribution.yml
@@ -15,7 +15,6 @@ jobs:
- name: GreetingFirstTimeCodeContribution
uses: peter-evans/create-or-update-comment@v2
with:
- repo-token: ${{ secrets.GITHUB_TOKEN }}
issue-number: ${{ github.event.issue.number }}
body: |
As a general advice for newcomers: check out [Contributing](https://github.com/JabRef/jabref/blob/main/CONTRIBUTING.md) for a start. Also, [guidelines for setting up a local workspace](https://devdocs.jabref.org/getting-into-the-code/guidelines-for-setting-up-a-local-workspace) is worth having a look at.
diff --git a/.github/workflows/automerge.yml b/.github/workflows/automerge.yml
index 624ead6dc9f..2d3633931d4 100644
--- a/.github/workflows/automerge.yml
+++ b/.github/workflows/automerge.yml
@@ -16,12 +16,12 @@ jobs:
env:
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
- name: Auto approve
- uses: hmarr/auto-approve-action@v3.0.0
+ uses: hmarr/auto-approve-action@v3.1.0
if: steps.waitforstatuschecks.outputs.status == 'success'
with:
github-token: "${{ secrets.GITHUB_TOKEN }}"
- name: Merge pull requests
- uses: pascalgn/automerge-action@v0.15.3
+ uses: pascalgn/automerge-action@v0.15.5
if: steps.waitforstatuschecks.outputs.status == 'success'
env:
MERGE_METHOD: "merge"
diff --git a/.github/workflows/check-links.yaml b/.github/workflows/check-links.yaml
index 9c0ab03ccea..f4e470394d4 100644
--- a/.github/workflows/check-links.yaml
+++ b/.github/workflows/check-links.yaml
@@ -4,6 +4,8 @@ on:
push:
paths:
- '**.md'
+ paths-ignore:
+ - 'CHANGELOG.md'
schedule:
# Run on the first of each month at 9:00 AM (See https://pubs.opengroup.org/onlinepubs/9699919799/utilities/crontab.html#tag_20_25_07)
- cron: "0 9 1 * *"
diff --git a/.github/workflows/cleanup_pr.yml b/.github/workflows/cleanup_pr.yml
index 20747030207..3ec8192a27c 100644
--- a/.github/workflows/cleanup_pr.yml
+++ b/.github/workflows/cleanup_pr.yml
@@ -13,10 +13,10 @@ jobs:
id: checksecrets
shell: bash
run: |
- if [ -z "$BUILDJABREFPRIVATEKEY" ]; then
- echo ::set-output name=secretspresent::false
+ if [ "$BUILDJABREFPRIVATEKEY" == "" ]; then
+ echo "secretspresent=NO" >> $GITHUB_OUTPUT
else
- echo ::set-output name=secretspresent::true
+ echo "secretspresent=YES" >> $GITHUB_OUTPUT
fi
env:
BUILDJABREFPRIVATEKEY: ${{ secrets.buildJabRefPrivateKey }}
@@ -24,10 +24,10 @@ jobs:
id: extract_branch
if: ${{ steps.checksecrets.outputs.secretspresent }}
run: |
- echo "##[set-output name=branch;]$(echo ${{ github.event.pull_request.head.ref }})"
+ echo "branch=${{ github.event.pull_request.head.ref }}" >> $GITHUB_OUTPUT
- name: Delete folder on builds.jabref.org
- if: ${{ steps.checksecrets.outputs.secretspresent }}
- uses: appleboy/ssh-action@v0.1.5
+ if: steps.checksecrets.outputs.secretspresent == 'YES'
+ uses: appleboy/ssh-action@v0.1.6
with:
script: rm -rf /var/www/builds.jabref.org/www/${{ steps.extract_branch.outputs.branch }} || true
host: build-upload.jabref.org
diff --git a/.github/workflows/deployment-arm64.yml b/.github/workflows/deployment-arm64.yml
new file mode 100644
index 00000000000..6e68d03c582
--- /dev/null
+++ b/.github/workflows/deployment-arm64.yml
@@ -0,0 +1,196 @@
+name: Deployment Release for ARM64 - Run manually!
+
+on:
+ workflow_dispatch:
+ push:
+ branches:
+ - arm64mac-release
+
+
+env:
+ SpringerNatureAPIKey: ${{ secrets.SpringerNatureAPIKey }}
+ AstrophysicsDataSystemAPIKey: ${{ secrets.AstrophysicsDataSystemAPIKey }}
+ IEEEAPIKey: ${{ secrets.IEEEAPIKey }}
+ BiodiversityHeritageApiKey: ${{ secrets.BiodiversityHeritageApiKey}}
+ OSXCERT: ${{ secrets.OSX_SIGNING_CERT }}
+ GRADLE_OPTS: -Xmx4g -Dorg.gradle.daemon=false -Dorg.gradle.vfs.watch=false
+ JAVA_OPTS: -Xmx4g
+
+concurrency:
+ group: ${{ github.ref }}
+ cancel-in-progress: true
+
+jobs:
+ build:
+ strategy:
+ fail-fast: false
+ matrix:
+ os: [self-hosted]
+ include:
+ - os: self-hosted
+ displayName: macOS (Arm64)
+ suffix: '_arm64'
+ archivePortable: tar -c -C build/distribution JabRef.app | pigz --rsyncable > build/distribution/JabRef-portable_macos_arm64.tar.gz && rm -R build/distribution/JabRef.app
+ runs-on: ${{ matrix.os }}
+ name: Create installer and portable version for ${{ matrix.displayName }}
+ steps:
+ - name: Check secrets presence
+ id: checksecrets
+ shell: bash
+ run: |
+ if [ "$BUILDJABREFPRIVATEKEY" == "" ]; then
+ echo "secretspresent=NO" >> $GITHUB_OUTPUT
+ else
+ echo "secretspresent=YES" >> $GITHUB_OUTPUT
+ fi
+ env:
+ BUILDJABREFPRIVATEKEY: ${{ secrets.buildJabRefPrivateKey }}
+ - name: Fetch all history for all tags and branches
+ uses: actions/checkout@v3
+ with:
+ fetch-depth: 0
+ - name: Install GitVersion
+ uses: gittools/actions/gitversion/setup@v0.9.15
+ with:
+ versionSpec: "5.x"
+ - name: Run GitVersion
+ id: gitversion
+ uses: gittools/actions/gitversion/execute@v0.9.15
+ - name: Set up JDK
+ uses: actions/setup-java@v3
+ with:
+ java-version: 18
+ distribution: 'temurin'
+ cache: 'gradle'
+ - name: Clean up keychain
+ if: (matrix.os == 'self-hosted') && (steps.checksecrets.outputs.secretspresent == 'YES')
+ run: |
+ security delete-keychain signing_temp.keychain
+ - name: Setup OSX key chain on OSX
+ if: (matrix.os == 'macos-latest' || matrix.os == 'self-hosted') && (steps.checksecrets.outputs.secretspresent == 'YES')
+ uses: apple-actions/import-codesign-certs@v1
+ with:
+ p12-file-base64: ${{ secrets.OSX_SIGNING_CERT }}
+ p12-password: ${{ secrets.OSX_CERT_PWD }}
+ keychain-password: jabref
+ - name: Setup OSX key chain on OSX for app id cert
+ if: (matrix.os == 'macos-latest' || matrix.os == 'self-hosted') && (steps.checksecrets.outputs.secretspresent == 'YES')
+ uses: apple-actions/import-codesign-certs@v1
+ with:
+ p12-file-base64: ${{ secrets.OSX_SIGNING_CERT_APPLICATION }}
+ p12-password: ${{ secrets.OSX_CERT_PWD }}
+ create-keychain: false
+ keychain-password: jabref
+ - name: Build runtime image
+ if: (matrix.os != 'macos-latest') || (steps.checksecrets.outputs.secretspresent == 'YES')
+ run: ./gradlew -i -PprojVersion="${{ steps.gitversion.outputs.AssemblySemVer }}" -PprojVersionInfo="${{ steps.gitversion.outputs.InformationalVersion }}" jlinkZip
+ - name: Build installer
+ if: (matrix.os != 'macos-latest') || (steps.checksecrets.outputs.secretspresent == 'YES')
+ run: ./gradlew -i -PprojVersion="${{ steps.gitversion.outputs.AssemblySemVer }}" -PprojVersionInfo="${{ steps.gitversion.outputs.InformationalVersion }}" jpackage
+ shell: bash
+ - name: Resign app image for OSX and build dmg
+ if: (matrix.os == 'macos-latest' || matrix.os == 'self-hosted') && (steps.checksecrets.outputs.secretspresent == 'YES')
+ shell: bash
+ run: |
+ codesign --entitlements buildres/mac/jabref.entitlements --options runtime -vvv -f --sign "Developer ID Application: JabRef e.V. (6792V39SK3)" build/distribution/JabRef.app/Contents/runtime/Contents/MacOS/libjli.dylib
+ codesign --entitlements buildres/mac/jabref.entitlements --options runtime -vvv -f --sign "Developer ID Application: JabRef e.V. (6792V39SK3)" build/distribution/JabRef.app/Contents/MacOS/JabRef
+ codesign --entitlements buildres/mac/jabref.entitlements --options runtime -vvv -f --sign "Developer ID Application: JabRef e.V. (6792V39SK3)" build/distribution/JabRef.app
+ jpackage --type pkg --dest build/distribution --name JabRef --app-version "${{ steps.gitversion.outputs.Major }}.${{ steps.gitversion.outputs.Minor }}" --app-image build/distribution/JabRef.app --verbose --type dmg --vendor JabRef --app-version "${{ steps.gitversion.outputs.Major }}.${{ steps.gitversion.outputs.Minor }}" --file-associations buildres/mac/bibtexAssociations.properties --resource-dir buildres/mac
+ codesign -s "Developer ID Application: JabRef e.V. (6792V39SK3)" --options runtime --entitlements buildres/mac/jabref.entitlements -vvvv --deep "build/distribution/JabRef-${{ steps.gitversion.outputs.Major }}.${{ steps.gitversion.outputs.Minor }}.dmg"
+ jpackage --type pkg --dest build/distribution --name JabRef --mac-package-identifier JabRef --app-version "${{ steps.gitversion.outputs.Major }}.${{ steps.gitversion.outputs.Minor }}" --app-image build/distribution/JabRef.app --verbose --type pkg --vendor JabRef --app-version "${{ steps.gitversion.outputs.Major }}.${{ steps.gitversion.outputs.Minor }}" --file-associations buildres/mac/bibtexAssociations.properties --resource-dir buildres/mac
+ productsign --sign "Developer ID Installer: JabRef e.V. (6792V39SK3)" "build/distribution/JabRef-${{ steps.gitversion.outputs.Major }}.${{ steps.gitversion.outputs.Minor }}.pkg" "build/distribution/JabRef-${{ steps.gitversion.outputs.Major }}.${{ steps.gitversion.outputs.Minor }}-signed.pkg"
+ - name: Notarize dmg and pkg installer
+ if: (matrix.os == 'macos-latest' || matrix.os == 'self-hosted' ) && startsWith(github.ref, 'refs/tags/') && (steps.checksecrets.outputs.secretspresent == 'YES')
+ shell: bash
+ run: |
+ REQUEST_UUID_DMG=$(xcrun altool --verbose --notarize-app --primary-bundle-id "org.jabref" --username ${{ secrets.OSX_NOTARIZATION_APP_USERNAME }} --password ${{ secrets.OSX_NOTARIZATION_APP_PWD }} --asc-provider "6792V39SK3" --file "build/distribution/JabRef-${{ steps.gitversion.outputs.Major }}.${{ steps.gitversion.outputs.Minor }}.dmg" | grep RequestUUID | awk '{print $3}')
+ while xcrun altool --notarization-info "$REQUEST_UUID_DMG" -u ${{ secrets.OSX_NOTARIZATION_APP_USERNAME }} -p ${{ secrets.OSX_NOTARIZATION_APP_PWD }} | grep "Status: in progress" > /dev/null; do
+ echo "Verification in progress..."
+ sleep 30
+ done
+ xcrun stapler staple "build/distribution/JabRef-${{ steps.gitversion.outputs.Major }}.${{ steps.gitversion.outputs.Minor }}.dmg"
+ spctl -vvv --assess --type exec build/distribution/JabRef.app
+ codesign -vvv --deep --strict "build/distribution/JabRef-${{ steps.gitversion.outputs.Major }}.${{ steps.gitversion.outputs.Minor }}.dmg"
+ codesign -dvv "build/distribution/JabRef-${{ steps.gitversion.outputs.Major }}.${{ steps.gitversion.outputs.Minor }}.dmg"
+ REQUEST_UUID_PKG=$(xcrun altool --verbose --notarize-app --primary-bundle-id "org.jabref" --username ${{ secrets.OSX_NOTARIZATION_APP_USERNAME }} --password ${{ secrets.OSX_NOTARIZATION_APP_PWD }} --asc-provider "6792V39SK3" --file "build/distribution/JabRef-${{ steps.gitversion.outputs.Major }}.${{ steps.gitversion.outputs.Minor }}-signed.pkg" | grep RequestUUID | awk '{print $3}')
+ while xcrun altool --notarization-info "$REQUEST_UUID_PKG" -u ${{ secrets.OSX_NOTARIZATION_APP_USERNAME }} -p ${{ secrets.OSX_NOTARIZATION_APP_PWD }} | grep "Status: in progress" > /dev/null; do
+ echo "Verification in progress..."
+ sleep 30
+ done
+ xcrun stapler staple "build/distribution/JabRef-${{ steps.gitversion.outputs.Major }}.${{ steps.gitversion.outputs.Minor }}-signed.pkg"
+ rm "build/distribution/JabRef-${{ steps.gitversion.outputs.Major }}.${{ steps.gitversion.outputs.Minor }}.pkg"
+ mv "build/distribution/JabRef-${{ steps.gitversion.outputs.Major }}.${{ steps.gitversion.outputs.Minor }}-signed.pkg" "build/distribution/JabRef-${{ steps.gitversion.outputs.Major }}.${{ steps.gitversion.outputs.Minor }}.pkg"
+ - name: Package application image
+ if: (matrix.os != 'macos-latest' && matrix.os != 'self-hosted') || (steps.checksecrets.outputs.secretspresent == 'YES')
+ shell: bash
+ run: ${{ matrix.archivePortable }}
+ - name: Rename files (non-macos)
+ if: (matrix.os != 'macos-latest' && matrix.os != 'self-hosted')
+ shell: pwsh
+ run: |
+ get-childitem -Path build/distribution/* | rename-item -NewName {$_.name -replace "${{ steps.gitversion.outputs.AssemblySemVer }}","${{ steps.gitversion.outputs.Major }}.${{ steps.gitversion.outputs.Minor }}"}
+ get-childitem -Path build/distribution/* | rename-item -NewName {$_.name -replace "portable","${{ steps.gitversion.outputs.Major }}.${{ steps.gitversion.outputs.Minor }}-portable"}
+ - name: Rename files (arm64)
+ if: (matrix.os == 'self-hosted') && (steps.checksecrets.outputs.secretspresent == 'YES')
+ shell: pwsh
+ run: |
+ get-childitem -Path build/distribution/* | rename-item -NewName {$_.name -replace ".dmg", ".${{ matrix.suffix }}.dmg"}
+ get-childitem -Path build/distribution/* | rename-item -NewName {$_.name -replace ".pkg", ".${{ matrix.suffix }}.pkg"}
+ - name: Upload to GitHub workflow artifacts store
+ if: (matrix.os != 'macos-latest' && matrix.os !='self-hosted') || (steps.checksecrets.outputs.secretspresent == 'YES')
+ uses: actions/upload-artifact@v3
+ with:
+ name: JabRef-${{ matrix.displayName }}
+ path: build/distribution
+ deploy:
+ strategy:
+ fail-fast: false
+ name: Deploy binaries on builds.jabref.org
+ runs-on: ubuntu-latest
+ needs: [build]
+ steps:
+ - name: Check secrets presence
+ id: checksecrets
+ shell: bash
+ run: |
+ if [ "$BUILDJABREFPRIVATEKEY" == "" ]; then
+ echo "secretspresent=NO" >> $GITHUB_OUTPUT
+ else
+ echo "secretspresent=YES" >> $GITHUB_OUTPUT
+ fi
+ env:
+ BUILDJABREFPRIVATEKEY: ${{ secrets.buildJabRefPrivateKey }}
+ - name: Checkout source
+ if: steps.checksecrets.outputs.secretspresent == 'YES'
+ uses: actions/checkout@v3
+ - name: Fetch all history for all tags and branches
+ if: steps.checksecrets.outputs.secretspresent == 'YES'
+ run: git fetch --prune --unshallow
+ - name: Install GitVersion
+ if: steps.checksecrets.outputs.secretspresent == 'YES'
+ uses: gittools/actions/gitversion/setup@v0.9.15
+ with:
+ versionSpec: '5.x'
+ - name: Run GitVersion
+ if: steps.checksecrets.outputs.secretspresent == 'YES'
+ id: gitversion
+ uses: gittools/actions/gitversion/execute@v0.9.15
+ - name: Get macOSArm64 binaries
+ if: steps.checksecrets.outputs.secretspresent == 'YES'
+ uses: actions/download-artifact@master
+ with:
+ name: JabRef-macOS (Arm64)
+ path: build/distribution/
+ - name: Deploy to builds.jabref.org
+ id: deploy
+ if: steps.checksecrets.outputs.secretspresent == 'YES'
+ uses: Pendect/action-rsyncer@v2.0.0
+ env:
+ DEPLOY_KEY: ${{ secrets.buildJabRefPrivateKey }}
+ BRANCH: ${{ steps.gitversion.outputs.branchName }}
+ with:
+ flags: -vaz --itemize-changes --stats --partial-dir=/tmp/partial --rsync-path="mkdir -p /var/www/builds.jabref.org/www/${{ steps.gitversion.outputs.branchName }} && rsync"
+ options: ''
+ ssh_options: '-p 9922'
+ src: 'build/distribution/'
+ dest: jrrsync@build-upload.jabref.org:/var/www/builds.jabref.org/www/${{ steps.gitversion.outputs.branchName }}/
diff --git a/.github/workflows/deployment.yml b/.github/workflows/deployment.yml
index 66315e75afe..f73a8a9734d 100644
--- a/.github/workflows/deployment.yml
+++ b/.github/workflows/deployment.yml
@@ -31,13 +31,12 @@ concurrency:
group: ${{ github.ref }}
cancel-in-progress: true
-
jobs:
build:
strategy:
fail-fast: false
matrix:
- os: [ubuntu-latest, windows-latest, macOS-latest]
+ os: [ubuntu-latest, windows-latest, macos-latest]
include:
- os: ubuntu-latest
displayName: linux
@@ -45,23 +44,34 @@ jobs:
- os: windows-latest
displayName: windows
archivePortable: 7z a -r build/distribution/JabRef-portable_windows.zip ./build/distribution/JabRef && rm -R build/distribution/JabRef
- - os: macOS-latest
+ - os: macos-latest
displayName: macOS
archivePortable: brew install pigz && tar -c -C build/distribution JabRef.app | pigz --rsyncable > build/distribution/JabRef-portable_macos.tar.gz && rm -R build/distribution/JabRef.app
runs-on: ${{ matrix.os }}
name: Create installer and portable version for ${{ matrix.displayName }}
steps:
+ - name: Check secrets presence
+ id: checksecrets
+ shell: bash
+ run: |
+ if [ "$BUILDJABREFPRIVATEKEY" == "" ]; then
+ echo "secretspresent=NO" >> $GITHUB_OUTPUT
+ else
+ echo "secretspresent=YES" >> $GITHUB_OUTPUT
+ fi
+ env:
+ BUILDJABREFPRIVATEKEY: ${{ secrets.buildJabRefPrivateKey }}
- name: Fetch all history for all tags and branches
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Install GitVersion
- uses: gittools/actions/gitversion/setup@v0.9.14
+ uses: gittools/actions/gitversion/setup@v0.9.15
with:
versionSpec: "5.x"
- name: Run GitVersion
id: gitversion
- uses: gittools/actions/gitversion/execute@v0.9.14
+ uses: gittools/actions/gitversion/execute@v0.9.15
- name: Set up JDK
uses: actions/setup-java@v3
with:
@@ -69,14 +79,14 @@ jobs:
distribution: 'temurin'
cache: 'gradle'
- name: Setup OSX key chain on OSX
- if: matrix.os == 'macos-latest'
+ if: (matrix.os == 'macos-latest') && (steps.checksecrets.outputs.secretspresent == 'YES')
uses: apple-actions/import-codesign-certs@v1
with:
p12-file-base64: ${{ secrets.OSX_SIGNING_CERT }}
p12-password: ${{ secrets.OSX_CERT_PWD }}
keychain-password: jabref
- name: Setup OSX key chain on OSX for app id cert
- if: matrix.os == 'macos-latest'
+ if: (matrix.os == 'macos-latest') && (steps.checksecrets.outputs.secretspresent == 'YES')
uses: apple-actions/import-codesign-certs@v1
with:
p12-file-base64: ${{ secrets.OSX_SIGNING_CERT_APPLICATION }}
@@ -84,12 +94,14 @@ jobs:
create-keychain: false
keychain-password: jabref
- name: Build runtime image
+ if: (matrix.os != 'macos-latest') || (steps.checksecrets.outputs.secretspresent == 'YES')
run: ./gradlew -i -PprojVersion="${{ steps.gitversion.outputs.AssemblySemVer }}" -PprojVersionInfo="${{ steps.gitversion.outputs.InformationalVersion }}" jlinkZip
- name: Build installer
+ if: (matrix.os != 'macos-latest') || (steps.checksecrets.outputs.secretspresent == 'YES')
run: ./gradlew -i -PprojVersion="${{ steps.gitversion.outputs.AssemblySemVer }}" -PprojVersionInfo="${{ steps.gitversion.outputs.InformationalVersion }}" jpackage
shell: bash
- name: Resign app image for OSX and build dmg
- if: matrix.os == 'macos-latest'
+ if: (matrix.os == 'macos-latest') && (steps.checksecrets.outputs.secretspresent == 'YES')
shell: bash
run: |
codesign --entitlements buildres/mac/jabref.entitlements --options runtime -vvv -f --sign "Developer ID Application: JabRef e.V. (6792V39SK3)" build/distribution/JabRef.app/Contents/runtime/Contents/MacOS/libjli.dylib
@@ -100,7 +112,7 @@ jobs:
jpackage --type pkg --dest build/distribution --name JabRef --mac-package-identifier JabRef --app-version "${{ steps.gitversion.outputs.Major }}.${{ steps.gitversion.outputs.Minor }}" --app-image build/distribution/JabRef.app --verbose --type pkg --vendor JabRef --app-version "${{ steps.gitversion.outputs.Major }}.${{ steps.gitversion.outputs.Minor }}" --file-associations buildres/mac/bibtexAssociations.properties --resource-dir buildres/mac
productsign --sign "Developer ID Installer: JabRef e.V. (6792V39SK3)" "build/distribution/JabRef-${{ steps.gitversion.outputs.Major }}.${{ steps.gitversion.outputs.Minor }}.pkg" "build/distribution/JabRef-${{ steps.gitversion.outputs.Major }}.${{ steps.gitversion.outputs.Minor }}-signed.pkg"
- name: Notarize dmg and pkg installer
- if: matrix.os == 'macos-latest' && startsWith(github.ref, 'refs/tags/')
+ if: (matrix.os == 'macos-latest') && startsWith(github.ref, 'refs/tags/') && (steps.checksecrets.outputs.secretspresent == 'YES')
shell: bash
run: |
REQUEST_UUID_DMG=$(xcrun altool --verbose --notarize-app --primary-bundle-id "org.jabref" --username ${{ secrets.OSX_NOTARIZATION_APP_USERNAME }} --password ${{ secrets.OSX_NOTARIZATION_APP_PWD }} --asc-provider "6792V39SK3" --file "build/distribution/JabRef-${{ steps.gitversion.outputs.Major }}.${{ steps.gitversion.outputs.Minor }}.dmg" | grep RequestUUID | awk '{print $3}')
@@ -121,15 +133,17 @@ jobs:
rm "build/distribution/JabRef-${{ steps.gitversion.outputs.Major }}.${{ steps.gitversion.outputs.Minor }}.pkg"
mv "build/distribution/JabRef-${{ steps.gitversion.outputs.Major }}.${{ steps.gitversion.outputs.Minor }}-signed.pkg" "build/distribution/JabRef-${{ steps.gitversion.outputs.Major }}.${{ steps.gitversion.outputs.Minor }}.pkg"
- name: Package application image
+ if: (matrix.os != 'macos-latest') || (steps.checksecrets.outputs.secretspresent == 'YES')
shell: bash
run: ${{ matrix.archivePortable }}
- name: Rename files
+ if: (matrix.os != 'macos-latest') || (steps.checksecrets.outputs.secretspresent == 'YES')
shell: pwsh
run: |
- get-childitem -Path build/distribution/*
get-childitem -Path build/distribution/* | rename-item -NewName {$_.name -replace "${{ steps.gitversion.outputs.AssemblySemVer }}","${{ steps.gitversion.outputs.Major }}.${{ steps.gitversion.outputs.Minor }}"}
get-childitem -Path build/distribution/* | rename-item -NewName {$_.name -replace "portable","${{ steps.gitversion.outputs.Major }}.${{ steps.gitversion.outputs.Minor }}-portable"}
- name: Upload to GitHub workflow artifacts store
+ if: (matrix.os != 'macos-latest') || (steps.checksecrets.outputs.secretspresent == 'YES')
uses: actions/upload-artifact@v3
with:
name: JabRef-${{ matrix.displayName }}
@@ -146,42 +160,49 @@ jobs:
shell: bash
run: |
if [ "$BUILDJABREFPRIVATEKEY" == "" ]; then
- echo ::set-output name=secretspresent::false
+ echo "secretspresent=NO" >> $GITHUB_OUTPUT
else
- echo ::set-output name=secretspresent::true
+ echo "secretspresent=YES" >> $GITHUB_OUTPUT
fi
env:
BUILDJABREFPRIVATEKEY: ${{ secrets.buildJabRefPrivateKey }}
- name: Checkout source
+ if: steps.checksecrets.outputs.secretspresent == 'YES'
uses: actions/checkout@v3
- name: Fetch all history for all tags and branches
+ if: steps.checksecrets.outputs.secretspresent == 'YES'
run: git fetch --prune --unshallow
- name: Install GitVersion
- uses: gittools/actions/gitversion/setup@v0.9.14
+ if: steps.checksecrets.outputs.secretspresent == 'YES'
+ uses: gittools/actions/gitversion/setup@v0.9.15
with:
versionSpec: '5.x'
- name: Run GitVersion
+ if: steps.checksecrets.outputs.secretspresent == 'YES'
id: gitversion
- uses: gittools/actions/gitversion/execute@v0.9.14
+ uses: gittools/actions/gitversion/execute@v0.9.15
- name: Get linux binaries
+ if: steps.checksecrets.outputs.secretspresent == 'YES'
uses: actions/download-artifact@master
with:
name: JabRef-linux
path: build/distribution
- name: Get windows binaries
+ if: steps.checksecrets.outputs.secretspresent == 'YES'
uses: actions/download-artifact@master
with:
name: JabRef-windows
path: build/distribution
- name: Get macOS binaries
+ if: steps.checksecrets.outputs.secretspresent == 'YES'
uses: actions/download-artifact@master
with:
name: JabRef-macOS
path: build/distribution/
- name: Deploy to builds.jabref.org
id: deploy
- if: ${{ steps.checksecrets.outputs.secretspresent }}
- uses: Pendect/action-rsyncer@v1.1.0
+ if: steps.checksecrets.outputs.secretspresent == 'YES'
+ uses: Pendect/action-rsyncer@v2.0.0
env:
DEPLOY_KEY: ${{ secrets.buildJabRefPrivateKey }}
BRANCH: ${{ steps.gitversion.outputs.branchName }}
diff --git a/.github/workflows/gource.yml b/.github/workflows/gource.yml
index 48c9e55d1e7..05109275365 100644
--- a/.github/workflows/gource.yml
+++ b/.github/workflows/gource.yml
@@ -19,9 +19,9 @@ jobs:
- name: Determine dates
id: dates
run: |
- echo ::set-output name=start_date::`date -d "$(date +%Y-%m-01) -3 months" +%Y-%m-%d`
- echo ::set-output name=stop_date::`date -d "$(date +%Y-%m-01) -1 day" +%Y-%m-%d`
- echo ::set-output name=quarter::`date -d "$(date +%Y-%m-01) -1 day" +%Y`-Q$(( ((`date -d "$(date +%Y-%m-01) -1 day" +%m`)-1)/3+1 ))
+ echo "start_date=`date -d "$(date +%Y-%m-01) -3 months" +%Y-%m-%d`" >> $GITHUB_STATE
+ echo "stop_date=`date -d "$(date +%Y-%m-01) -1 day" +%Y-%m-%d`" >> $GITHUB_STATE
+ echo "quarter=`date -d "$(date +%Y-%m-01) -1 day" +%Y`-Q$(( ((`date -d "$(date +%Y-%m-01) -1 day" +%m`)-1)/3+1 ))" >> $GITHUB_STATE
- name: 'Development history of last quarter'
uses: nbprojekt/gource-action@v1
with:
diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml
index ad5b3fcfba9..1b6c4b6e417 100644
--- a/.github/workflows/pages.yml
+++ b/.github/workflows/pages.yml
@@ -1,6 +1,10 @@
name: Deploy Jekyll site to Pages
on:
+ pull_request:
+ paths:
+ - 'docs/**'
+ - '.github/workflows/pages.yml'
push:
paths:
- 'docs/**'
@@ -27,19 +31,21 @@ jobs:
- name: Setup Ruby
uses: ruby/setup-ruby@v1
with:
- ruby-version: '3.0' # Not needed with a .ruby-version file
+ ruby-version: '2.7' # Not needed with a .ruby-version file
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
- cache-version: 0 # Increment this number if you need to re-download cached gems
+ cache-version: 1 # Increment this number if you need to re-download cached gems
+ working-directory: docs/
- name: Setup Pages
id: pages
uses: actions/configure-pages@v2
- name: Build with Jekyll
- uses: actions/jekyll-build-pages@v1
- with:
- source: docs/
- destination: ./_site
+ run: |
+ cd docs
+ bundle exec jekyll build
- name: Upload artifact
uses: actions/upload-pages-artifact@v1
+ with:
+ path: docs/_site/
deploy:
if: github.ref == 'refs/heads/main'
diff --git a/.github/workflows/snap.yml b/.github/workflows/snap.yml
index 24bdde21743..f858e08d8c9 100644
--- a/.github/workflows/snap.yml
+++ b/.github/workflows/snap.yml
@@ -22,10 +22,10 @@ jobs:
id: checksecrets
shell: bash
run: |
- if [ "SNAPCRAFT_LOGIN_FILE" == "" ]; then
- echo ::set-output name=secretspresent::false
+ if [ "$SNAPCRAFT_LOGIN_FILE" == "" ]; then
+ echo "secretspresent=NO" >> $GITHUB_OUTPUT
else
- echo ::set-output name=secretspresent::true
+ echo "secretspresent=YES" >> $GITHUB_OUTPUT
fi
env:
SNAPCRAFT_LOGIN_FILE: ${{ secrets.SNAPCRAFT_LOGIN_FILE }}
@@ -39,7 +39,7 @@ jobs:
with:
snapcraft-args: "--debug"
- name: Upload snap
- if: ${{ steps.checksecrets.outputs.secretspresent }}
+ if: steps.checksecrets.outputs.secretspresent == 'YES'
uses: snapcore/action-publish@v1
with:
store_login: ${{ secrets.SNAPCRAFT_LOGIN_FILE }}
diff --git a/.github/workflows/tests-fetchers.yml b/.github/workflows/tests-fetchers.yml
index 8bf681fb7cc..66148b73038 100644
--- a/.github/workflows/tests-fetchers.yml
+++ b/.github/workflows/tests-fetchers.yml
@@ -11,10 +11,8 @@ on:
- 'build.gradle'
pull_request:
paths:
- - 'src/main/java/org/jabref/logic/**'
- - 'src/test/java/org/jabref/logic/**'
- - 'src/main/java/org/jabref/model/**'
- - 'src/test/java/org/jabref/model/**'
+ - 'src/main/java/org/jabref/logic/importer/fetcher/**'
+ - 'src/test/java/org/jabref/logic/importer/fetcher/**'
- '.github/workflows/tests-fetchers.yml'
- 'build.gradle'
schedule:
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index 70cad8a62dd..e958a75816e 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -149,10 +149,10 @@ jobs:
id: checksecrets
shell: bash
run: |
- if [ "CODECOV_TOKEN" == "" ]; then
- echo ::set-output name=secretspresent::false
+ if [ "$CODECOV_TOKEN" == "" ]; then
+ echo "secretspresent=NO" >> $GITHUB_OUTPUT
else
- echo ::set-output name=secretspresent::true
+ echo "secretspresent=YES" >> $GITHUB_OUTPUT
fi
env:
SNAPCRAFT_LOGIN_FILE: ${{ secrets.CODECOV_TOKEN }}
@@ -165,7 +165,7 @@ jobs:
distribution: 'temurin'
cache: 'gradle'
- name: Update test coverage metrics
- if: ${{ steps.checksecrets.outputs.secretspresent }}
+ if: steps.checksecrets.outputs.secretspresent == 'YES'
run: xvfb-run --auto-servernum ./gradlew jacocoTestReport && bash <(curl -s https://codecov.io/bash);
env:
CI: "true"
diff --git a/.idea/runConfigurations/JabRef_Main.xml b/.idea/runConfigurations/JabRef_Main.xml
deleted file mode 100644
index 731578b5c75..00000000000
--- a/.idea/runConfigurations/JabRef_Main.xml
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6ed74a68e2c..d67fc90cba2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -11,76 +11,125 @@ Note that this project **does not** adhere to [Semantic Versioning](http://semve
### Added
-- In case a backup is found, the filename of the backup is shown.
-- On startup, JabRef notifies the user if there were parsing errors during opening.
+
+
+
+
+### Changed
+
+- We changed database structure: in MySQL/MariaDB we renamed tables by adding a `JABREF_` prefix, and in PGSQL we moved tables in `jabref` schema. We added `VersionDBStructure` variable in `METADATA` table to indicate current version of structure, this variable is needed for automatic migration [#9312](https://github.com/JabRef/jabref/issues/9312)
+- We moved some preferences options to a new tab in the preferences dialog. [#9442](https://github.com/JabRef/jabref/pull/9308)
+
+
+
+
+
+### Fixed
+
+- The tab "deprecated fields" is shown in biblatex-mode only. [#7757](https://github.com/JabRef/jabref/issues/7757)
+
+
+
+### Removed
+
+
+
+
+
+
+
+
+## [5.8] - 2022-12-18
+
+### Added
+
- We integrated a new three-way merge UI for merging entries in the Entries Merger Dialog, the Duplicate Resolver Dialog, the Entry Importer Dialog, and the External Changes Resolver Dialog. [#8945](https://github.com/JabRef/jabref/pull/8945)
- We added the ability to merge groups, keywords, comments and files when merging entries. [#9022](https://github.com/JabRef/jabref/pull/9022)
- We added a warning message next to the authors field in the merge dialog to warn users when the authors are the same but formatted differently. [#8745](https://github.com/JabRef/jabref/issues/8745)
-- The properties of an existing systematic literature review can be edited. [koppor#604](https://github.com/koppor/jabref/issues/604)
-- An SLR can now be started from the SLR itself. [#9131](https://github.com/JabRef/jabref/pull/9131), [koppor#601](https://github.com/koppor/jabref/issues/601)
-- Implement a new ISBN Fetcher ([doi-to-bibtex-converter.herokuapp.com](http://doi-to-bibtex-converter.herokuapp.com) as source). [#9145](https://github.com/JabRef/jabref/pull/9145)
+- The default file directory of a library is used as default directory for [unlinked file lookup](https://docs.jabref.org/collect/findunlinkedfiles#link-the-pdfs-to-your-bib-library). [koppor#546](https://github.com/koppor/jabref/issues/546)
+- The properties of an existing systematic literature review (SLR) can be edited. [koppor#604](https://github.com/koppor/jabref/issues/604)
+- An systematic literature review (SLR) can now be started from the SLR itself. [#9131](https://github.com/JabRef/jabref/pull/9131), [koppor#601](https://github.com/koppor/jabref/issues/601)
+- On startup, JabRef notifies the user if there were parsing errors during opening.
+- We added support for the field `fjournal` (in `@article`) for abbreviation and unabbreviation functionalities. [#321](https://github.com/JabRef/jabref/pull/321)
+- In case a backup is found, the filename of the backup is shown and one can navigate to the file. [#9311](https://github.com/JabRef/jabref/pull/9311)
- We added support for the Ukrainian and Arabic languages. [#9236](https://github.com/JabRef/jabref/pull/9236), [#9243](https://github.com/JabRef/jabref/pull/9243)
### Changed
-- We disabled the "change case" menu for empty fields [#9214](https://github.com/JabRef/jabref/issues/9214)
-- We disabled the conversion menu for empty fields [#9200](https://github.com/JabRef/jabref/issues/9200)
-- We upgraded to Lucene 9.4 for the fulltext search.
- Thus, the now created search index cannot be read from older versions of JabRef anylonger.
- ⚠️ JabRef will recreate the index in a new folder for new files and this will take a long time for a huge library.
- Moreover, switching back and forth JabRef versions and meanwhile adding PDFs also requires rebuilding the index now and then.
- [#9213](https://github.com/JabRef/jabref/pull/9213)
-- Genres are now mapped correctly to entry types when importing MODS files. [#9185](https://github.com/JabRef/jabref/issues/9185)
- We improved the Citavi Importer to also import so called Knowledge-items into the field `comment` of the corresponding entry [#9025](https://github.com/JabRef/jabref/issues/9025)
-- We removed wrapping of string constants when writing to a `.bib` file.
+- We modified the change case sub-menus and their corresponding tips (displayed when you stay long over the menu) to properly reflect exemplified cases. [#9339](https://github.com/Jabref/jabref/issues/9339)
- We call backup files `.bak` and temporary writing files now `.sav`.
- JabRef keeps 10 older versions of a `.bib` file in the [user data dir](https://github.com/harawata/appdirs#supported-directories) (instead of a single `.sav` (now: `.bak`) file in the directory of the `.bib` file)
-- We changed the button label from "Return to JabRef" to "Return to library" to better indicate the purpose of the action.
-- A user can now add arbitrary data into `study.yml`. JabRef just ignores this data. [#9124](https://github.com/JabRef/jabref/pull/9124)
-- We reworked the External Changes Resolver dialog. [#9021](https://github.com/JabRef/jabref/pull/9021)
+- We improved the External Changes Resolver dialog to be more usaable. [#9021](https://github.com/JabRef/jabref/pull/9021)
+- We simplified the actions to fast-resolve duplicates to 'Keep Left', 'Keep Right', 'Keep Both' and 'Keep Merged'. [#9056](https://github.com/JabRef/jabref/issues/9056)
- The fallback directory of the file folder now is the general file directory. In case there was a directory configured for a library and this directory was not found, JabRef placed the PDF next to the .bib file and not into the general file directory.
- The global default directory for storing PDFs is now the documents folder in the user's home.
-- We reworked the Define study parameters dialog. [#9123](https://github.com/JabRef/jabref/pull/9123)
-- We simplified the actions to fast-resolve duplicates to 'Keep Left', 'Keep Right', 'Keep Both' and 'Keep Merged'. [#9056](https://github.com/JabRef/jabref/issues/9056)
-- We fixed an issue where a message about changed metadata would occur on saving although nothing changed. [#9159](https://github.com/JabRef/jabref/issues/9159)
- When adding or editing a subgroup it is placed w.r.t. to alphabetical ordering rather than at the end. [koppor#577](https://github.com/koppor/jabref/issues/577)
+- Groups context menu now shows appropriate options depending on number of subgroups. [koppor#579](https://github.com/koppor/jabref/issues/579)
+- We modified the "Delete file" dialog and added the full file path to the dialog text. The file path in the title was changed to file name only. [koppor#534](https://github.com/koppor/jabref/issues/534)
+- Download from URL now automatically fills with URL from clipboard. [koppor#535](https://github.com/koppor/jabref/issues/535)
+- We added HTML and Markdown files to Find Unlinked Files and removed BibTeX. [koppor#547](https://github.com/koppor/jabref/issues/547)
+- ArXiv fetcher now retrieves additional data from related DOIs (both ArXiv and user-assigned). [#9170](https://github.com/JabRef/jabref/pull/9170)
- We modified the Directory of Open Access Books (DOAB) fetcher so that it will now also fetch the ISBN when possible. [#8708](https://github.com/JabRef/jabref/issues/8708)
+- Genres are now mapped correctly to entry types when importing MODS files. [#9185](https://github.com/JabRef/jabref/issues/9185)
+- We changed the button label from "Return to JabRef" to "Return to library" to better indicate the purpose of the action.
+- We changed the color of found text from red to high-contrast colors (background: yellow; font color: purple). [koppor#552](https://github.com/koppor/jabref/issues/552)
+- We fixed an issue where the wrong icon for a successful import of a bib entry was shown. [#9308](https://github.com/JabRef/jabref/pull/9308)
+- We changed the messages after importing unlinked local files to past tense. [koppor#548](https://github.com/koppor/jabref/issues/548)
+- We fixed an issue where the wrong icon for a successful import of a bib entry was shown [#9308](https://github.com/JabRef/jabref/pull/9308)
+- In the context of the [Cleanup dialog](https://docs.jabref.org/finding-sorting-and-cleaning-entries/cleanupentries) we changed the text of the conversion of BibTeX to biblatex (and vice versa) to make it more clear. [koppor#545](https://github.com/koppor/jabref/issues/545)
+- We removed wrapping of string constants when writing to a `.bib` file.
+- In the context of a systematic literature review (SLR), a user can now add arbitrary data into `study.yml`. JabRef just ignores this data. [#9124](https://github.com/JabRef/jabref/pull/9124)
+- In the context of a systematic literature review (SLR), we reworked the "Define study" parameters dialog. [#9123](https://github.com/JabRef/jabref/pull/9123)
+- We upgraded to Lucene 9.4 for the fulltext search. The search index will be rebuild. [#9213](https://github.com/JabRef/jabref/pull/9213)
+- We disabled the "change case" menu for empty fields. [#9214](https://github.com/JabRef/jabref/issues/9214)
+- We disabled the conversion menu for empty fields. [#9200](https://github.com/JabRef/jabref/issues/9200)
### Fixed
-- We fixed the Cleanup entries dialog is partially visible [#9223](https://github.com/JabRef/jabref/issues/9223)
-- We fixed the display of the "Customize Entry Types" dialogue title [#9198](https://github.com/JabRef/jabref/issues/9198)
-- We fixed an issue where author names with tilde accents (for example ñ) were marked as "Names are not in the standard BibTex format" [#8071](https://github.com/JabRef/jabref/issues/8071)
-- We fixed an issue where the possibility to generate a subdatabase from an aux file was writing empty files when called from the commandline [#9115](https://github.com/JabRef/jabref/issues/9115), [forum#3516](https://discourse.jabref.org/t/export-subdatabase-from-aux-file-on-macos-command-line/3516)
+- We fixed an issue where applied save actions on saving the library file would lead to the dialog "The library has been modified by another program" popping up. [#4877](https://github.com/JabRef/jabref/issues/4877)
+- We fixed issues with save actions not correctly loaded when opening the library. [#9122](https://github.com/JabRef/jabref/pull/9122)
+- We fixed the behavior of "Discard changes" when reopening a modified library. [#9361](https://github.com/JabRef/jabref/issues/9361)
+- We fixed several bugs regarding the manual and the autosave of library files that could lead to exceptions. [#9067](https://github.com/JabRef/jabref/pull/9067), [#8448](https://github.com/JabRef/jabref/issues/8484), [#8746](https://github.com/JabRef/jabref/issues/8746), [#6684](https://github.com/JabRef/jabref/issues/6684), [#6644](https://github.com/JabRef/jabref/issues/6644), [#6102](https://github.com/JabRef/jabref/issues/6102), [#6002](https://github.com/JabRef/jabref/issues/6000)
+- We fixed an issue where pdfs were re-indexed on each startup. [#9166](https://github.com/JabRef/jabref/pull/9166)
+- We fixed an issue when using an unsafe character in the citation key, the auto-linking feature fails to link files. [#9267](https://github.com/JabRef/jabref/issues/9267)
+- We fixed an issue where a message about changed metadata would occur on saving although nothing changed. [#9159](https://github.com/JabRef/jabref/issues/9159)
+- We fixed an issue where the possibility to generate a subdatabase from an aux file was writing empty files when called from the commandline. [#9115](https://github.com/JabRef/jabref/issues/9115), [forum#3516](https://discourse.jabref.org/t/export-subdatabase-from-aux-file-on-macos-command-line/3516)
+- We fixed an issue where author names with tilde accents (for example ñ) were marked as "Names are not in the standard BibTeX format". [#8071](https://github.com/JabRef/jabref/issues/8071)
+- We fixed an issue where capitalize didn't capitalize words after hyphen characters. [#9157](https://github.com/JabRef/jabref/issues/9157)
+- We fixed an issue where title case didn't capitalize words after en-dash characters and skip capitalization of conjunctions that comes after en-dash characters. [#9068](https://github.com/JabRef/jabref/pull/9068),[#9142](https://github.com/JabRef/jabref/pull/9142)
+- We fixed an issue with the message that is displayed when fetcher returns an empty list of entries for given query. [#9195](https://github.com/JabRef/jabref/issues/9195)
+- We fixed an issue where editing entry's "date" field in library mode "biblatex" causes an uncaught exception. [#8747](https://github.com/JabRef/jabref/issues/8747)
+- We fixed an issue where importing from XMP would fail for certain PDFs. [#9383](https://github.com/JabRef/jabref/issues/9383)
+- We fixed an issue that JabRef displayed the wrong group tree after loading. [koppor#637](https://github.com/koppor/jabref/issues/637)
+- We fixed that sorting of entries in the maintable by special fields is updated immediately. [#9334](https://github.com/JabRef/jabref/issues/9334)
- We fixed the display of issue, number, eid and pages fields in the entry preview. [#8607](https://github.com/JabRef/jabref/pull/8607), [#8372](https://github.com/JabRef/jabref/issues/8372), [Koppor#514](https://github.com/koppor/jabref/issues/514), [forum#2390](https://discourse.jabref.org/t/unable-to-edit-my-bibtex-file-that-i-used-before-vers-5-1/2390), [forum#3462](https://discourse.jabref.org/t/jabref-5-6-need-help-with-export-from-jabref-to-microsoft-word-entry-preview-of-apa-7-not-rendering-correctly/3462)
- We fixed the page ranges checker to detect article numbers in the pages field (used at [Check Integrity](https://docs.jabref.org/finding-sorting-and-cleaning-entries/checkintegrity)). [#8607](https://github.com/JabRef/jabref/pull/8607)
- The [HtmlToLaTeXFormatter](https://docs.jabref.org/finding-sorting-and-cleaning-entries/saveactions#html-to-latex) keeps single `<` characters.
-- We fixed a performance regression when opening large libraries [#9041](https://github.com/JabRef/jabref/issues/9041)
+- We fixed a performance regression when opening large libraries. [#9041](https://github.com/JabRef/jabref/issues/9041)
- We fixed a bug where spaces are trimmed when highlighting differences in the Entries merge dialog. [koppor#371](https://github.com/koppor/jabref/issues/371)
- We fixed some visual glitches with the linked files editor field in the entry editor and increased its height. [#8823](https://github.com/JabRef/jabref/issues/8823)
-- We fixed several bugs regarding the manual and the autosave of library files that sometimes lead to exceptions or data loss. [#9067](https://github.com/JabRef/jabref/pull/9067), [#8448](https://github.com/JabRef/jabref/issues/8484), [#8746](https://github.com/JabRef/jabref/issues/8746), [#6684](https://github.com/JabRef/jabref/issues/6684), [#6644](https://github.com/JabRef/jabref/issues/6644), [#6102](https://github.com/JabRef/jabref/issues/6102), [#6002](https://github.com/JabRef/jabref/issues/6000)
-- We fixed an issue where applied save actions on saving the library file would lead to the dialog "The library has been modified by another program" popping up [#4877](https://github.com/JabRef/jabref/issues/4877)
-- We fixed issues with save actions not correctly loaded when opening the library. [#9122](https://github.com/JabRef/jabref/pull/9122)
-- We fixed an issue where title case didn't capitalize words after en-dash characters and skip capitalization of conjunctions that comes after en-dash characters. [#9068](https://github.com/JabRef/jabref/pull/9068),[#9142](https://github.com/JabRef/jabref/pull/9142)
+- We fixed some visual inconsistencies (round corners of highlighted buttons). [#8806](https://github.com/JabRef/jabref/issues/8806)
- We fixed an issue where JabRef would not exit when a connection to a LibreOffice document was established previously and the document is still open. [#9075](https://github.com/JabRef/jabref/issues/9075)
- We fixed an issue about selecting the save order in the preferences. [#9175](https://github.com/JabRef/jabref/issues/9147)
-- We fixed an issue where the CSS styles are missing in some dialogs. [#9150](https://github.com/JabRef/jabref/pull/9150)
-- We fixed an issue where pdfs were re-indexed on each startup. [#9166](https://github.com/JabRef/jabref/pull/9166)
-- We fixed an issue where Capitalize didn't capitalize words after hyphen characters. [#9157](https://github.com/JabRef/jabref/issues/9157)
-- We fixed an issue with the message that is displayed when fetcher returns an empty list of entries for given query. [#9195](https://github.com/JabRef/jabref/issues/9195)
-- We fixed an issue where an exception was not logged correctly. [koppor#627](https://github.com/JabRef/koppor/issues/627)
-- We fixed an issue where hitting enter on the search field within the preferences dialog closed the dialog. [koppor#630](https://github.com/koppor/jabref/issues/630)
+- We fixed an issue where an exception when fetching a DOI was not logged correctly. [koppor#627](https://github.com/koppor/jabref/issues/627)
+- We fixed an issue where a user could not open an attached file in a new unsaved library. [#9386](https://github.com/JabRef/jabref/issues/9386)
- We fixed a typo within a connection error message. [koppor#625](https://github.com/koppor/jabref/issues/625)
+- We fixed an issue where journal abbreviations would not abbreviate journal titles with escaped ampersands (\\&). [#8948](https://github.com/JabRef/jabref/issues/8948)
+- We fixed the readability of the file field in the dark theme. [#9340](https://github.com/JabRef/jabref/issues/9340)
- We fixed an issue where the 'close dialog' key binding was not closing the Preferences dialog. [#8888](https://github.com/jabref/jabref/issues/8888)
+- We fixed an issue where a known journal's medline/dot-less abbreviation does not switch to the full name. [#9370](https://github.com/JabRef/jabref/issues/9370)
+- We fixed an issue where hitting enter on the search field within the preferences dialog closed the dialog. [koppor#630](https://github.com/koppor/jabref/issues/630)
+- We fixed the "Cleanup entries" dialog is partially visible. [#9223](https://github.com/JabRef/jabref/issues/9223)
+- We fixed an issue where font size preferences did not apply correctly to preference dialog window and the menu bar. [#8386](https://github.com/JabRef/jabref/issues/8386) and [#9279](https://github.com/JabRef/jabref/issues/9279)
+- We fixed the display of the "Customize Entry Types" dialog title. [#9198](https://github.com/JabRef/jabref/issues/9198)
+- We fixed an issue where the CSS styles are missing in some dialogs. [#9150](https://github.com/JabRef/jabref/pull/9150)
+- We fixed an issue where controls in the preferences dialog could outgrow the window. [#9017](https://github.com/JabRef/jabref/issues/9017)
### Removed
-- We removed "last-search-date" from the SLR feature, because the last-search-date can be deducted from the git logs. [#9116](https://github.com/JabRef/jabref/pull/9116)
-
-
-
-
-
+- We removed "last-search-date" from the systematic literature review feature, because the last-search-date can be deducted from the git logs. [#9116](https://github.com/JabRef/jabref/pull/9116)
+- We removed the [CiteseerX](https://docs.jabref.org/collect/import-using-online-bibliographic-database#citeseerx) fetcher, because the API used by JabRef is sundowned. [#9466](https://github.com/JabRef/jabref/pull/9466)
## [5.7] - 2022-08-05
@@ -917,7 +966,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.7...HEAD
+[Unreleased]: https://github.com/JabRef/jabref/compare/v5.8...HEAD
+[5.8]: https://github.com/JabRef/jabref/compare/v5.7...v5.8
[5.7]: https://github.com/JabRef/jabref/compare/v5.6...v5.7
[5.6]: https://github.com/JabRef/jabref/compare/v5.5...v5.6
[5.5]: https://github.com/JabRef/jabref/compare/v5.4...v5.5
diff --git a/MAINTAINERS b/MAINTAINERS
index baec36a6925..8934ad98274 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -8,3 +8,4 @@ Carl Christian Snethlage (since 2020)
Dominik Voigt (since 2020)
Jonatan Askertop (since 2021)
Benedikt Tutzer (since 2021)
+Thilo Ertel (since 2021)
diff --git a/build.gradle b/build.gradle
index f9dcfee846c..8e2e220b433 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.6.2'
+ id 'com.github.andygoossens.modernizer' version '1.7.0'
id 'me.champeau.gradle.jmh' version '0.5.3'
@@ -40,8 +40,8 @@ group = "org.jabref"
version = project.findProperty('projVersion') ?: '100.0.0'
java {
- sourceCompatibility = JavaVersion.VERSION_18
- targetCompatibility = JavaVersion.VERSION_18
+ sourceCompatibility = JavaVersion.VERSION_17
+ targetCompatibility = JavaVersion.VERSION_17
// 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 )
@@ -115,17 +115,17 @@ dependencies {
implementation 'org.apache.pdfbox:fontbox:3.0.0-RC1'
implementation 'org.apache.pdfbox:xmpbox:3.0.0-RC1'
- implementation 'org.apache.lucene:lucene-core:9.4.0'
- implementation 'org.apache.lucene:lucene-queryparser:9.4.0'
- implementation 'org.apache.lucene:lucene-queries:9.4.0'
- implementation 'org.apache.lucene:lucene-analysis-common:9.4.0'
- implementation 'org.apache.lucene:lucene-highlighter:9.4.0'
+ implementation 'org.apache.lucene:lucene-core:9.4.1'
+ implementation 'org.apache.lucene:lucene-queryparser:9.4.2'
+ implementation 'org.apache.lucene:lucene-queries:9.4.1'
+ implementation 'org.apache.lucene:lucene-analysis-common:9.4.2'
+ implementation 'org.apache.lucene:lucene-highlighter:9.4.2'
implementation group: 'org.apache.commons', name: 'commons-csv', version: '1.9.0'
implementation group: 'org.apache.commons', name: 'commons-lang3', version: '3.12.0'
implementation 'com.h2database:h2-mvstore:2.1.214'
- implementation group: 'org.apache.tika', name: 'tika-core', version: '2.5.0'
+ implementation group: 'org.apache.tika', name: 'tika-core', version: '2.6.0'
// required for reading write-protected PDFs - see https://github.com/JabRef/jabref/pull/942#issuecomment-209252635
implementation 'org.bouncycastle:bcprov-jdk18on:1.71.1'
@@ -141,14 +141,14 @@ dependencies {
antlr4 'org.antlr:antlr4:4.9.3'
implementation 'org.antlr:antlr4-runtime:4.9.3'
- implementation group: 'org.eclipse.jgit', name: 'org.eclipse.jgit', version: '6.3.0.202209071007-r'
+ implementation group: 'org.eclipse.jgit', name: 'org.eclipse.jgit', version: '6.4.0.202211300538-r'
- implementation group: 'com.fasterxml.jackson.dataformat', name: 'jackson-dataformat-yaml', version: '2.13.4'
- implementation group: 'com.fasterxml.jackson.datatype', name: 'jackson-datatype-jsr310', version: '2.13.4'
+ implementation group: 'com.fasterxml.jackson.dataformat', name: 'jackson-dataformat-yaml', version: '2.14.1'
+ implementation group: 'com.fasterxml.jackson.datatype', name: 'jackson-datatype-jsr310', version: '2.14.1'
- implementation group: 'org.mariadb.jdbc', name: 'mariadb-java-client', version: '2.7.6'
+ implementation group: 'org.mariadb.jdbc', name: 'mariadb-java-client', version: '2.7.7'
- implementation 'org.postgresql:postgresql:42.5.0'
+ implementation 'org.postgresql:postgresql:42.5.1'
implementation ('com.oracle.ojdbc:ojdbc10:19.3.0.0') {
// causing module issues
@@ -170,15 +170,15 @@ dependencies {
implementation 'com.github.sialcasa.mvvmFX:mvvmfx-validation:f195849ca9' //jitpack
implementation 'de.saxsys:mvvmfx:1.8.0'
implementation 'com.tobiasdiez:easybind:2.2.1-SNAPSHOT'
- implementation 'org.fxmisc.flowless:flowless:0.6.10'
- implementation 'org.fxmisc.richtext:richtextfx:0.10.9'
+ implementation 'org.fxmisc.flowless:flowless:0.7.0'
+ implementation 'org.fxmisc.richtext:richtextfx:0.11.0'
implementation 'com.jfoenix:jfoenix:9.0.10'
implementation 'org.controlsfx:controlsfx:11.1.2'
implementation 'org.jsoup:jsoup:1.15.3'
- implementation 'com.konghq:unirest-java:3.13.11'
+ implementation 'com.konghq:unirest-java:3.14.1'
- implementation 'org.slf4j:slf4j-api:2.0.3'
+ implementation 'org.slf4j:slf4j-api:2.0.5'
implementation "org.tinylog:tinylog-api:2.5.0"
implementation "org.tinylog:slf4j-tinylog:2.5.0"
implementation "org.tinylog:tinylog-impl:2.5.0"
@@ -204,20 +204,20 @@ dependencies {
implementation group: 'net.harawata', name: 'appdirs', version: '1.2.1'
- testImplementation 'io.github.classgraph:classgraph:4.8.149'
+ testImplementation 'io.github.classgraph:classgraph:4.8.152'
testImplementation 'org.junit.jupiter:junit-jupiter:5.9.1'
testImplementation 'org.junit.platform:junit-platform-launcher:1.9.1'
- testImplementation 'org.mockito:mockito-core:4.8.0'
+ testImplementation 'org.mockito:mockito-core:4.10.0'
testImplementation 'org.xmlunit:xmlunit-core:2.9.0'
testImplementation 'org.xmlunit:xmlunit-matchers:2.9.0'
- testRuntimeOnly 'com.tngtech.archunit:archunit-junit5-engine:1.0.0'
- testImplementation 'com.tngtech.archunit:archunit-junit5-api:1.0.0'
+ testRuntimeOnly 'com.tngtech.archunit:archunit-junit5-engine:1.0.1'
+ testImplementation 'com.tngtech.archunit:archunit-junit5-api:1.0.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"
- checkstyle 'com.puppycrawl.tools:checkstyle:10.3.4'
+ checkstyle 'com.puppycrawl.tools:checkstyle:10.5.0'
// xjc needs the runtime as well for the ant task, otherwise it fails
xjc group: 'org.glassfish.jaxb', name: 'jaxb-xjc', version: '3.0.2'
xjc group: 'org.glassfish.jaxb', name: 'jaxb-runtime', version: '3.0.2'
diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle
index 3e9f9aa812c..a279e5caa28 100644
--- a/buildSrc/build.gradle
+++ b/buildSrc/build.gradle
@@ -8,7 +8,7 @@ repositories {
dependencies {
implementation 'com.h2database:h2-mvstore:2.1.214'
implementation 'org.apache.commons:commons-csv:1.9.0'
- implementation 'org.slf4j:slf4j-api:2.0.3'
+ implementation 'org.slf4j:slf4j-api:2.0.6'
}
sourceSets{
diff --git a/buildSrc/src/copied/java/org/jabref/logic/journals/Abbreviation.java b/buildSrc/src/copied/java/org/jabref/logic/journals/Abbreviation.java
index b6c8d4ddf89..7a7e4d9cf30 100644
--- a/buildSrc/src/copied/java/org/jabref/logic/journals/Abbreviation.java
+++ b/buildSrc/src/copied/java/org/jabref/logic/journals/Abbreviation.java
@@ -72,7 +72,7 @@ public boolean equals(Object obj) {
return true;
}
- if (obj == null || getClass() != obj.getClass()) {
+ if ((obj == null) || (getClass() != obj.getClass())) {
return false;
}
diff --git a/buildSrc/src/copied/java/org/jabref/logic/journals/JournalAbbreviationLoader.java b/buildSrc/src/copied/java/org/jabref/logic/journals/JournalAbbreviationLoader.java
index cf7d6a177ed..76ba9096791 100644
--- a/buildSrc/src/copied/java/org/jabref/logic/journals/JournalAbbreviationLoader.java
+++ b/buildSrc/src/copied/java/org/jabref/logic/journals/JournalAbbreviationLoader.java
@@ -52,6 +52,6 @@ public static JournalAbbreviationRepository loadRepository(JournalAbbreviationPr
}
public static JournalAbbreviationRepository loadBuiltInRepository() {
- return loadRepository(new JournalAbbreviationPreferences(Collections.emptyList(), StandardCharsets.UTF_8));
+ return loadRepository(new JournalAbbreviationPreferences(Collections.emptyList(), StandardCharsets.UTF_8, true));
}
}
diff --git a/buildSrc/src/copied/java/org/jabref/logic/journals/JournalAbbreviationPreferences.java b/buildSrc/src/copied/java/org/jabref/logic/journals/JournalAbbreviationPreferences.java
index 300592b0172..442fda33ddc 100644
--- a/buildSrc/src/copied/java/org/jabref/logic/journals/JournalAbbreviationPreferences.java
+++ b/buildSrc/src/copied/java/org/jabref/logic/journals/JournalAbbreviationPreferences.java
@@ -7,10 +7,12 @@ public class JournalAbbreviationPreferences {
private final Charset defaultEncoding;
private List externalJournalLists;
+ private boolean useFJournalField;
- public JournalAbbreviationPreferences(List externalJournalLists, Charset defaultEncoding) {
+ public JournalAbbreviationPreferences(List externalJournalLists, Charset defaultEncoding, boolean useFJournalField) {
this.externalJournalLists = externalJournalLists;
this.defaultEncoding = defaultEncoding;
+ this.useFJournalField = useFJournalField;
}
public List getExternalJournalLists() {
@@ -24,4 +26,12 @@ public void setExternalJournalLists(List externalJournalLists) {
public Charset getDefaultEncoding() {
return defaultEncoding;
}
+
+ public boolean useAMSFJournalFieldForAbbrevAndUnabbrev() {
+ return useFJournalField;
+ }
+
+ public void setUseAMSFJournalFieldForAbbrevAndUnabbrev(boolean useFJournalField) {
+ this.useFJournalField = useFJournalField;
+ }
}
diff --git a/buildSrc/src/copied/java/org/jabref/logic/journals/JournalAbbreviationRepository.java b/buildSrc/src/copied/java/org/jabref/logic/journals/JournalAbbreviationRepository.java
index 523e1323be1..0437e1ae2ae 100644
--- a/buildSrc/src/copied/java/org/jabref/logic/journals/JournalAbbreviationRepository.java
+++ b/buildSrc/src/copied/java/org/jabref/logic/journals/JournalAbbreviationRepository.java
@@ -7,6 +7,8 @@
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import java.util.stream.Collectors;
import org.h2.mvstore.MVMap;
@@ -16,6 +18,8 @@
* A repository for all journal abbreviations, including add and find methods.
*/
public class JournalAbbreviationRepository {
+ static final Pattern DOT = Pattern.compile("\\.");
+ static final Pattern QUESTION_MARK = Pattern.compile("\\?");
private final MVMap fullToAbbreviation;
private final MVMap abbreviationToFull;
@@ -48,14 +52,20 @@ private static boolean isMatchedAbbreviated(String name, Abbreviation abbreviati
* Letters) or its abbreviated form (e.g. Phys. Rev. Lett.).
*/
public boolean isKnownName(String journalName) {
- String journal = journalName.trim();
+ // check for at least one "?"
+ if (QUESTION_MARK.matcher(journalName).find()) {
+ return false;
+ }
+
+ String journal = journalName.trim().replaceAll(Matcher.quoteReplacement("\\&"), "&");
boolean isKnown = customAbbreviations.stream().anyMatch(abbreviation -> isMatched(journal, abbreviation));
if (isKnown) {
return true;
}
- return fullToAbbreviation.containsKey(journal) || abbreviationToFull.containsKey(journal);
+ return fullToAbbreviation.containsKey(journal) || abbreviationToFull.containsKey(journal)
+ || findDottedAbbrFromDotless(journal).length() > 0;
}
/**
@@ -65,8 +75,40 @@ public boolean isKnownName(String journalName) {
public boolean isAbbreviatedName(String journalName) {
String journal = journalName.trim();
+ // journal abbreviation must be at least 2 words
+ boolean isMoreThanTwoWords = journalName.split(" ").length >= 2;
+
return customAbbreviations.stream().anyMatch(abbreviation -> isMatchedAbbreviated(journal, abbreviation))
- || abbreviationToFull.containsKey(journal);
+ || abbreviationToFull.containsKey(journal)
+ || (isMoreThanTwoWords && findDottedAbbrFromDotless(journal).length() > 0);
+ }
+
+ public String findDottedAbbrFromDotless(String journalName) {
+ // check for at least one "?"
+ if (QUESTION_MARK.matcher(journalName).find()) {
+ return "UNKNOWN";
+ }
+
+ String foundKey = "";
+
+ // check for a dot-less abbreviation
+ if (!DOT.matcher(journalName).find()) {
+ // use dot-less abbr to find full name using regex
+ String[] journalSplit = journalName.split(" ");
+
+ for (int i = 0; i < journalSplit.length; i++) {
+ String word = journalSplit[i] + "[\\.\\s]*";
+ journalSplit[i] = word;
+ }
+
+ String joined = String.join("", journalSplit);
+
+ foundKey = abbreviationToFull.keySet().stream()
+ .filter(s -> Pattern.compile(joined).matcher(s).find())
+ .collect(Collectors.joining());
+ }
+
+ return foundKey;
}
/**
@@ -75,7 +117,7 @@ public boolean isAbbreviatedName(String journalName) {
* @param input The journal name (either abbreviated or full name).
*/
public Optional get(String input) {
- String journal = input.trim();
+ String journal = input.trim().replaceAll(Matcher.quoteReplacement("\\&"), "&");
Optional customAbbreviation = customAbbreviations.stream()
.filter(abbreviation -> isMatched(journal, abbreviation))
@@ -86,7 +128,17 @@ public Optional get(String input) {
return Optional.ofNullable(fullToAbbreviation.get(journal))
.map(abbreviation -> new Abbreviation(journal, abbreviation))
- .or(() -> Optional.ofNullable(abbreviationToFull.get(journal)).map(fullName -> new Abbreviation(fullName, journal)));
+ .or(() -> {
+ String abbr = "";
+
+ // check for dot-less abbr
+ if (isKnownName(journal) && isAbbreviatedName(journal)) {
+ abbr = findDottedAbbrFromDotless(journal);
+ }
+
+ return Optional.ofNullable(abbreviationToFull.get(abbr.equals("") ? journal : abbr))
+ .map(fullName -> new Abbreviation(fullName, journal));
+ });
}
public void addCustomAbbreviation(Abbreviation abbreviation) {
diff --git a/buildres/csl/csl-locales/.github/workflows/merge.yaml b/buildres/csl/csl-locales/.github/workflows/merge.yaml
index c1efb7eab07..d61a1eaef56 100644
--- a/buildres/csl/csl-locales/.github/workflows/merge.yaml
+++ b/buildres/csl/csl-locales/.github/workflows/merge.yaml
@@ -141,7 +141,7 @@ jobs:
- name: Bump version and push tag
id: tag_version
- uses: mathieudutour/github-tag-action@v6.0
+ uses: mathieudutour/github-tag-action@v6.1
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
default_bump: patch
diff --git a/buildres/csl/csl-styles/.github/workflows/merge.yaml b/buildres/csl/csl-styles/.github/workflows/merge.yaml
index c1efb7eab07..d61a1eaef56 100644
--- a/buildres/csl/csl-styles/.github/workflows/merge.yaml
+++ b/buildres/csl/csl-styles/.github/workflows/merge.yaml
@@ -141,7 +141,7 @@ jobs:
- name: Bump version and push tag
id: tag_version
- uses: mathieudutour/github-tag-action@v6.0
+ uses: mathieudutour/github-tag-action@v6.1
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
default_bump: patch
diff --git a/buildres/csl/csl-styles/Gemfile.lock b/buildres/csl/csl-styles/Gemfile.lock
index dde35577b7f..8414bfbd6a4 100644
--- a/buildres/csl/csl-styles/Gemfile.lock
+++ b/buildres/csl/csl-styles/Gemfile.lock
@@ -65,17 +65,17 @@ GEM
mini_portile2 (2.8.0)
multipart-post (2.1.1)
namae (1.1.1)
- nokogiri (1.13.6)
+ nokogiri (1.13.10)
mini_portile2 (~> 2.8.0)
racc (~> 1.4)
- nokogiri (1.13.6-x64-mingw32)
+ nokogiri (1.13.10-x64-mingw32)
racc (~> 1.4)
octokit (4.21.0)
faraday (>= 0.9)
sawyer (~> 0.8.0, >= 0.5.3)
ostruct (0.5.2)
public_suffix (4.0.6)
- racc (1.6.0)
+ racc (1.6.1)
rake (13.0.6)
reverse_markdown (2.1.1)
nokogiri
diff --git a/buildres/csl/csl-styles/american-chemical-society.csl b/buildres/csl/csl-styles/american-chemical-society.csl
index b7d421858bd..c3b618341fb 100644
--- a/buildres/csl/csl-styles/american-chemical-society.csl
+++ b/buildres/csl/csl-styles/american-chemical-society.csl
@@ -18,10 +18,13 @@
Sebastian Karcher
+
+ Patrick O'Brien
+
The American Chemical Society style
- 2021-05-22T12:00:00+00:00
+ 2022-09-19T18:32:56+00:00
This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 License
@@ -138,6 +141,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -249,18 +276,10 @@
-
+
-
-
-
-
-
-
-
-
@@ -277,7 +296,7 @@
-
+
diff --git a/buildres/csl/csl-styles/american-medical-association-no-url-alphabetical.csl b/buildres/csl/csl-styles/american-medical-association-no-url-alphabetical.csl
new file mode 100644
index 00000000000..dc6ea2b4aad
--- /dev/null
+++ b/buildres/csl/csl-styles/american-medical-association-no-url-alphabetical.csl
@@ -0,0 +1,263 @@
+
+
diff --git a/buildres/csl/csl-styles/arcadia-science.csl b/buildres/csl/csl-styles/arcadia-science.csl
new file mode 100644
index 00000000000..74c7dc8d89a
--- /dev/null
+++ b/buildres/csl/csl-styles/arcadia-science.csl
@@ -0,0 +1,139 @@
+
+
diff --git a/buildres/csl/csl-styles/association-for-computational-linguistics.csl b/buildres/csl/csl-styles/association-for-computational-linguistics.csl
index ec95b2a95b5..0fd76b842e5 100644
--- a/buildres/csl/csl-styles/association-for-computational-linguistics.csl
+++ b/buildres/csl/csl-styles/association-for-computational-linguistics.csl
@@ -68,8 +68,17 @@
-
-
+
+
+
+
+
+
+
+
+
+
+
diff --git a/buildres/csl/csl-styles/biotechnology-and-bioprocess-engineering.csl b/buildres/csl/csl-styles/biotechnology-and-bioprocess-engineering.csl
new file mode 100644
index 00000000000..75cab4f5718
--- /dev/null
+++ b/buildres/csl/csl-styles/biotechnology-and-bioprocess-engineering.csl
@@ -0,0 +1,237 @@
+
+
diff --git a/buildres/csl/csl-styles/bluebook-law-review.csl b/buildres/csl/csl-styles/bluebook-law-review.csl
index 5ce50f1f0ab..7525b6b182e 100644
--- a/buildres/csl/csl-styles/bluebook-law-review.csl
+++ b/buildres/csl/csl-styles/bluebook-law-review.csl
@@ -40,9 +40,22 @@
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -59,10 +72,10 @@
-
+
-
+
diff --git a/buildres/csl/csl-styles/cahiers-mondes-anciens.csl b/buildres/csl/csl-styles/cahiers-mondes-anciens.csl
new file mode 100644
index 00000000000..07abbdc9fc4
--- /dev/null
+++ b/buildres/csl/csl-styles/cahiers-mondes-anciens.csl
@@ -0,0 +1,264 @@
+
+
+
diff --git a/buildres/csl/csl-styles/cambridge-university-press-author-date-cambridge-a.csl b/buildres/csl/csl-styles/cambridge-university-press-author-date-cambridge-a.csl
new file mode 100644
index 00000000000..a0db9d9521a
--- /dev/null
+++ b/buildres/csl/csl-styles/cambridge-university-press-author-date-cambridge-a.csl
@@ -0,0 +1,537 @@
+
+
diff --git a/buildres/csl/csl-styles/cambridge-university-press-author-date.csl b/buildres/csl/csl-styles/cambridge-university-press-author-date.csl
index 21cdfa8f944..ae1df9beb63 100644
--- a/buildres/csl/csl-styles/cambridge-university-press-author-date.csl
+++ b/buildres/csl/csl-styles/cambridge-university-press-author-date.csl
@@ -1,11 +1,11 @@
diff --git a/buildres/csl/csl-styles/constructivist-foundations.csl b/buildres/csl/csl-styles/constructivist-foundations.csl
new file mode 100644
index 00000000000..afa6b6a57a4
--- /dev/null
+++ b/buildres/csl/csl-styles/constructivist-foundations.csl
@@ -0,0 +1,153 @@
+
+
diff --git a/buildres/csl/csl-styles/dependent/conservation-science-and-practice.csl b/buildres/csl/csl-styles/dependent/conservation-science-and-practice.csl
new file mode 100644
index 00000000000..fab6eedb68e
--- /dev/null
+++ b/buildres/csl/csl-styles/dependent/conservation-science-and-practice.csl
@@ -0,0 +1,15 @@
+
+
diff --git a/buildres/csl/csl-styles/dependent/development-growth-differentiation.csl b/buildres/csl/csl-styles/dependent/development-growth-differentiation.csl
new file mode 100644
index 00000000000..f11d24bc750
--- /dev/null
+++ b/buildres/csl/csl-styles/dependent/development-growth-differentiation.csl
@@ -0,0 +1,17 @@
+
+
diff --git a/buildres/csl/csl-styles/dependent/the-american-journal-of-sports-medicine.csl b/buildres/csl/csl-styles/dependent/the-american-journal-of-sports-medicine.csl
index 62ca2578ff4..24184080887 100644
--- a/buildres/csl/csl-styles/dependent/the-american-journal-of-sports-medicine.csl
+++ b/buildres/csl/csl-styles/dependent/the-american-journal-of-sports-medicine.csl
@@ -2,15 +2,16 @@
diff --git a/buildres/csl/csl-styles/dependent/veterinary-record-open.csl b/buildres/csl/csl-styles/dependent/veterinary-record-open.csl
deleted file mode 100644
index 7976ce1ce56..00000000000
--- a/buildres/csl/csl-styles/dependent/veterinary-record-open.csl
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
diff --git a/buildres/csl/csl-styles/emerald-harvard.csl b/buildres/csl/csl-styles/emerald-harvard.csl
index 3b4a484c002..ead41080a33 100644
--- a/buildres/csl/csl-styles/emerald-harvard.csl
+++ b/buildres/csl/csl-styles/emerald-harvard.csl
@@ -30,7 +30,7 @@
- 2020-08-26T12:00:00+00:00
+ 2022-12-12T18:17:18+00:00
This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 License
@@ -108,28 +108,23 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/buildres/csl/csl-styles/engineering-technology-and-applied-science-research.csl b/buildres/csl/csl-styles/engineering-technology-and-applied-science-research.csl
index 350a9590491..c81f84bed77 100644
--- a/buildres/csl/csl-styles/engineering-technology-and-applied-science-research.csl
+++ b/buildres/csl/csl-styles/engineering-technology-and-applied-science-research.csl
@@ -15,8 +15,8 @@
2241-4487
1792-8036
- ETASR style largely based on IEEE 2018 guidelines, V 11.12.2018 without the use of abbreviations for containers.
- 2020-06-05T21:02:10+00:00
+ ETASR style largely based on IEEE 2018 guidelines, V 11.12.2018 without the use of abbreviations for containers and with the use of full URL for DOIs.
+ 2022-09-25T12:59:16+00:00
This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 License
@@ -187,7 +187,7 @@
-
+
diff --git a/buildres/csl/csl-styles/gnosis-journal-of-gnostic-studies.csl b/buildres/csl/csl-styles/gnosis-journal-of-gnostic-studies.csl
new file mode 100644
index 00000000000..93f292d5faa
--- /dev/null
+++ b/buildres/csl/csl-styles/gnosis-journal-of-gnostic-studies.csl
@@ -0,0 +1,153 @@
+
+
diff --git a/buildres/csl/csl-styles/harvard-university-of-abertay-dundee.csl b/buildres/csl/csl-styles/harvard-university-of-abertay-dundee.csl
index 0e21a947c7d..0a793b00138 100644
--- a/buildres/csl/csl-styles/harvard-university-of-abertay-dundee.csl
+++ b/buildres/csl/csl-styles/harvard-university-of-abertay-dundee.csl
@@ -13,10 +13,13 @@
Sebastian Karcher
+
+ Naman Merchant
+
The Abertay version of the Harvard author-date style
- 2012-11-16T18:17:12+00:00
+ 2022-09-11T21:02:45+00:00
This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 License
@@ -47,6 +50,7 @@
+
@@ -56,6 +60,7 @@
+
@@ -64,14 +69,7 @@
-
-
-
-
-
-
-
-
+
@@ -145,22 +143,16 @@
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
@@ -171,9 +163,11 @@
-
+
-
+
+
+
diff --git a/buildres/csl/csl-styles/iawa-journal.csl b/buildres/csl/csl-styles/iawa-journal.csl
index ffae3813e49..97443927d13 100644
--- a/buildres/csl/csl-styles/iawa-journal.csl
+++ b/buildres/csl/csl-styles/iawa-journal.csl
@@ -9,6 +9,9 @@
Patrick O'Brien
+
+ Ronald Visser
+
0928-1541
@@ -127,11 +130,11 @@
-
+
-
+
@@ -157,7 +160,7 @@
-
+
@@ -167,24 +170,24 @@
-
+
-
+
-
-
+
+
-
+
-
+
-
+
@@ -193,9 +196,9 @@
-
-
-
+
+
+
diff --git a/buildres/csl/csl-styles/ieee.csl b/buildres/csl/csl-styles/ieee.csl
index bff5b9197ed..8238e0aac1d 100644
--- a/buildres/csl/csl-styles/ieee.csl
+++ b/buildres/csl/csl-styles/ieee.csl
@@ -45,7 +45,7 @@
IEEE style as per the 2021 guidelines, V 01.29.2021.
- 2021-05-07T00:52:46+10:00
+ 2022-10-11T00:52:46+10:00
This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 License
@@ -177,7 +177,7 @@
-
+
diff --git a/buildres/csl/csl-styles/international-union-of-crystallography.csl b/buildres/csl/csl-styles/international-union-of-crystallography.csl
index dcaa4332ce8..564787bf09d 100644
--- a/buildres/csl/csl-styles/international-union-of-crystallography.csl
+++ b/buildres/csl/csl-styles/international-union-of-crystallography.csl
@@ -1,6 +1,5 @@
-
diff --git a/buildres/csl/csl-styles/journal-of-medical-internet-research.csl b/buildres/csl/csl-styles/journal-of-medical-internet-research.csl
index b75708a423d..b597dcd425a 100644
--- a/buildres/csl/csl-styles/journal-of-medical-internet-research.csl
+++ b/buildres/csl/csl-styles/journal-of-medical-internet-research.csl
@@ -20,11 +20,15 @@
Patrick O'Brien
+
+ Andrei Bosco Bezerra Torres
+ andrei.torres@ontariotechu.net
+
1439-4456
1438-8871
- 2017-06-02T10:20:48+00:00
+ 2022-09-08T13:25:00+00:00
This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 License
@@ -74,16 +78,19 @@
-
+
-
-
-
+
+
+
+
+
+
-
+
@@ -96,29 +103,20 @@
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
-
-
-
-
-
@@ -143,11 +141,6 @@
-
-
-
-
-
@@ -179,7 +172,6 @@
-
@@ -204,7 +196,6 @@
-
diff --git a/buildres/csl/csl-styles/journal-of-the-american-college-of-cardiology.csl b/buildres/csl/csl-styles/journal-of-the-american-college-of-cardiology.csl
index 49b9a5c56fd..eb47b7cb49b 100644
--- a/buildres/csl/csl-styles/journal-of-the-american-college-of-cardiology.csl
+++ b/buildres/csl/csl-styles/journal-of-the-american-college-of-cardiology.csl
@@ -6,7 +6,8 @@
http://www.zotero.org/styles/journal-of-the-american-college-of-cardiology
-
+
+
Julian Onions
julian.onions@gmail.com
@@ -19,9 +20,14 @@
0735-1097
1558-3597
Journal of the American College of Cardiology
- 2012-09-27T22:06:38+00:00
+ 2022-12-09T09:35:42+00:00
This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 License
+
+
+ published online
+
+
@@ -45,24 +51,34 @@
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
@@ -92,11 +108,11 @@
-
+
-
+
@@ -108,7 +124,7 @@
-
+
@@ -120,7 +136,7 @@
-
+
@@ -136,21 +152,21 @@
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
-
diff --git a/buildres/csl/csl-styles/les-mondes-du-travail.csl b/buildres/csl/csl-styles/les-mondes-du-travail.csl
new file mode 100644
index 00000000000..ba6774b283e
--- /dev/null
+++ b/buildres/csl/csl-styles/les-mondes-du-travail.csl
@@ -0,0 +1,280 @@
+
+
diff --git a/buildres/csl/csl-styles/lund-university-school-of-economics-and-management.csl b/buildres/csl/csl-styles/lund-university-school-of-economics-and-management.csl
index 8a7984a43ed..8f87d987152 100644
--- a/buildres/csl/csl-styles/lund-university-school-of-economics-and-management.csl
+++ b/buildres/csl/csl-styles/lund-university-school-of-economics-and-management.csl
@@ -364,7 +364,7 @@
-
+
diff --git a/buildres/csl/csl-styles/medizinische-hochschule-hannover.csl b/buildres/csl/csl-styles/medizinische-hochschule-hannover.csl
index d4988b5f214..9c63592e170 100644
--- a/buildres/csl/csl-styles/medizinische-hochschule-hannover.csl
+++ b/buildres/csl/csl-styles/medizinische-hochschule-hannover.csl
@@ -13,12 +13,16 @@
Style for MHH based on the guidelines from 02.12.2020.
- 2022-05-12T08:17:27+00:00
+ 2022-12-07T11:41:15+00:00
This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 License
+
+
+
+
@@ -29,8 +33,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -44,8 +64,8 @@
-
-
+
+
@@ -53,6 +73,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -84,28 +117,54 @@
-
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
diff --git a/buildres/csl/csl-styles/mots.csl b/buildres/csl/csl-styles/mots.csl
new file mode 100644
index 00000000000..f6a0b1f2ad4
--- /dev/null
+++ b/buildres/csl/csl-styles/mots.csl
@@ -0,0 +1,259 @@
+
+
+
diff --git a/buildres/csl/csl-styles/norsk-apa-manual-note.csl b/buildres/csl/csl-styles/norsk-apa-manual-note.csl
index f82681543dd..fe4cddf45ef 100644
--- a/buildres/csl/csl-styles/norsk-apa-manual-note.csl
+++ b/buildres/csl/csl-styles/norsk-apa-manual-note.csl
@@ -163,8 +163,8 @@
-
-
+
+
diff --git a/buildres/csl/csl-styles/norsk-apa-manual.csl b/buildres/csl/csl-styles/norsk-apa-manual.csl
index 85883ebaec4..cbfec7058ed 100644
--- a/buildres/csl/csl-styles/norsk-apa-manual.csl
+++ b/buildres/csl/csl-styles/norsk-apa-manual.csl
@@ -191,8 +191,8 @@
-
-
+
+
diff --git a/buildres/csl/csl-styles/oil-shale.csl b/buildres/csl/csl-styles/oil-shale.csl
index 4d0d9c08619..6b7da61f500 100644
--- a/buildres/csl/csl-styles/oil-shale.csl
+++ b/buildres/csl/csl-styles/oil-shale.csl
@@ -1,338 +1,286 @@
-
diff --git a/buildres/csl/csl-styles/pallas.csl b/buildres/csl/csl-styles/pallas.csl
new file mode 100644
index 00000000000..dfe83e5541d
--- /dev/null
+++ b/buildres/csl/csl-styles/pallas.csl
@@ -0,0 +1,252 @@
+
+
+
diff --git a/buildres/csl/csl-styles/physiotherapy-theory-and-practice.csl b/buildres/csl/csl-styles/physiotherapy-theory-and-practice.csl
index b10b7c0d7c6..5ff20e0edcf 100644
--- a/buildres/csl/csl-styles/physiotherapy-theory-and-practice.csl
+++ b/buildres/csl/csl-styles/physiotherapy-theory-and-practice.csl
@@ -14,7 +14,7 @@
0959-3985
1532-5040
- 2022-01-11T08:37:50+00:00
+ 2022-09-05T16:08:42+00:00
This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 License
@@ -63,8 +63,8 @@
-
+
@@ -73,7 +73,7 @@
-
+
@@ -81,9 +81,11 @@
-
-
-
+
+
+
+
+
diff --git a/buildres/csl/csl-styles/rassegna-degli-archivi-di-stato-bibliografia-generale.csl b/buildres/csl/csl-styles/rassegna-degli-archivi-di-stato-bibliografia-generale.csl
new file mode 100644
index 00000000000..cf345426e82
--- /dev/null
+++ b/buildres/csl/csl-styles/rassegna-degli-archivi-di-stato-bibliografia-generale.csl
@@ -0,0 +1,784 @@
+
+
diff --git a/buildres/csl/csl-styles/review-of-international-studies.csl b/buildres/csl/csl-styles/review-of-international-studies.csl
index 19fed42975c..2fea05e8949 100644
--- a/buildres/csl/csl-styles/review-of-international-studies.csl
+++ b/buildres/csl/csl-styles/review-of-international-studies.csl
@@ -14,7 +14,7 @@
0260-2105
1469-9044
- 2018-03-29T14:20:30+00:00
+ 2022-10-24T16:26:57+00:00
This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 License
@@ -131,9 +131,9 @@
-
+
-
+
diff --git a/buildres/csl/csl-styles/royal-society-of-chemistry.csl b/buildres/csl/csl-styles/royal-society-of-chemistry.csl
index 0f9cebdbb0c..b13885f3257 100644
--- a/buildres/csl/csl-styles/royal-society-of-chemistry.csl
+++ b/buildres/csl/csl-styles/royal-society-of-chemistry.csl
@@ -14,10 +14,14 @@
Ivan Bushmarinov
ib@ineos.ac.ru
+
+ Steven R. Kirk
+ steven.kirk@cantab.net
+
The Royal Society of Chemistry journal style.
- 2018-12-26T22:22:35+00:00
+ 2022-12-05T05:15:00+00:00
This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 License
@@ -176,6 +180,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/buildres/csl/csl-styles/scientific-online-letters-on-the-atmosphere.csl b/buildres/csl/csl-styles/scientific-online-letters-on-the-atmosphere.csl
new file mode 100644
index 00000000000..7b818c5d878
--- /dev/null
+++ b/buildres/csl/csl-styles/scientific-online-letters-on-the-atmosphere.csl
@@ -0,0 +1,226 @@
+
+
diff --git a/buildres/csl/csl-styles/sn-computer-science.csl b/buildres/csl/csl-styles/sn-computer-science.csl
new file mode 100644
index 00000000000..d867efcd7d4
--- /dev/null
+++ b/buildres/csl/csl-styles/sn-computer-science.csl
@@ -0,0 +1,169 @@
+
+
diff --git a/buildres/csl/csl-styles/social-science-history.csl b/buildres/csl/csl-styles/social-science-history.csl
new file mode 100644
index 00000000000..da5d4c8d1e0
--- /dev/null
+++ b/buildres/csl/csl-styles/social-science-history.csl
@@ -0,0 +1,598 @@
+
+
diff --git a/buildres/csl/csl-styles/spec/filters.yaml b/buildres/csl/csl-styles/spec/filters.yaml
index 48cb27087d4..69b1c6cdacf 100644
--- a/buildres/csl/csl-styles/spec/filters.yaml
+++ b/buildres/csl/csl-styles/spec/filters.yaml
@@ -41,6 +41,7 @@ ISSN:
- 2496-7114
- 1736-6046
- 1736-7530
+- 0037-2781
# These titles are ignored when checking for duplicate titles
TITLES: []
diff --git a/buildres/csl/csl-styles/triangle.csl b/buildres/csl/csl-styles/triangle.csl
index eefba067154..79e0953412a 100644
--- a/buildres/csl/csl-styles/triangle.csl
+++ b/buildres/csl/csl-styles/triangle.csl
@@ -20,7 +20,7 @@
Derived from Style EHESS-histoire, available at http://www.boiteaoutils.info/2011/06/styles-francais-de-citation-sous-zotero.html
First version online in november 2012. Changes made to cover more documents types and to lighten URLS display. Should preferably be used ticking the quoting option (preferences/citer/styles): include URLs addresses in references.
- 2021-04-07T01:50:30+00:00
+ 2022-09-29T01:50:30+00:00
This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 License
@@ -62,27 +62,27 @@
-
+
-
+
-
+
-
+
@@ -95,27 +95,27 @@
-
+
-
+
-
+
-
+
@@ -126,7 +126,7 @@
-
+
@@ -137,7 +137,7 @@
-
+
@@ -151,7 +151,7 @@
-
+
@@ -174,7 +174,7 @@
-
+
@@ -189,7 +189,7 @@
-
+
@@ -227,7 +227,7 @@
-
+
@@ -240,18 +240,18 @@
-
+
-
+
-
+
@@ -263,7 +263,7 @@
-
+
@@ -275,18 +275,22 @@
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -295,7 +299,7 @@
-
+
@@ -304,14 +308,14 @@
-
+
-
+
-
+
@@ -337,7 +341,7 @@
-
+
@@ -445,7 +449,10 @@
-
+
+
+
+
@@ -459,11 +466,14 @@
-
+
-
+
+
+
+
@@ -481,7 +491,10 @@
-
+
+
+
+
@@ -507,7 +520,10 @@
-
+
+
+
+
diff --git a/buildres/csl/csl-styles/ucl-university-college-apa.csl b/buildres/csl/csl-styles/ucl-university-college-apa.csl
index 234c9ef9926..b1f9a4b210e 100644
--- a/buildres/csl/csl-styles/ucl-university-college-apa.csl
+++ b/buildres/csl/csl-styles/ucl-university-college-apa.csl
@@ -17,7 +17,7 @@
- 2021-08-24T02:54:29+00:00
+ 2022-09-06T13:44:06+00:00
This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 License
@@ -1555,7 +1555,8 @@
-
+
+
diff --git a/buildres/csl/csl-styles/veterinary-record-open.csl b/buildres/csl/csl-styles/veterinary-record-open.csl
new file mode 100644
index 00000000000..322dac9de53
--- /dev/null
+++ b/buildres/csl/csl-styles/veterinary-record-open.csl
@@ -0,0 +1,211 @@
+
+
diff --git a/buildres/csl/csl-styles/zeitschrift-fur-deutsche-philologie.csl b/buildres/csl/csl-styles/zeitschrift-fur-deutsche-philologie.csl
index ca808105db0..ea029796d49 100644
--- a/buildres/csl/csl-styles/zeitschrift-fur-deutsche-philologie.csl
+++ b/buildres/csl/csl-styles/zeitschrift-fur-deutsche-philologie.csl
@@ -49,8 +49,8 @@
-
-
+
+
diff --git a/buildres/linux/jabref.spec b/buildres/linux/jabref.spec
index e877c424058..c17cdaf63ee 100644
--- a/buildres/linux/jabref.spec
+++ b/buildres/linux/jabref.spec
@@ -6,13 +6,13 @@ License: APPLICATION_LICENSE_TYPE
Vendor: APPLICATION_VENDOR
Prefix: %{dirname:APPLICATION_DIRECTORY}
Provides: APPLICATION_PACKAGE
-%if "xAPPLICATION_GROUP" != x
+%if "xAPPLICATION_GROUP" != "x"
Group: APPLICATION_GROUP
%endif
Autoprov: 0
Autoreq: 0
-%if "xPACKAGE_DEFAULT_DEPENDENCIES" != x || "xPACKAGE_CUSTOM_DEPENDENCIES" != x
+%if "xPACKAGE_DEFAULT_DEPENDENCIES" != "x" || "xPACKAGE_CUSTOM_DEPENDENCIES" != "x"
Requires: PACKAGE_DEFAULT_DEPENDENCIES PACKAGE_CUSTOM_DEPENDENCIES
%endif
@@ -35,14 +35,14 @@ APPLICATION_DESCRIPTION
rm -rf %{buildroot}
install -d -m 755 %{buildroot}APPLICATION_DIRECTORY
cp -r %{_sourcedir}APPLICATION_DIRECTORY/* %{buildroot}APPLICATION_DIRECTORY
-%if "xAPPLICATION_LICENSE_FILE" != x
+%if "xAPPLICATION_LICENSE_FILE" != "x"
%define license_install_file %{_defaultlicensedir}/%{name}-%{version}/%{basename:APPLICATION_LICENSE_FILE}
install -d -m 755 %{buildroot}%{dirname:%{license_install_file}}
install -m 644 APPLICATION_LICENSE_FILE %{buildroot}%{license_install_file}
%endif
%files
-%if "xAPPLICATION_LICENSE_FILE" != x
+%if "xAPPLICATION_LICENSE_FILE" != "x"
%license %{license_install_file}
%{dirname:%{license_install_file}}
%endif
diff --git a/buildres/linux/jabrefHost.py b/buildres/linux/jabrefHost.py
index 557768fc9f6..31bc5e2015a 100755
--- a/buildres/linux/jabrefHost.py
+++ b/buildres/linux/jabrefHost.py
@@ -11,27 +11,49 @@
import sys
from pathlib import Path
+def which(command):
+ if os.getenv("FLATPAK_ID"):
+ try:
+ return subprocess.check_output(["flatpak-spawn", "--host", "which", command]).decode().strip()
+ except subprocess.CalledProcessError:
+ pass
+ path = shutil.which(command)
+ if path != "":
+ return path
+ else:
+ return None
+
+
+JABREF_PATH = ""
+if os.getenv("FLATPAK_ID"):
+ try:
+ subprocess.check_output(["flatpak-spawn", "--host", "true"])
+ except subprocess.CalledProcessError:
+ logging.error("Failed to call JabRef: Flatpak browser missing permissions")
+ send_message({"message": "flatpakPermissionsError", "output": "Flatpak browser missing permissions"})
+ exit(-1)
+ JABREF_PATH = "flatpak-spawn --host "
+
# Try a set of possible launchers to execute JabRef
script_dir = Path(__file__).resolve().parent.parent
-relpath_path = script_dir / "bin/JabRef"
-lowercase_path = shutil.which("jabref")
-uppercase_path = shutil.which("JabRef")
+relpath_path = str(script_dir / "bin/JabRef")
+lowercase_path = which("jabref")
+uppercase_path = which("JabRef")
# Relative path used in the portable install
-if relpath_path.exists():
- JABREF_PATH = relpath_path
+if which(relpath_path) is not None:
+ JABREF_PATH += relpath_path
# Lowercase launcher used in deb/rpm/snap packages
-elif lowercase_path is not None and os.path.exists(lowercase_path):
- JABREF_PATH = Path(lowercase_path)
+elif lowercase_path is not None:
+ JABREF_PATH += lowercase_path
# Uppercase launcher used in Arch AUR package
-elif uppercase_path is not None and os.path.exists(uppercase_path):
- JABREF_PATH = Path(uppercase_path)
+elif uppercase_path is not None:
+ JABREF_PATH += uppercase_path
# FLatpak support
-elif subprocess.run(["flatpak", "info", "org.jabref.jabref"], capture_output=True).returncode == 0:
- JABREF_PATH = "flatpak run org.jabref.jabref"
+elif which("/var/lib/flatpak/exports/bin/org.jabref.jabref") is not None:
+ JABREF_PATH += "/var/lib/flatpak/exports/bin/org.jabref.jabref"
else:
logging.error("Could not determine JABREF_PATH")
- send_message({"message": "error", "output": "Could not find JabRef. Please check that it is installed correctly."})
sys.exit(-1)
logging_dir = Path.home() / ".mozilla/native-messaging-hosts/"
diff --git a/buildres/linux/postrm b/buildres/linux/postrm
index abcd727ac76..eb36765e646 100644
--- a/buildres/linux/postrm
+++ b/buildres/linux/postrm
@@ -25,12 +25,12 @@ case "$1" in
"/etc/opt/chrome/native-messaging-hosts/org.jabref.jabref.json"\
"/etc/opt/edge/native-messaging-hosts/org.jabref.jabref.json"; do
if [ -e $NATIVE_MESSAGING_JSON ] && grep --quiet '"path": "/opt' $NATIVE_MESSAGING_JSON; then
- rm $NATIVE_MESSAGING_JSON
+ rm -f $NATIVE_MESSAGING_JSON
fi
done
# Remove the auto-install triggers of the browser addon for chrom/chromium
- rm /opt/google/chrome/extensions/bifehkofibaamoeaopjglfkddgkijdlh.json
- rm /usr/share/google-chrome/extensions/bifehkofibaamoeaopjglfkddgkijdlh.json
+ rm -f /opt/google/chrome/extensions/bifehkofibaamoeaopjglfkddgkijdlh.json
+ rm -f /usr/share/google-chrome/extensions/bifehkofibaamoeaopjglfkddgkijdlh.json
;;
*)
diff --git a/docs/.dockerignore b/docs/.dockerignore
new file mode 100644
index 00000000000..30e5c368249
--- /dev/null
+++ b/docs/.dockerignore
@@ -0,0 +1,3 @@
+.jekyll-cache
+.jekyll-metadata
+_site/
diff --git a/docs/.gitignore b/docs/.gitignore
index b4dfc49b992..b29706bc605 100644
--- a/docs/.gitignore
+++ b/docs/.gitignore
@@ -1,2 +1,4 @@
+Gemfile.lock
.jekyll-cache
+.jekyll-metadata
_site/
diff --git a/docs/Dockerfile b/docs/Dockerfile
new file mode 100644
index 00000000000..4c2afd5dda0
--- /dev/null
+++ b/docs/Dockerfile
@@ -0,0 +1,14 @@
+FROM ruby:2.7
+
+ENV LC_ALL C.UTF-8
+ENV LANG en_US.UTF-8
+ENV LANGUAGE en_US.UTF-8
+
+EXPOSE 4000
+
+WORKDIR /srv/jekyll
+COPY . /srv/jekyll
+
+RUN gem install bundler && bundle install
+
+CMD bundle exec jekyll serve -H 0.0.0.0 -t
diff --git a/docs/Gemfile b/docs/Gemfile
index 02e1405888b..4c00e14eb36 100644
--- a/docs/Gemfile
+++ b/docs/Gemfile
@@ -1,6 +1,22 @@
source 'https://rubygems.org'
-gem "jekyll", "~> 4.2" # installed by `gem jekyll`
-gem "webrick" # required when using Ruby >= 3
+gem "jekyll", "~> 4.3" # installed by `gem jekyll`
+
+# Homepage: https://github.com/paulrobertlloyd/jekyll-figure#jekyll-figure
+gem 'jekyll-figure'
gem "just-the-docs", "0.4.0.rc3"
+
+gem "jekyll-remote-theme"
+
+# Added, because defualt layout with path "**/*.md" does not work at Jekyll
+# Source: https://jekyllrb.com/docs/configuration/front-matter-defaults/#glob-patterns-in-front-matter-defaults
+# Homepage: https://github.com/benbalter/jekyll-default-layout#jekyll-default-layout
+gem 'jekyll-default-layout'
+
+# Somehow, "title" is set "magically" when on GitHub, but not when running locally
+# Homepage: https://github.com/benbalter/jekyll-titles-from-headings#jekyll-titles-from-headings
+gem 'jekyll-titles-from-headings'
+
+# Homepage: https://github.com/benbalter/jekyll-relative-links
+gem 'jekyll-relative-links'
diff --git a/docs/README.md b/docs/README.md
index 710d167bd6f..59c88f9c829 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -19,7 +19,11 @@ and go to in your browser.
On **Windows**, using a dockerized environment is recommended:
```terminal
-docker run -p 4000:4000 --rm --volume="C:\git-repositories\jabref\docs":/srv/jekyll jekyll/jekyll:4 jekyll serve
+docker build . -t jrjekyll
+docker run -p 4000:4000 -it --rm --volume="C:\git-repositories\jabref\docs":/srv/jekyll jrjekyll jekyll serve -H 0.0.0.0 -t
```
-In case you get errors regarding `Gemfile.lock`, just delete `Gemfile.lock` and rerun.
+* With Ctrl+C you can stop the server (this is enabled by the `-it` switch).
+* In case you get errors regarding `Gemfile.lock`, just delete `Gemfile.lock` and rerun.
+* The current `Dockerfile` is based on .
+ The [Jekyll Docker image](https://github.com/envygeeks/jekyll-docker#jekyll-docker) did not work end of 20222 (because Ruby was too new).
diff --git a/docs/_config.yml b/docs/_config.yml
index aaea82c0290..ba9ab574bc5 100644
--- a/docs/_config.yml
+++ b/docs/_config.yml
@@ -3,6 +3,8 @@ title: "Developer Documentation"
remote_theme: just-the-docs/just-the-docs@v0.4.0.rc3
color_scheme: light
+exclude: [CNAME, README.md, mkdocs-custom.css]
+
# Hint by https://github.com/just-the-docs/just-the-docs/issues/374#issuecomment-680273258
# Theme read from https://github.com/StylishThemes/Syntax-Themes/blob/master/pygments/css-github/
# Browse alternative themes at https://stylishthemes.github.io/Syntax-Themes/pygments/
@@ -34,4 +36,29 @@ baseurl: ""
url: ""
plugins:
+ - jekyll-default-layout
+ - jekyll-figure
- jekyll-remote-theme
+ - jekyll-titles-from-headings
+ - jekyll-relative-links
+
+jekyll-figure:
+ paragraphs: false
+
+callouts_level: quiet # or loud
+callouts:
+ highlight:
+ title: Summary
+ color: blue
+ important:
+ title: Important
+ color: yellow
+ new:
+ title: New
+ color: green
+ note:
+ title: Note
+ color: purple
+ warning:
+ title: Warning
+ color: red
diff --git a/docs/_sass/custom/custom.scss b/docs/_sass/custom/custom.scss
new file mode 100644
index 00000000000..d5e70410f1c
--- /dev/null
+++ b/docs/_sass/custom/custom.scss
@@ -0,0 +1,8 @@
+figcaption {
+ font-size: .75em;
+ font-weight: 550;
+}
+
+figcaption:before {
+ content: "Figure: "
+}
diff --git a/docs/code-howtos/javafx.md b/docs/code-howtos/javafx.md
index c5c5822681a..c08fe96b489 100644
--- a/docs/code-howtos/javafx.md
+++ b/docs/code-howtos/javafx.md
@@ -185,6 +185,11 @@ private void openJabrefWebsite() {
The view consists a FXML file `MyDialog.fxml` which defines the structure and the layout of the UI. Moreover, the FXML file may be accompanied by a style file that should have the same name as the FXML file but with a `css` ending, e.g., `MyDialog.css`. It is recommended to use a graphical design tools like [SceneBuilder](http://gluonhq.com/labs/scene-builder/) to edit the FXML file. The tool [Scenic View](https://github.com/JonathanGiles/scenic-view) is very helpful in debugging styling issues.
+## Properties and Bindings
+
+JabRef makes heavy use of Properties and Bindings. These are wrappers around Observables. A good explanation on the concept can be found here:
+[JavaFX Bindings and Properties](https://edencoding.com/javafx-properties-and-binding-a-complete-guide/)
+
## Resources
* [curated list of awesome JavaFX frameworks, libraries, books and etc...](https://github.com/mhrimaz/AwesomeJavaFX)
diff --git a/docs/contributing.md b/docs/contributing.md
index 8e2e5d9f5f9..7b1423836d4 100644
--- a/docs/contributing.md
+++ b/docs/contributing.md
@@ -52,7 +52,7 @@ Your contribution is considered being made under [MIT license](https://tldrlegal
#### Write a good commit message
-See [good commit message](https://github.com/joelparkerhenderson/git\_commit\_message) or [commit guidelines section of Pro Git](http://git-scm.com/book/en/Distributed-Git-Contributing-to-a-Project#Commit-Guidelines). The first line of your commit message is automatically taken as the title for the pull-request. All other lines make up the body of the pull request. Add the words `fixes #xxx` to your PR to auto-close the corresponding issue.
+See [good commit message](https://github.com/joelparkerhenderson/git-commit-message) or [commit guidelines section of Pro Git](http://git-scm.com/book/en/Distributed-Git-Contributing-to-a-Project#Commit-Guidelines). For the curious: [Why good commit messages matter!](https://cbea.ms/git-commit/). The first line of your commit message is automatically taken as the title for the pull-request. All other lines make up the body of the pull request. Add the words `fixes #xxx` to your PR to auto-close the corresponding issue.
#### Test your code
@@ -76,7 +76,7 @@ Actual :[Opens\ JabRef's\ Twitter\ page (src\main\java\org\jabref\gui\JabRefFr
Add the above snippet to the English translation file located at `src/main/resources/l10n/JabRef_en.properties`. [Crowdin](https://crowdin.com/project/jabref) will automatically pick up the new string and add it to the other translations.
-You can also directly run the specific test in your IDE. The test "LocalizationConsistencyTest" is placed under `src/test/java/net.sf.jabref.logic.l10n/LocalizationConsistencyTest.java`. Find more information in the [JabRef developer docs](https://devdocs.jabref.org/getting-into-the-code/code-howtos#using-localization-correctly).
+You can also directly run the specific test in your IDE. The test "LocalizationConsistencyTest" is placed under `src/test/java/org.jabref.logic.l10n/LocalizationConsistencyTest.java`. Find more information in the [JabRef developer docs](https://devdocs.jabref.org/getting-into-the-code/code-howtos#using-localization-correctly).
#### When adding a library
diff --git a/docs/getting-into-the-code/guidelines-choose-module.png b/docs/getting-into-the-code/guidelines-choose-module.png
new file mode 100644
index 00000000000..ab2c3149a3b
Binary files /dev/null and b/docs/getting-into-the-code/guidelines-choose-module.png differ
diff --git a/docs/getting-into-the-code/guidelines-choose-open-as-project.png b/docs/getting-into-the-code/guidelines-choose-open-as-project.png
new file mode 100644
index 00000000000..0c69ca713ab
Binary files /dev/null and b/docs/getting-into-the-code/guidelines-choose-open-as-project.png differ
diff --git a/docs/getting-into-the-code/guidelines-for-setting-up-a-local-workspace.md b/docs/getting-into-the-code/guidelines-for-setting-up-a-local-workspace.md
index 35622009285..bfd13f0ad37 100644
--- a/docs/getting-into-the-code/guidelines-for-setting-up-a-local-workspace.md
+++ b/docs/getting-into-the-code/guidelines-for-setting-up-a-local-workspace.md
@@ -6,25 +6,17 @@ nav_order: 2
This guide explains how to set up your environment for development of JabRef. It includes information about prerequisites, configuring your IDE, and running JabRef locally to verify your setup.
-> **The most important step is to configure your IDE.**\
-> **In case you know how to install JDK 18 and [to fork JabRef's code](#get-the-code),**\
-> **please scroll down to the [IDE setup](#configure-your-ide).**
-
-For a complete step-by-step guide for Linux using IntelliJ IDEA as the IDE, have a look at the following video instructions:
-
-[![YouTube video showing the step-by-step guide](https://img.youtube.com/vi/JkFVJ6p0urw/mqdefault.jpg)](https://youtu.be/JkFVJ6p0urw)
+{: .highlight }
+The most important step is to configure your IDE.
+If you know how to fork and check out JabRef's code,
+already have an IDE installed, then
+please scroll down to the [IDE setup](#configure-your-ide).
+Otherwise, please keep on reading.
## Prerequisites
This section list the prerequisites you need to get started to develop JabRef. After this section, you are ready to get the code.
-### Java Development Kit 18
-
-A working Java (Development Kit) 18 installation is required. In the command line (terminal in Linux, cmd in Windows) run `javac -version` and make sure that the reported version is Java 18 (e.g., `javac 18`). If `javac` is not found or a wrong version is reported, check your `PATH` environment variable, your `JAVA_HOME` environment variable or install the most recent JDK.
-
-Please head to to download JDK 18.
-Note that JDK 19 does not work.
-
### GitHub Account
If you do not yet have a GitHub account, please [create one](https://github.com/join).
@@ -47,29 +39,31 @@ Examples:
### git
-It is strongly recommend that you have git installed.
-
-* In Debian-based distros: `sudo apt-get install git`
-* In Windows: [Download the installer](http://git-scm.com/download/win) and install it. Using chocolatey, you can run `choco install git.install -y --params "/GitAndUnixToolsOnPath /WindowsTerminal` to a) install git and b) have linux commands such as `grep` available in your `PATH`.
-* [official installation instructions](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)
-
-### IDE
-
-We suggest [IntelliJ IDEA](https://www.jetbrains.com/idea/?from=jabref). For advanced users, [Eclipse](https://eclipse.org) (`2022-03` or newer) is also possible.
+It is strongly recommended that you have git installed.
-#### IntelliJ
+* On Debian-based distros: `sudo apt-get install git`
+* On Windows: [Download the installer](http://git-scm.com/download/win) and install it. Using [chocolatey](https://chocolatey.org/), you can run `choco install git.install -y --params "/GitAndUnixToolsOnPath /WindowsTerminal` to a) install git and b) have Linux commands such as `grep` available in your `PATH`.
+* [Official installation instructions](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)
-We recommend to install IntelliJ IDEA using [JetBrains Toolbox App](https://www.jetbrains.com/toolbox-app/), because IDE updates are automatically installed.
+### Installed IDE
-#### Eclipse
+We suggest [IntelliJ IDEA](https://www.jetbrains.com/idea/?from=jabref).
+The Community Edition works well.
+Most contributors use the Ultimate Edition, because they are students getting that edition for free.
-On Ubuntu Linux, you can follow the [documentation from the Ubuntu Community](https://help.ubuntu.com/community/EclipseIDE#Download\_Eclipse) or the [step-by-step guideline from Krizna](https://www.krizna.com/ubuntu/install-eclipse-in-ubuntu-12-04/) to install Eclipse. On Windows, download it from [www.eclipse.org](http://www.eclipse.org/downloads/) and run the installer.
+For advanced users, [Eclipse](https://eclipse.org) (`2022-03` or newer) is also possible.
+On Ubuntu Linux, you can follow the [documentation from the Ubuntu Community](https://help.ubuntu.com/community/EclipseIDE#Download\_Eclipse) or the [step-by-step guideline from Krizna](https://www.krizna.com/ubuntu/install-eclipse-in-ubuntu-12-04/) to install Eclipse.
+On Windows, download it from [www.eclipse.org](http://www.eclipse.org/downloads/) and run the installer.
-Eclipse JEE 2022-03 or newer is required.
+### Java Development Kit 18
-### Other Tooling
+For Eclipse, a working Java (Development Kit) 18 installation is required.
+In the case of IntelliJ, this will be downloaded inside the IDE (if you follow the steps below).
-See our [tool recommendations](../code-howtos/tools.md).
+In the command line (terminal in Linux, cmd in Windows) run `javac -version` and make sure that the reported version is Java 18 (e.g., `javac 18`).
+If `javac` is not found or a wrong version is reported, check your `PATH` environment variable, your `JAVA_HOME` environment variable or install the most recent JDK.
+Please head to to download JDK 18.
+Note that JDK 19 does not work.
## Get the code
@@ -80,133 +74,449 @@ This section explains how you get the JabRef code onto your machine in a form al
1. Log into your GitHub account
2. Go to [https://github.com/JabRef/jabref](https://github.com/JabRef/jabref)
3. Create a fork by clicking at fork button on the right top corner
-4. A fork repository will be created under your account `https://github.com/YOUR\_USERNAME/jabref`.
+4. A fork repository will be created under your account `https://github.com/YOUR_USERNAME/jabref`.
### Clone your forked repository on your local machine
-* In a command line, navigate to the folder where you want to place the source code (parent folder of `jabref/`). To prevent issues along the way, it is strongly recommend to choose a path that does not contain any special (non-ASCII or whitespace) characters. Note that placing jabref folder directly in the disk root directory may cause an error.
-* Run `git clone --depth=10 https://github.com/YOUR_USERNAME/jabref.git`. The `--depth--10` is used to limit the download to \~20 MB instead of downloading the complete history (\~800 MB). If you want to dig in our commit history, feel free to download everything.
-* Go to the newly created jabref folder: `cd jabref`
+In a command line, navigate to the folder where you want to place the source code (parent folder of `jabref`).
+To prevent issues along the way, it is strongly recommend choosing a path that does not contain any special (non-ASCII or whitespace) characters.
+In the following, we will use `c:\git-repositories` as base folder:
+
+```cmd
+cd \
+mkdir git-repositories
+cd git-reposiroties
+```
+
+**Note that putting the repo jabref directly under `C:\` or any other drive letter under windows causes compile errors**
+
+Run the respective `git clone` command:
+
+```cmd
+git clone --depth=10 https://github.com/YOUR_USERNAME/jabref.git
+```
+
+The `--depth--10` is used to limit the download to \~20 MB instead of downloading the complete history (\~800 MB).
+If you want to dig in our commit history, feel free to download everything.
+
+Go to the newly created JabRef folder: `cd jabref`.
+
+Add JabRef's upstream code base as upstream:
+
+```
+git remote add upstream https://github.com/JabRef/jabref.git
+```
+
+Now, you have two remote repositories, where `origin` is yours and `upstream` is the one of the JabRef organization.
+
+You can see it with `git remote -v`:
+
+```cmd
+c:\git-repositories\jabref> git remote -v
+origin https://github.com/YOURUSERNAME/jabref.git (fetch)
+origin https://github.com/YOURUSERNAME/jabref.git (push)
+upstream https://github.com/jabref/jabref.git (fetch)
+upstream https://github.com/jabref/jabref.git (push)
+```
+
+### Generate source code
+
+{: .note }
+This is required for Eclipse only.
+In IntelliJ, this will be done in the IDE.
+
+Generate additional source code: `gradlew assemble` (on Linux/Mac: `./gradlew assemble`).
+
+The directory `src-gen` is now filled.
+
+## Get the code into your IDE
+
+Start IntelliJ.
+
+IntelliJ shows the following window:
+
+{% figure caption:"IntelliJ Start Window" %}
+![IntelliJ Start Window](guidelines-intellij-start-window.png)
+{% endfigure %}
+
+Click on "Open"
+
+Choose `build.gradle` in the root of the jabref source folder:
+
+{% figure caption:"Choose `build.gradle` in the “Open Project or File” dialog" %}
+![Open File or Project dialog](guidelines-intellij-choose-build-gradle.png)
+{% endfigure %}
+
+After pressing "OK", IntelliJ asks how that file should be opened.
+Answer: "Open as Project"
-### Create generate source code
+{% figure caption:"Choose “Open as Project” in the Open Project dialog" %}
+![Open Project dialog](guidelines-choose-open-as-project.png)
+{% endfigure %}
-1. Generate additional source code: `./gradlew assemble` (on Windows: `gradlew assemble`)
-2. Source code should now reside in `src-gen`.
-3. Start JabRef: `./gradlew run`
-4. JabRef's GUI should now start up.
+Then, trust the project:
-## Configure your IDE
+{% figure caption:"Choose “Trust Project” in the “Trust and Open Project” dialog" %}
+![Trust and Open Project dialog](guidelines-trust-project.png)
+{% endfigure %}
+
+## Configuration of IntelliJ IDEA
These steps are very important. They allow you to focus on the content and ensure that the code formatting always goes well. Did you know that [IntelliJ allows for reformatting selected code](https://www.jetbrains.com/help/idea/reformat-and-rearrange-code.html#reformat\_code) if you press Ctrl + Alt + L?
-### Configuration of IntelliJ IDEA
+IntelliJ IDEA fully supports Gradle as a build tool, but also has an internal build system which is usually faster. For JabRef, Gradle is required to make a full build.
+Once set up, IntelliJ IDEA's internal system can be used for subsequent builds.
-IntelliJ IDEA fully supports Gradle as a build tool, but also has an internal build system which is usually faster. For JabRef, Gradle is required to make a full build but once set up, IntelliJ IDEA's internal system can be used for sub-sequent builds.
+In case IntelliJ's internal build system does not work, just stick with using Gradle.
-To configure IntelliJ IDEA for developing JabRef, you should first ensure that you have enabled both bundled plugins _Gradle_ and _Gradle Extension_:
+### Ensure that JDK 18 is available to IntelliJ
-* Navigate to **File > Settings > Plugins > Installed** and check that you have the _Gradle_ and _Gradle Extension_ enabled.
+Ensure you have a Java 18 SDK configured by navigating to **File > Project Structure... > Platform Settings > SDKs**.
-After that, you can open `jabref/build.gradle` as a project. It is crucial that Java 18 is used consistently for the JabRef project which includes ensuring the right settings for your project structure, Gradle build, and run configurations.
+{% figure caption:"JDKs 11, 14, and 15 shown in available SDKs. JDK 18 is missing." %}
+![Plattform Settings - SDKs](../images/intellij-choose-jdk-adoptopenjdk-on-windows-project-settings.png)
+{% endfigure %}
-Ensure you have a Java 18 SDK configured by navigating to **File > Project Structure > Platform Settings > SDKs**. If you don't have one, add a new Java JDK and point it to the location of a JDK 18. ![Project Settings](../images/intellij-choose-jdk-adoptopenjdk-on-windows-project-settings.png)
+If there is another JDK than JDK 18 selected, click on the plus button and choose "Download JDK..."
-Navigate to **File > Project Structure > Project** and ensure that the projects' SDK is Java 18 ![Use JDK 18 as project SDK](../images/intellij-choose-jdk15-project-default.png)
+{% figure caption:"Download JDK..." %}
+![Plattform Settings - SDKs - plus button - Download JDK...](guidelines-select-download-jdk.png)
+{% endfigure %}
-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 a JDK 18.
+Select JDK version 18 and then Eclipse Temurin.
-To prepare IntelliJ's build system two additional steps are required:
+{% figure caption:"Example for JDK 18 - Choose Eclipse Temurin" %}
+![Download Eclipse Temurin](guidelines-select-jdk-18-eclipse-temurin.png)
+{% endfigure %}
-* Navigate to **File > Settings > Build, Execution, Deployment > Compiler > Java Compiler**, and under "Override compiler parameters per-module" add (\[+]) the following compiler arguments for the `JabRef.main` module. Otherwise, you will get: `java: package com.sun.javafx.scene.control is not visible (package com.sun.javafx.scene.control is declared in module javafx.controls, which does not export it to module org.jabref)`
+After clicking "Download", IntelliJ installs Eclipse Temurin:
- ```
- --add-exports=javafx.controls/com.sun.javafx.scene.control=org.jabref
- --add-exports=org.controlsfx.controls/impl.org.controlsfx.skin=org.jabref
- ```
+{% figure caption:"IntelliJ installs Eclipse Temurin" %}
+![IntelliJ installs Eclipse Temurin](guidelines-intellij-installs-temurin.png)
+{% endfigure %}
- Note that you need to focus another UI element before pressing OK. Otherwise, the setting will be empty.
-* Enable annotation processors by navigating to **File > Settings > Build, Execution, Deployment > Compiler > Annotation processors** and check "Enable annotation processing"
+Navigate to **Project Settings > Project** and ensure that the projects' SDK is Java 18
- ![Enable annotation processing](../images/intellij-enable-annotation-processing.png)
+{% figure caption:"Project SDK is pinned to the downloaded SDK (showing JDK 18 as example)" %}
+![Project SDK is JDK 18](guidelines-intellij-project-settings-jdk18.png)
+{% endfigure %}
-To have autoformat working properly in the context of line wrapping, "Wrap at right margin" has to be disabled as shown below. Details are found in [IntelliJ issue 240517](https://youtrack.jetbrains.com/issue/IDEA-240517).
+Click "OK" to store the changes.
-![](../images/intellij-wrap-at-right-margin.png)
+### Configure the Build System
-To enable "magic" creation and auto cleanup of imports, enable both "Add unambiguous imports on the fly" and "Optimize imports on the fly":
+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 a JDK 18.
-![](../images/grafik.png)
+{% figure caption:"Gradle JVM is project SDK (showing JDK 18 as example)" %}
+![Gradle JVM is project SDK](guidelines-settings-gradle-gradlejvm-is-projectjvm.png)
+{% endfigure %}
-Source: .
+To prepare IntelliJ's build system additional steps are required:
-#### Using Gradle from within IntelliJ IDEA
+Navigate to **Build, Execution, Deployment > Compiler > Java Compiler**, and under "Override compiler parameters per-module", click add (\[+]) and choose `JabRef.main`:
-Ensuring JabRef builds with Gradle should always the first step because, e.g. it generates additional sources that are required for compiling the code. After adjusting all settings mentioned earlier, your first step should be to
+{% figure caption:"Choose JabRef.main" %}
+![Gradle JVM is project SDK](guidelines-choose-module.png)
+{% endfigure %}
-* Open the Gradle Tool Window with the small button that can usually be found on the right side of IDEA or navigate to **View > Tool Windows > Gradle**.
-* In the Gradle Tool Window, press the "Reload All Gradle Projects" button to ensure that all settings are up-to-date with the setting changes.
+Then double click inside the cell "Compilation options" and enter following parameters:
-After that, you can use the Gradle Tool Window to build all parts JabRef and run it. To do so, expand the JabRef project in the Gradle Tool Window and navigate to Tasks. From there, you can
+```text
+--add-exports=javafx.controls/com.sun.javafx.scene.control=org.jabref
+--add-exports=org.controlsfx.controls/impl.org.controlsfx.skin=org.jabref
+```
-* Build and run JabRef by double-clicking **JabRef > Tasks > application > run**.
+Press Enter to have the value really stored.
+Otherwise, it seems like the setting is stored, but it is not there if you re-open this preference dialog.
+Then click on "Apply" to store the setting.
-After that a new entry called "jabref \[run]" will appear in the run configurations. Now you can also select "jabref \[run]" and either run or debug the application from within IntelliJ. You can run any other development task in a similar way. Equivalently, this can also be executed from the terminal by running `./gradlew run`.
+{% figure caption:"Resulting settings for module JabRef.main" %}
+![Overridden compiler parameters](guidelines-overridden-compiler-parameters.png)
+{% endfigure %}
-#### Using IntelliJ's internal build system
+If this step is omited, you will get: `java: package com.sun.javafx.scene.control is not visible (package com.sun.javafx.scene.control is declared in module javafx.controls, which does not export it to module org.jabref)`.
-You should use IntelliJ IDEA's internal build system for compiling and running JabRef during development, because it is usually more responsive. Thereby, **it is important** that you understand that JabRef relies on generated sources which are only build through Gradle. Therefore, to build or update these dependencies you need to run the `assemble` Gradle task at least once.
+Enable annotation processors by navigating to **Build, Execution, Deployment > Compiler > Annotation processors** and check "Enable annotation processing"
-To use IntelliJ IDEA's internal build system when you build JabRef through **Build > Build Project** or use the provided "JabRef Main" run configuration, ensure that
+{% figure caption:"Enabled annotation processing" %}
+![Enable annotation processing](guidelines-intellij-enable-annotation-processing.png)
+{% endfigure %}
-* In **File > Settings > Build, Execution, Deployment > Build Tools > Gradle** the setting "Build and run using" and "Test using" is set to "IntelliJ IDEA".
-* Ignore the Gradle project "buildSrc" by clicking the button **Select Project Data To Import** in the Gradle Tool Window and unchecking the folder "buildSrc".
+### Using Gradle from within IntelliJ IDEA
- ![Ignore the Gradle project "buildSrc"](<../images/intellij-gradle-config-ignore-buildSrc.png>)
-* Add `src-gen` as root:
- 1. Right click on the project "jabref".
- 2. Select "Open Module Settings"
- 3. Expand "JabRef"
- 4. Select "main"
- 5. Select tab "Sources"
- 6. Click "+ Add Content Root"
- 7. Select the `src-gen` directory
- 8. Click "OK". When expanding "main", "java" should have been selected as source
- 9. Click "OK" to save the changes
-* In case the above step does not work, run with gradle, import gradle project again, and try again.
+Ensuring JabRef builds with Gradle should always the first step because, e.g. it generates additional sources that are required for compiling the code.
-Essentially, you now have the best of both worlds: You can run Gradle tasks using the Gradle Tool Window and unless you haven't made changes to input files that generate sources, you can compile and run with IntelliJ's faster internal build system.
+Open the Gradle Tool Window with the small button that can usually be found on the right side of IDEA or navigate to **View > Tool Windows > Gradle**.
+In the Gradle Tool Window, press the "Reload All Gradle Projects" button to ensure that all settings are up-to-date with the setting changes.
-In case all steps are followed, and there are still issues with `SearchBaseVisitor` (e.g., `Error:(16, 25) java: package org.jabref.search does not exist`), you have to delete `src\main\generated\org\jabref\gui\logging\plugins\Log4jPlugins.java`. This is independent of having enabled or disabled Annotation Processing (see above at "Enable Annotation Processing").
+{% figure caption:"Reload of Gradle project" %}
+![Highlighted reload button](guidelines-gradle-tool-windows-refresh.png)
+{% endfigure %}
-~~Note that the above steps might not work on IntelliJ 2020.x.\*\*. You have to keep using gradle for executing tasks. See~~ [~~IDEA-249391~~](https://youtrack.jetbrains.com/issue/IDEA-249391) ~~for details.~~
+After that, you can use the Gradle Tool Window to build all parts JabRef and run it.
+To do so, expand the JabRef project in the Gradle Tool Window and navigate to Tasks.
+From there, you can build and run JabRef by double-clicking **JabRef > Tasks > application > run**.
+
+{% figure caption:"JabRef > Tasks > application > run" %}
+![JabRef > Tasks > application > run](guidelines-gradle-run.png)
+{% endfigure %}
+
+The Gradle run window opens, shows compilation and then the output of JabRef.
+The spinner will run as long as JabRef is opened.
+
+{% figure caption:"Gradle run Window" %}
+![Gradle run window](guidelines-gradle-run-output.png)
+{% endfigure %}
+
+You can close JabRef again.
+
+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.
+
+You can run any other development task in a similar way.
+Equivalently, this can also be executed from the terminal by running `./gradlew run`.
+
+### Using IntelliJ's internal build system for tests
+
+You should use IntelliJ IDEA's internal build system for compiling and running JabRef tests during development, because it is usually more responsive.
+
+{: .important}
+When using IntelliJ's build system, **it is important** that you understand that JabRef relies on generated sources which are only built through Gradle.
+Therefore, to build or update these dependencies you need to run the `run` (or `assemble`) Gradle task at least once.
+When you followed this guide, you should have done it in the Gradle setup.
+
+{: .note}
+Running JabRef itself through IntellIJ's build system is **not** possible as we encounter difficulties when reading resources though `.class.getResource(...)`.
+Although solutions are discussed in length [on stackoverflow](https://stackoverflow.com/q/26328040/873282), there is no "good" solution for us.
+
+In **File > Settings... > Build, Execution, Deployment > Build Tools > Gradle** the setting "Run tests using:" is set to "IntelliJ IDEA".
+
+{% figure caption:"IntelliJ setting: Run tests using IntelliJ" %}
+![IntelliJ setting: Run tests using IntelliJ"](guidelines-intellij-settings-run-tests-using-intellij.png)
+{% endfigure %}
+
+**In case there are difficulties later, this is the place to switch back to gradle.**
+
+Click "OK" to close the preference dialog.
+
+In the menubar, select **Build > Rebuild project**.
+
+IntelliJ now compiles JabRef.
+This should happen without any error.
+
+Now you can use IntelliJ IDEA's internal build system by using **Build > Build Project**.
-#### Using JabRef's code style
+To run an example test from IntelliJ, we let IntelliJ create a launch configuration:
+
+Locate the class `BibEntryTest`:
+Press Ctrl+N.
+Then, the "Search for classes dialog" pops up.
+Enter `bibenrytest`.
+Now, `BibEntryTest` should appear first:
+
+{% figure caption:"IntelliJ search for class “BibEntryTest”" %}
+![IntelliJ search for class "BibEntryTest"](guidelines-intellij-locate-bibentrytest.png)
+{% endfigure %}
+
+Press Enter to jump to that class.
+
+Hover on the green play button on `testDefaultConstructor`:
+
+{% figure caption:"However on green play button" %}
+![However on green play button](guidelines-intellij-run-single-test.png)
+{% endfigure %}
+
+Then, click on it.
+A popup menu opens.
+Choose the first entry "Run testDefaultConstructor" and click on it.
+
+{% figure caption:"Run testDefaultConstructor" %}
+![Popup menu - Run testDefaultConstructor](guidelines-intellij-run-single-test-launch-config.png)
+{% endfigure %}
+
+Then, the single test starts.
+
+You also have an entry in the Launch configurations to directly launch the test.
+You can also click on the debug symbol next to it to enable stopping at breakpoints.
+
+{% figure caption:"Launch menu contains BibEntry test case" %}
+![Launch menu contains BibEntry test case](guidelines-intellij-run-bibentry-test.png)
+{% endfigure %}
+
+The tests are green after the run.
+You can also use the play button there to re-execute the tests.
+A right-click on "BibEntryTests" enables to start the debugger.
+
+{% figure caption:"Run window for the BibEntry test case" %}
+![Run window for the BibEntry test case](guidelines-intellij-tests-are-green.png)
+{% endfigure %}
+
+{: .note }
+Essentially, you now have the best of both worlds:
+You can run Gradle tasks using the Gradle Tool Window.
+You can compile and run tests with IntelliJ's faster internal build system
+(unless you haven't made changes to input files that generate sources).
+
+### Using JabRef's code style
Contributions to JabRef's source code need to have a code formatting that is consistent with existing source code. For that purpose, JabRef provides code-style and check-style definitions.
-* Install the [CheckStyle-IDEA plugin](http://plugins.jetbrains.com/plugin/1065?pr=idea), it can be found via the plug-in repository:
- 1. Navigate to **File > Settings > Plugins > Marketplace** and search for "Checkstyle" and choose "CheckStyle-IDEA"
- 2. Close the settings afterwards and restart IntelliJ
-* Go to **File > Settings > Editor > Code Style**
-* Click on the settings wheel (next to the scheme chooser), then click "Import Scheme"
-* Select the IntelliJ configuration file `config/IntelliJ Code Style.xml`
+Install the [CheckStyle-IDEA plugin](http://plugins.jetbrains.com/plugin/1065?pr=idea), it can be found via the plug-in repository:
+Navigate to **File > Settings... > Plugins".
+On the top, click on "Marketplace".
+Then, search for "Checkstyle".
+Click on "Install" choose "CheckStyle-IDEA".
+
+{% figure caption:"Install CheckStyle" %}
+![Install CheckStyle](guidelines-intellij-install-checkstyle.png)
+{% endfigure %}
+
+After clicking, IntelliJ asks for confirmation:
+
+{% figure caption:"Third Party Plugin Privacy Notice" %}
+![Third Party Plugin Privacy Notice](guidelines-intellij-checkstyle-confirmation.png)
+{% endfigure %}
+
+If you agree, click on "Agree" and you can continue.
+
+Afterwards, use the "Restart IDE" button to restart IntelliJ.
+
+{% figure caption:"IntelliJ restart IDE" %}
+![IntelliJ restart IDE](guidelines-intellij-checkstyle-restart-ide.png)
+{% endfigure %}
+
+Click on "Restart" to finally restart.
-Finally, ensure that the checkstyle configuration file is in place:
+Wait for IntelliJ coming up again.
-1. Go to **File > Settings > Tools > Checkstyle > Configuration File**
-2. Import the CheckStyle configuration file by clicking the \[+] button
-3. For the description provide "JabRef"
-4. Click "Browse" and choose `config/checkstyle/checkstyle.xml`
-5. Click "Next" and "Finish"
-6. Activate the CheckStyle configuration file by ticking it in the list
-7. Ensure that the [latest CheckStyle version](https://checkstyle.org/releasenotes.html) is selected (8.36 or higher). 9.3 is required for Java 18.
-8. Set the "Scan Scope" to "Only Java sources (including tests)
-9. Save settings by clicking "OK"
-10. Your configuration should now look like this:
+Go to **File > Settings... > Editor > Code Style**
- ![checkstyle settings](<../images/intellij-checkstyle-settings.png>)
+Click on the settings wheel (next to the scheme chooser),
+then click "Import Scheme >",
+then click "IntelliJ IDEA code style XML"
-### Setup for Eclipse
+{% figure caption:"Location of “Import Scheme > IntelliJ IDEA code style XML”" %}
+![Location of IntelliJ IDEA code style XML](guidelines-intellij-codestyle-import.png)
+{% endfigure %}
+
+You have to browse for the directory `config` in JabRef's code.
+There is an `IntelliJ Code Style.xml`.
+
+{% figure caption:"Browsing for `config/IntelliJ Code Style.xml`" %}
+![Browsing for config/IntelliJ Code Style.xml](guidelines-intellij-codestyle-import-select-xml-file.png)
+{% endfigure %}
+
+Click "OK".
+
+At following dialog is "Import Scheme".
+Click there "OK", too.
+
+{% figure caption:"Import to JabRef" %}
+![Import to JabRef](guidelines-intellij-codestyle-import-as-jabref.png)
+{% endfigure %}
+
+Click on "Apply" to store the preferences.
+
+### Put JabRef's checkstyle configuration in place
+
+Now, put the checkstyle configuration file is in place:
+
+Go to **File > Settings... > Tools > Checkstyle > Configuration File**
+
+Trigger the import dialog of a CheckStyle style by clicking the \[+] button:
+
+{% figure caption:"Trigger the rule import dialog" %}
+![Trigger the rule import dialog](guidelines-intellij-checkstyle-start-import.png)
+{% endfigure %}
+
+Then:
+
+* Put "JabRef" as description.
+* Browse for `config/checkstyle/checkstyle.xml`
+* Tick "Store relative to project location"
+* Click "Next"
+
+{% figure caption:"Filled Rule Import Dialog" %}
+![Filled Rule Import Dialog](guidelines-intellij-checkstyle-import-file.png)
+{% endfigure %}
+
+Click on "Finish"
+
+Activate the CheckStyle configuration file by ticking it in the list
+
+{% figure caption:"JabRef's checkstyle config is activated" %}
+![JabRef's checkstyle config is activated](guidelines-intellij-checkstyle-jabref-active.png)
+{% endfigure %}
+
+Ensure that the [latest CheckStyle version](https://checkstyle.org/releasenotes.html) is selected (10.3.4 or higher).
+Also, set the "Scan Scope" to "Only Java sources (including tests)".
+
+{% figure caption:"Checkstyle is the highest version - and tests are also scanned" %}
+![Checkstyle is the highest version - and tests are also scanned](guidelines-intellij-checkstyle-final-settings.png)
+{% endfigure %}
+
+Save settings by clicking "Apply" and then "OK"
+
+In the lower part of IntelliJ's window, click on "Checkstyle".
+In "Rules", change to "JabRef".
+Then, you can run a check on all modified files.
+
+{% figure caption:"JabRef's style is active - and we are ready to run a check on all modified files" %}
+![JabRef's style is active - and we are ready to run a check on all modified files](guidelines-intellij-checkstyle-window.png)
+{% endfigure %}
+
+### Have auto format working properly in JavaDoc
+
+To have auto format working properly in the context of JavaDoc and line wrapping, "Wrap at right margin" has to be disabled. Details are found in [IntelliJ issue 240517](https://youtrack.jetbrains.com/issue/IDEA-240517).
+
+Go to **File > Settings... > Editor > Code Style > Java > JavaDoc**.
+
+At "Other", disable "Wrap at right margin"
+
+{% figure caption:"”Wrap at right margin” disabled" %}
+!["Wrap at right margin" disabled](guidelines-intellij-editor-javadoc-do-not-wrap.png)
+{% endfigure %}
+
+### Enable proper import cleanup
+
+To enable "magic" creation and auto cleanup of imports, go to **File > Settings... > Editor > General > Auto Import**.
+There, enable both "Add unambiguous imports on the fly" and "Optimize imports on the fly"
+(Source: [JetBrains help](https://www.jetbrains.com/help/idea/creating-and-optimizing-imports.html#automatically-add-import-statements)).
+
+{% figure caption:"Auto import enabled" %}
+![Enable auto import](guidelines-intellij-editor-autoimport.png)
+{% endfigure %}
+
+Press "OK".
+
+### Disable too advanced code folding
+
+Go to **File > Settings... > Editor > General > Code Folding**.
+At "Java", disable "General > File header", "General > Imports", and "Java > One-line methods".
+
+{% figure caption:"Code foldings disabled" %}
+![Code foldings disabled](guidelines-settings-intellij-code-foldings.png)
+{% endfigure %}
+
+Press "OK".
+
+{: .highlight }
+> Now you have configured IntelliJ completely.
+> You can run the main application using Gradle and the test cases using IntelliJ.
+> The code formatting rules are imported - and the most common styling issue at imports is automatically resolved by IntelliJ.
+> Finally, you have Checkstyle running locally so that you can check for styling errors before submitting the pull request.
+>
+> We wish you a successful contribution!
+
+### Key hints for IntelliJ
+
+* Shift+Shift (AKA double-shift): Open the search dialog.
+* Ctrl+N: Open the search dialog and select search for a class.
+* Ctrl+Shift+F: Search everywhere in the code base.
+* Alt+F1 and then Enter: Locate the file in the search bar on the left side.
+* Ctrl+Shift+T: Navigate from a class to the test class.
+
+## Setup for Eclipse
Make sure your Eclipse installation us up to date.
@@ -219,12 +529,12 @@ Make sure your Eclipse installation us up to date.
* Remark: Importing it as gradle project will not work correctly.
* Refresh the project in Eclipse
4. Create a run/debug configuration for the main class `org.jabref.cli.Launcher` and/or for `org.jabref.gui.JabRefMain` (both can be used equivalently)
- * Remark: The run/debug configuration needs to be added by right clicking the class (e.g. `Launcher` or JabRefMain) otherwise it will not work.
+ * Remark: The run/debug configuration needs to be added by right clicking the class (e.g. `Launcher` or JabRefMain) otherwise it will not work.
![Creating the run/debug configuration by right clicking on the class](<../images/eclipse-create-run-config.png>)
- * In the tab "Arguments" of the run/debug configuration, enter the following runtime VM arguments:
+ * In the tab "Arguments" of the run/debug configuration, enter the following runtime VM arguments:
- ```
+ ```text
--add-exports javafx.controls/com.sun.javafx.scene.control=org.jabref
--add-exports org.controlsfx.controls/impl.org.controlsfx.skin=org.jabref
--add-exports javafx.graphics/com.sun.javafx.scene=org.controlsfx.controls
@@ -243,30 +553,16 @@ Make sure your Eclipse installation us up to date.
--add-exports com.oracle.truffle.regex/com.oracle.truffle.regex=org.graalvm.truffle
--patch-module org.jabref=build/resources/main
```
+
* In the tab "Dependencies" of the run/debug configuration tick the checkbox "Exclude test code"
5. Optional: Install the [e(fx)clipse plugin](http://www.eclipse.org/efxclipse/index.html) from the Eclipse marketplace: 1. Help -> Eclipse Marketplace... -> Search tab 2. Enter "e(fx)clipse" in the search dialogue 3. Click "Go" 4. Click "Install" button next to the plugin 5. Click "Finish"
6. Now you can build and run/debug the application by either using `Launcher` or `JabRefMain`. This is the recommended way, since the application starts quite fast.
-### Localization Test Configuration (IDEA)
-
-In the run configuration, there is a "Build and run" section. At the "VM options" (the box below the java version), following content needs to be present:
-
-```
--ea
---add-exports=javafx.controls/com.sun.javafx.scene.control=org.jabref
---add-exports=org.controlsfx.controls/impl.org.controlsfx.skin=org.jabref
---add-exports javafx.graphics/com.sun.javafx.application=ALL-UNNAMED
---add-exports javafx.graphics/com.sun.javafx.stage=ALL-UNNAMED
---add-exports javafx.graphics/com.sun.javafx.stage=com.jfoenix
-```
-
-Now the test will run.
-
### Localization Test Configuration (Eclipse)
To run the `LocalizationConsistencyTest` you need to add some extra module information: Right-click on the file -> "Run/Debug as JUnit test". Go to the Run/debug configuration created for that file and in the arguments tab under VM-configurations add:
-```
+```text
--add-exports javafx.graphics/com.sun.javafx.application=ALL-UNNAMED
--add-exports javafx.graphics/com.sun.javafx.stage=ALL-UNNAMED
--add-exports javafx.graphics/com.sun.javafx.stage=com.jfoenix
@@ -274,7 +570,12 @@ To run the `LocalizationConsistencyTest` you need to add some extra module infor
## Final comments
-Got it running? GREAT! You are ready to lurk the code and contribute to JabRef. Please make sure to also read our [contribution guide](https://github.com/JabRef/jabref/blob/master/CONTRIBUTING.md).
+Got it running? GREAT! You are ready to lurk the code and contribute to JabRef. Please make sure to also read our [contribution guide](https://github.com/JabRef/jabref/blob/main/CONTRIBUTING.md).
+
+### Other tooling
+
+We collected some other tooling recommendations.
+We invite you to read on at our [tool recommendations](../code-howtos/tools.md).
## Common issues
@@ -282,7 +583,7 @@ Got it running? GREAT! You are ready to lurk the code and contribute to JabRef.
An indication that `JAVA_HOME` is not correctly set or no JDK 18 is installed is following error message:
-```
+```text
compileJava FAILED
FAILURE: Build failed with an exception.
@@ -294,7 +595,7 @@ Execution failed for task ':compileJava'.
Another indication is following output
-```
+```text
java.lang.UnsupportedClassVersionError: org/javamodularity/moduleplugin/ModuleSystemPlugin has been compiled by a more recent version of the Java Runtime (class file version 55.0), this version of the Java Runtime only recognizes class file versions up to 52.0
```
@@ -312,7 +613,7 @@ In rare cases you might encounter problems due to out-dated automatically genera
Following error message appears:
-```
+```text
Error occurred during initialization of boot layer
java.lang.module.FindException: Module org.jsoup not found, required by org.jabref
```
@@ -326,17 +627,17 @@ This can include different modules.
5. Click on "Invalidate and Restart".
6. After IntelliJ restarted, you have to do the "buildSrc", "Log4JAppender", and "src-gen" steps again.
-### Issues with openjfx libraries in local maven repository
+### Issues with OpenJFX libraries in local maven repository
-There might be problems with building if you have openjfx libraries in local maven repository, resulting in errors like this:
+There might be problems with building if you have OpenJFX libraries in local maven repository, resulting in errors like this:
-```
+```text
> Could not find javafx-fxml-18-mac.jar (org.openjfx:javafx-fxml:18).
Searched in the following locations:
file:/repository/org/openjfx/javafx-fxml/18/javafx-fxml-18-mac.jar
```
-As a workaround, you can remove all local openjfx artifacts by deleting the whole openjfx folder from specified location.
+As a workaround, you can remove all local OpenJFX artifacts by deleting the whole OpenJFX folder from specified location.
### Issues with `JournalAbbreviationLoader`
@@ -350,11 +651,85 @@ If that does not help:
4. Execute `./gradlew run`
5. Start IntelliJ and try again.
-### Issue with `_closure2.class is a duplicate but no duplicate handling strategy has been set`
+### Hints for IntelliJ 2021
+
+In older IntelliJ setups, more things were necessary:
+
+Ignore the Gradle project "buildSrc" by clicking the button **Select Project Data To Import** in the Gradle Tool Window and unchecking the folder "buildSrc".
+
+![Ignore the Gradle project "buildSrc"](../images/intellij-gradle-config-ignore-buildSrc.png)
+
+Add `src-gen` as root:
+
+1. Right click on the project "jabref".
+2. Select "Open Module Settings"
+3. Expand "JabRef"
+4. Select "main"
+5. Select tab "Sources"
+6. Click "+ Add Content Root"
+7. Select the `src-gen` directory
+8. Click "OK". When expanding "main", "java" should have been selected as source
+9. Click "OK" to save the changes
+
+In case the above step does not work, run with gradle, import gradle project again, and try again.
+
+~~Note that the above steps might not work on IntelliJ 2020.x.\*\*. You have to keep using gradle for executing tasks. See~~ [~~IDEA-249391~~](https://youtrack.jetbrains.com/issue/IDEA-249391) ~~for details.~~
+
+In case all steps are followed, and there are still issues with `SearchBaseVisitor` (e.g., `Error:(16, 25) java: package org.jabref.search does not exist`), you have to delete `src\main\generated\org\jabref\gui\logging\plugins\Log4jPlugins.java`. This is independent of having enabled or disabled Annotation Processing (see above at "Enable Annotation Processing").
+
+---
+
+In **File > Settings... > Build, Execution, Deployment > Build Tools > Gradle** the setting "Build and run using" and "Test using" is set to "IntelliJ IDEA".
+
+{% figure caption:"IntelliJ setting: Build and run using IntelliJ" %}
+![IntelliJ setting: Build and run using IntelliJ"](guidelines-intellij-settings-build-and-run-intellij.png)
+{% endfigure %}
+
+**In case there are difficulties later, this is the place to switch back to gradle.**
+
+Click "OK" to close the preference dialog.
+
+In the menubar, select **Build > Rebuild project**.
+
+IntelliJ now compiles JabRef.
+This should happen without any error.
+
+Now you can use IntelliJ IDEA's internal build system by using **Build > Build Project**.
+
+To run JabRef from IntelliJ, we let IntelliJ create a launch configuration:
+
+Locate the class `Launcher`:
+Press Ctrl+N.
+Then, the "Search for classes dialog" pops up.
+Enter `Launcher`.
+Now, only one class should have been found:
+
+{% figure caption:"IntelliJ search for class “Launcher”" %}
+![IntelliJ search for class "Launcher"](guidelines-intellij-search-for-launcher.png)
+{% endfigure %}
+
+Press Enter to jump to that class.
+
+Hover on the green play button.
+
+{% figure caption:"However on green play" %}
+![However on green play](guidelines-intellij-hover-on-play-button.png)
+{% endfigure %}
+
+Then, click on it.
+A popup menu opens.
+Choose the first entry and click on it.
+
+{% figure caption:"Run JabRef via launcher" %}
+![Popup menu - Run JabRef via launcher](guidelines-intellij-run-jabref-from-launcher.png)
+{% endfigure %}
+
+Then, JabRef starts.
-After changing the contents of `build.gradle`, on might get following error:
+You also have an entry in the Launch configurations to directly launch the JabRef GUI:
- Entry org/jabref/build/JournalAbbreviationConverter$_convert_closure1$_closure2.class is a duplicate but no duplicate handling strategy has been set.
+{% figure caption:"Launch menu contains “Launcher”" %}
+![Launch menu contains launcher](intellij-run-launcher.png)
+{% endfigure %}
-Please update to the latest development code.
-We applied the workaround from .
+You can also click on the debug symbol next to it to enable stopping at breakpoints.
diff --git a/docs/getting-into-the-code/guidelines-gradle-run-output.png b/docs/getting-into-the-code/guidelines-gradle-run-output.png
new file mode 100644
index 00000000000..9ecbae34a32
Binary files /dev/null and b/docs/getting-into-the-code/guidelines-gradle-run-output.png differ
diff --git a/docs/getting-into-the-code/guidelines-gradle-run.png b/docs/getting-into-the-code/guidelines-gradle-run.png
new file mode 100644
index 00000000000..a1a7ed25789
Binary files /dev/null and b/docs/getting-into-the-code/guidelines-gradle-run.png differ
diff --git a/docs/getting-into-the-code/guidelines-gradle-tool-windows-refresh.png b/docs/getting-into-the-code/guidelines-gradle-tool-windows-refresh.png
new file mode 100644
index 00000000000..1531a35d38e
Binary files /dev/null and b/docs/getting-into-the-code/guidelines-gradle-tool-windows-refresh.png differ
diff --git a/docs/getting-into-the-code/guidelines-intellij-checkstyle-confirmation.png b/docs/getting-into-the-code/guidelines-intellij-checkstyle-confirmation.png
new file mode 100644
index 00000000000..516a38d4b30
Binary files /dev/null and b/docs/getting-into-the-code/guidelines-intellij-checkstyle-confirmation.png differ
diff --git a/docs/getting-into-the-code/guidelines-intellij-checkstyle-final-settings.png b/docs/getting-into-the-code/guidelines-intellij-checkstyle-final-settings.png
new file mode 100644
index 00000000000..c49a964e1ea
Binary files /dev/null and b/docs/getting-into-the-code/guidelines-intellij-checkstyle-final-settings.png differ
diff --git a/docs/getting-into-the-code/guidelines-intellij-checkstyle-import-file.png b/docs/getting-into-the-code/guidelines-intellij-checkstyle-import-file.png
new file mode 100644
index 00000000000..950d2f2c528
Binary files /dev/null and b/docs/getting-into-the-code/guidelines-intellij-checkstyle-import-file.png differ
diff --git a/docs/getting-into-the-code/guidelines-intellij-checkstyle-jabref-active.png b/docs/getting-into-the-code/guidelines-intellij-checkstyle-jabref-active.png
new file mode 100644
index 00000000000..07225747b6e
Binary files /dev/null and b/docs/getting-into-the-code/guidelines-intellij-checkstyle-jabref-active.png differ
diff --git a/docs/getting-into-the-code/guidelines-intellij-checkstyle-restart-ide.png b/docs/getting-into-the-code/guidelines-intellij-checkstyle-restart-ide.png
new file mode 100644
index 00000000000..939f5e8954a
Binary files /dev/null and b/docs/getting-into-the-code/guidelines-intellij-checkstyle-restart-ide.png differ
diff --git a/docs/getting-into-the-code/guidelines-intellij-checkstyle-start-import.png b/docs/getting-into-the-code/guidelines-intellij-checkstyle-start-import.png
new file mode 100644
index 00000000000..5d1e52664e7
Binary files /dev/null and b/docs/getting-into-the-code/guidelines-intellij-checkstyle-start-import.png differ
diff --git a/docs/getting-into-the-code/guidelines-intellij-checkstyle-window.png b/docs/getting-into-the-code/guidelines-intellij-checkstyle-window.png
new file mode 100644
index 00000000000..581b05802ff
Binary files /dev/null and b/docs/getting-into-the-code/guidelines-intellij-checkstyle-window.png differ
diff --git a/docs/getting-into-the-code/guidelines-intellij-choose-build-gradle.png b/docs/getting-into-the-code/guidelines-intellij-choose-build-gradle.png
new file mode 100644
index 00000000000..d9a78c3b3db
Binary files /dev/null and b/docs/getting-into-the-code/guidelines-intellij-choose-build-gradle.png differ
diff --git a/docs/getting-into-the-code/guidelines-intellij-codestyle-import-as-jabref.png b/docs/getting-into-the-code/guidelines-intellij-codestyle-import-as-jabref.png
new file mode 100644
index 00000000000..59ce0514d7f
Binary files /dev/null and b/docs/getting-into-the-code/guidelines-intellij-codestyle-import-as-jabref.png differ
diff --git a/docs/getting-into-the-code/guidelines-intellij-codestyle-import-select-xml-file.png b/docs/getting-into-the-code/guidelines-intellij-codestyle-import-select-xml-file.png
new file mode 100644
index 00000000000..a31fb636ca2
Binary files /dev/null and b/docs/getting-into-the-code/guidelines-intellij-codestyle-import-select-xml-file.png differ
diff --git a/docs/getting-into-the-code/guidelines-intellij-codestyle-import.png b/docs/getting-into-the-code/guidelines-intellij-codestyle-import.png
new file mode 100644
index 00000000000..c1a32877c00
Binary files /dev/null and b/docs/getting-into-the-code/guidelines-intellij-codestyle-import.png differ
diff --git a/docs/images/grafik.png b/docs/getting-into-the-code/guidelines-intellij-editor-autoimport.png
similarity index 100%
rename from docs/images/grafik.png
rename to docs/getting-into-the-code/guidelines-intellij-editor-autoimport.png
diff --git a/docs/getting-into-the-code/guidelines-intellij-editor-javadoc-do-not-wrap.png b/docs/getting-into-the-code/guidelines-intellij-editor-javadoc-do-not-wrap.png
new file mode 100644
index 00000000000..f8ff03b84e3
Binary files /dev/null and b/docs/getting-into-the-code/guidelines-intellij-editor-javadoc-do-not-wrap.png differ
diff --git a/docs/getting-into-the-code/guidelines-intellij-enable-annotation-processing.png b/docs/getting-into-the-code/guidelines-intellij-enable-annotation-processing.png
new file mode 100644
index 00000000000..a6530681614
Binary files /dev/null and b/docs/getting-into-the-code/guidelines-intellij-enable-annotation-processing.png differ
diff --git a/docs/getting-into-the-code/guidelines-intellij-hover-on-play-button.png b/docs/getting-into-the-code/guidelines-intellij-hover-on-play-button.png
new file mode 100644
index 00000000000..fb824f7392f
Binary files /dev/null and b/docs/getting-into-the-code/guidelines-intellij-hover-on-play-button.png differ
diff --git a/docs/getting-into-the-code/guidelines-intellij-install-checkstyle.png b/docs/getting-into-the-code/guidelines-intellij-install-checkstyle.png
new file mode 100644
index 00000000000..672d568b41a
Binary files /dev/null and b/docs/getting-into-the-code/guidelines-intellij-install-checkstyle.png differ
diff --git a/docs/getting-into-the-code/guidelines-intellij-installs-temurin.png b/docs/getting-into-the-code/guidelines-intellij-installs-temurin.png
new file mode 100644
index 00000000000..e85b5608460
Binary files /dev/null and b/docs/getting-into-the-code/guidelines-intellij-installs-temurin.png differ
diff --git a/docs/getting-into-the-code/guidelines-intellij-locate-bibentrytest.png b/docs/getting-into-the-code/guidelines-intellij-locate-bibentrytest.png
new file mode 100644
index 00000000000..9a972b35380
Binary files /dev/null and b/docs/getting-into-the-code/guidelines-intellij-locate-bibentrytest.png differ
diff --git a/docs/getting-into-the-code/guidelines-intellij-project-settings-jdk18.png b/docs/getting-into-the-code/guidelines-intellij-project-settings-jdk18.png
new file mode 100644
index 00000000000..2f60589ae37
Binary files /dev/null and b/docs/getting-into-the-code/guidelines-intellij-project-settings-jdk18.png differ
diff --git a/docs/getting-into-the-code/guidelines-intellij-run-bibentry-test.png b/docs/getting-into-the-code/guidelines-intellij-run-bibentry-test.png
new file mode 100644
index 00000000000..c3470e66ae3
Binary files /dev/null and b/docs/getting-into-the-code/guidelines-intellij-run-bibentry-test.png differ
diff --git a/docs/getting-into-the-code/guidelines-intellij-run-jabref-from-launcher.png b/docs/getting-into-the-code/guidelines-intellij-run-jabref-from-launcher.png
new file mode 100644
index 00000000000..a2717408b2c
Binary files /dev/null and b/docs/getting-into-the-code/guidelines-intellij-run-jabref-from-launcher.png differ
diff --git a/docs/getting-into-the-code/guidelines-intellij-run-single-test-launch-config.png b/docs/getting-into-the-code/guidelines-intellij-run-single-test-launch-config.png
new file mode 100644
index 00000000000..ccd4c998ea5
Binary files /dev/null and b/docs/getting-into-the-code/guidelines-intellij-run-single-test-launch-config.png differ
diff --git a/docs/getting-into-the-code/guidelines-intellij-run-single-test.png b/docs/getting-into-the-code/guidelines-intellij-run-single-test.png
new file mode 100644
index 00000000000..4397e3d8ba7
Binary files /dev/null and b/docs/getting-into-the-code/guidelines-intellij-run-single-test.png differ
diff --git a/docs/getting-into-the-code/guidelines-intellij-search-for-launcher.png b/docs/getting-into-the-code/guidelines-intellij-search-for-launcher.png
new file mode 100644
index 00000000000..87f2dd7ef8e
Binary files /dev/null and b/docs/getting-into-the-code/guidelines-intellij-search-for-launcher.png differ
diff --git a/docs/getting-into-the-code/guidelines-intellij-settings-build-and-run-intellij.png b/docs/getting-into-the-code/guidelines-intellij-settings-build-and-run-intellij.png
new file mode 100644
index 00000000000..7d57f041936
Binary files /dev/null and b/docs/getting-into-the-code/guidelines-intellij-settings-build-and-run-intellij.png differ
diff --git a/docs/getting-into-the-code/guidelines-intellij-settings-run-tests-using-intellij.png b/docs/getting-into-the-code/guidelines-intellij-settings-run-tests-using-intellij.png
new file mode 100644
index 00000000000..9288ae40a81
Binary files /dev/null and b/docs/getting-into-the-code/guidelines-intellij-settings-run-tests-using-intellij.png differ
diff --git a/docs/getting-into-the-code/guidelines-intellij-start-window.png b/docs/getting-into-the-code/guidelines-intellij-start-window.png
new file mode 100644
index 00000000000..2ce208b45ae
Binary files /dev/null and b/docs/getting-into-the-code/guidelines-intellij-start-window.png differ
diff --git a/docs/getting-into-the-code/guidelines-intellij-tests-are-green.png b/docs/getting-into-the-code/guidelines-intellij-tests-are-green.png
new file mode 100644
index 00000000000..178e64d01e1
Binary files /dev/null and b/docs/getting-into-the-code/guidelines-intellij-tests-are-green.png differ
diff --git a/docs/getting-into-the-code/guidelines-overridden-compiler-parameters.png b/docs/getting-into-the-code/guidelines-overridden-compiler-parameters.png
new file mode 100644
index 00000000000..a492bc68f90
Binary files /dev/null and b/docs/getting-into-the-code/guidelines-overridden-compiler-parameters.png differ
diff --git a/docs/getting-into-the-code/guidelines-select-download-jdk.png b/docs/getting-into-the-code/guidelines-select-download-jdk.png
new file mode 100644
index 00000000000..596abe28a9b
Binary files /dev/null and b/docs/getting-into-the-code/guidelines-select-download-jdk.png differ
diff --git a/docs/getting-into-the-code/guidelines-select-jdk-18-eclipse-temurin.png b/docs/getting-into-the-code/guidelines-select-jdk-18-eclipse-temurin.png
new file mode 100644
index 00000000000..4472216f0c6
Binary files /dev/null and b/docs/getting-into-the-code/guidelines-select-jdk-18-eclipse-temurin.png differ
diff --git a/docs/getting-into-the-code/guidelines-settings-gradle-gradlejvm-is-projectjvm.png b/docs/getting-into-the-code/guidelines-settings-gradle-gradlejvm-is-projectjvm.png
new file mode 100644
index 00000000000..7a9b155bdf9
Binary files /dev/null and b/docs/getting-into-the-code/guidelines-settings-gradle-gradlejvm-is-projectjvm.png differ
diff --git a/docs/getting-into-the-code/guidelines-settings-intellij-code-foldings.png b/docs/getting-into-the-code/guidelines-settings-intellij-code-foldings.png
new file mode 100644
index 00000000000..735ca1bd159
Binary files /dev/null and b/docs/getting-into-the-code/guidelines-settings-intellij-code-foldings.png differ
diff --git a/docs/getting-into-the-code/guidelines-trust-project.png b/docs/getting-into-the-code/guidelines-trust-project.png
new file mode 100644
index 00000000000..47e7286d53f
Binary files /dev/null and b/docs/getting-into-the-code/guidelines-trust-project.png differ
diff --git a/docs/getting-into-the-code/high-level-documentation.md b/docs/getting-into-the-code/high-level-documentation.md
index 315246f062d..bc494940c1a 100644
--- a/docs/getting-into-the-code/high-level-documentation.md
+++ b/docs/getting-into-the-code/high-level-documentation.md
@@ -4,7 +4,7 @@ nav_order: 3
---
# High-level documentation
-This page describes relevant information about the code structure of JabRef precisely and succinctly. Closer-to-code documentation is available at [Code HowTos](code-howtos.md).
+This page describes relevant information about the code structure of JabRef precisely and succinctly. Closer-to-code documentation is available at [Code HowTos](../code-howtos).
We have been successfully transitioning from a spaghetti to a more structured architecture with the `model` in the center, and the `logic` as an intermediate layer towards the `gui` which is the outer shell. There are additional utility packages for `preferences` and the `cli`. The dependencies are only directed towards the center. We have JUnit tests to detect violations of the most crucial dependencies (between `logic`, `model`, and `gui`), and the build will fail automatically in these cases.
diff --git a/docs/getting-into-the-code/intellij-run-launcher.png b/docs/getting-into-the-code/intellij-run-launcher.png
new file mode 100644
index 00000000000..be78b64312c
Binary files /dev/null and b/docs/getting-into-the-code/intellij-run-launcher.png differ
diff --git a/docs/images/intellij-checkstyle-settings.png b/docs/images/intellij-checkstyle-settings.png
deleted file mode 100644
index e5f0c027190..00000000000
Binary files a/docs/images/intellij-checkstyle-settings.png and /dev/null differ
diff --git a/docs/images/intellij-choose-jdk15-project-default.png b/docs/images/intellij-choose-jdk15-project-default.png
deleted file mode 100644
index ec031773c25..00000000000
Binary files a/docs/images/intellij-choose-jdk15-project-default.png and /dev/null differ
diff --git a/docs/images/intellij-enable-annotation-processing.png b/docs/images/intellij-enable-annotation-processing.png
deleted file mode 100644
index 784ce6cd19e..00000000000
Binary files a/docs/images/intellij-enable-annotation-processing.png and /dev/null differ
diff --git a/docs/images/intellij-wrap-at-right-margin.png b/docs/images/intellij-wrap-at-right-margin.png
deleted file mode 100644
index c3b7615fdda..00000000000
Binary files a/docs/images/intellij-wrap-at-right-margin.png and /dev/null differ
diff --git a/docs/index.md b/docs/index.md
index 69f3624bedb..8712b91bacc 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -1,5 +1,6 @@
---
nav_order: 1
+layout: home
---
# Overview on Developing JabRef
diff --git a/docs/teaching.md b/docs/teaching.md
index 097110ce58e..d2f46159b49 100644
--- a/docs/teaching.md
+++ b/docs/teaching.md
@@ -17,13 +17,14 @@ By using JabRef as training object in exercises and labs, students can level-up
1. Choose task from the board [Candidates for university projects](https://github.com/orgs/JabRef/projects/3/views/3).
There, new functionality is categorized in small, medium, and large effort.
- Morevoer, categorization on the main focus (UI, logic, or both),
+ Moreover, categorization on the main focus (UI, logic, or both),
implementation effort, testing effort, and "issue understanding effort".
The latter category is important, because some issues are "quick wins" and others need thorough thinking.
In general, all issues of JabRef are free to take.
Be aware that the difficulty of bugs and feature vary.
- For the brave, the [Bug Board](https://github.com/JabRef/jabref/projects/5) or the [Feature Board](https://github.com/JabRef/jabref/projects/7) provide other issue sources.
+ For the brave, the [Bug Board](https://github.com/orgs/JabRef/projects/7) or the [Feature Board](https://github.com/JabRef/jabref/projects/6) provide other issue sources.
Especially for Master students, these are excellent boards to find issues that train maintenance knowledge (which is essential for industry work).
+ Finally, there is a [collection of good first issues](https://github.com/orgs/JabRef/projects/5), if you search for something to start guiding you though a focused aspect of JabRef's code.
2. Get in touch with the JabRef team to reserve issues for your student group and possibly to discuss details. We offer email, skype, [gitter.im](https://gitter.im/JabRef/jabref), discord. Get in touch with [@koppor](https://github.com/koppor/) to find the right channel and to start forming the success of your course.
3. Schedule tasks with students
4. Students implement code
@@ -74,6 +75,15 @@ Course: Open Source Software Development
* Summary: In this course, students will be introduced to the processes and tools specific to Open Source Software development, and they will analyze existing projects to understand the architecture and processes of these projects. Besides, students will attempt to contribute source code to a large existing Open Source Software project.
* Course offered in 2018 and 2019. Examples of merged pull requests: [4217](https://github.com/JabRef/jabref/pull/4217), [4255](https://github.com/JabRef/jabref/pull/4255), [4227](https://github.com/JabRef/jabref/pull/4227), [4260](https://github.com/JabRef/jabref/pull/4260), [5150](https://github.com/JabRef/jabref/pull/5150)
+#### King's College London
+
+Course: BSc Computer Science Individual Project
+
+* Summary: Students experience the procedure of finding and fixing small and medium issues in an open source project.
+* Successfully run in 2022/2023
+
+Do you have additions/suggestions for improvement?
+
#### Northern Arizona University (NAU), USA
Course [CS499 - Open Source Software Development](https://github.com/igorsteinmacher/CS499-OSS)
@@ -88,7 +98,7 @@ Course [CS499 - Open Source Software Development](https://github.com/igorsteinma
Course [10915-01: Software Engineering](https://dmi.unibas.ch/de/studium/computer-science-informatik/lehrangebot-hs18/vorlesung-software-engineering/)
* Lecture Materials: [https://github.com/unibas-marcelluethi/software-engineering](https://github.com/unibas-marcelluethi/software-engineering)
-* Successfully run Q3 2019.
+* Successfully run 2019 to 2022.
* Excercise touching JabRef:
* General idea: identify a feature missing in JabRef and develop the specification, system design, and implementation of the feature.
* Introduction to JabRef's code: [Exercise 5](https://github.com/unibas-marcelluethi/software-engineering/blob/master/docs/week5/exercises/practical-exercises.md): Introduction into JabRef code.
diff --git a/external-libraries.md b/external-libraries.md
index 1eb2b8eeb71..fce62adf6fd 100644
--- a/external-libraries.md
+++ b/external-libraries.md
@@ -49,6 +49,13 @@ URL: https://github.com/FasterXML/jackson
License: Apache-2.0
```
+```yaml
+Id: com.github.JabRef
+Project: afterburner.fx
+URL: https://github.com/JabRef/afterburner.fx
+License: Apache-2.0
+```
+
```yaml
Id: com.github.tomtung
Project: latex2unicode
@@ -232,6 +239,13 @@ URL: https://projects.eclipse.org/projects/ee4j.ca
License: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
```
+```yaml
+Id: jakarta.inject:jakarata.inject-api
+Project: Jakarta Inject
+URL: https://projects.eclipse.org/projects/ee4j.ca
+License: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+```
+
```yaml
Id: jakarta.xml.bind:jakarta.xml.bind-api
Project: Jakarta XML Binding project
@@ -503,17 +517,18 @@ License: Apache-2.0
## Sorted list of runtime dependencies output by gradle
-1. `gradlew dependencies > build\dependencies.txt`
+1. `gradlew dependencies > build/dependencies.txt`
2. Manually edit depedencies.txt to contain the tree of "compileClasspath" and "implementation" only. Otherwise, libraries such as "Apache Commons Lang 3" are missed.
3. (on WSL) `sed 's/[^a-z]*//' < build/dependencies.txt | sed "s/\(.*\) .*/\1/" | grep -v "\->" | sort | uniq > build/dependencies-for-external-libraries.txt`
```text
-com.fasterxml.jackson.core:jackson-annotations:2.13.3
-com.fasterxml.jackson.core:jackson-core:2.13.3
-com.fasterxml.jackson.core:jackson-databind:2.13.3
-com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.13.3
-com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.13.3
-com.fasterxml.jackson:jackson-bom:2.13.3
+com.fasterxml.jackson.core:jackson-annotations:2.14.1
+com.fasterxml.jackson.core:jackson-core:2.14.1
+com.fasterxml.jackson.core:jackson-databind:2.14.1
+com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.14.1
+com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.14.1
+com.fasterxml.jackson:jackson-bom:2.14.1
+com.github.JabRef:afterburner.fx:testmoduleinfo-SNAPSHOT
com.github.sialcasa.mvvmFX:mvvmfx-validation:f195849ca9
com.github.tomtung:latex2unicode_2.13:0.3.2
com.google.code.gson:gson:2.9.0
@@ -525,7 +540,7 @@ com.google.j2objc:j2objc-annotations:1.3
com.googlecode.javaewah:JavaEWAH:1.1.13
com.h2database:h2-mvstore:2.1.214
com.jfoenix:jfoenix:9.0.10
-com.konghq:unirest-java:3.13.10
+com.konghq:unirest-java:3.14.1
com.microsoft.azure:applicationinsights-core:2.4.1
com.microsoft.azure:applicationinsights-logging-log4j2:2.4.1
com.oracle.ojdbc:ojdbc10:19.3.0.0
@@ -536,7 +551,7 @@ com.oracle.ojdbc:simplefan:19.3.0.0
com.oracle.ojdbc:ucp:19.3.0.0
com.sun.activation:jakarta.activation:2.0.1
com.sun.istack:istack-commons-runtime:4.0.1
-com.tobiasdiez:easybind:2.2
+com.tobiasdiez:easybind:2.2.1-SNAPSHOT
com.vladsch.flexmark:flexmark-ext-gfm-strikethrough:0.64.0
com.vladsch.flexmark:flexmark-ext-gfm-tasklist:0.64.0
com.vladsch.flexmark:flexmark-util-ast:0.64.0
@@ -561,7 +576,8 @@ de.undercouch:citeproc-java:3.0.0-alpha.6
eu.lestard:doc-annotations:0.2
info.debatty:java-string-similarity:2.0.0
io.github.java-diff-utils:java-diff-utils:4.12
-jakarta.annotation:jakarta.annotation-api:1.3.5
+jakarta.annotation:jakarta.annotation-api:2.1.1
+jakarta.inject:jakarta.inject-api:2.0.1
jakarta.xml.bind:jakarta.xml.bind-api:3.0.1
net.harawata:appdirs:1.2.1
net.java.dev.jna:jna-platform:5.6.0
@@ -571,58 +587,57 @@ net.jodah:typetools:0.6.1
org.antlr:antlr4-runtime:4.9.3
org.apache.commons:commons-csv:1.9.0
org.apache.commons:commons-lang3:3.12.0
-org.apache.commons:commons-text:1.9
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.lucene:lucene-analysis-common:9.3.0
-org.apache.lucene:lucene-core:9.3.0
-org.apache.lucene:lucene-highlighter:9.3.0
-org.apache.lucene:lucene-memory:9.3.0
-org.apache.lucene:lucene-queries:9.3.0
-org.apache.lucene:lucene-queryparser:9.3.0
-org.apache.lucene:lucene-sandbox:9.3.0
+org.apache.lucene:lucene-analysis-common:9.4.2
+org.apache.lucene:lucene-core:9.4.1
+org.apache.lucene:lucene-core:9.4.2
+org.apache.lucene:lucene-highlighter:9.4.2
+org.apache.lucene:lucene-queries:9.4.1
+org.apache.lucene:lucene-queries:9.4.2
+org.apache.lucene:lucene-queryparser:9.4.2
+org.apache.lucene:lucene-sandbox:9.4.2
org.apache.pdfbox:fontbox:3.0.0-RC1
org.apache.pdfbox:pdfbox:3.0.0-RC1
org.apache.pdfbox:xmpbox:3.0.0-RC1
-org.apache.tika:tika-core:2.4.1
-org.bouncycastle:bcprov-jdk18on:1.71
+org.apache.tika:tika-core:2.6.0
+org.bouncycastle:bcprov-jdk18on:1.71.1
org.checkerframework:checker-qual:3.12.0
org.codehaus.mojo:animal-sniffer-annotations:1.18
-org.controlsfx:controlsfx:11.1.1
-org.eclipse.jgit:org.eclipse.jgit:6.2.0.202206071550-r
-org.fxmisc.flowless:flowless:0.6.10
-org.fxmisc.richtext:richtextfx:0.10.9
+org.controlsfx:controlsfx:11.1.2
+org.eclipse.jgit:org.eclipse.jgit:6.3.0.202209071007-r
+org.fxmisc.flowless:flowless:0.7.0
+org.fxmisc.richtext:richtextfx:0.11.0
org.fxmisc.undo:undofx:2.1.1
org.fxmisc.wellbehaved:wellbehavedfx:0.3.3
-org.glassfish.hk2.external:jakarta.inject:2.6.1
org.glassfish.jaxb:jaxb-core:3.0.2
org.glassfish.jaxb:jaxb-runtime:3.0.2
org.glassfish.jaxb:txw2:3.0.2
org.jbibtex:jbibtex:1.0.19
org.jetbrains:annotations:15.0
-org.jsoup:jsoup:1.15.1
+org.jsoup:jsoup:1.15.3
org.kordamp.ikonli:ikonli-core:12.3.1
org.kordamp.ikonli:ikonli-javafx:12.3.1
org.kordamp.ikonli:ikonli-materialdesign2-pack:12.3.1
-org.libreoffice:libreoffice:7.3.5
-org.libreoffice:unoloader:7.3.5
-org.mariadb.jdbc:mariadb-java-client:2.7.6
-org.openjfx:javafx-base:18.0.1
-org.openjfx:javafx-controls:18.0.1
-org.openjfx:javafx-fxml:18.0.1
-org.openjfx:javafx-graphics:18.0.1
-org.openjfx:javafx-media:18.0.1
-org.openjfx:javafx-swing:18.0.1
-org.openjfx:javafx-web:18.0.1
-org.postgresql:postgresql:42.4.0
+org.libreoffice:libreoffice:7.4.1
+org.libreoffice:unoloader:7.4.1
+org.mariadb.jdbc:mariadb-java-client:2.7.7
+org.openjfx:javafx-base:19
+org.openjfx:javafx-controls:19
+org.openjfx:javafx-fxml:19
+org.openjfx:javafx-graphics:19
+org.openjfx:javafx-media:19
+org.openjfx:javafx-swing:19
+org.openjfx:javafx-web:19
+org.postgresql:postgresql:42.5.1
org.reactfx:reactfx:2.0-M5
org.scala-lang:scala-library:2.13.8
-org.slf4j:slf4j-api:2.0.0-alpha7
-org.tinylog:slf4j-tinylog:2.4.1
-org.tinylog:tinylog-api:2.4.1
-org.tinylog:tinylog-impl:2.4.1
-org.yaml:snakeyaml:1.30
+org.slf4j:slf4j-api:2.0.5
+org.tinylog:slf4j-tinylog:2.5.0
+org.tinylog:tinylog-api:2.5.0
+org.tinylog:tinylog-impl:2.5.0
+org.yaml:snakeyaml:1.33
```
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
index 249e5832f09..943f0cbfa75 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 b916c04dbb2..adb6acbd81b 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionSha256Sum=f6b8596b10cce501591e92f229816aa4046424f3b24d771751b06779d58c8ec4
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip
+distributionSha256Sum=7ba68c54029790ab444b39d7e293d3236b2632631fb5f2e012bb28b4ff669e4b
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip
+networkTimeout=10000
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/gradlew b/gradlew
index a69d9cb6c20..65dcd68d65c 100755
--- a/gradlew
+++ b/gradlew
@@ -55,7 +55,7 @@
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
-# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
@@ -80,10 +80,10 @@ do
esac
done
-APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
-
-APP_NAME="Gradle"
+# This is normally unused
+# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
+APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
@@ -143,12 +143,16 @@ fi
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
+ # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
+ # shellcheck disable=SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
+ # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
+ # shellcheck disable=SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
diff --git a/gradlew.bat b/gradlew.bat
index f127cfd49d4..93e3f59f135 100644
--- a/gradlew.bat
+++ b/gradlew.bat
@@ -26,6 +26,7 @@ if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=.
+@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
diff --git a/mlc_config.json b/mlc_config.json
index cc21f202877..e4ae50df899 100644
--- a/mlc_config.json
+++ b/mlc_config.json
@@ -31,7 +31,7 @@
"pattern": "tldrlegal.com"
},
{
- "pattern": "^https://web\.archive\.org"
+ "pattern": "^https://web\\.archive\\.org"
},
{
"pattern": "^https://www\\.slant\\.co/"
diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml
index 2d32b9f9627..43513b09a47 100644
--- a/snap/snapcraft.yaml
+++ b/snap/snapcraft.yaml
@@ -57,7 +57,7 @@ environment:
parts:
jabref:
plugin: dump
- source: https://builds.jabref.org/main/JabRef-5.8-portable_linux.tar.gz
+ source: https://builds.jabref.org/main/JabRef-6.0-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 fbc9f463f2d..685d42ce735 100644
--- a/src/main/java/module-info.java
+++ b/src/main/java/module-info.java
@@ -114,6 +114,7 @@
requires com.fasterxml.jackson.dataformat.yaml;
requires com.fasterxml.jackson.datatype.jsr310;
requires net.harawata.appdirs;
+ requires com.sun.jna;
requires com.sun.jna.platform;
requires org.eclipse.jgit;
diff --git a/src/main/java/org/jabref/cli/ArgumentProcessor.java b/src/main/java/org/jabref/cli/ArgumentProcessor.java
index fad33ed0d0d..adaa4aa1b3d 100644
--- a/src/main/java/org/jabref/cli/ArgumentProcessor.java
+++ b/src/main/java/org/jabref/cli/ArgumentProcessor.java
@@ -10,7 +10,6 @@
import java.util.Locale;
import java.util.Optional;
import java.util.Set;
-import java.util.Vector;
import java.util.prefs.BackingStoreException;
import org.jabref.gui.Globals;
@@ -288,8 +287,8 @@ private void writeMetadatatoPdf(List loaded, String filesAndCiteke
return;
}
- Vector citeKeys = new Vector<>();
- Vector pdfs = new Vector<>();
+ List citeKeys = new ArrayList<>();
+ List pdfs = new ArrayList<>();
for (String fileOrCiteKey : filesAndCitekeys.split(",")) {
if (fileOrCiteKey.toLowerCase(Locale.ROOT).endsWith(".pdf")) {
pdfs.add(fileOrCiteKey);
@@ -323,7 +322,7 @@ private void writeMetadatatoPDFsOfEntry(BibDatabaseContext databaseContext, Stri
}
}
- private void writeMetadatatoPdfByCitekey(BibDatabaseContext databaseContext, BibDatabase dataBase, Vector citeKeys, FilePreferences filePreferences, XmpPdfExporter xmpPdfExporter, EmbeddedBibFilePdfExporter embeddedBibFilePdfExporter, boolean writeXMP, boolean embeddBibfile) {
+ private void writeMetadatatoPdfByCitekey(BibDatabaseContext databaseContext, BibDatabase dataBase, List citeKeys, FilePreferences filePreferences, XmpPdfExporter xmpPdfExporter, EmbeddedBibFilePdfExporter embeddedBibFilePdfExporter, boolean writeXMP, boolean embeddBibfile) {
for (String citeKey : citeKeys) {
List bibEntryList = dataBase.getEntriesByCitationKey(citeKey);
if (bibEntryList.isEmpty()) {
@@ -336,8 +335,8 @@ private void writeMetadatatoPdfByCitekey(BibDatabaseContext databaseContext, Bib
}
}
- private void writeMetadatatoPdfByFileNames(BibDatabaseContext databaseContext, BibDatabase dataBase, Vector fileNames, FilePreferences filePreferences, XmpPdfExporter xmpPdfExporter, EmbeddedBibFilePdfExporter embeddedBibFilePdfExporter, boolean writeXMP, boolean embeddBibfile) {
- for (String fileName : fileNames) {
+ private void writeMetadatatoPdfByFileNames(BibDatabaseContext databaseContext, BibDatabase dataBase, List pdfs, FilePreferences filePreferences, XmpPdfExporter xmpPdfExporter, EmbeddedBibFilePdfExporter embeddedBibFilePdfExporter, boolean writeXMP, boolean embeddBibfile) {
+ for (String fileName : pdfs) {
Path filePath = Path.of(fileName);
if (!filePath.isAbsolute()) {
filePath = FileHelper.find(fileName, databaseContext.getFileDirectories(filePreferences)).orElse(FileHelper.find(fileName, List.of(Path.of("").toAbsolutePath())).orElse(filePath));
diff --git a/src/main/java/org/jabref/cli/CrossrefFetcherEvaluator.java b/src/main/java/org/jabref/cli/CrossrefFetcherEvaluator.java
deleted file mode 100644
index f4fda96c23e..00000000000
--- a/src/main/java/org/jabref/cli/CrossrefFetcherEvaluator.java
+++ /dev/null
@@ -1,100 +0,0 @@
-package org.jabref.cli;
-
-import java.io.FileReader;
-import java.io.IOException;
-import java.util.List;
-import java.util.Optional;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.atomic.AtomicInteger;
-
-import org.jabref.gui.Globals;
-import org.jabref.logic.importer.FetcherException;
-import org.jabref.logic.importer.ParserResult;
-import org.jabref.logic.importer.fetcher.CrossRef;
-import org.jabref.logic.importer.fileformat.BibtexParser;
-import org.jabref.model.database.BibDatabase;
-import org.jabref.model.entry.BibEntry;
-import org.jabref.model.entry.field.StandardField;
-import org.jabref.model.entry.identifier.DOI;
-import org.jabref.preferences.JabRefPreferences;
-
-/**
- * Useful for checking out new algorithm improvements and thresholds. Not used inside the JabRef code itself.
- */
-public class CrossrefFetcherEvaluator {
-
- private CrossrefFetcherEvaluator() {
- }
-
- public static void main(String[] args) throws IOException, InterruptedException {
- Globals.prefs = JabRefPreferences.getInstance();
- try (FileReader reader = new FileReader(args[0])) {
- BibtexParser parser = new BibtexParser(Globals.prefs.getImportFormatPreferences(), Globals.getFileUpdateMonitor());
- ParserResult result = parser.parse(reader);
- BibDatabase db = result.getDatabase();
-
- List entries = db.getEntries();
-
- AtomicInteger dois = new AtomicInteger();
- AtomicInteger doiFound = new AtomicInteger();
- AtomicInteger doiNew = new AtomicInteger();
- AtomicInteger doiIdentical = new AtomicInteger();
-
- int total = entries.size();
-
- CountDownLatch countDownLatch = new CountDownLatch(total);
-
- ExecutorService executorService = Executors.newFixedThreadPool(5);
-
- for (BibEntry entry : entries) {
- executorService.execute(new Runnable() {
- @Override
- public void run() {
- Optional origDOI = entry.getField(StandardField.DOI).flatMap(DOI::parse);
- if (origDOI.isPresent()) {
- dois.incrementAndGet();
- try {
- Optional crossrefDOI = new CrossRef().findIdentifier(entry);
- if (crossrefDOI.isPresent()) {
- doiFound.incrementAndGet();
- if (origDOI.get().getDOI().equalsIgnoreCase(crossrefDOI.get().getDOI())) {
- doiIdentical.incrementAndGet();
- } else {
- System.out.println("DOI not identical for : " + entry);
- }
- } else {
- System.out.println("DOI not found for: " + entry);
- }
- } catch (FetcherException e) {
- e.printStackTrace();
- }
- } else {
- try {
- Optional crossrefDOI = new CrossRef().findIdentifier(entry);
- if (crossrefDOI.isPresent()) {
- System.out.println("New DOI found for: " + entry);
- doiNew.incrementAndGet();
- }
- } catch (FetcherException e) {
- e.printStackTrace();
- }
- }
- countDownLatch.countDown();
- }
- });
- }
- countDownLatch.await();
-
- System.out.println("---------------------------------");
- System.out.println("Total DB size: " + total);
- System.out.println("Total DOIs: " + dois);
- System.out.println("DOIs found: " + doiFound);
- System.out.println("DOIs identical: " + doiIdentical);
- System.out.println("New DOIs found: " + doiNew);
-
- executorService.shutdown();
- }
- }
-}
diff --git a/src/main/java/org/jabref/cli/GenerateCharacterTable.java b/src/main/java/org/jabref/cli/GenerateCharacterTable.java
deleted file mode 100644
index 57a1690bb10..00000000000
--- a/src/main/java/org/jabref/cli/GenerateCharacterTable.java
+++ /dev/null
@@ -1,50 +0,0 @@
-package org.jabref.cli;
-
-import java.util.Map;
-import java.util.TreeMap;
-
-import org.jabref.logic.util.strings.HTMLUnicodeConversionMaps;
-
-public class GenerateCharacterTable {
-
- private GenerateCharacterTable() {
- }
-
- public static void main(String[] args) {
- Map characterMap = new TreeMap<>(HTMLUnicodeConversionMaps.NUMERICAL_LATEX_CONVERSION_MAP);
- System.out.println("\\documentclass[10pt, a4paper]{article}");
- System.out.println("\\usepackage[T5,T1]{fontenc}");
- System.out.println("\\usepackage{amssymb}");
- System.out.println("\\usepackage{amsmath}");
- System.out.println("\\usepackage{txfonts}");
- System.out.println("\\usepackage{xfrac}");
- System.out.println("\\usepackage{combelow}");
- System.out.println("\\usepackage{textcomp}");
- System.out.println("\\usepackage{mathspec}");
- System.out.println("\\usepackage{fontspec}");
- System.out.println("\\usepackage[a4paper,margin=1cm]{geometry}");
- System.out.println("\\usepackage{supertabular}");
- System.out.println("\\usepackage{mathabx}");
- System.out.println("\\fontspec{Cambria}");
- System.out.println("\\DeclareTextSymbolDefault{\\OHORN}{T5}");
- System.out.println("\\DeclareTextSymbolDefault{\\UHORN}{T5}");
- System.out.println("\\DeclareTextSymbolDefault{\\ohorn}{T5}");
- System.out.println("\\DeclareTextSymbolDefault{\\uhorn}{T5}");
- System.out.println("\\begin{document}");
- System.out.println("\\twocolumn");
- System.out.println("\\begin{supertabular}{c|c|c|c|c}");
- System.out.println("No. & Uni & Symb & \\LaTeX & Code \\\\ \n \\hline");
-
- for (Map.Entry character : characterMap.entrySet()) {
- System.out
- .println(
- character.getKey() + " & "
- + ((character.getKey() > 128) ?
- String.valueOf(Character.toChars(character.getKey())) : "")
- + " & \\symbol{" + Integer.toString(character.getKey()) + "} & "
- + character.getValue() + " & \\verb¤" + character.getValue() + "¤ \\\\");
- }
- System.out.println("\\end{supertabular}");
- System.out.println("\\end{document}");
- }
-}
diff --git a/src/main/java/org/jabref/cli/Launcher.java b/src/main/java/org/jabref/cli/Launcher.java
index e0a99e0a710..fb49340b5a7 100644
--- a/src/main/java/org/jabref/cli/Launcher.java
+++ b/src/main/java/org/jabref/cli/Launcher.java
@@ -11,7 +11,6 @@
import org.jabref.gui.Globals;
import org.jabref.gui.MainApplication;
-import org.jabref.gui.remote.JabRefMessageHandler;
import org.jabref.logic.exporter.ExporterFactory;
import org.jabref.logic.journals.JournalAbbreviationLoader;
import org.jabref.logic.l10n.Localization;
@@ -54,7 +53,7 @@ public static void main(String[] args) {
// Init preferences
final JabRefPreferences preferences = JabRefPreferences.getInstance();
Globals.prefs = preferences;
- PreferencesMigrations.runMigrations();
+ PreferencesMigrations.runMigrations(preferences);
// Early exit in case another instance is already running
if (!handleMultipleAppInstances(args, preferences)) {
@@ -134,10 +133,6 @@ private static boolean handleMultipleAppInstances(String[] args, PreferencesServ
} else {
LOGGER.warn("Could not communicate with other running JabRef instance.");
}
- } else {
- // We are alone, so we start the server
- Globals.REMOTE_LISTENER.openAndStart(new JabRefMessageHandler(), remotePreferences.getPort(),
- preferences);
}
}
return true;
diff --git a/src/main/java/org/jabref/gui/Base.css b/src/main/java/org/jabref/gui/Base.css
index 9ecf96eea3a..e2d0aff7afb 100644
--- a/src/main/java/org/jabref/gui/Base.css
+++ b/src/main/java/org/jabref/gui/Base.css
@@ -1,5 +1,4 @@
-
.root {
-jr-row-odd-background: -fx-control-inner-background-alt;
-jr-row-even-background: -fx-control-inner-background;
@@ -256,7 +255,7 @@
}
.unchanged {
- -rtfx-background-color:#0000;
+ -rtfx-background-color: #0000;
}
.updated {
@@ -280,18 +279,32 @@
* The base css file defining the style that is valid for every pane and dialog.
*/
+TextFlow > * {
+ -fx-fill: -fx-text-background-color;
+}
+
+TextFlow > .hyperlink,
.hyperlink {
-fx-padding: 0;
-fx-underline: false;
-fx-border-style: null;
-fx-border-color: null;
-fx-text-fill: -jr-theme;
+ -fx-fill: -jr-theme;
}
+TextFlow > .hyperlink:visited,
.hyperlink:visited {
-fx-text-fill: -jr-accent;
+ -fx-fill: -jr-accent;
+}
+
+.TextFlow > .hyperlink:hover,
+.hyperlink:hover {
+ -fx-underline: true;
}
+
.glyph-icon {
/* This adjusts text alignment within the bounds of text nodes so that
the text is always vertically centered within the bounds. Based on
@@ -329,20 +342,28 @@ TextFlow > .tooltip-text-monospaced {
-fx-font-family: monospace;
}
+
+.radio-button > .radio,
+.check-box > .box {
+ -fx-background-color: transparent;
+ -fx-background-insets: 0;
+ -fx-background-radius: 0;
+ -fx-text-fill: -fx-text-base-color;
+}
+
.button,
.toggle-button,
-.radio-button > .radio,
-.check-box > .box,
.menu-button,
.choice-box,
.combo-box-base,
.combo-box-base:editable > .arrow-button {
-fx-background-color: transparent;
-fx-background-insets: 0;
- -fx-background-radius: 0;
+ -fx-background-radius: 4px;
-fx-text-fill: -fx-text-base-color;
}
+
.button {
-fx-background-color: transparent;
-fx-border-color: -fx-outer-border; /* rgba(0, 0, 0, 0.23); */
@@ -1323,10 +1344,6 @@ We want to have a look that matches our icons in the tool-bar */
-fx-label-padding: 5 0 10 10;
}
-TextFlow * {
- -fx-fill: -fx-text-background-color;
-}
-
.chips-pane > .editor {
-fx-pref-height: 30px;
-fx-padding: 0px 0px 0px -8px;
diff --git a/src/main/java/org/jabref/gui/Dark.css b/src/main/java/org/jabref/gui/Dark.css
index 4a9f76d6c1b..f53d25734e5 100644
--- a/src/main/java/org/jabref/gui/Dark.css
+++ b/src/main/java/org/jabref/gui/Dark.css
@@ -170,3 +170,8 @@
.rating > .container > .button.strong {
-fx-icon-color: -fx-light-text-color;
}
+
+
+.file-row-text {
+ -fx-text-fill: -fx-light-text-color;
+}
diff --git a/src/main/java/org/jabref/gui/Globals.java b/src/main/java/org/jabref/gui/Globals.java
index 1696a1dfba9..0e575e8a6bd 100644
--- a/src/main/java/org/jabref/gui/Globals.java
+++ b/src/main/java/org/jabref/gui/Globals.java
@@ -7,6 +7,7 @@
import org.jabref.architecture.AllowedToUseAwt;
import org.jabref.gui.keyboard.KeyBindingRepository;
+import org.jabref.gui.remote.CLIMessageHandler;
import org.jabref.gui.theme.ThemeManager;
import org.jabref.gui.undo.CountingUndoManager;
import org.jabref.gui.util.DefaultFileUpdateMonitor;
@@ -16,7 +17,8 @@
import org.jabref.logic.importer.ImportFormatReader;
import org.jabref.logic.journals.JournalAbbreviationRepository;
import org.jabref.logic.protectedterms.ProtectedTermsLoader;
-import org.jabref.logic.remote.server.RemoteListenerServerLifecycle;
+import org.jabref.logic.remote.RemotePreferences;
+import org.jabref.logic.remote.server.RemoteListenerServerManager;
import org.jabref.logic.util.BuildInfo;
import org.jabref.model.entry.BibEntryTypesManager;
import org.jabref.model.strings.StringUtil;
@@ -41,9 +43,7 @@ public class Globals {
*/
public static final BuildInfo BUILD_INFO = new BuildInfo();
- // Remote listener
- public static final RemoteListenerServerLifecycle REMOTE_LISTENER = new RemoteListenerServerLifecycle();
-
+ public static final RemoteListenerServerManager REMOTE_LISTENER = new RemoteListenerServerManager();
/**
* Manager for the state of the GUI.
*/
@@ -118,6 +118,10 @@ public static void startBackgroundTasks() {
/* if (Globals.prefs.getTelemetryPreferences().shouldCollectTelemetry() && !GraphicsEnvironment.isHeadless()) {
startTelemetryClient();
} */
+ RemotePreferences remotePreferences = prefs.getRemotePreferences();
+ if (remotePreferences.useRemoteServer()) {
+ Globals.REMOTE_LISTENER.openAndStart(new CLIMessageHandler(prefs), remotePreferences.getPort());
+ }
}
private static void stopTelemetryClient() {
diff --git a/src/main/java/org/jabref/gui/JabRefExecutorService.java b/src/main/java/org/jabref/gui/JabRefExecutorService.java
index 99c6fc1ea32..7f68d4b6305 100644
--- a/src/main/java/org/jabref/gui/JabRefExecutorService.java
+++ b/src/main/java/org/jabref/gui/JabRefExecutorService.java
@@ -117,9 +117,9 @@ public void executeInterruptableTaskAndWait(Runnable runnable) {
}
}
- public void manageRemoteThread(Thread thread) {
+ public void startRemoteThread(Thread thread) {
if (this.remoteThread != null) {
- throw new IllegalStateException("Remote thread is already attached");
+ throw new IllegalStateException("Tele thread is already attached");
} else {
this.remoteThread = thread;
remoteThread.start();
@@ -141,7 +141,6 @@ public void submit(TimerTask timerTask, long millisecondsDelay) {
* Shuts everything down. After termination, this method returns.
*/
public void shutdownEverything() {
- // kill the remote thread
stopRemoteThread();
gracefullyShutdown(this.executorService);
diff --git a/src/main/java/org/jabref/gui/JabRefFrame.java b/src/main/java/org/jabref/gui/JabRefFrame.java
index 9da1e8fd77f..19da0ef09be 100644
--- a/src/main/java/org/jabref/gui/JabRefFrame.java
+++ b/src/main/java/org/jabref/gui/JabRefFrame.java
@@ -5,7 +5,6 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
-import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.TimerTask;
@@ -659,6 +658,15 @@ public void init() {
Platform.runLater(() -> stateManager.focusOwnerProperty().bind(
EasyBind.map(mainStage.getScene().focusOwnerProperty(), Optional::ofNullable)));
+ EasyBind.subscribe(tabbedPane.getSelectionModel().selectedItemProperty(), selectedTab -> {
+ if (selectedTab instanceof LibraryTab libraryTab) {
+ stateManager.setActiveDatabase(libraryTab.getBibDatabaseContext());
+ } else if (selectedTab == null) {
+ // All databases are closed
+ stateManager.setActiveDatabase(null);
+ }
+ });
+
/*
* The following state listener makes sure focus is registered with the
* correct database when the user switches tabs. Without this,
@@ -821,7 +829,7 @@ private MenuBar createMenu() {
quality.getItems().addAll(
factory.createMenuItem(StandardActions.FIND_DUPLICATES, new DuplicateSearch(this, dialogService, stateManager, prefs)),
- factory.createMenuItem(StandardActions.MERGE_ENTRIES, new MergeEntriesAction(dialogService, stateManager)),
+ factory.createMenuItem(StandardActions.MERGE_ENTRIES, new MergeEntriesAction(dialogService, stateManager, prefs.getBibEntryPreferences())),
factory.createMenuItem(StandardActions.CHECK_INTEGRITY, new IntegrityCheckAction(this, stateManager, Globals.TASK_EXECUTOR)),
factory.createMenuItem(StandardActions.CLEANUP_ENTRIES, new CleanupAction(this, this.prefs, dialogService, stateManager)),
@@ -832,11 +840,11 @@ private MenuBar createMenu() {
new SeparatorMenuItem(),
factory.createSubMenu(StandardActions.ABBREVIATE,
- factory.createMenuItem(StandardActions.ABBREVIATE_DEFAULT, new AbbreviateAction(StandardActions.ABBREVIATE_DEFAULT, this, dialogService, stateManager)),
- factory.createMenuItem(StandardActions.ABBREVIATE_MEDLINE, new AbbreviateAction(StandardActions.ABBREVIATE_MEDLINE, this, dialogService, stateManager)),
- factory.createMenuItem(StandardActions.ABBREVIATE_SHORTEST_UNIQUE, new AbbreviateAction(StandardActions.ABBREVIATE_SHORTEST_UNIQUE, this, dialogService, stateManager))),
+ factory.createMenuItem(StandardActions.ABBREVIATE_DEFAULT, new AbbreviateAction(StandardActions.ABBREVIATE_DEFAULT, this, dialogService, stateManager, prefs.getJournalAbbreviationPreferences())),
+ factory.createMenuItem(StandardActions.ABBREVIATE_MEDLINE, new AbbreviateAction(StandardActions.ABBREVIATE_MEDLINE, this, dialogService, stateManager, prefs.getJournalAbbreviationPreferences())),
+ factory.createMenuItem(StandardActions.ABBREVIATE_SHORTEST_UNIQUE, new AbbreviateAction(StandardActions.ABBREVIATE_SHORTEST_UNIQUE, this, dialogService, stateManager, prefs.getJournalAbbreviationPreferences()))),
- factory.createMenuItem(StandardActions.UNABBREVIATE, new AbbreviateAction(StandardActions.UNABBREVIATE, this, dialogService, stateManager))
+ factory.createMenuItem(StandardActions.UNABBREVIATE, new AbbreviateAction(StandardActions.UNABBREVIATE, this, dialogService, stateManager, prefs.getJournalAbbreviationPreferences()))
);
Menu lookupIdentifiers = factory.createSubMenu(StandardActions.LOOKUP_DOC_IDENTIFIER);
@@ -1106,8 +1114,8 @@ public void addTab(LibraryTab libraryTab, boolean raisePanel) {
tabbedPane.getTabs().add(libraryTab);
libraryTab.setOnCloseRequest(event -> {
+ libraryTab.cancelLoading();
closeTab(libraryTab);
- libraryTab.getDataLoadingTask().cancel();
event.consume();
});
@@ -1120,17 +1128,14 @@ public void addTab(LibraryTab libraryTab, boolean raisePanel) {
libraryTab.getUndoManager().registerListener(new UndoRedoEventManager());
}
- private void trackOpenNewDatabase(LibraryTab libraryTab) {
- Globals.getTelemetryClient().ifPresent(client -> client.trackEvent(
- "OpenNewDatabase",
- Map.of(),
- Map.of("NumberOfEntries", (double) libraryTab.getBibDatabaseContext().getDatabase().getEntryCount())));
- }
-
+ /**
+ * Opens a new tab with existing data.
+ * Asynchronous loading is done at {@link #createLibraryTab(BackgroundTask, Path, PreferencesService, StateManager, JabRefFrame, ThemeManager)}.
+ */
public LibraryTab addTab(BibDatabaseContext databaseContext, boolean raisePanel) {
Objects.requireNonNull(databaseContext);
- LibraryTab libraryTab = new LibraryTab(this, prefs, stateManager, themeManager, databaseContext, importFormatReader);
+ LibraryTab libraryTab = new LibraryTab(databaseContext, this, prefs, stateManager, themeManager);
addTab(libraryTab, raisePanel);
return libraryTab;
}
@@ -1155,9 +1160,10 @@ public FileHistoryMenu getFileHistory() {
}
/**
- * Ask if the user really wants to close the given database
+ * Ask if the user really wants to close the given database.
+ * Offers to save or discard the changes -- or return to the library
*
- * @return true if the user choose to close the database
+ * @return true
if the user choose to close the database
*/
private boolean confirmClose(LibraryTab libraryTab) {
String filename = libraryTab.getBibDatabaseContext()
@@ -1168,15 +1174,24 @@ private boolean confirmClose(LibraryTab libraryTab) {
ButtonType saveChanges = new ButtonType(Localization.lang("Save changes"), ButtonBar.ButtonData.YES);
ButtonType discardChanges = new ButtonType(Localization.lang("Discard changes"), ButtonBar.ButtonData.NO);
- ButtonType cancel = new ButtonType(Localization.lang("Return to library"), ButtonBar.ButtonData.CANCEL_CLOSE);
+ ButtonType returnToLibrary = new ButtonType(Localization.lang("Return to library"), ButtonBar.ButtonData.CANCEL_CLOSE);
Optional response = dialogService.showCustomButtonDialogAndWait(Alert.AlertType.CONFIRMATION,
Localization.lang("Save before closing"),
Localization.lang("Library '%0' has changed.", filename),
- saveChanges, discardChanges, cancel);
+ saveChanges, discardChanges, returnToLibrary);
- if (response.isPresent() && response.get().equals(saveChanges)) {
- // The user wants to save.
+ if (response.isEmpty()) {
+ return true;
+ }
+
+ ButtonType buttonType = response.get();
+
+ if (buttonType.equals(returnToLibrary)) {
+ return false;
+ }
+
+ if (buttonType.equals(saveChanges)) {
try {
SaveDatabaseAction saveAction = new SaveDatabaseAction(libraryTab, prefs, Globals.entryTypesManager);
if (saveAction.save()) {
@@ -1191,7 +1206,13 @@ private boolean confirmClose(LibraryTab libraryTab) {
// Save was cancelled or an error occurred.
return false;
}
- return response.isEmpty() || !response.get().equals(cancel);
+
+ if (buttonType.equals(discardChanges)) {
+ BackupManager.discardBackup(libraryTab.getBibDatabaseContext());
+ return true;
+ }
+
+ return false;
}
/**
diff --git a/src/main/java/org/jabref/gui/JabRefGUI.java b/src/main/java/org/jabref/gui/JabRefGUI.java
index 29883a51ebb..a7d693400ac 100644
--- a/src/main/java/org/jabref/gui/JabRefGUI.java
+++ b/src/main/java/org/jabref/gui/JabRefGUI.java
@@ -157,7 +157,7 @@ private void openDatabases() {
if (pr.getDatabase().isShared()) {
try {
- new SharedDatabaseUIManager(mainFrame).openSharedDatabaseFromParserResult(pr);
+ new SharedDatabaseUIManager(mainFrame, preferencesService).openSharedDatabaseFromParserResult(pr);
} catch (SQLException | DatabaseNotSupportedException | InvalidDBMSConnectionPropertiesException |
NotASharedDatabaseException e) {
pr.getDatabaseContext().clearDatabasePath(); // do not open the original file
diff --git a/src/main/java/org/jabref/gui/LibraryTab.java b/src/main/java/org/jabref/gui/LibraryTab.java
index 607bcb474f7..c7fee3a26a0 100644
--- a/src/main/java/org/jabref/gui/LibraryTab.java
+++ b/src/main/java/org/jabref/gui/LibraryTab.java
@@ -43,7 +43,6 @@
import org.jabref.logic.autosaveandbackup.AutosaveManager;
import org.jabref.logic.autosaveandbackup.BackupManager;
import org.jabref.logic.citationstyle.CitationStyleCache;
-import org.jabref.logic.importer.ImportFormatReader;
import org.jabref.logic.importer.ParserResult;
import org.jabref.logic.importer.util.FileFieldParser;
import org.jabref.logic.l10n.Localization;
@@ -115,18 +114,15 @@ public class LibraryTab extends Tab {
private Optional changeMonitor = Optional.empty();
- // initializing it so we prevent NullPointerException
- private BackgroundTask dataLoadingTask = BackgroundTask.wrap(() -> null);
+ private BackgroundTask dataLoadingTask;
private final IndexingTaskManager indexingTaskManager = new IndexingTaskManager(Globals.TASK_EXECUTOR);
- private final ImportFormatReader importFormatReader;
- public LibraryTab(JabRefFrame frame,
+ public LibraryTab(BibDatabaseContext bibDatabaseContext,
+ JabRefFrame frame,
PreferencesService preferencesService,
StateManager stateManager,
- ThemeManager themeManager,
- BibDatabaseContext bibDatabaseContext,
- ImportFormatReader importFormatReader) {
+ ThemeManager themeManager) {
this.frame = Objects.requireNonNull(frame);
this.bibDatabaseContext = Objects.requireNonNull(bibDatabaseContext);
this.undoManager = frame.getUndoManager();
@@ -134,7 +130,6 @@ public LibraryTab(JabRefFrame frame,
this.preferencesService = Objects.requireNonNull(preferencesService);
this.stateManager = Objects.requireNonNull(stateManager);
this.themeManager = Objects.requireNonNull(themeManager);
- this.importFormatReader = importFormatReader;
bibDatabaseContext.getDatabase().registerListener(this);
bibDatabaseContext.getMetaData().registerListener(this);
@@ -189,15 +184,19 @@ private static void addSharedDbInformation(StringBuilder text, BibDatabaseContex
text.append("]");
}
- public BackgroundTask> getDataLoadingTask() {
- return dataLoadingTask;
- }
-
public void setDataLoadingTask(BackgroundTask dataLoadingTask) {
this.dataLoadingTask = dataLoadingTask;
}
- /* The layout to display in the tab when it's loading*/
+ public void cancelLoading() {
+ if (dataLoadingTask != null) {
+ dataLoadingTask.cancel();
+ }
+ }
+
+ /**
+ * The layout to display in the tab when it's loading
+ */
public Node createLoadingAnimationLayout() {
ProgressIndicator progressIndicator = new ProgressIndicator(ProgressIndicator.INDETERMINATE_PROGRESS);
BorderPane pane = new BorderPane();
@@ -209,7 +208,6 @@ public Node createLoadingAnimationLayout() {
public void onDatabaseLoadingStarted() {
Node loadingLayout = createLoadingAnimationLayout();
getMainTable().placeholderProperty().setValue(loadingLayout);
-
frame.addTab(this, true);
}
@@ -226,14 +224,6 @@ public void onDatabaseLoadingSucceed(ParserResult result) {
LOGGER.error("Cannot access lucene index", e);
}
}
-
- // a temporary workaround to update groups pane
- stateManager.activeDatabaseProperty().bind(
- EasyBind.map(frame.getTabbedPane().getSelectionModel().selectedItemProperty(),
- selectedTab -> Optional.ofNullable(selectedTab)
- .filter(tab -> tab instanceof LibraryTab)
- .map(tab -> (LibraryTab) tab)
- .map(LibraryTab::getBibDatabaseContext)));
}
public void onDatabaseLoadingFailed(Exception ex) {
@@ -243,17 +233,32 @@ public void onDatabaseLoadingFailed(Exception ex) {
dialogService.showErrorDialogAndWait(title, content, ex);
}
- public void feedData(BibDatabaseContext bibDatabaseContext) {
+ public void feedData(BibDatabaseContext bibDatabaseContextFromParserResult) {
cleanUp();
- this.bibDatabaseContext = Objects.requireNonNull(bibDatabaseContext);
+ if (this.getTabPane().getSelectionModel().selectedItemProperty().get().equals(this)) {
+ // If you open an existing library, a library tab with a loading animation is added immediately.
+ // At that point, the library tab is given a temporary bibDatabaseContext with no entries.
+ // This line is necessary because, while there is already a binding that updates the active database when a new tab is added,
+ // it doesn't handle the case when a library is loaded asynchronously.
+ // See org.jabref.gui.LibraryTab.createLibraryTab for the asynchronous loading.
+ stateManager.setActiveDatabase(bibDatabaseContextFromParserResult);
+ }
- bibDatabaseContext.getDatabase().registerListener(this);
- bibDatabaseContext.getMetaData().registerListener(this);
+ // Remove existing dummy BibDatabaseContext and add correct BibDatabaseContext from ParserResult to trigger changes in the openDatabases list in the stateManager
+ Optional foundExistingBibDatabase = stateManager.getOpenDatabases().stream().filter(databaseContext -> databaseContext.equals(this.bibDatabaseContext)).findFirst();
+ foundExistingBibDatabase.ifPresent(databaseContext -> stateManager.getOpenDatabases().remove(databaseContext));
+
+ this.bibDatabaseContext = Objects.requireNonNull(bibDatabaseContextFromParserResult);
+
+ stateManager.getOpenDatabases().add(bibDatabaseContextFromParserResult);
+
+ bibDatabaseContextFromParserResult.getDatabase().registerListener(this);
+ bibDatabaseContextFromParserResult.getMetaData().registerListener(this);
this.tableModel = new MainTableDataModel(getBibDatabaseContext(), preferencesService, stateManager);
- citationStyleCache = new CitationStyleCache(bibDatabaseContext);
- annotationCache = new FileAnnotationCache(bibDatabaseContext, preferencesService.getFilePreferences());
+ citationStyleCache = new CitationStyleCache(bibDatabaseContextFromParserResult);
+ annotationCache = new FileAnnotationCache(bibDatabaseContextFromParserResult, preferencesService.getFilePreferences());
setupMainPanel();
setupAutoCompletion();
@@ -822,21 +827,25 @@ public void resetChangedProperties() {
this.changedProperty.setValue(false);
}
- public static class Factory {
- public LibraryTab createLibraryTab(JabRefFrame frame, PreferencesService preferencesService, StateManager stateManager, ThemeManager themeManager, Path file, BackgroundTask dataLoadingTask, ImportFormatReader importFormatReader) {
- BibDatabaseContext context = new BibDatabaseContext();
- context.setDatabasePath(file);
+ /**
+ * Creates a new library tab. Contents are loaded by the {@code dataLoadingTask}. Most of the other parameters are required by {@code resetChangeMonitor()}.
+ *
+ * @param dataLoadingTask The task to execute to load the data. It is executed using {@link Globals.TASK_EXECUTOR}.
+ * @param file the path to the file (loaded by the dataLoadingTask)
+ */
+ public static LibraryTab createLibraryTab(BackgroundTask dataLoadingTask, Path file, PreferencesService preferencesService, StateManager stateManager, JabRefFrame frame, ThemeManager themeManager) {
+ BibDatabaseContext context = new BibDatabaseContext();
+ context.setDatabasePath(file);
- LibraryTab newTab = new LibraryTab(frame, preferencesService, stateManager, themeManager, context, importFormatReader);
- newTab.setDataLoadingTask(dataLoadingTask);
+ LibraryTab newTab = new LibraryTab(context, frame, preferencesService, stateManager, themeManager);
- dataLoadingTask.onRunning(newTab::onDatabaseLoadingStarted)
- .onSuccess(newTab::onDatabaseLoadingSucceed)
- .onFailure(newTab::onDatabaseLoadingFailed)
- .executeWith(Globals.TASK_EXECUTOR);
+ newTab.setDataLoadingTask(dataLoadingTask);
+ dataLoadingTask.onRunning(newTab::onDatabaseLoadingStarted)
+ .onSuccess(newTab::onDatabaseLoadingSucceed)
+ .onFailure(newTab::onDatabaseLoadingFailed)
+ .executeWith(Globals.TASK_EXECUTOR);
- return newTab;
- }
+ return newTab;
}
private class GroupTreeListener {
diff --git a/src/main/java/org/jabref/gui/StateManager.java b/src/main/java/org/jabref/gui/StateManager.java
index 9a27649eb9c..9cab5af0169 100644
--- a/src/main/java/org/jabref/gui/StateManager.java
+++ b/src/main/java/org/jabref/gui/StateManager.java
@@ -34,6 +34,8 @@
import com.tobiasdiez.easybind.EasyBind;
import com.tobiasdiez.easybind.EasyBinding;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
* This class manages the GUI-state of JabRef, including:
@@ -49,6 +51,7 @@
*/
public class StateManager {
+ private static final Logger LOGGER = LoggerFactory.getLogger(StateManager.class);
private final CustomLocalDragboard localDragboard = new CustomLocalDragboard();
private final ObservableList openDatabases = FXCollections.observableArrayList();
private final OptionalObjectProperty activeDatabase = OptionalObjectProperty.empty();
@@ -129,6 +132,15 @@ public Optional getActiveDatabase() {
return activeDatabase.get();
}
+ public void setActiveDatabase(BibDatabaseContext database) {
+ if (database == null) {
+ LOGGER.info("No open database detected");
+ activeDatabaseProperty().set(Optional.empty());
+ } else {
+ activeDatabaseProperty().set(Optional.of(database));
+ }
+ }
+
public List getEntriesInCurrentDatabase() {
return OptionalUtil.flatMap(activeDatabase.get(), BibDatabaseContext::getEntries)
.collect(Collectors.toList());
diff --git a/src/main/java/org/jabref/gui/backup/BackupResolverDialog.java b/src/main/java/org/jabref/gui/backup/BackupResolverDialog.java
new file mode 100644
index 00000000000..64f24662ecf
--- /dev/null
+++ b/src/main/java/org/jabref/gui/backup/BackupResolverDialog.java
@@ -0,0 +1,67 @@
+package org.jabref.gui.backup;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.Optional;
+
+import javafx.scene.control.ButtonBar;
+import javafx.scene.control.ButtonType;
+import javafx.scene.control.Hyperlink;
+
+import org.jabref.gui.FXDialog;
+import org.jabref.gui.Globals;
+import org.jabref.gui.desktop.JabRefDesktop;
+import org.jabref.logic.l10n.Localization;
+import org.jabref.logic.util.BackupFileType;
+import org.jabref.logic.util.io.BackupFileUtil;
+
+import org.controlsfx.control.HyperlinkLabel;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class BackupResolverDialog extends FXDialog {
+ public static final ButtonType RESTORE_FROM_BACKUP = new ButtonType(Localization.lang("Restore from backup"), ButtonBar.ButtonData.OK_DONE);
+ public static final ButtonType REVIEW_BACKUP = new ButtonType(Localization.lang("Review backup"), ButtonBar.ButtonData.LEFT);
+ public static final ButtonType IGNORE_BACKUP = new ButtonType(Localization.lang("Ignore backup"), ButtonBar.ButtonData.CANCEL_CLOSE);
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(BackupResolverDialog.class);
+
+ public BackupResolverDialog(Path originalPath) {
+ super(AlertType.CONFIRMATION, Localization.lang("Backup found"), true);
+ setHeaderText(null);
+ getDialogPane().setMinHeight(180);
+ getDialogPane().getButtonTypes().setAll(RESTORE_FROM_BACKUP, REVIEW_BACKUP, IGNORE_BACKUP);
+
+ Optional backupPathOpt = BackupFileUtil.getPathOfLatestExisingBackupFile(originalPath, BackupFileType.BACKUP);
+ String backupFilename = backupPathOpt.map(Path::getFileName).map(Path::toString).orElse(Localization.lang("File not found"));
+ String content = new StringBuilder()
+ .append(Localization.lang("A backup file for '%0' was found at [%1]",
+ originalPath.getFileName().toString(),
+ backupFilename))
+ .append("\n")
+ .append(Localization.lang("This could indicate that JabRef did not shut down cleanly last time the file was used."))
+ .append("\n\n")
+ .append(Localization.lang("Do you want to recover the library from the backup file?"))
+ .toString();
+ setContentText(content);
+
+ HyperlinkLabel contentLabel = new HyperlinkLabel(content);
+ contentLabel.setPrefWidth(360);
+ contentLabel.setOnAction((e) -> {
+ if (backupPathOpt.isPresent()) {
+ if (!(e.getSource() instanceof Hyperlink)) {
+ return;
+ }
+ String clickedLinkText = ((Hyperlink) (e.getSource())).getText();
+ if (backupFilename.equals(clickedLinkText)) {
+ try {
+ JabRefDesktop.openFolderAndSelectFile(backupPathOpt.get(), Globals.prefs, null);
+ } catch (IOException ex) {
+ LOGGER.error("Could not open backup folder", ex);
+ }
+ }
+ }
+ });
+ getDialogPane().setContent(contentLabel);
+ }
+}
diff --git a/src/main/java/org/jabref/gui/cleanup/CleanupPresetPanel.fxml b/src/main/java/org/jabref/gui/cleanup/CleanupPresetPanel.fxml
index 72183521894..2ffb576b405 100644
--- a/src/main/java/org/jabref/gui/cleanup/CleanupPresetPanel.fxml
+++ b/src/main/java/org/jabref/gui/cleanup/CleanupPresetPanel.fxml
@@ -30,9 +30,9 @@
+ text="%Convert to biblatex format (e.g., store publication date in date field)"/>
+ text="%Convert to BibTeX format (e.g., store publication date in year and month fields)"/>
scanForChanges() {
+ public List scanForChanges() {
if (database.getDatabasePath().isEmpty()) {
return Collections.emptyList();
}
try {
- List changes = new ArrayList<>();
-
// Parse the modified file
// Important: apply all post-load actions
ImportFormatPreferences importFormatPreferences = preferencesService.getImportFormatPreferences();
- GeneralPreferences generalPreferences = preferencesService.getGeneralPreferences();
ParserResult result = OpenDatabase.loadDatabase(database.getDatabasePath().get(), importFormatPreferences, new DummyFileUpdateMonitor());
BibDatabaseContext databaseOnDisk = result.getDatabaseContext();
- // Start looking at changes.
- BibDatabaseDiff differences = BibDatabaseDiff.compare(database, databaseOnDisk);
- differences.getMetaDataDifferences().ifPresent(diff -> {
- changes.add(new MetadataChange(diff, database, externalChangeResolverFactory));
- diff.getGroupDifferences().ifPresent(groupDiff -> changes.add(new GroupChange(
- groupDiff, database, externalChangeResolverFactory
- )));
- });
- differences.getPreambleDifferences().ifPresent(diff -> changes.add(new PreambleChange(diff, database, externalChangeResolverFactory)));
- differences.getBibStringDifferences().forEach(diff -> changes.add(createBibStringDiff(diff)));
- differences.getEntryDifferences().forEach(diff -> changes.add(createBibEntryDiff(diff)));
- return changes;
+ return DatabaseChangeList.compareAndGetChanges(database, databaseOnDisk, databaseChangeResolverFactory);
} catch (IOException e) {
LOGGER.warn("Error while parsing changed file.", e);
return Collections.emptyList();
}
}
-
- private ExternalChange createBibStringDiff(BibStringDiff diff) {
- if (diff.getOriginalString() == null) {
- return new BibTexStringAdd(diff.getNewString(), database, externalChangeResolverFactory);
- }
-
- if (diff.getNewString() == null) {
- return new BibTexStringDelete(diff.getOriginalString(), database, externalChangeResolverFactory);
- }
-
- if (diff.getOriginalString().getName().equals(diff.getNewString().getName())) {
- return new BibTexStringChange(diff.getOriginalString(), diff.getNewString(), database, externalChangeResolverFactory);
- }
-
- return new BibTexStringRename(diff.getOriginalString(), diff.getNewString(), database, externalChangeResolverFactory);
- }
-
- private ExternalChange createBibEntryDiff(BibEntryDiff diff) {
- if (diff.getOriginalEntry() == null) {
- return new EntryAdd(diff.getNewEntry(), database, externalChangeResolverFactory);
- }
-
- if (diff.getNewEntry() == null) {
- return new EntryDelete(diff.getOriginalEntry(), database, externalChangeResolverFactory);
- }
-
- return new EntryChange(diff.getOriginalEntry(), diff.getNewEntry(), database, externalChangeResolverFactory);
- }
}
diff --git a/src/main/java/org/jabref/gui/collab/ExternalChange.java b/src/main/java/org/jabref/gui/collab/DatabaseChange.java
similarity index 81%
rename from src/main/java/org/jabref/gui/collab/ExternalChange.java
rename to src/main/java/org/jabref/gui/collab/DatabaseChange.java
index 00d13c92d29..ec28509b864 100644
--- a/src/main/java/org/jabref/gui/collab/ExternalChange.java
+++ b/src/main/java/org/jabref/gui/collab/DatabaseChange.java
@@ -21,18 +21,18 @@
import org.jabref.gui.util.OptionalObjectProperty;
import org.jabref.model.database.BibDatabaseContext;
-public sealed abstract class ExternalChange permits EntryAdd, EntryChange, EntryDelete, GroupChange, MetadataChange, PreambleChange, BibTexStringAdd, BibTexStringChange, BibTexStringDelete, BibTexStringRename {
+public sealed abstract class DatabaseChange permits EntryAdd, EntryChange, EntryDelete, GroupChange, MetadataChange, PreambleChange, BibTexStringAdd, BibTexStringChange, BibTexStringDelete, BibTexStringRename {
protected final BibDatabaseContext databaseContext;
- protected final OptionalObjectProperty externalChangeResolver = OptionalObjectProperty.empty();
+ protected final OptionalObjectProperty externalChangeResolver = OptionalObjectProperty.empty();
private final BooleanProperty accepted = new SimpleBooleanProperty();
private final StringProperty name = new SimpleStringProperty();
- protected ExternalChange(BibDatabaseContext databaseContext, ExternalChangeResolverFactory externalChangeResolverFactory) {
+ protected DatabaseChange(BibDatabaseContext databaseContext, DatabaseChangeResolverFactory databaseChangeResolverFactory) {
this.databaseContext = databaseContext;
setChangeName("Unnamed Change!");
- if (externalChangeResolverFactory != null) {
- externalChangeResolver.set(externalChangeResolverFactory.create(this));
+ if (databaseChangeResolverFactory != null) {
+ externalChangeResolver.set(databaseChangeResolverFactory.create(this));
}
}
@@ -49,7 +49,7 @@ public void setAccepted(boolean accepted) {
}
/**
- * Convinience method for accepting changes
+ * Convenience method for accepting changes
* */
public void accept() {
setAccepted(true);
@@ -63,7 +63,7 @@ protected void setChangeName(String changeName) {
name.set(changeName);
}
- public Optional getExternalChangeResolver() {
+ public Optional getExternalChangeResolver() {
return externalChangeResolver.get();
}
diff --git a/src/main/java/org/jabref/gui/collab/ExternalChangeDetailsView.java b/src/main/java/org/jabref/gui/collab/DatabaseChangeDetailsView.java
similarity index 94%
rename from src/main/java/org/jabref/gui/collab/ExternalChangeDetailsView.java
rename to src/main/java/org/jabref/gui/collab/DatabaseChangeDetailsView.java
index d83940060da..fa13bc804a4 100644
--- a/src/main/java/org/jabref/gui/collab/ExternalChangeDetailsView.java
+++ b/src/main/java/org/jabref/gui/collab/DatabaseChangeDetailsView.java
@@ -13,5 +13,5 @@
import org.jabref.gui.collab.stringdelete.BibTexStringDeleteDetailsView;
import org.jabref.gui.collab.stringrename.BibTexStringRenameDetailsView;
-public sealed abstract class ExternalChangeDetailsView extends AnchorPane permits EntryAddDetailsView, EntryChangeDetailsView, EntryDeleteDetailsView, GroupChangeDetailsView, MetadataChangeDetailsView, PreambleChangeDetailsView, BibTexStringAddDetailsView, BibTexStringChangeDetailsView, BibTexStringDeleteDetailsView, BibTexStringRenameDetailsView {
+public sealed abstract class DatabaseChangeDetailsView extends AnchorPane permits EntryAddDetailsView, EntryChangeDetailsView, EntryDeleteDetailsView, GroupChangeDetailsView, MetadataChangeDetailsView, PreambleChangeDetailsView, BibTexStringAddDetailsView, BibTexStringChangeDetailsView, BibTexStringDeleteDetailsView, BibTexStringRenameDetailsView {
}
diff --git a/src/main/java/org/jabref/gui/collab/ExternalChangeDetailsViewFactory.java b/src/main/java/org/jabref/gui/collab/DatabaseChangeDetailsViewFactory.java
similarity index 78%
rename from src/main/java/org/jabref/gui/collab/ExternalChangeDetailsViewFactory.java
rename to src/main/java/org/jabref/gui/collab/DatabaseChangeDetailsViewFactory.java
index 1935d72a898..dcb8dfaf407 100644
--- a/src/main/java/org/jabref/gui/collab/ExternalChangeDetailsViewFactory.java
+++ b/src/main/java/org/jabref/gui/collab/DatabaseChangeDetailsViewFactory.java
@@ -26,14 +26,14 @@
import org.jabref.model.database.BibDatabaseContext;
import org.jabref.preferences.PreferencesService;
-public class ExternalChangeDetailsViewFactory {
+public class DatabaseChangeDetailsViewFactory {
private final BibDatabaseContext databaseContext;
private final DialogService dialogService;
private final StateManager stateManager;
private final ThemeManager themeManager;
private final PreferencesService preferencesService;
- public ExternalChangeDetailsViewFactory(BibDatabaseContext databaseContext, DialogService dialogService, StateManager stateManager, ThemeManager themeManager, PreferencesService preferencesService) {
+ public DatabaseChangeDetailsViewFactory(BibDatabaseContext databaseContext, DialogService dialogService, StateManager stateManager, ThemeManager themeManager, PreferencesService preferencesService) {
this.databaseContext = databaseContext;
this.dialogService = dialogService;
this.stateManager = stateManager;
@@ -41,29 +41,29 @@ public ExternalChangeDetailsViewFactory(BibDatabaseContext databaseContext, Dial
this.preferencesService = preferencesService;
}
- public ExternalChangeDetailsView create(ExternalChange externalChange) {
+ public DatabaseChangeDetailsView create(DatabaseChange databaseChange) {
// TODO: Use Pattern Matching for switch once it's out of preview
- if (externalChange instanceof EntryChange entryChange) {
+ if (databaseChange instanceof EntryChange entryChange) {
return new EntryChangeDetailsView(entryChange, databaseContext, dialogService, stateManager, themeManager, preferencesService);
- } else if (externalChange instanceof EntryAdd entryAdd) {
+ } else if (databaseChange instanceof EntryAdd entryAdd) {
return new EntryAddDetailsView(entryAdd, databaseContext, dialogService, stateManager, themeManager, preferencesService);
- } else if (externalChange instanceof EntryDelete entryDelete) {
+ } else if (databaseChange instanceof EntryDelete entryDelete) {
return new EntryDeleteDetailsView(entryDelete, databaseContext, dialogService, stateManager, themeManager, preferencesService);
- } else if (externalChange instanceof BibTexStringAdd stringAdd) {
+ } else if (databaseChange instanceof BibTexStringAdd stringAdd) {
return new BibTexStringAddDetailsView(stringAdd);
- } else if (externalChange instanceof BibTexStringDelete stringDelete) {
+ } else if (databaseChange instanceof BibTexStringDelete stringDelete) {
return new BibTexStringDeleteDetailsView(stringDelete);
- } else if (externalChange instanceof BibTexStringChange stringChange) {
+ } else if (databaseChange instanceof BibTexStringChange stringChange) {
return new BibTexStringChangeDetailsView(stringChange);
- } else if (externalChange instanceof BibTexStringRename stringRename) {
+ } else if (databaseChange instanceof BibTexStringRename stringRename) {
return new BibTexStringRenameDetailsView(stringRename);
- } else if (externalChange instanceof MetadataChange metadataChange) {
+ } else if (databaseChange instanceof MetadataChange metadataChange) {
return new MetadataChangeDetailsView(metadataChange, preferencesService);
- } else if (externalChange instanceof GroupChange groupChange) {
+ } else if (databaseChange instanceof GroupChange groupChange) {
return new GroupChangeDetailsView(groupChange);
- } else if (externalChange instanceof PreambleChange preambleChange) {
+ } else if (databaseChange instanceof PreambleChange preambleChange) {
return new PreambleChangeDetailsView(preambleChange);
}
- throw new UnsupportedOperationException("Cannot preview the given change: " + externalChange.getName());
+ throw new UnsupportedOperationException("Cannot preview the given change: " + databaseChange.getName());
}
}
diff --git a/src/main/java/org/jabref/gui/collab/DatabaseChangeList.java b/src/main/java/org/jabref/gui/collab/DatabaseChangeList.java
new file mode 100644
index 00000000000..de4f9423303
--- /dev/null
+++ b/src/main/java/org/jabref/gui/collab/DatabaseChangeList.java
@@ -0,0 +1,78 @@
+package org.jabref.gui.collab;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.jabref.gui.collab.entryadd.EntryAdd;
+import org.jabref.gui.collab.entrychange.EntryChange;
+import org.jabref.gui.collab.entrydelete.EntryDelete;
+import org.jabref.gui.collab.groupchange.GroupChange;
+import org.jabref.gui.collab.metedatachange.MetadataChange;
+import org.jabref.gui.collab.preamblechange.PreambleChange;
+import org.jabref.gui.collab.stringadd.BibTexStringAdd;
+import org.jabref.gui.collab.stringchange.BibTexStringChange;
+import org.jabref.gui.collab.stringdelete.BibTexStringDelete;
+import org.jabref.gui.collab.stringrename.BibTexStringRename;
+import org.jabref.logic.bibtex.comparator.BibDatabaseDiff;
+import org.jabref.logic.bibtex.comparator.BibEntryDiff;
+import org.jabref.logic.bibtex.comparator.BibStringDiff;
+import org.jabref.model.database.BibDatabaseContext;
+
+public class DatabaseChangeList {
+ private DatabaseChangeList() {
+ }
+
+ /**
+ * Compares the given two databases, and returns the list of changes required to change the {@code originalDatabase} into the {@code otherDatabase}
+ *
+ * @param originalDatabase This is the original database
+ * @param otherDatabase This is the other database.
+ * @return an unmodifiable list of {@code DatabaseChange} required to change {@code originalDatabase} into {@code otherDatabase}
+ */
+ public static List compareAndGetChanges(BibDatabaseContext originalDatabase, BibDatabaseContext otherDatabase, DatabaseChangeResolverFactory databaseChangeResolverFactory) {
+ List changes = new ArrayList<>();
+
+ BibDatabaseDiff differences = BibDatabaseDiff.compare(originalDatabase, otherDatabase);
+
+ differences.getMetaDataDifferences().ifPresent(diff -> {
+ changes.add(new MetadataChange(diff, originalDatabase, databaseChangeResolverFactory));
+ diff.getGroupDifferences().ifPresent(groupDiff -> changes.add(new GroupChange(
+ groupDiff, originalDatabase, databaseChangeResolverFactory
+ )));
+ });
+ differences.getPreambleDifferences().ifPresent(diff -> changes.add(new PreambleChange(diff, originalDatabase, databaseChangeResolverFactory)));
+ differences.getBibStringDifferences().forEach(diff -> changes.add(createBibStringDiff(originalDatabase, databaseChangeResolverFactory, diff)));
+ differences.getEntryDifferences().forEach(diff -> changes.add(createBibEntryDiff(originalDatabase, databaseChangeResolverFactory, diff)));
+
+ return Collections.unmodifiableList(changes);
+ }
+
+ private static DatabaseChange createBibStringDiff(BibDatabaseContext originalDatabase, DatabaseChangeResolverFactory databaseChangeResolverFactory, BibStringDiff diff) {
+ if (diff.getOriginalString() == null) {
+ return new BibTexStringAdd(diff.getNewString(), originalDatabase, databaseChangeResolverFactory);
+ }
+
+ if (diff.getNewString() == null) {
+ return new BibTexStringDelete(diff.getOriginalString(), originalDatabase, databaseChangeResolverFactory);
+ }
+
+ if (diff.getOriginalString().getName().equals(diff.getNewString().getName())) {
+ return new BibTexStringChange(diff.getOriginalString(), diff.getNewString(), originalDatabase, databaseChangeResolverFactory);
+ }
+
+ return new BibTexStringRename(diff.getOriginalString(), diff.getNewString(), originalDatabase, databaseChangeResolverFactory);
+ }
+
+ private static DatabaseChange createBibEntryDiff(BibDatabaseContext originalDatabase, DatabaseChangeResolverFactory databaseChangeResolverFactory, BibEntryDiff diff) {
+ if (diff.getOriginalEntry() == null) {
+ return new EntryAdd(diff.getNewEntry(), originalDatabase, databaseChangeResolverFactory);
+ }
+
+ if (diff.getNewEntry() == null) {
+ return new EntryDelete(diff.getOriginalEntry(), originalDatabase, databaseChangeResolverFactory);
+ }
+
+ return new EntryChange(diff.getOriginalEntry(), diff.getNewEntry(), originalDatabase, databaseChangeResolverFactory);
+ }
+}
diff --git a/src/main/java/org/jabref/gui/collab/DatabaseChangeListener.java b/src/main/java/org/jabref/gui/collab/DatabaseChangeListener.java
index b26a511ecf1..4ae14899da9 100644
--- a/src/main/java/org/jabref/gui/collab/DatabaseChangeListener.java
+++ b/src/main/java/org/jabref/gui/collab/DatabaseChangeListener.java
@@ -3,5 +3,5 @@
import java.util.List;
public interface DatabaseChangeListener {
- void databaseChanged(List changes);
+ void databaseChanged(List changes);
}
diff --git a/src/main/java/org/jabref/gui/collab/DatabaseChangeMonitor.java b/src/main/java/org/jabref/gui/collab/DatabaseChangeMonitor.java
index 3f1b3b4fb5e..2369f1aedad 100644
--- a/src/main/java/org/jabref/gui/collab/DatabaseChangeMonitor.java
+++ b/src/main/java/org/jabref/gui/collab/DatabaseChangeMonitor.java
@@ -33,8 +33,6 @@ public class DatabaseChangeMonitor implements FileUpdateListener {
private final TaskExecutor taskExecutor;
private final DialogService dialogService;
private final PreferencesService preferencesService;
- private final StateManager stateManager;
- private final ThemeManager themeManager;
public DatabaseChangeMonitor(BibDatabaseContext database,
FileUpdateMonitor fileMonitor,
@@ -49,8 +47,6 @@ public DatabaseChangeMonitor(BibDatabaseContext database,
this.taskExecutor = taskExecutor;
this.dialogService = dialogService;
this.preferencesService = preferencesService;
- this.stateManager = stateManager;
- this.themeManager = themeManager;
this.listeners = new ArrayList<>();
@@ -67,7 +63,7 @@ public DatabaseChangeMonitor(BibDatabaseContext database,
Localization.lang("The library has been modified by another program."),
List.of(new Action(Localization.lang("Dismiss changes"), event -> notificationPane.hide()),
new Action(Localization.lang("Review changes"), event -> {
- dialogService.showCustomDialogAndWait(new ExternalChangesResolverDialog(changes, database, dialogService, stateManager, themeManager, preferencesService));
+ dialogService.showCustomDialogAndWait(new DatabaseChangesResolverDialog(changes, database, dialogService, stateManager, themeManager, preferencesService, Localization.lang("External Changes Resolver")));
notificationPane.hide();
})),
Duration.ZERO));
@@ -77,7 +73,7 @@ public DatabaseChangeMonitor(BibDatabaseContext database,
public void fileUpdated() {
synchronized (database) {
// File on disk has changed, thus look for notable changes and notify listeners in case there are such changes
- ChangeScanner scanner = new ChangeScanner(database, dialogService, preferencesService, stateManager, themeManager);
+ ChangeScanner scanner = new ChangeScanner(database, dialogService, preferencesService);
BackgroundTask.wrap(scanner::scanForChanges)
.onSuccess(changes -> {
if (!changes.isEmpty()) {
diff --git a/src/main/java/org/jabref/gui/collab/ExternalChangeResolver.java b/src/main/java/org/jabref/gui/collab/DatabaseChangeResolver.java
similarity index 60%
rename from src/main/java/org/jabref/gui/collab/ExternalChangeResolver.java
rename to src/main/java/org/jabref/gui/collab/DatabaseChangeResolver.java
index 17b53e704d3..83c318d4965 100644
--- a/src/main/java/org/jabref/gui/collab/ExternalChangeResolver.java
+++ b/src/main/java/org/jabref/gui/collab/DatabaseChangeResolver.java
@@ -5,12 +5,12 @@
import org.jabref.gui.DialogService;
import org.jabref.gui.collab.entrychange.EntryChangeResolver;
-public sealed abstract class ExternalChangeResolver permits EntryChangeResolver {
+public sealed abstract class DatabaseChangeResolver permits EntryChangeResolver {
protected final DialogService dialogService;
- protected ExternalChangeResolver(DialogService dialogService) {
+ protected DatabaseChangeResolver(DialogService dialogService) {
this.dialogService = dialogService;
}
- public abstract Optional askUserToResolveChange();
+ public abstract Optional askUserToResolveChange();
}
diff --git a/src/main/java/org/jabref/gui/collab/ExternalChangeResolverFactory.java b/src/main/java/org/jabref/gui/collab/DatabaseChangeResolverFactory.java
similarity index 57%
rename from src/main/java/org/jabref/gui/collab/ExternalChangeResolverFactory.java
rename to src/main/java/org/jabref/gui/collab/DatabaseChangeResolverFactory.java
index 937fd486e9d..2aed7bb22ed 100644
--- a/src/main/java/org/jabref/gui/collab/ExternalChangeResolverFactory.java
+++ b/src/main/java/org/jabref/gui/collab/DatabaseChangeResolverFactory.java
@@ -6,19 +6,22 @@
import org.jabref.gui.collab.entrychange.EntryChange;
import org.jabref.gui.collab.entrychange.EntryChangeResolver;
import org.jabref.model.database.BibDatabaseContext;
+import org.jabref.preferences.BibEntryPreferences;
-public class ExternalChangeResolverFactory {
+public class DatabaseChangeResolverFactory {
private final DialogService dialogService;
private final BibDatabaseContext databaseContext;
+ private final BibEntryPreferences bibEntryPreferences;
- public ExternalChangeResolverFactory(DialogService dialogService, BibDatabaseContext databaseContext) {
+ public DatabaseChangeResolverFactory(DialogService dialogService, BibDatabaseContext databaseContext, BibEntryPreferences bibEntryPreferences) {
this.dialogService = dialogService;
this.databaseContext = databaseContext;
+ this.bibEntryPreferences = bibEntryPreferences;
}
- public Optional create(ExternalChange change) {
+ public Optional create(DatabaseChange change) {
if (change instanceof EntryChange entryChange) {
- return Optional.of(new EntryChangeResolver(entryChange, dialogService, databaseContext));
+ return Optional.of(new EntryChangeResolver(entryChange, dialogService, databaseContext, bibEntryPreferences));
}
return Optional.empty();
diff --git a/src/main/java/org/jabref/gui/collab/ExternalChangesResolverDialog.fxml b/src/main/java/org/jabref/gui/collab/DatabaseChangesResolverDialog.fxml
similarity index 97%
rename from src/main/java/org/jabref/gui/collab/ExternalChangesResolverDialog.fxml
rename to src/main/java/org/jabref/gui/collab/DatabaseChangesResolverDialog.fxml
index ff1ee32f50b..d1cf7444138 100644
--- a/src/main/java/org/jabref/gui/collab/ExternalChangesResolverDialog.fxml
+++ b/src/main/java/org/jabref/gui/collab/DatabaseChangesResolverDialog.fxml
@@ -11,7 +11,7 @@
-
+
diff --git a/src/main/java/org/jabref/gui/collab/ExternalChangesResolverDialog.java b/src/main/java/org/jabref/gui/collab/DatabaseChangesResolverDialog.java
similarity index 71%
rename from src/main/java/org/jabref/gui/collab/ExternalChangesResolverDialog.java
rename to src/main/java/org/jabref/gui/collab/DatabaseChangesResolverDialog.java
index f99dfab3c70..5e0f32739c2 100644
--- a/src/main/java/org/jabref/gui/collab/ExternalChangesResolverDialog.java
+++ b/src/main/java/org/jabref/gui/collab/DatabaseChangesResolverDialog.java
@@ -6,7 +6,6 @@
import javax.swing.undo.UndoManager;
-import javafx.application.Platform;
import javafx.beans.property.SimpleStringProperty;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
@@ -19,7 +18,6 @@
import org.jabref.gui.StateManager;
import org.jabref.gui.theme.ThemeManager;
import org.jabref.gui.util.BaseDialog;
-import org.jabref.logic.l10n.Localization;
import org.jabref.model.database.BibDatabaseContext;
import org.jabref.preferences.PreferencesService;
@@ -29,37 +27,44 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-public class ExternalChangesResolverDialog extends BaseDialog {
- private final static Logger LOGGER = LoggerFactory.getLogger(ExternalChangesResolverDialog.class);
+public class DatabaseChangesResolverDialog extends BaseDialog {
+ private final static Logger LOGGER = LoggerFactory.getLogger(DatabaseChangesResolverDialog.class);
/**
- * Reconstructing the details view to preview an {@link ExternalChange} every time it's selected is a heavy operation.
+ * Reconstructing the details view to preview an {@link DatabaseChange} every time it's selected is a heavy operation.
* It is also useless because changes are static and if the change data is static then the view doesn't have to change
- * either. This cache is used to ensure that we only create the detail view instance once for each {@link ExternalChange}.
+ * either. This cache is used to ensure that we only create the detail view instance once for each {@link DatabaseChange}.
*/
- private final Map DETAILS_VIEW_CACHE = new HashMap<>();
+ private final Map DETAILS_VIEW_CACHE = new HashMap<>();
@FXML
- private TableView changesTableView;
+ private TableView changesTableView;
@FXML
- private TableColumn changeName;
+ private TableColumn changeName;
@FXML
private Button askUserToResolveChangeButton;
@FXML
private BorderPane changeInfoPane;
- private final List changes;
+ private final List changes;
private ExternalChangesResolverViewModel viewModel;
- private final ExternalChangeDetailsViewFactory externalChangeDetailsViewFactory;
+ private final DatabaseChangeDetailsViewFactory databaseChangeDetailsViewFactory;
@Inject private UndoManager undoManager;
- public ExternalChangesResolverDialog(List changes, BibDatabaseContext database, DialogService dialogService, StateManager stateManager, ThemeManager themeManager, PreferencesService preferencesService) {
+ /**
+ * A dialog going through given changes
, which are diffs to the provided database
.
+ * Each accepted change is written to the provided database
.
+ *
+ * @param changes The list of changes
+ * @param database The database to apply the changes to
+ */
+ public DatabaseChangesResolverDialog(List changes, BibDatabaseContext database, DialogService dialogService, StateManager stateManager, ThemeManager themeManager, PreferencesService preferencesService, String dialogTitle) {
this.changes = changes;
- this.externalChangeDetailsViewFactory = new ExternalChangeDetailsViewFactory(database, dialogService, stateManager, themeManager, preferencesService);
+ this.databaseChangeDetailsViewFactory = new DatabaseChangeDetailsViewFactory(database, dialogService, stateManager, themeManager, preferencesService);
- this.setTitle(Localization.lang("External Changes Resolver"));
+ this.setTitle(dialogTitle);
ViewLoader.view(this)
.load()
.setAsDialogPane(this);
@@ -90,16 +95,15 @@ private void initialize() {
viewModel.selectedChangeProperty().bind(changesTableView.getSelectionModel().selectedItemProperty());
EasyBind.subscribe(viewModel.selectedChangeProperty(), selectedChange -> {
if (selectedChange != null) {
- ExternalChangeDetailsView detailsView = DETAILS_VIEW_CACHE.computeIfAbsent(selectedChange, externalChangeDetailsViewFactory::create);
+ DatabaseChangeDetailsView detailsView = DETAILS_VIEW_CACHE.computeIfAbsent(selectedChange, databaseChangeDetailsViewFactory::create);
changeInfoPane.setCenter(detailsView);
}
});
EasyBind.subscribe(viewModel.areAllChangesResolvedProperty(), isResolved -> {
if (isResolved) {
- Platform.runLater(viewModel::applyChanges);
+ viewModel.applyChanges();
close();
- LOGGER.debug("Closing ExternalChangesResolverDialog");
}
});
}
@@ -116,7 +120,7 @@ public void acceptChanges() {
@FXML
public void askUserToResolveChange() {
- viewModel.getSelectedChange().flatMap(ExternalChange::getExternalChangeResolver)
- .flatMap(ExternalChangeResolver::askUserToResolveChange).ifPresent(viewModel::acceptMergedChange);
+ viewModel.getSelectedChange().flatMap(DatabaseChange::getExternalChangeResolver)
+ .flatMap(DatabaseChangeResolver::askUserToResolveChange).ifPresent(viewModel::acceptMergedChange);
}
}
diff --git a/src/main/java/org/jabref/gui/collab/ExternalChangesResolverViewModel.java b/src/main/java/org/jabref/gui/collab/ExternalChangesResolverViewModel.java
index efa06a6dd06..0f3906594dc 100644
--- a/src/main/java/org/jabref/gui/collab/ExternalChangesResolverViewModel.java
+++ b/src/main/java/org/jabref/gui/collab/ExternalChangesResolverViewModel.java
@@ -25,15 +25,15 @@ public class ExternalChangesResolverViewModel extends AbstractViewModel {
private final static Logger LOGGER = LoggerFactory.getLogger(ExternalChangesResolverViewModel.class);
private final NamedCompound ce = new NamedCompound(Localization.lang("Merged external changes"));
- private final ObservableList visibleChanges = FXCollections.observableArrayList();
+ private final ObservableList visibleChanges = FXCollections.observableArrayList();
/**
* Because visible changes list will be bound to the UI, certain changes can be removed. This list is used to keep
* track of changes even when they're removed from the UI.
*/
- private final ObservableList changes = FXCollections.observableArrayList();
+ private final ObservableList changes = FXCollections.observableArrayList();
- private final ObjectProperty selectedChange = new SimpleObjectProperty<>();
+ private final ObjectProperty selectedChange = new SimpleObjectProperty<>();
private final BooleanBinding areAllChangesResolved;
@@ -41,7 +41,7 @@ public class ExternalChangesResolverViewModel extends AbstractViewModel {
private final UndoManager undoManager;
- public ExternalChangesResolverViewModel(List externalChanges, UndoManager undoManager) {
+ public ExternalChangesResolverViewModel(List externalChanges, UndoManager undoManager) {
Objects.requireNonNull(externalChanges);
assert !externalChanges.isEmpty();
@@ -53,15 +53,15 @@ public ExternalChangesResolverViewModel(List externalChanges, Un
canAskUserToResolveChange = Bindings.createBooleanBinding(() -> selectedChange.isNotNull().get() && selectedChange.get().getExternalChangeResolver().isPresent(), selectedChange);
}
- public ObservableList getVisibleChanges() {
+ public ObservableList getVisibleChanges() {
return visibleChanges;
}
- public ObjectProperty selectedChangeProperty() {
+ public ObjectProperty selectedChangeProperty() {
return selectedChange;
}
- public Optional getSelectedChange() {
+ public Optional getSelectedChange() {
return Optional.ofNullable(selectedChangeProperty().get());
}
@@ -88,19 +88,19 @@ public void denyChange() {
getSelectedChange().ifPresent(getVisibleChanges()::remove);
}
- public void acceptMergedChange(ExternalChange externalChange) {
- Objects.requireNonNull(externalChange);
+ public void acceptMergedChange(DatabaseChange databaseChange) {
+ Objects.requireNonNull(databaseChange);
getSelectedChange().ifPresent(oldChange -> {
changes.remove(oldChange);
- changes.add(externalChange);
+ changes.add(databaseChange);
+ databaseChange.accept();
getVisibleChanges().remove(oldChange);
- externalChange.accept();
});
}
public void applyChanges() {
- changes.stream().filter(ExternalChange::isAccepted).forEach(change -> change.applyChange(ce));
+ changes.stream().filter(DatabaseChange::isAccepted).forEach(change -> change.applyChange(ce));
ce.end();
undoManager.addEdit(ce);
}
diff --git a/src/main/java/org/jabref/gui/collab/entryadd/EntryAdd.java b/src/main/java/org/jabref/gui/collab/entryadd/EntryAdd.java
index c9be0167535..abf8ae633bf 100644
--- a/src/main/java/org/jabref/gui/collab/entryadd/EntryAdd.java
+++ b/src/main/java/org/jabref/gui/collab/entryadd/EntryAdd.java
@@ -1,18 +1,18 @@
package org.jabref.gui.collab.entryadd;
-import org.jabref.gui.collab.ExternalChange;
-import org.jabref.gui.collab.ExternalChangeResolverFactory;
+import org.jabref.gui.collab.DatabaseChange;
+import org.jabref.gui.collab.DatabaseChangeResolverFactory;
import org.jabref.gui.undo.NamedCompound;
import org.jabref.gui.undo.UndoableInsertEntries;
import org.jabref.logic.l10n.Localization;
import org.jabref.model.database.BibDatabaseContext;
import org.jabref.model.entry.BibEntry;
-public final class EntryAdd extends ExternalChange {
+public final class EntryAdd extends DatabaseChange {
private final BibEntry addedEntry;
- public EntryAdd(BibEntry addedEntry, BibDatabaseContext databaseContext, ExternalChangeResolverFactory externalChangeResolverFactory) {
- super(databaseContext, externalChangeResolverFactory);
+ public EntryAdd(BibEntry addedEntry, BibDatabaseContext databaseContext, DatabaseChangeResolverFactory databaseChangeResolverFactory) {
+ super(databaseContext, databaseChangeResolverFactory);
this.addedEntry = addedEntry;
setChangeName(addedEntry.getCitationKey()
.map(key -> Localization.lang("Added entry '%0'", key))
diff --git a/src/main/java/org/jabref/gui/collab/entryadd/EntryAddDetailsView.java b/src/main/java/org/jabref/gui/collab/entryadd/EntryAddDetailsView.java
index a967d2a1381..81cc8740e8c 100644
--- a/src/main/java/org/jabref/gui/collab/entryadd/EntryAddDetailsView.java
+++ b/src/main/java/org/jabref/gui/collab/entryadd/EntryAddDetailsView.java
@@ -2,13 +2,13 @@
import org.jabref.gui.DialogService;
import org.jabref.gui.StateManager;
-import org.jabref.gui.collab.ExternalChangeDetailsView;
+import org.jabref.gui.collab.DatabaseChangeDetailsView;
import org.jabref.gui.preview.PreviewViewer;
import org.jabref.gui.theme.ThemeManager;
import org.jabref.model.database.BibDatabaseContext;
import org.jabref.preferences.PreferencesService;
-public final class EntryAddDetailsView extends ExternalChangeDetailsView {
+public final class EntryAddDetailsView extends DatabaseChangeDetailsView {
public EntryAddDetailsView(EntryAdd entryAdd, BibDatabaseContext bibDatabaseContext, DialogService dialogService, StateManager stateManager, ThemeManager themeManager, PreferencesService preferencesService) {
PreviewViewer previewViewer = new PreviewViewer(bibDatabaseContext, dialogService, stateManager, themeManager);
diff --git a/src/main/java/org/jabref/gui/collab/entrychange/EntryChange.java b/src/main/java/org/jabref/gui/collab/entrychange/EntryChange.java
index 34a70872c50..44189beef93 100644
--- a/src/main/java/org/jabref/gui/collab/entrychange/EntryChange.java
+++ b/src/main/java/org/jabref/gui/collab/entrychange/EntryChange.java
@@ -2,8 +2,8 @@
import javax.swing.undo.CompoundEdit;
-import org.jabref.gui.collab.ExternalChange;
-import org.jabref.gui.collab.ExternalChangeResolverFactory;
+import org.jabref.gui.collab.DatabaseChange;
+import org.jabref.gui.collab.DatabaseChangeResolverFactory;
import org.jabref.gui.undo.NamedCompound;
import org.jabref.gui.undo.UndoableInsertEntries;
import org.jabref.gui.undo.UndoableRemoveEntries;
@@ -11,12 +11,12 @@
import org.jabref.model.database.BibDatabaseContext;
import org.jabref.model.entry.BibEntry;
-public final class EntryChange extends ExternalChange {
+public final class EntryChange extends DatabaseChange {
private final BibEntry oldEntry;
private final BibEntry newEntry;
- public EntryChange(BibEntry oldEntry, BibEntry newEntry, BibDatabaseContext databaseContext, ExternalChangeResolverFactory externalChangeResolverFactory) {
- super(databaseContext, externalChangeResolverFactory);
+ public EntryChange(BibEntry oldEntry, BibEntry newEntry, BibDatabaseContext databaseContext, DatabaseChangeResolverFactory databaseChangeResolverFactory) {
+ super(databaseContext, databaseChangeResolverFactory);
this.oldEntry = oldEntry;
this.newEntry = newEntry;
setChangeName(oldEntry.getCitationKey().map(key -> Localization.lang("Modified entry '%0'", key))
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 18286e5fc99..ef7abeeb86b 100644
--- a/src/main/java/org/jabref/gui/collab/entrychange/EntryChangeDetailsView.java
+++ b/src/main/java/org/jabref/gui/collab/entrychange/EntryChangeDetailsView.java
@@ -2,13 +2,13 @@
import org.jabref.gui.DialogService;
import org.jabref.gui.StateManager;
-import org.jabref.gui.collab.ExternalChangeDetailsView;
+import org.jabref.gui.collab.DatabaseChangeDetailsView;
import org.jabref.gui.preview.PreviewViewer;
import org.jabref.gui.theme.ThemeManager;
import org.jabref.model.database.BibDatabaseContext;
import org.jabref.preferences.PreferencesService;
-public final class EntryChangeDetailsView extends ExternalChangeDetailsView {
+public final class EntryChangeDetailsView extends DatabaseChangeDetailsView {
public EntryChangeDetailsView(EntryChange entryChange, BibDatabaseContext bibDatabaseContext, DialogService dialogService, StateManager stateManager, ThemeManager themeManager, PreferencesService preferencesService) {
PreviewViewer previewViewer = new PreviewViewer(bibDatabaseContext, dialogService, stateManager, themeManager);
previewViewer.setLayout(preferencesService.getPreviewPreferences().getSelectedPreviewLayout());
diff --git a/src/main/java/org/jabref/gui/collab/entrychange/EntryChangeResolver.java b/src/main/java/org/jabref/gui/collab/entrychange/EntryChangeResolver.java
index dfb3a1a1470..53445107d4a 100644
--- a/src/main/java/org/jabref/gui/collab/entrychange/EntryChangeResolver.java
+++ b/src/main/java/org/jabref/gui/collab/entrychange/EntryChangeResolver.java
@@ -3,32 +3,36 @@
import java.util.Optional;
import org.jabref.gui.DialogService;
-import org.jabref.gui.collab.ExternalChange;
-import org.jabref.gui.collab.ExternalChangeResolver;
+import org.jabref.gui.collab.DatabaseChange;
+import org.jabref.gui.collab.DatabaseChangeResolver;
import org.jabref.gui.mergeentries.EntriesMergeResult;
import org.jabref.gui.mergeentries.MergeEntriesDialog;
import org.jabref.gui.mergeentries.newmergedialog.ShowDiffConfig;
-import org.jabref.gui.mergeentries.newmergedialog.diffhighlighter.DiffHighlighter;
+import org.jabref.gui.mergeentries.newmergedialog.diffhighlighter.DiffHighlighter.BasicDiffMethod;
import org.jabref.gui.mergeentries.newmergedialog.toolbar.ThreeWayMergeToolbar;
import org.jabref.logic.l10n.Localization;
import org.jabref.model.database.BibDatabaseContext;
+import org.jabref.preferences.BibEntryPreferences;
-public final class EntryChangeResolver extends ExternalChangeResolver {
+public final class EntryChangeResolver extends DatabaseChangeResolver {
private final EntryChange entryChange;
private final BibDatabaseContext databaseContext;
- public EntryChangeResolver(EntryChange entryChange, DialogService dialogService, BibDatabaseContext databaseContext) {
+ private final BibEntryPreferences bibEntryPreferences;
+
+ public EntryChangeResolver(EntryChange entryChange, DialogService dialogService, BibDatabaseContext databaseContext, BibEntryPreferences bibEntryPreferences) {
super(dialogService);
this.entryChange = entryChange;
this.databaseContext = databaseContext;
+ this.bibEntryPreferences = bibEntryPreferences;
}
@Override
- public Optional askUserToResolveChange() {
- MergeEntriesDialog mergeEntriesDialog = new MergeEntriesDialog(entryChange.getOldEntry(), entryChange.getNewEntry());
+ public Optional askUserToResolveChange() {
+ MergeEntriesDialog mergeEntriesDialog = new MergeEntriesDialog(entryChange.getOldEntry(), entryChange.getNewEntry(), bibEntryPreferences);
mergeEntriesDialog.setLeftHeaderText(Localization.lang("On JabRef"));
mergeEntriesDialog.setRightHeaderText(Localization.lang("On disk"));
- mergeEntriesDialog.configureDiff(new ShowDiffConfig(ThreeWayMergeToolbar.DiffView.SPLIT, DiffHighlighter.DiffMethod.WORDS));
+ mergeEntriesDialog.configureDiff(new ShowDiffConfig(ThreeWayMergeToolbar.DiffView.SPLIT, BasicDiffMethod.WORDS));
return dialogService.showCustomDialogAndWait(mergeEntriesDialog)
.map(this::mapMergeResultToExternalChange);
diff --git a/src/main/java/org/jabref/gui/collab/entrydelete/EntryDelete.java b/src/main/java/org/jabref/gui/collab/entrydelete/EntryDelete.java
index 13913a7974d..36116bb5845 100644
--- a/src/main/java/org/jabref/gui/collab/entrydelete/EntryDelete.java
+++ b/src/main/java/org/jabref/gui/collab/entrydelete/EntryDelete.java
@@ -1,18 +1,18 @@
package org.jabref.gui.collab.entrydelete;
-import org.jabref.gui.collab.ExternalChange;
-import org.jabref.gui.collab.ExternalChangeResolverFactory;
+import org.jabref.gui.collab.DatabaseChange;
+import org.jabref.gui.collab.DatabaseChangeResolverFactory;
import org.jabref.gui.undo.NamedCompound;
import org.jabref.gui.undo.UndoableRemoveEntries;
import org.jabref.logic.l10n.Localization;
import org.jabref.model.database.BibDatabaseContext;
import org.jabref.model.entry.BibEntry;
-public final class EntryDelete extends ExternalChange {
+public final class EntryDelete extends DatabaseChange {
private final BibEntry deletedEntry;
- public EntryDelete(BibEntry deletedEntry, BibDatabaseContext databaseContext, ExternalChangeResolverFactory externalChangeResolverFactory) {
- super(databaseContext, externalChangeResolverFactory);
+ public EntryDelete(BibEntry deletedEntry, BibDatabaseContext databaseContext, DatabaseChangeResolverFactory databaseChangeResolverFactory) {
+ super(databaseContext, databaseChangeResolverFactory);
this.deletedEntry = deletedEntry;
setChangeName(deletedEntry.getCitationKey()
.map(key -> Localization.lang("Deleted entry '%0'", key))
diff --git a/src/main/java/org/jabref/gui/collab/entrydelete/EntryDeleteDetailsView.java b/src/main/java/org/jabref/gui/collab/entrydelete/EntryDeleteDetailsView.java
index e60051af592..a1630805435 100644
--- a/src/main/java/org/jabref/gui/collab/entrydelete/EntryDeleteDetailsView.java
+++ b/src/main/java/org/jabref/gui/collab/entrydelete/EntryDeleteDetailsView.java
@@ -2,13 +2,13 @@
import org.jabref.gui.DialogService;
import org.jabref.gui.StateManager;
-import org.jabref.gui.collab.ExternalChangeDetailsView;
+import org.jabref.gui.collab.DatabaseChangeDetailsView;
import org.jabref.gui.preview.PreviewViewer;
import org.jabref.gui.theme.ThemeManager;
import org.jabref.model.database.BibDatabaseContext;
import org.jabref.preferences.PreferencesService;
-public final class EntryDeleteDetailsView extends ExternalChangeDetailsView {
+public final class EntryDeleteDetailsView extends DatabaseChangeDetailsView {
public EntryDeleteDetailsView(EntryDelete entryDelete, BibDatabaseContext bibDatabaseContext, DialogService dialogService, StateManager stateManager, ThemeManager themeManager, PreferencesService preferencesService) {
PreviewViewer previewViewer = new PreviewViewer(bibDatabaseContext, dialogService, stateManager, themeManager);
diff --git a/src/main/java/org/jabref/gui/collab/groupchange/GroupChange.java b/src/main/java/org/jabref/gui/collab/groupchange/GroupChange.java
index e6259766614..4444d0f79c3 100644
--- a/src/main/java/org/jabref/gui/collab/groupchange/GroupChange.java
+++ b/src/main/java/org/jabref/gui/collab/groupchange/GroupChange.java
@@ -1,7 +1,7 @@
package org.jabref.gui.collab.groupchange;
-import org.jabref.gui.collab.ExternalChange;
-import org.jabref.gui.collab.ExternalChangeResolverFactory;
+import org.jabref.gui.collab.DatabaseChange;
+import org.jabref.gui.collab.DatabaseChangeResolverFactory;
import org.jabref.gui.groups.GroupTreeNodeViewModel;
import org.jabref.gui.groups.UndoableModifySubtree;
import org.jabref.gui.undo.NamedCompound;
@@ -11,11 +11,11 @@
import org.jabref.model.database.BibDatabaseContext;
import org.jabref.model.groups.GroupTreeNode;
-public final class GroupChange extends ExternalChange {
+public final class GroupChange extends DatabaseChange {
private final GroupDiff groupDiff;
- public GroupChange(GroupDiff groupDiff, BibDatabaseContext databaseContext, ExternalChangeResolverFactory externalChangeResolverFactory) {
- super(databaseContext, externalChangeResolverFactory);
+ public GroupChange(GroupDiff groupDiff, BibDatabaseContext databaseContext, DatabaseChangeResolverFactory databaseChangeResolverFactory) {
+ super(databaseContext, databaseChangeResolverFactory);
this.groupDiff = groupDiff;
setChangeName(groupDiff.getOriginalGroupRoot() == null ? Localization.lang("Removed all groups") : Localization
.lang("Modified groups tree"));
diff --git a/src/main/java/org/jabref/gui/collab/groupchange/GroupChangeDetailsView.java b/src/main/java/org/jabref/gui/collab/groupchange/GroupChangeDetailsView.java
index 0c3f6eda15a..914a2aceb75 100644
--- a/src/main/java/org/jabref/gui/collab/groupchange/GroupChangeDetailsView.java
+++ b/src/main/java/org/jabref/gui/collab/groupchange/GroupChangeDetailsView.java
@@ -2,10 +2,10 @@
import javafx.scene.control.Label;
-import org.jabref.gui.collab.ExternalChangeDetailsView;
+import org.jabref.gui.collab.DatabaseChangeDetailsView;
import org.jabref.logic.l10n.Localization;
-public final class GroupChangeDetailsView extends ExternalChangeDetailsView {
+public final class GroupChangeDetailsView extends DatabaseChangeDetailsView {
public GroupChangeDetailsView(GroupChange groupChange) {
String labelValue = "";
diff --git a/src/main/java/org/jabref/gui/collab/metedatachange/MetadataChange.java b/src/main/java/org/jabref/gui/collab/metedatachange/MetadataChange.java
index ae1340df6c6..571fe74b752 100644
--- a/src/main/java/org/jabref/gui/collab/metedatachange/MetadataChange.java
+++ b/src/main/java/org/jabref/gui/collab/metedatachange/MetadataChange.java
@@ -1,17 +1,17 @@
package org.jabref.gui.collab.metedatachange;
-import org.jabref.gui.collab.ExternalChange;
-import org.jabref.gui.collab.ExternalChangeResolverFactory;
+import org.jabref.gui.collab.DatabaseChange;
+import org.jabref.gui.collab.DatabaseChangeResolverFactory;
import org.jabref.gui.undo.NamedCompound;
import org.jabref.logic.bibtex.comparator.MetaDataDiff;
import org.jabref.logic.l10n.Localization;
import org.jabref.model.database.BibDatabaseContext;
-public final class MetadataChange extends ExternalChange {
+public final class MetadataChange extends DatabaseChange {
private final MetaDataDiff metaDataDiff;
- public MetadataChange(MetaDataDiff metaDataDiff, BibDatabaseContext databaseContext, ExternalChangeResolverFactory externalChangeResolverFactory) {
- super(databaseContext, externalChangeResolverFactory);
+ public MetadataChange(MetaDataDiff metaDataDiff, BibDatabaseContext databaseContext, DatabaseChangeResolverFactory databaseChangeResolverFactory) {
+ super(databaseContext, databaseChangeResolverFactory);
this.metaDataDiff = metaDataDiff;
setChangeName(Localization.lang("Metadata change"));
}
diff --git a/src/main/java/org/jabref/gui/collab/metedatachange/MetadataChangeDetailsView.java b/src/main/java/org/jabref/gui/collab/metedatachange/MetadataChangeDetailsView.java
index 078ef89fabd..cddcdf32e2d 100644
--- a/src/main/java/org/jabref/gui/collab/metedatachange/MetadataChangeDetailsView.java
+++ b/src/main/java/org/jabref/gui/collab/metedatachange/MetadataChangeDetailsView.java
@@ -3,12 +3,12 @@
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
-import org.jabref.gui.collab.ExternalChangeDetailsView;
+import org.jabref.gui.collab.DatabaseChangeDetailsView;
import org.jabref.logic.bibtex.comparator.MetaDataDiff;
import org.jabref.logic.l10n.Localization;
import org.jabref.preferences.PreferencesService;
-public final class MetadataChangeDetailsView extends ExternalChangeDetailsView {
+public final class MetadataChangeDetailsView extends DatabaseChangeDetailsView {
public MetadataChangeDetailsView(MetadataChange metadataChange, PreferencesService preferencesService) {
VBox container = new VBox(15);
diff --git a/src/main/java/org/jabref/gui/collab/preamblechange/PreambleChange.java b/src/main/java/org/jabref/gui/collab/preamblechange/PreambleChange.java
index 50922e67d35..5789d2bbbcc 100644
--- a/src/main/java/org/jabref/gui/collab/preamblechange/PreambleChange.java
+++ b/src/main/java/org/jabref/gui/collab/preamblechange/PreambleChange.java
@@ -1,7 +1,7 @@
package org.jabref.gui.collab.preamblechange;
-import org.jabref.gui.collab.ExternalChange;
-import org.jabref.gui.collab.ExternalChangeResolverFactory;
+import org.jabref.gui.collab.DatabaseChange;
+import org.jabref.gui.collab.DatabaseChangeResolverFactory;
import org.jabref.gui.undo.NamedCompound;
import org.jabref.gui.undo.UndoablePreambleChange;
import org.jabref.logic.bibtex.comparator.PreambleDiff;
@@ -11,13 +11,13 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-public final class PreambleChange extends ExternalChange {
+public final class PreambleChange extends DatabaseChange {
private static final Logger LOGGER = LoggerFactory.getLogger(PreambleChange.class);
private final PreambleDiff preambleDiff;
- public PreambleChange(PreambleDiff preambleDiff, BibDatabaseContext databaseContext, ExternalChangeResolverFactory externalChangeResolverFactory) {
- super(databaseContext, externalChangeResolverFactory);
+ public PreambleChange(PreambleDiff preambleDiff, BibDatabaseContext databaseContext, DatabaseChangeResolverFactory databaseChangeResolverFactory) {
+ super(databaseContext, databaseChangeResolverFactory);
this.preambleDiff = preambleDiff;
setChangeName(Localization.lang("Changed preamble"));
diff --git a/src/main/java/org/jabref/gui/collab/preamblechange/PreambleChangeDetailsView.java b/src/main/java/org/jabref/gui/collab/preamblechange/PreambleChangeDetailsView.java
index 5d621403478..8630f62c240 100644
--- a/src/main/java/org/jabref/gui/collab/preamblechange/PreambleChangeDetailsView.java
+++ b/src/main/java/org/jabref/gui/collab/preamblechange/PreambleChangeDetailsView.java
@@ -3,12 +3,12 @@
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
-import org.jabref.gui.collab.ExternalChangeDetailsView;
+import org.jabref.gui.collab.DatabaseChangeDetailsView;
import org.jabref.logic.bibtex.comparator.PreambleDiff;
import org.jabref.logic.l10n.Localization;
import org.jabref.model.strings.StringUtil;
-public final class PreambleChangeDetailsView extends ExternalChangeDetailsView {
+public final class PreambleChangeDetailsView extends DatabaseChangeDetailsView {
public PreambleChangeDetailsView(PreambleChange preambleChange) {
PreambleDiff preambleDiff = preambleChange.getPreambleDiff();
diff --git a/src/main/java/org/jabref/gui/collab/stringadd/BibTexStringAdd.java b/src/main/java/org/jabref/gui/collab/stringadd/BibTexStringAdd.java
index 38a2a8a0168..98c311ca6dc 100644
--- a/src/main/java/org/jabref/gui/collab/stringadd/BibTexStringAdd.java
+++ b/src/main/java/org/jabref/gui/collab/stringadd/BibTexStringAdd.java
@@ -1,7 +1,7 @@
package org.jabref.gui.collab.stringadd;
-import org.jabref.gui.collab.ExternalChange;
-import org.jabref.gui.collab.ExternalChangeResolverFactory;
+import org.jabref.gui.collab.DatabaseChange;
+import org.jabref.gui.collab.DatabaseChangeResolverFactory;
import org.jabref.gui.undo.NamedCompound;
import org.jabref.gui.undo.UndoableInsertString;
import org.jabref.logic.l10n.Localization;
@@ -12,13 +12,13 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-public final class BibTexStringAdd extends ExternalChange {
+public final class BibTexStringAdd extends DatabaseChange {
private static final Logger LOGGER = LoggerFactory.getLogger(BibTexStringAdd.class);
private final BibtexString addedString;
- public BibTexStringAdd(BibtexString addedString, BibDatabaseContext databaseContext, ExternalChangeResolverFactory externalChangeResolverFactory) {
- super(databaseContext, externalChangeResolverFactory);
+ public BibTexStringAdd(BibtexString addedString, BibDatabaseContext databaseContext, DatabaseChangeResolverFactory databaseChangeResolverFactory) {
+ super(databaseContext, databaseChangeResolverFactory);
this.addedString = addedString;
setChangeName(Localization.lang("Added string: '%0'", addedString.getName()));
}
diff --git a/src/main/java/org/jabref/gui/collab/stringadd/BibTexStringAddDetailsView.java b/src/main/java/org/jabref/gui/collab/stringadd/BibTexStringAddDetailsView.java
index 4097f03c343..5e34729d108 100644
--- a/src/main/java/org/jabref/gui/collab/stringadd/BibTexStringAddDetailsView.java
+++ b/src/main/java/org/jabref/gui/collab/stringadd/BibTexStringAddDetailsView.java
@@ -3,10 +3,10 @@
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
-import org.jabref.gui.collab.ExternalChangeDetailsView;
+import org.jabref.gui.collab.DatabaseChangeDetailsView;
import org.jabref.logic.l10n.Localization;
-public final class BibTexStringAddDetailsView extends ExternalChangeDetailsView {
+public final class BibTexStringAddDetailsView extends DatabaseChangeDetailsView {
public BibTexStringAddDetailsView(BibTexStringAdd stringAdd) {
VBox container = new VBox();
diff --git a/src/main/java/org/jabref/gui/collab/stringchange/BibTexStringChange.java b/src/main/java/org/jabref/gui/collab/stringchange/BibTexStringChange.java
index 928a15bf73c..8cb58e99d46 100644
--- a/src/main/java/org/jabref/gui/collab/stringchange/BibTexStringChange.java
+++ b/src/main/java/org/jabref/gui/collab/stringchange/BibTexStringChange.java
@@ -1,7 +1,7 @@
package org.jabref.gui.collab.stringchange;
-import org.jabref.gui.collab.ExternalChange;
-import org.jabref.gui.collab.ExternalChangeResolverFactory;
+import org.jabref.gui.collab.DatabaseChange;
+import org.jabref.gui.collab.DatabaseChangeResolverFactory;
import org.jabref.gui.undo.NamedCompound;
import org.jabref.gui.undo.UndoableStringChange;
import org.jabref.logic.l10n.Localization;
@@ -11,14 +11,14 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-public final class BibTexStringChange extends ExternalChange {
+public final class BibTexStringChange extends DatabaseChange {
private static final Logger LOGGER = LoggerFactory.getLogger(BibTexStringChange.class);
private final BibtexString oldString;
private final BibtexString newString;
- public BibTexStringChange(BibtexString oldString, BibtexString newString, BibDatabaseContext databaseContext, ExternalChangeResolverFactory externalChangeResolverFactory) {
- super(databaseContext, externalChangeResolverFactory);
+ public BibTexStringChange(BibtexString oldString, BibtexString newString, BibDatabaseContext databaseContext, DatabaseChangeResolverFactory databaseChangeResolverFactory) {
+ super(databaseContext, databaseChangeResolverFactory);
this.oldString = oldString;
this.newString = newString;
diff --git a/src/main/java/org/jabref/gui/collab/stringchange/BibTexStringChangeDetailsView.java b/src/main/java/org/jabref/gui/collab/stringchange/BibTexStringChangeDetailsView.java
index f0775409f61..58fa700aa2c 100644
--- a/src/main/java/org/jabref/gui/collab/stringchange/BibTexStringChangeDetailsView.java
+++ b/src/main/java/org/jabref/gui/collab/stringchange/BibTexStringChangeDetailsView.java
@@ -3,10 +3,10 @@
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
-import org.jabref.gui.collab.ExternalChangeDetailsView;
+import org.jabref.gui.collab.DatabaseChangeDetailsView;
import org.jabref.logic.l10n.Localization;
-public final class BibTexStringChangeDetailsView extends ExternalChangeDetailsView {
+public final class BibTexStringChangeDetailsView extends DatabaseChangeDetailsView {
public BibTexStringChangeDetailsView(BibTexStringChange stringChange) {
VBox container = new VBox();
diff --git a/src/main/java/org/jabref/gui/collab/stringdelete/BibTexStringDelete.java b/src/main/java/org/jabref/gui/collab/stringdelete/BibTexStringDelete.java
index 1f7ca9e16ff..2f55cf7d331 100644
--- a/src/main/java/org/jabref/gui/collab/stringdelete/BibTexStringDelete.java
+++ b/src/main/java/org/jabref/gui/collab/stringdelete/BibTexStringDelete.java
@@ -1,7 +1,7 @@
package org.jabref.gui.collab.stringdelete;
-import org.jabref.gui.collab.ExternalChange;
-import org.jabref.gui.collab.ExternalChangeResolverFactory;
+import org.jabref.gui.collab.DatabaseChange;
+import org.jabref.gui.collab.DatabaseChangeResolverFactory;
import org.jabref.gui.undo.NamedCompound;
import org.jabref.gui.undo.UndoableRemoveString;
import org.jabref.logic.l10n.Localization;
@@ -11,13 +11,13 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-public final class BibTexStringDelete extends ExternalChange {
+public final class BibTexStringDelete extends DatabaseChange {
private static final Logger LOGGER = LoggerFactory.getLogger(BibTexStringDelete.class);
private final BibtexString deletedString;
- public BibTexStringDelete(BibtexString deletedString, BibDatabaseContext databaseContext, ExternalChangeResolverFactory externalChangeResolverFactory) {
- super(databaseContext, externalChangeResolverFactory);
+ public BibTexStringDelete(BibtexString deletedString, BibDatabaseContext databaseContext, DatabaseChangeResolverFactory databaseChangeResolverFactory) {
+ super(databaseContext, databaseChangeResolverFactory);
this.deletedString = deletedString;
setChangeName(Localization.lang("Deleted string: '%0'", deletedString.getName()));
}
diff --git a/src/main/java/org/jabref/gui/collab/stringdelete/BibTexStringDeleteDetailsView.java b/src/main/java/org/jabref/gui/collab/stringdelete/BibTexStringDeleteDetailsView.java
index d57af155a2b..2d35145b51d 100644
--- a/src/main/java/org/jabref/gui/collab/stringdelete/BibTexStringDeleteDetailsView.java
+++ b/src/main/java/org/jabref/gui/collab/stringdelete/BibTexStringDeleteDetailsView.java
@@ -3,10 +3,10 @@
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
-import org.jabref.gui.collab.ExternalChangeDetailsView;
+import org.jabref.gui.collab.DatabaseChangeDetailsView;
import org.jabref.logic.l10n.Localization;
-public final class BibTexStringDeleteDetailsView extends ExternalChangeDetailsView {
+public final class BibTexStringDeleteDetailsView extends DatabaseChangeDetailsView {
public BibTexStringDeleteDetailsView(BibTexStringDelete stringDelete) {
VBox container = new VBox();
diff --git a/src/main/java/org/jabref/gui/collab/stringrename/BibTexStringRename.java b/src/main/java/org/jabref/gui/collab/stringrename/BibTexStringRename.java
index 5c58cf11362..0236ebda467 100644
--- a/src/main/java/org/jabref/gui/collab/stringrename/BibTexStringRename.java
+++ b/src/main/java/org/jabref/gui/collab/stringrename/BibTexStringRename.java
@@ -1,7 +1,7 @@
package org.jabref.gui.collab.stringrename;
-import org.jabref.gui.collab.ExternalChange;
-import org.jabref.gui.collab.ExternalChangeResolverFactory;
+import org.jabref.gui.collab.DatabaseChange;
+import org.jabref.gui.collab.DatabaseChangeResolverFactory;
import org.jabref.gui.undo.NamedCompound;
import org.jabref.gui.undo.UndoableStringChange;
import org.jabref.logic.l10n.Localization;
@@ -11,14 +11,14 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-public final class BibTexStringRename extends ExternalChange {
+public final class BibTexStringRename extends DatabaseChange {
private static final Logger LOGGER = LoggerFactory.getLogger(BibTexStringRename.class);
private final BibtexString oldString;
private final BibtexString newString;
- public BibTexStringRename(BibtexString oldString, BibtexString newString, BibDatabaseContext databaseContext, ExternalChangeResolverFactory externalChangeResolverFactory) {
- super(databaseContext, externalChangeResolverFactory);
+ public BibTexStringRename(BibtexString oldString, BibtexString newString, BibDatabaseContext databaseContext, DatabaseChangeResolverFactory databaseChangeResolverFactory) {
+ super(databaseContext, databaseChangeResolverFactory);
this.oldString = oldString;
this.newString = newString;
diff --git a/src/main/java/org/jabref/gui/collab/stringrename/BibTexStringRenameDetailsView.java b/src/main/java/org/jabref/gui/collab/stringrename/BibTexStringRenameDetailsView.java
index 693feaf5def..b810a1c526e 100644
--- a/src/main/java/org/jabref/gui/collab/stringrename/BibTexStringRenameDetailsView.java
+++ b/src/main/java/org/jabref/gui/collab/stringrename/BibTexStringRenameDetailsView.java
@@ -2,9 +2,9 @@
import javafx.scene.control.Label;
-import org.jabref.gui.collab.ExternalChangeDetailsView;
+import org.jabref.gui.collab.DatabaseChangeDetailsView;
-public final class BibTexStringRenameDetailsView extends ExternalChangeDetailsView {
+public final class BibTexStringRenameDetailsView extends DatabaseChangeDetailsView {
public BibTexStringRenameDetailsView(BibTexStringRename stringRename) {
Label label = new Label(stringRename.getNewString().getName() + " : " + stringRename.getOldString().getContent());
diff --git a/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java b/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java
index c6f7cab9c5d..1d07ac2b982 100644
--- a/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java
+++ b/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java
@@ -1,41 +1,91 @@
package org.jabref.gui.dialogs;
+import java.io.IOException;
import java.nio.file.Path;
+import java.util.List;
+import java.util.Optional;
+
+import javafx.scene.control.ButtonType;
import org.jabref.gui.DialogService;
+import org.jabref.gui.Globals;
+import org.jabref.gui.backup.BackupResolverDialog;
+import org.jabref.gui.collab.DatabaseChange;
+import org.jabref.gui.collab.DatabaseChangeList;
+import org.jabref.gui.collab.DatabaseChangeResolverFactory;
+import org.jabref.gui.collab.DatabaseChangesResolverDialog;
import org.jabref.gui.util.DefaultTaskExecutor;
import org.jabref.logic.autosaveandbackup.BackupManager;
-import org.jabref.logic.l10n.Localization;
+import org.jabref.logic.importer.ImportFormatPreferences;
+import org.jabref.logic.importer.OpenDatabase;
+import org.jabref.logic.importer.ParserResult;
import org.jabref.logic.util.BackupFileType;
import org.jabref.logic.util.io.BackupFileUtil;
+import org.jabref.model.database.BibDatabaseContext;
+import org.jabref.model.util.DummyFileUpdateMonitor;
+import org.jabref.preferences.PreferencesService;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
* Stores all user dialogs related to {@link BackupManager}.
*/
public class BackupUIManager {
+ private static final Logger LOGGER = LoggerFactory.getLogger(BackupUIManager.class);
private BackupUIManager() {
}
- public static void showRestoreBackupDialog(DialogService dialogService, Path originalPath) {
- String content = new StringBuilder()
- .append(Localization.lang("A backup file for '%0' was found at '%1'.",
- originalPath.getFileName().toString(),
- // We need to determine the path "manually" as the path does not get passed through when a diff is detected.
- BackupFileUtil.getPathOfLatestExisingBackupFile(originalPath, BackupFileType.BACKUP).map(Path::toString).orElse(Localization.lang("File not found"))))
- .append("\n")
- .append(Localization.lang("This could indicate that JabRef did not shut down cleanly last time the file was used."))
- .append("\n\n")
- .append(Localization.lang("Do you want to recover the library from the backup file?"))
- .toString();
-
- boolean restoreClicked = DefaultTaskExecutor.runInJavaFXThread(() -> dialogService.showConfirmationDialogAndWait(
- Localization.lang("Backup found"), content,
- Localization.lang("Restore from backup"),
- Localization.lang("Ignore backup")));
-
- if (restoreClicked) {
- BackupManager.restoreBackup(originalPath);
+ public static Optional showRestoreBackupDialog(DialogService dialogService, Path originalPath, PreferencesService preferencesService) {
+ var actionOpt = showBackupResolverDialog(dialogService, originalPath);
+ return actionOpt.flatMap(action -> {
+ if (action == BackupResolverDialog.RESTORE_FROM_BACKUP) {
+ BackupManager.restoreBackup(originalPath);
+ return Optional.empty();
+ } else if (action == BackupResolverDialog.REVIEW_BACKUP) {
+ return showReviewBackupDialog(dialogService, originalPath, preferencesService);
+ }
+ return Optional.empty();
+ });
+ }
+
+ private static Optional showBackupResolverDialog(DialogService dialogService, Path originalPath) {
+ return DefaultTaskExecutor.runInJavaFXThread(() -> dialogService.showCustomDialogAndWait(new BackupResolverDialog(originalPath)));
+ }
+
+ private static Optional showReviewBackupDialog(DialogService dialogService, Path originalPath, PreferencesService preferencesService) {
+ try {
+ ImportFormatPreferences importFormatPreferences = Globals.prefs.getImportFormatPreferences();
+
+ // The database of the originalParserResult will be modified
+ ParserResult originalParserResult = OpenDatabase.loadDatabase(originalPath, importFormatPreferences, Globals.getFileUpdateMonitor());
+ // This will be modified by using the `DatabaseChangesResolverDialog`.
+ BibDatabaseContext originalDatabase = originalParserResult.getDatabaseContext();
+
+ Path backupPath = BackupFileUtil.getPathOfLatestExisingBackupFile(originalPath, BackupFileType.BACKUP).orElseThrow();
+ BibDatabaseContext backupDatabase = OpenDatabase.loadDatabase(backupPath, importFormatPreferences, new DummyFileUpdateMonitor()).getDatabaseContext();
+
+ DatabaseChangeResolverFactory changeResolverFactory = new DatabaseChangeResolverFactory(dialogService, originalDatabase, preferencesService.getBibEntryPreferences());
+
+ return DefaultTaskExecutor.runInJavaFXThread(() -> {
+ List changes = DatabaseChangeList.compareAndGetChanges(originalDatabase, backupDatabase, changeResolverFactory);
+ DatabaseChangesResolverDialog reviewBackupDialog = new DatabaseChangesResolverDialog(
+ changes,
+ originalDatabase, dialogService, Globals.stateManager, Globals.getThemeManager(), Globals.prefs, "Review Backup"
+ );
+ var allChangesResolved = dialogService.showCustomDialogAndWait(reviewBackupDialog);
+ if (allChangesResolved.isEmpty() || !allChangesResolved.get()) {
+ // In case not all changes are resolved, start from scratch
+ return showRestoreBackupDialog(dialogService, originalPath, preferencesService);
+ }
+
+ // This does NOT return the original ParserResult, but a modified version with all changes accepted or rejected
+ return Optional.of(originalParserResult);
+ });
+ } catch (IOException e) {
+ LOGGER.error("Error while loading backup or current database", e);
+ return Optional.empty();
}
}
}
diff --git a/src/main/java/org/jabref/gui/duplicationFinder/DuplicateResolverDialog.java b/src/main/java/org/jabref/gui/duplicationFinder/DuplicateResolverDialog.java
index 07644ca748a..205e4df75de 100644
--- a/src/main/java/org/jabref/gui/duplicationFinder/DuplicateResolverDialog.java
+++ b/src/main/java/org/jabref/gui/duplicationFinder/DuplicateResolverDialog.java
@@ -44,13 +44,21 @@ public enum DuplicateResolverResult {
private ThreeWayMergeView threeWayMerge;
private final DialogService dialogService;
private final ActionFactory actionFactory;
-
- public DuplicateResolverDialog(BibEntry one, BibEntry two, DuplicateResolverType type, BibDatabaseContext database, StateManager stateManager, DialogService dialogService, PreferencesService prefs) {
+ private final PreferencesService preferencesService;
+
+ public DuplicateResolverDialog(BibEntry one,
+ BibEntry two,
+ DuplicateResolverType type,
+ BibDatabaseContext database,
+ StateManager stateManager,
+ DialogService dialogService,
+ PreferencesService preferencesService) {
this.setTitle(Localization.lang("Possible duplicate entries"));
this.database = database;
this.stateManager = stateManager;
this.dialogService = dialogService;
- this.actionFactory = new ActionFactory(prefs.getKeyBindingRepository());
+ this.preferencesService = preferencesService;
+ this.actionFactory = new ActionFactory(preferencesService.getKeyBindingRepository());
init(one, two, type);
}
@@ -69,21 +77,21 @@ private void init(BibEntry one, BibEntry two, DuplicateResolverType type) {
first = new ButtonType(Localization.lang("Keep left"), ButtonData.LEFT);
second = new ButtonType(Localization.lang("Keep right"), ButtonData.LEFT);
both = new ButtonType(Localization.lang("Keep both"), ButtonData.LEFT);
- threeWayMerge = new ThreeWayMergeView(one, two);
+ threeWayMerge = new ThreeWayMergeView(one, two, preferencesService.getBibEntryPreferences());
}
case DUPLICATE_SEARCH_WITH_EXACT -> {
first = new ButtonType(Localization.lang("Keep left"), ButtonData.LEFT);
second = new ButtonType(Localization.lang("Keep right"), ButtonData.LEFT);
both = new ButtonType(Localization.lang("Keep both"), ButtonData.LEFT);
removeExactVisible = true;
- threeWayMerge = new ThreeWayMergeView(one, two);
+ threeWayMerge = new ThreeWayMergeView(one, two, preferencesService.getBibEntryPreferences());
}
case IMPORT_CHECK -> {
first = new ButtonType(Localization.lang("Keep old entry"), ButtonData.LEFT);
second = new ButtonType(Localization.lang("Keep from import"), ButtonData.LEFT);
both = new ButtonType(Localization.lang("Keep both"), ButtonData.LEFT);
threeWayMerge = new ThreeWayMergeView(one, two, Localization.lang("Old entry"),
- Localization.lang("From import"));
+ Localization.lang("From import"), preferencesService.getBibEntryPreferences());
}
default -> throw new IllegalStateException("Switch expression should be exhaustive");
}
diff --git a/src/main/java/org/jabref/gui/edit/ManageKeywordsDialog.java b/src/main/java/org/jabref/gui/edit/ManageKeywordsDialog.java
index 26fad7eb613..3ff79820697 100644
--- a/src/main/java/org/jabref/gui/edit/ManageKeywordsDialog.java
+++ b/src/main/java/org/jabref/gui/edit/ManageKeywordsDialog.java
@@ -49,7 +49,7 @@ public ManageKeywordsDialog(List entries) {
@FXML
public void initialize() {
- viewModel = new ManageKeywordsViewModel(preferences, entries);
+ viewModel = new ManageKeywordsViewModel(preferences.getBibEntryPreferences(), entries);
viewModel.displayTypeProperty().bind(
EasyBind.map(displayType.selectedToggleProperty(), toggle -> {
diff --git a/src/main/java/org/jabref/gui/edit/ManageKeywordsViewModel.java b/src/main/java/org/jabref/gui/edit/ManageKeywordsViewModel.java
index 2d4c48091ad..2730a20635b 100644
--- a/src/main/java/org/jabref/gui/edit/ManageKeywordsViewModel.java
+++ b/src/main/java/org/jabref/gui/edit/ManageKeywordsViewModel.java
@@ -15,7 +15,7 @@
import org.jabref.model.entry.BibEntry;
import org.jabref.model.entry.Keyword;
import org.jabref.model.entry.KeywordList;
-import org.jabref.preferences.PreferencesService;
+import org.jabref.preferences.BibEntryPreferences;
import com.tobiasdiez.easybind.EasyBind;
@@ -23,12 +23,12 @@ public class ManageKeywordsViewModel {
private final List entries;
private final KeywordList sortedKeywordsOfAllEntriesBeforeUpdateByUser = new KeywordList();
- private final PreferencesService preferences;
+ private final BibEntryPreferences bibEntryPreferences;
private final ObjectProperty displayType = new SimpleObjectProperty<>(ManageKeywordsDisplayType.CONTAINED_IN_ALL_ENTRIES);
private final ObservableList keywords;
- public ManageKeywordsViewModel(PreferencesService preferences, List entries) {
- this.preferences = preferences;
+ public ManageKeywordsViewModel(BibEntryPreferences bibEntryPreferences, List entries) {
+ this.bibEntryPreferences = bibEntryPreferences;
this.entries = entries;
this.keywords = FXCollections.observableArrayList();
@@ -46,22 +46,23 @@ public ObjectProperty displayTypeProperty() {
private void fillKeywordsList(ManageKeywordsDisplayType type) {
keywords.clear();
sortedKeywordsOfAllEntriesBeforeUpdateByUser.clear();
+ Character keywordSeparator = bibEntryPreferences.getKeywordSeparator();
if (type == ManageKeywordsDisplayType.CONTAINED_IN_ALL_ENTRIES) {
for (BibEntry entry : entries) {
- KeywordList separatedKeywords = entry.getKeywords(preferences.getKeywordDelimiter());
+ KeywordList separatedKeywords = entry.getKeywords(keywordSeparator);
sortedKeywordsOfAllEntriesBeforeUpdateByUser.addAll(separatedKeywords);
}
} else if (type == ManageKeywordsDisplayType.CONTAINED_IN_ANY_ENTRY) {
// all keywords from first entry have to be added
BibEntry firstEntry = entries.get(0);
- KeywordList separatedKeywords = firstEntry.getKeywords(preferences.getKeywordDelimiter());
+ KeywordList separatedKeywords = firstEntry.getKeywords(keywordSeparator);
sortedKeywordsOfAllEntriesBeforeUpdateByUser.addAll(separatedKeywords);
// for the remaining entries, intersection has to be used
// this approach ensures that one empty keyword list leads to an empty set of common keywords
for (BibEntry entry : entries) {
- separatedKeywords = entry.getKeywords(preferences.getKeywordDelimiter());
+ separatedKeywords = entry.getKeywords(keywordSeparator);
sortedKeywordsOfAllEntriesBeforeUpdateByUser.retainAll(separatedKeywords);
}
} else {
@@ -109,16 +110,18 @@ public void saveChanges() {
private NamedCompound updateKeywords(List entries, KeywordList keywordsToAdd,
KeywordList keywordsToRemove) {
+ Character keywordSeparator = bibEntryPreferences.getKeywordSeparator();
+
NamedCompound ce = new NamedCompound(Localization.lang("Update keywords"));
for (BibEntry entry : entries) {
- KeywordList keywords = entry.getKeywords(preferences.getKeywordDelimiter());
+ KeywordList keywords = entry.getKeywords(keywordSeparator);
// update keywords
keywords.removeAll(keywordsToRemove);
keywords.addAll(keywordsToAdd);
// put keywords back
- Optional change = entry.putKeywords(keywords, preferences.getKeywordDelimiter());
+ Optional change = entry.putKeywords(keywords, keywordSeparator);
change.ifPresent(fieldChange -> ce.addEdit(new UndoableFieldChange(fieldChange)));
}
ce.end();
diff --git a/src/main/java/org/jabref/gui/entryeditor/DeprecatedFieldsTab.java b/src/main/java/org/jabref/gui/entryeditor/DeprecatedFieldsTab.java
index d5bc20da9e6..91d876c41b5 100644
--- a/src/main/java/org/jabref/gui/entryeditor/DeprecatedFieldsTab.java
+++ b/src/main/java/org/jabref/gui/entryeditor/DeprecatedFieldsTab.java
@@ -19,12 +19,15 @@
import org.jabref.logic.l10n.Localization;
import org.jabref.logic.pdf.search.indexing.IndexingTaskManager;
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.preferences.PreferencesService;
+import com.tobiasdiez.easybind.EasyBind;
+
public class DeprecatedFieldsTab extends FieldsEditorTab {
private final BibEntryTypesManager entryTypesManager;
@@ -44,15 +47,22 @@ public DeprecatedFieldsTab(BibDatabaseContext databaseContext,
this.entryTypesManager = entryTypesManager;
setText(Localization.lang("Deprecated fields"));
- setTooltip(new Tooltip(Localization.lang("Show deprecated BibTeX fields")));
+ EasyBind.subscribe(preferences.getGeneralPreferences().showAdvancedHintsProperty(), advancedHints -> {
+ if (advancedHints) {
+ setTooltip(new Tooltip(Localization.lang("Shows fields having a successor in biblatex.\nFor instance, the publication month should be part of the date field.\nUse the Cleanup Entries functionality to convert the entry to biblatex.")));
+ } else {
+ setTooltip(new Tooltip(Localization.lang("Shows fields having a successor in biblatex.")));
+ }
+ });
setGraphic(IconTheme.JabRefIcons.OPTIONAL.getGraphicNode());
}
@Override
protected Set determineFieldsToShow(BibEntry entry) {
- Optional entryType = entryTypesManager.enrich(entry.getType(), databaseContext.getMode());
+ BibDatabaseMode mode = databaseContext.getMode();
+ Optional entryType = entryTypesManager.enrich(entry.getType(), mode);
if (entryType.isPresent()) {
- return entryType.get().getDeprecatedFields().stream().filter(field -> !entry.getField(field).isEmpty()).collect(Collectors.toSet());
+ return entryType.get().getDeprecatedFields(mode).stream().filter(field -> !entry.getField(field).isEmpty()).collect(Collectors.toSet());
} else {
// Entry type unknown -> treat all fields as required
return Collections.emptySet();
diff --git a/src/main/java/org/jabref/gui/entryeditor/EntryEditor.css b/src/main/java/org/jabref/gui/entryeditor/EntryEditor.css
index e53c688a081..4b2befd4dd3 100644
--- a/src/main/java/org/jabref/gui/entryeditor/EntryEditor.css
+++ b/src/main/java/org/jabref/gui/entryeditor/EntryEditor.css
@@ -122,7 +122,11 @@
}
#bibtexSourceCodeArea .search {
- -fx-fill: red;
+ -rtfx-background-color: #ffff00;
+ -fx-fill: #7800A9 ;
+ -fx-font-size: 1.2em;
+ -fx-font-weight: bolder;
+
}
#citationsPane {
diff --git a/src/main/java/org/jabref/gui/entryeditor/EntryEditor.java b/src/main/java/org/jabref/gui/entryeditor/EntryEditor.java
index b0c8111f570..af47f489e55 100644
--- a/src/main/java/org/jabref/gui/entryeditor/EntryEditor.java
+++ b/src/main/java/org/jabref/gui/entryeditor/EntryEditor.java
@@ -395,7 +395,7 @@ private void setupToolBar() {
}
private void fetchAndMerge(EntryBasedFetcher fetcher) {
- new FetchAndMergeEntry(libraryTab, taskExecutor).fetchAndMerge(entry, fetcher);
+ new FetchAndMergeEntry(libraryTab, taskExecutor, preferencesService, dialogService).fetchAndMerge(entry, fetcher);
}
public void setFocusToField(Field field) {
diff --git a/src/main/java/org/jabref/gui/entryeditor/OptionalFieldsTabBase.java b/src/main/java/org/jabref/gui/entryeditor/OptionalFieldsTabBase.java
index af1a5f9f0b6..edb22834ff2 100644
--- a/src/main/java/org/jabref/gui/entryeditor/OptionalFieldsTabBase.java
+++ b/src/main/java/org/jabref/gui/entryeditor/OptionalFieldsTabBase.java
@@ -18,6 +18,7 @@
import org.jabref.logic.l10n.Localization;
import org.jabref.logic.pdf.search.indexing.IndexingTaskManager;
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;
@@ -61,12 +62,13 @@ public OptionalFieldsTabBase(String title,
@Override
protected Set determineFieldsToShow(BibEntry entry) {
- Optional entryType = entryTypesManager.enrich(entry.getType(), databaseContext.getMode());
+ BibDatabaseMode mode = databaseContext.getMode();
+ Optional entryType = entryTypesManager.enrich(entry.getType(), mode);
if (entryType.isPresent()) {
if (isPrimaryOptionalFields) {
return entryType.get().getPrimaryOptionalFields();
} else {
- return entryType.get().getSecondaryOptionalNotDeprecatedFields();
+ return entryType.get().getSecondaryOptionalNotDeprecatedFields(mode);
}
} else {
// Entry type unknown -> treat all fields as required
diff --git a/src/main/java/org/jabref/gui/entryeditor/OtherFieldsTab.java b/src/main/java/org/jabref/gui/entryeditor/OtherFieldsTab.java
index 0fe1281c077..50a8066d2cd 100644
--- a/src/main/java/org/jabref/gui/entryeditor/OtherFieldsTab.java
+++ b/src/main/java/org/jabref/gui/entryeditor/OtherFieldsTab.java
@@ -21,6 +21,7 @@
import org.jabref.logic.l10n.Localization;
import org.jabref.logic.pdf.search.indexing.IndexingTaskManager;
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;
@@ -67,12 +68,13 @@ public OtherFieldsTab(BibDatabaseContext databaseContext,
@Override
protected Set determineFieldsToShow(BibEntry entry) {
- Optional entryType = entryTypesManager.enrich(entry.getType(), databaseContext.getMode());
+ BibDatabaseMode mode = databaseContext.getMode();
+ Optional entryType = entryTypesManager.enrich(entry.getType(), mode);
if (entryType.isPresent()) {
Set allKnownFields = entryType.get().getAllFields();
Set otherFields = entry.getFields().stream().filter(field -> !allKnownFields.contains(field)).collect(Collectors.toCollection(LinkedHashSet::new));
- otherFields.removeAll(entryType.get().getDeprecatedFields());
+ otherFields.removeAll(entryType.get().getDeprecatedFields(mode));
otherFields.removeAll(entryType.get().getOptionalFields().stream().map(BibField::getField).collect(Collectors.toSet()));
otherFields.remove(InternalField.KEY_FIELD);
otherFields.removeAll(customTabFieldNames);
diff --git a/src/main/java/org/jabref/gui/externalfiles/ImportHandler.java b/src/main/java/org/jabref/gui/externalfiles/ImportHandler.java
index 47a4c7253b4..691634bd129 100644
--- a/src/main/java/org/jabref/gui/externalfiles/ImportHandler.java
+++ b/src/main/java/org/jabref/gui/externalfiles/ImportHandler.java
@@ -29,7 +29,7 @@
import org.jabref.logic.importer.ImportFormatReader;
import org.jabref.logic.importer.ImportFormatReader.UnknownFormatImport;
import org.jabref.logic.importer.ParseException;
-import org.jabref.logic.importer.fetcher.ArXiv;
+import org.jabref.logic.importer.fetcher.ArXivFetcher;
import org.jabref.logic.importer.fetcher.DoiFetcher;
import org.jabref.logic.importer.fileformat.BibtexParser;
import org.jabref.logic.l10n.Localization;
@@ -119,10 +119,10 @@ protected List call() {
if (!pdfEntriesInFile.isEmpty()) {
entriesToAdd.addAll(pdfEntriesInFile);
- addResultToList(file, true, Localization.lang("Importing using extracted PDF data"));
+ addResultToList(file, true, Localization.lang("File was successfully imported as a new entry"));
} else {
entriesToAdd.add(createEmptyEntryWithLink(file));
- addResultToList(file, false, Localization.lang("No metadata found. Creating empty entry with file link"));
+ addResultToList(file, false, Localization.lang("No metadata was found. An empty entry was created with file link"));
}
} else if (FileUtil.isBibFile(file)) {
var bibtexParserResult = contentImporter.importFromBibFile(file, fileUpdateMonitor);
@@ -131,10 +131,10 @@ protected List call() {
}
entriesToAdd.addAll(bibtexParserResult.getDatabaseContext().getEntries());
- addResultToList(file, false, Localization.lang("Importing bib entry"));
+ addResultToList(file, true, Localization.lang("Bib entry was successfully imported"));
} else {
entriesToAdd.add(createEmptyEntryWithLink(file));
- addResultToList(file, false, Localization.lang("No BibTeX data found. Creating empty entry with file link"));
+ addResultToList(file, false, Localization.lang("No BibTeX data was found. An empty entry was created with file link"));
}
} catch (IOException ex) {
LOGGER.error("Error importing", ex);
@@ -303,7 +303,7 @@ private List fetchByDOI(DOI doi) throws FetcherException {
private List fetchByArXiv(ArXivIdentifier arXivIdentifier) throws FetcherException {
LOGGER.info("Found arxiv identifier in clipboard");
- Optional entry = new ArXiv(preferencesService.getImportFormatPreferences()).performSearchById(arXivIdentifier.getNormalizedWithoutVersion());
+ Optional entry = new ArXivFetcher(preferencesService.getImportFormatPreferences()).performSearchById(arXivIdentifier.getNormalizedWithoutVersion());
return OptionalUtil.toList(entry);
}
}
diff --git a/src/main/java/org/jabref/gui/externalfiles/UnlinkedFilesDialogView.java b/src/main/java/org/jabref/gui/externalfiles/UnlinkedFilesDialogView.java
index 7202751ef5e..13ddbe4bd34 100644
--- a/src/main/java/org/jabref/gui/externalfiles/UnlinkedFilesDialogView.java
+++ b/src/main/java/org/jabref/gui/externalfiles/UnlinkedFilesDialogView.java
@@ -1,5 +1,7 @@
package org.jabref.gui.externalfiles;
+import java.nio.file.Path;
+
import javax.swing.undo.UndoManager;
import javafx.application.Platform;
@@ -42,6 +44,7 @@
import org.jabref.gui.util.ViewModelTreeCellFactory;
import org.jabref.logic.importer.ImportFormatReader;
import org.jabref.logic.l10n.Localization;
+import org.jabref.model.database.BibDatabaseContext;
import org.jabref.model.util.FileUpdateMonitor;
import org.jabref.preferences.PreferencesService;
@@ -86,6 +89,8 @@ public class UnlinkedFilesDialogView extends BaseDialog {
private final ControlsFxVisualizer validationVisualizer;
private UnlinkedFilesDialogViewModel viewModel;
+ private BibDatabaseContext bibDatabaseContext;
+
public UnlinkedFilesDialogView() {
this.validationVisualizer = new ControlsFxVisualizer();
@@ -109,6 +114,8 @@ public UnlinkedFilesDialogView() {
private void initialize() {
viewModel = new UnlinkedFilesDialogViewModel(dialogService, undoManager, fileUpdateMonitor, preferencesService, stateManager, taskExecutor, importFormatReader);
+ this.bibDatabaseContext = stateManager.getActiveDatabase().orElseThrow(() -> new NullPointerException("No active library"));
+
progressDisplay.progressProperty().bind(viewModel.progressValueProperty());
progressText.textProperty().bind(viewModel.progressTextProperty());
progressPane.managedProperty().bind(viewModel.taskActiveProperty());
@@ -160,6 +167,8 @@ private void initDirectorySelection() {
fileSortCombo.setItems(viewModel.getSorters());
fileSortCombo.valueProperty().bindBidirectional(viewModel.selectedSortProperty());
fileSortCombo.getSelectionModel().selectFirst();
+
+ directoryPathField.setText(bibDatabaseContext.getFirstExistingFileDir(preferencesService.getFilePreferences()).map(Path::toString).orElse(""));
}
private void initUnlinkedFilesList() {
diff --git a/src/main/java/org/jabref/gui/externalfiles/UnlinkedFilesDialogViewModel.java b/src/main/java/org/jabref/gui/externalfiles/UnlinkedFilesDialogViewModel.java
index 2129e473c4a..92fc8904b5e 100644
--- a/src/main/java/org/jabref/gui/externalfiles/UnlinkedFilesDialogViewModel.java
+++ b/src/main/java/org/jabref/gui/externalfiles/UnlinkedFilesDialogViewModel.java
@@ -102,7 +102,8 @@ public UnlinkedFilesDialogViewModel(DialogService dialogService,
this.fileFilterList = FXCollections.observableArrayList(
new FileExtensionViewModel(StandardFileType.ANY_FILE, preferences.getFilePreferences()),
- new FileExtensionViewModel(StandardFileType.BIBTEX_DB, preferences.getFilePreferences()),
+ new FileExtensionViewModel(StandardFileType.HTML, preferences.getFilePreferences()),
+ new FileExtensionViewModel(StandardFileType.MARKDOWN, preferences.getFilePreferences()),
new FileExtensionViewModel(StandardFileType.PDF, preferences.getFilePreferences()));
this.dateFilterList = FXCollections.observableArrayList(DateRange.values());
diff --git a/src/main/java/org/jabref/gui/externalfiletype/StandardExternalFileType.java b/src/main/java/org/jabref/gui/externalfiletype/StandardExternalFileType.java
index 92718e09a7a..fc12e9670e8 100644
--- a/src/main/java/org/jabref/gui/externalfiletype/StandardExternalFileType.java
+++ b/src/main/java/org/jabref/gui/externalfiletype/StandardExternalFileType.java
@@ -28,8 +28,8 @@ public enum StandardExternalFileType implements ExternalFileType {
TIFF(Localization.lang("%0 image", "TIFF"), "tiff", "image/tiff", "gimp", "picture", IconTheme.JabRefIcons.PICTURE),
URL("URL", "html", "text/html", "firefox", "www", IconTheme.JabRefIcons.WWW),
MHT("MHT", "mht", "multipart/related", "firefox", "www", IconTheme.JabRefIcons.WWW),
- ePUB("ePUB", "epub", "application/epub+zip", "firefox", "www", IconTheme.JabRefIcons.WWW);
-
+ ePUB("ePUB", "epub", "application/epub+zip", "firefox", "www", IconTheme.JabRefIcons.WWW),
+ MARKDOWN("Markdown", "md", "text/markdown", "emacs", "emacs", IconTheme.JabRefIcons.FILE_TEXT);
private final String name;
private final String extension;
private final String mimeType;
diff --git a/src/main/java/org/jabref/gui/fieldeditors/DateEditor.java b/src/main/java/org/jabref/gui/fieldeditors/DateEditor.java
index cd8ad8785ca..8c25cdc1a97 100644
--- a/src/main/java/org/jabref/gui/fieldeditors/DateEditor.java
+++ b/src/main/java/org/jabref/gui/fieldeditors/DateEditor.java
@@ -11,6 +11,7 @@
import org.jabref.logic.integrity.FieldCheckers;
import org.jabref.model.entry.BibEntry;
import org.jabref.model.entry.field.Field;
+import org.jabref.preferences.PreferencesService;
import com.airhacks.afterburner.views.ViewLoader;
@@ -19,7 +20,7 @@ public class DateEditor extends HBox implements FieldEditorFX {
@FXML private DateEditorViewModel viewModel;
@FXML private TemporalAccessorPicker datePicker;
- public DateEditor(Field field, DateTimeFormatter dateFormatter, SuggestionProvider> suggestionProvider, FieldCheckers fieldCheckers) {
+ public DateEditor(Field field, DateTimeFormatter dateFormatter, SuggestionProvider> suggestionProvider, FieldCheckers fieldCheckers, PreferencesService preferences) {
this.viewModel = new DateEditorViewModel(field, suggestionProvider, dateFormatter, fieldCheckers);
ViewLoader.view(this)
@@ -28,6 +29,8 @@ public DateEditor(Field field, DateTimeFormatter dateFormatter, SuggestionProvid
datePicker.setStringConverter(viewModel.getDateToStringConverter());
datePicker.getEditor().textProperty().bindBidirectional(viewModel.textProperty());
+
+ new EditorValidator(preferences).configureValidation(viewModel.getFieldValidator().getValidationStatus(), datePicker.getEditor());
}
public DateEditorViewModel getViewModel() {
diff --git a/src/main/java/org/jabref/gui/fieldeditors/DateEditorViewModel.java b/src/main/java/org/jabref/gui/fieldeditors/DateEditorViewModel.java
index de7bdeb7583..61992127eb8 100644
--- a/src/main/java/org/jabref/gui/fieldeditors/DateEditorViewModel.java
+++ b/src/main/java/org/jabref/gui/fieldeditors/DateEditorViewModel.java
@@ -21,7 +21,7 @@ public DateEditorViewModel(Field field, SuggestionProvider> suggestionProvider
}
public StringConverter getDateToStringConverter() {
- return new StringConverter() {
+ return new StringConverter<>() {
@Override
public String toString(TemporalAccessor date) {
if (date != null) {
diff --git a/src/main/java/org/jabref/gui/fieldeditors/FieldEditors.java b/src/main/java/org/jabref/gui/fieldeditors/FieldEditors.java
index e038c6ba05f..6db3dac9412 100644
--- a/src/main/java/org/jabref/gui/fieldeditors/FieldEditors.java
+++ b/src/main/java/org/jabref/gui/fieldeditors/FieldEditors.java
@@ -55,9 +55,9 @@ public static FieldEditorFX getForField(final Field field,
boolean isMultiLine = FieldFactory.isMultiLineField(field, preferences.getFieldContentParserPreferences().getNonWrappableFields());
if (preferences.getTimestampPreferences().getTimestampField().equals(field)) {
- return new DateEditor(field, DateTimeFormatter.ofPattern(preferences.getTimestampPreferences().getTimestampFormat()), suggestionProvider, fieldCheckers);
+ return new DateEditor(field, DateTimeFormatter.ofPattern(preferences.getTimestampPreferences().getTimestampFormat()), suggestionProvider, fieldCheckers, preferences);
} else if (fieldProperties.contains(FieldProperty.DATE)) {
- return new DateEditor(field, DateTimeFormatter.ofPattern("[uuuu][-MM][-dd]"), suggestionProvider, fieldCheckers);
+ return new DateEditor(field, DateTimeFormatter.ofPattern("[uuuu][-MM][-dd]"), suggestionProvider, fieldCheckers, preferences);
} else if (fieldProperties.contains(FieldProperty.EXTERNAL)) {
return new UrlEditor(field, dialogService, suggestionProvider, fieldCheckers, preferences);
} else if (fieldProperties.contains(FieldProperty.JOURNAL_NAME)) {
diff --git a/src/main/java/org/jabref/gui/fieldeditors/IdentifierEditorViewModel.java b/src/main/java/org/jabref/gui/fieldeditors/IdentifierEditorViewModel.java
index 5d69547367d..41cbb9697d9 100644
--- a/src/main/java/org/jabref/gui/fieldeditors/IdentifierEditorViewModel.java
+++ b/src/main/java/org/jabref/gui/fieldeditors/IdentifierEditorViewModel.java
@@ -110,7 +110,7 @@ public BooleanProperty identifierLookupInProgressProperty() {
}
public void fetchInformationByIdentifier(BibEntry entry) {
- new FetchAndMergeEntry(JabRefGUI.getMainFrame().getCurrentLibraryTab(), taskExecutor).fetchAndMerge(entry, field);
+ new FetchAndMergeEntry(JabRefGUI.getMainFrame().getCurrentLibraryTab(), taskExecutor, preferences, dialogService).fetchAndMerge(entry, field);
}
public void lookupIdentifier(BibEntry entry) {
diff --git a/src/main/java/org/jabref/gui/fieldeditors/JournalEditorViewModel.java b/src/main/java/org/jabref/gui/fieldeditors/JournalEditorViewModel.java
index aeafdde2992..42effd401e4 100644
--- a/src/main/java/org/jabref/gui/fieldeditors/JournalEditorViewModel.java
+++ b/src/main/java/org/jabref/gui/fieldeditors/JournalEditorViewModel.java
@@ -1,7 +1,5 @@
package org.jabref.gui.fieldeditors;
-import java.util.Optional;
-
import org.jabref.gui.autocompleter.SuggestionProvider;
import org.jabref.logic.integrity.FieldCheckers;
import org.jabref.logic.journals.JournalAbbreviationRepository;
@@ -13,7 +11,6 @@ public class JournalEditorViewModel extends AbstractEditorViewModel {
public JournalEditorViewModel(Field field, SuggestionProvider> suggestionProvider, JournalAbbreviationRepository journalAbbreviationRepository, FieldCheckers fieldCheckers) {
super(field, suggestionProvider, fieldCheckers);
-
this.journalAbbreviationRepository = journalAbbreviationRepository;
}
@@ -25,14 +22,10 @@ public void toggleAbbreviation() {
// Ignore brackets when matching abbreviations.
final String name = StringUtil.ignoreCurlyBracket(text.get());
- if (journalAbbreviationRepository.isKnownName(name)) {
- Optional nextAbbreviation = journalAbbreviationRepository.getNextAbbreviation(name);
-
- if (nextAbbreviation.isPresent()) {
- text.set(nextAbbreviation.get());
- // TODO: Add undo
- // panel.getUndoManager().addEdit(new UndoableFieldChange(entry, editor.getName(), text, nextAbbreviation));
- }
- }
+ journalAbbreviationRepository.getNextAbbreviation(name).ifPresent(nextAbbreviation -> {
+ text.set(nextAbbreviation);
+ // TODO: Add undo
+ // panel.getUndoManager().addEdit(new UndoableFieldChange(entry, editor.getName(), text, nextAbbreviation));
+ });
}
}
diff --git a/src/main/java/org/jabref/gui/fieldeditors/LinkedFileViewModel.java b/src/main/java/org/jabref/gui/fieldeditors/LinkedFileViewModel.java
index 1b0c9f6da8f..e02143f4554 100644
--- a/src/main/java/org/jabref/gui/fieldeditors/LinkedFileViewModel.java
+++ b/src/main/java/org/jabref/gui/fieldeditors/LinkedFileViewModel.java
@@ -392,8 +392,8 @@ public boolean delete() {
ButtonType removeFromEntry = new ButtonType(Localization.lang("Remove from entry"), ButtonData.YES);
ButtonType deleteFromEntry = new ButtonType(Localization.lang("Delete from disk"));
Optional buttonType = dialogService.showCustomButtonDialogAndWait(AlertType.INFORMATION,
- Localization.lang("Delete '%0'", file.get().toString()),
- Localization.lang("Delete the selected file permanently from disk, or just remove the file from the entry? Pressing Delete will delete the file permanently from disk."),
+ Localization.lang("Delete '%0'", file.get().getFileName().toString()),
+ Localization.lang("Delete '%0' permanently from disk, or just remove the file from the entry? Pressing Delete will delete the file permanently from disk.", file.get().toString()),
removeFromEntry, deleteFromEntry, ButtonType.CANCEL);
if (buttonType.isPresent()) {
diff --git a/src/main/java/org/jabref/gui/fieldeditors/LinkedFilesEditorViewModel.java b/src/main/java/org/jabref/gui/fieldeditors/LinkedFilesEditorViewModel.java
index 44a3f15d538..94e292babd6 100644
--- a/src/main/java/org/jabref/gui/fieldeditors/LinkedFilesEditorViewModel.java
+++ b/src/main/java/org/jabref/gui/fieldeditors/LinkedFilesEditorViewModel.java
@@ -16,6 +16,7 @@
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
+import org.jabref.gui.ClipBoardManager;
import org.jabref.gui.DialogService;
import org.jabref.gui.autocompleter.SuggestionProvider;
import org.jabref.gui.externalfiles.AutoSetFileLinksUtil;
@@ -214,8 +215,15 @@ public void fetchFulltext() {
}
public void addFromURL() {
- Optional urlText = dialogService.showInputDialogAndWait(
- Localization.lang("Download file"), Localization.lang("Enter URL to download"));
+ String clipText = ClipBoardManager.getContents();
+ Optional urlText;
+ if (clipText.startsWith("http://") || clipText.startsWith("https://") || clipText.startsWith("ftp://")) {
+ urlText = dialogService.showInputDialogWithDefaultAndWait(
+ Localization.lang("Download file"), Localization.lang("Enter URL to download"), clipText);
+ } else {
+ urlText = dialogService.showInputDialogAndWait(
+ Localization.lang("Download file"), Localization.lang("Enter URL to download"));
+ }
if (urlText.isPresent()) {
try {
URL url = new URL(urlText.get());
diff --git a/src/main/java/org/jabref/gui/groups/GroupDialogViewModel.java b/src/main/java/org/jabref/gui/groups/GroupDialogViewModel.java
index 1d2b0bc5358..ffe1e70a965 100644
--- a/src/main/java/org/jabref/gui/groups/GroupDialogViewModel.java
+++ b/src/main/java/org/jabref/gui/groups/GroupDialogViewModel.java
@@ -111,7 +111,11 @@ public class GroupDialogViewModel {
private final AbstractGroup editedGroup;
private final GroupDialogHeader groupDialogHeader;
- public GroupDialogViewModel(DialogService dialogService, BibDatabaseContext currentDatabase, PreferencesService preferencesService, AbstractGroup editedGroup, GroupDialogHeader groupDialogHeader) {
+ public GroupDialogViewModel(DialogService dialogService,
+ BibDatabaseContext currentDatabase,
+ PreferencesService preferencesService,
+ AbstractGroup editedGroup,
+ GroupDialogHeader groupDialogHeader) {
this.dialogService = dialogService;
this.preferencesService = preferencesService;
this.currentDatabase = currentDatabase;
@@ -130,11 +134,11 @@ private void setupValidation() {
nameContainsDelimiterValidator = new FunctionBasedValidator<>(
nameProperty,
- name -> !name.contains(Character.toString(preferencesService.getKeywordDelimiter())),
+ name -> !name.contains(Character.toString(preferencesService.getBibEntryPreferences().getKeywordSeparator())),
ValidationMessage.warning(
Localization.lang(
"The group name contains the keyword separator \"%0\" and thus probably does not work as expected.",
- Character.toString(preferencesService.getKeywordDelimiter())
+ Character.toString(preferencesService.getBibEntryPreferences().getKeywordSeparator())
)));
sameNameValidator = new FunctionBasedValidator<>(
@@ -301,7 +305,7 @@ public AbstractGroup resultConverter(ButtonType button) {
resultingGroup = new ExplicitGroup(
groupName,
groupHierarchySelectedProperty.getValue(),
- preferencesService.getKeywordDelimiter());
+ preferencesService.getBibEntryPreferences().getKeywordSeparator());
} else if (typeKeywordsProperty.getValue()) {
if (keywordGroupRegexProperty.getValue()) {
resultingGroup = new RegexKeywordGroup(
@@ -317,7 +321,7 @@ public AbstractGroup resultConverter(ButtonType button) {
FieldFactory.parseField(keywordGroupSearchFieldProperty.getValue().trim()),
keywordGroupSearchTermProperty.getValue().trim(),
keywordGroupCaseSensitiveProperty.getValue(),
- preferencesService.getKeywordDelimiter(),
+ preferencesService.getBibEntryPreferences().getKeywordSeparator(),
false);
}
} else if (typeSearchProperty.getValue()) {
diff --git a/src/main/java/org/jabref/gui/groups/GroupTreeView.java b/src/main/java/org/jabref/gui/groups/GroupTreeView.java
index fd3ab5c7614..dabccd9204b 100644
--- a/src/main/java/org/jabref/gui/groups/GroupTreeView.java
+++ b/src/main/java/org/jabref/gui/groups/GroupTreeView.java
@@ -320,14 +320,14 @@ private void initialize() {
Dragboard dragboard = event.getDragboard();
boolean success = false;
- if (dragboard.hasContent(DragAndDropDataFormats.GROUP)) {
+ if (dragboard.hasContent(DragAndDropDataFormats.GROUP) && viewModel.canAddGroupsIn(row.getItem())) {
List pathToSources = (List) dragboard.getContent(DragAndDropDataFormats.GROUP);
List changedGroups = new LinkedList<>();
for (String pathToSource : pathToSources) {
Optional source = viewModel
.rootGroupProperty().get()
.getChildByPath(pathToSource);
- if (source.isPresent()) {
+ if (source.isPresent() && viewModel.canBeDragged(source.get())) {
source.get().draggedOn(row.getItem(), ControlHelper.getDroppingMouseLocation(row, event));
changedGroups.add(source.get());
success = true;
@@ -335,6 +335,9 @@ private void initialize() {
}
groupTree.getSelectionModel().clearSelection();
changedGroups.forEach(value -> selectNode(value, true));
+ if (success) {
+ viewModel.writeGroupChangesToMetaData();
+ }
}
if (localDragboard.hasBibEntries()) {
@@ -403,7 +406,11 @@ private Optional> getTreeItemByValue(TreeItem viewModel.removeGroupNoSubgroups(group));
MenuItem editGroup = new MenuItem(Localization.lang("Edit group"));
editGroup.setOnAction(event -> {
@@ -425,11 +432,9 @@ private ContextMenu createContextMenuForGroup(GroupNodeViewModel group) {
});
MenuItem removeSubgroups = new MenuItem(Localization.lang("Remove subgroups"));
- removeSubgroups.setOnAction(event -> {
- viewModel.removeSubgroups(group);
- });
+ removeSubgroups.setOnAction(event -> viewModel.removeSubgroups(group));
- MenuItem sortSubgroups = new MenuItem(Localization.lang("Sort subgroups"));
+ MenuItem sortSubgroups = new MenuItem(Localization.lang("Sort subgroups A-Z"));
sortSubgroups.setOnAction(event -> viewModel.sortAlphabeticallyRecursive(group.getGroupNode()));
MenuItem addEntries = new MenuItem(Localization.lang("Add selected entries to this group"));
@@ -438,17 +443,35 @@ private ContextMenu createContextMenuForGroup(GroupNodeViewModel group) {
MenuItem removeEntries = new MenuItem(Localization.lang("Remove selected entries from this group"));
removeEntries.setOnAction(event -> viewModel.removeSelectedEntries(group));
- menu.getItems().add(editGroup);
- removeGroup.getItems().add(removeGroupKeepSubgroups);
- removeGroup.getItems().add(removeGroupAndSubgroups);
- menu.getItems().add(removeGroup);
- menu.getItems().add(new SeparatorMenuItem());
- menu.getItems().add(addSubgroup);
- menu.getItems().add(removeSubgroups);
- menu.getItems().add(sortSubgroups);
- menu.getItems().add(new SeparatorMenuItem());
- menu.getItems().add(addEntries);
- menu.getItems().add(removeEntries);
+ menu.setOnShown(event -> {
+ menu.getItems().clear();
+ if (viewModel.isEditable(group)) {
+ menu.getItems().add(editGroup);
+ if (group.getChildren().size() > 0 && viewModel.canAddGroupsIn(group)) {
+ menu.getItems().add(removeGroupWithSubgroups);
+ menu.getItems().add(new SeparatorMenuItem());
+ menu.getItems().add(addSubgroup);
+ menu.getItems().add(removeSubgroups);
+ menu.getItems().add(sortSubgroups);
+ } else {
+ menu.getItems().add(removeGroupNoSubgroups);
+ if (viewModel.canAddGroupsIn(group)) {
+ menu.getItems().add(new SeparatorMenuItem());
+ menu.getItems().add(addSubgroup);
+ }
+ }
+ }
+
+ if (viewModel.canAddEntriesIn(group)) {
+ menu.getItems().add(new SeparatorMenuItem());
+ menu.getItems().add(addEntries);
+ menu.getItems().add(removeEntries);
+ }
+ });
+
+ menu.getItems().add(new Menu());
+ removeGroupWithSubgroups.getItems().add(removeGroupKeepSubgroups);
+ removeGroupWithSubgroups.getItems().add(removeGroupAndSubgroups);
// TODO: Disable some actions under certain conditions
// if (group.canBeEdited()) {
diff --git a/src/main/java/org/jabref/gui/groups/GroupTreeViewModel.java b/src/main/java/org/jabref/gui/groups/GroupTreeViewModel.java
index 8cf74178629..9b401dc6ab9 100644
--- a/src/main/java/org/jabref/gui/groups/GroupTreeViewModel.java
+++ b/src/main/java/org/jabref/gui/groups/GroupTreeViewModel.java
@@ -30,10 +30,13 @@
import org.jabref.model.database.BibDatabaseContext;
import org.jabref.model.entry.BibEntry;
import org.jabref.model.groups.AbstractGroup;
+import org.jabref.model.groups.AllEntriesGroup;
import org.jabref.model.groups.AutomaticKeywordGroup;
import org.jabref.model.groups.AutomaticPersonsGroup;
import org.jabref.model.groups.ExplicitGroup;
import org.jabref.model.groups.GroupTreeNode;
+import org.jabref.model.groups.KeywordGroup;
+import org.jabref.model.groups.LastNameGroup;
import org.jabref.model.groups.RegexKeywordGroup;
import org.jabref.model.groups.SearchGroup;
import org.jabref.model.groups.TexGroup;
@@ -184,7 +187,7 @@ public void addNewSubgroup(GroupNodeViewModel parent, GroupDialogHeader groupDia
});
}
- private void writeGroupChangesToMetaData() {
+ public void writeGroupChangesToMetaData() {
currentDatabase.ifPresent(database -> database.getMetaData().setGroups(rootGroup.get().getGroupNode()));
}
@@ -471,6 +474,43 @@ public void removeGroupAndSubgroups(GroupNodeViewModel group) {
}
}
+ /**
+ * Removes the specified group (after asking for confirmation).
+ */
+ public void removeGroupNoSubgroups(GroupNodeViewModel group) {
+ boolean confirmed;
+ if (selectedGroups.size() <= 1) {
+ confirmed = dialogService.showConfirmationDialogAndWait(
+ Localization.lang("Remove group"),
+ Localization.lang("Remove group \"%0\"?", group.getDisplayName()),
+ Localization.lang("Remove"));
+ } else {
+ confirmed = dialogService.showConfirmationDialogAndWait(
+ Localization.lang("Remove groups and subgroups"),
+ Localization.lang("Remove all selected groups and their subgroups?"),
+ Localization.lang("Remove all"));
+ }
+
+ if (confirmed) {
+ // TODO: Add undo
+ // final UndoableAddOrRemoveGroup undo = new UndoableAddOrRemoveGroup(groupsRoot, node, UndoableAddOrRemoveGroup.REMOVE_NODE_WITHOUT_CHILDREN);
+ // panel.getUndoManager().addEdit(undo);
+
+ ArrayList selectedGroupNodes = new ArrayList<>(selectedGroups);
+ selectedGroupNodes.forEach(eachNode -> {
+ removeGroupsAndSubGroupsFromEntries(eachNode);
+ eachNode.getGroupNode().removeFromParent();
+ });
+
+ if (selectedGroupNodes.size() > 1) {
+ dialogService.notify(Localization.lang("Removed all selected groups."));
+ } else {
+ dialogService.notify(Localization.lang("Removed group \"%0\".", group.getDisplayName()));
+ }
+ writeGroupChangesToMetaData();
+ }
+ }
+
void removeGroupsAndSubGroupsFromEntries(GroupNodeViewModel group) {
for (GroupNodeViewModel child : group.getChildren()) {
removeGroupsAndSubGroupsFromEntries(child);
@@ -534,4 +574,126 @@ public void removeSelectedEntries(GroupNodeViewModel group) {
public void sortAlphabeticallyRecursive(GroupTreeNode group) {
group.sortChildren(compAlphabetIgnoreCase, true);
}
+
+ public boolean canBeDragged(GroupNodeViewModel groupnode) {
+ AbstractGroup group = groupnode.getGroupNode().getGroup();
+ if (group instanceof AllEntriesGroup) {
+ return false;
+ } else if (group instanceof ExplicitGroup) {
+ return true;
+ } else if (group instanceof LastNameGroup || group instanceof KeywordGroup || group instanceof RegexKeywordGroup) {
+ if (groupnode.getParent().isPresent()) {
+ AbstractGroup groupParent = groupnode.getParent().get().getGroup();
+ if (groupParent instanceof AutomaticKeywordGroup || groupParent instanceof AutomaticPersonsGroup) {
+ return false;
+ } else {
+ return true;
+ }
+ } else {
+ return false;
+ }
+ } else if (group instanceof SearchGroup) {
+ return true;
+ } else if (group instanceof AutomaticKeywordGroup) {
+ return true;
+ } else if (group instanceof AutomaticPersonsGroup) {
+ return true;
+ } else if (group instanceof TexGroup) {
+ return true;
+ } else {
+ throw new UnsupportedOperationException("canBeDragged method not yet implemented in group: " + group.getClass().getName());
+ }
+ }
+
+ public boolean canAddGroupsIn(GroupNodeViewModel groupnode) {
+ AbstractGroup group = groupnode.getGroupNode().getGroup();
+ if (group instanceof AllEntriesGroup) {
+ return true;
+ } else if (group instanceof ExplicitGroup) {
+ return true;
+ } else if (group instanceof LastNameGroup || group instanceof KeywordGroup || group instanceof RegexKeywordGroup) {
+ if (groupnode.getParent().isPresent()) {
+ AbstractGroup groupParent = groupnode.getParent().get().getGroup();
+ if (groupParent instanceof AutomaticKeywordGroup || groupParent instanceof AutomaticPersonsGroup) {
+ return false;
+ } else {
+ return true;
+ }
+ } else {
+ return false;
+ }
+ } else if (group instanceof SearchGroup) {
+ return true;
+ } else if (group instanceof AutomaticKeywordGroup) {
+ return false;
+ } else if (group instanceof AutomaticPersonsGroup) {
+ return false;
+ } else if (group instanceof TexGroup) {
+ return true;
+ } else {
+ throw new UnsupportedOperationException("canAddGroupsIn method not yet implemented in group: " + group.getClass().getName());
+ }
+ }
+
+ public boolean canAddEntriesIn(GroupNodeViewModel groupnode) {
+ AbstractGroup group = groupnode.getGroupNode().getGroup();
+ if (group instanceof AllEntriesGroup) {
+ return false;
+ } else if (group instanceof ExplicitGroup) {
+ return true;
+ } else if (group instanceof LastNameGroup || group instanceof RegexKeywordGroup) {
+ if (groupnode.getParent().isPresent()) {
+ AbstractGroup groupParent = groupnode.getParent().get().getGroup();
+ if (groupParent instanceof AutomaticKeywordGroup || groupParent instanceof AutomaticPersonsGroup) {
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ } else if (group instanceof KeywordGroup) {
+ return true;
+ } else if (group instanceof SearchGroup) {
+ return false;
+ } else if (group instanceof AutomaticKeywordGroup) {
+ return false;
+ } else if (group instanceof AutomaticPersonsGroup) {
+ return false;
+ } else if (group instanceof TexGroup) {
+ return false;
+ } else {
+ throw new UnsupportedOperationException("canAddEntriesIn method not yet implemented in group: " + group.getClass().getName());
+ }
+ }
+
+ public boolean isEditable(GroupNodeViewModel groupnode) {
+ AbstractGroup group = groupnode.getGroupNode().getGroup();
+ if (group instanceof AllEntriesGroup) {
+ return false;
+ } else if (group instanceof ExplicitGroup) {
+ return true;
+ } else if (group instanceof LastNameGroup || group instanceof KeywordGroup || group instanceof RegexKeywordGroup) {
+ if (groupnode.getParent().isPresent()) {
+ AbstractGroup groupParent = groupnode.getParent().get().getGroup();
+ if (groupParent instanceof AutomaticKeywordGroup || groupParent instanceof AutomaticPersonsGroup) {
+ return false;
+ } else {
+ return true;
+ }
+ } else {
+ return false;
+ }
+ } else if (group instanceof SearchGroup) {
+ return true;
+ } else if (group instanceof AutomaticKeywordGroup) {
+ return true;
+ } else if (group instanceof AutomaticPersonsGroup) {
+ return true;
+ } else if (group instanceof TexGroup) {
+ return true;
+ } else {
+ throw new UnsupportedOperationException("isEditable method not yet implemented in group: " + group.getClass().getName());
+ }
+ }
}
diff --git a/src/main/java/org/jabref/gui/groups/GroupsPreferences.java b/src/main/java/org/jabref/gui/groups/GroupsPreferences.java
index a6bf6f67641..849f7dc808a 100644
--- a/src/main/java/org/jabref/gui/groups/GroupsPreferences.java
+++ b/src/main/java/org/jabref/gui/groups/GroupsPreferences.java
@@ -10,17 +10,14 @@ public class GroupsPreferences {
private final ObjectProperty groupViewMode;
private final BooleanProperty shouldAutoAssignGroup;
private final BooleanProperty shouldDisplayGroupCount;
- private final ObjectProperty keywordSeparator;
public GroupsPreferences(GroupViewMode groupViewMode,
boolean shouldAutoAssignGroup,
- boolean shouldDisplayGroupCount,
- ObjectProperty keywordSeparator) {
+ boolean shouldDisplayGroupCount) {
this.groupViewMode = new SimpleObjectProperty<>(groupViewMode);
this.shouldAutoAssignGroup = new SimpleBooleanProperty(shouldAutoAssignGroup);
this.shouldDisplayGroupCount = new SimpleBooleanProperty(shouldDisplayGroupCount);
- this.keywordSeparator = keywordSeparator;
}
public GroupViewMode getGroupViewMode() {
@@ -58,16 +55,4 @@ public BooleanProperty displayGroupCountProperty() {
public void setDisplayGroupCount(boolean shouldDisplayGroupCount) {
this.shouldDisplayGroupCount.set(shouldDisplayGroupCount);
}
-
- public Character getKeywordSeparator() {
- return keywordSeparator.getValue();
- }
-
- public ObjectProperty keywordSeparatorProperty() {
- return keywordSeparator;
- }
-
- public void setKeywordSeparator(Character keywordSeparator) {
- this.keywordSeparator.set(keywordSeparator);
- }
}
diff --git a/src/main/java/org/jabref/gui/importer/ImportAction.java b/src/main/java/org/jabref/gui/importer/ImportAction.java
index 4b32d0d74db..2a0c70fa3a1 100644
--- a/src/main/java/org/jabref/gui/importer/ImportAction.java
+++ b/src/main/java/org/jabref/gui/importer/ImportAction.java
@@ -173,7 +173,7 @@ private ParserResult mergeImportResults(List path.getFileName().toString()).orElse("unknown"),
diff --git a/src/main/java/org/jabref/gui/importer/ImportEntriesViewModel.java b/src/main/java/org/jabref/gui/importer/ImportEntriesViewModel.java
index cc198a34110..5b0202da235 100644
--- a/src/main/java/org/jabref/gui/importer/ImportEntriesViewModel.java
+++ b/src/main/java/org/jabref/gui/importer/ImportEntriesViewModel.java
@@ -155,8 +155,11 @@ public void importEntries(List entriesToImport, boolean shouldDownload
}
}
- new DatabaseMerger(preferences.getKeywordDelimiter()).mergeStrings(databaseContext.getDatabase(), parserResult.getDatabase());
- new DatabaseMerger(preferences.getKeywordDelimiter()).mergeMetaData(databaseContext.getMetaData(),
+ new DatabaseMerger(preferences.getBibEntryPreferences().getKeywordSeparator()).mergeStrings(
+ databaseContext.getDatabase(),
+ parserResult.getDatabase());
+ new DatabaseMerger(preferences.getBibEntryPreferences().getKeywordSeparator()).mergeMetaData(
+ databaseContext.getMetaData(),
parserResult.getMetaData(),
parserResult.getPath().map(path -> path.getFileName().toString()).orElse("unknown"),
parserResult.getDatabase().getEntries());
diff --git a/src/main/java/org/jabref/gui/importer/NewEntryAction.java b/src/main/java/org/jabref/gui/importer/NewEntryAction.java
index ef085920713..fcb25a991bc 100644
--- a/src/main/java/org/jabref/gui/importer/NewEntryAction.java
+++ b/src/main/java/org/jabref/gui/importer/NewEntryAction.java
@@ -24,11 +24,14 @@ public class NewEntryAction extends SimpleCommand {
private static final Logger LOGGER = LoggerFactory.getLogger(NewEntryAction.class);
private final JabRefFrame jabRefFrame;
+
/**
* The type of the entry to create.
*/
private Optional type;
+
private final DialogService dialogService;
+
private final PreferencesService preferences;
public NewEntryAction(JabRefFrame jabRefFrame, DialogService dialogService, PreferencesService preferences, StateManager stateManager) {
diff --git a/src/main/java/org/jabref/gui/importer/actions/OpenDatabaseAction.java b/src/main/java/org/jabref/gui/importer/actions/OpenDatabaseAction.java
index 45e95158e29..403359dd390 100644
--- a/src/main/java/org/jabref/gui/importer/actions/OpenDatabaseAction.java
+++ b/src/main/java/org/jabref/gui/importer/actions/OpenDatabaseAction.java
@@ -5,7 +5,6 @@
import java.nio.file.Path;
import java.sql.SQLException;
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
@@ -178,9 +177,8 @@ private void openTheFile(Path file) {
}
BackgroundTask backgroundTask = BackgroundTask.wrap(() -> loadDatabase(file));
- LibraryTab.Factory libraryTabFactory = new LibraryTab.Factory();
- LibraryTab newTab = libraryTabFactory.createLibraryTab(frame, preferencesService, stateManager, themeManager, file, backgroundTask, Globals.IMPORT_FORMAT_READER);
-
+ // The backgroundTask is executed within the method createLibraryTab
+ LibraryTab newTab = LibraryTab.createLibraryTab(backgroundTask, file, preferencesService, stateManager, frame, themeManager);
backgroundTask.onFinished(() -> trackOpenNewDatabase(newTab));
}
@@ -195,46 +193,51 @@ private ParserResult loadDatabase(Path file) throws Exception {
preferencesService.getFilePreferences().setWorkingDirectory(fileToLoad.getParent());
+ ParserResult parserResult = null;
if (BackupManager.backupFileDiffers(fileToLoad)) {
- BackupUIManager.showRestoreBackupDialog(dialogService, fileToLoad);
+ // In case the backup differs, ask the user what to do.
+ // In case the user opted for restoring a backup, the content of the backup is contained in parserResult.
+ parserResult = BackupUIManager.showRestoreBackupDialog(dialogService, fileToLoad, preferencesService).orElse(null);
}
- ParserResult result;
try {
- result = OpenDatabase.loadDatabase(fileToLoad,
- preferencesService.getImportFormatPreferences(),
- Globals.getFileUpdateMonitor());
- if (result.hasWarnings()) {
+ if (parserResult == null) {
+ // No backup was restored, do the "normal" loading
+ parserResult = OpenDatabase.loadDatabase(fileToLoad,
+ preferencesService.getImportFormatPreferences(),
+ Globals.getFileUpdateMonitor());
+ }
+
+ if (parserResult.hasWarnings()) {
String content = Localization.lang("Please check your library file for wrong syntax.")
- + "\n\n" + result.getErrorMessage();
+ + "\n\n" + parserResult.getErrorMessage();
DefaultTaskExecutor.runInJavaFXThread(() ->
dialogService.showWarningDialogAndWait(Localization.lang("Open library error"), content));
}
} catch (IOException e) {
- result = ParserResult.fromError(e);
+ parserResult = ParserResult.fromError(e);
LOGGER.error("Error opening file '{}'", fileToLoad, e);
}
- if (result.getDatabase().isShared()) {
+ if (parserResult.getDatabase().isShared()) {
try {
- new SharedDatabaseUIManager(frame).openSharedDatabaseFromParserResult(result);
+ new SharedDatabaseUIManager(frame, preferencesService).openSharedDatabaseFromParserResult(parserResult);
} catch (SQLException | DatabaseNotSupportedException | InvalidDBMSConnectionPropertiesException |
NotASharedDatabaseException e) {
- result.getDatabaseContext().clearDatabasePath(); // do not open the original file
- result.getDatabase().clearSharedDatabaseID();
+ parserResult.getDatabaseContext().clearDatabasePath(); // do not open the original file
+ parserResult.getDatabase().clearSharedDatabaseID();
LOGGER.error("Connection error", e);
throw e;
}
}
- return result;
+ return parserResult;
}
private void trackOpenNewDatabase(LibraryTab libraryTab) {
- Map properties = new HashMap<>();
- Map measurements = new HashMap<>();
- measurements.put("NumberOfEntries", (double) libraryTab.getBibDatabaseContext().getDatabase().getEntryCount());
-
- Globals.getTelemetryClient().ifPresent(client -> client.trackEvent("OpenNewDatabase", properties, measurements));
+ Globals.getTelemetryClient().ifPresent(client -> client.trackEvent(
+ "OpenNewDatabase",
+ Map.of(),
+ Map.of("NumberOfEntries", (double) libraryTab.getBibDatabaseContext().getDatabase().getEntryCount())));
}
}
diff --git a/src/main/java/org/jabref/gui/journals/AbbreviateAction.java b/src/main/java/org/jabref/gui/journals/AbbreviateAction.java
index 4a94a38e55e..610c250cd7b 100644
--- a/src/main/java/org/jabref/gui/journals/AbbreviateAction.java
+++ b/src/main/java/org/jabref/gui/journals/AbbreviateAction.java
@@ -17,6 +17,7 @@
import org.jabref.gui.actions.StandardActions;
import org.jabref.gui.undo.NamedCompound;
import org.jabref.gui.util.BackgroundTask;
+import org.jabref.logic.journals.JournalAbbreviationPreferences;
import org.jabref.logic.l10n.Localization;
import org.jabref.model.database.BibDatabaseContext;
import org.jabref.model.entry.BibEntry;
@@ -36,18 +37,20 @@ public class AbbreviateAction extends SimpleCommand {
private final JabRefFrame frame;
private final DialogService dialogService;
private final StateManager stateManager;
+ private final JournalAbbreviationPreferences journalAbbreviationPreferences;
private AbbreviationType abbreviationType;
public AbbreviateAction(StandardActions action,
JabRefFrame frame,
DialogService dialogService,
- StateManager stateManager) {
-
+ StateManager stateManager,
+ JournalAbbreviationPreferences journalAbbreviationPreferences) {
this.action = action;
this.frame = frame;
this.dialogService = dialogService;
this.stateManager = stateManager;
+ this.journalAbbreviationPreferences = journalAbbreviationPreferences;
switch (action) {
case ABBREVIATE_DEFAULT -> abbreviationType = AbbreviationType.DEFAULT;
@@ -61,10 +64,9 @@ public AbbreviateAction(StandardActions action,
@Override
public void execute() {
- if (action == StandardActions.ABBREVIATE_DEFAULT
- || action == StandardActions.ABBREVIATE_MEDLINE
- || action == StandardActions.ABBREVIATE_SHORTEST_UNIQUE) {
-
+ if ((action == StandardActions.ABBREVIATE_DEFAULT)
+ || (action == StandardActions.ABBREVIATE_MEDLINE)
+ || (action == StandardActions.ABBREVIATE_SHORTEST_UNIQUE)) {
dialogService.notify(Localization.lang("Abbreviating..."));
stateManager.getActiveDatabase().ifPresent(databaseContext ->
BackgroundTask.wrap(() -> abbreviate(stateManager.getActiveDatabase().get(), stateManager.getSelectedEntries()))
@@ -84,7 +86,8 @@ public void execute() {
private String abbreviate(BibDatabaseContext databaseContext, List entries) {
UndoableAbbreviator undoableAbbreviator = new UndoableAbbreviator(
Globals.journalAbbreviationRepository,
- abbreviationType);
+ abbreviationType,
+ journalAbbreviationPreferences.useAMSFJournalFieldForAbbrevAndUnabbrev());
NamedCompound ce = new NamedCompound(Localization.lang("Abbreviate journal names"));
diff --git a/src/main/java/org/jabref/gui/journals/UndoableAbbreviator.java b/src/main/java/org/jabref/gui/journals/UndoableAbbreviator.java
index e7527aa729e..21cce96f12d 100644
--- a/src/main/java/org/jabref/gui/journals/UndoableAbbreviator.java
+++ b/src/main/java/org/jabref/gui/journals/UndoableAbbreviator.java
@@ -7,16 +7,20 @@
import org.jabref.logic.journals.JournalAbbreviationRepository;
import org.jabref.model.database.BibDatabase;
import org.jabref.model.entry.BibEntry;
+import org.jabref.model.entry.field.AMSField;
import org.jabref.model.entry.field.Field;
+import org.jabref.model.entry.field.StandardField;
public class UndoableAbbreviator {
private final JournalAbbreviationRepository journalAbbreviationRepository;
private final AbbreviationType abbreviationType;
+ private final boolean useFJournalField;
- public UndoableAbbreviator(JournalAbbreviationRepository journalAbbreviationRepository, AbbreviationType abbreviationType) {
+ public UndoableAbbreviator(JournalAbbreviationRepository journalAbbreviationRepository, AbbreviationType abbreviationType, boolean useFJournalField) {
this.journalAbbreviationRepository = journalAbbreviationRepository;
this.abbreviationType = abbreviationType;
+ this.useFJournalField = useFJournalField;
}
/**
@@ -43,12 +47,19 @@ public boolean abbreviate(BibDatabase database, BibEntry entry, Field fieldName,
return false; // Unknown, cannot abbreviate anything.
}
- String newText = getAbbreviatedName(journalAbbreviationRepository.get(text).get());
+ Abbreviation abbreviation = journalAbbreviationRepository.get(text).get();
+ String newText = getAbbreviatedName(abbreviation);
if (newText.equals(origText)) {
return false;
}
+ // Store full name into fjournal but only if it exists
+ if (useFJournalField && (StandardField.JOURNAL.equals(fieldName) || StandardField.JOURNALTITLE.equals(fieldName))) {
+ entry.setField(AMSField.FJOURNAL, abbreviation.getName());
+ ce.addEdit(new UndoableFieldChange(entry, AMSField.FJOURNAL, null, abbreviation.getName()));
+ }
+
entry.setField(fieldName, newText);
ce.addEdit(new UndoableFieldChange(entry, fieldName, origText, newText));
return true;
diff --git a/src/main/java/org/jabref/gui/journals/UndoableUnabbreviator.java b/src/main/java/org/jabref/gui/journals/UndoableUnabbreviator.java
index 934488f01cd..c50c148db7d 100644
--- a/src/main/java/org/jabref/gui/journals/UndoableUnabbreviator.java
+++ b/src/main/java/org/jabref/gui/journals/UndoableUnabbreviator.java
@@ -7,7 +7,9 @@
import org.jabref.logic.journals.JournalAbbreviationRepository;
import org.jabref.model.database.BibDatabase;
import org.jabref.model.entry.BibEntry;
+import org.jabref.model.entry.field.AMSField;
import org.jabref.model.entry.field.Field;
+import org.jabref.model.entry.field.StandardField;
public class UndoableUnabbreviator {
@@ -30,6 +32,10 @@ public boolean unabbreviate(BibDatabase database, BibEntry entry, Field field, C
return false;
}
+ if (restoreFromFJournal(entry, field, ce)) {
+ return true;
+ }
+
String text = entry.getField(field).get();
String origText = text;
if (database != null) {
@@ -50,4 +56,21 @@ public boolean unabbreviate(BibDatabase database, BibEntry entry, Field field, C
ce.addEdit(new UndoableFieldChange(entry, field, origText, newText));
return true;
}
+
+ public boolean restoreFromFJournal(BibEntry entry, Field field, CompoundEdit ce) {
+ if ((!StandardField.JOURNAL.equals(field) && !StandardField.JOURNALTITLE.equals(field)) || !entry.hasField(AMSField.FJOURNAL)) {
+ return false;
+ }
+
+ String origText = entry.getField(field).get();
+ String newText = entry.getField(AMSField.FJOURNAL).get().trim();
+
+ entry.setField(AMSField.FJOURNAL, "");
+ ce.addEdit(new UndoableFieldChange(entry, AMSField.FJOURNAL, newText, ""));
+
+ entry.setField(field, newText);
+ ce.addEdit(new UndoableFieldChange(entry, field, origText, newText));
+
+ return true;
+ }
}
diff --git a/src/main/java/org/jabref/gui/maintable/BibEntryTableViewModel.java b/src/main/java/org/jabref/gui/maintable/BibEntryTableViewModel.java
index dcf6240fcea..da8c89c9e70 100644
--- a/src/main/java/org/jabref/gui/maintable/BibEntryTableViewModel.java
+++ b/src/main/java/org/jabref/gui/maintable/BibEntryTableViewModel.java
@@ -104,13 +104,24 @@ public ObservableValue> getMatchedGroups() {
public ObservableValue> getSpecialField(SpecialField field) {
OptionalBinding value = specialFieldValues.get(field);
+ // Fetch possibly updated value from BibEntry entry
+ Optional currentValue = this.entry.getField(field);
if (value != null) {
- return value;
+ if (currentValue.isEmpty() && value.getValue().isEmpty()) {
+ var zeroValue = getField(field).flatMapOpt(fieldValue -> field.parseValue("CLEAR_RANK").map(SpecialFieldValueViewModel::new));
+ specialFieldValues.put(field, zeroValue);
+ return zeroValue;
+ } else if (value.getValue().isEmpty() || !value.getValue().get().getValue().getFieldValue().equals(currentValue)) {
+ // specialFieldValues value and BibEntry value differ => Set specialFieldValues value to BibEntry value
+ value = getField(field).flatMapOpt(fieldValue -> field.parseValue(fieldValue).map(SpecialFieldValueViewModel::new));
+ specialFieldValues.put(field, value);
+ return value;
+ }
} else {
value = getField(field).flatMapOpt(fieldValue -> field.parseValue(fieldValue).map(SpecialFieldValueViewModel::new));
specialFieldValues.put(field, value);
- return value;
}
+ return value;
}
public ObservableValue getFields(OrFields fields) {
diff --git a/src/main/java/org/jabref/gui/maintable/CellFactory.java b/src/main/java/org/jabref/gui/maintable/CellFactory.java
index 13c65f6a336..e3309e94c1d 100644
--- a/src/main/java/org/jabref/gui/maintable/CellFactory.java
+++ b/src/main/java/org/jabref/gui/maintable/CellFactory.java
@@ -36,7 +36,7 @@ public CellFactory(PreferencesService preferencesService, UndoManager undoManage
TABLE_ICONS.put(new UnknownField("citeseerurl"), icon);
icon = IconTheme.JabRefIcons.WWW;
- // icon.setToolTipText(Localization.lang("Open") + " ArXiv URL");
+ // icon.setToolTipText(Localization.lang("Open") + " ArXivFetcher URL");
TABLE_ICONS.put(StandardField.EPRINT, icon);
icon = IconTheme.JabRefIcons.DOI;
diff --git a/src/main/java/org/jabref/gui/maintable/MainTable.java b/src/main/java/org/jabref/gui/maintable/MainTable.java
index 8f7fff99355..0697204bd40 100644
--- a/src/main/java/org/jabref/gui/maintable/MainTable.java
+++ b/src/main/java/org/jabref/gui/maintable/MainTable.java
@@ -188,6 +188,16 @@ public MainTable(MainTableDataModel model,
});
database.getDatabase().registerListener(this);
+
+ MainTableColumnFactory rightClickMenuFactory = new MainTableColumnFactory(
+ database,
+ preferencesService,
+ preferencesService.getColumnPreferences(),
+ libraryTab.getUndoManager(),
+ dialogService,
+ stateManager);
+ // Enable the header right-click menu.
+ new MainTableHeaderContextMenu(this, rightClickMenuFactory).show(true);
}
/**
diff --git a/src/main/java/org/jabref/gui/maintable/MainTableColumnFactory.java b/src/main/java/org/jabref/gui/maintable/MainTableColumnFactory.java
index 52cf6a2f04f..0ca2908b4d4 100644
--- a/src/main/java/org/jabref/gui/maintable/MainTableColumnFactory.java
+++ b/src/main/java/org/jabref/gui/maintable/MainTableColumnFactory.java
@@ -74,50 +74,55 @@ public MainTableColumnFactory(BibDatabaseContext database,
this.stateManager = stateManager;
}
+ public TableColumn createColumn(MainTableColumnModel column) {
+ TableColumn returnColumn = null;
+ switch (column.getType()) {
+ case INDEX:
+ returnColumn = createIndexColumn(column);
+ break;
+ case GROUPS:
+ returnColumn = createGroupColumn(column);
+ break;
+ case FILES:
+ returnColumn = createFilesColumn(column);
+ break;
+ case LINKED_IDENTIFIER:
+ returnColumn = createIdentifierColumn(column);
+ break;
+ case LIBRARY_NAME:
+ returnColumn = createLibraryColumn(column);
+ break;
+ case EXTRAFILE:
+ if (!column.getQualifier().isBlank()) {
+ returnColumn = createExtraFileColumn(column);
+ }
+ break;
+ case SPECIALFIELD:
+ if (!column.getQualifier().isBlank()) {
+ Field field = FieldFactory.parseField(column.getQualifier());
+ if (field instanceof SpecialField) {
+ returnColumn = createSpecialFieldColumn(column);
+ } else {
+ LOGGER.warn("Special field type '{}' is unknown. Using normal column type.", column.getQualifier());
+ returnColumn = createFieldColumn(column);
+ }
+ }
+ break;
+ default:
+ case NORMALFIELD:
+ if (!column.getQualifier().isBlank()) {
+ returnColumn = createFieldColumn(column);
+ }
+ break;
+ }
+ return returnColumn;
+ }
+
public List> createColumns() {
List> columns = new ArrayList<>();
columnPreferences.getColumns().forEach(column -> {
-
- switch (column.getType()) {
- case INDEX:
- columns.add(createIndexColumn(column));
- break;
- case GROUPS:
- columns.add(createGroupColumn(column));
- break;
- case FILES:
- columns.add(createFilesColumn(column));
- break;
- case LINKED_IDENTIFIER:
- columns.add(createIdentifierColumn(column));
- break;
- case LIBRARY_NAME:
- columns.add(createLibraryColumn(column));
- break;
- case EXTRAFILE:
- if (!column.getQualifier().isBlank()) {
- columns.add(createExtraFileColumn(column));
- }
- break;
- case SPECIALFIELD:
- if (!column.getQualifier().isBlank()) {
- Field field = FieldFactory.parseField(column.getQualifier());
- if (field instanceof SpecialField) {
- columns.add(createSpecialFieldColumn(column));
- } else {
- LOGGER.warn("Special field type '{}' is unknown. Using normal column type.", column.getQualifier());
- columns.add(createFieldColumn(column));
- }
- }
- break;
- default:
- case NORMALFIELD:
- if (!column.getQualifier().isBlank()) {
- columns.add(createFieldColumn(column));
- }
- break;
- }
+ columns.add(createColumn(column));
});
return columns;
diff --git a/src/main/java/org/jabref/gui/maintable/MainTableHeaderContextMenu.java b/src/main/java/org/jabref/gui/maintable/MainTableHeaderContextMenu.java
new file mode 100644
index 00000000000..37b628ed5cd
--- /dev/null
+++ b/src/main/java/org/jabref/gui/maintable/MainTableHeaderContextMenu.java
@@ -0,0 +1,227 @@
+package org.jabref.gui.maintable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javafx.collections.ObservableList;
+import javafx.scene.control.ContextMenu;
+import javafx.scene.control.RadioMenuItem;
+import javafx.scene.control.SeparatorMenuItem;
+import javafx.scene.control.TableColumn;
+import javafx.scene.layout.StackPane;
+
+import org.jabref.gui.maintable.columns.MainTableColumn;
+
+public class MainTableHeaderContextMenu extends ContextMenu {
+
+ private static final int OUT_OF_BOUNDS = -1;
+ MainTable mainTable;
+ MainTableColumnFactory factory;
+
+ /**
+ * Constructor for the right click menu
+ *
+ */
+ public MainTableHeaderContextMenu(MainTable mainTable, MainTableColumnFactory factory) {
+ super();
+ this.mainTable = mainTable;
+ this.factory = factory;
+ constructItems(mainTable);
+ }
+
+ /**
+ * Handles showing the menu in the cursors position when right-clicked.
+ */
+ public void show(boolean show) {
+ // TODO: 20/10/2022 unknown bug where issue does not show unless parameter is passed through this method.
+ mainTable.setOnContextMenuRequested(event -> {
+ // Display the menu if header is clicked, otherwise, remove from display.
+ if (!(event.getTarget() instanceof StackPane) && show) {
+ this.show(mainTable, event.getScreenX(), event.getScreenY());
+ } else if (this.isShowing()) {
+ this.hide();
+ }
+ event.consume();
+ });
+ }
+
+ /**
+ * Constructs the items for the list and places them in the menu.
+ */
+ private void constructItems(MainTable mainTable) {
+ // Reset the right-click menu
+ this.getItems().clear();
+ List> commonColumns = commonColumns();
+
+ // Populate the menu with currently used fields
+ for (TableColumn column : mainTable.getColumns()) {
+ // Append only if the column has not already been added (a common column)
+ RightClickMenuItem itemToAdd = createMenuItem(column, true);
+ this.getItems().add(itemToAdd);
+
+ // Remove from remaining common columns pool
+ MainTableColumn searchCol = (MainTableColumn>) column;
+ if (isACommonColumn(searchCol)) {
+ commonColumns.removeIf(tableCol -> ((MainTableColumn) tableCol).getModel().equals(searchCol.getModel()));
+ }
+ }
+
+ SeparatorMenuItem separator = new SeparatorMenuItem();
+ this.getItems().add(separator);
+
+ // Append to the menu the current remaining columns in the common columns.
+
+ for (TableColumn tableColumn : commonColumns) {
+ RightClickMenuItem itemToAdd = createMenuItem(tableColumn, false);
+ this.getItems().add(itemToAdd);
+ }
+ }
+
+ /**
+ * Creates an item for the menu constructed with the name/visibility of the table column.
+ *
+ */
+ @SuppressWarnings("rawtypes")
+ private RightClickMenuItem createMenuItem(TableColumn column, boolean isDisplaying) {
+ // Gets display name and constructs Radio Menu Item.
+ MainTableColumn tableColumn = (MainTableColumn) column;
+ String displayName = tableColumn.getDisplayName();
+ return new RightClickMenuItem(displayName, tableColumn, isDisplaying);
+ }
+
+ /**
+ * Returns the current position of the inputted column in the table (index).
+ *
+ */
+ private int obtainIndexOfColumn(MainTableColumn searchColumn) {
+ ObservableList> columns = mainTable.getColumns();
+ for (int i = 0; i < columns.size(); i++) {
+ TableColumn column = columns.get(i);
+ MainTableColumnModel model = ((MainTableColumn) column).getModel();
+ if (model.equals(searchColumn.getModel())) {
+ return i;
+ }
+ }
+ return OUT_OF_BOUNDS;
+ }
+
+ /**
+ * Adds the column into the MainTable for display.
+ */
+ @SuppressWarnings("rawtypes")
+ private void addColumn(MainTableColumn tableColumn, int index) {
+ if (index <= OUT_OF_BOUNDS || index >= mainTable.getColumns().size()) {
+ mainTable.getColumns().add(tableColumn);
+ } else {
+ mainTable.getColumns().add(index, tableColumn);
+ }
+ }
+
+ /**
+ * Removes the column from the MainTable to remove visibility.
+ */
+ @SuppressWarnings("rawtypes")
+ private void removeColumn(MainTableColumn tableColumn) {
+ mainTable.getColumns().removeIf(tableCol -> ((MainTableColumn) tableCol).getModel().equals(tableColumn.getModel()));
+ }
+
+ /**
+ * Checks if a column is one of the commonly used columns.
+ */
+ private boolean isACommonColumn(MainTableColumn tableColumn) {
+ return isColumnInList(tableColumn, commonColumns());
+ }
+
+ /**
+ * Determines if a list of TableColumns contains the searched column.
+ */
+ private boolean isColumnInList(MainTableColumn searchColumn, List> tableColumns) {
+ for (TableColumn column:
+ tableColumns) {
+ MainTableColumnModel model = ((MainTableColumn) column).getModel();
+ if (model.equals(searchColumn.getModel())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Creates the list of the "commonly used" columns (currently based on the default preferences).
+ */
+ private List> commonColumns() {
+ // Qualifier strings
+ String entryTypeQualifier = "entrytype";
+ String authorEditQualifier = "author/editor";
+ String titleQualifier = "title";
+ String yearQualifier = "year";
+ String journalBookQualifier = "journal/booktitle";
+ String rankQualifier = "ranking";
+ String readStatusQualifier = "readstatus";
+ String priorityQualifier = "priority";
+
+ // Create the MainTableColumn Models from qualifiers + types.
+ List commonColumns = new ArrayList<>();
+ commonColumns.add(new MainTableColumnModel(MainTableColumnModel.Type.GROUPS));
+ commonColumns.add(new MainTableColumnModel(MainTableColumnModel.Type.FILES));
+ commonColumns.add(new MainTableColumnModel(MainTableColumnModel.Type.LINKED_IDENTIFIER));
+ commonColumns.add(new MainTableColumnModel(MainTableColumnModel.Type.NORMALFIELD, entryTypeQualifier));
+ commonColumns.add(new MainTableColumnModel(MainTableColumnModel.Type.NORMALFIELD, authorEditQualifier));
+ commonColumns.add(new MainTableColumnModel(MainTableColumnModel.Type.NORMALFIELD, titleQualifier));
+ commonColumns.add(new MainTableColumnModel(MainTableColumnModel.Type.NORMALFIELD, yearQualifier));
+ commonColumns.add(new MainTableColumnModel(MainTableColumnModel.Type.NORMALFIELD, journalBookQualifier));
+ commonColumns.add(new MainTableColumnModel(MainTableColumnModel.Type.SPECIALFIELD, rankQualifier));
+ commonColumns.add(new MainTableColumnModel(MainTableColumnModel.Type.SPECIALFIELD, readStatusQualifier));
+ commonColumns.add(new MainTableColumnModel(MainTableColumnModel.Type.SPECIALFIELD, priorityQualifier));
+
+ // Create the Table Columns from the models using factory methods.
+ List> commonTableColumns = new ArrayList<>();
+ for (MainTableColumnModel columnModel: commonColumns) {
+ TableColumn tableColumn = factory.createColumn(columnModel);
+ commonTableColumns.add(tableColumn);
+ }
+ return commonTableColumns;
+ }
+
+ /**
+ * RightClickMenuItem: RadioMenuItem holding position in MainTable and its visibility.
+ *
+ */
+ private class RightClickMenuItem extends RadioMenuItem {
+ private int index;
+ private boolean visibleInTable;
+
+ RightClickMenuItem(String displayName, MainTableColumn column, boolean isVisible) {
+ super(displayName);
+ setVisibleInTable(isVisible);
+ // Flag item as selected if the item is already in the main table.
+ this.setSelected(isVisible);
+
+ setIndex(OUT_OF_BOUNDS);
+
+ // Set action to toggle visibility from main table when item is clicked
+ this.setOnAction(event -> {
+ if (isVisibleInTable()) {
+ setIndex(obtainIndexOfColumn(column));
+ removeColumn(column);
+ } else {
+ addColumn(column, this.index);
+ setIndex(obtainIndexOfColumn(column));
+ }
+ setVisibleInTable(!this.visibleInTable);
+ });
+ }
+
+ public void setIndex(int index) {
+ this.index = index;
+ }
+
+ public void setVisibleInTable(boolean visibleInTable) {
+ this.visibleInTable = visibleInTable;
+ }
+
+ public boolean isVisibleInTable() {
+ return visibleInTable;
+ }
+ }
+}
diff --git a/src/main/java/org/jabref/gui/maintable/RightClickMenu.java b/src/main/java/org/jabref/gui/maintable/RightClickMenu.java
index 79b2a95006c..00e59daaa08 100644
--- a/src/main/java/org/jabref/gui/maintable/RightClickMenu.java
+++ b/src/main/java/org/jabref/gui/maintable/RightClickMenu.java
@@ -52,7 +52,7 @@ public static ContextMenu create(BibEntryTableViewModel entry,
createCopySubMenu(factory, dialogService, stateManager, preferencesService, clipBoardManager, taskExecutor),
factory.createMenuItem(StandardActions.PASTE, new EditAction(StandardActions.PASTE, libraryTab.frame(), stateManager)),
factory.createMenuItem(StandardActions.CUT, new EditAction(StandardActions.CUT, libraryTab.frame(), stateManager)),
- factory.createMenuItem(StandardActions.MERGE_ENTRIES, new MergeEntriesAction(dialogService, stateManager)),
+ factory.createMenuItem(StandardActions.MERGE_ENTRIES, new MergeEntriesAction(dialogService, stateManager, preferencesService.getBibEntryPreferences())),
factory.createMenuItem(StandardActions.DELETE_ENTRY, new EditAction(StandardActions.DELETE_ENTRY, libraryTab.frame(), stateManager)),
new SeparatorMenuItem(),
@@ -79,7 +79,7 @@ public static ContextMenu create(BibEntryTableViewModel entry,
new SeparatorMenuItem(),
new ChangeEntryTypeMenu(libraryTab.getSelectedEntries(), libraryTab.getBibDatabaseContext(), libraryTab.getUndoManager(), keyBindingRepository, entryTypesManager).asSubMenu(),
- factory.createMenuItem(StandardActions.MERGE_WITH_FETCHED_ENTRY, new MergeWithFetchedEntryAction(libraryTab, dialogService, stateManager))
+ factory.createMenuItem(StandardActions.MERGE_WITH_FETCHED_ENTRY, new MergeWithFetchedEntryAction(libraryTab, dialogService, stateManager, taskExecutor, preferencesService))
);
return contextMenu;
diff --git a/src/main/java/org/jabref/gui/maintable/columns/FieldColumn.java b/src/main/java/org/jabref/gui/maintable/columns/FieldColumn.java
index aee93952cd7..e1b8b1c1ff9 100644
--- a/src/main/java/org/jabref/gui/maintable/columns/FieldColumn.java
+++ b/src/main/java/org/jabref/gui/maintable/columns/FieldColumn.java
@@ -11,7 +11,7 @@
import org.jabref.model.entry.field.OrFields;
import org.jabref.model.entry.field.UnknownField;
-import com.google.common.collect.Iterables;
+import com.google.common.collect.MoreCollectors;
/**
* A column that displays the text-value of the field
@@ -33,9 +33,9 @@ public FieldColumn(MainTableColumnModel model) {
if (fields.size() == 1) {
// comparator can't parse more than one value
- Field field = Iterables.getOnlyElement(fields);
+ Field field = fields.stream().collect(MoreCollectors.onlyElement());
- if (field instanceof UnknownField || field.isNumeric()) {
+ if ((field instanceof UnknownField) || field.isNumeric()) {
this.setComparator(new NumericFieldComparator());
}
}
diff --git a/src/main/java/org/jabref/gui/mergeentries/FetchAndMergeEntry.java b/src/main/java/org/jabref/gui/mergeentries/FetchAndMergeEntry.java
index e6aff3d4ea2..83399cd4f7b 100644
--- a/src/main/java/org/jabref/gui/mergeentries/FetchAndMergeEntry.java
+++ b/src/main/java/org/jabref/gui/mergeentries/FetchAndMergeEntry.java
@@ -9,7 +9,6 @@
import java.util.TreeSet;
import org.jabref.gui.DialogService;
-import org.jabref.gui.Globals;
import org.jabref.gui.LibraryTab;
import org.jabref.gui.undo.NamedCompound;
import org.jabref.gui.undo.UndoableChangeType;
@@ -24,11 +23,14 @@
import org.jabref.logic.importer.WebFetcher;
import org.jabref.logic.importer.WebFetchers;
import org.jabref.logic.l10n.Localization;
+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.FieldFactory;
import org.jabref.model.entry.field.StandardField;
import org.jabref.model.entry.types.EntryType;
+import org.jabref.preferences.BibEntryPreferences;
+import org.jabref.preferences.PreferencesService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -41,14 +43,18 @@ public class FetchAndMergeEntry {
// A list of all field which are supported
public static List SUPPORTED_FIELDS = Arrays.asList(StandardField.DOI, StandardField.EPRINT, StandardField.ISBN);
private static final Logger LOGGER = LoggerFactory.getLogger(FetchAndMergeEntry.class);
- private final LibraryTab libraryTab;
private final DialogService dialogService;
private final TaskExecutor taskExecutor;
+ private final BibDatabaseContext bibDatabaseContext;
+ private final PreferencesService preferencesService;
+ private final LibraryTab libraryTab;
- public FetchAndMergeEntry(LibraryTab libraryTab, TaskExecutor taskExecutor) {
- this.dialogService = libraryTab.frame().getDialogService();
+ public FetchAndMergeEntry(LibraryTab libraryTab, TaskExecutor taskExecutor, PreferencesService preferencesService, DialogService dialogService) {
this.libraryTab = libraryTab;
+ this.bibDatabaseContext = libraryTab.getBibDatabaseContext();
this.taskExecutor = taskExecutor;
+ this.preferencesService = preferencesService;
+ this.dialogService = dialogService;
}
public void fetchAndMerge(BibEntry entry) {
@@ -63,15 +69,15 @@ public void fetchAndMerge(BibEntry entry, List fields) {
for (Field field : fields) {
Optional fieldContent = entry.getField(field);
if (fieldContent.isPresent()) {
- Optional fetcher = WebFetchers.getIdBasedFetcherForField(field, Globals.prefs.getImportFormatPreferences());
+ Optional fetcher = WebFetchers.getIdBasedFetcherForField(field, preferencesService.getImportFormatPreferences());
if (fetcher.isPresent()) {
BackgroundTask.wrap(() -> fetcher.get().performSearchById(fieldContent.get()))
.onSuccess(fetchedEntry -> {
- ImportCleanup cleanup = new ImportCleanup(libraryTab.getBibDatabaseContext().getMode());
+ ImportCleanup cleanup = new ImportCleanup(bibDatabaseContext.getMode());
String type = field.getDisplayName();
if (fetchedEntry.isPresent()) {
cleanup.doPostCleanup(fetchedEntry.get());
- showMergeDialog(entry, fetchedEntry.get(), fetcher.get());
+ showMergeDialog(entry, fetchedEntry.get(), fetcher.get(), preferencesService.getBibEntryPreferences());
} else {
dialogService.notify(Localization.lang("Cannot get info based on given %0: %1", type, fieldContent.get()));
}
@@ -86,7 +92,7 @@ public void fetchAndMerge(BibEntry entry, List fields) {
dialogService.showInformationDialogAndWait(Localization.lang("Fetching information using %0", fetcher.get().getName()), Localization.lang("Error occured %0", exception.getMessage()));
}
})
- .executeWith(Globals.TASK_EXECUTOR);
+ .executeWith(taskExecutor);
}
} else {
dialogService.notify(Localization.lang("No %0 found", field.getDisplayName()));
@@ -94,13 +100,13 @@ public void fetchAndMerge(BibEntry entry, List fields) {
}
}
- private void showMergeDialog(BibEntry originalEntry, BibEntry fetchedEntry, WebFetcher fetcher) {
- MergeEntriesDialog dialog = new MergeEntriesDialog(originalEntry, fetchedEntry);
+ private void showMergeDialog(BibEntry originalEntry, BibEntry fetchedEntry, WebFetcher fetcher, BibEntryPreferences bibEntryPreferences) {
+ MergeEntriesDialog dialog = new MergeEntriesDialog(originalEntry, fetchedEntry, bibEntryPreferences);
dialog.setTitle(Localization.lang("Merge entry with %0 information", fetcher.getName()));
dialog.setLeftHeaderText(Localization.lang("Original entry"));
dialog.setRightHeaderText(Localization.lang("Entry from %0", fetcher.getName()));
Optional mergedEntry = dialogService.showCustomDialogAndWait(dialog).map(EntriesMergeResult::mergedEntry);
-
+
if (mergedEntry.isPresent()) {
NamedCompound ce = new NamedCompound(Localization.lang("Merge entry with %0 information", fetcher.getName()));
@@ -161,7 +167,7 @@ public void fetchAndMerge(BibEntry entry, EntryBasedFetcher fetcher) {
if (fetchedEntry.isPresent()) {
ImportCleanup cleanup = new ImportCleanup(libraryTab.getBibDatabaseContext().getMode());
cleanup.doPostCleanup(fetchedEntry.get());
- showMergeDialog(entry, fetchedEntry.get(), fetcher);
+ showMergeDialog(entry, fetchedEntry.get(), fetcher, preferencesService.getBibEntryPreferences());
} else {
dialogService.notify(Localization.lang("Could not find any bibliographic information."));
}
diff --git a/src/main/java/org/jabref/gui/mergeentries/MergeEntriesAction.java b/src/main/java/org/jabref/gui/mergeentries/MergeEntriesAction.java
index a4c04975906..9c40605d11d 100644
--- a/src/main/java/org/jabref/gui/mergeentries/MergeEntriesAction.java
+++ b/src/main/java/org/jabref/gui/mergeentries/MergeEntriesAction.java
@@ -11,16 +11,20 @@
import org.jabref.logic.l10n.Localization;
import org.jabref.model.entry.BibEntry;
import org.jabref.model.entry.field.InternalField;
+import org.jabref.preferences.BibEntryPreferences;
public class MergeEntriesAction extends SimpleCommand {
+ private static final int NUMBER_OF_ENTRIES_NEEDED = 2;
private final DialogService dialogService;
private final StateManager stateManager;
+ private final BibEntryPreferences bibEntryPreferences;
- public MergeEntriesAction(DialogService dialogService, StateManager stateManager) {
+ public MergeEntriesAction(DialogService dialogService, StateManager stateManager, BibEntryPreferences bibEntryPreferences) {
this.dialogService = dialogService;
this.stateManager = stateManager;
+ this.bibEntryPreferences = bibEntryPreferences;
- this.executable.bind(ActionHelper.needsEntriesSelected(2, stateManager));
+ this.executable.bind(ActionHelper.needsEntriesSelected(NUMBER_OF_ENTRIES_NEEDED, stateManager));
}
@Override
@@ -55,9 +59,9 @@ public void execute() {
second = one;
}
- MergeEntriesDialog dialog = new MergeEntriesDialog(first, second);
+ MergeEntriesDialog dialog = new MergeEntriesDialog(first, second, bibEntryPreferences);
dialog.setTitle(Localization.lang("Merge entries"));
-
+
Optional mergeResultOpt = dialogService.showCustomDialogAndWait(dialog);
mergeResultOpt.ifPresentOrElse(entriesMergeResult -> {
new MergeTwoEntriesAction(entriesMergeResult, stateManager).execute();
diff --git a/src/main/java/org/jabref/gui/mergeentries/MergeEntriesDialog.java b/src/main/java/org/jabref/gui/mergeentries/MergeEntriesDialog.java
index ce4149a6bb1..23a82a1ddde 100644
--- a/src/main/java/org/jabref/gui/mergeentries/MergeEntriesDialog.java
+++ b/src/main/java/org/jabref/gui/mergeentries/MergeEntriesDialog.java
@@ -8,14 +8,15 @@
import org.jabref.gui.util.BaseDialog;
import org.jabref.logic.l10n.Localization;
import org.jabref.model.entry.BibEntry;
+import org.jabref.preferences.BibEntryPreferences;
public class MergeEntriesDialog extends BaseDialog {
private final ThreeWayMergeView threeWayMergeView;
private final BibEntry one;
private final BibEntry two;
- public MergeEntriesDialog(BibEntry one, BibEntry two) {
- threeWayMergeView = new ThreeWayMergeView(one, two);
+ public MergeEntriesDialog(BibEntry one, BibEntry two, BibEntryPreferences bibEntryPreferences) {
+ threeWayMergeView = new ThreeWayMergeView(one, two, bibEntryPreferences);
this.one = one;
this.two = two;
diff --git a/src/main/java/org/jabref/gui/mergeentries/MergeWithFetchedEntryAction.java b/src/main/java/org/jabref/gui/mergeentries/MergeWithFetchedEntryAction.java
index 11aeaac50e0..8c4d88aec5d 100644
--- a/src/main/java/org/jabref/gui/mergeentries/MergeWithFetchedEntryAction.java
+++ b/src/main/java/org/jabref/gui/mergeentries/MergeWithFetchedEntryAction.java
@@ -1,26 +1,31 @@
package org.jabref.gui.mergeentries;
import org.jabref.gui.DialogService;
-import org.jabref.gui.Globals;
import org.jabref.gui.LibraryTab;
import org.jabref.gui.StateManager;
import org.jabref.gui.actions.ActionHelper;
import org.jabref.gui.actions.SimpleCommand;
+import org.jabref.gui.util.TaskExecutor;
import org.jabref.logic.l10n.Localization;
import org.jabref.model.entry.BibEntry;
import org.jabref.model.entry.field.OrFields;
import org.jabref.model.entry.field.StandardField;
+import org.jabref.preferences.PreferencesService;
public class MergeWithFetchedEntryAction extends SimpleCommand {
private final LibraryTab libraryTab;
private final DialogService dialogService;
private final StateManager stateManager;
+ private final PreferencesService preferencesService;
+ private final TaskExecutor taskExecutor;
- public MergeWithFetchedEntryAction(LibraryTab libraryTab, DialogService dialogService, StateManager stateManager) {
+ public MergeWithFetchedEntryAction(LibraryTab libraryTab, DialogService dialogService, StateManager stateManager, TaskExecutor taskExecutor, PreferencesService preferencesService) {
this.libraryTab = libraryTab;
this.dialogService = dialogService;
this.stateManager = stateManager;
+ this.taskExecutor = taskExecutor;
+ this.preferencesService = preferencesService;
this.executable.bind(ActionHelper.needsEntriesSelected(1, stateManager)
.and(ActionHelper.isAnyFieldSetForSelectedEntry(FetchAndMergeEntry.SUPPORTED_FIELDS, stateManager)));
@@ -35,6 +40,6 @@ public void execute() {
}
BibEntry originalEntry = stateManager.getSelectedEntries().get(0);
- new FetchAndMergeEntry(libraryTab, Globals.TASK_EXECUTOR).fetchAndMerge(originalEntry);
+ new FetchAndMergeEntry(libraryTab, taskExecutor, preferencesService, dialogService).fetchAndMerge(originalEntry);
}
}
diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/DiffMethod.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/DiffMethod.java
new file mode 100644
index 00000000000..764a78ba420
--- /dev/null
+++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/DiffMethod.java
@@ -0,0 +1,5 @@
+package org.jabref.gui.mergeentries.newmergedialog;
+
+public interface DiffMethod {
+ public String separator();
+}
diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/FieldRowView.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/FieldRowView.java
index e6e1cb56f6a..16d104a572e 100644
--- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/FieldRowView.java
+++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/FieldRowView.java
@@ -6,6 +6,7 @@
import javafx.scene.control.ToggleGroup;
import javafx.scene.layout.GridPane;
+import org.jabref.gui.mergeentries.newmergedialog.FieldRowViewModel.Selection;
import org.jabref.gui.mergeentries.newmergedialog.cell.FieldNameCell;
import org.jabref.gui.mergeentries.newmergedialog.cell.FieldValueCell;
import org.jabref.gui.mergeentries.newmergedialog.cell.MergedFieldCell;
@@ -23,8 +24,6 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import static org.jabref.gui.mergeentries.newmergedialog.FieldRowViewModel.Selection;
-
/**
* A controller class to control left, right and merged field values
*/
diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/GroupDiffMode.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/GroupDiffMode.java
new file mode 100644
index 00000000000..e9e79069e01
--- /dev/null
+++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/GroupDiffMode.java
@@ -0,0 +1,15 @@
+package org.jabref.gui.mergeentries.newmergedialog;
+
+public class GroupDiffMode implements DiffMethod {
+
+ private final String separator;
+
+ public GroupDiffMode(String separator) {
+ this.separator = separator;
+ }
+
+ @Override
+ public String separator() {
+ return this.separator;
+ }
+}
diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ShowDiffConfig.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ShowDiffConfig.java
index 62697e3119b..0f36d9ad5d2 100644
--- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ShowDiffConfig.java
+++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ShowDiffConfig.java
@@ -1,7 +1,6 @@
package org.jabref.gui.mergeentries.newmergedialog;
-import static org.jabref.gui.mergeentries.newmergedialog.diffhighlighter.DiffHighlighter.DiffMethod;
-import static org.jabref.gui.mergeentries.newmergedialog.toolbar.ThreeWayMergeToolbar.DiffView;
+import org.jabref.gui.mergeentries.newmergedialog.toolbar.ThreeWayMergeToolbar.DiffView;
public record ShowDiffConfig(
DiffView diffView,
diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java
index f9c34886c86..e2114338fa9 100644
--- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java
+++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java
@@ -11,13 +11,13 @@
import javafx.scene.layout.VBox;
import javafx.stage.Screen;
-import org.jabref.gui.Globals;
import org.jabref.gui.mergeentries.newmergedialog.fieldsmerger.FieldMergerFactory;
import org.jabref.gui.mergeentries.newmergedialog.toolbar.ThreeWayMergeToolbar;
import org.jabref.logic.l10n.Localization;
import org.jabref.model.entry.BibEntry;
import org.jabref.model.entry.field.Field;
import org.jabref.model.entry.field.FieldProperty;
+import org.jabref.preferences.BibEntryPreferences;
public class ThreeWayMergeView extends VBox {
public static final int GRID_COLUMN_MIN_WIDTH = 250;
@@ -38,12 +38,13 @@ public class ThreeWayMergeView extends VBox {
private final List fieldRows = new ArrayList<>();
private final FieldMergerFactory fieldMergerFactory;
+ private final String keywordSeparator;
- public ThreeWayMergeView(BibEntry leftEntry, BibEntry rightEntry, String leftHeader, String rightHeader) {
+ public ThreeWayMergeView(BibEntry leftEntry, BibEntry rightEntry, String leftHeader, String rightHeader, BibEntryPreferences bibEntryPreferences) {
getStylesheets().add(ThreeWayMergeView.class.getResource("ThreeWayMergeView.css").toExternalForm());
viewModel = new ThreeWayMergeViewModel((BibEntry) leftEntry.clone(), (BibEntry) rightEntry.clone(), leftHeader, rightHeader);
- // TODO: Inject 'preferenceService' into the constructor
- this.fieldMergerFactory = new FieldMergerFactory(Globals.prefs);
+ this.fieldMergerFactory = new FieldMergerFactory(bibEntryPreferences);
+ this.keywordSeparator = bibEntryPreferences.getKeywordSeparator().toString();
mergeGridPane = new GridPane();
scrollPane = new ScrollPane();
@@ -62,8 +63,8 @@ public ThreeWayMergeView(BibEntry leftEntry, BibEntry rightEntry, String leftHea
getChildren().addAll(toolbar, headerView, scrollPane);
}
- public ThreeWayMergeView(BibEntry leftEntry, BibEntry rightEntry) {
- this(leftEntry, rightEntry, LEFT_DEFAULT_HEADER, RIGHT_DEFAULT_HEADER);
+ public ThreeWayMergeView(BibEntry leftEntry, BibEntry rightEntry, BibEntryPreferences bibEntryPreferences) {
+ this(leftEntry, rightEntry, LEFT_DEFAULT_HEADER, RIGHT_DEFAULT_HEADER, bibEntryPreferences);
}
private void initializeToolbar() {
@@ -73,11 +74,18 @@ private void initializeToolbar() {
toolbar.showDiffProperty().addListener(e -> updateDiff());
toolbar.diffViewProperty().addListener(e -> updateDiff());
toolbar.diffHighlightingMethodProperty().addListener(e -> updateDiff());
+ this.updateDiff();
}
private void updateDiff() {
if (toolbar.isShowDiffEnabled()) {
- fieldRows.forEach(row -> row.showDiff(new ShowDiffConfig(toolbar.getDiffView(), toolbar.getDiffHighlightingMethod())));
+ for (FieldRowView row : fieldRows) {
+ if (row.getFieldNameCell().getText().equals("Groups") && (row.getLeftValueCell().getText().contains(keywordSeparator) || row.getRightValueCell().getText().contains(keywordSeparator))) {
+ row.showDiff(new ShowDiffConfig(toolbar.getDiffView(), new GroupDiffMode(keywordSeparator)));
+ } else {
+ row.showDiff(new ShowDiffConfig(toolbar.getDiffView(), toolbar.getDiffHighlightingMethod()));
+ }
+ }
} else {
fieldRows.forEach(FieldRowView::hideDiff);
}
diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/diffhighlighter/DiffHighlighter.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/diffhighlighter/DiffHighlighter.java
index 17149e368d6..0e69aed2c1c 100644
--- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/diffhighlighter/DiffHighlighter.java
+++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/diffhighlighter/DiffHighlighter.java
@@ -4,6 +4,8 @@
import java.util.List;
import java.util.Objects;
+import org.jabref.gui.mergeentries.newmergedialog.DiffMethod;
+
import org.fxmisc.richtext.StyleClassedTextArea;
public abstract sealed class DiffHighlighter permits SplitDiffHighlighter, UnifiedDiffHighlighter {
@@ -39,15 +41,16 @@ public String getSeparator() {
return diffMethod.separator();
}
- public enum DiffMethod {
- WORDS(" "), CHARS("");
+ public enum BasicDiffMethod implements DiffMethod {
+ WORDS(" "), CHARS(""), COMMA(",");
private final String separator;
- DiffMethod(String separator) {
+ BasicDiffMethod(String separator) {
this.separator = separator;
}
+ @Override
public String separator() {
return separator;
}
diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/diffhighlighter/SplitDiffHighlighter.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/diffhighlighter/SplitDiffHighlighter.java
index 14570f6b2a8..f5194732eda 100644
--- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/diffhighlighter/SplitDiffHighlighter.java
+++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/diffhighlighter/SplitDiffHighlighter.java
@@ -2,6 +2,8 @@
import java.util.List;
+import org.jabref.gui.mergeentries.newmergedialog.DiffMethod;
+
import com.github.difflib.DiffUtils;
import com.github.difflib.patch.AbstractDelta;
import org.fxmisc.richtext.StyleClassedTextArea;
diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/diffhighlighter/UnifiedDiffHighlighter.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/diffhighlighter/UnifiedDiffHighlighter.java
index 54824f06d61..c15e0420f3b 100644
--- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/diffhighlighter/UnifiedDiffHighlighter.java
+++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/diffhighlighter/UnifiedDiffHighlighter.java
@@ -4,6 +4,8 @@
import java.util.List;
import java.util.Optional;
+import org.jabref.gui.mergeentries.newmergedialog.DiffMethod;
+
import com.github.difflib.DiffUtils;
import com.github.difflib.patch.AbstractDelta;
import com.github.difflib.patch.DeltaType;
@@ -89,7 +91,7 @@ public void highlight() {
appendToTextArea(targetTextview, getSeparator() + join(changeWords), "deletion");
changeInProgress = true;
}
- position = position + changeWords.size() - 1;
+ position = (position + changeWords.size()) - 1;
}
}
if (targetTextview.getLength() >= getSeparator().length()) {
diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/fieldsmerger/FieldMergerFactory.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/fieldsmerger/FieldMergerFactory.java
index 68a68c0359f..24be429302a 100644
--- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/fieldsmerger/FieldMergerFactory.java
+++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/fieldsmerger/FieldMergerFactory.java
@@ -2,20 +2,20 @@
import org.jabref.model.entry.field.Field;
import org.jabref.model.entry.field.StandardField;
-import org.jabref.preferences.PreferencesService;
+import org.jabref.preferences.BibEntryPreferences;
public class FieldMergerFactory {
- private final PreferencesService preferencesService;
+ private final BibEntryPreferences bibEntryPreferences;
- public FieldMergerFactory(PreferencesService preferencesService) {
- this.preferencesService = preferencesService;
+ public FieldMergerFactory(BibEntryPreferences bibEntryPreferences) {
+ this.bibEntryPreferences = bibEntryPreferences;
}
public FieldMerger create(Field field) {
if (field == StandardField.GROUPS) {
return new GroupMerger();
} else if (field == StandardField.KEYWORDS) {
- return new KeywordMerger(preferencesService);
+ return new KeywordMerger(bibEntryPreferences);
} else if (field == StandardField.COMMENT) {
return new CommentMerger();
} else if (field == StandardField.FILE) {
diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/fieldsmerger/KeywordMerger.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/fieldsmerger/KeywordMerger.java
index 3947a185b53..8e7e7540767 100644
--- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/fieldsmerger/KeywordMerger.java
+++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/fieldsmerger/KeywordMerger.java
@@ -4,22 +4,22 @@
import org.jabref.model.entry.KeywordList;
import org.jabref.model.entry.field.StandardField;
-import org.jabref.preferences.PreferencesService;
+import org.jabref.preferences.BibEntryPreferences;
/**
* A merger for the {@link StandardField#KEYWORDS} field
* */
public class KeywordMerger implements FieldMerger {
- private final PreferencesService preferencesService;
+ private final BibEntryPreferences bibEntryPreferences;
- public KeywordMerger(PreferencesService preferencesService) {
- Objects.requireNonNull(preferencesService);
- this.preferencesService = preferencesService;
+ public KeywordMerger(BibEntryPreferences bibEntryPreferences) {
+ Objects.requireNonNull(bibEntryPreferences);
+ this.bibEntryPreferences = bibEntryPreferences;
}
@Override
public String merge(String keywordsA, String keywordsB) {
- Character delimiter = preferencesService.getGroupsPreferences().getKeywordSeparator();
+ Character delimiter = bibEntryPreferences.getKeywordSeparator();
return KeywordList.merge(keywordsA, keywordsB, delimiter).getAsString(delimiter);
}
}
diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/toolbar/ThreeWayMergeToolbar.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/toolbar/ThreeWayMergeToolbar.java
index 08189a7647b..3b24ae8406a 100644
--- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/toolbar/ThreeWayMergeToolbar.java
+++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/toolbar/ThreeWayMergeToolbar.java
@@ -13,15 +13,14 @@
import javafx.scene.layout.AnchorPane;
import javafx.util.StringConverter;
-import org.jabref.gui.mergeentries.newmergedialog.diffhighlighter.DiffHighlighter;
+import org.jabref.gui.mergeentries.newmergedialog.DiffMethod;
+import org.jabref.gui.mergeentries.newmergedialog.diffhighlighter.DiffHighlighter.BasicDiffMethod;
import org.jabref.logic.l10n.Localization;
import com.airhacks.afterburner.views.ViewLoader;
import com.tobiasdiez.easybind.EasyBind;
import com.tobiasdiez.easybind.EasyBinding;
-import static org.jabref.gui.mergeentries.newmergedialog.diffhighlighter.DiffHighlighter.DiffMethod;
-
public class ThreeWayMergeToolbar extends AnchorPane {
@FXML
private RadioButton highlightCharactersRadioButtons;
@@ -44,7 +43,7 @@ public class ThreeWayMergeToolbar extends AnchorPane {
@FXML
private Button selectRightEntryValuesButton;
- private final ObjectProperty diffHighlightingMethod = new SimpleObjectProperty<>();
+ private final ObjectProperty diffHighlightingMethod = new SimpleObjectProperty<>();
private EasyBinding showDiff;
public ThreeWayMergeToolbar() {
@@ -56,9 +55,8 @@ public ThreeWayMergeToolbar() {
@FXML
public void initialize() {
showDiff = EasyBind.map(plainTextOrDiffComboBox.valueProperty(), plainTextOrDiff -> plainTextOrDiff == PlainTextOrDiff.Diff);
-
plainTextOrDiffComboBox.getItems().addAll(PlainTextOrDiff.values());
- plainTextOrDiffComboBox.getSelectionModel().select(PlainTextOrDiff.PLAIN_TEXT);
+ plainTextOrDiffComboBox.getSelectionModel().select(PlainTextOrDiff.Diff);
plainTextOrDiffComboBox.setConverter(new StringConverter<>() {
@Override
public String toString(PlainTextOrDiff plainTextOrDiff) {
@@ -91,13 +89,14 @@ public DiffView fromString(String string) {
diffHighlightingMethodToggleGroup.selectedToggleProperty().addListener((observable -> {
if (diffHighlightingMethodToggleGroup.getSelectedToggle().equals(highlightCharactersRadioButtons)) {
- diffHighlightingMethod.set(DiffMethod.CHARS);
+ diffHighlightingMethod.set(BasicDiffMethod.CHARS);
} else {
- diffHighlightingMethod.set(DiffMethod.WORDS);
+ diffHighlightingMethod.set(BasicDiffMethod.WORDS);
}
}));
diffHighlightingMethodToggleGroup.selectToggle(highlightWordsRadioButton);
+ plainTextOrDiffComboBox.valueProperty().set(PlainTextOrDiff.Diff);
}
public ObjectProperty diffViewProperty() {
diff --git a/src/main/java/org/jabref/gui/openoffice/DetectOpenOfficeInstallation.java b/src/main/java/org/jabref/gui/openoffice/DetectOpenOfficeInstallation.java
index d3f92e668fa..601c26a7ebd 100644
--- a/src/main/java/org/jabref/gui/openoffice/DetectOpenOfficeInstallation.java
+++ b/src/main/java/org/jabref/gui/openoffice/DetectOpenOfficeInstallation.java
@@ -14,7 +14,6 @@
import org.jabref.logic.util.OS;
import org.jabref.logic.util.io.FileUtil;
import org.jabref.model.strings.StringUtil;
-import org.jabref.preferences.PreferencesService;
/**
* Tools for automatically detecting OpenOffice or LibreOffice installations.
@@ -23,12 +22,10 @@ public class DetectOpenOfficeInstallation {
private final OpenOfficePreferences openOfficePreferences;
private final DialogService dialogService;
- private final PreferencesService preferencesService;
- public DetectOpenOfficeInstallation(PreferencesService preferencesService, DialogService dialogService) {
- this.preferencesService = preferencesService;
+ public DetectOpenOfficeInstallation(OpenOfficePreferences openOfficePreferences, DialogService dialogService) {
this.dialogService = dialogService;
- this.openOfficePreferences = preferencesService.getOpenOfficePreferences();
+ this.openOfficePreferences = openOfficePreferences;
}
public boolean isExecutablePathDefined() {
@@ -71,7 +68,6 @@ public boolean setOpenOfficePreferences(Path installDir) {
if (execPath.isPresent()) {
openOfficePreferences.setExecutablePath(execPath.get().toString());
- preferencesService.setOpenOfficePreferences(openOfficePreferences);
return true;
}
diff --git a/src/main/java/org/jabref/gui/openoffice/OpenOfficePanel.java b/src/main/java/org/jabref/gui/openoffice/OpenOfficePanel.java
index 298c0149309..4ddf2ac7a42 100644
--- a/src/main/java/org/jabref/gui/openoffice/OpenOfficePanel.java
+++ b/src/main/java/org/jabref/gui/openoffice/OpenOfficePanel.java
@@ -90,7 +90,6 @@ public class OpenOfficePanel {
private final UndoManager undoManager;
private final TaskExecutor taskExecutor;
private final StyleLoader loader;
- private OpenOfficePreferences openOfficePreferences;
private OOBibBase ooBase;
private OOBibStyle style;
@@ -102,7 +101,6 @@ public OpenOfficePanel(PreferencesService preferencesService,
StateManager stateManager,
UndoManager undoManager) {
ActionFactory factory = new ActionFactory(keyBindingRepository);
- this.openOfficePreferences = openOfficePreferences;
this.preferencesService = preferencesService;
this.taskExecutor = taskExecutor;
this.dialogService = dialogService;
@@ -265,7 +263,7 @@ private void exportEntries() {
private List getBaseList() {
List databases = new ArrayList<>();
- if (openOfficePreferences.getUseAllDatabases()) {
+ if (preferencesService.getOpenOfficePreferences().getUseAllDatabases()) {
for (BibDatabaseContext database : stateManager.getOpenDatabases()) {
databases.add(database.getDatabase());
}
@@ -279,7 +277,7 @@ private List getBaseList() {
}
private void connectAutomatically() {
- DetectOpenOfficeInstallation officeInstallation = new DetectOpenOfficeInstallation(preferencesService, dialogService);
+ DetectOpenOfficeInstallation officeInstallation = new DetectOpenOfficeInstallation(preferencesService.getOpenOfficePreferences(), dialogService);
if (officeInstallation.isExecutablePathDefined()) {
connect();
@@ -313,7 +311,7 @@ private void connectManually() {
var fileDialogConfiguration = new DirectoryDialogConfiguration.Builder().withInitialDirectory(System.getProperty("user.home")).build();
Optional selectedPath = dialogService.showDirectorySelectionDialog(fileDialogConfiguration);
- DetectOpenOfficeInstallation officeInstallation = new DetectOpenOfficeInstallation(preferencesService, dialogService);
+ DetectOpenOfficeInstallation officeInstallation = new DetectOpenOfficeInstallation(preferencesService.getOpenOfficePreferences(), dialogService);
if (selectedPath.isPresent()) {
BackgroundTask.wrap(() -> officeInstallation.setOpenOfficePreferences(selectedPath.get()))
@@ -358,14 +356,12 @@ private void updateButtonAvailability() {
}
private void connect() {
- openOfficePreferences = preferencesService.getOpenOfficePreferences();
-
Task connectTask = new Task<>() {
@Override
protected OOBibBase call() throws Exception {
updateProgress(ProgressBar.INDETERMINATE_PROGRESS, ProgressBar.INDETERMINATE_PROGRESS);
- var path = Path.of(openOfficePreferences.getExecutablePath());
+ var path = Path.of(preferencesService.getOpenOfficePreferences().getExecutablePath());
return createBibBase(path);
}
};
@@ -481,7 +477,7 @@ private void pushEntries(CitationType citationType, boolean addPageInfo) {
}
Optional syncOptions =
- (openOfficePreferences.getSyncWhenCiting()
+ (preferencesService.getOpenOfficePreferences().getSyncWhenCiting()
? Optional.of(new Update.SyncOptions(getBaseList()))
: Optional.empty());
@@ -546,10 +542,12 @@ private boolean checkThatEntriesHaveKeys(List entries) {
}
private ContextMenu createSettingsPopup() {
+ OpenOfficePreferences openOfficePreferences = preferencesService.getOpenOfficePreferences();
+
ContextMenu contextMenu = new ContextMenu();
CheckMenuItem autoSync = new CheckMenuItem(Localization.lang("Automatically sync bibliography when inserting citations"));
- autoSync.selectedProperty().set(openOfficePreferences.getSyncWhenCiting());
+ autoSync.selectedProperty().set(preferencesService.getOpenOfficePreferences().getSyncWhenCiting());
ToggleGroup toggleGroup = new ToggleGroup();
RadioMenuItem useActiveBase = new RadioMenuItem(Localization.lang("Look up BibTeX entries in the active tab only"));
@@ -565,22 +563,12 @@ private ContextMenu createSettingsPopup() {
useActiveBase.setSelected(true);
}
- autoSync.setOnAction(e -> {
- openOfficePreferences.setSyncWhenCiting(autoSync.isSelected());
- preferencesService.setOpenOfficePreferences(openOfficePreferences);
- });
- useAllBases.setOnAction(e -> {
- openOfficePreferences.setUseAllDatabases(useAllBases.isSelected());
- preferencesService.setOpenOfficePreferences(openOfficePreferences);
- });
- useActiveBase.setOnAction(e -> {
- openOfficePreferences.setUseAllDatabases(!useActiveBase.isSelected());
- preferencesService.setOpenOfficePreferences(openOfficePreferences);
- });
+ autoSync.setOnAction(e -> openOfficePreferences.setSyncWhenCiting(autoSync.isSelected()));
+ useAllBases.setOnAction(e -> openOfficePreferences.setUseAllDatabases(useAllBases.isSelected()));
+ useActiveBase.setOnAction(e -> openOfficePreferences.setUseAllDatabases(!useActiveBase.isSelected()));
clearConnectionSettings.setOnAction(e -> {
openOfficePreferences.clearConnectionSettings();
dialogService.notify(Localization.lang("Cleared connection settings"));
- preferencesService.setOpenOfficePreferences(openOfficePreferences);
});
contextMenu.getItems().addAll(
diff --git a/src/main/java/org/jabref/gui/openoffice/StyleSelectDialogViewModel.java b/src/main/java/org/jabref/gui/openoffice/StyleSelectDialogViewModel.java
index b78e69aeba9..91752c95407 100644
--- a/src/main/java/org/jabref/gui/openoffice/StyleSelectDialogViewModel.java
+++ b/src/main/java/org/jabref/gui/openoffice/StyleSelectDialogViewModel.java
@@ -32,21 +32,21 @@
public class StyleSelectDialogViewModel {
private final DialogService dialogService;
- private final StyleLoader loader;
- private final OpenOfficePreferences preferences;
+ private final StyleLoader styleLoader;
+ private final OpenOfficePreferences openOfficePreferences;
private final PreferencesService preferencesService;
private final ListProperty styles = new SimpleListProperty<>(FXCollections.observableArrayList());
private final ObjectProperty selectedItem = new SimpleObjectProperty<>();
- public StyleSelectDialogViewModel(DialogService dialogService, StyleLoader loader, PreferencesService preferencesService) {
+ public StyleSelectDialogViewModel(DialogService dialogService, StyleLoader styleLoader, PreferencesService preferencesService) {
this.dialogService = dialogService;
- this.preferences = preferencesService.getOpenOfficePreferences();
- this.loader = loader;
this.preferencesService = preferencesService;
+ this.openOfficePreferences = preferencesService.getOpenOfficePreferences();
+ this.styleLoader = styleLoader;
styles.addAll(loadStyles());
- String currentStyle = preferences.getCurrentStyle();
+ String currentStyle = openOfficePreferences.getCurrentStyle();
selectedItem.setValue(getStyleOrDefault(currentStyle));
}
@@ -66,8 +66,8 @@ public void addStyleFile() {
.build();
Optional path = dialogService.showFileOpenDialog(fileDialogConfiguration);
path.map(Path::toAbsolutePath).map(Path::toString).ifPresent(stylePath -> {
- if (loader.addStyleIfValid(stylePath)) {
- preferences.setCurrentStyle(stylePath);
+ if (styleLoader.addStyleIfValid(stylePath)) {
+ openOfficePreferences.setCurrentStyle(stylePath);
styles.setAll(loadStyles());
selectedItem.setValue(getStyleOrDefault(stylePath));
} else {
@@ -77,7 +77,7 @@ public void addStyleFile() {
}
public List loadStyles() {
- return loader.getStyles().stream().map(this::fromOOBibStyle).collect(Collectors.toList());
+ return styleLoader.getStyles().stream().map(this::fromOOBibStyle).collect(Collectors.toList());
}
public ListProperty stylesProperty() {
@@ -86,7 +86,7 @@ public ListProperty stylesProperty() {
public void deleteStyle() {
OOBibStyle style = selectedItem.getValue().getStyle();
- if (loader.removeStyle(style)) {
+ if (styleLoader.removeStyle(style)) {
styles.remove(selectedItem.get());
}
}
@@ -117,10 +117,13 @@ public ObjectProperty selectedItemProperty() {
}
public void storePrefs() {
- List externalStyles = styles.stream().map(this::toOOBibStyle).filter(style -> !style.isInternalStyle()).map(OOBibStyle::getPath).collect(Collectors.toList());
- preferences.setExternalStyles(externalStyles);
- preferences.setCurrentStyle(selectedItem.getValue().getStylePath());
- preferencesService.setOpenOfficePreferences(preferences);
+ List externalStyles = styles.stream()
+ .map(this::toOOBibStyle)
+ .filter(style -> !style.isInternalStyle())
+ .map(OOBibStyle::getPath)
+ .collect(Collectors.toList());
+ openOfficePreferences.setExternalStyles(externalStyles);
+ openOfficePreferences.setCurrentStyle(selectedItem.getValue().getStylePath());
}
private StyleSelectItemViewModel getStyleOrDefault(String stylePath) {
diff --git a/src/main/java/org/jabref/gui/preferences/PreferencesDialogView.java b/src/main/java/org/jabref/gui/preferences/PreferencesDialogView.java
index 54c544ae72b..f3a5c245913 100644
--- a/src/main/java/org/jabref/gui/preferences/PreferencesDialogView.java
+++ b/src/main/java/org/jabref/gui/preferences/PreferencesDialogView.java
@@ -93,7 +93,7 @@ private void initialize() {
EasyBind.subscribe(preferenceTabList.getSelectionModel().selectedItemProperty(), tab -> {
if (tab instanceof AbstractPreferenceTabView> preferencesTab) {
preferencesContainer.setContent(preferencesTab.getBuilder());
- preferencesTab.prefWidthProperty().bind(preferencesContainer.widthProperty());
+ preferencesTab.prefWidthProperty().bind(preferencesContainer.widthProperty().subtract(10d));
preferencesTab.getStyleClass().add("preferencesTab");
} else {
preferencesContainer.setContent(null);
diff --git a/src/main/java/org/jabref/gui/preferences/PreferencesDialogViewModel.java b/src/main/java/org/jabref/gui/preferences/PreferencesDialogViewModel.java
index ddb7057aff3..0e5f2f7155f 100644
--- a/src/main/java/org/jabref/gui/preferences/PreferencesDialogViewModel.java
+++ b/src/main/java/org/jabref/gui/preferences/PreferencesDialogViewModel.java
@@ -16,6 +16,7 @@
import org.jabref.gui.preferences.citationkeypattern.CitationKeyPatternTab;
import org.jabref.gui.preferences.customexporter.CustomExporterTab;
import org.jabref.gui.preferences.customimporter.CustomImporterTab;
+import org.jabref.gui.preferences.entry.EntryTab;
import org.jabref.gui.preferences.entryeditor.EntryEditorTab;
import org.jabref.gui.preferences.entryeditortabs.CustomEditorFieldsTab;
import org.jabref.gui.preferences.external.ExternalTab;
@@ -36,12 +37,10 @@
import org.jabref.gui.util.FileDialogConfiguration;
import org.jabref.logic.JabRefException;
import org.jabref.logic.exporter.ExporterFactory;
-import org.jabref.logic.exporter.SavePreferences;
import org.jabref.logic.exporter.TemplateExporter;
import org.jabref.logic.l10n.Localization;
import org.jabref.logic.layout.LayoutFormatterPreferences;
import org.jabref.logic.util.StandardFileType;
-import org.jabref.logic.xmp.XmpPreferences;
import org.jabref.preferences.PreferencesFilter;
import org.jabref.preferences.PreferencesService;
@@ -66,6 +65,7 @@ public PreferencesDialogViewModel(DialogService dialogService, PreferencesServic
new GeneralTab(),
new KeyBindingsTab(),
new FileTab(),
+ new EntryTab(),
new TableTab(),
new PreviewTab(),
new ProtectedTermsTab(),
@@ -169,10 +169,13 @@ private void updateAfterPreferenceChanges() {
List customExporters = preferences.getCustomExportFormats(Globals.journalAbbreviationRepository);
LayoutFormatterPreferences layoutPreferences = preferences.getLayoutFormatterPreferences(Globals.journalAbbreviationRepository);
- SavePreferences savePreferences = preferences.getSavePreferencesForExport();
- XmpPreferences xmpPreferences = preferences.getXmpPreferences();
- Globals.exportFactory = ExporterFactory.create(customExporters, layoutPreferences, savePreferences,
- xmpPreferences, preferences.getGeneralPreferences().getDefaultBibDatabaseMode(), Globals.entryTypesManager);
+ Globals.exportFactory = ExporterFactory.create(
+ customExporters,
+ layoutPreferences,
+ preferences.getSavePreferencesForExport(),
+ preferences.getXmpPreferences(),
+ preferences.getGeneralPreferences().getDefaultBibDatabaseMode(),
+ Globals.entryTypesManager);
frame.getLibraryTabs().forEach(panel -> panel.getMainTable().getTableModel().refresh());
}
diff --git a/src/main/java/org/jabref/gui/preferences/entry/EntryTab.fxml b/src/main/java/org/jabref/gui/preferences/entry/EntryTab.fxml
new file mode 100644
index 00000000000..81028a987d1
--- /dev/null
+++ b/src/main/java/org/jabref/gui/preferences/entry/EntryTab.fxml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/java/org/jabref/gui/preferences/entry/EntryTab.java b/src/main/java/org/jabref/gui/preferences/entry/EntryTab.java
new file mode 100644
index 00000000000..31fc50f0521
--- /dev/null
+++ b/src/main/java/org/jabref/gui/preferences/entry/EntryTab.java
@@ -0,0 +1,65 @@
+package org.jabref.gui.preferences.entry;
+
+import javafx.fxml.FXML;
+import javafx.scene.control.Button;
+import javafx.scene.control.CheckBox;
+import javafx.scene.control.TextField;
+
+import org.jabref.gui.actions.ActionFactory;
+import org.jabref.gui.actions.StandardActions;
+import org.jabref.gui.help.HelpAction;
+import org.jabref.gui.keyboard.KeyBindingRepository;
+import org.jabref.gui.preferences.AbstractPreferenceTabView;
+import org.jabref.gui.preferences.PreferencesTab;
+import org.jabref.logic.help.HelpFile;
+import org.jabref.logic.l10n.Localization;
+
+import com.airhacks.afterburner.views.ViewLoader;
+import jakarta.inject.Inject;
+
+public class EntryTab extends AbstractPreferenceTabView implements PreferencesTab {
+
+ @FXML private TextField keywordSeparator;
+
+ @FXML private CheckBox markOwner;
+ @FXML private TextField markOwnerName;
+ @FXML private CheckBox markOwnerOverwrite;
+ @FXML private Button markOwnerHelp;
+
+ @FXML private CheckBox addCreationDate;
+ @FXML private CheckBox addModificationDate;
+
+ @Inject private KeyBindingRepository keyBindingRepository;
+
+ public EntryTab() {
+ ViewLoader.view(this)
+ .root(this)
+ .load();
+ }
+
+ public void initialize() {
+ this.viewModel = new EntryTabViewModel(
+ preferencesService.getBibEntryPreferences(),
+ preferencesService.getOwnerPreferences(),
+ preferencesService.getTimestampPreferences());
+
+ keywordSeparator.textProperty().bindBidirectional(viewModel.keywordSeparatorProperty());
+
+ markOwner.selectedProperty().bindBidirectional(viewModel.markOwnerProperty());
+ markOwnerName.textProperty().bindBidirectional(viewModel.markOwnerNameProperty());
+ markOwnerName.disableProperty().bind(markOwner.selectedProperty().not());
+ markOwnerOverwrite.selectedProperty().bindBidirectional(viewModel.markOwnerOverwriteProperty());
+ markOwnerOverwrite.disableProperty().bind(markOwner.selectedProperty().not());
+
+ addCreationDate.selectedProperty().bindBidirectional(viewModel.addCreationDateProperty());
+ addModificationDate.selectedProperty().bindBidirectional(viewModel.addModificationDateProperty());
+
+ ActionFactory actionFactory = new ActionFactory(keyBindingRepository);
+ actionFactory.configureIconButton(StandardActions.HELP, new HelpAction(HelpFile.OWNER, dialogService), markOwnerHelp);
+ }
+
+ @Override
+ public String getTabName() {
+ return Localization.lang("Entry");
+ }
+}
diff --git a/src/main/java/org/jabref/gui/preferences/entry/EntryTabViewModel.java b/src/main/java/org/jabref/gui/preferences/entry/EntryTabViewModel.java
new file mode 100644
index 00000000000..2bf37676ac5
--- /dev/null
+++ b/src/main/java/org/jabref/gui/preferences/entry/EntryTabViewModel.java
@@ -0,0 +1,83 @@
+package org.jabref.gui.preferences.entry;
+
+import javafx.beans.property.BooleanProperty;
+import javafx.beans.property.SimpleBooleanProperty;
+import javafx.beans.property.SimpleStringProperty;
+import javafx.beans.property.StringProperty;
+
+import org.jabref.gui.preferences.PreferenceTabViewModel;
+import org.jabref.logic.preferences.OwnerPreferences;
+import org.jabref.logic.preferences.TimestampPreferences;
+import org.jabref.preferences.BibEntryPreferences;
+
+public class EntryTabViewModel implements PreferenceTabViewModel {
+
+ private final StringProperty keywordSeparatorProperty = new SimpleStringProperty("");
+ private final BooleanProperty markOwnerProperty = new SimpleBooleanProperty();
+ private final StringProperty markOwnerNameProperty = new SimpleStringProperty("");
+ private final BooleanProperty markOwnerOverwriteProperty = new SimpleBooleanProperty();
+ private final BooleanProperty addCreationDateProperty = new SimpleBooleanProperty();
+ private final BooleanProperty addModificationDateProperty = new SimpleBooleanProperty();
+
+ private final BibEntryPreferences bibEntryPreferences;
+ private final OwnerPreferences ownerPreferences;
+ private final TimestampPreferences timestampPreferences;
+
+ public EntryTabViewModel(BibEntryPreferences bibEntryPreferences, OwnerPreferences ownerPreferences, TimestampPreferences timestampPreferences) {
+ this.bibEntryPreferences = bibEntryPreferences;
+ this.ownerPreferences = ownerPreferences;
+ this.timestampPreferences = timestampPreferences;
+ }
+
+ @Override
+ public void setValues() {
+ keywordSeparatorProperty.setValue(bibEntryPreferences.getKeywordSeparator().toString());
+
+ markOwnerProperty.setValue(ownerPreferences.isUseOwner());
+ markOwnerNameProperty.setValue(ownerPreferences.getDefaultOwner());
+ markOwnerOverwriteProperty.setValue(ownerPreferences.isOverwriteOwner());
+
+ addCreationDateProperty.setValue(timestampPreferences.shouldAddCreationDate());
+ addModificationDateProperty.setValue(timestampPreferences.shouldAddModificationDate());
+ }
+
+ @Override
+ public void storeSettings() {
+ bibEntryPreferences.keywordSeparatorProperty().setValue(keywordSeparatorProperty.getValue().charAt(0));
+
+ ownerPreferences.setUseOwner(markOwnerProperty.getValue());
+ ownerPreferences.setDefaultOwner(markOwnerNameProperty.getValue());
+ ownerPreferences.setOverwriteOwner(markOwnerOverwriteProperty.getValue());
+
+ timestampPreferences.setAddCreationDate(addCreationDateProperty.getValue());
+ timestampPreferences.setAddModificationDate(addModificationDateProperty.getValue());
+ }
+
+ public StringProperty keywordSeparatorProperty() {
+ return keywordSeparatorProperty;
+ }
+
+ // Entry owner
+
+ public BooleanProperty markOwnerProperty() {
+ return this.markOwnerProperty;
+ }
+
+ public StringProperty markOwnerNameProperty() {
+ return this.markOwnerNameProperty;
+ }
+
+ public BooleanProperty markOwnerOverwriteProperty() {
+ return this.markOwnerOverwriteProperty;
+ }
+
+ // Time stamp
+
+ public BooleanProperty addCreationDateProperty() {
+ return addCreationDateProperty;
+ }
+
+ public BooleanProperty addModificationDateProperty() {
+ return addModificationDateProperty;
+ }
+}
diff --git a/src/main/java/org/jabref/gui/preferences/general/GeneralTab.fxml b/src/main/java/org/jabref/gui/preferences/general/GeneralTab.fxml
index 73a010b459a..9b7382c67b9 100644
--- a/src/main/java/org/jabref/gui/preferences/general/GeneralTab.fxml
+++ b/src/main/java/org/jabref/gui/preferences/general/GeneralTab.fxml
@@ -1,14 +1,10 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/main/java/org/jabref/gui/preferences/general/GeneralTab.java b/src/main/java/org/jabref/gui/preferences/general/GeneralTab.java
index 4f561b8f2a1..be2f4160630 100644
--- a/src/main/java/org/jabref/gui/preferences/general/GeneralTab.java
+++ b/src/main/java/org/jabref/gui/preferences/general/GeneralTab.java
@@ -1,30 +1,20 @@
package org.jabref.gui.preferences.general;
import javafx.fxml.FXML;
-import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ComboBox;
-import javafx.scene.control.TextField;
-import org.jabref.gui.Globals;
-import org.jabref.gui.actions.ActionFactory;
-import org.jabref.gui.actions.StandardActions;
-import org.jabref.gui.help.HelpAction;
import org.jabref.gui.preferences.AbstractPreferenceTabView;
import org.jabref.gui.preferences.PreferencesTab;
-import org.jabref.gui.util.IconValidationDecorator;
import org.jabref.gui.util.ViewModelListCellFactory;
-import org.jabref.logic.help.HelpFile;
import org.jabref.logic.l10n.Language;
import org.jabref.logic.l10n.Localization;
import org.jabref.model.database.BibDatabaseMode;
import com.airhacks.afterburner.views.ViewLoader;
-import de.saxsys.mvvmfx.utils.validation.visualization.ControlsFxVisualizer;
public class GeneralTab extends AbstractPreferenceTabView implements PreferencesTab {
- private final ControlsFxVisualizer validationVisualizer = new ControlsFxVisualizer();
@FXML private ComboBox language;
@FXML private ComboBox biblatexMode;
@FXML private CheckBox inspectionWarningDuplicate;
@@ -32,12 +22,6 @@ public class GeneralTab extends AbstractPreferenceTabView i
@FXML private CheckBox memoryStickMode;
@FXML private CheckBox collectTelemetry;
@FXML private CheckBox showAdvancedHints;
- @FXML private CheckBox markOwner;
- @FXML private TextField markOwnerName;
- @FXML private CheckBox markOwnerOverwrite;
- @FXML private Button markOwnerHelp;
- @FXML private CheckBox addCreationDate;
- @FXML private CheckBox addModificationDate;
public GeneralTab() {
ViewLoader.view(this)
@@ -51,7 +35,7 @@ public String getTabName() {
}
public void initialize() {
- this.viewModel = new GeneralTabViewModel(dialogService, preferencesService, preferencesService.getGeneralPreferences(), preferencesService.getTelemetryPreferences(), preferencesService.getOwnerPreferences(), preferencesService.getTimestampPreferences());
+ this.viewModel = new GeneralTabViewModel(dialogService, preferencesService.getGeneralPreferences(), preferencesService.getTelemetryPreferences());
new ViewModelListCellFactory()
.withText(Language::getDisplayName)
@@ -70,19 +54,5 @@ public void initialize() {
memoryStickMode.selectedProperty().bindBidirectional(viewModel.memoryStickModeProperty());
collectTelemetry.selectedProperty().bindBidirectional(viewModel.collectTelemetryProperty());
showAdvancedHints.selectedProperty().bindBidirectional(viewModel.showAdvancedHintsProperty());
-
- markOwner.selectedProperty().bindBidirectional(viewModel.markOwnerProperty());
- markOwnerName.textProperty().bindBidirectional(viewModel.markOwnerNameProperty());
- markOwnerName.disableProperty().bind(markOwner.selectedProperty().not());
- markOwnerOverwrite.selectedProperty().bindBidirectional(viewModel.markOwnerOverwriteProperty());
- markOwnerOverwrite.disableProperty().bind(markOwner.selectedProperty().not());
-
- addCreationDate.selectedProperty().bindBidirectional(viewModel.addCreationDateProperty());
- addModificationDate.selectedProperty().bindBidirectional(viewModel.addModificationDateProperty());
-
- ActionFactory actionFactory = new ActionFactory(Globals.getKeyPrefs());
- actionFactory.configureIconButton(StandardActions.HELP, new HelpAction(HelpFile.OWNER, dialogService), markOwnerHelp);
-
- validationVisualizer.setDecoration(new IconValidationDecorator());
}
}
diff --git a/src/main/java/org/jabref/gui/preferences/general/GeneralTabViewModel.java b/src/main/java/org/jabref/gui/preferences/general/GeneralTabViewModel.java
index 6e2143f31c4..9566ba81e3e 100644
--- a/src/main/java/org/jabref/gui/preferences/general/GeneralTabViewModel.java
+++ b/src/main/java/org/jabref/gui/preferences/general/GeneralTabViewModel.java
@@ -1,6 +1,5 @@
package org.jabref.gui.preferences.general;
-import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
@@ -11,28 +10,20 @@
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleListProperty;
import javafx.beans.property.SimpleObjectProperty;
-import javafx.beans.property.SimpleStringProperty;
-import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.transformation.SortedList;
import org.jabref.gui.DialogService;
import org.jabref.gui.preferences.PreferenceTabViewModel;
-import org.jabref.logic.l10n.Encodings;
import org.jabref.logic.l10n.Language;
import org.jabref.logic.l10n.Localization;
-import org.jabref.logic.preferences.OwnerPreferences;
-import org.jabref.logic.preferences.TimestampPreferences;
import org.jabref.model.database.BibDatabaseMode;
import org.jabref.preferences.GeneralPreferences;
-import org.jabref.preferences.PreferencesService;
import org.jabref.preferences.TelemetryPreferences;
public class GeneralTabViewModel implements PreferenceTabViewModel {
private final ListProperty languagesListProperty = new SimpleListProperty<>();
private final ObjectProperty selectedLanguageProperty = new SimpleObjectProperty<>();
- private final ListProperty encodingsListProperty = new SimpleListProperty<>();
- private final ObjectProperty selectedEncodingProperty = new SimpleObjectProperty<>();
private final ListProperty bibliographyModeListProperty = new SimpleListProperty<>();
private final ObjectProperty selectedBiblatexModeProperty = new SimpleObjectProperty<>();
@@ -41,36 +32,23 @@ public class GeneralTabViewModel implements PreferenceTabViewModel {
private final BooleanProperty memoryStickModeProperty = new SimpleBooleanProperty();
private final BooleanProperty collectTelemetryProperty = new SimpleBooleanProperty();
private final BooleanProperty showAdvancedHintsProperty = new SimpleBooleanProperty();
- private final BooleanProperty markOwnerProperty = new SimpleBooleanProperty();
- private final StringProperty markOwnerNameProperty = new SimpleStringProperty("");
- private final BooleanProperty markOwnerOverwriteProperty = new SimpleBooleanProperty();
- private final BooleanProperty addCreationDateProperty = new SimpleBooleanProperty();
- private final BooleanProperty addModificationDateProperty = new SimpleBooleanProperty();
private final DialogService dialogService;
private final GeneralPreferences generalPreferences;
- private final PreferencesService preferencesService;
private final TelemetryPreferences telemetryPreferences;
- private final OwnerPreferences ownerPreferences;
- private final TimestampPreferences timestampPreferences;
- private List restartWarning = new ArrayList<>();
+ private final List restartWarning = new ArrayList<>();
@SuppressWarnings("ReturnValueIgnored")
- public GeneralTabViewModel(DialogService dialogService, PreferencesService preferencesService, GeneralPreferences generalPreferences, TelemetryPreferences telemetryPreferences, OwnerPreferences ownerPreferences, TimestampPreferences timestampPreferences) {
+ public GeneralTabViewModel(DialogService dialogService, GeneralPreferences generalPreferences, TelemetryPreferences telemetryPreferences) {
this.dialogService = dialogService;
this.generalPreferences = generalPreferences;
- this.preferencesService = preferencesService;
this.telemetryPreferences = telemetryPreferences;
- this.ownerPreferences = ownerPreferences;
- this.timestampPreferences = timestampPreferences;
}
public void setValues() {
languagesListProperty.setValue(new SortedList<>(FXCollections.observableArrayList(Language.values()), Comparator.comparing(Language::getDisplayName)));
- selectedLanguageProperty.setValue(preferencesService.getGeneralPreferences().getLanguage());
-
- encodingsListProperty.setValue(FXCollections.observableArrayList(Encodings.getCharsets()));
+ selectedLanguageProperty.setValue(generalPreferences.getLanguage());
bibliographyModeListProperty.setValue(FXCollections.observableArrayList(BibDatabaseMode.values()));
selectedBiblatexModeProperty.setValue(generalPreferences.getDefaultBibDatabaseMode());
@@ -80,19 +58,12 @@ public void setValues() {
memoryStickModeProperty.setValue(generalPreferences.isMemoryStickMode());
collectTelemetryProperty.setValue(telemetryPreferences.shouldCollectTelemetry());
showAdvancedHintsProperty.setValue(generalPreferences.shouldShowAdvancedHints());
-
- markOwnerProperty.setValue(ownerPreferences.isUseOwner());
- markOwnerNameProperty.setValue(ownerPreferences.getDefaultOwner());
- markOwnerOverwriteProperty.setValue(ownerPreferences.isOverwriteOwner());
-
- addCreationDateProperty.setValue(timestampPreferences.shouldAddCreationDate());
- addModificationDateProperty.setValue(timestampPreferences.shouldAddModificationDate());
}
public void storeSettings() {
Language newLanguage = selectedLanguageProperty.getValue();
- if (newLanguage != preferencesService.getGeneralPreferences().getLanguage()) {
- preferencesService.getGeneralPreferences().setLanguage(newLanguage);
+ if (newLanguage != generalPreferences.getLanguage()) {
+ generalPreferences.setLanguage(newLanguage);
Localization.setLanguage(newLanguage);
restartWarning.add(Localization.lang("Changed language") + ": " + newLanguage.getDisplayName());
}
@@ -110,13 +81,6 @@ public void storeSettings() {
generalPreferences.setShowAdvancedHints(showAdvancedHintsProperty.getValue());
telemetryPreferences.setCollectTelemetry(collectTelemetryProperty.getValue());
-
- ownerPreferences.setUseOwner(markOwnerProperty.getValue());
- ownerPreferences.setDefaultOwner(markOwnerNameProperty.getValue());
- ownerPreferences.setOverwriteOwner(markOwnerOverwriteProperty.getValue());
-
- timestampPreferences.setAddCreationDate(addCreationDateProperty.getValue());
- timestampPreferences.setAddModificationDate(addModificationDateProperty.getValue());
}
@Override
@@ -134,14 +98,6 @@ public ObjectProperty selectedLanguageProperty() {
return this.selectedLanguageProperty;
}
- public ListProperty encodingsListProperty() {
- return this.encodingsListProperty;
- }
-
- public ObjectProperty selectedEncodingProperty() {
- return this.selectedEncodingProperty;
- }
-
public ListProperty biblatexModeListProperty() {
return this.bibliographyModeListProperty;
}
@@ -169,28 +125,4 @@ public BooleanProperty collectTelemetryProperty() {
public BooleanProperty showAdvancedHintsProperty() {
return this.showAdvancedHintsProperty;
}
-
- // Entry owner
-
- public BooleanProperty markOwnerProperty() {
- return this.markOwnerProperty;
- }
-
- public StringProperty markOwnerNameProperty() {
- return this.markOwnerNameProperty;
- }
-
- public BooleanProperty markOwnerOverwriteProperty() {
- return this.markOwnerOverwriteProperty;
- }
-
- // Time stamp
-
- public BooleanProperty addCreationDateProperty() {
- return addCreationDateProperty;
- }
-
- public BooleanProperty addModificationDateProperty() {
- return addModificationDateProperty;
- }
}
diff --git a/src/main/java/org/jabref/gui/preferences/groups/GroupsTab.fxml b/src/main/java/org/jabref/gui/preferences/groups/GroupsTab.fxml
index 74e95b91e59..dafe1478ab5 100644
--- a/src/main/java/org/jabref/gui/preferences/groups/GroupsTab.fxml
+++ b/src/main/java/org/jabref/gui/preferences/groups/GroupsTab.fxml
@@ -3,9 +3,7 @@
-
-
-
-
-
-
-
diff --git a/src/main/java/org/jabref/gui/preferences/groups/GroupsTab.java b/src/main/java/org/jabref/gui/preferences/groups/GroupsTab.java
index 053967702e0..1bb3bb1f2bb 100644
--- a/src/main/java/org/jabref/gui/preferences/groups/GroupsTab.java
+++ b/src/main/java/org/jabref/gui/preferences/groups/GroupsTab.java
@@ -3,7 +3,6 @@
import javafx.fxml.FXML;
import javafx.scene.control.CheckBox;
import javafx.scene.control.RadioButton;
-import javafx.scene.control.TextField;
import org.jabref.gui.preferences.AbstractPreferenceTabView;
import org.jabref.gui.preferences.PreferencesTab;
@@ -17,7 +16,6 @@ public class GroupsTab extends AbstractPreferenceTabView imp
@FXML private RadioButton groupViewModeUnion;
@FXML private CheckBox autoAssignGroup;
@FXML private CheckBox displayGroupCount;
- @FXML private TextField keywordSeparator;
public GroupsTab() {
ViewLoader.view(this)
@@ -31,12 +29,11 @@ public String getTabName() {
}
public void initialize() {
- this.viewModel = new GroupsTabViewModel(dialogService, preferencesService.getGroupsPreferences());
+ this.viewModel = new GroupsTabViewModel(preferencesService.getGroupsPreferences());
groupViewModeIntersection.selectedProperty().bindBidirectional(viewModel.groupViewModeIntersectionProperty());
groupViewModeUnion.selectedProperty().bindBidirectional(viewModel.groupViewModeUnionProperty());
autoAssignGroup.selectedProperty().bindBidirectional(viewModel.autoAssignGroupProperty());
displayGroupCount.selectedProperty().bindBidirectional(viewModel.displayGroupCount());
- keywordSeparator.textProperty().bindBidirectional(viewModel.keywordSeparatorProperty());
}
}
diff --git a/src/main/java/org/jabref/gui/preferences/groups/GroupsTabViewModel.java b/src/main/java/org/jabref/gui/preferences/groups/GroupsTabViewModel.java
index b3a0a8a4a8b..84286ab62f3 100644
--- a/src/main/java/org/jabref/gui/preferences/groups/GroupsTabViewModel.java
+++ b/src/main/java/org/jabref/gui/preferences/groups/GroupsTabViewModel.java
@@ -2,10 +2,7 @@
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
-import javafx.beans.property.SimpleStringProperty;
-import javafx.beans.property.StringProperty;
-import org.jabref.gui.DialogService;
import org.jabref.gui.groups.GroupViewMode;
import org.jabref.gui.groups.GroupsPreferences;
import org.jabref.gui.preferences.PreferenceTabViewModel;
@@ -16,13 +13,10 @@ public class GroupsTabViewModel implements PreferenceTabViewModel {
private final BooleanProperty groupViewModeUnionProperty = new SimpleBooleanProperty();
private final BooleanProperty autoAssignGroupProperty = new SimpleBooleanProperty();
private final BooleanProperty displayGroupCountProperty = new SimpleBooleanProperty();
- private final StringProperty keywordSeparatorProperty = new SimpleStringProperty("");
- private final DialogService dialogService;
private final GroupsPreferences groupsPreferences;
- public GroupsTabViewModel(DialogService dialogService, GroupsPreferences groupsPreferences) {
- this.dialogService = dialogService;
+ public GroupsTabViewModel(GroupsPreferences groupsPreferences) {
this.groupsPreferences = groupsPreferences;
}
@@ -40,7 +34,6 @@ public void setValues() {
}
autoAssignGroupProperty.setValue(groupsPreferences.shouldAutoAssignGroup());
displayGroupCountProperty.setValue(groupsPreferences.shouldDisplayGroupCount());
- keywordSeparatorProperty.setValue(groupsPreferences.getKeywordSeparator().toString());
}
@Override
@@ -48,7 +41,6 @@ public void storeSettings() {
groupsPreferences.setGroupViewMode(groupViewModeIntersectionProperty.getValue() ? GroupViewMode.INTERSECTION : GroupViewMode.UNION);
groupsPreferences.setAutoAssignGroup(autoAssignGroupProperty.getValue());
groupsPreferences.setDisplayGroupCount(displayGroupCountProperty.getValue());
- groupsPreferences.keywordSeparatorProperty().setValue(keywordSeparatorProperty.getValue().charAt(0));
}
public BooleanProperty groupViewModeIntersectionProperty() {
@@ -66,8 +58,4 @@ public BooleanProperty autoAssignGroupProperty() {
public BooleanProperty displayGroupCount() {
return displayGroupCountProperty;
}
-
- public StringProperty keywordSeparatorProperty() {
- return keywordSeparatorProperty;
- }
}
diff --git a/src/main/java/org/jabref/gui/preferences/journals/JournalAbbreviationsTab.fxml b/src/main/java/org/jabref/gui/preferences/journals/JournalAbbreviationsTab.fxml
index 9ccd806b5dc..ad62f73a086 100644
--- a/src/main/java/org/jabref/gui/preferences/journals/JournalAbbreviationsTab.fxml
+++ b/src/main/java/org/jabref/gui/preferences/journals/JournalAbbreviationsTab.fxml
@@ -3,6 +3,7 @@
+
@@ -80,4 +81,8 @@
+
+
+
+
diff --git a/src/main/java/org/jabref/gui/preferences/journals/JournalAbbreviationsTab.java b/src/main/java/org/jabref/gui/preferences/journals/JournalAbbreviationsTab.java
index 37bda9ad270..a915c2b69a8 100644
--- a/src/main/java/org/jabref/gui/preferences/journals/JournalAbbreviationsTab.java
+++ b/src/main/java/org/jabref/gui/preferences/journals/JournalAbbreviationsTab.java
@@ -12,6 +12,7 @@
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
+import javafx.scene.control.CheckBox;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressIndicator;
@@ -57,6 +58,7 @@ public class JournalAbbreviationsTab extends AbstractPreferenceTabView {
filteredAbbreviations.setPredicate(abbreviation -> searchTerm.isEmpty() || abbreviation.containsCaseIndependent(searchTerm));
});
+
+ useFJournal.selectedProperty().bindBidirectional(viewModel.useFJournalProperty());
}
private void setAnimations() {
diff --git a/src/main/java/org/jabref/gui/preferences/journals/JournalAbbreviationsTabViewModel.java b/src/main/java/org/jabref/gui/preferences/journals/JournalAbbreviationsTabViewModel.java
index d83eb917ecb..43d5cf4b18e 100644
--- a/src/main/java/org/jabref/gui/preferences/journals/JournalAbbreviationsTabViewModel.java
+++ b/src/main/java/org/jabref/gui/preferences/journals/JournalAbbreviationsTabViewModel.java
@@ -49,6 +49,7 @@ public class JournalAbbreviationsTabViewModel implements PreferenceTabViewModel
private final SimpleBooleanProperty isLoading = new SimpleBooleanProperty(false);
private final SimpleBooleanProperty isEditableAndRemovable = new SimpleBooleanProperty(false);
private final SimpleBooleanProperty isAbbreviationEditableAndRemovable = new SimpleBooleanProperty(false);
+ private final SimpleBooleanProperty useFJournal = new SimpleBooleanProperty(true);
private final PreferencesService preferences;
private final DialogService dialogService;
@@ -358,7 +359,8 @@ public void storeSettings() {
preferences.storeJournalAbbreviationPreferences(new JournalAbbreviationPreferences(
journalStringList,
- abbreviationsPreferences.getDefaultEncoding()
+ abbreviationsPreferences.getDefaultEncoding(),
+ useFJournal.getValue()
));
if (shouldWriteLists) {
@@ -407,4 +409,8 @@ public SimpleBooleanProperty isAbbreviationEditableAndRemovable() {
public SimpleBooleanProperty isFileRemovableProperty() {
return isFileRemovable;
}
+
+ public SimpleBooleanProperty useFJournalProperty() {
+ return useFJournal;
+ }
}
diff --git a/src/main/java/org/jabref/gui/preferences/network/NetworkTabViewModel.java b/src/main/java/org/jabref/gui/preferences/network/NetworkTabViewModel.java
index c3962e241e8..8af629de4cf 100644
--- a/src/main/java/org/jabref/gui/preferences/network/NetworkTabViewModel.java
+++ b/src/main/java/org/jabref/gui/preferences/network/NetworkTabViewModel.java
@@ -21,7 +21,7 @@
import org.jabref.gui.DialogService;
import org.jabref.gui.Globals;
import org.jabref.gui.preferences.PreferenceTabViewModel;
-import org.jabref.gui.remote.JabRefMessageHandler;
+import org.jabref.gui.remote.CLIMessageHandler;
import org.jabref.gui.util.FileDialogConfiguration;
import org.jabref.logic.l10n.Localization;
import org.jabref.logic.net.ProxyPreferences;
@@ -73,7 +73,7 @@ public class NetworkTabViewModel implements PreferenceTabViewModel {
private final TrustStoreManager trustStoreManager;
- private AtomicBoolean sslCertificatesChanged = new AtomicBoolean(false);
+ private final AtomicBoolean sslCertificatesChanged = new AtomicBoolean(false);
public NetworkTabViewModel(DialogService dialogService, PreferencesService preferences) {
this.dialogService = dialogService;
@@ -139,6 +139,7 @@ public NetworkTabViewModel(DialogService dialogService, PreferencesService prefe
this.trustStoreManager = new TrustStoreManager(Path.of(sslPreferences.getTruststorePath()));
}
+ @Override
public void setValues() {
remoteServerProperty.setValue(remotePreferences.useRemoteServer());
remotePortProperty.setValue(String.valueOf(remotePreferences.getPort()));
@@ -174,6 +175,7 @@ private void setSSLValues() {
});
}
+ @Override
public void storeSettings() {
storeRemoteSettings();
storeProxySettings(new ProxyPreferences(
@@ -201,7 +203,7 @@ private void storeRemoteSettings() {
if (remoteServerProperty.getValue()) {
remotePreferences.setUseRemoteServer(true);
- Globals.REMOTE_LISTENER.openAndStart(new JabRefMessageHandler(), remotePreferences.getPort(), preferences);
+ Globals.REMOTE_LISTENER.openAndStart(new CLIMessageHandler(preferences), remotePreferences.getPort());
} else {
remotePreferences.setUseRemoteServer(false);
Globals.REMOTE_LISTENER.stop();
@@ -256,6 +258,7 @@ public ValidationStatus proxyPasswordValidationStatus() {
return proxyPasswordValidator.getValidationStatus();
}
+ @Override
public boolean validateSettings() {
CompositeValidator validator = new CompositeValidator();
diff --git a/src/main/java/org/jabref/gui/preferences/protectedterms/ProtectedTermsTabViewModel.java b/src/main/java/org/jabref/gui/preferences/protectedterms/ProtectedTermsTabViewModel.java
index 62c09375df6..ac17b25d736 100644
--- a/src/main/java/org/jabref/gui/preferences/protectedterms/ProtectedTermsTabViewModel.java
+++ b/src/main/java/org/jabref/gui/preferences/protectedterms/ProtectedTermsTabViewModel.java
@@ -1,8 +1,7 @@
package org.jabref.gui.preferences.protectedterms;
-import java.io.File;
-import java.io.FileNotFoundException;
import java.io.IOException;
+import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
@@ -57,6 +56,7 @@ public void setValues() {
termsFilesProperty.addAll(termsLoader.getProtectedTermsLists().stream().map(ProtectedTermsListItemModel::new).toList());
}
+ @Override
public void storeSettings() {
List enabledExternalList = new ArrayList<>();
List disabledExternalList = new ArrayList<>();
@@ -97,12 +97,8 @@ public void addFile() {
dialogService.showFileOpenDialog(fileDialogConfiguration)
.ifPresent(file -> {
- String fileName = file.toAbsolutePath().toString();
- try {
- termsFilesProperty.add(new ProtectedTermsListItemModel(ProtectedTermsLoader.readProtectedTermsListFromFile(new File(fileName), true)));
- } catch (FileNotFoundException e) {
- LOGGER.warn("Cannot find protected terms file " + fileName, e);
- }
+ Path fileName = file.toAbsolutePath();
+ termsFilesProperty.add(new ProtectedTermsListItemModel(ProtectedTermsLoader.readProtectedTermsListFromFile(fileName, true)));
});
}
@@ -155,16 +151,12 @@ public void displayContent(ProtectedTermsListItemModel itemModel) {
public void reloadList(ProtectedTermsListItemModel oldItemModel) {
ProtectedTermsList oldList = oldItemModel.getTermsList();
- try {
- ProtectedTermsList newList = ProtectedTermsLoader.readProtectedTermsListFromFile(new File(oldList.getLocation()), oldList.isEnabled());
- int index = termsFilesProperty.indexOf(oldItemModel);
- if (index >= 0) {
- termsFilesProperty.set(index, new ProtectedTermsListItemModel(newList));
- } else {
- LOGGER.warn("Problem reloading protected terms file {}.", oldList.getLocation());
- }
- } catch (IOException e) {
- LOGGER.warn("Problem reloading protected terms file {}.", oldList.getLocation(), e);
+ ProtectedTermsList newList = ProtectedTermsLoader.readProtectedTermsListFromFile(Path.of(oldList.getLocation()), oldList.isEnabled());
+ int index = termsFilesProperty.indexOf(oldItemModel);
+ if (index >= 0) {
+ termsFilesProperty.set(index, new ProtectedTermsListItemModel(newList));
+ } else {
+ LOGGER.warn("Problem reloading protected terms file {}.", oldList.getLocation());
}
}
diff --git a/src/main/java/org/jabref/gui/remote/JabRefMessageHandler.java b/src/main/java/org/jabref/gui/remote/CLIMessageHandler.java
similarity index 74%
rename from src/main/java/org/jabref/gui/remote/JabRefMessageHandler.java
rename to src/main/java/org/jabref/gui/remote/CLIMessageHandler.java
index a93e78f15a7..8a796e45be6 100644
--- a/src/main/java/org/jabref/gui/remote/JabRefMessageHandler.java
+++ b/src/main/java/org/jabref/gui/remote/CLIMessageHandler.java
@@ -7,18 +7,24 @@
import org.jabref.cli.ArgumentProcessor;
import org.jabref.gui.JabRefGUI;
import org.jabref.logic.importer.ParserResult;
-import org.jabref.logic.remote.server.MessageHandler;
+import org.jabref.logic.remote.server.RemoteMessageHandler;
import org.jabref.preferences.PreferencesService;
import org.apache.commons.cli.ParseException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-public class JabRefMessageHandler implements MessageHandler {
- private static final Logger LOGGER = LoggerFactory.getLogger(JabRefMessageHandler.class);
+public class CLIMessageHandler implements RemoteMessageHandler {
+ private static final Logger LOGGER = LoggerFactory.getLogger(CLIMessageHandler.class);
+
+ private final PreferencesService preferencesService;
+
+ public CLIMessageHandler(PreferencesService preferencesService) {
+ this.preferencesService = preferencesService;
+ }
@Override
- public void handleCommandLineArguments(String[] message, PreferencesService preferencesService) {
+ public void handleCommandLineArguments(String[] message) {
try {
ArgumentProcessor argumentProcessor = new ArgumentProcessor(message, ArgumentProcessor.Mode.REMOTE_START, preferencesService);
diff --git a/src/main/java/org/jabref/gui/shared/SharedDatabaseLoginDialogViewModel.java b/src/main/java/org/jabref/gui/shared/SharedDatabaseLoginDialogViewModel.java
index c0f07359b12..36411223cae 100644
--- a/src/main/java/org/jabref/gui/shared/SharedDatabaseLoginDialogViewModel.java
+++ b/src/main/java/org/jabref/gui/shared/SharedDatabaseLoginDialogViewModel.java
@@ -157,7 +157,7 @@ private boolean openSharedDatabase(DBMSConnectionProperties connectionProperties
loading.set(true);
try {
- SharedDatabaseUIManager manager = new SharedDatabaseUIManager(frame);
+ SharedDatabaseUIManager manager = new SharedDatabaseUIManager(frame, preferencesService);
LibraryTab libraryTab = manager.openNewSharedDatabaseTab(connectionProperties);
setPreferences();
@@ -253,7 +253,7 @@ private void applyPreferences() {
}
rememberPassword.set(sharedDatabaseRememberPassword);
-
+
sharedDatabaseFolder.ifPresent(folder::set);
autosave.set(sharedDatabaseAutosave);
}
diff --git a/src/main/java/org/jabref/gui/shared/SharedDatabaseUIManager.java b/src/main/java/org/jabref/gui/shared/SharedDatabaseUIManager.java
index 0a04e325fe9..24530b79e0d 100644
--- a/src/main/java/org/jabref/gui/shared/SharedDatabaseUIManager.java
+++ b/src/main/java/org/jabref/gui/shared/SharedDatabaseUIManager.java
@@ -33,6 +33,7 @@
import org.jabref.logic.shared.prefs.SharedDatabasePreferences;
import org.jabref.model.database.BibDatabaseContext;
import org.jabref.model.entry.BibEntry;
+import org.jabref.preferences.PreferencesService;
import com.google.common.eventbus.Subscribe;
@@ -41,10 +42,12 @@ public class SharedDatabaseUIManager {
private final JabRefFrame jabRefFrame;
private DatabaseSynchronizer dbmsSynchronizer;
private final DialogService dialogService;
+ private final PreferencesService preferencesService;
- public SharedDatabaseUIManager(JabRefFrame jabRefFrame) {
+ public SharedDatabaseUIManager(JabRefFrame jabRefFrame, PreferencesService preferencesService) {
this.jabRefFrame = jabRefFrame;
this.dialogService = jabRefFrame.getDialogService();
+ this.preferencesService = preferencesService;
}
@Subscribe
@@ -81,25 +84,19 @@ public void listen(UpdateRefusedEvent updateRefusedEvent) {
BibEntry localBibEntry = updateRefusedEvent.getLocalBibEntry();
BibEntry sharedBibEntry = updateRefusedEvent.getSharedBibEntry();
- StringBuilder message = new StringBuilder();
- message.append(Localization.lang("Update could not be performed due to existing change conflicts."));
- message.append("\r\n");
- message.append(Localization.lang("You are not working on the newest version of BibEntry."));
- message.append("\r\n");
- message.append(Localization.lang("Shared version: %0", String.valueOf(sharedBibEntry.getSharedBibEntryData().getVersion())));
- message.append("\r\n");
- message.append(Localization.lang("Local version: %0", String.valueOf(localBibEntry.getSharedBibEntryData().getVersion())));
- message.append("\r\n");
- message.append(Localization.lang("Press \"Merge entries\" to merge the changes and resolve this problem."));
- message.append("\r\n");
- message.append(Localization.lang("Canceling this operation will leave your changes unsynchronized."));
+ String message = Localization.lang("Update could not be performed due to existing change conflicts.") + "\r\n" +
+ Localization.lang("You are not working on the newest version of BibEntry.") + "\r\n" +
+ Localization.lang("Shared version: %0", String.valueOf(sharedBibEntry.getSharedBibEntryData().getVersion())) + "\r\n" +
+ Localization.lang("Local version: %0", String.valueOf(localBibEntry.getSharedBibEntryData().getVersion())) + "\r\n" +
+ Localization.lang("Press \"Merge entries\" to merge the changes and resolve this problem.") + "\r\n" +
+ Localization.lang("Canceling this operation will leave your changes unsynchronized.");
ButtonType merge = new ButtonType(Localization.lang("Merge entries"), ButtonBar.ButtonData.YES);
- Optional response = dialogService.showCustomButtonDialogAndWait(AlertType.CONFIRMATION, Localization.lang("Update refused"), message.toString(), ButtonType.CANCEL, merge);
+ Optional response = dialogService.showCustomButtonDialogAndWait(AlertType.CONFIRMATION, Localization.lang("Update refused"), message, ButtonType.CANCEL, merge);
if (response.isPresent() && response.get().equals(merge)) {
- MergeEntriesDialog dialog = new MergeEntriesDialog(localBibEntry, sharedBibEntry);
+ MergeEntriesDialog dialog = new MergeEntriesDialog(localBibEntry, sharedBibEntry, preferencesService.getBibEntryPreferences());
dialog.setTitle(Localization.lang("Update refused"));
Optional mergedEntry = dialogService.showCustomDialogAndWait(dialog).map(EntriesMergeResult::mergedEntry);
@@ -139,8 +136,12 @@ public LibraryTab openNewSharedDatabaseTab(DBMSConnectionProperties dbmsConnecti
throws SQLException, DatabaseNotSupportedException, InvalidDBMSConnectionPropertiesException {
BibDatabaseContext bibDatabaseContext = new BibDatabaseContext();
- bibDatabaseContext.setMode(Globals.prefs.getGeneralPreferences().getDefaultBibDatabaseMode());
- DBMSSynchronizer synchronizer = new DBMSSynchronizer(bibDatabaseContext, Globals.prefs.getKeywordDelimiter(), Globals.prefs.getGlobalCitationKeyPattern(), Globals.getFileUpdateMonitor());
+ bibDatabaseContext.setMode(preferencesService.getGeneralPreferences().getDefaultBibDatabaseMode());
+ DBMSSynchronizer synchronizer = new DBMSSynchronizer(
+ bibDatabaseContext,
+ preferencesService.getBibEntryPreferences().getKeywordSeparator(),
+ preferencesService.getGlobalCitationKeyPattern(),
+ Globals.getFileUpdateMonitor());
bibDatabaseContext.convertToSharedDatabase(synchronizer);
dbmsSynchronizer = bibDatabaseContext.getDBMSSynchronizer();
@@ -164,8 +165,12 @@ public void openSharedDatabaseFromParserResult(ParserResult parserResult)
DBMSConnectionProperties dbmsConnectionProperties = new DBMSConnectionProperties(new SharedDatabasePreferences(sharedDatabaseID));
BibDatabaseContext bibDatabaseContext = new BibDatabaseContext();
- bibDatabaseContext.setMode(Globals.prefs.getGeneralPreferences().getDefaultBibDatabaseMode());
- DBMSSynchronizer synchronizer = new DBMSSynchronizer(bibDatabaseContext, Globals.prefs.getKeywordDelimiter(), Globals.prefs.getGlobalCitationKeyPattern(), Globals.getFileUpdateMonitor());
+ bibDatabaseContext.setMode(preferencesService.getGeneralPreferences().getDefaultBibDatabaseMode());
+ DBMSSynchronizer synchronizer = new DBMSSynchronizer(
+ bibDatabaseContext,
+ preferencesService.getBibEntryPreferences().getKeywordSeparator(),
+ preferencesService.getGlobalCitationKeyPattern(),
+ Globals.getFileUpdateMonitor());
bibDatabaseContext.convertToSharedDatabase(synchronizer);
bibDatabaseContext.getDatabase().setSharedDatabaseID(sharedDatabaseID);
diff --git a/src/main/java/org/jabref/gui/theme/ThemeManager.java b/src/main/java/org/jabref/gui/theme/ThemeManager.java
index de6a9f197fa..b6f42071d76 100644
--- a/src/main/java/org/jabref/gui/theme/ThemeManager.java
+++ b/src/main/java/org/jabref/gui/theme/ThemeManager.java
@@ -213,7 +213,7 @@ public void updateFontStyle(Scene scene) {
if (appearancePreferences.shouldOverrideDefaultFontSize()) {
scene.getRoot().setStyle("-fx-font-size: " + appearancePreferences.getMainFontSize() + "pt;");
} else {
- scene.getRoot().setStyle("");
+ scene.getRoot().setStyle("-fx-font-size: " + appearancePreferences.getDefaultFontSize() + "pt;");
}
}
diff --git a/src/main/java/org/jabref/gui/util/component/TemporalAccessorPicker.java b/src/main/java/org/jabref/gui/util/component/TemporalAccessorPicker.java
index f2aecc17947..bd572e2deb5 100644
--- a/src/main/java/org/jabref/gui/util/component/TemporalAccessorPicker.java
+++ b/src/main/java/org/jabref/gui/util/component/TemporalAccessorPicker.java
@@ -7,6 +7,7 @@
import java.time.Year;
import java.time.YearMonth;
import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalQueries;
import java.util.Objects;
@@ -21,6 +22,8 @@
import org.jabref.gui.fieldeditors.TextInputControlBehavior;
import org.jabref.gui.fieldeditors.contextmenu.EditorContextAction;
import org.jabref.gui.util.BindingsHelper;
+import org.jabref.model.entry.Date;
+import org.jabref.model.strings.StringUtil;
/**
* A date picker with configurable datetime format where both date and time can be changed via the text field and the
@@ -75,6 +78,11 @@ private static LocalDate getDate(TemporalAccessor temporalAccessor) {
}
private static LocalDate getLocalDate(TemporalAccessor dateTime) {
+ // Return null when dateTime is null pointer
+ if (dateTime == null) {
+ return null;
+ }
+
// Try to get as much information from the temporal accessor
LocalDate date = dateTime.query(TemporalQueries.localDate());
if (date != null) {
@@ -101,7 +109,15 @@ public String toString(TemporalAccessor value) {
@Override
public TemporalAccessor fromString(String value) {
- return LocalDateTime.parse(value, defaultFormatter);
+ if (StringUtil.isNotBlank(value)) {
+ try {
+ return defaultFormatter.parse(value);
+ } catch (DateTimeParseException exception) {
+ return Date.parse(value).map(Date::toTemporalAccessor).orElse(null);
+ }
+ } else {
+ return null;
+ }
}
};
return Objects.requireNonNullElseGet(stringConverterProperty().get(), () -> newConverter);
@@ -127,7 +143,9 @@ private class InternalConverter extends StringConverter {
@Override
public String toString(LocalDate object) {
TemporalAccessor value = getTemporalAccessorValue();
- return (value != null) ? getStringConverter().toString(value) : "";
+
+ // Keeps the original text when it is an invalid date
+ return (value != null) ? getStringConverter().toString(value) : getEditor().getText();
}
@Override
@@ -139,6 +157,7 @@ public LocalDate fromString(String value) {
TemporalAccessor dateTime = getStringConverter().fromString(value);
temporalAccessorValue.set(dateTime);
+
return getLocalDate(dateTime);
}
}
diff --git a/src/main/java/org/jabref/logic/autosaveandbackup/BackupManager.java b/src/main/java/org/jabref/logic/autosaveandbackup/BackupManager.java
index 11228558c48..c88930619dd 100644
--- a/src/main/java/org/jabref/logic/autosaveandbackup/BackupManager.java
+++ b/src/main/java/org/jabref/logic/autosaveandbackup/BackupManager.java
@@ -66,7 +66,7 @@ public class BackupManager {
private boolean needsBackup = true;
- private BackupManager(BibDatabaseContext bibDatabaseContext, BibEntryTypesManager entryTypesManager, PreferencesService preferences) {
+ BackupManager(BibDatabaseContext bibDatabaseContext, BibEntryTypesManager entryTypesManager, PreferencesService preferences) {
this.bibDatabaseContext = bibDatabaseContext;
this.entryTypesManager = entryTypesManager;
this.preferences = preferences;
@@ -105,6 +105,16 @@ public static BackupManager start(BibDatabaseContext bibDatabaseContext, BibEntr
return backupManager;
}
+ /**
+ * Marks the backup as discarded at the library which is associated with the given {@link BibDatabaseContext}.
+ *
+ * @param bibDatabaseContext Associated {@link BibDatabaseContext}
+ */
+ public static void discardBackup(BibDatabaseContext bibDatabaseContext) {
+ runningInstances.stream().filter(instance -> instance.bibDatabaseContext == bibDatabaseContext).forEach(
+ BackupManager::discardBackup);
+ }
+
/**
* Shuts down the BackupManager which is associated with the given {@link BibDatabaseContext}.
*
@@ -120,13 +130,25 @@ public static void shutdown(BibDatabaseContext bibDatabaseContext) {
* Checks whether a backup file exists for the given database file. If it exists, it is checked whether it is
* newer and different from the original.
*
+ * In case a discarded file is present, the method also returns false
, See also {@link #discardBackup()}.
+ *
* @param originalPath Path to the file a backup should be checked for. Example: jabref.bib.
*
* @return true
if backup file exists AND differs from originalPath. false
is the
- * "default" return value in the good case. In the case of an exception true
is returned to ensure that
- * the user checks the output.
+ * "default" return value in the good case. In case a discarded file exists, false
is returned, too.
+ * In the case of an exception true
is returned to ensure that the user checks the output.
*/
public static boolean backupFileDiffers(Path originalPath) {
+ Path discardedFile = determineDiscardedFile(originalPath);
+ if (Files.exists(discardedFile)) {
+ try {
+ Files.delete(discardedFile);
+ } catch (IOException e) {
+ LOGGER.error("Could not remove discarded file {}", discardedFile, e);
+ return true;
+ }
+ return false;
+ }
return getLatestBackupPath(originalPath).map(latestBackupPath -> {
FileTime latestBackupFileLastModifiedTime;
try {
@@ -177,7 +199,7 @@ public static void restoreBackup(Path originalPath) {
}
}
- private Optional determineBackupPathForNewBackup() {
+ Optional determineBackupPathForNewBackup() {
return bibDatabaseContext.getDatabasePath().map(BackupManager::getBackupPathForNewBackup);
}
@@ -188,7 +210,7 @@ private Optional determineBackupPathForNewBackup() {
*
* @param backupPath the path where the library should be backed up to
*/
- private void performBackup(Path backupPath) {
+ void performBackup(Path backupPath) {
if (!needsBackup) {
return;
}
@@ -226,6 +248,27 @@ private void performBackup(Path backupPath) {
}
}
+ private static Path determineDiscardedFile(Path file) {
+ return BackupFileUtil.getAppDataBackupDir().resolve(
+ BackupFileUtil.getUniqueFilePrefix(file) + "--" + file.getFileName() + "--discarded"
+ );
+ }
+
+ /**
+ * Marks the backups as discarded.
+ *
+ * We do not delete any files, because the user might want to recover old backup files.
+ * Therefore, we mark discarded backups by a --discarded file.
+ */
+ public void discardBackup() {
+ Path path = determineDiscardedFile(bibDatabaseContext.getDatabasePath().get());
+ try {
+ Files.createFile(path);
+ } catch (IOException e) {
+ LOGGER.info("Could not create backup file {}", path, e);
+ }
+ }
+
private void logIfCritical(Path backupPath, IOException e) {
Throwable innermostCause = e;
while (innermostCause.getCause() != null) {
diff --git a/src/main/java/org/jabref/logic/cleanup/ConvertToBiblatexCleanup.java b/src/main/java/org/jabref/logic/cleanup/ConvertToBiblatexCleanup.java
index c6e2ab13309..470d01f335b 100644
--- a/src/main/java/org/jabref/logic/cleanup/ConvertToBiblatexCleanup.java
+++ b/src/main/java/org/jabref/logic/cleanup/ConvertToBiblatexCleanup.java
@@ -21,7 +21,7 @@ public class ConvertToBiblatexCleanup implements CleanupJob {
@Override
public List cleanup(BibEntry entry) {
List changes = new ArrayList<>();
- for (Map.Entry alias : EntryConverter.FIELD_ALIASES_TEX_TO_LTX.entrySet()) {
+ for (Map.Entry alias : EntryConverter.FIELD_ALIASES_BIBTEX_TO_BIBLATEX.entrySet()) {
Field oldField = alias.getKey();
Field newField = alias.getValue();
entry.getField(oldField).ifPresent(oldValue -> {
diff --git a/src/main/java/org/jabref/logic/cleanup/ConvertToBibtexCleanup.java b/src/main/java/org/jabref/logic/cleanup/ConvertToBibtexCleanup.java
index 20a524cfac8..5d14bec9eef 100644
--- a/src/main/java/org/jabref/logic/cleanup/ConvertToBibtexCleanup.java
+++ b/src/main/java/org/jabref/logic/cleanup/ConvertToBibtexCleanup.java
@@ -36,7 +36,7 @@ public List cleanup(BibEntry entry) {
}
});
- for (Map.Entry alias : EntryConverter.FIELD_ALIASES_TEX_TO_LTX.entrySet()) {
+ for (Map.Entry alias : EntryConverter.FIELD_ALIASES_BIBTEX_TO_BIBLATEX.entrySet()) {
Field oldField = alias.getValue();
Field newField = alias.getKey();
entry.getField(oldField).ifPresent(oldValue -> {
diff --git a/src/main/java/org/jabref/logic/cleanup/DoiCleanup.java b/src/main/java/org/jabref/logic/cleanup/DoiCleanup.java
index 0b3b335fa94..3657acabb5f 100644
--- a/src/main/java/org/jabref/logic/cleanup/DoiCleanup.java
+++ b/src/main/java/org/jabref/logic/cleanup/DoiCleanup.java
@@ -1,7 +1,7 @@
package org.jabref.logic.cleanup;
-import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
+import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -34,11 +34,7 @@ public List cleanup(BibEntry entry) {
String doiFieldValue = entry.getField(StandardField.DOI).orElse(null);
String decodeDoiFieldValue = "";
- try {
- decodeDoiFieldValue = URLDecoder.decode(doiFieldValue, "UTF-8");
- } catch (UnsupportedEncodingException e) {
- decodeDoiFieldValue = doiFieldValue;
- }
+ decodeDoiFieldValue = URLDecoder.decode(doiFieldValue, StandardCharsets.UTF_8);
doiFieldValue = decodeDoiFieldValue;
Optional