2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
5 * This file is part of Jalview.
7 * Jalview is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation, either version 3
10 * of the License, or (at your option) any later version.
12 * Jalview is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19 * The Jalview Authors are detailed in the 'AUTHORS' file.
23 import java.awt.Color;
24 import java.io.BufferedReader;
26 import java.io.FileOutputStream;
27 import java.io.IOException;
28 import java.io.InputStreamReader;
29 import java.io.OutputStreamWriter;
30 import java.io.PrintWriter;
31 import java.net.MalformedURLException;
33 import java.net.URISyntaxException;
35 import java.security.AllPermission;
36 import java.security.CodeSource;
37 import java.security.PermissionCollection;
38 import java.security.Permissions;
39 import java.security.Policy;
40 import java.util.HashMap;
41 import java.util.Locale;
43 import java.util.Properties;
44 import java.util.Vector;
45 import java.util.logging.ConsoleHandler;
46 import java.util.logging.Level;
47 import java.util.logging.Logger;
49 import javax.swing.JDialog;
50 import javax.swing.JFrame;
51 import javax.swing.JOptionPane;
52 import javax.swing.SwingUtilities;
53 import javax.swing.UIManager;
54 import javax.swing.UIManager.LookAndFeelInfo;
55 import javax.swing.UnsupportedLookAndFeelException;
57 import com.formdev.flatlaf.FlatLightLaf;
58 import com.formdev.flatlaf.themes.FlatMacLightLaf;
59 import com.formdev.flatlaf.util.SystemInfo;
60 import com.threerings.getdown.util.LaunchUtil;
62 //import edu.stanford.ejalbert.launching.IBrowserLaunching;
63 import groovy.lang.Binding;
64 import groovy.util.GroovyScriptEngine;
65 import jalview.bin.ArgParser.Arg;
66 import jalview.ext.so.SequenceOntology;
67 import jalview.gui.AlignFrame;
68 import jalview.gui.Desktop;
69 import jalview.gui.PromptUserConfig;
70 import jalview.gui.QuitHandler;
71 import jalview.gui.QuitHandler.QResponse;
72 import jalview.io.AppletFormatAdapter;
73 import jalview.io.BioJsHTMLOutput;
74 import jalview.io.DataSourceType;
75 import jalview.io.FileFormat;
76 import jalview.io.FileFormatException;
77 import jalview.io.FileFormatI;
78 import jalview.io.FileFormats;
79 import jalview.io.FileLoader;
80 import jalview.io.HtmlSvgOutput;
81 import jalview.io.IdentifyFile;
82 import jalview.io.NewickFile;
83 import jalview.io.gff.SequenceOntologyFactory;
84 import jalview.schemes.ColourSchemeI;
85 import jalview.schemes.ColourSchemeProperty;
86 import jalview.util.ChannelProperties;
87 import jalview.util.HttpUtils;
88 import jalview.util.LaunchUtils;
89 import jalview.util.MessageManager;
90 import jalview.util.Platform;
91 import jalview.ws.jws2.Jws2Discoverer;
94 * Main class for Jalview Application <br>
96 * start with: java -classpath "$PATH_TO_LIB$/*:$PATH_TO_CLASSES$" \
99 * or on Windows: java -classpath "$PATH_TO_LIB$/*;$PATH_TO_CLASSES$" \
100 * jalview.bin.Jalview jalview.bin.Jalview
102 * (ensure -classpath arg is quoted to avoid shell expansion of '*' and do not
103 * embellish '*' to e.g. '*.jar')
106 * @version $Revision$
112 Platform.getURLCommandArguments();
113 Platform.addJ2SDirectDatabaseCall("https://www.jalview.org");
114 Platform.addJ2SDirectDatabaseCall("http://www.jalview.org");
115 Platform.addJ2SDirectDatabaseCall("http://www.compbio.dundee.ac.uk");
116 Platform.addJ2SDirectDatabaseCall("https://www.compbio.dundee.ac.uk");
120 * singleton instance of this class
122 private static Jalview instance;
124 private Desktop desktop;
126 public static AlignFrame currentAlignFrame;
130 if (!Platform.isJS())
137 // grab all the rights we can for the JVM
138 Policy.setPolicy(new Policy()
141 public PermissionCollection getPermissions(CodeSource codesource)
143 Permissions perms = new Permissions();
144 perms.add(new AllPermission());
149 public void refresh()
157 * keep track of feature fetching tasks.
165 * TODO: generalise to track all jalview events to orchestrate batch processing
169 private int queued = 0;
171 private int running = 0;
173 public FeatureFetcher()
178 public void addFetcher(final AlignFrame af,
179 final Vector<String> dasSources)
181 final long id = System.currentTimeMillis();
183 final FeatureFetcher us = this;
184 new Thread(new Runnable()
196 af.setProgressBar(MessageManager
197 .getString("status.das_features_being_retrived"), id);
198 af.featureSettings_actionPerformed(null);
199 af.setProgressBar(null, id);
208 public synchronized boolean allFinished()
210 return queued == 0 && running == 0;
215 public static Jalview getInstance()
221 * main class for Jalview application
224 * open <em>filename</em>
226 public static void main(String[] args)
228 // setLogging(); // BH - for event debugging in JavaScript
229 instance = new Jalview();
230 instance.doMain(args);
233 private static void logClass(String name)
235 // BH - for event debugging in JavaScript
236 ConsoleHandler consoleHandler = new ConsoleHandler();
237 consoleHandler.setLevel(Level.ALL);
238 Logger logger = Logger.getLogger(name);
239 logger.setLevel(Level.ALL);
240 logger.addHandler(consoleHandler);
243 @SuppressWarnings("unused")
244 private static void setLogging()
252 System.out.println("not in js");
255 // BH - for event debugging in JavaScript (Java mode only)
256 if (!Platform.isJS())
263 Logger.getLogger("").setLevel(Level.ALL);
264 logClass("java.awt.EventDispatchThread");
265 logClass("java.awt.EventQueue");
266 logClass("java.awt.Component");
267 logClass("java.awt.focus.Component");
268 logClass("java.awt.focus.DefaultKeyboardFocusManager");
276 void doMain(String[] args)
279 if (!Platform.isJS())
281 System.setSecurityManager(null);
284 // get args needed before proper ArgParser
285 Map<ArgParser.Arg, String> bootstrapArgs = ArgParser
286 .bootstrapArgs(args);
289 .println("Java version: " + System.getProperty("java.version"));
290 System.out.println("Java Home: " + System.getProperty("java.home"));
291 System.out.println(System.getProperty("os.arch") + " "
292 + System.getProperty("os.name") + " "
293 + System.getProperty("os.version"));
295 String val = System.getProperty("sys.install4jVersion");
298 System.out.println("Install4j version: " + val);
300 val = System.getProperty("installer_template_version");
303 System.out.println("Install4j template version: " + val);
305 val = System.getProperty("launcher_version");
308 System.out.println("Launcher version: " + val);
311 if (Platform.isLinux() && LaunchUtils.getJavaVersion() < 11)
313 System.setProperty("flatlaf.uiScale", "1");
316 // get bootstrap properties (mainly for the logger level)
317 Properties bootstrapProperties = Cache
318 .bootstrapProperties(bootstrapArgs.get(Arg.PROPS));
320 // report Jalview version
321 Cache.loadBuildProperties(true);
324 ArgsParser aparser = new ArgsParser(args);
327 boolean headless = false;
329 boolean headlessArg = false;
333 String logLevel = bootstrapArgs.containsKey(Arg.DEBUG) ? "DEBUG"
335 if (logLevel == null && !(bootstrapProperties == null))
337 logLevel = bootstrapProperties.getProperty(Cache.JALVIEWLOGLEVEL);
339 Console.initLogger(logLevel);
340 } catch (NoClassDefFoundError error)
342 error.printStackTrace();
343 System.out.println("\nEssential logging libraries not found."
344 + "\nUse: java -classpath \"$PATH_TO_LIB$/*:$PATH_TO_CLASSES$\" jalview.bin.Jalview");
348 // register SIGTERM listener
349 Runtime.getRuntime().addShutdownHook(new Thread()
353 Console.debug("Running shutdown hook");
354 if (QuitHandler.gotQuitResponse() == QResponse.CANCEL_QUIT)
356 // Got to here by a SIGTERM signal.
357 // Note we will not actually cancel the quit from here -- it's too
358 // late -- but we can wait for saving files.
359 Console.debug("Checking for saving files");
360 QuitHandler.getQuitResponse(false);
364 Console.debug("Nothing more to do");
366 Console.debug("Exiting, bye!");
367 // shutdownHook cannot be cancelled, JVM will now halt
371 String usrPropsFile = bootstrapArgs.containsKey(Arg.PROPS)
372 ? bootstrapArgs.get(Arg.PROPS)
373 : aparser.getValue("props");
374 Cache.loadProperties(usrPropsFile);
375 if (usrPropsFile != null)
378 "CMD [-props " + usrPropsFile + "] executed successfully!");
382 ArgParser argparser = new ArgParser(args);
384 if (!Platform.isJS())
391 if (aparser.contains("help") || aparser.contains("h")
392 || argparser.getBool(Arg.HELP))
398 if (argparser.isSet(Arg.HEADLESS))
400 System.setProperty("java.awt.headless", "true");
402 headlessArg = argparser.getBool(Arg.HEADLESS);
404 if (aparser.contains("nodisplay") || aparser.contains("nogui")
405 || aparser.contains("headless"))
407 System.setProperty("java.awt.headless", "true");
413 // allow https handshakes to download intermediate certs if necessary
414 System.setProperty("com.sun.security.enableAIAcaIssuers", "true");
416 final String jabawsUrl = aparser.getValue("jabaws");
417 if (jabawsUrl != null)
421 Jws2Discoverer.getDiscoverer().setPreferredUrl(jabawsUrl);
423 "CMD [-jabaws " + jabawsUrl + "] executed successfully!");
424 } catch (MalformedURLException e)
427 "Invalid jabaws parameter: " + jabawsUrl + " ignored");
432 String defs = aparser.getValue("setprop");
435 int p = defs.indexOf('=');
438 System.err.println("Ignoring invalid setprop argument : " + defs);
442 System.out.println("Executing setprop argument: " + defs);
445 Cache.setProperty(defs.substring(0, p), defs.substring(p + 1));
447 // DISABLED FOR SECURITY REASONS
448 // TODO: add a property to allow properties to be overriden by cli args
449 // Cache.setProperty(defs.substring(0,p), defs.substring(p+1));
451 defs = aparser.getValue("setprop");
453 if (System.getProperty("java.awt.headless") != null
454 && System.getProperty("java.awt.headless").equals("true"))
458 System.setProperty("http.agent",
459 "Jalview Desktop/" + Cache.getDefault("VERSION", "Unknown"));
463 Console.initLogger();
466 NoClassDefFoundError error)
468 error.printStackTrace();
469 System.out.println("\nEssential logging libraries not found."
470 + "\nUse: java -classpath \"$PATH_TO_LIB$/*:$PATH_TO_CLASSES$\" jalview.bin.Jalview");
475 if (!(headless || headlessArg))
479 * configure 'full' SO model if preferences say to, else use the default (full SO)
480 * - as JS currently doesn't have OBO parsing, it must use 'Lite' version
482 boolean soDefault = !Platform.isJS();
483 if (Cache.getDefault("USE_FULL_SO", soDefault))
485 SequenceOntologyFactory.setInstance(new SequenceOntology());
488 if (!(headless || headlessArg))
490 Desktop.nosplash = aparser.contains("nosplash");
491 desktop = new Desktop();
492 desktop.setInBatchMode(true); // indicate we are starting up
496 JalviewTaskbar.setTaskbar(this);
497 } catch (Exception e)
499 Console.info("Cannot set Taskbar");
500 Console.error(e.getMessage());
501 // e.printStackTrace();
502 } catch (Throwable t)
504 Console.info("Cannot set Taskbar");
505 Console.error(t.getMessage());
506 // t.printStackTrace();
509 // set Proxy settings before all the internet calls
510 Cache.setProxyPropertiesFromPreferences();
512 desktop.setVisible(true);
514 if (!Platform.isJS())
523 * Check to see that the JVM version being run is suitable for the Java
524 * version this Jalview was compiled for. Popup a warning if not.
526 if (!LaunchUtils.checkJavaVersion())
528 Console.warn("The Java version being used (Java "
529 + LaunchUtils.getJavaVersion()
530 + ") may lead to problems. This installation of Jalview should be used with Java "
531 + LaunchUtils.getJavaCompileVersion() + ".");
534 .getBooleanUserPreference("IGNORE_JVM_WARNING_POPUP"))
537 MessageManager.getString("label.continue") };
538 JOptionPane.showOptionDialog(null,
539 MessageManager.formatMessage(
540 "warning.wrong_jvm_version_message",
541 LaunchUtils.getJavaVersion(),
542 LaunchUtils.getJavaCompileVersion()),
544 .getString("warning.wrong_jvm_version_title"),
545 JOptionPane.DEFAULT_OPTION, JOptionPane.WARNING_MESSAGE,
546 null, options, options[0]);
550 if (!aparser.contains("nowebservicediscovery"))
552 desktop.startServiceDiscovery();
554 if (!aparser.contains("nousagestats"))
556 startUsageStats(desktop);
560 System.err.println("CMD [-nousagestats] executed successfully!");
563 if (!aparser.contains("noquestionnaire"))
565 String url = aparser.getValue("questionnaire");
568 // Start the desktop questionnaire prompter with the specified
570 Console.debug("Starting questionnaire url at " + url);
571 desktop.checkForQuestionnaire(url);
572 System.out.println("CMD questionnaire[-" + url
573 + "] executed successfully!");
577 if (Cache.getProperty("NOQUESTIONNAIRES") == null)
579 // Start the desktop questionnaire prompter with the specified
582 // "http://anaplog.compbio.dundee.ac.uk/cgi-bin/questionnaire.pl";
584 String defurl = "https://www.jalview.org/cgi-bin/questionnaire.pl";
586 "Starting questionnaire with default url: " + defurl);
587 desktop.checkForQuestionnaire(defurl);
594 .println("CMD [-noquestionnaire] executed successfully!");
597 if (!aparser.contains("nonews")
598 || Cache.getProperty("NONEWS") == null)
600 desktop.checkForNews();
603 if (!aparser.contains("nohtmltemplates")
604 || Cache.getProperty("NOHTMLTEMPLATES") == null)
606 BioJsHTMLOutput.updateBioJS();
610 // Run Commands from cli
611 boolean commandsSuccess = Commands.processArgs(argparser, headless);
614 Console.info("Successfully completed commands");
620 Console.warn("Error when running commands");
625 // Check if JVM and compile version might cause problems and log if it
627 if (headless && !Platform.isJS() && !LaunchUtils.checkJavaVersion())
629 Console.warn("The Java version being used (Java "
630 + LaunchUtils.getJavaVersion()
631 + ") may lead to problems. This installation of Jalview should be used with Java "
632 + LaunchUtils.getJavaCompileVersion() + ".");
635 // Move any new getdown-launcher-new.jar into place over old
636 // getdown-launcher.jar
637 String appdirString = System.getProperty("getdownappdir");
638 if (appdirString != null && appdirString.length() > 0)
640 final File appdir = new File(appdirString);
646 LaunchUtil.upgradeGetdown(
647 new File(appdir, "getdown-launcher-old.jar"),
648 new File(appdir, "getdown-launcher.jar"),
649 new File(appdir, "getdown-launcher-new.jar"));
654 String file = null, data = null;
656 FileFormatI format = null;
658 DataSourceType protocol = null;
660 FileLoader fileLoader = new FileLoader(!headless);
662 String groovyscript = null; // script to execute after all loading is
663 // completed one way or another
664 // extract groovy argument and execute if necessary
665 groovyscript = aparser.getValue("groovy", true);
666 file = aparser.getValue("open", true);
668 if (file == null && desktop == null)
670 System.out.println("No files to open!");
675 // Finally, deal with the remaining input data.
680 desktop.setProgressBar(
682 .getString("status.processing_commandline_args"),
683 progress = System.currentTimeMillis());
685 System.out.println("CMD [-open " + file + "] executed successfully!");
687 if (!Platform.isJS())
689 * ignore in JavaScript -- can't just file existence - could load it?
694 if (!HttpUtils.startsWithHttpOrHttps(file))
696 if (!(new File(file)).exists())
698 System.out.println("Can't find " + file);
707 protocol = AppletFormatAdapter.checkProtocol(file);
711 format = new IdentifyFile().identify(file, protocol);
712 } catch (FileFormatException e1)
717 AlignFrame af = fileLoader.LoadFileWaitTillLoaded(file, protocol,
721 System.out.println("error");
725 setCurrentAlignFrame(af);
726 data = aparser.getValue("colour", true);
729 data.replaceAll("%20", " ");
731 ColourSchemeI cs = ColourSchemeProperty.getColourScheme(
732 af.getViewport(), af.getViewport().getAlignment(), data);
737 "CMD [-colour " + data + "] executed successfully!");
742 // Must maintain ability to use the groups flag
743 data = aparser.getValue("groups", true);
746 af.parseFeaturesFile(data,
747 AppletFormatAdapter.checkProtocol(data));
748 // System.out.println("Added " + data);
750 "CMD groups[-" + data + "] executed successfully!");
752 data = aparser.getValue("features", true);
755 af.parseFeaturesFile(data,
756 AppletFormatAdapter.checkProtocol(data));
757 // System.out.println("Added " + data);
759 "CMD [-features " + data + "] executed successfully!");
762 data = aparser.getValue("annotations", true);
765 af.loadJalviewDataFile(data, null, null, null);
766 // System.out.println("Added " + data);
768 "CMD [-annotations " + data + "] executed successfully!");
770 // set or clear the sortbytree flag.
771 if (aparser.contains("sortbytree"))
773 af.getViewport().setSortByTree(true);
774 if (af.getViewport().getSortByTree())
776 System.out.println("CMD [-sortbytree] executed successfully!");
779 if (aparser.contains("no-annotation"))
781 af.getViewport().setShowAnnotation(false);
782 if (!af.getViewport().isShowAnnotation())
784 System.out.println("CMD no-annotation executed successfully!");
787 if (aparser.contains("nosortbytree"))
789 af.getViewport().setSortByTree(false);
790 if (!af.getViewport().getSortByTree())
793 .println("CMD [-nosortbytree] executed successfully!");
796 data = aparser.getValue("tree", true);
802 "CMD [-tree " + data + "] executed successfully!");
803 NewickFile nf = new NewickFile(data,
804 AppletFormatAdapter.checkProtocol(data));
806 .setCurrentTree(af.showNewickTree(nf, data).getTree());
807 } catch (IOException ex)
809 System.err.println("Couldn't add tree " + data);
810 ex.printStackTrace(System.err);
813 // TODO - load PDB structure(s) to alignment JAL-629
814 // (associate with identical sequence in alignment, or a specified
816 if (groovyscript != null)
818 // Execute the groovy script after we've done all the rendering stuff
819 // and before any images or figures are generated.
820 System.out.println("Executing script " + groovyscript);
821 executeGroovyScript(groovyscript, af);
822 System.out.println("CMD groovy[" + groovyscript
823 + "] executed successfully!");
826 String imageName = "unnamed.png";
827 while (aparser.getSize() > 1)
829 String outputFormat = aparser.nextValue();
830 file = aparser.nextValue();
832 if (outputFormat.equalsIgnoreCase("png"))
834 af.createPNG(new File(file));
835 imageName = (new File(file)).getName();
836 System.out.println("Creating PNG image: " + file);
839 else if (outputFormat.equalsIgnoreCase("svg"))
841 File imageFile = new File(file);
842 imageName = imageFile.getName();
843 af.createSVG(imageFile);
844 System.out.println("Creating SVG image: " + file);
847 else if (outputFormat.equalsIgnoreCase("html"))
849 File imageFile = new File(file);
850 imageName = imageFile.getName();
851 HtmlSvgOutput htmlSVG = new HtmlSvgOutput(af.alignPanel);
852 htmlSVG.exportHTML(file);
854 System.out.println("Creating HTML image: " + file);
857 else if (outputFormat.equalsIgnoreCase("biojsmsa"))
861 System.err.println("The output html file must not be null");
866 BioJsHTMLOutput.refreshVersionInfo(
867 BioJsHTMLOutput.BJS_TEMPLATES_LOCAL_DIRECTORY);
868 } catch (URISyntaxException e)
872 BioJsHTMLOutput bjs = new BioJsHTMLOutput(af.alignPanel);
873 bjs.exportHTML(file);
875 .println("Creating BioJS MSA Viwer HTML file: " + file);
878 else if (outputFormat.equalsIgnoreCase("imgMap"))
880 af.createImageMap(new File(file), imageName);
881 System.out.println("Creating image map: " + file);
884 else if (outputFormat.equalsIgnoreCase("eps"))
886 File outputFile = new File(file);
888 "Creating EPS file: " + outputFile.getAbsolutePath());
889 af.createEPS(outputFile);
892 FileFormatI outFormat = null;
895 outFormat = FileFormats.getInstance().forName(outputFormat);
896 } catch (Exception formatP)
898 System.out.println("Couldn't parse " + outFormat
899 + " as a valid Jalview format string.");
901 if (outFormat != null)
903 if (!outFormat.isWritable())
906 "This version of Jalview does not support alignment export as "
911 af.saveAlignment(file, outFormat);
912 if (af.isSaveAlignmentSuccessful())
914 System.out.println("Written alignment in "
915 + outFormat.getName() + " format to " + file);
919 System.out.println("Error writing file " + file + " in "
920 + outFormat.getName() + " format!!");
927 while (aparser.getSize() > 0)
929 System.out.println("Unknown arg: " + aparser.nextValue());
934 AlignFrame startUpAlframe = null;
935 // We'll only open the default file if the desktop is visible.
937 // ////////////////////
939 if (!Platform.isJS() && !headless && file == null
940 && Cache.getDefault("SHOW_STARTUP_FILE", true))
947 file = Cache.getDefault("STARTUP_FILE",
948 Cache.getDefault("www.jalview.org", "https://www.jalview.org")
949 + "/examples/exampleFile_2_7.jvp");
950 if (file.equals("http://www.jalview.org/examples/exampleFile_2_3.jar")
952 "http://www.jalview.org/examples/exampleFile_2_7.jar"))
954 file.replace("http:", "https:");
955 // hardwire upgrade of the startup file
956 file.replace("_2_3", "_2_7");
957 file.replace("2_7.jar", "2_7.jvp");
958 // and remove the stale setting
959 Cache.removeProperty("STARTUP_FILE");
962 protocol = AppletFormatAdapter.checkProtocol(file);
964 if (file.endsWith(".jar"))
966 format = FileFormat.Jalview;
972 format = new IdentifyFile().identify(file, protocol);
973 } catch (FileFormatException e)
979 startUpAlframe = fileLoader.LoadFileWaitTillLoaded(file, protocol,
981 // don't ask to save when quitting if only the startup file has been
983 Console.debug("Resetting up-to-date flag for startup file");
984 startUpAlframe.getViewport().setSavedUpToDate(true);
985 // extract groovy arguments before anything else.
988 // Once all other stuff is done, execute any groovy scripts (in order)
989 if (groovyscript != null)
991 if (Cache.groovyJarsPresent())
993 System.out.println("Executing script " + groovyscript);
994 executeGroovyScript(groovyscript, startUpAlframe);
999 "Sorry. Groovy Support is not available, so ignoring the provided groovy script "
1003 // and finally, turn off batch mode indicator - if the desktop still exists
1004 if (desktop != null)
1008 desktop.setProgressBar(null, progress);
1010 desktop.setInBatchMode(false);
1014 private static void setLookAndFeel()
1016 // property laf = "crossplatform", "system", "gtk", "metal", "nimbus",
1018 // If not set (or chosen laf fails), use the normal SystemLaF and if on Mac,
1019 // try Quaqua/Vaqua.
1020 String lafProp = System.getProperty("laf");
1021 String lafSetting = Cache.getDefault("PREFERRED_LAF", null);
1022 String laf = "none";
1023 if (lafProp != null)
1027 else if (lafSetting != null)
1031 boolean lafSet = false;
1034 case "crossplatform":
1035 lafSet = setCrossPlatformLookAndFeel();
1038 Console.error("Could not set requested laf=" + laf);
1042 lafSet = setSystemLookAndFeel();
1045 Console.error("Could not set requested laf=" + laf);
1049 lafSet = setGtkLookAndFeel();
1052 Console.error("Could not set requested laf=" + laf);
1056 lafSet = setMetalLookAndFeel();
1059 Console.error("Could not set requested laf=" + laf);
1063 lafSet = setNimbusLookAndFeel();
1066 Console.error("Could not set requested laf=" + laf);
1070 lafSet = setFlatLookAndFeel();
1073 Console.error("Could not set requested laf=" + laf);
1077 lafSet = setMacLookAndFeel();
1080 Console.error("Could not set requested laf=" + laf);
1086 Console.error("Requested laf=" + laf + " not implemented");
1090 setSystemLookAndFeel();
1091 if (Platform.isLinux())
1093 setLinuxLookAndFeel();
1095 if (Platform.isMac())
1097 setMacLookAndFeel();
1102 private static boolean setCrossPlatformLookAndFeel()
1104 boolean set = false;
1107 UIManager.setLookAndFeel(
1108 UIManager.getCrossPlatformLookAndFeelClassName());
1110 } catch (Exception ex)
1112 Console.error("Unexpected Look and Feel Exception");
1113 Console.error(ex.getMessage());
1114 Console.debug(Cache.getStackTraceString(ex));
1119 private static boolean setSystemLookAndFeel()
1121 boolean set = false;
1124 UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
1126 } catch (Exception ex)
1128 Console.error("Unexpected Look and Feel Exception");
1129 Console.error(ex.getMessage());
1130 Console.debug(Cache.getStackTraceString(ex));
1135 private static boolean setSpecificLookAndFeel(String name,
1136 String className, boolean nameStartsWith)
1138 boolean set = false;
1141 for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels())
1143 if (info.getName() != null && nameStartsWith
1144 ? info.getName().toLowerCase(Locale.ROOT)
1145 .startsWith(name.toLowerCase(Locale.ROOT))
1146 : info.getName().toLowerCase(Locale.ROOT)
1147 .equals(name.toLowerCase(Locale.ROOT)))
1149 className = info.getClassName();
1153 UIManager.setLookAndFeel(className);
1155 } catch (Exception ex)
1157 Console.error("Unexpected Look and Feel Exception");
1158 Console.error(ex.getMessage());
1159 Console.debug(Cache.getStackTraceString(ex));
1164 private static boolean setGtkLookAndFeel()
1166 return setSpecificLookAndFeel("gtk",
1167 "com.sun.java.swing.plaf.gtk.GTKLookAndFeel", true);
1170 private static boolean setMetalLookAndFeel()
1172 return setSpecificLookAndFeel("metal",
1173 "javax.swing.plaf.metal.MetalLookAndFeel", false);
1176 private static boolean setNimbusLookAndFeel()
1178 return setSpecificLookAndFeel("nimbus",
1179 "javax.swing.plaf.nimbus.NimbusLookAndFeel", false);
1182 private static boolean setFlatLookAndFeel()
1184 boolean set = false;
1185 if (SystemInfo.isMacOS)
1189 UIManager.setLookAndFeel(
1190 "com.formdev.flatlaf.themes.FlatMacLightLaf");
1192 Console.debug("Using FlatMacLightLaf");
1193 } catch (ClassNotFoundException | InstantiationException
1194 | IllegalAccessException | UnsupportedLookAndFeelException e)
1196 Console.debug("Exception loading FlatLightLaf", e);
1198 System.setProperty("apple.laf.useScreenMenuBar", "true");
1199 System.setProperty("apple.awt.application.name",
1200 ChannelProperties.getProperty("app_name"));
1201 System.setProperty("apple.awt.application.appearance", "system");
1202 if (SystemInfo.isMacFullWindowContentSupported
1203 && Desktop.desktop != null)
1205 Console.debug("Setting transparent title bar");
1206 Desktop.desktop.getRootPane()
1207 .putClientProperty("apple.awt.fullWindowContent", true);
1208 Desktop.desktop.getRootPane()
1209 .putClientProperty("apple.awt.transparentTitleBar", true);
1210 Desktop.desktop.getRootPane()
1211 .putClientProperty("apple.awt.fullscreenable", true);
1213 SwingUtilities.invokeLater(() -> {
1214 FlatMacLightLaf.setup();
1216 Console.debug("Using FlatMacLightLaf");
1223 UIManager.setLookAndFeel("com.formdev.flatlaf.FlatLightLaf");
1225 Console.debug("Using FlatLightLaf");
1226 } catch (ClassNotFoundException | InstantiationException
1227 | IllegalAccessException | UnsupportedLookAndFeelException e)
1229 Console.debug("Exception loading FlatLightLaf", e);
1231 // Windows specific properties here
1232 SwingUtilities.invokeLater(() -> {
1233 FlatLightLaf.setup();
1235 Console.debug("Using FlatLightLaf");
1238 else if (SystemInfo.isLinux)
1242 UIManager.setLookAndFeel("com.formdev.flatlaf.FlatLightLaf");
1244 Console.debug("Using FlatLightLaf");
1245 } catch (ClassNotFoundException | InstantiationException
1246 | IllegalAccessException | UnsupportedLookAndFeelException e)
1248 Console.debug("Exception loading FlatLightLaf", e);
1250 // enable custom window decorations
1251 JFrame.setDefaultLookAndFeelDecorated(true);
1252 JDialog.setDefaultLookAndFeelDecorated(true);
1253 SwingUtilities.invokeLater(() -> {
1254 FlatLightLaf.setup();
1256 Console.debug("Using FlatLightLaf");
1264 UIManager.setLookAndFeel("com.formdev.flatlaf.FlatLightLaf");
1266 Console.debug("Using FlatLightLaf");
1267 } catch (ClassNotFoundException | InstantiationException
1268 | IllegalAccessException | UnsupportedLookAndFeelException e)
1270 Console.debug("Exception loading FlatLightLaf", e);
1276 UIManager.put("TabbedPane.tabType", "card");
1277 UIManager.put("TabbedPane.showTabSeparators", true);
1278 UIManager.put("TabbedPane.showContentSeparator", true);
1279 UIManager.put("TabbedPane.tabSeparatorsFullHeight", true);
1280 UIManager.put("TabbedPane.tabsOverlapBorder", true);
1281 UIManager.put("TabbedPane.hasFullBorder", true);
1282 UIManager.put("TabbedPane.tabLayoutPolicy", "scroll");
1283 UIManager.put("TabbedPane.scrollButtonsPolicy", "asNeeded");
1284 UIManager.put("TabbedPane.smoothScrolling", true);
1285 UIManager.put("TabbedPane.tabWidthMode", "compact");
1286 UIManager.put("TabbedPane.selectedBackground", Color.white);
1289 Desktop.setLiveDragMode(Cache.getDefault("FLAT_LIVE_DRAG_MODE", true));
1293 private static boolean setMacLookAndFeel()
1295 boolean set = false;
1296 System.setProperty("com.apple.mrj.application.apple.menu.about.name",
1297 ChannelProperties.getProperty("app_name"));
1298 System.setProperty("apple.laf.useScreenMenuBar", "true");
1300 * broken native LAFs on (ARM?) macbooks
1301 set = setQuaquaLookAndFeel();
1302 if ((!set) || !UIManager.getLookAndFeel().getClass().toString()
1303 .toLowerCase(Locale.ROOT).contains("quaqua"))
1305 set = setVaquaLookAndFeel();
1308 set = setFlatLookAndFeel();
1312 private static boolean setLinuxLookAndFeel()
1314 boolean set = false;
1315 set = setFlatLookAndFeel();
1317 set = setMetalLookAndFeel();
1318 // avoid GtkLookAndFeel -- not good results especially on HiDPI
1320 set = setNimbusLookAndFeel();
1324 private static void showUsage()
1327 "Usage: jalview -open [FILE] [OUTPUT_FORMAT] [OUTPUT_FILE]\n\n"
1328 + "-nodisplay\tRun Jalview without User Interface.\n"
1329 + "-props FILE\tUse the given Jalview properties file instead of users default.\n"
1330 + "-colour COLOURSCHEME\tThe colourscheme to be applied to the alignment\n"
1331 + "-annotations FILE\tAdd precalculated annotations to the alignment.\n"
1332 + "-tree FILE\tLoad the given newick format tree file onto the alignment\n"
1333 + "-features FILE\tUse the given file to mark features on the alignment.\n"
1334 + "-fasta FILE\tCreate alignment file FILE in Fasta format.\n"
1335 + "-clustal FILE\tCreate alignment file FILE in Clustal format.\n"
1336 + "-pfam FILE\tCreate alignment file FILE in PFAM format.\n"
1337 + "-msf FILE\tCreate alignment file FILE in MSF format.\n"
1338 + "-pileup FILE\tCreate alignment file FILE in Pileup format\n"
1339 + "-pir FILE\tCreate alignment file FILE in PIR format.\n"
1340 + "-blc FILE\tCreate alignment file FILE in BLC format.\n"
1341 + "-json FILE\tCreate alignment file FILE in JSON format.\n"
1342 + "-jalview FILE\tCreate alignment file FILE in Jalview format.\n"
1343 + "-png FILE\tCreate PNG image FILE from alignment.\n"
1344 + "-svg FILE\tCreate SVG image FILE from alignment.\n"
1345 + "-html FILE\tCreate HTML file from alignment.\n"
1346 + "-biojsMSA FILE\tCreate BioJS MSA Viewer HTML file from alignment.\n"
1347 + "-imgMap FILE\tCreate HTML file FILE with image map of PNG image.\n"
1348 + "-eps FILE\tCreate EPS file FILE from alignment.\n"
1349 + "-questionnaire URL\tQueries the given URL for information about any Jalview user questionnaires.\n"
1350 + "-noquestionnaire\tTurn off questionnaire check.\n"
1351 + "-nonews\tTurn off check for Jalview news.\n"
1352 + "-nousagestats\tTurn off google analytics tracking for this session.\n"
1353 + "-sortbytree OR -nosortbytree\tEnable or disable sorting of the given alignment by the given tree\n"
1355 // "-setprop PROPERTY=VALUE\tSet the given Jalview property,
1356 // after all other properties files have been read\n\t
1357 // (quote the 'PROPERTY=VALUE' pair to ensure spaces are
1358 // passed in correctly)"
1359 + "-jabaws URL\tSpecify URL for Jabaws services (e.g. for a local installation).\n"
1360 + "-fetchfrom nickname\tQuery nickname for features for the alignments and display them.\n"
1361 + "-groovy FILE\tExecute groovy script in FILE, after all other arguments have been processed (if FILE is the text 'STDIN' then the file will be read from STDIN)\n"
1362 + "-jvmmempc=PERCENT\tOnly available with standalone executable jar or jalview.bin.Launcher. Limit maximum heap size (memory) to PERCENT% of total physical memory detected. This defaults to 90 if total physical memory can be detected. See https://www.jalview.org/help/html/memory.html for more details.\n"
1363 + "-jvmmemmax=MAXMEMORY\tOnly available with standalone executable jar or jalview.bin.Launcher. Limit maximum heap size (memory) to MAXMEMORY. MAXMEMORY can be specified in bytes, kilobytes(k), megabytes(m), gigabytes(g) or if you're lucky enough, terabytes(t). This defaults to 32g if total physical memory can be detected, or to 8g if total physical memory cannot be detected. See https://www.jalview.org/help/html/memory.html for more details.\n"
1364 + "\n~Read documentation in Application or visit https://www.jalview.org for description of Features and Annotations file~\n\n");
1367 private static void startUsageStats(final Desktop desktop)
1370 * start a User Config prompt asking if we can log usage statistics.
1372 PromptUserConfig prompter = new PromptUserConfig(Desktop.desktop,
1373 "USAGESTATS", "Jalview Usage Statistics",
1374 "Do you want to help make Jalview better by enabling "
1375 + "the collection of usage statistics with Google Analytics ?"
1376 + "\n\n(you can enable or disable usage tracking in the preferences)",
1383 "Initialising googletracker for usage stats.");
1384 Cache.initGoogleTracker();
1385 Console.debug("Tracking enabled.");
1392 Console.debug("Not enabling Google Tracking.");
1395 desktop.addDialogThread(prompter);
1399 * Locate the given string as a file and pass it to the groovy interpreter.
1401 * @param groovyscript
1402 * the script to execute
1403 * @param jalviewContext
1404 * the Jalview Desktop object passed in to the groovy binding as the
1407 private void executeGroovyScript(String groovyscript, AlignFrame af)
1410 * for scripts contained in files
1417 if (groovyscript.trim().equals("STDIN"))
1419 // read from stdin into a tempfile and execute it
1422 tfile = File.createTempFile("jalview", "groovy");
1423 PrintWriter outfile = new PrintWriter(
1424 new OutputStreamWriter(new FileOutputStream(tfile)));
1425 BufferedReader br = new BufferedReader(
1426 new InputStreamReader(System.in));
1428 while ((line = br.readLine()) != null)
1430 outfile.write(line + "\n");
1436 } catch (Exception ex)
1438 System.err.println("Failed to read from STDIN into tempfile "
1439 + ((tfile == null) ? "(tempfile wasn't created)"
1440 : tfile.toString()));
1441 ex.printStackTrace();
1446 sfile = tfile.toURI().toURL();
1447 } catch (Exception x)
1450 "Unexpected Malformed URL Exception for temporary file created from STDIN: "
1452 x.printStackTrace();
1460 sfile = new URI(groovyscript).toURL();
1461 } catch (Exception x)
1463 tfile = new File(groovyscript);
1464 if (!tfile.exists())
1466 System.err.println("File '" + groovyscript + "' does not exist.");
1469 if (!tfile.canRead())
1471 System.err.println("File '" + groovyscript + "' cannot be read.");
1474 if (tfile.length() < 1)
1476 System.err.println("File '" + groovyscript + "' is empty.");
1481 sfile = tfile.getAbsoluteFile().toURI().toURL();
1482 } catch (Exception ex)
1484 System.err.println("Failed to create a file URL for "
1485 + tfile.getAbsoluteFile());
1492 Map<String, java.lang.Object> vbinding = new HashMap<>();
1493 vbinding.put("Jalview", this);
1496 vbinding.put("currentAlFrame", af);
1498 Binding gbinding = new Binding(vbinding);
1499 GroovyScriptEngine gse = new GroovyScriptEngine(new URL[] { sfile });
1500 gse.run(sfile.toString(), gbinding);
1501 if ("STDIN".equals(groovyscript))
1503 // delete temp file that we made -
1504 // only if it was successfully executed
1507 } catch (Exception e)
1509 System.err.println("Exception Whilst trying to execute file " + sfile
1510 + " as a groovy script.");
1511 e.printStackTrace(System.err);
1516 public static boolean isHeadlessMode()
1518 String isheadless = System.getProperty("java.awt.headless");
1519 if (isheadless != null && isheadless.equalsIgnoreCase("true"))
1526 public AlignFrame[] getAlignFrames()
1528 return desktop == null ? new AlignFrame[] { getCurrentAlignFrame() }
1529 : Desktop.getAlignFrames();
1534 * jalview.bin.Jalview.quit() will just run the non-GUI shutdownHook and exit
1538 // System.exit will run the shutdownHook first
1542 public static AlignFrame getCurrentAlignFrame()
1544 return Jalview.currentAlignFrame;
1547 public static void setCurrentAlignFrame(AlignFrame currentAlignFrame)
1549 Jalview.currentAlignFrame = currentAlignFrame;