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.util.Locale;
24 import java.awt.BorderLayout;
25 import java.awt.Color;
26 import java.awt.Dimension;
27 import java.awt.FontMetrics;
28 import java.awt.Graphics;
29 import java.awt.Graphics2D;
30 import java.awt.GridLayout;
31 import java.awt.Point;
32 import java.awt.Rectangle;
33 import java.awt.Toolkit;
34 import java.awt.Window;
35 import java.awt.datatransfer.Clipboard;
36 import java.awt.datatransfer.ClipboardOwner;
37 import java.awt.datatransfer.DataFlavor;
38 import java.awt.datatransfer.Transferable;
39 import java.awt.dnd.DnDConstants;
40 import java.awt.dnd.DropTargetDragEvent;
41 import java.awt.dnd.DropTargetDropEvent;
42 import java.awt.dnd.DropTargetEvent;
43 import java.awt.dnd.DropTargetListener;
44 import java.awt.event.ActionEvent;
45 import java.awt.event.ActionListener;
46 import java.awt.event.InputEvent;
47 import java.awt.event.KeyEvent;
48 import java.awt.event.MouseAdapter;
49 import java.awt.event.MouseEvent;
50 import java.awt.event.WindowAdapter;
51 import java.awt.event.WindowEvent;
52 import java.awt.geom.AffineTransform;
53 import java.beans.PropertyChangeEvent;
54 import java.beans.PropertyChangeListener;
56 import java.io.FileWriter;
57 import java.io.IOException;
58 import java.lang.reflect.Field;
60 import java.util.ArrayList;
61 import java.util.Arrays;
62 import java.util.HashMap;
63 import java.util.Hashtable;
64 import java.util.List;
65 import java.util.ListIterator;
66 import java.util.Vector;
67 import java.util.concurrent.ExecutionException;
68 import java.util.concurrent.ExecutorService;
69 import java.util.concurrent.Executors;
70 import java.util.concurrent.Future;
71 import java.util.concurrent.FutureTask;
72 import java.util.concurrent.Semaphore;
74 import javax.swing.AbstractAction;
75 import javax.swing.Action;
76 import javax.swing.ActionMap;
77 import javax.swing.Box;
78 import javax.swing.BoxLayout;
79 import javax.swing.DefaultDesktopManager;
80 import javax.swing.DesktopManager;
81 import javax.swing.InputMap;
82 import javax.swing.JButton;
83 import javax.swing.JCheckBox;
84 import javax.swing.JComboBox;
85 import javax.swing.JComponent;
86 import javax.swing.JDesktopPane;
87 import javax.swing.JInternalFrame;
88 import javax.swing.JLabel;
89 import javax.swing.JMenuItem;
90 import javax.swing.JPanel;
91 import javax.swing.JPopupMenu;
92 import javax.swing.JProgressBar;
93 import javax.swing.JTextField;
94 import javax.swing.KeyStroke;
95 import javax.swing.SwingUtilities;
96 import javax.swing.event.HyperlinkEvent;
97 import javax.swing.event.HyperlinkEvent.EventType;
98 import javax.swing.event.InternalFrameAdapter;
99 import javax.swing.event.InternalFrameEvent;
101 import org.stackoverflowusers.file.WindowsShortcut;
103 import jalview.api.AlignViewportI;
104 import jalview.api.AlignmentViewPanel;
105 import jalview.api.StructureSelectionManagerProvider;
106 import jalview.bin.ApplicationSingletonProvider;
107 import jalview.bin.ApplicationSingletonProvider.ApplicationSingletonI;
108 import jalview.bin.Cache;
109 import jalview.bin.Jalview;
110 import jalview.gui.ImageExporter.ImageWriterI;
111 import jalview.io.BackupFiles;
112 import jalview.io.DataSourceType;
113 import jalview.io.FileFormat;
114 import jalview.io.FileFormatException;
115 import jalview.io.FileFormatI;
116 import jalview.io.FileFormats;
117 import jalview.io.FileLoader;
118 import jalview.io.FormatAdapter;
119 import jalview.io.IdentifyFile;
120 import jalview.io.JalviewFileChooser;
121 import jalview.io.JalviewFileView;
122 import jalview.jbgui.APQHandlers;
123 import jalview.jbgui.GDesktop;
124 import jalview.jbgui.GSplitFrame;
125 import jalview.jbgui.GStructureViewer;
126 import jalview.project.Jalview2XML;
127 import jalview.structure.StructureSelectionManager;
128 import jalview.urls.IdOrgSettings;
129 import jalview.util.BrowserLauncher;
130 import jalview.util.ChannelProperties;
131 import jalview.util.ImageMaker.TYPE;
132 import jalview.util.LaunchUtils;
133 import jalview.util.MessageManager;
134 import jalview.util.Platform;
135 import jalview.util.UrlConstants;
136 import jalview.viewmodel.AlignmentViewport;
137 import jalview.ws.WSDiscovererI;
138 import jalview.ws.params.ParamManager;
139 import jalview.ws.utils.UrlDownloadClient;
146 * @version $Revision: 1.155 $
148 @SuppressWarnings("serial")
149 public class Desktop extends GDesktop
150 implements DropTargetListener, ClipboardOwner, IProgressIndicator,
151 StructureSelectionManagerProvider, ApplicationSingletonI
154 private static final String CITATION;
156 URL bg_logo_url = ChannelProperties.getImageURL("bg_logo." + String.valueOf(SplashScreen.logoSize));
157 URL uod_logo_url = ChannelProperties.getImageURL("uod_banner." + String.valueOf(SplashScreen.logoSize));
158 boolean logo = (bg_logo_url != null || uod_logo_url != null);
159 StringBuilder sb = new StringBuilder();
161 "<br><br>Jalview is free software released under GPLv3.<br><br>Development is managed by The Barton Group, University of Dundee, Scotland, UK.");
166 sb.append(bg_logo_url == null ? "" : "<img alt=\"Barton Group logo\" src=\"" + bg_logo_url.toString() + "\">");
167 sb.append(uod_logo_url == null ? ""
168 : " <img alt=\"University of Dundee shield\" src=\"" + uod_logo_url.toString() + "\">");
170 "<br><br>For help, see <a href=\"https://www.jalview.org/faq\">www.jalview.org/faq</a> and join <a href=\"https://discourse.jalview.org\">discourse.jalview.org</a>");
171 sb.append("<br><br>If you use Jalview, please cite:"
172 + "<br>Waterhouse, A.M., Procter, J.B., Martin, D.M.A, Clamp, M. and Barton, G. J. (2009)"
173 + "<br>Jalview Version 2 - a multiple sequence alignment editor and analysis workbench"
174 + "<br>Bioinformatics <a href=\"https://doi.org/10.1093/bioinformatics/btp033\">doi: 10.1093/bioinformatics/btp033</a>");
175 CITATION = sb.toString();
178 private static final String DEFAULT_AUTHORS = "The Jalview Authors (See AUTHORS file for current list)";
180 private static int DEFAULT_MIN_WIDTH = 300;
182 private static int DEFAULT_MIN_HEIGHT = 250;
184 private static int ALIGN_FRAME_DEFAULT_MIN_WIDTH = 600;
186 private static int ALIGN_FRAME_DEFAULT_MIN_HEIGHT = 70;
188 private static final String EXPERIMENTAL_FEATURES = "EXPERIMENTAL_FEATURES";
190 public static final String CONFIRM_KEYBOARD_QUIT = "CONFIRM_KEYBOARD_QUIT";
192 public static HashMap<String, FileWriter> savingFiles = new HashMap<String, FileWriter>();
194 @SuppressWarnings("deprecation")
195 private JalviewChangeSupport changeSupport = new JalviewChangeSupport();
197 public static boolean nosplash = false;
200 * news reader - null if it was never started.
202 private BlogReader jvnews = null;
204 private File projectFile;
208 * @see jalview.gui.JalviewChangeSupport#addJalviewPropertyChangeListener(java.beans.PropertyChangeListener)
211 public void addJalviewPropertyChangeListener(
212 PropertyChangeListener listener)
214 changeSupport.addJalviewPropertyChangeListener(listener);
218 * @param propertyName
220 * @see jalview.gui.JalviewChangeSupport#addJalviewPropertyChangeListener(java.lang.String,
221 * java.beans.PropertyChangeListener)
224 public void addJalviewPropertyChangeListener(String propertyName,
225 PropertyChangeListener listener)
227 changeSupport.addJalviewPropertyChangeListener(propertyName, listener);
231 * @param propertyName
233 * @see jalview.gui.JalviewChangeSupport#removeJalviewPropertyChangeListener(java.lang.String,
234 * java.beans.PropertyChangeListener)
237 public void removeJalviewPropertyChangeListener(String propertyName,
238 PropertyChangeListener listener)
240 changeSupport.removeJalviewPropertyChangeListener(propertyName,
244 private MyDesktopPane desktopPane;
246 public static MyDesktopPane getDesktopPane()
248 Desktop desktop = getInstance();
249 return desktop == null ? null : desktop.desktopPane;
253 * Answers an 'application scope' singleton instance of this class. Separate
254 * SwingJS 'applets' running in the same browser page will each have a
255 * distinct instance of Desktop.
259 public static Desktop getInstance()
261 return Jalview.isHeadlessMode() ? null
262 : ApplicationSingletonProvider.getInstance(Desktop.class);
265 public static StructureSelectionManager getStructureSelectionManager()
267 return StructureSelectionManager
268 .getStructureSelectionManager(getInstance());
271 int openFrameCount = 0;
273 final int xOffset = 30;
275 final int yOffset = 30;
277 public jalview.ws.jws1.Discoverer discoverer;
279 public Object[] jalviewClipboard;
281 public boolean internalCopy = false;
283 int fileLoadingCount = 0;
285 class MyDesktopManager implements DesktopManager
288 private DesktopManager delegate;
290 public MyDesktopManager(DesktopManager delegate)
292 this.delegate = delegate;
296 public void activateFrame(JInternalFrame f)
300 delegate.activateFrame(f);
301 } catch (NullPointerException npe)
303 Point p = getMousePosition();
304 showPasteMenu(p.x, p.y);
309 public void beginDraggingFrame(JComponent f)
311 delegate.beginDraggingFrame(f);
315 public void beginResizingFrame(JComponent f, int direction)
317 delegate.beginResizingFrame(f, direction);
321 public void closeFrame(JInternalFrame f)
323 delegate.closeFrame(f);
327 public void deactivateFrame(JInternalFrame f)
329 delegate.deactivateFrame(f);
333 public void deiconifyFrame(JInternalFrame f)
335 delegate.deiconifyFrame(f);
339 public void dragFrame(JComponent f, int newX, int newY)
345 delegate.dragFrame(f, newX, newY);
349 public void endDraggingFrame(JComponent f)
351 delegate.endDraggingFrame(f);
352 desktopPane.repaint();
356 public void endResizingFrame(JComponent f)
358 delegate.endResizingFrame(f);
359 desktopPane.repaint();
363 public void iconifyFrame(JInternalFrame f)
365 delegate.iconifyFrame(f);
369 public void maximizeFrame(JInternalFrame f)
371 delegate.maximizeFrame(f);
375 public void minimizeFrame(JInternalFrame f)
377 delegate.minimizeFrame(f);
381 public void openFrame(JInternalFrame f)
383 delegate.openFrame(f);
387 public void resizeFrame(JComponent f, int newX, int newY, int newWidth,
394 delegate.resizeFrame(f, newX, newY, newWidth, newHeight);
398 public void setBoundsForFrame(JComponent f, int newX, int newY,
399 int newWidth, int newHeight)
401 delegate.setBoundsForFrame(f, newX, newY, newWidth, newHeight);
404 // All other methods, simply delegate
409 * Private constructor enforces singleton pattern. It is called by reflection
410 * from ApplicationSingletonProvider.getInstance().
418 * A note to implementors. It is ESSENTIAL that any activities that might
419 * block are spawned off as threads rather than waited for during this
423 doConfigureStructurePrefs();
424 setTitle(ChannelProperties.getProperty("app_name") + " " + Cache.getProperty("VERSION"));
427 * Set taskbar "grouped windows" name for linux desktops (works in GNOME and
428 * KDE). This uses sun.awt.X11.XToolkit.awtAppClassName which is not officially
429 * documented or guaranteed to exist, so we access it via reflection. There
430 * appear to be unfathomable criteria about what this string can contain, and it
431 * if doesn't meet those criteria then "java" (KDE) or "jalview-bin-Jalview"
432 * (GNOME) is used. "Jalview", "Jalview Develop" and "Jalview Test" seem okay,
433 * but "Jalview non-release" does not. The reflection access may generate a
434 * warning: WARNING: An illegal reflective access operation has occurred
435 * WARNING: Illegal reflective access by jalview.gui.Desktop () to field
436 * sun.awt.X11.XToolkit.awtAppClassName which I don't think can be avoided.
438 if (Platform.isLinux())
440 if (LaunchUtils.getJavaVersion() >= 11)
442 jalview.bin.Console.info(
443 "Linux platform only! You may have the following warning next: \"WARNING: An illegal reflective access operation has occurred\"\nThis is expected and cannot be avoided, sorry about that.");
447 Toolkit xToolkit = Toolkit.getDefaultToolkit();
448 Field[] declaredFields = xToolkit.getClass().getDeclaredFields();
449 Field awtAppClassNameField = null;
451 if (Arrays.stream(declaredFields).anyMatch(f -> f.getName().equals("awtAppClassName"))) {
452 awtAppClassNameField = xToolkit.getClass().getDeclaredField("awtAppClassName");
455 String title = ChannelProperties.getProperty("app_name");
456 if (awtAppClassNameField != null) {
457 awtAppClassNameField.setAccessible(true);
458 awtAppClassNameField.set(xToolkit, title);
462 jalview.bin.Console.debug("XToolkit: awtAppClassName not found");
464 } catch (Exception e)
466 jalview.bin.Console.debug("Error setting awtAppClassName");
467 jalview.bin.Console.trace(Cache.getStackTraceString(e));
472 * APQHandlers sets handlers for About, Preferences and Quit actions peculiar to
473 * macOS's application menu. APQHandlers will check to see if a handler is
474 * supported before setting it.
477 APQHandlers.setAPQHandlers(this);
478 } catch (Exception e) {
479 System.out.println("Cannot set APQHandlers");
480 // e.printStackTrace();
481 } catch (Throwable t) {
482 jalview.bin.Console.warn("Error setting APQHandlers: " + t.toString());
483 jalview.bin.Console.trace(Cache.getStackTraceString(t));
486 setIconImages(ChannelProperties.getIconList());
488 addWindowListener(new WindowAdapter() {
491 public void windowClosing(WindowEvent ev) {
496 boolean selmemusage = Cache.getDefault("SHOW_MEMUSAGE", false);
498 boolean showjconsole = Cache.getDefault("SHOW_JAVA_CONSOLE", false);
499 desktopPane = new MyDesktopPane(selmemusage);
501 showMemusage.setSelected(selmemusage);
502 desktopPane.setBackground(Color.white);
504 getContentPane().setLayout(new BorderLayout());
505 // alternate config - have scrollbars - see notes in JAL-153
506 // JScrollPane sp = new JScrollPane();
507 // sp.getViewport().setView(desktop);
508 // getContentPane().add(sp, BorderLayout.CENTER);
510 // BH 2018 - just an experiment to try unclipped JInternalFrames.
513 getRootPane().putClientProperty("swingjs.overflow.hidden", "false");
516 getContentPane().add(desktopPane, BorderLayout.CENTER);
517 desktopPane.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
520 // This line prevents Windows Look&Feel resizing all new windows to
522 // if previous window was maximised
523 desktopPane.setDesktopManager(new MyDesktopManager(
524 (Platform.isWindowsAndNotJS() ? new DefaultDesktopManager()
525 : Platform.isAMacAndNotJS()
526 ? new AquaInternalFrameManager(
527 desktopPane.getDesktopManager())
528 : desktopPane.getDesktopManager())));
530 Rectangle dims = getLastKnownDimensions("");
537 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
538 int xPos = Math.max(5, (screenSize.width - 900) / 2);
539 int yPos = Math.max(5, (screenSize.height - 650) / 2);
540 setBounds(xPos, yPos, 900, 650);
543 if (!Platform.isJS())
550 jconsole = new Console(this, showjconsole);
551 jconsole.setHeader(Cache.getVersionDetailsForConsole());
552 showConsole(showjconsole);
554 showNews.setVisible(false); // not sure if we should only do this for interactive session?
556 experimentalFeatures.setSelected(showExperimental());
558 getIdentifiersOrgData();
560 if (Jalview.isInteractive())
562 // disabled for SeqCanvasTest
565 // Spawn a thread that shows the splashscreen
567 SwingUtilities.invokeLater(new Runnable()
572 new SplashScreen(true);
577 // Thread off a new instance of the file chooser - this reduces the
580 // takes to open it later on.
581 new Thread(new Runnable()
586 jalview.bin.Console.debug("Filechooser init thread started.");
587 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
588 JalviewFileChooser.forRead(
589 Cache.getProperty("LAST_DIRECTORY"), fileFormat);
590 jalview.bin.Console.debug("Filechooser init thread finished.");
593 // Add the service change listener
594 changeSupport.addJalviewPropertyChangeListener("services",
595 new PropertyChangeListener()
599 public void propertyChange(PropertyChangeEvent evt)
601 jalview.bin.Console.debug("Firing service changed event for "
602 + evt.getNewValue());
603 JalviewServicesChanged(evt);
609 this.setDropTarget(new java.awt.dnd.DropTarget(desktopPane, this));
611 this.addWindowListener(new WindowAdapter()
614 public void windowClosing(WindowEvent evt)
621 this.addMouseListener(ma = new MouseAdapter()
624 public void mousePressed(MouseEvent evt)
626 if (evt.isPopupTrigger()) // Mac
628 showPasteMenu(evt.getX(), evt.getY());
632 public void mouseReleased(MouseEvent evt)
634 if (evt.isPopupTrigger()) // Windows
636 showPasteMenu(evt.getX(), evt.getY());
640 desktopPane.addMouseListener(ma);
641 } catch (Throwable t)
648 * Answers true if user preferences to enable experimental features is True
653 public boolean showExperimental()
655 String experimental = Cache.getDefault(EXPERIMENTAL_FEATURES,
656 Boolean.FALSE.toString());
657 return Boolean.valueOf(experimental).booleanValue();
660 public void doConfigureStructurePrefs()
662 // configure services
663 StructureSelectionManager ssm = StructureSelectionManager.getStructureSelectionManager(this);
664 if (Cache.getDefault(Preferences.ADD_SS_ANN, true)) {
665 ssm.setAddTempFacAnnot(Cache.getDefault(Preferences.ADD_TEMPFACT_ANN, true));
666 ssm.setProcessSecondaryStructure(Cache.getDefault(Preferences.STRUCT_FROM_PDB, true));
667 // JAL-3915 - RNAView is no longer an option so this has no effect
668 ssm.setSecStructServices(Cache.getDefault(Preferences.USE_RNAVIEW, false));
670 ssm.setAddTempFacAnnot(false);
671 ssm.setProcessSecondaryStructure(false);
672 ssm.setSecStructServices(false);
676 public void checkForNews() {
677 final Desktop me = this;
678 // Thread off the news reader, in case there are connection problems.
679 new Thread(new Runnable() {
683 jalview.bin.Console.debug("Starting news thread.");
684 jvnews = new BlogReader(me);
685 showNews.setVisible(true);
686 jalview.bin.Console.debug("Completed news thread.");
691 public void getIdentifiersOrgData() {
692 if (Cache.getProperty("NOIDENTIFIERSSERVICE") == null) {
693 // Thread off the identifiers fetcher
694 new Thread(new Runnable() {
698 jalview.bin.Console.debug("Downloading data from identifiers.org");
701 UrlDownloadClient.download(IdOrgSettings.getUrl(),
702 IdOrgSettings.getDownloadLocation());
703 } catch (IOException e)
705 jalview.bin.Console.debug("Exception downloading identifiers.org data"
714 protected void showNews_actionPerformed(ActionEvent e) {
715 showNews(showNews.isSelected());
718 void showNews(boolean visible)
720 jalview.bin.Console.debug((visible ? "Showing" : "Hiding") + " news.");
721 showNews.setSelected(visible);
722 if (visible && !jvnews.isVisible())
724 new Thread(new Runnable()
729 long now = System.currentTimeMillis();
730 setProgressBar(MessageManager.getString("status.refreshing_news"),
732 jvnews.refreshNews();
733 setProgressBar(null, now);
741 * recover the last known dimensions for a jalview window
744 * - empty string is desktop, all other windows have unique prefix
745 * @return null or last known dimensions scaled to current geometry (if last
746 * window geom was known)
748 Rectangle getLastKnownDimensions(String windowName)
750 // TODO: lock aspect ratio for scaling desktop Bug #0058199
751 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
752 String x = Cache.getProperty(windowName + "SCREEN_X");
753 String y = Cache.getProperty(windowName + "SCREEN_Y");
754 String width = Cache.getProperty(windowName + "SCREEN_WIDTH");
755 String height = Cache.getProperty(windowName + "SCREEN_HEIGHT");
756 if ((x != null) && (y != null) && (width != null) && (height != null)) {
757 int ix = Integer.parseInt(x), iy = Integer.parseInt(y), iw = Integer.parseInt(width),
758 ih = Integer.parseInt(height);
759 if (Cache.getProperty("SCREENGEOMETRY_WIDTH") != null) {
760 // attempt #1 - try to cope with change in screen geometry - this
761 // version doesn't preserve original jv aspect ratio.
762 // take ratio of current screen size vs original screen size.
763 double sw = ((1f * screenSize.width) / (1f * Integer.parseInt(Cache.getProperty("SCREENGEOMETRY_WIDTH"))));
764 double sh = ((1f * screenSize.height) / (1f * Integer.parseInt(Cache.getProperty("SCREENGEOMETRY_HEIGHT"))));
765 // rescale the bounds depending upon the current screen geometry.
766 ix = (int) (ix * sw);
767 iw = (int) (iw * sw);
768 iy = (int) (iy * sh);
769 ih = (int) (ih * sh);
770 while (ix >= screenSize.width)
772 jalview.bin.Console.debug(
773 "Window geometry location recall error: shifting horizontal to within screenbounds.");
774 ix -= screenSize.width;
776 while (iy >= screenSize.height)
778 jalview.bin.Console.debug(
779 "Window geometry location recall error: shifting vertical to within screenbounds.");
780 iy -= screenSize.height;
782 jalview.bin.Console.debug(
783 "Got last known dimensions for " + windowName + ": x:" + ix
784 + " y:" + iy + " width:" + iw + " height:" + ih);
786 // return dimensions for new instance
787 return new Rectangle(ix, iy, iw, ih);
792 void showPasteMenu(int x, int y)
794 JPopupMenu popup = new JPopupMenu();
795 JMenuItem item = new JMenuItem(
796 MessageManager.getString("label.paste_new_window"));
797 item.addActionListener(new ActionListener()
800 public void actionPerformed(ActionEvent evt)
807 popup.show(this, x, y);
814 Clipboard c = Toolkit.getDefaultToolkit().getSystemClipboard();
815 Transferable contents = c.getContents(this);
817 if (contents != null)
819 String file = (String) contents
820 .getTransferData(DataFlavor.stringFlavor);
822 FileFormatI format = new IdentifyFile().identify(file,
823 DataSourceType.PASTE);
825 new FileLoader().LoadFile(file, DataSourceType.PASTE, format);
828 } catch (Exception ex)
831 "Unable to paste alignment from system clipboard:\n" + ex);
838 * Adds and opens the given frame to the desktop that is visible, allowed to
839 * resize, and has a 300px minimum width.
850 public static synchronized void addInternalFrame(
851 final JInternalFrame frame, String title, int w, int h)
855 addInternalFrame(frame, title, Desktop.FRAME_MAKE_VISIBLE, w, h,
856 FRAME_ALLOW_RESIZE, FRAME_SET_MIN_SIZE_300);
860 * Add an internal frame to the Jalview desktop that may optionally be
861 * visible, resizable, and allowed to be any size
868 * When true, display frame immediately, otherwise, caller must call
869 * setVisible themselves.
876 * @param ignoreMinSize
877 * Do not set the default minimum size for frame
879 public static synchronized void addInternalFrame(
880 final JInternalFrame frame, String title, boolean makeVisible,
881 int w, int h, boolean resizable, boolean ignoreMinSize)
883 // 15 classes call this method directly.
886 // TODO: allow callers to determine X and Y position of frame (eg. via
888 // TODO: consider fixing method to update entries in the window submenu with
889 // the current window title
891 frame.setTitle(title);
892 if (frame.getWidth() < 1 || frame.getHeight() < 1)
896 if (getInstance() != null)
897 getInstance().addFrame(frame, makeVisible, resizable,
901 // These can now by put into a single int flag, if desired:
903 public final static boolean FRAME_MAKE_VISIBLE = true;
905 public final static boolean FRAME_NOT_VISIBLE = false;
907 public final static boolean FRAME_ALLOW_RESIZE = true;
909 public final static boolean FRAME_NOT_RESIZABLE = false;
911 public final static boolean FRAME_ALLOW_ANY_SIZE = true;
913 public final static boolean FRAME_SET_MIN_SIZE_300 = false;
915 private void addFrame(JInternalFrame frame,
916 boolean makeVisible, boolean resizable,
917 boolean ignoreMinSize)
923 boolean isEmbedded = (Platform.getEmbeddedAttribute(frame, "id") != null);
924 boolean hasEmbeddedSize = (Platform.getDimIfEmbedded(frame, -1, -1) != null);
925 // Web page embedding allows us to ignore minimum size
926 ignoreMinSize |= hasEmbeddedSize;
931 // Set default dimension for Alignment Frame window.
932 // The Alignment Frame window could be added from a number of places,
934 // I did this here in order not to miss out on any Alignment frame.
935 if (frame instanceof AlignFrame)
937 frame.setMinimumSize(new Dimension(ALIGN_FRAME_DEFAULT_MIN_WIDTH,
938 ALIGN_FRAME_DEFAULT_MIN_HEIGHT));
940 frame.setMinimumSize(
941 new Dimension(DEFAULT_MIN_WIDTH, DEFAULT_MIN_HEIGHT));
946 frame.setVisible(makeVisible);
947 frame.setClosable(true);
948 frame.setResizable(resizable);
949 frame.setMaximizable(resizable);
950 frame.setIconifiable(resizable);
951 frame.setOpaque(Platform.isJS());
953 if (!isEmbedded && frame.getX() < 1 && frame.getY() < 1)
955 frame.setLocation(xOffset * openFrameCount,
956 yOffset * ((openFrameCount - 1) % 10) + yOffset);
960 * add an entry for the new frame in the Window menu
961 * (and remove it when the frame is closed)
963 final JMenuItem menuItem = new JMenuItem(frame.getTitle());
964 frame.addInternalFrameListener(new InternalFrameAdapter()
967 public void internalFrameActivated(InternalFrameEvent evt)
969 JInternalFrame itf = getDesktopPane().getSelectedFrame();
972 if (itf instanceof AlignFrame)
974 Jalview.setCurrentAlignFrame((AlignFrame) itf);
981 public void internalFrameClosed(InternalFrameEvent evt)
983 PaintRefresher.RemoveComponent(frame);
986 * defensive check to prevent frames being
987 * added half off the window
989 if (openFrameCount > 0)
995 * ensure no reference to alignFrame retained by menu item listener
997 if (menuItem.getActionListeners().length > 0)
999 menuItem.removeActionListener(menuItem.getActionListeners()[0]);
1001 getInstance().windowMenu.remove(menuItem);
1005 menuItem.addActionListener(new ActionListener()
1008 public void actionPerformed(ActionEvent e)
1012 frame.setSelected(true);
1013 frame.setIcon(false);
1014 } catch (java.beans.PropertyVetoException ex)
1016 // System.err.println(ex.toString());
1022 setKeyBindings(frame);
1024 getDesktopPane().add(frame);
1026 getInstance().windowMenu.add(menuItem);
1031 frame.setSelected(true);
1032 frame.requestFocus();
1033 } catch (java.beans.PropertyVetoException ve)
1035 } catch (java.lang.ClassCastException cex)
1037 jalview.bin.Console.warn(
1038 "Squashed a possible GUI implementation error. If you can recreate this, please look at https://issues.jalview.org/browse/JAL-869",
1044 * Add key bindings to a JInternalFrame so that Ctrl-W and Cmd-W will close
1049 private static void setKeyBindings(JInternalFrame frame)
1051 @SuppressWarnings("serial")
1052 final Action closeAction = new AbstractAction()
1055 public void actionPerformed(ActionEvent e)
1062 * set up key bindings for Ctrl-W and Cmd-W, with the same (Close) action
1064 KeyStroke ctrlWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W, InputEvent.CTRL_DOWN_MASK);
1065 KeyStroke cmdWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W, Platform.SHORTCUT_KEY_MASK);
1067 InputMap inputMap = frame
1068 .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
1069 String ctrlW = ctrlWKey.toString();
1070 inputMap.put(ctrlWKey, ctrlW);
1071 inputMap.put(cmdWKey, ctrlW);
1073 ActionMap actionMap = frame.getActionMap();
1074 actionMap.put(ctrlW, closeAction);
1078 public void lostOwnership(Clipboard clipboard, Transferable contents)
1082 jalviewClipboard = null;
1085 internalCopy = false;
1089 public void dragEnter(DropTargetDragEvent evt)
1094 public void dragExit(DropTargetEvent evt)
1099 public void dragOver(DropTargetDragEvent evt)
1104 public void dropActionChanged(DropTargetDragEvent evt)
1115 public void drop(DropTargetDropEvent evt)
1117 boolean success = true;
1118 // JAL-1552 - acceptDrop required before getTransferable call for
1119 // Java's Transferable for native dnd
1120 evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
1121 Transferable t = evt.getTransferable();
1122 List<Object> files = new ArrayList<>();
1123 List<DataSourceType> protocols = new ArrayList<>();
1127 transferFromDropTarget(files, protocols, evt, t);
1128 } catch (Exception e)
1130 e.printStackTrace();
1138 for (int i = 0; i < files.size(); i++)
1140 // BH 2018 File or String
1141 Object file = files.get(i);
1142 String fileName = file.toString();
1143 DataSourceType protocol = (protocols == null)
1144 ? DataSourceType.FILE
1146 FileFormatI format = null;
1148 if (fileName.endsWith(".jar"))
1150 format = FileFormat.Jalview;
1155 format = new IdentifyFile().identify(file, protocol);
1157 if (file instanceof File)
1159 Platform.cacheFileData((File) file);
1161 new FileLoader().LoadFile(null, file, protocol, format);
1164 } catch (Exception ex)
1169 evt.dropComplete(success); // need this to ensure input focus is properly
1170 // transfered to any new windows created
1180 public void inputLocalFileMenuItem_actionPerformed(AlignViewport viewport)
1182 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
1183 JalviewFileChooser chooser = JalviewFileChooser.forRead(
1184 Cache.getProperty("LAST_DIRECTORY"), fileFormat,
1185 BackupFiles.getEnabled());
1187 chooser.setFileView(new JalviewFileView());
1188 chooser.setDialogTitle(
1189 MessageManager.getString("label.open_local_file"));
1190 chooser.setToolTipText(MessageManager.getString("action.open"));
1192 chooser.setResponseHandler(0, new Runnable()
1197 File selectedFile = chooser.getSelectedFile();
1198 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1200 FileFormatI format = chooser.getSelectedFormat();
1203 * Call IdentifyFile to verify the file contains what its extension implies.
1204 * Skip this step for dynamically added file formats, because
1205 * IdentifyFile does not know how to recognise them.
1207 if (FileFormats.getInstance().isIdentifiable(format))
1211 format = new IdentifyFile().identify(selectedFile,
1212 DataSourceType.FILE);
1213 } catch (FileFormatException e)
1215 // format = null; //??
1219 new FileLoader().LoadFile(viewport, selectedFile,
1220 DataSourceType.FILE, format);
1223 chooser.showOpenDialog(this);
1227 * Shows a dialog for input of a URL at which to retrieve alignment data
1232 public void inputURLMenuItem_actionPerformed(AlignViewport viewport)
1234 // This construct allows us to have a wider textfield
1236 JLabel label = new JLabel(
1237 MessageManager.getString("label.input_file_url"));
1239 JPanel panel = new JPanel(new GridLayout(2, 1));
1243 * the URL to fetch is
1244 * Java: an editable combobox with history
1245 * JS: (pending JAL-3038) a plain text field
1248 String urlBase = "https://www.";
1249 if (Platform.isJS())
1251 history = new JTextField(urlBase, 35);
1260 JComboBox<String> asCombo = new JComboBox<>();
1261 asCombo.setPreferredSize(new Dimension(400, 20));
1262 asCombo.setEditable(true);
1263 asCombo.addItem(urlBase);
1264 String historyItems = Cache.getProperty("RECENT_URL");
1265 if (historyItems != null)
1267 for (String token : historyItems.split("\\t"))
1269 asCombo.addItem(token);
1276 Object[] options = new Object[] { MessageManager.getString("action.ok"),
1277 MessageManager.getString("action.cancel") };
1278 Runnable action = new Runnable()
1283 @SuppressWarnings("unchecked")
1284 String url = (history instanceof JTextField ? ((JTextField) history).getText()
1285 : ((JComboBox<String>) history).getEditor().getItem().toString().trim());
1287 if (url.toLowerCase(Locale.ROOT).endsWith(".jar")) {
1288 if (viewport != null) {
1289 new FileLoader().LoadFile(viewport, url, DataSourceType.URL, FileFormat.Jalview);
1291 new FileLoader().LoadFile(url, DataSourceType.URL, FileFormat.Jalview);
1294 FileFormatI format = null;
1297 format = new IdentifyFile().identify(url, DataSourceType.URL);
1298 } catch (FileFormatException e)
1300 // TODO revise error handling, distinguish between
1301 // URL not found and response not valid
1306 String msg = MessageManager
1307 .formatMessage("label.couldnt_locate", url);
1308 JvOptionPane.showInternalMessageDialog(getDesktopPane(), msg,
1309 MessageManager.getString("label.url_not_found"),
1310 JvOptionPane.WARNING_MESSAGE);
1315 if (viewport != null)
1317 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1322 new FileLoader().LoadFile(url, DataSourceType.URL, format);
1327 String dialogOption = MessageManager
1328 .getString("label.input_alignment_from_url");
1329 JvOptionPane.newOptionDialog(desktopPane).setResponseHandler(0, action)
1330 .showInternalDialog(panel, dialogOption,
1331 JvOptionPane.YES_NO_CANCEL_OPTION,
1332 JvOptionPane.PLAIN_MESSAGE, null, options,
1333 MessageManager.getString("action.ok"));
1337 * Opens the CutAndPaste window for the user to paste an alignment in to
1340 * - if not null, the pasted alignment is added to the current
1341 * alignment; if null, to a new alignment window
1344 public void inputTextboxMenuItem_actionPerformed(
1345 AlignmentViewPanel viewPanel)
1347 CutAndPasteTransfer cap = new CutAndPasteTransfer();
1348 cap.setForInput(viewPanel);
1349 addInternalFrame(cap,
1350 MessageManager.getString("label.cut_paste_alignmen_file"),
1351 FRAME_MAKE_VISIBLE, 600, 500, FRAME_ALLOW_RESIZE,
1352 FRAME_SET_MIN_SIZE_300);
1361 Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
1362 Cache.setProperty("SCREENGEOMETRY_WIDTH", screen.width + "");
1363 Cache.setProperty("SCREENGEOMETRY_HEIGHT", screen.height + "");
1364 storeLastKnownDimensions("", new Rectangle(getBounds().x, getBounds().y,
1365 getWidth(), getHeight()));
1367 if (jconsole != null)
1369 storeLastKnownDimensions("JAVA_CONSOLE_", jconsole.getBounds());
1370 jconsole.stopConsole();
1374 storeLastKnownDimensions("JALVIEW_RSS_WINDOW_", jvnews.getBounds());
1377 if (dialogExecutor != null)
1379 dialogExecutor.shutdownNow();
1381 closeAll_actionPerformed(null);
1383 if (groovyConsole != null)
1385 // suppress a possible repeat prompt to save script
1386 groovyConsole.setDirty(false);
1387 groovyConsole.exit();
1392 private void storeLastKnownDimensions(String string, Rectangle jc)
1394 jalview.bin.Console.debug("Storing last known dimensions for " + string
1395 + ": x:" + jc.x + " y:" + jc.y + " width:" + jc.width
1396 + " height:" + jc.height);
1398 Cache.setProperty(string + "SCREEN_X", jc.x + "");
1399 Cache.setProperty(string + "SCREEN_Y", jc.y + "");
1400 Cache.setProperty(string + "SCREEN_WIDTH", jc.width + "");
1401 Cache.setProperty(string + "SCREEN_HEIGHT", jc.height + "");
1411 public void aboutMenuItem_actionPerformed(ActionEvent e)
1413 new Thread(new Runnable()
1418 new SplashScreen(false);
1424 * Returns the html text for the About screen, including any available version
1425 * number, build details, author details and citation reference, but without
1426 * the enclosing {@code html} tags
1430 public String getAboutMessage()
1432 StringBuilder message = new StringBuilder(1024);
1433 message.append("<div style=\"font-family: sans-serif;\">").append("<h1><strong>Version: ")
1434 .append(Cache.getProperty("VERSION")).append("</strong></h1>").append("<strong>Built: <em>")
1435 .append(Cache.getDefault("BUILD_DATE", "unknown")).append("</em> from ")
1436 .append(Cache.getBuildDetailsForSplash()).append("</strong>");
1438 String latestVersion = Cache.getDefault("LATEST_VERSION", "Checking");
1439 if (latestVersion.equals("Checking")) {
1440 // JBP removed this message for 2.11: May be reinstated in future version
1441 // message.append("<br>...Checking latest version...</br>");
1442 } else if (!latestVersion.equals(Cache.getProperty("VERSION"))) {
1443 boolean red = false;
1444 if (Cache.getProperty("VERSION").toLowerCase(Locale.ROOT).indexOf("automated build") == -1) {
1446 // Displayed when code version and jnlp version do not match and code
1447 // version is not a development build
1448 message.append("<div style=\"color: #FF0000;font-style: bold;\">");
1451 message.append("<br>!! Version ").append(Cache.getDefault("LATEST_VERSION", "..Checking.."))
1452 .append(" is available for download from ")
1453 .append(Cache.getDefault("www.jalview.org", "https://www.jalview.org")).append(" !!");
1455 message.append("</div>");
1458 message.append("<br>Authors: ");
1459 message.append(Cache.getDefault("AUTHORFNAMES", DEFAULT_AUTHORS));
1460 message.append(CITATION);
1462 message.append("</div>");
1464 return message.toString();
1468 * Action on requesting Help documentation
1471 public void documentationMenuItem_actionPerformed() {
1473 if (Platform.isJS()) {
1474 BrowserLauncher.openURL("https://www.jalview.org/help.html");
1482 Help.showHelpWindow();
1484 } catch (Exception ex) {
1485 System.err.println("Error opening help: " + ex.getMessage());
1490 public void closeAll_actionPerformed(ActionEvent e)
1492 // TODO show a progress bar while closing?
1493 JInternalFrame[] frames = desktopPane.getAllFrames();
1494 for (int i = 0; i < frames.length; i++)
1498 frames[i].setClosed(true);
1499 } catch (java.beans.PropertyVetoException ex)
1503 Jalview.setCurrentAlignFrame(null);
1504 System.out.println("ALL CLOSED");
1507 * reset state of singleton objects as appropriate (clear down session state
1508 * when all windows are closed)
1510 StructureSelectionManager ssm = StructureSelectionManager
1511 .getStructureSelectionManager(this);
1519 public void raiseRelated_actionPerformed(ActionEvent e)
1521 reorderAssociatedWindows(false, false);
1525 public void minimizeAssociated_actionPerformed(ActionEvent e)
1527 reorderAssociatedWindows(true, false);
1530 void closeAssociatedWindows()
1532 reorderAssociatedWindows(false, true);
1538 * @seejalview.jbgui.GDesktop#garbageCollect_actionPerformed(java.awt.event.
1542 protected void garbageCollect_actionPerformed(ActionEvent e)
1544 // We simply collect the garbage
1545 jalview.bin.Console.debug("Collecting garbage...");
1547 jalview.bin.Console.debug("Finished garbage collection.");
1554 * jalview.jbgui.GDesktop#showMemusage_actionPerformed(java.awt.event.ActionEvent
1558 protected void showMemusage_actionPerformed(ActionEvent e)
1560 desktopPane.showMemoryUsage(showMemusage.isSelected());
1567 * jalview.jbgui.GDesktop#showConsole_actionPerformed(java.awt.event.ActionEvent
1571 protected void showConsole_actionPerformed(ActionEvent e)
1573 showConsole(showConsole.isSelected());
1576 Console jconsole = null;
1579 * control whether the java console is visible or not
1583 void showConsole(boolean selected)
1585 // TODO: decide if we should update properties file
1586 if (jconsole != null) // BH 2018
1588 showConsole.setSelected(selected);
1589 Cache.setProperty("SHOW_JAVA_CONSOLE",
1590 Boolean.valueOf(selected).toString());
1591 jconsole.setVisible(selected);
1595 void reorderAssociatedWindows(boolean minimize, boolean close)
1597 JInternalFrame[] frames = desktopPane.getAllFrames();
1598 if (frames == null || frames.length < 1)
1603 AlignViewportI source = null;
1604 AlignViewportI target = null;
1605 if (frames[0] instanceof AlignFrame)
1607 source = ((AlignFrame) frames[0]).getCurrentView();
1609 else if (frames[0] instanceof TreePanel)
1611 source = ((TreePanel) frames[0]).getViewPort();
1613 else if (frames[0] instanceof PCAPanel)
1615 source = ((PCAPanel) frames[0]).av;
1617 else if (frames[0].getContentPane() instanceof PairwiseAlignPanel)
1619 source = ((PairwiseAlignPanel) frames[0].getContentPane()).av;
1624 for (int i = 0; i < frames.length; i++)
1627 if (frames[i] == null)
1631 if (frames[i] instanceof AlignFrame)
1633 target = ((AlignFrame) frames[i]).getCurrentView();
1635 else if (frames[i] instanceof TreePanel)
1637 target = ((TreePanel) frames[i]).getViewPort();
1639 else if (frames[i] instanceof PCAPanel)
1641 target = ((PCAPanel) frames[i]).av;
1643 else if (frames[i].getContentPane() instanceof PairwiseAlignPanel)
1645 target = ((PairwiseAlignPanel) frames[i].getContentPane()).av;
1648 if (source == target)
1654 frames[i].setClosed(true);
1658 frames[i].setIcon(minimize);
1661 frames[i].toFront();
1665 } catch (java.beans.PropertyVetoException ex)
1680 protected void preferences_actionPerformed(ActionEvent e) {
1681 Preferences.openPreferences();
1685 * Prompts the user to choose a file and then saves the Jalview state as a
1686 * Jalview project file
1689 public void saveState_actionPerformed()
1691 saveState_actionPerformed(false);
1694 public void saveState_actionPerformed(boolean saveAs)
1696 java.io.File projectFile = getProjectFile();
1697 // autoSave indicates we already have a file and don't need to ask
1698 boolean autoSave = projectFile != null && !saveAs
1699 && BackupFiles.getEnabled();
1701 // System.out.println("autoSave="+autoSave+", projectFile='"+projectFile+"',
1702 // saveAs="+saveAs+", Backups
1703 // "+(BackupFiles.getEnabled()?"enabled":"disabled"));
1705 boolean approveSave = false;
1708 JalviewFileChooser chooser = new JalviewFileChooser("jvp",
1711 chooser.setFileView(new JalviewFileView());
1712 chooser.setDialogTitle(MessageManager.getString("label.save_state"));
1714 int value = chooser.showSaveDialog(this);
1716 if (value == JalviewFileChooser.APPROVE_OPTION)
1718 projectFile = chooser.getSelectedFile();
1719 setProjectFile(projectFile);
1724 if (approveSave || autoSave) {
1725 final Desktop me = this;
1726 final java.io.File chosenFile = projectFile;
1727 new Thread(new Runnable()
1732 // TODO: refactor to Jalview desktop session controller action.
1733 setProgressBar(MessageManager.formatMessage(
1734 "label.saving_jalview_project", new Object[]
1735 { chosenFile.getName() }), chosenFile.hashCode());
1736 Cache.setProperty("LAST_DIRECTORY", chosenFile.getParent());
1737 // TODO catch and handle errors for savestate
1738 // TODO prevent user from messing with the Desktop whilst we're saving
1741 boolean doBackup = BackupFiles.getEnabled();
1742 BackupFiles backupfiles = doBackup ? new BackupFiles(chosenFile)
1745 new Jalview2XML().saveState(
1746 doBackup ? backupfiles.getTempFile() : chosenFile);
1750 backupfiles.setWriteSuccess(true);
1751 backupfiles.rollBackupsAndRenameTempFile();
1753 } catch (OutOfMemoryError oom)
1755 new OOMWarning("Whilst saving current state to "
1756 + chosenFile.getName(), oom);
1757 } catch (Exception ex)
1759 jalview.bin.Console.error("Problems whilst trying to save to "
1760 + chosenFile.getName(), ex);
1761 JvOptionPane.showMessageDialog(me,
1762 MessageManager.formatMessage(
1763 "label.error_whilst_saving_current_state_to",
1765 { chosenFile.getName() }),
1766 MessageManager.getString("label.couldnt_save_project"),
1767 JvOptionPane.WARNING_MESSAGE);
1769 setProgressBar(null, chosenFile.hashCode());
1776 public void saveAsState_actionPerformed(ActionEvent e)
1778 saveState_actionPerformed(true);
1781 private void setProjectFile(File choice)
1783 this.projectFile = choice;
1786 public File getProjectFile()
1788 return this.projectFile;
1792 * Shows a file chooser dialog and tries to read in the selected file as a
1796 public void loadState_actionPerformed()
1798 final String[] suffix = new String[] { "jvp", "jar" };
1799 final String[] desc = new String[] { "Jalview Project",
1800 "Jalview Project (old)" };
1801 JalviewFileChooser chooser = new JalviewFileChooser(
1802 Cache.getProperty("LAST_DIRECTORY"), suffix, desc,
1803 "Jalview Project", true, BackupFiles.getEnabled()); // last two
1807 chooser.setFileView(new JalviewFileView());
1808 chooser.setDialogTitle(MessageManager.getString("label.restore_state"));
1809 chooser.setResponseHandler(0, new Runnable()
1814 File selectedFile = chooser.getSelectedFile();
1815 setProjectFile(selectedFile);
1816 String choice = selectedFile.getAbsolutePath();
1817 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1818 new Thread(new Runnable()
1825 new Jalview2XML().loadJalviewAlign(selectedFile);
1826 } catch (OutOfMemoryError oom)
1828 new OOMWarning("Whilst loading project from " + choice, oom);
1829 } catch (Exception ex)
1831 jalview.bin.Console.error(
1832 "Problems whilst loading project from " + choice, ex);
1833 JvOptionPane.showMessageDialog(getDesktopPane(),
1834 MessageManager.formatMessage(
1835 "label.error_whilst_loading_project_from",
1839 .getString("label.couldnt_load_project"),
1840 JvOptionPane.WARNING_MESSAGE);
1843 }, "Project Loader").start();
1847 chooser.showOpenDialog(this);
1851 public void inputSequence_actionPerformed(ActionEvent e)
1853 new SequenceFetcher(this);
1856 JPanel progressPanel;
1858 ArrayList<JPanel> fileLoadingPanels = new ArrayList<>();
1860 public void startLoading(final Object fileName)
1862 if (fileLoadingCount == 0)
1864 fileLoadingPanels.add(addProgressPanel(MessageManager
1865 .formatMessage("label.loading_file", new Object[]
1871 private JPanel addProgressPanel(String string)
1873 if (progressPanel == null)
1875 progressPanel = new JPanel(new GridLayout(1, 1));
1876 totalProgressCount = 0;
1877 getContentPane().add(progressPanel, BorderLayout.SOUTH);
1879 JPanel thisprogress = new JPanel(new BorderLayout(10, 5));
1880 JProgressBar progressBar = new JProgressBar();
1881 progressBar.setIndeterminate(true);
1883 thisprogress.add(new JLabel(string), BorderLayout.WEST);
1885 thisprogress.add(progressBar, BorderLayout.CENTER);
1886 progressPanel.add(thisprogress);
1887 ((GridLayout) progressPanel.getLayout()).setRows(
1888 ((GridLayout) progressPanel.getLayout()).getRows() + 1);
1889 ++totalProgressCount;
1891 return thisprogress;
1894 int totalProgressCount = 0;
1896 private void removeProgressPanel(JPanel progbar)
1898 if (progressPanel != null)
1900 synchronized (progressPanel)
1902 progressPanel.remove(progbar);
1903 GridLayout gl = (GridLayout) progressPanel.getLayout();
1904 gl.setRows(gl.getRows() - 1);
1905 if (--totalProgressCount < 1)
1907 this.getContentPane().remove(progressPanel);
1908 progressPanel = null;
1915 public void stopLoading()
1918 if (fileLoadingCount < 1)
1920 while (fileLoadingPanels.size() > 0)
1922 removeProgressPanel(fileLoadingPanels.remove(0));
1924 fileLoadingPanels.clear();
1925 fileLoadingCount = 0;
1930 public static int getViewCount(String alignmentId)
1932 AlignmentViewport[] aps = getViewports(alignmentId);
1933 return (aps == null) ? 0 : aps.length;
1938 * @param alignmentId
1939 * - if null, all sets are returned
1940 * @return all AlignmentPanels concerning the alignmentId sequence set
1942 public static AlignmentPanel[] getAlignmentPanels(String alignmentId)
1944 if (getDesktopPane() == null)
1946 // no frames created and in headless mode
1947 // TODO: verify that frames are recoverable when in headless mode
1950 List<AlignmentPanel> aps = new ArrayList<>();
1951 AlignFrame[] frames = getAlignFrames();
1956 for (AlignFrame af : frames)
1958 for (AlignmentPanel ap : af.alignPanels)
1960 if (alignmentId == null
1961 || alignmentId.equals(ap.av.getSequenceSetId()))
1967 if (aps.size() == 0)
1971 AlignmentPanel[] vap = aps.toArray(new AlignmentPanel[aps.size()]);
1976 * get all the viewports on an alignment.
1978 * @param sequenceSetId
1979 * unique alignment id (may be null - all viewports returned in that
1981 * @return all viewports on the alignment bound to sequenceSetId
1983 public static AlignmentViewport[] getViewports(String sequenceSetId)
1985 List<AlignmentViewport> viewp = new ArrayList<>();
1986 if (getDesktopPane() != null)
1988 AlignFrame[] frames = getAlignFrames();
1990 for (AlignFrame afr : frames)
1992 if (sequenceSetId == null || afr.getViewport().getSequenceSetId()
1993 .equals(sequenceSetId))
1995 if (afr.alignPanels != null)
1997 for (AlignmentPanel ap : afr.alignPanels)
1999 if (sequenceSetId == null
2000 || sequenceSetId.equals(ap.av.getSequenceSetId()))
2008 viewp.add(afr.getViewport());
2012 if (viewp.size() > 0)
2014 return viewp.toArray(new AlignmentViewport[viewp.size()]);
2021 * Explode the views in the given frame into separate AlignFrame
2025 public static void explodeViews(AlignFrame af)
2027 int size = af.alignPanels.size();
2033 // FIXME: ideally should use UI interface API
2034 FeatureSettings viewFeatureSettings = (af.featureSettings != null
2035 && af.featureSettings.isOpen()) ? af.featureSettings : null;
2036 Rectangle fsBounds = af.getFeatureSettingsGeometry();
2037 for (int i = 0; i < size; i++)
2039 AlignmentPanel ap = af.alignPanels.get(i);
2041 AlignFrame newaf = new AlignFrame(ap);
2043 // transfer reference for existing feature settings to new alignFrame
2044 if (ap == af.alignPanel)
2046 if (viewFeatureSettings != null && viewFeatureSettings.fr.ap == ap)
2048 newaf.featureSettings = viewFeatureSettings;
2050 newaf.setFeatureSettingsGeometry(fsBounds);
2054 * Restore the view's last exploded frame geometry if known. Multiple
2055 * views from one exploded frame share and restore the same (frame)
2056 * position and size.
2058 Rectangle geometry = ap.av.getExplodedGeometry();
2059 if (geometry != null)
2061 newaf.setBounds(geometry);
2064 ap.av.setGatherViewsHere(false);
2066 addInternalFrame(newaf, af.getTitle(), AlignFrame.DEFAULT_WIDTH,
2067 AlignFrame.DEFAULT_HEIGHT);
2068 // and materialise a new feature settings dialog instance for the new
2070 // (closes the old as if 'OK' was pressed)
2071 if (ap == af.alignPanel && newaf.featureSettings != null
2072 && newaf.featureSettings.isOpen()
2073 && af.alignPanel.getAlignViewport().isShowSequenceFeatures())
2075 newaf.showFeatureSettingsUI();
2079 af.featureSettings = null;
2080 af.alignPanels.clear();
2081 af.closeMenuItem_actionPerformed(true);
2086 * Gather expanded views (separate AlignFrame's) with the same sequence set
2087 * identifier back in to this frame as additional views, and close the
2088 * expanded views. Note the expanded frames may themselves have multiple
2089 * views. We take the lot.
2093 public void gatherViews(AlignFrame source)
2095 source.viewport.setGatherViewsHere(true);
2096 source.viewport.setExplodedGeometry(source.getBounds());
2097 JInternalFrame[] frames = desktopPane.getAllFrames();
2098 String viewId = source.viewport.getSequenceSetId();
2099 for (int t = 0; t < frames.length; t++)
2101 if (frames[t] instanceof AlignFrame && frames[t] != source)
2103 AlignFrame af = (AlignFrame) frames[t];
2104 boolean gatherThis = false;
2105 for (int a = 0; a < af.alignPanels.size(); a++)
2107 AlignmentPanel ap = af.alignPanels.get(a);
2108 if (viewId.equals(ap.av.getSequenceSetId()))
2111 ap.av.setGatherViewsHere(false);
2112 ap.av.setExplodedGeometry(af.getBounds());
2113 source.addAlignmentPanel(ap, false);
2119 if (af.featureSettings != null && af.featureSettings.isOpen())
2121 if (source.featureSettings == null)
2123 // preserve the feature settings geometry for this frame
2124 source.featureSettings = af.featureSettings;
2125 source.setFeatureSettingsGeometry(
2126 af.getFeatureSettingsGeometry());
2130 // close it and forget
2131 af.featureSettings.close();
2134 af.alignPanels.clear();
2135 af.closeMenuItem_actionPerformed(true);
2140 // refresh the feature setting UI for the source frame if it exists
2141 if (source.featureSettings != null && source.featureSettings.isOpen())
2143 source.showFeatureSettingsUI();
2148 public JInternalFrame[] getAllFrames()
2150 return desktopPane.getAllFrames();
2154 * Checks the given url to see if it gives a response indicating that the user
2155 * should be informed of a new questionnaire.
2159 public void checkForQuestionnaire(String url)
2161 UserQuestionnaireCheck jvq = new UserQuestionnaireCheck(url);
2162 // javax.swing.SwingUtilities.invokeLater(jvq);
2163 new Thread(jvq).start();
2166 public void checkURLLinks()
2168 // Thread off the URL link checker
2169 addDialogThread(new Runnable()
2174 if (Cache.getDefault("CHECKURLLINKS", true))
2176 // check what the actual links are - if it's just the default don't
2177 // bother with the warning
2178 List<String> links = Preferences.sequenceUrlLinks
2181 // only need to check links if there is one with a
2182 // SEQUENCE_ID which is not the default EMBL_EBI link
2183 ListIterator<String> li = links.listIterator();
2184 boolean check = false;
2185 List<JLabel> urls = new ArrayList<>();
2186 while (li.hasNext())
2188 String link = li.next();
2189 if (link.contains(jalview.util.UrlConstants.SEQUENCE_ID)
2190 && !UrlConstants.isDefaultString(link))
2193 int barPos = link.indexOf("|");
2194 String urlMsg = barPos == -1 ? link
2195 : link.substring(0, barPos) + ": "
2196 + link.substring(barPos + 1);
2197 urls.add(new JLabel(urlMsg));
2205 // ask user to check in case URL links use old style tokens
2206 // ($SEQUENCE_ID$ for sequence id _or_ accession id)
2207 JPanel msgPanel = new JPanel();
2208 msgPanel.setLayout(new BoxLayout(msgPanel, BoxLayout.PAGE_AXIS));
2209 msgPanel.add(Box.createVerticalGlue());
2210 JLabel msg = new JLabel(MessageManager
2211 .getString("label.SEQUENCE_ID_for_DB_ACCESSION1"));
2212 JLabel msg2 = new JLabel(MessageManager
2213 .getString("label.SEQUENCE_ID_for_DB_ACCESSION2"));
2215 for (JLabel url : urls)
2221 final JCheckBox jcb = new JCheckBox(
2222 MessageManager.getString("label.do_not_display_again"));
2223 jcb.addActionListener(new ActionListener()
2226 public void actionPerformed(ActionEvent e)
2228 // update Cache settings for "don't show this again"
2229 boolean showWarningAgain = !jcb.isSelected();
2230 Cache.setProperty("CHECKURLLINKS",
2231 Boolean.valueOf(showWarningAgain).toString());
2236 JvOptionPane.showMessageDialog(desktopPane, msgPanel,
2238 .getString("label.SEQUENCE_ID_no_longer_used"),
2239 JvOptionPane.WARNING_MESSAGE);
2246 * Proxy class for JDesktopPane which optionally displays the current memory
2247 * usage and highlights the desktop area with a red bar if free memory runs
2252 public class MyDesktopPane extends JDesktopPane implements Runnable
2254 private static final float ONE_MB = 1048576f;
2256 boolean showMemoryUsage = false;
2260 java.text.NumberFormat df;
2262 float maxMemory, allocatedMemory, freeMemory, totalFreeMemory,
2265 public MyDesktopPane(boolean showMemoryUsage)
2267 showMemoryUsage(showMemoryUsage);
2270 public void showMemoryUsage(boolean showMemory)
2272 this.showMemoryUsage = showMemory;
2275 Thread worker = new Thread(this);
2281 public boolean isShowMemoryUsage()
2283 return showMemoryUsage;
2289 df = java.text.NumberFormat.getNumberInstance();
2290 df.setMaximumFractionDigits(2);
2291 runtime = Runtime.getRuntime();
2293 while (showMemoryUsage)
2297 maxMemory = runtime.maxMemory() / ONE_MB;
2298 allocatedMemory = runtime.totalMemory() / ONE_MB;
2299 freeMemory = runtime.freeMemory() / ONE_MB;
2300 totalFreeMemory = freeMemory + (maxMemory - allocatedMemory);
2302 percentUsage = (totalFreeMemory / maxMemory) * 100;
2304 // if (percentUsage < 20)
2306 // border1 = BorderFactory.createMatteBorder(12, 12, 12, 12,
2308 // instance.set.setBorder(border1);
2311 // sleep after showing usage
2313 } catch (Exception ex)
2315 ex.printStackTrace();
2321 public void paintComponent(Graphics g)
2323 if (showMemoryUsage && g != null && df != null)
2325 if (percentUsage < 20)
2327 g.setColor(Color.red);
2329 FontMetrics fm = g.getFontMetrics();
2332 g.drawString(MessageManager.formatMessage("label.memory_stats",
2334 { df.format(totalFreeMemory), df.format(maxMemory),
2335 df.format(percentUsage) }),
2336 10, getHeight() - fm.getHeight());
2340 // output debug scale message. Important for jalview.bin.HiDPISettingTest2
2341 Desktop.debugScaleMessage(Desktop.getDesktopPane().getGraphics());
2346 * Accessor method to quickly get all the AlignmentFrames loaded.
2348 * @return an array of AlignFrame, or null if none found
2350 public static AlignFrame[] getAlignFrames()
2352 if (Jalview.isHeadlessMode())
2354 return new AlignFrame[] { Jalview.getInstance().currentAlignFrame };
2357 JInternalFrame[] frames = getDesktopPane().getAllFrames();
2363 List<AlignFrame> avp = new ArrayList<>();
2365 for (int i = frames.length - 1; i > -1; i--)
2367 if (frames[i] instanceof AlignFrame)
2369 avp.add((AlignFrame) frames[i]);
2371 else if (frames[i] instanceof SplitFrame)
2374 * Also check for a split frame containing an AlignFrame
2376 GSplitFrame sf = (GSplitFrame) frames[i];
2377 if (sf.getTopFrame() instanceof AlignFrame)
2379 avp.add((AlignFrame) sf.getTopFrame());
2381 if (sf.getBottomFrame() instanceof AlignFrame)
2383 avp.add((AlignFrame) sf.getBottomFrame());
2387 if (avp.size() == 0)
2391 AlignFrame afs[] = avp.toArray(new AlignFrame[avp.size()]);
2396 * Returns an array of any AppJmol frames in the Desktop (or null if none).
2400 public GStructureViewer[] getJmols()
2402 JInternalFrame[] frames = desktopPane.getAllFrames();
2408 List<GStructureViewer> avp = new ArrayList<>();
2410 for (int i = frames.length - 1; i > -1; i--)
2412 if (frames[i] instanceof AppJmol)
2414 GStructureViewer af = (GStructureViewer) frames[i];
2418 if (avp.size() == 0)
2422 GStructureViewer afs[] = avp.toArray(new GStructureViewer[avp.size()]);
2427 * Add Groovy Support to Jalview
2430 public void groovyShell_actionPerformed()
2434 openGroovyConsole();
2435 } catch (Exception ex)
2437 jalview.bin.Console.error("Groovy Shell Creation failed.", ex);
2438 JvOptionPane.showInternalMessageDialog(desktopPane,
2440 MessageManager.getString("label.couldnt_create_groovy_shell"),
2441 MessageManager.getString("label.groovy_support_failed"),
2442 JvOptionPane.ERROR_MESSAGE);
2447 * Open the Groovy console
2449 void openGroovyConsole()
2451 if (groovyConsole == null)
2453 groovyConsole = new groovy.ui.Console();
2454 groovyConsole.setVariable("Jalview", this);
2455 groovyConsole.run();
2458 * We allow only one console at a time, so that AlignFrame menu option
2459 * 'Calculate | Run Groovy script' is unambiguous.
2460 * Disable 'Groovy Console', and enable 'Run script', when the console is
2461 * opened, and the reverse when it is closed
2463 Window window = (Window) groovyConsole.getFrame();
2464 window.addWindowListener(new WindowAdapter()
2467 public void windowClosed(WindowEvent e)
2470 * rebind CMD-Q from Groovy Console to Jalview Quit
2473 enableExecuteGroovy(false);
2479 * show Groovy console window (after close and reopen)
2481 ((Window) groovyConsole.getFrame()).setVisible(true);
2484 * if we got this far, enable 'Run Groovy' in AlignFrame menus
2485 * and disable opening a second console
2487 enableExecuteGroovy(true);
2491 * Bind Ctrl/Cmd-Q to Quit - for reset as Groovy Console takes over this
2492 * binding when opened
2494 protected void addQuitHandler()
2497 getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
2498 .put(KeyStroke.getKeyStroke(KeyEvent.VK_Q,
2499 Platform.SHORTCUT_KEY_MASK),
2501 getRootPane().getActionMap().put("Quit", new AbstractAction()
2504 public void actionPerformed(ActionEvent e)
2512 * Enable or disable 'Run Groovy script' in AlignFrame calculate menus
2515 * true if Groovy console is open
2517 public void enableExecuteGroovy(boolean enabled)
2520 * disable opening a second Groovy console
2521 * (or re-enable when the console is closed)
2523 groovyShell.setEnabled(!enabled);
2525 AlignFrame[] alignFrames = getAlignFrames();
2526 if (alignFrames != null)
2528 for (AlignFrame af : alignFrames)
2530 af.setGroovyEnabled(enabled);
2536 * Progress bars managed by the IProgressIndicator method.
2538 private Hashtable<Long, JPanel> progressBars;
2540 private Hashtable<Long, IProgressIndicatorHandler> progressBarHandlers;
2545 * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
2548 public void setProgressBar(String message, long id)
2550 // Platform.timeCheck("Desktop " + message, Platform.TIME_MARK);
2552 if (progressBars == null)
2554 progressBars = new Hashtable<>();
2555 progressBarHandlers = new Hashtable<>();
2558 if (progressBars.get(Long.valueOf(id)) != null)
2560 JPanel panel = progressBars.remove(Long.valueOf(id));
2561 if (progressBarHandlers.contains(Long.valueOf(id)))
2563 progressBarHandlers.remove(Long.valueOf(id));
2565 removeProgressPanel(panel);
2569 progressBars.put(Long.valueOf(id), addProgressPanel(message));
2574 public void addProgressBar(long id, String message)
2577 throw new UnsupportedOperationException("not implemented");
2581 public void removeProgressBar(long id)
2584 throw new UnsupportedOperationException("not implemented");
2590 * @see jalview.gui.IProgressIndicator#registerHandler(long,
2591 * jalview.gui.IProgressIndicatorHandler)
2594 public void registerHandler(final long id,
2595 final IProgressIndicatorHandler handler)
2597 if (progressBarHandlers == null
2598 || !progressBars.containsKey(Long.valueOf(id)))
2600 throw new Error(MessageManager.getString(
2601 "error.call_setprogressbar_before_registering_handler"));
2603 progressBarHandlers.put(Long.valueOf(id), handler);
2604 final JPanel progressPanel = progressBars.get(Long.valueOf(id));
2605 if (handler.canCancel())
2607 JButton cancel = new JButton(
2608 MessageManager.getString("action.cancel"));
2609 final IProgressIndicator us = this;
2610 cancel.addActionListener(new ActionListener()
2614 public void actionPerformed(ActionEvent e)
2616 handler.cancelActivity(id);
2617 us.setProgressBar(MessageManager
2618 .formatMessage("label.cancelled_params", new Object[]
2619 { ((JLabel) progressPanel.getComponent(0)).getText() }),
2623 progressPanel.add(cancel, BorderLayout.EAST);
2629 * @return true if any progress bars are still active
2632 public boolean operationInProgress()
2634 if (progressBars != null && progressBars.size() > 0)
2642 * This will return the first AlignFrame holding the given viewport instance.
2643 * It will break if there are more than one AlignFrames viewing a particular
2647 * @return alignFrame for viewport
2649 public static AlignFrame getAlignFrameFor(AlignViewportI viewport)
2651 if (getDesktopPane() != null)
2653 AlignmentPanel[] aps = getAlignmentPanels(
2654 viewport.getSequenceSetId());
2655 for (int panel = 0; aps != null && panel < aps.length; panel++)
2657 if (aps[panel] != null && aps[panel].av == viewport)
2659 return aps[panel].alignFrame;
2666 public VamsasApplication getVamsasApplication()
2668 // TODO: JAL-3311 remove remaining code from Jalview relating to VAMSAS
2674 * flag set if jalview GUI is being operated programmatically
2676 private boolean inBatchMode = false;
2679 * check if jalview GUI is being operated programmatically
2681 * @return inBatchMode
2683 public boolean isInBatchMode()
2689 * set flag if jalview GUI is being operated programmatically
2691 * @param inBatchMode
2693 public void setInBatchMode(boolean inBatchMode)
2695 this.inBatchMode = inBatchMode;
2699 * start service discovery and wait till it is done
2701 public void startServiceDiscovery()
2703 startServiceDiscovery(false);
2707 * start service discovery threads - blocking or non-blocking
2711 public void startServiceDiscovery(boolean blocking)
2713 jalview.bin.Console.debug("Starting service discovery");
2715 var tasks = new ArrayList<Future<?>>();
2716 // JAL-940 - JALVIEW 1 services are now being EOLed as of JABA 2.1 release
2718 System.out.println("loading services");
2722 // todo: changesupport handlers need to be transferred
2723 if (discoverer == null)
2725 discoverer = jalview.ws.jws1.Discoverer.getInstance();
2726 // register PCS handler for desktop.
2727 discoverer.addPropertyChangeListener(changeSupport);
2729 // JAL-940 - disabled JWS1 service configuration - always start discoverer
2730 // until we phase out completely
2731 var f = new FutureTask<Void>(discoverer, null);
2732 new Thread(f).start();
2736 if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
2738 tasks.add(jalview.ws.jws2.Jws2Discoverer.getInstance().startDiscoverer());
2740 if (Cache.getDefault("SHOW_SLIVKA_SERVICES", true))
2742 tasks.add(jalview.ws2.client.slivka.SlivkaWSDiscoverer
2743 .getInstance().startDiscoverer());
2745 if (Cache.getDefault("SHOW_EBI_SERVICES", true))
2747 tasks.add(jalview.ws2.client.ebi.JobDispatcherWSDiscoverer
2748 .getInstance().startDiscoverer());
2752 for (Future<?> task : tasks) {
2755 // block until all discovery tasks are done
2757 } catch (Exception e)
2759 e.printStackTrace();
2766 * called to check if the service discovery process completed successfully.
2770 protected void JalviewServicesChanged(PropertyChangeEvent evt)
2772 if (evt.getNewValue() == null || evt.getNewValue() instanceof Vector)
2774 final WSDiscovererI discoverer = jalview.ws.jws2.Jws2Discoverer
2776 final String ermsg = discoverer.getErrorMessages();
2777 // CONFLICT:ALT:? final String ermsg = jalview.ws.jws2.Jws2Discoverer.getInstance()
2780 if (Cache.getDefault("SHOW_WSDISCOVERY_ERRORS", true))
2782 if (serviceChangedDialog == null)
2784 // only run if we aren't already displaying one of these.
2785 addDialogThread(serviceChangedDialog = new Runnable()
2792 * JalviewDialog jd =new JalviewDialog() {
2794 * @Override protected void cancelPressed() { // TODO
2795 * Auto-generated method stub
2797 * }@Override protected void okPressed() { // TODO
2798 * Auto-generated method stub
2800 * }@Override protected void raiseClosed() { // TODO
2801 * Auto-generated method stub
2803 * } }; jd.initDialogFrame(new
2804 * JLabel("<html><table width=\"450\"><tr><td>" + ermsg +
2805 * "<br/>It may be that you have invalid JABA URLs in your web service preferences,"
2806 * + " or mis-configured HTTP proxy settings.<br/>" +
2807 * "Check the <em>Connections</em> and <em>Web services</em> tab of the"
2809 * " Tools->Preferences dialog box to change them.</td></tr></table></html>"
2810 * ), true, true, "Web Service Configuration Problem", 450,
2813 * jd.waitForInput();
2815 JvOptionPane.showConfirmDialog(desktopPane,
2816 new JLabel("<html><table width=\"450\"><tr><td>"
2817 + ermsg + "</td></tr></table>"
2818 + "<p>It may be that you have invalid JABA URLs<br/>in your web service preferences,"
2819 + "<br>or as a command-line argument, or mis-configured HTTP proxy settings.</p>"
2820 + "<p>Check the <em>Connections</em> and <em>Web services</em> tab<br/>of the"
2821 + " Tools->Preferences dialog box to change them.</p></html>"),
2822 "Web Service Configuration Problem",
2823 JvOptionPane.DEFAULT_OPTION,
2824 JvOptionPane.ERROR_MESSAGE);
2825 serviceChangedDialog = null;
2833 jalview.bin.Console.error(
2834 "Errors reported by JABA discovery service. Check web services preferences.\n"
2841 private Runnable serviceChangedDialog = null;
2844 * start a thread to open a URL in the configured browser. Pops up a warning
2845 * dialog to the user if there is an exception when calling out to the browser
2850 public static void showUrl(final String url)
2852 showUrl(url, getInstance());
2856 * Like showUrl but allows progress handler to be specified
2860 * (null) or object implementing IProgressIndicator
2862 public static void showUrl(final String url,
2863 final IProgressIndicator progress)
2865 new Thread(new Runnable()
2872 if (progress != null)
2874 progress.setProgressBar(MessageManager
2875 .formatMessage("status.opening_params", new Object[]
2876 { url }), this.hashCode());
2878 jalview.util.BrowserLauncher.openURL(url);
2879 } catch (Exception ex)
2881 JvOptionPane.showInternalMessageDialog(getDesktopPane(),
2883 .getString("label.web_browser_not_found_unix"),
2884 MessageManager.getString("label.web_browser_not_found"),
2885 JvOptionPane.WARNING_MESSAGE);
2887 ex.printStackTrace();
2889 if (progress != null)
2891 progress.setProgressBar(null, this.hashCode());
2897 public static WsParamSetManager wsparamManager = null;
2899 public static ParamManager getUserParameterStore()
2901 if (wsparamManager == null)
2903 wsparamManager = new WsParamSetManager();
2905 return wsparamManager;
2909 * static hyperlink handler proxy method for use by Jalview's internal windows
2913 public static void hyperlinkUpdate(HyperlinkEvent e)
2915 if (e.getEventType() == EventType.ACTIVATED)
2920 url = e.getURL().toString();
2922 } catch (Exception x)
2926 // TODO does error send to stderr if no log exists ?
2927 jalview.bin.Console.error("Couldn't handle string " + url + " as a URL.");
2929 // ignore any exceptions due to dud links.
2936 * single thread that handles display of dialogs to user.
2938 ExecutorService dialogExecutor = Executors.newSingleThreadExecutor();
2941 * flag indicating if dialogExecutor should try to acquire a permit
2943 private volatile boolean dialogPause = true;
2948 private java.util.concurrent.Semaphore block = new Semaphore(0);
2950 private static groovy.ui.Console groovyConsole;
2953 * add another dialog thread to the queue
2957 public void addDialogThread(final Runnable prompter)
2959 dialogExecutor.submit(new Runnable()
2969 } catch (InterruptedException x)
2973 if (Jalview.isHeadlessMode())
2979 SwingUtilities.invokeAndWait(prompter);
2980 } catch (Exception q)
2982 jalview.bin.Console.warn("Unexpected Exception in dialog thread.", q);
2988 public void startDialogQueue()
2990 // set the flag so we don't pause waiting for another permit and semaphore
2991 // the current task to begin
2992 dialogPause = false;
2997 * Outputs an image of the desktop to file in EPS format, after prompting the
2998 * user for choice of Text or Lineart character rendering (unless a preference
2999 * has been set). The file name is generated as
3002 * Jalview_snapshot_nnnnn.eps where nnnnn is the current timestamp in milliseconds
3006 protected void snapShotWindow_actionPerformed(ActionEvent e)
3008 // currently the menu option to do this is not shown
3011 int width = getWidth();
3012 int height = getHeight();
3014 "Jalview_snapshot_" + System.currentTimeMillis() + ".eps");
3015 ImageWriterI writer = new ImageWriterI()
3018 public void exportImage(Graphics g) throws Exception
3021 jalview.bin.Console.info("Successfully written snapshot to file "
3022 + of.getAbsolutePath());
3025 String title = "View of desktop";
3026 ImageExporter exporter = new ImageExporter(writer, null, TYPE.EPS,
3028 exporter.doExport(of, this, width, height, title);
3032 * Explode the views in the given SplitFrame into separate SplitFrame windows.
3033 * This respects (remembers) any previous 'exploded geometry' i.e. the size
3034 * and location last time the view was expanded (if any). However it does not
3035 * remember the split pane divider location - this is set to match the
3036 * 'exploding' frame.
3040 public void explodeViews(SplitFrame sf)
3042 AlignFrame oldTopFrame = (AlignFrame) sf.getTopFrame();
3043 AlignFrame oldBottomFrame = (AlignFrame) sf.getBottomFrame();
3044 List<? extends AlignmentViewPanel> topPanels = oldTopFrame
3046 List<? extends AlignmentViewPanel> bottomPanels = oldBottomFrame
3048 int viewCount = topPanels.size();
3055 * Processing in reverse order works, forwards order leaves the first panels
3056 * not visible. I don't know why!
3058 for (int i = viewCount - 1; i >= 0; i--)
3061 * Make new top and bottom frames. These take over the respective
3062 * AlignmentPanel objects, including their AlignmentViewports, so the
3063 * cdna/protein relationships between the viewports is carried over to the
3066 * explodedGeometry holds the (x, y) position of the previously exploded
3067 * SplitFrame, and the (width, height) of the AlignFrame component
3069 AlignmentPanel topPanel = (AlignmentPanel) topPanels.get(i);
3070 AlignFrame newTopFrame = new AlignFrame(topPanel);
3071 newTopFrame.setSize(oldTopFrame.getSize());
3072 newTopFrame.setVisible(true);
3073 Rectangle geometry = ((AlignViewport) topPanel.getAlignViewport())
3074 .getExplodedGeometry();
3075 if (geometry != null)
3077 newTopFrame.setSize(geometry.getSize());
3080 AlignmentPanel bottomPanel = (AlignmentPanel) bottomPanels.get(i);
3081 AlignFrame newBottomFrame = new AlignFrame(bottomPanel);
3082 newBottomFrame.setSize(oldBottomFrame.getSize());
3083 newBottomFrame.setVisible(true);
3084 geometry = ((AlignViewport) bottomPanel.getAlignViewport())
3085 .getExplodedGeometry();
3086 if (geometry != null)
3088 newBottomFrame.setSize(geometry.getSize());
3091 topPanel.av.setGatherViewsHere(false);
3092 bottomPanel.av.setGatherViewsHere(false);
3093 JInternalFrame splitFrame = new SplitFrame(newTopFrame,
3095 if (geometry != null)
3097 splitFrame.setLocation(geometry.getLocation());
3099 addInternalFrame(splitFrame, sf.getTitle(), -1, -1);
3103 * Clear references to the panels (now relocated in the new SplitFrames)
3104 * before closing the old SplitFrame.
3107 bottomPanels.clear();
3112 * Gather expanded split frames, sharing the same pairs of sequence set ids,
3113 * back into the given SplitFrame as additional views. Note that the gathered
3114 * frames may themselves have multiple views.
3118 public void gatherViews(GSplitFrame source)
3121 * special handling of explodedGeometry for a view within a SplitFrame: - it
3122 * holds the (x, y) position of the enclosing SplitFrame, and the (width,
3123 * height) of the AlignFrame component
3125 AlignFrame myTopFrame = (AlignFrame) source.getTopFrame();
3126 AlignFrame myBottomFrame = (AlignFrame) source.getBottomFrame();
3127 myTopFrame.viewport.setExplodedGeometry(new Rectangle(source.getX(),
3128 source.getY(), myTopFrame.getWidth(), myTopFrame.getHeight()));
3129 myBottomFrame.viewport
3130 .setExplodedGeometry(new Rectangle(source.getX(), source.getY(),
3131 myBottomFrame.getWidth(), myBottomFrame.getHeight()));
3132 myTopFrame.viewport.setGatherViewsHere(true);
3133 myBottomFrame.viewport.setGatherViewsHere(true);
3134 String topViewId = myTopFrame.viewport.getSequenceSetId();
3135 String bottomViewId = myBottomFrame.viewport.getSequenceSetId();
3137 JInternalFrame[] frames = desktopPane.getAllFrames();
3138 for (JInternalFrame frame : frames)
3140 if (frame instanceof SplitFrame && frame != source)
3142 SplitFrame sf = (SplitFrame) frame;
3143 AlignFrame topFrame = (AlignFrame) sf.getTopFrame();
3144 AlignFrame bottomFrame = (AlignFrame) sf.getBottomFrame();
3145 boolean gatherThis = false;
3146 for (int a = 0; a < topFrame.alignPanels.size(); a++)
3148 AlignmentPanel topPanel = topFrame.alignPanels.get(a);
3149 AlignmentPanel bottomPanel = bottomFrame.alignPanels.get(a);
3150 if (topViewId.equals(topPanel.av.getSequenceSetId())
3151 && bottomViewId.equals(bottomPanel.av.getSequenceSetId()))
3154 topPanel.av.setGatherViewsHere(false);
3155 bottomPanel.av.setGatherViewsHere(false);
3156 topPanel.av.setExplodedGeometry(
3157 new Rectangle(sf.getLocation(), topFrame.getSize()));
3158 bottomPanel.av.setExplodedGeometry(
3159 new Rectangle(sf.getLocation(), bottomFrame.getSize()));
3160 myTopFrame.addAlignmentPanel(topPanel, false);
3161 myBottomFrame.addAlignmentPanel(bottomPanel, false);
3167 topFrame.getAlignPanels().clear();
3168 bottomFrame.getAlignPanels().clear();
3175 * The dust settles...give focus to the tab we did this from.
3177 myTopFrame.setDisplayedView(myTopFrame.alignPanel);
3180 public static groovy.ui.Console getGroovyConsole()
3182 return groovyConsole;
3186 * handles the payload of a drag and drop event.
3188 * TODO refactor to desktop utilities class
3191 * - Data source strings extracted from the drop event
3193 * - protocol for each data source extracted from the drop event
3197 * - the payload from the drop event
3200 @SuppressWarnings("unchecked")
3201 public static void transferFromDropTarget(List<Object> files,
3202 List<DataSourceType> protocols, DropTargetDropEvent evt,
3203 Transferable t) throws Exception
3206 // BH 2018 changed List<String> to List<Object> to allow for File from
3209 // DataFlavor[] flavors = t.getTransferDataFlavors();
3210 // for (int i = 0; i < flavors.length; i++) {
3211 // if (flavors[i].isFlavorJavaFileListType()) {
3212 // evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
3213 // List<File> list = (List<File>) t.getTransferData(flavors[i]);
3214 // for (int j = 0; j < list.size(); j++) {
3215 // File file = (File) list.get(j);
3216 // byte[] data = getDroppedFileBytes(file);
3217 // fileName.setText(file.getName() + " - " + data.length + " " +
3218 // evt.getLocation());
3219 // JTextArea target = (JTextArea) ((DropTarget)
3220 // evt.getSource()).getComponent();
3221 // target.setText(new String(data));
3223 // dtde.dropComplete(true);
3228 DataFlavor uriListFlavor = new DataFlavor(
3229 "text/uri-list;class=java.lang.String"), urlFlavour = null;
3232 urlFlavour = new DataFlavor(
3233 "application/x-java-url; class=java.net.URL");
3234 } catch (ClassNotFoundException cfe)
3236 jalview.bin.Console.debug("Couldn't instantiate the URL dataflavor.",
3240 if (urlFlavour != null && t.isDataFlavorSupported(urlFlavour))
3245 java.net.URL url = (URL) t.getTransferData(urlFlavour);
3246 // nb: java 8 osx bug https://bugs.openjdk.java.net/browse/JDK-8156099
3247 // means url may be null.
3250 protocols.add(DataSourceType.URL);
3251 files.add(url.toString());
3252 jalview.bin.Console.debug("Drop handled as URL dataflavor "
3253 + files.get(files.size() - 1));
3258 if (Platform.isAMacAndNotJS())
3261 "Please ignore plist error - occurs due to problem with java 8 on OSX");
3264 } catch (Throwable ex)
3266 jalview.bin.Console.debug("URL drop handler failed.", ex);
3269 if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
3271 // Works on Windows and MacOSX
3272 jalview.bin.Console.debug("Drop handled as javaFileListFlavor");
3273 for (File file : (List<File>) t
3274 .getTransferData(DataFlavor.javaFileListFlavor))
3277 protocols.add(DataSourceType.FILE);
3282 // Unix like behaviour
3283 boolean added = false;
3285 if (t.isDataFlavorSupported(uriListFlavor))
3287 jalview.bin.Console.debug("Drop handled as uriListFlavor");
3288 // This is used by Unix drag system
3289 data = (String) t.getTransferData(uriListFlavor);
3293 // fallback to text: workaround - on OSX where there's a JVM bug
3295 .debug("standard URIListFlavor failed. Trying text");
3296 // try text fallback
3297 DataFlavor textDf = new DataFlavor(
3298 "text/plain;class=java.lang.String");
3299 if (t.isDataFlavorSupported(textDf))
3301 data = (String) t.getTransferData(textDf);
3304 jalview.bin.Console.debug("Plain text drop content returned "
3305 + (data == null ? "Null - failed" : data));
3310 while (protocols.size() < files.size())
3312 jalview.bin.Console.debug("Adding missing FILE protocol for "
3313 + files.get(protocols.size()));
3314 protocols.add(DataSourceType.FILE);
3316 for (java.util.StringTokenizer st = new java.util.StringTokenizer(
3317 data, "\r\n"); st.hasMoreTokens();)
3320 String s = st.nextToken();
3321 if (s.startsWith("#"))
3323 // the line is a comment (as per the RFC 2483)
3326 java.net.URI uri = new java.net.URI(s);
3327 if (uri.getScheme().toLowerCase(Locale.ROOT).startsWith("http"))
3329 protocols.add(DataSourceType.URL);
3330 files.add(uri.toString());
3334 // otherwise preserve old behaviour: catch all for file objects
3335 java.io.File file = new java.io.File(uri);
3336 protocols.add(DataSourceType.FILE);
3337 files.add(file.toString());
3342 if (jalview.bin.Console.isDebugEnabled())
3344 if (data == null || !added)
3347 if (t.getTransferDataFlavors() != null
3348 && t.getTransferDataFlavors().length > 0)
3350 jalview.bin.Console.debug(
3351 "Couldn't resolve drop data. Here are the supported flavors:");
3352 for (DataFlavor fl : t.getTransferDataFlavors())
3354 jalview.bin.Console.debug(
3355 "Supported transfer dataflavor: " + fl.toString());
3356 Object df = t.getTransferData(fl);
3359 jalview.bin.Console.debug("Retrieves: " + df);
3363 jalview.bin.Console.debug("Retrieved nothing");
3369 jalview.bin.Console.debug("Couldn't resolve dataflavor for drop: "
3375 if (Platform.isWindowsAndNotJS())
3377 jalview.bin.Console.debug("Scanning dropped content for Windows Link Files");
3379 // resolve any .lnk files in the file drop
3380 for (int f = 0; f < files.size(); f++)
3382 String source = files.get(f).toString().toLowerCase(Locale.ROOT);
3383 if (protocols.get(f).equals(DataSourceType.FILE)
3384 && (source.endsWith(".lnk") || source.endsWith(".url")
3385 || source.endsWith(".site")))
3389 Object obj = files.get(f);
3390 File lf = (obj instanceof File ? (File) obj
3391 : new File((String) obj));
3392 // process link file to get a URL
3393 jalview.bin.Console.debug("Found potential link file: " + lf);
3394 WindowsShortcut wscfile = new WindowsShortcut(lf);
3395 String fullname = wscfile.getRealFilename();
3396 protocols.set(f, FormatAdapter.checkProtocol(fullname));
3397 files.set(f, fullname);
3398 jalview.bin.Console.debug("Parsed real filename " + fullname
3399 + " to extract protocol: " + protocols.get(f));
3400 } catch (Exception ex)
3402 jalview.bin.Console.error(
3403 "Couldn't parse " + files.get(f) + " as a link file.",
3412 * Sets the Preferences property for experimental features to True or False
3413 * depending on the state of the controlling menu item
3416 protected void showExperimental_actionPerformed(boolean selected)
3418 Cache.setProperty(EXPERIMENTAL_FEATURES, Boolean.toString(selected));
3422 * Answers a (possibly empty) list of any structure viewer frames (currently
3423 * for either Jmol or Chimera) which are currently open. This may optionally
3424 * be restricted to viewers of a specified class, or viewers linked to a
3425 * specified alignment panel.
3428 * if not null, only return viewers linked to this panel
3429 * @param structureViewerClass
3430 * if not null, only return viewers of this class
3433 public List<StructureViewerBase> getStructureViewers(
3434 AlignmentPanel apanel,
3435 Class<? extends StructureViewerBase> structureViewerClass)
3437 List<StructureViewerBase> result = new ArrayList<>();
3438 JInternalFrame[] frames = getAllFrames();
3440 for (JInternalFrame frame : frames)
3442 if (frame instanceof StructureViewerBase)
3444 if (structureViewerClass == null
3445 || structureViewerClass.isInstance(frame))
3448 || ((StructureViewerBase) frame).isLinkedWith(apanel))
3450 result.add((StructureViewerBase) frame);
3458 public static final String debugScaleMessage = "Desktop graphics transform scale=";
3460 private static boolean debugScaleMessageDone = false;
3462 public static void debugScaleMessage(Graphics g) {
3463 if (debugScaleMessageDone) {
3466 // output used by tests to check HiDPI scaling settings in action
3468 Graphics2D gg = (Graphics2D) g;
3470 AffineTransform t = gg.getTransform();
3471 double scaleX = t.getScaleX();
3472 double scaleY = t.getScaleY();
3473 jalview.bin.Console.debug(debugScaleMessage + scaleX + " (X)");
3474 jalview.bin.Console.debug(debugScaleMessage + scaleY + " (Y)");
3475 debugScaleMessageDone = true;
3479 jalview.bin.Console.debug("Desktop graphics null");
3481 } catch (Exception e)
3483 jalview.bin.Console.debug(Cache.getStackTraceString(e));