diff --git a/.circleci/config.yml b/.circleci/config.yml
deleted file mode 100644
index f140a5511af..00000000000
--- a/.circleci/config.yml
+++ /dev/null
@@ -1,85 +0,0 @@
-version: 2
-
-jobs:
- downloadinstall4j:
- docker:
- - image: circleci/openjdk:8-jdk
- steps:
- - checkout
- - run: git submodule sync
- - run: git submodule update --init
- - restore_cache:
- keys:
- - install4j-{{ checksum "scripts/extract-install4j.sh" }}
- - run: scripts/download-install4j-and-jres.sh
- - save_cache:
- key: install4j-{{ checksum "scripts/extract-install4j.sh" }}
- paths:
- - "~/downloads"
- - "~/.install4j7"
- filters:
- tags:
- only: /.*/
-
- buildDev:
- docker:
- - image: circleci/openjdk:8-jdk
- steps:
- - restore_cache:
- key: dependency-cache
- - checkout
- - run: git submodule sync
- - run: git submodule update --init
- - restore_cache:
- key: install4j-{{ checksum "scripts/extract-install4j.sh" }}
- - run: scripts/extract-install4j.sh
- - run: install4j7/bin/install4jc --verbose --license=$INSTALL4J_KEY
- - run: ./gradlew -Pdev=true -Pinstall4jDir="install4j7" release --stacktrace
- - save_cache:
- key: dependency-cache
- paths:
- - "~/.gradle"
- - store_artifacts:
- path: build/releases
- destination: build
- - run: scripts/upload-to-builds.jabref.org.sh
-
- buildRelease:
- docker:
- - image: circleci/openjdk:8-jdk
- steps:
- - restore_cache:
- key: dependency-cache
- - checkout
- - run: git submodule sync
- - run: git submodule update --init
- - restore_cache:
- key: install4j-{{ checksum "scripts/extract-install4j.sh" }}
- - run: scripts/extract-install4j.sh
- - run: install4j7/bin/install4jc --verbose --license=$INSTALL4J_KEY
- - run: ./gradlew -Pinstall4jDir="install4j7" release --stacktrace
- - store_artifacts:
- path: build/releases
- destination: release
- - run: scripts/upload-to-builds.jabref.org.sh
- filters:
- tags:
- only: /.*/
-
-workflows:
- version: 2
- build:
- jobs:
- - downloadinstall4j
- - buildDev:
- requires:
- - downloadinstall4j
- - buildRelease:
- requires:
- - downloadinstall4j
- filters:
- branches:
- ignore: /.*/
- tags:
- only: /.*/
-
diff --git a/.dependabot/config.yml b/.dependabot/config.yml
new file mode 100644
index 00000000000..8a1ed8ef340
--- /dev/null
+++ b/.dependabot/config.yml
@@ -0,0 +1,23 @@
+# Configuration for https://dependabot.com
+version: 1
+
+update_configs:
+ - package_manager: "java:gradle"
+ directory: "/"
+ update_schedule: "weekly"
+ default_labels:
+ - "dependencies"
+ automerged_updates:
+ - match:
+ dependency_type: "all"
+ update_type: "semver:minor"
+
+ - package_manager: "submodules"
+ directory: "/"
+ update_schedule: "weekly"
+ default_labels:
+ - "dependencies"
+ automerged_updates:
+ - match:
+ dependency_type: "all"
+ update_type: "all"
diff --git a/.github/workflows/deployment.yml b/.github/workflows/deployment.yml
new file mode 100644
index 00000000000..5b7c968e62d
--- /dev/null
+++ b/.github/workflows/deployment.yml
@@ -0,0 +1,102 @@
+name: Deployment
+
+on: [push]
+
+jobs:
+ deploy:
+ strategy:
+ fail-fast: false
+ matrix:
+ os: [ubuntu-latest, windows-latest, macOS-latest]
+ include:
+ - os: ubuntu-latest
+ displayName: linux
+ jpackageDownload: https://download.java.net/java/early_access/jpackage/1/openjdk-14-jpackage+1-49_linux-x64_bin.tar.gz
+ jdk14Path: /jdk-14
+ archivePortable: tar -czf build/distribution/JabRef-portable_linux.tar.gz -C build/distribution JabRef && rm -R build/distribution/JabRef
+ - os: windows-latest
+ displayName: windows
+ jpackageDownload: https://download.java.net/java/early_access/jpackage/1/openjdk-14-jpackage+1-49_windows-x64_bin.zip
+ jdk14Path: /jdk-14
+ archivePortable: 7z a -r build/distribution/JabRef-portable_windows.zip ./build/distribution/JabRef && rm -R build/distribution/JabRef
+ - os: macOS-latest
+ displayName: macOS
+ jpackageDownload: https://download.java.net/java/early_access/jpackage/1/openjdk-14-jpackage+1-49_osx-x64_bin.tar.gz
+ jdk14Path: /jdk-14.jdk/Contents/Home
+ archivePortable: tar -czf build/distribution/JabRef-portable_macos.tar.gz -C build/distribution JabRef.app && rm -R build/distribution/JabRef.app
+
+ runs-on: ${{ matrix.os }}
+ name: Deploy on ${{ matrix.displayName }}
+
+ steps:
+ - name: Checkout source
+ uses: actions/checkout@v1
+ with:
+ depth: 1
+ submodules: true
+ - name: Extract branch name
+ shell: bash
+ run: |
+ ref=${GITHUB_REF#refs/heads/}
+ ref=${ref#refs/pull/}
+ ref=${ref%/merge}
+ echo "##[set-output name=branch;]${ref}"
+ id: extract_branch
+ - name: Set up JDK
+ uses: actions/setup-java@v1
+ with:
+ java-version: 12.0.2
+ - name: Download jpackage
+ # We need to download jpackage from https://jdk.java.net/jpackage/
+ run: |
+ import tarfile
+ import zipfile
+ import sys
+ if sys.version_info[0] >= 3:
+ from urllib.request import urlretrieve
+ else:
+ from urllib import urlretrieve
+
+ url = "${{ matrix.jpackageDownload }}"
+ tmpfile, headers = urlretrieve(url)
+ if (url.endswith("tar.gz")):
+ tar = tarfile.open(tmpfile)
+ tar.extractall()
+ tar.close()
+ elif (url.endswith("zip")):
+ zip = zipfile.ZipFile(tmpfile)
+ zip.extractall()
+ zip.close()
+ shell: python
+ - name: Build runtime image
+ run: ./gradlew -Pdev=true jlinkZip
+ - name: Build installer
+ run: |
+ export BADASS_JLINK_JPACKAGE_HOME="${GITHUB_WORKSPACE}${{ matrix.jdk14Path }}"
+ ./gradlew -Pdev=true jpackage
+ shell: bash
+ - name: Add installer as artifact
+ uses: actions/upload-artifact@master
+ with:
+ name: JabRef-${{ matrix.displayName }}
+ path: build/distribution
+ - name: Package application image
+ run: ${{ matrix.archivePortable }}
+ shell: bash
+ - name: Build and publish snap
+ if: matrix.os == 'ubuntu-latest' && steps.extract_branch.outputs.branch == 'master'
+ env:
+ SNAPCRAFT_LOGIN_FILE: ${{ secrets.SNAPCRAFT_LOGIN_FILE }}
+ run: |
+ mkdir .snapcraft && echo ${SNAPCRAFT_LOGIN_FILE} | base64 --decode --ignore-garbage > .snapcraft/snapcraft.cfg
+ docker run -v $(pwd):$(pwd) -t lyzardking/snapcraft-bionic sh -c "apt update -qq && cd $(pwd) && snapcraft && mv jabref*.snap build/distribution/ && snapcraft push build/distribution/jabref*.snap --release edge || true"
+ shell: bash
+ - name: Upload to builds.jabref.org
+ uses: garygrossgarten/github-action-scp@release
+ with:
+ local: build/distribution
+ remote: www/${{ steps.extract_branch.outputs.branch }}
+ host: builds.jabref.org
+ username: builds_jabref_org
+ privateKey: ${{ secrets.buildJabRefPrivateKey }}
+ port: 9922
diff --git a/.gitignore b/.gitignore
index f464e64c585..62ceb1d36b6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,6 +10,9 @@ status.md
# Install4J
install4j6/
+# Python
+__pycache__/
+
# Snap
parts/
stage/
@@ -21,9 +24,13 @@ snap/.snapcraft/
# Gradle
# generated when `gradlew --gui` is called
ui/
+# for workspace specific gradle properties
+gradle.properties
+
# IntelliJ IDEA
-.idea/
+.idea/*
+!.idea/runConfigurations/
*.ipr
*.iml
@@ -31,10 +38,11 @@ ui/
jabref.xml
*.sonargraph
+
# 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
### Eclipse ###
-
.metadata
bin/
tmp/
@@ -59,6 +67,9 @@ local.properties
# CDT-specific (C/C++ Development Tooling)
.cproject
+# CDT- autotools
+.autotools
+
# Java annotation processor (APT)
.factorypath
@@ -80,6 +91,9 @@ local.properties
# Code Recommenders
.recommenders/
+# Annotation Processing
+.apt_generated/
+
# Scala IDE specific (Scala & Java development for Eclipse)
.cache-main
.scala_dependencies
@@ -92,41 +106,59 @@ local.properties
# JDT-specific (Eclipse Java Development Tools)
.classpath
+# Annotation Processing
+.apt_generated
+
+.sts4-cache/
+
### Intellij ###
-# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
+# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
-# User-specific stuff:
+# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
-.idea/dictionaries
+.idea/**/usage.statistics.xml
+.idea/**/dictionaries
+.idea/**/shelf
-# Sensitive or high-churn files:
+# Generated files
+.idea/**/contentModel.xml
+
+# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
-.idea/**/dataSources.xml
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
+.idea/**/dbnavigator.xml
-# Gradle:
+# Gradle
.idea/**/gradle.xml
.idea/**/libraries
+# Gradle and Maven with auto-import
+# 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/modules.xml
+# .idea/*.iml
+# .idea/modules
+# *.iml
+# *.ipr
+
# CMake
-cmake-build-debug/
+cmake-build-*/
-# Mongo Explorer plugin:
+# Mongo Explorer plugin
.idea/**/mongoSettings.xml
-## File-based project format:
+# File-based project format
*.iws
-## Plugin-specific files:
-
# IntelliJ
-/out/
+out/
# mpeltonen/sbt-idea plugin
.idea_modules/
@@ -143,6 +175,12 @@ crashlytics.properties
crashlytics-build.properties
fabric.properties
+# Editor-based Rest Client
+.idea/httpRequests
+
+# Android studio 3.1+ serialized cache file
+.idea/caches/build_file_checksums.ser
+
### Intellij Patch ###
# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
@@ -152,7 +190,14 @@ fabric.properties
# *.ipr
# Sonarlint plugin
-.idea/sonarlint
+.idea/**/sonarlint/
+
+# SonarQube Plugin
+.idea/**/sonarIssues.xml
+
+# Markdown Navigator plugin
+.idea/**/markdown-navigator.xml
+.idea/**/markdown-navigator/
### JabRef ###
# JabRef - https://www.jabref.org/
@@ -174,6 +219,7 @@ fabric.properties
# Package Files #
*.jar
*.war
+*.nar
*.ear
*.zip
*.tar.gz
@@ -198,7 +244,8 @@ hs_err_pid*
.nfs*
### macOS ###
-*.DS_Store
+# General
+.DS_Store
.AppleDouble
.LSOverride
@@ -225,7 +272,9 @@ Temporary Items
.apdisk
### NetBeans ###
-nbproject/private/
+**/nbproject/private/
+**/nbproject/Makefile-*.mk
+**/nbproject/Package-*.bash
build/
nbbuild/
dist/
@@ -238,6 +287,10 @@ logs
npm-debug.log*
yarn-debug.log*
yarn-error.log*
+lerna-debug.log*
+
+# Diagnostic reports (https://nodejs.org/api/report.html)
+report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
@@ -250,11 +303,12 @@ lib-cov
# Coverage directory used by tools like istanbul
coverage
+*.lcov
# nyc test coverage
.nyc_output
-# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
+# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
@@ -263,16 +317,19 @@ bower_components
# node-waf configuration
.lock-wscript
-# Compiled binary addons (http://nodejs.org/api/addons.html)
+# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
-# Typescript v1 declaration files
+# TypeScript v1 declaration files
typings/
+# TypeScript cache
+*.tsbuildinfo
+
# Optional npm cache directory
.npm
@@ -290,23 +347,57 @@ typings/
# dotenv environment variables file
.env
+.env.test
+
+# parcel-bundler cache (https://parceljs.org/)
+.cache
+
+# next.js build output
+.next
+
+# nuxt.js build output
+.nuxt
+
+# react / gatsby
+public/
+# vuepress build output
+.vuepress/dist
+
+# Serverless directories
+.serverless/
+
+# FuseBox cache
+.fusebox/
+
+# DynamoDB Local files
+.dynamodb/
### Snapcraft ###
-# Snapcraft
-parts/
-prime/
-stage/
-*.snap
+/parts/
+/stage/
+/prime/
+/*.snap
+
+# Snapcraft global state tracking data(automatically generated)
+# https://forum.snapcraft.io/t/location-to-save-global-state/768
+/snap/.snapcraft/
+
+# Source archive packed by `snapcraft cleanbuild` before pushing to the LXD container
+/*_source.tar.bz2
### Windows ###
# Windows thumbnail cache files
Thumbs.db
+Thumbs.db:encryptable
ehthumbs.db
ehthumbs_vista.db
+# Dump file
+*.stackdump
+
# Folder config file
-Desktop.ini
+[Dd]esktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
@@ -314,6 +405,7 @@ $RECYCLE.BIN/
# Windows Installer files
*.cab
*.msi
+*.msix
*.msm
*.msp
@@ -322,7 +414,6 @@ $RECYCLE.BIN/
### Gradle ###
.gradle
-/build/
# Ignore Gradle GUI config
gradle-app.setting
@@ -336,14 +427,20 @@ gradle-app.setting
# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898
# gradle/wrapper/gradle-wrapper.properties
-# End of https://www.gitignore.io/api/gradle,java,jabref,intellij,eclipse,netbeans,windows,linux,macos,node,snapcraft
-
+### Gradle Patch ###
+**/build/
-# we really version .jar files - needs to be go after the www.gitignore.io-generated ones, because they ignore *.jar files
-!/lib/*.jar
-
-# do not distribute Oracle's JDBC driver
-lib/ojdbc.jar
+# End of https://www.gitignore.io/api/gradle,java,jabref,intellij,eclipse,netbeans,windows,linux,macos,node,snapcraft
-# do not ignore the source of the build
-!/buildSrc/src/
+-# we really version .jar files - needs to be go after the www.gitignore.io-generated ones, because they ignore *.jar files
+-!/lib/*.jar
+-
+-# do not distribute Oracle's JDBC driver
+-lib/ojdbc.jar
+-
+-# do not ignore the source of the build
+-!/buildSrc/src/
+-!/buildSrc/src/main/groovy/org/jabref/build
+-
+-# do not ignore JabRef icons (they are ignored by the macos setting above)
+-!src/main/java/org/jabref/gui/icon
\ No newline at end of file
diff --git a/.idea/runConfigurations/JabRef_Main.xml b/.idea/runConfigurations/JabRef_Main.xml
new file mode 100644
index 00000000000..048f2dd8126
--- /dev/null
+++ b/.idea/runConfigurations/JabRef_Main.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.travis.yml b/.travis.yml
index 489044d8ec4..f96a0afbbfd 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,4 +1,6 @@
language: java
+jdk:
+ - openjdk12
# we test at Ubuntu Trusty (Ubuntu 14.04 LTS)
# see https://docs.travis-ci.com/user/trusty-ci-environment/
@@ -17,7 +19,7 @@ env:
global:
- GRADLE_OPTS=-Dorg.gradle.daemon=false
matrix:
- - TEST_SUITE=check OPTIONS=modernizer
+ - TEST_SUITE=check
- TEST_SUITE=checkstyle
- TEST_SUITE=fetcherTest
- TEST_SUITE=databaseTest
@@ -29,6 +31,7 @@ matrix:
fast_finish: true
allow_failures:
- env: TEST_SUITE=fetcherTest
+ - env: TEST_SUITE=guiTest
- env: TEST_SUITE=codecov
- env: DEPENDENCY_UPDATES=check
@@ -50,7 +53,7 @@ script:
- if [ "$TEST_SUITE" != "guiTest" ] && [ "$TEST_SUITE" != "checkstyle" ] && [ "$TEST_SUITE" != "codecov" ]; then ./gradlew $TEST_SUITE $OPTIONS -x checkstyleJmh -x checkstyleMain -x checkstyleTest --scan; fi
- if [ "$TEST_SUITE" == "checkstyle" ]; then ./gradlew checkstyleMain checkstyleTest checkstyleJmh; fi
- if [ "$TEST_SUITE" == "guiTest" ]; then ./buildres/gui-tests.sh; fi
- - if [ "$TEST_SUITE" == "codecov" ]; then ./gradlew jacocoTestReport; bash <(curl -s https://codecov.io/bash); fi
+ - if [ "$TEST_SUITE" == "codecov" ]; then ./gradlew jacocoTestReport && bash <(curl -s https://codecov.io/bash); fi
- if [ "$DEPENDENCY_UPDATES" == "check" ]; then ./gradlew -q checkOutdatedDependencies; fi
after_failure:
diff --git a/AUTHORS b/AUTHORS
index 9c42e1f1bbc..799b72a6fdc 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -56,7 +56,6 @@ Eduard Braun
Eduardo Greco
Egon Willighagen
Ellen Reitmayr
-Erdem Derebasoglu
Erdem Derebaşoğlu
Erik Putrycz
Ervin Kolenovic
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 40bb662ecb4..ac70ed8452b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,5 @@
# Changelog
+
All notable changes to this project will be documented in this file.
This project **does not** adhere to [Semantic Versioning](http://semver.org/).
This file tries to follow the conventions proposed by [keepachangelog.com](http://keepachangelog.com/).
@@ -11,6 +12,109 @@ We refer to [GitHub issues](https://github.com/JabRef/jabref/issues) by using `#
## [Unreleased]
### Changed
+
+- We added a short DOI field formatter which shortens DOI to more human-readable form. [koppor#343](https://github.com/koppor/jabref/issues/343)
+- We improved the display of group memberships by adding multiple colored bars if the entry belongs to more than one group. [#4574](https://github.com/JabRef/jabref/issues/4574)
+- We added an option to show the preview as an extra tab in the entry editor (instead of in a split view). [#5244](https://github.com/JabRef/jabref/issues/5244)
+- A custom Open/LibreOffice jstyle file now requires a layout line for the entry type `default` [#5452](https://github.com/JabRef/jabref/issues/5452)
+- The entry editor is now open by default when JabRef starts up. [#5460](https://github.com/JabRef/jabref/issues/5460)
+- We added a new ADS fetcher to use the new ADS API [#4949](https://github.com/JabRef/jabref/issues/4949)
+- We added support of the [X11 primary selection](https://unix.stackexchange.com/a/139193/18033) [#2389](https://github.com/JabRef/jabref/issues/2389)
+
+### Fixed
+
+- Inherit fields from cross-referenced entries as specified by biblatex. [#5045](https://github.com/JabRef/jabref/issues/5045)
+- We fixed an issue where it was no longer possible to connect to LibreOffice. [#5261](https://github.com/JabRef/jabref/issues/5261)
+- The "All entries group" is no longer shown when no library is open.
+- We fixed an exception which occurred when closing JabRef. [#5348](https://github.com/JabRef/jabref/issues/5348)
+- We fixed an issue where JabRef reports incorrectly about customized entry types. [#5332](https://github.com/JabRef/jabref/issues/5332)
+- We fixed a few problems that prevented JabFox to communicate with JabRef. [#4737](https://github.com/JabRef/jabref/issues/4737) [#4303](https://github.com/JabRef/jabref/issues/4303)
+- We fixed an error where the groups containing an entry loose their highlight color when scrolling. [#5022](https://github.com/JabRef/jabref/issues/5022)
+- We fixed an error where scrollbars were not shown. [#5374](https://github.com/JabRef/jabref/issues/5374)
+- We fixed an error where an exception was thrown when merging entries. [#5169](https://github.com/JabRef/jabref/issues/5169)
+- We fixed an error where certain metadata items were not serialized alphabetically.
+- After assigning an entry to a group, the item count is now properly colored to reflect the new membership of the entry. [#3112](https://github.com/JabRef/jabref/issues/3112)
+- The group panel is now properly updated when switching between libraries (or when closing/opening one). [#3142](https://github.com/JabRef/jabref/issues/3142)
+- We fixed an error where the number of matched entries shown in the group pane was not updated correctly. [#4441](https://github.com/JabRef/jabref/issues/4441)
+- We fixed a "null" error when writing XMP metadata. [#5449](https://github.com/JabRef/jabref/issues/5449)
+- We fixed an issue where empty keywords lead to a strange display of automatic keyword groups. [#5333](https://github.com/JabRef/jabref/issues/5333)
+- We fixed an error where the default color of a new group was white instead of dark gray. [#4868](https://github.com/JabRef/jabref/issues/4868)
+- We fixed an issue where the first field in the entry editor got the focus while performing a different action (like searching). [#5084](https://github.com/JabRef/jabref/issues/5084)
+- We fixed an issue where multiple entries were highlighted in the web search result after scrolling. [#5035](https://github.com/JabRef/jabref/issues/5035)
+- We fixed an issue where the hover indication in the web search pane was not working. [#5277](https://github.com/JabRef/jabref/issues/5277)
+- We fixed an error mentioning "javafx.controls/com.sun.javafx.scene.control" that was thrown when interacting with the toolbar.
+- We fixed an error where a cleared search was restored after switching libraries. [#4846](https://github.com/JabRef/jabref/issues/4846)
+- We fixed an exception which occurred when trying to open a non-existing file from the "Recent files"-menu [#5334](https://github.com/JabRef/jabref/issues/5334)
+- We fixed an issues where the search highlight in the entry preview did not worked. [#5069](https://github.com/JabRef/jabref/issues/5069)
+- The context menu for fields in the entry editor is back. [#5254](https://github.com/JabRef/jabref/issues/5254)
+- We fixed an exception which occurred when trying to open a non-existing file from the "Recent files"-menu [#5334](https://github.com/JabRef/jabref/issues/5334)
+- We fixed a problem where the "editor" information has been duplicated during saving a .bib-Database. [#5359](https://github.com/JabRef/jabref/issues/5359)
+- We re-introduced the feature to switch between different preview styles. [#5221](https://github.com/JabRef/jabref/issues/5221)
+- We fixed various issues (including [#5263](https://github.com/JabRef/jabref/issues/5263)) related to copying entries to the clipboard
+- We fixed some display errors in the preferences dialog and replaced some of the controls [#5033](https://github.com/JabRef/jabref/pull/5033) [#5047](https://github.com/JabRef/jabref/pull/5047) [#5062](https://github.com/JabRef/jabref/pull/5062) [#5141](https://github.com/JabRef/jabref/pull/5141) [#5185](https://github.com/JabRef/jabref/pull/5185) [#5265](https://github.com/JabRef/jabref/pull/5265) [#5315](https://github.com/JabRef/jabref/pull/5315) [#5360](https://github.com/JabRef/jabref/pull/5360)
+- We fixed an exception which occurred when trying to import entries without an open library. [#5447](https://github.com/JabRef/jabref/issues/5447)
+- After successful import of one or multiple bib entries the main table scrolls to the first imported entry [#5383](https://github.com/JabRef/jabref/issues/5383)
+- We fixed an exception which occurred when an invalid jstyle was loaded. [#5452](https://github.com/JabRef/jabref/issues/5452)
+- We fixed an error where the preview theme did not adapt to the "Dark" mode [#5463](https://github.com/JabRef/jabref/issues/5463)
+- We fixed an issue where the merge dialog showed the wrong text colour in "Dark" mode [#5516](https://github.com/JabRef/jabref/issues/5516)
+
+### Removed
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+## [5.0-alpha] – 2019-08-25
+
+### Changed
+- We added eventitle, eventdate and venue fields to @unpublished entry type.
+- We added @software and @dataSet entry type to biblatex.
- All fields are now properly sorted alphabetically (in the subgroups of required/optional fields) when the entry is written to the bib file.
- We fixed an issue where some importers used the field `pubstatus` instead of the standard BibTeX field `pubstate`.
- We changed the latex command removal for docbook exporter. [#3838](https://github.com/JabRef/jabref/issues/3838)
@@ -21,13 +125,13 @@ We refer to [GitHub issues](https://github.com/JabRef/jabref/issues) by using `#
- Moreover, empty deprecated fields are no longer shown
- Added server timezone parameter when connecting to a shared database.
- We updated the dialog for setting up general fields.
-- URL field formatting is updated. All whitespace chars, located at the beginning/ending of the url, are trimmed automatically
+- URL field formatting is updated. All whitespace chars, located at the beginning/ending of the URL, are trimmed automatically
- We changed the behavior of the field formatting dialog such that the `bibtexkey` is not changed when formatting all fields or all text fields.
- We added a "Move file to file directory and rename file" option for simultaneously moving and renaming of document file. [#4166](https://github.com/JabRef/jabref/issues/4166)
- Use integrated graphics card instead of discrete on macOS [#4070](https://github.com/JabRef/jabref/issues/4070)
-- We added a cleanup operation that detects an arXiv identifier in the note, journal or url field and moves it to the `eprint` field.
+- We added a cleanup operation that detects an arXiv identifier in the note, journal or URL field and moves it to the `eprint` field.
Because of this change, the last-used cleanup operations were reset.
-- We changed the minimum required version of Java to 1.8.0_171, as this is the latest release for which the automatic Java update works. [4093](https://github.com/JabRef/jabref/issues/4093)
+- We changed the minimum required version of Java to 1.8.0_171, as this is the latest release for which the automatic Java update works. [#4093](https://github.com/JabRef/jabref/issues/4093)
- The special fields like `Printed` and `Read status` now show gray icons when the row is hovered.
- We added a button in the tab header which allows you to close the database with one click. https://github.com/JabRef/jabref/issues/494
- Sorting in the main table now takes information from cross-referenced entries into account. https://github.com/JabRef/jabref/issues/2808
@@ -44,18 +148,19 @@ We refer to [GitHub issues](https://github.com/JabRef/jabref/issues) by using `#
- Files without a defined external file type are now directly opened with the default application of the operating system
- We streamlined the process to rename and move files by removing the confirmation dialogs.
- We removed the redundant new lines of markings and wrapped the summary in the File annotation tab. [#3823](https://github.com/JabRef/jabref/issues/3823)
-- We add auto url formatting when user paste link to URL field in entry editor. [koppor#254](https://github.com/koppor/jabref/issues/254)
-- We added a minimal height for the entry editor so that it can no longer be hidden by accident. [#4279](https://github.com/JabRef/jabref/issues/4279)
+- We add auto URL formatting when user paste link to URL field in entry editor. [koppor#254](https://github.com/koppor/jabref/issues/254)
+- We added a minimum height for the entry editor so that it can no longer be hidden by accident. [#4279](https://github.com/JabRef/jabref/issues/4279)
- We added a new keyboard shortcut so that the entry editor could be closed by Ctrl + E . [#4222] (https://github.com/JabRef/jabref/issues/4222)
- We added an option in the preference dialog box, that allows user to pick the dark or light theme option. [#4130] (https://github.com/JabRef/jabref/issues/4130)
-- We updated updated the Related Articles tab to accept JSON from the new version of the Mr. DLib service
+- We updated the Related Articles tab to accept JSON from the new version of the Mr. DLib service
- We added an option in the preference dialog box that allows user to choose behavior after dragging and dropping files in Entry Editor. [#4356](https://github.com/JabRef/jabref/issues/4356)
- We added the ability to have an export preference where previously "File"-->"Export"/"Export selected entries" would not save the user's preference[#4495](https://github.com/JabRef/jabref/issues/4495)
- We optimized the code responsible for connecting to an external database, which should lead to huge improvements in performance.
- For automatically created groups, added ability to filter groups by entry type. [#4539](https://github.com/JabRef/jabref/issues/4539)
- We added the ability to add field names from the Preferences Dialog [#4546](https://github.com/JabRef/jabref/issues/4546)
-- We added the ability change the column widths directly in the main table. [#4546](https://github.com/JabRef/jabref/issues/4546)
-- We added description of how recommendations where chosen and better error handling to Related Articles tab
+- We added the ability to change the column widths directly in the main
+. [#4546](https://github.com/JabRef/jabref/issues/4546)
+- We added a description of how recommendations were chosen and better error handling to Related Articles tab
- We added the ability to execute default action in dialog by using with Ctrl + Enter combination [#4496](https://github.com/JabRef/jabref/issues/4496)
- We grouped and reordered the Main Menu (File, Edit, Library, Quality, Tools, and View tabs & icons). [#4666](https://github.com/JabRef/jabref/issues/4666) [#4667](https://github.com/JabRef/jabref/issues/4667) [#4668](https://github.com/JabRef/jabref/issues/4668) [#4669](https://github.com/JabRef/jabref/issues/4669) [#4670](https://github.com/JabRef/jabref/issues/4670) [#4671](https://github.com/JabRef/jabref/issues/4671) [#4672](https://github.com/JabRef/jabref/issues/4672) [#4673](https://github.com/JabRef/jabref/issues/4673)
- We added additional modifiers (capitalize, titlecase and sentencecase) to the Bibtex key generator. [#1506](https://github.com/JabRef/jabref/issues/1506)
@@ -64,7 +169,7 @@ We refer to [GitHub issues](https://github.com/JabRef/jabref/issues) by using `#
- We added a browse button next to the path text field for aux-based groups. [#4586](https://github.com/JabRef/jabref/issues/4586)
- We changed the title of Group Dialog to "Add subgroup" from "Edit group" when we select Add subgroup option.
- We enable import button only if entries are selected. [#4755](https://github.com/JabRef/jabref/issues/4755)
-- We made modifications to improve contrast of UI elements. [#4583](https://github.com/JabRef/jabref/issues/4583)
+- We made modifications to improve the contrast of UI elements. [#4583](https://github.com/JabRef/jabref/issues/4583)
- We added a warning for empty BibTeX keys in the entry editor. [#4440](https://github.com/JabRef/jabref/issues/4440)
- We added an option in the settings to set the default action in JabRef when right clicking on any entry in any database and selecting "Open folder". [#4763](https://github.com/JabRef/jabref/issues/4763)
- The Medline fetcher now normalizes the author names according to the BibTeX-Standard [#4345](https://github.com/JabRef/jabref/issues/4345)
@@ -81,6 +186,7 @@ We refer to [GitHub issues](https://github.com/JabRef/jabref/issues) by using `#
### Fixed
+
- We fixed an issue where JabRef died silently for the user without enough inotify instances [#4874](https://github.com/JabRef/jabref/issues/4847)
- We fixed an issue where corresponding groups are sometimes not highlighted when clicking on entries [#3112](https://github.com/JabRef/jabref/issues/3112)
- We fixed an issue where custom exports could not be selected in the 'Export (selected) entries' dialog [#4013](https://github.com/JabRef/jabref/issues/4013)
@@ -90,7 +196,7 @@ We refer to [GitHub issues](https://github.com/JabRef/jabref/issues) by using `#
- We fixed an issue where the group tree was not updated correctly after an entry was changed. https://github.com/JabRef/jabref/issues/3618
- We fixed an issue where a right-click in the main table selected a wrong entry. https://github.com/JabRef/jabref/issues/3267
- We fixed an issue where in rare cases entries where overlayed in the main table. https://github.com/JabRef/jabref/issues/3281
-- We fixed an issue where selecting a group messed up the focus of the main table / entry editor. https://github.com/JabRef/jabref/issues/3367
+- We fixed an issue where selecting a group messed up the focus of the main table and the entry editor. https://github.com/JabRef/jabref/issues/3367
- We fixed an issue where composite author names were sorted incorrectly. https://github.com/JabRef/jabref/issues/2828
- We fixed an issue where commands followed by `-` didn't work. [#3805](https://github.com/JabRef/jabref/issues/3805)
- We fixed an issue where a non-existing aux file in a group made it impossible to open the library. [#4735](https://github.com/JabRef/jabref/issues/4735)
@@ -123,9 +229,9 @@ We refer to [GitHub issues](https://github.com/JabRef/jabref/issues) by using `#
- We fixed an issue where "Move to group" would always move the first entry in the library and not the selected [#4414](https://github.com/JabRef/jabref/issues/4414)
- We fixed an issue where an older dialog appears when downloading full texts from the quality menu. [#4489](https://github.com/JabRef/jabref/issues/4489)
- We fixed an issue where right clicking on any entry in any database and selecting "Open folder" results in the NullPointer exception. [#4763](https://github.com/JabRef/jabref/issues/4763)
-- We fixed an issue where option 'open terminal here' with custom command was passing wrong argument. [#4802](https://github.com/JabRef/jabref/issues/4802)
+- We fixed an issue where option 'open terminal here' with custom command was passing the wrong argument. [#4802](https://github.com/JabRef/jabref/issues/4802)
- We fixed an issue where ranking an entry would generate an IllegalArgumentException. [#4754](https://github.com/JabRef/jabref/issues/4754)
-- We fixed an issue where special characters where removed from non label key generation pattern parts [#4767](https://github.com/JabRef/jabref/issues/4767)
+- We fixed an issue where special characters where removed from non-label key generation pattern parts [#4767](https://github.com/JabRef/jabref/issues/4767)
- We fixed an issue where the RIS import would overwite the article date with the value of the acessed date [#4816](https://github.com/JabRef/jabref/issues/4816)
- We fixed an issue where an NullPointer exception was thrown when a referenced entry in an Open/Libre Office document was no longer present in the library. Now an error message with the reference marker of the missing entry is shown. [#4932](https://github.com/JabRef/jabref/issues/4932)
- We fixed an issue where a database exception related to a missing timezone was too big. [#4827](https://github.com/JabRef/jabref/issues/4827)
@@ -136,64 +242,27 @@ We refer to [GitHub issues](https://github.com/JabRef/jabref/issues) by using `#
- We fixed an issue where the JabRef Icon in the macOS launchpad was not displayed correctly [#5003](https://github.com/JabRef/jabref/issues/5003)
- We fixed an issue where the "Search for unlinked local files" would throw an exception when parsing the content of a PDF-file with missing "series" information [#5128](https://github.com/JabRef/jabref/issues/5128)
-
### Removed
+
- The feature to "mark entries" was removed and merged with the groups functionality. For migration, a group is created for every value of the `__markedentry` field and the entry is added to this group.
- The number column was removed.
- We removed the global search feature.
-- We removed the coloring of cells in the maintable according to whether the field is optional/required.
+- We removed the coloring of cells in the main table according to whether the field is optional/required.
- We removed the feature to find and resolve duplicate BibTeX keys (as this use case is already covered by the integrity check).
- We removed a few commands from the right-click menu that are not needed often and thus don't need to be placed that prominently:
- Print entry preview: available through entry preview
- All commands related to marking: marking is not yet reimplemented
- Set/clear/append/rename fields: available through Edit menu
- - Manage keywords: available through Edit menu
+ - Manage keywords: available through the Edit menu
- Copy linked files to folder: available through File menu
- Add/move/remove from group: removed completely (functionality still available through group interface)
- We removed the option to change the column widths in the preferences dialog. [#4546](https://github.com/JabRef/jabref/issues/4546)
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
## Older versions
The changelog of JabRef 4.x is available at the [v4.x branch](https://github.com/JabRef/jabref/blob/v4.x/CHANGELOG.md).
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/v4.3...HEAD
-[4.3]: https://github.com/JabRef/jabref/compare/v4.2...v4.3
-[4.2]: https://github.com/JabRef/jabref/compare/v4.1...v4.2
-[4.1]: https://github.com/JabRef/jabref/compare/v4.0...v4.1
-[4.0]: https://github.com/JabRef/jabref/compare/v4.0-beta3...v4.0
-[4.0-beta3]: https://github.com/JabRef/jabref/compare/v4.0-beta2...v4.0-beta3
-[4.0-beta2]: https://github.com/JabRef/jabref/compare/v4.0-beta...v4.0-beta2
-[4.0-beta]: https://github.com/JabRef/jabref/compare/v3.8.2...v4.0-beta
-[2.11.1]: https://github.com/JabRef/jabref/compare/v2.11...v2.11.1
-[JavaFX]: https://en.wikipedia.org/wiki/JavaFX
-
+[Unreleased]: https://github.com/JabRef/jabref/compare/v5.0-alpha...HEAD
+[5.0-alpha]: https://github.com/JabRef/jabref/compare/v4.3...v5.0-alpha
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 24c25a05b9a..0c1d2eb4722 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,28 +1,35 @@
+# Contributing Guide
+
After reading through this guide, check out some good first issues to contribute to by clicking here: [Good First Issues](https://github.com/JabRef/jabref/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22)
## Understanding the basics
+
We welcome contributions to JabRef and encourage you to follow the GitHub workflow specified below. If you are not familiar with this type of workflow, take a look at GitHub's excellent overview on the [GitHub flow](https://guides.github.com/introduction/flow/index.html) and the explanation of [Feature Branch Workflow](https://atlassian.com/git/tutorials/comparing-workflows#feature-branch-workflow) for the idea behind this kind of development.
+
1. Get the JabRef code on your local machine. Detailed instructions about this step can be found in our [guidelines for setting up a local workspace](https://github.com/JabRef/jabref/wiki/Guidelines-for-setting-up-a-local-workspace).
1. Fork the JabRef into your GitHub account.
2. Clone your forked repository on your local machine.
-3. **Create a new branch** (such as `fix-for-issue-121`). Be sure to create a **separate branch** for each improvement you implement.
-4. Do your work on the **new branch - not the master branch.** Refer to our [code howtos](https://github.com/JabRef/jabref/wiki/Code-Howtos) if you have questions about your implementation.
-5. 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/).
-
-In case you have any questions, do not hesitate to write one of our [JabRef developers](https://github.com/orgs/JabRef/teams/developers) an email. We should also be online at [gitter](https://gitter.im/JabRef/jabref).
+2. **Create a new branch** (such as `fix-for-issue-121`). Be sure to create a **separate branch** for each improvement you implement.
+3. Do your work on the **new branch - not the master branch.** Refer to our [code howtos](https://github.com/JabRef/jabref/wiki/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, consider creating a [draft pull request](https://github.blog/2019-02-14-introducing-draft-pull-requests/) instead.
+In case you have any questions, do not hesitate to write one of our [JabRef developers](https://github.com/orgs/JabRef/teams/developers) an email.
+We should also be online at [gitter](https://gitter.im/JabRef/jabref).
## Formal requirements for a pull request
+
The main goal of the formal requirements is to provide credit to you and to be able to understand the patch.
### Add your change to CHANGELOG.md
+
You should edit the [CHANGELOG.md](CHANGELOG.md) located in the root directory of the JabRef source.
Add a line with your changes in the appropriate section.
If you did internal refactorings or improvements not visible to the user (e.g., UI, .bib file), then you don't need to put an entry there.
-
#### Key format
+
Example: `Ctrl + Enter `
In case you add keys to the changelog, please follow these rules:
@@ -32,10 +39,10 @@ In case you add keys to the changelog, please follow these rules:
- Combined keys separated by `+`
- Spaces before and after separator `+`
-
### Author credits
-You will be given credit in the `AUTHORS` file in the root of the repository and the 'About' pages inside the main application.
-We will periodically update the contributors list inside `AUTHORS`.
+
+You will be given credit in the [`AUTHORS`](AUTHORS) file in the root of the repository and the 'About' pages inside the main application.
+We will periodically update the contributors list inside [`AUTHORS`](AUTHORS).
This is done by an automatic shell script `scripts/generate-authors.sh`.
If you want to add yourself directly with your pull request please run this script.
@@ -48,20 +55,20 @@ The contribution information is tracked via the version control system.
Your contribution is considered being made under [MIT license](https://tldrlegal.com/license/mit-license).
-
### Write a good commit message
+
See [good commit message] or [commit guidelines section of Pro Git].
-The first line of your commit message is automatically taken as title for the pull-request.
+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
-We know that writing test cases causes a lot of time.
-Nevertheless, we rely on our test cases to ensure that a bug fix or a feature implementation doesn't break anything.
-In case you do not have time to add a test case, we nevertheless ask you to run `gradlew check` to ensure that your change doesn't break anything else.
+We know that writing test cases takes a lot of time.
+Nevertheless, we rely on our test cases to ensure that a bug fix or a feature implementation doesn't break anything.
+In case you do not have time to add a test case, we nevertheless ask you to at least run `gradlew check` to ensure that your change doesn't break anything else.
### When adding a library
+
Please try to use a version available at jCenter and add it to `build.gradle`.
In any case, describe the library at [external-libraries.txt](external-libraries.txt).
We need that information for our package maintainers (e.g., those of the [debian package](https://tracker.debian.org/pkg/jabref)).
@@ -69,8 +76,8 @@ Also add a txt file stating the license in `libraries/`.
It is used at `gradlew processResources` to generate the About.html files.
You can see the result in `build\resources\main\help\en\About.html` or when clicking Help -> About.
-
### When making an architectural decision
+
In case you add a library or do major code rewrites, we ask you to document your decision.
Recommended reading: .
@@ -78,7 +85,7 @@ We simply ask to create a new markdown file in `docs/adr` following the template
In case you want to directly add a comment to a class, simply use following template (based on [sustainable architectural decisions](https://www.infoq.com/articles/sustainable-architectural-design-decisions)):
-```
+```text
In the context of ,
facing
we decided for
@@ -88,14 +95,14 @@ accepting ,
because .
```
-
### When adding a new Localization.lang entry
-Add new `Localization.lang("KEY")` to Java file.
-Tests fail. In the test output a snippet is generated which must be added to the English translation file.
+
+Add new `Localization.lang("KEY")` to a Java file.
+The tests will fail. In the test output a snippet is generated, which must be added to the English translation file.
Example:
-```
+```text
java.lang.AssertionError: DETECTED LANGUAGE KEYS WHICH ARE NOT IN THE ENGLISH LANGUAGE FILE
PASTE THESE INTO THE ENGLISH LANGUAGE FILE
[
@@ -105,20 +112,20 @@ Expected :[]
Actual :[Opens\ JabRef's\ Twitter\ page (src\main\java\org\jabref\gui\JabRefFrame.java LANG)]
```
-Add snippet to English translation file located at `src/main/resources/l10n/JabRef_en.properties`.
+Add the above snippet to the English translation file located at `src/main/resources/l10n/JabRef_en.properties`.
[Crowdin](http://translate.jabref.org/) 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 Wiki](https://github.com/JabRef/jabref/wiki/Code-Howtos#using-localization-correctly).
-
### Create a pull request
+
Create a pull request on GitHub.
For text inspirations, consider [How to write the perfect pull request](https://github.com/blog/1943-how-to-write-the-perfect-pull-request).
-You can add the prefix `[WIP]` to indicate that the pull request is not yet complete, but you want to discuss something or inform about the current state of affairs.
-
+If you want to indicate that a pull request is not yet complete **before** creating the pull request, you may consider creating a [draft pull request](https://github.blog/2019-02-14-introducing-draft-pull-requests/).
+Alternatively, once the PR has been created, you can add the prefix `[WIP]` (which stands for "Work in Progress") to indicate that the pull request is not yet complete, but you want to discuss something or inform about the current state of affairs.
[commit guidelines section of Pro Git]: http://git-scm.com/book/en/Distributed-Git-Contributing-to-a-Project#Commit-Guidelines
[good commit message]: https://github.com/joelparkerhenderson/git_commit_message
diff --git a/README.md b/README.md
index da4f8b31e64..bcb54489c1d 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-# JabRef Bibliography Management
+# JabRef Bibliography Management [![Hacktoberfest](https://img.shields.io/github/hacktoberfest/2019/JabRef/jabref?suggestion_label=hacktoberfest)](https://www.jabref.org/hacktoberfest/2019.html)
[![Build Status](https://travis-ci.org/JabRef/jabref.svg?branch=master)](https://travis-ci.org/JabRef/jabref)
[![codecov.io](https://codecov.io/github/JabRef/jabref/coverage.svg?branch=master)](https://codecov.io/github/JabRef/jabref?branch=master)
@@ -34,7 +34,7 @@ JabRef supports you in every step of your research work.
#### Organize
-- Group your research into hierarchical collections and organize research items based on keywords/tags, search terms or your own 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 catalogues such as Google Scholar, Springer or MathSciNet
- Customizable citation key generator
@@ -48,12 +48,12 @@ JabRef supports you in every step of your research work.
- Native [BibTeX] and [Biblatex] support
- Cite-as-you-write functionality for external applications such as Emacs, Kile, LyX, Texmaker, TeXstudio, Vim and WinEdt.
-- Format references in one of the many thousand built-in citation styles or create your own style
+- Format references in one of the many thousand built-in citation styles or create your style
- Support for Word and LibreOffice/OpenOffice for inserting and formatting citations
#### Share
-- Many built-in export options or create your own export format
+- Many built-in export options or create your export format
- Library is saved as a simple text file and thus it is easy to share with others via Dropbox and is version-control friendly
- Work in a team: sync the contents of your library via a SQL database
@@ -62,7 +62,7 @@ JabRef supports you in every step of your research work.
Fresh development builds are available at [builds.jabref.org](https://builds.jabref.org/master/).
The [latest stable release is available at FossHub](https://www.fosshub.com/JabRef.html).
-JabRef runs on any system equipped with the Java Virtual Machine (1.8), which can be downloaded at no cost from [Oracle](http://www.oracle.com/technetwork/java/javase/downloads/index.html). Note that Java 9 is currently not supported.
+JabRef runs on any system equipped with the Java Virtual Machine (Java 11 or higher), which can be downloaded at no cost from [AdoptOpenDJK](https://adoptopenjdk.net/).
From JabRef 4.0 onwards, [JavaFX] support has to be installed.
- Windows: JabRef offers an installer, which also adds a shortcut to JabRef to your start menu. Please also see our [Windows FAQ](https://help.jabref.org/en/FAQwindows)
- Linux: Please see our [Installation Guide](http://help.jabref.org/en/Installation).
@@ -91,7 +91,7 @@ An explanation of donation possibilities and usage of donations is available at
> Not a programmer? [Learn how to help.](http://contribute.jabref.org)
-Want to be part of a free and open-source project that tens of thousands scientist use every day? Check out the ways you can contribute, below:
+Want to be part of a free and open-source project that tens of thousands of scientists use every day? Check out the ways you can contribute, below:
- For details on how to contribute, have a look at our [guidelines for contributing](CONTRIBUTING.md).
- You are welcome to contribute new features. To get your code included into JabRef, just [fork](https://help.github.com/en/articles/fork-a-repo) the JabRef repository, make your changes, and create a [pull request](https://help.github.com/en/articles/about-pull-requests).
- To work on existing JabRef issues, check out our [issue tracker](https://github.com/JabRef/jabref/issues). New to open source contributing? Look for issues with the ["good first issue"](https://github.com/JabRef/jabref/labels/good%20first%20issue) label to get started.
diff --git a/build.gradle b/build.gradle
index 15c0633d027..5e3b16b3270 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,5 +1,8 @@
import groovy.json.JsonSlurper
import org.gradle.internal.os.OperatingSystem
+import org.jabref.build.antlr.JabRefAntlrPlugin
+import org.jabref.build.localization.LocalizationPlugin
+import org.jabref.build.xjc.XjcPlugin
import org.jabref.build.xjc.XjcTask
// to update the gradle wrapper, execute
@@ -9,50 +12,53 @@ buildscript {
repositories {
mavenLocal()
jcenter()
- maven {
- url 'https://oss.sonatype.org/content/groups/public'
- }
}
}
plugins {
- id 'com.gradle.build-scan' version '2.4.1'
- id 'com.install4j.gradle' version '7.0.12'
- id 'com.github.johnrengelman.shadow' version '5.1.0'
+ id 'application'
+ id 'com.gradle.build-scan' version '2.4.2'
id "com.simonharrer.modernizer" version '1.8.0-1'
id 'me.champeau.gradle.jmh' version '0.4.8'
- id 'net.ltgt.errorprone' version '0.8.1'
- id 'com.github.ben-manes.versions' version '0.22.0'
+ //id 'net.ltgt.errorprone' version '0.8.1'
+ id 'com.github.ben-manes.versions' version '0.27.0'
+ id 'org.javamodularity.moduleplugin' version '1.5.0'
+ id 'org.openjfx.javafxplugin' version '0.0.8'
+ id 'org.beryx.jlink' version '2.16.2'
}
// use the gradle build scan feature: https://scans.gradle.com/get-started
buildScan { termsOfServiceUrl = 'https://gradle.com/terms-of-service'; termsOfServiceAgree = 'yes' }
-
apply plugin: 'java'
apply plugin: 'application'
apply plugin: 'project-report'
apply plugin: 'jacoco'
-apply plugin: 'install4j'
apply plugin: 'me.champeau.gradle.jmh'
apply plugin: 'checkstyle'
-apply plugin: org.jabref.build.antlr.AntlrPlugin
-apply plugin: org.jabref.build.xjc.XjcPlugin
-apply plugin: org.jabref.build.localization.LocalizationPlugin
+apply plugin: JabRefAntlrPlugin
+apply plugin: XjcPlugin
+apply plugin: LocalizationPlugin
apply from: 'eclipse.gradle'
group = "org.jabref"
-version = "5.0-dev"
-project.ext.threeDotVersion = "5.0.0.0"
-project.ext.install4jDir = hasProperty("install4jDir") ? getProperty("install4jDir") : (OperatingSystem.current().isWindows() ? 'C:/Program Files/install4j7' : 'install4j7')
-sourceCompatibility = 1.8
-targetCompatibility = 1.8
-mainClassName = "org.jabref.JabRefMain"
+version = "5.0.0"
+sourceCompatibility = 12
+targetCompatibility = 12
+mainClassName = "$moduleName/org.jabref.JabRefLauncher"
+
+// TODO: Ugly workaround to temporarily ignore build errors to dependencies of latex2unicode
+// These should be removed, as well as the files in the lib folder, as soon as they have valid module names
+patchModules.config = [
+ "test=fastparse_2.12-1.0.0.jar",
+ "test2=fastparse-utils_2.12-1.0.0.jar",
+ "test3=sourcecode_2.12-0.1.4.jar"
+]
// These are the Java version requirements we will check on each start of JabRef
ext.minRequiredJavaVersion = "1.8.0_171"
-ext.allowJava9 = false
+ext.allowJava9 = true
sourceSets {
main {
@@ -77,34 +83,48 @@ sourceSets {
repositories {
mavenLocal()
jcenter()
- maven {
- url 'https://oss.sonatype.org/content/groups/public'
- }
+ maven { url 'https://oss.sonatype.org/content/groups/public' }
+ maven { url 'https://repository.apache.org/snapshots' }
}
configurations {
- errorprone
+ //errorprone
+ libreoffice
+
+ // TODO: Remove the following workaround for split error messages such as
+ // error: module java.xml.bind reads package javax.annotation from both jsr305 and java.annotation
+ compile {
+ exclude group: "javax.activation"
+ }
+}
+
+javafx {
+ version = "13"
+ modules = [ 'javafx.controls', 'javafx.fxml', 'javafx.web', 'javafx.swing' ]
}
dependencies {
// Include all jar-files in the 'lib' folder as dependencies
compile fileTree(dir: 'lib', includes: ['*.jar'])
- compile 'org.apache.pdfbox:pdfbox:2.0.16'
- compile 'org.apache.pdfbox:fontbox:2.0.16'
- compile 'org.apache.pdfbox:xmpbox:2.0.16'
+ compile 'org.apache.pdfbox:pdfbox:2.0.17'
+ compile 'org.apache.pdfbox:fontbox:2.0.17'
+ compile 'org.apache.pdfbox:xmpbox:2.0.17'
compile group: 'org.apache.tika', name: 'tika-core', version: '1.22'
// required for reading write-protected PDFs - see https://github.com/JabRef/jabref/pull/942#issuecomment-209252635
- compile 'org.bouncycastle:bcprov-jdk15on:1.62'
+ compile 'org.bouncycastle:bcprov-jdk15on:1.64'
compile 'commons-cli:commons-cli:1.4'
- compile "org.libreoffice:juh:6.2.3"
- compile "org.libreoffice:jurt:6.2.3"
- compile "org.libreoffice:ridl:6.2.3"
- compile "org.libreoffice:unoil:6.2.3"
+ // For Java 9+ compatibility, we include a bundled version of the libreoffice libraries
+ // See https://bugs.documentfoundation.org/show_bug.cgi?id=117331#c8 for background information
+ // Use the task bundleLibreOffice to update the bundled jar
+ //compile "org.libreoffice:juh:6.2.3"
+ //compile "org.libreoffice:jurt:6.2.3"
+ //compile "org.libreoffice:ridl:6.2.3"
+ //compile "org.libreoffice:unoil:6.2.3"
compile 'io.github.java-diff-utils:java-diff-utils:4.0'
compile 'info.debatty:java-string-similarity:1.2.1'
@@ -115,70 +135,93 @@ dependencies {
antlr4 'org.antlr:antlr4:4.7.2'
compile 'org.antlr:antlr4-runtime:4.7.2'
- compile group: 'org.mariadb.jdbc', name: 'mariadb-java-client', version: '2.4.3'
+ compile group: 'org.mariadb.jdbc', name: 'mariadb-java-client', version: '2.5.1'
- compile 'org.postgresql:postgresql:42.2.6'
+ compile 'org.postgresql:postgresql:42.2.8'
- compile 'com.google.guava:guava:28.0-jre'
+ compile ('com.google.guava:guava:28.1-jre') {
+ // TODO: Remove this as soon as https://github.com/google/guava/issues/2960 is fixed
+ exclude module: "jsr305"
+ }
+ compile group: 'jakarta.annotation', name: 'jakarta.annotation-api', version: '1.3.5'
// JavaFX stuff
- compile 'de.jensd:fontawesomefx-commons:8.15'
- compile 'de.jensd:fontawesomefx-materialdesignfont:1.7.22-4'
- compile 'de.saxsys:mvvmfx-validation:1.8.0'
+ compile 'de.jensd:fontawesomefx-commons:11.0'
+ compile 'de.jensd:fontawesomefx-materialdesignfont:1.7.22-11'
+ compile 'de.saxsys:mvvmfx-validation:1.9.0-SNAPSHOT'
compile 'de.saxsys:mvvmfx:1.8.0'
compile 'org.fxmisc.easybind:easybind:1.0.3'
compile 'org.fxmisc.flowless:flowless:0.6.1'
- compile 'org.fxmisc.richtext:richtextfx:0.10.1'
- compile 'com.sibvisions.external.jvxfx:dndtabpane:0.1'
- compile 'javax.inject:javax.inject:1'
- compile 'com.jfoenix:jfoenix:8.0.9'
-
- // Cannot be updated to 9.*.* until Jabref works with Java 9
- compile 'org.controlsfx:controlsfx:8.40.16-SNAPSHOT'
+ compile 'org.fxmisc.richtext:richtextfx:0.10.2'
+ compile group: 'org.glassfish.hk2.external', name: 'jakarta.inject', version: '2.6.1'
+ compile 'com.jfoenix:jfoenix:9.0.9'
+ compile 'org.controlsfx:controlsfx:11.0.0'
compile 'org.jsoup:jsoup:1.12.1'
compile 'com.mashape.unirest:unirest-java:1.4.9'
- // >1.8.0-beta is required for java 9 compatibility
- compile 'org.slf4j:slf4j-api:2.0.0-alpha0'
- compile 'org.apache.logging.log4j:log4j-slf4j18-impl:2.12.1'
- compile 'org.apache.logging.log4j:log4j-jcl:2.12.1'
- compile 'org.apache.logging.log4j:log4j-api:2.12.1'
- compile 'org.apache.logging.log4j:log4j-core:2.12.1'
+ compile 'org.slf4j:slf4j-api:2.0.0-alpha1'
+ compile group: 'org.apache.logging.log4j', name: 'log4j-jcl', version: '3.0.0-SNAPSHOT'
+ compile group: 'org.apache.logging.log4j', name: 'log4j-slf4j18-impl', version: '3.0.0-SNAPSHOT'
+ compile group: 'org.apache.logging.log4j', name: 'log4j-plugins', version: '3.0.0-SNAPSHOT'
+ implementation group: 'org.apache.logging.log4j', name: 'log4j-api', version: '3.0.0-SNAPSHOT'
+ compile group: 'org.apache.logging.log4j', name: 'log4j-core', version: '3.0.0-SNAPSHOT'
+ annotationProcessor group: 'org.apache.logging.log4j', name: 'log4j-core', version: '3.0.0-SNAPSHOT'
compile 'de.undercouch:citeproc-java:1.0.1'
- compile 'com.github.tomtung:latex2unicode_2.12:0.2.2'
+ compile group: 'jakarta.activation', name: 'jakarta.activation-api', version: '1.2.1'
+ compile group: 'jakarta.xml.bind', name: 'jakarta.xml.bind-api', version: '2.3.2'
+ compile group: 'org.glassfish.jaxb', name: 'jaxb-runtime', version: '2.3.2'
+
+ compile ('com.github.tomtung:latex2unicode_2.12:0.2.6') {
+ exclude module: 'fastparse_2.12'
+ }
- errorproneJavac 'com.google.errorprone:javac:1.8.0-u20'
+ /*
+ TODO: Reenable error prone as soon as https://github.com/google/error-prone/issues/1210 is fixed
+ errorprone ('com.google.errorprone:error_prone_core:2.3.2') {
+ exclude module: "jsr305"
+ exclude group: 'org.checkerframework', module: 'checker-qual'
+ exclude group: 'com.google.errorprone', module: 'error_prone_annotation'
+ exclude group: 'com.google.errorprone', module: 'error_prone_annotations'
+ exclude group: 'com.google.errorprone', module: 'error_prone_check_api'
+ }
+ */
compile group: 'com.microsoft.azure', name: 'applicationinsights-core', version: '2.4.1'
- compile group: 'com.microsoft.azure', name: 'applicationinsights-logging-log4j2', version: '2.4.1'
+ compile (group: 'com.microsoft.azure', name: 'applicationinsights-logging-log4j2', version: '2.4.1') {
+ exclude module: "log4j-core"
+ }
+
- testImplementation 'org.junit.jupiter:junit-jupiter:5.5.1'
- testRuntimeOnly 'org.junit.vintage:junit-vintage-engine:5.5.1'
- testCompile 'org.junit.platform:junit-platform-launcher:1.5.1'
+ testCompile 'io.github.classgraph:classgraph:4.8.52'
+ testCompile 'junit:junit:4.12'
+ testImplementation 'org.junit.jupiter:junit-jupiter:5.5.2'
+ testRuntimeOnly 'org.junit.vintage:junit-vintage-engine:5.5.2'
+ testCompile 'org.junit.platform:junit-platform-launcher:1.5.2'
- testRuntime 'org.apache.logging.log4j:log4j-core:2.12.0'
- testRuntime 'org.apache.logging.log4j:log4j-jul:2.12.1'
- testCompile 'org.mockito:mockito-core:3.0.0'
- testCompile 'com.github.tomakehurst:wiremock:2.24.1'
- testCompile 'org.reflections:reflections:0.9.11'
+ testCompile 'net.bytebuddy:byte-buddy-parent:1.10.2'
+ testRuntime group: 'org.apache.logging.log4j', name: 'log4j-core', version: '3.0.0-SNAPSHOT'
+ testRuntime group: 'org.apache.logging.log4j', name: 'log4j-jul', version: '3.0.0-SNAPSHOT'
+ testCompile 'org.mockito:mockito-core:3.1.0'
+ //testCompile 'com.github.tomakehurst:wiremock:2.24.1'
testCompile 'org.xmlunit:xmlunit-core:2.6.3'
testCompile 'org.xmlunit:xmlunit-matchers:2.6.3'
- testRuntime 'com.tngtech.archunit:archunit-junit5-engine:0.11.0'
testCompile 'com.tngtech.archunit:archunit-junit5-api:0.11.0'
- testCompile "org.testfx:testfx-core:4.0.+"
- testCompile "org.testfx:testfx-junit5:4.0.+"
+ //testRuntime 'com.tngtech.archunit:archunit-junit5-engine:0.11.0'
+ testCompile 'com.tngtech.archunit:archunit-junit5-api:0.11.0'
+ testCompile "org.testfx:testfx-core:4.0.15-alpha"
+ testCompile "org.testfx:testfx-junit5:4.0.15-alpha"
- checkstyle 'com.puppycrawl.tools:checkstyle:8.23'
- xjc 'com.sun.xml.bind:jaxb-xjc:2.2.4-1'
+ checkstyle 'com.puppycrawl.tools:checkstyle:8.25'
+ xjc group: 'org.glassfish.jaxb', name: 'jaxb-xjc', version: '2.3.2'
jython 'org.python:jython-standalone:2.7.1'
}
jacoco {
- toolVersion = '0.8.1'
+ toolVersion = '0.8.3'
}
dependencyUpdates {
@@ -196,11 +239,6 @@ dependencyUpdates.resolutionStrategy = {
selection.reject('Release candidate')
}
}
- rules.withModule("org.controlsfx:controlsfx") { ComponentSelection selection ->
- if (selection.candidate.version ==~ /9.*/) { // Reject version 9 or higher
- selection.reject("Cannot be updated to 9.*.* until Jabref works with Java 9")
- }
- }
rules.withModule("com.github.tomtung:latex2unicode_2.12") { ComponentSelection selection ->
if (selection.candidate.version ==~ /0.2.*/) {
// Reject version higher than 2.0.2. see https://github.com/JabRef/jabref/pull/3781
@@ -212,24 +250,9 @@ dependencyUpdates.resolutionStrategy = {
selection.reject("Version 4.X breaks the release process.")
}
}
- rules.withModule("de.jensd:fontawesomefx-materialdesignfont") { ComponentSelection selection ->
- if (selection.candidate.version ==~ /2.*/) {
- selection.reject("Cannot be upgraded to version 2")
- }
- }
- rules.withModule("com.jfoenix:jfoenix") { ComponentSelection selection ->
- if (selection.candidate.version ==~ /9.*/) { // Reject version 9 or higher
- selection.reject("Cannot be updated to 9.*.* until Jabref works with Java 9")
- }
- }
- rules.withModule("com.google.errorprone:javac") { ComponentSelection selection ->
- if (selection.candidate.version ==~ /1.9.*/ || selection.candidate.version ==~ /9.*/) {
- selection.reject("Cannot be updated to 9.*.* until Jabref works with Java 9")
- }
- }
- rules.withModule("com.sun.xml.bind:jaxb-xjc") { ComponentSelection selection ->
- if (!(selection.candidate.version ==~ /2.2.4.*/) || selection.candidate.version ==~ /2.[3-9].*/) {
- selection.reject("Cannot be updated to 2.2.5 or higher.")
+ rules.withModule("com.google.errorprone:error_prone_core") { ComponentSelection selection ->
+ if (selection.candidate.version ==~ /2.3.3/) {
+ selection.reject("Does not work due to bug https://github.com/google/error-prone/issues/1240")
}
}
}
@@ -250,7 +273,7 @@ processResources {
filteringCharset = 'UTF-8'
filesMatching("build.properties") {
- expand(version: project.version,
+ expand(version: createVersionString(),
"year": String.valueOf(Calendar.getInstance().get(Calendar.YEAR)),
"authors": new File('AUTHORS').readLines().findAll { !it.startsWith("#") }.join(", "),
"developers": new File('DEVELOPERS').readLines().findAll { !it.startsWith("#") }.join(", "),
@@ -319,10 +342,9 @@ task generateEndnoteSource(type: XjcTask) {
group = 'JabRef'
description = "Generates java files for the endnote importer."
- schemaFile = "src/main/resources/xjc/endnote/RSXML.dtd"
+ schemaFile = "src/main/resources/xjc/endnote/endnote.xsd"
outputDirectory = "src/main/gen/"
javaPackage = "org.jabref.logic.importer.fileformat.endnote"
- arguments = '-dtd'
}
task generateModsSource(type: XjcTask) {
@@ -339,14 +361,62 @@ task generateModsSource(type: XjcTask) {
tasks.withType(JavaCompile) {
// use UTF-8
options.encoding = 'UTF-8'
-
- // ignore annotation processor from log4j2
- options.compilerArgs += '-proc:none'
}
compileJava {
options.compilerArgs << "-Xlint:none"
dependsOn "generateSource"
+
+ moduleOptions {
+ // TODO: Remove access to internal api
+ addExports = [
+ 'javafx.controls/com.sun.javafx.scene.control' : 'org.jabref',
+ 'org.controlsfx.controls/impl.org.controlsfx.skin' : 'org.jabref'
+ ]
+ }
+}
+
+run {
+ // TODO: Remove access to internal api
+ jvmArgs '--add-exports', 'javafx.controls/com.sun.javafx.scene.control=org.jabref',
+ '--add-exports', 'org.controlsfx.controls/impl.org.controlsfx.skin=org.jabref',
+ '--add-opens', 'javafx.controls/javafx.scene.control=org.jabref',
+ '--add-opens', 'org.controlsfx.controls/org.controlsfx.control.textfield=org.jabref',
+ '--add-opens', 'javafx.controls/com.sun.javafx.scene.control=org.jabref',
+ // Not sure why we need to restate the controlfx exports
+ // Taken from here: https://github.com/controlsfx/controlsfx/blob/9.0.0/build.gradle#L1
+ "--add-exports", "javafx.graphics/com.sun.javafx.scene=org.controlsfx.controls",
+ "--add-exports", "javafx.graphics/com.sun.javafx.scene.traversal=org.controlsfx.controls",
+ "--add-exports", "javafx.graphics/com.sun.javafx.css=org.controlsfx.controls",
+ "--add-exports", "javafx.controls/com.sun.javafx.scene.control.behavior=org.controlsfx.controls",
+ "--add-exports", "javafx.controls/com.sun.javafx.scene.control=org.controlsfx.controls",
+ "--add-exports", "javafx.controls/com.sun.javafx.scene.control.inputmap=org.controlsfx.controls",
+ "--add-exports", "javafx.base/com.sun.javafx.event=org.controlsfx.controls",
+ "--add-exports", "javafx.base/com.sun.javafx.collections=org.controlsfx.controls",
+ "--add-exports", "javafx.base/com.sun.javafx.runtime=org.controlsfx.controls",
+ "--add-exports", "javafx.web/com.sun.webkit=org.controlsfx.controls",
+ "--add-exports", "javafx.graphics/com.sun.javafx.css=org.controlsfx.controls",
+ "--add-opens", "javafx.controls/javafx.scene.control.skin=org.controlsfx.controls",
+ "--add-opens", "javafx.graphics/javafx.scene=org.controlsfx.controls",
+ "--add-exports", "javafx.controls/com.sun.javafx.scene.control.behavior=com.jfoenix",
+ "--add-opens", "javafx.controls/com.sun.javafx.scene.control.behavior=com.jfoenix",
+ "--add-opens", "javafx.base/com.sun.javafx.binding=com.jfoenix",
+ "--add-opens", "javafx.graphics/com.sun.javafx.stage=com.jfoenix",
+ "--add-opens", "javafx.base/com.sun.javafx.event=com.jfoenix"
+
+ // TODO: The following code should have the same affect as the above one, but doesn't work for some reason
+ // https://github.com/java9-modularity/gradle-modules-plugin/issues/89
+ moduleOptions {
+ addExports = [
+ 'javafx.controls/com.sun.javafx.scene.control' : 'org.jabref',
+ 'org.controlsfx.controls/impl.org.controlsfx.skin' : 'org.jabref'
+ ]
+
+ addOpens = [
+ 'javafx.controls/javafx.scene.control' : 'org.jabref',
+ 'org.controlsfx.controls/org.controlsfx.control.textfield' : 'org.jabref'
+ ]
+ }
}
javadoc {
@@ -399,10 +469,6 @@ processTestResources.dependsOn copyTestResources
tasks.withType(Test) {
reports.html.destination = file("${reporting.baseDir}/${name}")
-
- jacoco {
- append = true
- }
}
task jacocoMerge(type: JacocoMerge) {
@@ -422,7 +488,8 @@ jacocoTestReport {
// Code quality tasks
checkstyle {
-
+ // will only run when called explicitly from the command line
+ sourceSets = []
}
modernizer {
@@ -432,102 +499,144 @@ modernizer {
}
// Release tasks
-shadowJar {
- transform(com.github.jengelman.gradle.plugins.shadow.transformers.Log4j2PluginsCacheFileTransformer)
- classifier 'fat'
- zip64 true
-}
-
-/*
- * Changes project.version to VERSION--snapshot--DATE--GIT_HASH
- */
-if (hasProperty('dev')) {
- String command = "git log --pretty=format:%cd--%h -n 1 --date=short"
- String commitInfo = ""
- if (OperatingSystem.current().isWindows()) {
- commitInfo = "cmd /c $command".execute().in.text
- } else {
- commitInfo = command.execute().in.text
- }
+task deleteInstallerTemp(type: Delete) {
+ delete "$buildDir/installer"
+}
- // determine branch
- command = "git symbolic-ref -q --short HEAD"
- String branchName = ""
- if (OperatingSystem.current().isWindows()) {
- branchName = "cmd /c $command".execute().in.text
- } else {
- branchName = command.execute().in.text
+jpackage.dependsOn deleteInstallerTemp
+jlink {
+ options = ['--strip-debug', '--compress', '2', '--no-header-files', '--no-man-pages']
+ launcher {
+ name = 'JabRef'
}
- // A newline is returned. Remove it. (trim())
- // In the context of github, the branch name could be something like "pull/277"
- // "/" is an illegal character. To be safe, all illegal filename characters are replaced by "_"
- // http://stackoverflow.com/a/15075907/873282 describes the used pattern.
- branchName = branchName.trim().replaceAll("[^a-zA-Z0-9.-]", "_")
- // hack string
- // first the date (%cd), then the branch name, and finally the commit id (%h)
- String infoString = commitInfo.substring(0, 10) + "--" + branchName + "--" + commitInfo.substring(12)
+ addOptions("--bind-services")
+
+ // TODO: Remove the following as soon as the dependencies are fixed (upstream)
+ // forceMerge is usually needed when some non-modular artifacts in the dependency graph use code that was previously part of the JDK
+ // but it was removed in the newer releases.
+ // The pom.xml associated with such a non-modular artifact does not mention that the artifact depends on the removed code
+ // (because the artifact was published when this code was still available in the JDK).
+ forceMerge "javafx"
+ forceMerge "controlsfx", "bcprov", "jaxb", "istack", "stax", "log4j"
+
+ // TODO: Remove the following correction to the merged module
+ // The module descriptor automatically generated by the plugin for the merged module contained some invalid entries.
+ // Execute ./gradlew suggestMergedModuleInfo and include the incorrect directives here.
+ mergedModule {
+ requires 'java.logging'
+ requires 'jdk.xml.dom'
+ requires 'java.sql'
+ requires 'java.rmi'
+ requires 'java.xml'
+ requires 'com.sun.xml.txw2'
+ requires 'java.desktop'
+ requires 'java.security.jgss'
+ requires 'jdk.jsobject'
+ requires 'jdk.unsupported'
+ requires 'java.management'
+ requires 'java.naming'
+ requires 'jdk.unsupported.desktop'
+ requires 'java.security.sasl'
+ requires 'java.scripting'
+ requires 'java.datatransfer'
+ requires 'java.compiler'
+ requires 'java.transaction.xa'
+ requires 'com.sun.xml.fastinfoset'
+ requires 'org.slf4j'
+ uses 'com.airhacks.afterburner.injection.PresenterFactory'
+ uses 'org.controlsfx.glyphfont.GlyphFont'
+ uses 'com.airhacks.afterburner.views.ResourceLocator'
+ provides 'java.sql.Driver' with 'org.postgresql.Driver'
+ provides 'org.controlsfx.glyphfont.GlyphFont' with 'org.controlsfx.glyphfont.FontAwesome'
+ provides 'org.apache.commons.logging.LogFactory' with 'org.apache.logging.log4j.jcl.LogFactoryImpl'
+ provides 'org.slf4j.spi.SLF4JServiceProvider' with 'org.apache.logging.slf4j.SLF4JServiceProvider'
+ provides 'com.microsoft.applicationinsights.core.dependencies.io.grpc.ServerProvider' with 'com.microsoft.applicationinsights.core.dependencies.io.grpc.netty.shaded.io.grpc.netty.NettyServerProvider'
+ provides 'com.microsoft.applicationinsights.core.dependencies.io.grpc.NameResolverProvider' with 'com.microsoft.applicationinsights.core.dependencies.io.grpc.internal.DnsNameResolverProvider'
+ provides 'java.security.Provider' with 'org.bouncycastle.jce.provider.BouncyCastleProvider', 'org.bouncycastle.pqc.jcajce.provider.BouncyCastlePQCProvider'
+ provides 'com.microsoft.applicationinsights.core.dependencies.io.grpc.ManagedChannelProvider' with 'com.microsoft.applicationinsights.core.dependencies.io.grpc.netty.shaded.io.grpc.netty.NettyChannelProvider'
+ }
- project.version += "--snapshot--" + infoString
-}
+ jpackage {
+ // In order for this to work, you need to dowload jpackage from https://jdk.java.net/jpackage/
+ // and put the path to the jdk-14 folder in the environment variable BADASS_JLINK_JPACKAGE_HOME
+ outputDir = "distribution"
+
+ if (OperatingSystem.current().isWindows()) {
+ // This requires WiX to be installed: https://github.com/wixtoolset/wix3/releases
+ installerType = "msi"
+ imageOptions = [
+ '--win-console',
+ '--icon', "${projectDir}/src/main/resources/icons/jabref.ico",
+ ]
+ installerOptions = [
+ '--vendor', 'JabRef',
+ '--app-version', "${project.version}",
+ '--win-upgrade-uuid', 'd636b4ee-6f10-451e-bf57-c89656780e36',
+ '--win-dir-chooser',
+ '--win-shortcut',
+ '--temp', "$buildDir/installer",
+ '--resource-dir', "${projectDir}/buildres/windows"
+ ]
+ }
-install4j {
- installDir = file(project.ext.install4jDir)
-}
+ if (OperatingSystem.current().isLinux()) {
+ imageOptions = [
+ '--icon', "${projectDir}/src/main/resources/icons/JabRef-icon-64.png",
+ ]
+ installerOptions = [
+ '--vendor', 'JabRef',
+ '--app-version', "${project.version}",
+ // '--temp', "$buildDir/installer",
+ '--resource-dir', "${projectDir}/buildres/linux",
+ '--linux-menu-group', 'Office;',
+ '--linux-rpm-license-type', 'MIT',
+ // '--license-file', "${projectDir}/LICENSE.md",
+ '--description', 'JabRef is an open source bibliography reference manager. The native file format used by JabRef is BibTeX, the standard LaTeX bibliography format.',
+ '--linux-shortcut'
+ ]
+ }
-task generateFinalJabRefPS1File(type: Copy) {
- from('buildres') {
- include 'JabRef.ps1'
+ if (OperatingSystem.current().isMacOsX()) {
+ imageOptions = [
+ '--icon', "${projectDir}/src/main/resources/icons/jabref.icns",
+ ]
+ installerOptions = [
+ '--vendor', 'JabRef',
+ '--app-version', "${project.version}"
+ ]
+ }
}
- into 'build'
- filter(org.apache.tools.ant.filters.ReplaceTokens, tokens: [jabRefJarFileName: jar.archiveName])
-}
-
-// has to be defined AFTER 'dev' things to have the correct project.version
-task media(type: com.install4j.gradle.Install4jTask, dependsOn: ["releaseJar", "generateFinalJabRefPS1File"]) {
- projectFile = file('jabref.install4j')
- release = project.version
- winKeystorePassword = System.getenv('CERTIFICATE_PW')
- macKeystorePassword = System.getenv('CERTIFICATE_PW')
- variables = [
- versionFourDots: project.ext.threeDotVersion,
- buildFileName : jar.archiveName,
- version : project.version
- ]
+}
- doLast {
+if (OperatingSystem.current().isWindows()) {
+ tasks.jpackageImage.doLast {
copy {
- from "build/install4j"
- into "build/releases"
+ from("${projectDir}/buildres/windows") {
+ include "jabref.json", "JabRefHost.bat", "JabRefHost.ps1"
+ }
+ into "$buildDir/distribution/JabRef"
}
}
}
-
-task release(dependsOn: ["media", "releaseJar"]) {
- group = 'JabRef - Release'
- description 'Creates a release for all target platforms.'
-}
-
-task releaseJar(dependsOn: "shadowJar") {
- group = 'JabRef - Release'
- description "Creates a Jar release."
- doLast {
+if (OperatingSystem.current().isLinux()) {
+ tasks.jpackageImage.doLast {
copy {
- from("$buildDir/libs/JabRef-${project.version}-fat.jar")
- into("$buildDir/releases")
- rename { String fileName ->
- fileName.replace('-fat', '')
+ from("${projectDir}/buildres/linux") {
+ include "org.jabref.jabref.json", "jabrefHost.py"
}
+ into "$buildDir/distribution/JabRef/lib"
}
- // set executable with read permissions (first true) and for all (false)
- file("$buildDir/releases/JabRef-${project.version}.jar").setExecutable(true, false)
}
}
-task snapJar(dependsOn: "releaseJar", type: Delete) {
- delete fileTree(dir: "$buildDir/releases/", exclude: "JabRef-${project.version}.jar")
+// Delete unnecessary folder in application image
+// TODO: Remove this workaround as soon as https://github.com/beryx/badass-jlink-plugin/issues/51 is fixed
+task deleteModsFolder(type: Delete) {
+ delete "$buildDir/distribution/JabRef/app/mods"
}
+deleteModsFolder.shouldRunAfter jpackageImage
jmh {
warmupIterations = 5
@@ -550,3 +659,46 @@ task downloadDependencies {
}
}
}
+
+task bundleLibreOffice(type: Jar) {
+ from configurations.libreoffice.collect { zipTree it }
+
+ manifest {
+ attributes 'Automatic-Module-Name': 'org.jabref.thirdparty.libreoffice'
+ }
+
+ destinationDir = file('lib')
+ archiveName = 'libreoffice.jar'
+}
+
+// Returns the value of project.version in production and a string of the format VERSION-dev--DATE--BRANCH--GIT_HASH for development versions
+def createVersionString() {
+ if (hasProperty('dev')) {
+ String command = "git log --pretty=format:%cd--%h -n 1 --date=short"
+ String commitInfo
+ if (OperatingSystem.current().isWindows()) {
+ commitInfo = "cmd /c $command".execute().in.text
+ } else {
+ commitInfo = command.execute().in.text
+ }
+
+ // determine branch
+ command = "git symbolic-ref -q --short HEAD"
+ String branchName
+ if (OperatingSystem.current().isWindows()) {
+ branchName = "cmd /c $command".execute().in.text
+ } else {
+ branchName = command.execute().in.text
+ }
+ // A newline is returned. Remove it. (trim())
+ // In the context of github, the branch name could be something like "pull/277"
+ // "/" is an illegal character. To be safe, all illegal filename characters are replaced by "_"
+ // http://stackoverflow.com/a/15075907/873282 describes the used pattern.
+ branchName = branchName.trim().replaceAll("[^a-zA-Z0-9.-]", "_")
+
+ // first the date (%cd), then the branch name, and finally the commit id (%h)
+ return project.version + "-dev--" + commitInfo.substring(0, 10) + "--" + branchName + "--" + commitInfo.substring(12)
+ } else {
+ return project.version
+ }
+}
diff --git a/buildSrc/src/main/groovy/org/jabref/build/antlr/AntlrTask.groovy b/buildSrc/src/main/groovy/org/jabref/build/antlr/AntlrTask.groovy
index 6ff19a1a02f..c31aefc1d19 100644
--- a/buildSrc/src/main/groovy/org/jabref/build/antlr/AntlrTask.groovy
+++ b/buildSrc/src/main/groovy/org/jabref/build/antlr/AntlrTask.groovy
@@ -13,7 +13,7 @@ class AntlrTask extends JavaExec {
private String outputDir = ""
private String javaPackage = ""
- public AntlrTask() {
+ AntlrTask() {
project.configurations {
antlr3
antlr4
diff --git a/buildSrc/src/main/groovy/org/jabref/build/antlr/AntlrPlugin.groovy b/buildSrc/src/main/groovy/org/jabref/build/antlr/JabRefAntlrPlugin.groovy
similarity index 92%
rename from buildSrc/src/main/groovy/org/jabref/build/antlr/AntlrPlugin.groovy
rename to buildSrc/src/main/groovy/org/jabref/build/antlr/JabRefAntlrPlugin.groovy
index ce9a7301445..0fc139d7b80 100644
--- a/buildSrc/src/main/groovy/org/jabref/build/antlr/AntlrPlugin.groovy
+++ b/buildSrc/src/main/groovy/org/jabref/build/antlr/JabRefAntlrPlugin.groovy
@@ -6,7 +6,7 @@ import org.gradle.api.Project
/**
* Configures the project for use with ANTLR 3 or 4.
*/
-class AntlrPlugin implements Plugin {
+class JabRefAntlrPlugin implements Plugin {
public static final def ANTLR3_CONFIGURATION_NAME = "antlr3"
public static final def ANTLR4_CONFIGURATION_NAME = "antlr4"
diff --git a/buildSrc/src/main/groovy/org/jabref/build/xjc/XjcTask.groovy b/buildSrc/src/main/groovy/org/jabref/build/xjc/XjcTask.groovy
index bb859ea10c1..2b6fd9b7bbd 100644
--- a/buildSrc/src/main/groovy/org/jabref/build/xjc/XjcTask.groovy
+++ b/buildSrc/src/main/groovy/org/jabref/build/xjc/XjcTask.groovy
@@ -12,13 +12,14 @@ class XjcTask extends DefaultTask {
private def bindingFile
private def outputDirectory
private String javaPackage
+ private String encoding
@Optional
private String arguments
@TaskAction
def generateClasses() {
project.mkdir(outputDirectory)
- project.ant.xjc(destdir: outputDirectory, package: javaPackage) {
+ project.ant.xjc(destdir: outputDirectory, package: javaPackage, encoding: getEncoding()) {
schema(dir: schemaFile.getParent(), includes: schemaFile.getName())
if (bindingFile != null) {
binding(dir: bindingFile.getParent(), includes: bindingFile.getName())
@@ -62,6 +63,19 @@ class XjcTask extends DefaultTask {
updateOutput()
}
+ String getEncoding() {
+ if(encoding == null ) {
+ // use UTF-8 as default encoding
+ return "UTF-8"
+ } else {
+ return encoding
+ }
+ }
+
+ void setEncoding(String encoding) {
+ this.encoding = encoding
+ }
+
String getArguments() {
return arguments
}
diff --git a/buildres/gui-tests.sh b/buildres/gui-tests.sh
index 846879a7e93..137818a1bb3 100755
--- a/buildres/gui-tests.sh
+++ b/buildres/gui-tests.sh
@@ -13,5 +13,4 @@ sudo service rabbitmq-server stop
sudo service resolvconf stop
sudo service sshguard stop
sudo service ssh stop
-# Integration tests run in a timeout. Just start them and kill them after 60s.
-timeout 60 ./gradlew guiTest -Dscan --info || true
+timeout 60 ./gradlew guiTest -Dscan --info
diff --git a/buildres/linux/JabRef.png b/buildres/linux/JabRef.png
new file mode 100644
index 00000000000..e807e0448d3
Binary files /dev/null and b/buildres/linux/JabRef.png differ
diff --git a/buildres/linux/copyright b/buildres/linux/copyright
new file mode 100644
index 00000000000..19375cdd331
--- /dev/null
+++ b/buildres/linux/copyright
@@ -0,0 +1,5 @@
+Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
+
+Files: *
+Copyright: Copyright (C) 2003-2019 JabRef Authors
+License: MIT
diff --git a/buildres/linux/jabref-JabRef.desktop b/buildres/linux/jabref-JabRef.desktop
new file mode 100644
index 00000000000..38d696ec584
--- /dev/null
+++ b/buildres/linux/jabref-JabRef.desktop
@@ -0,0 +1,12 @@
+[Desktop Entry]
+Name=APPLICATION_NAME
+GenericName=BibTeX Editor
+Comment=APPLICATION_DESCRIPTION
+Exec=APPLICATION_LAUNCHER
+Icon=APPLICATION_ICON
+Terminal=false
+Type=Application
+MimeType=text/x-bibtex;
+Categories=DEPLOY_BUNDLE_CATEGORY
+Keywords=bibtex;biblatex;latex;bibliography
+StartupWMClass=org-jabref-JabRefMain
\ No newline at end of file
diff --git a/buildres/linux/jabrefHost.py b/buildres/linux/jabrefHost.py
new file mode 100755
index 00000000000..6b01fc4b0a6
--- /dev/null
+++ b/buildres/linux/jabrefHost.py
@@ -0,0 +1,89 @@
+#!/usr/bin/python3 -u
+
+# Note that running python with the `-u` flag is required on Windows,
+# in order to ensure that stdin and stdout are opened in binary, rather
+# than text, mode.
+
+import json
+import sys
+import struct
+import subprocess
+import shlex
+import logging
+import shutil
+from pathlib import Path
+
+# We assume that this python script is located in "jabref/lib" while the executable is "jabref/bin/JabRef"
+script_dir = Path(__file__).resolve().parent.parent
+JABREF_PATH = script_dir / "bin/JabRef"
+if not JABREF_PATH.exists():
+ JABREF_PATH = shutil.which("jabref")
+
+logging_dir = Path.home() / ".mozilla/native-messaging-hosts/"
+if not logging_dir.exists():
+ logging_dir.mkdir(parents=True)
+logging.basicConfig(filename=logging_dir / 'jabref_browser_extension.log')
+
+# Read a message from stdin and decode it.
+def get_message():
+ raw_length = sys.stdin.buffer.read(4)
+ if not raw_length:
+ logging.error("Raw_length \n")
+ sys.exit(0)
+ message_length = struct.unpack('=I', raw_length)[0]
+ logging.info("Got length: {} bytes to be read\n".format(message_length))
+ message = sys.stdin.buffer.read(message_length).decode("utf-8")
+ logging.info("Got message of {} chars\n".format(len(message)))
+ data = json.loads(message)
+ logging.info("Successfully retrieved JSON\n")
+ return data
+
+
+# Encode a message for transmission, given its content.
+def encode_message(message_content):
+ encoded_content = json.dumps(message_content).encode("utf-8")
+ encoded_length = struct.pack('=I', len(encoded_content))
+ return {'length': encoded_length, 'content': struct.pack(str(len(encoded_content))+"s",encoded_content)}
+
+
+# Send an encoded message to stdout.
+def send_message(message):
+ encoded_message = encode_message(message)
+ sys.stdout.buffer.write(encoded_message['length'])
+ sys.stdout.buffer.write(encoded_message['content'])
+ sys.stdout.buffer.flush()
+
+def add_jabref_entry(data):
+ cmd = str(JABREF_PATH) + " -importBibtex " + "\"" + data + "\""
+ try:
+ response = subprocess.check_output(shlex.split(cmd), stderr=subprocess.STDOUT)
+ except subprocess.CalledProcessError as exc:
+ logging.error("Failed to call JabRef: %s %s", exc.returncode, exc.output)
+ else:
+ logging.info(f'Called JabRef and got: {response}')
+ return response
+
+
+logging.info("Starting JabRef backend")
+
+try:
+ message = get_message()
+except Exception as e:
+ message = str(e)
+logging.info(str(message))
+
+if 'status' in message and message["status"] == "validate":
+ cmd = str(JABREF_PATH) + " -version"
+ try:
+ response = subprocess.check_output(shlex.split(cmd), stderr=subprocess.STDOUT)
+ except subprocess.CalledProcessError as exc:
+ logging.error("Failed to call JabRef: %s %s", exc.returncode, exc.output)
+ send_message({"message": "jarNotFound", "path": JABREF_PATH})
+ else:
+ logging.info(f'{response}')
+ send_message({"message": "jarFound"})
+else:
+ entry = message["text"]
+ output = add_jabref_entry(entry)
+ send_message({"message": "ok", "output": str(output)})
+
diff --git a/buildres/linux/org.jabref.jabref.json b/buildres/linux/org.jabref.jabref.json
new file mode 100644
index 00000000000..091f38e4281
--- /dev/null
+++ b/buildres/linux/org.jabref.jabref.json
@@ -0,0 +1,9 @@
+{
+ "name": "org.jabref.jabref",
+ "description": "JabRef",
+ "path": "/opt/jabref/lib/jabrefHost.py",
+ "type": "stdio",
+ "allowed_extensions": [
+ "@jabfox"
+ ]
+}
diff --git a/buildres/linux/postinst b/buildres/linux/postinst
new file mode 100644
index 00000000000..40791c02384
--- /dev/null
+++ b/buildres/linux/postinst
@@ -0,0 +1,36 @@
+#!/bin/sh
+# postinst script for my jabref
+#
+# see: dh_installdeb(1)
+
+set -e
+
+# summary of how this script can be called:
+# * `configure'
+# * `abort-upgrade'
+# * `abort-remove' `in-favour'
+#
+# * `abort-remove'
+# * `abort-deconfigure' `in-favour'
+# `removing'
+#
+# for details, see http://www.debian.org/doc/debian-policy/ or
+# the debian-policy package
+
+case "$1" in
+ configure)
+ INSTALL_PATH="/usr/lib/mozilla/native-messaging-hosts/"
+ install -D -m0755 /opt/jabref/lib/org.jabref.jabref.json $INSTALL_PATH
+ DESKTOP_COMMANDS_INSTALL
+ ;;
+
+ abort-upgrade|abort-remove|abort-deconfigure)
+ ;;
+
+ *)
+ echo "postinst called with unknown argument \`$1'" >&2
+ exit 1
+ ;;
+esac
+
+exit 0
diff --git a/buildres/linux/postrm b/buildres/linux/postrm
new file mode 100644
index 00000000000..7cb08bc3746
--- /dev/null
+++ b/buildres/linux/postrm
@@ -0,0 +1,33 @@
+#!/bin/sh
+# postrm script for APPLICATION_PACKAGE
+#
+# see: dh_installdeb(1)
+set -e
+
+# summary of how this script can be called:
+# * `remove'
+# * `purge'
+# * `upgrade'
+# * `failed-upgrade'
+# * `abort-install'
+# * `abort-install'
+# * `abort-upgrade'
+# * `disappear'
+#
+# for details, see http://www.debian.org/doc/debian-policy/ or
+# the debian-policy package
+case "$1" in
+ purge|remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)
+ INSTALL_PATH="/usr/lib/mozilla/native-messaging-hosts"
+ if grep --quiet '"path": "/opt' $INSTALL_PATH/org.jabref.jabref.json; then
+ rm $INSTALL_PATH/org.jabref.jabref.json
+ fi
+ ;;
+
+ *)
+ echo "postrm called with unknown argument \`$1'" >&2
+ exit 1
+ ;;
+esac
+
+exit 0
\ No newline at end of file
diff --git a/buildres/windows/JabRef-post-image.wsf b/buildres/windows/JabRef-post-image.wsf
new file mode 100644
index 00000000000..aa1b3e90030
--- /dev/null
+++ b/buildres/windows/JabRef-post-image.wsf
@@ -0,0 +1,35 @@
+
+
+
+
+
+
diff --git a/buildres/JabRef.bat b/buildres/windows/JabRefHost.bat
similarity index 60%
rename from buildres/JabRef.bat
rename to buildres/windows/JabRefHost.bat
index 7a2cee363b0..d44d94f282c 100644
--- a/buildres/JabRef.bat
+++ b/buildres/windows/JabRefHost.bat
@@ -1,3 +1,3 @@
@echo off
pushd %~dp0
-@powershell.exe -ExecutionPolicy Bypass -NoLogo -NonInteractive -NoProfile -WindowStyle Hidden -File ".\JabRef.ps1"
+@powershell.exe -ExecutionPolicy Bypass -NoLogo -NonInteractive -NoProfile -WindowStyle Hidden -File ".\JabRefHost.ps1"
diff --git a/buildres/JabRef.ps1 b/buildres/windows/JabRefHost.ps1
similarity index 72%
rename from buildres/JabRef.ps1
rename to buildres/windows/JabRefHost.ps1
index fe814e6013e..f31df86f74e 100644
--- a/buildres/JabRef.ps1
+++ b/buildres/windows/JabRefHost.ps1
@@ -11,8 +11,7 @@ function Respond($response) {
}
}
-$jabRefJarFileName = "@jabRefJarFileName@"
-$jabRefJar = [System.IO.Path]::Combine($PSScriptRoot, $jabRefJarFileName)
+$jabRefExe = [System.IO.Path]::Combine($PSScriptRoot, "JabRef.exe")
try {
$reader = New-Object System.IO.BinaryReader([System.Console]::OpenStandardInput())
@@ -21,16 +20,16 @@ try {
$message = $messageRaw | ConvertFrom-Json
if ($message.Status -eq "validate") {
- if (-not (Test-Path $jabRefJar)) {
- return Respond @{message="jarNotFound";path=$jabRefJar}
+ if (-not (Test-Path $jabRefExe)) {
+ return Respond @{message="jarNotFound";path=$jabRefExe}
} else {
return Respond @{message="jarFound"}
}
}
-
- if (-not (Test-Path $jabRefJar)) {
+
+ if (-not (Test-Path $jabRefExe)) {
$wshell = New-Object -ComObject Wscript.Shell
- $popup = "Unable to locate '$jabRefJarFileName' in '$([System.IO.Path]::GetDirectoryName($jabRefJar))'."
+ $popup = "Unable to locate '$jabRefExe'."
$wshell.Popup($popup,0,"JabRef", 0x0 + 0x30)
return
}
@@ -39,7 +38,7 @@ try {
#$wshell.Popup($message.Text,0,"JabRef", 0x0 + 0x30)
$messageText = $message.Text
- $output = & java -jar $jabRefJar -importBibtex "$messageText" 2>&1
+ $output = & $jabRefExe -importBibtex "$messageText" 2>&1
#$output = & echoargs -importBibtex $messageText 2>&1
#$wshell.Popup($output,0,"JabRef", 0x0 + 0x30)
return Respond @{message="ok";output="$output"}
diff --git a/buildres/windows/JabRefTopBanner.bmp b/buildres/windows/JabRefTopBanner.bmp
new file mode 100644
index 00000000000..7e95c3b8fd7
Binary files /dev/null and b/buildres/windows/JabRefTopBanner.bmp differ
diff --git a/buildres/jabref.json b/buildres/windows/jabref.json
similarity index 81%
rename from buildres/jabref.json
rename to buildres/windows/jabref.json
index dee50f008fe..9024c5b17f4 100644
--- a/buildres/jabref.json
+++ b/buildres/windows/jabref.json
@@ -1,7 +1,7 @@
{
"name": "org.jabref.jabref",
"description": "JabRef",
- "path": "JabRef.bat",
+ "path": "JabRefHost.bat",
"type": "stdio",
"allowed_extensions": [
"@jabfox"
diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml
index d0d037d677b..046936eb127 100644
--- a/config/checkstyle/checkstyle.xml
+++ b/config/checkstyle/checkstyle.xml
@@ -1,23 +1,37 @@
+ "http://www.checkstyle.org/dtds/configuration_1_3.dtd">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
@@ -26,17 +40,18 @@
+
+
+
+
-
-
-
-
+
+
-
-
-
-
-
+
+
diff --git a/config/checkstyle/suppressions.xml b/config/checkstyle/suppressions.xml
index 71943814250..65babae5cb6 100644
--- a/config/checkstyle/suppressions.xml
+++ b/config/checkstyle/suppressions.xml
@@ -2,7 +2,7 @@
+ "http://www.checkstyle.org/dtds/suppressions_1_1.dtd">
diff --git a/docs/adr/0006-only-translated-strings-in-language-file.md b/docs/adr/0006-only-translated-strings-in-language-file.md
new file mode 100644
index 00000000000..9c888cae803
--- /dev/null
+++ b/docs/adr/0006-only-translated-strings-in-language-file.md
@@ -0,0 +1,47 @@
+# Only translated strings in language file
+
+## Context and Problem Statement
+
+JabRef has translation files `JabRef_it.properties`, ...
+There are translated and unstranslated strings.
+Which ones should be in the translation file?
+
+## Decision Drivers
+
+* Translators should find new strings to translate easily
+* New strings to translate should be written into `JabRef_en.properties` to enable translation by the translators
+* Crowdin should be kept as translation platform, because 1) it is much easier for the translators than the GitHub workflow and 2) it is free for OSS projects.
+
+## Considered Options
+
+* Only translated strings in language file
+* Translated and untranslated strings in language file, have value the untranslated string to indicate untranslated
+* Translated and untranslated strings in language file, have empty to indicate untranslated
+
+## Decision Outcome
+
+Chosen option: "Only translated strings in language file", because comes out best (see below.
+
+## Pros and Cons of the Options
+
+### Only translated strings in language file
+
+* Good, because Crowdin supports it
+* Bad, because translators need tooling to see untranslated strings
+* Bad, because issues with FXML (https://github.com/JabRef/jabref/issues/3796)
+
+### Translated and untranslated strings in language file, have value the untranslated string to indicate untranslated
+
+* Good, because no issues with FXML
+* Good, because Crowin supports it
+* Bad, because untranslated strings cannot be identified easily in latin languages
+
+### Translated and untranslated strings in language file, have empty to indicate untranslated
+
+* Good, because untranslated strings can be identified easily
+* Good, because works with FMXL (?)
+* Bad, because Crowdin does not support it
+
+## Links
+
+* Related to [ADR-0001](0001-use-crowdin-for-translations.md).
diff --git a/docs/adr/index.md b/docs/adr/README.md
similarity index 89%
rename from docs/adr/index.md
rename to docs/adr/README.md
index de1818aea3c..37fa76720a7 100644
--- a/docs/adr/index.md
+++ b/docs/adr/README.md
@@ -1,3 +1,4 @@
+
# Architectural Decision Log
This log lists the architectural decisions for JabRef.
@@ -10,6 +11,7 @@ This log lists the architectural decisions for JabRef.
- [ADR-0003](0003-use-gradle-as-build-tool.md) - Use Gradle as build tool
- [ADR-0004](0004-use-mariadb-connector.md) - Use MariaDB Connector
- [ADR-0005](0005-fully-support-utf8-only-for-latex-files.md) - Fully Support UTF-8 Only For LaTeX Files
+- [ADR-0006](0006-only-translated-strings-in-language-file.md) - Only translated strings in language file
diff --git a/eclipse.gradle b/eclipse.gradle
index 8efd725e98e..60e1a845380 100644
--- a/eclipse.gradle
+++ b/eclipse.gradle
@@ -1,4 +1,3 @@
-import org.gradle.plugins.ide.eclipse.model.AccessRule
apply plugin: "eclipse"
// ensure that source code is generated, otherwise class `BstLexer` cannot be found
@@ -11,15 +10,51 @@ eclipseJdt.doLast {
f.append('encoding/=UTF-8')
}
eclipse {
- classpath {
+ classpath {
file {
whenMerged {
- def jre = entries.find { it.path.contains 'org.eclipse.jdt.launching.JRE_CONTAINER' }
- jre.accessRules.add(new AccessRule('accessible', '**/javafx/**'))
+ entries.findAll { isModule(it) }.each { //this was already necessary to build modular projects
+ it.entryAttributes['module'] = 'true'
+ }
+ def controlsfx = entries.find { isControlsfx(it) };
+ controlsfx.entryAttributes['add-exports'] = 'org.controlsfx.controls/impl.org.controlsfx.skin=org.jabref:org.controlsfx.controls/org.controlsfx.control.textfield=org.jabref:org.controlsfx.controls/impl.org.controlsfx.autocompletion=org.jabref';
+ controlsfx.entryAttributes['add-opens'] = 'org.controlsfx.controls/impl.org.controlsfx.skin=org.jabref:org.controlsfx.controls/org.controlsfx.control.textfield=org.jabref:org.controlsfx.controls/impl.org.controlsfx.autocompletion=org.jabref';
+
+ entries.findAll { isSource(it) && isTestScope(it) }.each { //mark test source folders
+ it.entryAttributes['test'] = 'true'
+ }
+
+ def javafxcontrols = entries.find { isJavafxControls(it) };
+ javafxcontrols.entryAttributes['add-exports'] = 'javafx.controls/com.sun.javafx.scene.control=org.jabref:javafx.controls/com.sun.javafx.scene.control.behavior=org.jabref:javafx.controls/javafx.scene.control=org.jabref';
+ javafxcontrols.entryAttributes['add-opens'] = 'javafx.controls/com.sun.javafx.scene.control=org.jabref:javafx.controls/com.sun.javafx.scene.control.behavior=org.jabref:javafx.controls/javafx.scene.control=org.jabref';
+
+
+ entries.findAll { isLibrary(it) && isTestScope(it) }.each { //mark test source files
+ it.entryAttributes['test'] = 'true'
+ }
}
}
+
+ defaultOutputDir = file('build')
+ downloadSources = true
+ downloadJavadoc = true
}
}
+
+boolean isLibrary(entry) { return entry.properties.kind.equals('lib') }
+
+boolean isTestScope(entry) { return !entry.entryAttributes.get('gradle_used_by_scope').contains('main') }
+
+boolean isModule(entry) {
+ isLibrary(entry) && !isTestScope(entry);
+} //a test-scope library should be put on the classpath instead of the modulepath
+boolean isSource(entry) { return entry.properties.kind.equals('src'); }
+
+boolean isControlsfx(entry) { return entry.properties.path.contains('controlsfx'); }
+
+boolean isJavafxControls(entry) { return entry.properties.path.contains('javafx-controls'); }
+
+
// add formatter and cleanup settings to Eclipse settings
// see http://stackoverflow.com/a/27461890/873282
@@ -441,7 +476,7 @@ tasks.eclipse.doFirst {
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
org.eclipse.jdt.core.javaFormatter=org.eclipse.jdt.core.defaultJavaFormatter
- '''. stripIndent())
+ '''.stripIndent())
File jdt_ui_prefs = file("${project.projectDir}/.settings/org.eclipse.jdt.ui.prefs")
if (jdt_ui_prefs.exists()) {
@@ -573,6 +608,6 @@ tasks.eclipse.doFirst {
sp_cleanup.use_this_for_non_static_method_access=false
sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=false
sp_cleanup.use_type_arguments=false
- '''. stripIndent())
+ '''.stripIndent())
}
}
diff --git a/external-libraries.txt b/external-libraries.txt
index 107ae83f6e4..385d4d18217 100644
--- a/external-libraries.txt
+++ b/external-libraries.txt
@@ -1,7 +1,7 @@
This document lists the fonts, icons, and libraries used by JabRef.
This file is manually kept in sync with build.gradle and the binary jars contained in the lib/ directory.
-One can list all depdencies by using Gradle task `depdencyReport`.
+One can list all dependencies by using Gradle task `dependencyReport`.
It generated the file [build/reports/project/dependencies.txt](build/reports/project/dependencies.txt).
# Legend
@@ -16,7 +16,7 @@ Note that the SPDX license identifiers are different from the ones used by debia
# Fonts and Icons
-The loading animation during loading of recommendations from Mr. DLib is created by and is free of use under licence CC0 1.0.
+The loading animation during loading of recommendations from Mr. DLib is created by and is free of use under license CC0 1.0.
Id: material-design-icons.font
Project: Material Design Icons
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
index 87b738cbd05..5c2d1cf016b 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 ea13fdfd192..7c4388a9216 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-5.3.1-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/gradlew b/gradlew
index cccdd3d517f..83f2acfdc31 100755
--- a/gradlew
+++ b/gradlew
@@ -1,5 +1,21 @@
#!/usr/bin/env sh
+#
+# Copyright 2015 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
##############################################################################
##
## Gradle start up script for UN*X
@@ -28,7 +44,7 @@ APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-DEFAULT_JVM_OPTS=""
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
@@ -109,8 +125,8 @@ if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
-# For Cygwin, switch paths to Windows format before running java
-if $cygwin ; then
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
diff --git a/gradlew.bat b/gradlew.bat
index f9553162f12..9618d8d9607 100644
--- a/gradlew.bat
+++ b/gradlew.bat
@@ -1,3 +1,19 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@@ -14,7 +30,7 @@ set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-set DEFAULT_JVM_OPTS=
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
diff --git a/jabref.install4j b/jabref.install4j
index 53b83c0d3bc..9dac4968342 100644
--- a/jabref.install4j
+++ b/jabref.install4j
@@ -1,33 +1,32 @@
-
+
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -37,155 +36,157 @@
-
-
-
+
+
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
-
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
-
-
+
+
+
-
-
-
-
-
-
-
+
-
-
-
-
-
-
+
+ 32
+
+
-XX:+UseG1GC
-XX:+UseStringDeduplication
-XX:StringTableSize=1000003
-
-
-
-
- <key>NSSupportsAutomaticGraphicsSwitching</key>
-<true/>
-
-
+ <key>NSSupportsAutomaticGraphicsSwitching</key>
+<true/>
-
-
-
-
-
-
-
+
-
+
-
-
-
- true
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
+
-
+
-
-
-
-
-
- sys.installationDir
-
-
-
-
-
+
+ sys.installationDir
+
context.getBooleanVariable("sys.confirmedUpdateInstallation")
-
+
-
-
-
-
-
- if (Util.hasFullAdminRights() || Util.isAdminGroup()) {
+
+
+ if (Util.hasFullAdminRights() || Util.isAdminGroup()) {
context.setInstallationDirectory(context.getInstallationDirectory());
} else {
if (Util.isAtLeastWindowsVista()) {
@@ -194,138 +195,65 @@
context.setInstallationDirectory(new File(System.getProperty("user.home"), "JabRef"));
}
}
-return true;
-
-
-
+return true;
-
+
-
-
+
-
-
-
- ${form:welcomeMessage}
-
-
-
+ ${form:welcomeMessage}
-
!context.isConsole()
-
-
+
-
-
-
-
-
- String message = context.getMessage("ConsoleWelcomeLabel", context.getApplicationName());
+
+
+ String message = context.getMessage("ConsoleWelcomeLabel", context.getApplicationName());
return console.askOkCancel(message, true);
-
-
-
-
+
-
+
-
-
-
-
-
-
-
-
-
-
-
+
updateCheck
-
+
-
-
-
- ${i18n:ClickNext}
-
-
-
+ ${i18n:ClickNext}
-
-
-
-
-
-
-
-
-
-
+
!context.getBooleanVariable("sys.confirmedUpdateInstallation")
-
-
-
-
+
-
-
-
-
-
- sys.installationDir
-
-
-
-
-
+
+ sys.installationDir
+
context.getVariable("sys.responseFile") == null
-
+
-
-
-
- ${i18n:SelectDirLabel(${compiler:sys.fullName})}
-
-
-
+ ${i18n:SelectDirLabel(${compiler:sys.fullName})}
-
-
-
-
+
-
-
-
- true
-
-
- false
-
-
-
+
+
-
-
suggestAppDir
validateApplicationId
@@ -342,674 +270,257 @@ return console.askOkCancel(message, true);
-
+
-
-
-
- ${compiler:sys.fullName}
-
-
-
+ ${compiler:sys.fullName}
-
!context.getBooleanVariable("sys.confirmedUpdateInstallation")
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
+
-
-
-
- ${form:confirmationMessage}
-
-
-
+ ${form:confirmationMessage}
-
!context.isConsole()
-
-
+
-
-
-
- ${i18n:CreateDesktopIcon}
-
-
- true
-
-
- createDesktopLinkAction
-
-
-
+ ${i18n:CreateDesktopIcon}
+
+ createDesktopLinkAction
-
-
-
-
+
-
-
-
- ${i18n:AddToDock}
-
-
- true
-
-
- addToDockAction
-
-
-
+ ${i18n:AddToDock}
+
+ addToDockAction
-
Util.isMacOS()
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
+
+
+
-
-
-
-
-
-
-
-
-
-
-
- true
-
-
- ${i18n:UninstallerMenuEntry(${compiler:sys.fullName})}
-
-
-
+
+ ${i18n:UninstallerMenuEntry(${compiler:sys.fullName})}
!context.getBooleanVariable("sys.programGroupDisabled")
-
+
-
-
-
- ${compiler:sys.fullName} ${compiler:sys.version}
-
-
-
+ ${compiler:sys.fullName} ${compiler:sys.version}
-
-
+
-
-
-
-
- JabRef
-
-
-
- ${compiler:sys.fullName}
-
+
+
+ JabRef
-
+
+ ${compiler:sys.fullName}
context.getBooleanVariable("createDesktopLinkAction")
-
+
-
-
-
-
- JabRef
-
-
+
+
+ JabRef
-
+
context.getBooleanVariable("addToDockAction")
-
-
-
-
-
-
+
-
+
-
-
-
- SOFTWARE\JabRef
-
-
-
- com.install4j.api.windows.RegistryRoot
- HKEY_LOCAL_MACHINE
-
-
-
- ${installer:sys.installationDir}
-
-
- Path
-
-
-
+ SOFTWARE\JabRef
+
+ ${installer:sys.installationDir}
+ Path
Util.hasFullAdminRights() || Util.isAdminGroup()
-
+
-
-
-
- SOFTWARE\JabRef
-
-
-
- com.install4j.api.windows.RegistryRoot
- HKEY_CURRENT_USER
-
-
-
- ${installer:sys.installationDir}
-
-
- Path
-
-
-
+ SOFTWARE\JabRef
+
+ ${installer:sys.installationDir}
+ Path
!(Util.hasFullAdminRights() || Util.isAdminGroup())
-
+
-
-
-
- SOFTWARE\Mozilla\NativeMessagingHosts\org.jabref.jabref
-
-
-
- com.install4j.api.windows.RegistryRoot
- HKEY_LOCAL_MACHINE
-
-
-
- ${installer:sys.installationDir}\jabref.json
-
-
-
+ SOFTWARE\Mozilla\NativeMessagingHosts\org.jabref.jabref
+
+ ${installer:sys.installationDir}\jabref.json
-
-
+
-
-
-
- ${i18n:WizardPreparing}
-
-
-
+ ${i18n:WizardPreparing}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
+
-
-
-
- BibTeX
-
-
- bib
-
-
- 23
-
-
-
- ./src/main/resources/icons/jabref.icns
-
-
-
-
- com.install4j.runtime.beans.actions.desktop.MacAssociationRole
- EDITOR
-
-
-
-
-
-
-
- ./src/main/resources/icons/jabref.ico
-
-
+ BibTeX
+ bib
+ 23
+
+
+ ./src/main/resources/icons/jabref.icns
+
+
+
+
+
+
+ ./src/main/resources/icons/jabref.ico
-
+
-
-
+
-
-
-
- ${i18n:SelectAssociationsLabel}
-
-
-
+ ${i18n:SelectAssociationsLabel}
-
-
-
-
+
-
-
-
- true
-
-
-
+
-
-
showSelectionButtons
-
-
-
-
-
-
-
-
-
-
-
+
-
+
-
-
-
- 23
-
-
-
+ 23
context.getBooleanVariable("executeLauncherAction") && (!context.isUnattended())
-
+
-
-
-
- ${form:finishedMessage}
-
-
-
+ ${form:finishedMessage}
-
-
-
-
+
-
-
-
- ${i18n:RunEntryExec("${compiler:sys.fullName}")}
-
-
- true
-
-
- executeLauncherAction
-
-
-
+ ${i18n:RunEntryExec("${compiler:sys.fullName}")}
+
+ executeLauncherAction
-
-
-
-
+
-
-
-
- ${i18n:UninstallerMenuEntry(${compiler:sys.fullName})}
-
-
- true
-
-
-
+ ${i18n:UninstallerMenuEntry(${compiler:sys.fullName})}
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
+
-
-
-
- ${form:welcomeMessage}
-
-
-
+ ${form:welcomeMessage}
-
!context.isConsole()
-
-
+
-
-
-
-
-
- String message = context.getMessage("ConfirmUninstall", context.getApplicationName());
+
+
+ String message = context.getMessage("ConfirmUninstall", context.getApplicationName());
return console.askYesNo(message, true);
-
-
-
-
+
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
+
-
-
-
- SOFTWARE\JabRef
-
-
- false
-
-
-
- com.install4j.api.windows.RegistryRoot
- HKEY_LOCAL_MACHINE
-
-
-
-
+ SOFTWARE\JabRef
+
+
Util.hasFullAdminRights() || Util.isAdminGroup()
-
+
-
-
-
- SOFTWARE\JabRef
-
-
- false
-
-
-
- com.install4j.api.windows.RegistryRoot
- HKEY_CURRENT_USER
-
-
-
-
+ SOFTWARE\JabRef
+
+
!(Util.hasFullAdminRights() || Util.isAdminGroup())
-
+
-
-
-
- SOFTWARE\Mozilla\NativeMessagingHosts\org.jabref.jabref
-
-
- false
-
-
-
- com.install4j.api.windows.RegistryRoot
- HKEY_LOCAL_MACHINE
-
-
-
-
+ SOFTWARE\Mozilla\NativeMessagingHosts\org.jabref.jabref
+
+
-
-
+
-
-
-
- ${i18n:UninstallerPreparing}
-
-
-
+ ${i18n:UninstallerPreparing}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
+
-
-
-
- ${form:successMessage}
-
-
-
+ ${form:successMessage}
-
-
-
@@ -1017,180 +528,81 @@ return console.askYesNo(message, true);
-
-
-
-
-
-
-
-
+
-
-
@@ -1464,46 +736,14 @@ return console.askYesNo(message, true);
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
-
-
diff --git a/lib/fastparse-1.0.0.jar b/lib/fastparse-1.0.0.jar
new file mode 100644
index 00000000000..7c38dac21c6
Binary files /dev/null and b/lib/fastparse-1.0.0.jar differ
diff --git a/lib/fastparse-utils-1.0.0.jar b/lib/fastparse-utils-1.0.0.jar
new file mode 100644
index 00000000000..58d520f52fb
Binary files /dev/null and b/lib/fastparse-utils-1.0.0.jar differ
diff --git a/lib/libreoffice.jar b/lib/libreoffice.jar
new file mode 100644
index 00000000000..e8b40bece7d
Binary files /dev/null and b/lib/libreoffice.jar differ
diff --git a/lib/sourcecode-0.1.4.jar b/lib/sourcecode-0.1.4.jar
new file mode 100644
index 00000000000..2aa74d89a0d
Binary files /dev/null and b/lib/sourcecode-0.1.4.jar differ
diff --git a/scripts/download-install4j-and-jres.sh b/scripts/download-install4j-and-jres.sh
deleted file mode 100755
index 4368ef31012..00000000000
--- a/scripts/download-install4j-and-jres.sh
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-
-# fetch intall4j binary
-if [ ! -d ~/downloads ]; then
- mkdir ~/downloads
-fi
-cd ~/downloads
-wget --quiet -nc --show-progress http://download-keycdn.ej-technologies.com/install4j/install4j_unix_7_0_11.tar.gz
-
-# fetch JREs
-if [ ! -d ~/.install4j7/jres ]; then
- mkdir -p ~/.install4j7/jres
-fi
-cd ~/.install4j7/jres
-wget --quiet -nc https://files.jabref.org/jres/windows-x86-1.8.0_172.tar.gz
-wget --quiet -nc https://files.jabref.org/jres/windows-amd64-1.8.0_172.tar.gz
-wget --quiet -nc https://files.jabref.org/jres/macosx-amd64-1.8.0_172_unpacked.tar.gz
diff --git a/scripts/extract-install4j.sh b/scripts/extract-install4j.sh
deleted file mode 100755
index 74b703b2981..00000000000
--- a/scripts/extract-install4j.sh
+++ /dev/null
@@ -1,4 +0,0 @@
-#!/bin/bash
-tar -xf ~/downloads/install4j_unix_7_0_11.tar.gz
-# fix directory name (until install4j 6.1.5 it was install4j6
-mv install4j7.0.11 install4j7
diff --git a/scripts/prepare-install4j.sh b/scripts/prepare-install4j.sh
new file mode 100755
index 00000000000..c0c533c6c9e
--- /dev/null
+++ b/scripts/prepare-install4j.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+wget --quiet -nc --show-progress https://download-gcdn.ej-technologies.com/install4j/install4j_unix_8_0_1.tar.gz
+tar -xf install4j_unix_8_0_1.tar.gz
+mv install4j8.0.1 install4j8
diff --git a/scripts/upload-to-builds.jabref.org.sh b/scripts/upload-to-builds.jabref.org.sh
deleted file mode 100755
index 5e49c2c577c..00000000000
--- a/scripts/upload-to-builds.jabref.org.sh
+++ /dev/null
@@ -1,51 +0,0 @@
-#!/bin/bash
-
-# We assume that there is a single build in build/releases
-# We take out the branch name from the first matching file and then upload everything
-
-# just to be sure
-branch="snapshot"
-
-# simple solution to treat first file matching a pattern
-# hint by http://unix.stackexchange.com/a/156207/18033
-for buildfile in build/releases/*--snapshot--*; do
- # the last "--" part is the branch name
- branch=`echo $buildfile | sed "sX.*--\(.*\)--.*X\1X"`
- break;
-done
-
-for buildfile in build/releases/*--snapshot--*.jar; do
- # remove build/releases/ from the filename
- jarname=`echo $buildfile | sed "sXbuild/releases/XX"`
- break;
-done
-
-# now the branch name is in the variable "branch"
-
-command="cd www/\n"
-
-# if there was a branch determined, create that directory
-# the for returns the literal string "build/releases/*--snapshot--*" if no file was found
-# then, "snapshot" is extracted
-if [ "snapshot" != "$branch" ] ; then
- # change into dir and delete old snapshots
- command="${command}mkdir $branch\ncd $branch\nrm *.dmg\nrm *.jar\nrm *.exe\n"
-fi
-
-# only upload JabRef*, not md5sums, updates.xml, etc.
-command="${command}mput build/releases/JabRef*\n"
-
-# create symlink ...--latest.jar to latest version
-command="${command}symlink ${jarname} /www/${branch}/JabRef--${branch}--latest.jar\n"
-
-command="${command}exit\n"
-
-# now $command is complete
-
-# add host key of build-upload.jabref.org to SSH known hosts
-cat <> ~/.ssh/known_hosts
-|1|/E0gFRKMKG83OQVcwqFPIy3mnE4=|tLYRVZQ/3nCkBTZ9NtBVxx3si+Y= ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBNjYLP9C+PhQrpKfYsdgr8dDB/50S3BnaXAYQOVC5o3H0SqKisWw8iTkij/u8H20Rmsf/ABduOLPOBubfPFlE34=
-|1|dEeue80RCldo/x5XyhbGIkS72d8=|09t8muprLf6YoXsc3r3kxicBykI= ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBNjYLP9C+PhQrpKfYsdgr8dDB/50S3BnaXAYQOVC5o3H0SqKisWw8iTkij/u8H20Rmsf/ABduOLPOBubfPFlE34=
-EOF
-
-echo -e "$command" | sftp -P 9922 builds_jabref_org@build-upload.jabref.org
diff --git a/snap/hooks/connect-plug-browser-extension b/snap/hooks/connect-plug-browser-extension
new file mode 100755
index 00000000000..df5cde565f3
--- /dev/null
+++ b/snap/hooks/connect-plug-browser-extension
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+if [ ! -d /var/lib/snapd/hostfs/usr/lib/mozilla/native-messaging-hosts ]; then
+ echo "Missing directory, create it manually then try again:"
+ echo "sudo mkdir -p /usr/lib/mozilla/native-messaging-hosts"
+ exit 1
+fi
+
+cp $SNAP/lib/org.jabref.jabref.json /var/lib/snapd/hostfs/usr/lib/mozilla/native-messaging-hosts/org.jabref.jabref.json
\ No newline at end of file
diff --git a/snap/hooks/disconnect-plug-browser-extension b/snap/hooks/disconnect-plug-browser-extension
new file mode 100755
index 00000000000..5af57cf27f9
--- /dev/null
+++ b/snap/hooks/disconnect-plug-browser-extension
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+if [ ! -f /var/lib/snapd/hostfs/usr/lib/mozilla/native-messaging-hosts/org.jabref.jabref.json ]; then
+ exit 0
+elif grep --quiet '"path": "/snap' /var/lib/snapd/hostfs/usr/lib/mozilla/native-messaging-hosts/org.jabref.jabref.json; then
+ rm /var/lib/snapd/hostfs/usr/lib/mozilla/native-messaging-hosts/org.jabref.jabref.json
+fi
\ No newline at end of file
diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml
index b26f84fef0d..889418ed128 100644
--- a/snap/snapcraft.yaml
+++ b/snap/snapcraft.yaml
@@ -1,41 +1,52 @@
name: jabref
-version: "git"
-version-script: cat build.gradle | grep "^version =" | cut -d'"' -f2
-#icon: snap/gui/jabref.png
+adopt-info: jabref
+icon: snap/gui/jabref.png
+license: MIT
summary: Bibliography manager
description: JabRef is an open source bibliography reference manager. The native file format used by JabRef is BibTeX, the standard LaTeX bibliography format.
grade: devel
confinement: strict
-
+base: core18
+license: MIT
architectures:
- build-on: amd64
- - build-on: i386
apps:
jabref:
- command: desktop-launch java -jar $SNAP/jar/JabRef-$SNAP_VERSION.jar
- environment:
- _JAVA_OPTIONS: "-Duser.home=$SNAP_USER_DATA"
- plugs:
- - desktop
- - desktop-legacy
- - wayland
- - unity7
- - home
- - opengl
- - network-bind
- - removable-media
+ command: bin/JabRef
+ extensions: [gnome-3-28]
+ browser-proxy:
+ command: lib/jabrefHost.py
+ extensions: [gnome-3-28]
+
+environment:
+ _JAVA_OPTIONS: "-Duser.home=$SNAP_USER_DATA"
+plugs:
+ desktop:
+ desktop-legacy:
+ wayland:
+ unity7:
+ home:
+ opengl:
+ network-bind:
+ removable-media:
+ browser-extension:
+ interface: system-files
+ read:
+ - /var/lib/snapd/hostfs/usr/lib/mozilla/native-messaging-hosts
+ write:
+ - /var/lib/snapd/hostfs/usr/lib/mozilla/native-messaging-hosts/org.jabref.jabref.json
parts:
jabref:
- plugin: gradle
- source: .
- source-type: git
+ plugin: dump
+ source: build/distribution/JabRef-portable_linux.tar.gz
+ # Use this source for debug purposes:
+ # source: https://builds.jabref.org/master/JabRef-portable_linux.tar.gz
stage-packages:
- - openjdk-8-jre
- - openjfx
- x11-utils
- gradle-options: [snapJar, -xtest]
- gradle-output-dir: 'build/releases'
- after: [desktop-gtk2]
+ override-build: |
+ snapcraftctl build
+ snapcraftctl set-version "$(cat $SNAPCRAFT_PART_INSTALL/lib/app/JabRef.cfg | grep "app.version=" | cut -d'=' -f2)"
+ sed -i 's|/opt/jabref/lib/jabrefHost.py|/snap/bin/jabref.browser-proxy|g' $SNAPCRAFT_PART_INSTALL/lib/org.jabref.jabref.json
diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java
new file mode 100644
index 00000000000..791e2e50698
--- /dev/null
+++ b/src/main/java/module-info.java
@@ -0,0 +1,68 @@
+open module org.jabref {
+ // Swing
+ requires java.desktop;
+
+ // SQL
+ requires java.sql;
+
+ // JavaFX
+ requires javafx.graphics;
+ requires javafx.swing;
+ requires javafx.controls;
+ requires javafx.web;
+ requires javafx.fxml;
+ requires afterburner.fx;
+ requires com.jfoenix;
+ requires de.saxsys.mvvmfx;
+ requires de.jensd.fx.fontawesomefx.commons;
+ requires de.jensd.fx.fontawesomefx.materialdesignicons;
+ requires org.controlsfx.controls;
+
+ provides com.airhacks.afterburner.views.ResourceLocator
+ with org.jabref.gui.util.JabRefResourceLocator;
+
+ provides com.airhacks.afterburner.injection.PresenterFactory
+ with org.jabref.gui.DefaultInjector;
+
+ // Logging
+ requires org.slf4j;
+ requires org.apache.logging.log4j;
+ requires org.apache.logging.log4j.core;
+ requires org.apache.logging.log4j.plugins;
+ requires applicationinsights.logging.log4j2;
+ provides org.apache.logging.log4j.plugins.processor.PluginService
+ with org.jabref.gui.logging.plugins.Log4jPlugins;
+
+ // Preferences and XML
+ requires java.prefs;
+ requires java.xml.bind;
+ requires jdk.xml.dom;
+
+ // Annotations (@PostConstruct)
+ requires java.annotation;
+
+ // Microsoft application insights
+ requires applicationinsights.core;
+
+ // Libre Office
+ requires org.jabref.thirdparty.libreoffice;
+
+ // Other modules
+ requires commons.logging;
+ requires com.google.common;
+ requires easybind;
+ requires jakarta.inject;
+ requires org.apache.pdfbox;
+ requires reactfx;
+ requires commons.cli;
+ requires httpclient;
+ requires com.github.tomtung.latex2unicode;
+ requires jbibtex;
+ requires citeproc.java;
+ requires antlr.runtime;
+ requires commons.lang3;
+ requires org.apache.xmpbox;
+ requires de.saxsys.mvvmfx.validation;
+ requires richtextfx;
+ requires unirest.java;
+}
diff --git a/src/main/java/org/jabref/Globals.java b/src/main/java/org/jabref/Globals.java
index 71466ee47c7..f65daeb27e6 100644
--- a/src/main/java/org/jabref/Globals.java
+++ b/src/main/java/org/jabref/Globals.java
@@ -40,7 +40,7 @@ public class Globals {
public static final RemoteListenerServerLifecycle REMOTE_LISTENER = new RemoteListenerServerLifecycle();
public static final ImportFormatReader IMPORT_FORMAT_READER = new ImportFormatReader();
public static final TaskExecutor TASK_EXECUTOR = new DefaultTaskExecutor();
- // In the main program, this field is initialized in JabRef.java
+ // In the main program, this field is initialized in JabRefMain.java
// Each test case initializes this field if required
public static JabRefPreferences prefs;
/**
@@ -56,13 +56,11 @@ public class Globals {
/**
* Manager for the state of the GUI.
*/
-
- public static ClipBoardManager clipboardManager = new ClipBoardManager();
-
public static StateManager stateManager = new StateManager();
public static ExporterFactory exportFactory;
public static CountingUndoManager undoManager = new CountingUndoManager();
public static BibEntryTypesManager entryTypesManager = new BibEntryTypesManager();
+ public static ClipBoardManager clipboardManager = new ClipBoardManager();
// Key binding preferences
private static KeyBindingRepository keyBindingRepository;
private static DefaultFileUpdateMonitor fileUpdateMonitor;
diff --git a/src/main/java/org/jabref/JabRefGUI.java b/src/main/java/org/jabref/JabRefGUI.java
index 66fe3694ffa..e5070ca912b 100644
--- a/src/main/java/org/jabref/JabRefGUI.java
+++ b/src/main/java/org/jabref/JabRefGUI.java
@@ -6,6 +6,7 @@
import java.util.Iterator;
import java.util.List;
+import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.stage.Stage;
@@ -42,37 +43,66 @@ public class JabRefGUI {
private final List failed = new ArrayList<>();
private final List toOpenTab = new ArrayList<>();
- private final String focusedFile;
-
- public JabRefGUI(Stage mainStage, List argsDatabases, boolean isBlank) {
- this.bibDatabases = argsDatabases;
+ public JabRefGUI(Stage mainStage, List databases, boolean isBlank) {
+ this.bibDatabases = databases;
this.isBlank = isBlank;
mainFrame = new JabRefFrame(mainStage);
- // passed file (we take the first one) should be focused
- focusedFile = argsDatabases.stream()
- .findFirst()
- .flatMap(ParserResult::getFile)
- .map(File::getAbsolutePath)
- .orElse(Globals.prefs.get(JabRefPreferences.LAST_FOCUSED));
-
openWindow(mainStage);
new VersionWorker(Globals.BUILD_INFO.getVersion(), Globals.prefs.getVersionPreferences().getIgnoredVersion(), mainFrame.getDialogService(), Globals.TASK_EXECUTOR)
- .checkForNewVersionAsync(false);
+ .checkForNewVersionDelayed();
}
private void openWindow(Stage mainStage) {
- applyFontRenderingTweak();
+ GUIGlobals.init();
+
+ LOGGER.debug("Initializing frame");
+ mainFrame.init();
+
+ // Restore window location and/or maximised state
+ if (Globals.prefs.getBoolean(JabRefPreferences.WINDOW_MAXIMISED)) {
+ mainStage.setMaximized(true);
+ } else {
+ mainStage.setX(Globals.prefs.getDouble(JabRefPreferences.POS_X));
+ mainStage.setY(Globals.prefs.getDouble(JabRefPreferences.POS_Y));
+ mainStage.setWidth(Globals.prefs.getDouble(JabRefPreferences.SIZE_X));
+ mainStage.setHeight(Globals.prefs.getDouble(JabRefPreferences.SIZE_Y));
+ }
+
+ // We create a decoration pane ourselves for performance reasons
+ // (otherwise it has to be injected later, leading to a complete redraw/relayout of the complete scene)
+ DecorationPane root = new DecorationPane();
+ root.getChildren().add(JabRefGUI.mainFrame);
+
+ Scene scene = new Scene(root, 800, 800);
+ Globals.getThemeLoader().installCss(scene, Globals.prefs);
+ mainStage.setTitle(JabRefFrame.FRAME_TITLE);
+ mainStage.getIcons().addAll(IconTheme.getLogoSetFX());
+ mainStage.setScene(scene);
+ mainStage.show();
+ mainStage.setOnCloseRequest(event -> {
+ saveWindowState(mainStage);
+ boolean reallyQuit = mainFrame.quit();
+ if (!reallyQuit) {
+ event.consume();
+ }
+ });
+ Platform.runLater(this::openDatabases);
+ }
+
+ private void openDatabases() {
// If the option is enabled, open the last edited libraries, if any.
if (!isBlank && Globals.prefs.getBoolean(JabRefPreferences.OPEN_LAST_EDITED)) {
openLastEditedDatabases();
}
- GUIGlobals.init();
-
- LOGGER.debug("Initializing frame");
- mainFrame.init();
+ // passed file (we take the first one) should be focused
+ String focusedFile = bibDatabases.stream()
+ .findFirst()
+ .flatMap(ParserResult::getFile)
+ .map(File::getAbsolutePath)
+ .orElse(Globals.prefs.get(JabRefPreferences.LAST_FOCUSED));
// Add all bibDatabases databases to the frame:
boolean first = false;
@@ -119,44 +149,11 @@ private void openWindow(Stage mainStage) {
first = false;
}
- // If we are set to remember the window location, we also remember the maximised
- // state. This needs to be set after the window has been made visible, so we
- // do it here:
- if (Globals.prefs.getBoolean(JabRefPreferences.WINDOW_MAXIMISED)) {
- mainStage.setMaximized(true);
- } else {
- mainStage.setX(Globals.prefs.getDouble(JabRefPreferences.POS_X));
- mainStage.setY(Globals.prefs.getDouble(JabRefPreferences.POS_Y));
- mainStage.setWidth(Globals.prefs.getDouble(JabRefPreferences.SIZE_X));
- mainStage.setHeight(Globals.prefs.getDouble(JabRefPreferences.SIZE_Y));
- }
-
- // We create a decoration pane ourselves for performance reasons
- // (otherwise it has to be injected later, leading to a complete redraw/relayout of the complete scene)
- DecorationPane root = new DecorationPane();
- root.getChildren().add(JabRefGUI.mainFrame);
-
- Scene scene = new Scene(root, 800, 800);
- Globals.getThemeLoader().installCss(scene, Globals.prefs);
- mainStage.setTitle(JabRefFrame.FRAME_TITLE);
- mainStage.getIcons().addAll(IconTheme.getLogoSetFX());
- mainStage.setScene(scene);
- mainStage.show();
-
- mainStage.setOnCloseRequest(event -> {
- saveWindowState(mainStage);
- boolean reallyQuit = mainFrame.quit();
- if (!reallyQuit) {
- event.consume();
- }
- });
-
for (ParserResult pr : failed) {
String message = Localization.lang("Error opening file '%0'.", pr.getFile().get().getName()) + "\n"
+ pr.getErrorMessage();
mainFrame.getDialogService().showErrorDialogAndWait(Localization.lang("Error opening file"), message);
-
}
// Display warnings, if any
@@ -177,6 +174,7 @@ private void openWindow(Stage mainStage) {
for (int i = 0; (i < bibDatabases.size()) && (i < mainFrame.getBasePanelCount()); i++) {
ParserResult pr = bibDatabases.get(i);
BasePanel panel = mainFrame.getBasePanelAt(i);
+
OpenDatabaseAction.performPostOpenActions(panel, pr);
}
@@ -229,15 +227,6 @@ private boolean isLoaded(File fileToOpen) {
return false;
}
- private void applyFontRenderingTweak() {
- // On Linux, Java FX fonts look blurry per default. This can be improved by using a non-default rendering setting.
- // See https://github.com/woky/javafx-hates-linux
- if (Globals.prefs.getBoolean(JabRefPreferences.FX_FONT_RENDERING_TWEAK)) {
- System.setProperty("prism.text", "t2k");
- System.setProperty("prism.lcdtext", "true");
- }
- }
-
public static JabRefFrame getMainFrame() {
return mainFrame;
}
diff --git a/src/main/java/org/jabref/JabRefLauncher.java b/src/main/java/org/jabref/JabRefLauncher.java
new file mode 100644
index 00000000000..bdbe6245a9f
--- /dev/null
+++ b/src/main/java/org/jabref/JabRefLauncher.java
@@ -0,0 +1,10 @@
+package org.jabref;
+
+/**
+ * JabRef Launcher
+ */
+public class JabRefLauncher {
+ public static void main(String[] args) {
+ JabRefMain.main(args);
+ }
+}
diff --git a/src/main/java/org/jabref/JabRefMain.java b/src/main/java/org/jabref/JabRefMain.java
index b33849356b5..7b4a6a15368 100644
--- a/src/main/java/org/jabref/JabRefMain.java
+++ b/src/main/java/org/jabref/JabRefMain.java
@@ -8,6 +8,7 @@
import javafx.stage.Stage;
import org.jabref.cli.ArgumentProcessor;
+import org.jabref.cli.JabRefCLI;
import org.jabref.gui.FXDialog;
import org.jabref.gui.remote.JabRefMessageHandler;
import org.jabref.logic.journals.JournalAbbreviationLoader;
@@ -25,6 +26,7 @@
import org.jabref.model.database.BibDatabaseMode;
import org.jabref.preferences.JabRefPreferences;
+import org.apache.commons.cli.ParseException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -61,28 +63,35 @@ public void start(Stage mainStage) throws Exception {
applyPreferences(preferences);
- // Process arguments
- ArgumentProcessor argumentProcessor = new ArgumentProcessor(arguments, ArgumentProcessor.Mode.INITIAL_START);
+ try {
+ // Process arguments
+ ArgumentProcessor argumentProcessor = new ArgumentProcessor(arguments, ArgumentProcessor.Mode.INITIAL_START);
+ // Check for running JabRef
+ if (!handleMultipleAppInstances(arguments) || argumentProcessor.shouldShutDown()) {
+ Platform.exit();
+ return;
+ }
+
+ // If not, start GUI
+ new JabRefGUI(mainStage, argumentProcessor.getParserResults(), argumentProcessor.isBlank());
+ } catch (ParseException e) {
+ LOGGER.error("Problem parsing arguments", e);
- // Check for running JabRef
- if (!handleMultipleAppInstances(arguments) || argumentProcessor.shouldShutDown()) {
+ JabRefCLI.printUsage();
Platform.exit();
- return;
}
-
- // If not, start GUI
- new JabRefGUI(mainStage, argumentProcessor.getParserResults(), argumentProcessor.isBlank());
} catch (Exception ex) {
LOGGER.error("Unexpected exception", ex);
+ Platform.exit();
}
}
-
+
@Override
public void stop() {
Globals.stopBackgroundTasks();
Globals.shutdownThreadPools();
}
-
+
/**
* Tests if we are running an acceptable Java and terminates JabRef when we are sure the version is not supported.
* This test uses the requirements for the Java version as specified in gradle.build
. It is possible to
@@ -157,7 +166,7 @@ private static void applyPreferences(JabRefPreferences preferences) {
// Build list of Import and Export formats
Globals.IMPORT_FORMAT_READER.resetImportFormats(Globals.prefs.getImportFormatPreferences(),
Globals.prefs.getXMPPreferences(), Globals.getFileUpdateMonitor());
- Globals.entryTypesManager.addCustomizedEntryTypes(preferences.loadBibEntryTypes(BibDatabaseMode.BIBTEX),
+ Globals.entryTypesManager.addCustomOrModifiedTypes(preferences.loadBibEntryTypes(BibDatabaseMode.BIBTEX),
preferences.loadBibEntryTypes(BibDatabaseMode.BIBLATEX));
Globals.exportFactory = Globals.prefs.getExporterFactory(Globals.journalAbbreviationLoader);
diff --git a/src/main/java/org/jabref/cli/ArgumentProcessor.java b/src/main/java/org/jabref/cli/ArgumentProcessor.java
index 4fd9a9f2b51..bfe62a61579 100644
--- a/src/main/java/org/jabref/cli/ArgumentProcessor.java
+++ b/src/main/java/org/jabref/cli/ArgumentProcessor.java
@@ -9,6 +9,7 @@
import java.util.List;
import java.util.Locale;
import java.util.Optional;
+import java.util.Set;
import java.util.prefs.BackingStoreException;
import org.jabref.Globals;
@@ -63,7 +64,7 @@ public class ArgumentProcessor {
private final Mode startupMode;
private boolean noGUINeeded;
- public ArgumentProcessor(String[] args, Mode startupMode) {
+ public ArgumentProcessor(String[] args, Mode startupMode) throws org.apache.commons.cli.ParseException {
cli = new JabRefCLI(args);
this.startupMode = startupMode;
parserResults = processArguments();
@@ -175,7 +176,7 @@ private List processArguments() {
}
if ((startupMode == Mode.INITIAL_START) && cli.isHelp()) {
- cli.printUsage();
+ JabRefCLI.printUsage();
noGUINeeded = true;
return Collections.emptyList();
}
@@ -457,7 +458,7 @@ private void exportFile(List loaded, String[] data) {
private void importPreferences() {
try {
Globals.prefs.importPreferences(cli.getPreferencesImport());
- Globals.entryTypesManager.addCustomizedEntryTypes(Globals.prefs.loadBibEntryTypes(BibDatabaseMode.BIBTEX),
+ Globals.entryTypesManager.addCustomOrModifiedTypes(Globals.prefs.loadBibEntryTypes(BibDatabaseMode.BIBTEX),
Globals.prefs.loadBibEntryTypes(BibDatabaseMode.BIBLATEX));
List customExporters = Globals.prefs.getCustomExportFormats(Globals.journalAbbreviationLoader);
LayoutFormatterPreferences layoutPreferences = Globals.prefs
@@ -533,7 +534,7 @@ private Optional fetch(String fetchCommand) {
String engine = split[0];
String query = split[1];
- List fetchers = WebFetchers.getSearchBasedFetchers(Globals.prefs.getImportFormatPreferences());
+ Set fetchers = WebFetchers.getSearchBasedFetchers(Globals.prefs.getImportFormatPreferences());
Optional selectedFetcher = fetchers.stream()
.filter(fetcher -> fetcher.getName().equalsIgnoreCase(engine))
.findFirst();
diff --git a/src/main/java/org/jabref/cli/JabRefCLI.java b/src/main/java/org/jabref/cli/JabRefCLI.java
index dc2edfe8984..a279a266aa4 100644
--- a/src/main/java/org/jabref/cli/JabRefCLI.java
+++ b/src/main/java/org/jabref/cli/JabRefCLI.java
@@ -21,16 +21,11 @@ public class JabRefCLI {
private final CommandLine cl;
private List leftOver;
- public JabRefCLI(String[] args) {
+ public JabRefCLI(String[] args) throws ParseException {
Options options = getOptions();
- try {
- this.cl = new DefaultParser().parse(options, args);
- this.leftOver = cl.getArgList();
- } catch (ParseException e) {
- LOGGER.warn("Problem parsing arguments", e);
- this.printUsage();
- throw new RuntimeException();
- }
+
+ this.cl = new DefaultParser().parse(options, args, true);
+ this.leftOver = cl.getArgList();
}
public static String getExportMatchesSyntax() {
@@ -152,7 +147,7 @@ public boolean isAutomaticallySetFileLinks() {
return cl.hasOption("automaticallySetFileLinks");
}
- private Options getOptions() {
+ private static Options getOptions() {
Options options = new Options();
// boolean options
@@ -244,7 +239,7 @@ public void displayVersion() {
System.out.println(getVersionInfo());
}
- public void printUsage() {
+ public static void printUsage() {
String header = "";
String importFormats = Globals.IMPORT_FORMAT_READER.getImportFormatList();
diff --git a/src/main/java/org/jabref/gui/Base.css b/src/main/java/org/jabref/gui/Base.css
index 5236268d894..b7532c4c5eb 100644
--- a/src/main/java/org/jabref/gui/Base.css
+++ b/src/main/java/org/jabref/gui/Base.css
@@ -26,7 +26,7 @@
/* Highlights */
-jr-blue: #0abde3;
-jr-light-blue: #48dbfb;
- -jr-purple: #f368e0;
+ -jr-purple: #7559C2;
-jr-light-purple: #ff9ff3;
-jr-green: #10ac84;
-jr-light-green: #1dd1a1;
@@ -53,8 +53,6 @@
-jr-menu-item-foreground: -fx-dark-text-color;
-jr-menu-forground-active: -fx-dark-text-color;
- -jr-drag-target: -jr-accent;
-
-jr-head-fg: -fx-text-inner-color;
/* All icons/text on toolbars */
@@ -89,13 +87,17 @@
-jr-sidepane-header-color: -jr-theme-text;
/* Specs for the scrollbars */
- -jr-scrollbar-thumb: -fx-outer-border;
- -jr-scrollbar-track: -fx-control-inner-background;
+ -jr-scrollbar-thumb: derive(-fx-outer-border, -30%);
+ -jr-scrollbar-track: derive(-fx-control-inner-background, -10%);
-jr-separator: derive(-fx-color, -5%);
-jr-search-text: -fx-text-base-color;
+ /* For drag and drop actions */
+ -jr-drag-target: -jr-purple;
+ -jr-drag-target-hover: derive(-jr-purple, 80%);
+
/*
Here are redefinitions of the default properties of modena. They should in principle all be derived from the
above colors. Goal should be to make as few as possible direct color-changes to elements and only do this for
@@ -491,6 +493,17 @@
-fx-border-radius: 0;
}
+.tab-pane > .tab-header-area > .headers-region > .tab.drop {
+ -fx-border-color: -jr-drag-target;
+ -fx-background-color: -jr-drag-target-hover;
+ -fx-border-width: 3 1 1 1;
+}
+
+.tab-pane > .tab-header-area > .headers-region > .tab.drop .tab-label {
+ -fx-fill: -jr-drag-target;
+ -fx-text-fill: -jr-drag-target;
+}
+
.tab-pane > .tab-header-area > .tab-header-background {
-fx-background-color: -jr-background-alt;
}
@@ -685,13 +698,13 @@
.scroll-bar {
-fx-background-color: transparent;
- -fx-opacity: 0;
+ -fx-opacity: 0.3;
}
.scroll-bar:horizontal .track,
.scroll-bar:vertical .track {
-fx-background-color: -jr-scrollbar-track;
- -fx-opacity: 0.2;
+ -fx-opacity: 0.6;
-fx-background-radius: 0em;
}
@@ -705,7 +718,7 @@
.scroll-bar .thumb:hover,
.scroll-bar .thumb:pressed {
- -fx-background-color: derive(-jr-scrollbar-thumb, -20%);
+ -fx-background-color: derive(-jr-scrollbar-thumb, -30%);
}
/* Hide increment and decrement buttons */
@@ -738,7 +751,7 @@
-fx-padding: 0em 0.333em 0em 0.333em; /* 2 4 2 4 */
}
-/* Only show scrollbars for hovered elements */
+/* Restore full visibility of scrollbars for active elements */
.list-view:hover .scroll-bar,
.tree-view:hover .scroll-bar,
.table-view:hover .scroll-bar,
@@ -1046,7 +1059,3 @@ We want to have a look that matches our icons in the tool-bar */
.dialog-pane {
-fx-background-color: -fx-control-inner-background;
}
-
-.preference-sidepane {
- -fx-background-color: -jr-sidepane-background;
-}
diff --git a/src/main/java/org/jabref/gui/BasePanel.java b/src/main/java/org/jabref/gui/BasePanel.java
index 189c58636ef..909d8b38a29 100644
--- a/src/main/java/org/jabref/gui/BasePanel.java
+++ b/src/main/java/org/jabref/gui/BasePanel.java
@@ -20,7 +20,6 @@
import javafx.beans.binding.Bindings;
import javafx.geometry.Orientation;
import javafx.scene.Node;
-import javafx.scene.control.ScrollPane;
import javafx.scene.control.SplitPane;
import javafx.scene.layout.StackPane;
@@ -95,7 +94,6 @@
import org.jabref.model.entry.field.SpecialFieldValue;
import org.jabref.model.entry.field.StandardField;
import org.jabref.preferences.JabRefPreferences;
-import org.jabref.preferences.PreviewPreferences;
import com.google.common.eventbus.Subscribe;
import org.fxmisc.easybind.EasyBind;
@@ -121,12 +119,12 @@ public class BasePanel extends StackPane {
// Keeps track of the string dialog if it is open.
private final Map actions = new HashMap<>();
private final SidePaneManager sidePaneManager;
- private final BasePanelPreferences preferences;
private final ExternalFileTypes externalFileTypes;
private final EntryEditor entryEditor;
private final DialogService dialogService;
private MainTable mainTable;
+ private BasePanelPreferences preferences;
// To contain instantiated entry editors. This is to save time
// As most enums, this must not be null
private BasePanelMode mode = BasePanelMode.SHOWING_NOTHING;
@@ -144,10 +142,12 @@ public class BasePanel extends StackPane {
// the query the user searches when this BasePanel is active
private Optional currentSearchQuery = Optional.empty();
private Optional changeMonitor = Optional.empty();
+ private JabRefExecutorService executorService;
public BasePanel(JabRefFrame frame, BasePanelPreferences preferences, BibDatabaseContext bibDatabaseContext, ExternalFileTypes externalFileTypes) {
this.preferences = Objects.requireNonNull(preferences);
this.frame = Objects.requireNonNull(frame);
+ this.executorService = JabRefExecutorService.INSTANCE;
this.bibDatabaseContext = Objects.requireNonNull(bibDatabaseContext);
this.externalFileTypes = Objects.requireNonNull(externalFileTypes);
this.undoManager = frame.getUndoManager();
@@ -177,6 +177,8 @@ public BasePanel(JabRefFrame frame, BasePanelPreferences preferences, BibDatabas
this.getDatabase().registerListener(new UpdateTimestampListener(Globals.prefs));
this.entryEditor = new EntryEditor(this, externalFileTypes);
+ // Open entry editor for first entry on start up.
+ Platform.runLater(() -> clearAndSelectFirst());
}
@Subscribe
@@ -240,7 +242,7 @@ public void output(String s) {
}
private void setupActions() {
- SaveDatabaseAction saveAction = new SaveDatabaseAction(this, Globals.prefs);
+ SaveDatabaseAction saveAction = new SaveDatabaseAction(this, Globals.prefs, Globals.entryTypesManager);
CleanupAction cleanUpAction = new CleanupAction(this, Globals.prefs, Globals.TASK_EXECUTOR);
actions.put(Actions.UNDO, undoAction);
@@ -352,6 +354,13 @@ private void setupActions() {
new SpecialFieldViewModel(SpecialField.READ_STATUS, undoManager).getSpecialFieldAction(status, this.frame));
}
+ actions.put(Actions.NEXT_PREVIEW_STYLE, () -> {
+ entryEditor.nextPreviewStyle();
+ });
+ actions.put(Actions.PREVIOUS_PREVIEW_STYLE, () -> {
+ entryEditor.previousPreviewStyle();
+ });
+
actions.put(Actions.SEND_AS_EMAIL, new SendAsEMailAction(frame));
actions.put(Actions.WRITE_XMP, new WriteXMPAction(this)::execute);
@@ -727,22 +736,20 @@ public void actionPerformed(ActionEvent e) {
}
public void setupMainPanel() {
+ preferences = BasePanelPreferences.from(Globals.prefs);
+
splitPane = new SplitPane();
splitPane.setOrientation(Orientation.VERTICAL);
adjustSplitter(); // restore last splitting state (before mainTable is created as creation affects the stored size of the entryEditors)
createMainTable();
- ScrollPane pane = mainTable.getPane();
- pane.setFitToHeight(true);
- pane.setFitToWidth(true);
- splitPane.getItems().add(pane);
+ splitPane.getItems().add(mainTable);
// Set up name autocompleter for search:
- instantiateSearchAutoCompleter();
- this.getDatabase().registerListener(new SearchAutoCompleteListener());
-
setupAutoCompletion();
+ executorService.execute(this::instantiateSearchAutoCompleter);
+ this.getDatabase().registerListener(new SearchAutoCompleteListener());
// Saves the divider position as soon as it changes
// We need to keep a reference to the subscription, otherwise the binding gets garbage collected
@@ -842,23 +849,6 @@ private void showAndEdit() {
}
}
- public void nextPreviewStyle() {
- cyclePreview(Globals.prefs.getPreviewPreferences().getPreviewCyclePosition() + 1);
- }
-
- public void previousPreviewStyle() {
- cyclePreview(Globals.prefs.getPreviewPreferences().getPreviewCyclePosition() - 1);
- }
-
- private void cyclePreview(int newPosition) {
- PreviewPreferences previewPreferences = Globals.prefs.getPreviewPreferences()
- .getBuilder()
- .withPreviewCyclePosition(newPosition)
- .build();
- Globals.prefs.storePreviewPreferences(previewPreferences);
- entryEditor.updatePreviewInTabs(previewPreferences);
- }
-
/**
* Removes the bottom component.
*/
@@ -875,6 +865,14 @@ public void clearAndSelect(final BibEntry bibEntry) {
mainTable.clearAndSelect(bibEntry);
}
+ /**
+ * Select and open entry editor for first entry in main table.
+ */
+ private void clearAndSelectFirst() {
+ mainTable.clearAndSelectFirst();
+ showAndEdit();
+ }
+
public void selectPreviousEntry() {
mainTable.getSelectionModel().clearAndSelect(mainTable.getSelectionModel().getSelectedIndex() - 1);
}
@@ -1049,11 +1047,9 @@ public Optional getCurrentSearchQuery() {
/**
* Set the query the user currently searches while this basepanel is active
- *
- * @param currentSearchQuery can be null
*/
- public void setCurrentSearchQuery(SearchQuery currentSearchQuery) {
- this.currentSearchQuery = Optional.ofNullable(currentSearchQuery);
+ public void setCurrentSearchQuery(Optional currentSearchQuery) {
+ this.currentSearchQuery = currentSearchQuery;
}
public CitationStyleCache getCitationStyleCache() {
diff --git a/src/main/java/org/jabref/gui/ClipBoardManager.java b/src/main/java/org/jabref/gui/ClipBoardManager.java
index 0411f8afd4e..64bc6617533 100644
--- a/src/main/java/org/jabref/gui/ClipBoardManager.java
+++ b/src/main/java/org/jabref/gui/ClipBoardManager.java
@@ -1,5 +1,10 @@
package org.jabref.gui;
+import java.awt.Toolkit;
+import java.awt.datatransfer.DataFlavor;
+import java.awt.datatransfer.StringSelection;
+import java.awt.datatransfer.Transferable;
+import java.awt.datatransfer.UnsupportedFlavorException;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
@@ -7,9 +12,11 @@
import java.util.List;
import java.util.Optional;
+import javafx.scene.control.TextInputControl;
import javafx.scene.input.Clipboard;
import javafx.scene.input.ClipboardContent;
import javafx.scene.input.DataFormat;
+import javafx.scene.input.MouseButton;
import org.jabref.Globals;
import org.jabref.logic.bibtex.BibEntryWriter;
@@ -30,36 +37,53 @@
import org.slf4j.LoggerFactory;
public class ClipBoardManager {
+
public static final DataFormat XML = new DataFormat("application/xml");
private static final Logger LOGGER = LoggerFactory.getLogger(ClipBoardManager.class);
- private final Clipboard clipboard;
- private final ImportFormatReader importFormatReader;
+ private static Clipboard clipboard;
+ private static java.awt.datatransfer.Clipboard primary;
+ private static ImportFormatReader importFormatReader;
- public ClipBoardManager() {
- this(Clipboard.getSystemClipboard(), Globals.IMPORT_FORMAT_READER);
+ public ClipBoardManager(Clipboard clipboard, java.awt.datatransfer.Clipboard primary, ImportFormatReader importFormatReader) {
+ ClipBoardManager.clipboard = clipboard;
+ ClipBoardManager.primary = primary;
+ ClipBoardManager.importFormatReader = importFormatReader;
}
- public ClipBoardManager(Clipboard clipboard, ImportFormatReader importFormatReader) {
- this.clipboard = clipboard;
- this.importFormatReader = importFormatReader;
+ public ClipBoardManager() {
+ this(Clipboard.getSystemClipboard(), Toolkit.getDefaultToolkit().getSystemSelection(), Globals.IMPORT_FORMAT_READER);
}
/**
- * Puts content onto the clipboard.
+ * Add X11 clipboard support to a text input control.
+ * It is necessary to call this method in every input where you want to use it:
+ * {@code ClipBoardManager.addX11Support(TextInputControl input);}.
+ *
+ * @param input the TextInputControl (e.g., TextField, TextArea, and children) where adding this functionality.
+ * @see Short summary for X11 clipboards
+ * @see Longer text over clipboards
*/
- public void setContent(ClipboardContent content) {
- clipboard.setContent(content);
+ public static void addX11Support(TextInputControl input) {
+ input.selectedTextProperty().addListener((observable, oldValue, newValue) -> {
+ if (!newValue.isEmpty()) {
+ primary.setContents(new StringSelection(newValue), null);
+ }
+ });
+ input.setOnMouseClicked(event -> {
+ if (event.getButton() == MouseButton.MIDDLE) {
+ input.insertText(input.getCaretPosition(), getContentsPrimary());
+ }
+ });
}
/**
- * Get the String residing on the clipboard.
+ * Get the String residing on the system clipboard.
*
- * @return any text found on the Clipboard; if none found, return an
- * empty String.
+ * @return any text found on the Clipboard; if none found, return an empty String.
*/
- public String getContents() {
+ public static String getContents() {
String result = clipboard.getString();
if (result == null) {
return "";
@@ -67,16 +91,54 @@ public String getContents() {
return result;
}
+ /**
+ * Get the String residing on the primary clipboard.
+ *
+ * @return any text found on the primary Clipboard; if none found, try with the system clipboard.
+ */
+ public static String getContentsPrimary() {
+ Transferable contents = primary.getContents(null);
+ if (contents != null && contents.isDataFlavorSupported(DataFlavor.stringFlavor)) {
+ try {
+ return (String) contents.getTransferData(DataFlavor.stringFlavor);
+ } catch (UnsupportedFlavorException | IOException e) {
+ LOGGER.warn(e.getMessage());
+ }
+ }
+ return getContents();
+ }
+
+ /**
+ * Puts content onto the system clipboard.
+ *
+ * @param content the ClipboardContent to set as current value of the system clipboard.
+ */
+ public void setContent(ClipboardContent content) {
+ clipboard.setContent(content);
+ setPrimaryClipboardContent(content);
+ }
+
+ /**
+ * Puts content onto the primary clipboard.
+ *
+ * @param content the ClipboardContent to set as current value of the primary clipboard.
+ */
+ public void setPrimaryClipboardContent(ClipboardContent content) {
+ primary.setContents(new StringSelection(content.getString()), null);
+ }
+
public void setHtmlContent(String html) {
final ClipboardContent content = new ClipboardContent();
content.putHtml(html);
clipboard.setContent(content);
+ setPrimaryClipboardContent(content);
}
public void setContent(String string) {
final ClipboardContent content = new ClipboardContent();
content.putString(string);
clipboard.setContent(content);
+ setPrimaryClipboardContent(content);
}
public void setContent(List entries) throws IOException {
@@ -86,6 +148,7 @@ public void setContent(List entries) throws IOException {
content.put(DragAndDropDataFormats.ENTRIES, serializedEntries);
content.putString(serializedEntries);
clipboard.setContent(content);
+ setPrimaryClipboardContent(content);
}
public List extractData() {
diff --git a/src/main/java/org/jabref/gui/Dark.css b/src/main/java/org/jabref/gui/Dark.css
index 8df9935c460..cde1709d0ea 100644
--- a/src/main/java/org/jabref/gui/Dark.css
+++ b/src/main/java/org/jabref/gui/Dark.css
@@ -50,3 +50,17 @@
-jr-tooltip-fg: derive(-fx-light-text-color, 50%);
}
+
+#previewBody{
+ background-color: #272b38; /* -fx-control-inner-background*/
+ color : #7d8591; /* -fx-mid-text-color*/
+}
+
+.text-unchanged {
+ -fx-fill: -fx-light-text-color;
+}
+
+.radio-button > .radio {
+ -fx-background-color: -fx-light-text-color, -fx-control-inner-background;
+}
+
diff --git a/src/main/java/org/jabref/gui/DragAndDropDataFormats.java b/src/main/java/org/jabref/gui/DragAndDropDataFormats.java
index 82f3e8606ac..67aa1cefce0 100644
--- a/src/main/java/org/jabref/gui/DragAndDropDataFormats.java
+++ b/src/main/java/org/jabref/gui/DragAndDropDataFormats.java
@@ -5,7 +5,6 @@
import javafx.scene.input.DataFormat;
import org.jabref.logic.citationstyle.PreviewLayout;
-import org.jabref.model.entry.BibEntry;
/**
* Contains all the different {@link DataFormat}s that may occur in JabRef.
@@ -15,7 +14,6 @@ public class DragAndDropDataFormats {
public static final DataFormat GROUP = new DataFormat("dnd/org.jabref.model.groups.GroupTreeNode");
public static final DataFormat LINKED_FILE = new DataFormat("dnd/org.jabref.model.entry.LinkedFile");
public static final DataFormat ENTRIES = new DataFormat("dnd/org.jabref.model.entry.BibEntries");
- @SuppressWarnings("unchecked") public static final Class> BIBENTRY_LIST_CLASS = (Class>) (Class>) List.class;
public static final DataFormat PREVIEWLAYOUTS = new DataFormat("dnd/org.jabref.logic.citationstyle.PreviewLayouts");
@SuppressWarnings("unchecked") public static final Class> PREVIEWLAYOUT_LIST_CLASS = (Class>) (Class>) List.class;
}
diff --git a/src/main/java/org/jabref/gui/DragAndDropHelper.java b/src/main/java/org/jabref/gui/DragAndDropHelper.java
new file mode 100644
index 00000000000..cbc16c3c573
--- /dev/null
+++ b/src/main/java/org/jabref/gui/DragAndDropHelper.java
@@ -0,0 +1,26 @@
+package org.jabref.gui;
+
+import java.io.File;
+import java.nio.file.Path;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import javafx.scene.input.Dragboard;
+
+import org.jabref.logic.util.io.FileUtil;
+
+public class DragAndDropHelper {
+
+ public static boolean hasBibFiles(Dragboard dragboard) {
+ return !getBibFiles(dragboard).isEmpty();
+ }
+
+ public static List getBibFiles(Dragboard dragboard) {
+ if (!dragboard.hasFiles()) {
+ return Collections.emptyList();
+ } else {
+ return dragboard.getFiles().stream().map(File::toPath).filter(FileUtil::isBibFile).collect(Collectors.toList());
+ }
+ }
+}
diff --git a/src/main/java/org/jabref/gui/JabRefFrame.java b/src/main/java/org/jabref/gui/JabRefFrame.java
index 04414186ea1..364a834056e 100644
--- a/src/main/java/org/jabref/gui/JabRefFrame.java
+++ b/src/main/java/org/jabref/gui/JabRefFrame.java
@@ -1,6 +1,5 @@
package org.jabref.gui;
-import java.awt.Window;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
@@ -12,7 +11,6 @@
import java.util.Objects;
import java.util.Optional;
import java.util.TimerTask;
-import java.util.stream.Collectors;
import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
@@ -35,7 +33,7 @@
import javafx.scene.control.TextInputControl;
import javafx.scene.control.ToolBar;
import javafx.scene.control.Tooltip;
-import javafx.scene.input.DataFormat;
+import javafx.scene.control.skin.TabPaneSkin;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.TransferMode;
import javafx.scene.layout.BorderPane;
@@ -106,11 +104,9 @@
import org.jabref.logic.autosaveandbackup.AutosaveManager;
import org.jabref.logic.autosaveandbackup.BackupManager;
import org.jabref.logic.importer.IdFetcher;
-import org.jabref.logic.importer.OpenDatabase;
import org.jabref.logic.importer.ParserResult;
import org.jabref.logic.importer.WebFetchers;
import org.jabref.logic.l10n.Localization;
-import org.jabref.logic.search.SearchQuery;
import org.jabref.logic.undo.AddUndoableActionEvent;
import org.jabref.logic.undo.UndoChangeEvent;
import org.jabref.logic.undo.UndoRedoEvent;
@@ -126,8 +122,6 @@
import org.jabref.preferences.LastFocusedTabPreferences;
import com.google.common.eventbus.Subscribe;
-import org.eclipse.fx.ui.controls.tabpane.DndTabPane;
-import org.eclipse.fx.ui.controls.tabpane.DndTabPaneFactory;
import org.fxmisc.easybind.EasyBind;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -144,18 +138,19 @@ public class JabRefFrame extends BorderPane {
private final SplitPane splitPane = new SplitPane();
private final JabRefPreferences prefs = Globals.prefs;
- private final GlobalSearchBar globalSearchBar = new GlobalSearchBar(this);
+ private final GlobalSearchBar globalSearchBar = new GlobalSearchBar(this, Globals.stateManager);
private final ProgressBar progressBar = new ProgressBar();
- private final FileHistoryMenu fileHistory = new FileHistoryMenu(prefs, this);
+ private final FileHistoryMenu fileHistory;
private final Stage mainStage;
private final StateManager stateManager;
private final CountingUndoManager undoManager;
- private SidePaneManager sidePaneManager;
- private TabPane tabbedPane;
private final PushToApplicationsManager pushToApplicationsManager;
private final DialogService dialogService;
+ private final JabRefExecutorService executorService;
+ private SidePaneManager sidePaneManager;
+ private TabPane tabbedPane;
private SidePane sidePane;
public JabRefFrame(Stage mainStage) {
@@ -164,88 +159,55 @@ public JabRefFrame(Stage mainStage) {
this.stateManager = Globals.stateManager;
this.pushToApplicationsManager = new PushToApplicationsManager(dialogService, stateManager);
this.undoManager = Globals.undoManager;
+ this.fileHistory = new FileHistoryMenu(prefs, dialogService, getOpenDatabaseAction());
+ this.executorService = JabRefExecutorService.INSTANCE;
}
- public void init() {
- sidePaneManager = new SidePaneManager(Globals.prefs, this);
- sidePane = sidePaneManager.getPane();
+ private static BasePanel getBasePanel(Tab tab) {
+ return (BasePanel) tab.getContent();
+ }
- Pane containerPane = DndTabPaneFactory.createDefaultDnDPane(DndTabPaneFactory.FeedbackType.MARKER, null);
- tabbedPane = (DndTabPane) containerPane.getChildren().get(0);
+ private void initDragAndDrop() {
+ Tab dndIndicator = new Tab(Localization.lang("Open files..."), null);
+ dndIndicator.getStyleClass().add("drop");
- initLayout();
-
- initKeyBindings();
-
- tabbedPane.setOnDragOver(event -> {
- if (event.getDragboard().hasFiles()) {
- event.acceptTransferModes(TransferMode.COPY, TransferMode.MOVE, TransferMode.LINK);
+ EasyBind.subscribe(tabbedPane.skinProperty(), skin -> {
+ if (!(skin instanceof TabPaneSkin)) {
+ return;
}
- });
-
- tabbedPane.setOnDragDropped(event -> {
- boolean success = false;
-
- if (event.getDragboard().hasContent(DataFormat.FILES)) {
- List files = event.getDragboard().getFiles().stream().map(File::toPath).filter(FileUtil::isBibFile).collect(Collectors.toList());
- success = true;
- for (Path file : files) {
- ParserResult pr = OpenDatabase.loadDatabase(file.toString(), Globals.prefs.getImportFormatPreferences(), Globals.getFileUpdateMonitor());
- addParserResult(pr, true);
+ // We need to get the tab header, the following is a ugly workaround
+ Node tabHeaderArea = ((TabPaneSkin) this.tabbedPane.getSkin())
+ .getChildren()
+ .stream()
+ .filter(node -> node.getStyleClass().contains("tab-header-area"))
+ .findFirst()
+ .orElseThrow();
+
+ tabHeaderArea.setOnDragOver(event -> {
+ if (DragAndDropHelper.hasBibFiles(event.getDragboard())) {
+ event.acceptTransferModes(TransferMode.ANY);
+ if (!tabbedPane.getTabs().contains(dndIndicator)) {
+ tabbedPane.getTabs().add(dndIndicator);
+ }
+ event.consume();
+ } else {
+ tabbedPane.getTabs().remove(dndIndicator);
}
- }
-
- event.setDropCompleted(success);
- event.consume();
- });
-
- //setBounds(GraphicsEnvironment.getLocalGraphicsEnvironment().getMaximumWindowBounds());
- //WindowLocation pw = new WindowLocation(this, JabRefPreferences.POS_X, JabRefPreferences.POS_Y, JabRefPreferences.SIZE_X,
- // JabRefPreferences.SIZE_Y);
- //pw.displayWindowAtStoredLocation();
-
- /*
- * The following state listener makes sure focus is registered with the
- * correct database when the user switches tabs. Without this,
- * cut/paste/copy operations would some times occur in the wrong tab.
- */
- EasyBind.subscribe(tabbedPane.getSelectionModel().selectedItemProperty(), e -> {
- if (e == null) {
- stateManager.activeDatabaseProperty().setValue(Optional.empty());
- return;
- }
+ });
- BasePanel currentBasePanel = getCurrentBasePanel();
- if (currentBasePanel == null) {
- return;
- }
+ tabHeaderArea.setOnDragExited(event -> tabbedPane.getTabs().remove(dndIndicator));
- // Poor-mans binding to global state
- stateManager.activeDatabaseProperty().setValue(Optional.of(currentBasePanel.getBibDatabaseContext()));
- stateManager.setSelectedEntries(currentBasePanel.getSelectedEntries());
-
- // Update search query
- String content = "";
- Optional currentSearchQuery = currentBasePanel.getCurrentSearchQuery();
- if (currentSearchQuery.isPresent()) {
- content = currentSearchQuery.get().getQuery();
- }
- globalSearchBar.setSearchTerm(content);
+ tabHeaderArea.setOnDragDropped(event -> {
+ tabbedPane.getTabs().remove(dndIndicator);
- // groupSidePane.getToggleCommand().setSelected(sidePaneManager.isComponentVisible(GroupSidePane.class));
- //previewToggle.setSelected(Globals.prefs.getPreviewPreferences().isPreviewPanelEnabled());
- //generalFetcher.getToggleCommand().setSelected(sidePaneManager.isComponentVisible(WebSearchPane.class));
- //openOfficePanel.getToggleCommand().setSelected(sidePaneManager.isComponentVisible(OpenOfficeSidePanel.class));
-
- setWindowTitle();
- // Update search autocompleter with information for the correct database:
- currentBasePanel.updateSearchManager();
-
- currentBasePanel.getUndoManager().postUndoRedoEvent();
- currentBasePanel.getMainTable().requestFocus();
+ List bibFiles = DragAndDropHelper.getBibFiles(event.getDragboard());
+ OpenDatabaseAction openDatabaseAction = this.getOpenDatabaseAction();
+ openDatabaseAction.openFiles(bibFiles, true);
+ event.setDropCompleted(true);
+ event.consume();
+ });
});
- initShowTrackingNotification();
}
private void initKeyBindings() {
@@ -292,7 +254,7 @@ private void initShowTrackingNotification() {
@Override
public void run() {
- DefaultTaskExecutor.runInJavaFXThread(JabRefFrame.this::showTrackingNotification);
+ DefaultTaskExecutor.runInJavaFXThread(JabRefFrame.this::showTrackingNotification);
}
}, 60000); // run in one minute
}
@@ -396,13 +358,6 @@ private void tearDownJabRef(List filenames) {
fileHistory.storeHistory();
prefs.flush();
-
- // dispose all windows, even if they are not displayed anymore
- // TODO: javafx variant only avaiable in java 9 and updwards
- // https://docs.oracle.com/javase/9/docs/api/javafx/stage/Window.html#getWindows--
- for (Window window : Window.getWindows()) {
- window.dispose();
- }
}
/**
@@ -585,6 +540,70 @@ public void showBasePanel(BasePanel bp) {
tabbedPane.getSelectionModel().select(getTab(bp));
}
+ public void init() {
+ sidePaneManager = new SidePaneManager(Globals.prefs, this);
+ sidePane = sidePaneManager.getPane();
+
+ tabbedPane = new TabPane();
+ tabbedPane.setTabDragPolicy(TabPane.TabDragPolicy.REORDER);
+
+ initLayout();
+
+ initKeyBindings();
+
+ initDragAndDrop();
+
+ //setBounds(GraphicsEnvironment.getLocalGraphicsEnvironment().getMaximumWindowBounds());
+ //WindowLocation pw = new WindowLocation(this, JabRefPreferences.POS_X, JabRefPreferences.POS_Y, JabRefPreferences.SIZE_X,
+ // JabRefPreferences.SIZE_Y);
+ //pw.displayWindowAtStoredLocation();
+
+ // Bind global state
+ stateManager.activeDatabaseProperty().bind(
+ EasyBind.map(tabbedPane.getSelectionModel().selectedItemProperty(),
+ tab -> Optional.ofNullable(tab).map(JabRefFrame::getBasePanel).map(BasePanel::getBibDatabaseContext)));
+
+ // Subscribe to the search
+ EasyBind.subscribe(stateManager.activeSearchQueryProperty(),
+ query -> {
+ if (getCurrentBasePanel() != null) {
+ getCurrentBasePanel().setCurrentSearchQuery(query);
+ }
+ });
+
+ /*
+ * The following state listener makes sure focus is registered with the
+ * correct database when the user switches tabs. Without this,
+ * cut/paste/copy operations would some times occur in the wrong tab.
+ */
+ EasyBind.subscribe(tabbedPane.getSelectionModel().selectedItemProperty(), tab -> {
+ if (tab == null) {
+ return;
+ }
+
+ BasePanel newBasePanel = getBasePanel(tab);
+
+ // Poor-mans binding to global state
+ stateManager.setSelectedEntries(newBasePanel.getSelectedEntries());
+
+ // Update active search query when switching between databases
+ stateManager.activeSearchQueryProperty().set(newBasePanel.getCurrentSearchQuery());
+
+ // groupSidePane.getToggleCommand().setSelected(sidePaneManager.isComponentVisible(GroupSidePane.class));
+ //previewToggle.setSelected(Globals.prefs.getPreviewPreferences().isPreviewPanelEnabled());
+ //generalFetcher.getToggleCommand().setSelected(sidePaneManager.isComponentVisible(WebSearchPane.class));
+ //openOfficePanel.getToggleCommand().setSelected(sidePaneManager.isComponentVisible(OpenOfficeSidePanel.class));
+
+ setWindowTitle();
+ // Update search autocompleter with information for the correct database:
+ newBasePanel.updateSearchManager();
+
+ newBasePanel.getUndoManager().postUndoRedoEvent();
+ newBasePanel.getMainTable().requestFocus();
+ });
+ initShowTrackingNotification();
+ }
+
/**
* Returns the currently viewed BasePanel.
*/
@@ -592,7 +611,7 @@ public BasePanel getCurrentBasePanel() {
if ((tabbedPane == null) || (tabbedPane.getSelectionModel().getSelectedItem() == null)) {
return null;
}
- return (BasePanel) tabbedPane.getSelectionModel().getSelectedItem().getContent();
+ return getBasePanel(tabbedPane.getSelectionModel().getSelectedItem());
}
/**
@@ -653,8 +672,8 @@ private MenuBar createMenu() {
factory.createSubMenu(StandardActions.IMPORT,
factory.createMenuItem(StandardActions.MERGE_DATABASE, new OldDatabaseCommandWrapper(Actions.MERGE_DATABASE, this, stateManager)), // TODO: merge with import
- factory.createMenuItem(StandardActions.IMPORT_INTO_CURRENT_LIBRARY, new ImportCommand(this, false)),
- factory.createMenuItem(StandardActions.IMPORT_INTO_NEW_LIBRARY, new ImportCommand(this, true))),
+ factory.createMenuItem(StandardActions.IMPORT_INTO_CURRENT_LIBRARY, new ImportCommand(this, false, stateManager)),
+ factory.createMenuItem(StandardActions.IMPORT_INTO_NEW_LIBRARY, new ImportCommand(this, true, stateManager))),
factory.createSubMenu(StandardActions.EXPORT,
factory.createMenuItem(StandardActions.EXPORT_ALL, new ExportCommand(this, false, Globals.prefs)),
@@ -696,41 +715,17 @@ private MenuBar createMenu() {
);
if (Globals.prefs.getBoolean(JabRefPreferences.SPECIALFIELDSENABLED)) {
- boolean menuItemAdded = false;
- if (Globals.prefs.getBoolean(JabRefPreferences.SHOWCOLUMN_RANKING)) {
- edit.getItems().add(SpecialFieldMenuItemFactory.createSpecialFieldMenuForActiveDatabase(SpecialField.RANKING, factory, undoManager));
- menuItemAdded = true;
- }
-
- if (Globals.prefs.getBoolean(JabRefPreferences.SHOWCOLUMN_RELEVANCE)) {
- edit.getItems().add(SpecialFieldMenuItemFactory.getSpecialFieldSingleItemForActiveDatabase(SpecialField.RELEVANCE, factory));
- menuItemAdded = true;
- }
-
- if (Globals.prefs.getBoolean(JabRefPreferences.SHOWCOLUMN_QUALITY)) {
- edit.getItems().add(SpecialFieldMenuItemFactory.getSpecialFieldSingleItemForActiveDatabase(SpecialField.QUALITY, factory));
- menuItemAdded = true;
- }
-
- if (Globals.prefs.getBoolean(JabRefPreferences.SHOWCOLUMN_PRINTED)) {
- edit.getItems().add(SpecialFieldMenuItemFactory.getSpecialFieldSingleItemForActiveDatabase(SpecialField.PRINTED, factory));
- menuItemAdded = true;
- }
-
- if (Globals.prefs.getBoolean(JabRefPreferences.SHOWCOLUMN_PRIORITY)) {
- edit.getItems().add(SpecialFieldMenuItemFactory.createSpecialFieldMenuForActiveDatabase(SpecialField.PRIORITY, factory, undoManager));
- menuItemAdded = true;
- }
-
- if (Globals.prefs.getBoolean(JabRefPreferences.SHOWCOLUMN_READ)) {
- edit.getItems().add(SpecialFieldMenuItemFactory.createSpecialFieldMenuForActiveDatabase(SpecialField.READ_STATUS, factory, undoManager));
- menuItemAdded = true;
- }
-
- if (menuItemAdded) {
- edit.getItems().add(new SeparatorMenuItem());
- }
+ edit.getItems().addAll(
+ SpecialFieldMenuItemFactory.createSpecialFieldMenuForActiveDatabase(SpecialField.RANKING, factory, undoManager),
+ SpecialFieldMenuItemFactory.getSpecialFieldSingleItemForActiveDatabase(SpecialField.RELEVANCE, factory),
+ SpecialFieldMenuItemFactory.getSpecialFieldSingleItemForActiveDatabase(SpecialField.QUALITY, factory),
+ SpecialFieldMenuItemFactory.getSpecialFieldSingleItemForActiveDatabase(SpecialField.PRINTED, factory),
+ SpecialFieldMenuItemFactory.createSpecialFieldMenuForActiveDatabase(SpecialField.PRIORITY, factory, undoManager),
+ SpecialFieldMenuItemFactory.createSpecialFieldMenuForActiveDatabase(SpecialField.READ_STATUS, factory, undoManager),
+ new SeparatorMenuItem()
+ );
}
+
//@formatter:off
library.getItems().addAll(
factory.createMenuItem(StandardActions.NEW_ENTRY, new NewEntryAction(this, dialogService, Globals.prefs, stateManager)),
@@ -1121,7 +1116,7 @@ private boolean confirmClose(BasePanel panel) {
if (response.isPresent() && response.get().equals(saveChanges)) {
// The user wants to save.
try {
- SaveDatabaseAction saveAction = new SaveDatabaseAction(panel, Globals.prefs);
+ SaveDatabaseAction saveAction = new SaveDatabaseAction(panel, Globals.prefs, Globals.entryTypesManager);
if (saveAction.save()) {
// Saved, now exit.
return true;
@@ -1203,6 +1198,34 @@ public DialogService getDialogService() {
return dialogService;
}
+ private void setDefaultTableFontSize() {
+ GUIGlobals.setFont(Globals.prefs.getIntDefault(JabRefPreferences.FONT_SIZE));
+ for (BasePanel basePanel : getBasePanelList()) {
+ basePanel.updateTableFont();
+ }
+ dialogService.notify(Localization.lang("Table font size is %0", String.valueOf(GUIGlobals.currentFont.getSize())));
+ }
+
+ private void increaseTableFontSize() {
+ GUIGlobals.setFont(GUIGlobals.currentFont.getSize() + 1);
+ for (BasePanel basePanel : getBasePanelList()) {
+ basePanel.updateTableFont();
+ }
+ dialogService.notify(Localization.lang("Table font size is %0", String.valueOf(GUIGlobals.currentFont.getSize())));
+ }
+
+ private void decreaseTableFontSize() {
+ double currentSize = GUIGlobals.currentFont.getSize();
+ if (currentSize < 2) {
+ return;
+ }
+ GUIGlobals.setFont(currentSize - 1);
+ for (BasePanel basePanel : getBasePanelList()) {
+ basePanel.updateTableFont();
+ }
+ dialogService.notify(Localization.lang("Table font size is %0", String.valueOf(GUIGlobals.currentFont.getSize())));
+ }
+
/**
* The action concerned with closing the window.
*/
@@ -1271,34 +1294,6 @@ public void execute() {
}
}
- private void setDefaultTableFontSize() {
- GUIGlobals.setFont(Globals.prefs.getIntDefault(JabRefPreferences.FONT_SIZE));
- for (BasePanel basePanel : getBasePanelList()) {
- basePanel.updateTableFont();
- }
- dialogService.notify(Localization.lang("Table font size is %0", String.valueOf(GUIGlobals.currentFont.getSize())));
- }
-
- private void increaseTableFontSize() {
- GUIGlobals.setFont(GUIGlobals.currentFont.getSize() + 1);
- for (BasePanel basePanel : getBasePanelList()) {
- basePanel.updateTableFont();
- }
- dialogService.notify(Localization.lang("Table font size is %0", String.valueOf(GUIGlobals.currentFont.getSize())));
- }
-
- private void decreaseTableFontSize() {
- double currentSize = GUIGlobals.currentFont.getSize();
- if (currentSize < 2) {
- return;
- }
- GUIGlobals.setFont(currentSize - 1);
- for (BasePanel basePanel : getBasePanelList()) {
- basePanel.updateTableFont();
- }
- dialogService.notify(Localization.lang("Table font size is %0", String.valueOf(GUIGlobals.currentFont.getSize())));
- }
-
private class CloseDatabaseAction extends SimpleCommand {
@Override
diff --git a/src/main/java/org/jabref/gui/SaveOrderConfigDisplay.fxml b/src/main/java/org/jabref/gui/SaveOrderConfigDisplay.fxml
index 14c2655be95..64fac7cdc94 100644
--- a/src/main/java/org/jabref/gui/SaveOrderConfigDisplay.fxml
+++ b/src/main/java/org/jabref/gui/SaveOrderConfigDisplay.fxml
@@ -8,35 +8,53 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/java/org/jabref/gui/SaveOrderConfigDisplayView.java b/src/main/java/org/jabref/gui/SaveOrderConfigDisplayView.java
index 2c4ef4c5f6f..15679bc9ac2 100644
--- a/src/main/java/org/jabref/gui/SaveOrderConfigDisplayView.java
+++ b/src/main/java/org/jabref/gui/SaveOrderConfigDisplayView.java
@@ -7,8 +7,10 @@
import javafx.scene.control.ComboBox;
import javafx.scene.control.RadioButton;
import javafx.scene.control.ToggleGroup;
-import javafx.scene.layout.GridPane;
+import javafx.scene.layout.VBox;
+import org.jabref.gui.util.FieldsUtil;
+import org.jabref.gui.util.ViewModelListCellFactory;
import org.jabref.logic.l10n.Localization;
import org.jabref.model.entry.field.Field;
import org.jabref.model.metadata.SaveOrderConfig;
@@ -16,17 +18,15 @@
import com.airhacks.afterburner.views.ViewLoader;
-public class SaveOrderConfigDisplayView extends GridPane {
-
- private final SaveOrderConfig config;
+public class SaveOrderConfigDisplayView extends VBox {
@FXML private ToggleGroup saveOrderToggleGroup;
- @FXML private ComboBox savePriSort;
- @FXML private ComboBox saveSecSort;
- @FXML private ComboBox saveTerSort;
@FXML private RadioButton exportInSpecifiedOrder;
@FXML private RadioButton exportInTableOrder;
@FXML private RadioButton exportInOriginalOrder;
+ @FXML private ComboBox savePriSort;
+ @FXML private ComboBox saveSecSort;
+ @FXML private ComboBox saveTerSort;
@FXML private CheckBox savePriDesc;
@FXML private CheckBox saveSecDesc;
@FXML private CheckBox saveTerDesc;
@@ -34,9 +34,7 @@ public class SaveOrderConfigDisplayView extends GridPane {
private SaveOrderConfigDisplayViewModel viewModel;
- public SaveOrderConfigDisplayView(SaveOrderConfig config) {
- this.config = config;
-
+ public SaveOrderConfigDisplayView() {
ViewLoader.view(this)
.root(this)
.load();
@@ -45,19 +43,32 @@ public SaveOrderConfigDisplayView(SaveOrderConfig config) {
@FXML
private void initialize() {
- viewModel = new SaveOrderConfigDisplayViewModel(config, preferencesService);
+ viewModel = new SaveOrderConfigDisplayViewModel(preferencesService);
- exportInSpecifiedOrder.selectedProperty().bindBidirectional(viewModel.saveInSpecifiedOrderProperty());
- exportInTableOrder.selectedProperty().bindBidirectional(viewModel.saveInTableOrderProperty());
exportInOriginalOrder.selectedProperty().bindBidirectional(viewModel.saveInOriginalProperty());
+ exportInTableOrder.selectedProperty().bindBidirectional(viewModel.saveInTableOrderProperty());
+ exportInSpecifiedOrder.selectedProperty().bindBidirectional(viewModel.saveInSpecifiedOrderProperty());
+ new ViewModelListCellFactory()
+ .withText(FieldsUtil::getNameWithType)
+ .install(savePriSort);
savePriSort.itemsProperty().bindBidirectional(viewModel.priSortFieldsProperty());
- saveSecSort.itemsProperty().bindBidirectional(viewModel.secSortFieldsProperty());
- saveTerSort.itemsProperty().bindBidirectional(viewModel.terSortFieldsProperty());
-
savePriSort.valueProperty().bindBidirectional(viewModel.savePriSortSelectedValueProperty());
+ savePriSort.setConverter(FieldsUtil.fieldStringConverter);
+
+ new ViewModelListCellFactory()
+ .withText(FieldsUtil::getNameWithType)
+ .install(saveSecSort);
+ saveSecSort.itemsProperty().bindBidirectional(viewModel.secSortFieldsProperty());
saveSecSort.valueProperty().bindBidirectional(viewModel.saveSecSortSelectedValueProperty());
+ saveSecSort.setConverter(FieldsUtil.fieldStringConverter);
+
+ new ViewModelListCellFactory()
+ .withText(FieldsUtil::getNameWithType)
+ .install(saveTerSort);
+ saveTerSort.itemsProperty().bindBidirectional(viewModel.terSortFieldsProperty());
saveTerSort.valueProperty().bindBidirectional(viewModel.saveTerSortSelectedValueProperty());
+ saveTerSort.setConverter(FieldsUtil.fieldStringConverter);
savePriDesc.selectedProperty().bindBidirectional(viewModel.savePriDescPropertySelected());
saveSecDesc.selectedProperty().bindBidirectional(viewModel.saveSecDescPropertySelected());
@@ -65,6 +76,10 @@ private void initialize() {
}
+ public void setValues(SaveOrderConfig config) {
+ viewModel.setSaveOrderConfig(config);
+ }
+
public void changeExportDescriptionToSave() {
exportInOriginalOrder.setText(Localization.lang("Save entries in their original order"));
exportInSpecifiedOrder.setText(Localization.lang("Save entries ordered as specified"));
diff --git a/src/main/java/org/jabref/gui/SaveOrderConfigDisplayViewModel.java b/src/main/java/org/jabref/gui/SaveOrderConfigDisplayViewModel.java
index cc06c9e8cd9..89ef04f1c25 100644
--- a/src/main/java/org/jabref/gui/SaveOrderConfigDisplayViewModel.java
+++ b/src/main/java/org/jabref/gui/SaveOrderConfigDisplayViewModel.java
@@ -37,15 +37,13 @@ public class SaveOrderConfigDisplayViewModel {
private final PreferencesService prefs;
- public SaveOrderConfigDisplayViewModel(SaveOrderConfig config, PreferencesService prefs) {
+ public SaveOrderConfigDisplayViewModel(PreferencesService prefs) {
this.prefs = prefs;
Set fieldNames = FieldFactory.getCommonFields();
priSortFieldsProperty.addAll(fieldNames);
secSortFieldsProperty.addAll(fieldNames);
terSortFieldsProperty.addAll(fieldNames);
-
- setSaveOrderConfig(config);
}
public ListProperty priSortFieldsProperty() {
diff --git a/src/main/java/org/jabref/gui/actions/ActionFactory.java b/src/main/java/org/jabref/gui/actions/ActionFactory.java
index f0d8ec8703f..18ea69c745a 100644
--- a/src/main/java/org/jabref/gui/actions/ActionFactory.java
+++ b/src/main/java/org/jabref/gui/actions/ActionFactory.java
@@ -15,7 +15,7 @@
import org.jabref.gui.keyboard.KeyBindingRepository;
import org.jabref.model.strings.StringUtil;
-import com.sun.javafx.scene.control.skin.ContextMenuContent;
+import com.sun.javafx.scene.control.ContextMenuContent;
import de.saxsys.mvvmfx.utils.commands.Command;
import org.controlsfx.control.action.ActionUtils;
import org.fxmisc.easybind.EasyBind;
@@ -56,7 +56,7 @@ private static void setGraphic(MenuItem node, Action action) {
* should not be used since it's marked as deprecated.
*/
private static Label getAssociatedNode(MenuItem menuItem) {
- ContextMenuContent.MenuItemContainer container = (ContextMenuContent.MenuItemContainer) menuItem.impl_styleableGetNode();
+ ContextMenuContent.MenuItemContainer container = (ContextMenuContent.MenuItemContainer) menuItem.getStyleableNode();
if (container == null) {
return null;
diff --git a/src/main/java/org/jabref/gui/bibtexkeypattern/BibtexKeyPatternDialog.java b/src/main/java/org/jabref/gui/bibtexkeypattern/BibtexKeyPatternDialog.java
index 3246ff21267..b6f7c913d66 100644
--- a/src/main/java/org/jabref/gui/bibtexkeypattern/BibtexKeyPatternDialog.java
+++ b/src/main/java/org/jabref/gui/bibtexkeypattern/BibtexKeyPatternDialog.java
@@ -40,6 +40,26 @@ private void init() {
return null;
});
+ // Keep this for later conversion of the library-properties
+/* void storeSettings() {
+ DataBaseKeyPattern newKeyPattern = new DatabaseBibtexKeyPattern(preferences.getKeyPattern());
+
+ bibtexKeyPatternTableView.patternListProperty.forEach(item -> {
+ String patternString = item.getPattern();
+ if (!item.getEntryType().getName().equals("default")) {
+ if (!patternString.trim().isEmpty()) {
+ newKeyPattern.addBibtexKeyPattern(item.getEntryType(), patternString);
+ }
+ }
+ });
+
+ if (!defaultItem.getPattern().trim().isEmpty()) {
+ // we do not trim the value at the assignment to enable users to have spaces at the beginning and
+ // at the end of the pattern
+ newKeyPattern.setDefaultValue(defaultItemProperty.getPattern());
+ }
+ } */
+
}
}
diff --git a/src/main/java/org/jabref/gui/bibtexkeypattern/BibtexKeyPatternPanel.java b/src/main/java/org/jabref/gui/bibtexkeypattern/BibtexKeyPatternPanel.java
index 34c1fd863b6..7a5123476bb 100644
--- a/src/main/java/org/jabref/gui/bibtexkeypattern/BibtexKeyPatternPanel.java
+++ b/src/main/java/org/jabref/gui/bibtexkeypattern/BibtexKeyPatternPanel.java
@@ -19,7 +19,6 @@
import org.jabref.logic.l10n.Localization;
import org.jabref.model.bibtexkeypattern.AbstractBibtexKeyPattern;
import org.jabref.model.bibtexkeypattern.DatabaseBibtexKeyPattern;
-import org.jabref.model.bibtexkeypattern.GlobalBibtexKeyPattern;
import org.jabref.model.database.BibDatabaseMode;
import org.jabref.model.entry.BibEntryType;
import org.jabref.model.entry.types.EntryType;
@@ -118,12 +117,6 @@ private void buildGUI() {
gridPane.add(btnDefaultAll1, 2, rowIndex);
}
- protected GlobalBibtexKeyPattern getKeyPatternAsGlobalBibtexKeyPattern() {
- GlobalBibtexKeyPattern res = GlobalBibtexKeyPattern.fromPattern(Globals.prefs.get(JabRefPreferences.DEFAULT_BIBTEX_KEY_PATTERN));
- fillPatternUsingPanelData(res);
- return res;
- }
-
public DatabaseBibtexKeyPattern getKeyPatternAsDatabaseBibtexKeyPattern() {
DatabaseBibtexKeyPattern res = new DatabaseBibtexKeyPattern(Globals.prefs.getKeyPattern());
fillPatternUsingPanelData(res);
diff --git a/src/main/java/org/jabref/gui/bibtexkeypattern/BibtexKeyPatternTable.fxml b/src/main/java/org/jabref/gui/bibtexkeypattern/BibtexKeyPatternTable.fxml
new file mode 100644
index 00000000000..a905a82060b
--- /dev/null
+++ b/src/main/java/org/jabref/gui/bibtexkeypattern/BibtexKeyPatternTable.fxml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/java/org/jabref/gui/bibtexkeypattern/BibtexKeyPatternTableItemModel.java b/src/main/java/org/jabref/gui/bibtexkeypattern/BibtexKeyPatternTableItemModel.java
new file mode 100644
index 00000000000..7431346de46
--- /dev/null
+++ b/src/main/java/org/jabref/gui/bibtexkeypattern/BibtexKeyPatternTableItemModel.java
@@ -0,0 +1,39 @@
+package org.jabref.gui.bibtexkeypattern;
+
+import java.util.Objects;
+
+import javafx.beans.property.ObjectProperty;
+import javafx.beans.property.SimpleObjectProperty;
+import javafx.beans.property.SimpleStringProperty;
+import javafx.beans.property.StringProperty;
+
+import org.jabref.model.entry.types.EntryType;
+
+public class BibtexKeyPatternTableItemModel {
+ private final ObjectProperty entryType = new SimpleObjectProperty<>();
+ private final StringProperty pattern = new SimpleStringProperty("");
+
+ public BibtexKeyPatternTableItemModel(EntryType entryType, String pattern) {
+ Objects.requireNonNull(entryType);
+ Objects.requireNonNull(pattern);
+ this.entryType.setValue(entryType);
+ this.pattern.setValue(pattern);
+ }
+
+ public EntryType getEntryType() { return entryType.getValue(); }
+
+ public ObjectProperty entryType() { return entryType; }
+
+ public void setPattern(String pattern) {
+ this.pattern.setValue(pattern);
+ }
+
+ public String getPattern() {
+ return pattern.getValue();
+ }
+
+ public StringProperty pattern() { return pattern; }
+
+ @Override
+ public String toString() { return "[" + entryType.getValue().getName() + "," + pattern.getValue() + "]"; }
+}
diff --git a/src/main/java/org/jabref/gui/bibtexkeypattern/BibtexKeyPatternTableView.java b/src/main/java/org/jabref/gui/bibtexkeypattern/BibtexKeyPatternTableView.java
new file mode 100644
index 00000000000..124e8b8c48c
--- /dev/null
+++ b/src/main/java/org/jabref/gui/bibtexkeypattern/BibtexKeyPatternTableView.java
@@ -0,0 +1,123 @@
+package org.jabref.gui.bibtexkeypattern;
+
+import java.util.Collection;
+
+import javafx.beans.property.ListProperty;
+import javafx.beans.property.ObjectProperty;
+import javafx.fxml.FXML;
+import javafx.scene.control.TableColumn;
+import javafx.scene.control.TableRow;
+import javafx.scene.control.TableView;
+import javafx.scene.control.cell.TextFieldTableCell;
+import javafx.scene.input.KeyEvent;
+
+import org.jabref.gui.icon.IconTheme;
+import org.jabref.gui.util.ValueTableCellFactory;
+import org.jabref.logic.l10n.Localization;
+import org.jabref.model.bibtexkeypattern.AbstractBibtexKeyPattern;
+import org.jabref.model.entry.BibEntryType;
+import org.jabref.model.entry.types.EntryType;
+import org.jabref.preferences.JabRefPreferences;
+
+import com.airhacks.afterburner.views.ViewLoader;
+
+public class BibtexKeyPatternTableView extends TableView {
+
+ @FXML public TableColumn entryTypeColumn;
+ @FXML public TableColumn patternColumn;
+ @FXML public TableColumn actionsColumn;
+
+ private BibtexKeyPatternTableViewModel viewModel;
+
+ private long lastKeyPressTime;
+ private String tableSearchTerm;
+
+ public BibtexKeyPatternTableView(JabRefPreferences preferences, Collection entryTypeList, AbstractBibtexKeyPattern keyPattern) {
+ super();
+
+ viewModel = new BibtexKeyPatternTableViewModel(preferences, entryTypeList, keyPattern);
+
+ ViewLoader.view(this)
+ .root(this)
+ .load();
+ }
+
+ @FXML
+ private void initialize() {
+ this.setEditable(true);
+
+ entryTypeColumn.setSortable(true);
+ entryTypeColumn.setReorderable(false);
+ entryTypeColumn.setCellValueFactory(cellData -> cellData.getValue().entryType());
+ new ValueTableCellFactory()
+ .withText(EntryType::getDisplayName)
+ .install(entryTypeColumn);
+ this.setOnSort(event ->
+ viewModel.patternListProperty().sort(BibtexKeyPatternTableViewModel.defaultOnTopComparator));
+
+ patternColumn.setSortable(true);
+ patternColumn.setReorderable(false);
+ patternColumn.setCellValueFactory(cellData -> cellData.getValue().pattern());
+ patternColumn.setCellFactory(TextFieldTableCell.forTableColumn());
+ patternColumn.setEditable(true);
+ patternColumn.setOnEditCommit(
+ (TableColumn.CellEditEvent event) ->
+ event.getRowValue().setPattern(event.getNewValue()));
+
+ actionsColumn.setSortable(false);
+ actionsColumn.setReorderable(false);
+ actionsColumn.setCellValueFactory(cellData -> cellData.getValue().entryType());
+ new ValueTableCellFactory()
+ .withGraphic(entryType -> IconTheme.JabRefIcons.REFRESH.getGraphicNode())
+ .withTooltip(entryType ->
+ String.format(Localization.lang("Reset %s to default value"), entryType.getDisplayName()))
+ .withOnMouseClickedEvent(item -> evt ->
+ viewModel.setItemToDefaultPattern(this.getFocusModel().getFocusedItem()))
+ .install(actionsColumn);
+
+ this.setRowFactory(item -> new HighlightTableRow());
+ this.setOnKeyTyped(this::jumpToSearchKey);
+ this.itemsProperty().bindBidirectional(viewModel.patternListProperty());
+ }
+
+ public void setValues() { viewModel.setValues(); }
+
+ public void resetAll() { viewModel.resetAll(); }
+
+ public ListProperty patternListProperty() { return viewModel.patternListProperty(); }
+
+ public ObjectProperty defaultKeyPatternProperty() { return viewModel.defaultKeyPatternProperty(); }
+
+ private void jumpToSearchKey(KeyEvent keypressed) {
+ if (keypressed.getCharacter() == null) {
+ return;
+ }
+
+ if (System.currentTimeMillis() - lastKeyPressTime < 1000) {
+ tableSearchTerm += keypressed.getCharacter().toLowerCase();
+ } else {
+ tableSearchTerm = keypressed.getCharacter().toLowerCase();
+ }
+
+ lastKeyPressTime = System.currentTimeMillis();
+
+ this.getItems().stream().filter(item -> item.getEntryType().getName().toLowerCase().startsWith(tableSearchTerm))
+ .findFirst().ifPresent(this::scrollTo);
+ }
+
+ private static class HighlightTableRow extends TableRow {
+ @Override
+ public void updateItem(BibtexKeyPatternTableItemModel item, boolean empty) {
+ super.updateItem(item, empty);
+ if (item == null || item.getEntryType() == null) {
+ setStyle("");
+ } else if (isSelected()) {
+ setStyle("-fx-background-color: -fx-selection-bar");
+ } else if (item.getEntryType().getName().equals(BibtexKeyPatternTableViewModel.ENTRY_TYPE_DEFAULT_NAME)) {
+ setStyle("-fx-background-color: -fx-default-button");
+ } else {
+ setStyle("");
+ }
+ }
+ }
+}
diff --git a/src/main/java/org/jabref/gui/bibtexkeypattern/BibtexKeyPatternTableViewModel.java b/src/main/java/org/jabref/gui/bibtexkeypattern/BibtexKeyPatternTableViewModel.java
new file mode 100644
index 00000000000..971347b4b3f
--- /dev/null
+++ b/src/main/java/org/jabref/gui/bibtexkeypattern/BibtexKeyPatternTableViewModel.java
@@ -0,0 +1,94 @@
+package org.jabref.gui.bibtexkeypattern;
+
+import java.util.Collection;
+import java.util.Comparator;
+
+import javafx.beans.property.ListProperty;
+import javafx.beans.property.ObjectProperty;
+import javafx.beans.property.SimpleListProperty;
+import javafx.beans.property.SimpleObjectProperty;
+import javafx.collections.FXCollections;
+
+import org.jabref.logic.l10n.Localization;
+import org.jabref.model.bibtexkeypattern.AbstractBibtexKeyPattern;
+import org.jabref.model.entry.BibEntryType;
+import org.jabref.model.entry.types.EntryType;
+import org.jabref.preferences.JabRefPreferences;
+
+public class BibtexKeyPatternTableViewModel {
+
+ public static final String ENTRY_TYPE_DEFAULT_NAME = "default";
+
+ public static Comparator defaultOnTopComparator = (o1, o2) -> {
+ String itemOneName = o1.getEntryType().getName();
+ String itemTwoName = o2.getEntryType().getName();
+
+ if (itemOneName.equals(itemTwoName)) {
+ return 0;
+ } else if (itemOneName.equals(ENTRY_TYPE_DEFAULT_NAME)) {
+ return -1;
+ } else if (itemTwoName.equals(ENTRY_TYPE_DEFAULT_NAME)) {
+ return 1;
+ }
+
+ return 0;
+ };
+
+ private final ListProperty patternListProperty = new SimpleListProperty<>();
+ private final ObjectProperty defaultItemProperty = new SimpleObjectProperty<>();
+ private final AbstractBibtexKeyPattern initialKeyPattern;
+ private final Collection bibEntryTypeList;
+ private final JabRefPreferences preferences;
+
+ public BibtexKeyPatternTableViewModel(JabRefPreferences preferences, Collection entryTypeList, AbstractBibtexKeyPattern initialKeyPattern) {
+ this.preferences = preferences;
+ this.bibEntryTypeList = entryTypeList;
+ this.initialKeyPattern = initialKeyPattern;
+ }
+
+ public void setValues() {
+ String defaultPattern;
+ if ((initialKeyPattern.getDefaultValue() == null) || initialKeyPattern.getDefaultValue().isEmpty()) {
+ defaultPattern = "";
+ } else {
+ defaultPattern = initialKeyPattern.getDefaultValue().get(0);
+ }
+
+ defaultItemProperty.setValue(new BibtexKeyPatternTableItemModel(new DefaultEntryType(), defaultPattern));
+ patternListProperty.setValue(FXCollections.observableArrayList());
+ patternListProperty.add(defaultItemProperty.getValue());
+
+ bibEntryTypeList.stream()
+ .map(BibEntryType::getType)
+ .forEach(entryType -> {
+ String pattern;
+ if (initialKeyPattern.isDefaultValue(entryType)) {
+ pattern = "";
+ } else {
+ pattern = initialKeyPattern.getPatterns().get(entryType).get(0);
+ }
+ patternListProperty.add(new BibtexKeyPatternTableItemModel(entryType, pattern));
+ });
+ }
+
+ public void setItemToDefaultPattern(BibtexKeyPatternTableItemModel item) {
+ item.setPattern((String) preferences.defaults.get(JabRefPreferences.DEFAULT_BIBTEX_KEY_PATTERN));
+ }
+
+ public void resetAll() {
+ patternListProperty.forEach(item -> item.setPattern(""));
+ defaultItemProperty.getValue().setPattern((String) preferences.defaults.get(JabRefPreferences.DEFAULT_BIBTEX_KEY_PATTERN));
+ }
+
+ public ListProperty patternListProperty() { return patternListProperty; }
+
+ public ObjectProperty defaultKeyPatternProperty() { return defaultItemProperty; }
+
+ public static class DefaultEntryType implements EntryType {
+ @Override
+ public String getName() { return ENTRY_TYPE_DEFAULT_NAME; }
+
+ @Override
+ public String getDisplayName() { return Localization.lang("Default pattern"); }
+ }
+}
diff --git a/src/main/java/org/jabref/gui/copyfiles/CopyFilesDialogView.java b/src/main/java/org/jabref/gui/copyfiles/CopyFilesDialogView.java
index 52f46bf22d3..e8d6d20f859 100644
--- a/src/main/java/org/jabref/gui/copyfiles/CopyFilesDialogView.java
+++ b/src/main/java/org/jabref/gui/copyfiles/CopyFilesDialogView.java
@@ -5,20 +5,19 @@
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.paint.Color;
-import javafx.scene.text.Text;
+import org.jabref.gui.icon.IconTheme;
+import org.jabref.gui.icon.JabRefIcon;
import org.jabref.gui.util.BaseDialog;
import org.jabref.gui.util.ValueTableCellFactory;
import org.jabref.logic.l10n.Localization;
import com.airhacks.afterburner.views.ViewLoader;
-import de.jensd.fx.glyphs.materialdesignicons.MaterialDesignIcon;
-import de.jensd.fx.glyphs.materialdesignicons.utils.MaterialDesignIconFactory;
public class CopyFilesDialogView extends BaseDialog {
@FXML private TableView tvResult;
- @FXML private TableColumn colStatus;
+ @FXML private TableColumn colStatus;
@FXML private TableColumn colMessage;
@FXML private TableColumn colFile;
private final CopyFilesDialogViewModel viewModel;
@@ -46,16 +45,14 @@ private void setupTable() {
colStatus.setCellValueFactory(cellData -> cellData.getValue().getIcon());
colFile.setCellFactory(new ValueTableCellFactory().withText(item -> item).withTooltip(item -> item));
- colStatus.setCellFactory(new ValueTableCellFactory().withGraphic(item -> {
-
- Text icon = MaterialDesignIconFactory.get().createIcon(item);
- if (item == MaterialDesignIcon.CHECK) {
- icon.setFill(Color.GREEN);
+ colStatus.setCellFactory(new ValueTableCellFactory().withGraphic(item -> {
+ if (item == IconTheme.JabRefIcons.CHECK) {
+ item = item.withColor(Color.GREEN);
}
- if (item == MaterialDesignIcon.ALERT) {
- icon.setFill(Color.RED);
+ if (item == IconTheme.JabRefIcons.WARNING) {
+ item = item.withColor(Color.RED);
}
- return icon;
+ return item.getGraphicNode();
}));
tvResult.setItems(viewModel.copyFilesResultListProperty());
diff --git a/src/main/java/org/jabref/gui/copyfiles/CopyFilesResultItemViewModel.java b/src/main/java/org/jabref/gui/copyfiles/CopyFilesResultItemViewModel.java
index 12fce765c84..e48f0685112 100644
--- a/src/main/java/org/jabref/gui/copyfiles/CopyFilesResultItemViewModel.java
+++ b/src/main/java/org/jabref/gui/copyfiles/CopyFilesResultItemViewModel.java
@@ -7,19 +7,20 @@
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
-import de.jensd.fx.glyphs.materialdesignicons.MaterialDesignIcon;
+import org.jabref.gui.icon.IconTheme;
+import org.jabref.gui.icon.JabRefIcon;
public class CopyFilesResultItemViewModel {
private final StringProperty file = new SimpleStringProperty("");
- private final ObjectProperty icon = new SimpleObjectProperty<>(MaterialDesignIcon.ALERT);
+ private final ObjectProperty icon = new SimpleObjectProperty<>(IconTheme.JabRefIcons.WARNING);
private final StringProperty message = new SimpleStringProperty("");
public CopyFilesResultItemViewModel(Path file, boolean success, String message) {
this.file.setValue(file.toString());
this.message.setValue(message);
if (success) {
- this.icon.setValue(MaterialDesignIcon.CHECK);
+ this.icon.setValue(IconTheme.JabRefIcons.CHECK);
}
}
@@ -31,7 +32,7 @@ public StringProperty getMessage() {
return message;
}
- public ObjectProperty getIcon() {
+ public ObjectProperty getIcon() {
return icon;
}
diff --git a/src/main/java/org/jabref/gui/dialogs/AutosaveUIManager.java b/src/main/java/org/jabref/gui/dialogs/AutosaveUIManager.java
index 2ce31738e54..213d8bffb00 100644
--- a/src/main/java/org/jabref/gui/dialogs/AutosaveUIManager.java
+++ b/src/main/java/org/jabref/gui/dialogs/AutosaveUIManager.java
@@ -26,7 +26,7 @@ public AutosaveUIManager(BasePanel panel) {
@Subscribe
public void listen(@SuppressWarnings("unused") AutosaveEvent event) {
try {
- new SaveDatabaseAction(panel, Globals.prefs).save();
+ new SaveDatabaseAction(panel, Globals.prefs, Globals.entryTypesManager).save();
} catch (Throwable e) {
LOGGER.error("Problem occured while saving.", e);
}
diff --git a/src/main/java/org/jabref/gui/entryeditor/DeprecatedFieldsTab.java b/src/main/java/org/jabref/gui/entryeditor/DeprecatedFieldsTab.java
index a6be86e5b8d..c3b7b224a40 100644
--- a/src/main/java/org/jabref/gui/entryeditor/DeprecatedFieldsTab.java
+++ b/src/main/java/org/jabref/gui/entryeditor/DeprecatedFieldsTab.java
@@ -11,19 +11,27 @@
import javafx.scene.control.Tooltip;
-import org.jabref.Globals;
import org.jabref.gui.DialogService;
import org.jabref.gui.autocompleter.SuggestionProviders;
+import org.jabref.gui.externalfiletype.ExternalFileTypes;
import org.jabref.gui.icon.IconTheme;
+import org.jabref.gui.util.TaskExecutor;
+import org.jabref.logic.journals.JournalAbbreviationLoader;
import org.jabref.logic.l10n.Localization;
import org.jabref.model.database.BibDatabaseContext;
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.JabRefPreferences;
public class DeprecatedFieldsTab extends FieldsEditorTab {
- public DeprecatedFieldsTab(BibDatabaseContext databaseContext, SuggestionProviders suggestionProviders, UndoManager undoManager, DialogService dialogService) {
- super(false, databaseContext, suggestionProviders, undoManager, dialogService);
+
+ private final BibEntryTypesManager entryTypesManager;
+
+ public DeprecatedFieldsTab(BibDatabaseContext databaseContext, SuggestionProviders suggestionProviders, UndoManager undoManager, DialogService dialogService, JabRefPreferences preferences, BibEntryTypesManager entryTypesManager, ExternalFileTypes externalFileTypes, TaskExecutor taskExecutor, JournalAbbreviationLoader journalAbbreviationLoader) {
+ super(false, databaseContext, suggestionProviders, undoManager, dialogService, preferences, externalFileTypes, taskExecutor, journalAbbreviationLoader);
+ this.entryTypesManager = entryTypesManager;
setText(Localization.lang("Deprecated fields"));
setTooltip(new Tooltip(Localization.lang("Show deprecated BibTeX fields")));
@@ -32,7 +40,7 @@ public DeprecatedFieldsTab(BibDatabaseContext databaseContext, SuggestionProvide
@Override
protected SortedSet determineFieldsToShow(BibEntry entry) {
- Optional entryType = Globals.entryTypesManager.enrich(entry.getType(), databaseContext.getMode());
+ Optional entryType = entryTypesManager.enrich(entry.getType(), databaseContext.getMode());
if (entryType.isPresent()) {
return entryType.get().getDeprecatedFields()
.stream()
diff --git a/src/main/java/org/jabref/gui/entryeditor/EntryEditor.java b/src/main/java/org/jabref/gui/entryeditor/EntryEditor.java
index dadcad8be9c..02efa83d935 100644
--- a/src/main/java/org/jabref/gui/entryeditor/EntryEditor.java
+++ b/src/main/java/org/jabref/gui/entryeditor/EntryEditor.java
@@ -25,6 +25,7 @@
import javafx.scene.input.TransferMode;
import javafx.scene.layout.BorderPane;
+import org.jabref.Globals;
import org.jabref.gui.BasePanel;
import org.jabref.gui.DialogService;
import org.jabref.gui.GUIGlobals;
@@ -50,7 +51,6 @@
import org.jabref.model.entry.field.Field;
import org.jabref.model.util.FileUpdateMonitor;
import org.jabref.preferences.PreferencesService;
-import org.jabref.preferences.PreviewPreferences;
import com.airhacks.afterburner.views.ViewLoader;
import org.fxmisc.easybind.EasyBind;
@@ -263,22 +263,23 @@ private void navigateToNextEntry() {
}
private List createTabs() {
+ // Preview tab
+ entryEditorTabs.add(new PreviewTab(databaseContext, dialogService, Globals.prefs, ExternalFileTypes.getInstance()));
// Required fields
- entryEditorTabs.add(new RequiredFieldsTab(databaseContext, panel.getSuggestionProviders(), undoManager, dialogService));
+ entryEditorTabs.add(new RequiredFieldsTab(databaseContext, panel.getSuggestionProviders(), undoManager, dialogService, Globals.prefs, Globals.entryTypesManager, ExternalFileTypes.getInstance(), Globals.TASK_EXECUTOR, Globals.journalAbbreviationLoader));
// Optional fields
- entryEditorTabs.add(new OptionalFieldsTab(databaseContext, panel.getSuggestionProviders(), undoManager, dialogService));
- entryEditorTabs.add(new OptionalFields2Tab(databaseContext, panel.getSuggestionProviders(), undoManager, dialogService));
- entryEditorTabs.add(new DeprecatedFieldsTab(databaseContext, panel.getSuggestionProviders(), undoManager, dialogService));
+ entryEditorTabs.add(new OptionalFieldsTab(databaseContext, panel.getSuggestionProviders(), undoManager, dialogService, Globals.prefs, Globals.entryTypesManager, ExternalFileTypes.getInstance(), Globals.TASK_EXECUTOR, Globals.journalAbbreviationLoader));
+ entryEditorTabs.add(new OptionalFields2Tab(databaseContext, panel.getSuggestionProviders(), undoManager, dialogService, Globals.prefs, Globals.entryTypesManager, ExternalFileTypes.getInstance(), Globals.TASK_EXECUTOR, Globals.journalAbbreviationLoader));
+ entryEditorTabs.add(new DeprecatedFieldsTab(databaseContext, panel.getSuggestionProviders(), undoManager, dialogService, Globals.prefs, Globals.entryTypesManager, ExternalFileTypes.getInstance(), Globals.TASK_EXECUTOR, Globals.journalAbbreviationLoader));
// Other fields
- entryEditorTabs.add(new OtherFieldsTab(databaseContext, panel.getSuggestionProviders(), undoManager,
- entryEditorPreferences.getCustomTabFieldNames(), dialogService));
+ entryEditorTabs.add(new OtherFieldsTab(databaseContext, panel.getSuggestionProviders(), undoManager, entryEditorPreferences.getCustomTabFieldNames(), dialogService, Globals.prefs, Globals.entryTypesManager, ExternalFileTypes.getInstance(), Globals.TASK_EXECUTOR, Globals.journalAbbreviationLoader));
// General fields from preferences
for (Map.Entry> tab : entryEditorPreferences.getEntryEditorTabList().entrySet()) {
- entryEditorTabs.add(new UserDefinedFieldsTab(tab.getKey(), tab.getValue(), databaseContext, panel.getSuggestionProviders(), undoManager, dialogService));
+ entryEditorTabs.add(new UserDefinedFieldsTab(tab.getKey(), tab.getValue(), databaseContext, panel.getSuggestionProviders(), undoManager, dialogService, Globals.prefs, Globals.entryTypesManager, ExternalFileTypes.getInstance(), Globals.TASK_EXECUTOR, Globals.journalAbbreviationLoader));
}
// Special tabs
@@ -404,11 +405,11 @@ public void setFocusToField(Field field) {
});
}
- public void updatePreviewInTabs(PreviewPreferences previewPreferences) {
- for (Tab tab : this.entryEditorTabs) {
- if (tab instanceof FieldsEditorTab) {
- ((FieldsEditorTab) tab).previewPanel.updateLayout(previewPreferences);
- }
- }
+ public void nextPreviewStyle() {
+ this.entryEditorTabs.forEach(EntryEditorTab::nextPreviewStyle);
+ }
+
+ public void previousPreviewStyle() {
+ this.entryEditorTabs.forEach(EntryEditorTab::previousPreviewStyle);
}
}
diff --git a/src/main/java/org/jabref/gui/entryeditor/EntryEditorTab.java b/src/main/java/org/jabref/gui/entryeditor/EntryEditorTab.java
index a0b91a914f7..6c5da5ec0f6 100644
--- a/src/main/java/org/jabref/gui/entryeditor/EntryEditorTab.java
+++ b/src/main/java/org/jabref/gui/entryeditor/EntryEditorTab.java
@@ -44,4 +44,18 @@ public void notifyAboutFocus(BibEntry entry) {
handleFocus();
}
+ /**
+ * Switch to next Preview style - should be overriden if a EntryEditorTab is actually showing a preview
+ */
+ protected void nextPreviewStyle() {
+ // do nothing by default
+ }
+
+ /**
+ * Switch to previous Preview style - should be overriden if a EntryEditorTab is actually showing a preview
+ */
+ protected void previousPreviewStyle() {
+ // do nothing by default
+ }
+
}
diff --git a/src/main/java/org/jabref/gui/entryeditor/FieldsEditorTab.java b/src/main/java/org/jabref/gui/entryeditor/FieldsEditorTab.java
index ce226b512f8..603450b39ce 100644
--- a/src/main/java/org/jabref/gui/entryeditor/FieldsEditorTab.java
+++ b/src/main/java/org/jabref/gui/entryeditor/FieldsEditorTab.java
@@ -5,13 +5,12 @@
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
-import java.util.Optional;
+import java.util.Objects;
import java.util.SortedSet;
import java.util.stream.Stream;
import javax.swing.undo.UndoManager;
-import javafx.application.Platform;
import javafx.geometry.VPos;
import javafx.scene.Node;
import javafx.scene.Parent;
@@ -24,7 +23,6 @@
import javafx.scene.layout.Region;
import javafx.scene.layout.RowConstraints;
-import org.jabref.Globals;
import org.jabref.gui.DialogService;
import org.jabref.gui.autocompleter.SuggestionProviders;
import org.jabref.gui.externalfiletype.ExternalFileTypes;
@@ -32,31 +30,41 @@
import org.jabref.gui.fieldeditors.FieldEditors;
import org.jabref.gui.fieldeditors.FieldNameLabel;
import org.jabref.gui.preview.PreviewPanel;
+import org.jabref.gui.util.TaskExecutor;
+import org.jabref.logic.journals.JournalAbbreviationLoader;
import org.jabref.model.database.BibDatabaseContext;
import org.jabref.model.entry.BibEntry;
import org.jabref.model.entry.field.Field;
+import org.jabref.preferences.JabRefPreferences;
/**
* A single tab displayed in the EntryEditor holding several FieldEditors.
*/
abstract class FieldsEditorTab extends EntryEditorTab {
- public PreviewPanel previewPanel;
protected final BibDatabaseContext databaseContext;
private final Map editors = new LinkedHashMap<>();
private final boolean isCompressed;
private final SuggestionProviders suggestionProviders;
private final DialogService dialogService;
- private FieldEditorFX activeField;
+ private final JabRefPreferences preferences;
+ private final ExternalFileTypes externalFileTypes;
+ private final TaskExecutor taskExecutor;
+ private final JournalAbbreviationLoader journalAbbreviationLoader;
+ private PreviewPanel previewPanel;
private UndoManager undoManager;
private Collection fields = new ArrayList<>();
private GridPane gridPane;
- public FieldsEditorTab(boolean compressed, BibDatabaseContext databaseContext, SuggestionProviders suggestionProviders, UndoManager undoManager, DialogService dialogService) {
+ public FieldsEditorTab(boolean compressed, BibDatabaseContext databaseContext, SuggestionProviders suggestionProviders, UndoManager undoManager, DialogService dialogService, JabRefPreferences preferences, ExternalFileTypes externalFileTypes, TaskExecutor taskExecutor, JournalAbbreviationLoader journalAbbreviationLoader) {
this.isCompressed = compressed;
- this.databaseContext = databaseContext;
- this.suggestionProviders = suggestionProviders;
- this.undoManager = undoManager;
- this.dialogService = dialogService;
+ this.databaseContext = Objects.requireNonNull(databaseContext);
+ this.suggestionProviders = Objects.requireNonNull(suggestionProviders);
+ this.undoManager = Objects.requireNonNull(undoManager);
+ this.dialogService = Objects.requireNonNull(dialogService);
+ this.preferences = Objects.requireNonNull(preferences);
+ this.externalFileTypes = Objects.requireNonNull(externalFileTypes);
+ this.taskExecutor = Objects.requireNonNull(taskExecutor);
+ this.journalAbbreviationLoader = Objects.requireNonNull(journalAbbreviationLoader);
}
private static void addColumn(GridPane gridPane, int columnIndex, List nodes) {
@@ -67,10 +75,10 @@ private static void addColumn(GridPane gridPane, int columnIndex, Stream
gridPane.addColumn(columnIndex, nodes.toArray(Node[]::new));
}
- private void setupPanel(BibEntry entry, boolean compressed, SuggestionProviders suggestionProviders, UndoManager undoManager) {
+ private void setupPanel(BibEntry entry, boolean compressed) {
// The preferences might be not initialized in tests -> return immediately
// TODO: Replace this ugly workaround by proper injection propagation
- if (Globals.prefs == null) {
+ if (preferences == null) {
return;
}
@@ -82,19 +90,13 @@ private void setupPanel(BibEntry entry, boolean compressed, SuggestionProviders
fields = determineFieldsToShow(entry);
List labels = new ArrayList<>();
- boolean isFirstField = true;
for (Field field : fields) {
- FieldEditorFX fieldEditor = FieldEditors.getForField(field, Globals.TASK_EXECUTOR, dialogService,
- Globals.journalAbbreviationLoader.getRepository(Globals.prefs.getJournalAbbreviationPreferences()),
- Globals.prefs, databaseContext, entry.getType(), suggestionProviders, undoManager);
+ FieldEditorFX fieldEditor = FieldEditors.getForField(field, taskExecutor, dialogService,
+ journalAbbreviationLoader.getRepository(preferences.getJournalAbbreviationPreferences()),
+ preferences, databaseContext, entry.getType(), suggestionProviders, undoManager);
fieldEditor.bindToEntry(entry);
editors.put(field, fieldEditor);
- if (isFirstField) {
- activeField = fieldEditor;
- isFirstField = false;
- }
-
labels.add(new FieldNameLabel(field));
}
@@ -161,8 +163,7 @@ private void setCompressedRowLayout(GridPane gridPane, int rows) {
*/
public void requestFocus(Field fieldName) {
if (editors.containsKey(fieldName)) {
- activeField = editors.get(fieldName);
- activeField.focus();
+ editors.get(fieldName).focus();
}
}
@@ -172,29 +173,27 @@ public boolean shouldShow(BibEntry entry) {
}
@Override
- public void handleFocus() {
- if (activeField != null) {
- activeField.focus();
+ protected void bindToEntry(BibEntry entry) {
+ initPanel();
+ setupPanel(entry, isCompressed);
+
+ if (previewPanel != null) {
+ previewPanel.setEntry(entry);
}
}
@Override
- protected void bindToEntry(BibEntry entry) {
- Optional selectedFieldName = editors.entrySet()
- .stream()
- .filter(editor -> editor.getValue().childIsFocused())
- .map(Map.Entry::getKey)
- .findFirst();
-
- initPanel();
- setupPanel(entry, isCompressed, suggestionProviders, undoManager);
-
- previewPanel.setEntry(entry);
+ protected void nextPreviewStyle() {
+ if (previewPanel != null) {
+ previewPanel.nextPreviewStyle();
+ }
+ }
- Platform.runLater(() -> {
- // Restore focus to field (run this async so that editor is already initialized correctly)
- selectedFieldName.ifPresent(this::requestFocus);
- });
+ @Override
+ protected void previousPreviewStyle() {
+ if (previewPanel != null) {
+ previewPanel.previousPreviewStyle();
+ }
}
protected abstract SortedSet determineFieldsToShow(BibEntry entry);
@@ -208,8 +207,6 @@ private void initPanel() {
gridPane = new GridPane();
gridPane.getStyleClass().add("editorPane");
- previewPanel = new PreviewPanel(databaseContext, null, dialogService, ExternalFileTypes.getInstance(), Globals.getKeyPrefs(), Globals.prefs.getPreviewPreferences());
-
// Warp everything in a scroll-pane
ScrollPane scrollPane = new ScrollPane();
scrollPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
@@ -218,7 +215,11 @@ private void initPanel() {
scrollPane.setFitToWidth(true);
scrollPane.setFitToHeight(true);
- SplitPane container = new SplitPane(scrollPane, previewPanel);
+ SplitPane container = new SplitPane(scrollPane);
+ if (!preferences.getPreviewPreferences().showPreviewAsExtraTab()) {
+ previewPanel = new PreviewPanel(databaseContext, dialogService, externalFileTypes, preferences.getKeyBindingRepository(), preferences);
+ container.getItems().add(previewPanel);
+ }
setContent(container);
}
diff --git a/src/main/java/org/jabref/gui/entryeditor/OptionalFields2Tab.java b/src/main/java/org/jabref/gui/entryeditor/OptionalFields2Tab.java
index 13305dad0b4..7553bc46496 100644
--- a/src/main/java/org/jabref/gui/entryeditor/OptionalFields2Tab.java
+++ b/src/main/java/org/jabref/gui/entryeditor/OptionalFields2Tab.java
@@ -8,19 +8,27 @@
import javafx.scene.control.Tooltip;
-import org.jabref.Globals;
import org.jabref.gui.DialogService;
import org.jabref.gui.autocompleter.SuggestionProviders;
+import org.jabref.gui.externalfiletype.ExternalFileTypes;
import org.jabref.gui.icon.IconTheme;
+import org.jabref.gui.util.TaskExecutor;
+import org.jabref.logic.journals.JournalAbbreviationLoader;
import org.jabref.logic.l10n.Localization;
import org.jabref.model.database.BibDatabaseContext;
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.JabRefPreferences;
public class OptionalFields2Tab extends FieldsEditorTab {
- public OptionalFields2Tab(BibDatabaseContext databaseContext, SuggestionProviders suggestionProviders, UndoManager undoManager, DialogService dialogService) {
- super(true, databaseContext, suggestionProviders, undoManager, dialogService);
+
+ private final BibEntryTypesManager entryTypesManager;
+
+ public OptionalFields2Tab(BibDatabaseContext databaseContext, SuggestionProviders suggestionProviders, UndoManager undoManager, DialogService dialogService, JabRefPreferences preferences, BibEntryTypesManager entryTypesManager, ExternalFileTypes externalFileTypes, TaskExecutor taskExecutor, JournalAbbreviationLoader journalAbbreviationLoader) {
+ super(true, databaseContext, suggestionProviders, undoManager, dialogService, preferences, externalFileTypes, taskExecutor, journalAbbreviationLoader);
+ this.entryTypesManager = entryTypesManager;
setText(Localization.lang("Optional fields 2"));
setTooltip(new Tooltip(Localization.lang("Show optional fields")));
@@ -29,7 +37,7 @@ public OptionalFields2Tab(BibDatabaseContext databaseContext, SuggestionProvider
@Override
protected SortedSet determineFieldsToShow(BibEntry entry) {
- Optional entryType = Globals.entryTypesManager.enrich(entry.getType(), databaseContext.getMode());
+ Optional entryType = entryTypesManager.enrich(entry.getType(), databaseContext.getMode());
if (entryType.isPresent()) {
return entryType.get().getSecondaryOptionalNotDeprecatedFields();
} else {
diff --git a/src/main/java/org/jabref/gui/entryeditor/OptionalFieldsTab.java b/src/main/java/org/jabref/gui/entryeditor/OptionalFieldsTab.java
index ab78b7e6677..805105f956a 100644
--- a/src/main/java/org/jabref/gui/entryeditor/OptionalFieldsTab.java
+++ b/src/main/java/org/jabref/gui/entryeditor/OptionalFieldsTab.java
@@ -11,20 +11,27 @@
import javafx.scene.control.Tooltip;
-import org.jabref.Globals;
import org.jabref.gui.DialogService;
import org.jabref.gui.autocompleter.SuggestionProviders;
+import org.jabref.gui.externalfiletype.ExternalFileTypes;
import org.jabref.gui.icon.IconTheme;
+import org.jabref.gui.util.TaskExecutor;
+import org.jabref.logic.journals.JournalAbbreviationLoader;
import org.jabref.logic.l10n.Localization;
import org.jabref.model.database.BibDatabaseContext;
import org.jabref.model.entry.BibEntry;
import org.jabref.model.entry.BibEntryType;
+import org.jabref.model.entry.BibEntryTypesManager;
import org.jabref.model.entry.field.BibField;
import org.jabref.model.entry.field.Field;
+import org.jabref.preferences.JabRefPreferences;
public class OptionalFieldsTab extends FieldsEditorTab {
- public OptionalFieldsTab(BibDatabaseContext databaseContext, SuggestionProviders suggestionProviders, UndoManager undoManager, DialogService dialogService) {
- super(true, databaseContext, suggestionProviders, undoManager, dialogService);
+ private final BibEntryTypesManager entryTypesManager;
+
+ public OptionalFieldsTab(BibDatabaseContext databaseContext, SuggestionProviders suggestionProviders, UndoManager undoManager, DialogService dialogService, JabRefPreferences preferences, BibEntryTypesManager entryTypesManager, ExternalFileTypes externalFileTypes, TaskExecutor taskExecutor, JournalAbbreviationLoader journalAbbreviationLoader) {
+ super(true, databaseContext, suggestionProviders, undoManager, dialogService, preferences, externalFileTypes, taskExecutor, journalAbbreviationLoader);
+ this.entryTypesManager = entryTypesManager;
setText(Localization.lang("Optional fields"));
setTooltip(new Tooltip(Localization.lang("Show optional fields")));
@@ -33,7 +40,7 @@ public OptionalFieldsTab(BibDatabaseContext databaseContext, SuggestionProviders
@Override
protected SortedSet determineFieldsToShow(BibEntry entry) {
- Optional entryType = Globals.entryTypesManager.enrich(entry.getType(), databaseContext.getMode());
+ Optional entryType = entryTypesManager.enrich(entry.getType(), databaseContext.getMode());
if (entryType.isPresent()) {
return entryType.get().getPrimaryOptionalFields().stream().map(BibField::getField).collect(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(Field::getName))));
} else {
diff --git a/src/main/java/org/jabref/gui/entryeditor/OtherFieldsTab.java b/src/main/java/org/jabref/gui/entryeditor/OtherFieldsTab.java
index fd02cc2da74..cf4a9b8323d 100644
--- a/src/main/java/org/jabref/gui/entryeditor/OtherFieldsTab.java
+++ b/src/main/java/org/jabref/gui/entryeditor/OtherFieldsTab.java
@@ -13,34 +13,40 @@
import javafx.scene.control.Tooltip;
-import org.jabref.Globals;
import org.jabref.gui.DialogService;
import org.jabref.gui.autocompleter.SuggestionProviders;
+import org.jabref.gui.externalfiletype.ExternalFileTypes;
import org.jabref.gui.icon.IconTheme;
+import org.jabref.gui.util.TaskExecutor;
+import org.jabref.logic.journals.JournalAbbreviationLoader;
import org.jabref.logic.l10n.Localization;
import org.jabref.model.database.BibDatabaseContext;
import org.jabref.model.entry.BibEntry;
import org.jabref.model.entry.BibEntryType;
+import org.jabref.model.entry.BibEntryTypesManager;
import org.jabref.model.entry.field.BibField;
import org.jabref.model.entry.field.Field;
import org.jabref.model.entry.field.InternalField;
+import org.jabref.preferences.JabRefPreferences;
public class OtherFieldsTab extends FieldsEditorTab {
private final List customTabFieldNames;
+ private final BibEntryTypesManager entryTypesManager;
- public OtherFieldsTab(BibDatabaseContext databaseContext, SuggestionProviders suggestionProviders, UndoManager undoManager, List customTabFieldNames, DialogService dialogService) {
- super(false, databaseContext, suggestionProviders, undoManager, dialogService);
+ public OtherFieldsTab(BibDatabaseContext databaseContext, SuggestionProviders suggestionProviders, UndoManager undoManager, List customTabFieldNames, DialogService dialogService, JabRefPreferences preferences, BibEntryTypesManager entryTypesManager, ExternalFileTypes externalFileTypes, TaskExecutor taskExecutor, JournalAbbreviationLoader journalAbbreviationLoader) {
+ super(false, databaseContext, suggestionProviders, undoManager, dialogService, preferences, externalFileTypes, taskExecutor, journalAbbreviationLoader);
+ this.entryTypesManager = entryTypesManager;
+ this.customTabFieldNames = customTabFieldNames;
setText(Localization.lang("Other fields"));
setTooltip(new Tooltip(Localization.lang("Show remaining fields")));
setGraphic(IconTheme.JabRefIcons.OPTIONAL.getGraphicNode());
- this.customTabFieldNames = customTabFieldNames;
}
@Override
protected SortedSet determineFieldsToShow(BibEntry entry) {
- Optional entryType = Globals.entryTypesManager.enrich(entry.getType(), databaseContext.getMode());
+ Optional entryType = entryTypesManager.enrich(entry.getType(), databaseContext.getMode());
if (entryType.isPresent()) {
Set allKnownFields = entryType.get().getAllFields().stream().map(BibField::getField).collect(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(Field::getName))));
SortedSet otherFields = entry.getFields().stream().filter(field -> !allKnownFields.contains(field)).collect(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(Field::getName))));
diff --git a/src/main/java/org/jabref/gui/entryeditor/PreviewTab.java b/src/main/java/org/jabref/gui/entryeditor/PreviewTab.java
new file mode 100644
index 00000000000..35d3a197501
--- /dev/null
+++ b/src/main/java/org/jabref/gui/entryeditor/PreviewTab.java
@@ -0,0 +1,55 @@
+package org.jabref.gui.entryeditor;
+
+import org.jabref.gui.DialogService;
+import org.jabref.gui.externalfiletype.ExternalFileTypes;
+import org.jabref.gui.icon.IconTheme;
+import org.jabref.gui.preview.PreviewPanel;
+import org.jabref.model.database.BibDatabaseContext;
+import org.jabref.model.entry.BibEntry;
+import org.jabref.preferences.JabRefPreferences;
+
+public class PreviewTab extends EntryEditorTab {
+ private final DialogService dialogService;
+ private final BibDatabaseContext databaseContext;
+ private final JabRefPreferences preferences;
+ private final ExternalFileTypes externalFileTypes;
+ private PreviewPanel previewPanel;
+
+ public PreviewTab(BibDatabaseContext databaseContext, DialogService dialogService, JabRefPreferences preferences, ExternalFileTypes externalFileTypes) {
+ this.databaseContext = databaseContext;
+ this.dialogService = dialogService;
+ this.preferences = preferences;
+ this.externalFileTypes = externalFileTypes;
+
+ setGraphic(IconTheme.JabRefIcons.TOGGLE_ENTRY_PREVIEW.getGraphicNode());
+ }
+
+ @Override
+ protected void nextPreviewStyle() {
+ if (previewPanel != null) {
+ previewPanel.nextPreviewStyle();
+ }
+ }
+
+ @Override
+ protected void previousPreviewStyle() {
+ if (previewPanel != null) {
+ previewPanel.previousPreviewStyle();
+ }
+ }
+
+ @Override
+ public boolean shouldShow(BibEntry entry) {
+ return preferences.getPreviewPreferences().showPreviewAsExtraTab();
+ }
+
+ @Override
+ protected void bindToEntry(BibEntry entry) {
+ if (previewPanel == null) {
+ previewPanel = new PreviewPanel(databaseContext, dialogService, externalFileTypes, preferences.getKeyBindingRepository(), preferences);
+ setContent(previewPanel);
+ }
+
+ previewPanel.setEntry(entry);
+ }
+}
diff --git a/src/main/java/org/jabref/gui/entryeditor/RequiredFieldsTab.java b/src/main/java/org/jabref/gui/entryeditor/RequiredFieldsTab.java
index 04cec9c657d..2378f64c4ca 100644
--- a/src/main/java/org/jabref/gui/entryeditor/RequiredFieldsTab.java
+++ b/src/main/java/org/jabref/gui/entryeditor/RequiredFieldsTab.java
@@ -10,21 +10,28 @@
import javafx.scene.control.Tooltip;
-import org.jabref.Globals;
import org.jabref.gui.DialogService;
import org.jabref.gui.autocompleter.SuggestionProviders;
+import org.jabref.gui.externalfiletype.ExternalFileTypes;
import org.jabref.gui.icon.IconTheme;
+import org.jabref.gui.util.TaskExecutor;
+import org.jabref.logic.journals.JournalAbbreviationLoader;
import org.jabref.logic.l10n.Localization;
import org.jabref.model.database.BibDatabaseContext;
import org.jabref.model.entry.BibEntry;
import org.jabref.model.entry.BibEntryType;
+import org.jabref.model.entry.BibEntryTypesManager;
import org.jabref.model.entry.field.Field;
import org.jabref.model.entry.field.InternalField;
import org.jabref.model.entry.field.OrFields;
+import org.jabref.preferences.JabRefPreferences;
public class RequiredFieldsTab extends FieldsEditorTab {
- public RequiredFieldsTab(BibDatabaseContext databaseContext, SuggestionProviders suggestionProviders, UndoManager undoManager, DialogService dialogService) {
- super(false, databaseContext, suggestionProviders, undoManager, dialogService);
+ private final BibEntryTypesManager entryTypesManager;
+
+ public RequiredFieldsTab(BibDatabaseContext databaseContext, SuggestionProviders suggestionProviders, UndoManager undoManager, DialogService dialogService, JabRefPreferences preferences, BibEntryTypesManager entryTypesManager, ExternalFileTypes externalFileTypes, TaskExecutor taskExecutor, JournalAbbreviationLoader journalAbbreviationLoader) {
+ super(false, databaseContext, suggestionProviders, undoManager, dialogService, preferences, externalFileTypes, taskExecutor, journalAbbreviationLoader);
+ this.entryTypesManager = entryTypesManager;
setText(Localization.lang("Required fields"));
setTooltip(new Tooltip(Localization.lang("Show required fields")));
@@ -33,7 +40,7 @@ public RequiredFieldsTab(BibDatabaseContext databaseContext, SuggestionProviders
@Override
protected SortedSet determineFieldsToShow(BibEntry entry) {
- Optional entryType = Globals.entryTypesManager.enrich(entry.getType(), databaseContext.getMode());
+ Optional entryType = entryTypesManager.enrich(entry.getType(), databaseContext.getMode());
SortedSet fields = new TreeSet<>(Comparator.comparing(Field::getName));
if (entryType.isPresent()) {
fields.addAll(entryType.get().getRequiredFields().stream().map(OrFields::getPrimary).collect(Collectors.toSet()));
diff --git a/src/main/java/org/jabref/gui/entryeditor/UserDefinedFieldsTab.java b/src/main/java/org/jabref/gui/entryeditor/UserDefinedFieldsTab.java
index e602dbff242..f9275df0566 100644
--- a/src/main/java/org/jabref/gui/entryeditor/UserDefinedFieldsTab.java
+++ b/src/main/java/org/jabref/gui/entryeditor/UserDefinedFieldsTab.java
@@ -9,16 +9,22 @@
import org.jabref.gui.DialogService;
import org.jabref.gui.autocompleter.SuggestionProviders;
+import org.jabref.gui.externalfiletype.ExternalFileTypes;
import org.jabref.gui.icon.IconTheme;
+import org.jabref.gui.util.TaskExecutor;
+import org.jabref.logic.journals.JournalAbbreviationLoader;
import org.jabref.model.database.BibDatabaseContext;
import org.jabref.model.entry.BibEntry;
+import org.jabref.model.entry.BibEntryTypesManager;
import org.jabref.model.entry.field.Field;
+import org.jabref.preferences.JabRefPreferences;
public class UserDefinedFieldsTab extends FieldsEditorTab {
private final SortedSet fields;
- public UserDefinedFieldsTab(String name, Set fields, BibDatabaseContext databaseContext, SuggestionProviders suggestionProviders, UndoManager undoManager, DialogService dialogService) {
- super(false, databaseContext, suggestionProviders, undoManager, dialogService);
+ public UserDefinedFieldsTab(String name, Set fields, BibDatabaseContext databaseContext, SuggestionProviders suggestionProviders, UndoManager undoManager, DialogService dialogService, JabRefPreferences preferences, BibEntryTypesManager entryTypesManager, ExternalFileTypes externalFileTypes, TaskExecutor taskExecutor, JournalAbbreviationLoader journalAbbreviationLoader) {
+ super(false, databaseContext, suggestionProviders, undoManager, dialogService, preferences, externalFileTypes, taskExecutor, journalAbbreviationLoader);
+
this.fields = new TreeSet<>(Comparator.comparing(Field::getName));
this.fields.addAll(fields);
diff --git a/src/main/java/org/jabref/gui/exporter/ExportToClipboardAction.java b/src/main/java/org/jabref/gui/exporter/ExportToClipboardAction.java
index 122b5baf610..94b63150d15 100644
--- a/src/main/java/org/jabref/gui/exporter/ExportToClipboardAction.java
+++ b/src/main/java/org/jabref/gui/exporter/ExportToClipboardAction.java
@@ -5,6 +5,7 @@
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
@@ -20,6 +21,7 @@
import org.jabref.gui.util.BackgroundTask;
import org.jabref.logic.exporter.Exporter;
import org.jabref.logic.l10n.Localization;
+import org.jabref.logic.util.FileType;
import org.jabref.logic.util.OS;
import org.jabref.model.entry.BibEntry;
import org.jabref.preferences.JabRefPreferences;
@@ -31,6 +33,9 @@ public class ExportToClipboardAction extends SimpleCommand {
private static final Logger LOGGER = LoggerFactory.getLogger(ExportToClipboardAction.class);
+ // Only text based exporters can be used
+ private static final List SUPPORTED_FILETYPES = Arrays.asList("txt", "rtf", "rdf", "xml", "html", "htm", "csv", "ris");
+
private JabRefFrame frame;
private final DialogService dialogService;
private BasePanel panel;
@@ -59,6 +64,7 @@ public void execute() {
List exporters = Globals.exportFactory.getExporters().stream()
.sorted(Comparator.comparing(Exporter::getName))
+ .filter(exporter -> SUPPORTED_FILETYPES.containsAll(exporter.getFileType().getExtensions()))
.collect(Collectors.toList());
//Find default choice, if any
@@ -71,12 +77,12 @@ public void execute() {
Localization.lang("Export"), defaultChoice, exporters);
selectedExporter.ifPresent(exporter -> BackgroundTask.wrap(() -> exportToClipboard(exporter))
- .onSuccess(this::setContentToClipboard)
- .executeWith(Globals.TASK_EXECUTOR));
-
+ .onSuccess(this::setContentToClipboard)
+ .onFailure(ex -> { /* swallow as already logged */ })
+ .executeWith(Globals.TASK_EXECUTOR));
}
- private String exportToClipboard(Exporter exporter) {
+ private ExportResult exportToClipboard(Exporter exporter) throws Exception {
// Set the global variable for this database's file directory before exporting,
// so formatters can resolve linked files correctly.
// (This is an ugly hack!)
@@ -102,9 +108,10 @@ private String exportToClipboard(Exporter exporter) {
entries);
// Read the file and put the contents on the clipboard:
- return readFileToString(tmp);
+ return new ExportResult(readFileToString(tmp), exporter.getFileType());
} catch (Exception e) {
LOGGER.error("Error exporting to clipboard", e);
+ throw new Exception("Rethrow ", e);
} finally {
// Clean up:
if ((tmp != null) && Files.exists(tmp)) {
@@ -115,12 +122,19 @@ private String exportToClipboard(Exporter exporter) {
}
}
}
- return "";
}
- private void setContentToClipboard(String content) {
+ private void setContentToClipboard(ExportResult result) {
ClipboardContent clipboardContent = new ClipboardContent();
- clipboardContent.putRtf(content);
+ List extensions = result.fileType.getExtensions();
+ if (extensions.contains("html")) {
+ clipboardContent.putHtml(result.content);
+ } else if (extensions.contains("rtf")) {
+ clipboardContent.putRtf(result.content);
+ } else if (extensions.contains("rdf")) {
+ clipboardContent.putRtf(result.content);
+ }
+ clipboardContent.putString(result.content);
Globals.clipboardManager.setContent(clipboardContent);
dialogService.notify(Localization.lang("Entries exported to clipboard") + ": " + entries.size());
@@ -135,4 +149,14 @@ private String readFileToString(Path tmp) throws IOException {
return reader.lines().collect(Collectors.joining(OS.NEWLINE));
}
}
+
+ private class ExportResult {
+ final String content;
+ final FileType fileType;
+
+ ExportResult(String content, FileType fileType) {
+ this.content = content;
+ this.fileType = fileType;
+ }
+ }
}
diff --git a/src/main/java/org/jabref/gui/exporter/SaveDatabaseAction.java b/src/main/java/org/jabref/gui/exporter/SaveDatabaseAction.java
index fcfcaf4e5a7..22c2828d162 100644
--- a/src/main/java/org/jabref/gui/exporter/SaveDatabaseAction.java
+++ b/src/main/java/org/jabref/gui/exporter/SaveDatabaseAction.java
@@ -14,7 +14,6 @@
import javafx.scene.layout.VBox;
import javafx.scene.text.Text;
-import org.jabref.Globals;
import org.jabref.gui.BasePanel;
import org.jabref.gui.DialogService;
import org.jabref.gui.JabRefFrame;
@@ -56,12 +55,12 @@ public class SaveDatabaseAction {
private final JabRefPreferences prefs;
private final BibEntryTypesManager entryTypesManager;
- public SaveDatabaseAction(BasePanel panel, JabRefPreferences prefs) {
+ public SaveDatabaseAction(BasePanel panel, JabRefPreferences prefs, BibEntryTypesManager entryTypesManager) {
this.panel = panel;
this.frame = panel.frame();
this.dialogService = frame.getDialogService();
this.prefs = prefs;
- entryTypesManager = Globals.entryTypesManager;
+ this.entryTypesManager = entryTypesManager;
}
private boolean saveDatabase(Path file, boolean selectedOnly, Charset encoding, SavePreferences.DatabaseSaveType saveType) throws SaveException {
diff --git a/src/main/java/org/jabref/gui/externalfiles/ImportHandler.java b/src/main/java/org/jabref/gui/externalfiles/ImportHandler.java
index d0fde05997c..365ce487941 100644
--- a/src/main/java/org/jabref/gui/externalfiles/ImportHandler.java
+++ b/src/main/java/org/jabref/gui/externalfiles/ImportHandler.java
@@ -87,6 +87,8 @@ public void importAsNewEntries(List files) {
entriesToAdd = Collections.singletonList(createEmptyEntryWithLink(file));
}
}
+ } else if (FileUtil.isBibFile(file)) {
+ entriesToAdd = contentImporter.importFromBibFile(file, fileUpdateMonitor);
} else {
entriesToAdd = Collections.singletonList(createEmptyEntryWithLink(file));
}
@@ -105,11 +107,6 @@ private BibEntry createEmptyEntryWithLink(Path file) {
return entry;
}
- public void importEntriesFromBibFiles(Path bibFile) {
- List entriesToImport = contentImporter.importFromBibFile(bibFile, fileUpdateMonitor);
- importEntries(entriesToImport);
- }
-
public void importEntries(List entries) {
//TODO: Add undo/redo
//ce.addEdit(new UndoableInsertEntry(panel.getDatabase(), entry));
diff --git a/src/main/java/org/jabref/gui/fieldeditors/EditorTextArea.java b/src/main/java/org/jabref/gui/fieldeditors/EditorTextArea.java
index 08454e72968..13a7f7f6ac3 100644
--- a/src/main/java/org/jabref/gui/fieldeditors/EditorTextArea.java
+++ b/src/main/java/org/jabref/gui/fieldeditors/EditorTextArea.java
@@ -9,16 +9,14 @@
import javafx.fxml.Initializable;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.MenuItem;
-import javafx.scene.control.TextArea;
-import javafx.scene.input.KeyCode;
-import javafx.scene.input.KeyEvent;
-import com.sun.javafx.scene.control.skin.TextAreaSkin;
+import org.jabref.gui.ClipBoardManager;
-public class EditorTextArea extends TextArea implements Initializable, ContextMenuAddable {
+public class EditorTextArea extends javafx.scene.control.TextArea implements Initializable, ContextMenuAddable {
+ private final ContextMenu contextMenu = new ContextMenu();
/**
- * Variable that contains user-defined behavior for paste action.
+ * Variable that contains user-defined behavior for paste action.
*/
private PasteActionHandler pasteActionHandler = () -> {
// Set empty paste behavior by default
@@ -34,37 +32,17 @@ public EditorTextArea(final String text) {
// Hide horizontal scrollbar and always wrap text
setWrapText(true);
- // Should behave as a normal text field with respect to TAB behaviour
- addEventFilter(KeyEvent.KEY_PRESSED, event -> {
- if (event.getCode() == KeyCode.TAB) {
- TextAreaSkin skin = (TextAreaSkin) getSkin();
- if (event.isShiftDown()) {
- // Shift + Tab > previous text area
- skin.getBehavior().traversePrevious();
- } else {
- if (event.isControlDown()) {
- // Ctrl + Tab > insert tab
- skin.getBehavior().callAction("InsertTab");
- } else {
- // Tab > next text area
- skin.getBehavior().traverseNext();
- }
- }
- event.consume();
- }
- });
+ ClipBoardManager.addX11Support(this);
}
@Override
public void addToContextMenu(final Supplier> items) {
- TextAreaSkin customContextSkin = new TextAreaSkin(this) {
- @Override
- public void populateContextMenu(ContextMenu contextMenu) {
- super.populateContextMenu(contextMenu);
- contextMenu.getItems().addAll(0, items.get());
- }
- };
- setSkin(customContextSkin);
+ setOnContextMenuRequested(event -> {
+ contextMenu.getItems().setAll(TextInputControlBehavior.getDefaultContextMenuItems(this));
+ contextMenu.getItems().addAll(0, items.get());
+
+ TextInputControlBehavior.showContextMenu(this, contextMenu, event);
+ });
}
@Override
@@ -74,7 +52,8 @@ public void initialize(URL location, ResourceBundle resources) {
/**
* Set pasteActionHandler variable to passed handler
- * @param handler an instance of PasteActionHandler that describes paste behavior
+ *
+ * @param handler an instance of PasteActionHandler that describes paste behavior
*/
public void setPasteActionHandler(PasteActionHandler handler) {
Objects.requireNonNull(handler);
@@ -82,7 +61,7 @@ public void setPasteActionHandler(PasteActionHandler handler) {
}
/**
- * Override javafx TextArea method applying TextArea.paste() and pasteActionHandler after
+ * Override javafx TextArea method applying TextArea.paste() and pasteActionHandler after
*/
@Override
public void paste() {
@@ -91,7 +70,7 @@ public void paste() {
}
/**
- * Interface presents user-described paste behaviour applying to paste method
+ * Interface presents user-described paste behaviour applying to paste method
*/
@FunctionalInterface
public interface PasteActionHandler {
diff --git a/src/main/java/org/jabref/gui/fieldeditors/EditorTextField.java b/src/main/java/org/jabref/gui/fieldeditors/EditorTextField.java
index 6452c64957b..215083980a8 100644
--- a/src/main/java/org/jabref/gui/fieldeditors/EditorTextField.java
+++ b/src/main/java/org/jabref/gui/fieldeditors/EditorTextField.java
@@ -8,15 +8,14 @@
import javafx.fxml.Initializable;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.MenuItem;
-import javafx.scene.control.TextField;
-import javafx.scene.input.KeyCode;
-import javafx.scene.input.KeyEvent;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
-import com.sun.javafx.scene.control.skin.TextFieldSkin;
+import org.jabref.gui.ClipBoardManager;
-public class EditorTextField extends TextField implements Initializable, ContextMenuAddable {
+public class EditorTextField extends javafx.scene.control.TextField implements Initializable, ContextMenuAddable {
+
+ private final ContextMenu contextMenu = new ContextMenu();
public EditorTextField() {
this("");
@@ -29,37 +28,17 @@ public EditorTextField(final String text) {
setPrefHeight(Double.POSITIVE_INFINITY);
HBox.setHgrow(this, Priority.ALWAYS);
- // Should behave as a normal text field with respect to TAB behaviour
- addEventFilter(KeyEvent.KEY_PRESSED, event -> {
- if (event.getCode() == KeyCode.TAB) {
- TextFieldSkin skin = (TextFieldSkin) getSkin();
- if (event.isShiftDown()) {
- // Shift + Tab > previous text area
- skin.getBehavior().traversePrevious();
- } else {
- if (event.isControlDown()) {
- // Ctrl + Tab > insert tab
- skin.getBehavior().callAction("InsertTab");
- } else {
- // Tab > next text area
- skin.getBehavior().traverseNext();
- }
- }
- event.consume();
- }
- });
+ ClipBoardManager.addX11Support(this);
}
@Override
public void addToContextMenu(final Supplier> items) {
- TextFieldSkin customContextSkin = new TextFieldSkin(this) {
- @Override
- public void populateContextMenu(ContextMenu contextMenu) {
- super.populateContextMenu(contextMenu);
- contextMenu.getItems().addAll(0, items.get());
- }
- };
- setSkin(customContextSkin);
+ setOnContextMenuRequested(event -> {
+ contextMenu.getItems().setAll(TextInputControlBehavior.getDefaultContextMenuItems(this));
+ contextMenu.getItems().addAll(0, items.get());
+
+ TextInputControlBehavior.showContextMenu(this, contextMenu, event);
+ });
}
@Override
diff --git a/src/main/java/org/jabref/gui/fieldeditors/FieldEditors.java b/src/main/java/org/jabref/gui/fieldeditors/FieldEditors.java
index f1f588faca0..d44cb882aad 100644
--- a/src/main/java/org/jabref/gui/fieldeditors/FieldEditors.java
+++ b/src/main/java/org/jabref/gui/fieldeditors/FieldEditors.java
@@ -90,14 +90,12 @@ public static FieldEditorFX getForField(final Field field,
return new PersonsEditor(field, suggestionProvider, preferences, fieldCheckers, isSingleLine);
} else if (StandardField.KEYWORDS.equals(field)) {
return new KeywordsEditor(field, suggestionProvider, fieldCheckers, preferences);
- } else if (fieldProperties.contains(FieldProperty.MULTILINE_TEXT)) {
- return new MultilineEditor(field, suggestionProvider, fieldCheckers, preferences);
} else if (field == InternalField.KEY_FIELD) {
return new BibtexKeyEditor(field, preferences, suggestionProvider, fieldCheckers, databaseContext, undoManager, dialogService);
+ } else {
+ // default
+ return new SimpleEditor(field, suggestionProvider, fieldCheckers, preferences, isSingleLine);
}
-
- // default
- return new SimpleEditor(field, suggestionProvider, fieldCheckers, preferences, isSingleLine);
}
@SuppressWarnings("unchecked")
diff --git a/src/main/java/org/jabref/gui/fieldeditors/LinkedFilesEditor.java b/src/main/java/org/jabref/gui/fieldeditors/LinkedFilesEditor.java
index 725c206ffa6..d41c7beafcf 100644
--- a/src/main/java/org/jabref/gui/fieldeditors/LinkedFilesEditor.java
+++ b/src/main/java/org/jabref/gui/fieldeditors/LinkedFilesEditor.java
@@ -30,9 +30,11 @@
import org.jabref.gui.DragAndDropDataFormats;
import org.jabref.gui.autocompleter.AutoCompleteSuggestionProvider;
import org.jabref.gui.copyfiles.CopySingleFileAction;
+import org.jabref.gui.icon.IconTheme;
import org.jabref.gui.keyboard.KeyBinding;
import org.jabref.gui.util.TaskExecutor;
import org.jabref.gui.util.ViewModelListCellFactory;
+import org.jabref.gui.util.uithreadaware.UiThreadObservableList;
import org.jabref.logic.integrity.FieldCheckers;
import org.jabref.logic.l10n.Localization;
import org.jabref.model.database.BibDatabaseContext;
@@ -42,8 +44,6 @@
import org.jabref.preferences.JabRefPreferences;
import com.airhacks.afterburner.views.ViewLoader;
-import de.jensd.fx.glyphs.materialdesignicons.MaterialDesignIcon;
-import de.jensd.fx.glyphs.materialdesignicons.utils.MaterialDesignIconFactory;
public class LinkedFilesEditor extends HBox implements FieldEditorFX {
@@ -52,6 +52,7 @@ public class LinkedFilesEditor extends HBox implements FieldEditorFX {
private final DialogService dialogService;
private final BibDatabaseContext databaseContext;
+ private final UiThreadObservableList decoratedModelList;
public LinkedFilesEditor(Field field, DialogService dialogService, BibDatabaseContext databaseContext, TaskExecutor taskExecutor, AutoCompleteSuggestionProvider> suggestionProvider,
FieldCheckers fieldCheckers,
@@ -75,7 +76,8 @@ public LinkedFilesEditor(Field field, DialogService dialogService, BibDatabaseCo
listView.setCellFactory(cellFactory);
- Bindings.bindContentBidirectional(listView.itemsProperty().get(), viewModel.filesProperty());
+ decoratedModelList = new UiThreadObservableList<>(viewModel.filesProperty());
+ Bindings.bindContentBidirectional(listView.itemsProperty().get(), decoratedModelList);
setUpKeyBindings();
}
@@ -142,13 +144,13 @@ private static Node createFileDisplay(LinkedFileViewModel linkedFile) {
info.setStyle("-fx-padding: 0.5em 0 0.5em 0;"); // To align with buttons below which also have 0.5em padding
info.getChildren().setAll(icon, link, desc, progressIndicator);
- Button acceptAutoLinkedFile = MaterialDesignIconFactory.get().createIconButton(MaterialDesignIcon.BRIEFCASE_CHECK);
+ Button acceptAutoLinkedFile = IconTheme.JabRefIcons.AUTO_LINKED_FILE.asButton();
acceptAutoLinkedFile.setTooltip(new Tooltip(Localization.lang("This file was found automatically. Do you want to link it to this entry?")));
acceptAutoLinkedFile.visibleProperty().bind(linkedFile.isAutomaticallyFoundProperty());
acceptAutoLinkedFile.setOnAction(event -> linkedFile.acceptAsLinked());
acceptAutoLinkedFile.getStyleClass().setAll("icon-button");
- Button writeXMPMetadata = MaterialDesignIconFactory.get().createIconButton(MaterialDesignIcon.IMPORT);
+ Button writeXMPMetadata = IconTheme.JabRefIcons.IMPORT.asButton();
writeXMPMetadata.setTooltip(new Tooltip(Localization.lang("Write BibTeXEntry as XMP-metadata to PDF.")));
writeXMPMetadata.visibleProperty().bind(linkedFile.canWriteXMPMetadataProperty());
writeXMPMetadata.setOnAction(event -> linkedFile.writeXMPMetadata());
diff --git a/src/main/java/org/jabref/gui/fieldeditors/MultilineEditor.java b/src/main/java/org/jabref/gui/fieldeditors/MultilineEditor.java
deleted file mode 100644
index ac1323c9737..00000000000
--- a/src/main/java/org/jabref/gui/fieldeditors/MultilineEditor.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package org.jabref.gui.fieldeditors;
-
-import org.jabref.gui.autocompleter.AutoCompleteSuggestionProvider;
-import org.jabref.logic.integrity.FieldCheckers;
-import org.jabref.model.entry.field.Field;
-import org.jabref.preferences.JabRefPreferences;
-
-public class MultilineEditor extends SimpleEditor implements FieldEditorFX {
-
- public MultilineEditor(Field field, AutoCompleteSuggestionProvider> suggestionProvider, FieldCheckers fieldCheckers, JabRefPreferences preferences) {
- super(field, suggestionProvider, fieldCheckers, preferences);
- }
-
- @Override
- public double getWeight() {
- return 4;
- }
-}
diff --git a/src/main/java/org/jabref/gui/fieldeditors/PersonsEditor.java b/src/main/java/org/jabref/gui/fieldeditors/PersonsEditor.java
index 9b30d5b99dd..5faa0dbb26d 100644
--- a/src/main/java/org/jabref/gui/fieldeditors/PersonsEditor.java
+++ b/src/main/java/org/jabref/gui/fieldeditors/PersonsEditor.java
@@ -7,6 +7,7 @@
import org.jabref.gui.autocompleter.AutoCompleteSuggestionProvider;
import org.jabref.gui.autocompleter.AutoCompletionTextInputBinding;
import org.jabref.gui.fieldeditors.contextmenu.EditorMenus;
+import org.jabref.gui.util.uithreadaware.UiThreadStringProperty;
import org.jabref.logic.integrity.FieldCheckers;
import org.jabref.model.entry.BibEntry;
import org.jabref.model.entry.field.Field;
@@ -16,6 +17,7 @@ public class PersonsEditor extends HBox implements FieldEditorFX {
private final PersonsEditorViewModel viewModel;
private final TextInputControl textInput;
+ private final UiThreadStringProperty decoratedStringProperty;
public PersonsEditor(final Field field,
final AutoCompleteSuggestionProvider> suggestionProvider,
@@ -28,7 +30,8 @@ public PersonsEditor(final Field field,
? new EditorTextField()
: new EditorTextArea();
- textInput.textProperty().bindBidirectional(viewModel.textProperty());
+ decoratedStringProperty = new UiThreadStringProperty(viewModel.textProperty());
+ textInput.textProperty().bindBidirectional(decoratedStringProperty);
((ContextMenuAddable) textInput).addToContextMenu(EditorMenus.getNameMenu(textInput));
this.getChildren().add(textInput);
diff --git a/src/main/java/org/jabref/gui/fieldeditors/TextInputControlBehavior.java b/src/main/java/org/jabref/gui/fieldeditors/TextInputControlBehavior.java
new file mode 100644
index 00000000000..bbe745e8de9
--- /dev/null
+++ b/src/main/java/org/jabref/gui/fieldeditors/TextInputControlBehavior.java
@@ -0,0 +1,206 @@
+package org.jabref.gui.fieldeditors;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import javafx.geometry.Point2D;
+import javafx.geometry.Rectangle2D;
+import javafx.scene.Scene;
+import javafx.scene.control.ContextMenu;
+import javafx.scene.control.IndexRange;
+import javafx.scene.control.MenuItem;
+import javafx.scene.control.PasswordField;
+import javafx.scene.control.SeparatorMenuItem;
+import javafx.scene.control.TextArea;
+import javafx.scene.control.TextField;
+import javafx.scene.control.TextInputControl;
+import javafx.scene.control.skin.TextAreaSkin;
+import javafx.scene.control.skin.TextFieldSkin;
+import javafx.scene.input.Clipboard;
+import javafx.scene.input.ContextMenuEvent;
+import javafx.stage.Screen;
+import javafx.stage.Window;
+
+import org.jabref.logic.l10n.Localization;
+import org.jabref.logic.util.OS;
+
+import com.sun.javafx.scene.control.Properties;
+
+/**
+ * This class contains some code taken from {@link com.sun.javafx.scene.control.behavior.TextInputControlBehavior},
+ * witch is not accessible and thus we have no other choice.
+ * TODO: remove this ugly workaround as soon as control behavior is made public
+ * reported at https://github.com/javafxports/openjdk-jfx/issues/583
+ */
+public class TextInputControlBehavior {
+
+ private static final boolean SHOW_HANDLES = Properties.IS_TOUCH_SUPPORTED && !OS.OS_X;
+
+ /**
+ * Returns the default context menu items (except undo/redo)
+ */
+ public static List getDefaultContextMenuItems(TextInputControl textInputControl) {
+ boolean editable = textInputControl.isEditable();
+ boolean hasText = (textInputControl.getLength() > 0);
+ boolean hasSelection = (textInputControl.getSelection().getLength() > 0);
+ boolean allSelected = (textInputControl.getSelection().getLength() == textInputControl.getLength());
+ boolean maskText = (textInputControl instanceof PasswordField); // (maskText("A") != "A");
+ ArrayList items = new ArrayList<>();
+
+ MenuItem cutMI = new MenuItem(Localization.lang("Cut"));
+ cutMI.setOnAction(e -> textInputControl.cut());
+ MenuItem copyMI = new MenuItem(Localization.lang("Copy"));
+ copyMI.setOnAction(e -> textInputControl.copy());
+ MenuItem pasteMI = new MenuItem(Localization.lang("Paste"));
+ pasteMI.setOnAction(e -> textInputControl.paste());
+ MenuItem deleteMI = new MenuItem(Localization.lang("Delete"));
+ deleteMI.setOnAction(e -> {
+ IndexRange selection = textInputControl.getSelection();
+ textInputControl.deleteText(selection);
+ });
+ MenuItem selectAllMI = new MenuItem(Localization.lang("Select all"));
+ selectAllMI.setOnAction(e -> textInputControl.selectAll());
+ MenuItem separatorMI = new SeparatorMenuItem();
+
+ if (SHOW_HANDLES) {
+ if (!maskText && hasSelection) {
+ if (editable) {
+ items.add(cutMI);
+ }
+ items.add(copyMI);
+ }
+ if (editable && Clipboard.getSystemClipboard().hasString()) {
+ items.add(pasteMI);
+ }
+ if (hasText && !allSelected) {
+ items.add(selectAllMI);
+ }
+ selectAllMI.getProperties().put("refreshMenu", Boolean.TRUE);
+ } else {
+ if (editable) {
+ items.addAll(Arrays.asList(cutMI, copyMI, pasteMI, deleteMI, separatorMI, selectAllMI));
+ } else {
+ items.addAll(Arrays.asList(copyMI, separatorMI, selectAllMI));
+ }
+ cutMI.setDisable(maskText || !hasSelection);
+ copyMI.setDisable(maskText || !hasSelection);
+ pasteMI.setDisable(!Clipboard.getSystemClipboard().hasString());
+ deleteMI.setDisable(!hasSelection);
+ }
+
+ return items;
+ }
+
+ /**
+ * @implNote taken from {@link com.sun.javafx.scene.control.behavior.TextFieldBehavior#contextMenuRequested(javafx.scene.input.ContextMenuEvent)}
+ */
+ public static void showContextMenu(TextField textField, ContextMenu contextMenu, ContextMenuEvent e) {
+ double screenX = e.getScreenX();
+ double screenY = e.getScreenY();
+ double sceneX = e.getSceneX();
+
+ TextFieldSkin skin = (TextFieldSkin) textField.getSkin();
+
+ if (Properties.IS_TOUCH_SUPPORTED) {
+ Point2D menuPos;
+ if (textField.getSelection().getLength() == 0) {
+ skin.positionCaret(skin.getIndex(e.getX(), e.getY()), false);
+ menuPos = skin.getMenuPosition();
+ } else {
+ menuPos = skin.getMenuPosition();
+ if (menuPos != null && (menuPos.getX() <= 0 || menuPos.getY() <= 0)) {
+ skin.positionCaret(skin.getIndex(e.getX(), e.getY()), false);
+ menuPos = skin.getMenuPosition();
+ }
+ }
+
+ if (menuPos != null) {
+ Point2D p = textField.localToScene(menuPos);
+ Scene scene = textField.getScene();
+ Window window = scene.getWindow();
+ Point2D location = new Point2D(window.getX() + scene.getX() + p.getX(),
+ window.getY() + scene.getY() + p.getY());
+ screenX = location.getX();
+ sceneX = p.getX();
+ screenY = location.getY();
+ }
+ }
+
+ double menuWidth = contextMenu.prefWidth(-1);
+ double menuX = screenX - (Properties.IS_TOUCH_SUPPORTED ? (menuWidth / 2) : 0);
+ Screen currentScreen = Screen.getPrimary();
+ Rectangle2D bounds = currentScreen.getBounds();
+
+ if (menuX < bounds.getMinX()) {
+ textField.getProperties().put("CONTEXT_MENU_SCREEN_X", screenX);
+ textField.getProperties().put("CONTEXT_MENU_SCENE_X", sceneX);
+ contextMenu.show(textField, bounds.getMinX(), screenY);
+ } else if (screenX + menuWidth > bounds.getMaxX()) {
+ double leftOver = menuWidth - (bounds.getMaxX() - screenX);
+ textField.getProperties().put("CONTEXT_MENU_SCREEN_X", screenX);
+ textField.getProperties().put("CONTEXT_MENU_SCENE_X", sceneX);
+ contextMenu.show(textField, screenX - leftOver, screenY);
+ } else {
+ textField.getProperties().put("CONTEXT_MENU_SCREEN_X", 0);
+ textField.getProperties().put("CONTEXT_MENU_SCENE_X", 0);
+ contextMenu.show(textField, menuX, screenY);
+ }
+ }
+
+ /**
+ * @implNote taken from {@link com.sun.javafx.scene.control.behavior.TextAreaBehavior#contextMenuRequested(javafx.scene.input.ContextMenuEvent)}
+ */
+ public static void showContextMenu(TextArea textArea, ContextMenu contextMenu, ContextMenuEvent e) {
+ double screenX = e.getScreenX();
+ double screenY = e.getScreenY();
+ double sceneX = e.getSceneX();
+
+ TextAreaSkin skin = (TextAreaSkin) textArea.getSkin();
+
+ if (Properties.IS_TOUCH_SUPPORTED) {
+ Point2D menuPos;
+ if (textArea.getSelection().getLength() == 0) {
+ skin.positionCaret(skin.getIndex(e.getX(), e.getY()), false);
+ menuPos = skin.getMenuPosition();
+ } else {
+ menuPos = skin.getMenuPosition();
+ if (menuPos != null && (menuPos.getX() <= 0 || menuPos.getY() <= 0)) {
+ skin.positionCaret(skin.getIndex(e.getX(), e.getY()), false);
+ menuPos = skin.getMenuPosition();
+ }
+ }
+
+ if (menuPos != null) {
+ Point2D p = textArea.localToScene(menuPos);
+ Scene scene = textArea.getScene();
+ Window window = scene.getWindow();
+ Point2D location = new Point2D(window.getX() + scene.getX() + p.getX(),
+ window.getY() + scene.getY() + p.getY());
+ screenX = location.getX();
+ sceneX = p.getX();
+ screenY = location.getY();
+ }
+ }
+
+ double menuWidth = contextMenu.prefWidth(-1);
+ double menuX = screenX - (Properties.IS_TOUCH_SUPPORTED ? (menuWidth / 2) : 0);
+ Screen currentScreen = Screen.getPrimary();
+ Rectangle2D bounds = currentScreen.getBounds();
+
+ if (menuX < bounds.getMinX()) {
+ textArea.getProperties().put("CONTEXT_MENU_SCREEN_X", screenX);
+ textArea.getProperties().put("CONTEXT_MENU_SCENE_X", sceneX);
+ contextMenu.show(textArea, bounds.getMinX(), screenY);
+ } else if (screenX + menuWidth > bounds.getMaxX()) {
+ double leftOver = menuWidth - (bounds.getMaxX() - screenX);
+ textArea.getProperties().put("CONTEXT_MENU_SCREEN_X", screenX);
+ textArea.getProperties().put("CONTEXT_MENU_SCENE_X", sceneX);
+ contextMenu.show(textArea, screenX - leftOver, screenY);
+ } else {
+ textArea.getProperties().put("CONTEXT_MENU_SCREEN_X", 0);
+ textArea.getProperties().put("CONTEXT_MENU_SCENE_X", 0);
+ contextMenu.show(textArea, menuX, screenY);
+ }
+ }
+}
diff --git a/src/main/java/org/jabref/gui/fieldeditors/UrlEditor.java b/src/main/java/org/jabref/gui/fieldeditors/UrlEditor.java
index 6e5d6012761..08d22698a8c 100644
--- a/src/main/java/org/jabref/gui/fieldeditors/UrlEditor.java
+++ b/src/main/java/org/jabref/gui/fieldeditors/UrlEditor.java
@@ -30,16 +30,15 @@ public UrlEditor(Field field, DialogService dialogService, AutoCompleteSuggestio
this.viewModel = new UrlEditorViewModel(field, suggestionProvider, dialogService, fieldCheckers);
ViewLoader.view(this)
- .root(this)
- .load();
+ .root(this)
+ .load();
textArea.textProperty().bindBidirectional(viewModel.textProperty());
Supplier> contextMenuSupplier = EditorMenus.getCleanupURLMenu(textArea);
textArea.addToContextMenu(contextMenuSupplier);
// init paste handler for URLEditor to format pasted url link in textArea
- textArea.setPasteActionHandler(()->
- textArea.setText(new CleanupURLFormatter().format(new TrimWhitespaceFormatter().format(textArea.getText()))));
+ textArea.setPasteActionHandler(() -> textArea.setText(new CleanupURLFormatter().format(new TrimWhitespaceFormatter().format(textArea.getText()))));
new EditorValidator(preferences).configureValidation(viewModel.getFieldValidator().getValidationStatus(), textArea);
diff --git a/src/main/java/org/jabref/gui/groups/GroupDialog.java b/src/main/java/org/jabref/gui/groups/GroupDialog.java
index b5ef52ee741..c97550dce12 100644
--- a/src/main/java/org/jabref/gui/groups/GroupDialog.java
+++ b/src/main/java/org/jabref/gui/groups/GroupDialog.java
@@ -18,6 +18,7 @@
import javafx.scene.control.Button;
import javafx.scene.control.ButtonType;
import javafx.scene.control.CheckBox;
+import javafx.scene.control.ColorPicker;
import javafx.scene.control.Label;
import javafx.scene.control.RadioButton;
import javafx.scene.control.ScrollPane;
@@ -39,6 +40,7 @@
import org.jabref.JabRefGUI;
import org.jabref.gui.BasePanel;
import org.jabref.gui.DialogService;
+import org.jabref.gui.icon.IconTheme;
import org.jabref.gui.search.rules.describer.SearchDescribers;
import org.jabref.gui.util.BaseDialog;
import org.jabref.gui.util.FileDialogConfiguration;
@@ -53,6 +55,7 @@
import org.jabref.model.entry.field.FieldFactory;
import org.jabref.model.entry.field.StandardField;
import org.jabref.model.groups.AbstractGroup;
+import org.jabref.model.groups.AutomaticGroup;
import org.jabref.model.groups.AutomaticKeywordGroup;
import org.jabref.model.groups.AutomaticPersonsGroup;
import org.jabref.model.groups.ExplicitGroup;
@@ -66,7 +69,7 @@
import org.jabref.model.strings.StringUtil;
import org.jabref.preferences.JabRefPreferences;
-import com.jfoenix.controls.JFXColorPicker;
+// import com.jfoenix.controls.JFXColorPicker;
/**
* Dialog for creating or modifying groups. Operates directly on the Vector
@@ -83,7 +86,8 @@ class GroupDialog extends BaseDialog {
// for all types
private final TextField nameField = new TextField();
private final TextField descriptionField = new TextField();
- private final JFXColorPicker colorField = new JFXColorPicker();
+ //private final JFXColorPicker colorField = new JFXColorPicker();
+ private final ColorPicker colorField = new ColorPicker();
private final TextField iconField = new TextField();
private final RadioButton explicitRadioButton = new RadioButton(Localization.lang("Statically group entries by manual assignment"));
private final RadioButton keywordsRadioButton = new RadioButton(Localization.lang("Dynamically group entries by searching a field for a keyword"));
@@ -336,7 +340,7 @@ groupName, getContext(),
}
} else if (texRadioButton.isSelected()) {
resultingGroup = TexGroup.create(groupName, getContext(),
- Paths.get(texGroupFilePath.getText().trim()), new DefaultAuxParser(new BibDatabase()), Globals.getFileUpdateMonitor(), basePanel.getBibDatabaseContext().getMetaData());
+ Paths.get(texGroupFilePath.getText().trim()), new DefaultAuxParser(new BibDatabase()), Globals.getFileUpdateMonitor(), basePanel.getBibDatabaseContext().getMetaData());
}
resultingGroup.setColor(colorField.getValue());
@@ -369,13 +373,15 @@ groupName, getContext(),
// configure for current type
if (editedGroup == null) {
// creating new group -> defaults!
+ colorField.setValue(IconTheme.getDefaultGroupColor());
explicitRadioButton.setSelected(true);
setContext(GroupHierarchyType.INDEPENDENT);
} else {
nameField.setText(editedGroup.getName());
- editedGroup.getColor().ifPresent(colorField::setValue);
+ colorField.setValue(editedGroup.getColor().orElse(IconTheme.getDefaultGroupColor()));
descriptionField.setText(editedGroup.getDescription().orElse(""));
iconField.setText(editedGroup.getIconName().orElse(""));
+ setContext(editedGroup.getHierarchicalContext());
if (editedGroup.getClass() == WordKeywordGroup.class) {
WordKeywordGroup group = (WordKeywordGroup) editedGroup;
@@ -384,7 +390,6 @@ groupName, getContext(),
keywordGroupCaseSensitive.setSelected(group.isCaseSensitive());
keywordGroupRegExp.setSelected(false);
keywordsRadioButton.setSelected(true);
- setContext(editedGroup.getHierarchicalContext());
} else if (editedGroup.getClass() == RegexKeywordGroup.class) {
RegexKeywordGroup group = (RegexKeywordGroup) editedGroup;
keywordGroupSearchField.setText(group.getSearchField().getName());
@@ -392,20 +397,16 @@ groupName, getContext(),
keywordGroupCaseSensitive.setSelected(group.isCaseSensitive());
keywordGroupRegExp.setSelected(true);
keywordsRadioButton.setSelected(true);
- setContext(editedGroup.getHierarchicalContext());
} else if (editedGroup.getClass() == SearchGroup.class) {
SearchGroup group = (SearchGroup) editedGroup;
searchGroupSearchExpression.setText(group.getSearchExpression());
searchGroupCaseSensitive.setSelected(group.isCaseSensitive());
searchGroupRegExp.setSelected(group.isRegularExpression());
searchRadioButton.setSelected(true);
- setContext(editedGroup.getHierarchicalContext());
} else if (editedGroup.getClass() == ExplicitGroup.class) {
explicitRadioButton.setSelected(true);
- setContext(editedGroup.getHierarchicalContext());
- } else if (editedGroup.getClass() == AutomaticKeywordGroup.class) {
+ } else if (editedGroup instanceof AutomaticGroup) {
autoRadioButton.setSelected(true);
- setContext(editedGroup.getHierarchicalContext());
if (editedGroup.getClass() == AutomaticKeywordGroup.class) {
AutomaticKeywordGroup group = (AutomaticKeywordGroup) editedGroup;
@@ -418,7 +419,6 @@ groupName, getContext(),
}
} else if (editedGroup.getClass() == TexGroup.class) {
texRadioButton.setSelected(true);
- setContext(editedGroup.getHierarchicalContext());
TexGroup group = (TexGroup) editedGroup;
texGroupFilePath.setText(group.getFilePath().toString());
@@ -588,7 +588,7 @@ private void updateComponents() {
if (okEnabled) {
setDescription(fromTextFlowToHTMLString(SearchDescribers.getSearchDescriberFor(
new SearchQuery(s1, isCaseSensitive(), isRegex()))
- .getDescription()));
+ .getDescription()));
if (isRegex()) {
try {
@@ -615,9 +615,9 @@ private void updateComponents() {
private void openBrowseDialog() {
FileDialogConfiguration fileDialogConfiguration = new FileDialogConfiguration.Builder()
- .addExtensionFilter(StandardFileType.AUX)
- .withDefaultExtension(StandardFileType.AUX)
- .withInitialDirectory(Globals.prefs.get(JabRefPreferences.WORKING_DIRECTORY)).build();
+ .addExtensionFilter(StandardFileType.AUX)
+ .withDefaultExtension(StandardFileType.AUX)
+ .withInitialDirectory(Globals.prefs.get(JabRefPreferences.WORKING_DIRECTORY)).build();
dialogService.showFileOpenDialog(fileDialogConfiguration).ifPresent(file -> texGroupFilePath.setText(relativize(file.toAbsolutePath()).toString()));
}
@@ -672,21 +672,18 @@ private ArrayList createFormattedDescription(String descriptionHTML) {
Text textElement = new Text(bs);
textElement.setStyle("-fx-font-weight: bold");
nodes.add(textElement);
-
} else if (bs.matches("[^<>]* ")) {
bs = bs.replaceAll("| ", "");
Text textElement = new Text(bs);
textElement.setStyle("-fx-font-style: italic");
nodes.add(textElement);
-
} else if (bs.matches("[^<>]* |[^<>]* ")) {
bs = bs.replaceAll("| || ", "");
Text textElement = new Text(bs);
textElement.setStyle("-fx-font-family: 'Courier New', Courier, monospace");
nodes.add(textElement);
-
} else {
nodes.add(new Text(bs));
}
@@ -724,12 +721,16 @@ private GroupHierarchyType getContext() {
}
private void setContext(GroupHierarchyType context) {
- if (context == GroupHierarchyType.REFINING) {
- intersectionButton.setSelected(true);
- } else if (context == GroupHierarchyType.INCLUDING) {
- unionButton.setSelected(true);
- } else {
- independentButton.setSelected(true);
+ switch (context) {
+ case INDEPENDENT:
+ independentButton.setSelected(true);
+ break;
+ case REFINING:
+ intersectionButton.setSelected(true);
+ break;
+ case INCLUDING:
+ unionButton.setSelected(true);
+ break;
}
}
}
diff --git a/src/main/java/org/jabref/gui/groups/GroupNodeViewModel.java b/src/main/java/org/jabref/gui/groups/GroupNodeViewModel.java
index cb17247dd0c..864abe24598 100644
--- a/src/main/java/org/jabref/gui/groups/GroupNodeViewModel.java
+++ b/src/main/java/org/jabref/gui/groups/GroupNodeViewModel.java
@@ -24,6 +24,7 @@
import org.jabref.gui.util.BackgroundTask;
import org.jabref.gui.util.BindingsHelper;
import org.jabref.gui.util.CustomLocalDragboard;
+import org.jabref.gui.util.DroppingMouseLocation;
import org.jabref.gui.util.TaskExecutor;
import org.jabref.logic.groups.DefaultGroupsFactory;
import org.jabref.logic.layout.format.LatexToUnicodeFormatter;
@@ -37,7 +38,6 @@
import org.jabref.model.strings.StringUtil;
import com.google.common.base.Enums;
-import de.jensd.fx.glyphs.materialdesignicons.MaterialDesignIcon;
import org.fxmisc.easybind.EasyBind;
public class GroupNodeViewModel {
@@ -113,8 +113,13 @@ public List addEntriesToGroup(List entries) {
// return; // user aborted operation
//}
- return groupNode.addEntriesToGroup(entries);
+ var changes = groupNode.addEntriesToGroup(entries);
+ // Update appearance of group
+ anySelectedEntriesMatched.invalidate();
+ allSelectedEntriesMatched.invalidate();
+
+ return changes;
// TODO: Store undo
// if (!undo.isEmpty()) {
// groupSelector.concludeAssignment(UndoableChangeEntriesOfGroup.getUndoableEdit(target, undo), target.getNode(), assignedEntries);
@@ -191,16 +196,12 @@ public JabRefIcon getIcon() {
}
private JabRefIcon createDefaultIcon() {
- Optional color = groupNode.getGroup().getColor();
- if (color.isPresent()) {
- return IconTheme.JabRefIcons.DEFAULT_GROUP_ICON_COLORED.withColor(color.get());
- } else {
- return IconTheme.JabRefIcons.DEFAULT_GROUP_ICON_COLORED.withColor(Color.web("#8a8a8a"));
- }
+ Color color = groupNode.getGroup().getColor().orElse(IconTheme.getDefaultGroupColor());
+ return IconTheme.JabRefIcons.DEFAULT_GROUP_ICON_COLORED.withColor(color);
}
private Optional parseIcon(String iconCode) {
- return Enums.getIfPresent(MaterialDesignIcon.class, iconCode.toUpperCase(Locale.ENGLISH))
+ return Enums.getIfPresent(IconTheme.JabRefIcons.class, iconCode.toUpperCase(Locale.ENGLISH))
.toJavaUtil()
.map(icon -> new InternalMaterialDesignIcon(getColor(), icon));
}
@@ -243,7 +244,7 @@ boolean isMatchedBy(String searchString) {
}
public Color getColor() {
- return groupNode.getGroup().getColor().orElse(IconTheme.getDefaultColor());
+ return groupNode.getGroup().getColor().orElse(IconTheme.getDefaultGroupColor());
}
public String getPath() {
@@ -263,8 +264,7 @@ public Optional getChildByPath(String pathToSource) {
public boolean acceptableDrop(Dragboard dragboard) {
// TODO: we should also check isNodeDescendant
boolean canDropOtherGroup = dragboard.hasContent(DragAndDropDataFormats.GROUP);
- boolean canDropEntries = localDragBoard.hasType(DragAndDropDataFormats.BIBENTRY_LIST_CLASS)
- && (groupNode.getGroup() instanceof GroupEntryChanger);
+ boolean canDropEntries = localDragBoard.hasBibEntries() && (groupNode.getGroup() instanceof GroupEntryChanger);
return canDropOtherGroup || canDropEntries;
}
diff --git a/src/main/java/org/jabref/gui/groups/GroupTree.css b/src/main/java/org/jabref/gui/groups/GroupTree.css
index ecee1015d44..7d72131694c 100644
--- a/src/main/java/org/jabref/gui/groups/GroupTree.css
+++ b/src/main/java/org/jabref/gui/groups/GroupTree.css
@@ -49,8 +49,9 @@
.tree-table-row-cell:dragOver-center {
-fx-border-color: -jr-drag-target;
- -fx-border-width: 2 2 2 2;
- -fx-padding: -2 -2 -2 -2;
+ -fx-border-width: 1 1 1 1;
+ -fx-padding: -1 -1 -1 -1;
+ -fx-background-color: -jr-drag-target-hover;
}
.tree-table-row-cell:dragOver-top {
diff --git a/src/main/java/org/jabref/gui/groups/GroupTreeView.java b/src/main/java/org/jabref/gui/groups/GroupTreeView.java
index 1d2c4646dc6..ddfbf0434e1 100644
--- a/src/main/java/org/jabref/gui/groups/GroupTreeView.java
+++ b/src/main/java/org/jabref/gui/groups/GroupTreeView.java
@@ -6,15 +6,12 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
-import java.util.function.Consumer;
import java.util.stream.Collectors;
import javax.inject.Inject;
import javafx.beans.property.ObjectProperty;
-import javafx.collections.ObservableList;
import javafx.css.PseudoClass;
-import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.Control;
@@ -27,7 +24,6 @@
import javafx.scene.control.TreeTableRow;
import javafx.scene.control.TreeTableView;
import javafx.scene.input.ClipboardContent;
-import javafx.scene.input.DragEvent;
import javafx.scene.input.Dragboard;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
@@ -40,6 +36,7 @@
import org.jabref.gui.GUIGlobals;
import org.jabref.gui.StateManager;
import org.jabref.gui.util.BindingsHelper;
+import org.jabref.gui.util.ControlHelper;
import org.jabref.gui.util.CustomLocalDragboard;
import org.jabref.gui.util.RecursiveTreeItem;
import org.jabref.gui.util.TaskExecutor;
@@ -74,12 +71,6 @@ public class GroupTreeView {
private DragExpansionHandler dragExpansionHandler;
- private static void removePseudoClasses(TreeTableRow row, PseudoClass... pseudoClasses) {
- for (PseudoClass pseudoClass : pseudoClasses) {
- row.pseudoClassStateChanged(pseudoClass, false);
- }
- }
-
@FXML
public void initialize() {
this.localDragboard = GUIGlobals.localDragboard;
@@ -90,13 +81,10 @@ public void initialize() {
dragExpansionHandler = new DragExpansionHandler();
// Set-up bindings
- Consumer> updateSelectedGroups =
- (newSelectedGroups) -> newSelectedGroups.forEach(this::selectNode);
-
BindingsHelper.bindContentBidirectional(
groupTree.getSelectionModel().getSelectedItems(),
viewModel.selectedGroupsProperty(),
- updateSelectedGroups,
+ (newSelectedGroups) -> newSelectedGroups.forEach(this::selectNode),
this::updateSelection
);
@@ -110,15 +98,20 @@ public void initialize() {
groupTree.rootProperty().bind(
EasyBind.map(viewModel.rootGroupProperty(),
- group -> new RecursiveTreeItem<>(
- group,
- GroupNodeViewModel::getChildren,
- GroupNodeViewModel::expandedProperty,
- viewModel.filterPredicateProperty())));
+ group -> {
+ if (group == null) {
+ return null;
+ } else {
+ return new RecursiveTreeItem<>(
+ group,
+ GroupNodeViewModel::getChildren,
+ GroupNodeViewModel::expandedProperty,
+ viewModel.filterPredicateProperty());
+ }
+ }));
// Icon and group name
- mainColumn.setCellValueFactory(cellData -> cellData.getValue().valueProperty());
- new ViewModelTreeTableCellFactory()
+ new ViewModelTreeTableCellFactory()
.withText(GroupNodeViewModel::getDisplayName)
.withIcon(GroupNodeViewModel::getIcon)
.withTooltip(GroupNodeViewModel::getDescription)
@@ -127,7 +120,7 @@ public void initialize() {
// Number of hits
PseudoClass anySelected = PseudoClass.getPseudoClass("any-selected");
PseudoClass allSelected = PseudoClass.getPseudoClass("all-selected");
- new ViewModelTreeTableCellFactory()
+ new ViewModelTreeTableCellFactory()
.withGraphic(group -> {
final StackPane node = new StackPane();
node.getStyleClass().setAll("hits");
@@ -147,8 +140,7 @@ public void initialize() {
.install(numberColumn);
// Arrow indicating expanded status
- disclosureNodeColumn.setCellValueFactory(cellData -> cellData.getValue().valueProperty());
- new ViewModelTreeTableCellFactory()
+ new ViewModelTreeTableCellFactory()
.withGraphic(viewModel -> {
final StackPane disclosureNode = new StackPane();
disclosureNode.visibleProperty().bind(viewModel.hasChildrenProperty());
@@ -169,10 +161,6 @@ public void initialize() {
PseudoClass rootPseudoClass = PseudoClass.getPseudoClass("root");
PseudoClass subElementPseudoClass = PseudoClass.getPseudoClass("sub");
- // Pseudo-classes for drag and drop
- PseudoClass dragOverBottom = PseudoClass.getPseudoClass("dragOver-bottom");
- PseudoClass dragOverCenter = PseudoClass.getPseudoClass("dragOver-center");
- PseudoClass dragOverTop = PseudoClass.getPseudoClass("dragOver-top");
groupTree.setRowFactory(treeTable -> {
TreeTableRow row = new TreeTableRow<>();
row.treeItemProperty().addListener((ov, oldTreeItem, newTreeItem) -> {
@@ -226,23 +214,16 @@ public void initialize() {
//expand node and all children on drag over
dragExpansionHandler.expandGroup(row.getTreeItem());
- removePseudoClasses(row, dragOverBottom, dragOverCenter, dragOverTop);
- switch (getDroppingMouseLocation(row, event)) {
- case BOTTOM:
- row.pseudoClassStateChanged(dragOverBottom, true);
- break;
- case CENTER:
- row.pseudoClassStateChanged(dragOverCenter, true);
- break;
- case TOP:
- row.pseudoClassStateChanged(dragOverTop, true);
- break;
+ if (localDragboard.hasBibEntries()) {
+ ControlHelper.setDroppingPseudoClasses(row);
+ } else {
+ ControlHelper.setDroppingPseudoClasses(row, event);
}
}
event.consume();
});
row.setOnDragExited(event -> {
- removePseudoClasses(row, dragOverBottom, dragOverCenter, dragOverTop);
+ ControlHelper.removeDroppingPseudoClasses(row);
});
row.setOnDragDropped(event -> {
@@ -255,13 +236,13 @@ public void initialize() {
Optional source = viewModel.rootGroupProperty().get()
.getChildByPath(pathToSource);
if (source.isPresent()) {
- source.get().draggedOn(row.getItem(), getDroppingMouseLocation(row, event));
+ source.get().draggedOn(row.getItem(), ControlHelper.getDroppingMouseLocation(row, event));
success = true;
}
}
}
- if (localDragboard.hasType(DragAndDropDataFormats.BIBENTRY_LIST_CLASS)) {
+ if (localDragboard.hasBibEntries()) {
List entries = localDragboard.getBibEntries();
row.getItem().addEntriesToGroup(entries);
success = true;
@@ -281,7 +262,7 @@ private void updateSelection(List> newSelectedGroup
if ((newSelectedGroups == null) || newSelectedGroups.isEmpty()) {
viewModel.selectedGroupsProperty().clear();
} else {
- List list = newSelectedGroups.stream().filter(model -> model != null && !(model.getValue().getGroupNode().getGroup() instanceof AllEntriesGroup)).map(TreeItem::getValue).collect(Collectors.toList());
+ List list = newSelectedGroups.stream().filter(model -> model != null && !(model.getValue().getGroupNode().getGroup() instanceof AllEntriesGroup)).map(TreeItem::getValue).collect(Collectors.toList());
viewModel.selectedGroupsProperty().setAll(list);
}
}
@@ -360,7 +341,8 @@ private ContextMenu createContextMenuForGroup(GroupNodeViewModel group) {
return menu;
}
- public void addNewGroup(ActionEvent actionEvent) {
+ @FXML
+ private void addNewGroup() {
viewModel.addNewGroupToRoot();
}
@@ -369,8 +351,8 @@ public void addNewGroup(ActionEvent actionEvent) {
*/
private void setupClearButtonField(CustomTextField customTextField) {
try {
- Method m = TextFields.class.getDeclaredMethod("setupClearButtonField", TextField.class,
- ObjectProperty.class);
+ // TODO: reflective access, should be removed
+ Method m = TextFields.class.getDeclaredMethod("setupClearButtonField", TextField.class, ObjectProperty.class);
m.setAccessible(true);
m.invoke(null, customTextField, customTextField.rightProperty());
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ex) {
@@ -378,19 +360,6 @@ private void setupClearButtonField(CustomTextField customTextField) {
}
}
- /**
- * Determines where the mouse is in the given row.
- */
- private DroppingMouseLocation getDroppingMouseLocation(TreeTableRow row, DragEvent event) {
- if ((row.getHeight() * 0.25) > event.getY()) {
- return DroppingMouseLocation.TOP;
- } else if ((row.getHeight() * 0.75) < event.getY()) {
- return DroppingMouseLocation.BOTTOM;
- } else {
- return DroppingMouseLocation.CENTER;
- }
- }
-
private class DragExpansionHandler {
private static final long DRAG_TIME_BEFORE_EXPANDING_MS = 1000;
private TreeItem draggedItem;
diff --git a/src/main/java/org/jabref/gui/groups/GroupTreeViewModel.java b/src/main/java/org/jabref/gui/groups/GroupTreeViewModel.java
index bccddf297e5..47ae8b58c62 100644
--- a/src/main/java/org/jabref/gui/groups/GroupTreeViewModel.java
+++ b/src/main/java/org/jabref/gui/groups/GroupTreeViewModel.java
@@ -52,6 +52,7 @@ public GroupTreeViewModel(StateManager stateManager, DialogService dialogService
this.dialogService = Objects.requireNonNull(dialogService);
this.taskExecutor = Objects.requireNonNull(taskExecutor);
this.localDragboard = Objects.requireNonNull(localDragboard);
+
// Register listener
EasyBind.subscribe(stateManager.activeDatabaseProperty(), this::onActiveDatabaseChanged);
EasyBind.subscribe(selectedGroups, this::onSelectedGroupChanged);
@@ -126,12 +127,12 @@ private void onActiveDatabaseChanged(Optional newDatabase) {
.orElse(GroupNodeViewModel.getAllEntriesGroup(newDatabase.get(), stateManager, taskExecutor, localDragboard));
rootGroup.setValue(newRoot);
- this.selectedGroups.setAll(
+ selectedGroups.setAll(
stateManager.getSelectedGroup(newDatabase.get()).stream()
.map(selectedGroup -> new GroupNodeViewModel(newDatabase.get(), stateManager, taskExecutor, selectedGroup, localDragboard))
.collect(Collectors.toList()));
} else {
- rootGroup.setValue(GroupNodeViewModel.getAllEntriesGroup(new BibDatabaseContext(), stateManager, taskExecutor, localDragboard));
+ rootGroup.setValue(null);
}
currentDatabase = newDatabase;
diff --git a/src/main/java/org/jabref/gui/help/SearchForUpdateAction.java b/src/main/java/org/jabref/gui/help/SearchForUpdateAction.java
index c1d951aaa01..3e85c2fc9e1 100644
--- a/src/main/java/org/jabref/gui/help/SearchForUpdateAction.java
+++ b/src/main/java/org/jabref/gui/help/SearchForUpdateAction.java
@@ -23,6 +23,6 @@ public SearchForUpdateAction(BuildInfo buildInfo, VersionPreferences versionPref
@Override
public void execute() {
new VersionWorker(buildInfo.getVersion(), versionPreferences.getIgnoredVersion(), dialogService, taskExecutor)
- .checkForNewVersionAsync(true);
+ .checkForNewVersionAsync();
}
}
diff --git a/src/main/java/org/jabref/gui/help/VersionWorker.java b/src/main/java/org/jabref/gui/help/VersionWorker.java
index 2250be98a8f..f89eede48b9 100644
--- a/src/main/java/org/jabref/gui/help/VersionWorker.java
+++ b/src/main/java/org/jabref/gui/help/VersionWorker.java
@@ -4,6 +4,7 @@
import java.util.List;
import java.util.Objects;
import java.util.Optional;
+import java.util.concurrent.TimeUnit;
import org.jabref.gui.DialogService;
import org.jabref.gui.util.BackgroundTask;
@@ -54,16 +55,20 @@ private Optional getNewVersion() throws IOException {
return installedVersion.shouldBeUpdatedTo(availableVersions);
}
- /**
- * @param manualExecution if this versions check is executed automatically (eg. on startup) or manually by the user
- */
- public void checkForNewVersionAsync(boolean manualExecution) {
+ public void checkForNewVersionAsync() {
BackgroundTask.wrap(this::getNewVersion)
- .onSuccess(version -> showUpdateInfo(version, manualExecution))
- .onFailure(exception -> showConnectionError(exception, manualExecution))
+ .onSuccess(version -> showUpdateInfo(version, true))
+ .onFailure(exception -> showConnectionError(exception, true))
.executeWith(taskExecutor);
}
+ public void checkForNewVersionDelayed() {
+ BackgroundTask.wrap(this::getNewVersion)
+ .onSuccess(version -> showUpdateInfo(version, false))
+ .onFailure(exception -> showConnectionError(exception, false))
+ .scheduleWith(taskExecutor, 30, TimeUnit.SECONDS);
+ }
+
/**
* Prints the connection problem to the status bar and shows a dialog if it was executed manually
*/
diff --git a/src/main/java/org/jabref/gui/icon/IconTheme.java b/src/main/java/org/jabref/gui/icon/IconTheme.java
index 131292628cf..3dec2613770 100644
--- a/src/main/java/org/jabref/gui/icon/IconTheme.java
+++ b/src/main/java/org/jabref/gui/icon/IconTheme.java
@@ -18,7 +18,6 @@
import javafx.scene.image.Image;
import javafx.scene.paint.Color;
-import org.jabref.logic.groups.DefaultGroupsFactory;
import org.jabref.preferences.JabRefPreferences;
import de.jensd.fx.glyphs.GlyphIcons;
@@ -28,10 +27,6 @@
public class IconTheme {
- /**
- * JabRef's default color
- */
- public static final Color DEFAULT_COLOR = JabRefPreferences.getInstance().getColor(JabRefPreferences.ICON_ENABLED_COLOR);
public static final Color DEFAULT_DISABLED_COLOR = JabRefPreferences.getInstance().getColor(JabRefPreferences.ICON_DISABLED_COLOR);
public static final javafx.scene.paint.Color SELECTED_COLOR = javafx.scene.paint.Color.web("#50618F");
private static final String DEFAULT_ICON_PATH = "/images/external/red.png";
@@ -61,8 +56,8 @@ private static InputStream getJabRefMaterialDesignIconsStream() throws IOExcepti
return IconTheme.class.getResource("/fonts/JabRefMaterialDesign.ttf").openStream();
}
- public static Color getDefaultColor() {
- return DEFAULT_COLOR;
+ public static Color getDefaultGroupColor() {
+ return Color.web("#8a8a8a");
}
public static Image getJabRefImageFX() {
@@ -209,6 +204,7 @@ public enum JabRefIcons implements JabRefIcon {
EXPORT_TO_CLIPBOARD(MaterialDesignIcon.CLIPBOARD_ARROW_LEFT) /*css: clipboard-arrow-left */,
ATTACH_FILE(MaterialDesignIcon.PAPERCLIP) /*css: paperclip*/,
AUTO_FILE_LINK(MaterialDesignIcon.FILE_FIND) /*css: file-find */,
+ AUTO_LINKED_FILE(MaterialDesignIcon.BRIEFCASE_CHECK) /*css: briefcase-check */,
QUALITY_ASSURED(MaterialDesignIcon.CERTIFICATE), /*css: certificate */
QUALITY(MaterialDesignIcon.CERTIFICATE), /*css: certificate */
OPEN(MaterialDesignIcon.FOLDER_OUTLINE) /*css: folder */,
@@ -251,6 +247,7 @@ public enum JabRefIcons implements JabRefIcon {
FIND_DUPLICATES(MaterialDesignIcon.CODE_EQUAL), /*css: code-equal */
CONNECT_DB(MaterialDesignIcon.CLOUD_UPLOAD), /*cloud-upload*/
SUCCESS(MaterialDesignIcon.CHECK_CIRCLE),
+ CHECK(MaterialDesignIcon.CHECK) /*css: check */,
WARNING(MaterialDesignIcon.ALERT),
ERROR(MaterialDesignIcon.ALERT_CIRCLE),
CASE_SENSITIVE(MaterialDesignIcon.ALPHABETICAL), /* css: mdi-alphabetical */
@@ -263,13 +260,17 @@ public enum JabRefIcons implements JabRefIcon {
DATE_PICKER(MaterialDesignIcon.CALENDAR), /* css: calendar */
DEFAULT_GROUP_ICON_COLORED(MaterialDesignIcon.PLAY),
DEFAULT_GROUP_ICON(MaterialDesignIcon.LABEL_OUTLINE),
- ALL_ENTRIES_GROUP_ICON(DefaultGroupsFactory.ALL_ENTRIES_GROUP_DEFAULT_ICON),
+ ALL_ENTRIES_GROUP_ICON(MaterialDesignIcon.DATABASE),
IMPORT(MaterialDesignIcon.CALL_RECEIVED),
EXPORT(MaterialDesignIcon.CALL_MADE),
PREVIOUS_LEFT(MaterialDesignIcon.CHEVRON_LEFT),
PREVIOUS_UP(MaterialDesignIcon.CHEVRON_UP),
NEXT_RIGHT(MaterialDesignIcon.CHEVRON_RIGHT),
NEXT_DOWN(MaterialDesignIcon.CHEVRON_DOWN),
+ LIST_MOVE_LEFT(MaterialDesignIcon.CHEVRON_LEFT),
+ LIST_MOVE_UP(MaterialDesignIcon.CHEVRON_UP),
+ LIST_MOVE_RIGHT(MaterialDesignIcon.CHEVRON_RIGHT),
+ LIST_MOVE_DOWN(MaterialDesignIcon.CHEVRON_DOWN),
FIT_WIDTH(MaterialDesignIcon.ARROW_EXPAND_ALL),
FIT_SINGLE_PAGE(MaterialDesignIcon.NOTE),
ZOOM_OUT(MaterialDesignIcon.MAGNIFY_MINUS),
diff --git a/src/main/java/org/jabref/gui/importer/ImportCommand.java b/src/main/java/org/jabref/gui/importer/ImportCommand.java
index 9299dac7868..8eb8284ca35 100644
--- a/src/main/java/org/jabref/gui/importer/ImportCommand.java
+++ b/src/main/java/org/jabref/gui/importer/ImportCommand.java
@@ -11,6 +11,7 @@
import org.jabref.Globals;
import org.jabref.gui.DialogService;
import org.jabref.gui.JabRefFrame;
+import org.jabref.gui.StateManager;
import org.jabref.gui.actions.SimpleCommand;
import org.jabref.gui.util.FileDialogConfiguration;
import org.jabref.gui.util.FileFilterConverter;
@@ -18,6 +19,8 @@
import org.jabref.logic.l10n.Localization;
import org.jabref.preferences.JabRefPreferences;
+import static org.jabref.gui.actions.ActionHelper.needsDatabase;
+
/**
* Perform import operation
*/
@@ -30,9 +33,14 @@ public class ImportCommand extends SimpleCommand {
/**
* @param openInNew Indicate whether the entries should import into a new database or into the currently open one.
*/
- public ImportCommand(JabRefFrame frame, boolean openInNew) {
+ public ImportCommand(JabRefFrame frame, boolean openInNew, StateManager stateManager) {
this.frame = frame;
this.openInNew = openInNew;
+
+ if (!openInNew) {
+ this.executable.bind(needsDatabase(stateManager));
+ }
+
this.dialogService = frame.getDialogService();
}
diff --git a/src/main/java/org/jabref/gui/importer/ImportCustomEntryTypesDialog.fxml b/src/main/java/org/jabref/gui/importer/ImportCustomEntryTypesDialog.fxml
index ad1606546b8..791543daf23 100644
--- a/src/main/java/org/jabref/gui/importer/ImportCustomEntryTypesDialog.fxml
+++ b/src/main/java/org/jabref/gui/importer/ImportCustomEntryTypesDialog.fxml
@@ -9,7 +9,7 @@
xmlns="http://javafx.com/javafx/8.0.171"
fx:controller="org.jabref.gui.importer.ImportCustomEntryTypesDialog">
-
+
diff --git a/src/main/java/org/jabref/gui/importer/ImportCustomEntryTypesDialog.java b/src/main/java/org/jabref/gui/importer/ImportCustomEntryTypesDialog.java
index cc1aa40cab6..e61fa7910d0 100644
--- a/src/main/java/org/jabref/gui/importer/ImportCustomEntryTypesDialog.java
+++ b/src/main/java/org/jabref/gui/importer/ImportCustomEntryTypesDialog.java
@@ -45,13 +45,13 @@ public ImportCustomEntryTypesDialog(BibDatabaseMode mode, List cus
});
setTitle(Localization.lang("Custom entry types"));
-
}
@FXML
public void initialize() {
viewModel = new ImportCustomEntryTypesDialogViewModel(mode, customEntryTypes, preferencesService);
+ boxDifferentCustomization.visibleProperty().bind(Bindings.isNotEmpty(viewModel.differentCustomizations()));
boxDifferentCustomization.managedProperty().bind(Bindings.isNotEmpty(viewModel.differentCustomizations()));
unknownEntryTypesCheckList.setItems(viewModel.newTypes());
differentCustomizationCheckList.setItems(viewModel.differentCustomizations());
diff --git a/src/main/java/org/jabref/gui/importer/ImportCustomEntryTypesDialogViewModel.java b/src/main/java/org/jabref/gui/importer/ImportCustomEntryTypesDialogViewModel.java
index 7504ded4cf2..2695361c9d0 100644
--- a/src/main/java/org/jabref/gui/importer/ImportCustomEntryTypesDialogViewModel.java
+++ b/src/main/java/org/jabref/gui/importer/ImportCustomEntryTypesDialogViewModel.java
@@ -26,7 +26,7 @@ public ImportCustomEntryTypesDialogViewModel(BibDatabaseMode mode, List currentlyStoredType = Globals.entryTypesManager.enrich(customType.getType(), mode);
- if (!currentlyStoredType.isPresent()) {
+ if (currentlyStoredType.isEmpty()) {
newTypes.add(customType);
} else {
if (!EntryTypeFactory.isEqualNameAndFieldBased(customType, currentlyStoredType.get())) {
@@ -47,11 +47,11 @@ public ObservableList differentCustomizations() {
public void importBibEntryTypes(List checkedUnknownEntryTypes, List checkedDifferentEntryTypes) {
if (!checkedUnknownEntryTypes.isEmpty()) {
- checkedUnknownEntryTypes.forEach(type -> Globals.entryTypesManager.addCustomizedEntryType(type, mode));
+ checkedUnknownEntryTypes.forEach(type -> Globals.entryTypesManager.addCustomOrModifiedType(type, mode));
preferencesService.saveCustomEntryTypes();
}
if (!checkedDifferentEntryTypes.isEmpty()) {
- checkedUnknownEntryTypes.forEach(type -> Globals.entryTypesManager.addCustomizedEntryType(type, mode));
+ checkedUnknownEntryTypes.forEach(type -> Globals.entryTypesManager.addCustomOrModifiedType(type, mode));
preferencesService.saveCustomEntryTypes();
}
diff --git a/src/main/java/org/jabref/gui/importer/ImportEntriesDialog.java b/src/main/java/org/jabref/gui/importer/ImportEntriesDialog.java
index 59d7a6c29cf..1e8671150fb 100644
--- a/src/main/java/org/jabref/gui/importer/ImportEntriesDialog.java
+++ b/src/main/java/org/jabref/gui/importer/ImportEntriesDialog.java
@@ -1,5 +1,6 @@
package org.jabref.gui.importer;
+import java.util.EnumSet;
import java.util.List;
import javax.inject.Inject;
@@ -26,7 +27,6 @@
import org.jabref.gui.icon.IconTheme;
import org.jabref.gui.util.BackgroundTask;
import org.jabref.gui.util.BaseDialog;
-import org.jabref.gui.util.BindingsHelper;
import org.jabref.gui.util.NoSelectionModel;
import org.jabref.gui.util.TaskExecutor;
import org.jabref.gui.util.TextFlowLimited;
@@ -109,7 +109,6 @@ private void initialize() {
HBox.setHgrow(entryNode, Priority.ALWAYS);
HBox container = new HBox(entryNode, separator, addToggle);
container.getStyleClass().add("entry-container");
- BindingsHelper.includePseudoClassWhen(container, entrySelected, addToggle.selectedProperty());
BackgroundTask.wrap(() -> viewModel.hasDuplicate(entry)).onSuccess(duplicateFound -> {
if (duplicateFound) {
@@ -156,8 +155,11 @@ private Node getEntryNode(BibEntry entry) {
}
private IconTheme.JabRefIcons getIcon(EntryType type) {
- if (StandardEntryType.Book.equals(type)) {
+ EnumSet crossRefTypes = EnumSet.of(StandardEntryType.InBook, StandardEntryType.InProceedings, StandardEntryType.InCollection);
+ if (type == StandardEntryType.Book) {
return IconTheme.JabRefIcons.BOOK;
+ } else if (crossRefTypes.contains(type)) {
+ return IconTheme.JabRefIcons.OPEN_LINK;
}
return IconTheme.JabRefIcons.ARTICLE;
}
diff --git a/src/main/java/org/jabref/gui/importer/actions/CheckForNewEntryTypesAction.java b/src/main/java/org/jabref/gui/importer/actions/CheckForNewEntryTypesAction.java
index d65d23d34c3..867e2b8565b 100644
--- a/src/main/java/org/jabref/gui/importer/actions/CheckForNewEntryTypesAction.java
+++ b/src/main/java/org/jabref/gui/importer/actions/CheckForNewEntryTypesAction.java
@@ -27,7 +27,6 @@ public void performAction(BasePanel panel, ParserResult parserResult) {
ImportCustomEntryTypesDialog importBibEntryTypesDialog = new ImportCustomEntryTypesDialog(mode, getListOfUnknownAndUnequalCustomizations(parserResult));
importBibEntryTypesDialog.showAndWait();
-
}
private List getListOfUnknownAndUnequalCustomizations(ParserResult parserResult) {
@@ -35,7 +34,7 @@ private List getListOfUnknownAndUnequalCustomizations(ParserResult
return parserResult.getEntryTypes()
.stream()
- .filter(type -> Globals.entryTypesManager.isCustomizedType(type, mode))
+ .filter(type -> Globals.entryTypesManager.isDifferentCustomOrModifiedType(type, mode))
.collect(Collectors.toList());
}
diff --git a/src/main/java/org/jabref/gui/importer/fetcher/WebSearchPaneViewModel.java b/src/main/java/org/jabref/gui/importer/fetcher/WebSearchPaneViewModel.java
index b88a0cd5fe7..ab322e11b5f 100644
--- a/src/main/java/org/jabref/gui/importer/fetcher/WebSearchPaneViewModel.java
+++ b/src/main/java/org/jabref/gui/importer/fetcher/WebSearchPaneViewModel.java
@@ -1,7 +1,7 @@
package org.jabref.gui.importer.fetcher;
-import java.util.Comparator;
import java.util.List;
+import java.util.SortedSet;
import javafx.beans.property.ListProperty;
import javafx.beans.property.ObjectProperty;
@@ -18,7 +18,6 @@
import org.jabref.gui.util.BackgroundTask;
import org.jabref.logic.importer.ImportFormatPreferences;
import org.jabref.logic.importer.SearchBasedFetcher;
-import org.jabref.logic.importer.WebFetcher;
import org.jabref.logic.importer.WebFetchers;
import org.jabref.logic.l10n.Localization;
import org.jabref.model.entry.BibEntry;
@@ -40,8 +39,7 @@ public WebSearchPaneViewModel(ImportFormatPreferences importPreferences, JabRefF
this.frame = frame;
this.dialogService = dialogService;
- List allFetchers = WebFetchers.getSearchBasedFetchers(importPreferences);
- allFetchers.sort(Comparator.comparing(WebFetcher::getName));
+ SortedSet allFetchers = WebFetchers.getSearchBasedFetchers(importPreferences);
fetchers.setAll(allFetchers);
// Choose last-selected fetcher as default
@@ -97,7 +95,7 @@ public void search() {
BackgroundTask> task = BackgroundTask.wrap(() -> activeFetcher.performSearch(getQuery().trim()))
.withInitialMessage(Localization.lang("Processing %0", getQuery()));
- task.onFailure(ex -> dialogService.showErrorDialogAndWait(ex));
+ task.onFailure(dialogService::showErrorDialogAndWait);
ImportEntriesDialog dialog = new ImportEntriesDialog(frame.getCurrentBasePanel().getBibDatabaseContext(), task);
dialog.setTitle(activeFetcher.getName());
diff --git a/src/main/java/org/jabref/gui/keyboard/KeyBindingsDialogView.java b/src/main/java/org/jabref/gui/keyboard/KeyBindingsDialogView.java
index c58665b93e2..69528fc8927 100644
--- a/src/main/java/org/jabref/gui/keyboard/KeyBindingsDialogView.java
+++ b/src/main/java/org/jabref/gui/keyboard/KeyBindingsDialogView.java
@@ -29,7 +29,7 @@ public class KeyBindingsDialogView extends BaseDialog {
@FXML private TreeTableView keyBindingsTable;
@FXML private TreeTableColumn actionColumn;
@FXML private TreeTableColumn shortcutColumn;
- @FXML private TreeTableColumn resetColumn;
+ @FXML private TreeTableColumn resetColumn;
@Inject private KeyBindingRepository keyBindingRepository;
@Inject private DialogService dialogService;
@@ -65,10 +65,10 @@ private void initialize() {
);
actionColumn.setCellValueFactory(cellData -> cellData.getValue().getValue().nameProperty());
shortcutColumn.setCellValueFactory(cellData -> cellData.getValue().getValue().shownBindingProperty());
- resetColumn.setCellFactory(new ViewModelTreeTableCellFactory()
+ new ViewModelTreeTableCellFactory()
.withGraphic(keyBinding -> keyBinding.getIcon().map(JabRefIcon::getGraphicNode).orElse(null))
.withOnMouseClickedEvent(keyBinding -> evt -> keyBinding.resetToDefault())
- );
+ .install(resetColumn);
}
@FXML
diff --git a/src/main/java/org/jabref/gui/libraryproperties/LibraryPropertiesDialogView.java b/src/main/java/org/jabref/gui/libraryproperties/LibraryPropertiesDialogView.java
index b4c2a0a4694..42197cbc5a0 100644
--- a/src/main/java/org/jabref/gui/libraryproperties/LibraryPropertiesDialogView.java
+++ b/src/main/java/org/jabref/gui/libraryproperties/LibraryPropertiesDialogView.java
@@ -79,14 +79,9 @@ private void initialize() {
encoding.disableProperty().bind(viewModel.encodingDisableProperty());
protect.disableProperty().bind(viewModel.protectDisableProperty());
+ saveOrderConfigDisplayView = new SaveOrderConfigDisplayView();
Optional storedSaveOrderConfig = panel.getBibDatabaseContext().getMetaData().getSaveOrderConfig();
- if (storedSaveOrderConfig.isPresent()) {
- saveOrderConfigDisplayView = new SaveOrderConfigDisplayView(storedSaveOrderConfig.get());
- oldSaveOrderConfig = storedSaveOrderConfig.get();
- } else {
- oldSaveOrderConfig = preferencesService.loadExportSaveOrder();
- saveOrderConfigDisplayView = new SaveOrderConfigDisplayView(preferencesService.loadExportSaveOrder());
- }
+ oldSaveOrderConfig = storedSaveOrderConfig.orElseGet(preferencesService::loadExportSaveOrder);
saveOrderConfigDisplayView.changeExportDescriptionToSave();
fieldFormatterCleanupsPanel = new FieldFormatterCleanupsPanel(Localization.lang("Enable save actions"),
@@ -103,6 +98,7 @@ private void initialize() {
private void setValues() {
fieldFormatterCleanupsPanel.setValues(panel.getBibDatabaseContext().getMetaData());
+ saveOrderConfigDisplayView.setValues(oldSaveOrderConfig);
}
@FXML
diff --git a/src/main/java/org/jabref/gui/logging/ApplicationInsightsAppender.java b/src/main/java/org/jabref/gui/logging/ApplicationInsightsAppender.java
index 2f82d8f76ea..85a355a4692 100644
--- a/src/main/java/org/jabref/gui/logging/ApplicationInsightsAppender.java
+++ b/src/main/java/org/jabref/gui/logging/ApplicationInsightsAppender.java
@@ -3,30 +3,27 @@
import org.jabref.Globals;
import org.jabref.logic.logging.LogMessages;
-import com.microsoft.applicationinsights.log4j.v2.internal.ApplicationInsightsLogEvent;
import com.microsoft.applicationinsights.telemetry.ExceptionTelemetry;
import com.microsoft.applicationinsights.telemetry.Telemetry;
import com.microsoft.applicationinsights.telemetry.TraceTelemetry;
import org.apache.logging.log4j.core.Filter;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.appender.AbstractAppender;
-import org.apache.logging.log4j.core.config.plugins.Plugin;
-import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
-import org.apache.logging.log4j.core.config.plugins.PluginElement;
-import org.apache.logging.log4j.core.config.plugins.PluginFactory;
+import org.apache.logging.log4j.core.config.Property;
+import org.apache.logging.log4j.plugins.Plugin;
+import org.apache.logging.log4j.plugins.PluginFactory;
@Plugin(name = "OurApplicationInsightsAppender", category = "Core", elementType = "appender", printObject = true)
@SuppressWarnings("unused") // class is indirectly constructed by log4j
public class ApplicationInsightsAppender extends AbstractAppender {
- private ApplicationInsightsAppender(String name, Filter filter) {
- super(name, filter, null);
+ private ApplicationInsightsAppender(String name, Filter filter, boolean ignoreExceptions, Property[] properties) {
+ super(name, filter, null, ignoreExceptions, properties);
}
@PluginFactory
- public static ApplicationInsightsAppender createAppender(@PluginAttribute("name") String name,
- @PluginElement("Filters") Filter filter) {
- return new ApplicationInsightsAppender(name, filter);
+ public static > B newBuilder() {
+ return new Builder().asBuilder();
}
/**
@@ -51,4 +48,14 @@ public void append(LogEvent rawEvent) {
Globals.getTelemetryClient().ifPresent(client -> client.track(telemetry));
}
+
+ public static class Builder> extends AbstractAppender.Builder
+ implements org.apache.logging.log4j.plugins.util.Builder {
+
+ @Override
+ public ApplicationInsightsAppender build() {
+ return new ApplicationInsightsAppender(this.getName(), this.getFilter(), this.isIgnoreExceptions(), this.getPropertyArray());
+ }
+ }
+
}
diff --git a/src/main/java/org/jabref/gui/logging/ApplicationInsightsLogEvent.java b/src/main/java/org/jabref/gui/logging/ApplicationInsightsLogEvent.java
new file mode 100644
index 00000000000..7bc27dbabae
--- /dev/null
+++ b/src/main/java/org/jabref/gui/logging/ApplicationInsightsLogEvent.java
@@ -0,0 +1,130 @@
+package org.jabref.gui.logging;
+
+/*
+ * ApplicationInsights-Java
+ * Copyright (c) Microsoft Corporation
+ * All rights reserved.
+ *
+ * MIT License
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this
+ * software and associated documentation files (the ""Software""), to deal in the Software
+ * without restriction, including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software, and to permit
+ * persons to whom the Software is furnished to do so, subject to the following conditions:
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ * THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+ * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.microsoft.applicationinsights.internal.common.ApplicationInsightsEvent;
+import com.microsoft.applicationinsights.internal.logger.InternalLogger;
+import com.microsoft.applicationinsights.telemetry.SeverityLevel;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.spi.StandardLevel;
+
+// TODO: Remove this copy as soon as the one included in AI is compatible with log4j 3
+public final class ApplicationInsightsLogEvent extends ApplicationInsightsEvent {
+
+ private LogEvent logEvent;
+
+ public ApplicationInsightsLogEvent(LogEvent logEvent) {
+ this.logEvent = logEvent;
+ }
+
+ @Override
+ public String getMessage() {
+ String message = this.logEvent.getMessage() != null ?
+ this.logEvent.getMessage().getFormattedMessage() :
+ "Log4j Trace";
+
+ return message;
+ }
+
+ @Override
+ public boolean isException() {
+ return this.logEvent.getThrown() != null;
+ }
+
+ @Override
+ public Exception getException() {
+ Exception exception = null;
+
+ if (isException()) {
+ Throwable throwable = this.logEvent.getThrown();
+ exception = throwable instanceof Exception ? (Exception) throwable : new Exception(throwable);
+ }
+
+ return exception;
+ }
+
+ @Override
+ public Map getCustomParameters() {
+
+ Map metaData = new HashMap();
+
+ metaData.put("SourceType", "Log4j");
+
+ addLogEventProperty("LoggerName", logEvent.getLoggerName(), metaData);
+ addLogEventProperty("LoggingLevel", logEvent.getLevel() != null ? logEvent.getLevel().name() : null, metaData);
+ addLogEventProperty("ThreadName", logEvent.getThreadName(), metaData);
+ addLogEventProperty("TimeStamp", getFormattedDate(logEvent.getTimeMillis()), metaData);
+
+ if (isException()) {
+ addLogEventProperty("Logger Message", getMessage(), metaData);
+ }
+
+ if (logEvent.isIncludeLocation()) {
+ StackTraceElement stackTraceElement = logEvent.getSource();
+
+ addLogEventProperty("ClassName", stackTraceElement.getClassName(), metaData);
+ addLogEventProperty("FileName", stackTraceElement.getFileName(), metaData);
+ addLogEventProperty("MethodName", stackTraceElement.getMethodName(), metaData);
+ addLogEventProperty("LineNumber", String.valueOf(stackTraceElement.getLineNumber()), metaData);
+ }
+
+ for (Map.Entry entry : logEvent.getContextData().toMap().entrySet()) {
+ addLogEventProperty(entry.getKey(), entry.getValue(), metaData);
+ }
+
+ // TODO: Username, domain and identity should be included as in .NET version.
+ // TODO: Should check, seems that it is not included in Log4j2.
+
+ return metaData;
+ }
+
+ @Override
+ public SeverityLevel getNormalizedSeverityLevel() {
+ int log4jLevelAsInt = logEvent.getLevel().intLevel();
+
+ switch (StandardLevel.getStandardLevel(log4jLevelAsInt)) {
+ case FATAL:
+ return SeverityLevel.Critical;
+
+ case ERROR:
+ return SeverityLevel.Error;
+
+ case WARN:
+ return SeverityLevel.Warning;
+
+ case INFO:
+ return SeverityLevel.Information;
+
+ case TRACE:
+ case DEBUG:
+ case ALL:
+ return SeverityLevel.Verbose;
+
+ default:
+ InternalLogger.INSTANCE.error("Unknown Log4j v2 option, %d, using TRACE level as default", log4jLevelAsInt);
+ return SeverityLevel.Verbose;
+ }
+ }
+}
diff --git a/src/main/java/org/jabref/gui/logging/GuiAppender.java b/src/main/java/org/jabref/gui/logging/GuiAppender.java
index 4d5956b94c5..b6af87e9220 100644
--- a/src/main/java/org/jabref/gui/logging/GuiAppender.java
+++ b/src/main/java/org/jabref/gui/logging/GuiAppender.java
@@ -1,43 +1,27 @@
package org.jabref.gui.logging;
-import java.io.Serializable;
-
import org.jabref.gui.util.DefaultTaskExecutor;
import org.jabref.logic.logging.LogMessages;
import org.apache.logging.log4j.core.Filter;
-import org.apache.logging.log4j.core.Layout;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.appender.AbstractAppender;
-import org.apache.logging.log4j.core.config.plugins.Plugin;
-import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
-import org.apache.logging.log4j.core.config.plugins.PluginElement;
-import org.apache.logging.log4j.core.config.plugins.PluginFactory;
+import org.apache.logging.log4j.core.config.Property;
import org.apache.logging.log4j.core.impl.MutableLogEvent;
-import org.apache.logging.log4j.core.layout.PatternLayout;
+import org.apache.logging.log4j.plugins.Plugin;
+import org.apache.logging.log4j.plugins.PluginFactory;
@Plugin(name = "GuiAppender", category = "Core", elementType = "appender", printObject = true)
@SuppressWarnings("unused") // class is indirectly constructed by log4j
public class GuiAppender extends AbstractAppender {
- private GuiAppender(String name, Filter filter, Layout extends Serializable> layout) {
- super(name, filter, layout);
+ private GuiAppender(String name, Filter filter, boolean ignoreExceptions, Property[] properties) {
+ super(name, filter, null, ignoreExceptions, properties);
}
@PluginFactory
- public static GuiAppender createAppender(@PluginAttribute("name") String name,
- @PluginElement("Layout") Layout> layout,
- @PluginElement("Filters") Filter filter) {
-
- if (name == null) {
- LOGGER.error("No name provided for GuiAppender");
- return null;
- }
-
- if (layout == null) {
- layout = PatternLayout.createDefaultLayout();
- }
- return new GuiAppender(name, filter, layout);
+ public static > B newBuilder() {
+ return new GuiAppender.Builder().asBuilder();
}
/**
@@ -50,4 +34,13 @@ public void append(LogEvent event) {
copy.initFrom(event);
DefaultTaskExecutor.runInJavaFXThread(() -> LogMessages.getInstance().add(copy));
}
+
+ public static class Builder> extends AbstractAppender.Builder
+ implements org.apache.logging.log4j.plugins.util.Builder {
+
+ @Override
+ public GuiAppender build() {
+ return new GuiAppender(this.getName(), this.getFilter(), this.isIgnoreExceptions(), this.getPropertyArray());
+ }
+ }
}
diff --git a/src/main/java/org/jabref/gui/maintable/ColumnPreferences.java b/src/main/java/org/jabref/gui/maintable/ColumnPreferences.java
index c02ab26df61..f2a8364f7c7 100644
--- a/src/main/java/org/jabref/gui/maintable/ColumnPreferences.java
+++ b/src/main/java/org/jabref/gui/maintable/ColumnPreferences.java
@@ -5,29 +5,31 @@
import javafx.scene.control.TableColumn.SortType;
-import org.jabref.model.entry.field.SpecialField;
-
public class ColumnPreferences {
public static final double DEFAULT_FIELD_LENGTH = 100;
private final boolean showFileColumn;
private final boolean showUrlColumn;
private final boolean preferDoiOverUrl;
- private final boolean showEprintColumn;
- private final List normalColumns;
- private final List specialFieldColumns;
- private final List extraFileColumns;
+ private final boolean showEPrintColumn;
+ private final List columnNames;
+ private final boolean specialFieldsEnabled;
+ private final boolean autoSyncSpecialFieldsToKeyWords;
+ private final boolean serializeSpecialFields;
+ private final boolean extraFileColumnsEnabled;
private final Map columnWidths;
private final Map columnSortType;
- public ColumnPreferences(boolean showFileColumn, boolean showUrlColumn, boolean preferDoiOverUrl, boolean showEprintColumn, List normalColumns, List specialFieldColumns, List extraFileColumns, Map columnWidths, Map columnSortType) {
+ public ColumnPreferences(boolean showFileColumn, boolean showUrlColumn, boolean preferDoiOverUrl, boolean showEPrintColumn, List columnNames, boolean specialFieldsEnabled, boolean autoSyncSpecialFieldsToKeyWords, boolean serializeSpecialFields, boolean extraFileColumnsEnabled, Map columnWidths, Map columnSortType) {
this.showFileColumn = showFileColumn;
this.showUrlColumn = showUrlColumn;
this.preferDoiOverUrl = preferDoiOverUrl;
- this.showEprintColumn = showEprintColumn;
- this.normalColumns = normalColumns;
- this.specialFieldColumns = specialFieldColumns;
- this.extraFileColumns = extraFileColumns;
+ this.showEPrintColumn = showEPrintColumn;
+ this.columnNames = columnNames;
+ this.specialFieldsEnabled = specialFieldsEnabled;
+ this.autoSyncSpecialFieldsToKeyWords = autoSyncSpecialFieldsToKeyWords;
+ this.serializeSpecialFields = serializeSpecialFields;
+ this.extraFileColumnsEnabled = extraFileColumnsEnabled;
this.columnWidths = columnWidths;
this.columnSortType = columnSortType;
}
@@ -45,22 +47,30 @@ public boolean preferDoiOverUrl() {
}
public boolean showEprintColumn() {
- return showEprintColumn;
+ return showEPrintColumn;
}
- public List getExtraFileColumns() {
- return extraFileColumns;
+ public boolean getSpecialFieldsEnabled() { return specialFieldsEnabled; }
+
+ public boolean getAutoSyncSpecialFieldsToKeyWords() {
+ return autoSyncSpecialFieldsToKeyWords;
}
- public List getSpecialFieldColumns() {
- return specialFieldColumns;
+ public boolean getSerializeSpecialFields() {
+ return serializeSpecialFields;
+ }
+
+ public boolean getExtraFileColumnsEnabled() { return extraFileColumnsEnabled; }
+
+ public List getColumnNames() {
+ return columnNames;
}
- public List getNormalColumns() {
- return normalColumns;
+ public Map getColumnWidths() {
+ return columnWidths;
}
- public double getPrefColumnWidth(String columnName) {
+ public double getColumnWidth(String columnName) {
return columnWidths.getOrDefault(columnName, DEFAULT_FIELD_LENGTH);
}
diff --git a/src/main/java/org/jabref/gui/maintable/MainTable.css b/src/main/java/org/jabref/gui/maintable/MainTable.css
index 6e5bac6ed4e..5154c6fe7cc 100644
--- a/src/main/java/org/jabref/gui/maintable/MainTable.css
+++ b/src/main/java/org/jabref/gui/maintable/MainTable.css
@@ -17,6 +17,25 @@
-fx-fill: -jr-gray-2;
}
+.table-row-cell:dragOver-bottom {
+ -fx-border-color: -jr-drag-target;
+ -fx-border-width: 0 0 2 0;
+ -fx-padding: 0 0 -2 0;
+}
+
+.table-row-cell:dragOver-center {
+ -fx-border-color: -jr-drag-target;
+ -fx-border-width: 1 1 1 1;
+ -fx-padding: -1 -1 -1 -1;
+ -fx-background-color: -jr-drag-target-hover;
+}
+
+.table-row-cell:dragOver-top {
+ -fx-border-color: -jr-drag-target;
+ -fx-border-width: 2 0 0 0;
+ -fx-padding: -2 0 0 0;
+}
+
.rating > .container {
-fx-spacing: 2;
}
diff --git a/src/main/java/org/jabref/gui/maintable/MainTable.java b/src/main/java/org/jabref/gui/maintable/MainTable.java
index 6232d33a961..297bf4505f9 100644
--- a/src/main/java/org/jabref/gui/maintable/MainTable.java
+++ b/src/main/java/org/jabref/gui/maintable/MainTable.java
@@ -11,12 +11,10 @@
import javax.swing.undo.UndoManager;
import javafx.collections.ListChangeListener;
-import javafx.scene.control.ScrollPane;
import javafx.scene.control.SelectionMode;
import javafx.scene.control.TableRow;
import javafx.scene.control.TableView;
import javafx.scene.input.ClipboardContent;
-import javafx.scene.input.DataFormat;
import javafx.scene.input.DragEvent;
import javafx.scene.input.Dragboard;
import javafx.scene.input.KeyCode;
@@ -36,15 +34,18 @@
import org.jabref.gui.keyboard.KeyBindingRepository;
import org.jabref.gui.undo.NamedCompound;
import org.jabref.gui.undo.UndoableInsertEntry;
+import org.jabref.gui.util.ControlHelper;
import org.jabref.gui.util.CustomLocalDragboard;
+import org.jabref.gui.util.DefaultTaskExecutor;
import org.jabref.gui.util.ViewModelTableRowFactory;
import org.jabref.logic.l10n.Localization;
import org.jabref.logic.util.UpdateField;
-import org.jabref.logic.util.io.FileUtil;
import org.jabref.model.database.BibDatabaseContext;
+import org.jabref.model.database.event.AllInsertsFinishedEvent;
import org.jabref.model.entry.BibEntry;
import org.jabref.preferences.JabRefPreferences;
+import com.google.common.eventbus.Subscribe;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -54,7 +55,6 @@ public class MainTable extends TableView {
private final BasePanel panel;
- private final ScrollPane pane;
private final BibDatabaseContext database;
private final UndoManager undoManager;
@@ -69,6 +69,7 @@ public MainTable(MainTableDataModel model, JabRefFrame frame,
this.model = model;
this.database = Objects.requireNonNull(database);
+
this.undoManager = panel.getUndoManager();
importHandler = new ImportHandler(
@@ -92,6 +93,7 @@ public MainTable(MainTableDataModel model, JabRefFrame frame,
.setOnDragDetected(this::handleOnDragDetected)
.setOnDragDropped(this::handleOnDragDropped)
.setOnDragOver(this::handleOnDragOver)
+ .setOnDragExited(this::handleOnDragExited)
.setOnMouseDragEntered(this::handleOnDragEntered)
.install(this);
@@ -114,11 +116,7 @@ public MainTable(MainTableDataModel model, JabRefFrame frame,
this.panel = panel;
- pane = new ScrollPane(this);
- pane.setFitToHeight(true);
- pane.setFitToWidth(true);
-
- this.pane.getStylesheets().add(MainTable.class.getResource("MainTable.css").toExternalForm());
+ this.getStylesheets().add(MainTable.class.getResource("MainTable.css").toExternalForm());
// Store visual state
new PersistenceVisualStateTable(this, Globals.prefs);
@@ -127,6 +125,13 @@ public MainTable(MainTableDataModel model, JabRefFrame frame,
//model.updateMarkingState(Globals.prefs.getBoolean(JabRefPreferences.FLOAT_MARKED_ENTRIES));
setupKeyBindings(keyBindingRepository);
+
+ database.getDatabase().registerListener(this);
+ }
+
+ @Subscribe
+ public void listen(AllInsertsFinishedEvent event) {
+ DefaultTaskExecutor.runInJavaFXThread(() -> clearAndSelect(event.getBibEntry()));
}
public void clearAndSelect(BibEntry bibEntry) {
@@ -195,7 +200,7 @@ private void setupKeyBindings(KeyBindingRepository keyBindings) {
});
}
- private void clearAndSelectFirst() {
+ public void clearAndSelectFirst() {
getSelectionModel().clearSelection();
getSelectionModel().selectFirst();
scrollTo(0);
@@ -238,16 +243,12 @@ public void paste() {
}
}
- private void handleOnDragOver(BibEntryTableViewModel originalItem, DragEvent event) {
- if ((event.getGestureSource() != originalItem) && localDragboard.hasType(DragAndDropDataFormats.BIBENTRY_LIST_CLASS)) {
- event.acceptTransferModes(TransferMode.MOVE);
-
+ private void handleOnDragOver(TableRow row, BibEntryTableViewModel item, DragEvent event) {
+ if (event.getDragboard().hasFiles()) {
+ event.acceptTransferModes(TransferMode.ANY);
+ ControlHelper.setDroppingPseudoClasses(row, event);
}
- if (event.getDragboard().hasFiles() && (event.getSource() instanceof TableRow)) {
- event.acceptTransferModes(TransferMode.COPY, TransferMode.MOVE, TransferMode.LINK);
- }
- event.consume(); //need to consume it here to stop the DnDTabPane from getting the event
-
+ event.consume();
}
private void handleOnDragEntered(TableRow row, BibEntryTableViewModel entry, MouseDragEvent event) {
@@ -259,6 +260,10 @@ private void handleOnDragEntered(TableRow row, BibEntryT
getSelectionModel().selectRange(sourceRow.getIndex(), row.getIndex());
}
+ private void handleOnDragExited(TableRow row, BibEntryTableViewModel entry, DragEvent dragEvent) {
+ ControlHelper.removeDroppingPseudoClasses(row);
+ }
+
private void handleOnDragDetected(TableRow row, BibEntryTableViewModel entry, MouseEvent event) {
// Start drag'n'drop
row.startFullDrag();
@@ -279,41 +284,41 @@ private void handleOnDragDetected(TableRow row, BibEntry
event.consume();
}
- private void handleOnDragDropped(BibEntryTableViewModel originalItem, DragEvent event) {
+ private void handleOnDragDropped(TableRow row, BibEntryTableViewModel target, DragEvent event) {
boolean success = false;
- if (event.getDragboard().hasContent(DataFormat.FILES)) {
+ if (event.getDragboard().hasFiles()) {
List files = event.getDragboard().getFiles().stream().map(File::toPath).collect(Collectors.toList());
- List bibFiles = files.stream().filter(FileUtil::isBibFile).collect(Collectors.toList());
- if (!bibFiles.isEmpty()) {
- // Import all bibtex entries contained in the dropped bib files
- for (Path file : bibFiles) {
- importHandler.importEntriesFromBibFiles(file);
- }
- success = true;
- }
- if (event.getGestureTarget() instanceof TableRow) {
- // Depending on the pressed modifier, import as new entries or link to drop target
- BibEntry entry = originalItem.getEntry();
- if ((event.getTransferMode() == TransferMode.MOVE)) {
- LOGGER.debug("Mode MOVE"); //shift on win or no modifier
+ // Different actions depending on where the user releases the drop in the target row
+ // Bottom + top -> import entries
+ // Center -> link files to entry
+ switch (ControlHelper.getDroppingMouseLocation(row, event)) {
+ case TOP:
+ case BOTTOM:
importHandler.importAsNewEntries(files);
- success = true;
- }
-
- if (event.getTransferMode() == TransferMode.LINK) {
- LOGGER.debug("LINK"); //alt on win
- importHandler.getLinker().moveFilesToFileDirAndAddToEntry(entry, files);
- success = true;
- }
-
- if (event.getTransferMode() == TransferMode.COPY) {
- LOGGER.debug("Mode Copy"); //ctrl on win
- importHandler.getLinker().copyFilesToFileDirAndAddToEntry(entry, files);
- success = true;
- }
+ break;
+ case CENTER:
+ // Depending on the pressed modifier, move/copy/link files to drop target
+ BibEntry entry = target.getEntry();
+ switch (event.getTransferMode()) {
+ case LINK:
+ LOGGER.debug("Mode LINK"); //shift on win or no modifier
+ importHandler.getLinker().addFilesToEntry(entry, files);
+ break;
+ case MOVE:
+ LOGGER.debug("Mode MOVE"); //alt on win
+ importHandler.getLinker().moveFilesToFileDirAndAddToEntry(entry, files);
+ break;
+ case COPY:
+ LOGGER.debug("Mode Copy"); //ctrl on win
+ importHandler.getLinker().copyFilesToFileDirAndAddToEntry(entry, files);
+ break;
+ }
+ break;
}
+
+ success = true;
}
event.setDropCompleted(success);
@@ -324,10 +329,6 @@ public void addSelectionListener(ListChangeListener super BibEntryTableViewMod
getSelectionModel().getSelectedItems().addListener(listener);
}
- public ScrollPane getPane() {
- return pane;
- }
-
public MainTableDataModel getTableModel() {
return model;
}
diff --git a/src/main/java/org/jabref/gui/maintable/MainTableColumnFactory.java b/src/main/java/org/jabref/gui/maintable/MainTableColumnFactory.java
index 2c2845638e5..0ed9b632c2a 100644
--- a/src/main/java/org/jabref/gui/maintable/MainTableColumnFactory.java
+++ b/src/main/java/org/jabref/gui/maintable/MainTableColumnFactory.java
@@ -9,6 +9,7 @@
import javax.swing.undo.UndoManager;
+import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.control.ContextMenu;
@@ -17,8 +18,8 @@
import javafx.scene.control.Tooltip;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
+import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
-import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
@@ -33,6 +34,7 @@
import org.jabref.gui.icon.JabRefIcon;
import org.jabref.gui.specialfields.SpecialFieldValueViewModel;
import org.jabref.gui.specialfields.SpecialFieldViewModel;
+import org.jabref.gui.util.FieldsUtil;
import org.jabref.gui.util.OptionalValueTableCellFactory;
import org.jabref.gui.util.ValueTableCellFactory;
import org.jabref.gui.util.comparator.RankingFieldComparator;
@@ -81,12 +83,11 @@ public MainTableColumnFactory(BibDatabaseContext database, ColumnPreferences pre
public List> createColumns() {
List> columns = new ArrayList<>();
columns.add(createGroupColumn());
- // Add column for linked files
+
if (preferences.showFileColumn()) {
columns.add(createFileColumn());
}
- // Add column for DOI/URL
if (preferences.showUrlColumn()) {
if (preferences.preferDoiOverUrl()) {
columns.add(createUrlOrDoiColumn(IconTheme.JabRefIcons.DOI, StandardField.DOI, StandardField.URL));
@@ -95,21 +96,19 @@ public MainTableColumnFactory(BibDatabaseContext database, ColumnPreferences pre
}
}
- // Add column for eprints
if (preferences.showEprintColumn()) {
columns.add(createEprintColumn(IconTheme.JabRefIcons.WWW, StandardField.EPRINT));
}
- // Add columns for other file types
- columns.addAll(preferences.getExtraFileColumns().stream().map(this::createExtraFileColumn).collect(Collectors.toList()));
-
- // Add 'normal' bibtex fields as configured in the preferences
- columns.addAll(createNormalColumns());
-
- // Add the "special" icon columns (e.g., ranking, file, ...) that are enabled in preferences
- for (SpecialField field : preferences.getSpecialFieldColumns()) {
- columns.add(createSpecialFieldColumn((field)));
- }
+ preferences.getColumnNames().stream().map(FieldFactory::parseField).forEach(field -> {
+ if (field instanceof FieldsUtil.ExtraFilePseudoField) {
+ columns.add(createExtraFileColumn(field.getName()));
+ } else if (field instanceof SpecialField) {
+ columns.add(createSpecialFieldColumn((SpecialField) field));
+ } else {
+ columns.add(createNormalColumn(field));
+ }
+ });
return columns;
}
@@ -131,27 +130,27 @@ public MainTableColumnFactory(BibDatabaseContext database, ColumnPreferences pre
}
private Node createGroupColorRegion(BibEntryTableViewModel entry, List matchedGroups) {
- Color groupColor = matchedGroups.stream()
- .flatMap(group -> OptionalUtil.toStream(group.getColor()))
- .findFirst()
- .orElse(Color.TRANSPARENT);
-
- if (groupColor != Color.TRANSPARENT) {
- Rectangle border = new Rectangle();
- border.setWidth(5);
- border.setHeight(20);
- border.setFill(Color.DARKGRAY);
-
- Rectangle groupRectangle = new Rectangle();
- groupRectangle.setWidth(3);
- groupRectangle.setHeight(18);
- groupRectangle.setFill(groupColor);
-
- StackPane container = new StackPane();
- container.setMinWidth(10);
- container.setAlignment(Pos.CENTER);
+ List groupColors = matchedGroups.stream()
+ .flatMap(group -> OptionalUtil.toStream(group.getColor()))
+ .collect(Collectors.toList());
- container.getChildren().addAll(border,groupRectangle);
+ if (!groupColors.isEmpty()) {
+ HBox container = new HBox();
+ container.setSpacing(2);
+ container.setMinWidth(10);
+ container.setAlignment(Pos.CENTER_LEFT);
+ container.setPadding(new Insets(0, 2, 0, 2));
+
+ for (Color groupColor : groupColors) {
+ Rectangle groupRectangle = new Rectangle();
+ groupRectangle.setWidth(3);
+ groupRectangle.setHeight(18);
+ groupRectangle.setFill(groupColor);
+ groupRectangle.setStroke(Color.DARKGRAY);
+ groupRectangle.setStrokeWidth(1);
+
+ container.getChildren().add(groupRectangle);
+ }
String matchedGroupsString = matchedGroups.stream()
.map(AbstractGroup::getName)
@@ -163,20 +162,15 @@ private Node createGroupColorRegion(BibEntryTableViewModel entry, List