diff --git a/CHANGELOG.md b/CHANGELOG.md index 33d3ac00004..8807730b5bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,7 @@ For more details refer to the [field mapping help page](http://help.jabref.org/e - We added Facebook and Twitter icons in the toolbar to link to our [Facebook](https://www.facebook.com/JabRef/) and [Twitter](https://twitter.com/jabref_org) pages. - Renamed the _Review_ Tab into _Comments_ Tab - We no longer print empty lines when exporting an entry in RIS format [#3634](https://github.com/JabRef/jabref/issues/3634) +- We added the option to download linked URLs in the context menu in the entry editor. - We improved file saving so that hard links are now preserved when a save is performed [#2633](https://github.com/JabRef/jabref/issues/2633) - We changed the default dialog option when removing a [file link](http://help.jabref.org/en/FileLinks#adding-external-links-to-an-entry) from an entry. The new default removes the linked file from the entry instead of deleting the file from disk. [#3679](https://github.com/JabRef/jabref/issues/3679) @@ -40,6 +41,8 @@ The new default removes the linked file from the entry instead of deleting the f - Pressing ESC while searching will clear the search field and select the first entry, if available, in the table. [koppor#293](https://github.com/koppor/jabref/issues/293) - We changed the metadata reading and writing. DublinCore is now the only metadata format, JabRef supports. (https://github.com/JabRef/jabref/pull/3710) - We added another CLI functionality for reading and writing metadata to pdfs. (see https://github.com/JabRef/jabref/pull/3756 and see http://help.jabref.org/en/CommandLine) +- We no longer print errors in field values during autosave into the log [#3811](https://github.com/JabRef/jabref/issues/3811) + ### Fixed - We fixed several performance problems with the management of journal abbreviations [#3323](https://github.com/JabRef/jabref/issues/3323) @@ -47,6 +50,7 @@ The new default removes the linked file from the entry instead of deleting the f - We fixed an issue where pressing space caused the cursor to jump to the start of the text field. [#3471](https://github.com/JabRef/jabref/issues/3471) - We fixed the missing dot in the name of an exported file. [#3576](https://github.com/JabRef/jabref/issues/3576) - Autocompletion in the search bar can now be disabled via the preferences. [#3598](https://github.com/JabRef/jabref/issues/3598) +- We fixed an issue where the progress of an ongoing file download was not shown correctly. [#3614](https://github.com/JabRef/jabref/issues/3614) - We fixed an issue where odd linked files could not be selected in the entry editor. [#3639](https://github.com/JabRef/jabref/issues/3639) - We fixed and extended the RIS import functionality to cover more fields. [#3634](https://github.com/JabRef/jabref/issues/3634) [#2607](https://github.com/JabRef/jabref/issues/2607) - Chaining modifiers in BibTeX key pattern now works as described in the documentation. [#3648](https://github.com/JabRef/jabref/issues/3648) diff --git a/build.gradle b/build.gradle index dd891366ede..5729c590792 100644 --- a/build.gradle +++ b/build.gradle @@ -85,6 +85,7 @@ configurations { } dependencies { + // Include all jar-files in the 'lib' folder as dependencies compile fileTree(dir: 'lib', includes: ['*.jar']) compile 'com.jgoodies:jgoodies-common:1.8.1' @@ -123,7 +124,6 @@ dependencies { compile 'com.google.guava:guava:24.1-jre' // JavaFX stuff - compile 'com.airhacks:afterburner.fx:1.7.0' compile 'de.codecentric.centerdevice:javafxsvg:1.3.0' compile 'de.jensd:fontawesomefx-materialdesignfont:1.7.22-4' compile 'de.saxsys:mvvmfx-validation:1.7.0' @@ -132,6 +132,7 @@ dependencies { compile 'org.fxmisc.flowless:flowless:0.6' compile 'org.fxmisc.richtext:richtextfx:0.8.2' compile 'com.sibvisions.external.jvxfx:dndtabpane:0.1' + compile 'javax.inject:javax.inject:1' // Cannot be updated to 9.*.* until Jabref works with Java 9 @@ -141,7 +142,7 @@ dependencies { compile 'com.mashape.unirest:unirest-java:1.4.9' // >1.8.0-beta is required for java 9 compatibility - compile 'org.slf4j:slf4j-api:1.8.0-beta1' + compile 'org.slf4j:slf4j-api:1.8.0-beta2' compile 'org.apache.logging.log4j:log4j-slf4j-impl:2.11.0' compile 'org.apache.logging.log4j:log4j-jcl:2.11.0' compile 'org.apache.logging.log4j:log4j-api:2.11.0' diff --git a/docs/adr/0000-use-markdown-architectural-decision-records.md b/docs/adr/0000-use-markdown-architectural-decision-records.md index 3f3f2a283f4..be7d1ed5fa8 100644 --- a/docs/adr/0000-use-markdown-architectural-decision-records.md +++ b/docs/adr/0000-use-markdown-architectural-decision-records.md @@ -1,23 +1,25 @@ # Use Markdown Architectural Decision Records -Should we record the architectural decisions made in this project? -And if we do, wow to structure these recordings? +## Context and Problem Statement -## Considered Alternatives +We want to record architectural decisions made in this project. +Which format and structure should these records follow? -* [MADR](https://adr.github.io/madr/) - Markdown Architectural Decision Records -* [Michael Nygard's template](http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions) - The first incarnation of the term "ADR". Maintainable by [adr-tools](https://github.com/npryce/adr-tools). +## Considered Options + +* [MADR](https://adr.github.io/madr/) 2.0.3 - The Markdown Architectural Decision Records +* [Michael Nygard's template](http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions) - The first incarnation of the term "ADR" * [Sustainable Architectural Decisions](https://www.infoq.com/articles/sustainable-architectural-design-decisions) - The Y-Statements -* [DecisionRecord](https://github.com/schubmat/DecisionCapture) - Agile records by [@schubmat](https://github.com/schubmat/) * Other templates listed at -* No records +* Formless - No conventions for file format and structure ## Decision Outcome -* Chosen Alternative: MADR +Chosen option: "MADR 2.0.3", because * Implicit assumptions should be made explicit. Design documentation is important to enable people understanding the decisions later on. See also [A rational design process: How and why to fake it](https://doi.org/10.1109/TSE.1986.6312940). -* The MADR template is lean and fits our development style. - - +* The MADR format is lean and fits our development style. +* The MADR structure is comprehensible and facilitates usage & maintenance. +* The MADR project is vivid. +* Version 2.0.3 is the latest one available when starting to document ADRs. diff --git a/docs/adr/0001-use-crowdin-for-translations.md b/docs/adr/0001-use-crowdin-for-translations.md index c98ae57d733..0d8955782ab 100644 --- a/docs/adr/0001-use-crowdin-for-translations.md +++ b/docs/adr/0001-use-crowdin-for-translations.md @@ -1,14 +1,16 @@ -# Use Crowdin for tranlsations +# Use Crowdin for translations + +## Context and Problem Statement The JabRef UI is offered in multiple languages. It should be easy for translators to translate the strings. -## Considered Alternatives +## Considered Options * Use [Crowdin](http://crowdin.com/) * Use [popeye](https://github.com/JabRef/popeye) * Use [Lingohub](https://lingohub.com/) +* Keep current GitHub flow. See the [Step-by-step guide](https://github.com/JabRef/help.jabref.org/blob/d5569bebdb0e1de5c71401bbfba45311c19c80a8/en/TranslatingGUI.md#step-by-step-guide). ## Decision Outcome -* Chosen Alternative: Crowdin -* Crowdin is easy to use, integrates in our GitHub workflow, and is free for OSS projects. +Chosen option: "Use Crowdin", because Crowdin is easy to use, integrates in our GitHub workflow, and is free for OSS projects. diff --git a/docs/adr/0002-use-slf4j-for-logging.md b/docs/adr/0002-use-slf4j-for-logging.md index b9aabc823d0..b17996fe59d 100644 --- a/docs/adr/0002-use-slf4j-for-logging.md +++ b/docs/adr/0002-use-slf4j-for-logging.md @@ -1,7 +1,13 @@ # Use slf4j together with log4j2 for logging -* Up to version 4.1 JabRef uses apache-ommons-logging 1.2 for logging errors and messages. However, this is not compatible with java 9 and is superseded by log4j. -* SLF4J provides a facade for several logging frameworks, including log4j and supports already java 9 +## Context and Problem Statement + +Up to version 4.1 JabRef uses apache-commons-logging 1.2 for logging errors and messages. +However, this is not compatible with java 9 and is superseded by log4j. + +## Decision Drivers + +* SLF4J provides a façade for several logging frameworks, including log4j and supports already java 9 * Log4j is already defined as dependency and slf4j has already been required by a third party dependency ## Considered Alternatives @@ -11,33 +17,34 @@ * [SLF4J with Logback binding](https://logback.qos.ch/) ## Decision Outcome -* We chose slf4j with log4j2 binding, because it only requires minimal changes to our logging infrastructure and it is claimed that -> Apache Log4j 2 is an upgrade to Log4j that provides significant improvements over its predecessor, Log4j 1.x, and provides many of the improvements available in Logback while fixing some inherent problems in Logback’s architecture. -* Furthermore, as slf4j is a facade for several loggers, the underlying implementation can easily be changed in the future +Chosen option: "SLF4J with Log4j2 binding", because comes out best (see below). -## Pros and Cons of the Alternatives +## Pros and Cons of the Options ### Log4j2 -* `+` Dependency already exists -* `+` Java 9 support since version 2.10 -* `-` Direct dependency +* Good, because dependency already exists +* Good, because Java 9 support since version 2.10 +* Bad, because direct dependency ### SLF4J with log4j2 binding -* `+` Supports other loggers as well -* `+` Java 9 support -* `+` Already defined -* `+` Migration tool available -* `-` Logger statements require a slight different syntax +* Good, because it only requires minimal changes to our logging infrastructure +* Good, because Apache Log4j 2 is an upgrade to Log4j that provides significant improvements over its predecessor, Log4j 1.x, and provides many of the improvements available in Logback while fixing some inherent problems in Logback’s architecture. +* Good, because supports other loggers as well +* Good, because Java 9 support +* Good, because already defined +* Good, because migration tool available +* Good, because it is a façade for several loggers. Thus, the underlying implementation can easily be changed in the future. +* Bad, because logger statements require a slight different syntax ### SLF4J with Logback binding -* `+` Migration tool available -* `+` Native implementation of slf4j -* `-` Java 9 support only available in alpha -* `-` Different syntax than log4j/commons logging +* Good, because migration tool available +* Good, because native implementation of slf4j +* Bad, because Java 9 support only available in alpha +* Bad, because different syntax than log4j/commons logging diff --git a/docs/adr/index.md b/docs/adr/index.md index c622674a03b..98c4e216f4d 100644 --- a/docs/adr/index.md +++ b/docs/adr/index.md @@ -5,6 +5,8 @@ This log lists the architectural decisions for JabRef. - [ADR-0000](0000-use-markdown-architectural-decision-records.md) - Use Markdown Architectural Decision Records +- [ADR-0001](0001-use-crowdin-for-translations.md) - Use Crowdin for translations +- [ADR-0002](0002-use-slf4j-for-logging.md) - Use slf4j together with log4j2 for logging diff --git a/docs/adr/template.md b/docs/adr/template.md index 7402da4d20e..e4659b49b2b 100644 --- a/docs/adr/template.md +++ b/docs/adr/template.md @@ -1,75 +1,70 @@ # [short title of solved problem and solution] -* Status: [accepted | superseeded by [ADR-0005](0005-example.md) | deprecated | ...] +* Status: [accepted | superseeded by [ADR-0005](0005-example.md) | deprecated | …] * Deciders: [list everyone involved in the decision] -* Date: [when the decision was last updated] +* Date: [YYYY-MM-DD when the decision was last updated] Technical Story: [description | ticket/issue URL] - ## Context and Problem Statement -[Describe the context and problem statement, e.g., in free form using two to three sentences. You may want to articulate the problem in form of a question.] +[Describe the context and problem statement, e.g., in free form using two to three sentences. You may want to articulate the problem in form of a question.] ## Decision Drivers -* [driver 1, e.g., a force, facing concern, ...] -* [driver 2, e.g., a force, facing concern, ...] -* ... - +* [driver 1, e.g., a force, facing concern, …] +* [driver 2, e.g., a force, facing concern, …] +* … ## Considered Options * [option 1] * [option 2] * [option 3] -* ... - +* … ## Decision Outcome -Chosen option: "[option 1]", because [justification. e.g., only option, which meets k.o. criterion decision driver | which resolves force force | ... | comes out best (see below)]. +Chosen option: "[option 1]", because [justification. e.g., only option, which meets k.o. criterion decision driver | which resolves force force | … | comes out best (see below)]. Positive Consequences: -* [e.g., improvement of quality attribute satisfaction, follow-up decisions required, ...] -* ... +* [e.g., improvement of quality attribute satisfaction, follow-up decisions required, …] +* … Negative consequences: -* [e.g., compromising quality attribute, follow-up decisions required, ...] -* ... - +* [e.g., compromising quality attribute, follow-up decisions required, …] +* … ## Pros and Cons of the Options ### [option 1] -[example | description | pointer to more information | ...] +[example | description | pointer to more information | …] * Good, because [argument a] * Good, because [argument b] * Bad, because [argument c] -* ... +* … ### [option 2] -[example | description | pointer to more information | ...] +[example | description | pointer to more information | …] * Good, because [argument a] * Good, because [argument b] * Bad, because [argument c] -* ... +* … ### [option 3] -[example | description | pointer to more information | ...] +[example | description | pointer to more information | …] * Good, because [argument a] * Good, because [argument b] * Bad, because [argument c] -* ... - +* … ## Links * [Link type] [Link to ADR] -* ... +* … diff --git a/javafx/scene/control/annotations.xml b/javafx/scene/control/annotations.xml new file mode 100644 index 00000000000..240687d556c --- /dev/null +++ b/javafx/scene/control/annotations.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/lib/afterburner.fx.jar b/lib/afterburner.fx.jar new file mode 100644 index 00000000000..816b3e27f5d Binary files /dev/null and b/lib/afterburner.fx.jar differ diff --git a/src/main/java/org/jabref/Globals.java b/src/main/java/org/jabref/Globals.java index 652a79ef828..69476160034 100644 --- a/src/main/java/org/jabref/Globals.java +++ b/src/main/java/org/jabref/Globals.java @@ -12,6 +12,7 @@ import org.jabref.gui.util.DefaultFileUpdateMonitor; import org.jabref.gui.util.DefaultTaskExecutor; import org.jabref.gui.util.TaskExecutor; +import org.jabref.gui.util.ThemeLoader; import org.jabref.logic.exporter.ExporterFactory; import org.jabref.logic.importer.ImportFormatReader; import org.jabref.logic.journals.JournalAbbreviationLoader; @@ -61,6 +62,7 @@ public class Globals { // Background tasks private static GlobalFocusListener focusListener; private static DefaultFileUpdateMonitor fileUpdateMonitor; + private static ThemeLoader themeLoader; private static TelemetryClient telemetryClient; private Globals() { @@ -81,6 +83,8 @@ public static void startBackgroundTasks() { Globals.fileUpdateMonitor = new DefaultFileUpdateMonitor(); JabRefExecutorService.INSTANCE.executeInterruptableTask(Globals.fileUpdateMonitor, "FileUpdateMonitor"); + themeLoader = new ThemeLoader(fileUpdateMonitor); + if (Globals.prefs.shouldCollectTelemetry() && !GraphicsEnvironment.isHeadless()) { startTelemetryClient(); } @@ -130,4 +134,8 @@ public static void stopBackgroundTasks() { public static Optional getTelemetryClient() { return Optional.ofNullable(telemetryClient); } + + public static ThemeLoader getThemeLoader() { + return themeLoader; + } } diff --git a/src/main/java/org/jabref/JabRefGUI.java b/src/main/java/org/jabref/JabRefGUI.java index 7a3acdedecf..c7c287a2073 100644 --- a/src/main/java/org/jabref/JabRefGUI.java +++ b/src/main/java/org/jabref/JabRefGUI.java @@ -18,14 +18,13 @@ import javafx.scene.Scene; import javafx.stage.Stage; -import org.jabref.gui.AbstractView; import org.jabref.gui.BasePanel; import org.jabref.gui.DialogService; import org.jabref.gui.FXDialogService; import org.jabref.gui.GUIGlobals; -import org.jabref.gui.IconTheme; import org.jabref.gui.JabRefFrame; import org.jabref.gui.dialogs.BackupUIManager; +import org.jabref.gui.icon.IconTheme; import org.jabref.gui.importer.ParserResultWarningDialog; import org.jabref.gui.importer.actions.OpenDatabaseAction; import org.jabref.gui.shared.SharedDatabaseUIManager; @@ -150,7 +149,7 @@ private void openWindow(Stage mainStage) { } Scene scene = new Scene(JabRefGUI.mainFrame, 800, 800); - scene.getStylesheets().add(AbstractView.class.getResource("Main.css").toExternalForm()); + Globals.getThemeLoader().installBaseCss(scene); mainStage.setTitle(JabRefFrame.FRAME_TITLE); mainStage.getIcons().addAll(IconTheme.getLogoSetFX()); mainStage.setScene(scene); diff --git a/src/main/java/org/jabref/gui/AbstractController.java b/src/main/java/org/jabref/gui/AbstractController.java deleted file mode 100644 index 53ad25c113b..00000000000 --- a/src/main/java/org/jabref/gui/AbstractController.java +++ /dev/null @@ -1,35 +0,0 @@ -package org.jabref.gui; - -import java.util.Objects; - -import javafx.fxml.FXML; -import javafx.fxml.FXMLLoader; -import javafx.stage.Stage; - -public class AbstractController { - - @FXML protected T viewModel; - private Stage stage; - - /** - * Gets the associated view model. - * - * Without this method the {@link FXMLLoader} is not able to resolve references in the fxml file of the form - * text="${controller.viewModel.someProperty}" - */ - public T getViewModel() { - return viewModel; - } - - /** - * Returns the stage where this controller is displayed. - * The stage can be used to e.g. close the dialog. - */ - public Stage getStage() { - return stage; - } - - public void setStage(Stage stage) { - this.stage = Objects.requireNonNull(stage); - } -} diff --git a/src/main/java/org/jabref/gui/AbstractDialogView.java b/src/main/java/org/jabref/gui/AbstractDialogView.java deleted file mode 100644 index d7880e3cd11..00000000000 --- a/src/main/java/org/jabref/gui/AbstractDialogView.java +++ /dev/null @@ -1,16 +0,0 @@ -package org.jabref.gui; - -import java.util.function.Function; - -public abstract class AbstractDialogView extends AbstractView { - - public AbstractDialogView() { - super(); - } - - public AbstractDialogView(Function injectionContext) { - super(injectionContext); - } - - public abstract void show(); -} diff --git a/src/main/java/org/jabref/gui/AbstractView.java b/src/main/java/org/jabref/gui/AbstractView.java deleted file mode 100644 index 055d5a69d0e..00000000000 --- a/src/main/java/org/jabref/gui/AbstractView.java +++ /dev/null @@ -1,49 +0,0 @@ -package org.jabref.gui; - -import java.util.Optional; -import java.util.function.Function; - -import javafx.scene.Parent; -import javafx.stage.Stage; - -import org.jabref.logic.l10n.Localization; - -import com.airhacks.afterburner.views.FXMLView; - -public class AbstractView extends FXMLView { - - public AbstractView() { - this(f -> null); - } - - public AbstractView(Function injectionContext) { - super(injectionContext); - - // Set resource bundle to internal localizations - bundle = Localization.getMessages(); - } - - @Override - public Parent getView() { - Parent view = super.getView(); - - // Add our base css file - view.getStylesheets().add(0, AbstractDialogView.class.getResource("Main.css").toExternalForm()); - - // Notify controller about the stage, where it is displayed - view.sceneProperty().addListener((observable, oldValue, newValue) -> { - if (newValue != null && newValue.getWindow() instanceof Stage) { - Stage stage = (Stage) newValue.getWindow(); - if (stage != null) { - getController().ifPresent(controller -> controller.setStage(stage)); - } - } - }); - return view; - } - - private Optional getController() { - return Optional.ofNullable(presenterProperty.get()).map( - presenter -> (AbstractController) presenter); - } -} diff --git a/src/main/java/org/jabref/gui/BasePanel.java b/src/main/java/org/jabref/gui/BasePanel.java index e5e947cc9c6..959e7508b64 100644 --- a/src/main/java/org/jabref/gui/BasePanel.java +++ b/src/main/java/org/jabref/gui/BasePanel.java @@ -60,7 +60,6 @@ import org.jabref.gui.externalfiletype.ExternalFileMenuItem; import org.jabref.gui.externalfiletype.ExternalFileType; import org.jabref.gui.externalfiletype.ExternalFileTypes; -import org.jabref.gui.filelist.AttachFileAction; import org.jabref.gui.filelist.FileListEntry; import org.jabref.gui.filelist.FileListTableModel; import org.jabref.gui.groups.GroupAddRemoveDialog; @@ -292,9 +291,7 @@ public String getTabTitle() { } } } else if (databaseLocation == DatabaseLocation.SHARED) { - title.append( - this.bibDatabaseContext.getDBMSSynchronizer().getDBName() + " [" + Localization.lang("shared") - + "]"); + title.append(this.bibDatabaseContext.getDBMSSynchronizer().getDBName() + " [" + Localization.lang("shared") + "]"); } return title.toString(); @@ -386,9 +383,8 @@ public void init() { numSelected = entries.size(); if (entries.isEmpty()) { // None selected. Inform the user to select entries first. - dialogService.showWarningDialogAndWait( - Localization.lang("Autogenerate BibTeX keys"), - Localization.lang("First select the entries you want keys to be generated for.")); + dialogService.showWarningDialogAndWait(Localization.lang("Autogenerate BibTeX keys"), + Localization.lang("First select the entries you want keys to be generated for.")); return; } output(formatOutputMessage(Localization.lang("Generating BibTeX key for"), numSelected)); @@ -405,12 +401,13 @@ public void run() { } else if (Globals.prefs.getBoolean(JabRefPreferences.WARN_BEFORE_OVERWRITING_KEY)) { if (entries.parallelStream().anyMatch(BibEntry::hasCiteKey)) { - boolean overwriteKeysPressed = dialogService.showConfirmationDialogWithOptOutAndWait(Localization.lang("Overwrite keys"), - Localization.lang("One or more keys will be overwritten. Continue?"), - Localization.lang("Overwrite keys"), - Localization.lang("Cancel"), - Localization.lang("Disable this confirmation dialog"), - optOut -> Globals.prefs.putBoolean(JabRefPreferences.WARN_BEFORE_OVERWRITING_KEY, !optOut)); + boolean overwriteKeysPressed = dialogService.showConfirmationDialogWithOptOutAndWait( + Localization.lang("Overwrite keys"), + Localization.lang("One or more keys will be overwritten. Continue?"), + Localization.lang("Overwrite keys"), + Localization.lang("Cancel"), + Localization.lang("Disable this confirmation dialog"), + optOut -> Globals.prefs.putBoolean(JabRefPreferences.WARN_BEFORE_OVERWRITING_KEY, !optOut)); // The user doesn't want to overide cite keys if (!overwriteKeysPressed) { @@ -467,29 +464,21 @@ public void update() { // The action for copying the BibTeX key and the title for the first selected entry actions.put(Actions.COPY_KEY_AND_TITLE, (BaseAction) () -> copyKeyAndTitle()); - actions.put(Actions.COPY_CITATION_ASCII_DOC, - (BaseAction) () -> copyCitationToClipboard(CitationStyleOutputFormat.ASCII_DOC)); - actions.put(Actions.COPY_CITATION_XSLFO, - (BaseAction) () -> copyCitationToClipboard(CitationStyleOutputFormat.XSL_FO)); - actions.put(Actions.COPY_CITATION_HTML, - (BaseAction) () -> copyCitationToClipboard(CitationStyleOutputFormat.HTML)); - actions.put(Actions.COPY_CITATION_RTF, - (BaseAction) () -> copyCitationToClipboard(CitationStyleOutputFormat.RTF)); - actions.put(Actions.COPY_CITATION_TEXT, - (BaseAction) () -> copyCitationToClipboard(CitationStyleOutputFormat.TEXT)); + actions.put(Actions.COPY_CITATION_ASCII_DOC, (BaseAction) () -> copyCitationToClipboard(CitationStyleOutputFormat.ASCII_DOC)); + actions.put(Actions.COPY_CITATION_XSLFO, (BaseAction) () -> copyCitationToClipboard(CitationStyleOutputFormat.XSL_FO)); + actions.put(Actions.COPY_CITATION_HTML, (BaseAction) () -> copyCitationToClipboard(CitationStyleOutputFormat.HTML)); + actions.put(Actions.COPY_CITATION_RTF, (BaseAction) () -> copyCitationToClipboard(CitationStyleOutputFormat.RTF)); + actions.put(Actions.COPY_CITATION_TEXT, (BaseAction) () -> copyCitationToClipboard(CitationStyleOutputFormat.TEXT)); // The action for copying the BibTeX keys as hyperlinks to the urls of the selected entries actions.put(Actions.COPY_KEY_AND_LINK, new CopyBibTeXKeyAndLinkAction(mainTable)); actions.put(Actions.MERGE_DATABASE, new AppendDatabaseAction(frame, this)); - actions.put(Actions.ADD_FILE_LINK, new AttachFileAction(this)); - actions.put(Actions.OPEN_EXTERNAL_FILE, (BaseAction) () -> openExternalFile()); actions.put(Actions.OPEN_FOLDER, (BaseAction) () -> JabRefExecutorService.INSTANCE.execute(() -> { - final List files = FileUtil.getListOfLinkedFiles(mainTable.getSelectedEntries(), - bibDatabaseContext.getFileDirectoriesAsPaths(Globals.prefs.getFileDirectoryPreferences())); + final List files = FileUtil.getListOfLinkedFiles(mainTable.getSelectedEntries(), bibDatabaseContext.getFileDirectoriesAsPaths(Globals.prefs.getFileDirectoryPreferences())); for (final Path f : files) { try { JabRefDesktop.openFolderAndSelectFile(f.toAbsolutePath()); @@ -500,12 +489,12 @@ public void update() { })); actions.put(Actions.OPEN_CONSOLE, (BaseAction) () -> JabRefDesktop - .openConsole(frame.getCurrentBasePanel().getBibDatabaseContext().getDatabaseFile().orElse(null))); + .openConsole(frame.getCurrentBasePanel().getBibDatabaseContext().getDatabaseFile().orElse(null))); actions.put(Actions.PULL_CHANGES_FROM_SHARED_DATABASE, (BaseAction) () -> { DatabaseSynchronizer dbmsSynchronizer = frame.getCurrentBasePanel() - .getBibDatabaseContext() - .getDBMSSynchronizer(); + .getBibDatabaseContext() + .getDBMSSynchronizer(); dbmsSynchronizer.pullChanges(); }); @@ -546,35 +535,33 @@ public void update() { // Note that we can't put the number of entries that have been reverted into the undoText as the concrete number cannot be injected actions.put(new SpecialFieldValueViewModel(SpecialField.RELEVANCE.getValues().get(0)).getCommand(), - new SpecialFieldViewModel(SpecialField.RELEVANCE, undoManager).getSpecialFieldAction( - SpecialField.RELEVANCE.getValues().get(0), frame)); + new SpecialFieldViewModel(SpecialField.RELEVANCE, undoManager).getSpecialFieldAction(SpecialField.RELEVANCE.getValues().get(0), frame)); + actions.put(new SpecialFieldValueViewModel(SpecialField.QUALITY.getValues().get(0)).getCommand(), - new SpecialFieldViewModel(SpecialField.QUALITY, undoManager) - .getSpecialFieldAction(SpecialField.QUALITY.getValues().get(0), frame)); + new SpecialFieldViewModel(SpecialField.QUALITY, undoManager).getSpecialFieldAction(SpecialField.QUALITY.getValues().get(0), frame)); + actions.put(new SpecialFieldValueViewModel(SpecialField.PRINTED.getValues().get(0)).getCommand(), - new SpecialFieldViewModel(SpecialField.PRINTED, undoManager).getSpecialFieldAction( - SpecialField.PRINTED.getValues().get(0), frame)); + new SpecialFieldViewModel(SpecialField.PRINTED, undoManager).getSpecialFieldAction(SpecialField.PRINTED.getValues().get(0), frame)); for (SpecialFieldValue prio : SpecialField.PRIORITY.getValues()) { actions.put(new SpecialFieldValueViewModel(prio).getCommand(), - new SpecialFieldViewModel(SpecialField.PRIORITY, undoManager).getSpecialFieldAction(prio, this.frame)); + new SpecialFieldViewModel(SpecialField.PRIORITY, undoManager).getSpecialFieldAction(prio, this.frame)); } for (SpecialFieldValue rank : SpecialField.RANKING.getValues()) { actions.put(new SpecialFieldValueViewModel(rank).getCommand(), - new SpecialFieldViewModel(SpecialField.RANKING, undoManager).getSpecialFieldAction(rank, this.frame)); + new SpecialFieldViewModel(SpecialField.RANKING, undoManager).getSpecialFieldAction(rank, this.frame)); } for (SpecialFieldValue status : SpecialField.READ_STATUS.getValues()) { actions.put(new SpecialFieldValueViewModel(status).getCommand(), - new SpecialFieldViewModel(SpecialField.READ_STATUS, undoManager).getSpecialFieldAction(status, this.frame)); + new SpecialFieldViewModel(SpecialField.READ_STATUS, undoManager).getSpecialFieldAction(status, this.frame)); } actions.put(Actions.TOGGLE_PREVIEW, (BaseAction) () -> { PreviewPreferences previewPreferences = Globals.prefs.getPreviewPreferences(); boolean enabled = !previewPreferences.isPreviewPanelEnabled(); - PreviewPreferences newPreviewPreferences = previewPreferences - .getBuilder() - .withPreviewPanelEnabled(enabled) - .build(); + PreviewPreferences newPreviewPreferences = previewPreferences.getBuilder() + .withPreviewPanelEnabled(enabled) + .build(); Globals.prefs.storePreviewPreferences(newPreviewPreferences); DefaultTaskExecutor.runInJavaFXThread(() -> setPreviewActiveBasePanels(enabled)); }); @@ -648,11 +635,9 @@ private void delete(boolean cut, List entries) { NamedCompound compound; if (cut) { - compound = new NamedCompound( - (entries.size() > 1 ? Localization.lang("cut entries") : Localization.lang("cut entry"))); + compound = new NamedCompound((entries.size() > 1 ? Localization.lang("cut entries") : Localization.lang("cut entry"))); } else { - compound = new NamedCompound( - (entries.size() > 1 ? Localization.lang("delete entries") : Localization.lang("delete entry"))); + compound = new NamedCompound((entries.size() > 1 ? Localization.lang("delete entries") : Localization.lang("delete entry"))); } for (BibEntry entry : entries) { compound.addEdit(new UndoableRemoveEntry(bibDatabaseContext.getDatabase(), entry, BasePanel.this)); @@ -663,8 +648,7 @@ private void delete(boolean cut, List entries) { getUndoManager().addEdit(compound); markBaseChanged(); - frame.output( - formatOutputMessage(cut ? Localization.lang("Cut") : Localization.lang("Deleted"), entries.size())); + frame.output(formatOutputMessage(cut ? Localization.lang("Cut") : Localization.lang("Deleted"), entries.size())); // prevent the main table from loosing focus mainTable.requestFocus(); @@ -679,9 +663,9 @@ private void copyTitle() { if (!selectedBibEntries.isEmpty()) { // Collect all non-null titles. List titles = selectedBibEntries.stream() - .filter(bibEntry -> bibEntry.getTitle().isPresent()) - .map(bibEntry -> bibEntry.getTitle().get()) - .collect(Collectors.toList()); + .filter(bibEntry -> bibEntry.getTitle().isPresent()) + .map(bibEntry -> bibEntry.getTitle().get()) + .collect(Collectors.toList()); if (titles.isEmpty()) { output(Localization.lang("None of the selected entries have titles.")); @@ -692,12 +676,9 @@ private void copyTitle() { if (titles.size() == selectedBibEntries.size()) { // All entries had titles. - output((selectedBibEntries.size() > 1 ? Localization.lang("Copied titles") : Localization - .lang("Copied title")) + '.'); + output((selectedBibEntries.size() > 1 ? Localization.lang("Copied titles") : Localization.lang("Copied title")) + '.'); } else { - output(Localization.lang("Warning: %0 out of %1 entries have undefined title.", - Integer.toString(selectedBibEntries.size() - titles.size()), - Integer.toString(selectedBibEntries.size()))); + output(Localization.lang("Warning: %0 out of %1 entries have undefined title.", Integer.toString(selectedBibEntries.size() - titles.size()), Integer.toString(selectedBibEntries.size()))); } } } @@ -717,8 +698,8 @@ private void copyCiteKey() { String sb = String.join(",", keys); String citeCommand = Optional.ofNullable(Globals.prefs.get(JabRefPreferences.CITE_COMMAND)) - .filter(cite -> cite.contains("\\")) // must contain \ - .orElse("\\cite"); + .filter(cite -> cite.contains("\\")) // must contain \ + .orElse("\\cite"); StringSelection ss = new StringSelection(citeCommand + "{" + sb + '}'); Toolkit.getDefaultToolkit().getSystemClipboard().setContents(ss, BasePanel.this); @@ -726,8 +707,7 @@ private void copyCiteKey() { // All entries had keys. output(bes.size() > 1 ? Localization.lang("Copied keys") : Localization.lang("Copied key") + '.'); } else { - output(Localization.lang("Warning: %0 out of %1 entries have undefined BibTeX key.", - Integer.toString(bes.size() - keys.size()), Integer.toString(bes.size()))); + output(Localization.lang("Warning: %0 out of %1 entries have undefined BibTeX key.", Integer.toString(bes.size() - keys.size()), Integer.toString(bes.size()))); } } } @@ -752,8 +732,7 @@ private void copyKey() { // All entries had keys. output((bes.size() > 1 ? Localization.lang("Copied keys") : Localization.lang("Copied key")) + '.'); } else { - output(Localization.lang("Warning: %0 out of %1 entries have undefined BibTeX key.", - Integer.toString(bes.size() - keys.size()), Integer.toString(bes.size()))); + output(Localization.lang("Warning: %0 out of %1 entries have undefined BibTeX key.", Integer.toString(bes.size() - keys.size()), Integer.toString(bes.size()))); } } } @@ -762,13 +741,11 @@ private void copyKeyAndTitle() { List bes = mainTable.getSelectedEntries(); if (!bes.isEmpty()) { // OK: in a future version, this string should be configurable to allow arbitrary exports - StringReader sr = new StringReader( - "\\bibtexkey - \\begin{title}\\format[RemoveBrackets]{\\title}\\end{title}\n"); + StringReader sr = new StringReader("\\bibtexkey - \\begin{title}\\format[RemoveBrackets]{\\title}\\end{title}\n"); Layout layout; try { - layout = new LayoutHelper(sr, - Globals.prefs.getLayoutFormatterPreferences(Globals.journalAbbreviationLoader)) - .getLayoutFromText(); + layout = new LayoutHelper(sr, Globals.prefs.getLayoutFormatterPreferences(Globals.journalAbbreviationLoader)) + .getLayoutFromText(); } catch (IOException e) { LOGGER.info("Could not get layout", e); return; @@ -797,8 +774,7 @@ private void copyKeyAndTitle() { // All entries had keys. output((bes.size() > 1 ? Localization.lang("Copied keys") : Localization.lang("Copied key")) + '.'); } else { - output(Localization.lang("Warning: %0 out of %1 entries have undefined BibTeX key.", - Integer.toString(bes.size() - copied), Integer.toString(bes.size()))); + output(Localization.lang("Warning: %0 out of %1 entries have undefined BibTeX key.", Integer.toString(bes.size() - copied), Integer.toString(bes.size()))); } } } @@ -825,8 +801,7 @@ private void openExternalFile() { return; } FileListEntry flEntry = fileListTableModel.getEntry(0); - ExternalFileMenuItem item = new ExternalFileMenuItem(frame(), entry, "", flEntry.getLink(), - flEntry.getType().get().getIcon().getSmallIcon(), bibDatabaseContext, flEntry.getType()); + ExternalFileMenuItem item = new ExternalFileMenuItem(frame(), "", flEntry.getLink(), flEntry.getType().get().getIcon().getSmallIcon(), bibDatabaseContext, flEntry.getType()); item.doClick(); }); } @@ -855,16 +830,14 @@ public void runCommand(final Actions command) { } } - private boolean saveDatabase(File file, boolean selectedOnly, Charset enc, - SavePreferences.DatabaseSaveType saveType) throws SaveException { + private boolean saveDatabase(File file, boolean selectedOnly, Charset enc, SavePreferences.DatabaseSaveType saveType) throws SaveException { SaveSession session; final String SAVE_DATABASE = Localization.lang("Save library"); try { SavePreferences prefs = SavePreferences.loadForSaveFromPreferences(Globals.prefs) - .withEncoding(enc) - .withSaveType(saveType); - BibtexDatabaseWriter databaseWriter = new BibtexDatabaseWriter<>( - FileSaveSession::new); + .withEncoding(enc) + .withSaveType(saveType); + BibtexDatabaseWriter databaseWriter = new BibtexDatabaseWriter<>(FileSaveSession::new); if (selectedOnly) { session = databaseWriter.savePartOfDatabase(bibDatabaseContext, mainTable.getSelectedEntries(), prefs); } else { @@ -875,10 +848,8 @@ private boolean saveDatabase(File file, boolean selectedOnly, Charset enc, } // FIXME: not sure if this is really thrown anywhere catch (UnsupportedCharsetException ex) { - JOptionPane.showMessageDialog(null, - Localization.lang("Could not save file.") + ' ' - + Localization.lang("Character encoding '%0' is not supported.", enc.displayName()), - SAVE_DATABASE, JOptionPane.ERROR_MESSAGE); + JOptionPane.showMessageDialog(null, Localization.lang("Could not save file.") + ' ' + + Localization.lang("Character encoding '%0' is not supported.", enc.displayName()), SAVE_DATABASE, JOptionPane.ERROR_MESSAGE); throw new SaveException("rt"); } catch (SaveException ex) { if (ex.specificEntry()) { @@ -889,33 +860,26 @@ private boolean saveDatabase(File file, boolean selectedOnly, Charset enc, LOGGER.warn("Could not save", ex); } - dialogService.showErrorDialogAndWait( - SAVE_DATABASE, - Localization.lang("Could not save file."), - ex); + dialogService.showErrorDialogAndWait(SAVE_DATABASE, Localization.lang("Could not save file."), ex); throw new SaveException("rt"); } boolean commit = true; if (!session.getWriter().couldEncodeAll()) { FormBuilder builder = FormBuilder.create() - .layout(new FormLayout("left:pref, 4dlu, fill:pref", "pref, 4dlu, pref")); + .layout(new FormLayout("left:pref, 4dlu, fill:pref", "pref, 4dlu, pref")); JTextArea ta = new JTextArea(session.getWriter().getProblemCharacters()); ta.setEditable(false); - builder.add(Localization.lang("The chosen encoding '%0' could not encode the following characters:", - session.getEncoding().displayName())).xy(1, 1); + builder.add(Localization.lang("The chosen encoding '%0' could not encode the following characters:", session.getEncoding().displayName())).xy(1, 1); builder.add(ta).xy(3, 1); builder.add(Localization.lang("What do you want to do?")).xy(1, 3); String tryDiff = Localization.lang("Try different encoding"); - int answer = JOptionPane.showOptionDialog(null, builder.getPanel(), SAVE_DATABASE, - JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE, null, - new String[] {Localization.lang("Save"), tryDiff, Localization.lang("Cancel")}, tryDiff); + int answer = JOptionPane.showOptionDialog(null, builder.getPanel(), SAVE_DATABASE, JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE, null, new String[] {Localization.lang("Save"), tryDiff, Localization.lang("Cancel")}, tryDiff); if (answer == JOptionPane.NO_OPTION) { // The user wants to use another encoding. - Object choice = JOptionPane.showInputDialog(null, Localization.lang("Select encoding"), SAVE_DATABASE, - JOptionPane.QUESTION_MESSAGE, null, Encodings.ENCODINGS_DISPLAYNAMES, enc); + Object choice = JOptionPane.showInputDialog(null, Localization.lang("Select encoding"), SAVE_DATABASE, JOptionPane.QUESTION_MESSAGE, null, Encodings.ENCODINGS_DISPLAYNAMES, enc); if (choice == null) { commit = false; } else { @@ -1044,7 +1008,7 @@ public void updateTableFont() { private void createMainTable() { bibDatabaseContext.getDatabase().registerListener(SpecialFieldDatabaseChangeListener.INSTANCE); - mainTable = new MainTable(tableModel, frame, this, bibDatabaseContext, preferences.getTablePreferences(), externalFileTypes, Globals.getKeyPrefs()); + mainTable = new MainTable(tableModel, frame, this, bibDatabaseContext, preferences.getTablePreferences(), externalFileTypes, preferences.getKeyBindings()); mainTable.updateFont(); @@ -1053,12 +1017,12 @@ private void createMainTable() { // Update entry editor and preview according to selected entries mainTable.addSelectionListener(event -> mainTable.getSelectedEntries() - .stream() - .findFirst() - .ifPresent(entry -> { - preview.setEntry(entry); - entryEditor.setEntry(entry); - })); + .stream() + .findFirst() + .ifPresent(entry -> { + preview.setEntry(entry); + entryEditor.setEntry(entry); + })); // TODO: Register these actions globally /* @@ -1186,8 +1150,8 @@ public void setupMainPanel() { // Saves the divider position as soon as it changes // We need to keep a reference to the subscription, otherwise the binding gets garbage collected dividerPositionSubscription = EasyBind.monadic(Bindings.valueAt(splitPane.getDividers(), 0)) - .flatMap(SplitPane.Divider::positionProperty) - .subscribe((observable, oldValue, newValue) -> saveDividerLocation(newValue)); + .flatMap(SplitPane.Divider::positionProperty) + .subscribe((observable, oldValue, newValue) -> saveDividerLocation(newValue)); } /** @@ -1320,9 +1284,9 @@ public void previousPreviewStyle() { private void cyclePreview(int newPosition) { PreviewPreferences previewPreferences = Globals.prefs.getPreviewPreferences() - .getBuilder() - .withPreviewCyclePosition(newPosition) - .build(); + .getBuilder() + .withPreviewCyclePosition(newPosition) + .build(); Globals.prefs.storePreviewPreferences(previewPreferences); preview.updateLayout(previewPreferences); @@ -1509,10 +1473,12 @@ public boolean showDeleteConfirmationDialog(int numberOfEntries) { cancelButton = Localization.lang("Keep entries"); } - return dialogService.showConfirmationDialogWithOptOutAndWait(title, message, - okButton, cancelButton, - Localization.lang("Disable this confirmation dialog"), - optOut -> Globals.prefs.putBoolean(JabRefPreferences.CONFIRM_DELETE, !optOut)); + return dialogService.showConfirmationDialogWithOptOutAndWait(title, + message, + okButton, + cancelButton, + Localization.lang("Disable this confirmation dialog"), + optOut -> Globals.prefs.putBoolean(JabRefPreferences.CONFIRM_DELETE, !optOut)); } else { return true; } @@ -1552,9 +1518,9 @@ private void saveDividerLocation(Number position) { if (mode == BasePanelMode.SHOWING_PREVIEW) { PreviewPreferences previewPreferences = Globals.prefs.getPreviewPreferences() - .getBuilder() - .withPreviewPanelDividerPosition(position) - .build(); + .getBuilder() + .withPreviewPanelDividerPosition(position) + .build(); Globals.prefs.storePreviewPreferences(previewPreferences); } else if (mode == BasePanelMode.SHOWING_EDITOR) { preferences.setEntryEditorDividerPosition(position.doubleValue()); @@ -1629,8 +1595,7 @@ private BibEntry getShowing() { } public String formatOutputMessage(String start, int count) { - return String.format("%s %d %s.", start, count, - (count > 1 ? Localization.lang("entries") : Localization.lang("entry"))); + return String.format("%s %d %s.", start, count, (count > 1 ? Localization.lang("entries") : Localization.lang("entry"))); } /** @@ -1800,7 +1765,6 @@ public void listen(EntryAddedEvent addedEntryEvent) { @Subscribe public void listen(EntryChangedEvent entryChangedEvent) { - frame.getGlobalSearchBar().setDontSelectSearchBar(); frame.getGlobalSearchBar().performSearch(); } @@ -1859,16 +1823,18 @@ public void action() { List files = bes.get(0).getFiles(); Optional linkedFile = files.stream() - .filter(file -> (FieldName.URL.equalsIgnoreCase(file.getFileType()) - || FieldName.PS.equalsIgnoreCase(file.getFileType()) - || FieldName.PDF.equalsIgnoreCase(file.getFileType()))) - .findFirst(); + .filter(file -> (FieldName.URL.equalsIgnoreCase(file.getFileType()) + || FieldName.PS.equalsIgnoreCase(file.getFileType()) + || FieldName.PDF.equalsIgnoreCase(file.getFileType()))) + .findFirst(); if (linkedFile.isPresent()) { try { - JabRefDesktop.openExternalFileAnyFormat(bibDatabaseContext, linkedFile.get().getLink(), ExternalFileTypes.getInstance().fromLinkedFile(linkedFile.get(), true)); + JabRefDesktop.openExternalFileAnyFormat(bibDatabaseContext, + linkedFile.get().getLink(), + ExternalFileTypes.getInstance().fromLinkedFile(linkedFile.get(), true)); output(Localization.lang("External viewer called") + '.'); } catch (IOException e) { @@ -1921,10 +1887,10 @@ public SaveSelectedAction(SavePreferences.DatabaseSaveType saveType) { @Override public void action() throws SaveException { FileDialogConfiguration fileDialogConfiguration = new FileDialogConfiguration.Builder() - .withDefaultExtension(FileType.BIBTEX_DB) - .addExtensionFilter(FileType.BIBTEX_DB) - .withInitialDirectory(Globals.prefs.get(JabRefPreferences.WORKING_DIRECTORY)) - .build(); + .withDefaultExtension(FileType.BIBTEX_DB) + .addExtensionFilter(FileType.BIBTEX_DB) + .withInitialDirectory(Globals.prefs.get(JabRefPreferences.WORKING_DIRECTORY)) + .build(); Optional chosenFile = dialogService.showFileSaveDialog(fileDialogConfiguration); if (chosenFile.isPresent()) { diff --git a/src/main/java/org/jabref/gui/DefaultInjector.java b/src/main/java/org/jabref/gui/DefaultInjector.java index 5c96fe7395b..e94e6fe8697 100644 --- a/src/main/java/org/jabref/gui/DefaultInjector.java +++ b/src/main/java/org/jabref/gui/DefaultInjector.java @@ -59,4 +59,14 @@ public T instantiatePresenter(Class clazz, Function injec return Injector.instantiatePresenter(clazz, injectionContext); } + + @Override + public void injectMembers(Object instance, Function injectionContext) { + LOGGER.debug("Inject into " + instance.getClass().getName()); + + // Use our own method to construct dependencies + Injector.setInstanceSupplier(DefaultInjector::createDependency); + + Injector.injectMembers(instance, injectionContext); + } } diff --git a/src/main/java/org/jabref/gui/DialogService.java b/src/main/java/org/jabref/gui/DialogService.java index 0ffaa0de55a..a19f831470c 100644 --- a/src/main/java/org/jabref/gui/DialogService.java +++ b/src/main/java/org/jabref/gui/DialogService.java @@ -26,10 +26,19 @@ public interface DialogService { /** - * This will create and display new {@link ChoiceDialog} of type T with a default choice (can be null) and a collection of possible choices + * This will create and display new {@link ChoiceDialog} of type T with a default choice and a collection of possible choices + * + * @implNote The implementation should accept {@code null} for {@code defaultChoice}, but callers should use {@link #showChoiceDialogAndWait(String, String, String, Collection)}. */ Optional showChoiceDialogAndWait(String title, String content, String okButtonLabel, T defaultChoice, Collection choices); + /** + * This will create and display new {@link ChoiceDialog} of type T with a collection of possible choices + */ + default Optional showChoiceDialogAndWait(String title, String content, String okButtonLabel, Collection choices) { + return showChoiceDialogAndWait(title, content, okButtonLabel, null, choices); + } + /** * This will create and display new {@link TextInputDialog} with a text fields to enter data */ diff --git a/src/main/java/org/jabref/gui/DragDropPane.java b/src/main/java/org/jabref/gui/DragDropPane.java index 8b8a139015a..b9cf0377386 100644 --- a/src/main/java/org/jabref/gui/DragDropPane.java +++ b/src/main/java/org/jabref/gui/DragDropPane.java @@ -15,6 +15,9 @@ import javax.swing.JTabbedPane; import javax.swing.SwingUtilities; +import org.jabref.gui.icon.IconTheme; +import org.jabref.gui.icon.JabRefIcon; + /** * Extends the JTabbedPane class to support Drag&Drop of Tabs. * diff --git a/src/main/java/org/jabref/gui/EntryTypeDialog.java b/src/main/java/org/jabref/gui/EntryTypeDialog.java index 5a0f7eb7b8a..f99d386690e 100644 --- a/src/main/java/org/jabref/gui/EntryTypeDialog.java +++ b/src/main/java/org/jabref/gui/EntryTypeDialog.java @@ -116,7 +116,7 @@ private JPanel createCancelButtonBarPanel() { cancel.addActionListener(this); // Make ESC close dialog, equivalent to clicking Cancel. - cancel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(Globals.getKeyPrefs().getKey(KeyBinding.CLOSE_DIALOG), "close"); + cancel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(Globals.getKeyPrefs().getKey(KeyBinding.CLOSE), "close"); cancel.getActionMap().put("close", cancelAction); JPanel buttons = new JPanel(); diff --git a/src/main/java/org/jabref/gui/FXDialog.java b/src/main/java/org/jabref/gui/FXDialog.java index 6b99c8c18c5..e577568267a 100644 --- a/src/main/java/org/jabref/gui/FXDialog.java +++ b/src/main/java/org/jabref/gui/FXDialog.java @@ -9,6 +9,7 @@ import javafx.stage.Stage; import org.jabref.Globals; +import org.jabref.gui.icon.IconTheme; import org.jabref.gui.keyboard.KeyBinding; import org.jabref.gui.keyboard.KeyBindingRepository; @@ -59,7 +60,7 @@ public FXDialog(AlertType type, boolean isModal) { dialogWindow.getScene().setOnKeyPressed(event -> { KeyBindingRepository keyBindingRepository = Globals.getKeyPrefs(); - if (keyBindingRepository.checkKeyCombinationEquality(KeyBinding.CLOSE_DIALOG, event)) { + if (keyBindingRepository.checkKeyCombinationEquality(KeyBinding.CLOSE, event)) { dialogWindow.close(); } }); diff --git a/src/main/java/org/jabref/gui/GenFieldsCustomizer.java b/src/main/java/org/jabref/gui/GenFieldsCustomizer.java index 4d2fc236d93..6bff3e65d56 100644 --- a/src/main/java/org/jabref/gui/GenFieldsCustomizer.java +++ b/src/main/java/org/jabref/gui/GenFieldsCustomizer.java @@ -97,7 +97,7 @@ private void jbInit() { // Key bindings: ActionMap am = buttons.getActionMap(); InputMap im = buttons.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW); - im.put(Globals.getKeyPrefs().getKey(KeyBinding.CLOSE_DIALOG), "close"); + im.put(Globals.getKeyPrefs().getKey(KeyBinding.CLOSE), "close"); am.put("close", new AbstractAction() { @Override diff --git a/src/main/java/org/jabref/gui/JabRefFrame.java b/src/main/java/org/jabref/gui/JabRefFrame.java index 22495817dba..1050bc8574f 100644 --- a/src/main/java/org/jabref/gui/JabRefFrame.java +++ b/src/main/java/org/jabref/gui/JabRefFrame.java @@ -97,6 +97,7 @@ import org.jabref.gui.externalfiletype.ExternalFileTypes; import org.jabref.gui.help.AboutAction; import org.jabref.gui.help.HelpAction; +import org.jabref.gui.icon.IconTheme; import org.jabref.gui.importer.ImportCommand; import org.jabref.gui.importer.ImportInspectionDialog; import org.jabref.gui.importer.actions.OpenDatabaseAction; diff --git a/src/main/java/org/jabref/gui/Main.css b/src/main/java/org/jabref/gui/Main.css index 8ec16e91e05..68ded58af53 100644 --- a/src/main/java/org/jabref/gui/Main.css +++ b/src/main/java/org/jabref/gui/Main.css @@ -1,69 +1,249 @@ -/* - * The base css file defining the style that is valid for every pane and dialog. - */ - .root { + /* - * A strong blue to indicate something neutral. - */ - -fx-neutral: #2980b9; + The theme color and some derived colors from it are used for icons, tab-headers, marking of selected inputs and + hover colors for the main menu. It generally defines the look of JabRef. The highlighting colors below should + work nicely with this base color + */ + + /*-jr-theme: #2467ab;*/ + + /* This theme is the original JabRef dark blue color */ + -jr-theme: #50618F; + /* The base gray. Most gray-tones in the application are derived from this color. */ + -jr-base: #f0f0f0; + -jr-accent: derive(-jr-theme, 50%); + + -jr-white: #ffffff; + -jr-gray-0: #f0f0f0; + -jr-gray-1: #e6e6e6; + -jr-gray-2: #808080; + -jr-gray-3: #404040; + -jr-black: #000; + + /* Highlights */ + -jr-blue: #0abde3; + -jr-light-blue: #48dbfb; + -jr-purple: #f368e0; + -jr-light-purple: #ff9ff3; + -jr-green: #10ac84; + -jr-light-green: #1dd1a1; + -jr-red: #ee5253; + -jr-light-red: #ff6b6b; + -jr-yellow: #feca57; + -jr-orange: #ff9f43; + + /* Some blueish greys */ + -jr-blue-gray-1: #c8d6e5; + -jr-blue-gray-2: #8395a7; + -jr-blue-gray-3: #576574; + -jr-blue-gray-4: #222f3e; + + + /* maintable and group view cells*/ + -jr-hover-cell: derive(-jr-yellow, 50%); + -jr-selected-cell: derive(-jr-yellow, 5%); + -jr-drag-target: -jr-orange; + + /* Background-color of the toolbar */ + -jr-toolbar: derive(-jr-base, 80%); + + /* All icons on toolbars or next to inputs */ + -jr-icon: -jr-theme; + + /* Colors for messages and errors */ + -jr-info: -jr-light-green; + -jr-warn: -jr-orange; + -jr-error: -jr-light-red; + + /* Background-color for the small group view indicator for the number of hits */ + -jr-group-hits-bg: -jr-gray-0; + + /* Specific color for general tooltips */ + -jr-tooltip-bg: -jr-info; + -jr-tooltip-fg: -jr-black; + + /* Finally, some specific jr styles that depend on -fx definitions in *this* style */ + -jr-sidepane-background: -fx-control-inner-background; + -jr-sidepane-header-background: -fx-background; + -jr-sidepane-header-color: -jr-icon; + + /* Specs for the scrollbars */ + -jr-scrollbar-thumb: -fx-outer-border; + -jr-scrollbar-track: -fx-control-inner-background; /* - * A strong green to indicate something positive. + 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 + very specific purposes. */ - -fx-positive: #27ae60; + -fx-base: -jr-base; - /* - * A strong red to indicate something negative. + /* A very light grey used for the background of windows. See also + * -fx-text-background-color, which should be used as the -fx-text-fill + * value for text painted on top of backgrounds colored with -fx-background. */ - -fx-negative: #c0392b; + -fx-background: derive(-fx-base, 26.4%); - /* - * A dark grayish blue to display information (but kind of highlighted). + /* Used for the inside of text boxes, password boxes, lists, trees, and + * tables. See also -fx-text-inner-color, which should be used as the + * -fx-text-fill value for text painted on top of backgrounds colored + * with -fx-control-inner-background. */ - -fx-info: #2c3e50; + -fx-control-inner-background: derive(-fx-base, 80%); + /* Version of -fx-control-inner-background for alternative rows */ + -fx-control-inner-background-alt: derive(-fx-control-inner-background, -2%); + + /* One of these colors will be chosen based upon a ladder calculation + * that uses the brightness of a background color. Instead of using these + * colors directly as -fx-text-fill values, the sections in this file should + * use a derived color to match the background in use. See also: + * + * -fx-text-base-color for text on top of -fx-base, -fx-color, and -fx-body-color + * -fx-text-background-color for text on top of -fx-background + * -fx-text-inner-color for text on top of -fx-control-inner-color + * -fx-selection-bar-text for text on top of -fx-selection-bar + */ + -fx-dark-text-color: -jr-black; + -fx-mid-text-color: derive(-jr-base, -60%); + -fx-light-text-color: -jr-white; - /* - * A dark gray to indicate something not so important + /* A bright blue for highlighting/accenting objects. For example: selected + * text; selected items in menus, lists, trees, and tables; progress bars */ + -fx-accent: -jr-accent; + + /* Default buttons color, this is similar to accent but more subtle */ + -fx-default-button: derive(-fx-accent, 50%); + + /* A bright blue for the focus indicator of objects. Typically used as the + * first color in -fx-background-color for the "focused" pseudo-class. Also + * typically used with insets of -1.4 to provide a glowing effect. + */ + -fx-focus-color: -fx-accent; + -fx-faint-focus-color: derive(-fx-accent, 50%); + + /* The color that is used in styling controls. The default value is based + * on -fx-base, but is changed by pseudoclasses to change the base color. + * For example, the "hover" pseudoclass will typically set -fx-color to + * -fx-hover-base (see below) and the "armed" pseudoclass will typically + * set -fx-color to -fx-pressed-base. */ - -fx-unimportant: #595959; + -fx-color: -fx-base; - /* - * A light gray for accented background + -fx-hover-base: derive(-fx-base,30%); + + /* A little darker than -fx-base and used as the -fx-color for the + * "armed" pseudoclass state. + * + * TODO: should this be renamed to -fx-armed-base? */ - -fx-accented-background: #dadad8; + -fx-pressed-base: derive(-fx-base,-6%); - /* - * A darker gray as background + /* The color to use for -fx-text-fill when text is to be painted on top of + * a background filled with the -fx-background color. */ - -fx-dark-background: #aaaaaa; + -fx-text-background-color: -fx-dark-text-color; - /* - * A strong white as background + /* A little darker than -fx-color and used to draw boxes around objects such + * as progress bars, scroll bars, scroll panes, trees, tables, and lists. */ - -fx-light-background: #ffffff; + -fx-box-border: derive(-fx-color,-5%); - /* - * A dark gray for borders + /* Darker than -fx-background and used to draw boxes around text boxes and + * password boxes. */ - -fx-border: #868786; + -fx-text-box-border: derive(-fx-background, -15%); - /* - * A light gray for background for active items in list like elements such as menus, lists, trees, and tables. + /* Lighter than -fx-background and used to provide a small highlight when + * needed on top of -fx-background. This is never a shadow in Modena but + * keep -fx-shadow-highlight-color name to be compatible with Caspian. */ - -fx-selection-bar: #e5e5e5; + -fx-shadow-highlight-color: rgba(255,255,255,0.07) 70%; - /* - * The base color for the theme + /* A gradient that goes from a little darker than -fx-color on the top to + * even more darker than -fx-color on the bottom. Typically is the second + * color in the -fx-background-color list as the small thin border around + * a control. It is typically the same size as the control (i.e., insets + * are 0). */ - -fx-theme-base: #4d4673; + -fx-outer-border: derive(-fx-color,-5%); - /* - * A slightly lighter theme-based color for backgrounds of active items (toggle button, selected list item) + /* A gradient that goes from a bit lighter than -fx-color on the top to + * a little darker at the bottom. Typically is the third color in the + * -fx-background-color list as a thin highlight inside the outer border. + * Insets are typically 1. + */ + -fx-inner-border: derive(-fx-color,65%); + -fx-inner-border-horizontal: derive(-fx-color,65%); + -fx-inner-border-bottomup: derive(-fx-color,65%); + + /*-fx-inner-border: red;*/ + /*-fx-inner-border-horizontal: green;*/ + /*-fx-inner-border-bottomup: blue;*/ + + /* A gradient that goes from a little lighter than -fx-color at the top to + * a little darker than -fx-color at the bottom and is used to fill the + * body of many controls such as buttons. */ - -fx-active-background: derive(-fx-theme-base, 20%); + -fx-body-color: derive(-fx-color,20%); + -fx-body-color-bottomup: derive(-fx-color,20%); + -fx-body-color-to-right: derive(-fx-color,20%); + + /* The color to use as -fx-text-fill when painting text on top of + * backgrounds filled with -fx-base, -fx-color, and -fx-body-color. + */ + -fx-text-base-color: -fx-dark-text-color 46%; + + /* The color to use as -fx-text-fill when painting text on top of + * backgrounds filled with -fx-control-inner-background. + */ + -fx-text-inner-color: -fx-dark-text-color; + + /* The color to use for small mark-like objects such as checks on check + * boxes, filled in circles in radio buttons, arrows on scroll bars, etc. + */ + -fx-mark-color: -jr-theme; + + /* The small thin light "shadow" for mark-like objects. Typically used in + * conjunction with -fx-mark-color with an insets of 1 0 -1 0. */ + -fx-mark-highlight-color: transparent; + /*-fx-mark-highlight-color: derive(-fx-color,80%);*/ + + /* Background for items in list like things such as menus, lists, trees, + * and tables. */ + -fx-selection-bar: -fx-accent; + + /* Background color to use for selection of list cells etc. This is when + * the control doesn't have focus or the row of a previously selected item. */ + -fx-selection-bar-non-focused: lightgrey; + + /* The color to use as -fx-text-fill when painting text on top of + * backgrounds filled with -fx-selection-bar. + * + * TODO: this can be removed + */ + -fx-selection-bar-text: -fx-text-background-color; + + /* These are needed for Popup */ + -fx-background-color: inherit; + -fx-background-radius: inherit; + -fx-background-insets: inherit; + -fx-padding: inherit; + + /** Focus line for keyboard focus traversal on cell based controls */ + -fx-cell-focus-inner-border: derive(-fx-selection-bar,30%); + + -fx-focused-text-base-color : -fx-dark-text-color; + -fx-focused-mark-color : -fx-focused-text-base-color; + + /* Consistent size for headers of tab-pane and side-panels*/ + -jr-header-height: 3em; } +/* + * The base css file defining the style that is valid for every pane and dialog. + */ + .hyperlink { -fx-padding: 0; -fx-underline: false; @@ -85,14 +265,13 @@ } .tooltip { - -fx-background-color: #616161; + -fx-background-color: -jr-tooltip-bg; -fx-opacity: 95%; - -fx-text-fill: rgba(255, 255, 255, 0.9); + -fx-text-fill: -jr-tooltip-fg; -fx-font-size: 1em; } .tooltip > TextFlow > Text { - -fx-fill: rgba(255, 255, 255, 0.9); -fx-font-size: 1em; } @@ -114,13 +293,27 @@ -fx-shadow-highlight-color: transparent; -fx-outer-border: transparent; -fx-inner-border: transparent; - -fx-focus-color: -fx-active-background; -fx-faint-focus-color: transparent; -fx-background-color: transparent; - -fx-text-background-color: dimgray; + -fx-text-background-color: transparent; -fx-padding: 0.5em; } +.flatButtonNoSpaceBottom .glyph-icon, +.flatButtonNoSpaceTop .glyph-icon, +.flatButton .glyph-icon { + -fx-fill: -jr-icon; + -fx-text-fill: -jr-icon; +} + +.flatButtonNoSpaceBottom:hover .glyph-icon, +.flatButtonNoSpaceTop:hover .glyph-icon, +.flatButton:hover .glyph-icon { + -fx-fill: derive(-jr-icon, -30%); + -fx-text-fill: derive(-jr-icon, -30%); + -fx-effect: dropshadow(one-pass-box, -jr-icon, 5, 0, 1, 1); +} + .flatButton:selected { -fx-background-color: -fx-active-background; -fx-text-fill: white; @@ -136,22 +329,22 @@ } .menu-bar { - -fx-background-color: white; + -fx-background-color: -fx-body-color; -fx-background-insets: 0; -fx-background-radius: 0; } -.menu-bar > .container > .menu-button > .label { - -fx-padding: 0.41777em 0.41777em 0.41777em 0.41777em; /* 5 5 5 5 */ -} +/*.menu-bar > .container > .menu-button > .label {*/ + /*-fx-padding: 0.41777em 0.41777em 0.41777em 0.41777em; !* 5 5 5 5 *!*/ +/*}*/ -.menu-bar > .menu { - -fx-padding: 0.0em 0.666667em 0.0em 0.666667em; /* 0 8 0 8 */ -} +/*.menu-bar > .menu {*/ + /*-fx-padding: 0.0em 0.666667em 0.0em 0.666667em; !* 0 8 0 8 *!*/ +/*}*/ -.menu-item { - -fx-padding: 0.5em 0.41777em 0.5em 0.41777em; /* 6 5 6 5 */ -} +/*.menu-item {*/ + /*-fx-padding: 0.5em 0.41777em 0.5em 0.41777em; !* 6 5 6 5 *!*/ +/*}*/ .tab-pane { -fx-open-tab-animation: NONE; @@ -161,24 +354,54 @@ .tab-pane > .tab-header-area > .headers-region > .tab { -fx-background-insets: 0; -fx-background-radius: 0; - -fx-background-color: -fx-accented-background; - -fx-border-color: -fx-border; + /*-fx-background-color: green;*/ + /*-fx-background-color: -fx-accented-background;*/ + -fx-border-color: -fx-outer-border; -fx-border-width: 0.5 0.5 0.5 0.5; - -fx-padding: 0.4em 0.9em 0.4em 0.9em; + -fx-padding: 0.3em 0.9em 0.3em 0.9em; + -fx-pref-height: -jr-header-height; + } .tab-pane > .tab-header-area > .headers-region > .tab .tab-label { - -fx-text-fill: -fx-unimportant; + -fx-text-fill: -fx-mid-text-color; +} + +.tab-pane > .tab-header-area > .headers-region > .control-buttons-tab { + -fx-border-color: -jr-theme; + -fx-fill: -jr-theme; + -fx-text-fill: -jr-theme; } .tab-pane > .tab-header-area > .headers-region > .tab:selected { - -fx-background-color: -fx-light-background; - -fx-border-color: -fx-active-background; + /*-fx-background-color: -fx-control-inner-background;*/ + -fx-border-color: -jr-theme; + -fx-fill: -jr-theme; + -fx-text-fill: -jr-theme; -fx-border-width: 3 0 0 0; } -.tab-pane > .tab-header-area > .headers-region > .tab:selected .tab-label { - -fx-text-fill: black; + +.tab-pane > .tab-header-area > .headers-region > .tab:selected .tab-label{ + -fx-text-fill: -jr-theme; + -fx-fill: -jr-theme; +} + +.tab-pane > .tab-header-area > .headers-region > .tab:selected .tab-close-button { + -fx-background-color: -jr-theme; +} + +.tab-pane > .tab-header-area > .headers-region > .tab:selected .tab-close-button:hover { + -fx-background-color: -jr-theme; + -fx-fill: derive(-jr-icon, -30%); + -fx-text-fill: derive(-jr-icon, -30%); + -fx-effect: dropshadow(one-pass-box, -jr-icon, 5, 0, 1, 1); +} + + +.tab-pane > .tab-header-area > .headers-region > .tab:selected .glyph-icon { + -fx-text-fill: -jr-theme; + -fx-fill: -jr-theme; } .tab-pane:focused > .tab-header-area > .headers-region > .tab:selected .focus-indicator { @@ -188,22 +411,23 @@ } .tab-pane > .tab-header-area > .tab-header-background { - -fx-background-color: -fx-accented-background; + -fx-background-color: -fx-outer-border; } .tab-pane > .tab-header-area > .headers-region > .tab .glyph-icon { -glyph-size: 13px; - -fx-text-fill: -fx-unimportant; - -fx-fill: -fx-unimportant; + -fx-text-fill: -fx-mid-text-color; + -fx-fill: -fx-mid-text-color; } .tab-pane > .tab-header-area > .headers-region > .tab:selected .glyph-icon { - -fx-text-fill: black; - -fx-fill: black; + -fx-text-fill: -jr-icon; + -fx-fill: -jr-icon; } .tab-pane > .tab-header-area { - -fx-padding: 0.416667em 5 0.0em 0em; + /*-fx-padding: 0.416667em 5 0.0em 0em;*/ + -fx-padding: 0 0 0 0; } .table-view { @@ -216,25 +440,25 @@ } .split-pane > .split-pane-divider { - -fx-background-color: -fx-accented-background; - -fx-padding: 0 0.15ex 0 0.15ex; + /*-fx-background-color: -jr-separator;*/ + -fx-padding: 0 .5 0 .5; } .table-row-cell:hover, .tree-table-row-cell:hover { - -fx-background-color: lightgrey; + -fx-background-color: -jr-hover-cell; } .table-row-cell:selected, .tree-table-row-cell:selected { - -fx-background-color: -fx-active-background; - -fx-text-fill: white; - -fx-fill: white; + -fx-background-color: -jr-selected-cell; + /*-fx-text-fill: white;*/ + /*-fx-fill: white;*/ } .tree-table-row-cell:selected > .tree-table-cell > .glyph-icon { - -fx-fill: white; - -fx-text-fill: white; + /*-fx-fill: white;*/ + /*-fx-text-fill: white;*/ } .table-view > .virtual-flow > .clipped-container > .sheet > .table-row-cell .table-cell:selected, @@ -272,32 +496,32 @@ } .text-input { - -fx-background-color: #cacbca, -fx-control-inner-background; + -fx-background-color: -fx-outer-border, -fx-control-inner-background; -fx-background-insets: 0, 1; } .text-input:focused { - -fx-background-color: -fx-active-background, -fx-control-inner-background; + -fx-background-color: -fx-accent, -fx-control-inner-background; -fx-background-insets: 0, 2; } .text-area { - -fx-background-color: white; + -fx-background-color: -fx-control-inner-background; } .text-area .content { - -fx-background-color: #cacbca, -fx-control-inner-background; + /*-fx-background-color: -jr-separator, -fx-control-inner-background;*/ -fx-background-insets: 0, 1; -fx-padding: 0.333333em 0.583em 0.333333em 0.583em; } .text-area:focused .content { - -fx-background-color: -fx-active-background, -fx-control-inner-background; + -fx-background-color: -fx-accent, -fx-control-inner-background; -fx-background-insets: 0, 2; } .date-picker > .text-field:focused { - -fx-background-color: -fx-active-background, -fx-control-inner-background; + -fx-background-color: -fx-accent, -fx-control-inner-background; -fx-background-insets: 0, 2; } @@ -308,21 +532,21 @@ .scroll-bar:horizontal .track, .scroll-bar:vertical .track { - -fx-background-color: derive(#868786, 20%); + -fx-background-color: -jr-scrollbar-track; -fx-opacity: 0.2; -fx-background-radius: 0em; } .scroll-bar:horizontal .thumb, .scroll-bar:vertical .thumb { - -fx-background-color: derive(#757575, 60%); + -fx-background-color: -jr-scrollbar-thumb; -fx-background-insets: 0, 0, 0; -fx-background-radius: 0em; } .scroll-bar .thumb:hover, .scroll-bar .thumb:pressed { - -fx-background-color: derive(black, 70%); + -fx-background-color: derive(-jr-scrollbar-thumb, -20%); } /* Hide increment and decrement buttons */ @@ -365,38 +589,65 @@ } .sidePaneComponentHeader { - -fx-background-color: -fx-dark-background; + -fx-background-color: -jr-sidepane-header-background; + -fx-padding: 0.3em 0.9em 0.3em 0.9em; + -fx-pref-height: -jr-header-height; } -.mainMenu { - -fx-background-color: derive(-fx-theme-base, -10%); +.sidePaneComponentHeader > .label { + -fx-text-fill: -jr-sidepane-header-color; + -fx-font-weight: bold; + -fx-padding: 0.3em 0.9em 0.3em 0.9em; } -.menu-bar > .container > .menu-button:hover, -.menu-bar > .container > .menu-button:focused, -.menu-bar > .container > .menu-button:showing { - -fx-background: derive(-fx-theme-base, 10%); +.sidePaneComponentHeader .glyph-icon{ + -fx-font-size: 16px; } +/*.mainMenu {*/ + /*-fx-background-color: -jr-background;*/ +/*}*/ -.mainMenu > .container > .menu-button > .label { - -fx-text-fill: white; - -fx-font-weight: bold; -} +/*.menu-bar > .container > .menu-button:hover,*/ +/*.menu-bar > .container > .menu-button:focused,*/ +/*.menu-bar > .container > .menu-button:showing {*/ + /*-fx-background: -jr-hover-cell;*/ +/*}*/ + +/*.mainMenu > .container > .menu-button > .label {*/ + /*-fx-text-fill: black;*/ + /*!*-fx-font-weight: bold;*!*/ +/*}*/ + +/*.menu-item:focused {*/ + /*-fx-background: -jr-hover-cell;*/ + /*-fx-background-color: -fx-background;*/ + /*-fx-text-fill: black;*/ +/*}*/ +/*.menu-item:focused > .label {*/ + /*-fx-text-fill: black;*/ +/*}*/ .mainToolbar { - -fx-background-color: derive(-fx-theme-base, -10%); + -fx-background-color: -jr-toolbar; + -fx-border-color: derive(-jr-toolbar, 50%); + -fx-border-width: 1 1 1 1; + -fx-border-radius: 2 2 2 2; + -fx-background-radius: 2 2 2 2; } .mainToolbar .glyph-icon { -fx-font-size: 1.7em; - -fx-text-fill: white; - -fx-fill: white; } -.mainToolbar .search-field { - -fx-background-color: derive(-fx-theme-base, -15%); +.mainToolbar .search-field .glyph-icon { + -fx-fill: -jr-icon; + -fx-text-fill: -jr-icon; } -.mainToolbar .search-field:focused { - -fx-background-color: derive(-fx-theme-base, 15%); +/* The little arrow that shows up when not all tool-bar icons fit into the tool-bar. +We want to have a look that matches our icons in the tool-bar */ +.mainToolbar .tool-bar-overflow-button:hover > .arrow { + -fx-background-color: -fx-mark-highlight-color, derive(-jr-icon, -30%); + -fx-effect: dropshadow(one-pass-box, -jr-icon, 5, 0, 1, 1); } + diff --git a/src/main/java/org/jabref/gui/MergeDialog.java b/src/main/java/org/jabref/gui/MergeDialog.java index 3148e9ecc6d..b843419c624 100644 --- a/src/main/java/org/jabref/gui/MergeDialog.java +++ b/src/main/java/org/jabref/gui/MergeDialog.java @@ -91,7 +91,7 @@ private void jbInit() { // Key bindings: ActionMap am = jPanel1.getActionMap(); InputMap im = jPanel1.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW); - im.put(Globals.getKeyPrefs().getKey(KeyBinding.CLOSE_DIALOG), "close"); + im.put(Globals.getKeyPrefs().getKey(KeyBinding.CLOSE), "close"); am.put("close", new AbstractAction() { @Override diff --git a/src/main/java/org/jabref/gui/PreviewPanel.java b/src/main/java/org/jabref/gui/PreviewPanel.java index 9afe203c4f2..d2b1fd8c118 100644 --- a/src/main/java/org/jabref/gui/PreviewPanel.java +++ b/src/main/java/org/jabref/gui/PreviewPanel.java @@ -18,6 +18,7 @@ import javafx.scene.web.WebView; import org.jabref.Globals; +import org.jabref.gui.icon.IconTheme; import org.jabref.gui.keyboard.KeyBinding; import org.jabref.gui.keyboard.KeyBindingRepository; import org.jabref.gui.util.BackgroundTask; @@ -112,7 +113,7 @@ private void createKeyBindings() { copyPreviewToClipBoard(); event.consume(); break; - case CLOSE_DIALOG: + case CLOSE: close(); event.consume(); break; diff --git a/src/main/java/org/jabref/gui/ReplaceStringDialog.java b/src/main/java/org/jabref/gui/ReplaceStringDialog.java index 6d3e6fec37f..10fd5ccc1cf 100644 --- a/src/main/java/org/jabref/gui/ReplaceStringDialog.java +++ b/src/main/java/org/jabref/gui/ReplaceStringDialog.java @@ -81,7 +81,7 @@ public void actionPerformed(ActionEvent e) { JPanel settings = new JPanel(); ActionMap am = settings.getActionMap(); InputMap im = settings.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW); - im.put(Globals.getKeyPrefs().getKey(KeyBinding.CLOSE_DIALOG), "close"); + im.put(Globals.getKeyPrefs().getKey(KeyBinding.CLOSE), "close"); am.put("close", cancelAction); // Layout starts here. diff --git a/src/main/java/org/jabref/gui/SidePaneComponent.java b/src/main/java/org/jabref/gui/SidePaneComponent.java index ebff523fdff..87c1cbc0ec7 100644 --- a/src/main/java/org/jabref/gui/SidePaneComponent.java +++ b/src/main/java/org/jabref/gui/SidePaneComponent.java @@ -9,6 +9,8 @@ import org.jabref.gui.actions.Action; import org.jabref.gui.actions.SimpleCommand; +import org.jabref.gui.icon.IconTheme; +import org.jabref.gui.icon.JabRefIcon; public abstract class SidePaneComponent { @@ -101,8 +103,9 @@ public final Node getHeader() { BorderPane graphic = new BorderPane(); graphic.setCenter(icon.getGraphicNode()); BorderPane container = new BorderPane(); - container.setLeft(graphic); - container.setCenter(new Label(title)); +// container.setLeft(graphic); + final Label label = new Label(title); + container.setCenter(label); container.setRight(buttonContainer); container.getStyleClass().add("sidePaneComponentHeader"); diff --git a/src/main/java/org/jabref/gui/StringDialog.java b/src/main/java/org/jabref/gui/StringDialog.java index 1ec90ffecd8..796a557dbec 100644 --- a/src/main/java/org/jabref/gui/StringDialog.java +++ b/src/main/java/org/jabref/gui/StringDialog.java @@ -33,6 +33,7 @@ import org.jabref.Globals; import org.jabref.gui.actions.Actions; import org.jabref.gui.help.HelpAction; +import org.jabref.gui.icon.IconTheme; import org.jabref.gui.keyboard.KeyBinding; import org.jabref.gui.undo.UndoableInsertString; import org.jabref.gui.undo.UndoableRemoveString; @@ -119,7 +120,7 @@ protected boolean accept(Component c) { am.put("remove", removeStringAction); im.put(Globals.getKeyPrefs().getKey(KeyBinding.SAVE_DATABASE), "save"); am.put("save", saveAction); - im.put(Globals.getKeyPrefs().getKey(KeyBinding.CLOSE_DIALOG), "close"); + im.put(Globals.getKeyPrefs().getKey(KeyBinding.CLOSE), "close"); am.put("close", closeAction); im.put(Globals.getKeyPrefs().getKey(KeyBinding.HELP), "help"); am.put("help", helpAction); @@ -217,7 +218,7 @@ public StringTable(StringTableModel stm) { TableColumnModel cm = getColumnModel(); cm.getColumn(0).setPreferredWidth(800); cm.getColumn(1).setPreferredWidth(2000); - getInputMap().put(Globals.getKeyPrefs().getKey(KeyBinding.CLOSE_DIALOG), "close"); + getInputMap().put(Globals.getKeyPrefs().getKey(KeyBinding.CLOSE), "close"); getActionMap().put("close", closeAction); getInputMap().put(Globals.getKeyPrefs().getKey(KeyBinding.HELP), "help"); getActionMap().put("help", helpAction); diff --git a/src/main/java/org/jabref/gui/actions/Action.java b/src/main/java/org/jabref/gui/actions/Action.java index 6b3700eece3..5d940ffba55 100644 --- a/src/main/java/org/jabref/gui/actions/Action.java +++ b/src/main/java/org/jabref/gui/actions/Action.java @@ -2,7 +2,7 @@ import java.util.Optional; -import org.jabref.gui.JabRefIcon; +import org.jabref.gui.icon.JabRefIcon; import org.jabref.gui.keyboard.KeyBinding; public interface Action { diff --git a/src/main/java/org/jabref/gui/actions/CleanupAction.java b/src/main/java/org/jabref/gui/actions/CleanupAction.java index 2ea260d32fd..aa611b8a6ed 100644 --- a/src/main/java/org/jabref/gui/actions/CleanupAction.java +++ b/src/main/java/org/jabref/gui/actions/CleanupAction.java @@ -3,16 +3,11 @@ import java.util.List; import java.util.Optional; -import javax.swing.JScrollPane; - -import javafx.embed.swing.SwingNode; -import javafx.scene.control.ButtonType; -import javafx.scene.control.DialogPane; - import org.jabref.Globals; import org.jabref.gui.BasePanel; import org.jabref.gui.DialogService; import org.jabref.gui.JabRefFrame; +import org.jabref.gui.cleanup.CleanupDialog; import org.jabref.gui.cleanup.CleanupPresetPanel; import org.jabref.gui.undo.NamedCompound; import org.jabref.gui.undo.UndoableFieldChange; @@ -66,27 +61,14 @@ public void run() { if (canceled) { return; } - CleanupPresetPanel presetPanel = new CleanupPresetPanel(panel.getBibDatabaseContext(), - CleanupPreset.loadFromPreferences(preferences)); - - SwingNode node = new SwingNode(); - presetPanel.getScrollPane().setVisible(true); - - JScrollPane scrollPane = presetPanel.getScrollPane(); - node.setContent(scrollPane); - node.setVisible(true); - - DialogPane pane = new DialogPane(); - pane.setContent(node); - pane.setPrefSize(600, 600); - - Optional ok = dialogService.showCustomDialogAndWait(Localization.lang("Cleanup entries"), pane, ButtonType.OK, ButtonType.CANCEL); + CleanupDialog cleanupDialog = new CleanupDialog(panel.getBibDatabaseContext(), CleanupPreset.loadFromPreferences(preferences)); - if (!ok.isPresent() || ((ok.isPresent() && (ok.get() == ButtonType.CANCEL)))) { + Optional chosenPreset = cleanupDialog.showAndWait(); + if (!chosenPreset.isPresent()) { canceled = true; return; } - CleanupPreset cleanupPreset = presetPanel.getCleanupPreset(); + CleanupPreset cleanupPreset = chosenPreset.get(); cleanupPreset.storeInPreferences(preferences); if (cleanupPreset.isRenamePDF() && Globals.prefs.getBoolean(JabRefPreferences.ASK_AUTO_NAMING_PDFS_AGAIN)) { diff --git a/src/main/java/org/jabref/gui/actions/CopyFilesAction.java b/src/main/java/org/jabref/gui/actions/CopyFilesAction.java index 8ba3e2db382..5efa6415506 100644 --- a/src/main/java/org/jabref/gui/actions/CopyFilesAction.java +++ b/src/main/java/org/jabref/gui/actions/CopyFilesAction.java @@ -47,8 +47,8 @@ private void showDialog(List data) { dialogService.showInformationDialogAndWait(Localization.lang("Copy linked files to folder..."), Localization.lang("No linked files found for export.")); return; } - CopyFilesDialogView dlg = new CopyFilesDialogView(databaseContext, new CopyFilesResultListDependency(data)); - dlg.show(); + CopyFilesDialogView dialog = new CopyFilesDialogView(databaseContext, new CopyFilesResultListDependency(data)); + dialog.showAndWait(); } @Override diff --git a/src/main/java/org/jabref/gui/actions/LookupIdentifierAction.java b/src/main/java/org/jabref/gui/actions/LookupIdentifierAction.java index 7cce1ebd39e..895f9854dca 100644 --- a/src/main/java/org/jabref/gui/actions/LookupIdentifierAction.java +++ b/src/main/java/org/jabref/gui/actions/LookupIdentifierAction.java @@ -4,7 +4,7 @@ import org.jabref.gui.BasePanel; import org.jabref.gui.JabRefFrame; -import org.jabref.gui.JabRefIcon; +import org.jabref.gui.icon.JabRefIcon; import org.jabref.gui.keyboard.KeyBinding; import org.jabref.gui.worker.LookupIdentifiersWorker; import org.jabref.logic.importer.IdFetcher; diff --git a/src/main/java/org/jabref/gui/actions/ManageKeywordsAction.java b/src/main/java/org/jabref/gui/actions/ManageKeywordsAction.java index dbb2f7beed6..0af7a02f91c 100644 --- a/src/main/java/org/jabref/gui/actions/ManageKeywordsAction.java +++ b/src/main/java/org/jabref/gui/actions/ManageKeywordsAction.java @@ -197,7 +197,7 @@ public void keyPressed(KeyEvent e) { // Key bindings: ActionMap am = builder.getPanel().getActionMap(); InputMap im = builder.getPanel().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW); - im.put(Globals.getKeyPrefs().getKey(KeyBinding.CLOSE_DIALOG), "close"); + im.put(Globals.getKeyPrefs().getKey(KeyBinding.CLOSE), "close"); am.put("close", cancelAction); diag.getContentPane().add(builder.getPanel(), BorderLayout.CENTER); diff --git a/src/main/java/org/jabref/gui/actions/MassSetFieldAction.java b/src/main/java/org/jabref/gui/actions/MassSetFieldAction.java index 2d6e2a907db..953d420d024 100644 --- a/src/main/java/org/jabref/gui/actions/MassSetFieldAction.java +++ b/src/main/java/org/jabref/gui/actions/MassSetFieldAction.java @@ -189,7 +189,7 @@ public void actionPerformed(ActionEvent e) { // Key bindings: ActionMap am = builder.getPanel().getActionMap(); InputMap im = builder.getPanel().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW); - im.put(Globals.getKeyPrefs().getKey(KeyBinding.CLOSE_DIALOG), "close"); + im.put(Globals.getKeyPrefs().getKey(KeyBinding.CLOSE), "close"); am.put("close", cancelAction); } diff --git a/src/main/java/org/jabref/gui/actions/MnemonicAwareAction.java b/src/main/java/org/jabref/gui/actions/MnemonicAwareAction.java index 13ba654cc38..b1bfa9f87d5 100644 --- a/src/main/java/org/jabref/gui/actions/MnemonicAwareAction.java +++ b/src/main/java/org/jabref/gui/actions/MnemonicAwareAction.java @@ -4,7 +4,7 @@ import javax.swing.Action; import javax.swing.Icon; -import org.jabref.gui.IconTheme; +import org.jabref.gui.icon.IconTheme; /** * This class extends {@link AbstractAction} with the ability to set diff --git a/src/main/java/org/jabref/gui/actions/ShowPreferencesAction.java b/src/main/java/org/jabref/gui/actions/ShowPreferencesAction.java index c6b551ef0a9..6208911fc6b 100644 --- a/src/main/java/org/jabref/gui/actions/ShowPreferencesAction.java +++ b/src/main/java/org/jabref/gui/actions/ShowPreferencesAction.java @@ -14,17 +14,12 @@ public ShowPreferencesAction(JabRefFrame jabRefFrame) { @Override public void execute() { - // output(Localization.lang("Opening preferences...")); if (prefsDialog == null) { prefsDialog = new PreferencesDialog(jabRefFrame); - //prefsDialog.setLocationRelativeTo(JabRefFrame.this); } else { prefsDialog.setValues(); } - prefsDialog.setVisible(true); - //output(""); - + prefsDialog.show(); } - } diff --git a/src/main/java/org/jabref/gui/actions/StandardActions.java b/src/main/java/org/jabref/gui/actions/StandardActions.java index 957277f84a2..0d581cc8333 100644 --- a/src/main/java/org/jabref/gui/actions/StandardActions.java +++ b/src/main/java/org/jabref/gui/actions/StandardActions.java @@ -2,8 +2,8 @@ import java.util.Optional; -import org.jabref.gui.IconTheme; -import org.jabref.gui.JabRefIcon; +import org.jabref.gui.icon.IconTheme; +import org.jabref.gui.icon.JabRefIcon; import org.jabref.gui.keyboard.KeyBinding; import org.jabref.logic.l10n.Localization; import org.jabref.model.database.BibDatabaseMode; @@ -34,7 +34,7 @@ public enum StandardActions implements Action { OPEN_EXTERNAL_FILE(Localization.lang("Open file"), IconTheme.JabRefIcons.FILE, KeyBinding.OPEN_FILE), OPEN_URL(Localization.lang("Open URL or DOI"), IconTheme.JabRefIcons.WWW, KeyBinding.OPEN_URL_OR_DOI), MERGE_WITH_FETCHED_ENTRY(Localization.lang("Get BibTeX data from %0", "DOI/ISBN/...")), - ADD_FILE_LINK(Localization.lang("Attach file"), IconTheme.JabRefIcons.ATTACH_FILE), + ATTACH_FILE(Localization.lang("Attach file"), IconTheme.JabRefIcons.ATTACH_FILE), ADD_TO_GROUP(Localization.lang("Add to group")), REMOVE_FROM_GROUP(Localization.lang("Remove from group")), MOVE_TO_GROUP(Localization.lang("Move to group")), diff --git a/src/main/java/org/jabref/gui/auximport/FromAuxDialog.java b/src/main/java/org/jabref/gui/auximport/FromAuxDialog.java index 7e879d1a36f..22a65babf34 100644 --- a/src/main/java/org/jabref/gui/auximport/FromAuxDialog.java +++ b/src/main/java/org/jabref/gui/auximport/FromAuxDialog.java @@ -132,7 +132,7 @@ private void jbInit() { // Key bindings: ActionMap am = statusPanel.getActionMap(); InputMap im = statusPanel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW); - im.put(Globals.getKeyPrefs().getKey(KeyBinding.CLOSE_DIALOG), "close"); + im.put(Globals.getKeyPrefs().getKey(KeyBinding.CLOSE), "close"); am.put("close", new AbstractAction() { @Override diff --git a/src/main/java/org/jabref/gui/bibtexkeypattern/BibtexKeyPatternPanel.java b/src/main/java/org/jabref/gui/bibtexkeypattern/BibtexKeyPatternPanel.java index cabdd4b00bc..5d92dce5e62 100644 --- a/src/main/java/org/jabref/gui/bibtexkeypattern/BibtexKeyPatternPanel.java +++ b/src/main/java/org/jabref/gui/bibtexkeypattern/BibtexKeyPatternPanel.java @@ -19,8 +19,8 @@ import org.jabref.Globals; import org.jabref.gui.BasePanel; -import org.jabref.gui.IconTheme; import org.jabref.gui.help.HelpAction; +import org.jabref.gui.icon.IconTheme; import org.jabref.logic.help.HelpFile; import org.jabref.logic.l10n.Localization; import org.jabref.model.EntryTypes; diff --git a/src/main/java/org/jabref/gui/bibtexkeypattern/ResolveDuplicateLabelDialog.java b/src/main/java/org/jabref/gui/bibtexkeypattern/ResolveDuplicateLabelDialog.java index 3a36c4f686e..e4ecee93456 100644 --- a/src/main/java/org/jabref/gui/bibtexkeypattern/ResolveDuplicateLabelDialog.java +++ b/src/main/java/org/jabref/gui/bibtexkeypattern/ResolveDuplicateLabelDialog.java @@ -104,7 +104,7 @@ public void actionPerformed(ActionEvent e) { ActionMap am = b.getPanel().getActionMap(); InputMap im = b.getPanel().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW); - im.put(Globals.getKeyPrefs().getKey(KeyBinding.CLOSE_DIALOG), "close"); + im.put(Globals.getKeyPrefs().getKey(KeyBinding.CLOSE), "close"); am.put("close", closeAction); } diff --git a/src/main/java/org/jabref/gui/cleanup/CleanupDialog.java b/src/main/java/org/jabref/gui/cleanup/CleanupDialog.java new file mode 100644 index 00000000000..4ac358e8434 --- /dev/null +++ b/src/main/java/org/jabref/gui/cleanup/CleanupDialog.java @@ -0,0 +1,34 @@ +package org.jabref.gui.cleanup; + +import javax.swing.JScrollPane; + +import javafx.scene.control.ButtonType; + +import org.jabref.gui.util.BaseDialog; +import org.jabref.gui.util.ControlHelper; +import org.jabref.logic.cleanup.CleanupPreset; +import org.jabref.logic.l10n.Localization; +import org.jabref.model.database.BibDatabaseContext; + +public class CleanupDialog extends BaseDialog { + + public CleanupDialog(BibDatabaseContext databaseContext, CleanupPreset initialPreset) { + setTitle(Localization.lang("Cleanup entries")); + getDialogPane().setPrefSize(600, 600); + getDialogPane().getButtonTypes().setAll(ButtonType.OK, ButtonType.CANCEL); + + CleanupPresetPanel presetPanel = new CleanupPresetPanel(databaseContext, initialPreset); + presetPanel.getScrollPane().setVisible(true); + JScrollPane scrollPane = presetPanel.getScrollPane(); + + setResultConverter(button -> { + if (button == ButtonType.OK) { + return presetPanel.getCleanupPreset(); + } else { + return null; + } + }); + + ControlHelper.setSwingContent(getDialogPane(), scrollPane); + } +} diff --git a/src/main/java/org/jabref/gui/collab/FileUpdatePanel.java b/src/main/java/org/jabref/gui/collab/FileUpdatePanel.java index 71b54b96888..ea270674a02 100644 --- a/src/main/java/org/jabref/gui/collab/FileUpdatePanel.java +++ b/src/main/java/org/jabref/gui/collab/FileUpdatePanel.java @@ -17,11 +17,11 @@ import javafx.scene.layout.Priority; import org.jabref.gui.BasePanel; -import org.jabref.gui.IconTheme; import org.jabref.gui.SidePaneComponent; import org.jabref.gui.SidePaneManager; import org.jabref.gui.SidePaneType; import org.jabref.gui.actions.Action; +import org.jabref.gui.icon.IconTheme; import org.jabref.logic.l10n.Localization; public class FileUpdatePanel extends SidePaneComponent implements ActionListener, ChangeScanner.DisplayResultCallback { diff --git a/src/main/resources/org/jabref/gui/copyfiles/CopyFilesDialog.fxml b/src/main/java/org/jabref/gui/copyfiles/CopyFilesDialog.fxml similarity index 61% rename from src/main/resources/org/jabref/gui/copyfiles/CopyFilesDialog.fxml rename to src/main/java/org/jabref/gui/copyfiles/CopyFilesDialog.fxml index 88aa1b0a03b..6e7bb95f5f2 100644 --- a/src/main/resources/org/jabref/gui/copyfiles/CopyFilesDialog.fxml +++ b/src/main/java/org/jabref/gui/copyfiles/CopyFilesDialog.fxml @@ -6,8 +6,9 @@ - - +
@@ -22,13 +23,6 @@
- - - - diff --git a/src/main/java/org/jabref/gui/fieldeditors/IdentifierEditor.java b/src/main/java/org/jabref/gui/fieldeditors/IdentifierEditor.java index 2e0bc0a457f..1c671091b2f 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/IdentifierEditor.java +++ b/src/main/java/org/jabref/gui/fieldeditors/IdentifierEditor.java @@ -12,7 +12,6 @@ import org.jabref.gui.DialogService; import org.jabref.gui.autocompleter.AutoCompleteSuggestionProvider; import org.jabref.gui.fieldeditors.contextmenu.EditorMenus; -import org.jabref.gui.util.ControlHelper; import org.jabref.gui.util.TaskExecutor; import org.jabref.logic.integrity.FieldCheckers; import org.jabref.logic.l10n.Localization; @@ -20,6 +19,8 @@ import org.jabref.model.entry.FieldName; import org.jabref.preferences.JabRefPreferences; +import com.airhacks.afterburner.views.ViewLoader; + public class IdentifierEditor extends HBox implements FieldEditorFX { @FXML private IdentifierEditorViewModel viewModel; @@ -31,7 +32,9 @@ public class IdentifierEditor extends HBox implements FieldEditorFX { public IdentifierEditor(String fieldName, TaskExecutor taskExecutor, DialogService dialogService, AutoCompleteSuggestionProvider suggestionProvider, FieldCheckers fieldCheckers, JabRefPreferences preferences) { this.viewModel = new IdentifierEditorViewModel(fieldName, suggestionProvider, taskExecutor, dialogService, fieldCheckers); - ControlHelper.loadFXMLForControl(this); + ViewLoader.view(this) + .root(this) + .load(); textArea.textProperty().bindBidirectional(viewModel.textProperty()); diff --git a/src/main/java/org/jabref/gui/fieldeditors/JournalEditor.fxml b/src/main/java/org/jabref/gui/fieldeditors/JournalEditor.fxml index 929680695a9..a678a7778c5 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/JournalEditor.fxml +++ b/src/main/java/org/jabref/gui/fieldeditors/JournalEditor.fxml @@ -4,13 +4,14 @@ - - + + +
+
diff --git a/src/main/java/org/jabref/gui/filelist/LinkedFileEditDialogView.java b/src/main/java/org/jabref/gui/filelist/LinkedFileEditDialogView.java new file mode 100644 index 00000000000..3833f493437 --- /dev/null +++ b/src/main/java/org/jabref/gui/filelist/LinkedFileEditDialogView.java @@ -0,0 +1,71 @@ +package org.jabref.gui.filelist; + +import javax.inject.Inject; + +import javafx.event.ActionEvent; +import javafx.fxml.FXML; +import javafx.scene.control.ButtonType; +import javafx.scene.control.ComboBox; +import javafx.scene.control.TextField; + +import org.jabref.gui.DialogService; +import org.jabref.gui.StateManager; +import org.jabref.gui.externalfiletype.ExternalFileType; +import org.jabref.gui.externalfiletype.ExternalFileTypes; +import org.jabref.gui.util.BaseDialog; +import org.jabref.model.entry.LinkedFile; +import org.jabref.preferences.PreferencesService; + +import com.airhacks.afterburner.views.ViewLoader; + +public class LinkedFileEditDialogView extends BaseDialog { + + @FXML private TextField link; + @FXML private TextField description; + @FXML private ComboBox fileType; + + @Inject private DialogService dialogService; + @Inject private StateManager stateManager; + + @Inject private PreferencesService preferences; + + private LinkedFilesEditDialogViewModel viewModel; + + private final LinkedFile linkedFile; + private ExternalFileTypes externalFileTypes; + + public LinkedFileEditDialogView(LinkedFile linkedFile) { + this.linkedFile = linkedFile; + + this.externalFileTypes = ExternalFileTypes.getInstance(); + ViewLoader.view(this) + .load() + .setAsContent(this.getDialogPane()); + + this.getDialogPane().getButtonTypes().addAll(ButtonType.APPLY, ButtonType.CANCEL); + + this.setResultConverter(button -> { + if (button == ButtonType.APPLY) { + return viewModel.getNewLinkedFile(); + } else { + return null; + } + }); + } + + @FXML + private void initialize() { + + viewModel = new LinkedFilesEditDialogViewModel(linkedFile, stateManager.getActiveDatabase().get(), dialogService, preferences, externalFileTypes); + fileType.itemsProperty().bindBidirectional(viewModel.externalFileTypeProperty()); + description.textProperty().bindBidirectional(viewModel.descriptionProperty()); + link.textProperty().bindBidirectional(viewModel.linkProperty()); + fileType.valueProperty().bindBidirectional(viewModel.selectedExternalFileTypeProperty()); + } + + @FXML + private void openBrowseDialog(ActionEvent event) { + viewModel.openBrowseDialog(); + link.requestFocus(); + } +} diff --git a/src/main/java/org/jabref/gui/filelist/LinkedFilesEditDialogViewModel.java b/src/main/java/org/jabref/gui/filelist/LinkedFilesEditDialogViewModel.java new file mode 100644 index 00000000000..5a24ba38822 --- /dev/null +++ b/src/main/java/org/jabref/gui/filelist/LinkedFilesEditDialogViewModel.java @@ -0,0 +1,126 @@ +package org.jabref.gui.filelist; + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; +import java.util.Optional; +import java.util.regex.Pattern; + +import javafx.beans.property.ListProperty; +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleListProperty; +import javafx.beans.property.SimpleObjectProperty; +import javafx.beans.property.SimpleStringProperty; +import javafx.beans.property.StringProperty; +import javafx.collections.FXCollections; + +import org.jabref.gui.AbstractViewModel; +import org.jabref.gui.DialogService; +import org.jabref.gui.externalfiletype.ExternalFileType; +import org.jabref.gui.externalfiletype.ExternalFileTypes; +import org.jabref.gui.externalfiletype.UnknownExternalFileType; +import org.jabref.gui.util.FileDialogConfiguration; +import org.jabref.logic.util.io.FileUtil; +import org.jabref.model.database.BibDatabaseContext; +import org.jabref.model.entry.LinkedFile; +import org.jabref.model.util.FileHelper; +import org.jabref.preferences.PreferencesService; + +public class LinkedFilesEditDialogViewModel extends AbstractViewModel { + + private static final Pattern REMOTE_LINK_PATTERN = Pattern.compile("[a-z]+://.*"); + private final StringProperty link = new SimpleStringProperty(""); + private final StringProperty description = new SimpleStringProperty(""); + private final ListProperty allExternalFileTypes = new SimpleListProperty<>(FXCollections.emptyObservableList()); + private final ObjectProperty selectedExternalFileType = new SimpleObjectProperty<>(); + private final BibDatabaseContext database; + private final DialogService dialogService; + private final PreferencesService preferences; + private final ExternalFileTypes externalFileTypes; + + public LinkedFilesEditDialogViewModel(LinkedFile linkedFile, BibDatabaseContext database, DialogService dialogService, PreferencesService preferences, ExternalFileTypes externalFileTypes) { + this.database = database; + this.dialogService = dialogService; + this.preferences = preferences; + this.externalFileTypes = externalFileTypes; + allExternalFileTypes.set(FXCollections.observableArrayList(externalFileTypes.getExternalFileTypeSelection())); + setValues(linkedFile); + } + + private void setExternalFileTypeByExtension(String link) { + if (!link.isEmpty()) { + + // Check if this looks like a remote link: + if (REMOTE_LINK_PATTERN.matcher(link).matches()) { + externalFileTypes.getExternalFileTypeByExt("html").ifPresent(selectedExternalFileType::setValue); + } + + // Try to guess the file type: + String theLink = link.trim(); + externalFileTypes.getExternalFileTypeForName(theLink).ifPresent(selectedExternalFileType::setValue); + } + } + + public void openBrowseDialog() { + String fileText = link.get(); + + Optional file = FileHelper.expandFilename(database, fileText, preferences.getFileDirectoryPreferences()); + + Path workingDir = file.orElse(preferences.getWorkingDir()); + String fileName = Paths.get(fileText).getFileName().toString(); + + FileDialogConfiguration fileDialogConfiguration = new FileDialogConfiguration.Builder() + .withInitialDirectory(workingDir) + .withInitialFileName(fileName) + .build(); + + dialogService.showFileOpenDialog(fileDialogConfiguration).ifPresent(path -> { + // Store the directory for next time: + preferences.setWorkingDir(path); + + // If the file is below the file directory, make the path relative: + List fileDirectories = database.getFileDirectoriesAsPaths(preferences.getFileDirectoryPreferences()); + path = FileUtil.shortenFileName(path, fileDirectories); + + link.set(path.toString()); + setExternalFileTypeByExtension(link.getValueSafe()); + }); + } + + public void setValues(LinkedFile linkedFile) { + description.set(linkedFile.getDescription()); + link.set(linkedFile.getLink()); + + selectedExternalFileType.setValue(null); + + // See what is a reasonable selection for the type combobox: + Optional fileType = externalFileTypes.fromLinkedFile(linkedFile, false); + if (fileType.isPresent() && !(fileType.get() instanceof UnknownExternalFileType)) { + selectedExternalFileType.setValue(fileType.get()); + } else if ((linkedFile.getLink() != null) && (!linkedFile.getLink().isEmpty())) { + setExternalFileTypeByExtension(linkedFile.getLink()); + } + } + + public StringProperty linkProperty() { + return link; + } + + public StringProperty descriptionProperty() { + return description; + } + + public ListProperty externalFileTypeProperty() { + return allExternalFileTypes; + } + + public ObjectProperty selectedExternalFileTypeProperty() { + return selectedExternalFileType; + } + + public LinkedFile getNewLinkedFile() { + return new LinkedFile(description.getValue(), link.getValue(), selectedExternalFileType.getValue().toString()); + + } + +} diff --git a/src/main/java/org/jabref/gui/groups/GroupAddRemoveDialog.java b/src/main/java/org/jabref/gui/groups/GroupAddRemoveDialog.java index 6497cdd0db4..9cfc6d32bef 100644 --- a/src/main/java/org/jabref/gui/groups/GroupAddRemoveDialog.java +++ b/src/main/java/org/jabref/gui/groups/GroupAddRemoveDialog.java @@ -109,7 +109,7 @@ public void action() throws Exception { // Key bindings: ActionMap am = sp.getActionMap(); InputMap im = sp.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW); - im.put(Globals.getKeyPrefs().getKey(KeyBinding.CLOSE_DIALOG), "close"); + im.put(Globals.getKeyPrefs().getKey(KeyBinding.CLOSE), "close"); am.put("close", new AbstractAction() { @Override diff --git a/src/main/java/org/jabref/gui/groups/GroupDialog.java b/src/main/java/org/jabref/gui/groups/GroupDialog.java index fb743b8c4c0..4abd8d6ac83 100644 --- a/src/main/java/org/jabref/gui/groups/GroupDialog.java +++ b/src/main/java/org/jabref/gui/groups/GroupDialog.java @@ -348,7 +348,7 @@ public void actionPerformed(ActionEvent e) { }; mCancel.addActionListener(cancelAction); builderAll.getPanel().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW) - .put(Globals.getKeyPrefs().getKey(KeyBinding.CLOSE_DIALOG), "close"); + .put(Globals.getKeyPrefs().getKey(KeyBinding.CLOSE), "close"); builderAll.getPanel().getActionMap().put("close", cancelAction); okButton.addActionListener(e -> { diff --git a/src/main/java/org/jabref/gui/groups/GroupNodeViewModel.java b/src/main/java/org/jabref/gui/groups/GroupNodeViewModel.java index 65c088b7f31..934c4d40af6 100644 --- a/src/main/java/org/jabref/gui/groups/GroupNodeViewModel.java +++ b/src/main/java/org/jabref/gui/groups/GroupNodeViewModel.java @@ -16,10 +16,10 @@ import javafx.scene.paint.Color; import org.jabref.gui.DragAndDropDataFormats; -import org.jabref.gui.IconTheme; -import org.jabref.gui.InternalMaterialDesignIcon; -import org.jabref.gui.JabRefIcon; import org.jabref.gui.StateManager; +import org.jabref.gui.icon.IconTheme; +import org.jabref.gui.icon.InternalMaterialDesignIcon; +import org.jabref.gui.icon.JabRefIcon; import org.jabref.gui.util.BackgroundTask; import org.jabref.gui.util.BindingsHelper; import org.jabref.gui.util.LocalDragboard; diff --git a/src/main/java/org/jabref/gui/groups/GroupSidePane.java b/src/main/java/org/jabref/gui/groups/GroupSidePane.java index 5e59a42d430..d8c837619db 100644 --- a/src/main/java/org/jabref/gui/groups/GroupSidePane.java +++ b/src/main/java/org/jabref/gui/groups/GroupSidePane.java @@ -3,15 +3,17 @@ import javafx.scene.Node; import javafx.scene.layout.Priority; -import org.jabref.gui.IconTheme; import org.jabref.gui.SidePaneComponent; import org.jabref.gui.SidePaneManager; import org.jabref.gui.SidePaneType; import org.jabref.gui.actions.Action; import org.jabref.gui.actions.StandardActions; +import org.jabref.gui.icon.IconTheme; import org.jabref.logic.l10n.Localization; import org.jabref.preferences.JabRefPreferences; +import com.airhacks.afterburner.views.ViewLoader; + /** * The groups side pane. */ @@ -46,7 +48,9 @@ public Action getToggleAction() { @Override protected Node createContentPane() { - return new GroupTreeView().getView(); + return ViewLoader.view(GroupTreeView.class) + .load() + .getView(); } @Override diff --git a/src/main/java/org/jabref/gui/groups/GroupTree.css b/src/main/java/org/jabref/gui/groups/GroupTree.css index ca0fd0eb453..a05f6bbbc19 100644 --- a/src/main/java/org/jabref/gui/groups/GroupTree.css +++ b/src/main/java/org/jabref/gui/groups/GroupTree.css @@ -1,10 +1,10 @@ .tree-table-view, .tree-table-row-cell { - -fx-background-color: #dadad8; + -fx-background-color: -jr-sidepane-background; -fx-table-cell-border-color: transparent; /* hide grid lines */ } .tree-table-cell { - -fx-font-size: 105%; + /*-fx-font-size: 105%;*/ -fx-padding: 0.35em 0em 0.25em 0em; } @@ -14,19 +14,19 @@ } .numberColumn > .hits { - -fx-font-size: 85%; - -fx-background-color: #c6c6c4; + /*-fx-font-size: 85%;*/ + -fx-background-color: -jr-group-hits-bg; -fx-padding: 0.1em 0.4em 0.1em 0.4em; -fx-background-insets: 0; -fx-background-radius: 0.8em; } .numberColumn > .hits:any-selected { - -fx-background-color: #c6c44f; + -fx-background-color: derive(-jr-green, 70%); } .numberColumn > .hits:all-selected { - -fx-background-color: #4dc64f; + -fx-background-color: -jr-green; } .disclosureNodeColumn { @@ -34,19 +34,19 @@ } .tree-table-row-cell:dragOver-bottom { - -fx-border-color: #eea82f; + -fx-border-color: -jr-drag-target; -fx-border-width: 0 0 2 0; -fx-padding: 0 0 -2 0; } .tree-table-row-cell:dragOver-center { - -fx-border-color: #eea82f; + -fx-border-color: -jr-drag-target; -fx-border-width: 2 2 2 2; -fx-padding: -2 -2 -2 -2; } .tree-table-row-cell:dragOver-top { - -fx-border-color: #eea82f; + -fx-border-color: -jr-drag-target; -fx-border-width: 2 0 0 0; -fx-padding: -2 0 0 0; } @@ -61,7 +61,7 @@ .tree-table-row-cell:root { -fx-border-width: 0 0 1 0; - -fx-border-color: darkgrey; + -fx-border-color: -fx-outer-border; } .tree-table-row-cell:root > .tree-table-cell { @@ -76,9 +76,9 @@ -fx-padding: 0.45em 0.2em 0.45em 0.2em; } -.tree-table-row-cell:selected > .tree-table-cell > .tree-disclosure-node > .arrow { - -fx-background-color: white; -} +/*.tree-table-row-cell:selected > .tree-table-cell > .tree-disclosure-node > .arrow {*/ + /*-fx-background-color: -jr-white;*/ +/*}*/ .tree-table-row-cell:empty { -fx-background-color: transparent; /* hide cells which are not bound to a group */ @@ -92,7 +92,6 @@ .tree-table-row-cell > .tree-table-cell > .tree-disclosure-node > .arrow { -fx-max-height: 0.5em; -fx-max-width: 0.5em; - -fx-background-color: #5e5f5e; -fx-padding: 0.333333em 0.229em 0.333333em 0.229em; /* 4 */ -fx-shape: "m64,416l96,96l256,-256l-256,-256l-96,96l160,160l-160,160z"; } @@ -106,12 +105,12 @@ } #barBottom { - -fx-background-color: #dadad8; - -fx-border-color: dimgray; - -fx-border-width: 1 0 0 0; + -fx-border-color: -fx-outer-border; + -fx-border-width: 0 0 1 0; -fx-padding: 0em 1em 0em 1em; } #barBottom .glyph-icon { + -fx-fill: -jr-icon; -fx-font-size: 2em; } diff --git a/src/main/java/org/jabref/gui/groups/GroupTree.fxml b/src/main/java/org/jabref/gui/groups/GroupTree.fxml index 4760b197537..0c129da4e52 100644 --- a/src/main/java/org/jabref/gui/groups/GroupTree.fxml +++ b/src/main/java/org/jabref/gui/groups/GroupTree.fxml @@ -8,10 +8,10 @@ - + + xmlns="http://javafx.com/javafx/8.0.112" fx:controller="org.jabref.gui.groups.GroupTreeView">
@@ -33,7 +33,7 @@