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.BorderLayout;
24 import java.awt.Color;
25 import java.awt.Dimension;
26 import java.awt.FontMetrics;
27 import java.awt.Graphics;
28 import java.awt.Graphics2D;
29 import java.awt.GridLayout;
30 import java.awt.Point;
31 import java.awt.Rectangle;
32 import java.awt.Toolkit;
33 import java.awt.Window;
34 import java.awt.datatransfer.Clipboard;
35 import java.awt.datatransfer.ClipboardOwner;
36 import java.awt.datatransfer.DataFlavor;
37 import java.awt.datatransfer.Transferable;
38 import java.awt.dnd.DnDConstants;
39 import java.awt.dnd.DropTargetDragEvent;
40 import java.awt.dnd.DropTargetDropEvent;
41 import java.awt.dnd.DropTargetEvent;
42 import java.awt.dnd.DropTargetListener;
43 import java.awt.event.ActionEvent;
44 import java.awt.event.ActionListener;
45 import java.awt.event.InputEvent;
46 import java.awt.event.KeyEvent;
47 import java.awt.event.MouseAdapter;
48 import java.awt.event.MouseEvent;
49 import java.awt.event.WindowAdapter;
50 import java.awt.event.WindowEvent;
51 import java.awt.geom.AffineTransform;
52 import java.beans.PropertyChangeEvent;
53 import java.beans.PropertyChangeListener;
55 import java.io.FileWriter;
56 import java.io.IOException;
57 import java.lang.reflect.Field;
59 import java.util.ArrayList;
60 import java.util.Arrays;
61 import java.util.HashMap;
62 import java.util.Hashtable;
63 import java.util.List;
64 import java.util.ListIterator;
65 import java.util.Locale;
66 import java.util.Vector;
67 import java.util.concurrent.ExecutorService;
68 import java.util.concurrent.Executors;
69 import java.util.concurrent.Semaphore;
71 import javax.swing.AbstractAction;
72 import javax.swing.Action;
73 import javax.swing.ActionMap;
74 import javax.swing.Box;
75 import javax.swing.BoxLayout;
76 import javax.swing.DefaultDesktopManager;
77 import javax.swing.DesktopManager;
78 import javax.swing.InputMap;
79 import javax.swing.JButton;
80 import javax.swing.JCheckBox;
81 import javax.swing.JComboBox;
82 import javax.swing.JComponent;
83 import javax.swing.JDesktopPane;
84 import javax.swing.JInternalFrame;
85 import javax.swing.JLabel;
86 import javax.swing.JMenuItem;
87 import javax.swing.JPanel;
88 import javax.swing.JPopupMenu;
89 import javax.swing.JProgressBar;
90 import javax.swing.JTextField;
91 import javax.swing.KeyStroke;
92 import javax.swing.SwingUtilities;
93 import javax.swing.event.HyperlinkEvent;
94 import javax.swing.event.HyperlinkEvent.EventType;
95 import javax.swing.event.InternalFrameAdapter;
96 import javax.swing.event.InternalFrameEvent;
98 import org.stackoverflowusers.file.WindowsShortcut;
100 import jalview.api.AlignViewportI;
101 import jalview.api.AlignmentViewPanel;
102 import jalview.bin.Cache;
103 import jalview.bin.Jalview;
104 import jalview.gui.ImageExporter.ImageWriterI;
105 import jalview.io.BackupFiles;
106 import jalview.io.DataSourceType;
107 import jalview.io.FileFormat;
108 import jalview.io.FileFormatException;
109 import jalview.io.FileFormatI;
110 import jalview.io.FileFormats;
111 import jalview.io.FileLoader;
112 import jalview.io.FormatAdapter;
113 import jalview.io.IdentifyFile;
114 import jalview.io.JalviewFileChooser;
115 import jalview.io.JalviewFileView;
116 import jalview.jbgui.GSplitFrame;
117 import jalview.jbgui.GStructureViewer;
118 import jalview.project.Jalview2XML;
119 import jalview.structure.StructureSelectionManager;
120 import jalview.urls.IdOrgSettings;
121 import jalview.util.BrowserLauncher;
122 import jalview.util.ChannelProperties;
123 import jalview.util.ImageMaker.TYPE;
124 import jalview.util.LaunchUtils;
125 import jalview.util.MessageManager;
126 import jalview.util.Platform;
127 import jalview.util.ShortcutKeyMaskExWrapper;
128 import jalview.util.UrlConstants;
129 import jalview.viewmodel.AlignmentViewport;
130 import jalview.ws.params.ParamManager;
131 import jalview.ws.utils.UrlDownloadClient;
138 * @version $Revision: 1.155 $
140 public class Desktop extends jalview.jbgui.GDesktop
141 implements DropTargetListener, ClipboardOwner, IProgressIndicator,
142 jalview.api.StructureSelectionManagerProvider
144 private static final String CITATION;
147 URL bg_logo_url = ChannelProperties.getImageURL(
148 "bg_logo." + String.valueOf(SplashScreen.logoSize));
149 URL uod_logo_url = ChannelProperties.getImageURL(
150 "uod_banner." + String.valueOf(SplashScreen.logoSize));
151 boolean logo = (bg_logo_url != null || uod_logo_url != null);
152 StringBuilder sb = new StringBuilder();
154 "<br><br>Jalview is free software released under GPLv3.<br><br>Development is managed by The Barton Group, University of Dundee, Scotland, UK.");
159 sb.append(bg_logo_url == null ? ""
160 : "<img alt=\"Barton Group logo\" src=\""
161 + bg_logo_url.toString() + "\">");
162 sb.append(uod_logo_url == null ? ""
163 : " <img alt=\"University of Dundee shield\" src=\""
164 + uod_logo_url.toString() + "\">");
166 "<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>");
167 sb.append("<br><br>If you use Jalview, please cite:"
168 + "<br>Waterhouse, A.M., Procter, J.B., Martin, D.M.A, Clamp, M. and Barton, G. J. (2009)"
169 + "<br>Jalview Version 2 - a multiple sequence alignment editor and analysis workbench"
170 + "<br>Bioinformatics <a href=\"https://doi.org/10.1093/bioinformatics/btp033\">doi: 10.1093/bioinformatics/btp033</a>");
171 CITATION = sb.toString();
174 private static final String DEFAULT_AUTHORS = "The Jalview Authors (See AUTHORS file for current list)";
176 private static int DEFAULT_MIN_WIDTH = 300;
178 private static int DEFAULT_MIN_HEIGHT = 250;
180 private static int ALIGN_FRAME_DEFAULT_MIN_WIDTH = 600;
182 private static int ALIGN_FRAME_DEFAULT_MIN_HEIGHT = 70;
184 private static final String EXPERIMENTAL_FEATURES = "EXPERIMENTAL_FEATURES";
186 public static final String CONFIRM_KEYBOARD_QUIT = "CONFIRM_KEYBOARD_QUIT";
188 public static HashMap<String, FileWriter> savingFiles = new HashMap<String, FileWriter>();
190 private static int DRAG_MODE = JDesktopPane.OUTLINE_DRAG_MODE;
192 public static void setLiveDragMode(boolean b)
194 DRAG_MODE = b ? JDesktopPane.LIVE_DRAG_MODE
195 : JDesktopPane.OUTLINE_DRAG_MODE;
198 private JalviewChangeSupport changeSupport = new JalviewChangeSupport();
200 public static boolean nosplash = false;
203 * news reader - null if it was never started.
205 private BlogReader jvnews = null;
207 private File projectFile;
211 * @see jalview.gui.JalviewChangeSupport#addJalviewPropertyChangeListener(java.beans.PropertyChangeListener)
213 public void addJalviewPropertyChangeListener(
214 PropertyChangeListener listener)
216 changeSupport.addJalviewPropertyChangeListener(listener);
220 * @param propertyName
222 * @see jalview.gui.JalviewChangeSupport#addJalviewPropertyChangeListener(java.lang.String,
223 * java.beans.PropertyChangeListener)
225 public void addJalviewPropertyChangeListener(String propertyName,
226 PropertyChangeListener listener)
228 changeSupport.addJalviewPropertyChangeListener(propertyName, listener);
232 * @param propertyName
234 * @see jalview.gui.JalviewChangeSupport#removeJalviewPropertyChangeListener(java.lang.String,
235 * java.beans.PropertyChangeListener)
237 public void removeJalviewPropertyChangeListener(String propertyName,
238 PropertyChangeListener listener)
240 changeSupport.removeJalviewPropertyChangeListener(propertyName,
244 /** Singleton Desktop instance */
245 public static Desktop instance;
247 public static MyDesktopPane desktop;
249 public static MyDesktopPane getDesktop()
251 // BH 2018 could use currentThread() here as a reference to a
252 // Hashtable<Thread, MyDesktopPane> in JavaScript
256 static int openFrameCount = 0;
258 static final int xOffset = 30;
260 static final int yOffset = 30;
262 public static jalview.ws.jws1.Discoverer discoverer;
264 public static Object[] jalviewClipboard;
266 public static boolean internalCopy = false;
268 static int fileLoadingCount = 0;
270 class MyDesktopManager implements DesktopManager
273 private DesktopManager delegate;
275 public MyDesktopManager(DesktopManager delegate)
277 this.delegate = delegate;
281 public void activateFrame(JInternalFrame f)
285 delegate.activateFrame(f);
286 } catch (NullPointerException npe)
288 Point p = getMousePosition();
289 instance.showPasteMenu(p.x, p.y);
294 public void beginDraggingFrame(JComponent f)
296 delegate.beginDraggingFrame(f);
300 public void beginResizingFrame(JComponent f, int direction)
302 delegate.beginResizingFrame(f, direction);
306 public void closeFrame(JInternalFrame f)
308 delegate.closeFrame(f);
312 public void deactivateFrame(JInternalFrame f)
314 delegate.deactivateFrame(f);
318 public void deiconifyFrame(JInternalFrame f)
320 delegate.deiconifyFrame(f);
324 public void dragFrame(JComponent f, int newX, int newY)
330 delegate.dragFrame(f, newX, newY);
334 public void endDraggingFrame(JComponent f)
336 delegate.endDraggingFrame(f);
341 public void endResizingFrame(JComponent f)
343 delegate.endResizingFrame(f);
348 public void iconifyFrame(JInternalFrame f)
350 delegate.iconifyFrame(f);
354 public void maximizeFrame(JInternalFrame f)
356 delegate.maximizeFrame(f);
360 public void minimizeFrame(JInternalFrame f)
362 delegate.minimizeFrame(f);
366 public void openFrame(JInternalFrame f)
368 delegate.openFrame(f);
372 public void resizeFrame(JComponent f, int newX, int newY, int newWidth,
379 delegate.resizeFrame(f, newX, newY, newWidth, newHeight);
383 public void setBoundsForFrame(JComponent f, int newX, int newY,
384 int newWidth, int newHeight)
386 delegate.setBoundsForFrame(f, newX, newY, newWidth, newHeight);
389 // All other methods, simply delegate
394 * Creates a new Desktop object.
400 * A note to implementors. It is ESSENTIAL that any activities that might
401 * block are spawned off as threads rather than waited for during this
406 doConfigureStructurePrefs();
407 setTitle(ChannelProperties.getProperty("app_name") + " "
408 + Cache.getProperty("VERSION"));
411 * Set taskbar "grouped windows" name for linux desktops (works in GNOME and
412 * KDE). This uses sun.awt.X11.XToolkit.awtAppClassName which is not
413 * officially documented or guaranteed to exist, so we access it via
414 * reflection. There appear to be unfathomable criteria about what this
415 * string can contain, and it if doesn't meet those criteria then "java"
416 * (KDE) or "jalview-bin-Jalview" (GNOME) is used. "Jalview", "Jalview
417 * Develop" and "Jalview Test" seem okay, but "Jalview non-release" does
418 * not. The reflection access may generate a warning: WARNING: An illegal
419 * reflective access operation has occurred WARNING: Illegal reflective
420 * access by jalview.gui.Desktop () to field
421 * sun.awt.X11.XToolkit.awtAppClassName which I don't think can be avoided.
423 if (Platform.isLinux())
425 if (LaunchUtils.getJavaVersion() >= 11)
427 jalview.bin.Console.info(
428 "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.");
432 Toolkit xToolkit = Toolkit.getDefaultToolkit();
433 Field[] declaredFields = xToolkit.getClass().getDeclaredFields();
434 Field awtAppClassNameField = null;
436 if (Arrays.stream(declaredFields)
437 .anyMatch(f -> f.getName().equals("awtAppClassName")))
439 awtAppClassNameField = xToolkit.getClass()
440 .getDeclaredField("awtAppClassName");
443 String title = ChannelProperties.getProperty("app_name");
444 if (awtAppClassNameField != null)
446 awtAppClassNameField.setAccessible(true);
447 awtAppClassNameField.set(xToolkit, title);
451 jalview.bin.Console.debug("XToolkit: awtAppClassName not found");
453 } catch (Exception e)
455 jalview.bin.Console.debug("Error setting awtAppClassName");
456 jalview.bin.Console.trace(Cache.getStackTraceString(e));
460 setIconImages(ChannelProperties.getIconList());
462 addWindowListener(new WindowAdapter()
466 public void windowClosing(WindowEvent ev)
472 boolean selmemusage = Cache.getDefault("SHOW_MEMUSAGE", false);
474 boolean showjconsole = Cache.getDefault("SHOW_JAVA_CONSOLE", false);
475 desktop = new MyDesktopPane(selmemusage);
477 showMemusage.setSelected(selmemusage);
478 desktop.setBackground(Color.white);
480 getContentPane().setLayout(new BorderLayout());
481 // alternate config - have scrollbars - see notes in JAL-153
482 // JScrollPane sp = new JScrollPane();
483 // sp.getViewport().setView(desktop);
484 // getContentPane().add(sp, BorderLayout.CENTER);
486 // BH 2018 - just an experiment to try unclipped JInternalFrames.
489 getRootPane().putClientProperty("swingjs.overflow.hidden", "false");
492 getContentPane().add(desktop, BorderLayout.CENTER);
493 desktop.setDragMode(DRAG_MODE);
495 // This line prevents Windows Look&Feel resizing all new windows to maximum
496 // if previous window was maximised
497 desktop.setDesktopManager(new MyDesktopManager(
498 (Platform.isWindowsAndNotJS() ? new DefaultDesktopManager()
499 : Platform.isAMacAndNotJS()
500 ? new AquaInternalFrameManager(
501 desktop.getDesktopManager())
502 : desktop.getDesktopManager())));
504 Rectangle dims = getLastKnownDimensions("");
511 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
512 int xPos = Math.max(5, (screenSize.width - 900) / 2);
513 int yPos = Math.max(5, (screenSize.height - 650) / 2);
514 setBounds(xPos, yPos, 900, 650);
517 if (!Platform.isJS())
524 jconsole = new Console(this, showjconsole);
525 jconsole.setHeader(Cache.getVersionDetailsForConsole());
526 showConsole(showjconsole);
528 showNews.setVisible(false);
530 experimentalFeatures.setSelected(showExperimental());
532 getIdentifiersOrgData();
536 // Spawn a thread that shows the splashscreen
539 SwingUtilities.invokeLater(new Runnable()
544 new SplashScreen(true);
549 // Thread off a new instance of the file chooser - this reduces the time
551 // takes to open it later on.
552 new Thread(new Runnable()
557 jalview.bin.Console.debug("Filechooser init thread started.");
558 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
559 JalviewFileChooser.forRead(Cache.getProperty("LAST_DIRECTORY"),
561 jalview.bin.Console.debug("Filechooser init thread finished.");
564 // Add the service change listener
565 changeSupport.addJalviewPropertyChangeListener("services",
566 new PropertyChangeListener()
570 public void propertyChange(PropertyChangeEvent evt)
573 .debug("Firing service changed event for "
574 + evt.getNewValue());
575 JalviewServicesChanged(evt);
580 this.setDropTarget(new java.awt.dnd.DropTarget(desktop, this));
582 this.addWindowListener(new WindowAdapter()
585 public void windowClosing(WindowEvent evt)
592 this.addMouseListener(ma = new MouseAdapter()
595 public void mousePressed(MouseEvent evt)
597 if (evt.isPopupTrigger()) // Mac
599 showPasteMenu(evt.getX(), evt.getY());
604 public void mouseReleased(MouseEvent evt)
606 if (evt.isPopupTrigger()) // Windows
608 showPasteMenu(evt.getX(), evt.getY());
612 desktop.addMouseListener(ma);
616 * Answers true if user preferences to enable experimental features is True
621 public boolean showExperimental()
623 String experimental = Cache.getDefault(EXPERIMENTAL_FEATURES,
624 Boolean.FALSE.toString());
625 return Boolean.valueOf(experimental).booleanValue();
628 public void doConfigureStructurePrefs()
630 // configure services
631 StructureSelectionManager ssm = StructureSelectionManager
632 .getStructureSelectionManager(this);
633 if (Cache.getDefault(Preferences.ADD_SS_ANN, true))
635 ssm.setAddTempFacAnnot(
636 Cache.getDefault(Preferences.ADD_TEMPFACT_ANN, true));
637 ssm.setProcessSecondaryStructure(
638 Cache.getDefault(Preferences.STRUCT_FROM_PDB, true));
639 // JAL-3915 - RNAView is no longer an option so this has no effect
640 ssm.setSecStructServices(
641 Cache.getDefault(Preferences.USE_RNAVIEW, false));
645 ssm.setAddTempFacAnnot(false);
646 ssm.setProcessSecondaryStructure(false);
647 ssm.setSecStructServices(false);
651 public void checkForNews()
653 final Desktop me = this;
654 // Thread off the news reader, in case there are connection problems.
655 new Thread(new Runnable()
660 jalview.bin.Console.debug("Starting news thread.");
661 jvnews = new BlogReader(me);
662 showNews.setVisible(true);
663 jalview.bin.Console.debug("Completed news thread.");
668 public void getIdentifiersOrgData()
670 if (Cache.getProperty("NOIDENTIFIERSSERVICE") == null)
671 {// Thread off the identifiers fetcher
672 new Thread(new Runnable()
678 .debug("Downloading data from identifiers.org");
681 UrlDownloadClient.download(IdOrgSettings.getUrl(),
682 IdOrgSettings.getDownloadLocation());
683 } catch (IOException e)
686 .debug("Exception downloading identifiers.org data"
696 protected void showNews_actionPerformed(ActionEvent e)
698 showNews(showNews.isSelected());
701 void showNews(boolean visible)
703 jalview.bin.Console.debug((visible ? "Showing" : "Hiding") + " news.");
704 showNews.setSelected(visible);
705 if (visible && !jvnews.isVisible())
707 new Thread(new Runnable()
712 long now = System.currentTimeMillis();
713 Desktop.instance.setProgressBar(
714 MessageManager.getString("status.refreshing_news"), now);
715 jvnews.refreshNews();
716 Desktop.instance.setProgressBar(null, now);
724 * recover the last known dimensions for a jalview window
727 * - empty string is desktop, all other windows have unique prefix
728 * @return null or last known dimensions scaled to current geometry (if last
729 * window geom was known)
731 Rectangle getLastKnownDimensions(String windowName)
733 // TODO: lock aspect ratio for scaling desktop Bug #0058199
734 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
735 String x = Cache.getProperty(windowName + "SCREEN_X");
736 String y = Cache.getProperty(windowName + "SCREEN_Y");
737 String width = Cache.getProperty(windowName + "SCREEN_WIDTH");
738 String height = Cache.getProperty(windowName + "SCREEN_HEIGHT");
739 if ((x != null) && (y != null) && (width != null) && (height != null))
741 int ix = Integer.parseInt(x), iy = Integer.parseInt(y),
742 iw = Integer.parseInt(width), ih = Integer.parseInt(height);
743 if (Cache.getProperty("SCREENGEOMETRY_WIDTH") != null)
745 // attempt #1 - try to cope with change in screen geometry - this
746 // version doesn't preserve original jv aspect ratio.
747 // take ratio of current screen size vs original screen size.
748 double sw = ((1f * screenSize.width) / (1f * Integer
749 .parseInt(Cache.getProperty("SCREENGEOMETRY_WIDTH"))));
750 double sh = ((1f * screenSize.height) / (1f * Integer
751 .parseInt(Cache.getProperty("SCREENGEOMETRY_HEIGHT"))));
752 // rescale the bounds depending upon the current screen geometry.
753 ix = (int) (ix * sw);
754 iw = (int) (iw * sw);
755 iy = (int) (iy * sh);
756 ih = (int) (ih * sh);
757 while (ix >= screenSize.width)
759 jalview.bin.Console.debug(
760 "Window geometry location recall error: shifting horizontal to within screenbounds.");
761 ix -= screenSize.width;
763 while (iy >= screenSize.height)
765 jalview.bin.Console.debug(
766 "Window geometry location recall error: shifting vertical to within screenbounds.");
767 iy -= screenSize.height;
769 jalview.bin.Console.debug(
770 "Got last known dimensions for " + windowName + ": x:" + ix
771 + " y:" + iy + " width:" + iw + " height:" + ih);
773 // return dimensions for new instance
774 return new Rectangle(ix, iy, iw, ih);
779 void showPasteMenu(int x, int y)
781 JPopupMenu popup = new JPopupMenu();
782 JMenuItem item = new JMenuItem(
783 MessageManager.getString("label.paste_new_window"));
784 item.addActionListener(new ActionListener()
787 public void actionPerformed(ActionEvent evt)
794 popup.show(this, x, y);
801 Clipboard c = Toolkit.getDefaultToolkit().getSystemClipboard();
802 Transferable contents = c.getContents(this);
804 if (contents != null)
806 String file = (String) contents
807 .getTransferData(DataFlavor.stringFlavor);
809 FileFormatI format = new IdentifyFile().identify(file,
810 DataSourceType.PASTE);
812 new FileLoader().LoadFile(file, DataSourceType.PASTE, format);
815 } catch (Exception ex)
818 "Unable to paste alignment from system clipboard:\n" + ex);
823 * Adds and opens the given frame to the desktop
834 public static synchronized void addInternalFrame(
835 final JInternalFrame frame, String title, int w, int h)
837 addInternalFrame(frame, title, true, w, h, true, false);
841 * Add an internal frame to the Jalview desktop
848 * When true, display frame immediately, otherwise, caller must call
849 * setVisible themselves.
855 public static synchronized void addInternalFrame(
856 final JInternalFrame frame, String title, boolean makeVisible,
859 addInternalFrame(frame, title, makeVisible, w, h, true, false);
863 * Add an internal frame to the Jalview desktop and make it visible
876 public static synchronized void addInternalFrame(
877 final JInternalFrame frame, String title, int w, int h,
880 addInternalFrame(frame, title, true, w, h, resizable, false);
884 * Add an internal frame to the Jalview desktop
891 * When true, display frame immediately, otherwise, caller must call
892 * setVisible themselves.
899 * @param ignoreMinSize
900 * Do not set the default minimum size for frame
902 public static synchronized void addInternalFrame(
903 final JInternalFrame frame, String title, boolean makeVisible,
904 int w, int h, boolean resizable, boolean ignoreMinSize)
907 // TODO: allow callers to determine X and Y position of frame (eg. via
909 // TODO: consider fixing method to update entries in the window submenu with
910 // the current window title
912 frame.setTitle(title);
913 if (frame.getWidth() < 1 || frame.getHeight() < 1)
917 // THIS IS A PUBLIC STATIC METHOD, SO IT MAY BE CALLED EVEN IN
918 // A HEADLESS STATE WHEN NO DESKTOP EXISTS. MUST RETURN
919 // IF JALVIEW IS RUNNING HEADLESS
920 // ///////////////////////////////////////////////
921 if (instance == null || (System.getProperty("java.awt.headless") != null
922 && System.getProperty("java.awt.headless").equals("true")))
931 frame.setMinimumSize(
932 new Dimension(DEFAULT_MIN_WIDTH, DEFAULT_MIN_HEIGHT));
934 // Set default dimension for Alignment Frame window.
935 // The Alignment Frame window could be added from a number of places,
937 // I did this here in order not to miss out on any Alignment frame.
938 if (frame instanceof AlignFrame)
940 frame.setMinimumSize(new Dimension(ALIGN_FRAME_DEFAULT_MIN_WIDTH,
941 ALIGN_FRAME_DEFAULT_MIN_HEIGHT));
945 frame.setVisible(makeVisible);
946 frame.setClosable(true);
947 frame.setResizable(resizable);
948 frame.setMaximizable(resizable);
949 frame.setIconifiable(resizable);
950 frame.setOpaque(Platform.isJS());
952 if (frame.getX() < 1 && frame.getY() < 1)
954 frame.setLocation(xOffset * openFrameCount,
955 yOffset * ((openFrameCount - 1) % 10) + yOffset);
959 * add an entry for the new frame in the Window menu (and remove it when the
962 final JMenuItem menuItem = new JMenuItem(title);
963 frame.addInternalFrameListener(new InternalFrameAdapter()
966 public void internalFrameActivated(InternalFrameEvent evt)
968 JInternalFrame itf = desktop.getSelectedFrame();
971 if (itf instanceof AlignFrame)
973 Jalview.setCurrentAlignFrame((AlignFrame) itf);
980 public void internalFrameClosed(InternalFrameEvent evt)
982 PaintRefresher.RemoveComponent(frame);
985 * defensive check to prevent frames being added half off the window
987 if (openFrameCount > 0)
993 * ensure no reference to alignFrame retained by menu item listener
995 if (menuItem.getActionListeners().length > 0)
997 menuItem.removeActionListener(menuItem.getActionListeners()[0]);
999 windowMenu.remove(menuItem);
1003 menuItem.addActionListener(new ActionListener()
1006 public void actionPerformed(ActionEvent e)
1010 frame.setSelected(true);
1011 frame.setIcon(false);
1012 } catch (java.beans.PropertyVetoException ex)
1019 setKeyBindings(frame);
1023 windowMenu.add(menuItem);
1028 frame.setSelected(true);
1029 frame.requestFocus();
1030 } catch (java.beans.PropertyVetoException ve)
1032 } catch (java.lang.ClassCastException cex)
1034 jalview.bin.Console.warn(
1035 "Squashed a possible GUI implementation error. If you can recreate this, please look at https://issues.jalview.org/browse/JAL-869",
1041 * Add key bindings to a JInternalFrame so that Ctrl-W and Cmd-W will close
1046 private static void setKeyBindings(JInternalFrame frame)
1048 @SuppressWarnings("serial")
1049 final Action closeAction = new AbstractAction()
1052 public void actionPerformed(ActionEvent e)
1059 * set up key bindings for Ctrl-W and Cmd-W, with the same (Close) action
1061 KeyStroke ctrlWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
1062 InputEvent.CTRL_DOWN_MASK);
1063 KeyStroke cmdWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
1064 ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx());
1066 InputMap inputMap = frame
1067 .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
1068 String ctrlW = ctrlWKey.toString();
1069 inputMap.put(ctrlWKey, ctrlW);
1070 inputMap.put(cmdWKey, ctrlW);
1072 ActionMap actionMap = frame.getActionMap();
1073 actionMap.put(ctrlW, closeAction);
1077 public void lostOwnership(Clipboard clipboard, Transferable contents)
1081 Desktop.jalviewClipboard = null;
1084 internalCopy = false;
1088 public void dragEnter(DropTargetDragEvent evt)
1093 public void dragExit(DropTargetEvent evt)
1098 public void dragOver(DropTargetDragEvent evt)
1103 public void dropActionChanged(DropTargetDragEvent evt)
1114 public void drop(DropTargetDropEvent evt)
1116 boolean success = true;
1117 // JAL-1552 - acceptDrop required before getTransferable call for
1118 // Java's Transferable for native dnd
1119 evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
1120 Transferable t = evt.getTransferable();
1121 List<Object> files = new ArrayList<>();
1122 List<DataSourceType> protocols = new ArrayList<>();
1126 Desktop.transferFromDropTarget(files, protocols, evt, t);
1127 } catch (Exception e)
1129 e.printStackTrace();
1137 for (int i = 0; i < files.size(); i++)
1139 // BH 2018 File or String
1140 Object file = files.get(i);
1141 String fileName = file.toString();
1142 DataSourceType protocol = (protocols == null)
1143 ? DataSourceType.FILE
1145 FileFormatI format = null;
1147 if (fileName.endsWith(".jar"))
1149 format = FileFormat.Jalview;
1154 format = new IdentifyFile().identify(file, protocol);
1156 if (file instanceof File)
1158 Platform.cacheFileData((File) file);
1160 new FileLoader().LoadFile(null, file, protocol, format);
1163 } catch (Exception ex)
1168 evt.dropComplete(success); // need this to ensure input focus is properly
1169 // transfered to any new windows created
1179 public void inputLocalFileMenuItem_actionPerformed(AlignViewport viewport)
1181 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
1182 JalviewFileChooser chooser = JalviewFileChooser.forRead(
1183 Cache.getProperty("LAST_DIRECTORY"), fileFormat,
1184 BackupFiles.getEnabled());
1186 chooser.setFileView(new JalviewFileView());
1187 chooser.setDialogTitle(
1188 MessageManager.getString("label.open_local_file"));
1189 chooser.setToolTipText(MessageManager.getString("action.open"));
1191 chooser.setResponseHandler(0, new Runnable()
1196 File selectedFile = chooser.getSelectedFile();
1197 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1199 FileFormatI format = chooser.getSelectedFormat();
1202 * Call IdentifyFile to verify the file contains what its extension implies.
1203 * Skip this step for dynamically added file formats, because IdentifyFile does
1204 * not know how to recognise them.
1206 if (FileFormats.getInstance().isIdentifiable(format))
1210 format = new IdentifyFile().identify(selectedFile,
1211 DataSourceType.FILE);
1212 } catch (FileFormatException e)
1214 // format = null; //??
1218 new FileLoader().LoadFile(viewport, selectedFile,
1219 DataSourceType.FILE, format);
1222 chooser.showOpenDialog(this);
1226 * Shows a dialog for input of a URL at which to retrieve alignment data
1231 public void inputURLMenuItem_actionPerformed(AlignViewport viewport)
1233 // This construct allows us to have a wider textfield
1235 JLabel label = new JLabel(
1236 MessageManager.getString("label.input_file_url"));
1238 JPanel panel = new JPanel(new GridLayout(2, 1));
1242 * the URL to fetch is input in Java: an editable combobox with history JS:
1243 * (pending JAL-3038) a plain text field
1246 String urlBase = "https://www.";
1247 if (Platform.isJS())
1249 history = new JTextField(urlBase, 35);
1258 JComboBox<String> asCombo = new JComboBox<>();
1259 asCombo.setPreferredSize(new Dimension(400, 20));
1260 asCombo.setEditable(true);
1261 asCombo.addItem(urlBase);
1262 String historyItems = Cache.getProperty("RECENT_URL");
1263 if (historyItems != null)
1265 for (String token : historyItems.split("\\t"))
1267 asCombo.addItem(token);
1274 Object[] options = new Object[] { MessageManager.getString("action.ok"),
1275 MessageManager.getString("action.cancel") };
1276 Runnable action = new Runnable()
1281 @SuppressWarnings("unchecked")
1282 String url = (history instanceof JTextField
1283 ? ((JTextField) history).getText()
1284 : ((JComboBox<String>) history).getEditor().getItem()
1285 .toString().trim());
1287 if (url.toLowerCase(Locale.ROOT).endsWith(".jar"))
1289 if (viewport != null)
1291 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1292 FileFormat.Jalview);
1296 new FileLoader().LoadFile(url, DataSourceType.URL,
1297 FileFormat.Jalview);
1302 FileFormatI format = null;
1305 format = new IdentifyFile().identify(url, DataSourceType.URL);
1306 } catch (FileFormatException e)
1308 // TODO revise error handling, distinguish between
1309 // URL not found and response not valid
1314 String msg = MessageManager
1315 .formatMessage("label.couldnt_locate", url);
1316 JvOptionPane.showInternalMessageDialog(Desktop.desktop, msg,
1317 MessageManager.getString("label.url_not_found"),
1318 JvOptionPane.WARNING_MESSAGE);
1323 if (viewport != null)
1325 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1330 new FileLoader().LoadFile(url, DataSourceType.URL, format);
1335 String dialogOption = MessageManager
1336 .getString("label.input_alignment_from_url");
1337 JvOptionPane.newOptionDialog(desktop).setResponseHandler(0, action)
1338 .showInternalDialog(panel, dialogOption,
1339 JvOptionPane.YES_NO_CANCEL_OPTION,
1340 JvOptionPane.PLAIN_MESSAGE, null, options,
1341 MessageManager.getString("action.ok"));
1345 * Opens the CutAndPaste window for the user to paste an alignment in to
1348 * - if not null, the pasted alignment is added to the current
1349 * alignment; if null, to a new alignment window
1352 public void inputTextboxMenuItem_actionPerformed(
1353 AlignmentViewPanel viewPanel)
1355 CutAndPasteTransfer cap = new CutAndPasteTransfer();
1356 cap.setForInput(viewPanel);
1357 Desktop.addInternalFrame(cap,
1358 MessageManager.getString("label.cut_paste_alignmen_file"), true,
1368 Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
1369 Cache.setProperty("SCREENGEOMETRY_WIDTH", screen.width + "");
1370 Cache.setProperty("SCREENGEOMETRY_HEIGHT", screen.height + "");
1371 storeLastKnownDimensions("", new Rectangle(getBounds().x, getBounds().y,
1372 getWidth(), getHeight()));
1374 if (jconsole != null)
1376 storeLastKnownDimensions("JAVA_CONSOLE_", jconsole.getBounds());
1377 jconsole.stopConsole();
1381 storeLastKnownDimensions("JALVIEW_RSS_WINDOW_", jvnews.getBounds());
1384 if (dialogExecutor != null)
1386 dialogExecutor.shutdownNow();
1388 closeAll_actionPerformed(null);
1390 if (groovyConsole != null)
1392 // suppress a possible repeat prompt to save script
1393 groovyConsole.setDirty(false);
1394 groovyConsole.exit();
1399 private void storeLastKnownDimensions(String string, Rectangle jc)
1401 jalview.bin.Console.debug("Storing last known dimensions for " + string
1402 + ": x:" + jc.x + " y:" + jc.y + " width:" + jc.width
1403 + " height:" + jc.height);
1405 Cache.setProperty(string + "SCREEN_X", jc.x + "");
1406 Cache.setProperty(string + "SCREEN_Y", jc.y + "");
1407 Cache.setProperty(string + "SCREEN_WIDTH", jc.width + "");
1408 Cache.setProperty(string + "SCREEN_HEIGHT", jc.height + "");
1418 public void aboutMenuItem_actionPerformed(ActionEvent e)
1420 new Thread(new Runnable()
1425 new SplashScreen(false);
1431 * Returns the html text for the About screen, including any available version
1432 * number, build details, author details and citation reference, but without
1433 * the enclosing {@code html} tags
1437 public String getAboutMessage()
1439 StringBuilder message = new StringBuilder(1024);
1440 message.append("<div style=\"font-family: sans-serif;\">")
1441 .append("<h1><strong>Version: ")
1442 .append(Cache.getProperty("VERSION")).append("</strong></h1>")
1443 .append("<strong>Built: <em>")
1444 .append(Cache.getDefault("BUILD_DATE", "unknown"))
1445 .append("</em> from ").append(Cache.getBuildDetailsForSplash())
1446 .append("</strong>");
1448 String latestVersion = Cache.getDefault("LATEST_VERSION", "Checking");
1449 if (latestVersion.equals("Checking"))
1451 // JBP removed this message for 2.11: May be reinstated in future version
1452 // message.append("<br>...Checking latest version...</br>");
1454 else if (!latestVersion.equals(Cache.getProperty("VERSION")))
1456 boolean red = false;
1457 if (Cache.getProperty("VERSION").toLowerCase(Locale.ROOT)
1458 .indexOf("automated build") == -1)
1461 // Displayed when code version and jnlp version do not match and code
1462 // version is not a development build
1463 message.append("<div style=\"color: #FF0000;font-style: bold;\">");
1466 message.append("<br>!! Version ")
1467 .append(Cache.getDefault("LATEST_VERSION", "..Checking.."))
1468 .append(" is available for download from ")
1469 .append(Cache.getDefault("www.jalview.org",
1470 "https://www.jalview.org"))
1474 message.append("</div>");
1477 message.append("<br>Authors: ");
1478 message.append(Cache.getDefault("AUTHORFNAMES", DEFAULT_AUTHORS));
1479 message.append(CITATION);
1481 message.append("</div>");
1483 return message.toString();
1487 * Action on requesting Help documentation
1490 public void documentationMenuItem_actionPerformed()
1494 if (Platform.isJS())
1496 BrowserLauncher.openURL("https://www.jalview.org/help.html");
1505 Help.showHelpWindow();
1507 } catch (Exception ex)
1509 System.err.println("Error opening help: " + ex.getMessage());
1514 public void closeAll_actionPerformed(ActionEvent e)
1516 // TODO show a progress bar while closing?
1517 JInternalFrame[] frames = desktop.getAllFrames();
1518 for (int i = 0; i < frames.length; i++)
1522 frames[i].setClosed(true);
1523 } catch (java.beans.PropertyVetoException ex)
1527 Jalview.setCurrentAlignFrame(null);
1528 System.out.println("ALL CLOSED");
1531 * reset state of singleton objects as appropriate (clear down session state
1532 * when all windows are closed)
1534 StructureSelectionManager ssm = StructureSelectionManager
1535 .getStructureSelectionManager(this);
1543 public void raiseRelated_actionPerformed(ActionEvent e)
1545 reorderAssociatedWindows(false, false);
1549 public void minimizeAssociated_actionPerformed(ActionEvent e)
1551 reorderAssociatedWindows(true, false);
1554 void closeAssociatedWindows()
1556 reorderAssociatedWindows(false, true);
1562 * @seejalview.jbgui.GDesktop#garbageCollect_actionPerformed(java.awt.event.
1566 protected void garbageCollect_actionPerformed(ActionEvent e)
1568 // We simply collect the garbage
1569 jalview.bin.Console.debug("Collecting garbage...");
1571 jalview.bin.Console.debug("Finished garbage collection.");
1577 * @see jalview.jbgui.GDesktop#showMemusage_actionPerformed(java.awt.event.
1581 protected void showMemusage_actionPerformed(ActionEvent e)
1583 desktop.showMemoryUsage(showMemusage.isSelected());
1590 * jalview.jbgui.GDesktop#showConsole_actionPerformed(java.awt.event.ActionEvent
1594 protected void showConsole_actionPerformed(ActionEvent e)
1596 showConsole(showConsole.isSelected());
1599 Console jconsole = null;
1602 * control whether the java console is visible or not
1606 void showConsole(boolean selected)
1608 // TODO: decide if we should update properties file
1609 if (jconsole != null) // BH 2018
1611 showConsole.setSelected(selected);
1612 Cache.setProperty("SHOW_JAVA_CONSOLE",
1613 Boolean.valueOf(selected).toString());
1614 jconsole.setVisible(selected);
1618 void reorderAssociatedWindows(boolean minimize, boolean close)
1620 JInternalFrame[] frames = desktop.getAllFrames();
1621 if (frames == null || frames.length < 1)
1626 AlignmentViewport source = null, target = null;
1627 if (frames[0] instanceof AlignFrame)
1629 source = ((AlignFrame) frames[0]).getCurrentView();
1631 else if (frames[0] instanceof TreePanel)
1633 source = ((TreePanel) frames[0]).getViewPort();
1635 else if (frames[0] instanceof PCAPanel)
1637 source = ((PCAPanel) frames[0]).av;
1639 else if (frames[0].getContentPane() instanceof PairwiseAlignPanel)
1641 source = ((PairwiseAlignPanel) frames[0].getContentPane()).av;
1646 for (int i = 0; i < frames.length; i++)
1649 if (frames[i] == null)
1653 if (frames[i] instanceof AlignFrame)
1655 target = ((AlignFrame) frames[i]).getCurrentView();
1657 else if (frames[i] instanceof TreePanel)
1659 target = ((TreePanel) frames[i]).getViewPort();
1661 else if (frames[i] instanceof PCAPanel)
1663 target = ((PCAPanel) frames[i]).av;
1665 else if (frames[i].getContentPane() instanceof PairwiseAlignPanel)
1667 target = ((PairwiseAlignPanel) frames[i].getContentPane()).av;
1670 if (source == target)
1676 frames[i].setClosed(true);
1680 frames[i].setIcon(minimize);
1683 frames[i].toFront();
1687 } catch (java.beans.PropertyVetoException ex)
1702 protected void preferences_actionPerformed(ActionEvent e)
1704 Preferences.openPreferences();
1708 * Prompts the user to choose a file and then saves the Jalview state as a
1709 * Jalview project file
1712 public void saveState_actionPerformed()
1714 saveState_actionPerformed(false);
1717 public void saveState_actionPerformed(boolean saveAs)
1719 java.io.File projectFile = getProjectFile();
1720 // autoSave indicates we already have a file and don't need to ask
1721 boolean autoSave = projectFile != null && !saveAs
1722 && BackupFiles.getEnabled();
1724 // System.out.println("autoSave="+autoSave+", projectFile='"+projectFile+"',
1725 // saveAs="+saveAs+", Backups
1726 // "+(BackupFiles.getEnabled()?"enabled":"disabled"));
1728 boolean approveSave = false;
1731 JalviewFileChooser chooser = new JalviewFileChooser("jvp",
1734 chooser.setFileView(new JalviewFileView());
1735 chooser.setDialogTitle(MessageManager.getString("label.save_state"));
1737 int value = chooser.showSaveDialog(this);
1739 if (value == JalviewFileChooser.APPROVE_OPTION)
1741 projectFile = chooser.getSelectedFile();
1742 setProjectFile(projectFile);
1747 if (approveSave || autoSave)
1749 final Desktop me = this;
1750 final java.io.File chosenFile = projectFile;
1751 new Thread(new Runnable()
1756 // TODO: refactor to Jalview desktop session controller action.
1757 setProgressBar(MessageManager.formatMessage(
1758 "label.saving_jalview_project", new Object[]
1759 { chosenFile.getName() }), chosenFile.hashCode());
1760 Cache.setProperty("LAST_DIRECTORY", chosenFile.getParent());
1761 // TODO catch and handle errors for savestate
1762 // TODO prevent user from messing with the Desktop whilst we're saving
1765 boolean doBackup = BackupFiles.getEnabled();
1766 BackupFiles backupfiles = doBackup ? new BackupFiles(chosenFile)
1769 new Jalview2XML().saveState(
1770 doBackup ? backupfiles.getTempFile() : chosenFile);
1774 backupfiles.setWriteSuccess(true);
1775 backupfiles.rollBackupsAndRenameTempFile();
1777 } catch (OutOfMemoryError oom)
1779 new OOMWarning("Whilst saving current state to "
1780 + chosenFile.getName(), oom);
1781 } catch (Exception ex)
1783 jalview.bin.Console.error("Problems whilst trying to save to "
1784 + chosenFile.getName(), ex);
1785 JvOptionPane.showMessageDialog(me,
1786 MessageManager.formatMessage(
1787 "label.error_whilst_saving_current_state_to",
1789 { chosenFile.getName() }),
1790 MessageManager.getString("label.couldnt_save_project"),
1791 JvOptionPane.WARNING_MESSAGE);
1793 setProgressBar(null, chosenFile.hashCode());
1800 public void saveAsState_actionPerformed(ActionEvent e)
1802 saveState_actionPerformed(true);
1805 private void setProjectFile(File choice)
1807 this.projectFile = choice;
1810 public File getProjectFile()
1812 return this.projectFile;
1816 * Shows a file chooser dialog and tries to read in the selected file as a
1820 public void loadState_actionPerformed()
1822 final String[] suffix = new String[] { "jvp", "jar" };
1823 final String[] desc = new String[] { "Jalview Project",
1824 "Jalview Project (old)" };
1825 JalviewFileChooser chooser = new JalviewFileChooser(
1826 Cache.getProperty("LAST_DIRECTORY"), suffix, desc,
1827 "Jalview Project", true, BackupFiles.getEnabled()); // last two
1831 chooser.setFileView(new JalviewFileView());
1832 chooser.setDialogTitle(MessageManager.getString("label.restore_state"));
1833 chooser.setResponseHandler(0, new Runnable()
1838 File selectedFile = chooser.getSelectedFile();
1839 setProjectFile(selectedFile);
1840 String choice = selectedFile.getAbsolutePath();
1841 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1842 new Thread(new Runnable()
1849 new Jalview2XML().loadJalviewAlign(selectedFile);
1850 } catch (OutOfMemoryError oom)
1852 new OOMWarning("Whilst loading project from " + choice, oom);
1853 } catch (Exception ex)
1855 jalview.bin.Console.error(
1856 "Problems whilst loading project from " + choice, ex);
1857 JvOptionPane.showMessageDialog(Desktop.desktop,
1858 MessageManager.formatMessage(
1859 "label.error_whilst_loading_project_from",
1863 .getString("label.couldnt_load_project"),
1864 JvOptionPane.WARNING_MESSAGE);
1867 }, "Project Loader").start();
1871 chooser.showOpenDialog(this);
1875 public void inputSequence_actionPerformed(ActionEvent e)
1877 new SequenceFetcher(this);
1880 JPanel progressPanel;
1882 ArrayList<JPanel> fileLoadingPanels = new ArrayList<>();
1884 public void startLoading(final Object fileName)
1886 if (fileLoadingCount == 0)
1888 fileLoadingPanels.add(addProgressPanel(MessageManager
1889 .formatMessage("label.loading_file", new Object[]
1895 private JPanel addProgressPanel(String string)
1897 if (progressPanel == null)
1899 progressPanel = new JPanel(new GridLayout(1, 1));
1900 totalProgressCount = 0;
1901 instance.getContentPane().add(progressPanel, BorderLayout.SOUTH);
1903 JPanel thisprogress = new JPanel(new BorderLayout(10, 5));
1904 JProgressBar progressBar = new JProgressBar();
1905 progressBar.setIndeterminate(true);
1907 thisprogress.add(new JLabel(string), BorderLayout.WEST);
1909 thisprogress.add(progressBar, BorderLayout.CENTER);
1910 progressPanel.add(thisprogress);
1911 ((GridLayout) progressPanel.getLayout()).setRows(
1912 ((GridLayout) progressPanel.getLayout()).getRows() + 1);
1913 ++totalProgressCount;
1914 instance.validate();
1915 return thisprogress;
1918 int totalProgressCount = 0;
1920 private void removeProgressPanel(JPanel progbar)
1922 if (progressPanel != null)
1924 synchronized (progressPanel)
1926 progressPanel.remove(progbar);
1927 GridLayout gl = (GridLayout) progressPanel.getLayout();
1928 gl.setRows(gl.getRows() - 1);
1929 if (--totalProgressCount < 1)
1931 this.getContentPane().remove(progressPanel);
1932 progressPanel = null;
1939 public void stopLoading()
1942 if (fileLoadingCount < 1)
1944 while (fileLoadingPanels.size() > 0)
1946 removeProgressPanel(fileLoadingPanels.remove(0));
1948 fileLoadingPanels.clear();
1949 fileLoadingCount = 0;
1954 public static int getViewCount(String alignmentId)
1956 AlignmentViewport[] aps = getViewports(alignmentId);
1957 return (aps == null) ? 0 : aps.length;
1962 * @param alignmentId
1963 * - if null, all sets are returned
1964 * @return all AlignmentPanels concerning the alignmentId sequence set
1966 public static AlignmentPanel[] getAlignmentPanels(String alignmentId)
1968 if (Desktop.desktop == null)
1970 // no frames created and in headless mode
1971 // TODO: verify that frames are recoverable when in headless mode
1974 List<AlignmentPanel> aps = new ArrayList<>();
1975 AlignFrame[] frames = getAlignFrames();
1980 for (AlignFrame af : frames)
1982 for (AlignmentPanel ap : af.alignPanels)
1984 if (alignmentId == null
1985 || alignmentId.equals(ap.av.getSequenceSetId()))
1991 if (aps.size() == 0)
1995 AlignmentPanel[] vap = aps.toArray(new AlignmentPanel[aps.size()]);
2000 * get all the viewports on an alignment.
2002 * @param sequenceSetId
2003 * unique alignment id (may be null - all viewports returned in that
2005 * @return all viewports on the alignment bound to sequenceSetId
2007 public static AlignmentViewport[] getViewports(String sequenceSetId)
2009 List<AlignmentViewport> viewp = new ArrayList<>();
2010 if (desktop != null)
2012 AlignFrame[] frames = Desktop.getAlignFrames();
2014 for (AlignFrame afr : frames)
2016 if (sequenceSetId == null || afr.getViewport().getSequenceSetId()
2017 .equals(sequenceSetId))
2019 if (afr.alignPanels != null)
2021 for (AlignmentPanel ap : afr.alignPanels)
2023 if (sequenceSetId == null
2024 || sequenceSetId.equals(ap.av.getSequenceSetId()))
2032 viewp.add(afr.getViewport());
2036 if (viewp.size() > 0)
2038 return viewp.toArray(new AlignmentViewport[viewp.size()]);
2045 * Explode the views in the given frame into separate AlignFrame
2049 public static void explodeViews(AlignFrame af)
2051 int size = af.alignPanels.size();
2057 // FIXME: ideally should use UI interface API
2058 FeatureSettings viewFeatureSettings = (af.featureSettings != null
2059 && af.featureSettings.isOpen()) ? af.featureSettings : null;
2060 Rectangle fsBounds = af.getFeatureSettingsGeometry();
2061 for (int i = 0; i < size; i++)
2063 AlignmentPanel ap = af.alignPanels.get(i);
2065 AlignFrame newaf = new AlignFrame(ap);
2067 // transfer reference for existing feature settings to new alignFrame
2068 if (ap == af.alignPanel)
2070 if (viewFeatureSettings != null && viewFeatureSettings.fr.ap == ap)
2072 newaf.featureSettings = viewFeatureSettings;
2074 newaf.setFeatureSettingsGeometry(fsBounds);
2078 * Restore the view's last exploded frame geometry if known. Multiple views from
2079 * one exploded frame share and restore the same (frame) position and size.
2081 Rectangle geometry = ap.av.getExplodedGeometry();
2082 if (geometry != null)
2084 newaf.setBounds(geometry);
2087 ap.av.setGatherViewsHere(false);
2089 addInternalFrame(newaf, af.getTitle(), AlignFrame.DEFAULT_WIDTH,
2090 AlignFrame.DEFAULT_HEIGHT);
2091 // and materialise a new feature settings dialog instance for the new
2093 // (closes the old as if 'OK' was pressed)
2094 if (ap == af.alignPanel && newaf.featureSettings != null
2095 && newaf.featureSettings.isOpen()
2096 && af.alignPanel.getAlignViewport().isShowSequenceFeatures())
2098 newaf.showFeatureSettingsUI();
2102 af.featureSettings = null;
2103 af.alignPanels.clear();
2104 af.closeMenuItem_actionPerformed(true);
2109 * Gather expanded views (separate AlignFrame's) with the same sequence set
2110 * identifier back in to this frame as additional views, and close the
2111 * expanded views. Note the expanded frames may themselves have multiple
2112 * views. We take the lot.
2116 public void gatherViews(AlignFrame source)
2118 source.viewport.setGatherViewsHere(true);
2119 source.viewport.setExplodedGeometry(source.getBounds());
2120 JInternalFrame[] frames = desktop.getAllFrames();
2121 String viewId = source.viewport.getSequenceSetId();
2122 for (int t = 0; t < frames.length; t++)
2124 if (frames[t] instanceof AlignFrame && frames[t] != source)
2126 AlignFrame af = (AlignFrame) frames[t];
2127 boolean gatherThis = false;
2128 for (int a = 0; a < af.alignPanels.size(); a++)
2130 AlignmentPanel ap = af.alignPanels.get(a);
2131 if (viewId.equals(ap.av.getSequenceSetId()))
2134 ap.av.setGatherViewsHere(false);
2135 ap.av.setExplodedGeometry(af.getBounds());
2136 source.addAlignmentPanel(ap, false);
2142 if (af.featureSettings != null && af.featureSettings.isOpen())
2144 if (source.featureSettings == null)
2146 // preserve the feature settings geometry for this frame
2147 source.featureSettings = af.featureSettings;
2148 source.setFeatureSettingsGeometry(
2149 af.getFeatureSettingsGeometry());
2153 // close it and forget
2154 af.featureSettings.close();
2157 af.alignPanels.clear();
2158 af.closeMenuItem_actionPerformed(true);
2163 // refresh the feature setting UI for the source frame if it exists
2164 if (source.featureSettings != null && source.featureSettings.isOpen())
2166 source.showFeatureSettingsUI();
2171 public JInternalFrame[] getAllFrames()
2173 return desktop.getAllFrames();
2177 * Checks the given url to see if it gives a response indicating that the user
2178 * should be informed of a new questionnaire.
2182 public void checkForQuestionnaire(String url)
2184 UserQuestionnaireCheck jvq = new UserQuestionnaireCheck(url);
2185 // javax.swing.SwingUtilities.invokeLater(jvq);
2186 new Thread(jvq).start();
2189 public void checkURLLinks()
2191 // Thread off the URL link checker
2192 addDialogThread(new Runnable()
2197 if (Cache.getDefault("CHECKURLLINKS", true))
2199 // check what the actual links are - if it's just the default don't
2200 // bother with the warning
2201 List<String> links = Preferences.sequenceUrlLinks
2204 // only need to check links if there is one with a
2205 // SEQUENCE_ID which is not the default EMBL_EBI link
2206 ListIterator<String> li = links.listIterator();
2207 boolean check = false;
2208 List<JLabel> urls = new ArrayList<>();
2209 while (li.hasNext())
2211 String link = li.next();
2212 if (link.contains(jalview.util.UrlConstants.SEQUENCE_ID)
2213 && !UrlConstants.isDefaultString(link))
2216 int barPos = link.indexOf("|");
2217 String urlMsg = barPos == -1 ? link
2218 : link.substring(0, barPos) + ": "
2219 + link.substring(barPos + 1);
2220 urls.add(new JLabel(urlMsg));
2228 // ask user to check in case URL links use old style tokens
2229 // ($SEQUENCE_ID$ for sequence id _or_ accession id)
2230 JPanel msgPanel = new JPanel();
2231 msgPanel.setLayout(new BoxLayout(msgPanel, BoxLayout.PAGE_AXIS));
2232 msgPanel.add(Box.createVerticalGlue());
2233 JLabel msg = new JLabel(MessageManager
2234 .getString("label.SEQUENCE_ID_for_DB_ACCESSION1"));
2235 JLabel msg2 = new JLabel(MessageManager
2236 .getString("label.SEQUENCE_ID_for_DB_ACCESSION2"));
2238 for (JLabel url : urls)
2244 final JCheckBox jcb = new JCheckBox(
2245 MessageManager.getString("label.do_not_display_again"));
2246 jcb.addActionListener(new ActionListener()
2249 public void actionPerformed(ActionEvent e)
2251 // update Cache settings for "don't show this again"
2252 boolean showWarningAgain = !jcb.isSelected();
2253 Cache.setProperty("CHECKURLLINKS",
2254 Boolean.valueOf(showWarningAgain).toString());
2259 JvOptionPane.showMessageDialog(Desktop.desktop, msgPanel,
2261 .getString("label.SEQUENCE_ID_no_longer_used"),
2262 JvOptionPane.WARNING_MESSAGE);
2269 * Proxy class for JDesktopPane which optionally displays the current memory
2270 * usage and highlights the desktop area with a red bar if free memory runs
2275 public class MyDesktopPane extends JDesktopPane implements Runnable
2277 private static final float ONE_MB = 1048576f;
2279 boolean showMemoryUsage = false;
2283 java.text.NumberFormat df;
2285 float maxMemory, allocatedMemory, freeMemory, totalFreeMemory,
2288 public MyDesktopPane(boolean showMemoryUsage)
2290 showMemoryUsage(showMemoryUsage);
2293 public void showMemoryUsage(boolean showMemory)
2295 this.showMemoryUsage = showMemory;
2298 Thread worker = new Thread(this);
2304 public boolean isShowMemoryUsage()
2306 return showMemoryUsage;
2312 df = java.text.NumberFormat.getNumberInstance();
2313 df.setMaximumFractionDigits(2);
2314 runtime = Runtime.getRuntime();
2316 while (showMemoryUsage)
2320 maxMemory = runtime.maxMemory() / ONE_MB;
2321 allocatedMemory = runtime.totalMemory() / ONE_MB;
2322 freeMemory = runtime.freeMemory() / ONE_MB;
2323 totalFreeMemory = freeMemory + (maxMemory - allocatedMemory);
2325 percentUsage = (totalFreeMemory / maxMemory) * 100;
2327 // if (percentUsage < 20)
2329 // border1 = BorderFactory.createMatteBorder(12, 12, 12, 12,
2331 // instance.set.setBorder(border1);
2334 // sleep after showing usage
2336 } catch (Exception ex)
2338 ex.printStackTrace();
2344 public void paintComponent(Graphics g)
2346 if (showMemoryUsage && g != null && df != null)
2348 if (percentUsage < 20)
2350 g.setColor(Color.red);
2352 FontMetrics fm = g.getFontMetrics();
2355 g.drawString(MessageManager.formatMessage("label.memory_stats",
2357 { df.format(totalFreeMemory), df.format(maxMemory),
2358 df.format(percentUsage) }),
2359 10, getHeight() - fm.getHeight());
2363 // output debug scale message. Important for jalview.bin.HiDPISettingTest2
2364 Desktop.debugScaleMessage(Desktop.getDesktop().getGraphics());
2369 * Accessor method to quickly get all the AlignmentFrames loaded.
2371 * @return an array of AlignFrame, or null if none found
2373 public static AlignFrame[] getAlignFrames()
2375 if (Jalview.isHeadlessMode())
2377 // Desktop.desktop is null in headless mode
2378 return new AlignFrame[] { Jalview.currentAlignFrame };
2381 JInternalFrame[] frames = Desktop.desktop.getAllFrames();
2387 List<AlignFrame> avp = new ArrayList<>();
2389 for (int i = frames.length - 1; i > -1; i--)
2391 if (frames[i] instanceof AlignFrame)
2393 avp.add((AlignFrame) frames[i]);
2395 else if (frames[i] instanceof SplitFrame)
2398 * Also check for a split frame containing an AlignFrame
2400 GSplitFrame sf = (GSplitFrame) frames[i];
2401 if (sf.getTopFrame() instanceof AlignFrame)
2403 avp.add((AlignFrame) sf.getTopFrame());
2405 if (sf.getBottomFrame() instanceof AlignFrame)
2407 avp.add((AlignFrame) sf.getBottomFrame());
2411 if (avp.size() == 0)
2415 AlignFrame afs[] = avp.toArray(new AlignFrame[avp.size()]);
2420 * Returns an array of any AppJmol frames in the Desktop (or null if none).
2424 public GStructureViewer[] getJmols()
2426 JInternalFrame[] frames = Desktop.desktop.getAllFrames();
2432 List<GStructureViewer> avp = new ArrayList<>();
2434 for (int i = frames.length - 1; i > -1; i--)
2436 if (frames[i] instanceof AppJmol)
2438 GStructureViewer af = (GStructureViewer) frames[i];
2442 if (avp.size() == 0)
2446 GStructureViewer afs[] = avp.toArray(new GStructureViewer[avp.size()]);
2451 * Add Groovy Support to Jalview
2454 public void groovyShell_actionPerformed()
2458 openGroovyConsole();
2459 } catch (Exception ex)
2461 jalview.bin.Console.error("Groovy Shell Creation failed.", ex);
2462 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
2464 MessageManager.getString("label.couldnt_create_groovy_shell"),
2465 MessageManager.getString("label.groovy_support_failed"),
2466 JvOptionPane.ERROR_MESSAGE);
2471 * Open the Groovy console
2473 void openGroovyConsole()
2475 if (groovyConsole == null)
2477 groovyConsole = new groovy.ui.Console();
2478 groovyConsole.setVariable("Jalview", this);
2479 groovyConsole.run();
2482 * We allow only one console at a time, so that AlignFrame menu option
2483 * 'Calculate | Run Groovy script' is unambiguous. Disable 'Groovy Console', and
2484 * enable 'Run script', when the console is opened, and the reverse when it is
2487 Window window = (Window) groovyConsole.getFrame();
2488 window.addWindowListener(new WindowAdapter()
2491 public void windowClosed(WindowEvent e)
2494 * rebind CMD-Q from Groovy Console to Jalview Quit
2497 enableExecuteGroovy(false);
2503 * show Groovy console window (after close and reopen)
2505 ((Window) groovyConsole.getFrame()).setVisible(true);
2508 * if we got this far, enable 'Run Groovy' in AlignFrame menus and disable
2509 * opening a second console
2511 enableExecuteGroovy(true);
2515 * Bind Ctrl/Cmd-Q to Quit - for reset as Groovy Console takes over this
2516 * binding when opened
2518 protected void addQuitHandler()
2521 .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
2523 .getKeyStroke(KeyEvent.VK_Q,
2524 jalview.util.ShortcutKeyMaskExWrapper
2525 .getMenuShortcutKeyMaskEx()),
2527 getRootPane().getActionMap().put("Quit", new AbstractAction()
2530 public void actionPerformed(ActionEvent e)
2538 * Enable or disable 'Run Groovy script' in AlignFrame calculate menus
2541 * true if Groovy console is open
2543 public void enableExecuteGroovy(boolean enabled)
2546 * disable opening a second Groovy console (or re-enable when the console is
2549 groovyShell.setEnabled(!enabled);
2551 AlignFrame[] alignFrames = getAlignFrames();
2552 if (alignFrames != null)
2554 for (AlignFrame af : alignFrames)
2556 af.setGroovyEnabled(enabled);
2562 * Progress bars managed by the IProgressIndicator method.
2564 private Hashtable<Long, JPanel> progressBars;
2566 private Hashtable<Long, IProgressIndicatorHandler> progressBarHandlers;
2571 * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
2574 public void setProgressBar(String message, long id)
2576 if (progressBars == null)
2578 progressBars = new Hashtable<>();
2579 progressBarHandlers = new Hashtable<>();
2582 if (progressBars.get(Long.valueOf(id)) != null)
2584 JPanel panel = progressBars.remove(Long.valueOf(id));
2585 if (progressBarHandlers.contains(Long.valueOf(id)))
2587 progressBarHandlers.remove(Long.valueOf(id));
2589 removeProgressPanel(panel);
2593 progressBars.put(Long.valueOf(id), addProgressPanel(message));
2600 * @see jalview.gui.IProgressIndicator#registerHandler(long,
2601 * jalview.gui.IProgressIndicatorHandler)
2604 public void registerHandler(final long id,
2605 final IProgressIndicatorHandler handler)
2607 if (progressBarHandlers == null
2608 || !progressBars.containsKey(Long.valueOf(id)))
2610 throw new Error(MessageManager.getString(
2611 "error.call_setprogressbar_before_registering_handler"));
2613 progressBarHandlers.put(Long.valueOf(id), handler);
2614 final JPanel progressPanel = progressBars.get(Long.valueOf(id));
2615 if (handler.canCancel())
2617 JButton cancel = new JButton(
2618 MessageManager.getString("action.cancel"));
2619 final IProgressIndicator us = this;
2620 cancel.addActionListener(new ActionListener()
2624 public void actionPerformed(ActionEvent e)
2626 handler.cancelActivity(id);
2627 us.setProgressBar(MessageManager
2628 .formatMessage("label.cancelled_params", new Object[]
2629 { ((JLabel) progressPanel.getComponent(0)).getText() }),
2633 progressPanel.add(cancel, BorderLayout.EAST);
2639 * @return true if any progress bars are still active
2642 public boolean operationInProgress()
2644 if (progressBars != null && progressBars.size() > 0)
2652 * This will return the first AlignFrame holding the given viewport instance.
2653 * It will break if there are more than one AlignFrames viewing a particular
2657 * @return alignFrame for viewport
2659 public static AlignFrame getAlignFrameFor(AlignViewportI viewport)
2661 if (desktop != null)
2663 AlignmentPanel[] aps = getAlignmentPanels(
2664 viewport.getSequenceSetId());
2665 for (int panel = 0; aps != null && panel < aps.length; panel++)
2667 if (aps[panel] != null && aps[panel].av == viewport)
2669 return aps[panel].alignFrame;
2676 public VamsasApplication getVamsasApplication()
2678 // TODO: JAL-3311 remove remaining code from Jalview relating to VAMSAS
2684 * flag set if jalview GUI is being operated programmatically
2686 private boolean inBatchMode = false;
2689 * check if jalview GUI is being operated programmatically
2691 * @return inBatchMode
2693 public boolean isInBatchMode()
2699 * set flag if jalview GUI is being operated programmatically
2701 * @param inBatchMode
2703 public void setInBatchMode(boolean inBatchMode)
2705 this.inBatchMode = inBatchMode;
2709 * start service discovery and wait till it is done
2711 public void startServiceDiscovery()
2713 startServiceDiscovery(false);
2717 * start service discovery threads - blocking or non-blocking
2721 public void startServiceDiscovery(boolean blocking)
2723 startServiceDiscovery(blocking, false);
2727 * start service discovery threads
2730 * - false means call returns immediately
2731 * @param ignore_SHOW_JWS2_SERVICES_preference
2732 * - when true JABA services are discovered regardless of user's JWS2
2733 * discovery preference setting
2735 public void startServiceDiscovery(boolean blocking,
2736 boolean ignore_SHOW_JWS2_SERVICES_preference)
2738 boolean alive = true;
2739 Thread t0 = null, t1 = null, t2 = null;
2740 // JAL-940 - JALVIEW 1 services are now being EOLed as of JABA 2.1 release
2743 // todo: changesupport handlers need to be transferred
2744 if (discoverer == null)
2746 discoverer = new jalview.ws.jws1.Discoverer();
2747 // register PCS handler for desktop.
2748 discoverer.addPropertyChangeListener(changeSupport);
2750 // JAL-940 - disabled JWS1 service configuration - always start discoverer
2751 // until we phase out completely
2752 (t0 = new Thread(discoverer)).start();
2755 if (ignore_SHOW_JWS2_SERVICES_preference
2756 || Cache.getDefault("SHOW_JWS2_SERVICES", true))
2758 t2 = jalview.ws.jws2.Jws2Discoverer.getDiscoverer()
2759 .startDiscoverer(changeSupport);
2763 // TODO: do rest service discovery
2772 } catch (Exception e)
2775 alive = (t1 != null && t1.isAlive()) || (t2 != null && t2.isAlive())
2776 || (t3 != null && t3.isAlive())
2777 || (t0 != null && t0.isAlive());
2783 * called to check if the service discovery process completed successfully.
2787 protected void JalviewServicesChanged(PropertyChangeEvent evt)
2789 if (evt.getNewValue() == null || evt.getNewValue() instanceof Vector)
2791 final String ermsg = jalview.ws.jws2.Jws2Discoverer.getDiscoverer()
2792 .getErrorMessages();
2795 if (Cache.getDefault("SHOW_WSDISCOVERY_ERRORS", true))
2797 if (serviceChangedDialog == null)
2799 // only run if we aren't already displaying one of these.
2800 addDialogThread(serviceChangedDialog = new Runnable()
2807 * JalviewDialog jd =new JalviewDialog() {
2809 * @Override protected void cancelPressed() { // TODO Auto-generated method stub
2811 * }@Override protected void okPressed() { // TODO Auto-generated method stub
2813 * }@Override protected void raiseClosed() { // TODO Auto-generated method stub
2815 * } }; jd.initDialogFrame(new JLabel("<html><table width=\"450\"><tr><td>" +
2817 * "<br/>It may be that you have invalid JABA URLs in your web service preferences,"
2818 * + " or mis-configured HTTP proxy settings.<br/>" +
2819 * "Check the <em>Connections</em> and <em>Web services</em> tab of the" +
2820 * " Tools->Preferences dialog box to change them.</td></tr></table></html>" ),
2821 * true, true, "Web Service Configuration Problem", 450, 400);
2823 * jd.waitForInput();
2825 JvOptionPane.showConfirmDialog(Desktop.desktop,
2826 new JLabel("<html><table width=\"450\"><tr><td>"
2827 + ermsg + "</td></tr></table>"
2828 + "<p>It may be that you have invalid JABA URLs<br/>in your web service preferences,"
2829 + "<br>or as a command-line argument, or mis-configured HTTP proxy settings.</p>"
2830 + "<p>Check the <em>Connections</em> and <em>Web services</em> tab<br/>of the"
2831 + " Tools->Preferences dialog box to change them.</p></html>"),
2832 "Web Service Configuration Problem",
2833 JvOptionPane.DEFAULT_OPTION,
2834 JvOptionPane.ERROR_MESSAGE);
2835 serviceChangedDialog = null;
2843 jalview.bin.Console.error(
2844 "Errors reported by JABA discovery service. Check web services preferences.\n"
2851 private Runnable serviceChangedDialog = null;
2854 * start a thread to open a URL in the configured browser. Pops up a warning
2855 * dialog to the user if there is an exception when calling out to the browser
2860 public static void showUrl(final String url)
2862 showUrl(url, Desktop.instance);
2866 * Like showUrl but allows progress handler to be specified
2870 * (null) or object implementing IProgressIndicator
2872 public static void showUrl(final String url,
2873 final IProgressIndicator progress)
2875 new Thread(new Runnable()
2882 if (progress != null)
2884 progress.setProgressBar(MessageManager
2885 .formatMessage("status.opening_params", new Object[]
2886 { url }), this.hashCode());
2888 jalview.util.BrowserLauncher.openURL(url);
2889 } catch (Exception ex)
2891 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
2893 .getString("label.web_browser_not_found_unix"),
2894 MessageManager.getString("label.web_browser_not_found"),
2895 JvOptionPane.WARNING_MESSAGE);
2897 ex.printStackTrace();
2899 if (progress != null)
2901 progress.setProgressBar(null, this.hashCode());
2907 public static WsParamSetManager wsparamManager = null;
2909 public static ParamManager getUserParameterStore()
2911 if (wsparamManager == null)
2913 wsparamManager = new WsParamSetManager();
2915 return wsparamManager;
2919 * static hyperlink handler proxy method for use by Jalview's internal windows
2923 public static void hyperlinkUpdate(HyperlinkEvent e)
2925 if (e.getEventType() == EventType.ACTIVATED)
2930 url = e.getURL().toString();
2931 Desktop.showUrl(url);
2932 } catch (Exception x)
2937 .error("Couldn't handle string " + url + " as a URL.");
2939 // ignore any exceptions due to dud links.
2946 * single thread that handles display of dialogs to user.
2948 ExecutorService dialogExecutor = Executors.newSingleThreadExecutor();
2951 * flag indicating if dialogExecutor should try to acquire a permit
2953 private volatile boolean dialogPause = true;
2958 private java.util.concurrent.Semaphore block = new Semaphore(0);
2960 private static groovy.ui.Console groovyConsole;
2963 * add another dialog thread to the queue
2967 public void addDialogThread(final Runnable prompter)
2969 dialogExecutor.submit(new Runnable()
2979 } catch (InterruptedException x)
2983 if (instance == null)
2989 SwingUtilities.invokeAndWait(prompter);
2990 } catch (Exception q)
2992 jalview.bin.Console.warn("Unexpected Exception in dialog thread.",
2999 public void startDialogQueue()
3001 // set the flag so we don't pause waiting for another permit and semaphore
3002 // the current task to begin
3003 dialogPause = false;
3008 * Outputs an image of the desktop to file in EPS format, after prompting the
3009 * user for choice of Text or Lineart character rendering (unless a preference
3010 * has been set). The file name is generated as
3013 * Jalview_snapshot_nnnnn.eps where nnnnn is the current timestamp in milliseconds
3017 protected void snapShotWindow_actionPerformed(ActionEvent e)
3019 // currently the menu option to do this is not shown
3022 int width = getWidth();
3023 int height = getHeight();
3025 "Jalview_snapshot_" + System.currentTimeMillis() + ".eps");
3026 ImageWriterI writer = new ImageWriterI()
3029 public void exportImage(Graphics g) throws Exception
3032 jalview.bin.Console.info("Successfully written snapshot to file "
3033 + of.getAbsolutePath());
3036 String title = "View of desktop";
3037 ImageExporter exporter = new ImageExporter(writer, null, TYPE.EPS,
3039 exporter.doExport(of, this, width, height, title);
3043 * Explode the views in the given SplitFrame into separate SplitFrame windows.
3044 * This respects (remembers) any previous 'exploded geometry' i.e. the size
3045 * and location last time the view was expanded (if any). However it does not
3046 * remember the split pane divider location - this is set to match the
3047 * 'exploding' frame.
3051 public void explodeViews(SplitFrame sf)
3053 AlignFrame oldTopFrame = (AlignFrame) sf.getTopFrame();
3054 AlignFrame oldBottomFrame = (AlignFrame) sf.getBottomFrame();
3055 List<? extends AlignmentViewPanel> topPanels = oldTopFrame
3057 List<? extends AlignmentViewPanel> bottomPanels = oldBottomFrame
3059 int viewCount = topPanels.size();
3066 * Processing in reverse order works, forwards order leaves the first panels not
3067 * visible. I don't know why!
3069 for (int i = viewCount - 1; i >= 0; i--)
3072 * Make new top and bottom frames. These take over the respective AlignmentPanel
3073 * objects, including their AlignmentViewports, so the cdna/protein
3074 * relationships between the viewports is carried over to the new split frames.
3076 * explodedGeometry holds the (x, y) position of the previously exploded
3077 * SplitFrame, and the (width, height) of the AlignFrame component
3079 AlignmentPanel topPanel = (AlignmentPanel) topPanels.get(i);
3080 AlignFrame newTopFrame = new AlignFrame(topPanel);
3081 newTopFrame.setSize(oldTopFrame.getSize());
3082 newTopFrame.setVisible(true);
3083 Rectangle geometry = ((AlignViewport) topPanel.getAlignViewport())
3084 .getExplodedGeometry();
3085 if (geometry != null)
3087 newTopFrame.setSize(geometry.getSize());
3090 AlignmentPanel bottomPanel = (AlignmentPanel) bottomPanels.get(i);
3091 AlignFrame newBottomFrame = new AlignFrame(bottomPanel);
3092 newBottomFrame.setSize(oldBottomFrame.getSize());
3093 newBottomFrame.setVisible(true);
3094 geometry = ((AlignViewport) bottomPanel.getAlignViewport())
3095 .getExplodedGeometry();
3096 if (geometry != null)
3098 newBottomFrame.setSize(geometry.getSize());
3101 topPanel.av.setGatherViewsHere(false);
3102 bottomPanel.av.setGatherViewsHere(false);
3103 JInternalFrame splitFrame = new SplitFrame(newTopFrame,
3105 if (geometry != null)
3107 splitFrame.setLocation(geometry.getLocation());
3109 Desktop.addInternalFrame(splitFrame, sf.getTitle(), -1, -1);
3113 * Clear references to the panels (now relocated in the new SplitFrames) before
3114 * closing the old SplitFrame.
3117 bottomPanels.clear();
3122 * Gather expanded split frames, sharing the same pairs of sequence set ids,
3123 * back into the given SplitFrame as additional views. Note that the gathered
3124 * frames may themselves have multiple views.
3128 public void gatherViews(GSplitFrame source)
3131 * special handling of explodedGeometry for a view within a SplitFrame: - it
3132 * holds the (x, y) position of the enclosing SplitFrame, and the (width,
3133 * height) of the AlignFrame component
3135 AlignFrame myTopFrame = (AlignFrame) source.getTopFrame();
3136 AlignFrame myBottomFrame = (AlignFrame) source.getBottomFrame();
3137 myTopFrame.viewport.setExplodedGeometry(new Rectangle(source.getX(),
3138 source.getY(), myTopFrame.getWidth(), myTopFrame.getHeight()));
3139 myBottomFrame.viewport
3140 .setExplodedGeometry(new Rectangle(source.getX(), source.getY(),
3141 myBottomFrame.getWidth(), myBottomFrame.getHeight()));
3142 myTopFrame.viewport.setGatherViewsHere(true);
3143 myBottomFrame.viewport.setGatherViewsHere(true);
3144 String topViewId = myTopFrame.viewport.getSequenceSetId();
3145 String bottomViewId = myBottomFrame.viewport.getSequenceSetId();
3147 JInternalFrame[] frames = desktop.getAllFrames();
3148 for (JInternalFrame frame : frames)
3150 if (frame instanceof SplitFrame && frame != source)
3152 SplitFrame sf = (SplitFrame) frame;
3153 AlignFrame topFrame = (AlignFrame) sf.getTopFrame();
3154 AlignFrame bottomFrame = (AlignFrame) sf.getBottomFrame();
3155 boolean gatherThis = false;
3156 for (int a = 0; a < topFrame.alignPanels.size(); a++)
3158 AlignmentPanel topPanel = topFrame.alignPanels.get(a);
3159 AlignmentPanel bottomPanel = bottomFrame.alignPanels.get(a);
3160 if (topViewId.equals(topPanel.av.getSequenceSetId())
3161 && bottomViewId.equals(bottomPanel.av.getSequenceSetId()))
3164 topPanel.av.setGatherViewsHere(false);
3165 bottomPanel.av.setGatherViewsHere(false);
3166 topPanel.av.setExplodedGeometry(
3167 new Rectangle(sf.getLocation(), topFrame.getSize()));
3168 bottomPanel.av.setExplodedGeometry(
3169 new Rectangle(sf.getLocation(), bottomFrame.getSize()));
3170 myTopFrame.addAlignmentPanel(topPanel, false);
3171 myBottomFrame.addAlignmentPanel(bottomPanel, false);
3177 topFrame.getAlignPanels().clear();
3178 bottomFrame.getAlignPanels().clear();
3185 * The dust settles...give focus to the tab we did this from.
3187 myTopFrame.setDisplayedView(myTopFrame.alignPanel);
3190 public static groovy.ui.Console getGroovyConsole()
3192 return groovyConsole;
3196 * handles the payload of a drag and drop event.
3198 * TODO refactor to desktop utilities class
3201 * - Data source strings extracted from the drop event
3203 * - protocol for each data source extracted from the drop event
3207 * - the payload from the drop event
3210 public static void transferFromDropTarget(List<Object> files,
3211 List<DataSourceType> protocols, DropTargetDropEvent evt,
3212 Transferable t) throws Exception
3215 DataFlavor uriListFlavor = new DataFlavor(
3216 "text/uri-list;class=java.lang.String"), urlFlavour = null;
3219 urlFlavour = new DataFlavor(
3220 "application/x-java-url; class=java.net.URL");
3221 } catch (ClassNotFoundException cfe)
3223 jalview.bin.Console.debug("Couldn't instantiate the URL dataflavor.",
3227 if (urlFlavour != null && t.isDataFlavorSupported(urlFlavour))
3232 java.net.URL url = (URL) t.getTransferData(urlFlavour);
3233 // nb: java 8 osx bug https://bugs.openjdk.java.net/browse/JDK-8156099
3234 // means url may be null.
3237 protocols.add(DataSourceType.URL);
3238 files.add(url.toString());
3239 jalview.bin.Console.debug("Drop handled as URL dataflavor "
3240 + files.get(files.size() - 1));
3245 if (Platform.isAMacAndNotJS())
3248 "Please ignore plist error - occurs due to problem with java 8 on OSX");
3251 } catch (Throwable ex)
3253 jalview.bin.Console.debug("URL drop handler failed.", ex);
3256 if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
3258 // Works on Windows and MacOSX
3259 jalview.bin.Console.debug("Drop handled as javaFileListFlavor");
3260 for (Object file : (List) t
3261 .getTransferData(DataFlavor.javaFileListFlavor))
3264 protocols.add(DataSourceType.FILE);
3269 // Unix like behaviour
3270 boolean added = false;
3272 if (t.isDataFlavorSupported(uriListFlavor))
3274 jalview.bin.Console.debug("Drop handled as uriListFlavor");
3275 // This is used by Unix drag system
3276 data = (String) t.getTransferData(uriListFlavor);
3280 // fallback to text: workaround - on OSX where there's a JVM bug
3282 .debug("standard URIListFlavor failed. Trying text");
3283 // try text fallback
3284 DataFlavor textDf = new DataFlavor(
3285 "text/plain;class=java.lang.String");
3286 if (t.isDataFlavorSupported(textDf))
3288 data = (String) t.getTransferData(textDf);
3291 jalview.bin.Console.debug("Plain text drop content returned "
3292 + (data == null ? "Null - failed" : data));
3297 while (protocols.size() < files.size())
3299 jalview.bin.Console.debug("Adding missing FILE protocol for "
3300 + files.get(protocols.size()));
3301 protocols.add(DataSourceType.FILE);
3303 for (java.util.StringTokenizer st = new java.util.StringTokenizer(
3304 data, "\r\n"); st.hasMoreTokens();)
3307 String s = st.nextToken();
3308 if (s.startsWith("#"))
3310 // the line is a comment (as per the RFC 2483)
3313 java.net.URI uri = new java.net.URI(s);
3314 if (uri.getScheme().toLowerCase(Locale.ROOT).startsWith("http"))
3316 protocols.add(DataSourceType.URL);
3317 files.add(uri.toString());
3321 // otherwise preserve old behaviour: catch all for file objects
3322 java.io.File file = new java.io.File(uri);
3323 protocols.add(DataSourceType.FILE);
3324 files.add(file.toString());
3329 if (jalview.bin.Console.isDebugEnabled())
3331 if (data == null || !added)
3334 if (t.getTransferDataFlavors() != null
3335 && t.getTransferDataFlavors().length > 0)
3337 jalview.bin.Console.debug(
3338 "Couldn't resolve drop data. Here are the supported flavors:");
3339 for (DataFlavor fl : t.getTransferDataFlavors())
3341 jalview.bin.Console.debug(
3342 "Supported transfer dataflavor: " + fl.toString());
3343 Object df = t.getTransferData(fl);
3346 jalview.bin.Console.debug("Retrieves: " + df);
3350 jalview.bin.Console.debug("Retrieved nothing");
3357 .debug("Couldn't resolve dataflavor for drop: "
3363 if (Platform.isWindowsAndNotJS())
3366 .debug("Scanning dropped content for Windows Link Files");
3368 // resolve any .lnk files in the file drop
3369 for (int f = 0; f < files.size(); f++)
3371 String source = files.get(f).toString().toLowerCase(Locale.ROOT);
3372 if (protocols.get(f).equals(DataSourceType.FILE)
3373 && (source.endsWith(".lnk") || source.endsWith(".url")
3374 || source.endsWith(".site")))
3378 Object obj = files.get(f);
3379 File lf = (obj instanceof File ? (File) obj
3380 : new File((String) obj));
3381 // process link file to get a URL
3382 jalview.bin.Console.debug("Found potential link file: " + lf);
3383 WindowsShortcut wscfile = new WindowsShortcut(lf);
3384 String fullname = wscfile.getRealFilename();
3385 protocols.set(f, FormatAdapter.checkProtocol(fullname));
3386 files.set(f, fullname);
3387 jalview.bin.Console.debug("Parsed real filename " + fullname
3388 + " to extract protocol: " + protocols.get(f));
3389 } catch (Exception ex)
3391 jalview.bin.Console.error(
3392 "Couldn't parse " + files.get(f) + " as a link file.",
3401 * Sets the Preferences property for experimental features to True or False
3402 * depending on the state of the controlling menu item
3405 protected void showExperimental_actionPerformed(boolean selected)
3407 Cache.setProperty(EXPERIMENTAL_FEATURES, Boolean.toString(selected));
3411 * Answers a (possibly empty) list of any structure viewer frames (currently
3412 * for either Jmol or Chimera) which are currently open. This may optionally
3413 * be restricted to viewers of a specified class, or viewers linked to a
3414 * specified alignment panel.
3417 * if not null, only return viewers linked to this panel
3418 * @param structureViewerClass
3419 * if not null, only return viewers of this class
3422 public List<StructureViewerBase> getStructureViewers(
3423 AlignmentPanel apanel,
3424 Class<? extends StructureViewerBase> structureViewerClass)
3426 List<StructureViewerBase> result = new ArrayList<>();
3427 JInternalFrame[] frames = Desktop.instance.getAllFrames();
3429 for (JInternalFrame frame : frames)
3431 if (frame instanceof StructureViewerBase)
3433 if (structureViewerClass == null
3434 || structureViewerClass.isInstance(frame))
3437 || ((StructureViewerBase) frame).isLinkedWith(apanel))
3439 result.add((StructureViewerBase) frame);
3447 public static final String debugScaleMessage = "Desktop graphics transform scale=";
3449 private static boolean debugScaleMessageDone = false;
3451 public static void debugScaleMessage(Graphics g)
3453 if (debugScaleMessageDone)
3457 // output used by tests to check HiDPI scaling settings in action
3460 Graphics2D gg = (Graphics2D) g;
3463 AffineTransform t = gg.getTransform();
3464 double scaleX = t.getScaleX();
3465 double scaleY = t.getScaleY();
3466 jalview.bin.Console.debug(debugScaleMessage + scaleX + " (X)");
3467 jalview.bin.Console.debug(debugScaleMessage + scaleY + " (Y)");
3468 debugScaleMessageDone = true;
3472 jalview.bin.Console.debug("Desktop graphics null");
3474 } catch (Exception e)
3476 jalview.bin.Console.debug(Cache.getStackTraceString(e));