diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 6151358ef2f..16199f2abf7 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,18 +1,18 @@ +### Mandatory checks -### Mandatory checks - - [ ] Change in `CHANGELOG.md` described in a way that is understandable for the average user (if applicable) - [ ] Tests created for changes (if applicable) - [ ] Manually tested changed features in running JabRef (always required) diff --git a/.github/workflows/add-to-projects.yml b/.github/workflows/add-to-projects.yml index 83ab818bbbb..3729f2bb062 100644 --- a/.github/workflows/add-to-projects.yml +++ b/.github/workflows/add-to-projects.yml @@ -4,6 +4,9 @@ on: issues: types: [labeled] +permissions: + issues: write + jobs: add-to-project: runs-on: ubuntu-latest @@ -12,28 +15,28 @@ jobs: - name: "good first issue" if: "${{ github.event.label.name == 'good first issue' }}" env: - GH_TOKEN: ${{ secrets.PROJECT_TOKEN }} + GH_TOKEN: ${{ github.token }} run: | ISSUE_URL=$(jq --raw-output .issue.html_url "$GITHUB_EVENT_PATH") gh project item-add 5 --owner jabref --url $ISSUE_URL - name: needs-refinement if: github.event.label.name == 'needs-refinement' env: - GH_TOKEN: ${{ secrets.PROJECT_TOKEN }} + GH_TOKEN: ${{ github.token }} run: | ISSUE_URL=$(jq --raw-output .issue.html_url "$GITHUB_EVENT_PATH") gh project item-add 15 --owner jabref --url $ISSUE_URL - name: "status: freeze" if: "${{ github.event.label.name == 'status: freeze' }}" env: - GH_TOKEN: ${{ secrets.PROJECT_TOKEN }} + GH_TOKEN: ${{ github.token }} run: | ISSUE_URL=$(jq --raw-output .issue.html_url "$GITHUB_EVENT_PATH") gh project item-add 9 --owner jabref --url $ISSUE_URL - name: ui if: "${{ github.event.label.name == 'ui' }}" env: - GH_TOKEN: ${{ secrets.PROJECT_TOKEN }} + GH_TOKEN: ${{ github.token }} run: | ISSUE_URL=$(jq --raw-output .issue.html_url "$GITHUB_EVENT_PATH") gh project item-add 8 --owner jabref --url $ISSUE_URL diff --git a/.github/workflows/automerge.yml b/.github/workflows/automerge.yml index f2cc18a22f9..7d339a6355d 100644 --- a/.github/workflows/automerge.yml +++ b/.github/workflows/automerge.yml @@ -10,10 +10,16 @@ jobs: runs-on: ubuntu-latest # Run only if PR is inside JabRef's main repository and created by dependabot or by an update workflow if: > - (github.repository == 'JabRef/jabref') && - ((github.actor == 'dependabot[bot]') || - ((startsWith(github.event.pull_request.title, '[Bot] ') || (startsWith(github.event.pull_request.title, 'Bump '))) && - (github.event.pull_request.head.repo.full_name == 'JabRef/jabref'))) + (github.repository == 'JabRef/jabref') && + (github.event.pull_request.head.repo.full_name == 'JabRef/jabref') && + ( + (github.actor == 'dependabot[bot]') || + ( + startsWith(github.event.pull_request.title, '[Bot] ') || + startsWith(github.event.pull_request.title, 'Bump ') || + startsWith(github.event.pull_request.title, 'New Crowdin updates') + ) + ) steps: - name: Approve PR run: gh pr review --approve "$PR_URL" diff --git a/.github/workflows/cleanup_pr.yml b/.github/workflows/cleanup-pr.yml similarity index 100% rename from .github/workflows/cleanup_pr.yml rename to .github/workflows/cleanup-pr.yml diff --git a/.github/workflows/deployment.yml b/.github/workflows/deployment.yml index 0352cf29551..c23f0ccf516 100644 --- a/.github/workflows/deployment.yml +++ b/.github/workflows/deployment.yml @@ -224,6 +224,13 @@ jobs: # tbn = to-be-notarized name: JabRef-macOS-tbn path: build/distribution + - name: Upload to GitHub workflow artifacts store + if: (steps.checksecrets.outputs.secretspresent != 'YES') + uses: actions/upload-artifact@v3 + with: + # tbn = to-be-notarized + name: JabRef-${{ matrix.os }} + path: build/distribution announce: name: Comment on pull request runs-on: ubuntu-latest @@ -242,7 +249,7 @@ jobs: env: BUILDJABREFPRIVATEKEY: ${{ secrets.buildJabRefPrivateKey }} - name: Comment PR - if: steps.checksecrets.outputs.secretspresent == 'YES' + if: (steps.checksecrets.outputs.secretspresent == 'YES') uses: thollander/actions-comment-pull-request@v2 with: message: | diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 5ec087a8e62..e1be0f8c869 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -136,7 +136,7 @@ jobs: submodules: 'false' show-progress: 'false' - name: markdownlint-cli2-action - uses: DavidAnson/markdownlint-cli2-action@v9 + uses: DavidAnson/markdownlint-cli2-action@v13 with: globs: | *.md @@ -304,6 +304,7 @@ jobs: steps: - name: Check secrets presence id: checksecrets + if: github.ref == 'refs/heads/main' shell: bash run: | if [ "$CODECOV_TOKEN" == "" ]; then @@ -312,25 +313,31 @@ jobs: echo "secretspresent=YES" >> $GITHUB_OUTPUT fi env: - SNAPCRAFT_LOGIN_FILE: ${{ secrets.CODECOV_TOKEN }} + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} - name: Checkout source + if: github.ref == 'refs/heads/main' uses: actions/checkout@v4 with: submodules: 'true' show-progress: 'false' - name: Set up JDK + if: github.ref == 'refs/heads/main' uses: actions/setup-java@v3 with: java-version: 21.0.1 distribution: 'liberica' cache: 'gradle' - name: Update test coverage metrics - if: steps.checksecrets.outputs.secretspresent == 'YES' - run: xvfb-run --auto-servernum ./gradlew jacocoTestReport && bash <(curl -s https://codecov.io/bash); + if: (github.ref == 'refs/heads/main') && (steps.checksecrets.outputs.secretspresent == 'YES') + run: xvfb-run --auto-servernum ./gradlew jacocoTestReport env: CI: "true" CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} DBMS: "postgresql" + - uses: codecov/codecov-action@v3 + if: (github.ref == 'refs/heads/main') && (steps.checksecrets.outputs.secretspresent == 'YES') + with: + token: ${{ secrets.CODECOV_TOKEN }} # This is https://github.com/marketplace/actions/gradle-wrapper-validation # It ensures that the jar file is from gradle and not by a strange third party. gradlevalidation: diff --git a/.gitignore b/.gitignore index 1d3864a220b..72603d9dc08 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,9 @@ src/main/gen/ src/main/generated/ src-gen/ +# generated by https://plugins.jetbrains.com/plugin/15991-plantuml-diagram-generator +*.puml + structure101-settings.java.hsw # private data @@ -59,8 +62,8 @@ jabref.xml ## !! IN CASE YOU UPDATE, PLEASE KEEP THE LINES AT THE END OF THE FILE !! -# Created by https://www.gitignore.io/api/gradle,java,jabref,intellij,eclipse,netbeans,windows,linux,macos,node,snapcraft -# Edit at https://www.gitignore.io/?templates=gradle,java,jabref,intellij,eclipse,netbeans,windows,linux,macos,node,snapcraft +# Created by https://www.toptal.com/developers/gitignore/api/gradle,java,jabref,jetbrains,eclipse,netbeans,windows,linux,macos,node,snapcraft +# Edit at https://www.toptal.com/developers/gitignore?templates=gradle,java,jabref,jetbrains,eclipse,netbeans,windows,linux,macos,node,snapcraft ### Eclipse ### .metadata @@ -113,26 +116,53 @@ local.properties # Annotation Processing .apt_generated/ +.apt_generated_test/ # Scala IDE specific (Scala & Java development for Eclipse) .cache-main .scala_dependencies .worksheet +# Uncomment this line if you wish to ignore the project description file. +# Typically, this file would be tracked if it contains build/dependency configurations: +#.project + ### Eclipse Patch ### -# Eclipse Core -.project +# Spring Boot Tooling +.sts4-cache/ -# JDT-specific (Eclipse Java Development Tools) -.classpath +### JabRef ### +# JabRef - https://www.jabref.org/ +*.sav -# Annotation Processing -.apt_generated +### Java ### +# Compiled class file +*.class -.sts4-cache/ +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar -### Intellij ### -# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* +replay_pid* + +### JetBrains ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 # User-specific stuff @@ -142,6 +172,9 @@ local.properties .idea/**/dictionaries .idea/**/shelf +# AWS User-specific +.idea/**/aws.xml + # Generated files .idea/**/contentModel.xml @@ -162,6 +195,9 @@ local.properties # When using Gradle or Maven with auto-import, you should exclude module files, # since they will be recreated, and may cause churn. Uncomment if using # auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml # .idea/modules.xml # .idea/*.iml # .idea/modules @@ -189,6 +225,9 @@ atlassian-ide-plugin.xml # Cursive Clojure plugin .idea/replstate.xml +# SonarLint plugin +.idea/sonarlint/ + # Crashlytics plugin (for Android Studio and IntelliJ) com_crashlytics_export_strings.xml crashlytics.properties @@ -201,7 +240,7 @@ fabric.properties # Android studio 3.1+ serialized cache file .idea/caches/build_file_checksums.ser -### Intellij Patch ### +### JetBrains Patch ### # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 # *.iml @@ -210,43 +249,30 @@ fabric.properties # *.ipr # Sonarlint plugin +# https://plugins.jetbrains.com/plugin/7973-sonarlint .idea/**/sonarlint/ # SonarQube Plugin +# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin .idea/**/sonarIssues.xml # Markdown Navigator plugin +# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced .idea/**/markdown-navigator.xml +.idea/**/markdown-navigator-enh.xml .idea/**/markdown-navigator/ -### JabRef ### -# JabRef - https://www.jabref.org/ -*.sav - -### Java ### -# Compiled class file -*.class - -# Log file -*.log +# Cache file creation bug +# See https://youtrack.jetbrains.com/issue/JBR-2257 +.idea/$CACHE_FILE$ -# BlueJ files -*.ctxt +# CodeStream plugin +# https://plugins.jetbrains.com/plugin/12206-codestream +.idea/codestream.xml -# Mobile Tools for Java (J2ME) -.mtj.tmp/ - -# Package Files # -*.jar -*.war -*.nar -*.ear -*.zip -*.tar.gz -*.rar - -# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml -hs_err_pid* +# Azure Toolkit for IntelliJ plugin +# https://plugins.jetbrains.com/plugin/8053-azure-toolkit-for-intellij +.idea/**/azureSettings.xml ### Linux ### *~ @@ -272,6 +298,7 @@ hs_err_pid* # Icon must end with two \r Icon + # Thumbnails ._* @@ -291,6 +318,10 @@ Network Trash Folder Temporary Items .apdisk +### macOS Patch ### +# iCloud generated files +*.icloud + ### NetBeans ### **/nbproject/private/ **/nbproject/Makefile-*.mk @@ -308,6 +339,7 @@ npm-debug.log* yarn-debug.log* yarn-error.log* lerna-debug.log* +.pnpm-debug.log* # Diagnostic reports (https://nodejs.org/api/report.html) report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json @@ -344,8 +376,8 @@ build/Release node_modules/ jspm_packages/ -# TypeScript v1 declaration files -typings/ +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ # TypeScript cache *.tsbuildinfo @@ -356,6 +388,15 @@ typings/ # Optional eslint cache .eslintcache +# Optional stylelint cache +.stylelintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + # Optional REPL history .node_repl_history @@ -365,33 +406,40 @@ typings/ # Yarn Integrity file .yarn-integrity -# dotenv environment variables file +# dotenv environment variable files .env -.env.test +.env.development.local +.env.test.local +.env.production.local +.env.local # parcel-bundler cache (https://parceljs.org/) .cache +.parcel-cache -# next.js build output +# Next.js build output .next +out -# nuxt.js build output +# Nuxt.js build / generate output .nuxt +dist -# rollup.js default build output - -# Uncomment the public line if your project uses Gatsby +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js # https://nextjs.org/blog/next-9-1#public-directory-support -# https://create-react-app.dev/docs/using-the-public-folder/#docsNav # public -# Storybook build outputs -.out -.storybook-out - # vuepress build output .vuepress/dist +# vuepress v2.x temp and cache directory +.temp + +# Docusaurus cache and generated files +.docusaurus + # Serverless directories .serverless/ @@ -401,8 +449,27 @@ typings/ # DynamoDB Local files .dynamodb/ -# Temporary folders -temp/ +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* + +### Node Patch ### +# Serverless Webpack directories +.webpack/ + +# Optional stylelint cache + +# SvelteKit build / generate output +.svelte-kit ### Snapcraft ### /parts/ @@ -445,6 +512,8 @@ $RECYCLE.BIN/ ### Gradle ### .gradle +**/build/ +!src/**/build/ # Ignore Gradle GUI config gradle-app.setting @@ -452,16 +521,24 @@ gradle-app.setting # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) !gradle-wrapper.jar +# Avoid ignore Gradle wrappper properties +!gradle-wrapper.properties + # Cache of project .gradletasknamecache -# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898 -# gradle/wrapper/gradle-wrapper.properties +# Eclipse Gradle plugin generated files +# Eclipse Core +.project +# JDT-specific (Eclipse Java Development Tools) +.classpath ### Gradle Patch ### -**/build/ +# Java heap dump +*.hprof + +# End of https://www.toptal.com/developers/gitignore/api/gradle,java,jabref,jetbrains,eclipse,netbeans,windows,linux,macos,node,snapcraft -# End of https://www.gitignore.io/api/gradle,java,jabref,intellij,eclipse,netbeans,windows,linux,macos,node,snapcraft ## !! KEEP THESE LINES !! diff --git a/.gitpod.Dockerfile b/.gitpod.Dockerfile index 007108a0f49..0e80edc3ec8 100644 --- a/.gitpod.Dockerfile +++ b/.gitpod.Dockerfile @@ -5,4 +5,4 @@ FROM gitpod/workspace-full # All available versions can be listed using sdk ls java # More information about SDKMAN available at https://github.com/sdkman/sdkman-cli#sdkman-cli RUN bash -c ". /home/gitpod/.sdkman/bin/sdkman-init.sh \ - && sdk install java 18.0.1.1-open" + && sdk install java 21-open" diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c4869a0d16..24c632a982d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,14 +12,20 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv ### Added - We added a dropdown menu to let users change the reference library during AUX file import. [#10472](https://github.com/JabRef/jabref/issues/10472) +- We added a button to let users reset the cite command to the default value. [#10569](https://github.com/JabRef/jabref/issues/10569) ### Changed +- We moved the location of the 'Open only one instance of JabRef' preference option from "Network" to "General". [#9306](https://github.com/JabRef/jabref/issues/9306) +- The two previews in the change resolver dialog now have their scrollbars synchronized. [#9576](https://github.com/JabRef/jabref/issues/9576). - We changed the setting of the keyword separator to accept a single character only. [#177](https://github.com/koppor/jabref/issues/177) +- Short DOI formatter now checks, if the value is already formatted. If so, it returns the value instead of calling the ShortDOIService again. [#10589](https://github.com/JabRef/jabref/issues/10589) ### Fixed -- We fixed an issue where the added protected term has unwanted leading and trailing whitespaces, where the formatted text has unwanted empty brackets and where the word at the cursor in the textbox can be added to the list [#10415](https://github.com/JabRef/jabref/issues/10415). +- We fixed an issue where the added protected term has unwanted leading and trailing whitespaces, where the formatted text has unwanted empty brackets and where the word at the cursor in the textbox can be added to the list. [#10415](https://github.com/JabRef/jabref/issues/10415) +- We fixed an issue where in the merge dialog the file field of entries was not correctly merged when the first and second entry both contained values inside the file field. [#10572](https://github.com/JabRef/jabref/issues/10572) +- We fixed some small inconsistencies in the user interface. [#10507](https://github.com/JabRef/jabref/issues/10507) [#10458](https://github.com/JabRef/jabref/issues/10458) ### Removed diff --git a/README.md b/README.md index 3583f748fbf..f3b478a77e0 100644 --- a/README.md +++ b/README.md @@ -18,13 +18,13 @@ It supports you in every step of your research work. - Easily retrieve and link full-text articles - Fetch complete bibliographic information based on ISBN, DOI, PubMed-ID and arXiv-ID - Extract metadata from PDFs -- Import new references directly from the browser with one click using the [official browser extension](https://github.com/JabRef/JabRef-Browser-Extension) for [Firefox](https://addons.mozilla.org/en-US/firefox/addon/jabref/?src=external-github), [Chrome](https://chrome.google.com/webstore/detail/jabref-browser-extension/bifehkofibaamoeaopjglfkddgkijdlh), [Edge](https://microsoftedge.microsoft.com/addons/detail/pgkajmkfgbehiomipedjhoddkejohfna) and [Vivaldi](https://chrome.google.com/webstore/detail/jabref-browser-extension/bifehkofibaamoeaopjglfkddgkijdlh) +- Import new references directly from the browser with one click using the [official browser extension](https://github.com/JabRef/JabRef-Browser-Extension) for [Firefox](https://addons.mozilla.org/en-US/firefox/addon/jabref/?src=external-github), [Chrome](https://chrome.google.com/webstore/detail/jabref-browser-extension/bifehkofibaamoeaopjglfkddgkijdlh), [Edge](https://microsoftedge.microsoft.com/addons/detail/pgkajmkfgbehiomipedjhoddkejohfna), and [Vivaldi](https://chrome.google.com/webstore/detail/jabref-browser-extension/bifehkofibaamoeaopjglfkddgkijdlh) ### Organize -- Group your research into hierarchical collections and organize research items based on keywords/tags, search terms or your manual assignments +- Group your research into hierarchical collections and organize research items based on keywords/tags, search terms, or your manual assignments - Advanced search and filter features -- Complete and fix bibliographic data by comparing with curated online catalogs such as Google Scholar, Springer or MathSciNet +- Complete and fix bibliographic data by comparing with curated online catalogs such as Google Scholar, Springer, or MathSciNet - Customizable citation key generator - Customize and add new metadata fields or reference types - Find and merge duplicates diff --git a/build.gradle b/build.gradle index cc3cd713f75..6e1338369a0 100644 --- a/build.gradle +++ b/build.gradle @@ -7,13 +7,13 @@ plugins { id 'com.github.andygoossens.modernizer' version '1.9.0' - id 'me.champeau.jmh' version '0.7.1' + id 'me.champeau.jmh' version '0.7.2' id 'org.javamodularity.moduleplugin' version '1.8.12' id 'org.openjfx.javafxplugin' version '0.1.0' - id 'org.beryx.jlink' version '3.0.0' + id 'org.beryx.jlink' version '3.0.1' // nicer test outputs during running and completion // Homepage: https://github.com/radarsh/gradle-test-logger-plugin @@ -27,7 +27,7 @@ plugins { id 'idea' - id 'org.openrewrite.rewrite' version '6.3.18' + id 'org.openrewrite.rewrite' version '6.4.0' } // Enable following for debugging @@ -48,7 +48,9 @@ java { modularity.inferModulePath.set(false) toolchain { - // If this is updated, also update .devcontainer/devcontainer.json#L34 + // If this is updated, also update + // - .devcontainer/devcontainer.json#L34 and + // - .gitpod.Dockerfile languageVersion = JavaLanguageVersion.of(21) } } @@ -128,7 +130,7 @@ dependencies { // required for reading write-protected PDFs - see https://github.com/JabRef/jabref/pull/942#issuecomment-209252635 implementation 'org.bouncycastle:bcprov-jdk18on:1.76' - implementation 'commons-cli:commons-cli:1.5.0' + implementation 'commons-cli:commons-cli:1.6.0' implementation 'org.libreoffice:unoloader:7.6.1' implementation 'org.libreoffice:libreoffice:7.6.1' @@ -145,7 +147,7 @@ dependencies { implementation group: 'com.fasterxml.jackson.dataformat', name: 'jackson-dataformat-yaml', version: '2.15.3' implementation group: 'com.fasterxml.jackson.datatype', name: 'jackson-datatype-jsr310', version: '2.15.3' - implementation 'com.fasterxml:aalto-xml:1.3.1' + implementation 'com.fasterxml:aalto-xml:1.3.2' implementation group: 'org.mariadb.jdbc', name: 'mariadb-java-client', version: '2.7.9' @@ -172,7 +174,7 @@ dependencies { implementation('com.tobiasdiez:easybind:2.2.1-SNAPSHOT') implementation 'org.fxmisc.flowless:flowless:0.7.1' implementation 'org.fxmisc.richtext:richtextfx:0.11.1' - implementation (group: 'com.dlsc.gemsfx', name: 'gemsfx', version: '1.82.0') { + implementation (group: 'com.dlsc.gemsfx', name: 'gemsfx', version: '1.84.0') { exclude module: 'javax.inject' // Split package, use only jakarta.inject exclude group: 'org.apache.logging.log4j' } @@ -190,7 +192,7 @@ dependencies { // route all requests to java.util.logging to SLF4J (which in turn routes to tinylog) implementation 'org.slf4j:jul-to-slf4j:2.0.9' // route all requests to log4j to SLF4J - implementation 'org.apache.logging.log4j:log4j-to-slf4j:2.20.0' + implementation 'org.apache.logging.log4j:log4j-to-slf4j:2.21.1' implementation('de.undercouch:citeproc-java:3.0.0-beta.2') { exclude group: 'org.antlr' @@ -221,7 +223,7 @@ dependencies { implementation 'org.glassfish.jersey.core:jersey-server:3.1.3' // injection framework implementation 'org.glassfish.jersey.inject:jersey-hk2:3.1.3' - implementation 'org.glassfish.hk2:hk2-api:3.0.4' + implementation 'org.glassfish.hk2:hk2-api:3.0.5' // testImplementation 'org.glassfish.hk2:hk2-testing:3.0.4' // implementation 'org.glassfish.hk2:hk2-testing-jersey:3.0.4' // testImplementation 'org.glassfish.hk2:hk2-junitrunner:3.0.4' @@ -250,7 +252,7 @@ dependencies { xjc group: 'org.glassfish.jaxb', name: 'jaxb-xjc', version: '3.0.2' xjc group: 'org.glassfish.jaxb', name: 'jaxb-runtime', version: '3.0.2' - rewrite(platform("org.openrewrite.recipe:rewrite-recipe-bom:2.3.1")) + rewrite(platform("org.openrewrite.recipe:rewrite-recipe-bom:2.4.1")) rewrite("org.openrewrite.recipe:rewrite-static-analysis") rewrite("org.openrewrite.recipe:rewrite-logging-frameworks") rewrite("org.openrewrite.recipe:rewrite-testing-frameworks") diff --git a/docs/contributing.md b/docs/contributing.md index c0b04513625..bd187bf1f3f 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -18,7 +18,7 @@ We welcome contributions to JabRef and encourage you to follow the GitHub workfl 1. Fork the JabRef into your GitHub account. 2. Clone your forked repository on your local machine. 2. **Create a new branch** (such as `fix-for-issue-121`). Be sure to create a **separate branch** for each improvement you implement. -3. Work on the **new branch — not the `main` branch.** Refer to our [code how-tos](https://devdocs.jabref.org/getting-into-the-code/code-howtos) if you have questions about your implementation. +3. Work on the **new branch — not the `main` branch.** Refer to our [code how-tos](https://devdocs.jabref.org/code-howtos) if you have questions about your implementation. 4. Create a pull request. For an overview of pull requests, take a look at GitHub's [pull request help documentation](https://help.github.com/articles/about-pull-requests/). 5. In case your pull request is not yet complete or not yet ready for review, create a [draft pull request](https://github.blog/2019-02-14-introducing-draft-pull-requests/) instead. diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index ba646d0737e..56ee2eca620 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -42,7 +42,7 @@ // Logging requires org.slf4j; requires jul.to.slf4j; - requires org.apache.logging.slf4j; + requires org.apache.logging.log4j.to.slf4j; requires org.tinylog.api; requires org.tinylog.api.slf4j; requires org.tinylog.impl; @@ -88,7 +88,7 @@ uses org.mariadb.jdbc.credential.CredentialPlugin; // Apache Commons and other (similar) helper libraries - requires commons.cli; + requires org.apache.commons.cli; requires org.apache.commons.csv; requires org.apache.commons.lang3; requires com.google.common; diff --git a/src/main/java/org/jabref/gui/Base.css b/src/main/java/org/jabref/gui/Base.css index 68f96eebf28..8aa35586865 100644 --- a/src/main/java/org/jabref/gui/Base.css +++ b/src/main/java/org/jabref/gui/Base.css @@ -1447,3 +1447,8 @@ We want to have a look that matches our icons in the tool-bar */ .table-column .rotated > .label { -fx-content-display: graphic-only; } + +.customGenerateButton { + -fx-padding: 0.5em 2em; + -fx-min-width: 10em; +} diff --git a/src/main/java/org/jabref/gui/EntryTypeView.java b/src/main/java/org/jabref/gui/EntryTypeView.java index 6b4bb2f8f0a..238d80d7d17 100644 --- a/src/main/java/org/jabref/gui/EntryTypeView.java +++ b/src/main/java/org/jabref/gui/EntryTypeView.java @@ -88,6 +88,7 @@ public EntryTypeView(LibraryTab libraryTab, DialogService dialogService, Prefere }); Button btnGenerate = (Button) this.getDialogPane().lookupButton(generateButton); + btnGenerate.getStyleClass().add("customGenerateButton"); btnGenerate.textProperty().bind(EasyBind.map(viewModel.searchingProperty(), searching -> searching ? Localization.lang("Searching...") : Localization.lang("Generate"))); btnGenerate.disableProperty().bind(viewModel.idFieldValidationStatus().validProperty().not().or(viewModel.searchingProperty())); diff --git a/src/main/java/org/jabref/gui/JabRefFrame.java b/src/main/java/org/jabref/gui/JabRefFrame.java index 3dff38bafbb..8a829134b96 100644 --- a/src/main/java/org/jabref/gui/JabRefFrame.java +++ b/src/main/java/org/jabref/gui/JabRefFrame.java @@ -298,9 +298,11 @@ private void initKeyBindings() { // Extra workaround for CodeArea, which does not inherit from TextInputControl if (!(stateManager.getFocusOwner().isPresent() && (stateManager.getFocusOwner().get() instanceof CodeArea))) { event.consume(); + break; } break; } + break; default: } } @@ -790,7 +792,7 @@ public void addTab(ParserResult parserResult, boolean raisePanel) { */ private void addImportedEntries(final LibraryTab panel, final ParserResult parserResult) { BackgroundTask task = BackgroundTask.wrap(() -> parserResult); - ImportCleanup cleanup = new ImportCleanup(panel.getBibDatabaseContext().getMode()); + ImportCleanup cleanup = ImportCleanup.targeting(panel.getBibDatabaseContext().getMode()); cleanup.doPostCleanup(parserResult.getDatabase().getEntries()); ImportEntriesDialog dialog = new ImportEntriesDialog(panel.getBibDatabaseContext(), task); dialog.setTitle(Localization.lang("Import")); 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 332057548a8..de482392470 100644 --- a/src/main/java/org/jabref/gui/collab/entrychange/EntryChangeDetailsView.java +++ b/src/main/java/org/jabref/gui/collab/entrychange/EntryChangeDetailsView.java @@ -1,10 +1,13 @@ package org.jabref.gui.collab.entrychange; +import javafx.event.Event; import javafx.geometry.Orientation; import javafx.scene.control.Label; import javafx.scene.control.SplitPane; import javafx.scene.control.TabPane; +import javafx.scene.input.MouseEvent; import javafx.scene.layout.VBox; +import javafx.scene.web.WebView; import org.jabref.gui.DialogService; import org.jabref.gui.StateManager; @@ -23,6 +26,7 @@ public final class EntryChangeDetailsView extends DatabaseChangeDetailsView { private final PreviewWithSourceTab oldPreviewWithSourcesTab = new PreviewWithSourceTab(); private final PreviewWithSourceTab newPreviewWithSourcesTab = new PreviewWithSourceTab(); + private boolean scrolling = false; public EntryChangeDetailsView(BibEntry oldEntry, BibEntry newEntry, @@ -42,6 +46,13 @@ public EntryChangeDetailsView(BibEntry oldEntry, // we need a copy here as we otherwise would set the same entry twice PreviewViewer previewClone = new PreviewViewer(databaseContext, dialogService, preferencesService, stateManager, themeManager, taskExecutor); + // The scroll bar used is not part of ScrollPane, but the attached WebView. + WebView previewCloneView = (WebView) previewClone.getContent(); + WebView previewViewerView = (WebView) previewViewer.getContent(); + synchronizeScrolling(previewCloneView, previewViewerView); + synchronizeScrolling(previewViewerView, previewCloneView); + // TODO: Also sync scrolling for BibTeX view. + TabPane oldEntryTabPane = oldPreviewWithSourcesTab.getPreviewWithSourceTab(oldEntry, databaseContext, preferencesService, entryTypesManager, previewClone, Localization.lang("Entry Preview")); TabPane newEntryTabPane = newPreviewWithSourcesTab.getPreviewWithSourceTab(newEntry, databaseContext, preferencesService, entryTypesManager, previewViewer, Localization.lang("Entry Preview")); @@ -68,4 +79,24 @@ public EntryChangeDetailsView(BibEntry oldEntry, this.getChildren().add(split); } + + // Method adapted from: + // https://stackoverflow.com/questions/49509395/synchronize-scrollbars-of-two-javafx-webviews + // https://stackoverflow.com/questions/31264847/how-to-set-remember-scrollbar-thumb-position-in-javafx-8-webview + private void synchronizeScrolling(WebView webView, WebView otherWebView) { + webView.addEventHandler(Event.ANY, event -> { + if (!scrolling) { + scrolling = true; + if (event instanceof MouseEvent) { + if (((MouseEvent) event).isPrimaryButtonDown()) { + int value = (Integer) webView.getEngine().executeScript("window.scrollY"); + otherWebView.getEngine().executeScript("window.scrollTo(0, " + value + ")"); + } + } else { + otherWebView.fireEvent(event.copyFor(otherWebView, otherWebView)); + } + scrolling = false; + } + }); + } } diff --git a/src/main/java/org/jabref/gui/entryeditor/RelatedArticlesTab.java b/src/main/java/org/jabref/gui/entryeditor/RelatedArticlesTab.java index baf22123c35..1cf3ad74e5b 100644 --- a/src/main/java/org/jabref/gui/entryeditor/RelatedArticlesTab.java +++ b/src/main/java/org/jabref/gui/entryeditor/RelatedArticlesTab.java @@ -78,7 +78,7 @@ private StackPane getRelatedArticlesPane(BibEntry entry) { .wrap(() -> fetcher.performSearch(entry)) .onRunning(() -> progress.setVisible(true)) .onSuccess(relatedArticles -> { - ImportCleanup cleanup = new ImportCleanup(BibDatabaseModeDetection.inferMode(new BibDatabase(List.of(entry)))); + ImportCleanup cleanup = ImportCleanup.targeting(BibDatabaseModeDetection.inferMode(new BibDatabase(List.of(entry)))); cleanup.doPostCleanup(relatedArticles); progress.setVisible(false); root.getChildren().add(getRelatedArticleInfo(relatedArticles, fetcher)); diff --git a/src/main/java/org/jabref/gui/externalfiles/DuplicateDecisionResult.java b/src/main/java/org/jabref/gui/externalfiles/DuplicateDecisionResult.java new file mode 100644 index 00000000000..8bfc842297d --- /dev/null +++ b/src/main/java/org/jabref/gui/externalfiles/DuplicateDecisionResult.java @@ -0,0 +1,11 @@ +package org.jabref.gui.externalfiles; + +import org.jabref.gui.duplicationFinder.DuplicateResolverDialog; +import org.jabref.model.entry.BibEntry; + +public record DuplicateDecisionResult( + DuplicateResolverDialog.DuplicateResolverResult decision, + BibEntry mergedEntry) { +} + + diff --git a/src/main/java/org/jabref/gui/externalfiles/ImportHandler.java b/src/main/java/org/jabref/gui/externalfiles/ImportHandler.java index 31edf671e97..a9fbbe28eb8 100644 --- a/src/main/java/org/jabref/gui/externalfiles/ImportHandler.java +++ b/src/main/java/org/jabref/gui/externalfiles/ImportHandler.java @@ -52,6 +52,7 @@ import org.jabref.model.util.OptionalUtil; import org.jabref.preferences.PreferencesService; +import com.google.common.annotations.VisibleForTesting; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -174,72 +175,95 @@ private BibEntry createEmptyEntryWithLink(Path file) { return entry; } + /** + * Cleans up the given entries and adds them to the library. + * There is no automatic download done. + */ public void importEntries(List entries) { - ImportCleanup cleanup = new ImportCleanup(bibDatabaseContext.getMode()); + ImportCleanup cleanup = ImportCleanup.targeting(bibDatabaseContext.getMode()); cleanup.doPostCleanup(entries); - bibDatabaseContext.getDatabase().insertEntries(entries); - - // Set owner/timestamp - UpdateField.setAutomaticFields(entries, - preferencesService.getOwnerPreferences(), - preferencesService.getTimestampPreferences()); - - // Generate citation keys - if (preferencesService.getImporterPreferences().isGenerateNewKeyOnImport()) { - generateKeys(entries); - } + importCleanedEntries(entries); + } - // Add to group + public void importCleanedEntries(List entries) { + bibDatabaseContext.getDatabase().insertEntries(entries); + generateKeys(entries); + setAutomaticFields(entries); addToGroups(entries, stateManager.getSelectedGroup(bibDatabaseContext)); } public void importEntryWithDuplicateCheck(BibDatabaseContext bibDatabaseContext, BibEntry entry) { - ImportCleanup cleanup = new ImportCleanup(bibDatabaseContext.getMode()); - BibEntry cleanedEntry = cleanup.doPostCleanup(entry); - BibEntry entryToInsert = cleanedEntry; - - Optional existingDuplicateInLibrary = new DuplicateCheck(Globals.entryTypesManager).containsDuplicate(bibDatabaseContext.getDatabase(), entryToInsert, bibDatabaseContext.getMode()); + BibEntry entryToInsert = cleanUpEntry(bibDatabaseContext, entry); + Optional existingDuplicateInLibrary = findDuplicate(bibDatabaseContext, entryToInsert); if (existingDuplicateInLibrary.isPresent()) { - DuplicateResolverDialog dialog = new DuplicateResolverDialog(existingDuplicateInLibrary.get(), entryToInsert, DuplicateResolverDialog.DuplicateResolverType.IMPORT_CHECK, bibDatabaseContext, stateManager, dialogService, preferencesService); - switch (dialogService.showCustomDialogAndWait(dialog).orElse(DuplicateResolverDialog.DuplicateResolverResult.BREAK)) { - case KEEP_RIGHT: - bibDatabaseContext.getDatabase().removeEntry(existingDuplicateInLibrary.get()); - break; - case KEEP_BOTH: - break; - case KEEP_MERGE: - bibDatabaseContext.getDatabase().removeEntry(existingDuplicateInLibrary.get()); - entryToInsert = dialog.getMergedEntry(); - break; - case KEEP_LEFT: - case AUTOREMOVE_EXACT: - case BREAK: - default: - return; + Optional duplicateHandledEntry = handleDuplicates(bibDatabaseContext, entryToInsert, existingDuplicateInLibrary.get()); + if (duplicateHandledEntry.isEmpty()) { + return; } + entryToInsert = duplicateHandledEntry.get(); } - // Regenerate CiteKey of imported BibEntry - if (preferencesService.getImporterPreferences().isGenerateNewKeyOnImport()) { - generateKeys(List.of(entryToInsert)); + importCleanedEntries(List.of(entryToInsert)); + downloadLinkedFiles(entryToInsert); + } + + @VisibleForTesting + BibEntry cleanUpEntry(BibDatabaseContext bibDatabaseContext, BibEntry entry) { + ImportCleanup cleanup = ImportCleanup.targeting(bibDatabaseContext.getMode()); + return cleanup.doPostCleanup(entry); + } + + public Optional findDuplicate(BibDatabaseContext bibDatabaseContext, BibEntry entryToCheck) { + return new DuplicateCheck(Globals.entryTypesManager).containsDuplicate(bibDatabaseContext.getDatabase(), entryToCheck, bibDatabaseContext.getMode()); + } + + public Optional handleDuplicates(BibDatabaseContext bibDatabaseContext, BibEntry originalEntry, BibEntry duplicateEntry) { + DuplicateDecisionResult decisionResult = getDuplicateDecision(originalEntry, duplicateEntry, bibDatabaseContext); + switch (decisionResult.decision()) { + case KEEP_RIGHT: + bibDatabaseContext.getDatabase().removeEntry(duplicateEntry); + break; + case KEEP_BOTH: + break; + case KEEP_MERGE: + bibDatabaseContext.getDatabase().removeEntry(duplicateEntry); + return Optional.of(decisionResult.mergedEntry()); + case KEEP_LEFT: + case AUTOREMOVE_EXACT: + case BREAK: + default: + return Optional.empty(); } - bibDatabaseContext.getDatabase().insertEntry(entryToInsert); + return Optional.of(originalEntry); + } - // Set owner/timestamp - UpdateField.setAutomaticFields(List.of(entryToInsert), - preferencesService.getOwnerPreferences(), - preferencesService.getTimestampPreferences()); + public DuplicateDecisionResult getDuplicateDecision(BibEntry originalEntry, BibEntry duplicateEntry, BibDatabaseContext bibDatabaseContext) { + DuplicateResolverDialog dialog = new DuplicateResolverDialog(duplicateEntry, originalEntry, DuplicateResolverDialog.DuplicateResolverType.IMPORT_CHECK, bibDatabaseContext, stateManager, dialogService, preferencesService); + DuplicateResolverDialog.DuplicateResolverResult decision = dialogService.showCustomDialogAndWait(dialog).orElse(DuplicateResolverDialog.DuplicateResolverResult.BREAK); + return new DuplicateDecisionResult(decision, dialog.getMergedEntry()); + } - addToGroups(List.of(entry), stateManager.getSelectedGroup(this.bibDatabaseContext)); + public void setAutomaticFields(List entries) { + UpdateField.setAutomaticFields( + entries, + preferencesService.getOwnerPreferences(), + preferencesService.getTimestampPreferences() + ); + } + public void downloadLinkedFiles(BibEntry entry) { if (preferencesService.getFilePreferences().shouldDownloadLinkedFiles()) { - entry.getFiles().stream().filter(LinkedFile::isOnlineLink).forEach(linkedFile -> - new LinkedFileViewModel( - linkedFile, - entry, - bibDatabaseContext, - taskExecutor, - dialogService, - preferencesService).download()); + entry.getFiles().stream() + .filter(LinkedFile::isOnlineLink) + .forEach(linkedFile -> + new LinkedFileViewModel( + linkedFile, + entry, + bibDatabaseContext, + taskExecutor, + dialogService, + preferencesService + ).download() + ); } } @@ -262,15 +286,15 @@ private void addToGroups(List entries, Collection group * @param entries entries to generate keys for */ private void generateKeys(List entries) { + if (!preferencesService.getImporterPreferences().isGenerateNewKeyOnImport()) { + return; + } CitationKeyGenerator keyGenerator = new CitationKeyGenerator( bibDatabaseContext.getMetaData().getCiteKeyPattern(preferencesService.getCitationKeyPatternPreferences() .getKeyPattern()), bibDatabaseContext.getDatabase(), preferencesService.getCitationKeyPatternPreferences()); - - for (BibEntry entry : entries) { - keyGenerator.generateAndSetKey(entry); - } + entries.forEach(keyGenerator::generateAndSetKey); } public List handleBibTeXData(String entries) { diff --git a/src/main/java/org/jabref/gui/fieldeditors/FieldEditors.java b/src/main/java/org/jabref/gui/fieldeditors/FieldEditors.java index 5148990b3be..5e817d5291c 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/FieldEditors.java +++ b/src/main/java/org/jabref/gui/fieldeditors/FieldEditors.java @@ -65,6 +65,8 @@ public static FieldEditorFX getForField(final Field field, return new JournalEditor(field, suggestionProvider, fieldCheckers); } else if (fieldProperties.contains(FieldProperty.DOI) || fieldProperties.contains(FieldProperty.EPRINT) || fieldProperties.contains(FieldProperty.ISBN)) { return new IdentifierEditor(field, suggestionProvider, fieldCheckers); + } else if (fieldProperties.contains(FieldProperty.ISSN)) { + return new ISSNEditor(field, suggestionProvider, fieldCheckers); } else if (field == StandardField.OWNER) { return new OwnerEditor(field, suggestionProvider, fieldCheckers); } else if (field == StandardField.GROUPS) { @@ -97,8 +99,6 @@ public static FieldEditorFX getForField(final Field field, return new KeywordsEditor(field, suggestionProvider, fieldCheckers, preferences, undoManager); } else if (field == InternalField.KEY_FIELD) { return new CitationKeyEditor(field, suggestionProvider, fieldCheckers, databaseContext); - } else if (field == StandardField.ISSN) { - return new ISSNEditor(field, suggestionProvider, fieldCheckers); } else { // default return new SimpleEditor(field, suggestionProvider, fieldCheckers, preferences, isMultiLine, undoManager); diff --git a/src/main/java/org/jabref/gui/fieldeditors/ISSNEditor.fxml b/src/main/java/org/jabref/gui/fieldeditors/ISSNEditor.fxml index 021a0f24f60..44d70fb78c4 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/ISSNEditor.fxml +++ b/src/main/java/org/jabref/gui/fieldeditors/ISSNEditor.fxml @@ -3,13 +3,11 @@ - - + - - + diff --git a/src/main/java/org/jabref/gui/fieldeditors/ISSNEditor.java b/src/main/java/org/jabref/gui/fieldeditors/ISSNEditor.java index db100d37416..7fc01a07c4a 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/ISSNEditor.java +++ b/src/main/java/org/jabref/gui/fieldeditors/ISSNEditor.java @@ -1,5 +1,7 @@ package org.jabref.gui.fieldeditors; +import java.util.Optional; + import javax.swing.undo.UndoManager; import javafx.fxml.FXML; @@ -8,6 +10,7 @@ import javafx.scene.layout.HBox; import org.jabref.gui.DialogService; +import org.jabref.gui.StateManager; import org.jabref.gui.autocompleter.SuggestionProvider; import org.jabref.gui.fieldeditors.contextmenu.DefaultMenu; import org.jabref.gui.util.TaskExecutor; @@ -23,11 +26,14 @@ public class ISSNEditor extends HBox implements FieldEditorFX { @FXML private ISSNEditorViewModel viewModel; @FXML private EditorTextArea textArea; @FXML private Button journalInfoButton; + @FXML private Button fetchInformationByIdentifierButton; @Inject private DialogService dialogService; @Inject private PreferencesService preferencesService; @Inject private UndoManager undoManager; @Inject private TaskExecutor taskExecutor; + @Inject private StateManager stateManager; + private Optional entry = Optional.empty(); public ISSNEditor(Field field, SuggestionProvider suggestionProvider, @@ -43,7 +49,9 @@ public ISSNEditor(Field field, fieldCheckers, taskExecutor, dialogService, - undoManager); + undoManager, + stateManager, + preferencesService); textArea.textProperty().bindBidirectional(viewModel.textProperty()); textArea.initContextMenu(new DefaultMenu(textArea)); @@ -57,6 +65,7 @@ public ISSNEditorViewModel getViewModel() { @Override public void bindToEntry(BibEntry entry) { + this.entry = Optional.of(entry); viewModel.bindToEntry(entry); } @@ -70,6 +79,11 @@ public void requestFocus() { textArea.requestFocus(); } + @FXML + private void fetchInformationByIdentifier() { + entry.ifPresent(viewModel::fetchBibliographyInformation); + } + @FXML private void showJournalInfo() { if (JournalInfoOptInDialogHelper.isJournalInfoEnabled(dialogService, preferencesService.getEntryEditorPreferences())) { diff --git a/src/main/java/org/jabref/gui/fieldeditors/ISSNEditorViewModel.java b/src/main/java/org/jabref/gui/fieldeditors/ISSNEditorViewModel.java index 4784580409d..89cbf76e949 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/ISSNEditorViewModel.java +++ b/src/main/java/org/jabref/gui/fieldeditors/ISSNEditorViewModel.java @@ -5,14 +5,23 @@ import javafx.scene.control.Button; import org.jabref.gui.DialogService; +import org.jabref.gui.StateManager; import org.jabref.gui.autocompleter.SuggestionProvider; +import org.jabref.gui.mergeentries.FetchAndMergeEntry; import org.jabref.gui.util.TaskExecutor; import org.jabref.logic.integrity.FieldCheckers; +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.StandardField; +import org.jabref.preferences.PreferencesService; public class ISSNEditorViewModel extends AbstractEditorViewModel { private final TaskExecutor taskExecutor; private final DialogService dialogService; + private final UndoManager undoManager; + private final StateManager stateManager; + private final PreferencesService preferencesService; public ISSNEditorViewModel( Field field, @@ -20,13 +29,26 @@ public ISSNEditorViewModel( FieldCheckers fieldCheckers, TaskExecutor taskExecutor, DialogService dialogService, - UndoManager undoManager) { + UndoManager undoManager, + StateManager stateManager, + PreferencesService preferencesService) { super(field, suggestionProvider, fieldCheckers, undoManager); this.taskExecutor = taskExecutor; this.dialogService = dialogService; + this.undoManager = undoManager; + this.stateManager = stateManager; + this.preferencesService = preferencesService; } public void showJournalInfo(Button journalInfoButton) { PopOverUtil.showJournalInfo(journalInfoButton, entry, dialogService, taskExecutor); } + + public void fetchBibliographyInformation(BibEntry bibEntry) { + stateManager.getActiveDatabase().ifPresentOrElse( + databaseContext -> new FetchAndMergeEntry(databaseContext, taskExecutor, preferencesService, dialogService, undoManager) + .fetchAndMerge(bibEntry, StandardField.ISSN), + () -> dialogService.notify(Localization.lang("No library selected")) + ); + } } diff --git a/src/main/java/org/jabref/gui/fieldeditors/JournalInfoOptInDialogHelper.java b/src/main/java/org/jabref/gui/fieldeditors/JournalInfoOptInDialogHelper.java index c811936a379..b1209ba95db 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/JournalInfoOptInDialogHelper.java +++ b/src/main/java/org/jabref/gui/fieldeditors/JournalInfoOptInDialogHelper.java @@ -15,12 +15,18 @@ public static boolean isJournalInfoEnabled(DialogService dialogService, EntryEdi } if (preferences.shouldEnableJournalPopup() == EntryEditorPreferences.JournalPopupEnabled.DISABLED) { - dialogService.notify( - Localization.lang("Please enable journal information fetching in %0 > %1", + boolean enableJournalPopup = dialogService.showConfirmationDialogAndWait( + Localization.lang("Enable Journal Information Fetching?"), + Localization.lang("Would you like to enable fetching of journal information? This can be changed later in %0 > %1.", Localization.lang("Preferences"), - Localization.lang("Web search")) + Localization.lang("Entry editor")), Localization.lang("Enable"), Localization.lang("Keep disabled") ); - return false; + + preferences.setEnableJournalPopup(enableJournalPopup + ? EntryEditorPreferences.JournalPopupEnabled.ENABLED + : EntryEditorPreferences.JournalPopupEnabled.DISABLED); + + return enableJournalPopup; } boolean journalInfoEnabled = dialogService.showConfirmationDialogAndWait( diff --git a/src/main/java/org/jabref/gui/fieldeditors/identifier/IdentifierEditor.java b/src/main/java/org/jabref/gui/fieldeditors/identifier/IdentifierEditor.java index c05501335b6..7afd5d23fde 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/identifier/IdentifierEditor.java +++ b/src/main/java/org/jabref/gui/fieldeditors/identifier/IdentifierEditor.java @@ -23,13 +23,16 @@ 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.StandardField; import org.jabref.preferences.PreferencesService; import com.airhacks.afterburner.injection.Injector; import com.airhacks.afterburner.views.ViewLoader; import jakarta.inject.Inject; +import static org.jabref.model.entry.field.StandardField.DOI; +import static org.jabref.model.entry.field.StandardField.EPRINT; +import static org.jabref.model.entry.field.StandardField.ISBN; + public class IdentifierEditor extends HBox implements FieldEditorFX { @FXML private BaseIdentifierEditorViewModel viewModel; @@ -53,14 +56,18 @@ public IdentifierEditor(Field field, // but we need the injected vars to create the viewmodels. Injector.registerExistingAndInject(this); - if (StandardField.DOI == field) { - this.viewModel = new DoiIdentifierEditorViewModel(suggestionProvider, fieldCheckers, dialogService, taskExecutor, preferencesService, undoManager, stateManager); - } else if (StandardField.ISBN == field) { - this.viewModel = new ISBNIdentifierEditorViewModel(suggestionProvider, fieldCheckers, dialogService, taskExecutor, preferencesService, undoManager, stateManager); - } else if (StandardField.EPRINT == field) { - this.viewModel = new EprintIdentifierEditorViewModel(suggestionProvider, fieldCheckers, dialogService, taskExecutor, preferencesService, undoManager); - } else { - throw new IllegalStateException(String.format("Unable to instantiate a view model for identifier field editor '%s'", field.getDisplayName())); + switch (field) { + case DOI -> + this.viewModel = new DoiIdentifierEditorViewModel(suggestionProvider, fieldCheckers, dialogService, taskExecutor, preferencesService, undoManager, stateManager); + case ISBN -> + this.viewModel = new ISBNIdentifierEditorViewModel(suggestionProvider, fieldCheckers, dialogService, taskExecutor, preferencesService, undoManager, stateManager); + case EPRINT -> + this.viewModel = new EprintIdentifierEditorViewModel(suggestionProvider, fieldCheckers, dialogService, taskExecutor, preferencesService, undoManager); + + case null, default -> { + assert field != null; + throw new IllegalStateException(String.format("Unable to instantiate a view model for identifier field editor '%s'", field.getDisplayName())); + } } ViewLoader.view(this) @@ -74,7 +81,7 @@ public IdentifierEditor(Field field, lookupIdentifierButton.setTooltip( new Tooltip(Localization.lang("Look up %0", field.getDisplayName()))); - if (field.equals(StandardField.DOI)) { + if (field.equals(DOI)) { textArea.initContextMenu(EditorMenus.getDOIMenu(textArea, dialogService, preferencesService)); } else { textArea.initContextMenu(new DefaultMenu(textArea)); @@ -89,7 +96,6 @@ public BaseIdentifierEditorViewModel getViewModel() { @Override public void bindToEntry(BibEntry entry) { - this.entry = Optional.of(entry); viewModel.bindToEntry(entry); } diff --git a/src/main/java/org/jabref/gui/mergeentries/FetchAndMergeEntry.java b/src/main/java/org/jabref/gui/mergeentries/FetchAndMergeEntry.java index 0aa765d81ba..c5c65b1cc5d 100644 --- a/src/main/java/org/jabref/gui/mergeentries/FetchAndMergeEntry.java +++ b/src/main/java/org/jabref/gui/mergeentries/FetchAndMergeEntry.java @@ -77,7 +77,7 @@ public void fetchAndMerge(BibEntry entry, List fields) { if (fetcher.isPresent()) { BackgroundTask.wrap(() -> fetcher.get().performSearchById(fieldContent.get())) .onSuccess(fetchedEntry -> { - ImportCleanup cleanup = new ImportCleanup(bibDatabaseContext.getMode()); + ImportCleanup cleanup = ImportCleanup.targeting(bibDatabaseContext.getMode()); String type = field.getDisplayName(); if (fetchedEntry.isPresent()) { cleanup.doPostCleanup(fetchedEntry.get()); @@ -169,7 +169,7 @@ public void fetchAndMerge(BibEntry entry, EntryBasedFetcher fetcher) { BackgroundTask.wrap(() -> fetcher.performSearch(entry).stream().findFirst()) .onSuccess(fetchedEntry -> { if (fetchedEntry.isPresent()) { - ImportCleanup cleanup = new ImportCleanup(bibDatabaseContext.getMode()); + ImportCleanup cleanup = ImportCleanup.targeting(bibDatabaseContext.getMode()); cleanup.doPostCleanup(fetchedEntry.get()); showMergeDialog(entry, fetchedEntry.get(), fetcher); } else { diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/fieldsmerger/FileMerger.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/fieldsmerger/FileMerger.java index 4b4e2034226..91063b931f1 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/fieldsmerger/FileMerger.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/fieldsmerger/FileMerger.java @@ -1,8 +1,6 @@ package org.jabref.gui.mergeentries.newmergedialog.fieldsmerger; import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.Stream; import org.jabref.logic.bibtex.FileFieldWriter; import org.jabref.logic.importer.util.FileFieldParser; @@ -25,10 +23,9 @@ public String merge(String filesA, String filesB) { } else { List linkedFilesA = FileFieldParser.parse(filesA); List linkedFilesB = FileFieldParser.parse(filesB); - // TODO: If one of the linked files list is empty then the its string value is malformed. - return Stream.concat(linkedFilesA.stream(), linkedFilesB.stream()) - .map(FileFieldWriter::getStringRepresentation) - .collect(Collectors.joining()); + + linkedFilesA.addAll(linkedFilesB); + return FileFieldWriter.getStringRepresentation(linkedFilesA); } } } diff --git a/src/main/java/org/jabref/gui/preferences/external/ExternalTab.fxml b/src/main/java/org/jabref/gui/preferences/external/ExternalTab.fxml index 803d9bead9a..8db73e2b093 100644 --- a/src/main/java/org/jabref/gui/preferences/external/ExternalTab.fxml +++ b/src/main/java/org/jabref/gui/preferences/external/ExternalTab.fxml @@ -33,7 +33,7 @@ - + @@ -60,6 +60,15 @@ styleClass="icon-button,narrow" prefHeight="20.0" prefWidth="20.0" GridPane.columnIndex="2" GridPane.rowIndex="2"/> + diff --git a/src/main/java/org/jabref/gui/preferences/external/ExternalTab.java b/src/main/java/org/jabref/gui/preferences/external/ExternalTab.java index 4a266a113c9..0ac80610051 100644 --- a/src/main/java/org/jabref/gui/preferences/external/ExternalTab.java +++ b/src/main/java/org/jabref/gui/preferences/external/ExternalTab.java @@ -101,4 +101,9 @@ void useTerminalCommandBrowse() { void useFileBrowserSpecialCommandBrowse() { viewModel.customFileBrowserBrowse(); } + + @FXML + void resetCiteCommandToDefault() { + viewModel.resetCiteCommandToDefault(); + } } diff --git a/src/main/java/org/jabref/gui/preferences/external/ExternalTabViewModel.java b/src/main/java/org/jabref/gui/preferences/external/ExternalTabViewModel.java index 59308f47c46..f8e7f96993c 100644 --- a/src/main/java/org/jabref/gui/preferences/external/ExternalTabViewModel.java +++ b/src/main/java/org/jabref/gui/preferences/external/ExternalTabViewModel.java @@ -227,4 +227,8 @@ public BooleanProperty useCustomFileBrowserProperty() { public StringProperty customFileBrowserCommandProperty() { return this.customFileBrowserCommandProperty; } + + public void resetCiteCommandToDefault() { + this.citeCommandProperty.setValue(preferences.getExternalApplicationsPreferences().getDefaultCiteCommand()); + } } 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 4d8d3c265b4..870886f8e5f 100644 --- a/src/main/java/org/jabref/gui/preferences/general/GeneralTab.fxml +++ b/src/main/java/org/jabref/gui/preferences/general/GeneralTab.fxml @@ -4,6 +4,7 @@ + @@ -13,7 +14,6 @@ - @@ -70,6 +70,13 @@ +