argsDatabases, boolean isBlank) {
this.bibDatabases = argsDatabases;
this.isBlank = isBlank;
+ this.dialogService = new FXDialogService(mainStage);
// passed file (we take the first one) should be focused
focusedFile = argsDatabases.stream()
- .findFirst()
- .flatMap(ParserResult::getFile)
- .map(File::getAbsolutePath)
- .orElse(Globals.prefs.get(JabRefPreferences.LAST_FOCUSED));
+ .findFirst()
+ .flatMap(ParserResult::getFile)
+ .map(File::getAbsolutePath)
+ .orElse(Globals.prefs.get(JabRefPreferences.LAST_FOCUSED));
- openWindow();
+ openWindow(mainStage);
JabRefGUI.checkForNewVersion(false);
}
@@ -74,13 +82,7 @@ public static void checkForNewVersion(boolean manualExecution) {
new VersionWorker(JabRefGUI.getMainFrame(), manualExecution, currentVersion, toBeIgnored).execute();
}
- private void openWindow() {
-
- // This property is set to make the Mac OSX Java VM move the menu bar to the top of the screen
- if (OS.OS_X) {
- System.setProperty("apple.laf.useScreenMenuBar", "true");
- }
-
+ 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 )
@@ -101,7 +103,7 @@ private void openWindow() {
GUIGlobals.init();
LOGGER.debug("Initializing frame");
- JabRefGUI.mainFrame = new JabRefFrame();
+ JabRefGUI.mainFrame = new JabRefFrame(mainStage);
// Add all bibDatabases databases to the frame:
boolean first = false;
@@ -120,14 +122,15 @@ private void openWindow() {
try {
new SharedDatabaseUIManager(mainFrame).openSharedDatabaseFromParserResult(pr);
} catch (SQLException | DatabaseNotSupportedException | InvalidDBMSConnectionPropertiesException |
- NotASharedDatabaseException e) {
+ NotASharedDatabaseException e) {
pr.getDatabaseContext().clearDatabaseFile(); // do not open the original file
pr.getDatabase().clearSharedDatabaseID();
LOGGER.error("Connection error", e);
- JOptionPane.showMessageDialog(mainFrame,
- e.getMessage() + "\n\n" + Localization.lang("A local copy will be opened."),
- Localization.lang("Connection error"), JOptionPane.WARNING_MESSAGE);
+ dialogService.showErrorDialogAndWait(
+ Localization.lang("Connection error"),
+ Localization.lang("A local copy will be opened."),
+ e);
}
toOpenTab.add(pr);
} else if (pr.toOpenTab()) {
@@ -151,18 +154,29 @@ private void openWindow() {
// state. This needs to be set after the window has been made visible, so we
// do it here:
if (Globals.prefs.getBoolean(JabRefPreferences.WINDOW_MAXIMISED)) {
- JabRefGUI.getMainFrame().setExtendedState(Frame.MAXIMIZED_BOTH);
+ mainStage.setMaximized(true);
}
- JabRefGUI.getMainFrame().setVisible(true);
+ Scene scene = new Scene(JabRefGUI.mainFrame, 800, 800);
+ Globals.getThemeLoader().installBaseCss(scene);
+ mainStage.setTitle(JabRefFrame.FRAME_TITLE);
+ mainStage.getIcons().addAll(IconTheme.getLogoSetFX());
+ mainStage.setScene(scene);
+ mainStage.show();
+
+ mainStage.setOnCloseRequest(event -> {
+ boolean reallyQuit = mainFrame.quit();
+ if (!reallyQuit) {
+ event.consume();
+ }
+ });
for (ParserResult pr : failed) {
- String message = "" + Localization.lang("Error opening file '%0'.", pr.getFile().get().getName())
- + ""
- + pr.getErrorMessage() + "";
+ String message = Localization.lang("Error opening file '%0'.", pr.getFile().get().getName()) + "\n"
+ + pr.getErrorMessage();
+
+ dialogService.showErrorDialogAndWait(Localization.lang("Error opening file"), message);
- JOptionPane.showMessageDialog(JabRefGUI.getMainFrame(), message, Localization.lang("Error opening file"),
- JOptionPane.ERROR_MESSAGE);
}
// Display warnings, if any
@@ -187,10 +201,6 @@ private void openWindow() {
}
LOGGER.debug("Finished adding panels");
-
- if (!bibDatabases.isEmpty()) {
- JabRefGUI.getMainFrame().getCurrentBasePanel().getMainTable().requestFocus();
- }
}
private void openLastEditedDatabases() {
@@ -208,11 +218,11 @@ private void openLastEditedDatabases() {
}
if (BackupManager.checkForBackupFile(dbFile.toPath())) {
- BackupUIManager.showRestoreBackupDialog(mainFrame, dbFile.toPath());
+ BackupUIManager.showRestoreBackupDialog(dialogService, dbFile.toPath());
}
ParserResult parsedDatabase = OpenDatabase.loadDatabase(fileName,
- Globals.prefs.getImportFormatPreferences(), Globals.getFileUpdateMonitor());
+ Globals.prefs.getImportFormatPreferences(), Globals.getFileUpdateMonitor());
if (parsedDatabase.isEmpty()) {
LOGGER.error(Localization.lang("Error opening file") + " '" + dbFile.getPath() + "'");
@@ -233,50 +243,30 @@ private boolean isLoaded(File fileToOpen) {
private void setLookAndFeel() {
try {
- String lookFeel;
- String systemLookFeel = UIManager.getSystemLookAndFeelClassName();
-
- if (Globals.prefs.getBoolean(JabRefPreferences.USE_DEFAULT_LOOK_AND_FEEL)) {
- // FIXME: Problems with GTK L&F on Linux and Mac. Needs reevaluation for Java9
- if (GTK_LF_CLASSNAME.equals(systemLookFeel)) {
- lookFeel = NIMBUS_LOOK_AND_FEEL;
- LOGGER.warn("There seems to be problems with GTK Look&Feel. Using Nimbus L&F instead. Change to another L&F with caution.");
- } else {
- lookFeel = systemLookFeel;
- }
- } else {
- lookFeel = Globals.prefs.get(JabRefPreferences.WIN_LOOK_AND_FEEL);
- }
- //Prevent metal l&f
- if (UIManager.getCrossPlatformLookAndFeelClassName().equals(lookFeel)) {
- UIManager.setLookAndFeel(NIMBUS_LOOK_AND_FEEL);
+ if (OS.WINDOWS) {
+ UIManager.setLookAndFeel(WINDOWS_LOOK_AND_FEEL);
+ }
+ if (OS.OS_X) {
+ UIManager.setLookAndFeel(OSX_AQUA_LOOk_AND_FEEL);
} else {
- try {
- UIManager.setLookAndFeel(lookFeel);
- } catch (ClassNotFoundException | InstantiationException | IllegalAccessException |
- UnsupportedLookAndFeelException e) {
- // specified look and feel does not exist on the classpath, so use system l&f
- UIManager.setLookAndFeel(systemLookFeel);
- // also set system l&f as default
- Globals.prefs.put(JabRefPreferences.WIN_LOOK_AND_FEEL, systemLookFeel);
- // notify the user
- JOptionPane.showMessageDialog(JabRefGUI.getMainFrame(),
- Localization
- .lang("Unable to find the requested look and feel and thus the default one is used."),
- Localization.lang("Warning"), JOptionPane.WARNING_MESSAGE);
- LOGGER.warn("Unable to find requested look and feel", e);
- }
+ UIManager.setLookAndFeel(NIMBUS_LOOK_AND_FEEL);
}
-
// 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");
}
- } catch (Exception e) {
- LOGGER.warn("Look and feel could not be set", e);
+ } catch (UnsupportedLookAndFeelException | ClassNotFoundException | InstantiationException | IllegalAccessException e) {
+ try {
+ LOGGER.warn("Setting Look and Feel to Nimbus", e);
+
+ UIManager.setLookAndFeel(NIMBUS_LOOK_AND_FEEL);
+ } catch (Exception ex) {
+ LOGGER.warn("Look and feel could not be set", e);
+ }
+
}
// In JabRef v2.8, we did it only on NON-Mac. Now, we try on all platforms
diff --git a/src/main/java/org/jabref/JabRefMain.java b/src/main/java/org/jabref/JabRefMain.java
index 4654655e860..fd4ffda4dde 100644
--- a/src/main/java/org/jabref/JabRefMain.java
+++ b/src/main/java/org/jabref/JabRefMain.java
@@ -4,7 +4,6 @@
import javax.swing.JFrame;
import javax.swing.JOptionPane;
-import javax.swing.SwingUtilities;
import javafx.application.Application;
import javafx.application.Platform;
@@ -47,8 +46,39 @@ public static void main(String[] args) {
@Override
public void start(Stage mainStage) throws Exception {
- Platform.setImplicitExit(false);
- SwingUtilities.invokeLater(() -> start(arguments));
+ // Fail on unsupported Java versions
+ ensureCorrectJavaVersion();
+ FallbackExceptionHandler.installExceptionHandler();
+
+ // Init preferences
+ final JabRefPreferences preferences = JabRefPreferences.getInstance();
+ Globals.prefs = preferences;
+ // Perform migrations
+ PreferencesMigrations.runMigrations();
+
+ configureProxy(preferences.getProxyPreferences());
+
+ Globals.startBackgroundTasks();
+
+ applyPreferences(preferences);
+
+ // Process arguments
+ ArgumentProcessor argumentProcessor = new ArgumentProcessor(arguments, ArgumentProcessor.Mode.INITIAL_START);
+
+ // Check for running JabRef
+ if (!handleMultipleAppInstances(arguments) || argumentProcessor.shouldShutDown()) {
+ shutdownCurrentInstance();
+ return;
+ }
+
+ // If not, start GUI
+ new JabRefGUI(mainStage, argumentProcessor.getParserResults(), argumentProcessor.isBlank());
+ }
+
+ @Override
+ public void stop() {
+ Platform.exit();
+ System.exit(0);
}
/**
@@ -73,11 +103,9 @@ private static void ensureCorrectJavaVersion() {
if (java9Fail || versionFail) {
StringBuilder versionError = new StringBuilder(
- Localization.lang("Your current Java version (%0) is not supported. Please install version %1 or higher.",
- checker.getJavaVersion(),
- buildInfo.getMinRequiredJavaVersion()
- )
- );
+ Localization.lang("Your current Java version (%0) is not supported. Please install version %1 or higher.",
+ checker.getJavaVersion(),
+ buildInfo.getMinRequiredJavaVersion()));
versionError.append("\n");
versionError.append(Localization.lang("Your Java Runtime Environment is located at %0.", checker.getJavaInstallationDirectory()));
@@ -87,7 +115,7 @@ private static void ensureCorrectJavaVersion() {
versionError.append(Localization.lang("Note that currently, JabRef does not run with Java 9."));
}
final JFrame frame = new JFrame();
- JOptionPane.showMessageDialog(frame, versionError, Localization.lang("Error"), JOptionPane.ERROR_MESSAGE);
+ JOptionPane.showMessageDialog(null, versionError, Localization.lang("Error"), JOptionPane.ERROR_MESSAGE);
frame.dispose();
// We exit on Java 9 error since this will definitely not work
@@ -97,61 +125,31 @@ private static void ensureCorrectJavaVersion() {
}
}
- private static void start(String[] args) {
- // Fail on unsupported Java versions
- ensureCorrectJavaVersion();
- FallbackExceptionHandler.installExceptionHandler();
-
- // Init preferences
- final JabRefPreferences preferences = JabRefPreferences.getInstance();
- Globals.prefs = preferences;
- // Perform migrations
- PreferencesMigrations.runMigrations();
-
- configureProxy(preferences.getProxyPreferences());
-
- Globals.startBackgroundTasks();
-
- applyPreferences(preferences);
-
- // Process arguments
- ArgumentProcessor argumentProcessor = new ArgumentProcessor(args, ArgumentProcessor.Mode.INITIAL_START);
-
- // Check for running JabRef
- if (!handleMultipleAppInstances(args) || argumentProcessor.shouldShutDown()) {
- shutdownCurrentInstance();
- return;
- }
-
- // If not, start GUI
- SwingUtilities
- .invokeLater(() -> new JabRefGUI(argumentProcessor.getParserResults(), argumentProcessor.isBlank()));
- }
-
private static boolean handleMultipleAppInstances(String[] args) {
RemotePreferences remotePreferences = Globals.prefs.getRemotePreferences();
if (remotePreferences.useRemoteServer()) {
- Globals.REMOTE_LISTENER.open(new JabRefMessageHandler(), remotePreferences.getPort());
-
- if (!Globals.REMOTE_LISTENER.isOpen()) {
- // we are not alone, there is already a server out there, try to contact already running JabRef:
- if (new RemoteClient(remotePreferences.getPort()).sendCommandLineArguments(args)) {
- // We have successfully sent our command line options through the socket to another JabRef instance.
+ // Try to contact already running JabRef
+ RemoteClient remoteClient = new RemoteClient(remotePreferences.getPort());
+ if (remoteClient.ping()) {
+ // We are not alone, there is already a server out there, send command line arguments to other instance
+ if (remoteClient.sendCommandLineArguments(args)) {
// So we assume it's all taken care of, and quit.
LOGGER.info(Localization.lang("Arguments passed on to running JabRef instance. Shutting down."));
return false;
}
+ } else {
+ // We are alone, so we start the server
+ Globals.REMOTE_LISTENER.openAndStart(new JabRefMessageHandler(), remotePreferences.getPort());
}
- // we are alone, we start the server
- Globals.REMOTE_LISTENER.start();
}
return true;
}
private static void shutdownCurrentInstance() {
+ Globals.stopBackgroundTasks();
Globals.shutdownThreadPools();
- // needed to tell JavaFx to stop
Platform.exit();
+ System.exit(0);
}
private static void applyPreferences(JabRefPreferences preferences) {
diff --git a/src/main/java/org/jabref/cli/JabRefCLI.java b/src/main/java/org/jabref/cli/JabRefCLI.java
index ee79dee219d..83459c97e01 100644
--- a/src/main/java/org/jabref/cli/JabRefCLI.java
+++ b/src/main/java/org/jabref/cli/JabRefCLI.java
@@ -4,6 +4,7 @@
import org.jabref.Globals;
import org.jabref.logic.l10n.Localization;
+import org.jabref.model.database.BibDatabaseMode;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.DefaultParser;
@@ -173,7 +174,7 @@ private Options getOptions() {
options.addOption(
Option.builder("ib")
.longOpt("importBibtex")
- .desc(String.format("%s: %s[,importBibtex bibtexString]", Localization.lang("Import") + " " + Localization.BIBTEX, Localization.lang("filename")))
+ .desc(String.format("%s: %s[,importBibtex bibtexString]", Localization.lang("Import") + " " + BibDatabaseMode.BIBTEX.getFormattedName(), Localization.lang("filename")))
.hasArg()
.argName("FILE")
.build());
diff --git a/src/main/java/org/jabref/gui/AbstractController.java b/src/main/java/org/jabref/gui/AbstractController.java
deleted file mode 100644
index 53ad25c113b..00000000000
--- a/src/main/java/org/jabref/gui/AbstractController.java
+++ /dev/null
@@ -1,35 +0,0 @@
-package org.jabref.gui;
-
-import java.util.Objects;
-
-import javafx.fxml.FXML;
-import javafx.fxml.FXMLLoader;
-import javafx.stage.Stage;
-
-public class AbstractController {
-
- @FXML protected T viewModel;
- private Stage stage;
-
- /**
- * Gets the associated view model.
- *
- * Without this method the {@link FXMLLoader} is not able to resolve references in the fxml file of the form
- * text="${controller.viewModel.someProperty}"
- */
- public T getViewModel() {
- return viewModel;
- }
-
- /**
- * Returns the stage where this controller is displayed.
- * The stage can be used to e.g. close the dialog.
- */
- public Stage getStage() {
- return stage;
- }
-
- public void setStage(Stage stage) {
- this.stage = Objects.requireNonNull(stage);
- }
-}
diff --git a/src/main/java/org/jabref/gui/AbstractDialogView.java b/src/main/java/org/jabref/gui/AbstractDialogView.java
deleted file mode 100644
index d7880e3cd11..00000000000
--- a/src/main/java/org/jabref/gui/AbstractDialogView.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package org.jabref.gui;
-
-import java.util.function.Function;
-
-public abstract class AbstractDialogView extends AbstractView {
-
- public AbstractDialogView() {
- super();
- }
-
- public AbstractDialogView(Function injectionContext) {
- super(injectionContext);
- }
-
- public abstract void show();
-}
diff --git a/src/main/java/org/jabref/gui/AbstractView.java b/src/main/java/org/jabref/gui/AbstractView.java
deleted file mode 100644
index 055d5a69d0e..00000000000
--- a/src/main/java/org/jabref/gui/AbstractView.java
+++ /dev/null
@@ -1,49 +0,0 @@
-package org.jabref.gui;
-
-import java.util.Optional;
-import java.util.function.Function;
-
-import javafx.scene.Parent;
-import javafx.stage.Stage;
-
-import org.jabref.logic.l10n.Localization;
-
-import com.airhacks.afterburner.views.FXMLView;
-
-public class AbstractView extends FXMLView {
-
- public AbstractView() {
- this(f -> null);
- }
-
- public AbstractView(Function injectionContext) {
- super(injectionContext);
-
- // Set resource bundle to internal localizations
- bundle = Localization.getMessages();
- }
-
- @Override
- public Parent getView() {
- Parent view = super.getView();
-
- // Add our base css file
- view.getStylesheets().add(0, AbstractDialogView.class.getResource("Main.css").toExternalForm());
-
- // Notify controller about the stage, where it is displayed
- view.sceneProperty().addListener((observable, oldValue, newValue) -> {
- if (newValue != null && newValue.getWindow() instanceof Stage) {
- Stage stage = (Stage) newValue.getWindow();
- if (stage != null) {
- getController().ifPresent(controller -> controller.setStage(stage));
- }
- }
- });
- return view;
- }
-
- private Optional getController() {
- return Optional.ofNullable(presenterProperty.get()).map(
- presenter -> (AbstractController) presenter);
- }
-}
diff --git a/src/main/java/org/jabref/gui/Base.css b/src/main/java/org/jabref/gui/Base.css
new file mode 100644
index 00000000000..0032fb67f0b
--- /dev/null
+++ b/src/main/java/org/jabref/gui/Base.css
@@ -0,0 +1,838 @@
+.root {
+
+ /*
+ The theme color and some derived colors from it are used for icons, tab-headers, marking of selected inputs and
+ hover colors for the main menu. It generally defines the look of JabRef. The highlighting colors below should
+ work nicely with this base color
+ */
+
+ /* This theme is the original JabRef dark blue color */
+ -jr-theme: #50618F;
+ -jr-accent: #a3b7e6;
+ -jr-selected: -jr-accent;
+ -jr-hover: #0002;
+
+ /* The base gray. Most gray-tones in the application are derived from this color. */
+ -jr-base: #ebebeb;
+
+ -jr-white: #ffffff;
+ -jr-gray-0: #f2f2f2;
+ -jr-gray-1: #e6e6e6;
+ -jr-gray-2: #808080;
+ -jr-gray-3: #404040;
+ -jr-black: #000;
+
+ /* Highlights */
+ -jr-blue: #0abde3;
+ -jr-light-blue: #48dbfb;
+ -jr-purple: #f368e0;
+ -jr-light-purple: #ff9ff3;
+ -jr-green: #10ac84;
+ -jr-light-green: #1dd1a1;
+ -jr-red: #ee5253;
+ -jr-light-red: #ff6b6b;
+ -jr-yellow: #feca57;
+ -jr-orange: #ff9f43;
+
+ /* Some blueish greys */
+ -jr-blue-gray-1: #c8d6e5;
+ -jr-blue-gray-2: #8395a7;
+ -jr-blue-gray-3: #576574;
+ -jr-blue-gray-4: #222f3e;
+
+ /* Background specs */
+ -jr-background-alt: -fx-background;
+ -jr-text-area-background: derive(-jr-base, 80%);
+ -jr-search-background: -jr-text-area-background;
+ -jr-toolbar: derive(-jr-base, 46.4%);
+ -jr-menu-background: derive(-jr-base, 46.4%);
+ -jr-menu-background-active: -jr-hover;
+
+ -jr-menu-foreground: -fx-dark-text-color;
+ -jr-menu-item-foreground: -fx-dark-text-color;
+ -jr-menu-forground-active: -fx-dark-text-color;
+
+ -jr-drag-target: -jr-accent;
+
+ -jr-head-fg: -fx-text-inner-color;
+
+ /* All icons/text on toolbars */
+ -jr-theme-text: -jr-theme;
+
+ -jr-icon-background: transparent;
+ -jr-icon-background-active: #0001;
+ -jr-icon-background-armed: #0002;
+
+ /* Colors for messages and errors */
+ -jr-info: -jr-light-green;
+ -jr-warn: -jr-orange;
+ -jr-error: -jr-light-red;
+
+ /* Color for the small group view indicator for the number of hits */
+ -jr-group-hits-bg: derive(-jr-sidepane-background, -5%);
+ -jr-group-hits-fg: ladder(
+ -jr-group-hits-bg,
+ -fx-light-text-color 45%,
+ -fx-dark-text-color 46%,
+ -fx-dark-text-color 59%,
+ -fx-mid-text-color 60%
+ );
+
+ /* Specific color for general tooltips */
+ -jr-tooltip-bg: -jr-accent;
+ -jr-tooltip-fg: -jr-black;
+
+ /* Finally, some specific jr styles that depend on -fx definitions in *this* style */
+ -jr-sidepane-background: -jr-gray-0;
+ -jr-sidepane-header-background: -jr-gray-0;
+ -jr-sidepane-header-color: -jr-theme-text;
+
+ /* Specs for the scrollbars */
+ -jr-scrollbar-thumb: -fx-outer-border;
+ -jr-scrollbar-track: -fx-control-inner-background;
+
+ -jr-separator: derive(-fx-color, -5%);
+
+ -jr-search-text: -fx-text-base-color;
+
+ /*
+ Here are redefinitions of the default properties of modena. They should in principle all be derived from the
+ above colors. Goal should be to make as few as possible direct color-changes to elements and only do this for
+ very specific purposes.
+ */
+ -fx-base: -jr-base;
+
+ /* A very light grey used for the background of windows. See also
+ * -fx-text-background-color, which should be used as the -fx-text-fill
+ * value for text painted on top of backgrounds colored with -fx-background.
+ */
+ -fx-background: derive(-fx-base, 26.4%);
+
+ /* Used for the inside of text boxes, password boxes, lists, trees, and
+ * tables. See also -fx-text-inner-color, which should be used as the
+ * -fx-text-fill value for text painted on top of backgrounds colored
+ * with -fx-control-inner-background.
+ */
+ -fx-control-inner-background: derive(-fx-base, 95%);
+ /* Version of -fx-control-inner-background for alternative rows */
+ -fx-control-inner-background-alt: derive(-fx-control-inner-background, -2%);
+
+ /* One of these colors will be chosen based upon a ladder calculation
+ * that uses the brightness of a background color. Instead of using these
+ * colors directly as -fx-text-fill values, the sections in this file should
+ * use a derived color to match the background in use. See also:
+ *
+ * -fx-text-base-color for text on top of -fx-base, -fx-color, and -fx-body-color
+ * -fx-text-background-color for text on top of -fx-background
+ * -fx-text-inner-color for text on top of -fx-control-inner-color
+ * -fx-selection-bar-text for text on top of -fx-selection-bar
+ */
+ -fx-dark-text-color: -jr-black;
+ -fx-mid-text-color: -jr-gray-3;
+ -fx-light-text-color: -jr-white;
+
+ /* We overwrite accents -> make old stick out */
+ -fx-accent: red;
+
+ /* Default buttons color, this is similar to accent but more subtle */
+ -fx-default-button: derive(-jr-accent, 50%);
+
+ /* A bright blue for the focus indicator of objects. Typically used as the
+ * first color in -fx-background-color for the "focused" pseudo-class. Also
+ * typically used with insets of -1.4 to provide a glowing effect.
+ */
+ -fx-focus-color: -jr-accent;
+ -fx-faint-focus-color: derive(-jr-accent, 50%);
+
+ /* The color that is used in styling controls. The default value is based
+ * on -fx-base, but is changed by pseudoclasses to change the base color.
+ * For example, the "hover" pseudoclass will typically set -fx-color to
+ * -fx-hover-base (see below) and the "armed" pseudoclass will typically
+ * set -fx-color to -fx-pressed-base.
+ */
+ -fx-color: -fx-base;
+
+ -fx-hover-base: derive(-fx-base,30%);
+
+ /* A little darker than -fx-base and used as the -fx-color for the
+ * "armed" pseudoclass state.
+ *
+ * TODO: should this be renamed to -fx-armed-base?
+ */
+ -fx-pressed-base: derive(-fx-base,-6%);
+
+ /* A little darker than -fx-color and used to draw boxes around objects such
+ * as progress bars, scroll bars, scroll panes, trees, tables, and lists.
+ */
+ -fx-box-border: derive(-fx-color,-5%);
+
+ /* Darker than -fx-background and used to draw boxes around text boxes and
+ * password boxes.
+ */
+ -fx-text-box-border: derive(-fx-background, -15%);
+
+ /* Lighter than -fx-background and used to provide a small highlight when
+ * needed on top of -fx-background. This is never a shadow in Modena but
+ * keep -fx-shadow-highlight-color name to be compatible with Caspian.
+ */
+ -fx-shadow-highlight-color: rgba(255,255,255,0.07) 70%;
+
+ /* A gradient that goes from a little darker than -fx-color on the top to
+ * even more darker than -fx-color on the bottom. Typically is the second
+ * color in the -fx-background-color list as the small thin border around
+ * a control. It is typically the same size as the control (i.e., insets
+ * are 0).
+ */
+ -fx-outer-border: derive(-fx-color,-5%);
+
+ /* A gradient that goes from a bit lighter than -fx-color on the top to
+ * a little darker at the bottom. Typically is the third color in the
+ * -fx-background-color list as a thin highlight inside the outer border.
+ * Insets are typically 1.
+ */
+ -fx-inner-border: derive(-fx-color,65%);
+ -fx-inner-border-horizontal: derive(-fx-color,65%);
+ -fx-inner-border-bottomup: derive(-fx-color,65%);
+
+ /*-fx-inner-border: red;*/
+ /*-fx-inner-border-horizontal: green;*/
+ /*-fx-inner-border-bottomup: blue;*/
+
+ /* A gradient that goes from a little lighter than -fx-color at the top to
+ * a little darker than -fx-color at the bottom and is used to fill the
+ * body of many controls such as buttons.
+ */
+ -fx-body-color: derive(-fx-color,20%);
+ -fx-body-color-bottomup: derive(-fx-color,20%);
+ -fx-body-color-to-right: derive(-fx-color,20%);
+
+ /* The small thin light "shadow" for mark-like objects. Typically used in
+ * conjunction with -fx-mark-color with an insets of 1 0 -1 0. */
+ -fx-mark-highlight-color: transparent;
+ /*-fx-mark-highlight-color: derive(-fx-color,80%);*/
+
+ /* Background for items in list like things such as menus, lists, trees,
+ * and tables. */
+ -fx-selection-bar: -jr-accent;
+
+ /* Background color to use for selection of list cells etc. This is when
+ * the control doesn't have focus or the row of a previously selected item. */
+ -fx-selection-bar-non-focused: lightgrey;
+
+ /* The color to use as -fx-text-fill when painting text on top of
+ * backgrounds filled with -fx-selection-bar. */
+ -fx-selection-bar-text: -fx-text-background-color;
+
+ /* These are needed for Popup */
+ -fx-background-color: inherit;
+ -fx-background-radius: inherit;
+ -fx-background-insets: inherit;
+ -fx-padding: inherit;
+
+ /** Focus line for keyboard focus traversal on cell based controls */
+ -fx-cell-focus-inner-border: derive(-fx-selection-bar,30%);
+
+ -fx-focused-mark-color : -fx-focused-text-base-color;
+
+ /* Consistent size for headers of tab-pane and side-panels*/
+ -jr-header-height: 3em;
+}
+
+/*
+ * The base css file defining the style that is valid for every pane and dialog.
+ */
+.hyperlink {
+ -fx-padding: 0;
+ -fx-underline: false;
+ -fx-border-style: null;
+ -fx-border-color: null;
+}
+
+.hyperlink:visited {
+ -fx-text-fill: -jr-accent;
+}
+
+.glyph-icon {
+ /* This adjusts text alignment within the bounds of text nodes so that
+ the text is always vertically centered within the bounds. Based on
+ the cap height of the text. */
+ -fx-bounds-type: logical_vertical_center;
+}
+
+.tooltip {
+ -fx-background-color: -jr-tooltip-bg;
+ -fx-opacity: 95%;
+ -fx-text-fill: -jr-tooltip-fg;
+ -fx-font-size: 1em;
+}
+
+.tooltip > TextFlow > Text {
+ -fx-font-size: 1em;
+}
+
+.tooltip > TextFlow > .tooltip-text-bold {
+ -fx-font-weight: bold;
+}
+
+.tooltip > TextFlow > .tooltip-text-italic {
+ -fx-font-style: italic;
+}
+
+.tooltip > TextFlow > .tooltip-text-monospaced {
+ -fx-font-family: monospace;
+}
+
+.flatButtonNoSpaceBottom,
+.flatButtonNoSpaceTop,
+.flatButton {
+ -fx-shadow-highlight-color: black;
+ -fx-outer-border: red;
+ -fx-inner-border: purple;
+ -fx-faint-focus-color: transparent;
+ -fx-background-color: -jr-icon-background;
+ -fx-text-background-color: blue;
+ -fx-padding: 0.5em;
+}
+
+.flatButtonNoSpaceBottom:hover,
+.flatButtonNoSpaceTop:hover,
+.flatButton:hover {
+ -fx-background-color: -jr-icon-background-active;
+}
+
+.flatButtonNoSpaceBottom:armed,
+.flatButtonNoSpaceTop:armed,
+.flatButton:armed {
+ -fx-background-color: -jr-icon-background-armed;
+}
+
+.flatButton:selected {
+ -fx-background-color: -jr-icon-background-active;
+ -fx-text-fill: white;
+ -fx-fill: white;
+}
+
+.flatButtonNoSpaceBottom {
+ -fx-padding: 0.5em 0.5em -0.1em 0.5em;
+}
+
+.flatButtonNoSpaceTop {
+ -fx-padding: -0.1em 0.5em 0.5em 0.5em;
+}
+
+.button,
+.toggle-button,
+.radio-button > .radio,
+.check-box > .box,
+.menu-button,
+.choice-box,
+.color-picker.split-button > .color-picker-label,
+.combo-box-base,
+.combo-box-base:editable > .arrow-button {
+ -fx-background-color: transparent;
+ -fx-background-insets: 0;
+ -fx-background-radius: 0;
+ -fx-text-fill: -fx-text-base-color;
+}
+
+.menu-bar {
+ -fx-background-color: -jr-menu-background;
+ -fx-background-insets: 0;
+ -fx-background-radius: 0;
+}
+
+.menu-bar > .container > .menu-button > .label {
+ -fx-padding: 0.41777em 0.41777em 0.41777em 0.41777em;
+}
+
+.menu-bar > .menu {
+ -fx-padding: 0.0em 0.666667em 0.0em 0.666667em;
+}
+
+.menu-item {
+ -fx-padding: 0.5em 0.41777em 0.5em 0.41777em;
+}
+
+.tab-pane {
+ -fx-open-tab-animation: NONE;
+ -fx-close-tab-animation: NONE;
+}
+
+.tab-pane > .tab-header-area > .headers-region > .tab {
+ -fx-background-insets: 0;
+ -fx-background-radius: 0;
+ -fx-background-color: -jr-background-alt;
+ -fx-border-color: -fx-outer-border;
+ -fx-border-width: 0.5 0.5 0.5 0.5;
+ -fx-padding: 0.3em 0.9em 0.3em 0.9em;
+ -fx-pref-height: -jr-header-height;
+}
+
+.tab-pane > .tab-header-area > .headers-region > .tab .tab-label {
+ -fx-text-fill: -fx-mid-text-color;
+}
+
+.tab-pane > .tab-header-area > .headers-region > .control-buttons-tab {
+ -fx-border-color: -jr-theme;
+ -fx-fill: -jr-theme-text;
+ -fx-text-fill: -jr-theme-text;
+}
+
+.tab-pane > .tab-header-area > .headers-region > .tab:selected {
+ -fx-background-color: -fx-control-inner-background;
+ -fx-border-color: -jr-theme;
+ -fx-border-width: 3 0 0 0;
+}
+
+.tab-pane > .tab-header-area > .headers-region > .tab:selected .tab-label{
+ -fx-fill: -jr-theme-text;
+ -fx-text-fill: -jr-theme-text;
+}
+
+.tab-pane > .tab-header-area > .headers-region > .tab:selected .tab-close-button {
+ -fx-background-color: -jr-theme-text;
+}
+
+.tab-pane > .tab-header-area > .headers-region > .tab:selected .glyph-icon {
+ -fx-text-fill: -fx-mid-text-color;
+ -fx-fill: -fx-mid-text-color;
+}
+
+.tab-pane:focused > .tab-header-area > .headers-region > .tab:selected .focus-indicator {
+ -fx-border-width: 0;
+ -fx-border-insets: 0;
+ -fx-border-radius: 0;
+}
+
+.tab-pane > .tab-header-area > .tab-header-background {
+ -fx-background-color: -jr-background-alt;
+}
+
+.tab-pane > .tab-header-area > .headers-region > .tab .glyph-icon {
+ -glyph-size: 13px;
+ -fx-text-fill: -fx-mid-text-color;
+ -fx-fill: -fx-mid-text-color;
+}
+
+.tab-pane > .tab-header-area > .headers-region > .tab:selected .glyph-icon {
+ -fx-text-fill: -jr-theme-text;
+ -fx-fill: -jr-theme-text;
+}
+
+.tab-pane > .tab-header-area {
+ -fx-padding: 0 0 0 0;
+}
+
+.table-view {
+ -fx-background-insets: 0;
+ -fx-padding: 0;
+}
+
+.table-view:focused {
+ -fx-background-insets: 0;
+}
+
+.split-pane > .split-pane-divider {
+ -fx-background-color: -jr-sidepane-background;
+ -fx-padding: 0 .5 0 .5;
+}
+
+.table-row-cell:hover,
+.tree-table-row-cell:hover {
+ -fx-background-color: -jr-hover;
+ -fx-text-fill: -fx-focused-text-base-color;
+ -fx-fill: -fx-focused-text-base-color;
+}
+
+.table-row-cell:hover,
+.tree-table-row-cell:selected > .tree-table-cell > .glyph-icon {
+ -fx-fill: white;
+ -fx-text-fill: white;
+}
+
+.table-view > .virtual-flow > .clipped-container > .sheet > .table-row-cell .table-cell:selected,
+.tree-table-view > .virtual-flow > .clipped-container > .sheet > .tree-table-row-cell .tree-table-cell:selected {
+ -fx-border-color: transparent;
+ -fx-background-insets: 0;
+}
+
+.scroll-pane:focused,
+.split-pane:focused,
+.list-view:focused,
+.tree-view:focused,
+.table-view:focused,
+.tree-table-view:focused,
+.html-editor:contains-focus {
+ -fx-background-color: -fx-outer-border, -fx-control-inner-background;
+ -fx-background-insets: 0, 1;
+ -fx-background-radius: 0, 0;
+}
+
+/* Selected rows */
+.list-view:focused > .virtual-flow > .clipped-container > .sheet > .list-cell:filled:selected,
+.tree-view:focused > .virtual-flow > .clipped-container > .sheet > .tree-cell:filled:selected,
+.table-view:focused > .virtual-flow > .clipped-container > .sheet > .table-row-cell:filled:selected,
+.tree-table-view:focused > .virtual-flow > .clipped-container > .sheet > .tree-table-row-cell:filled:selected,
+.table-view:focused > .virtual-flow > .clipped-container > .sheet > .table-row-cell .table-cell:selected,
+.tree-table-view:focused > .virtual-flow > .clipped-container > .sheet > .tree-table-row-cell .tree-table-cell:selected {
+ -fx-background: -jr-selected;
+ -fx-background-color: -jr-selected;
+ -fx-table-cell-border-color: transparent;
+}
+
+/* Selected when control is not focused */
+.list-cell:filled:selected,
+.tree-cell:filled:selected,
+.table-row-cell:filled:selected,
+.tree-table-row-cell:filled:selected,
+.table-row-cell:filled > .table-cell:selected,
+.tree-table-row-cell:filled > .tree-table-cell:selected {
+ -fx-background: -jr-selected;
+ -fx-background-color: -jr-selected;
+ -fx-table-cell-border-color: transparent;
+ -fx-fill: white;
+ -fx-text-fill: green;
+}
+
+.combo-box-base {
+ -fx-background-color: -fx-outer-border, -fx-control-inner-background;
+ -fx-background-insets: 0, 1;
+ -fx-background-radius: 0, 0;
+}
+
+.combo-box > .list-cell {
+ -fx-background-color: -fx-outer-border, -fx-control-inner-background;
+ -fx-background-insets: 0, 1 0 1 1;
+ -fx-text-fill: -fx-text-base-color;
+}
+
+.combo-box-popup > .list-view {
+ -fx-background-color: -fx-control-inner-background;
+ -fx-background-insets: 0;
+ -fx-effect: null;
+}
+
+.combo-box-popup > .list-view > .virtual-flow > .clipped-container > .sheet > .list-cell {
+ -fx-padding: 4 0 4 5;
+ /* No alternate highlighting */
+ -fx-background: -fx-control-inner-background;
+}
+
+.combo-box-popup > .list-view > .virtual-flow > .clipped-container > .sheet > .list-cell:filled:hover {
+ -fx-background: -fx-control-inner-background;
+ -fx-background-color: -jr-hover;
+}
+
+.combo-box-popup > .list-view > .virtual-flow > .clipped-container > .sheet > .list-cell:filled:selected,
+.combo-box-popup > .list-view > .virtual-flow > .clipped-container > .sheet > .list-cell:filled:selected:hover {
+ -fx-background: -fx-control-inner-background;
+ -fx-background-insets: 0;
+}
+
+.combo-box-popup > .list-view > .placeholder > .label {
+ -fx-text-fill: -fx-text-base-color;
+}
+
+.scroll-pane,
+.split-pane {
+ -fx-background-insets: 0, 0;
+ -fx-padding: 0;
+}
+
+.text-input {
+ -fx-background-color: -fx-outer-border, -fx-control-inner-background;
+ -fx-background-insets: 0, 1;
+ -fx-prompt-text-fill: -fx-mid-text-color;
+}
+
+.text-input:focused {
+ -fx-background-color: -jr-accent, -fx-control-inner-background;
+ -fx-background-insets: 0, 2;
+}
+
+.text-area {
+ -fx-background-color: -fx-control-inner-background;
+}
+
+.text-area .content {
+ -fx-background-color: -fx-outer-border, -fx-control-inner-background;
+ -fx-background-insets: 0, 1;
+ -fx-padding: 0.333333em 0.583em 0.333333em 0.583em;
+ -fx-background-radius: 0;
+}
+
+.text-area:focused .content {
+ -fx-background-color: -jr-accent, -fx-control-inner-background;
+ -fx-background-insets: 0, 2;
+ -fx-background-radius: 0;
+}
+
+.text-area > .scroll-pane > .corner {
+ -fx-background-radius: 0 0 0 0;
+ -fx-background-color: -fx-background;
+}
+
+.combo-box-base:editable > .text-field,
+.date-picker > .text-field {
+ -fx-background-color: -fx-outer-border, -fx-control-inner-background;
+ -fx-background-insets: 0, 1;
+ -fx-background-radius: 0;
+}
+
+.combo-box-base:editable:focused > .text-field,
+.combo-box-base:editable > .text-field:focused,
+.date-picker > .text-field:focused {
+ -fx-background-color: -jr-accent, -fx-control-inner-background;
+ -fx-background-insets: 0, 2;
+ -fx-background-radius: 0;
+}
+
+.date-picker:focused > .text-field {
+ -fx-background-color: -fx-control-inner-background;
+ -fx-background-insets: 0;
+}
+
+.scroll-bar {
+ -fx-background-color: transparent;
+ -fx-opacity: 0;
+}
+
+.scroll-bar:horizontal .track,
+.scroll-bar:vertical .track {
+ -fx-background-color: -jr-scrollbar-track;
+ -fx-opacity: 0.2;
+ -fx-background-radius: 0em;
+}
+
+.scroll-bar:horizontal .thumb,
+.scroll-bar:vertical .thumb {
+ -fx-background-color: -jr-scrollbar-thumb;
+ -fx-background-insets: 0, 0, 0;
+ -fx-background-radius: 0em;
+}
+
+.scroll-bar .thumb:hover,
+.scroll-bar .thumb:pressed {
+ -fx-background-color: derive(-jr-scrollbar-thumb, -20%);
+}
+
+/* Hide increment and decrement buttons */
+.scroll-bar > .increment-button,
+.scroll-bar > .decrement-button {
+ -fx-background-color: null;
+ -fx-background-radius: 0;
+ -fx-background-insets: 0;
+ -fx-padding: 0;
+}
+
+/* Hide increment and decrement arrows */
+.scroll-bar:horizontal > .decrement-button > .decrement-arrow,
+.scroll-bar:horizontal > .increment-button > .increment-arrow,
+.scroll-bar:vertical > .decrement-button > .decrement-arrow,
+.scroll-bar:vertical > .increment-button > .increment-arrow {
+ -fx-background-color: null;
+ -fx-background-radius: 0;
+ -fx-background-insets: 0;
+ -fx-shape: null;
+ -fx-padding: 0;
+}
+
+/* Need some padding since otherwise no scroll-bar is displayed at all */
+.scroll-bar:horizontal > .decrement-button > .decrement-arrow {
+ -fx-padding: 0.333em 0.167em 0.333em 0.167em; /* 4 2 4 2 */
+}
+
+.scroll-bar:vertical > .decrement-button > .decrement-arrow {
+ -fx-padding: 0em 0.333em 0em 0.333em; /* 2 4 2 4 */
+}
+
+/* Only show scrollbars for hovered elements */
+.list-view:hover .scroll-bar,
+.tree-view:hover .scroll-bar,
+.table-view:hover .scroll-bar,
+.tree-table-view:hover .scroll-bar,
+.text-input:hover .scroll-bar {
+ -fx-opacity: 1;
+}
+
+.sidePaneComponentHeader {
+ -fx-background-color: -jr-sidepane-header-background;
+ -fx-padding: 0.3em 0.9em 0.3em 0.9em;
+ -fx-pref-height: -jr-header-height;
+}
+
+.sidePaneComponentHeader > .label {
+ -fx-text-fill: -jr-sidepane-header-color;
+ -fx-font-weight: bold;
+ -fx-padding: 0.3em 0.9em 0.3em 0.9em;
+}
+
+.sidePaneComponentHeader .glyph-icon{
+ -fx-fill: -jr-sidepane-header-color;
+ -fx-text-fill: -jr-sidepane-header-color;
+ -fx-font-size: 16px;
+}
+
+.mainMenu {
+ -fx-background-color: -jr-menu-background;
+ -fx-background-insets: 0;
+}
+
+.menu-bar > .container {
+ -fx-border-width: 0;
+}
+
+.menu-bar > .container > .menu-button:hover,
+.menu-bar > .container > .menu-button:focused,
+.menu-bar > .container > .menu-button:showing {
+ -fx-background-color: -jr-menu-background-active;
+ -fx-background: -jr-menu-background-active;
+}
+
+.menu-bar > .container > .menu-button:hover > .label,
+.menu-bar > .container > .menu-button:focused > .label,
+.menu-bar > .container > .menu-button:showing > .label {
+ -fx-text-fill: -jr-menu-forground-active;
+}
+
+.mainMenu > .container > .menu-button > .label {
+ -fx-text-fill: -jr-menu-foreground;
+}
+
+.menu-item > .label {
+ -fx-text-fill: -jr-menu-item-foreground;
+}
+
+.menu-item:focused {
+ -fx-text-fill: -jr-menu-background-active;
+ -fx-background: -jr-menu-background-active;
+ -fx-background-color: -jr-menu-background-active;
+}
+
+.menu-item:focused > .label {
+ -fx-text-fill: -jr-menu-forground-active;
+}
+
+.menu-item .glyph-icon {
+ -fx-fill: -jr-menu-item-foreground;
+ -fx-text-fill: -jr-menu-item-foreground;
+}
+
+.menu-item:focused .glyph-icon {
+ -fx-fill: -jr-menu-forground-active;
+ -fx-text-fill: -jr-menu-forground-active;
+}
+
+.context-menu {
+ -fx-border-color: -fx-outer-border;
+ -fx-border-width: 1;
+}
+
+.separator:horizontal .line {
+ -fx-border-color: -jr-separator;
+ -fx-border-width: 0;
+ -fx-border-insets: 1 0 0 0;
+}
+
+.separator:vertical .line {
+ -fx-border-color: -jr-separator;
+ -fx-border-width: 1;
+ -fx-border-insets: 0 0 0 1;
+}
+
+.mainToolbar {
+ -fx-background-color: -jr-toolbar;
+ -fx-border-color: derive(-jr-toolbar, 50%);
+ -fx-border-width: 0;
+}
+
+.mainToolbar .glyph-icon {
+ -fx-font-size: 1.7em;
+ -fx-fill: -jr-theme-text;
+ -fx-text-fill: -jr-theme-text;
+}
+
+.mainToolbar .search-field {
+ -fx-background-color: -jr-search-background;
+ -fx-border-width: 1;
+ -fx-border-color: -jr-separator;
+ -fx-border-radius: 2;
+ -fx-fill: -jr-search-text;
+}
+
+.mainToolbar .search-field .glyph-icon {
+ -fx-fill: -jr-search-text;
+ -fx-text-fill: -jr-search-text;
+}
+
+/* The little arrow that shows up when not all tool-bar icons fit into the tool-bar.
+We want to have a look that matches our icons in the tool-bar */
+.mainToolbar .tool-bar-overflow-button > .arrow {
+ -fx-background-color: -jr-theme-text;
+}
+
+.mainToolbar .tool-bar-overflow-button:hover > .arrow {
+ -fx-background-color: -fx-mark-highlight-color, derive(-jr-theme-text, -30%);
+}
+
+.table-view {
+ -fx-border-width: 0;
+ -fx-padding: 0;
+ -fx-border-insets: 0;
+ -fx-table-cell-border-color: transparent;
+}
+
+.table-view .column-header-background {
+ -fx-background-color: -fx-control-inner-background;
+ -fx-border-width: 0;
+}
+
+.table-view .column-header-background:hover {
+ -fx-background-color: -fx-outer-border;
+}
+
+.table-view .column-header,
+.table-view .filler {
+ -fx-background-color: transparent, -fx-control-inner-background;
+ -fx-background-insets: 0, 0 0.02em 0 0.02em;
+ -fx-font-weight: bold;
+ -fx-size: 3em;
+ -fx-border-width: 0 0 1 0;
+ -fx-border-color: -fx-outer-border;
+}
+
+.table-view .column-header > .label {
+ -fx-padding: 0 1em 0 1em;
+ -fx-alignment: center-left;
+ -fx-text-fill: -jr-head-fg;
+}
+
+.table-view .column-header .glyph-icon {
+ -fx-alignment: baseline-center;
+ -fx-text-fill: -jr-head-fg;
+ -fx-fill: -jr-head-fg;
+}
+
+.table-cell, .table-cell .glyph-icon{
+ -fx-padding: 0.5em 1em 0.5em 1em;
+ -fx-cell-size: 4.0em;
+ -fx-text-fill: -fx-text-background-color;
+ -fx-fill: -fx-text-background-color;
+}
+
+/* Improve the context menu of the main toolbar, when icons don't fit and you have to press the little arrow to see them */
+.mainToolbar .context-menu .glyph-icon {
+ -fx-fill: -jr-theme-text;
+}
+
+.mainToolbar .context-menu .glyph-icon:hover {
+ -fx-fill: -jr-theme-active;
+ -fx-text-fill: -jr-theme-active;
+ -fx-background-color: -jr-icon-background-active; /* TODO: This has no effect */
+}
+
+
+/* This is awful, but I don't know a better way*/
+.mainToolbar .context-menu * {
+ -fx-background-color: -fx-control-inner-background;
+}
diff --git a/src/main/java/org/jabref/gui/BasePanel.java b/src/main/java/org/jabref/gui/BasePanel.java
index b201f5fb8ef..9dcfc93886a 100644
--- a/src/main/java/org/jabref/gui/BasePanel.java
+++ b/src/main/java/org/jabref/gui/BasePanel.java
@@ -1,14 +1,10 @@
package org.jabref.gui;
-import java.awt.BorderLayout;
import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.ClipboardOwner;
import java.awt.datatransfer.StringSelection;
import java.awt.datatransfer.Transferable;
-import java.awt.event.ActionEvent;
-import java.awt.event.KeyAdapter;
-import java.awt.event.KeyEvent;
import java.io.File;
import java.io.IOException;
import java.io.StringReader;
@@ -17,7 +13,6 @@
import java.nio.charset.UnsupportedCharsetException;
import java.nio.file.Path;
import java.util.ArrayList;
-import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
@@ -28,20 +23,19 @@
import java.util.Set;
import java.util.stream.Collectors;
-import javax.swing.AbstractAction;
-import javax.swing.BorderFactory;
-import javax.swing.JComponent;
import javax.swing.JOptionPane;
-import javax.swing.JPanel;
-import javax.swing.JSplitPane;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import javax.swing.undo.CannotRedoException;
import javax.swing.undo.CannotUndoException;
-import javafx.application.Platform;
-import javafx.embed.swing.JFXPanel;
-import javafx.scene.Scene;
+import javafx.beans.binding.Bindings;
+import javafx.geometry.Orientation;
+import javafx.scene.Node;
+import javafx.scene.control.ScrollPane;
+import javafx.scene.control.SplitPane;
+import javafx.scene.layout.AnchorPane;
+import javafx.scene.layout.StackPane;
import org.jabref.Globals;
import org.jabref.JabRefExecutorService;
@@ -57,33 +51,25 @@
import org.jabref.gui.collab.DatabaseChangeMonitor;
import org.jabref.gui.collab.FileUpdatePanel;
import org.jabref.gui.contentselector.ContentSelectorDialog;
-import org.jabref.gui.customjfx.CustomJFXPanel;
import org.jabref.gui.desktop.JabRefDesktop;
import org.jabref.gui.entryeditor.EntryEditor;
import org.jabref.gui.exporter.ExportToClipboardAction;
import org.jabref.gui.exporter.SaveDatabaseAction;
import org.jabref.gui.externalfiles.FindFullTextAction;
-import org.jabref.gui.externalfiles.SynchronizeFileField;
-import org.jabref.gui.externalfiles.WriteXMPAction;
+import org.jabref.gui.externalfiles.WriteXMPActionWorker;
import org.jabref.gui.externalfiletype.ExternalFileMenuItem;
import org.jabref.gui.externalfiletype.ExternalFileType;
import org.jabref.gui.externalfiletype.ExternalFileTypes;
-import org.jabref.gui.fieldeditors.FieldEditor;
-import org.jabref.gui.filelist.AttachFileAction;
import org.jabref.gui.filelist.FileListEntry;
import org.jabref.gui.filelist.FileListTableModel;
import org.jabref.gui.groups.GroupAddRemoveDialog;
import org.jabref.gui.importer.actions.AppendDatabaseAction;
import org.jabref.gui.journals.AbbreviateAction;
import org.jabref.gui.journals.UnabbreviateAction;
-import org.jabref.gui.keyboard.KeyBinding;
import org.jabref.gui.maintable.MainTable;
import org.jabref.gui.maintable.MainTableDataModel;
-import org.jabref.gui.maintable.MainTableFormat;
-import org.jabref.gui.maintable.MainTableSelectionListener;
import org.jabref.gui.mergeentries.MergeEntriesDialog;
import org.jabref.gui.mergeentries.MergeWithFetchedEntryAction;
-import org.jabref.gui.plaintextimport.TextInputDialog;
import org.jabref.gui.specialfields.SpecialFieldDatabaseChangeListener;
import org.jabref.gui.specialfields.SpecialFieldValueViewModel;
import org.jabref.gui.specialfields.SpecialFieldViewModel;
@@ -96,11 +82,9 @@
import org.jabref.gui.undo.UndoableRemoveEntry;
import org.jabref.gui.util.DefaultTaskExecutor;
import org.jabref.gui.util.FileDialogConfiguration;
-import org.jabref.gui.util.component.CheckBoxMessage;
import org.jabref.gui.worker.AbstractWorker;
import org.jabref.gui.worker.CallBack;
import org.jabref.gui.worker.CitationStyleToClipboardWorker;
-import org.jabref.gui.worker.MarkEntriesAction;
import org.jabref.gui.worker.SendAsEMailAction;
import org.jabref.logic.bibtexkeypattern.BibtexKeyGenerator;
import org.jabref.logic.citationstyle.CitationStyleCache;
@@ -135,6 +119,7 @@
import org.jabref.model.entry.EntryType;
import org.jabref.model.entry.FieldName;
import org.jabref.model.entry.InternalBibtexFields;
+import org.jabref.model.entry.LinkedFile;
import org.jabref.model.entry.event.EntryChangedEvent;
import org.jabref.model.entry.event.EntryEventSource;
import org.jabref.model.entry.specialfields.SpecialField;
@@ -146,16 +131,15 @@
import com.google.common.eventbus.Subscribe;
import com.jgoodies.forms.builder.FormBuilder;
import com.jgoodies.forms.layout.FormLayout;
+import org.fxmisc.easybind.EasyBind;
+import org.fxmisc.easybind.Subscription;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-public class BasePanel extends JPanel implements ClipboardOwner {
+public class BasePanel extends StackPane implements ClipboardOwner {
private static final Logger LOGGER = LoggerFactory.getLogger(BasePanel.class);
- // Divider size for BaseFrame split pane. 0 means non-resizable.
- private static final int SPLIT_PANE_DIVIDER_SIZE = 4;
-
private final BibDatabaseContext bibDatabaseContext;
private final MainTableDataModel tableModel;
@@ -166,22 +150,20 @@ public class BasePanel extends JPanel implements ClipboardOwner {
// The undo manager.
private final UndoAction undoAction = new UndoAction();
private final RedoAction redoAction = new RedoAction();
- private final CountingUndoManager undoManager = new CountingUndoManager();
- private final List previousEntries = new ArrayList<>();
- private final List nextEntries = new ArrayList<>();
+ private final CountingUndoManager undoManager;
// Keeps track of the string dialog if it is open.
- private final Map actions = new HashMap<>();
+ private final Map actions = new HashMap<>();
private final SidePaneManager sidePaneManager;
private final PreviewPanel preview;
- private final JFXPanel previewContainer;
+ private final BasePanelPreferences preferences;
+ private final ExternalFileTypes externalFileTypes;
+ private final EntryEditor entryEditor;
+ private MainTable mainTable;
// To contain instantiated entry editors. This is to save time
// As most enums, this must not be null
private BasePanelMode mode = BasePanelMode.SHOWING_NOTHING;
- private final EntryEditor entryEditor;
- private final JFXPanel entryEditorContainer;
- private MainTableSelectionListener selectionListener;
- private JSplitPane splitPane;
+ private SplitPane splitPane;
private boolean saving;
// AutoCompleter used in the search bar
@@ -189,42 +171,36 @@ public class BasePanel extends JPanel implements ClipboardOwner {
private boolean baseChanged;
private boolean nonUndoableChange;
// Used to track whether the base has changed since last save.
- private MainTable mainTable;
- private MainTableFormat tableFormat;
private BibEntry showing;
- // Variable to prevent erroneous update of back/forward histories at the time
- // when a Back or Forward operation is being processed:
- private boolean backOrForwardInProgress;
- // in switching between entries.
- private PreambleEditor preambleEditor;
- // Keeps track of the preamble dialog if it is open.
+
private StringDialog stringDialog;
private SuggestionProviders suggestionProviders;
+ @SuppressWarnings({"FieldCanBeLocal", "unused"}) private Subscription dividerPositionSubscription;
+
// the query the user searches when this BasePanel is active
private Optional currentSearchQuery = Optional.empty();
private Optional changeMonitor = Optional.empty();
+ private final DialogService dialogService;
- public BasePanel(JabRefFrame frame, BibDatabaseContext bibDatabaseContext) {
- Objects.requireNonNull(frame);
- Objects.requireNonNull(bibDatabaseContext);
+ public BasePanel(JabRefFrame frame, BasePanelPreferences preferences, BibDatabaseContext bibDatabaseContext, ExternalFileTypes externalFileTypes) {
+ this.preferences = Objects.requireNonNull(preferences);
+ this.frame = Objects.requireNonNull(frame);
+ this.bibDatabaseContext = Objects.requireNonNull(bibDatabaseContext);
+ this.externalFileTypes = Objects.requireNonNull(externalFileTypes);
+ this.undoManager = frame.getUndoManager();
+ this.dialogService = frame.getDialogService();
- this.bibDatabaseContext = bibDatabaseContext;
bibDatabaseContext.getDatabase().registerListener(this);
bibDatabaseContext.getMetaData().registerListener(this);
this.sidePaneManager = frame.getSidePaneManager();
- this.frame = frame;
this.tableModel = new MainTableDataModel(getBibDatabaseContext());
citationStyleCache = new CitationStyleCache(bibDatabaseContext);
annotationCache = new FileAnnotationCache(bibDatabaseContext, Globals.prefs.getFileDirectoryPreferences());
- this.preview = new PreviewPanel(this, getBibDatabaseContext());
- DefaultTaskExecutor.runInJavaFXThread(() -> frame().getGlobalSearchBar().getSearchQueryHighlightObservable().addSearchListener(preview));
- this.previewContainer = CustomJFXPanel.wrap(new Scene(preview));
-
setupMainPanel();
setupActions();
@@ -234,6 +210,8 @@ public BasePanel(JabRefFrame frame, BibDatabaseContext bibDatabaseContext) {
// ensure that at each addition of a new entry, the entry is added to the groups interface
this.bibDatabaseContext.getDatabase().registerListener(new GroupTreeListener());
+ // ensure that all entry changes mark the panel as changed
+ this.bibDatabaseContext.getDatabase().registerListener(this);
Optional file = bibDatabaseContext.getDatabaseFile();
if (file.isPresent()) {
@@ -250,36 +228,10 @@ public BasePanel(JabRefFrame frame, BibDatabaseContext bibDatabaseContext) {
this.getDatabase().registerListener(new UpdateTimestampListener(Globals.prefs));
- entryEditor = new EntryEditor(this);
- entryEditorContainer = setupEntryEditor(entryEditor);
-
- }
-
- private static JFXPanel setupEntryEditor(EntryEditor entryEditor) {
- JFXPanel container = CustomJFXPanel.wrap(new Scene(entryEditor));
- container.addKeyListener(new KeyAdapter() {
+ this.entryEditor = new EntryEditor(this, preferences.getEntryEditorPreferences(), Globals.getFileUpdateMonitor(), dialogService);
- @Override
- public void keyPressed(KeyEvent e) {
-
- //We need to consume this event here to prevent the propgation of keybinding events back to the JFrame
- Optional keyBinding = Globals.getKeyPrefs().mapToKeyBinding(e);
- if (keyBinding.isPresent()) {
- switch (keyBinding.get()) {
- case CUT:
- case COPY:
- case PASTE:
- case DELETE_ENTRY:
- case SELECT_ALL:
- e.consume();
- break;
- default:
- //do nothing
- }
- }
- }
- });
- return container;
+ this.preview = new PreviewPanel(this, getBibDatabaseContext(), preferences.getKeyBindings(), preferences.getPreviewPreferences(), dialogService);
+ frame().getGlobalSearchBar().getSearchQueryHighlightObservable().addSearchListener(preview);
}
public static void runWorker(AbstractWorker worker) throws Exception {
@@ -304,8 +256,7 @@ public static void runWorker(AbstractWorker worker) throws Exception {
@Subscribe
public void listen(BibDatabaseContextChangedEvent event) {
- SwingUtilities.invokeLater(() -> this.markBaseChanged());
-
+ this.markBaseChanged();
}
/**
@@ -337,9 +288,7 @@ public String getTabTitle() {
}
}
} else if (databaseLocation == DatabaseLocation.SHARED) {
- title.append(
- this.bibDatabaseContext.getDBMSSynchronizer().getDBName() + " [" + Localization.lang("shared")
- + "]");
+ title.append(this.bibDatabaseContext.getDBMSSynchronizer().getDBName() + " [" + Localization.lang("shared") + "]");
}
return title.toString();
@@ -372,32 +321,23 @@ private void setupActions() {
actions.put(Actions.UNDO, undoAction);
actions.put(Actions.REDO, redoAction);
- actions.put(Actions.FOCUS_TABLE, (BaseAction) () -> {
- mainTable.requestFocus();
- });
-
// The action for opening an entry editor.
- actions.put(Actions.EDIT, (BaseAction) selectionListener::editSignalled);
+ actions.put(Actions.EDIT, (BaseAction) this::showAndEdit);
// The action for saving a database.
actions.put(Actions.SAVE, saveAction);
actions.put(Actions.SAVE_AS, (BaseAction) saveAction::saveAs);
- actions.put(Actions.SAVE_SELECTED_AS, new SaveSelectedAction(SavePreferences.DatabaseSaveType.ALL));
-
- actions.put(Actions.SAVE_SELECTED_AS_PLAIN,
- new SaveSelectedAction(SavePreferences.DatabaseSaveType.PLAIN_BIBTEX));
+ actions.put(Actions.SAVE_SELECTED_AS_PLAIN, new SaveSelectedAction(SavePreferences.DatabaseSaveType.PLAIN_BIBTEX));
// The action for copying selected entries.
- actions.put(Actions.COPY, (BaseAction) () -> copy());
+ actions.put(Actions.COPY, (BaseAction) mainTable::copy);
actions.put(Actions.PRINT_PREVIEW, new PrintPreviewAction());
- actions.put(Actions.CUT, (BaseAction) this::cut);
+ actions.put(Actions.CUT, (BaseAction) mainTable::cut);
- //when you modify this action be sure to adjust Actions.CUT,
- //they are the same except of the Localization, delete confirmation and Actions.COPY call
actions.put(Actions.DELETE, (BaseAction) () -> delete(false));
// The action for pasting entries or cell contents.
@@ -406,21 +346,9 @@ private void setupActions() {
// This allows you to (a) paste entire bibtex entries from a text editor, web browser, etc
// (b) copy and paste entries between multiple instances of JabRef (since
// only the text representation seems to get as far as the X clipboard, at least on my system)
- actions.put(Actions.PASTE, (BaseAction) () -> paste());
-
- actions.put(Actions.SELECT_ALL, (BaseAction) mainTable::selectAll);
+ actions.put(Actions.PASTE, (BaseAction) mainTable::paste);
- // The action for opening the preamble editor
- actions.put(Actions.EDIT_PREAMBLE, (BaseAction) () -> {
- if (preambleEditor == null) {
- PreambleEditor form = new PreambleEditor(frame, BasePanel.this, bibDatabaseContext.getDatabase());
- form.setLocationRelativeTo(frame);
- form.setVisible(true);
- preambleEditor = form;
- } else {
- preambleEditor.setVisible(true);
- }
- });
+ actions.put(Actions.SELECT_ALL, (BaseAction) mainTable.getSelectionModel()::selectAll);
// The action for opening the string editor
actions.put(Actions.EDIT_STRINGS, (BaseAction) () -> {
@@ -433,12 +361,6 @@ private void setupActions() {
}
});
- actions.put(FindUnlinkedFilesDialog.ACTION_COMMAND, (BaseAction) () -> {
- final FindUnlinkedFilesDialog dialog = new FindUnlinkedFilesDialog(frame, frame, BasePanel.this);
- dialog.setLocationRelativeTo(frame);
- dialog.setVisible(true);
- });
-
// The action for auto-generating keys.
actions.put(Actions.MAKE_KEY, new AbstractWorker() {
@@ -453,12 +375,10 @@ public void init() {
numSelected = entries.size();
if (entries.isEmpty()) { // None selected. Inform the user to select entries first.
- JOptionPane.showMessageDialog(frame,
- Localization.lang("First select the entries you want keys to be generated for."),
- Localization.lang("Autogenerate BibTeX keys"), JOptionPane.INFORMATION_MESSAGE);
+ dialogService.showWarningDialogAndWait(Localization.lang("Autogenerate BibTeX keys"),
+ Localization.lang("First select the entries you want keys to be generated for."));
return;
}
- frame.block();
output(formatOutputMessage(Localization.lang("Generating BibTeX key for"), numSelected));
}
@@ -472,15 +392,17 @@ public void run() {
// if we're going to override some cite keys warn the user about it
} else if (Globals.prefs.getBoolean(JabRefPreferences.WARN_BEFORE_OVERWRITING_KEY)) {
if (entries.parallelStream().anyMatch(BibEntry::hasCiteKey)) {
- CheckBoxMessage cbm = new CheckBoxMessage(
- Localization.lang("One or more keys will be overwritten. Continue?"),
- Localization.lang("Disable this confirmation dialog"), false);
- final int answer = JOptionPane.showConfirmDialog(frame, cbm,
- Localization.lang("Overwrite keys"), JOptionPane.YES_NO_OPTION);
- Globals.prefs.putBoolean(JabRefPreferences.WARN_BEFORE_OVERWRITING_KEY, !cbm.isSelected());
+
+ boolean overwriteKeysPressed = dialogService.showConfirmationDialogWithOptOutAndWait(
+ Localization.lang("Overwrite keys"),
+ Localization.lang("One or more keys will be overwritten. Continue?"),
+ Localization.lang("Overwrite keys"),
+ Localization.lang("Cancel"),
+ Localization.lang("Disable this confirmation dialog"),
+ optOut -> Globals.prefs.putBoolean(JabRefPreferences.WARN_BEFORE_OVERWRITING_KEY, !optOut));
// The user doesn't want to overide cite keys
- if (answer == JOptionPane.NO_OPTION) {
+ if (!overwriteKeysPressed) {
canceled = true;
return;
}
@@ -506,33 +428,18 @@ public void run() {
@Override
public void update() {
if (canceled) {
- frame.unblock();
return;
}
markBaseChanged();
numSelected = entries.size();
-
- ////////////////////////////////////////////////////////////////////////////////
- // Prevent selection loss for autogenerated BibTeX-Keys
- ////////////////////////////////////////////////////////////////////////////////
- for (final BibEntry bibEntry : entries) {
- SwingUtilities.invokeLater(() -> {
- final int row = mainTable.findEntry(bibEntry);
- if ((row >= 0) && (mainTable.getSelectedRowCount() < entries.size())) {
- mainTable.addRowSelectionInterval(row, row);
- }
- });
- }
- ////////////////////////////////////////////////////////////////////////////////
output(formatOutputMessage(Localization.lang("Generated BibTeX key for"), numSelected));
- frame.unblock();
}
});
// The action for cleaning up entry.
actions.put(Actions.CLEANUP, cleanUpAction);
- actions.put(Actions.MERGE_ENTRIES, (BaseAction) () -> new MergeEntriesDialog(BasePanel.this));
+ actions.put(Actions.MERGE_ENTRIES, (BaseAction) () -> new MergeEntriesDialog(BasePanel.this, dialogService));
actions.put(Actions.SEARCH, (BaseAction) frame.getGlobalSearchBar()::focus);
actions.put(Actions.GLOBAL_SEARCH, (BaseAction) frame.getGlobalSearchBar()::performGlobalSearch);
@@ -549,29 +456,21 @@ public void update() {
// The action for copying the BibTeX key and the title for the first selected entry
actions.put(Actions.COPY_KEY_AND_TITLE, (BaseAction) () -> copyKeyAndTitle());
- actions.put(Actions.COPY_CITATION_ASCII_DOC,
- (BaseAction) () -> copyCitationToClipboard(CitationStyleOutputFormat.ASCII_DOC));
- actions.put(Actions.COPY_CITATION_XSLFO,
- (BaseAction) () -> copyCitationToClipboard(CitationStyleOutputFormat.XSL_FO));
- actions.put(Actions.COPY_CITATION_HTML,
- (BaseAction) () -> copyCitationToClipboard(CitationStyleOutputFormat.HTML));
- actions.put(Actions.COPY_CITATION_RTF,
- (BaseAction) () -> copyCitationToClipboard(CitationStyleOutputFormat.RTF));
- actions.put(Actions.COPY_CITATION_TEXT,
- (BaseAction) () -> copyCitationToClipboard(CitationStyleOutputFormat.TEXT));
+ actions.put(Actions.COPY_CITATION_ASCII_DOC, (BaseAction) () -> copyCitationToClipboard(CitationStyleOutputFormat.ASCII_DOC));
+ actions.put(Actions.COPY_CITATION_XSLFO, (BaseAction) () -> copyCitationToClipboard(CitationStyleOutputFormat.XSL_FO));
+ actions.put(Actions.COPY_CITATION_HTML, (BaseAction) () -> copyCitationToClipboard(CitationStyleOutputFormat.HTML));
+ actions.put(Actions.COPY_CITATION_RTF, (BaseAction) () -> copyCitationToClipboard(CitationStyleOutputFormat.RTF));
+ actions.put(Actions.COPY_CITATION_TEXT, (BaseAction) () -> copyCitationToClipboard(CitationStyleOutputFormat.TEXT));
// The action for copying the BibTeX keys as hyperlinks to the urls of the selected entries
- actions.put(Actions.COPY_KEY_AND_LINK, new CopyBibTeXKeyAndLinkAction(mainTable));
+ actions.put(Actions.COPY_KEY_AND_LINK, new CopyBibTeXKeyAndLinkAction(mainTable, Globals.clipboardManager));
actions.put(Actions.MERGE_DATABASE, new AppendDatabaseAction(frame, this));
- actions.put(Actions.ADD_FILE_LINK, new AttachFileAction(this));
-
actions.put(Actions.OPEN_EXTERNAL_FILE, (BaseAction) () -> openExternalFile());
actions.put(Actions.OPEN_FOLDER, (BaseAction) () -> JabRefExecutorService.INSTANCE.execute(() -> {
- final List files = FileUtil.getListOfLinkedFiles(mainTable.getSelectedEntries(),
- bibDatabaseContext.getFileDirectoriesAsPaths(Globals.prefs.getFileDirectoryPreferences()));
+ final List files = FileUtil.getListOfLinkedFiles(mainTable.getSelectedEntries(), bibDatabaseContext.getFileDirectoriesAsPaths(Globals.prefs.getFileDirectoryPreferences()));
for (final Path f : files) {
try {
JabRefDesktop.openFolderAndSelectFile(f.toAbsolutePath());
@@ -582,7 +481,7 @@ public void update() {
}));
actions.put(Actions.OPEN_CONSOLE, (BaseAction) () -> JabRefDesktop
- .openConsole(frame.getCurrentBasePanel().getBibDatabaseContext().getDatabaseFile().orElse(null)));
+ .openConsole(frame.getCurrentBasePanel().getBibDatabaseContext().getDatabaseFile().orElse(null)));
actions.put(Actions.PULL_CHANGES_FROM_SHARED_DATABASE, (BaseAction) () -> {
DatabaseSynchronizer dbmsSynchronizer = frame.getCurrentBasePanel().getBibDatabaseContext().getDBMSSynchronizer();
@@ -591,7 +490,7 @@ public void update() {
actions.put(Actions.OPEN_URL, new OpenURLAction());
- actions.put(Actions.MERGE_WITH_FETCHED_ENTRY, new MergeWithFetchedEntryAction(this));
+ actions.put(Actions.MERGE_WITH_FETCHED_ENTRY, new MergeWithFetchedEntryAction(this, frame.getDialogService()));
actions.put(Actions.REPLACE_ALL, (BaseAction) () -> {
final ReplaceStringDialog rsd = new ReplaceStringDialog(frame);
@@ -620,128 +519,54 @@ public void update() {
}
});
- actions.put(Actions.DUPLI_CHECK,
- (BaseAction) () -> JabRefExecutorService.INSTANCE.execute(new DuplicateSearch(BasePanel.this)));
-
- actions.put(Actions.PLAIN_TEXT_IMPORT, (BaseAction) () -> {
- // get Type of new entry
- EntryTypeDialog etd = new EntryTypeDialog(frame);
- etd.setLocationRelativeTo(BasePanel.this);
- etd.setVisible(true);
- EntryType tp = etd.getChoice();
- if (tp == null) {
- return;
- }
-
- BibEntry bibEntry = new BibEntry(tp.getName());
- TextInputDialog tidialog = new TextInputDialog(frame, bibEntry);
- tidialog.setLocationRelativeTo(BasePanel.this);
- tidialog.setVisible(true);
+ actions.put(new SpecialFieldValueViewModel(SpecialField.RELEVANCE.getValues().get(0)).getCommand(),
+ new SpecialFieldViewModel(SpecialField.RELEVANCE, undoManager).getSpecialFieldAction(SpecialField.RELEVANCE.getValues().get(0), frame));
- if (tidialog.okPressed()) {
- UpdateField.setAutomaticFields(Collections.singletonList(bibEntry), false, false,
- Globals.prefs.getUpdateFieldPreferences());
- insertEntry(bibEntry);
- }
- });
+ actions.put(new SpecialFieldValueViewModel(SpecialField.QUALITY.getValues().get(0)).getCommand(),
+ new SpecialFieldViewModel(SpecialField.QUALITY, undoManager).getSpecialFieldAction(SpecialField.QUALITY.getValues().get(0), frame));
- actions.put(Actions.MARK_ENTRIES, new MarkEntriesAction(frame, 0));
-
- actions.put(Actions.UNMARK_ENTRIES, (BaseAction) () -> {
- try {
- List bes = mainTable.getSelectedEntries();
- if (bes.isEmpty()) {
- output(Localization.lang("This operation requires one or more entries to be selected."));
- return;
- }
- NamedCompound ce = new NamedCompound(Localization.lang("Unmark entries"));
- for (BibEntry be : bes) {
- EntryMarker.unmarkEntry(be, false, bibDatabaseContext.getDatabase(), ce);
- }
- ce.end();
- getUndoManager().addEdit(ce);
- markBaseChanged();
- String outputStr;
- if (bes.size() == 1) {
- outputStr = Localization.lang("Unmarked selected entry");
- } else {
- outputStr = Localization.lang("Unmarked all %0 selected entries", Integer.toString(bes.size()));
- }
- output(outputStr);
- } catch (Throwable ex) {
- LOGGER.warn("Could not unmark", ex);
- }
- });
-
- actions.put(Actions.UNMARK_ALL, (BaseAction) () -> {
- NamedCompound ce = new NamedCompound(Localization.lang("Unmark all"));
-
- for (BibEntry be : bibDatabaseContext.getDatabase().getEntries()) {
- EntryMarker.unmarkEntry(be, false, bibDatabaseContext.getDatabase(), ce);
- }
- ce.end();
- getUndoManager().addEdit(ce);
- markBaseChanged();
- output(Localization.lang("Unmarked all entries"));
- });
-
- // Note that we can't put the number of entries that have been reverted into the undoText as the concrete number cannot be injected
- actions.put(new SpecialFieldValueViewModel(SpecialField.RELEVANCE.getValues().get(0)).getActionName(),
- new SpecialFieldViewModel(SpecialField.RELEVANCE).getSpecialFieldAction(
- SpecialField.RELEVANCE.getValues().get(0), frame));
- actions.put(new SpecialFieldValueViewModel(SpecialField.QUALITY.getValues().get(0)).getActionName(),
- new SpecialFieldViewModel(SpecialField.QUALITY)
- .getSpecialFieldAction(SpecialField.QUALITY.getValues().get(0), frame));
- actions.put(new SpecialFieldValueViewModel(SpecialField.PRINTED.getValues().get(0)).getActionName(),
- new SpecialFieldViewModel(SpecialField.PRINTED).getSpecialFieldAction(
- SpecialField.PRINTED.getValues().get(0), frame));
+ actions.put(new SpecialFieldValueViewModel(SpecialField.PRINTED.getValues().get(0)).getCommand(),
+ new SpecialFieldViewModel(SpecialField.PRINTED, undoManager).getSpecialFieldAction(SpecialField.PRINTED.getValues().get(0), frame));
for (SpecialFieldValue prio : SpecialField.PRIORITY.getValues()) {
- actions.put(new SpecialFieldValueViewModel(prio).getActionName(),
- new SpecialFieldViewModel(SpecialField.PRIORITY).getSpecialFieldAction(prio, this.frame));
+ actions.put(new SpecialFieldValueViewModel(prio).getCommand(),
+ new SpecialFieldViewModel(SpecialField.PRIORITY, undoManager).getSpecialFieldAction(prio, this.frame));
}
for (SpecialFieldValue rank : SpecialField.RANKING.getValues()) {
- actions.put(new SpecialFieldValueViewModel(rank).getActionName(),
- new SpecialFieldViewModel(SpecialField.RANKING).getSpecialFieldAction(rank, this.frame));
+ actions.put(new SpecialFieldValueViewModel(rank).getCommand(),
+ new SpecialFieldViewModel(SpecialField.RANKING, undoManager).getSpecialFieldAction(rank, this.frame));
}
for (SpecialFieldValue status : SpecialField.READ_STATUS.getValues()) {
- actions.put(new SpecialFieldValueViewModel(status).getActionName(),
- new SpecialFieldViewModel(SpecialField.READ_STATUS).getSpecialFieldAction(status, this.frame));
+ actions.put(new SpecialFieldValueViewModel(status).getCommand(),
+ new SpecialFieldViewModel(SpecialField.READ_STATUS, undoManager).getSpecialFieldAction(status, this.frame));
}
actions.put(Actions.TOGGLE_PREVIEW, (BaseAction) () -> {
PreviewPreferences previewPreferences = Globals.prefs.getPreviewPreferences();
boolean enabled = !previewPreferences.isPreviewPanelEnabled();
- PreviewPreferences newPreviewPreferences = previewPreferences
- .getBuilder()
- .withPreviewPanelEnabled(enabled)
- .build();
+ PreviewPreferences newPreviewPreferences = previewPreferences.getBuilder()
+ .withPreviewPanelEnabled(enabled)
+ .build();
Globals.prefs.storePreviewPreferences(newPreviewPreferences);
- setPreviewActiveBasePanels(enabled);
- frame.setPreviewToggle(enabled);
+ DefaultTaskExecutor.runInJavaFXThread(() -> setPreviewActiveBasePanels(enabled));
});
actions.put(Actions.NEXT_PREVIEW_STYLE, (BaseAction) this::nextPreviewStyle);
actions.put(Actions.PREVIOUS_PREVIEW_STYLE, (BaseAction) this::previousPreviewStyle);
actions.put(Actions.MANAGE_SELECTORS, (BaseAction) () -> {
- ContentSelectorDialog csd = new ContentSelectorDialog(frame, frame, BasePanel.this, false, null);
- csd.setLocationRelativeTo(frame);
+ ContentSelectorDialog csd = new ContentSelectorDialog(null, frame, BasePanel.this, false, null);
csd.setVisible(true);
});
actions.put(Actions.EXPORT_TO_CLIPBOARD, new ExportToClipboardAction(frame));
actions.put(Actions.SEND_AS_EMAIL, new SendAsEMailAction(frame));
- actions.put(Actions.WRITE_XMP, new WriteXMPAction(this));
+ actions.put(Actions.WRITE_XMP, new WriteXMPActionWorker(this));
actions.put(Actions.ABBREVIATE_ISO, new AbbreviateAction(this, true));
actions.put(Actions.ABBREVIATE_MEDLINE, new AbbreviateAction(this, false));
actions.put(Actions.UNABBREVIATE, new UnabbreviateAction(this));
- actions.put(Actions.AUTO_SET_FILE, new SynchronizeFileField(this));
-
- actions.put(Actions.BACK, (BaseAction) BasePanel.this::back);
- actions.put(Actions.FORWARD, (BaseAction) BasePanel.this::forward);
actions.put(Actions.RESOLVE_DUPLICATE_KEYS, new SearchFixDuplicateLabels(this));
@@ -749,7 +574,7 @@ public void update() {
actions.put(Actions.REMOVE_FROM_GROUP, new GroupAddRemoveDialog(this, false, false));
actions.put(Actions.MOVE_TO_GROUP, new GroupAddRemoveDialog(this, true, true));
- actions.put(Actions.DOWNLOAD_FULL_TEXT, new FindFullTextAction(this));
+ actions.put(Actions.DOWNLOAD_FULL_TEXT, new FindFullTextAction(frame.getDialogService(), this));
}
/**
@@ -761,45 +586,13 @@ private void copyCitationToClipboard(CitationStyleOutputFormat outputFormat) {
new CitationStyleToClipboardWorker(this, outputFormat).execute();
}
- private void copy() {
- List bes = mainTable.getSelectedEntries();
-
- if (bes.isEmpty()) {
- // The user maybe selected a single cell.
- // TODO: Check if this can actually happen
- int[] rows = mainTable.getSelectedRows();
- int[] cols = mainTable.getSelectedColumns();
- if ((cols.length == 1) && (rows.length == 1)) {
- // Copy single value.
- Object o = mainTable.getValueAt(rows[0], cols[0]);
- if (o != null) {
- StringSelection ss = new StringSelection(o.toString());
- Toolkit.getDefaultToolkit().getSystemClipboard().setContents(ss, BasePanel.this);
-
- output(Localization.lang("Copied cell contents") + '.');
- }
- }
- } else {
- TransferableBibtexEntry trbe = new TransferableBibtexEntry(bes);
- // ! look at ClipBoardManager
- Toolkit.getDefaultToolkit().getSystemClipboard().setContents(trbe, BasePanel.this);
- output(formatOutputMessage(Localization.lang("Copied"), bes.size()));
- }
- }
-
- private void cut() {
- runCommand(Actions.COPY);
- // cannot call runCommand(Actions.DELETE), b/c it will call delete(false) with the wrong parameter
- delete(true);
- }
-
/**
* Removes the selected entries from the database
*
* @param cut If false the user will get asked if he really wants to delete the entries, and it will be localized as
* "deleted". If true the action will be localized as "cut"
*/
- private void delete(boolean cut) {
+ public void delete(boolean cut) {
delete(cut, mainTable.getSelectedEntries());
}
@@ -817,20 +610,11 @@ private void delete(boolean cut, List entries) {
return;
}
- // select the next entry to stay at the same place as before (or the previous if we're already at the end)
- if (mainTable.getSelectedRow() != (mainTable.getRowCount() - 1)) {
- selectNextEntry();
- } else {
- selectPreviousEntry();
- }
-
NamedCompound compound;
if (cut) {
- compound = new NamedCompound(
- (entries.size() > 1 ? Localization.lang("cut entries") : Localization.lang("cut entry")));
+ compound = new NamedCompound((entries.size() > 1 ? Localization.lang("cut entries") : Localization.lang("cut entry")));
} else {
- compound = new NamedCompound(
- (entries.size() > 1 ? Localization.lang("delete entries") : Localization.lang("delete entry")));
+ compound = new NamedCompound((entries.size() > 1 ? Localization.lang("delete entries") : Localization.lang("delete entry")));
}
for (BibEntry entry : entries) {
compound.addEdit(new UndoableRemoveEntry(bibDatabaseContext.getDatabase(), entry, BasePanel.this));
@@ -841,8 +625,7 @@ private void delete(boolean cut, List entries) {
getUndoManager().addEdit(compound);
markBaseChanged();
- frame.output(
- formatOutputMessage(cut ? Localization.lang("Cut") : Localization.lang("Deleted"), entries.size()));
+ frame.output(formatOutputMessage(cut ? Localization.lang("Cut") : Localization.lang("Deleted"), entries.size()));
// prevent the main table from loosing focus
mainTable.requestFocus();
@@ -852,62 +635,14 @@ public void delete(BibEntry entry) {
delete(false, Collections.singletonList(entry));
}
- private void paste() {
- Collection bes = new ClipBoardManager().extractBibEntriesFromClipboard();
-
- // finally we paste in the entries (if any), which either came from TransferableBibtexEntries
- // or were parsed from a string
- if (!bes.isEmpty()) {
-
- NamedCompound ce = new NamedCompound(
- (bes.size() > 1 ? Localization.lang("paste entries") : Localization.lang("paste entry")));
-
- // Store the first inserted bibtexentry.
- // bes[0] does not work as bes[0] is first clonded,
- // then inserted.
- // This entry is used to open up an entry editor
- // for the first inserted entry.
- BibEntry firstBE = null;
-
- for (BibEntry be1 : bes) {
-
- BibEntry be = (BibEntry) be1.clone();
- if (firstBE == null) {
- firstBE = be;
- }
- UpdateField.setAutomaticFields(be, Globals.prefs.getUpdateFieldPreferences());
-
- // We have to clone the
- // entries, since the pasted
- // entries must exist
- // independently of the copied
- // ones.
- bibDatabaseContext.getDatabase().insertEntry(be);
-
- ce.addEdit(new UndoableInsertEntry(bibDatabaseContext.getDatabase(), be, BasePanel.this));
- }
- ce.end();
- getUndoManager().addEdit(ce);
- output(formatOutputMessage(Localization.lang("Pasted"), bes.size()));
- markBaseChanged();
-
- highlightEntry(firstBE);
- mainTable.requestFocus();
-
- if (Globals.prefs.getBoolean(JabRefPreferences.AUTO_OPEN_FORM)) {
- selectionListener.editSignalled(firstBE);
- }
- }
- }
-
private void copyTitle() {
List selectedBibEntries = mainTable.getSelectedEntries();
if (!selectedBibEntries.isEmpty()) {
// Collect all non-null titles.
List titles = selectedBibEntries.stream()
- .filter(bibEntry -> bibEntry.getTitle().isPresent())
- .map(bibEntry -> bibEntry.getTitle().get())
- .collect(Collectors.toList());
+ .filter(bibEntry -> bibEntry.getTitle().isPresent())
+ .map(bibEntry -> bibEntry.getTitle().get())
+ .collect(Collectors.toList());
if (titles.isEmpty()) {
output(Localization.lang("None of the selected entries have titles."));
@@ -918,12 +653,9 @@ private void copyTitle() {
if (titles.size() == selectedBibEntries.size()) {
// All entries had titles.
- output((selectedBibEntries.size() > 1 ? Localization.lang("Copied titles") : Localization
- .lang("Copied title")) + '.');
+ output((selectedBibEntries.size() > 1 ? Localization.lang("Copied titles") : Localization.lang("Copied title")) + '.');
} else {
- output(Localization.lang("Warning: %0 out of %1 entries have undefined title.",
- Integer.toString(selectedBibEntries.size() - titles.size()),
- Integer.toString(selectedBibEntries.size())));
+ output(Localization.lang("Warning: %0 out of %1 entries have undefined title.", Integer.toString(selectedBibEntries.size() - titles.size()), Integer.toString(selectedBibEntries.size())));
}
}
}
@@ -943,8 +675,8 @@ private void copyCiteKey() {
String sb = String.join(",", keys);
String citeCommand = Optional.ofNullable(Globals.prefs.get(JabRefPreferences.CITE_COMMAND))
- .filter(cite -> cite.contains("\\")) // must contain \
- .orElse("\\cite");
+ .filter(cite -> cite.contains("\\")) // must contain \
+ .orElse("\\cite");
StringSelection ss = new StringSelection(citeCommand + "{" + sb + '}');
Toolkit.getDefaultToolkit().getSystemClipboard().setContents(ss, BasePanel.this);
@@ -952,8 +684,7 @@ private void copyCiteKey() {
// All entries had keys.
output(bes.size() > 1 ? Localization.lang("Copied keys") : Localization.lang("Copied key") + '.');
} else {
- output(Localization.lang("Warning: %0 out of %1 entries have undefined BibTeX key.",
- Integer.toString(bes.size() - keys.size()), Integer.toString(bes.size())));
+ output(Localization.lang("Warning: %0 out of %1 entries have undefined BibTeX key.", Integer.toString(bes.size() - keys.size()), Integer.toString(bes.size())));
}
}
}
@@ -978,8 +709,7 @@ private void copyKey() {
// All entries had keys.
output((bes.size() > 1 ? Localization.lang("Copied keys") : Localization.lang("Copied key")) + '.');
} else {
- output(Localization.lang("Warning: %0 out of %1 entries have undefined BibTeX key.",
- Integer.toString(bes.size() - keys.size()), Integer.toString(bes.size())));
+ output(Localization.lang("Warning: %0 out of %1 entries have undefined BibTeX key.", Integer.toString(bes.size() - keys.size()), Integer.toString(bes.size())));
}
}
}
@@ -988,13 +718,11 @@ private void copyKeyAndTitle() {
List bes = mainTable.getSelectedEntries();
if (!bes.isEmpty()) {
// OK: in a future version, this string should be configurable to allow arbitrary exports
- StringReader sr = new StringReader(
- "\\bibtexkey - \\begin{title}\\format[RemoveBrackets]{\\title}\\end{title}\n");
+ StringReader sr = new StringReader("\\bibtexkey - \\begin{title}\\format[RemoveBrackets]{\\title}\\end{title}\n");
Layout layout;
try {
- layout = new LayoutHelper(sr,
- Globals.prefs.getLayoutFormatterPreferences(Globals.journalAbbreviationLoader))
- .getLayoutFromText();
+ layout = new LayoutHelper(sr, Globals.prefs.getLayoutFormatterPreferences(Globals.journalAbbreviationLoader))
+ .getLayoutFromText();
} catch (IOException e) {
LOGGER.info("Could not get layout", e);
return;
@@ -1023,8 +751,7 @@ private void copyKeyAndTitle() {
// All entries had keys.
output((bes.size() > 1 ? Localization.lang("Copied keys") : Localization.lang("Copied key")) + '.');
} else {
- output(Localization.lang("Warning: %0 out of %1 entries have undefined BibTeX key.",
- Integer.toString(bes.size() - copied), Integer.toString(bes.size())));
+ output(Localization.lang("Warning: %0 out of %1 entries have undefined BibTeX key.", Integer.toString(bes.size() - copied), Integer.toString(bes.size())));
}
}
}
@@ -1051,8 +778,7 @@ private void openExternalFile() {
return;
}
FileListEntry flEntry = fileListTableModel.getEntry(0);
- ExternalFileMenuItem item = new ExternalFileMenuItem(frame(), entry, "", flEntry.getLink(),
- flEntry.getType().get().getIcon(), bibDatabaseContext, flEntry.getType());
+ ExternalFileMenuItem item = new ExternalFileMenuItem(frame(), "", flEntry.getLink(), flEntry.getType().get().getIcon().getSmallIcon(), bibDatabaseContext, flEntry.getType());
item.doClick();
});
}
@@ -1061,15 +787,15 @@ private void openExternalFile() {
* This method is called from JabRefFrame if a database specific action is requested by the user. Runs the command
* if it is defined, or prints an error message to the standard error stream.
*
- * @param _command The name of the command to run.
+ * @param command The name of the command to run.
*/
- public void runCommand(final String _command) {
- if (!actions.containsKey(_command)) {
- LOGGER.info("No action defined for '" + _command + '\'');
+ public void runCommand(final Actions command) {
+ if (!actions.containsKey(command)) {
+ LOGGER.info("No action defined for '" + command + '\'');
return;
}
- Object o = actions.get(_command);
+ Object o = actions.get(command);
try {
if (o instanceof BaseAction) {
((BaseAction) o).action();
@@ -1077,23 +803,21 @@ public void runCommand(final String _command) {
runWorker((AbstractWorker) o);
}
} catch (Throwable ex) {
- // If the action has blocked the JabRefFrame before crashing, we need to unblock it.
- // The call to unblock will simply hide the glasspane, so there is no harm in calling
- // it even if the frame hasn't been blocked.
- frame.unblock();
LOGGER.error("runCommand error: " + ex.getMessage(), ex);
}
}
- private boolean saveDatabase(File file, boolean selectedOnly, Charset enc,
+ /**
+ * FIXME: high code duplication with {@link SaveDatabaseAction#saveDatabase(File, boolean, Charset)}
+ */
+ private boolean saveDatabase(File file, boolean selectedOnly, Charset encoding,
SavePreferences.DatabaseSaveType saveType)
throws SaveException {
SaveSession session;
- frame.block();
final String SAVE_DATABASE = Localization.lang("Save library");
try {
SavePreferences prefs = Globals.prefs.loadForSaveFromPreferences()
- .withEncoding(enc)
+ .withEncoding(encoding)
.withSaveType(saveType);
BibtexDatabaseWriter databaseWriter = new BibtexDatabaseWriter<>(
FileSaveSession::new);
@@ -1107,46 +831,38 @@ private boolean saveDatabase(File file, boolean selectedOnly, Charset enc,
}
// FIXME: not sure if this is really thrown anywhere
catch (UnsupportedCharsetException ex) {
- JOptionPane.showMessageDialog(frame,
- Localization.lang("Could not save file.") + ' '
- + Localization.lang("Character encoding '%0' is not supported.", enc.displayName()),
- SAVE_DATABASE, JOptionPane.ERROR_MESSAGE);
+ frame.getDialogService().showErrorDialogAndWait(Localization.lang("Save library"), Localization.lang("Could not save file.")
+ + Localization.lang("Character encoding '%0' is not supported.", encoding.displayName()));
throw new SaveException("rt");
} catch (SaveException ex) {
if (ex.specificEntry()) {
// Error occurred during processing of the entry. Highlight it:
- highlightEntry(ex.getEntry());
+ clearAndSelect(ex.getEntry());
showAndEdit(ex.getEntry());
} else {
LOGGER.warn("Could not save", ex);
}
- JOptionPane.showMessageDialog(frame, Localization.lang("Could not save file.") + "\n" + ex.getMessage(),
- SAVE_DATABASE, JOptionPane.ERROR_MESSAGE);
+ dialogService.showErrorDialogAndWait(SAVE_DATABASE, Localization.lang("Could not save file."), ex);
throw new SaveException("rt");
- } finally {
- frame.unblock();
}
boolean commit = true;
if (!session.getWriter().couldEncodeAll()) {
FormBuilder builder = FormBuilder.create()
- .layout(new FormLayout("left:pref, 4dlu, fill:pref", "pref, 4dlu, pref"));
+ .layout(new FormLayout("left:pref, 4dlu, fill:pref", "pref, 4dlu, pref"));
JTextArea ta = new JTextArea(session.getWriter().getProblemCharacters());
ta.setEditable(false);
- builder.add(Localization.lang("The chosen encoding '%0' could not encode the following characters:",
- session.getEncoding().displayName())).xy(1, 1);
+ builder.add(Localization.lang("The chosen encoding '%0' could not encode the following characters:", session.getEncoding().displayName())).xy(1, 1);
builder.add(ta).xy(3, 1);
builder.add(Localization.lang("What do you want to do?")).xy(1, 3);
String tryDiff = Localization.lang("Try different encoding");
- int answer = JOptionPane.showOptionDialog(frame, builder.getPanel(), SAVE_DATABASE,
- JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE, null,
- new String[] {Localization.lang("Save"), tryDiff, Localization.lang("Cancel")}, tryDiff);
+ int answer = JOptionPane.showOptionDialog(null, builder.getPanel(), SAVE_DATABASE, JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE, null, new String[] {Localization.lang("Save"), tryDiff, Localization.lang("Cancel")}, tryDiff);
if (answer == JOptionPane.NO_OPTION) {
+
// The user wants to use another encoding.
- Object choice = JOptionPane.showInputDialog(frame, Localization.lang("Select encoding"), SAVE_DATABASE,
- JOptionPane.QUESTION_MESSAGE, null, Encodings.ENCODINGS_DISPLAYNAMES, enc);
+ Object choice = JOptionPane.showInputDialog(null, Localization.lang("Select encoding"), SAVE_DATABASE, JOptionPane.QUESTION_MESSAGE, null, Encodings.ENCODINGS_DISPLAYNAMES, encoding);
if (choice == null) {
commit = false;
} else {
@@ -1160,7 +876,7 @@ private boolean saveDatabase(File file, boolean selectedOnly, Charset enc,
if (commit) {
session.commit(file.toPath());
- this.bibDatabaseContext.getMetaData().setEncoding(enc); // Make sure to remember which encoding we used.
+ this.bibDatabaseContext.getMetaData().setEncoding(encoding); // Make sure to remember which encoding we used.
} else {
session.cancel();
}
@@ -1192,7 +908,6 @@ public BibEntry newEntry(EntryType type) {
// Find out what type is wanted.
final EntryTypeDialog etd = new EntryTypeDialog(frame);
// We want to center the dialog, to make it look nicer.
- etd.setLocationRelativeTo(frame);
etd.setVisible(true);
actualType = etd.getChoice();
}
@@ -1206,7 +921,7 @@ public BibEntry newEntry(EntryType type) {
UpdateField.setAutomaticFields(list, true, true, Globals.prefs.getUpdateFieldPreferences());
// Create an UndoableInsertEntry object.
- getUndoManager().addEdit(new UndoableInsertEntry(bibDatabaseContext.getDatabase(), be, BasePanel.this));
+ getUndoManager().addEdit(new UndoableInsertEntry(bibDatabaseContext.getDatabase(), be));
output(Localization.lang("Added new '%0' entry.", actualType.getName().toLowerCase(Locale.ROOT)));
// We are going to select the new entry. Before that, make sure that we are in
@@ -1217,7 +932,7 @@ public BibEntry newEntry(EntryType type) {
mode = BasePanelMode.WILL_SHOW_EDITOR;
}
- highlightEntry(be);
+ clearAndSelect(be);
// The database just changed.
markBaseChanged();
@@ -1246,14 +961,14 @@ public void insertEntry(final BibEntry bibEntry) {
UpdateField.setAutomaticFields(bibEntry, true, true, Globals.prefs.getUpdateFieldPreferences());
}
// Create an UndoableInsertEntry object.
- getUndoManager().addEdit(new UndoableInsertEntry(bibDatabaseContext.getDatabase(), bibEntry, BasePanel.this));
+ getUndoManager().addEdit(new UndoableInsertEntry(bibDatabaseContext.getDatabase(), bibEntry));
output(Localization.lang("Added new '%0' entry.", bibEntry.getType()));
markBaseChanged(); // The database just changed.
if (Globals.prefs.getBoolean(JabRefPreferences.AUTO_OPEN_FORM)) {
- selectionListener.editSignalled(bibEntry);
+ showAndEdit(bibEntry);
}
- highlightEntry(bibEntry);
+ clearAndSelect(bibEntry);
} catch (KeyCollisionException ex) {
LOGGER.info("Collision for bibtex key" + bibEntry.getId(), ex);
}
@@ -1262,8 +977,7 @@ public void insertEntry(final BibEntry bibEntry) {
public void editEntryByIdAndFocusField(final String entryId, final String fieldName) {
bibDatabaseContext.getDatabase().getEntryById(entryId).ifPresent(entry -> {
- mainTable.setSelected(mainTable.findEntry(entry));
- selectionListener.editSignalled();
+ clearAndSelect(entry);
showAndEdit(entry);
entryEditor.setFocusToField(fieldName);
});
@@ -1274,24 +988,26 @@ public void updateTableFont() {
}
private void createMainTable() {
- bibDatabaseContext.getDatabase().registerListener(tableModel.getListSynchronizer());
- bibDatabaseContext.getDatabase().registerListener(SpecialFieldDatabaseChangeListener.getInstance());
+ bibDatabaseContext.getDatabase().registerListener(SpecialFieldDatabaseChangeListener.INSTANCE);
- tableFormat = new MainTableFormat(bibDatabaseContext.getDatabase());
- tableFormat.updateTableFormat();
- mainTable = new MainTable(tableFormat, tableModel, frame, this);
+ mainTable = new MainTable(tableModel, frame, this, bibDatabaseContext, preferences.getTablePreferences(), externalFileTypes, preferences.getKeyBindings());
- selectionListener = new MainTableSelectionListener(this, mainTable);
mainTable.updateFont();
- mainTable.addSelectionListener(selectionListener);
- mainTable.addMouseListener(selectionListener);
- mainTable.addKeyListener(selectionListener);
- mainTable.addFocusListener(selectionListener);
// Add the listener that binds selection to state manager (TODO: should be replaced by proper JavaFX binding as soon as table is implemented in JavaFX)
- mainTable.addSelectionListener(listEvent -> Platform
- .runLater(() -> Globals.stateManager.setSelectedEntries(mainTable.getSelectedEntries())));
-
+ mainTable.addSelectionListener(listEvent -> Globals.stateManager.setSelectedEntries(mainTable.getSelectedEntries()));
+
+ // Update entry editor and preview according to selected entries
+ mainTable.addSelectionListener(event -> mainTable.getSelectedEntries()
+ .stream()
+ .findFirst()
+ .ifPresent(entry -> {
+ preview.setEntry(entry);
+ entryEditor.setEntry(entry);
+ }));
+
+ // TODO: Register these actions globally
+ /*
String clearSearch = "clearSearch";
mainTable.getInputMap().put(Globals.getKeyPrefs().getKey(KeyBinding.CLEAR_SEARCH), clearSearch);
mainTable.getActionMap().put(clearSearch, new AbstractAction() {
@@ -1350,68 +1066,24 @@ public void actionPerformed(ActionEvent e) {
}
}
});
-
- mainTable.addKeyListener(new KeyAdapter() {
-
- @Override
- public void keyPressed(KeyEvent e) {
- final int keyCode = e.getKeyCode();
-
- if (e.isControlDown()) {
- switch (keyCode) {
- case KeyEvent.VK_PAGE_DOWN:
- frame.nextTab.actionPerformed(null);
- e.consume();
- break;
- case KeyEvent.VK_PAGE_UP:
- frame.prevTab.actionPerformed(null);
- e.consume();
- break;
- default:
- break;
- }
- } else if (keyCode == KeyEvent.VK_ENTER) {
- e.consume();
- try {
- runCommand(Actions.EDIT);
- } catch (Throwable ex) {
- LOGGER.warn("Could not run action based on key press", ex);
- }
- }
- }
- });
+ */
}
public void setupMainPanel() {
- splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
- splitPane.setDividerSize(SPLIT_PANE_DIVIDER_SIZE);
+ splitPane = new SplitPane();
+ splitPane.setOrientation(Orientation.VERTICAL);
adjustSplitter(); // restore last splitting state (before mainTable is created as creation affects the stored size of the entryEditors)
- // check whether a mainTable already existed and a floatSearch was active
- boolean floatSearchActive = (mainTable != null) && (this.tableModel.getSearchState() == MainTableDataModel.DisplayOption.FLOAT);
-
createMainTable();
- splitPane.setTopComponent(mainTable.getPane());
-
- // Remove borders
- splitPane.setBorder(BorderFactory.createEmptyBorder());
- setBorder(BorderFactory.createEmptyBorder());
-
- // If an entry is currently being shown, make sure it stays shown,
- // otherwise set the bottom component to null.
- if (mode == BasePanelMode.SHOWING_PREVIEW) {
- mode = BasePanelMode.SHOWING_NOTHING;
- highlightEntry(selectionListener.getPreview().getEntry());
- } else if (mode == BasePanelMode.SHOWING_EDITOR) {
- mode = BasePanelMode.SHOWING_NOTHING;
- } else {
- splitPane.setBottomComponent(null);
- }
-
- setLayout(new BorderLayout());
- removeAll();
- add(splitPane, BorderLayout.CENTER);
+ 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);
// Set up name autocompleter for search:
instantiateSearchAutoCompleter();
@@ -1419,25 +1091,18 @@ public void setupMainPanel() {
setupAutoCompletion();
- // restore floating search result
- // (needed if preferences have been changed which causes a recreation of the main table)
- if (floatSearchActive) {
- mainTable.showFloatSearch();
- }
-
- splitPane.revalidate();
- revalidate();
- repaint();
-
- // saves the divider position as soon as it changes
- splitPane.addPropertyChangeListener(JSplitPane.DIVIDER_LOCATION_PROPERTY, event -> saveDividerLocation());
+ // Saves the divider position as soon as it changes
+ // We need to keep a reference to the subscription, otherwise the binding gets garbage collected
+ dividerPositionSubscription = EasyBind.monadic(Bindings.valueAt(splitPane.getDividers(), 0))
+ .flatMap(SplitPane.Divider::positionProperty)
+ .subscribe((observable, oldValue, newValue) -> saveDividerLocation(newValue));
}
/**
* Set up auto completion for this database
*/
private void setupAutoCompletion() {
- AutoCompletePreferences autoCompletePreferences = Globals.prefs.getAutoCompletePreferences();
+ AutoCompletePreferences autoCompletePreferences = preferences.getAutoCompletePreferences();
if (autoCompletePreferences.shouldAutoComplete()) {
suggestionProviders = new SuggestionProviders(autoCompletePreferences, Globals.journalAbbreviationLoader);
suggestionProviders.indexDatabase(getDatabase());
@@ -1461,12 +1126,6 @@ private void instantiateSearchAutoCompleter() {
}
}
- public void updatePreamble() {
- if (preambleEditor != null) {
- preambleEditor.updatePreamble();
- }
- }
-
public void assureStringDialogNotEditing() {
if (stringDialog != null) {
stringDialog.assureNotEditing();
@@ -1479,13 +1138,11 @@ public void updateStringDialog() {
}
}
- public void adjustSplitter() {
+ private void adjustSplitter() {
if (mode == BasePanelMode.SHOWING_PREVIEW) {
- splitPane.setDividerLocation(
- splitPane.getHeight() - Globals.prefs.getPreviewPreferences().getPreviewPanelHeight());
- } else {
- splitPane.setDividerLocation(
- splitPane.getHeight() - Globals.prefs.getInt(JabRefPreferences.ENTRY_EDITOR_HEIGHT));
+ splitPane.setDividerPositions(Globals.prefs.getPreviewPreferences().getPreviewPanelDividerPosition().doubleValue());
+ } else if (mode == BasePanelMode.SHOWING_EDITOR) {
+ splitPane.setDividerPositions(preferences.getEntryEditorDividerPosition());
}
}
@@ -1501,38 +1158,60 @@ public EntryEditor getEntryEditor() {
* @param entry The entry to edit.
*/
public void showAndEdit(BibEntry entry) {
-
- if (mode == BasePanelMode.SHOWING_EDITOR) {
- Globals.prefs.putInt(JabRefPreferences.ENTRY_EDITOR_HEIGHT, splitPane.getHeight() - splitPane.getDividerLocation());
- }
- mode = BasePanelMode.SHOWING_EDITOR;
- splitPane.setBottomComponent(entryEditorContainer);
DefaultTaskExecutor.runInJavaFXThread(() -> {
+
+ showBottomPane(BasePanelMode.SHOWING_EDITOR);
+
if (entry != getShowing()) {
entryEditor.setEntry(entry);
- newEntryShowing(entry);
+ showing = entry;
}
entryEditor.requestFocus();
});
+ }
+
+ private void showBottomPane(BasePanelMode newMode) {
+ Node pane;
+ switch (newMode) {
+ case SHOWING_PREVIEW:
+ pane = preview;
+ break;
+ case SHOWING_EDITOR:
+ pane = entryEditor;
+ break;
+ default:
+ throw new UnsupportedOperationException("new mode not recognized: " + newMode.name());
+ }
+ if (splitPane.getItems().size() == 2) {
+ splitPane.getItems().set(1, pane);
+ } else {
+ splitPane.getItems().add(1, pane);
+ }
+ mode = newMode;
adjustSplitter();
}
+ private void showAndEdit() {
+ if (!mainTable.getSelectedEntries().isEmpty()) {
+ showAndEdit(mainTable.getSelectedEntries().get(0));
+ }
+ }
+
/**
* Sets the given preview panel as the bottom component in the split panel. Updates the mode to SHOWING_PREVIEW.
*
* @param entry The entry to show in the preview.
*/
- public void showPreview(BibEntry entry) {
+ private void showPreview(BibEntry entry) {
+ showBottomPane(BasePanelMode.SHOWING_PREVIEW);
+
preview.setEntry(entry);
- mode = BasePanelMode.SHOWING_PREVIEW;
- splitPane.setBottomComponent(previewContainer);
- adjustSplitter();
}
private void showPreview() {
- if (!mainTable.getSelected().isEmpty()) {
- showPreview(mainTable.getSelected().get(0));
+ if (!mainTable.getSelectedEntries().isEmpty()) {
+ showPreview(mainTable.getSelectedEntries().get(0));
}
}
@@ -1546,9 +1225,9 @@ public void previousPreviewStyle() {
private void cyclePreview(int newPosition) {
PreviewPreferences previewPreferences = Globals.prefs.getPreviewPreferences()
- .getBuilder()
- .withPreviewCyclePosition(newPosition)
- .build();
+ .getBuilder()
+ .withPreviewCyclePosition(newPosition)
+ .build();
Globals.prefs.storePreviewPreferences(previewPreferences);
preview.updateLayout(previewPreferences);
@@ -1557,44 +1236,49 @@ private void cyclePreview(int newPosition) {
/**
* Removes the bottom component.
*/
- public void hideBottomComponent() {
+ public void closeBottomPane() {
mode = BasePanelMode.SHOWING_NOTHING;
- splitPane.setBottomComponent(null);
+ splitPane.getItems().removeAll(entryEditor, preview);
}
/**
* This method selects the given entry, and scrolls it into view in the table. If an entryEditor is shown, it is
* given focus afterwards.
*/
- public void highlightEntry(final BibEntry bibEntry) {
- highlightEntry(mainTable.findEntry(bibEntry));
+ public void clearAndSelect(final BibEntry bibEntry) {
+ mainTable.clearAndSelect(bibEntry);
}
/**
* This method selects the entry on the given position, and scrolls it into view in the table.
* If an entryEditor is shown, it is given focus afterwards.
+ *
+ * @deprecated use select by entry not by row
*/
- public void highlightEntry(int pos) {
- if ((pos >= 0) && (pos < mainTable.getRowCount())) {
- mainTable.setRowSelectionInterval(pos, pos);
- mainTable.ensureVisible(pos);
+ @Deprecated
+ private void clearAndSelect(int pos) {
+ if ((pos >= 0) && (pos < mainTable.getItems().size())) {
+ mainTable.getSelectionModel().clearAndSelect(pos);
}
}
public void selectPreviousEntry() {
- highlightEntry(((mainTable.getSelectedRow() - 1) + mainTable.getRowCount()) % mainTable.getRowCount());
+ mainTable.getSelectionModel().clearSelection();
+ mainTable.getSelectionModel().selectPrevious();
}
public void selectNextEntry() {
- highlightEntry((mainTable.getSelectedRow() + 1) % mainTable.getRowCount());
+ mainTable.getSelectionModel().clearSelection();
+ mainTable.getSelectionModel().selectNext();
}
public void selectFirstEntry() {
- highlightEntry(0);
+ clearAndSelect(0);
}
public void selectLastEntry() {
- highlightEntry(mainTable.getRowCount() - 1);
+ mainTable.getSelectionModel().clearSelection();
+ mainTable.getSelectionModel().selectLast();
}
/**
@@ -1604,10 +1288,12 @@ public void selectLastEntry() {
* @param editor The entry editor to close.
*/
public void entryEditorClosing(EntryEditor editor) {
- // Store divider location for next time:
- Globals.prefs.putInt(JabRefPreferences.ENTRY_EDITOR_HEIGHT,
- splitPane.getHeight() - splitPane.getDividerLocation());
- selectionListener.entryEditorClosing(editor);
+ if (Globals.prefs.getPreviewPreferences().isPreviewPanelEnabled()) {
+ showPreview(editor.getEntry());
+ } else {
+ closeBottomPane();
+ }
+ mainTable.requestFocus();
}
/**
@@ -1615,8 +1301,8 @@ public void entryEditorClosing(EntryEditor editor) {
*/
public void ensureNotShowingBottomPanel(BibEntry entry) {
if (((mode == BasePanelMode.SHOWING_EDITOR) && (entryEditor.getEntry() == entry))
- || ((mode == BasePanelMode.SHOWING_PREVIEW) && (selectionListener.getPreview().getEntry() == entry))) {
- hideBottomComponent();
+ || ((mode == BasePanelMode.SHOWING_PREVIEW) && (preview.getEntry() == entry))) {
+ closeBottomPane();
}
}
@@ -1644,7 +1330,7 @@ public void markBaseChanged() {
private void markBasedChangedInternal() {
// Put an asterisk behind the filename to indicate the database has changed.
frame.setWindowTitle();
- frame.updateAllTabTitles();
+ DefaultTaskExecutor.runInJavaFXThread(frame::updateAllTabTitles);
// If the status line states that the base has been saved, we
// remove this message, since it is no longer relevant. If a
// different message is shown, we leave it.
@@ -1678,10 +1364,6 @@ public BibDatabase getDatabase() {
return bibDatabaseContext.getDatabase();
}
- public void preambleEditorClosing() {
- preambleEditor = null;
- }
-
public void stringsClosing() {
stringDialog = null;
}
@@ -1698,10 +1380,8 @@ private void changeType(List entries, String newType) {
}
if (entries.size() > 1) {
- int choice = JOptionPane.showConfirmDialog(this,
- Localization.lang("Multiple entries selected. Do you want to change the type of all these to '%0'?", newType),
- Localization.lang("Change entry type"), JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE);
- if (choice == JOptionPane.NO_OPTION) {
+ 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;
}
}
@@ -1723,22 +1403,23 @@ private void changeType(List entries, String newType) {
public boolean showDeleteConfirmationDialog(int numberOfEntries) {
if (Globals.prefs.getBoolean(JabRefPreferences.CONFIRM_DELETE)) {
- String msg;
- msg = Localization.lang("Really delete the selected entry?");
String title = Localization.lang("Delete entry");
+ String message = Localization.lang("Really delete the selected entry?");
+ String okButton = Localization.lang("Delete entry");
+ String cancelButton = Localization.lang("Keep entry");
if (numberOfEntries > 1) {
- msg = Localization.lang("Really delete the %0 selected entries?", Integer.toString(numberOfEntries));
title = Localization.lang("Delete multiple entries");
+ message = Localization.lang("Really delete the %0 selected entries?", Integer.toString(numberOfEntries));
+ okButton = Localization.lang("Delete entries");
+ cancelButton = Localization.lang("Keep entries");
}
- CheckBoxMessage cb = new CheckBoxMessage(msg, Localization.lang("Disable this confirmation dialog"), false);
-
- int answer = JOptionPane.showConfirmDialog(frame, cb, title, JOptionPane.YES_NO_OPTION,
- JOptionPane.QUESTION_MESSAGE);
- if (cb.isSelected()) {
- Globals.prefs.putBoolean(JabRefPreferences.CONFIRM_DELETE, false);
- }
- return answer == JOptionPane.YES_OPTION;
+ return dialogService.showConfirmationDialogWithOptOutAndWait(title,
+ message,
+ okButton,
+ cancelButton,
+ Localization.lang("Disable this confirmation dialog"),
+ optOut -> Globals.prefs.putBoolean(JabRefPreferences.CONFIRM_DELETE, !optOut));
} else {
return true;
}
@@ -1769,20 +1450,21 @@ public void autoGenerateKeysBeforeSaving() {
}
/**
- * Depending on whether a preview or an entry editor is showing, save the current divider location in the correct
- * preference setting.
+ * Depending on whether a preview or an entry editor is showing, save the current divider location in the correct preference setting.
*/
- public void saveDividerLocation() {
+ private void saveDividerLocation(Number position) {
+ if (position == null) {
+ return;
+ }
+
if (mode == BasePanelMode.SHOWING_PREVIEW) {
- int previewPanelHeight = splitPane.getHeight() - splitPane.getDividerLocation();
PreviewPreferences previewPreferences = Globals.prefs.getPreviewPreferences()
- .getBuilder()
- .withPreviewPanelHeight(previewPanelHeight)
- .build();
+ .getBuilder()
+ .withPreviewPanelDividerPosition(position)
+ .build();
Globals.prefs.storePreviewPreferences(previewPreferences);
} else if (mode == BasePanelMode.SHOWING_EDITOR) {
- Globals.prefs.putInt(JabRefPreferences.ENTRY_EDITOR_HEIGHT,
- splitPane.getHeight() - splitPane.getDividerLocation());
+ preferences.setEntryEditorDividerPosition(position.doubleValue());
}
}
@@ -1798,12 +1480,11 @@ public void lostOwnership(Clipboard clipboard, Transferable contents) {
public void cleanUp() {
changeMonitor.ifPresent(DatabaseChangeMonitor::unregister);
- // Check if there is a FileUpdatePanel for this BasePanel being shown. If so,
- // remove it:
- if (sidePaneManager.hasComponent(FileUpdatePanel.class)) {
- FileUpdatePanel fup = (FileUpdatePanel) sidePaneManager.getComponent(FileUpdatePanel.class);
+ // Check if there is a FileUpdatePanel for this BasePanel being shown. If so remove it:
+ if (sidePaneManager.isComponentVisible(SidePaneType.FILE_UPDATE_NOTIFICATION)) {
+ FileUpdatePanel fup = (FileUpdatePanel) sidePaneManager.getComponent(SidePaneType.FILE_UPDATE_NOTIFICATION);
if (fup.getPanel() == this) {
- sidePaneManager.hideComponent(FileUpdatePanel.class);
+ sidePaneManager.hide(SidePaneType.FILE_UPDATE_NOTIFICATION);
}
}
}
@@ -1854,81 +1535,15 @@ private BibEntry getShowing() {
return showing;
}
- /**
- * Update the pointer to the currently shown entry in all cases where the user has moved to a new entry, except when
- * using Back and Forward commands. Also updates history for Back command, and clears history for Forward command.
- *
- * @param entry The entry that is now to be shown.
- */
- private void newEntryShowing(BibEntry entry) {
-
- // If this call is the result of a Back or Forward operation, we must take
- // care not to make any history changes, since the necessary changes will
- // already have been done in the back() or forward() method:
- if (backOrForwardInProgress) {
- showing = entry;
- backOrForwardInProgress = false;
- setBackAndForwardEnabledState();
- return;
- }
- nextEntries.clear();
- if (!Objects.equals(entry, showing)) {
- // Add the entry we are leaving to the history:
- if (showing != null) {
- previousEntries.add(showing);
- if (previousEntries.size() > GUIGlobals.MAX_BACK_HISTORY_SIZE) {
- previousEntries.remove(0);
- }
- }
- showing = entry;
- setBackAndForwardEnabledState();
- }
- }
-
- /**
- * Go back (if there is any recorded history) and update the histories for the Back and Forward commands.
- */
- private void back() {
- if (!previousEntries.isEmpty()) {
- BibEntry toShow = previousEntries.get(previousEntries.size() - 1);
- previousEntries.remove(previousEntries.size() - 1);
- // Add the entry we are going back from to the Forward history:
- if (showing != null) {
- nextEntries.add(showing);
- }
- backOrForwardInProgress = true; // to avoid the history getting updated erroneously
- highlightEntry(toShow);
- }
- }
-
- private void forward() {
- if (!nextEntries.isEmpty()) {
- BibEntry toShow = nextEntries.get(nextEntries.size() - 1);
- nextEntries.remove(nextEntries.size() - 1);
- // Add the entry we are going forward from to the Back history:
- if (showing != null) {
- previousEntries.add(showing);
- }
- backOrForwardInProgress = true; // to avoid the history getting updated erroneously
- highlightEntry(toShow);
- }
- }
-
- public void setBackAndForwardEnabledState() {
- frame.getBackAction().setEnabled(!previousEntries.isEmpty());
- frame.getForwardAction().setEnabled(!nextEntries.isEmpty());
- }
-
- private String formatOutputMessage(String start, int count) {
- return String.format("%s %d %s.", start, count,
- (count > 1 ? Localization.lang("entries") : Localization.lang("entry")));
+ 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().getTabCount(); i++) {
+ for (int i = 0; i < frame.getTabbedPane().getTabs().size(); i++) {
frame.getBasePanelAt(i).setPreviewActive(enabled);
}
}
@@ -2050,15 +1665,7 @@ private class EntryRemovedListener {
@Subscribe
public void listen(EntryRemovedEvent entryRemovedEvent) {
- // if the entry that is displayed in the current entry editor is removed, close the entry editor
- if ((mode == BasePanelMode.SHOWING_EDITOR) && entryEditor.getEntry().equals(entryRemovedEvent.getBibEntry())) {
- entryEditor.close();
- }
-
- BibEntry previewEntry = selectionListener.getPreview().getEntry();
- if ((previewEntry != null) && previewEntry.equals(entryRemovedEvent.getBibEntry())) {
- preview.close();
- }
+ ensureNotShowingBottomPanel(entryRemovedEvent.getBibEntry());
}
}
@@ -2091,7 +1698,6 @@ public void listen(EntryAddedEvent addedEntryEvent) {
@Subscribe
public void listen(EntryChangedEvent entryChangedEvent) {
- frame.getGlobalSearchBar().setDontSelectSearchBar();
frame.getGlobalSearchBar().performSearch();
}
@@ -2102,21 +1708,16 @@ public void listen(EntryRemovedEvent removedEntryEvent) {
}
}
+ @Subscribe
+ public void listen(EntryChangedEvent entryChangedEvent) {
+ this.markBaseChanged();
+ }
+
private class UndoAction implements BaseAction {
@Override
public void action() {
try {
- JComponent focused = Globals.getFocusListener().getFocused();
- if ((focused != null) && (focused instanceof FieldEditor) && focused.hasFocus()) {
- // User is currently editing a field:
- // Check if it is the preamble:
-
- FieldEditor fieldEditor = (FieldEditor) focused;
- if ((preambleEditor != null) && (fieldEditor.equals(preambleEditor.getFieldEditor()))) {
- preambleEditor.storeCurrentEdit();
- }
- }
getUndoManager().undo();
markBaseChanged();
frame.output(Localization.lang("Undo"));
@@ -2151,29 +1752,30 @@ public void action() {
} else {
// No URL or DOI found in the "url" and "doi" fields.
// Look for web links in the "file" field as a fallback:
- FileListEntry entry = null;
- FileListTableModel tm = new FileListTableModel();
- bes.get(0).getField(FieldName.FILE).ifPresent(tm::setContent);
- for (int i = 0; i < tm.getRowCount(); i++) {
- FileListEntry flEntry = tm.getEntry(i);
- if (FieldName.URL.equalsIgnoreCase(flEntry.getType().get().getName())
- || FieldName.PS.equalsIgnoreCase(flEntry.getType().get().getName())
- || FieldName.PDF.equalsIgnoreCase(flEntry.getType().get().getName())) {
- entry = flEntry;
- break;
- }
- }
- if (entry == null) {
- output(Localization.lang("No URL defined") + '.');
- } else {
+
+ List files = bes.get(0).getFiles();
+
+ Optional linkedFile = files.stream()
+ .filter(file -> (FieldName.URL.equalsIgnoreCase(file.getFileType())
+ || FieldName.PS.equalsIgnoreCase(file.getFileType())
+ || FieldName.PDF.equalsIgnoreCase(file.getFileType())))
+ .findFirst();
+
+ if (linkedFile.isPresent()) {
+
try {
- JabRefDesktop.openExternalFileAnyFormat(bibDatabaseContext, entry.getLink(),
- entry.getType());
+
+ JabRefDesktop.openExternalFileAnyFormat(bibDatabaseContext,
+ linkedFile.get().getLink(),
+ ExternalFileTypes.getInstance().fromLinkedFile(linkedFile.get(), true));
+
output(Localization.lang("External viewer called") + '.');
} catch (IOException e) {
output(Localization.lang("Could not open link"));
LOGGER.info("Could not open link", e);
}
+ } else {
+ output(Localization.lang("No URL defined") + '.');
}
}
} else {
@@ -2187,8 +1789,6 @@ private class RedoAction implements BaseAction {
@Override
public void action() {
try {
-
- JComponent focused = Globals.getFocusListener().getFocused();
getUndoManager().redo();
markBaseChanged();
frame.output(Localization.lang("Redo"));
@@ -2225,11 +1825,7 @@ public void action() throws SaveException {
.withInitialDirectory(Globals.prefs.get(JabRefPreferences.WORKING_DIRECTORY))
.build();
- DialogService ds = new FXDialogService();
-
- Optional chosenFile = DefaultTaskExecutor
- .runInJavaFXThread(() -> ds.showFileSaveDialog(fileDialogConfiguration));
-
+ Optional chosenFile = dialogService.showFileSaveDialog(fileDialogConfiguration);
if (chosenFile.isPresent()) {
Path path = chosenFile.get();
saveDatabase(path.toFile(), true, Globals.prefs.getDefaultEncoding(), saveType);
diff --git a/src/main/java/org/jabref/gui/BasePanelPreferences.java b/src/main/java/org/jabref/gui/BasePanelPreferences.java
new file mode 100644
index 00000000000..c248f869dcc
--- /dev/null
+++ b/src/main/java/org/jabref/gui/BasePanelPreferences.java
@@ -0,0 +1,80 @@
+package org.jabref.gui;
+
+import javafx.beans.property.DoubleProperty;
+import javafx.beans.property.SimpleDoubleProperty;
+
+import org.jabref.Globals;
+import org.jabref.gui.autocompleter.AutoCompletePreferences;
+import org.jabref.gui.entryeditor.EntryEditorPreferences;
+import org.jabref.gui.keyboard.KeyBindingRepository;
+import org.jabref.gui.maintable.MainTablePreferences;
+import org.jabref.preferences.JabRefPreferences;
+import org.jabref.preferences.PreviewPreferences;
+
+import org.fxmisc.easybind.EasyBind;
+
+public class BasePanelPreferences {
+ private MainTablePreferences tablePreferences;
+ private AutoCompletePreferences autoCompletePreferences;
+ private EntryEditorPreferences entryEditorPreferences;
+ private KeyBindingRepository keyBindings;
+ private PreviewPreferences previewPreferences;
+ private DoubleProperty entryEditorDividerPosition = new SimpleDoubleProperty();
+
+ public BasePanelPreferences(MainTablePreferences tablePreferences, AutoCompletePreferences autoCompletePreferences, EntryEditorPreferences entryEditorPreferences, KeyBindingRepository keyBindings, PreviewPreferences previewPreferences, Double entryEditorDividerPosition) {
+ this.tablePreferences = tablePreferences;
+ this.autoCompletePreferences = autoCompletePreferences;
+ this.entryEditorPreferences = entryEditorPreferences;
+ this.keyBindings = keyBindings;
+ this.previewPreferences = previewPreferences;
+ this.entryEditorDividerPosition.setValue(entryEditorDividerPosition);
+ }
+
+ public static BasePanelPreferences from(JabRefPreferences preferences) {
+ BasePanelPreferences basePanelPreferences = new BasePanelPreferences(
+ preferences.getMainTablePreferences(),
+ preferences.getAutoCompletePreferences(),
+ EntryEditorPreferences.from(preferences),
+ Globals.getKeyPrefs(),
+ preferences.getPreviewPreferences(),
+ preferences.getDouble(JabRefPreferences.ENTRY_EDITOR_HEIGHT));
+ EasyBind.subscribe(basePanelPreferences.entryEditorDividerPosition, value -> preferences.putDouble(JabRefPreferences.ENTRY_EDITOR_HEIGHT, value.doubleValue()));
+ return basePanelPreferences;
+ }
+
+ public double getEntryEditorDividerPosition() {
+ return entryEditorDividerPosition.get();
+ }
+
+ public void setEntryEditorDividerPosition(double entryEditorDividerPosition) {
+ this.entryEditorDividerPosition.set(entryEditorDividerPosition);
+ }
+
+ public DoubleProperty entryEditorDividerPositionProperty() {
+ return entryEditorDividerPosition;
+ }
+
+ public MainTablePreferences getTablePreferences() {
+ return tablePreferences;
+ }
+
+ public AutoCompletePreferences getAutoCompletePreferences() {
+ return autoCompletePreferences;
+ }
+
+ public void setAutoCompletePreferences(AutoCompletePreferences autoCompletePreferences) {
+ this.autoCompletePreferences = autoCompletePreferences;
+ }
+
+ public EntryEditorPreferences getEntryEditorPreferences() {
+ return entryEditorPreferences;
+ }
+
+ public KeyBindingRepository getKeyBindings() {
+ return keyBindings;
+ }
+
+ public PreviewPreferences getPreviewPreferences() {
+ return previewPreferences;
+ }
+}
diff --git a/src/main/java/org/jabref/gui/ClipBoardManager.java b/src/main/java/org/jabref/gui/ClipBoardManager.java
index a79eb860213..eddcd6a67de 100644
--- a/src/main/java/org/jabref/gui/ClipBoardManager.java
+++ b/src/main/java/org/jabref/gui/ClipBoardManager.java
@@ -11,13 +11,19 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
+import java.util.stream.Collectors;
+
+import javafx.scene.input.ClipboardContent;
import org.jabref.Globals;
+import org.jabref.logic.bibtex.BibEntryWriter;
+import org.jabref.logic.bibtex.LatexFieldFormatter;
import org.jabref.logic.importer.FetcherException;
import org.jabref.logic.importer.ImportException;
import org.jabref.logic.importer.ImportFormatReader;
import org.jabref.logic.importer.ImportFormatReader.UnknownFormatImport;
import org.jabref.logic.importer.fetcher.DoiFetcher;
+import org.jabref.model.database.BibDatabaseMode;
import org.jabref.model.entry.BibEntry;
import org.jabref.model.entry.identifier.DOI;
@@ -77,10 +83,40 @@ public String getClipboardContents() {
return result;
}
+ public void setClipboardHtmlContent(String html) {
+ // TODO: This works on Mac and Windows 10, but not on Ubuntu 16.04
+ final javafx.scene.input.Clipboard clipboard = javafx.scene.input.Clipboard.getSystemClipboard();
+ final ClipboardContent content = new ClipboardContent();
+ content.putHtml(html);
+ clipboard.setContent(content);
+ }
+
+ public void setClipboardContent(String string) {
+ // TODO: This works on Mac and Windows 10, but not on Ubuntu 16.04
+ final javafx.scene.input.Clipboard clipboard = javafx.scene.input.Clipboard.getSystemClipboard();
+ final ClipboardContent content = new ClipboardContent();
+ content.putString(string);
+ clipboard.setContent(content);
+ }
+
+ public void setClipboardContent(List entries) throws IOException {
+ // TODO: This works on Mac and Windows 10, but not on Ubuntu 16.04
+ final javafx.scene.input.Clipboard clipboard = javafx.scene.input.Clipboard.getSystemClipboard();
+ final ClipboardContent content = new ClipboardContent();
+ BibEntryWriter writer = new BibEntryWriter(new LatexFieldFormatter(Globals.prefs.getLatexFieldFormatterPreferences()), false);
+ String serializedEntries = writer.serializeAll(entries, BibDatabaseMode.BIBTEX);
+ content.put(DragAndDropDataFormats.ENTRIES, serializedEntries);
+ content.putString(serializedEntries);
+ clipboard.setContent(content);
+ }
+
/**
* Place a String on the clipboard, and make this class the
* owner of the Clipboard's contents.
+ *
+ * @deprecated use {@link #setClipboardContent(String)} instead
*/
+ @Deprecated
public void setClipboardContents(String aString) {
StringSelection stringSelection = new StringSelection(aString);
clipboard.setContents(stringSelection, this);
@@ -96,7 +132,8 @@ public List extractBibEntriesFromClipboard() {
try {
@SuppressWarnings("unchecked")
List contents = (List) content.getTransferData(TransferableBibtexEntry.ENTRY_FLAVOR);
- result = contents;
+ // We clone the entries to make sure we don't accidentally paste references
+ result = contents.stream().map(entry -> (BibEntry) entry.clone()).collect(Collectors.toList());
} catch (UnsupportedFlavorException | ClassCastException ex) {
LOGGER.warn("Could not paste this type", ex);
} catch (IOException ex) {
diff --git a/src/main/java/org/jabref/gui/Dark.css b/src/main/java/org/jabref/gui/Dark.css
new file mode 100644
index 00000000000..8df9935c460
--- /dev/null
+++ b/src/main/java/org/jabref/gui/Dark.css
@@ -0,0 +1,52 @@
+.root {
+ -jr-theme: #2c9490;
+ -jr-accent: #255652;
+ -jr-selected: -jr-accent;
+ -jr-hover: #fff1;
+
+
+ -jr-red: #b71c1f;
+ -jr-light-red: #db1d2b;
+ -jr-green: #1cb631;
+ -jr-light-green: #28d93c;
+ -jr-blue: #2c2cb7;
+ -jr-light-blue: #3a3ad9;
+ -jr-purple: #b72486;
+ -jr-light-purple: #d927a8;
+ -jr-yellow: #b5b021;
+ -jr-orange: #b77620;
+
+ -jr-base: #141824;
+
+ -jr-background-alt: #151924;
+ -jr-menu-background: #141824;
+ -jr-toolbar: -jr-menu-background;
+ -jr-sidepane-background: #212330;
+ -jr-search-background: #2c2e3b;
+
+ -jr-sidepane-header-background: -jr-background-alt;
+ -jr-group-hits-bg: -jr-background-alt;
+ -jr-group-hits-fg: -fx-light-text-color;
+ -fx-control-inner-background: #272b38;
+
+ -fx-control-inner-background-alt: -fx-control-inner-background;
+
+ -fx-dark-text-color: black;
+ -fx-mid-text-color: #7d8591;
+ -fx-light-text-color: #9aa3af;
+ -jr-separator: #333744;
+ -fx-outer-border: #424758;
+
+ -jr-icon: -fx-light-text-color;
+ -jr-icon-active: derive(-fx-light-text-color, 50%);
+ -jr-icon-background-active: -jr-hover;
+ -jr-icon-background-armed: #fff2;
+
+ -jr-menu-foreground: -fx-light-text-color;
+ -jr-menu-item-foreground: -fx-light-text-color;
+ -jr-menu-forground-active: derive(-fx-light-text-color, 50%);
+
+ -fx-focused-text-base-color: -fx-dark-text-color;
+
+ -jr-tooltip-fg: derive(-fx-light-text-color, 50%);
+}
diff --git a/src/main/java/org/jabref/gui/DefaultInjector.java b/src/main/java/org/jabref/gui/DefaultInjector.java
index ee81dd7072d..e94e6fe8697 100644
--- a/src/main/java/org/jabref/gui/DefaultInjector.java
+++ b/src/main/java/org/jabref/gui/DefaultInjector.java
@@ -38,6 +38,8 @@ private static Object createDependency(Class> clazz) {
return Globals.stateManager;
} else if (clazz == FileUpdateMonitor.class) {
return Globals.getFileUpdateMonitor();
+ } else if (clazz == ClipBoardManager.class) {
+ return Globals.clipboardManager;
} else {
try {
return clazz.newInstance();
@@ -57,4 +59,14 @@ public T instantiatePresenter(Class clazz, Function injec
return Injector.instantiatePresenter(clazz, injectionContext);
}
+
+ @Override
+ public void injectMembers(Object instance, Function injectionContext) {
+ LOGGER.debug("Inject into " + instance.getClass().getName());
+
+ // Use our own method to construct dependencies
+ Injector.setInstanceSupplier(DefaultInjector::createDependency);
+
+ Injector.injectMembers(instance, injectionContext);
+ }
}
diff --git a/src/main/java/org/jabref/gui/DialogService.java b/src/main/java/org/jabref/gui/DialogService.java
index 1ddbe50a90c..a19f831470c 100644
--- a/src/main/java/org/jabref/gui/DialogService.java
+++ b/src/main/java/org/jabref/gui/DialogService.java
@@ -1,14 +1,18 @@
package org.jabref.gui;
import java.nio.file.Path;
+import java.util.Collection;
import java.util.List;
import java.util.Optional;
+import java.util.function.Consumer;
import javafx.concurrent.Task;
import javafx.print.PrinterJob;
import javafx.scene.control.Alert;
import javafx.scene.control.ButtonType;
+import javafx.scene.control.ChoiceDialog;
import javafx.scene.control.DialogPane;
+import javafx.scene.control.TextInputDialog;
import org.jabref.gui.util.DirectoryDialogConfiguration;
import org.jabref.gui.util.FileDialogConfiguration;
@@ -21,6 +25,23 @@
*/
public interface DialogService {
+ /**
+ * This will create and display new {@link ChoiceDialog} of type T with a default choice and a collection of possible choices
+ *
+ * @implNote The implementation should accept {@code null} for {@code defaultChoice}, but callers should use {@link #showChoiceDialogAndWait(String, String, String, Collection)}.
+ */
+ Optional showChoiceDialogAndWait(String title, String content, String okButtonLabel, T defaultChoice, Collection choices);
+
+ /**
+ * This will create and display new {@link ChoiceDialog} of type T with a collection of possible choices
+ */
+ default Optional showChoiceDialogAndWait(String title, String content, String okButtonLabel, Collection choices) {
+ return showChoiceDialogAndWait(title, content, okButtonLabel, null, choices);
+ }
+
+ /**
+ * This will create and display new {@link TextInputDialog} with a text fields to enter data
+ */
Optional showInputDialogAndWait(String title, String content);
/**
@@ -64,6 +85,13 @@ default void showErrorDialogAndWait(Exception exception) {
showErrorDialogAndWait(Localization.lang("Unhandled exception occurred."), exception);
}
+ /**
+ * Create and display error dialog displaying the given exception.
+ *
+ * @param exception the exception causing the error
+ */
+ void showErrorDialogAndWait(String title, String content, Throwable exception);
+
/**
* Create and display error dialog displaying the given message.
*
@@ -85,7 +113,7 @@ default void showErrorDialogAndWait(Exception exception) {
* Create and display a new confirmation dialog.
* It will include a blue question icon on the left and
* a OK (with given label) and Cancel button. To create a confirmation dialog with custom
- * buttons see also {@link #showCustomButtonDialogAndWait(Alert.AlertType, String, String, ButtonType...)}
+ * buttons see also {@link #showCustomButtonDialogAndWait(Alert.AlertType, String, String, ButtonType...)}.
*
* @return true if the use clicked "OK" otherwise false
*/
@@ -95,12 +123,37 @@ default void showErrorDialogAndWait(Exception exception) {
* Create and display a new confirmation dialog.
* It will include a blue question icon on the left and
* a OK (with given label) and Cancel (also with given label) button. To create a confirmation dialog with custom
- * buttons see also {@link #showCustomButtonDialogAndWait(Alert.AlertType, String, String, ButtonType...)}
+ * buttons see also {@link #showCustomButtonDialogAndWait(Alert.AlertType, String, String, ButtonType...)}.
*
* @return true if the use clicked "OK" otherwise false
*/
boolean showConfirmationDialogAndWait(String title, String content, String okButtonLabel, String cancelButtonLabel);
+ /**
+ * Create and display a new confirmation dialog.
+ * It will include a blue question icon on the left and
+ * a YES (with given label) and Cancel (also with given label) button. To create a confirmation dialog with custom
+ * buttons see also {@link #showCustomButtonDialogAndWait(Alert.AlertType, String, String, ButtonType...)}.
+ * Moreover, the dialog contains a opt-out checkbox with the given text to support "Do not ask again"-behaviour.
+ *
+ * @return true if the use clicked "YES" otherwise false
+ */
+ boolean showConfirmationDialogWithOptOutAndWait(String title, String content,
+ String optOutMessage, Consumer optOutAction);
+
+ /**
+ * Create and display a new confirmation dialog.
+ * It will include a blue question icon on the left and
+ * a YES (with given label) and Cancel (also with given label) button. To create a confirmation dialog with custom
+ * buttons see also {@link #showCustomButtonDialogAndWait(Alert.AlertType, String, String, ButtonType...)}.
+ * Moreover, the dialog contains a opt-out checkbox with the given text to support "Do not ask again"-behaviour.
+ *
+ * @return true if the use clicked "YES" otherwise false
+ */
+ boolean showConfirmationDialogWithOptOutAndWait(String title, String content,
+ String okButtonLabel, String cancelButtonLabel,
+ String optOutMessage, Consumer optOutAction);
+
/**
* This will create and display a new dialog of the specified
* {@link Alert.AlertType} but with user defined buttons as optional
@@ -109,7 +162,7 @@ default void showErrorDialogAndWait(Exception exception) {
* @return Optional with the pressed Button as ButtonType
*/
Optional showCustomButtonDialogAndWait(Alert.AlertType type, String title, String content,
- ButtonType... buttonTypes);
+ ButtonType... buttonTypes);
/**
* This will create and display a new dialog showing a custom {@link DialogPane}
@@ -191,4 +244,5 @@ Optional showCustomButtonDialogAndWait(Alert.AlertType type, String
* @return false if the user opts to cancel printing
*/
boolean showPrintDialog(PrinterJob job);
+
}
diff --git a/src/main/java/org/jabref/gui/DragAndDropDataFormats.java b/src/main/java/org/jabref/gui/DragAndDropDataFormats.java
index 9ffe3125c10..bee99cbd86a 100644
--- a/src/main/java/org/jabref/gui/DragAndDropDataFormats.java
+++ b/src/main/java/org/jabref/gui/DragAndDropDataFormats.java
@@ -9,5 +9,5 @@ public class DragAndDropDataFormats {
public static final DataFormat GROUP = new DataFormat("dnd/org.jabref.model.groups.GroupTreeNode");
public static final DataFormat LINKED_FILE = new DataFormat("dnd/org.jabref.model.entry.LinkedFile");
- public static final DataFormat ENTRIES = new DataFormat("application/x-java-jvm-local-objectref");
+ public static final DataFormat ENTRIES = new DataFormat("dnd/org.jabref.model.entry.BibEntries");
}
diff --git a/src/main/java/org/jabref/gui/DragDropPane.java b/src/main/java/org/jabref/gui/DragDropPane.java
index 4382b1cfea5..99890bce454 100644
--- a/src/main/java/org/jabref/gui/DragDropPane.java
+++ b/src/main/java/org/jabref/gui/DragDropPane.java
@@ -15,10 +15,11 @@
import javax.swing.JTabbedPane;
import javax.swing.SwingUtilities;
+import org.jabref.gui.icon.IconTheme;
+import org.jabref.gui.icon.JabRefIcon;
+
/**
* Extends the JTabbedPane class to support Drag&Drop of Tabs.
- *
- * @author kleinms, strassfn
*/
class DragDropPane extends JTabbedPane {
@@ -124,14 +125,14 @@ public void mouseReleased(MouseEvent e) {
static class MarkerPane extends JPanel {
private Point locationP;
- private final IconTheme.JabRefIcon moveTabArrow;
+ private final JabRefIcon moveTabArrow;
public MarkerPane() {
setOpaque(false);
// Sets the marker fontIcon
- moveTabArrow = IconTheme.JabRefIcon.MOVE_TAB_ARROW;
+ moveTabArrow = IconTheme.JabRefIcons.MOVE_TAB_ARROW;
}
@Override
@@ -141,7 +142,7 @@ public void paintComponent(Graphics g) {
g2.setComposite(AlphaComposite.getInstance(
AlphaComposite.SRC_OVER, 0.9f)); // Set transparency
g.setFont(IconTheme.FONT.deriveFont(Font.BOLD, 24f));
- g.drawString(moveTabArrow.getCode(), locationP.x - (moveTabArrow.getIcon().getIconWidth() / 2),
+ moveTabArrow.getSmallIcon().paintIcon(this, g, locationP.x - (moveTabArrow.getIcon().getIconWidth() / 2),
locationP.y + (moveTabArrow.getIcon().getIconHeight() / 2));
}
diff --git a/src/main/java/org/jabref/gui/DuplicateResolverDialog.java b/src/main/java/org/jabref/gui/DuplicateResolverDialog.java
index baae8dac19a..2151be6eaa3 100644
--- a/src/main/java/org/jabref/gui/DuplicateResolverDialog.java
+++ b/src/main/java/org/jabref/gui/DuplicateResolverDialog.java
@@ -6,6 +6,7 @@
import javax.swing.Box;
import javax.swing.JButton;
+import javax.swing.JFrame;
import javax.swing.JPanel;
import org.jabref.gui.help.HelpAction;
@@ -45,7 +46,7 @@ public enum DuplicateResolverResult {
private MergeEntries me;
public DuplicateResolverDialog(JabRefFrame frame, BibEntry one, BibEntry two, DuplicateResolverType type) {
- super(frame, Localization.lang("Possible duplicate entries"), true, DuplicateResolverDialog.class);
+ super((JFrame) null, Localization.lang("Possible duplicate entries"), true, DuplicateResolverDialog.class);
this.frame = frame;
init(one, two, type);
}
diff --git a/src/main/java/org/jabref/gui/DuplicateSearch.java b/src/main/java/org/jabref/gui/DuplicateSearch.java
index fd6537ea3c5..f1a1a8266e6 100644
--- a/src/main/java/org/jabref/gui/DuplicateSearch.java
+++ b/src/main/java/org/jabref/gui/DuplicateSearch.java
@@ -10,6 +10,7 @@
import org.jabref.JabRefGUI;
import org.jabref.gui.DuplicateResolverDialog.DuplicateResolverResult;
import org.jabref.gui.DuplicateResolverDialog.DuplicateResolverType;
+import org.jabref.gui.actions.SimpleCommand;
import org.jabref.gui.undo.NamedCompound;
import org.jabref.gui.undo.UndoableInsertEntry;
import org.jabref.gui.undo.UndoableRemoveEntry;
@@ -20,19 +21,24 @@
import spin.Spin;
-public class DuplicateSearch implements Runnable {
+public class DuplicateSearch extends SimpleCommand {
- private final BasePanel panel;
+ private final JabRefFrame frame;
private List bes;
private final List> duplicates = new ArrayList<>();
-
- public DuplicateSearch(BasePanel bp) {
- panel = bp;
+ public DuplicateSearch(JabRefFrame frame) {
+ this.frame = frame;
}
@Override
+ public void execute() {
+ JabRefExecutorService.INSTANCE.execute(() -> run());
+
+ }
+
public void run() {
+ BasePanel panel = frame.getCurrentBasePanel();
panel.output(Localization.lang("Searching for duplicates..."));
@@ -125,7 +131,7 @@ public void run() {
if (!toAdd.isEmpty()) {
for (BibEntry entry : toAdd) {
panel.getDatabase().insertEntry(entry);
- ce.addEdit(new UndoableInsertEntry(panel.getDatabase(), entry, panel));
+ ce.addEdit(new UndoableInsertEntry(panel.getDatabase(), entry));
}
panel.markBaseChanged();
}
@@ -149,6 +155,7 @@ class SearcherRunnable implements Runnable {
@Override
public void run() {
+ BasePanel panel = frame.getCurrentBasePanel();
for (int i = 0; (i < (bes.size() - 1)) && !finished; i++) {
for (int j = i + 1; (j < bes.size()) && !finished; j++) {
BibEntry first = bes.get(i);
@@ -191,7 +198,6 @@ static class DuplicateCallBack implements CallBack {
private final DuplicateResolverType dialogType;
private BibEntry merged;
-
public DuplicateCallBack(JabRefFrame frame, BibEntry one, BibEntry two, DuplicateResolverType dialogType) {
this.frame = frame;
this.one = one;
diff --git a/src/main/java/org/jabref/gui/EntryMarker.java b/src/main/java/org/jabref/gui/EntryMarker.java
deleted file mode 100644
index 8237304aced..00000000000
--- a/src/main/java/org/jabref/gui/EntryMarker.java
+++ /dev/null
@@ -1,199 +0,0 @@
-package org.jabref.gui;
-
-import java.util.Set;
-import java.util.TreeSet;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import org.jabref.Globals;
-import org.jabref.gui.undo.NamedCompound;
-import org.jabref.gui.undo.UndoableFieldChange;
-import org.jabref.model.database.BibDatabase;
-import org.jabref.model.entry.BibEntry;
-import org.jabref.model.entry.FieldName;
-import org.jabref.preferences.JabRefPreferences;
-
-public class EntryMarker {
-
- public static final int MARK_COLOR_LEVELS = 6;
- public static final int MAX_MARKING_LEVEL = MARK_COLOR_LEVELS - 1;
- public static final int IMPORT_MARK_LEVEL = MARK_COLOR_LEVELS;
-
- private static final Pattern MARK_NUMBER_PATTERN = Pattern.compile(JabRefPreferences.getInstance().MARKING_WITH_NUMBER_PATTERN);
-
- private EntryMarker() {
- }
-
- /**
- * @param increment whether the given increment should be added to the current one. Currently never used in JabRef. Could be used to increase marking color ("Mark in specific color").
- */
- public static void markEntry(BibEntry be, int markIncrement, boolean increment, NamedCompound ce) {
- int prevMarkLevel;
- String newValue = null;
- if (be.hasField(FieldName.MARKED_INTERNAL)) {
- String markerString = be.getField(FieldName.MARKED_INTERNAL).get();
- int index = markerString.indexOf(Globals.prefs.getWrappedUsername());
- if (index >= 0) {
- // Already marked 1 for this user.
- prevMarkLevel = 1;
- newValue = markerString.substring(0, index)
- + markerString.substring(index + Globals.prefs.getWrappedUsername().length())
- + Globals.prefs.getWrappedUsername().substring(0,
- Globals.prefs.getWrappedUsername().length() - 1)
- + ":" + (increment ? Math.min(MAX_MARKING_LEVEL, prevMarkLevel + markIncrement) : markIncrement)
- + "]";
- } else {
- Matcher m = MARK_NUMBER_PATTERN.matcher(markerString);
- if (m.find()) {
- try {
- prevMarkLevel = Integer.parseInt(m.group(1));
- newValue = markerString.substring(0, m.start(1)) + (increment ? Math.min(MAX_MARKING_LEVEL, prevMarkLevel + markIncrement) : markIncrement) + markerString.substring(m.end(1));
- } catch (NumberFormatException ex) {
- // Do nothing.
- }
- }
- }
- }
- if (newValue == null) {
- newValue = Globals.prefs.getWrappedUsername().substring(0, Globals.prefs.getWrappedUsername().length() - 1) + ":" + markIncrement + "]";
- }
-
- ce.addEdit(new UndoableFieldChange(be, FieldName.MARKED_INTERNAL,
- be.getField(FieldName.MARKED_INTERNAL).orElse(null), newValue));
- be.setField(FieldName.MARKED_INTERNAL, newValue);
- }
-
- /**
- * SIDE EFFECT: Unselects given entry
- */
- public static void unmarkEntry(BibEntry be, boolean onlyMaxLevel, BibDatabase database, NamedCompound ce) {
- if (be.hasField(FieldName.MARKED_INTERNAL)) {
- String markerString = be.getField(FieldName.MARKED_INTERNAL).get();
- if ("0".equals(markerString)) {
- if (!onlyMaxLevel) {
- unmarkOldStyle(be, database, ce);
- }
- return;
- }
- String newValue = null;
- int index = markerString.indexOf(Globals.prefs.getWrappedUsername());
- if (index >= 0) {
- // Marked 1 for this user.
- if (onlyMaxLevel) {
- return;
- } else {
- newValue = markerString.substring(0, index)
- + markerString.substring(index + Globals.prefs.getWrappedUsername().length());
- }
- } else {
- Matcher m = MARK_NUMBER_PATTERN.matcher(markerString);
- if (m.find()) {
- try {
- int prevMarkLevel = Integer.parseInt(m.group(1));
- if (!onlyMaxLevel || (prevMarkLevel == MARK_COLOR_LEVELS)) {
- if (prevMarkLevel > 1) {
- newValue = markerString.substring(0, m.start(1)) + markerString.substring(m.end(1));
- } else {
- String toRemove = Globals.prefs.getWrappedUsername().substring(0,
- Globals.prefs.getWrappedUsername().length() - 1) + ":1]";
- index = markerString.indexOf(toRemove);
- if (index >= 0) {
- newValue = markerString.substring(0, index) + markerString.substring(index + toRemove.length());
- }
- }
- } else {
- return;
- }
- } catch (NumberFormatException ex) {
- // Do nothing.
- }
- }
- }
-
- /*int piv = 0, hit;
- StringBuffer sb = new StringBuffer();
- while ((hit = s.indexOf(G047749118118
- 1110lobals.prefs.WRAPPED_USERNAME, piv)) >= 0) {
- if (hit > 0)
- sb.append(s.substring(piv, hit));
- piv = hit + Globals.prefs.WRAPPED_USERNAME.length();
- }
- if (piv < s.length() - 1) {
- sb.append(s.substring(piv));
- }
- String newVal = sb.length() > 0 ? sb.toString() : null;*/
- ce.addEdit(new UndoableFieldChange(be, FieldName.MARKED_INTERNAL,
- be.getField(FieldName.MARKED_INTERNAL).get(), newValue));
- if (newValue == null) {
- be.clearField(FieldName.MARKED_INTERNAL);
- } else {
- be.setField(FieldName.MARKED_INTERNAL, newValue);
- }
- }
- }
-
- /**
- * An entry is marked with a "0", not in the new style with user names. We
- * want to unmark it as transparently as possible. Since this shouldn't
- * happen too often, we do it by scanning the "owner" fields of the entire
- * database, collecting all user names. We then mark the entry for all users
- * except the current one. Thus only the user who unmarks will see that it
- * is unmarked, and we get rid of the old-style marking.
- *
- * @param be
- * @param ce
- */
- private static void unmarkOldStyle(BibEntry be, BibDatabase database, NamedCompound ce) {
- Set