diff --git a/.mailmap b/.mailmap index b8698d75865..3af3033fd28 100644 --- a/.mailmap +++ b/.mailmap @@ -142,4 +142,5 @@ Johannes Manner Dominik Traczyk Cerrianne Santos Stefan Scheffel -Stefan Gerzmann \ No newline at end of file +Stefan Gerzmann +Deepak Kumar diff --git a/CHANGELOG.md b/CHANGELOG.md index 9e14888432a..651ab6ab391 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ We refer to [GitHub issues](https://github.com/JabRef/jabref/issues) by using `# ## [Unreleased] ### Changed +- We changed the latex command removal for docbook exporter. [#3838](https://github.com/JabRef/jabref/issues/3838) - We changed the location of some fields in the entry editor (you might need to reset your preferences for these changes to come into effect) - Journal/Year/Month in biblatex mode -> Deprecated (if filled) - DOI/URL: General -> Optional @@ -60,8 +61,12 @@ We refer to [GitHub issues](https://github.com/JabRef/jabref/issues) by using `# - We changed the title of Group Dialog to "Add subgroup" from "Edit group" when we select Add subgroup option. - We enable import button only if entries are selected. [#4755](https://github.com/JabRef/jabref/issues/4755) - We made modifications to improve contrast of UI elements. [#4583](https://github.com/JabRef/jabref/issues/4583) +- We added an option in the settings to set the default action in JabRef when right clicking on any entry in any database and selecting "Open folder". [#4763](https://github.com/JabRef/jabref/issues/4763) +- The Medline fetcher now normalizes the author names according to the BibTeX-Standard [#4345](https://github.com/JabRef/jabref/issues/4345) + ### Fixed +- We fixed an issue where JabRef died silently for the user without enough inotify instances [#4874](https://github.com/JabRef/jabref/issues/4847) - We fixed an issue where corresponding groups are sometimes not highlighted when clicking on entries [#3112](https://github.com/JabRef/jabref/issues/3112) - We fixed an issue where custom exports could not be selected in the 'Export (selected) entries' dialog [#4013](https://github.com/JabRef/jabref/issues/4013) - Italic text is now rendered correctly. https://github.com/JabRef/jabref/issues/3356 @@ -73,6 +78,7 @@ We refer to [GitHub issues](https://github.com/JabRef/jabref/issues) by using `# - We fixed an issue where selecting a group messed up the focus of the main table / entry editor. https://github.com/JabRef/jabref/issues/3367 - We fixed an issue where composite author names were sorted incorrectly. https://github.com/JabRef/jabref/issues/2828 - We fixed an issue where commands followed by `-` didn't work. [#3805](https://github.com/JabRef/jabref/issues/3805) +- We fixed an issue where a non-existing aux file in a group made it impossible to open the library. [#4735](https://github.com/JabRef/jabref/issues/4735) - We fixed an issue where some journal names were wrongly marked as abbreviated. [#4115](https://github.com/JabRef/jabref/issues/4115) - We fixed an issue where the custom file column were sorted incorrectly. https://github.com/JabRef/jabref/issues/3119 - We fixed an issues where the entry losses focus when a field is edited and at the same time used for sorting. https://github.com/JabRef/jabref/issues/3373 @@ -86,6 +92,7 @@ We refer to [GitHub issues](https://github.com/JabRef/jabref/issues) by using `# - We fixed an issue where the default icon of a group was not colored correctly. - We fixed an issue where the first field in entry editor was not focused when adding a new entry. [#4024](https://github.com/JabRef/jabref/issues/4024) - We reworked the "Edit file" dialog to make it resizeable and improved the workflow for adding and editing files https://github.com/JabRef/jabref/issues/2970 +- We fixed an issue where custom name formatters were no longer found correctly. [#3531](https://github.com/JabRef/jabref/issues/3531) - We fixed an issue where the month was not shown in the preview https://github.com/JabRef/jabref/issues/3239. - Rewritten logic to detect a second jabref instance. [#4023](https://github.com/JabRef/jabref/issues/4023) - We fixed an issue where the "Convert to BibTeX-Cleanup" moved the content of the `file` field to the `pdf` field [#4120](https://github.com/JabRef/jabref/issues/4120) @@ -99,14 +106,18 @@ We refer to [GitHub issues](https://github.com/JabRef/jabref/issues) by using `# - We fixed an issue where only one PDF file could be imported [#4422](https://github.com/JabRef/jabref/issues/4422) - We fixed an issue where "Move to group" would always move the first entry in the library and not the selected [#4414](https://github.com/JabRef/jabref/issues/4414) - We fixed an issue where an older dialog appears when downloading full texts from the quality menu. [#4489](https://github.com/JabRef/jabref/issues/4489) - - +- We fixed an issue where right clicking on any entry in any database and selecting "Open folder" results in the NullPointer exception. [#4763](https://github.com/JabRef/jabref/issues/4763) +- We fixed an issue where option 'open terminal here' with custom command was passing wrong argument. [#4802](https://github.com/JabRef/jabref/issues/4802) +- We fixed an issue where ranking an entry would generate an IllegalArgumentException. [#4754](https://github.com/JabRef/jabref/issues/4754) +- We fixed an issue where special characters where removed from non label key generation pattern parts [#4767](https://github.com/JabRef/jabref/issues/4767) +- We fixed an issue where the RIS import would overwite the article date with the value of the acessed date [#4816](https://github.com/JabRef/jabref/issues/4816) ### Removed - The feature to "mark entries" was removed and merged with the groups functionality. For migration, a group is created for every value of the `__markedentry` field and the entry is added to this group. - The number column was removed. +- We removed the global search feature. - We removed the coloring of cells in the maintable according to whether the field is optional/required. - We removed the feature to find and resolve duplicate BibTeX keys (as this use case is already covered by the integrity check). - We removed a few commands from the right-click menu that are not needed often and thus don't need to be placed that prominently: @@ -144,7 +155,6 @@ We refer to [GitHub issues](https://github.com/JabRef/jabref/issues) by using `# - ## Older versions diff --git a/build.gradle b/build.gradle index 91b5b010301..12a00be3230 100644 --- a/build.gradle +++ b/build.gradle @@ -93,9 +93,9 @@ dependencies { compile 'com.jgoodies:jgoodies-common:1.8.1' compile 'com.jgoodies:jgoodies-forms:1.9.0' - compile 'org.apache.pdfbox:pdfbox:2.0.14' - compile 'org.apache.pdfbox:fontbox:2.0.14' - compile 'org.apache.pdfbox:xmpbox:2.0.14' + compile 'org.apache.pdfbox:pdfbox:2.0.15' + compile 'org.apache.pdfbox:fontbox:2.0.15' + compile 'org.apache.pdfbox:xmpbox:2.0.15' compile group: 'org.apache.tika', name: 'tika-core', version: '1.20' @@ -104,13 +104,13 @@ dependencies { compile 'commons-cli:commons-cli:1.4' - compile "org.libreoffice:juh:5.4.2" - compile "org.libreoffice:jurt:5.4.2" - compile "org.libreoffice:ridl:5.4.2" - compile "org.libreoffice:unoil:5.4.2" + compile "org.libreoffice:juh:6.2.2" + compile "org.libreoffice:jurt:6.2.2" + compile "org.libreoffice:ridl:6.2.2" + compile "org.libreoffice:unoil:6.2.2" compile 'io.github.java-diff-utils:java-diff-utils:4.0' - compile 'info.debatty:java-string-similarity:1.1.0' + compile 'info.debatty:java-string-similarity:1.2.1' antlr3 'org.antlr:antlr:3.5.2' compile 'org.antlr:antlr-runtime:3.5.2' @@ -159,23 +159,23 @@ dependencies { compile group: 'com.microsoft.azure', name: 'applicationinsights-core', version: '2.3.1' compile group: 'com.microsoft.azure', name: 'applicationinsights-logging-log4j2', version: '2.3.1' - testImplementation 'org.junit.jupiter:junit-jupiter:5.4.0' - testRuntimeOnly 'org.junit.vintage:junit-vintage-engine:5.4.0' - testCompile 'org.junit.platform:junit-platform-launcher:1.4.0' + testImplementation 'org.junit.jupiter:junit-jupiter:5.4.2' + testRuntimeOnly 'org.junit.vintage:junit-vintage-engine:5.4.2' + testCompile 'org.junit.platform:junit-platform-launcher:1.4.2' testRuntime 'org.apache.logging.log4j:log4j-core:2.11.1' testRuntime 'org.apache.logging.log4j:log4j-jul:2.11.2' - testCompile 'org.mockito:mockito-core:2.25.0' - testCompile 'com.github.tomakehurst:wiremock:2.21.0' + testCompile 'org.mockito:mockito-core:2.27.0' + testCompile 'com.github.tomakehurst:wiremock:2.22.0' testCompile 'org.reflections:reflections:0.9.11' testCompile 'org.xmlunit:xmlunit-core:2.6.2' testCompile 'org.xmlunit:xmlunit-matchers:2.6.2' - testRuntime 'com.tngtech.archunit:archunit-junit5-engine:0.9.3' - testCompile 'com.tngtech.archunit:archunit-junit5-api:0.9.3' + testRuntime 'com.tngtech.archunit:archunit-junit5-engine:0.10.2' + testCompile 'com.tngtech.archunit:archunit-junit5-api:0.10.2' testCompile "org.testfx:testfx-core:4.0.+" testCompile "org.testfx:testfx-junit5:4.0.+" - checkstyle 'com.puppycrawl.tools:checkstyle:8.18' + checkstyle 'com.puppycrawl.tools:checkstyle:8.19' } jacoco { diff --git a/lib/AppleJavaExtensions.jar b/lib/AppleJavaExtensions.jar deleted file mode 100644 index 6659a81c69f..00000000000 Binary files a/lib/AppleJavaExtensions.jar and /dev/null differ diff --git a/src/main/java/org/jabref/JabRefException.java b/src/main/java/org/jabref/JabRefException.java index 0c312fc10df..a1dd034d89e 100644 --- a/src/main/java/org/jabref/JabRefException.java +++ b/src/main/java/org/jabref/JabRefException.java @@ -33,7 +33,7 @@ public JabRefException(Throwable cause) { @Override public String getLocalizedMessage() { if (localizedMessage == null) { - LOGGER.debug("No localized message exception message defined. Falling back to getMessage()."); + LOGGER.debug("No localized exception message defined. Falling back to getMessage()."); return getMessage(); } else { return localizedMessage; diff --git a/src/main/java/org/jabref/JabRefGUI.java b/src/main/java/org/jabref/JabRefGUI.java index 57214d9ef9f..66fe3694ffa 100644 --- a/src/main/java/org/jabref/JabRefGUI.java +++ b/src/main/java/org/jabref/JabRefGUI.java @@ -10,8 +10,6 @@ import javafx.stage.Stage; import org.jabref.gui.BasePanel; -import org.jabref.gui.DialogService; -import org.jabref.gui.FXDialogService; import org.jabref.gui.GUIGlobals; import org.jabref.gui.JabRefFrame; import org.jabref.gui.dialogs.BackupUIManager; @@ -43,14 +41,13 @@ public class JabRefGUI { private final boolean isBlank; private final List failed = new ArrayList<>(); private final List toOpenTab = new ArrayList<>(); - private final DialogService dialogService; private final String focusedFile; public JabRefGUI(Stage mainStage, List argsDatabases, boolean isBlank) { this.bibDatabases = argsDatabases; this.isBlank = isBlank; - this.dialogService = new FXDialogService(mainStage); + mainFrame = new JabRefFrame(mainStage); // passed file (we take the first one) should be focused focusedFile = argsDatabases.stream() @@ -60,22 +57,12 @@ public JabRefGUI(Stage mainStage, List argsDatabases, boolean isBl .orElse(Globals.prefs.get(JabRefPreferences.LAST_FOCUSED)); openWindow(mainStage); - new VersionWorker(Globals.BUILD_INFO.getVersion(), Globals.prefs.getVersionPreferences().getIgnoredVersion(), JabRefGUI.getMainFrame().getDialogService(), Globals.TASK_EXECUTOR) + new VersionWorker(Globals.BUILD_INFO.getVersion(), Globals.prefs.getVersionPreferences().getIgnoredVersion(), mainFrame.getDialogService(), Globals.TASK_EXECUTOR) .checkForNewVersionAsync(false); } private void openWindow(Stage mainStage) { - // Set antialiasing on everywhere. This only works in JRE >= 1.5. - // Or... it doesn't work, period. - // TODO test and maybe remove this! I found this commented out with no additional info ( payload@lavabit.com ) - // Enabled since JabRef 2.11 beta 4 - System.setProperty("swing.aatext", "true"); - // Default is "on". - // "lcd" instead of "on" because of http://wiki.netbeans.org/FaqFontRendering and http://docs.oracle.com/javase/6/docs/technotes/guides/2d/flags.html#aaFonts - System.setProperty("awt.useSystemAAFontSettings", "lcd"); - - // look and feel. This MUST be the first thing to do before loading any Swing-specific code! - setLookAndFeel(); + applyFontRenderingTweak(); // If the option is enabled, open the last edited libraries, if any. if (!isBlank && Globals.prefs.getBoolean(JabRefPreferences.OPEN_LAST_EDITED)) { @@ -85,7 +72,7 @@ private void openWindow(Stage mainStage) { GUIGlobals.init(); LOGGER.debug("Initializing frame"); - JabRefGUI.mainFrame = new JabRefFrame(mainStage); + mainFrame.init(); // Add all bibDatabases databases to the frame: boolean first = false; @@ -109,7 +96,7 @@ private void openWindow(Stage mainStage) { pr.getDatabase().clearSharedDatabaseID(); LOGGER.error("Connection error", e); - dialogService.showErrorDialogAndWait( + mainFrame.getDialogService().showErrorDialogAndWait( Localization.lang("Connection error"), Localization.lang("A local copy will be opened."), e); @@ -120,7 +107,7 @@ private void openWindow(Stage mainStage) { // add them to the list toOpenTab.add(pr); } else { - JabRefGUI.getMainFrame().addParserResult(pr, first); + mainFrame.addParserResult(pr, first); first = false; } } @@ -128,7 +115,7 @@ private void openWindow(Stage mainStage) { // finally add things to the currently opened tab for (ParserResult pr : toOpenTab) { - JabRefGUI.getMainFrame().addParserResult(pr, first); + mainFrame.addParserResult(pr, first); first = false; } @@ -168,14 +155,14 @@ private void openWindow(Stage mainStage) { String message = Localization.lang("Error opening file '%0'.", pr.getFile().get().getName()) + "\n" + pr.getErrorMessage(); - dialogService.showErrorDialogAndWait(Localization.lang("Error opening file"), message); + mainFrame.getDialogService().showErrorDialogAndWait(Localization.lang("Error opening file"), message); } // Display warnings, if any int tabNumber = 0; for (ParserResult pr : bibDatabases) { - ParserResultWarningDialog.showParserResultWarningDialog(pr, JabRefGUI.getMainFrame(), tabNumber++); + ParserResultWarningDialog.showParserResultWarningDialog(pr, mainFrame, tabNumber++); } // After adding the databases, go through each and see if @@ -187,9 +174,9 @@ private void openWindow(Stage mainStage) { // This is because importToOpen might have been used, which adds to // loadedDatabases, but not to getBasePanelCount() - for (int i = 0; (i < bibDatabases.size()) && (i < JabRefGUI.getMainFrame().getBasePanelCount()); i++) { + for (int i = 0; (i < bibDatabases.size()) && (i < mainFrame.getBasePanelCount()); i++) { ParserResult pr = bibDatabases.get(i); - BasePanel panel = JabRefGUI.getMainFrame().getBasePanelAt(i); + BasePanel panel = mainFrame.getBasePanelAt(i); OpenDatabaseAction.performPostOpenActions(panel, pr); } @@ -219,7 +206,7 @@ private void openLastEditedDatabases() { } if (BackupManager.checkForBackupFile(dbFile.toPath())) { - BackupUIManager.showRestoreBackupDialog(dialogService, dbFile.toPath()); + BackupUIManager.showRestoreBackupDialog(mainFrame.getDialogService(), dbFile.toPath()); } ParserResult parsedDatabase = OpenDatabase.loadDatabase(fileName, @@ -242,9 +229,9 @@ private boolean isLoaded(File fileToOpen) { return false; } - private void setLookAndFeel() { - // On Linux, Java FX fonts look blurry per default. This can be improved by using a non-default rendering - // setting. See https://github.com/woky/javafx-hates-linux + private void applyFontRenderingTweak() { + // On Linux, Java FX fonts look blurry per default. This can be improved by using a non-default rendering setting. + // See https://github.com/woky/javafx-hates-linux if (Globals.prefs.getBoolean(JabRefPreferences.FX_FONT_RENDERING_TWEAK)) { System.setProperty("prism.text", "t2k"); System.setProperty("prism.lcdtext", "true"); @@ -254,9 +241,4 @@ private void setLookAndFeel() { public static JabRefFrame getMainFrame() { return mainFrame; } - - // Only used for testing, other than that do NOT set the mainFrame... - public static void setMainFrame(JabRefFrame mainFrame) { - JabRefGUI.mainFrame = mainFrame; - } } diff --git a/src/main/java/org/jabref/JabRefMain.java b/src/main/java/org/jabref/JabRefMain.java index 66e624a002d..62758e9c9f0 100644 --- a/src/main/java/org/jabref/JabRefMain.java +++ b/src/main/java/org/jabref/JabRefMain.java @@ -2,14 +2,13 @@ import java.net.Authenticator; -import javax.swing.JFrame; -import javax.swing.JOptionPane; - import javafx.application.Application; import javafx.application.Platform; +import javafx.scene.control.Alert; import javafx.stage.Stage; import org.jabref.cli.ArgumentProcessor; +import org.jabref.gui.FXDialog; import org.jabref.gui.remote.JabRefMessageHandler; import org.jabref.logic.journals.JournalAbbreviationLoader; import org.jabref.logic.l10n.Localization; @@ -89,11 +88,11 @@ public void stop() { /** * Tests if we are running an acceptable Java and terminates JabRef when we are sure the version is not supported. * This test uses the requirements for the Java version as specified in gradle.build. It is possible to - * define a minimum version including the built number and to indicate whether Java 9 can be use (which it currently + * define a minimum version including the built number and to indicate whether Java 9 can be used (which it currently * can't). It tries to compare this version number to the version of the currently running JVM. The check is * optimistic and will rather return true even if we could not exactly determine the version. *

- * Note: Users with an very old version like 1.6 will not profit from this since class versions are incompatible and + * Note: Users with a very old version like 1.6 will not profit from this since class versions are incompatible and * JabRef won't even start. Currently, JabRef won't start with Java 9 either, but the warning that it cannot be used * with this version is helpful anyway to prevent users to update from an old 1.8 directly to version 9. Additionally, * we soon might have a JabRef that does start with Java 9 but is not perfectly compatible. Therefore, we should leave @@ -119,9 +118,10 @@ private static void ensureCorrectJavaVersion() { versionError.append("\n"); versionError.append(Localization.lang("Note that currently, JabRef does not run with Java 9.")); } - final JFrame frame = new JFrame(); - JOptionPane.showMessageDialog(null, versionError, Localization.lang("Error"), JOptionPane.ERROR_MESSAGE); - frame.dispose(); + + FXDialog alert = new FXDialog(Alert.AlertType.ERROR, Localization.lang("Error"), true); + alert.setHeaderText(null); + alert.setContentText(versionError.toString()); // We exit on Java 9 error since this will definitely not work if (java9Fail) { @@ -163,7 +163,7 @@ private static void applyPreferences(JabRefPreferences preferences) { // Read list(s) of journal names and abbreviations Globals.journalAbbreviationLoader = new JournalAbbreviationLoader(); - /* Build list of Import and Export formats */ + // Build list of Import and Export formats Globals.IMPORT_FORMAT_READER.resetImportFormats(Globals.prefs.getImportFormatPreferences(), Globals.prefs.getXMPPreferences(), Globals.getFileUpdateMonitor()); EntryTypes.loadCustomEntryTypes(preferences.loadCustomEntryTypes(BibDatabaseMode.BIBTEX), @@ -173,7 +173,7 @@ private static void applyPreferences(JabRefPreferences preferences) { // Initialize protected terms loader Globals.protectedTermsLoader = new ProtectedTermsLoader(Globals.prefs.getProtectedTermsPreferences()); - // override used newline character with the one stored in the preferences + // Override used newline character with the one stored in the preferences // The preferences return the system newline character sequence as default OS.NEWLINE = Globals.prefs.get(JabRefPreferences.NEWLINE); } diff --git a/src/main/java/org/jabref/cli/ArgumentProcessor.java b/src/main/java/org/jabref/cli/ArgumentProcessor.java index f97a582daab..56a4de65bde 100644 --- a/src/main/java/org/jabref/cli/ArgumentProcessor.java +++ b/src/main/java/org/jabref/cli/ArgumentProcessor.java @@ -13,7 +13,9 @@ import org.jabref.Globals; import org.jabref.JabRefException; -import org.jabref.gui.externalfiles.AutoSetLinks; +import org.jabref.gui.externalfiles.AutoSetFileLinksUtil; +import org.jabref.gui.externalfiletype.ExternalFileTypes; +import org.jabref.gui.undo.NamedCompound; import org.jabref.logic.bibtexkeypattern.BibtexKeyGenerator; import org.jabref.logic.exporter.AtomicFileWriter; import org.jabref.logic.exporter.BibDatabaseWriter; @@ -496,7 +498,8 @@ private void automaticallySetFileLinks(List loaded) { for (ParserResult parserResult : loaded) { BibDatabase database = parserResult.getDatabase(); LOGGER.info(Localization.lang("Automatically setting file links")); - AutoSetLinks.autoSetLinks(database.getEntries(), parserResult.getDatabaseContext()); + AutoSetFileLinksUtil util = new AutoSetFileLinksUtil(parserResult.getDatabaseContext(), Globals.prefs.getFilePreferences(), Globals.prefs.getAutoLinkPreferences(), ExternalFileTypes.getInstance()); + util.linkAssociatedFiles(database.getEntries(), new NamedCompound("")); } } diff --git a/src/main/java/org/jabref/gui/BasePanel.java b/src/main/java/org/jabref/gui/BasePanel.java index ea88315f304..c6501ec7da9 100644 --- a/src/main/java/org/jabref/gui/BasePanel.java +++ b/src/main/java/org/jabref/gui/BasePanel.java @@ -1,6 +1,5 @@ package org.jabref.gui; -import java.io.File; import java.io.IOException; import java.io.StringReader; import java.lang.reflect.InvocationTargetException; @@ -25,7 +24,6 @@ import javafx.scene.Node; import javafx.scene.control.ScrollPane; import javafx.scene.control.SplitPane; -import javafx.scene.layout.AnchorPane; import javafx.scene.layout.StackPane; import org.jabref.Globals; @@ -65,7 +63,6 @@ import org.jabref.gui.specialfields.SpecialFieldViewModel; import org.jabref.gui.undo.CountingUndoManager; import org.jabref.gui.undo.NamedCompound; -import org.jabref.gui.undo.UndoableChangeType; import org.jabref.gui.undo.UndoableFieldChange; import org.jabref.gui.undo.UndoableInsertEntry; import org.jabref.gui.undo.UndoableRemoveEntry; @@ -116,7 +113,6 @@ public class BasePanel extends StackPane { private final BibDatabaseContext bibDatabaseContext; private final MainTableDataModel tableModel; - private final DatabaseChangePane changePane; private final CitationStyleCache citationStyleCache; private final FileAnnotationCache annotationCache; @@ -139,6 +135,7 @@ public class BasePanel extends StackPane { // As most enums, this must not be null private BasePanelMode mode = BasePanelMode.SHOWING_NOTHING; private SplitPane splitPane; + private DatabaseChangePane changePane; private boolean saving; // AutoCompleter used in the search bar @@ -187,22 +184,6 @@ public BasePanel(JabRefFrame frame, BasePanelPreferences preferences, BibDatabas // ensure that all entry changes mark the panel as changed this.bibDatabaseContext.getDatabase().registerListener(this); - Optional file = bibDatabaseContext.getDatabaseFile(); - if (file.isPresent()) { - // Register so we get notifications about outside changes to the file. - changeMonitor = Optional.of(new DatabaseChangeMonitor(bibDatabaseContext, Globals.getFileUpdateMonitor(), Globals.TASK_EXECUTOR)); - changePane = new DatabaseChangePane(mainTable.getPane(), bibDatabaseContext, changeMonitor.get()); - getChildren().add(changePane); - } else { - if (bibDatabaseContext.getDatabase().hasEntries()) { - // if the database is not empty and no file is assigned, - // the database came from an import and has to be treated somehow - // -> mark as changed - this.baseChanged = true; - } - changePane = null; - } - this.getDatabase().registerListener(new UpdateTimestampListener(Globals.prefs)); this.entryEditor = new EntryEditor(this, preferences.getEntryEditorPreferences(), Globals.getFileUpdateMonitor(), dialogService, externalFileTypes, Globals.TASK_EXECUTOR); @@ -269,7 +250,7 @@ public JabRefFrame frame() { } public void output(String s) { - frame.output(s); + dialogService.notify(s); } private void setupActions() { @@ -394,7 +375,7 @@ private void setupActions() { .withPreviewPanelEnabled(enabled) .build(); Globals.prefs.storePreviewPreferences(newPreviewPreferences); - DefaultTaskExecutor.runInJavaFXThread(() -> setPreviewActiveBasePanels(enabled)); + setPreviewActive(enabled); }); actions.put(Actions.NEXT_PREVIEW_STYLE, this::nextPreviewStyle); @@ -460,7 +441,7 @@ private void delete(boolean cut, List entries) { getUndoManager().addEdit(compound); markBaseChanged(); - frame.output(formatOutputMessage(cut ? Localization.lang("Cut") : Localization.lang("Deleted"), entries.size())); + this.output(formatOutputMessage(cut ? Localization.lang("Cut") : Localization.lang("Deleted"), entries.size())); // prevent the main table from loosing focus mainTable.requestFocus(); @@ -588,13 +569,12 @@ private void copyKeyAndTitle() { } private void openExternalFile() { + final List selectedEntries = mainTable.getSelectedEntries(); + if (selectedEntries.size() != 1) { + output(Localization.lang("This operation requires exactly one item to be selected.")); + return; + } JabRefExecutorService.INSTANCE.execute(() -> { - final List selectedEntries = mainTable.getSelectedEntries(); - if (selectedEntries.size() != 1) { - output(Localization.lang("This operation requires exactly one item to be selected.")); - return; - } - final BibEntry entry = selectedEntries.get(0); if (!entry.hasField(FieldName.FILE)) { // no bibtex field @@ -776,13 +756,9 @@ public void setupMainPanel() { createMainTable(); ScrollPane pane = mainTable.getPane(); - AnchorPane anchorPane = new AnchorPane(pane); - AnchorPane.setBottomAnchor(pane, 0.0); - AnchorPane.setTopAnchor(pane, 0.0); - AnchorPane.setLeftAnchor(pane, 0.0); - AnchorPane.setRightAnchor(pane, 0.0); - splitPane.getItems().add(anchorPane); - this.getChildren().setAll(splitPane); + pane.setFitToHeight(true); + pane.setFitToWidth(true); + splitPane.getItems().add(pane); // Set up name autocompleter for search: instantiateSearchAutoCompleter(); @@ -795,6 +771,22 @@ public void setupMainPanel() { dividerPositionSubscription = EasyBind.monadic(Bindings.valueAt(splitPane.getDividers(), 0)) .flatMap(SplitPane.Divider::positionProperty) .subscribe((observable, oldValue, newValue) -> saveDividerLocation(newValue)); + + // Add changePane in case a file is present - otherwise just add the splitPane to the panel + Optional file = bibDatabaseContext.getDatabasePath(); + if (file.isPresent()) { + // create changeMonitor and changePane so we get notifications about outside changes to the file. + resetChangeMonitorAndChangePane(); + } else { + if (bibDatabaseContext.getDatabase().hasEntries()) { + // if the database is not empty and no file is assigned, + // the database came from an import and has to be treated somehow + // -> mark as changed + this.baseChanged = true; + } + changePane = null; + getChildren().add(splitPane); + } } /** @@ -845,17 +837,13 @@ public EntryEditor getEntryEditor() { * @param entry The entry to edit. */ public void showAndEdit(BibEntry entry) { - DefaultTaskExecutor.runInJavaFXThread(() -> { - - showBottomPane(BasePanelMode.SHOWING_EDITOR); + showBottomPane(BasePanelMode.SHOWING_EDITOR); - if (entry != getShowing()) { - entryEditor.setEntry(entry); - showing = entry; - } - entryEditor.requestFocus(); - - }); + if (entry != getShowing()) { + entryEditor.setEntry(entry); + showing = entry; + } + entryEditor.requestFocus(); } private void showBottomPane(BasePanelMode newMode) { @@ -1021,39 +1009,6 @@ public BibDatabase getDatabase() { return bibDatabaseContext.getDatabase(); } - public void changeTypeOfSelectedEntries(String newType) { - List bes = mainTable.getSelectedEntries(); - changeType(bes, newType); - } - - private void changeType(List entries, String newType) { - if ((entries == null) || (entries.isEmpty())) { - LOGGER.error("At least one entry must be selected to be able to change the type."); - return; - } - - if (entries.size() > 1) { - boolean proceed = dialogService.showConfirmationDialogAndWait(Localization.lang("Change entry type"), Localization.lang("Multiple entries selected. Do you want to change the type of all these to '%0'?")); - if (!proceed) { - return; - } - } - - NamedCompound compound = new NamedCompound(Localization.lang("Change entry type")); - for (BibEntry entry : entries) { - compound.addEdit(new UndoableChangeType(entry, entry.getType(), newType)); - DefaultTaskExecutor.runInJavaFXThread(() -> { - entry.setType(newType); - }); - } - - output(formatOutputMessage(Localization.lang("Changed type to '%0' for", newType), entries.size())); - compound.end(); - getUndoManager().addEdit(compound); - markBaseChanged(); - updateEntryEditorIfShowing(); - } - public boolean showDeleteConfirmationDialog(int numberOfEntries) { if (Globals.prefs.getBoolean(JabRefPreferences.CONFIRM_DELETE)) { String title = Localization.lang("Delete entry"); @@ -1150,15 +1105,6 @@ public String formatOutputMessage(String start, int count) { return String.format("%s %d %s.", start, count, (count > 1 ? Localization.lang("entries") : Localization.lang("entry"))); } - /** - * Set the preview active state for all BasePanel instances. - */ - private void setPreviewActiveBasePanels(boolean enabled) { - for (int i = 0; i < frame.getTabbedPane().getTabs().size(); i++) { - frame.getBasePanelAt(i).setPreviewActive(enabled); - } - } - private void setPreviewActive(boolean enabled) { if (enabled) { showPreview(); @@ -1200,9 +1146,13 @@ public FileAnnotationCache getAnnotationCache() { return annotationCache; } - public void resetChangeMonitor() { + public void resetChangeMonitorAndChangePane() { changeMonitor.ifPresent(DatabaseChangeMonitor::unregister); changeMonitor = Optional.of(new DatabaseChangeMonitor(bibDatabaseContext, Globals.getFileUpdateMonitor(), Globals.TASK_EXECUTOR)); + + changePane = new DatabaseChangePane(splitPane, bibDatabaseContext, changeMonitor.get()); + + this.getChildren().setAll(changePane); } public void updateTimeStamp() { @@ -1341,10 +1291,10 @@ public void action() { try { getUndoManager().undo(); markBaseChanged(); - frame.output(Localization.lang("Undo")); + output(Localization.lang("Undo")); } catch (CannotUndoException ex) { LOGGER.warn("Nothing to undo", ex); - frame.output(Localization.lang("Nothing to undo") + '.'); + output(Localization.lang("Nothing to undo") + '.'); } markChangedOrUnChanged(); @@ -1412,9 +1362,9 @@ public void action() { try { getUndoManager().redo(); markBaseChanged(); - frame.output(Localization.lang("Redo")); + output(Localization.lang("Redo")); } catch (CannotRedoException ex) { - frame.output(Localization.lang("Nothing to redo") + '.'); + output(Localization.lang("Nothing to redo") + '.'); } markChangedOrUnChanged(); diff --git a/src/main/java/org/jabref/gui/DefaultInjector.java b/src/main/java/org/jabref/gui/DefaultInjector.java index ee7c8c05839..ed9963b55c5 100644 --- a/src/main/java/org/jabref/gui/DefaultInjector.java +++ b/src/main/java/org/jabref/gui/DefaultInjector.java @@ -5,6 +5,7 @@ import javax.swing.undo.UndoManager; import org.jabref.Globals; +import org.jabref.JabRefGUI; import org.jabref.gui.keyboard.KeyBindingRepository; import org.jabref.gui.util.TaskExecutor; import org.jabref.logic.journals.JournalAbbreviationLoader; @@ -28,7 +29,7 @@ public class DefaultInjector implements PresenterFactory { */ private static Object createDependency(Class clazz) { if (clazz == DialogService.class) { - return new FXDialogService(); + return JabRefGUI.getMainFrame().getDialogService(); } else if (clazz == TaskExecutor.class) { return Globals.TASK_EXECUTOR; } else if (clazz == PreferencesService.class) { diff --git a/src/main/java/org/jabref/gui/DuplicateResolverDialog.java b/src/main/java/org/jabref/gui/DuplicateResolverDialog.java index 858a7c7e7f2..a56da356f6f 100644 --- a/src/main/java/org/jabref/gui/DuplicateResolverDialog.java +++ b/src/main/java/org/jabref/gui/DuplicateResolverDialog.java @@ -39,8 +39,8 @@ public enum DuplicateResolverResult { public DuplicateResolverDialog(BibEntry one, BibEntry two, DuplicateResolverType type, BibDatabaseContext database) { this.setTitle(Localization.lang("Possible duplicate entries")); - init(one, two, type); this.database = database; + init(one, two, type); } private void init(BibEntry one, BibEntry two, DuplicateResolverType type) { @@ -116,7 +116,7 @@ private void init(BibEntry one, BibEntry two, DuplicateResolverType type) { getDialogPane().setContent(borderPane); Button helpButton = (Button) this.getDialogPane().lookupButton(help); - helpButton.setOnAction(evt -> helpCommand.getCommand().execute()); + helpButton.setOnAction(evt -> helpCommand.execute()); } public BibEntry getMergedEntry() { diff --git a/src/main/java/org/jabref/gui/FXDialogService.java b/src/main/java/org/jabref/gui/FXDialogService.java index f3edaba275a..579b4de8bdd 100644 --- a/src/main/java/org/jabref/gui/FXDialogService.java +++ b/src/main/java/org/jabref/gui/FXDialogService.java @@ -24,21 +24,27 @@ import javafx.scene.control.ChoiceDialog; import javafx.scene.control.DialogPane; import javafx.scene.control.TextInputDialog; +import javafx.scene.layout.Pane; import javafx.scene.layout.Region; import javafx.stage.DirectoryChooser; import javafx.stage.FileChooser; import javafx.stage.Stage; import javafx.stage.Window; +import javafx.util.Duration; -import org.jabref.JabRefGUI; import org.jabref.gui.icon.IconTheme; import org.jabref.gui.util.DirectoryDialogConfiguration; import org.jabref.gui.util.FileDialogConfiguration; import org.jabref.gui.util.ZipFileChooser; import org.jabref.logic.l10n.Localization; +import com.jfoenix.controls.JFXSnackbar; +import com.jfoenix.controls.JFXSnackbar.SnackbarEvent; +import com.jfoenix.controls.JFXSnackbarLayout; import org.controlsfx.dialog.ExceptionDialog; import org.controlsfx.dialog.ProgressDialog; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * This class provides methods to create default @@ -51,18 +57,15 @@ */ public class FXDialogService implements DialogService { - private final Window mainWindow; + private static final Duration TOAST_MESSAGE_DISPLAY_TIME = Duration.millis(3000); + private static final Logger LOGGER = LoggerFactory.getLogger(FXDialogService.class); - /** - * @deprecated try not to initialize a new dialog service but reuse the one constructed in {@link org.jabref.gui.JabRefFrame}. - */ - @Deprecated - public FXDialogService() { - this(null); - } + private final Window mainWindow; + private final JFXSnackbar statusLine; - public FXDialogService(Window mainWindow) { + public FXDialogService(Window mainWindow, Pane mainPane) { this.mainWindow = mainWindow; + this.statusLine = new JFXSnackbar(mainPane); } private static FXDialog createDialog(AlertType type, String title, String content) { @@ -253,7 +256,8 @@ public void showProgressDialogAndWait(String title, String content, Task @Override public void notify(String message) { - JabRefGUI.getMainFrame().output(message); + LOGGER.info(message); + statusLine.fireEvent(new SnackbarEvent(new JFXSnackbarLayout(message), TOAST_MESSAGE_DISPLAY_TIME, null)); } @Override diff --git a/src/main/java/org/jabref/gui/GUIGlobals.java b/src/main/java/org/jabref/gui/GUIGlobals.java index e1f145054ff..12d55f69bbe 100644 --- a/src/main/java/org/jabref/gui/GUIGlobals.java +++ b/src/main/java/org/jabref/gui/GUIGlobals.java @@ -10,9 +10,6 @@ import org.jabref.logic.l10n.Localization; import org.jabref.preferences.JabRefPreferences; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - /** * Static variables for graphics files and keyboard shortcuts. */ @@ -32,11 +29,6 @@ public class GUIGlobals { public static final String UNTITLED_TITLE = Localization.lang("untitled"); - // Colors. - public static final Color ENTRY_EDITOR_LABEL_COLOR = new Color(100, 100, 150); // Empty field, blue. - - private static final Logger LOGGER = LoggerFactory.getLogger(GUIGlobals.class); - private GUIGlobals() { } diff --git a/src/main/java/org/jabref/gui/JEditorPaneWithHighlighting.java b/src/main/java/org/jabref/gui/JEditorPaneWithHighlighting.java deleted file mode 100644 index 71235c5d08e..00000000000 --- a/src/main/java/org/jabref/gui/JEditorPaneWithHighlighting.java +++ /dev/null @@ -1,57 +0,0 @@ -package org.jabref.gui; - -import java.util.Optional; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import javax.swing.JEditorPane; -import javax.swing.text.BadLocationException; -import javax.swing.text.DefaultHighlighter; -import javax.swing.text.Document; -import javax.swing.text.Highlighter; -import javax.swing.text.LayeredHighlighter.LayerPainter; - -import org.jabref.gui.fieldeditors.JTextAreaWithHighlighting; -import org.jabref.logic.search.SearchQueryHighlightListener; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class JEditorPaneWithHighlighting extends JEditorPane implements SearchQueryHighlightListener { - - private static final Logger LOGGER = LoggerFactory.getLogger(JTextAreaWithHighlighting.class); - - public void highlightPattern(Optional highlightPattern) { - Highlighter highlighter = getHighlighter(); - highlighter.removeAllHighlights(); - - if ((highlightPattern == null) || !highlightPattern.isPresent()) { - return; - } - - String text = getDocumentText(); - - Matcher matcher = highlightPattern.get().matcher(text); - LayerPainter painter = DefaultHighlighter.DefaultPainter; - while (matcher.find()) { - try { - highlighter.addHighlight(matcher.start(), matcher.end(), painter); - } catch (BadLocationException ble) { - // should not occur if matcher works right - LOGGER.warn("Highlighting not possible, bad location", ble); - } - } - } - - private String getDocumentText() { - Document doc = getDocument(); - String text; - try { - text = doc.getText(0, doc.getLength()); - } catch (Exception e) { - LOGGER.error("Error while getting document text"); - text = ""; - } - return text; - } -} diff --git a/src/main/java/org/jabref/gui/JabRefFrame.java b/src/main/java/org/jabref/gui/JabRefFrame.java index ff496a25e09..abc4abf83a2 100644 --- a/src/main/java/org/jabref/gui/JabRefFrame.java +++ b/src/main/java/org/jabref/gui/JabRefFrame.java @@ -50,13 +50,11 @@ import javafx.scene.layout.Pane; import javafx.scene.layout.Priority; import javafx.stage.Stage; -import javafx.util.Duration; import org.jabref.Globals; import org.jabref.JabRefExecutorService; import org.jabref.gui.actions.ActionFactory; import org.jabref.gui.actions.Actions; -import org.jabref.gui.actions.AutoLinkFilesAction; import org.jabref.gui.actions.BibtexKeyPatternAction; import org.jabref.gui.actions.ConnectToSharedDatabaseCommand; import org.jabref.gui.actions.CopyFilesAction; @@ -71,7 +69,6 @@ import org.jabref.gui.actions.ManageJournalsAction; import org.jabref.gui.actions.NewDatabaseAction; import org.jabref.gui.actions.NewEntryAction; -import org.jabref.gui.actions.NewEntryFromPlainTextAction; import org.jabref.gui.actions.NewSubLibraryAction; import org.jabref.gui.actions.OldDatabaseCommandWrapper; import org.jabref.gui.actions.OpenBrowserAction; @@ -88,6 +85,7 @@ import org.jabref.gui.exporter.ExportToClipboardAction; import org.jabref.gui.exporter.SaveAllAction; import org.jabref.gui.exporter.SaveDatabaseAction; +import org.jabref.gui.externalfiles.AutoLinkFilesAction; import org.jabref.gui.externalfiles.FindUnlinkedFilesAction; import org.jabref.gui.externalfiletype.ExternalFileTypes; import org.jabref.gui.help.AboutAction; @@ -102,7 +100,7 @@ import org.jabref.gui.metadata.BibtexStringEditorAction; import org.jabref.gui.metadata.PreambleEditor; import org.jabref.gui.protectedterms.ManageProtectedTermsAction; -import org.jabref.gui.push.PushToApplicationButton; +import org.jabref.gui.push.PushToApplicationAction; import org.jabref.gui.push.PushToApplications; import org.jabref.gui.search.GlobalSearchBar; import org.jabref.gui.specialfields.SpecialFieldMenuItemFactory; @@ -113,7 +111,6 @@ import org.jabref.logic.autosaveandbackup.BackupManager; import org.jabref.logic.importer.IdFetcher; import org.jabref.logic.importer.OpenDatabase; -import org.jabref.logic.importer.OutputPrinter; import org.jabref.logic.importer.ParserResult; import org.jabref.logic.importer.WebFetchers; import org.jabref.logic.l10n.Localization; @@ -121,7 +118,6 @@ import org.jabref.logic.undo.AddUndoableActionEvent; import org.jabref.logic.undo.UndoChangeEvent; import org.jabref.logic.undo.UndoRedoEvent; -import org.jabref.logic.util.OS; import org.jabref.logic.util.io.FileUtil; import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.database.BibDatabaseMode; @@ -132,34 +128,28 @@ import org.jabref.model.entry.specialfields.SpecialField; import org.jabref.preferences.JabRefPreferences; import org.jabref.preferences.LastFocusedTabPreferences; -import org.jabref.preferences.SearchPreferences; import com.google.common.eventbus.Subscribe; -import com.jfoenix.controls.JFXSnackbar; -import com.jfoenix.controls.JFXSnackbar.SnackbarEvent; -import com.jfoenix.controls.JFXSnackbarLayout; import org.eclipse.fx.ui.controls.tabpane.DndTabPane; import org.eclipse.fx.ui.controls.tabpane.DndTabPaneFactory; import org.fxmisc.easybind.EasyBind; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import osx.macadapter.MacAdapter; /** * The main window of the application. */ -public class JabRefFrame extends BorderPane implements OutputPrinter { +public class JabRefFrame extends BorderPane { // Frame titles. public static final String FRAME_TITLE = "JabRef"; private static final Logger LOGGER = LoggerFactory.getLogger(JabRefFrame.class); - private static final Duration TOAST_MESSAGE_DISPLAY_TIME = Duration.millis(3000); private final SplitPane splitPane = new SplitPane(); private final JabRefPreferences prefs = Globals.prefs; private final GlobalSearchBar globalSearchBar = new GlobalSearchBar(this); - private final JFXSnackbar statusLine = new JFXSnackbar(this); + private final ProgressBar progressBar = new ProgressBar(); private final FileHistoryMenu fileHistory = new FileHistoryMenu(prefs, this); @@ -185,8 +175,7 @@ public class JabRefFrame extends BorderPane implements OutputPrinter { public JabRefFrame(Stage mainStage) { this.mainStage = mainStage; - this.dialogService = new FXDialogService(mainStage); - init(); + this.dialogService = new FXDialogService(mainStage, this); } /** @@ -212,7 +201,7 @@ private static void setEnabled(List list, boolean enabled) { } } - private void init() { + public void init() { sidePaneManager = new SidePaneManager(Globals.prefs, this); sidePane = sidePaneManager.getPane(); @@ -272,16 +261,14 @@ private void init() { // Poor-mans binding to global state // We need to invoke this in the JavaFX thread as all the listeners sit there Platform.runLater(() -> Globals.stateManager.activeDatabaseProperty().setValue(Optional.of(currentBasePanel.getBibDatabaseContext()))); - if (new SearchPreferences(Globals.prefs).isGlobalSearch()) { - globalSearchBar.performSearch(); - } else { - String content = ""; - Optional currentSearchQuery = currentBasePanel.getCurrentSearchQuery(); - if (currentSearchQuery.isPresent()) { - content = currentSearchQuery.get().getQuery(); - } - globalSearchBar.setSearchTerm(content); + + // Update search query + String content = ""; + Optional currentSearchQuery = currentBasePanel.getCurrentSearchQuery(); + if (currentSearchQuery.isPresent()) { + content = currentSearchQuery.get().getQuery(); } + globalSearchBar.setSearchTerm(content); currentBasePanel.getPreviewPanel().updateLayout(Globals.prefs.getPreviewPreferences()); @@ -297,18 +284,6 @@ private void init() { currentBasePanel.getUndoManager().postUndoRedoEvent(); currentBasePanel.getMainTable().requestFocus(); }); - - //Note: The registration of Apple event is at the end of initialization, because - //if the events happen too early (ie when the window is not initialized yet), the - //opened (double-clicked) documents are not displayed. - if (OS.OS_X) { - try { - new MacAdapter().registerMacEvents(this); - } catch (Exception e) { - LOGGER.error("Could not interface with Mac OS X methods.", e); - } - } - initShowTrackingNotification(); } @@ -550,32 +525,6 @@ public void changed(ObservableValue observable, Boolean oldVa }); setCenter(splitPane); - - /* - GridBagLayout gbl = new GridBagLayout(); - GridBagConstraints con = new GridBagConstraints(); - con.fill = GridBagConstraints.BOTH; - con.anchor = GridBagConstraints.WEST; - JPanel status = new JPanel(); - status.setLayout(gbl); - con.weighty = 0; - con.weightx = 0; - con.gridwidth = 1; - con.insets = new Insets(0, 2, 0, 0); - gbl.setConstraints(statusLabel, con); - status.add(statusLabel); - con.weightx = 1; - con.insets = new Insets(0, 4, 0, 0); - con.gridwidth = 1; - gbl.setConstraints(statusLine, con); - status.add(statusLine); - con.weightx = 0; - con.gridwidth = GridBagConstraints.REMAINDER; - con.insets = new Insets(2, 4, 2, 2); - gbl.setConstraints(progressBar, con); - status.add(progressBar); - statusLabel.setForeground(GUIGlobals.ENTRY_EDITOR_LABEL_COLOR.darker()); - */ } private void setDividerPosition() { @@ -610,7 +559,8 @@ private Node createToolbar() { leftSide.setMinWidth(100); leftSide.prefWidthProperty().bind(sidePane.widthProperty()); leftSide.maxWidthProperty().bind(sidePane.widthProperty()); - PushToApplicationButton pushToExternal = new PushToApplicationButton(this, pushApplications.getApplications()); + + PushToApplicationAction pushToApplicationAction = new PushToApplicationAction(this, Globals.stateManager); HBox rightSide = new HBox( factory.createIconButton(StandardActions.NEW_ARTICLE, new NewEntryAction(this, BiblatexEntryTypes.ARTICLE, dialogService, Globals.prefs)), factory.createIconButton(StandardActions.DELETE_ENTRY, new OldDatabaseCommandWrapper(Actions.DELETE, this, Globals.stateManager)), @@ -621,7 +571,7 @@ private Node createToolbar() { factory.createIconButton(StandardActions.COPY, new OldDatabaseCommandWrapper(Actions.COPY, this, Globals.stateManager)), factory.createIconButton(StandardActions.PASTE, new OldDatabaseCommandWrapper(Actions.PASTE, this, Globals.stateManager)), new Separator(Orientation.VERTICAL), - factory.createIconButton(pushToExternal.getMenuAction(), pushToExternal), + factory.createIconButton(pushToApplicationAction.getActionInformation(), pushToApplicationAction), factory.createIconButton(StandardActions.GENERATE_CITE_KEYS, new OldDatabaseCommandWrapper(Actions.MAKE_KEY, this, Globals.stateManager)), factory.createIconButton(StandardActions.CLEANUP_ENTRIES, new OldDatabaseCommandWrapper(Actions.CLEANUP, this, Globals.stateManager)), new Separator(Orientation.VERTICAL), @@ -821,7 +771,6 @@ private MenuBar createMenu() { //@formatter:off library.getItems().addAll( factory.createMenuItem(StandardActions.NEW_ENTRY, new NewEntryAction(this, dialogService, Globals.prefs)), - factory.createMenuItem(StandardActions.NEW_ENTRY_FROM_PLAINTEX, new NewEntryFromPlainTextAction(this, Globals.prefs.getUpdateFieldPreferences(), dialogService, Globals.prefs)), factory.createMenuItem(StandardActions.DELETE_ENTRY, new OldDatabaseCommandWrapper(Actions.DELETE, this, Globals.stateManager)), new SeparatorMenuItem(), @@ -847,10 +796,10 @@ private MenuBar createMenu() { new SeparatorMenuItem(), - factory.createMenuItem(StandardActions.SET_FILE_LINKS, new AutoLinkFilesAction()) + factory.createMenuItem(StandardActions.SET_FILE_LINKS, new AutoLinkFilesAction(this, prefs)) ); - PushToApplicationButton pushToExternal = new PushToApplicationButton(this, pushApplications.getApplications()); + final PushToApplicationAction pushToApplicationAction = new PushToApplicationAction(this, Globals.stateManager); tools.getItems().addAll( factory.createMenuItem(StandardActions.NEW_SUB_LIBRARY_FROM_AUX, new NewSubLibraryAction(this)), factory.createMenuItem(StandardActions.FIND_UNLINKED_FILES, new FindUnlinkedFilesAction(this)), @@ -867,7 +816,7 @@ private MenuBar createMenu() { factory.createMenuItem(StandardActions.GENERATE_CITE_KEYS, new OldDatabaseCommandWrapper(Actions.MAKE_KEY, this, Globals.stateManager)), factory.createMenuItem(StandardActions.REPLACE_ALL, new OldDatabaseCommandWrapper(Actions.REPLACE_ALL, this, Globals.stateManager)), factory.createMenuItem(StandardActions.SEND_AS_EMAIL, new OldDatabaseCommandWrapper(Actions.SEND_AS_EMAIL, this, Globals.stateManager)), - factory.createMenuItem(pushToExternal.getMenuAction(), pushToExternal), + factory.createMenuItem(pushToApplicationAction.getActionInformation(), pushToApplicationAction), factory.createSubMenu(StandardActions.ABBREVIATE, factory.createMenuItem(StandardActions.ABBREVIATE_ISO, new OldDatabaseCommandWrapper(Actions.ABBREVIATE_ISO, this, Globals.stateManager)), @@ -994,16 +943,6 @@ public void addParserResult(ParserResult pr, boolean focusPanel) { } } - /** - * Displays the given message at the bottom of the main frame - * - * @deprecated use {@link DialogService#notify(String)} instead. However, do not remove this method, it's called from the dialogService - */ - @Deprecated - public void output(final String message) { - DefaultTaskExecutor.runInJavaFXThread(() -> statusLine.fireEvent(new SnackbarEvent(new JFXSnackbarLayout(message), TOAST_MESSAGE_DISPLAY_TIME, null))); - } - private void initActions() { /* openDatabaseOnlyActions.clear(); @@ -1154,6 +1093,10 @@ public void addTab(BasePanel basePanel, boolean raisePanel) { // add tab Tab newTab = new Tab(basePanel.getTabTitle(), basePanel); tabbedPane.getTabs().add(newTab); + newTab.setOnCloseRequest(event -> { + closeTab((BasePanel) newTab.getContent()); + event.consume(); + }); // update all tab titles updateAllTabTitles(); @@ -1273,17 +1216,6 @@ private boolean isExistURLorDOI(List selectEntryList) { return false; } - @Override - public void showMessage(String message, String title, int msgType) { - JOptionPane.showMessageDialog(null, message, title, msgType); - } - - @Override - public void setStatus(String s) { - output(s); - } - - @Override public void showMessage(String message) { JOptionPane.showMessageDialog(null, message); } @@ -1318,7 +1250,7 @@ private boolean confirmClose(BasePanel panel) { return true; } // The action was either canceled or unsuccessful. - output(Localization.lang("Unable to save library")); + dialogService.notify(Localization.lang("Unable to save library")); } catch (Throwable ex) { LOGGER.error("A problem occurred when trying to save the file", ex); dialogService.showErrorDialogAndWait(Localization.lang("Save library"), Localization.lang("Could not save file."), ex); @@ -1340,6 +1272,8 @@ private void closeTab(BasePanel panel) { if (panel.isModified() && (context.getLocation() == DatabaseLocation.LOCAL)) { if (confirmClose(panel)) { removeTab(panel); + } else { + return; } } else if (context.getLocation() == DatabaseLocation.SHARED) { context.convertToLocalDatabase(); @@ -1358,7 +1292,7 @@ private void removeTab(BasePanel panel) { panel.cleanUp(); tabbedPane.getTabs().remove(getTab(panel)); setWindowTitle(); - output(Localization.lang("Closed library") + '.'); + dialogService.notify(Localization.lang("Closed library") + '.'); // update tab titles updateAllTabTitles(); }); @@ -1460,7 +1394,7 @@ private void setDefaultTableFontSize() { for (BasePanel basePanel : getBasePanelList()) { basePanel.updateTableFont(); } - setStatus(Localization.lang("Table font size is %0", String.valueOf(GUIGlobals.currentFont.getSize()))); + dialogService.notify(Localization.lang("Table font size is %0", String.valueOf(GUIGlobals.currentFont.getSize()))); } private void increaseTableFontSize() { @@ -1468,7 +1402,7 @@ private void increaseTableFontSize() { for (BasePanel basePanel : getBasePanelList()) { basePanel.updateTableFont(); } - setStatus(Localization.lang("Table font size is %0", String.valueOf(GUIGlobals.currentFont.getSize()))); + dialogService.notify(Localization.lang("Table font size is %0", String.valueOf(GUIGlobals.currentFont.getSize()))); } private void decreaseTableFontSize() { @@ -1480,7 +1414,7 @@ private void decreaseTableFontSize() { for (BasePanel basePanel : getBasePanelList()) { basePanel.updateTableFont(); } - setStatus(Localization.lang("Table font size is %0", String.valueOf(GUIGlobals.currentFont.getSize()))); + dialogService.notify(Localization.lang("Table font size is %0", String.valueOf(GUIGlobals.currentFont.getSize()))); } private class CloseDatabaseAction extends SimpleCommand { diff --git a/src/main/java/org/jabref/gui/OSXCompatibleToolbar.java b/src/main/java/org/jabref/gui/OSXCompatibleToolbar.java deleted file mode 100644 index f71c9e72af5..00000000000 --- a/src/main/java/org/jabref/gui/OSXCompatibleToolbar.java +++ /dev/null @@ -1,39 +0,0 @@ -package org.jabref.gui; - -import java.awt.Component; - -import javax.swing.JButton; -import javax.swing.JToolBar; - -import org.jabref.logic.util.OS; - -public class OSXCompatibleToolbar extends JToolBar { - - public OSXCompatibleToolbar() { - } - - public OSXCompatibleToolbar(int orientation) { - super(orientation); - } - - public OSXCompatibleToolbar(String name) { - super(name); - } - - public OSXCompatibleToolbar(String name, int orientation) { - super(name, orientation); - } - - @Override - public Component add(Component a) { - if (a instanceof JButton) { - JButton button = (JButton) a; - if (OS.OS_X) { - button.putClientProperty("JButton.buttonType", "toolbar"); - } - } - - return super.add(a); - } - -} diff --git a/src/main/java/org/jabref/gui/PreviewPanel.java b/src/main/java/org/jabref/gui/PreviewPanel.java index 796fafdd016..ff26ee6765b 100644 --- a/src/main/java/org/jabref/gui/PreviewPanel.java +++ b/src/main/java/org/jabref/gui/PreviewPanel.java @@ -84,7 +84,7 @@ public class PreviewPanel extends ScrollPane implements SearchQueryHighlightList /** * @param panel (may be null) Only set this if the preview is associated to the main window. - * @param databaseContext Used for resolving pdf directories for links. Must not be null. + * @param databaseContext Used for resolving pdf directories for links. Must not be null, just pass a new empty BibDatabaseContext() */ public PreviewPanel(BasePanel panel, BibDatabaseContext databaseContext, KeyBindingRepository keyBindingRepository, PreviewPreferences preferences, DialogService dialogService, ExternalFileTypes externalFileTypes) { this.databaseContext = Objects.requireNonNull(databaseContext); diff --git a/src/main/java/org/jabref/gui/actions/AutoLinkFilesAction.java b/src/main/java/org/jabref/gui/actions/AutoLinkFilesAction.java deleted file mode 100644 index 7b77c9f59b4..00000000000 --- a/src/main/java/org/jabref/gui/actions/AutoLinkFilesAction.java +++ /dev/null @@ -1,52 +0,0 @@ -package org.jabref.gui.actions; - -import java.util.List; - -import javax.swing.JDialog; -import javax.swing.JFrame; - -import org.jabref.JabRefExecutorService; -import org.jabref.JabRefGUI; -import org.jabref.gui.externalfiles.AutoSetLinks; -import org.jabref.gui.undo.NamedCompound; -import org.jabref.logic.l10n.Localization; -import org.jabref.model.entry.BibEntry; - -/** - * This Action may only be used in a menu or button. - * Never in the entry editor. FileListEditor and EntryEditor have other ways to update the file links - */ -public class AutoLinkFilesAction extends SimpleCommand { - - public AutoLinkFilesAction() { - - } - - @Override - public void execute() { - List entries = JabRefGUI.getMainFrame().getCurrentBasePanel().getSelectedEntries(); - if (entries.isEmpty()) { - JabRefGUI.getMainFrame().getCurrentBasePanel() - .output(Localization.lang("This operation requires one or more entries to be selected.")); - return; - } - JDialog diag = new JDialog((JFrame) null, true); - final NamedCompound nc = new NamedCompound(Localization.lang("Automatically set file links")); - Runnable runnable = AutoSetLinks.autoSetLinks(entries, nc, null, - JabRefGUI.getMainFrame().getCurrentBasePanel().getBibDatabaseContext(), e -> { - if (e.getID() > 0) { - // entry has been updated in Util.autoSetLinks, only treat nc and status message - if (nc.hasEdits()) { - nc.end(); - JabRefGUI.getMainFrame().getCurrentBasePanel().getUndoManager().addEdit(nc); - JabRefGUI.getMainFrame().getCurrentBasePanel().markBaseChanged(); - } - JabRefGUI.getMainFrame().output(Localization.lang("Finished automatically setting external links.")); - } else { - JabRefGUI.getMainFrame().output(Localization.lang("Finished automatically setting external links.") + " " - + Localization.lang("No files found.")); - } - } , diag); - JabRefExecutorService.INSTANCE.execute(runnable); - } -} diff --git a/src/main/java/org/jabref/gui/actions/ChangeTypeAction.java b/src/main/java/org/jabref/gui/actions/ChangeTypeAction.java deleted file mode 100644 index b7b907a662c..00000000000 --- a/src/main/java/org/jabref/gui/actions/ChangeTypeAction.java +++ /dev/null @@ -1,52 +0,0 @@ -package org.jabref.gui.actions; - -import java.awt.event.ActionEvent; -import java.util.Collections; -import java.util.List; - -import javax.swing.AbstractAction; -import javax.swing.undo.UndoManager; - -import javafx.scene.control.MenuItem; - -import org.jabref.gui.BasePanel; -import org.jabref.gui.undo.NamedCompound; -import org.jabref.gui.undo.UndoableChangeType; -import org.jabref.logic.l10n.Localization; -import org.jabref.model.entry.BibEntry; -import org.jabref.model.entry.EntryType; - -public class ChangeTypeAction extends AbstractAction { - - private final String type; - private final BasePanel panel; - - public ChangeTypeAction(EntryType type, BasePanel bp) { - super(type.getName()); - this.type = type.getName(); - panel = bp; - } - - @Override - public void actionPerformed(ActionEvent evt) { - panel.changeTypeOfSelectedEntries(type); - } - - public static MenuItem as(EntryType type, BibEntry entry, UndoManager undoManager) { - return as(type, Collections.singletonList(entry), undoManager); - } - - public static MenuItem as(EntryType type, List entries, UndoManager undoManager) { - MenuItem menuItem = new MenuItem(type.getName()); - menuItem.setOnAction(event -> { - NamedCompound compound = new NamedCompound(Localization.lang("Change entry type")); - for (BibEntry entry : entries) { - entry.setType(type) - .ifPresent(change -> compound.addEdit(new UndoableChangeType(change))); - } - - undoManager.addEdit(compound); - }); - return menuItem; - } -} diff --git a/src/main/java/org/jabref/gui/actions/CleanupAction.java b/src/main/java/org/jabref/gui/actions/CleanupAction.java index 648af8313b7..d79a8a62c4f 100644 --- a/src/main/java/org/jabref/gui/actions/CleanupAction.java +++ b/src/main/java/org/jabref/gui/actions/CleanupAction.java @@ -9,7 +9,6 @@ import org.jabref.gui.cleanup.CleanupDialog; import org.jabref.gui.undo.NamedCompound; import org.jabref.gui.undo.UndoableFieldChange; -import org.jabref.gui.util.BackgroundTask; import org.jabref.logic.cleanup.CleanupPreset; import org.jabref.logic.cleanup.CleanupWorker; import org.jabref.logic.l10n.Localization; @@ -59,9 +58,8 @@ public void action() { preferences.setCleanupPreset(chosenPreset.get()); - BackgroundTask.wrap(() -> cleanup(chosenPreset.get())) - .onSuccess(result -> showResults()) - .executeWith(Globals.TASK_EXECUTOR); + this.cleanup(chosenPreset.get()); + this.showResults(); } } @@ -73,7 +71,7 @@ public void init() { isCanceled = true; return; } - panel.output(Localization.lang("Doing a cleanup for %0 entries...", + dialogService.notify(Localization.lang("Doing a cleanup for %0 entries...", Integer.toString(panel.getSelectedEntries().size()))); } @@ -117,7 +115,7 @@ private void showResults() { message = Localization.lang("%0 entries needed a clean up", Integer.toString(modifiedEntriesCount)); break; } - panel.output(message); + dialogService.notify(message); } private void cleanup(CleanupPreset cleanupPreset) { diff --git a/src/main/java/org/jabref/gui/actions/CopyBibTeXKeyAndLinkAction.java b/src/main/java/org/jabref/gui/actions/CopyBibTeXKeyAndLinkAction.java index 6f55af1e27b..49c8a5f46ec 100644 --- a/src/main/java/org/jabref/gui/actions/CopyBibTeXKeyAndLinkAction.java +++ b/src/main/java/org/jabref/gui/actions/CopyBibTeXKeyAndLinkAction.java @@ -36,7 +36,7 @@ public void action() throws Exception { List entriesWithKey = entries.stream().filter(BibEntry::hasCiteKey).collect(Collectors.toList()); if (entriesWithKey.isEmpty()) { - JabRefGUI.getMainFrame().output(Localization.lang("None of the selected entries have BibTeX keys.")); + JabRefGUI.getMainFrame().getDialogService().notify(Localization.lang("None of the selected entries have BibTeX keys.")); return; } @@ -53,9 +53,9 @@ public void action() throws Exception { int toCopy = entries.size(); if (copied == toCopy) { // All entries had keys. - JabRefGUI.getMainFrame().output((entries.size() > 1 ? Localization.lang("Copied keys") : Localization.lang("Copied key")) + '.'); + JabRefGUI.getMainFrame().getDialogService().notify((entries.size() > 1 ? Localization.lang("Copied keys") : Localization.lang("Copied key")) + '.'); } else { - JabRefGUI.getMainFrame().output(Localization.lang("Warning: %0 out of %1 entries have undefined BibTeX key.", + JabRefGUI.getMainFrame().getDialogService().notify(Localization.lang("Warning: %0 out of %1 entries have undefined BibTeX key.", Long.toString(toCopy - copied), Integer.toString(toCopy))); } } diff --git a/src/main/java/org/jabref/gui/actions/CopyDoiUrlAction.java b/src/main/java/org/jabref/gui/actions/CopyDoiUrlAction.java index aa8ee344abe..e541cd58d5a 100644 --- a/src/main/java/org/jabref/gui/actions/CopyDoiUrlAction.java +++ b/src/main/java/org/jabref/gui/actions/CopyDoiUrlAction.java @@ -1,10 +1,7 @@ package org.jabref.gui.actions; -import java.awt.event.ActionEvent; import java.util.Optional; -import javax.swing.AbstractAction; - import javafx.scene.control.TextArea; import org.jabref.Globals; @@ -15,31 +12,24 @@ /** * Copies the doi url to the clipboard */ -public class CopyDoiUrlAction extends AbstractAction { - - private TextArea component = null; - private String identifier; +public class CopyDoiUrlAction extends SimpleCommand { - public CopyDoiUrlAction(String identifier) { - super(Localization.lang("Copy DOI url")); - this.identifier = identifier; - } + private TextArea component; public CopyDoiUrlAction(TextArea component) { - this(component.getText()); this.component = component; } @Override - public void actionPerformed(ActionEvent e) { - identifier = component == null ? identifier : component.getText(); + public void execute() { + String identifier = component.getText(); Optional urlOptional = DOI.parse(identifier).map(DOI::getURIAsASCIIString); if (urlOptional.isPresent()) { Globals.clipboardManager.setContent(urlOptional.get()); - JabRefGUI.getMainFrame().output(Localization.lang("The link has been copied to the clipboard.")); + JabRefGUI.getMainFrame().getDialogService().notify(Localization.lang("The link has been copied to the clipboard.")); } else { - JabRefGUI.getMainFrame().output(Localization.lang("Invalid DOI: '%0'.", identifier)); + JabRefGUI.getMainFrame().getDialogService().notify(Localization.lang("Invalid DOI: '%0'.", identifier)); } } } diff --git a/src/main/java/org/jabref/gui/actions/GenerateBibtexKeyAction.java b/src/main/java/org/jabref/gui/actions/GenerateBibtexKeyAction.java index 90b61800215..744db49711c 100644 --- a/src/main/java/org/jabref/gui/actions/GenerateBibtexKeyAction.java +++ b/src/main/java/org/jabref/gui/actions/GenerateBibtexKeyAction.java @@ -33,7 +33,7 @@ public void init() { Localization.lang("First select the entries you want keys to be generated for.")); return; } - basePanel.output(formatOutputMessage(Localization.lang("Generating BibTeX key for"), entries.size())); + dialogService.notify(formatOutputMessage(Localization.lang("Generating BibTeX key for"), entries.size())); } public static boolean confirmOverwriteKeys(DialogService dialogService) { @@ -87,7 +87,7 @@ private void generateKeys() { } basePanel.markBaseChanged(); - basePanel.output(formatOutputMessage(Localization.lang("Generated BibTeX key for"), entries.size())); + dialogService.notify(formatOutputMessage(Localization.lang("Generated BibTeX key for"), entries.size())); } private String formatOutputMessage(String start, int count) { diff --git a/src/main/java/org/jabref/gui/actions/LookupIdentifierAction.java b/src/main/java/org/jabref/gui/actions/LookupIdentifierAction.java index f1f6e6f0846..a2d6c7372d9 100644 --- a/src/main/java/org/jabref/gui/actions/LookupIdentifierAction.java +++ b/src/main/java/org/jabref/gui/actions/LookupIdentifierAction.java @@ -12,6 +12,7 @@ import org.jabref.gui.undo.NamedCompound; import org.jabref.gui.undo.UndoableFieldChange; import org.jabref.gui.util.BackgroundTask; +import org.jabref.gui.util.DefaultTaskExecutor; import org.jabref.logic.importer.FetcherException; import org.jabref.logic.importer.IdFetcher; import org.jabref.logic.l10n.Localization; @@ -39,7 +40,7 @@ public LookupIdentifierAction(JabRefFrame frame, IdFetcher fetcher) { public void execute() { try { BackgroundTask.wrap(this::lookupIdentifiers) - .onSuccess(frame::output) + .onSuccess(frame.getDialogService()::notify) .executeWith(Globals.TASK_EXECUTOR); } catch (Exception e) { LOGGER.error("Problem running ID Worker", e); @@ -84,8 +85,9 @@ private String lookupIdentifiers() { int foundCount = 0; for (BibEntry bibEntry : bibEntries) { count++; - frame.output(Localization.lang("Looking up %0... - entry %1 out of %2 - found %3", - fetcher.getIdentifierName(), Integer.toString(count), totalCount, Integer.toString(foundCount))); + final String statusMessage = Localization.lang("Looking up %0... - entry %1 out of %2 - found %3", + fetcher.getIdentifierName(), Integer.toString(count), totalCount, Integer.toString(foundCount)); + DefaultTaskExecutor.runInJavaFXThread(() -> frame.getDialogService().notify(statusMessage)); Optional identifier = Optional.empty(); try { identifier = fetcher.findIdentifier(bibEntry); @@ -97,8 +99,9 @@ private String lookupIdentifiers() { if (fieldChange.isPresent()) { namedCompound.addEdit(new UndoableFieldChange(fieldChange.get())); foundCount++; - frame.output(Localization.lang("Looking up %0... - entry %1 out of %2 - found %3", - Integer.toString(count), totalCount, Integer.toString(foundCount))); + final String nextStatusMessage = Localization.lang("Looking up %0... - entry %1 out of %2 - found %3", + fetcher.getIdentifierName(), Integer.toString(count), totalCount, Integer.toString(foundCount)); + DefaultTaskExecutor.runInJavaFXThread(() -> frame.getDialogService().notify(nextStatusMessage)); } } } diff --git a/src/main/java/org/jabref/gui/actions/MnemonicAwareAction.java b/src/main/java/org/jabref/gui/actions/MnemonicAwareAction.java deleted file mode 100644 index b1bfa9f87d5..00000000000 --- a/src/main/java/org/jabref/gui/actions/MnemonicAwareAction.java +++ /dev/null @@ -1,44 +0,0 @@ -package org.jabref.gui.actions; - -import javax.swing.AbstractAction; -import javax.swing.Action; -import javax.swing.Icon; - -import org.jabref.gui.icon.IconTheme; - -/** - * This class extends {@link AbstractAction} with the ability to set - * the mnemonic key based on a '&' character inserted in front of - * the desired mnemonic letter. This is done by setting the action's - * name using putValue(NAME, actionname). - * This facilitates localized mnemonics. - */ -public abstract class MnemonicAwareAction extends AbstractAction { - - public MnemonicAwareAction() { } - - public MnemonicAwareAction(Icon icon) { - if (icon instanceof IconTheme.FontBasedIcon) { - putValue(Action.SMALL_ICON, ((IconTheme.FontBasedIcon) icon).createSmallIcon()); - putValue(Action.LARGE_ICON_KEY, icon); - } else { - putValue(Action.SMALL_ICON, icon); - } - } - - @Override - public void putValue(String key, Object value) { - if (key.equals(Action.NAME)) { - String name = value.toString(); - int i = name.indexOf('&'); - if (i >= 0) { - char mnemonic = Character.toUpperCase(name.charAt(i + 1)); - putValue(Action.MNEMONIC_KEY, (int) mnemonic); - value = name.substring(0, i) + name.substring(i + 1); - } else { - value = name; - } - } - super.putValue(key, value); - } -} diff --git a/src/main/java/org/jabref/gui/actions/NewDatabaseAction.java b/src/main/java/org/jabref/gui/actions/NewDatabaseAction.java index 90b2f8ced30..e00604e3a1b 100644 --- a/src/main/java/org/jabref/gui/actions/NewDatabaseAction.java +++ b/src/main/java/org/jabref/gui/actions/NewDatabaseAction.java @@ -24,6 +24,6 @@ public void execute() { BibDatabaseContext bibDatabaseContext = new BibDatabaseContext(new Defaults(BibDatabaseMode.BIBTEX)); bibDatabaseContext.setMode(mode); jabRefFrame.addTab(bibDatabaseContext, true); - jabRefFrame.output(Localization.lang("New %0 library created.", mode.getFormattedName())); + jabRefFrame.getDialogService().notify(Localization.lang("New %0 library created.", mode.getFormattedName())); } } diff --git a/src/main/java/org/jabref/gui/actions/NewEntryFromPlainTextAction.java b/src/main/java/org/jabref/gui/actions/NewEntryFromPlainTextAction.java deleted file mode 100644 index b4c3faa9a2b..00000000000 --- a/src/main/java/org/jabref/gui/actions/NewEntryFromPlainTextAction.java +++ /dev/null @@ -1,54 +0,0 @@ -package org.jabref.gui.actions; - -import org.jabref.gui.DialogService; -import org.jabref.gui.EntryTypeView; -import org.jabref.gui.JabRefFrame; -import org.jabref.gui.plaintextimport.TextInputDialog; -import org.jabref.logic.util.UpdateField; -import org.jabref.logic.util.UpdateFieldPreferences; -import org.jabref.model.entry.BibEntry; -import org.jabref.model.entry.EntryType; -import org.jabref.preferences.JabRefPreferences; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class NewEntryFromPlainTextAction extends SimpleCommand { - - private static final Logger LOGGER = LoggerFactory.getLogger(NewEntryFromPlainTextAction.class); - - private final UpdateFieldPreferences prefs; - private final JabRefFrame jabRefFrame; - private final DialogService dialogService; - private final JabRefPreferences preferences; - - public NewEntryFromPlainTextAction(JabRefFrame jabRefFrame, UpdateFieldPreferences prefs, DialogService dialogService, JabRefPreferences preferences) { - this.jabRefFrame = jabRefFrame; - this.prefs = prefs; - this.dialogService = dialogService; - this.preferences = preferences; - - } - - @Override - public void execute() { - if (jabRefFrame.getBasePanelCount() <= 0) { - LOGGER.error("Action 'New entry' must be disabled when no database is open."); - return; - } - - EntryTypeView typeChoiceDialog = new EntryTypeView(jabRefFrame.getCurrentBasePanel(), dialogService, preferences); - EntryType selectedType = typeChoiceDialog.showAndWait().orElse(null); - if (selectedType == null) { - return; - } - BibEntry bibEntry = new BibEntry(selectedType); - - TextInputDialog tidialog = new TextInputDialog(jabRefFrame, bibEntry); - tidialog.setVisible(true); - if (tidialog.okPressed()) { - UpdateField.setAutomaticFields(bibEntry, false, false, prefs); - jabRefFrame.getCurrentBasePanel().insertEntry(bibEntry); - } - } -} diff --git a/src/main/java/org/jabref/gui/actions/OldDatabaseCommandWrapper.java b/src/main/java/org/jabref/gui/actions/OldDatabaseCommandWrapper.java index a752da3ce9c..58a702e4190 100644 --- a/src/main/java/org/jabref/gui/actions/OldDatabaseCommandWrapper.java +++ b/src/main/java/org/jabref/gui/actions/OldDatabaseCommandWrapper.java @@ -14,7 +14,10 @@ /** * A command that is only executable if a database is open. + * Deprecated use instead + * @see org.jabref.gui.actions.SimpleCommand */ +@Deprecated public class OldDatabaseCommandWrapper extends CommandBase { private static final Logger LOGGER = LoggerFactory.getLogger(OldDatabaseCommandWrapper.class); diff --git a/src/main/java/org/jabref/gui/actions/StandardActions.java b/src/main/java/org/jabref/gui/actions/StandardActions.java index 6e7493f32d7..eb4c6508955 100644 --- a/src/main/java/org/jabref/gui/actions/StandardActions.java +++ b/src/main/java/org/jabref/gui/actions/StandardActions.java @@ -44,11 +44,11 @@ public enum StandardActions implements Action { QUALITY_ASSURED(Localization.lang("Toggle quality assured"), IconTheme.JabRefIcons.QUALITY_ASSURED), RANKING(Localization.lang("Rank"), IconTheme.JabRefIcons.RANKING), CLEAR_RANK(Localization.lang("Clear rank")), - RANK_1("", IconTheme.JabRefIcons.RANK1), - RANK_2("", IconTheme.JabRefIcons.RANK2), - RANK_3("", IconTheme.JabRefIcons.RANK3), - RANK_4("", IconTheme.JabRefIcons.RANK4), - RANK_5("", IconTheme.JabRefIcons.RANK5), + RANK_1(Localization.lang("Set rank to one"), IconTheme.JabRefIcons.RANK1), + RANK_2(Localization.lang("Set rank to two"), IconTheme.JabRefIcons.RANK2), + RANK_3(Localization.lang("Set rank to three"), IconTheme.JabRefIcons.RANK3), + RANK_4(Localization.lang("Set rank to four"), IconTheme.JabRefIcons.RANK4), + RANK_5(Localization.lang("Set rank to five"), IconTheme.JabRefIcons.RANK5), PRINTED(Localization.lang("Printed"), IconTheme.JabRefIcons.PRINTED), TOGGLE_PRINTED(Localization.lang("Toggle print status"), IconTheme.JabRefIcons.PRINTED), READ_STATUS(Localization.lang("Read status"), IconTheme.JabRefIcons.READ_STATUS), @@ -91,6 +91,7 @@ public enum StandardActions implements Action { OPEN_FILE(Localization.lang("Open file"), Localization.lang("Open file"), IconTheme.JabRefIcons.FILE, KeyBinding.OPEN_FILE), OPEN_CONSOLE(Localization.lang("Open terminal here"), Localization.lang("Open terminal here"), IconTheme.JabRefIcons.CONSOLE, KeyBinding.OPEN_CONSOLE), COPY_LINKED_FILES(Localization.lang("Copy linked files to folder...")), + COPY_DOI(Localization.lang("Copy DOI url")), ABBREVIATE(Localization.lang("Abbreviate journal names")), ABBREVIATE_ISO("ISO", Localization.lang("Abbreviate journal names of the selected entries (ISO abbreviation)"), KeyBinding.ABBREVIATE), ABBREVIATE_MEDLINE("MEDLINE", Localization.lang("Abbreviate journal names of the selected entries (MEDLINE abbreviation)")), @@ -138,6 +139,10 @@ public enum StandardActions implements Action { SET_FILE_LINKS(Localization.lang("Automatically set file links"), KeyBinding.AUTOMATICALLY_LINK_FILES), HELP(Localization.lang("Online help"), IconTheme.JabRefIcons.HELP, KeyBinding.HELP), + HELP_KEY_PATTERNS(Localization.lang("Help on key patterns"), IconTheme.JabRefIcons.HELP, KeyBinding.HELP), + HELP_REGEX_SEARCH(Localization.lang("Help on regular expression search"), IconTheme.JabRefIcons.HELP, KeyBinding.HELP), + HELP_NAME_FORMATTER(Localization.lang("Help on Name Formatting"), IconTheme.JabRefIcons.HELP, KeyBinding.HELP), + HELP_SPECIAL_FIELDS(Localization.lang("Help on special fields"), IconTheme.JabRefIcons.HELP, KeyBinding.HELP), WEB_MENU(Localization.lang("JabRef resources")), OPEN_WEBPAGE(Localization.lang("Website"), Localization.lang("Opens JabRef's website")), OPEN_FACEBOOK("Facebook", Localization.lang("Opens JabRef's Facebook page"), IconTheme.JabRefIcons.FACEBOOK), diff --git a/src/main/java/org/jabref/gui/actions/WriteXMPAction.java b/src/main/java/org/jabref/gui/actions/WriteXMPAction.java index de4face8b53..67a1217a113 100644 --- a/src/main/java/org/jabref/gui/actions/WriteXMPAction.java +++ b/src/main/java/org/jabref/gui/actions/WriteXMPAction.java @@ -92,7 +92,7 @@ public void init() { } optionsDialog.open(); - basePanel.output(Localization.lang("Writing XMP-metadata...")); + dialogService.notify(Localization.lang("Writing XMP-metadata...")); } private void writeXMP() { @@ -162,7 +162,7 @@ private void writeXMP() { return; } - basePanel.output(Localization.lang("Finished writing XMP for %0 file (%1 skipped, %2 errors).", + dialogService.notify(Localization.lang("Finished writing XMP for %0 file (%1 skipped, %2 errors).", String.valueOf(entriesChanged), String.valueOf(skipped), String.valueOf(errors))); } diff --git a/src/main/java/org/jabref/gui/bibtexkeypattern/BibtexKeyPatternPanel.java b/src/main/java/org/jabref/gui/bibtexkeypattern/BibtexKeyPatternPanel.java index e3796c623ee..e1e81d2bc03 100644 --- a/src/main/java/org/jabref/gui/bibtexkeypattern/BibtexKeyPatternPanel.java +++ b/src/main/java/org/jabref/gui/bibtexkeypattern/BibtexKeyPatternPanel.java @@ -1,7 +1,5 @@ package org.jabref.gui.bibtexkeypattern; -import java.awt.GridBagConstraints; -import java.awt.GridBagLayout; import java.util.HashMap; import java.util.Locale; import java.util.Map; @@ -30,15 +28,9 @@ public class BibtexKeyPatternPanel extends Pane { - // used by both BibtexKeyPatternPanel and TabLabelPAttern - protected final GridBagLayout gbl = new GridBagLayout(); - protected final GridBagConstraints con = new GridBagConstraints(); - // default pattern protected final TextField defaultPat = new TextField(); - private final int COLUMNS = 2; - // one field for each type private final Map textFields = new HashMap<>(); private final BasePanel panel; @@ -64,7 +56,8 @@ private void buildGUI() { int rowIndex = 1; int columnIndex = 0; // The header - can be removed - for (int i = 0; i < COLUMNS; i++) { + int columnsNumber = 2; + for (int i = 0; i < columnsNumber; i++) { Label label = new Label(Localization.lang("Entry type")); Label keyPattern = new Label(Localization.lang("Key pattern")); gridPane.add(label, ++columnIndex, rowIndex); @@ -93,7 +86,7 @@ private void buildGUI() { textFields.put(type.getName().toLowerCase(Locale.ROOT), textField); - if (columnIndex == (COLUMNS - 1)) { + if (columnIndex == (columnsNumber - 1)) { columnIndex = 0; rowIndex++; } else { @@ -104,7 +97,7 @@ private void buildGUI() { rowIndex++; ActionFactory factory = new ActionFactory(Globals.prefs.getKeyBindingRepository()); - Button help = factory.createIconButton(StandardActions.HELP, new HelpAction(Localization.lang("Help on key patterns"), HelpFile.BIBTEX_KEY_PATTERN).getCommand()); + Button help = factory.createIconButton(StandardActions.HELP_KEY_PATTERNS, new HelpAction(HelpFile.BIBTEX_KEY_PATTERN)); gridPane.add(help, 1, rowIndex); Button btnDefaultAll1 = new Button(Localization.lang("Reset all")); @@ -138,8 +131,7 @@ private void fillPatternUsingPanelData(AbstractBibtexKeyPattern keypatterns) { } protected GlobalBibtexKeyPattern getKeyPatternAsGlobalBibtexKeyPattern() { - GlobalBibtexKeyPattern res = GlobalBibtexKeyPattern.fromPattern( - JabRefPreferences.getInstance().get(JabRefPreferences.DEFAULT_BIBTEX_KEY_PATTERN)); + GlobalBibtexKeyPattern res = GlobalBibtexKeyPattern.fromPattern(Globals.prefs.get(JabRefPreferences.DEFAULT_BIBTEX_KEY_PATTERN)); fillPatternUsingPanelData(res); return res; } diff --git a/src/main/java/org/jabref/gui/cleanup/CleanupActionsListModel.java b/src/main/java/org/jabref/gui/cleanup/CleanupActionsListModel.java deleted file mode 100644 index f7bf8a5e463..00000000000 --- a/src/main/java/org/jabref/gui/cleanup/CleanupActionsListModel.java +++ /dev/null @@ -1,81 +0,0 @@ -package org.jabref.gui.cleanup; - -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; - -import javax.swing.ListModel; -import javax.swing.event.ListDataEvent; -import javax.swing.event.ListDataListener; - -import org.jabref.model.cleanup.FieldFormatterCleanup; -import org.jabref.model.cleanup.FieldFormatterCleanups; - -public class CleanupActionsListModel implements ListModel { - - private List cleanupActions; - private final List listeners = new ArrayList<>(); - - - public CleanupActionsListModel(List cleanupAction) { - Objects.requireNonNull(cleanupAction); - this.cleanupActions = cleanupAction; - - } - - public void addCleanupAction(FieldFormatterCleanup action) { - if (!cleanupActions.contains(action)) { - cleanupActions.add(action); - for (ListDataListener listener : listeners) { - listener.intervalAdded(new ListDataEvent(action, ListDataEvent.INTERVAL_ADDED, cleanupActions.size(), - cleanupActions.size())); - } - } - } - - /** - * Removes the action at the specified index from the list. - * Removal is only done when index {@code >=0} and index {@code<=} list size - * @param index The index to remove - */ - public void removeAtIndex(int index) { - - if ((index >= 0) && (index < cleanupActions.size())) { - FieldFormatterCleanup action = cleanupActions.remove(index); - for (ListDataListener listener : listeners) { - listener.intervalRemoved(new ListDataEvent(action, ListDataEvent.INTERVAL_REMOVED, index, index)); - } - } - } - - public List getAllActions() { - return cleanupActions; - } - - @Override - public int getSize() { - return cleanupActions.size(); - } - - @Override - public FieldFormatterCleanup getElementAt(int index) { - return cleanupActions.get(index); - } - - @Override - public void addListDataListener(ListDataListener l) { - listeners.add(l); - } - - @Override - public void removeListDataListener(ListDataListener l) { - listeners.remove(l); - } - - public void reset(FieldFormatterCleanups defaultFormatters) { - cleanupActions = new ArrayList<>(defaultFormatters.getConfiguredActions()); - for (ListDataListener listener : listeners) { - listener.contentsChanged(new ListDataEvent(this, ListDataEvent.CONTENTS_CHANGED, 0, cleanupActions.size())); - } - } -} diff --git a/src/main/java/org/jabref/gui/collab/EntryAddChangeViewModel.java b/src/main/java/org/jabref/gui/collab/EntryAddChangeViewModel.java index 8284f544cab..a84904534bb 100644 --- a/src/main/java/org/jabref/gui/collab/EntryAddChangeViewModel.java +++ b/src/main/java/org/jabref/gui/collab/EntryAddChangeViewModel.java @@ -3,7 +3,7 @@ import javafx.scene.Node; import org.jabref.Globals; -import org.jabref.gui.FXDialogService; +import org.jabref.JabRefGUI; import org.jabref.gui.PreviewPanel; import org.jabref.gui.externalfiletype.ExternalFileTypes; import org.jabref.gui.undo.NamedCompound; @@ -29,7 +29,7 @@ public void makeChange(BibDatabaseContext database, NamedCompound undoEdit) { @Override public Node description() { - PreviewPanel previewPanel = new PreviewPanel(null, null, Globals.getKeyPrefs(), Globals.prefs.getPreviewPreferences(), new FXDialogService(), ExternalFileTypes.getInstance()); + PreviewPanel previewPanel = new PreviewPanel(null, new BibDatabaseContext(), Globals.getKeyPrefs(), Globals.prefs.getPreviewPreferences(), JabRefGUI.getMainFrame().getDialogService(), ExternalFileTypes.getInstance()); previewPanel.setEntry(diskEntry); return previewPanel; } diff --git a/src/main/java/org/jabref/gui/collab/EntryDeleteChangeViewModel.java b/src/main/java/org/jabref/gui/collab/EntryDeleteChangeViewModel.java index 22f4c9a80b9..3efe55cb530 100644 --- a/src/main/java/org/jabref/gui/collab/EntryDeleteChangeViewModel.java +++ b/src/main/java/org/jabref/gui/collab/EntryDeleteChangeViewModel.java @@ -3,7 +3,7 @@ import javafx.scene.Node; import org.jabref.Globals; -import org.jabref.gui.FXDialogService; +import org.jabref.JabRefGUI; import org.jabref.gui.PreviewPanel; import org.jabref.gui.externalfiletype.ExternalFileTypes; import org.jabref.gui.undo.NamedCompound; @@ -46,7 +46,7 @@ public void makeChange(BibDatabaseContext database, NamedCompound undoEdit) { @Override public Node description() { - PreviewPanel previewPanel = new PreviewPanel(null, null, Globals.getKeyPrefs(), Globals.prefs.getPreviewPreferences(), new FXDialogService(), ExternalFileTypes.getInstance()); + PreviewPanel previewPanel = new PreviewPanel(null, new BibDatabaseContext(), Globals.getKeyPrefs(), Globals.prefs.getPreviewPreferences(), JabRefGUI.getMainFrame().getDialogService(), ExternalFileTypes.getInstance()); previewPanel.setEntry(memEntry); return previewPanel; } diff --git a/src/main/java/org/jabref/gui/customizefields/CustomizeGeneralFieldsDialogView.java b/src/main/java/org/jabref/gui/customizefields/CustomizeGeneralFieldsDialogView.java index a36f44bb86d..e2e6fe781fb 100644 --- a/src/main/java/org/jabref/gui/customizefields/CustomizeGeneralFieldsDialogView.java +++ b/src/main/java/org/jabref/gui/customizefields/CustomizeGeneralFieldsDialogView.java @@ -36,11 +36,7 @@ public CustomizeGeneralFieldsDialogView() { .load() .setAsDialogPane(this); - HelpAction helpCommand = new HelpAction(HelpFile.GENERAL_FIELDS); - //HelpAction is written with Swing, not JavaFX, so runCommand() cannot be used normally. Here I am reaching into - //the command and running execute. When HelpAction is converted to JavaFX, - //the following will need to be changed. - ControlHelper.setAction(helpButton, getDialogPane(), event -> helpCommand.getCommand().execute()); + ControlHelper.setAction(helpButton, getDialogPane(), event -> new HelpAction(HelpFile.GENERAL_FIELDS).execute()); ControlHelper.setAction(resetButton, getDialogPane(), event -> resetFields()); ControlHelper.setAction(okButton, getDialogPane(), event -> saveFieldsAndCloseDialog()); diff --git a/src/main/java/org/jabref/gui/desktop/JabRefDesktop.java b/src/main/java/org/jabref/gui/desktop/JabRefDesktop.java index aa7f9dd22fc..40993772e54 100644 --- a/src/main/java/org/jabref/gui/desktop/JabRefDesktop.java +++ b/src/main/java/org/jabref/gui/desktop/JabRefDesktop.java @@ -10,8 +10,6 @@ import java.util.Optional; import java.util.regex.Pattern; -import javax.swing.JOptionPane; - import org.jabref.Globals; import org.jabref.JabRefGUI; import org.jabref.gui.desktop.os.DefaultDesktop; @@ -168,7 +166,33 @@ private static void openExternalFilePlatformIndependent(Optional typeMenu.show(typeLabel, Side.RIGHT, 0, 0)); typeChangeButton.setOnMouseClicked(event -> typeMenu.show(typeChangeButton, Side.RIGHT, 0, 0)); // Add menu for fetching bibliographic information diff --git a/src/main/java/org/jabref/gui/entryeditor/FieldsEditorTab.java b/src/main/java/org/jabref/gui/entryeditor/FieldsEditorTab.java index bbd23b29d97..26f22eb2b6f 100644 --- a/src/main/java/org/jabref/gui/entryeditor/FieldsEditorTab.java +++ b/src/main/java/org/jabref/gui/entryeditor/FieldsEditorTab.java @@ -103,7 +103,7 @@ private void setupPanel(BibEntry entry, boolean compressed, SuggestionProviders } ColumnConstraints columnExpand = new ColumnConstraints(); - columnExpand.setHgrow(Priority.ALWAYS); + columnExpand.setPercentWidth(60); ColumnConstraints columnDoNotContract = new ColumnConstraints(); columnDoNotContract.setMinWidth(Region.USE_PREF_SIZE); diff --git a/src/main/java/org/jabref/gui/entryeditor/SourceTab.java b/src/main/java/org/jabref/gui/entryeditor/SourceTab.java index 89f97ffbf2e..2752103316d 100644 --- a/src/main/java/org/jabref/gui/entryeditor/SourceTab.java +++ b/src/main/java/org/jabref/gui/entryeditor/SourceTab.java @@ -145,7 +145,7 @@ protected void bindToEntry(BibEntry entry) { // and update source code for every change of entry field values BindingsHelper.bindContentBidirectional(entry.getFieldsObservable(), codeArea.focusedProperty(), onFocus -> { if (!onFocus) { - storeSource(codeArea.textProperty().getValue()); + storeSource(entry, codeArea.textProperty().getValue()); } }, fields -> { DefaultTaskExecutor.runAndWaitInJavaFXThread(() -> { @@ -163,8 +163,8 @@ protected void bindToEntry(BibEntry entry) { } - private void storeSource(String text) { - if ((currentEntry == null) || text.isEmpty()) { + private void storeSource(BibEntry outOfFocusEntry, String text) { + if ((outOfFocusEntry == null) || text.isEmpty()) { return; } @@ -197,42 +197,42 @@ private void storeSource(String text) { String newKey = newEntry.getCiteKeyOptional().orElse(null); if (newKey != null) { - currentEntry.setCiteKey(newKey); + outOfFocusEntry.setCiteKey(newKey); } else { - currentEntry.clearCiteKey(); + outOfFocusEntry.clearCiteKey(); } // First, remove fields that the user has removed. - for (Map.Entry field : currentEntry.getFieldMap().entrySet()) { + for (Map.Entry field : outOfFocusEntry.getFieldMap().entrySet()) { String fieldName = field.getKey(); String fieldValue = field.getValue(); if (InternalBibtexFields.isDisplayableField(fieldName) && !newEntry.hasField(fieldName)) { compound.addEdit( - new UndoableFieldChange(currentEntry, fieldName, fieldValue, null)); - currentEntry.clearField(fieldName); + new UndoableFieldChange(outOfFocusEntry, fieldName, fieldValue, null)); + outOfFocusEntry.clearField(fieldName); } } // Then set all fields that have been set by the user. for (Map.Entry field : newEntry.getFieldMap().entrySet()) { String fieldName = field.getKey(); - String oldValue = currentEntry.getField(fieldName).orElse(null); + String oldValue = outOfFocusEntry.getField(fieldName).orElse(null); String newValue = field.getValue(); if (!Objects.equals(oldValue, newValue)) { // Test if the field is legally set. new LatexFieldFormatter(fieldFormatterPreferences) .format(newValue, fieldName); - compound.addEdit(new UndoableFieldChange(currentEntry, fieldName, oldValue, newValue)); - currentEntry.setField(fieldName, newValue); + compound.addEdit(new UndoableFieldChange(outOfFocusEntry, fieldName, oldValue, newValue)); + outOfFocusEntry.setField(fieldName, newValue); } } // See if the user has changed the entry type: - if (!Objects.equals(newEntry.getType(), currentEntry.getType())) { - compound.addEdit(new UndoableChangeType(currentEntry, currentEntry.getType(), newEntry.getType())); - currentEntry.setType(newEntry.getType()); + if (!Objects.equals(newEntry.getType(), outOfFocusEntry.getType())) { + compound.addEdit(new UndoableChangeType(outOfFocusEntry, outOfFocusEntry.getType(), newEntry.getType())); + outOfFocusEntry.setType(newEntry.getType()); } compound.end(); undoManager.addEdit(compound); diff --git a/src/main/java/org/jabref/gui/exporter/ExportCommand.java b/src/main/java/org/jabref/gui/exporter/ExportCommand.java index 36bb095a027..77b3a8430de 100644 --- a/src/main/java/org/jabref/gui/exporter/ExportCommand.java +++ b/src/main/java/org/jabref/gui/exporter/ExportCommand.java @@ -113,14 +113,14 @@ private void export(Path file, FileChooser.ExtensionFilter selectedExtensionFilt finEntries); return null; // can not use BackgroundTask.wrap(Runnable) because Runnable.run() can't throw Exceptions }) - .onSuccess(x -> frame.output(Localization.lang("%0 export successful", format.getName()))) + .onSuccess(x -> frame.getDialogService().notify(Localization.lang("%0 export successful", format.getName()))) .onFailure(this::handleError) .executeWith(Globals.TASK_EXECUTOR); } private void handleError(Exception ex) { LOGGER.warn("Problem exporting", ex); - frame.output(Localization.lang("Could not save file.")); + frame.getDialogService().notify(Localization.lang("Could not save file.")); // Need to warn the user that saving failed! frame.getDialogService().showErrorDialogAndWait(Localization.lang("Save library"), Localization.lang("Could not save file."), ex); } diff --git a/src/main/java/org/jabref/gui/exporter/ExportToClipboardAction.java b/src/main/java/org/jabref/gui/exporter/ExportToClipboardAction.java index 2148a0146c0..122b5baf610 100644 --- a/src/main/java/org/jabref/gui/exporter/ExportToClipboardAction.java +++ b/src/main/java/org/jabref/gui/exporter/ExportToClipboardAction.java @@ -53,7 +53,7 @@ public void execute() { } if (panel.getSelectedEntries().isEmpty()) { - panel.output(Localization.lang("This operation requires one or more entries to be selected.")); + dialogService.notify(Localization.lang("This operation requires one or more entries to be selected.")); return; } @@ -123,7 +123,7 @@ private void setContentToClipboard(String content) { clipboardContent.putRtf(content); Globals.clipboardManager.setContent(clipboardContent); - panel.output(Localization.lang("Entries exported to clipboard") + ": " + entries.size()); + dialogService.notify(Localization.lang("Entries exported to clipboard") + ": " + entries.size()); } diff --git a/src/main/java/org/jabref/gui/exporter/SaveDatabaseAction.java b/src/main/java/org/jabref/gui/exporter/SaveDatabaseAction.java index f1bfe5c0739..db4a591b1ec 100644 --- a/src/main/java/org/jabref/gui/exporter/SaveDatabaseAction.java +++ b/src/main/java/org/jabref/gui/exporter/SaveDatabaseAction.java @@ -140,7 +140,7 @@ private boolean doSave() { // Reset title of tab frame.setTabTitle(panel, panel.getTabTitle(), panel.getBibDatabaseContext().getDatabaseFile().get().getAbsolutePath()); - frame.output(Localization.lang("Saved library") + " '" + frame.getDialogService().notify(Localization.lang("Saved library") + " '" + panel.getBibDatabaseContext().getDatabaseFile().get().getPath() + "'."); frame.setWindowTitle(); frame.updateAllTabTitles(); @@ -158,7 +158,7 @@ private boolean doSave() { public boolean save() { if (panel.getBibDatabaseContext().getDatabasePath().isPresent()) { - panel.frame().output(Localization.lang("Saving library") + "..."); + panel.frame().getDialogService().notify(Localization.lang("Saving library") + "..."); panel.setSaving(true); return doSave(); } else { @@ -211,7 +211,7 @@ public void saveAs(Path file) { save(); // Reinstall AutosaveManager and BackupManager - panel.resetChangeMonitor(); + panel.resetChangeMonitorAndChangePane(); if (readyForAutosave(context)) { AutosaveManager autosaver = AutosaveManager.start(context); autosaver.registerListener(new AutosaveUIManager(panel)); @@ -240,7 +240,7 @@ public void saveSelectedAsPlain() { try { saveDatabase(path, true, prefs.getDefaultEncoding(), SavePreferences.DatabaseSaveType.PLAIN_BIBTEX); frame.getFileHistory().newFile(path); - frame.output(Localization.lang("Saved selected to '%0'.", path.toString())); + frame.getDialogService().notify(Localization.lang("Saved selected to '%0'.", path.toString())); } catch (SaveException ex) { LOGGER.error("A problem occurred when trying to save the file", ex); frame.getDialogService().showErrorDialogAndWait(Localization.lang("Save library"), Localization.lang("Could not save file."), ex); diff --git a/src/main/java/org/jabref/gui/externalfiles/AutoLinkFilesAction.java b/src/main/java/org/jabref/gui/externalfiles/AutoLinkFilesAction.java new file mode 100644 index 00000000000..967243e840b --- /dev/null +++ b/src/main/java/org/jabref/gui/externalfiles/AutoLinkFilesAction.java @@ -0,0 +1,68 @@ +package org.jabref.gui.externalfiles; + +import java.util.List; + +import javafx.concurrent.Task; + +import org.jabref.gui.DialogService; +import org.jabref.gui.JabRefFrame; +import org.jabref.gui.actions.SimpleCommand; +import org.jabref.gui.externalfiletype.ExternalFileTypes; +import org.jabref.gui.undo.NamedCompound; +import org.jabref.logic.l10n.Localization; +import org.jabref.model.entry.BibEntry; +import org.jabref.preferences.JabRefPreferences; + +/** + * This Action may only be used in a menu or button. + * Never in the entry editor. FileListEditor and EntryEditor have other ways to update the file links + */ +public class AutoLinkFilesAction extends SimpleCommand { + + private final DialogService dialogService; + private final JabRefFrame frame; + private final JabRefPreferences preferences; + + public AutoLinkFilesAction(JabRefFrame frame, JabRefPreferences preferences) { + this.frame = frame; + this.dialogService = frame.getDialogService(); + this.preferences = preferences; + } + + @Override + public void execute() { + List entries = frame.getCurrentBasePanel().getSelectedEntries(); + if (entries.isEmpty()) { + dialogService.notify(Localization.lang("This operation requires one or more entries to be selected.")); + return; + } + + final NamedCompound nc = new NamedCompound(Localization.lang("Automatically set file links")); + AutoSetFileLinksUtil util = new AutoSetFileLinksUtil(frame.getCurrentBasePanel().getBibDatabaseContext(), preferences.getFilePreferences(), preferences.getAutoLinkPreferences(), ExternalFileTypes.getInstance()); + Task> linkFilesTask = new Task>() { + @Override + protected List call() { + return util.linkAssociatedFiles(entries, nc); + } + + @Override + protected void succeeded() { + if (!getValue().isEmpty()) { + if (nc.hasEdits()) { + nc.end(); + frame.getCurrentBasePanel().getUndoManager().addEdit(nc); + } + dialogService.notify(Localization.lang("Finished automatically setting external links.")); + } else { + dialogService.notify(Localization.lang("Finished automatically setting external links.") + " " + Localization.lang("No files found.")); + } + } + }; + + dialogService.showProgressDialogAndWait( + Localization.lang("Automatically setting file links"), + Localization.lang("Searching for files"), + linkFilesTask + ); + } +} diff --git a/src/main/java/org/jabref/gui/externalfiles/AutoSetFileLinksUtil.java b/src/main/java/org/jabref/gui/externalfiles/AutoSetFileLinksUtil.java index f1f07088a16..284eba0f689 100644 --- a/src/main/java/org/jabref/gui/externalfiles/AutoSetFileLinksUtil.java +++ b/src/main/java/org/jabref/gui/externalfiles/AutoSetFileLinksUtil.java @@ -11,12 +11,17 @@ import org.jabref.gui.externalfiletype.ExternalFileType; import org.jabref.gui.externalfiletype.ExternalFileTypes; import org.jabref.gui.externalfiletype.UnknownExternalFileType; +import org.jabref.gui.undo.NamedCompound; +import org.jabref.gui.undo.UndoableFieldChange; +import org.jabref.gui.util.DefaultTaskExecutor; import org.jabref.logic.util.io.AutoLinkPreferences; import org.jabref.logic.util.io.FileFinder; import org.jabref.logic.util.io.FileFinders; import org.jabref.logic.util.io.FileUtil; import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.entry.BibEntry; +import org.jabref.model.entry.FieldName; +import org.jabref.model.entry.FileFieldWriter; import org.jabref.model.entry.LinkedFile; import org.jabref.model.metadata.FilePreferences; import org.jabref.model.util.FileHelper; @@ -26,7 +31,7 @@ public class AutoSetFileLinksUtil { - private static final Logger LOGGER = LoggerFactory.getLogger(AutoSetLinks.class); + private static final Logger LOGGER = LoggerFactory.getLogger(AutoSetFileLinksUtil.class); private List directories; private AutoLinkPreferences autoLinkPreferences; private ExternalFileTypes externalFileTypes; @@ -35,12 +40,44 @@ public AutoSetFileLinksUtil(BibDatabaseContext databaseContext, FilePreferences this(databaseContext.getFileDirectoriesAsPaths(filePreferences), autoLinkPreferences, externalFileTypes); } - public AutoSetFileLinksUtil(List directories, AutoLinkPreferences autoLinkPreferences, ExternalFileTypes externalFileTypes) { + private AutoSetFileLinksUtil(List directories, AutoLinkPreferences autoLinkPreferences, ExternalFileTypes externalFileTypes) { this.directories = directories; this.autoLinkPreferences = autoLinkPreferences; this.externalFileTypes = externalFileTypes; } + public List linkAssociatedFiles(List entries, NamedCompound ce) { + List changedEntries = new ArrayList<>(); + for (BibEntry entry : entries) { + + List linkedFiles = new ArrayList<>(); + try { + linkedFiles = findAssociatedNotLinkedFiles(entry); + } catch (IOException e) { + LOGGER.error("Problem finding files", e); + } + + if (ce != null) { + for (LinkedFile linkedFile : linkedFiles) { + // store undo information + String newVal = FileFieldWriter.getStringRepresentation(linkedFile); + + String oldVal = entry.getField(FieldName.FILE).orElse(null); + + UndoableFieldChange fieldChange = new UndoableFieldChange(entry, FieldName.FILE, oldVal, newVal); + ce.addEdit(fieldChange); + + DefaultTaskExecutor.runInJavaFXThread(() -> { + entry.addFile(linkedFile); + }); + } + + changedEntries.add(entry); + } + } + return changedEntries; + } + public List findAssociatedNotLinkedFiles(BibEntry entry) throws IOException { List linkedFiles = new ArrayList<>(); @@ -53,22 +90,23 @@ public List findAssociatedNotLinkedFiles(BibEntry entry) throws IOEx // Collect the found files that are not yet linked for (Path foundFile : result) { boolean fileAlreadyLinked = entry.getFiles().stream() - .map(file -> file.findIn(directories)) - .anyMatch(file -> { - try { - return file.isPresent() && Files.isSameFile(file.get(), foundFile); - } catch (IOException e) { - LOGGER.error("Problem with isSameFile", e); - } - return false; - }); + .map(file -> file.findIn(directories)) + .anyMatch(file -> { + try { + return file.isPresent() && Files.isSameFile(file.get(), foundFile); + } catch (IOException e) { + LOGGER.error("Problem with isSameFile", e); + } + return false; + }); + if (!fileAlreadyLinked) { Optional type = FileHelper.getFileExtension(foundFile) - .map(externalFileTypes::getExternalFileTypeByExt) - .orElse(Optional.of(new UnknownExternalFileType(""))); + .map(externalFileTypes::getExternalFileTypeByExt) + .orElse(Optional.of(new UnknownExternalFileType(""))); String strType = type.isPresent() ? type.get().getName() : ""; - String relativeFilePath = FileUtil.relativize(foundFile, directories).toString(); + Path relativeFilePath = FileUtil.relativize(foundFile, directories); LinkedFile linkedFile = new LinkedFile("", relativeFilePath, strType); linkedFiles.add(linkedFile); } diff --git a/src/main/java/org/jabref/gui/externalfiles/AutoSetLinks.java b/src/main/java/org/jabref/gui/externalfiles/AutoSetLinks.java deleted file mode 100644 index d874493a98d..00000000000 --- a/src/main/java/org/jabref/gui/externalfiles/AutoSetLinks.java +++ /dev/null @@ -1,172 +0,0 @@ -package org.jabref.gui.externalfiles; - -import java.awt.BorderLayout; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Set; - -import javax.swing.BorderFactory; -import javax.swing.JDialog; -import javax.swing.JLabel; -import javax.swing.JProgressBar; -import javax.swing.SwingConstants; -import javax.swing.SwingUtilities; - -import org.jabref.Globals; -import org.jabref.gui.externalfiletype.ExternalFileType; -import org.jabref.gui.externalfiletype.ExternalFileTypes; -import org.jabref.gui.undo.NamedCompound; -import org.jabref.gui.undo.UndoableFieldChange; -import org.jabref.gui.util.DefaultTaskExecutor; -import org.jabref.logic.l10n.Localization; -import org.jabref.model.database.BibDatabaseContext; -import org.jabref.model.entry.BibEntry; -import org.jabref.model.entry.FieldName; -import org.jabref.model.entry.FileFieldWriter; -import org.jabref.model.entry.LinkedFile; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class AutoSetLinks { - - private static final Logger LOGGER = LoggerFactory.getLogger(AutoSetLinks.class); - - private AutoSetLinks() { - } - - /** - * Shortcut method if links are set without using the GUI - * - * @param entries the entries for which links should be set - * @param databaseContext the database for which links are set - */ - public static void autoSetLinks(List entries, BibDatabaseContext databaseContext) { - autoSetLinks(entries, null, null, databaseContext, null, null); - } - - /** - * Automatically add links for this set of entries, based on the globally stored list of external file types. The - * entries are modified, and corresponding UndoEdit elements added to the NamedCompound given as argument. - * Furthermore, all entries which are modified are added to the Set of entries given as an argument. - *

- * The entries' bibtex keys must have been set - entries lacking key are ignored. The operation is done in a new - * thread, which is returned for the caller to wait for if needed. - * - * @param entries A collection of BibEntry objects to find links for. - * @param ce A NamedCompound to add UndoEdit elements to. - * @param changedEntries MODIFIED, optional. A Set of BibEntry objects to which all modified entries is added. - - * @param databaseContext The database providing the relevant file directory, if any. - * @param callback An ActionListener that is notified (on the event dispatch thread) when the search is finished. - * The ActionEvent has id=0 if no new links were added, and id=1 if one or more links were added. This - * parameter can be null, which means that no callback will be notified. - * @param diag An instantiated modal JDialog which will be used to display the progress of the automatically setting. This - * parameter can be null, which means that no progress update will be shown. - * @return the thread performing the automatically setting - */ - public static Runnable autoSetLinks(final List entries, final NamedCompound ce, - final Set changedEntries, - final BibDatabaseContext databaseContext, final ActionListener callback, final JDialog diag) { - final Collection types = ExternalFileTypes.getInstance().getExternalFileTypeSelection(); - if (diag != null) { - final JProgressBar prog = new JProgressBar(SwingConstants.HORIZONTAL, 0, types.size() - 1); - final JLabel label = new JLabel(Localization.lang("Searching for files")); - prog.setIndeterminate(true); - prog.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); - diag.setTitle(Localization.lang("Automatically setting file links")); - diag.getContentPane().add(prog, BorderLayout.CENTER); - diag.getContentPane().add(label, BorderLayout.SOUTH); - - diag.pack(); - diag.setLocationRelativeTo(diag.getParent()); - } - - Runnable r = () -> { - boolean foundAny = false; - AutoSetFileLinksUtil util = new AutoSetFileLinksUtil(databaseContext, Globals.prefs.getFilePreferences(), Globals.prefs.getAutoLinkPreferences(), ExternalFileTypes.getInstance()); - - for (BibEntry entry : entries) { - - List linkedFiles = new ArrayList<>(); - try { - linkedFiles = util.findAssociatedNotLinkedFiles(entry); - } catch (IOException e) { - LOGGER.error("Problem finding files", e); - } - - if (ce != null) { - for (LinkedFile linkedFile : linkedFiles) { - // store undo information - String newVal = FileFieldWriter.getStringRepresentation(linkedFile); - - String oldVal = entry.getField(FieldName.FILE).orElse(null); - - UndoableFieldChange fieldChange = new UndoableFieldChange(entry, FieldName.FILE, oldVal, newVal); - ce.addEdit(fieldChange); - - DefaultTaskExecutor.runInJavaFXThread(() -> { - entry.addFile(linkedFile); - }); - foundAny = true; - } - - if (changedEntries != null) { - changedEntries.add(entry); - } - } - - } - - final int id = foundAny ? 1 : 0; - SwingUtilities.invokeLater(() -> { - - if (diag != null) { - diag.dispose(); - } - if (callback != null) { - callback.actionPerformed(new ActionEvent(AutoSetLinks.class, id, "")); - } - - }); - - }; - - SwingUtilities.invokeLater(() -> { - // show dialog which will be hidden when the task is done - if (diag != null) { - diag.setVisible(true); - } - }); - - return r; - } - - /** - * Automatically add links for this entry to the table model given as an argument, based on the globally stored list - * of external file types. The entry itself is not modified. The entry's bibtex key must have been set. - * - * @param entry The BibEntry to find links for. - - * @param databaseContext The database providing the relevant file directory, if any. - * @param callback An ActionListener that is notified (on the event dispatch thread) when the search is finished. - * The ActionEvent has id=0 if no new links were added, and id=1 if one or more links were added. This - * parameter can be null, which means that no callback will be notified. The passed ActionEvent is - * constructed with (this, id, ""), where id is 1 if something has been done and 0 if nothing has been - * done. - * @param diag An instantiated modal JDialog which will be used to display the progress of the automatically setting. This - * parameter can be null, which means that no progress update will be shown. - * @return the runnable able to perform the automatically setting - */ - public static Runnable autoSetLinks(final BibEntry entry, - final BibDatabaseContext databaseContext, final ActionListener callback, final JDialog diag) { - return autoSetLinks(Collections.singletonList(entry), null, null, databaseContext, callback, - diag); - } - -} diff --git a/src/main/java/org/jabref/gui/externalfiles/DownloadExternalFile.java b/src/main/java/org/jabref/gui/externalfiles/DownloadExternalFile.java deleted file mode 100644 index b4e1ad794db..00000000000 --- a/src/main/java/org/jabref/gui/externalfiles/DownloadExternalFile.java +++ /dev/null @@ -1,345 +0,0 @@ -package org.jabref.gui.externalfiles; - -import java.io.File; -import java.io.IOException; -import java.net.MalformedURLException; -import java.net.URL; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.List; -import java.util.Optional; - -import javax.swing.SwingUtilities; - -import org.jabref.Globals; -import org.jabref.JabRefExecutorService; -import org.jabref.gui.DialogService; -import org.jabref.gui.externalfiletype.ExternalFileType; -import org.jabref.gui.externalfiletype.ExternalFileTypes; -import org.jabref.gui.filelist.FileListEntryEditor; -import org.jabref.logic.l10n.Localization; -import org.jabref.logic.net.URLDownload; -import org.jabref.logic.util.OS; -import org.jabref.logic.util.io.FileUtil; -import org.jabref.model.database.BibDatabaseContext; -import org.jabref.model.entry.BibEntry; -import org.jabref.model.entry.LinkedFile; -import org.jabref.preferences.JabRefPreferences; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * This class handles the download of an external file. Typically called when the user clicks - * the "Download" button in a FileListEditor shown in an EntryEditor. - *

- * The FileListEditor constructs the DownloadExternalFile instance, then calls the download() - * method passing a reference to itself as a callback. The download() method asks for the URL, - * then starts the download. When the download is completed, it calls the downloadCompleted() - * method on the callback FileListEditor, which then needs to take care of linking to the file. - * The local filename is passed as an argument to the downloadCompleted() method. - *

- * If the download is canceled, or failed, the user is informed. The callback is never called. - */ -public class DownloadExternalFile { - - private static final Logger LOGGER = LoggerFactory.getLogger(DownloadExternalFile.class); - - private final BibDatabaseContext databaseContext; - private final BibEntry entry; - private final DialogService dialogService; - - private FileListEntryEditor editor; - private boolean downloadFinished; - private boolean dontShowDialog; - - public DownloadExternalFile(DialogService dialogService, BibDatabaseContext databaseContext, BibEntry entry) { - this.dialogService = dialogService; - this.databaseContext = databaseContext; - this.entry = entry; - } - - /** - * Look for the last '.' in the link, and return the following characters. - * This gives the extension for most reasonably named links. - * - * @param link The link - * @return The suffix, excluding the dot (e.g. "pdf") - */ - public static String getSuffix(final String link) { - String strippedLink = link; - try { - // Try to strip the query string, if any, to get the correct suffix: - URL url = new URL(link); - if ((url.getQuery() != null) && (url.getQuery().length() < (link.length() - 1))) { - strippedLink = link.substring(0, link.length() - url.getQuery().length() - 1); - } - } catch (MalformedURLException e) { - // Don't report this error, since this getting the suffix is a non-critical - // operation, and this error will be triggered and reported elsewhere. - } - // First see if the stripped link gives a reasonable suffix: - String suffix; - int strippedLinkIndex = strippedLink.lastIndexOf('.'); - if ((strippedLinkIndex <= 0) || (strippedLinkIndex == (strippedLink.length() - 1))) { - suffix = null; - } else { - suffix = strippedLink.substring(strippedLinkIndex + 1); - } - if (!ExternalFileTypes.getInstance().isExternalFileTypeByExt(suffix)) { - // If the suffix doesn't seem to give any reasonable file type, try - // with the non-stripped link: - int index = link.lastIndexOf('.'); - if ((index <= 0) || (index == (link.length() - 1))) { - // No occurrence, or at the end - // Check if there are path separators in the suffix - if so, it is definitely - // not a proper suffix, so we should give up: - if (strippedLink.substring(strippedLinkIndex + 1).indexOf('/') >= 1) { - return ""; - } else { - return suffix; // return the first one we found, anyway. - } - } else { - // Check if there are path separators in the suffix - if so, it is definitely - // not a proper suffix, so we should give up: - if (link.substring(index + 1).indexOf('/') >= 1) { - return ""; - } else { - return link.substring(index + 1); - } - } - } else { - return suffix; - } - } - - /** - * Start a download. - * - * @param callback The object to which the filename should be reported when download - * is complete. - */ - public void download(final DownloadCallback callback) throws IOException { - dontShowDialog = false; - - Optional res = dialogService.showInputDialogAndWait(Localization.lang("Download file"), Localization.lang("Enter URL to download")); - - if (res.isPresent()) { - URL url; - try { - url = new URL(res.get()); - } catch (MalformedURLException ex1) { - dialogService.showErrorDialogAndWait(Localization.lang("Download file"), - Localization.lang("Invalid URL")); - - return; - } - download(url, callback); - } - } - - public void download(URL url, final DownloadCallback callback) throws IOException { - // TODO: what if this takes long time? - // TODO: stop editor dialog if this results in an error? - String mimeType = new URLDownload(url).getMimeType(); - download(url, mimeType, callback); - } - - private Optional getExternalFileType(String mimeType) { - Optional suggestedType = Optional.empty(); - if (mimeType != null) { - LOGGER.debug("MIME Type suggested: " + mimeType); - suggestedType = ExternalFileTypes.getInstance().getExternalFileTypeByMimeType(mimeType); - } - return suggestedType; - } - - /** - * Start a download. - * - * @param callback The object to which the filename should be reported when download - * is complete. - */ - public void download(URL url, String mimeType, final DownloadCallback callback) throws IOException { - Optional fileType = getExternalFileType(mimeType); - - // First of all, start the download itself in the background to a temporary file: - final Path tempFile = Files.createTempFile("jabref_download", "tmp"); - tempFile.toFile().deleteOnExit(); - - final URLDownload fileDownload = new URLDownload(url); - - JabRefExecutorService.INSTANCE.execute(() -> { - try { - fileDownload.toFile(tempFile); - } catch (IOException e) { - dontShowDialog = true; - if ((editor != null) && editor.isVisible()) { - editor.setVisible(false, false); - } - - LOGGER.info("Error while downloading " + "'" + url + "'", e); - - dialogService.showErrorDialogAndWait(Localization.lang("Download file"), Localization.lang("Invalid URL") + ": " + e.getMessage()); - - return; - } - // Download finished: call the method that stops the progress bar etc.: - SwingUtilities.invokeLater(DownloadExternalFile.this::downloadFinished); - }); - - // Then, while the download is proceeding, let the user choose the details of the file: - String suffix; - if (fileType.isPresent()) { - suffix = fileType.get().getExtension(); - } else { - // If we did not find a file type from the MIME type, try based on extension: - suffix = getSuffix(url.toString()); - if (suffix == null) { - suffix = ""; - } - fileType = ExternalFileTypes.getInstance().getExternalFileTypeByExt(suffix); - } - String suggestedName = getSuggestedFileName(suffix); - List fDirectory = databaseContext.getFileDirectories(Globals.prefs.getFilePreferences()); - String directory; - if (fDirectory.isEmpty()) { - directory = null; - } else { - directory = fDirectory.get(0); - } - final String suggestDir = directory == null ? System.getProperty("user.home") : directory; - File file = new File(new File(suggestDir), suggestedName); - LinkedFile fileListEntry = new LinkedFile("", file.getCanonicalPath(), fileType.map(ExternalFileType::getName).orElse("")); - editor = new FileListEntryEditor(fileListEntry, true, false, databaseContext, true); - editor.getProgressBar().setIndeterminate(true); - editor.setOkEnabled(false); - editor.setExternalConfirm(closeEntry -> { - File f = directory == null ? new File(closeEntry.getLink()) : expandFilename(directory, closeEntry.getLink()); - if (f.isDirectory()) { - dialogService.showErrorDialogAndWait(Localization.lang("Download file"), - Localization.lang("Target file cannot be a directory.")); - - return false; - } - if (f.exists()) { - return dialogService.showConfirmationDialogAndWait(Localization.lang("Download file"), - Localization.lang("'%0' exists. Overwrite file?", f.getName()), Localization.lang("Overwrite file"), - Localization.lang("Cancel")); - - } else { - return true; - } - }); - if (dontShowDialog) { - return; - } else { - editor.setVisible(true, false); - } - // Editor closed. Go on: - if (editor.okPressed()) { - File toFile = directory == null ? new File(fileListEntry.getLink()) : expandFilename(directory, - fileListEntry.getLink()); - String dirPrefix; - if (directory == null) { - dirPrefix = null; - } else { - if (directory.endsWith(OS.FILE_SEPARATOR)) { - dirPrefix = directory; - } else { - dirPrefix = directory + OS.FILE_SEPARATOR; - } - } - - boolean success = FileUtil.copyFile(tempFile, Paths.get(toFile.toURI()), true); - if (!success) { - // OOps, the file exists! - LOGGER.error("File already exists! DownloadExternalFile.download()"); - } - - // If the local file is in or below the main file directory, change the - // path to relative: - if ((dirPrefix != null) && fileListEntry.getLink().startsWith(directory) - && (fileListEntry.getLink().length() > dirPrefix.length())) { - fileListEntry = new LinkedFile(fileListEntry.getDescription(), - fileListEntry.getLink().substring(dirPrefix.length()), fileListEntry.getFileType()); - } - callback.downloadComplete(fileListEntry); - - if (!Files.deleteIfExists(tempFile)) { - LOGGER.info("Cannot delete temporary file"); - } - } else { - // Canceled. Just delete the temp file: - if (downloadFinished && !Files.deleteIfExists(tempFile)) { - LOGGER.info("Cannot delete temporary file"); - } - } - } - - /** - * Construct a File object pointing to the file linked, whether the link is - * absolute or relative to the main directory. - * - * @param directory The main directory. - * @param link The absolute or relative link. - * @return The expanded File. - */ - private File expandFilename(String directory, String link) { - File toFile = new File(link); - // If this is a relative link, we should perhaps append the directory: - String dirPrefix = directory + OS.FILE_SEPARATOR; - if (!toFile.isAbsolute()) { - toFile = new File(dirPrefix + link); - } - return toFile; - } - - /** - * This is called by the download thread when download is completed. - */ - private void downloadFinished() { - downloadFinished = true; - editor.getProgressBar().setVisible(false); - editor.getProgressBarLabel().setVisible(false); - editor.setOkEnabled(true); - editor.getProgressBar().setValue(editor.getProgressBar().getMaximum()); - } - - private String getSuggestedFileName(String suffix) { - String plannedName = FileUtil.createFileNameFromPattern(databaseContext.getDatabase(), entry, - Globals.prefs.get(JabRefPreferences.IMPORT_FILENAMEPATTERN)); - - if (!suffix.isEmpty()) { - plannedName += "." + suffix; - } - - /* - * [ 1548875 ] download pdf produces unsupported filename - * - * http://sourceforge.net/tracker/index.php?func=detail&aid=1548875&group_id=92314&atid=600306 - * FIXME: rework this! just allow alphanumeric stuff or so? - * https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx#naming_conventions - * http://superuser.com/questions/358855/what-characters-are-safe-in-cross-platform-file-names-for-linux-windows-and-os - * https://support.apple.com/en-us/HT202808 - */ - if (OS.WINDOWS) { - plannedName = plannedName.replaceAll("\\?|\\*|\\<|\\>|\\||\\\"|\\:|\\.$|\\[|\\]", ""); - } else if (OS.OS_X) { - plannedName = plannedName.replace(":", ""); - } - - return plannedName; - } - - /** - * Callback interface that users of this class must implement in order to receive - * notification when download is complete. - */ - @FunctionalInterface - public interface DownloadCallback { - - void downloadComplete(LinkedFile file); - } -} diff --git a/src/main/java/org/jabref/gui/externalfiles/FindFullTextAction.java b/src/main/java/org/jabref/gui/externalfiles/FindFullTextAction.java index 3874a503d16..adb6affcc4c 100644 --- a/src/main/java/org/jabref/gui/externalfiles/FindFullTextAction.java +++ b/src/main/java/org/jabref/gui/externalfiles/FindFullTextAction.java @@ -2,8 +2,6 @@ import java.net.URL; import java.nio.file.Path; -import java.util.ArrayList; -import java.util.List; import java.util.Map; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; @@ -42,12 +40,6 @@ public FindFullTextAction(BasePanel basePanel) { @Override public void execute() { - BackgroundTask.wrap(this::findFullTexts) - .onSuccess(this::downloadFullTexts) - .executeWith(Globals.TASK_EXECUTOR); - } - - private Map, BibEntry> findFullTexts() { if (!basePanel.getSelectedEntries().isEmpty()) { basePanel.output(Localization.lang("Looking for full text document...")); } else { @@ -68,21 +60,24 @@ private Map, BibEntry> findFullTexts() { if (!confirmDownload) { basePanel.output(Localization.lang("Operation canceled.")); - return null; + return; } } + BackgroundTask.wrap(this::findFullTexts) + .onSuccess(this::downloadFullTexts) + .executeWith(Globals.TASK_EXECUTOR); + } + private Map, BibEntry> findFullTexts() { Map, BibEntry> downloads = new ConcurrentHashMap<>(); for (BibEntry entry : basePanel.getSelectedEntries()) { FulltextFetchers fetchers = new FulltextFetchers(Globals.prefs.getImportFormatPreferences()); downloads.put(fetchers.findFullTextPDF(entry), entry); } - return downloads; } private void downloadFullTexts(Map, BibEntry> downloads) { - List> finishedTasks = new ArrayList<>(); for (Map.Entry, BibEntry> download : downloads.entrySet()) { BibEntry entry = download.getValue(); Optional result = download.getKey(); @@ -94,20 +89,15 @@ private void downloadFullTexts(Map, BibEntry> downloads) { dialogService.showErrorDialogAndWait(Localization.lang("Directory not found"), Localization.lang("Main file directory not set!") + " " + Localization.lang("Preferences") + " -> " + Localization.lang("File")); - return; } - - //Download full text + //Download and link full text addLinkedFileFromURL(result.get(), entry); + } else { dialogService.notify(Localization.lang("No full text document found for entry %0.", entry.getCiteKeyOptional().orElse(Localization.lang("undefined")))); } - finishedTasks.add(result); - } - for (Optional result : finishedTasks) { - downloads.remove(result); } } @@ -119,7 +109,6 @@ private void downloadFullTexts(Map, BibEntry> downloads) { * @param entry the entry "value" */ private void addLinkedFileFromURL(URL url, BibEntry entry) { - LinkedFile newLinkedFile = new LinkedFile(url, ""); if (!entry.getFiles().contains(newLinkedFile)) { diff --git a/src/main/java/org/jabref/gui/externalfiletype/ExternalFileMenuItem.java b/src/main/java/org/jabref/gui/externalfiletype/ExternalFileMenuItem.java index a2ebcb02dae..e204ca6b761 100644 --- a/src/main/java/org/jabref/gui/externalfiletype/ExternalFileMenuItem.java +++ b/src/main/java/org/jabref/gui/externalfiletype/ExternalFileMenuItem.java @@ -58,12 +58,12 @@ public void actionPerformed(ActionEvent e) { boolean success = openLink(); if (!success) { List searchedDirs = databaseContext.getFileDirectoriesAsPaths(Globals.prefs.getFilePreferences()); - frame.output(Localization.lang("Unable to open %0", link) + " " + Arrays.toString(searchedDirs.toArray())); + frame.getDialogService().notify(Localization.lang("Unable to open %0", link) + " " + Arrays.toString(searchedDirs.toArray())); } } private boolean openLink() { - frame.output(Localization.lang("External viewer called") + "."); + frame.getDialogService().notify(Localization.lang("External viewer called") + "."); try { Optional type = fileType; if (!this.fileType.isPresent()) { diff --git a/src/main/java/org/jabref/gui/externalfiletype/ExternalFileTypeEntryEditor.java b/src/main/java/org/jabref/gui/externalfiletype/ExternalFileTypeEntryEditor.java index e620e69e84b..676868ac898 100644 --- a/src/main/java/org/jabref/gui/externalfiletype/ExternalFileTypeEntryEditor.java +++ b/src/main/java/org/jabref/gui/externalfiletype/ExternalFileTypeEntryEditor.java @@ -20,8 +20,8 @@ import javax.swing.event.DocumentListener; import org.jabref.Globals; +import org.jabref.JabRefGUI; import org.jabref.gui.DialogService; -import org.jabref.gui.FXDialogService; import org.jabref.gui.icon.IconTheme; import org.jabref.gui.util.DefaultTaskExecutor; import org.jabref.gui.util.FileDialogConfiguration; @@ -62,7 +62,7 @@ public class ExternalFileTypeEntryEditor { FileDialogConfiguration fileDialogConfiguration = new FileDialogConfiguration.Builder() .withInitialDirectory(Paths.get(appDir)).build(); - DialogService ds = new FXDialogService(); + DialogService ds = JabRefGUI.getMainFrame().getDialogService(); Optional path = DefaultTaskExecutor .runInJavaFXThread(() -> ds.showFileOpenDialog(fileDialogConfiguration)); diff --git a/src/main/java/org/jabref/gui/fieldeditors/BibtexKeyEditor.fxml b/src/main/java/org/jabref/gui/fieldeditors/BibtexKeyEditor.fxml index 99d6d3f7601..7feb0fe3335 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/BibtexKeyEditor.fxml +++ b/src/main/java/org/jabref/gui/fieldeditors/BibtexKeyEditor.fxml @@ -6,5 +6,5 @@ -