package org.jabref;
import java.awt.Frame;
import java.io.File;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import javax.swing.JOptionPane;
import javax.swing.UIDefaults;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.plaf.FontUIResource;
import javax.swing.plaf.metal.MetalLookAndFeel;
import org.jabref.gui.BasePanel;
import org.jabref.gui.GUIGlobals;
import org.jabref.gui.JabRefFrame;
import org.jabref.gui.autosaveandbackup.BackupUIManager;
import org.jabref.gui.importer.ParserResultWarningDialog;
import org.jabref.gui.importer.actions.OpenDatabaseAction;
import org.jabref.gui.shared.SharedDatabaseUIManager;
import org.jabref.gui.worker.VersionWorker;
import org.jabref.logic.autosaveandbackup.BackupManager;
import org.jabref.logic.importer.OpenDatabase;
import org.jabref.logic.importer.ParserResult;
import org.jabref.logic.l10n.Localization;
import org.jabref.logic.util.OS;
import org.jabref.logic.util.Version;
import org.jabref.preferences.JabRefPreferences;
import org.jabref.shared.exception.DatabaseNotSupportedException;
import org.jabref.shared.exception.InvalidDBMSConnectionPropertiesException;
import org.jabref.shared.exception.NotASharedDatabaseException;
import com.jgoodies.looks.plastic.Plastic3DLookAndFeel;
import com.jgoodies.looks.plastic.theme.SkyBluer;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class JabRefGUI {
private static final Log LOGGER = LogFactory.getLog(JabRefGUI.class);
private static JabRefFrame mainFrame;
private final List<ParserResult> bibDatabases;
private final boolean isBlank;
private final List<ParserResult> failed = new ArrayList<>();
private final List<ParserResult> toOpenTab = new ArrayList<>();
private String focusedFile;
public JabRefGUI(List<ParserResult> argsDatabases, boolean isBlank) {
this.bibDatabases = argsDatabases;
this.isBlank = isBlank;
// passed file (we take the first one) should be focused
focusedFile = argsDatabases.stream().findFirst().flatMap(ParserResult::getFile).map(File::getAbsolutePath)
.orElse(Globals.prefs.get(JabRefPreferences.LAST_FOCUSED));
openWindow();
JabRefGUI.checkForNewVersion(false);
}
public static void checkForNewVersion(boolean manualExecution) {
Version toBeIgnored = Globals.prefs.getVersionPreferences().getIgnoredVersion();
Version currentVersion = Globals.BUILD_INFO.getVersion();
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");
}
// Set antialiasing on everywhere. This only works in JRE >= 1.5.
// Or... it doesn't work, period.
// TODO test and maybe remove this! I found this commented out with no additional info ( payload@lavabit.com )
// Enabled since JabRef 2.11 beta 4
System.setProperty("swing.aatext", "true");
// Default is "on".
// "lcd" instead of "on" because of http://wiki.netbeans.org/FaqFontRendering and http://docs.oracle.com/javase/6/docs/technotes/guides/2d/flags.html#aaFonts
System.setProperty("awt.useSystemAAFontSettings", "lcd");
// look and feel. This MUST be the first thing to do before loading any Swing-specific code!
setLookAndFeel();
// If the option is enabled, open the last edited libraries, if any.
if (!isBlank && Globals.prefs.getBoolean(JabRefPreferences.OPEN_LAST_EDITED)) {
openLastEditedDatabases();
}
GUIGlobals.init();
LOGGER.debug("Initializing frame");
JabRefGUI.mainFrame = new JabRefFrame();
// Add all bibDatabases databases to the frame:
boolean first = false;
if (!bibDatabases.isEmpty()) {
for (Iterator<ParserResult> parserResultIterator = bibDatabases.iterator(); parserResultIterator.hasNext();) {
ParserResult pr = parserResultIterator.next();
// Define focused tab
if (pr.getFile().get().getAbsolutePath().equals(focusedFile)) {
first = true;
}
if (pr.isInvalid()) {
failed.add(pr);
parserResultIterator.remove();
} else if (pr.getDatabase().isShared()) {
try {
new SharedDatabaseUIManager(mainFrame).openSharedDatabaseFromParserResult(pr);
} catch (SQLException | DatabaseNotSupportedException | InvalidDBMSConnectionPropertiesException |
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);
}
toOpenTab.add(pr);
} else if (pr.toOpenTab()) {
// things to be appended to an opened tab should be done after opening all tabs
// add them to the list
toOpenTab.add(pr);
} else {
JabRefGUI.getMainFrame().addParserResult(pr, first);
first = false;
}
}
}
// finally add things to the currently opened tab
for (ParserResult pr : toOpenTab) {
JabRefGUI.getMainFrame().addParserResult(pr, first);
first = false;
}
// If we are set to remember the window location, we also remember the maximised
// state. This needs to be set after the window has been made visible, so we
// do it here:
if (Globals.prefs.getBoolean(JabRefPreferences.WINDOW_MAXIMISED)) {
JabRefGUI.getMainFrame().setExtendedState(Frame.MAXIMIZED_BOTH);
}
JabRefGUI.getMainFrame().setVisible(true);
for (ParserResult pr : failed) {
String message = "<html>" + Localization.lang("Error opening file '%0'.", pr.getFile().get().getName())
+ "<p>"
+ pr.getErrorMessage() + "</html>";
JOptionPane.showMessageDialog(JabRefGUI.getMainFrame(), message, Localization.lang("Error opening file"),
JOptionPane.ERROR_MESSAGE);
}
// Display warnings, if any
int tabNumber = 0;
for (ParserResult pr : bibDatabases) {
ParserResultWarningDialog.showParserResultWarningDialog(pr, JabRefGUI.getMainFrame(), tabNumber++);
}
// After adding the databases, go through each and see if
// any post open actions need to be done. For instance, checking
// if we found new entry types that can be imported, or checking
// if the database contents should be modified due to new features
// in this version of JabRef.
// Note that we have to check whether i does not go over getBasePanelCount().
// This is because importToOpen might have been used, which adds to
// loadedDatabases, but not to getBasePanelCount()
for (int i = 0; (i < bibDatabases.size()) && (i < JabRefGUI.getMainFrame().getBasePanelCount()); i++) {
ParserResult pr = bibDatabases.get(i);
BasePanel panel = JabRefGUI.getMainFrame().getBasePanelAt(i);
OpenDatabaseAction.performPostOpenActions(panel, pr);
}
LOGGER.debug("Finished adding panels");
if (!bibDatabases.isEmpty()) {
JabRefGUI.getMainFrame().getCurrentBasePanel().getMainTable().requestFocus();
}
}
private void openLastEditedDatabases() {
if (Globals.prefs.get(JabRefPreferences.LAST_EDITED) == null) {
return;
}
List<String> lastFiles = Globals.prefs.getStringList(JabRefPreferences.LAST_EDITED);
for (String fileName : lastFiles) {
File dbFile = new File(fileName);
// Already parsed via command line parameter, e.g., "jabref.jar somefile.bib"
if (isLoaded(dbFile) || !dbFile.exists()) {
continue;
}
if (BackupManager.checkForBackupFile(dbFile.toPath())) {
BackupUIManager.showRestoreBackupDialog(mainFrame, dbFile.toPath());
}
ParserResult parsedDatabase = OpenDatabase.loadDatabase(fileName,
Globals.prefs.getImportFormatPreferences());
if (parsedDatabase.isEmpty()) {
LOGGER.error(Localization.lang("Error opening file") + " '" + dbFile.getPath() + "'");
} else {
bibDatabases.add(parsedDatabase);
}
}
}
private boolean isLoaded(File fileToOpen) {
for (ParserResult pr : bibDatabases) {
if (pr.getFile().isPresent() && pr.getFile().get().equals(fileToOpen)) {
return true;
}
}
return false;
}
private void setLookAndFeel() {
try {
String lookFeel;
String systemLookFeel = UIManager.getSystemLookAndFeelClassName();
if (Globals.prefs.getBoolean(JabRefPreferences.USE_DEFAULT_LOOK_AND_FEEL)) {
// FIXME: Problems with OpenJDK and GTK L&F
// See https://github.com/JabRef/jabref/issues/393, https://github.com/JabRef/jabref/issues/638
if (System.getProperty("java.runtime.name").contains("OpenJDK")) {
// Metal L&F
lookFeel = UIManager.getCrossPlatformLookAndFeelClassName();
LOGGER.warn(
"There seem to be problems with OpenJDK and the default GTK Look&Feel. Using Metal L&F instead. Change to another L&F with caution.");
} else {
lookFeel = systemLookFeel;
}
} else {
lookFeel = Globals.prefs.get(JabRefPreferences.WIN_LOOK_AND_FEEL);
}
// FIXME: Open JDK problem
if (UIManager.getCrossPlatformLookAndFeelClassName().equals(lookFeel)
&& !System.getProperty("java.runtime.name").contains("OpenJDK")) {
// try to avoid ending up with the ugly Metal L&F
Plastic3DLookAndFeel lnf = new Plastic3DLookAndFeel();
MetalLookAndFeel.setCurrentTheme(new SkyBluer());
com.jgoodies.looks.Options.setPopupDropShadowEnabled(true);
UIManager.setLookAndFeel(lnf);
} 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);
}
}
} catch (Exception e) {
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
boolean overrideDefaultFonts = Globals.prefs.getBoolean(JabRefPreferences.OVERRIDE_DEFAULT_FONTS);
if (overrideDefaultFonts) {
int fontSize = Globals.prefs.getInt(JabRefPreferences.MENU_FONT_SIZE);
UIDefaults defaults = UIManager.getDefaults();
Enumeration<Object> keys = defaults.keys();
for (Object key : Collections.list(keys)) {
if ((key instanceof String) && ((String) key).endsWith(".font")) {
FontUIResource font = (FontUIResource) UIManager.get(key);
font = new FontUIResource(font.getName(), font.getStyle(), fontSize);
defaults.put(key, font);
}
}
}
}
public static JabRefFrame getMainFrame() {
return mainFrame;
}
// Only used for testing, other than that do NOT set the mainFrame...
public static void setMainFrame(JabRefFrame mainFrame) {
JabRefGUI.mainFrame = mainFrame;
}
}