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.WindowConstants;
94 import javax.swing.event.HyperlinkEvent;
95 import javax.swing.event.HyperlinkEvent.EventType;
96 import javax.swing.event.InternalFrameAdapter;
97 import javax.swing.event.InternalFrameEvent;
99 import org.stackoverflowusers.file.WindowsShortcut;
101 import jalview.api.AlignViewportI;
102 import jalview.api.AlignmentViewPanel;
103 import jalview.bin.Cache;
104 import jalview.bin.Jalview;
105 import jalview.gui.ImageExporter.ImageWriterI;
106 import jalview.io.BackupFiles;
107 import jalview.io.DataSourceType;
108 import jalview.io.FileFormat;
109 import jalview.io.FileFormatException;
110 import jalview.io.FileFormatI;
111 import jalview.io.FileFormats;
112 import jalview.io.FileLoader;
113 import jalview.io.FormatAdapter;
114 import jalview.io.IdentifyFile;
115 import jalview.io.JalviewFileChooser;
116 import jalview.io.JalviewFileView;
117 import jalview.jbgui.GSplitFrame;
118 import jalview.jbgui.GStructureViewer;
119 import jalview.jbgui.QuitHandler;
120 import jalview.jbgui.QuitHandler.QResponse;
121 import jalview.project.Jalview2XML;
122 import jalview.structure.StructureSelectionManager;
123 import jalview.urls.IdOrgSettings;
124 import jalview.util.BrowserLauncher;
125 import jalview.util.ChannelProperties;
126 import jalview.util.ImageMaker.TYPE;
127 import jalview.util.LaunchUtils;
128 import jalview.util.MessageManager;
129 import jalview.util.Platform;
130 import jalview.util.ShortcutKeyMaskExWrapper;
131 import jalview.util.UrlConstants;
132 import jalview.viewmodel.AlignmentViewport;
133 import jalview.ws.params.ParamManager;
134 import jalview.ws.utils.UrlDownloadClient;
141 * @version $Revision: 1.155 $
143 public class Desktop extends jalview.jbgui.GDesktop
144 implements DropTargetListener, ClipboardOwner, IProgressIndicator,
145 jalview.api.StructureSelectionManagerProvider
147 private static final String CITATION;
150 URL bg_logo_url = ChannelProperties.getImageURL(
151 "bg_logo." + String.valueOf(SplashScreen.logoSize));
152 URL uod_logo_url = ChannelProperties.getImageURL(
153 "uod_banner." + String.valueOf(SplashScreen.logoSize));
154 boolean logo = (bg_logo_url != null || uod_logo_url != null);
155 StringBuilder sb = new StringBuilder();
157 "<br><br>Jalview is free software released under GPLv3.<br><br>Development is managed by The Barton Group, University of Dundee, Scotland, UK.");
162 sb.append(bg_logo_url == null ? ""
163 : "<img alt=\"Barton Group logo\" src=\""
164 + bg_logo_url.toString() + "\">");
165 sb.append(uod_logo_url == null ? ""
166 : " <img alt=\"University of Dundee shield\" src=\""
167 + uod_logo_url.toString() + "\">");
169 "<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>");
170 sb.append("<br><br>If you use Jalview, please cite:"
171 + "<br>Waterhouse, A.M., Procter, J.B., Martin, D.M.A, Clamp, M. and Barton, G. J. (2009)"
172 + "<br>Jalview Version 2 - a multiple sequence alignment editor and analysis workbench"
173 + "<br>Bioinformatics <a href=\"https://doi.org/10.1093/bioinformatics/btp033\">doi: 10.1093/bioinformatics/btp033</a>");
174 CITATION = sb.toString();
177 private static final String DEFAULT_AUTHORS = "The Jalview Authors (See AUTHORS file for current list)";
179 private static int DEFAULT_MIN_WIDTH = 300;
181 private static int DEFAULT_MIN_HEIGHT = 250;
183 private static int ALIGN_FRAME_DEFAULT_MIN_WIDTH = 600;
185 private static int ALIGN_FRAME_DEFAULT_MIN_HEIGHT = 70;
187 private static final String EXPERIMENTAL_FEATURES = "EXPERIMENTAL_FEATURES";
189 public static final String CONFIRM_KEYBOARD_QUIT = "CONFIRM_KEYBOARD_QUIT";
191 public static HashMap<String, FileWriter> savingFiles = new HashMap<String, FileWriter>();
193 private JalviewChangeSupport changeSupport = new JalviewChangeSupport();
195 public static boolean nosplash = false;
198 * news reader - null if it was never started.
200 private BlogReader jvnews = null;
202 private File projectFile;
206 * @see jalview.gui.JalviewChangeSupport#addJalviewPropertyChangeListener(java.beans.PropertyChangeListener)
208 public void addJalviewPropertyChangeListener(
209 PropertyChangeListener listener)
211 changeSupport.addJalviewPropertyChangeListener(listener);
215 * @param propertyName
217 * @see jalview.gui.JalviewChangeSupport#addJalviewPropertyChangeListener(java.lang.String,
218 * java.beans.PropertyChangeListener)
220 public void addJalviewPropertyChangeListener(String propertyName,
221 PropertyChangeListener listener)
223 changeSupport.addJalviewPropertyChangeListener(propertyName, listener);
227 * @param propertyName
229 * @see jalview.gui.JalviewChangeSupport#removeJalviewPropertyChangeListener(java.lang.String,
230 * java.beans.PropertyChangeListener)
232 public void removeJalviewPropertyChangeListener(String propertyName,
233 PropertyChangeListener listener)
235 changeSupport.removeJalviewPropertyChangeListener(propertyName,
239 /** Singleton Desktop instance */
240 public static Desktop instance;
242 public static MyDesktopPane desktop;
244 public static MyDesktopPane getDesktop()
246 // BH 2018 could use currentThread() here as a reference to a
247 // Hashtable<Thread, MyDesktopPane> in JavaScript
251 static int openFrameCount = 0;
253 static final int xOffset = 30;
255 static final int yOffset = 30;
257 public static jalview.ws.jws1.Discoverer discoverer;
259 public static Object[] jalviewClipboard;
261 public static boolean internalCopy = false;
263 static int fileLoadingCount = 0;
265 class MyDesktopManager implements DesktopManager
268 private DesktopManager delegate;
270 public MyDesktopManager(DesktopManager delegate)
272 this.delegate = delegate;
276 public void activateFrame(JInternalFrame f)
280 delegate.activateFrame(f);
281 } catch (NullPointerException npe)
283 Point p = getMousePosition();
284 instance.showPasteMenu(p.x, p.y);
289 public void beginDraggingFrame(JComponent f)
291 delegate.beginDraggingFrame(f);
295 public void beginResizingFrame(JComponent f, int direction)
297 delegate.beginResizingFrame(f, direction);
301 public void closeFrame(JInternalFrame f)
303 delegate.closeFrame(f);
307 public void deactivateFrame(JInternalFrame f)
309 delegate.deactivateFrame(f);
313 public void deiconifyFrame(JInternalFrame f)
315 delegate.deiconifyFrame(f);
319 public void dragFrame(JComponent f, int newX, int newY)
325 delegate.dragFrame(f, newX, newY);
329 public void endDraggingFrame(JComponent f)
331 delegate.endDraggingFrame(f);
336 public void endResizingFrame(JComponent f)
338 delegate.endResizingFrame(f);
343 public void iconifyFrame(JInternalFrame f)
345 delegate.iconifyFrame(f);
349 public void maximizeFrame(JInternalFrame f)
351 delegate.maximizeFrame(f);
355 public void minimizeFrame(JInternalFrame f)
357 delegate.minimizeFrame(f);
361 public void openFrame(JInternalFrame f)
363 delegate.openFrame(f);
367 public void resizeFrame(JComponent f, int newX, int newY, int newWidth,
374 delegate.resizeFrame(f, newX, newY, newWidth, newHeight);
378 public void setBoundsForFrame(JComponent f, int newX, int newY,
379 int newWidth, int newHeight)
381 delegate.setBoundsForFrame(f, newX, newY, newWidth, newHeight);
384 // All other methods, simply delegate
389 * Creates a new Desktop object.
395 * A note to implementors. It is ESSENTIAL that any activities that might
396 * block are spawned off as threads rather than waited for during this
401 doConfigureStructurePrefs();
402 setTitle(ChannelProperties.getProperty("app_name") + " "
403 + Cache.getProperty("VERSION"));
406 * Set taskbar "grouped windows" name for linux desktops (works in GNOME and
407 * KDE). This uses sun.awt.X11.XToolkit.awtAppClassName which is not
408 * officially documented or guaranteed to exist, so we access it via
409 * reflection. There appear to be unfathomable criteria about what this
410 * string can contain, and it if doesn't meet those criteria then "java"
411 * (KDE) or "jalview-bin-Jalview" (GNOME) is used. "Jalview", "Jalview
412 * Develop" and "Jalview Test" seem okay, but "Jalview non-release" does
413 * not. The reflection access may generate a warning: WARNING: An illegal
414 * reflective access operation has occurred WARNING: Illegal reflective
415 * access by jalview.gui.Desktop () to field
416 * sun.awt.X11.XToolkit.awtAppClassName which I don't think can be avoided.
418 if (Platform.isLinux())
420 if (LaunchUtils.getJavaVersion() >= 11)
422 jalview.bin.Console.info(
423 "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.");
427 Toolkit xToolkit = Toolkit.getDefaultToolkit();
428 Field[] declaredFields = xToolkit.getClass().getDeclaredFields();
429 Field awtAppClassNameField = null;
431 if (Arrays.stream(declaredFields)
432 .anyMatch(f -> f.getName().equals("awtAppClassName")))
434 awtAppClassNameField = xToolkit.getClass()
435 .getDeclaredField("awtAppClassName");
438 String title = ChannelProperties.getProperty("app_name");
439 if (awtAppClassNameField != null)
441 awtAppClassNameField.setAccessible(true);
442 awtAppClassNameField.set(xToolkit, title);
446 jalview.bin.Console.debug("XToolkit: awtAppClassName not found");
448 } catch (Exception e)
450 jalview.bin.Console.debug("Error setting awtAppClassName");
451 jalview.bin.Console.trace(Cache.getStackTraceString(e));
455 setIconImages(ChannelProperties.getIconList());
457 this.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
458 addWindowListener(new WindowAdapter()
461 public void windowClosing(WindowEvent ev)
463 QResponse qresponse = desktopQuit();
464 if (qresponse != QResponse.CANCEL_QUIT)
471 boolean selmemusage = Cache.getDefault("SHOW_MEMUSAGE", false);
473 boolean showjconsole = Cache.getDefault("SHOW_JAVA_CONSOLE", false);
474 desktop = new MyDesktopPane(selmemusage);
476 showMemusage.setSelected(selmemusage);
477 desktop.setBackground(Color.white);
479 getContentPane().setLayout(new BorderLayout());
480 // alternate config - have scrollbars - see notes in JAL-153
481 // JScrollPane sp = new JScrollPane();
482 // sp.getViewport().setView(desktop);
483 // getContentPane().add(sp, BorderLayout.CENTER);
485 // BH 2018 - just an experiment to try unclipped JInternalFrames.
488 getRootPane().putClientProperty("swingjs.overflow.hidden", "false");
491 getContentPane().add(desktop, BorderLayout.CENTER);
492 desktop.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
494 // This line prevents Windows Look&Feel resizing all new windows to maximum
495 // if previous window was maximised
496 desktop.setDesktopManager(new MyDesktopManager(
497 (Platform.isWindowsAndNotJS() ? new DefaultDesktopManager()
498 : Platform.isAMacAndNotJS()
499 ? new AquaInternalFrameManager(
500 desktop.getDesktopManager())
501 : desktop.getDesktopManager())));
503 Rectangle dims = getLastKnownDimensions("");
510 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
511 int xPos = Math.max(5, (screenSize.width - 900) / 2);
512 int yPos = Math.max(5, (screenSize.height - 650) / 2);
513 setBounds(xPos, yPos, 900, 650);
516 if (!Platform.isJS())
523 jconsole = new Console(this, showjconsole);
524 jconsole.setHeader(Cache.getVersionDetailsForConsole());
525 showConsole(showjconsole);
527 showNews.setVisible(false);
529 experimentalFeatures.setSelected(showExperimental());
531 getIdentifiersOrgData();
535 // Spawn a thread that shows the splashscreen
538 SwingUtilities.invokeLater(new Runnable()
543 new SplashScreen(true);
548 // Thread off a new instance of the file chooser - this reduces the time
550 // takes to open it later on.
551 new Thread(new Runnable()
556 jalview.bin.Console.debug("Filechooser init thread started.");
557 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
558 JalviewFileChooser.forRead(Cache.getProperty("LAST_DIRECTORY"),
560 jalview.bin.Console.debug("Filechooser init thread finished.");
563 // Add the service change listener
564 changeSupport.addJalviewPropertyChangeListener("services",
565 new PropertyChangeListener()
569 public void propertyChange(PropertyChangeEvent evt)
572 .debug("Firing service changed event for "
573 + evt.getNewValue());
574 JalviewServicesChanged(evt);
579 this.setDropTarget(new java.awt.dnd.DropTarget(desktop, this));
582 this.addMouseListener(ma = new MouseAdapter()
585 public void mousePressed(MouseEvent evt)
587 if (evt.isPopupTrigger()) // Mac
589 showPasteMenu(evt.getX(), evt.getY());
594 public void mouseReleased(MouseEvent evt)
596 if (evt.isPopupTrigger()) // Windows
598 showPasteMenu(evt.getX(), evt.getY());
602 desktop.addMouseListener(ma);
606 * Answers true if user preferences to enable experimental features is True
611 public boolean showExperimental()
613 String experimental = Cache.getDefault(EXPERIMENTAL_FEATURES,
614 Boolean.FALSE.toString());
615 return Boolean.valueOf(experimental).booleanValue();
618 public void doConfigureStructurePrefs()
620 // configure services
621 StructureSelectionManager ssm = StructureSelectionManager
622 .getStructureSelectionManager(this);
623 if (Cache.getDefault(Preferences.ADD_SS_ANN, true))
625 ssm.setAddTempFacAnnot(
626 Cache.getDefault(Preferences.ADD_TEMPFACT_ANN, true));
627 ssm.setProcessSecondaryStructure(
628 Cache.getDefault(Preferences.STRUCT_FROM_PDB, true));
629 // JAL-3915 - RNAView is no longer an option so this has no effect
630 ssm.setSecStructServices(
631 Cache.getDefault(Preferences.USE_RNAVIEW, false));
635 ssm.setAddTempFacAnnot(false);
636 ssm.setProcessSecondaryStructure(false);
637 ssm.setSecStructServices(false);
641 public void checkForNews()
643 final Desktop me = this;
644 // Thread off the news reader, in case there are connection problems.
645 new Thread(new Runnable()
650 jalview.bin.Console.debug("Starting news thread.");
651 jvnews = new BlogReader(me);
652 showNews.setVisible(true);
653 jalview.bin.Console.debug("Completed news thread.");
658 public void getIdentifiersOrgData()
660 if (Cache.getProperty("NOIDENTIFIERSSERVICE") == null)
661 {// Thread off the identifiers fetcher
662 new Thread(new Runnable()
668 .debug("Downloading data from identifiers.org");
671 UrlDownloadClient.download(IdOrgSettings.getUrl(),
672 IdOrgSettings.getDownloadLocation());
673 } catch (IOException e)
676 .debug("Exception downloading identifiers.org data"
686 protected void showNews_actionPerformed(ActionEvent e)
688 showNews(showNews.isSelected());
691 void showNews(boolean visible)
693 jalview.bin.Console.debug((visible ? "Showing" : "Hiding") + " news.");
694 showNews.setSelected(visible);
695 if (visible && !jvnews.isVisible())
697 new Thread(new Runnable()
702 long now = System.currentTimeMillis();
703 Desktop.instance.setProgressBar(
704 MessageManager.getString("status.refreshing_news"), now);
705 jvnews.refreshNews();
706 Desktop.instance.setProgressBar(null, now);
714 * recover the last known dimensions for a jalview window
717 * - empty string is desktop, all other windows have unique prefix
718 * @return null or last known dimensions scaled to current geometry (if last
719 * window geom was known)
721 Rectangle getLastKnownDimensions(String windowName)
723 // TODO: lock aspect ratio for scaling desktop Bug #0058199
724 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
725 String x = Cache.getProperty(windowName + "SCREEN_X");
726 String y = Cache.getProperty(windowName + "SCREEN_Y");
727 String width = Cache.getProperty(windowName + "SCREEN_WIDTH");
728 String height = Cache.getProperty(windowName + "SCREEN_HEIGHT");
729 if ((x != null) && (y != null) && (width != null) && (height != null))
731 int ix = Integer.parseInt(x), iy = Integer.parseInt(y),
732 iw = Integer.parseInt(width), ih = Integer.parseInt(height);
733 if (Cache.getProperty("SCREENGEOMETRY_WIDTH") != null)
735 // attempt #1 - try to cope with change in screen geometry - this
736 // version doesn't preserve original jv aspect ratio.
737 // take ratio of current screen size vs original screen size.
738 double sw = ((1f * screenSize.width) / (1f * Integer
739 .parseInt(Cache.getProperty("SCREENGEOMETRY_WIDTH"))));
740 double sh = ((1f * screenSize.height) / (1f * Integer
741 .parseInt(Cache.getProperty("SCREENGEOMETRY_HEIGHT"))));
742 // rescale the bounds depending upon the current screen geometry.
743 ix = (int) (ix * sw);
744 iw = (int) (iw * sw);
745 iy = (int) (iy * sh);
746 ih = (int) (ih * sh);
747 while (ix >= screenSize.width)
749 jalview.bin.Console.debug(
750 "Window geometry location recall error: shifting horizontal to within screenbounds.");
751 ix -= screenSize.width;
753 while (iy >= screenSize.height)
755 jalview.bin.Console.debug(
756 "Window geometry location recall error: shifting vertical to within screenbounds.");
757 iy -= screenSize.height;
759 jalview.bin.Console.debug(
760 "Got last known dimensions for " + windowName + ": x:" + ix
761 + " y:" + iy + " width:" + iw + " height:" + ih);
763 // return dimensions for new instance
764 return new Rectangle(ix, iy, iw, ih);
769 void showPasteMenu(int x, int y)
771 JPopupMenu popup = new JPopupMenu();
772 JMenuItem item = new JMenuItem(
773 MessageManager.getString("label.paste_new_window"));
774 item.addActionListener(new ActionListener()
777 public void actionPerformed(ActionEvent evt)
784 popup.show(this, x, y);
791 Clipboard c = Toolkit.getDefaultToolkit().getSystemClipboard();
792 Transferable contents = c.getContents(this);
794 if (contents != null)
796 String file = (String) contents
797 .getTransferData(DataFlavor.stringFlavor);
799 FileFormatI format = new IdentifyFile().identify(file,
800 DataSourceType.PASTE);
802 new FileLoader().LoadFile(file, DataSourceType.PASTE, format);
805 } catch (Exception ex)
808 "Unable to paste alignment from system clipboard:\n" + ex);
813 * Adds and opens the given frame to the desktop
824 public static synchronized void addInternalFrame(
825 final JInternalFrame frame, String title, int w, int h)
827 addInternalFrame(frame, title, true, w, h, true, false);
831 * Add an internal frame to the Jalview desktop
838 * When true, display frame immediately, otherwise, caller must call
839 * setVisible themselves.
845 public static synchronized void addInternalFrame(
846 final JInternalFrame frame, String title, boolean makeVisible,
849 addInternalFrame(frame, title, makeVisible, w, h, true, false);
853 * Add an internal frame to the Jalview desktop and make it visible
866 public static synchronized void addInternalFrame(
867 final JInternalFrame frame, String title, int w, int h,
870 addInternalFrame(frame, title, true, w, h, resizable, false);
874 * Add an internal frame to the Jalview desktop
881 * When true, display frame immediately, otherwise, caller must call
882 * setVisible themselves.
889 * @param ignoreMinSize
890 * Do not set the default minimum size for frame
892 public static synchronized void addInternalFrame(
893 final JInternalFrame frame, String title, boolean makeVisible,
894 int w, int h, boolean resizable, boolean ignoreMinSize)
897 // TODO: allow callers to determine X and Y position of frame (eg. via
899 // TODO: consider fixing method to update entries in the window submenu with
900 // the current window title
902 frame.setTitle(title);
903 if (frame.getWidth() < 1 || frame.getHeight() < 1)
907 // THIS IS A PUBLIC STATIC METHOD, SO IT MAY BE CALLED EVEN IN
908 // A HEADLESS STATE WHEN NO DESKTOP EXISTS. MUST RETURN
909 // IF JALVIEW IS RUNNING HEADLESS
910 // ///////////////////////////////////////////////
911 if (instance == null || (System.getProperty("java.awt.headless") != null
912 && System.getProperty("java.awt.headless").equals("true")))
921 frame.setMinimumSize(
922 new Dimension(DEFAULT_MIN_WIDTH, DEFAULT_MIN_HEIGHT));
924 // Set default dimension for Alignment Frame window.
925 // The Alignment Frame window could be added from a number of places,
927 // I did this here in order not to miss out on any Alignment frame.
928 if (frame instanceof AlignFrame)
930 frame.setMinimumSize(new Dimension(ALIGN_FRAME_DEFAULT_MIN_WIDTH,
931 ALIGN_FRAME_DEFAULT_MIN_HEIGHT));
935 frame.setVisible(makeVisible);
936 frame.setClosable(true);
937 frame.setResizable(resizable);
938 frame.setMaximizable(resizable);
939 frame.setIconifiable(resizable);
940 frame.setOpaque(Platform.isJS());
942 if (frame.getX() < 1 && frame.getY() < 1)
944 frame.setLocation(xOffset * openFrameCount,
945 yOffset * ((openFrameCount - 1) % 10) + yOffset);
949 * add an entry for the new frame in the Window menu (and remove it when the
952 final JMenuItem menuItem = new JMenuItem(title);
953 frame.addInternalFrameListener(new InternalFrameAdapter()
956 public void internalFrameActivated(InternalFrameEvent evt)
958 JInternalFrame itf = desktop.getSelectedFrame();
961 if (itf instanceof AlignFrame)
963 Jalview.setCurrentAlignFrame((AlignFrame) itf);
970 public void internalFrameClosed(InternalFrameEvent evt)
972 PaintRefresher.RemoveComponent(frame);
975 * defensive check to prevent frames being added half off the window
977 if (openFrameCount > 0)
983 * ensure no reference to alignFrame retained by menu item listener
985 if (menuItem.getActionListeners().length > 0)
987 menuItem.removeActionListener(menuItem.getActionListeners()[0]);
989 windowMenu.remove(menuItem);
993 menuItem.addActionListener(new ActionListener()
996 public void actionPerformed(ActionEvent e)
1000 frame.setSelected(true);
1001 frame.setIcon(false);
1002 } catch (java.beans.PropertyVetoException ex)
1009 setKeyBindings(frame);
1013 windowMenu.add(menuItem);
1018 frame.setSelected(true);
1019 frame.requestFocus();
1020 } catch (java.beans.PropertyVetoException ve)
1022 } catch (java.lang.ClassCastException cex)
1024 jalview.bin.Console.warn(
1025 "Squashed a possible GUI implementation error. If you can recreate this, please look at https://issues.jalview.org/browse/JAL-869",
1031 * Add key bindings to a JInternalFrame so that Ctrl-W and Cmd-W will close
1036 private static void setKeyBindings(JInternalFrame frame)
1038 @SuppressWarnings("serial")
1039 final Action closeAction = new AbstractAction()
1042 public void actionPerformed(ActionEvent e)
1049 * set up key bindings for Ctrl-W and Cmd-W, with the same (Close) action
1051 KeyStroke ctrlWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
1052 InputEvent.CTRL_DOWN_MASK);
1053 KeyStroke cmdWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
1054 ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx());
1056 InputMap inputMap = frame
1057 .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
1058 String ctrlW = ctrlWKey.toString();
1059 inputMap.put(ctrlWKey, ctrlW);
1060 inputMap.put(cmdWKey, ctrlW);
1062 ActionMap actionMap = frame.getActionMap();
1063 actionMap.put(ctrlW, closeAction);
1067 public void lostOwnership(Clipboard clipboard, Transferable contents)
1071 Desktop.jalviewClipboard = null;
1074 internalCopy = false;
1078 public void dragEnter(DropTargetDragEvent evt)
1083 public void dragExit(DropTargetEvent evt)
1088 public void dragOver(DropTargetDragEvent evt)
1093 public void dropActionChanged(DropTargetDragEvent evt)
1104 public void drop(DropTargetDropEvent evt)
1106 boolean success = true;
1107 // JAL-1552 - acceptDrop required before getTransferable call for
1108 // Java's Transferable for native dnd
1109 evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
1110 Transferable t = evt.getTransferable();
1111 List<Object> files = new ArrayList<>();
1112 List<DataSourceType> protocols = new ArrayList<>();
1116 Desktop.transferFromDropTarget(files, protocols, evt, t);
1117 } catch (Exception e)
1119 e.printStackTrace();
1127 for (int i = 0; i < files.size(); i++)
1129 // BH 2018 File or String
1130 Object file = files.get(i);
1131 String fileName = file.toString();
1132 DataSourceType protocol = (protocols == null)
1133 ? DataSourceType.FILE
1135 FileFormatI format = null;
1137 if (fileName.endsWith(".jar"))
1139 format = FileFormat.Jalview;
1144 format = new IdentifyFile().identify(file, protocol);
1146 if (file instanceof File)
1148 Platform.cacheFileData((File) file);
1150 new FileLoader().LoadFile(null, file, protocol, format);
1153 } catch (Exception ex)
1158 evt.dropComplete(success); // need this to ensure input focus is properly
1159 // transfered to any new windows created
1169 public void inputLocalFileMenuItem_actionPerformed(AlignViewport viewport)
1171 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
1172 JalviewFileChooser chooser = JalviewFileChooser.forRead(
1173 Cache.getProperty("LAST_DIRECTORY"), fileFormat,
1174 BackupFiles.getEnabled());
1176 chooser.setFileView(new JalviewFileView());
1177 chooser.setDialogTitle(
1178 MessageManager.getString("label.open_local_file"));
1179 chooser.setToolTipText(MessageManager.getString("action.open"));
1181 chooser.setResponseHandler(0, new Runnable()
1186 File selectedFile = chooser.getSelectedFile();
1187 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1189 FileFormatI format = chooser.getSelectedFormat();
1192 * Call IdentifyFile to verify the file contains what its extension implies.
1193 * Skip this step for dynamically added file formats, because IdentifyFile does
1194 * not know how to recognise them.
1196 if (FileFormats.getInstance().isIdentifiable(format))
1200 format = new IdentifyFile().identify(selectedFile,
1201 DataSourceType.FILE);
1202 } catch (FileFormatException e)
1204 // format = null; //??
1208 new FileLoader().LoadFile(viewport, selectedFile,
1209 DataSourceType.FILE, format);
1212 chooser.showOpenDialog(this);
1216 * Shows a dialog for input of a URL at which to retrieve alignment data
1221 public void inputURLMenuItem_actionPerformed(AlignViewport viewport)
1223 // This construct allows us to have a wider textfield
1225 JLabel label = new JLabel(
1226 MessageManager.getString("label.input_file_url"));
1228 JPanel panel = new JPanel(new GridLayout(2, 1));
1232 * the URL to fetch is input in Java: an editable combobox with history JS:
1233 * (pending JAL-3038) a plain text field
1236 String urlBase = "https://www.";
1237 if (Platform.isJS())
1239 history = new JTextField(urlBase, 35);
1248 JComboBox<String> asCombo = new JComboBox<>();
1249 asCombo.setPreferredSize(new Dimension(400, 20));
1250 asCombo.setEditable(true);
1251 asCombo.addItem(urlBase);
1252 String historyItems = Cache.getProperty("RECENT_URL");
1253 if (historyItems != null)
1255 for (String token : historyItems.split("\\t"))
1257 asCombo.addItem(token);
1264 Object[] options = new Object[] { MessageManager.getString("action.ok"),
1265 MessageManager.getString("action.cancel") };
1266 Runnable action = new Runnable()
1271 @SuppressWarnings("unchecked")
1272 String url = (history instanceof JTextField
1273 ? ((JTextField) history).getText()
1274 : ((JComboBox<String>) history).getEditor().getItem()
1275 .toString().trim());
1277 if (url.toLowerCase(Locale.ROOT).endsWith(".jar"))
1279 if (viewport != null)
1281 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1282 FileFormat.Jalview);
1286 new FileLoader().LoadFile(url, DataSourceType.URL,
1287 FileFormat.Jalview);
1292 FileFormatI format = null;
1295 format = new IdentifyFile().identify(url, DataSourceType.URL);
1296 } catch (FileFormatException e)
1298 // TODO revise error handling, distinguish between
1299 // URL not found and response not valid
1304 String msg = MessageManager
1305 .formatMessage("label.couldnt_locate", url);
1306 JvOptionPane.showInternalMessageDialog(Desktop.desktop, msg,
1307 MessageManager.getString("label.url_not_found"),
1308 JvOptionPane.WARNING_MESSAGE);
1313 if (viewport != null)
1315 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1320 new FileLoader().LoadFile(url, DataSourceType.URL, format);
1325 String dialogOption = MessageManager
1326 .getString("label.input_alignment_from_url");
1327 JvOptionPane.newOptionDialog(desktop).setResponseHandler(0, action)
1328 .showInternalDialog(panel, dialogOption,
1329 JvOptionPane.YES_NO_CANCEL_OPTION,
1330 JvOptionPane.PLAIN_MESSAGE, null, options,
1331 MessageManager.getString("action.ok"));
1335 * Opens the CutAndPaste window for the user to paste an alignment in to
1338 * - if not null, the pasted alignment is added to the current
1339 * alignment; if null, to a new alignment window
1342 public void inputTextboxMenuItem_actionPerformed(
1343 AlignmentViewPanel viewPanel)
1345 CutAndPasteTransfer cap = new CutAndPasteTransfer();
1346 cap.setForInput(viewPanel);
1347 Desktop.addInternalFrame(cap,
1348 MessageManager.getString("label.cut_paste_alignmen_file"), true,
1353 * Check with user and saving files before actually quitting
1355 public QResponse desktopQuit()
1357 return desktopQuit(true);
1360 public QResponse desktopQuit(boolean ui)
1362 QuitHandler.QResponse qresponse = QuitHandler.getQuitResponse(ui);
1364 if (qresponse == QResponse.CANCEL_QUIT)
1369 Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
1370 Cache.setProperty("SCREENGEOMETRY_WIDTH", screen.width + "");
1371 Cache.setProperty("SCREENGEOMETRY_HEIGHT", screen.height + "");
1372 storeLastKnownDimensions("", new Rectangle(getBounds().x, getBounds().y,
1373 getWidth(), getHeight()));
1375 if (jconsole != null)
1377 storeLastKnownDimensions("JAVA_CONSOLE_", jconsole.getBounds());
1378 jconsole.stopConsole();
1382 storeLastKnownDimensions("JALVIEW_RSS_WINDOW_", jvnews.getBounds());
1385 if (dialogExecutor != null)
1387 dialogExecutor.shutdownNow();
1389 closeAll_actionPerformed(null);
1391 if (groovyConsole != null)
1393 // suppress a possible repeat prompt to save script
1394 groovyConsole.setDirty(false);
1395 groovyConsole.exit();
1398 if (qresponse == QResponse.FORCE_QUIT)
1400 // note that shutdown hook will not be run
1401 jalview.bin.Console.debug("Force Quit selected by user");
1402 Runtime.getRuntime().halt(0);
1405 jalview.bin.Console.debug("Quit selected by user");
1408 // unlikely to reach here!
1409 return QResponse.QUIT;
1413 * Don't call this directly, use desktopQuit() above. Exits the program.
1418 // this will run the shutdownHook but QuitHandler.getQuitResponse() should
1419 // not run a second time if gotQuitResponse flag has been set (i.e. user
1420 // confirmed quit of some kind).
1424 private void storeLastKnownDimensions(String string, Rectangle jc)
1426 jalview.bin.Console.debug("Storing last known dimensions for " + string
1427 + ": x:" + jc.x + " y:" + jc.y + " width:" + jc.width
1428 + " height:" + jc.height);
1430 Cache.setProperty(string + "SCREEN_X", jc.x + "");
1431 Cache.setProperty(string + "SCREEN_Y", jc.y + "");
1432 Cache.setProperty(string + "SCREEN_WIDTH", jc.width + "");
1433 Cache.setProperty(string + "SCREEN_HEIGHT", jc.height + "");
1443 public void aboutMenuItem_actionPerformed(ActionEvent e)
1445 new Thread(new Runnable()
1450 new SplashScreen(false);
1456 * Returns the html text for the About screen, including any available version
1457 * number, build details, author details and citation reference, but without
1458 * the enclosing {@code html} tags
1462 public String getAboutMessage()
1464 StringBuilder message = new StringBuilder(1024);
1465 message.append("<div style=\"font-family: sans-serif;\">")
1466 .append("<h1><strong>Version: ")
1467 .append(Cache.getProperty("VERSION")).append("</strong></h1>")
1468 .append("<strong>Built: <em>")
1469 .append(Cache.getDefault("BUILD_DATE", "unknown"))
1470 .append("</em> from ").append(Cache.getBuildDetailsForSplash())
1471 .append("</strong>");
1473 String latestVersion = Cache.getDefault("LATEST_VERSION", "Checking");
1474 if (latestVersion.equals("Checking"))
1476 // JBP removed this message for 2.11: May be reinstated in future version
1477 // message.append("<br>...Checking latest version...</br>");
1479 else if (!latestVersion.equals(Cache.getProperty("VERSION")))
1481 boolean red = false;
1482 if (Cache.getProperty("VERSION").toLowerCase(Locale.ROOT)
1483 .indexOf("automated build") == -1)
1486 // Displayed when code version and jnlp version do not match and code
1487 // version is not a development build
1488 message.append("<div style=\"color: #FF0000;font-style: bold;\">");
1491 message.append("<br>!! Version ")
1492 .append(Cache.getDefault("LATEST_VERSION", "..Checking.."))
1493 .append(" is available for download from ")
1494 .append(Cache.getDefault("www.jalview.org",
1495 "https://www.jalview.org"))
1499 message.append("</div>");
1502 message.append("<br>Authors: ");
1503 message.append(Cache.getDefault("AUTHORFNAMES", DEFAULT_AUTHORS));
1504 message.append(CITATION);
1506 message.append("</div>");
1508 return message.toString();
1512 * Action on requesting Help documentation
1515 public void documentationMenuItem_actionPerformed()
1519 if (Platform.isJS())
1521 BrowserLauncher.openURL("https://www.jalview.org/help.html");
1530 Help.showHelpWindow();
1532 } catch (Exception ex)
1534 System.err.println("Error opening help: " + ex.getMessage());
1539 public void closeAll_actionPerformed(ActionEvent e)
1541 // TODO show a progress bar while closing?
1542 JInternalFrame[] frames = desktop.getAllFrames();
1543 for (int i = 0; i < frames.length; i++)
1547 frames[i].setClosed(true);
1548 } catch (java.beans.PropertyVetoException ex)
1552 Jalview.setCurrentAlignFrame(null);
1553 System.out.println("ALL CLOSED");
1556 * reset state of singleton objects as appropriate (clear down session state
1557 * when all windows are closed)
1559 StructureSelectionManager ssm = StructureSelectionManager
1560 .getStructureSelectionManager(this);
1568 public void raiseRelated_actionPerformed(ActionEvent e)
1570 reorderAssociatedWindows(false, false);
1574 public void minimizeAssociated_actionPerformed(ActionEvent e)
1576 reorderAssociatedWindows(true, false);
1579 void closeAssociatedWindows()
1581 reorderAssociatedWindows(false, true);
1587 * @seejalview.jbgui.GDesktop#garbageCollect_actionPerformed(java.awt.event.
1591 protected void garbageCollect_actionPerformed(ActionEvent e)
1593 // We simply collect the garbage
1594 jalview.bin.Console.debug("Collecting garbage...");
1596 jalview.bin.Console.debug("Finished garbage collection.");
1602 * @see jalview.jbgui.GDesktop#showMemusage_actionPerformed(java.awt.event.
1606 protected void showMemusage_actionPerformed(ActionEvent e)
1608 desktop.showMemoryUsage(showMemusage.isSelected());
1615 * jalview.jbgui.GDesktop#showConsole_actionPerformed(java.awt.event.ActionEvent
1619 protected void showConsole_actionPerformed(ActionEvent e)
1621 showConsole(showConsole.isSelected());
1624 Console jconsole = null;
1627 * control whether the java console is visible or not
1631 void showConsole(boolean selected)
1633 // TODO: decide if we should update properties file
1634 if (jconsole != null) // BH 2018
1636 showConsole.setSelected(selected);
1637 Cache.setProperty("SHOW_JAVA_CONSOLE",
1638 Boolean.valueOf(selected).toString());
1639 jconsole.setVisible(selected);
1643 void reorderAssociatedWindows(boolean minimize, boolean close)
1645 JInternalFrame[] frames = desktop.getAllFrames();
1646 if (frames == null || frames.length < 1)
1651 AlignmentViewport source = null, target = null;
1652 if (frames[0] instanceof AlignFrame)
1654 source = ((AlignFrame) frames[0]).getCurrentView();
1656 else if (frames[0] instanceof TreePanel)
1658 source = ((TreePanel) frames[0]).getViewPort();
1660 else if (frames[0] instanceof PCAPanel)
1662 source = ((PCAPanel) frames[0]).av;
1664 else if (frames[0].getContentPane() instanceof PairwiseAlignPanel)
1666 source = ((PairwiseAlignPanel) frames[0].getContentPane()).av;
1671 for (int i = 0; i < frames.length; i++)
1674 if (frames[i] == null)
1678 if (frames[i] instanceof AlignFrame)
1680 target = ((AlignFrame) frames[i]).getCurrentView();
1682 else if (frames[i] instanceof TreePanel)
1684 target = ((TreePanel) frames[i]).getViewPort();
1686 else if (frames[i] instanceof PCAPanel)
1688 target = ((PCAPanel) frames[i]).av;
1690 else if (frames[i].getContentPane() instanceof PairwiseAlignPanel)
1692 target = ((PairwiseAlignPanel) frames[i].getContentPane()).av;
1695 if (source == target)
1701 frames[i].setClosed(true);
1705 frames[i].setIcon(minimize);
1708 frames[i].toFront();
1712 } catch (java.beans.PropertyVetoException ex)
1727 protected void preferences_actionPerformed(ActionEvent e)
1729 Preferences.openPreferences();
1733 * Prompts the user to choose a file and then saves the Jalview state as a
1734 * Jalview project file
1737 public void saveState_actionPerformed()
1739 saveState_actionPerformed(false);
1742 public void saveState_actionPerformed(boolean saveAs)
1744 java.io.File projectFile = getProjectFile();
1745 // autoSave indicates we already have a file and don't need to ask
1746 boolean autoSave = projectFile != null && !saveAs
1747 && BackupFiles.getEnabled();
1749 // System.out.println("autoSave="+autoSave+", projectFile='"+projectFile+"',
1750 // saveAs="+saveAs+", Backups
1751 // "+(BackupFiles.getEnabled()?"enabled":"disabled"));
1753 boolean approveSave = false;
1756 JalviewFileChooser chooser = new JalviewFileChooser("jvp",
1759 chooser.setFileView(new JalviewFileView());
1760 chooser.setDialogTitle(MessageManager.getString("label.save_state"));
1762 int value = chooser.showSaveDialog(this);
1764 if (value == JalviewFileChooser.APPROVE_OPTION)
1766 projectFile = chooser.getSelectedFile();
1767 setProjectFile(projectFile);
1772 if (approveSave || autoSave)
1774 final Desktop me = this;
1775 final java.io.File chosenFile = projectFile;
1776 new Thread(new Runnable()
1781 // TODO: refactor to Jalview desktop session controller action.
1782 setProgressBar(MessageManager.formatMessage(
1783 "label.saving_jalview_project", new Object[]
1784 { chosenFile.getName() }), chosenFile.hashCode());
1785 Cache.setProperty("LAST_DIRECTORY", chosenFile.getParent());
1786 // TODO catch and handle errors for savestate
1787 // TODO prevent user from messing with the Desktop whilst we're saving
1790 boolean doBackup = BackupFiles.getEnabled();
1791 BackupFiles backupfiles = doBackup ? new BackupFiles(chosenFile)
1794 new Jalview2XML().saveState(
1795 doBackup ? backupfiles.getTempFile() : chosenFile);
1799 backupfiles.setWriteSuccess(true);
1800 backupfiles.rollBackupsAndRenameTempFile();
1802 } catch (OutOfMemoryError oom)
1804 new OOMWarning("Whilst saving current state to "
1805 + chosenFile.getName(), oom);
1806 } catch (Exception ex)
1808 jalview.bin.Console.error("Problems whilst trying to save to "
1809 + chosenFile.getName(), ex);
1810 JvOptionPane.showMessageDialog(me,
1811 MessageManager.formatMessage(
1812 "label.error_whilst_saving_current_state_to",
1814 { chosenFile.getName() }),
1815 MessageManager.getString("label.couldnt_save_project"),
1816 JvOptionPane.WARNING_MESSAGE);
1818 setProgressBar(null, chosenFile.hashCode());
1825 public void saveAsState_actionPerformed(ActionEvent e)
1827 saveState_actionPerformed(true);
1830 private void setProjectFile(File choice)
1832 this.projectFile = choice;
1835 public File getProjectFile()
1837 return this.projectFile;
1841 * Shows a file chooser dialog and tries to read in the selected file as a
1845 public void loadState_actionPerformed()
1847 final String[] suffix = new String[] { "jvp", "jar" };
1848 final String[] desc = new String[] { "Jalview Project",
1849 "Jalview Project (old)" };
1850 JalviewFileChooser chooser = new JalviewFileChooser(
1851 Cache.getProperty("LAST_DIRECTORY"), suffix, desc,
1852 "Jalview Project", true, BackupFiles.getEnabled()); // last two
1856 chooser.setFileView(new JalviewFileView());
1857 chooser.setDialogTitle(MessageManager.getString("label.restore_state"));
1858 chooser.setResponseHandler(0, new Runnable()
1863 File selectedFile = chooser.getSelectedFile();
1864 setProjectFile(selectedFile);
1865 String choice = selectedFile.getAbsolutePath();
1866 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1867 new Thread(new Runnable()
1874 new Jalview2XML().loadJalviewAlign(selectedFile);
1875 } catch (OutOfMemoryError oom)
1877 new OOMWarning("Whilst loading project from " + choice, oom);
1878 } catch (Exception ex)
1880 jalview.bin.Console.error(
1881 "Problems whilst loading project from " + choice, ex);
1882 JvOptionPane.showMessageDialog(Desktop.desktop,
1883 MessageManager.formatMessage(
1884 "label.error_whilst_loading_project_from",
1888 .getString("label.couldnt_load_project"),
1889 JvOptionPane.WARNING_MESSAGE);
1892 }, "Project Loader").start();
1896 chooser.showOpenDialog(this);
1900 public void inputSequence_actionPerformed(ActionEvent e)
1902 new SequenceFetcher(this);
1905 JPanel progressPanel;
1907 ArrayList<JPanel> fileLoadingPanels = new ArrayList<>();
1909 public void startLoading(final Object fileName)
1911 if (fileLoadingCount == 0)
1913 fileLoadingPanels.add(addProgressPanel(MessageManager
1914 .formatMessage("label.loading_file", new Object[]
1920 private JPanel addProgressPanel(String string)
1922 if (progressPanel == null)
1924 progressPanel = new JPanel(new GridLayout(1, 1));
1925 totalProgressCount = 0;
1926 instance.getContentPane().add(progressPanel, BorderLayout.SOUTH);
1928 JPanel thisprogress = new JPanel(new BorderLayout(10, 5));
1929 JProgressBar progressBar = new JProgressBar();
1930 progressBar.setIndeterminate(true);
1932 thisprogress.add(new JLabel(string), BorderLayout.WEST);
1934 thisprogress.add(progressBar, BorderLayout.CENTER);
1935 progressPanel.add(thisprogress);
1936 ((GridLayout) progressPanel.getLayout()).setRows(
1937 ((GridLayout) progressPanel.getLayout()).getRows() + 1);
1938 ++totalProgressCount;
1939 instance.validate();
1940 return thisprogress;
1943 int totalProgressCount = 0;
1945 private void removeProgressPanel(JPanel progbar)
1947 if (progressPanel != null)
1949 synchronized (progressPanel)
1951 progressPanel.remove(progbar);
1952 GridLayout gl = (GridLayout) progressPanel.getLayout();
1953 gl.setRows(gl.getRows() - 1);
1954 if (--totalProgressCount < 1)
1956 this.getContentPane().remove(progressPanel);
1957 progressPanel = null;
1964 public void stopLoading()
1967 if (fileLoadingCount < 1)
1969 while (fileLoadingPanels.size() > 0)
1971 removeProgressPanel(fileLoadingPanels.remove(0));
1973 fileLoadingPanels.clear();
1974 fileLoadingCount = 0;
1979 public static int getViewCount(String alignmentId)
1981 AlignmentViewport[] aps = getViewports(alignmentId);
1982 return (aps == null) ? 0 : aps.length;
1987 * @param alignmentId
1988 * - if null, all sets are returned
1989 * @return all AlignmentPanels concerning the alignmentId sequence set
1991 public static AlignmentPanel[] getAlignmentPanels(String alignmentId)
1993 if (Desktop.desktop == null)
1995 // no frames created and in headless mode
1996 // TODO: verify that frames are recoverable when in headless mode
1999 List<AlignmentPanel> aps = new ArrayList<>();
2000 AlignFrame[] frames = getAlignFrames();
2005 for (AlignFrame af : frames)
2007 for (AlignmentPanel ap : af.alignPanels)
2009 if (alignmentId == null
2010 || alignmentId.equals(ap.av.getSequenceSetId()))
2016 if (aps.size() == 0)
2020 AlignmentPanel[] vap = aps.toArray(new AlignmentPanel[aps.size()]);
2025 * get all the viewports on an alignment.
2027 * @param sequenceSetId
2028 * unique alignment id (may be null - all viewports returned in that
2030 * @return all viewports on the alignment bound to sequenceSetId
2032 public static AlignmentViewport[] getViewports(String sequenceSetId)
2034 List<AlignmentViewport> viewp = new ArrayList<>();
2035 if (desktop != null)
2037 AlignFrame[] frames = Desktop.getAlignFrames();
2039 for (AlignFrame afr : frames)
2041 if (sequenceSetId == null || afr.getViewport().getSequenceSetId()
2042 .equals(sequenceSetId))
2044 if (afr.alignPanels != null)
2046 for (AlignmentPanel ap : afr.alignPanels)
2048 if (sequenceSetId == null
2049 || sequenceSetId.equals(ap.av.getSequenceSetId()))
2057 viewp.add(afr.getViewport());
2061 if (viewp.size() > 0)
2063 return viewp.toArray(new AlignmentViewport[viewp.size()]);
2070 * Explode the views in the given frame into separate AlignFrame
2074 public static void explodeViews(AlignFrame af)
2076 int size = af.alignPanels.size();
2082 // FIXME: ideally should use UI interface API
2083 FeatureSettings viewFeatureSettings = (af.featureSettings != null
2084 && af.featureSettings.isOpen()) ? af.featureSettings : null;
2085 Rectangle fsBounds = af.getFeatureSettingsGeometry();
2086 for (int i = 0; i < size; i++)
2088 AlignmentPanel ap = af.alignPanels.get(i);
2090 AlignFrame newaf = new AlignFrame(ap);
2092 // transfer reference for existing feature settings to new alignFrame
2093 if (ap == af.alignPanel)
2095 if (viewFeatureSettings != null && viewFeatureSettings.fr.ap == ap)
2097 newaf.featureSettings = viewFeatureSettings;
2099 newaf.setFeatureSettingsGeometry(fsBounds);
2103 * Restore the view's last exploded frame geometry if known. Multiple views from
2104 * one exploded frame share and restore the same (frame) position and size.
2106 Rectangle geometry = ap.av.getExplodedGeometry();
2107 if (geometry != null)
2109 newaf.setBounds(geometry);
2112 ap.av.setGatherViewsHere(false);
2114 addInternalFrame(newaf, af.getTitle(), AlignFrame.DEFAULT_WIDTH,
2115 AlignFrame.DEFAULT_HEIGHT);
2116 // and materialise a new feature settings dialog instance for the new
2118 // (closes the old as if 'OK' was pressed)
2119 if (ap == af.alignPanel && newaf.featureSettings != null
2120 && newaf.featureSettings.isOpen()
2121 && af.alignPanel.getAlignViewport().isShowSequenceFeatures())
2123 newaf.showFeatureSettingsUI();
2127 af.featureSettings = null;
2128 af.alignPanels.clear();
2129 af.closeMenuItem_actionPerformed(true);
2134 * Gather expanded views (separate AlignFrame's) with the same sequence set
2135 * identifier back in to this frame as additional views, and close the
2136 * expanded views. Note the expanded frames may themselves have multiple
2137 * views. We take the lot.
2141 public void gatherViews(AlignFrame source)
2143 source.viewport.setGatherViewsHere(true);
2144 source.viewport.setExplodedGeometry(source.getBounds());
2145 JInternalFrame[] frames = desktop.getAllFrames();
2146 String viewId = source.viewport.getSequenceSetId();
2147 for (int t = 0; t < frames.length; t++)
2149 if (frames[t] instanceof AlignFrame && frames[t] != source)
2151 AlignFrame af = (AlignFrame) frames[t];
2152 boolean gatherThis = false;
2153 for (int a = 0; a < af.alignPanels.size(); a++)
2155 AlignmentPanel ap = af.alignPanels.get(a);
2156 if (viewId.equals(ap.av.getSequenceSetId()))
2159 ap.av.setGatherViewsHere(false);
2160 ap.av.setExplodedGeometry(af.getBounds());
2161 source.addAlignmentPanel(ap, false);
2167 if (af.featureSettings != null && af.featureSettings.isOpen())
2169 if (source.featureSettings == null)
2171 // preserve the feature settings geometry for this frame
2172 source.featureSettings = af.featureSettings;
2173 source.setFeatureSettingsGeometry(
2174 af.getFeatureSettingsGeometry());
2178 // close it and forget
2179 af.featureSettings.close();
2182 af.alignPanels.clear();
2183 af.closeMenuItem_actionPerformed(true);
2188 // refresh the feature setting UI for the source frame if it exists
2189 if (source.featureSettings != null && source.featureSettings.isOpen())
2191 source.showFeatureSettingsUI();
2196 public JInternalFrame[] getAllFrames()
2198 return desktop.getAllFrames();
2202 * Checks the given url to see if it gives a response indicating that the user
2203 * should be informed of a new questionnaire.
2207 public void checkForQuestionnaire(String url)
2209 UserQuestionnaireCheck jvq = new UserQuestionnaireCheck(url);
2210 // javax.swing.SwingUtilities.invokeLater(jvq);
2211 new Thread(jvq).start();
2214 public void checkURLLinks()
2216 // Thread off the URL link checker
2217 addDialogThread(new Runnable()
2222 if (Cache.getDefault("CHECKURLLINKS", true))
2224 // check what the actual links are - if it's just the default don't
2225 // bother with the warning
2226 List<String> links = Preferences.sequenceUrlLinks
2229 // only need to check links if there is one with a
2230 // SEQUENCE_ID which is not the default EMBL_EBI link
2231 ListIterator<String> li = links.listIterator();
2232 boolean check = false;
2233 List<JLabel> urls = new ArrayList<>();
2234 while (li.hasNext())
2236 String link = li.next();
2237 if (link.contains(jalview.util.UrlConstants.SEQUENCE_ID)
2238 && !UrlConstants.isDefaultString(link))
2241 int barPos = link.indexOf("|");
2242 String urlMsg = barPos == -1 ? link
2243 : link.substring(0, barPos) + ": "
2244 + link.substring(barPos + 1);
2245 urls.add(new JLabel(urlMsg));
2253 // ask user to check in case URL links use old style tokens
2254 // ($SEQUENCE_ID$ for sequence id _or_ accession id)
2255 JPanel msgPanel = new JPanel();
2256 msgPanel.setLayout(new BoxLayout(msgPanel, BoxLayout.PAGE_AXIS));
2257 msgPanel.add(Box.createVerticalGlue());
2258 JLabel msg = new JLabel(MessageManager
2259 .getString("label.SEQUENCE_ID_for_DB_ACCESSION1"));
2260 JLabel msg2 = new JLabel(MessageManager
2261 .getString("label.SEQUENCE_ID_for_DB_ACCESSION2"));
2263 for (JLabel url : urls)
2269 final JCheckBox jcb = new JCheckBox(
2270 MessageManager.getString("label.do_not_display_again"));
2271 jcb.addActionListener(new ActionListener()
2274 public void actionPerformed(ActionEvent e)
2276 // update Cache settings for "don't show this again"
2277 boolean showWarningAgain = !jcb.isSelected();
2278 Cache.setProperty("CHECKURLLINKS",
2279 Boolean.valueOf(showWarningAgain).toString());
2284 JvOptionPane.showMessageDialog(Desktop.desktop, msgPanel,
2286 .getString("label.SEQUENCE_ID_no_longer_used"),
2287 JvOptionPane.WARNING_MESSAGE);
2294 * Proxy class for JDesktopPane which optionally displays the current memory
2295 * usage and highlights the desktop area with a red bar if free memory runs
2300 public class MyDesktopPane extends JDesktopPane implements Runnable
2302 private static final float ONE_MB = 1048576f;
2304 boolean showMemoryUsage = false;
2308 java.text.NumberFormat df;
2310 float maxMemory, allocatedMemory, freeMemory, totalFreeMemory,
2313 public MyDesktopPane(boolean showMemoryUsage)
2315 showMemoryUsage(showMemoryUsage);
2318 public void showMemoryUsage(boolean showMemory)
2320 this.showMemoryUsage = showMemory;
2323 Thread worker = new Thread(this);
2329 public boolean isShowMemoryUsage()
2331 return showMemoryUsage;
2337 df = java.text.NumberFormat.getNumberInstance();
2338 df.setMaximumFractionDigits(2);
2339 runtime = Runtime.getRuntime();
2341 while (showMemoryUsage)
2345 maxMemory = runtime.maxMemory() / ONE_MB;
2346 allocatedMemory = runtime.totalMemory() / ONE_MB;
2347 freeMemory = runtime.freeMemory() / ONE_MB;
2348 totalFreeMemory = freeMemory + (maxMemory - allocatedMemory);
2350 percentUsage = (totalFreeMemory / maxMemory) * 100;
2352 // if (percentUsage < 20)
2354 // border1 = BorderFactory.createMatteBorder(12, 12, 12, 12,
2356 // instance.set.setBorder(border1);
2359 // sleep after showing usage
2361 } catch (Exception ex)
2363 ex.printStackTrace();
2369 public void paintComponent(Graphics g)
2371 if (showMemoryUsage && g != null && df != null)
2373 if (percentUsage < 20)
2375 g.setColor(Color.red);
2377 FontMetrics fm = g.getFontMetrics();
2380 g.drawString(MessageManager.formatMessage("label.memory_stats",
2382 { df.format(totalFreeMemory), df.format(maxMemory),
2383 df.format(percentUsage) }),
2384 10, getHeight() - fm.getHeight());
2388 // output debug scale message. Important for jalview.bin.HiDPISettingTest2
2389 Desktop.debugScaleMessage(Desktop.getDesktop().getGraphics());
2394 * Accessor method to quickly get all the AlignmentFrames loaded.
2396 * @return an array of AlignFrame, or null if none found
2398 public static AlignFrame[] getAlignFrames()
2400 if (Jalview.isHeadlessMode())
2402 // Desktop.desktop is null in headless mode
2403 return new AlignFrame[] { Jalview.currentAlignFrame };
2406 JInternalFrame[] frames = Desktop.desktop.getAllFrames();
2412 List<AlignFrame> avp = new ArrayList<>();
2414 for (int i = frames.length - 1; i > -1; i--)
2416 if (frames[i] instanceof AlignFrame)
2418 avp.add((AlignFrame) frames[i]);
2420 else if (frames[i] instanceof SplitFrame)
2423 * Also check for a split frame containing an AlignFrame
2425 GSplitFrame sf = (GSplitFrame) frames[i];
2426 if (sf.getTopFrame() instanceof AlignFrame)
2428 avp.add((AlignFrame) sf.getTopFrame());
2430 if (sf.getBottomFrame() instanceof AlignFrame)
2432 avp.add((AlignFrame) sf.getBottomFrame());
2436 if (avp.size() == 0)
2440 AlignFrame afs[] = avp.toArray(new AlignFrame[avp.size()]);
2445 * Returns an array of any AppJmol frames in the Desktop (or null if none).
2449 public GStructureViewer[] getJmols()
2451 JInternalFrame[] frames = Desktop.desktop.getAllFrames();
2457 List<GStructureViewer> avp = new ArrayList<>();
2459 for (int i = frames.length - 1; i > -1; i--)
2461 if (frames[i] instanceof AppJmol)
2463 GStructureViewer af = (GStructureViewer) frames[i];
2467 if (avp.size() == 0)
2471 GStructureViewer afs[] = avp.toArray(new GStructureViewer[avp.size()]);
2476 * Add Groovy Support to Jalview
2479 public void groovyShell_actionPerformed()
2483 openGroovyConsole();
2484 } catch (Exception ex)
2486 jalview.bin.Console.error("Groovy Shell Creation failed.", ex);
2487 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
2489 MessageManager.getString("label.couldnt_create_groovy_shell"),
2490 MessageManager.getString("label.groovy_support_failed"),
2491 JvOptionPane.ERROR_MESSAGE);
2496 * Open the Groovy console
2498 void openGroovyConsole()
2500 if (groovyConsole == null)
2502 groovyConsole = new groovy.ui.Console();
2503 groovyConsole.setVariable("Jalview", this);
2504 groovyConsole.run();
2507 * We allow only one console at a time, so that AlignFrame menu option
2508 * 'Calculate | Run Groovy script' is unambiguous. Disable 'Groovy Console', and
2509 * enable 'Run script', when the console is opened, and the reverse when it is
2512 Window window = (Window) groovyConsole.getFrame();
2513 window.addWindowListener(new WindowAdapter()
2516 public void windowClosed(WindowEvent e)
2519 * rebind CMD-Q from Groovy Console to Jalview Quit
2522 enableExecuteGroovy(false);
2528 * show Groovy console window (after close and reopen)
2530 ((Window) groovyConsole.getFrame()).setVisible(true);
2533 * if we got this far, enable 'Run Groovy' in AlignFrame menus and disable
2534 * opening a second console
2536 enableExecuteGroovy(true);
2540 * Bind Ctrl/Cmd-Q to Quit - for reset as Groovy Console takes over this
2541 * binding when opened
2543 protected void addQuitHandler()
2546 .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
2548 .getKeyStroke(KeyEvent.VK_Q,
2549 jalview.util.ShortcutKeyMaskExWrapper
2550 .getMenuShortcutKeyMaskEx()),
2552 getRootPane().getActionMap().put("Quit", new AbstractAction()
2555 public void actionPerformed(ActionEvent e)
2557 QResponse qresponse = desktopQuit();
2558 if (qresponse == QResponse.CANCEL_QUIT)
2561 .debug("Desktop: Quit action cancelled by user");
2568 * Enable or disable 'Run Groovy script' in AlignFrame calculate menus
2571 * true if Groovy console is open
2573 public void enableExecuteGroovy(boolean enabled)
2576 * disable opening a second Groovy console (or re-enable when the console is
2579 groovyShell.setEnabled(!enabled);
2581 AlignFrame[] alignFrames = getAlignFrames();
2582 if (alignFrames != null)
2584 for (AlignFrame af : alignFrames)
2586 af.setGroovyEnabled(enabled);
2592 * Progress bars managed by the IProgressIndicator method.
2594 private Hashtable<Long, JPanel> progressBars;
2596 private Hashtable<Long, IProgressIndicatorHandler> progressBarHandlers;
2601 * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
2604 public void setProgressBar(String message, long id)
2606 if (progressBars == null)
2608 progressBars = new Hashtable<>();
2609 progressBarHandlers = new Hashtable<>();
2612 if (progressBars.get(Long.valueOf(id)) != null)
2614 JPanel panel = progressBars.remove(Long.valueOf(id));
2615 if (progressBarHandlers.contains(Long.valueOf(id)))
2617 progressBarHandlers.remove(Long.valueOf(id));
2619 removeProgressPanel(panel);
2623 progressBars.put(Long.valueOf(id), addProgressPanel(message));
2630 * @see jalview.gui.IProgressIndicator#registerHandler(long,
2631 * jalview.gui.IProgressIndicatorHandler)
2634 public void registerHandler(final long id,
2635 final IProgressIndicatorHandler handler)
2637 if (progressBarHandlers == null
2638 || !progressBars.containsKey(Long.valueOf(id)))
2640 throw new Error(MessageManager.getString(
2641 "error.call_setprogressbar_before_registering_handler"));
2643 progressBarHandlers.put(Long.valueOf(id), handler);
2644 final JPanel progressPanel = progressBars.get(Long.valueOf(id));
2645 if (handler.canCancel())
2647 JButton cancel = new JButton(
2648 MessageManager.getString("action.cancel"));
2649 final IProgressIndicator us = this;
2650 cancel.addActionListener(new ActionListener()
2654 public void actionPerformed(ActionEvent e)
2656 handler.cancelActivity(id);
2657 us.setProgressBar(MessageManager
2658 .formatMessage("label.cancelled_params", new Object[]
2659 { ((JLabel) progressPanel.getComponent(0)).getText() }),
2663 progressPanel.add(cancel, BorderLayout.EAST);
2669 * @return true if any progress bars are still active
2672 public boolean operationInProgress()
2674 if (progressBars != null && progressBars.size() > 0)
2682 * This will return the first AlignFrame holding the given viewport instance.
2683 * It will break if there are more than one AlignFrames viewing a particular
2687 * @return alignFrame for viewport
2689 public static AlignFrame getAlignFrameFor(AlignViewportI viewport)
2691 if (desktop != null)
2693 AlignmentPanel[] aps = getAlignmentPanels(
2694 viewport.getSequenceSetId());
2695 for (int panel = 0; aps != null && panel < aps.length; panel++)
2697 if (aps[panel] != null && aps[panel].av == viewport)
2699 return aps[panel].alignFrame;
2706 public VamsasApplication getVamsasApplication()
2708 // TODO: JAL-3311 remove remaining code from Jalview relating to VAMSAS
2714 * flag set if jalview GUI is being operated programmatically
2716 private boolean inBatchMode = false;
2719 * check if jalview GUI is being operated programmatically
2721 * @return inBatchMode
2723 public boolean isInBatchMode()
2729 * set flag if jalview GUI is being operated programmatically
2731 * @param inBatchMode
2733 public void setInBatchMode(boolean inBatchMode)
2735 this.inBatchMode = inBatchMode;
2739 * start service discovery and wait till it is done
2741 public void startServiceDiscovery()
2743 startServiceDiscovery(false);
2747 * start service discovery threads - blocking or non-blocking
2751 public void startServiceDiscovery(boolean blocking)
2753 startServiceDiscovery(blocking, false);
2757 * start service discovery threads
2760 * - false means call returns immediately
2761 * @param ignore_SHOW_JWS2_SERVICES_preference
2762 * - when true JABA services are discovered regardless of user's JWS2
2763 * discovery preference setting
2765 public void startServiceDiscovery(boolean blocking,
2766 boolean ignore_SHOW_JWS2_SERVICES_preference)
2768 boolean alive = true;
2769 Thread t0 = null, t1 = null, t2 = null;
2770 // JAL-940 - JALVIEW 1 services are now being EOLed as of JABA 2.1 release
2773 // todo: changesupport handlers need to be transferred
2774 if (discoverer == null)
2776 discoverer = new jalview.ws.jws1.Discoverer();
2777 // register PCS handler for desktop.
2778 discoverer.addPropertyChangeListener(changeSupport);
2780 // JAL-940 - disabled JWS1 service configuration - always start discoverer
2781 // until we phase out completely
2782 (t0 = new Thread(discoverer)).start();
2785 if (ignore_SHOW_JWS2_SERVICES_preference
2786 || Cache.getDefault("SHOW_JWS2_SERVICES", true))
2788 t2 = jalview.ws.jws2.Jws2Discoverer.getDiscoverer()
2789 .startDiscoverer(changeSupport);
2793 // TODO: do rest service discovery
2802 } catch (Exception e)
2805 alive = (t1 != null && t1.isAlive()) || (t2 != null && t2.isAlive())
2806 || (t3 != null && t3.isAlive())
2807 || (t0 != null && t0.isAlive());
2813 * called to check if the service discovery process completed successfully.
2817 protected void JalviewServicesChanged(PropertyChangeEvent evt)
2819 if (evt.getNewValue() == null || evt.getNewValue() instanceof Vector)
2821 final String ermsg = jalview.ws.jws2.Jws2Discoverer.getDiscoverer()
2822 .getErrorMessages();
2825 if (Cache.getDefault("SHOW_WSDISCOVERY_ERRORS", true))
2827 if (serviceChangedDialog == null)
2829 // only run if we aren't already displaying one of these.
2830 addDialogThread(serviceChangedDialog = new Runnable()
2837 * JalviewDialog jd =new JalviewDialog() {
2839 * @Override protected void cancelPressed() { // TODO Auto-generated method stub
2841 * }@Override protected void okPressed() { // TODO Auto-generated method stub
2843 * }@Override protected void raiseClosed() { // TODO Auto-generated method stub
2845 * } }; jd.initDialogFrame(new JLabel("<html><table width=\"450\"><tr><td>" +
2847 * "<br/>It may be that you have invalid JABA URLs in your web service preferences,"
2848 * + " or mis-configured HTTP proxy settings.<br/>" +
2849 * "Check the <em>Connections</em> and <em>Web services</em> tab of the" +
2850 * " Tools->Preferences dialog box to change them.</td></tr></table></html>" ),
2851 * true, true, "Web Service Configuration Problem", 450, 400);
2853 * jd.waitForInput();
2855 JvOptionPane.showConfirmDialog(Desktop.desktop,
2856 new JLabel("<html><table width=\"450\"><tr><td>"
2857 + ermsg + "</td></tr></table>"
2858 + "<p>It may be that you have invalid JABA URLs<br/>in your web service preferences,"
2859 + "<br>or as a command-line argument, or mis-configured HTTP proxy settings.</p>"
2860 + "<p>Check the <em>Connections</em> and <em>Web services</em> tab<br/>of the"
2861 + " Tools->Preferences dialog box to change them.</p></html>"),
2862 "Web Service Configuration Problem",
2863 JvOptionPane.DEFAULT_OPTION,
2864 JvOptionPane.ERROR_MESSAGE);
2865 serviceChangedDialog = null;
2873 jalview.bin.Console.error(
2874 "Errors reported by JABA discovery service. Check web services preferences.\n"
2881 private Runnable serviceChangedDialog = null;
2884 * start a thread to open a URL in the configured browser. Pops up a warning
2885 * dialog to the user if there is an exception when calling out to the browser
2890 public static void showUrl(final String url)
2892 showUrl(url, Desktop.instance);
2896 * Like showUrl but allows progress handler to be specified
2900 * (null) or object implementing IProgressIndicator
2902 public static void showUrl(final String url,
2903 final IProgressIndicator progress)
2905 new Thread(new Runnable()
2912 if (progress != null)
2914 progress.setProgressBar(MessageManager
2915 .formatMessage("status.opening_params", new Object[]
2916 { url }), this.hashCode());
2918 jalview.util.BrowserLauncher.openURL(url);
2919 } catch (Exception ex)
2921 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
2923 .getString("label.web_browser_not_found_unix"),
2924 MessageManager.getString("label.web_browser_not_found"),
2925 JvOptionPane.WARNING_MESSAGE);
2927 ex.printStackTrace();
2929 if (progress != null)
2931 progress.setProgressBar(null, this.hashCode());
2937 public static WsParamSetManager wsparamManager = null;
2939 public static ParamManager getUserParameterStore()
2941 if (wsparamManager == null)
2943 wsparamManager = new WsParamSetManager();
2945 return wsparamManager;
2949 * static hyperlink handler proxy method for use by Jalview's internal windows
2953 public static void hyperlinkUpdate(HyperlinkEvent e)
2955 if (e.getEventType() == EventType.ACTIVATED)
2960 url = e.getURL().toString();
2961 Desktop.showUrl(url);
2962 } catch (Exception x)
2967 .error("Couldn't handle string " + url + " as a URL.");
2969 // ignore any exceptions due to dud links.
2976 * single thread that handles display of dialogs to user.
2978 ExecutorService dialogExecutor = Executors.newSingleThreadExecutor();
2981 * flag indicating if dialogExecutor should try to acquire a permit
2983 private volatile boolean dialogPause = true;
2988 private java.util.concurrent.Semaphore block = new Semaphore(0);
2990 private static groovy.ui.Console groovyConsole;
2993 * add another dialog thread to the queue
2997 public void addDialogThread(final Runnable prompter)
2999 dialogExecutor.submit(new Runnable()
3009 } catch (InterruptedException x)
3013 if (instance == null)
3019 SwingUtilities.invokeAndWait(prompter);
3020 } catch (Exception q)
3022 jalview.bin.Console.warn("Unexpected Exception in dialog thread.",
3029 public void startDialogQueue()
3031 // set the flag so we don't pause waiting for another permit and semaphore
3032 // the current task to begin
3033 dialogPause = false;
3038 * Outputs an image of the desktop to file in EPS format, after prompting the
3039 * user for choice of Text or Lineart character rendering (unless a preference
3040 * has been set). The file name is generated as
3043 * Jalview_snapshot_nnnnn.eps where nnnnn is the current timestamp in milliseconds
3047 protected void snapShotWindow_actionPerformed(ActionEvent e)
3049 // currently the menu option to do this is not shown
3052 int width = getWidth();
3053 int height = getHeight();
3055 "Jalview_snapshot_" + System.currentTimeMillis() + ".eps");
3056 ImageWriterI writer = new ImageWriterI()
3059 public void exportImage(Graphics g) throws Exception
3062 jalview.bin.Console.info("Successfully written snapshot to file "
3063 + of.getAbsolutePath());
3066 String title = "View of desktop";
3067 ImageExporter exporter = new ImageExporter(writer, null, TYPE.EPS,
3069 exporter.doExport(of, this, width, height, title);
3073 * Explode the views in the given SplitFrame into separate SplitFrame windows.
3074 * This respects (remembers) any previous 'exploded geometry' i.e. the size
3075 * and location last time the view was expanded (if any). However it does not
3076 * remember the split pane divider location - this is set to match the
3077 * 'exploding' frame.
3081 public void explodeViews(SplitFrame sf)
3083 AlignFrame oldTopFrame = (AlignFrame) sf.getTopFrame();
3084 AlignFrame oldBottomFrame = (AlignFrame) sf.getBottomFrame();
3085 List<? extends AlignmentViewPanel> topPanels = oldTopFrame
3087 List<? extends AlignmentViewPanel> bottomPanels = oldBottomFrame
3089 int viewCount = topPanels.size();
3096 * Processing in reverse order works, forwards order leaves the first panels not
3097 * visible. I don't know why!
3099 for (int i = viewCount - 1; i >= 0; i--)
3102 * Make new top and bottom frames. These take over the respective AlignmentPanel
3103 * objects, including their AlignmentViewports, so the cdna/protein
3104 * relationships between the viewports is carried over to the new split frames.
3106 * explodedGeometry holds the (x, y) position of the previously exploded
3107 * SplitFrame, and the (width, height) of the AlignFrame component
3109 AlignmentPanel topPanel = (AlignmentPanel) topPanels.get(i);
3110 AlignFrame newTopFrame = new AlignFrame(topPanel);
3111 newTopFrame.setSize(oldTopFrame.getSize());
3112 newTopFrame.setVisible(true);
3113 Rectangle geometry = ((AlignViewport) topPanel.getAlignViewport())
3114 .getExplodedGeometry();
3115 if (geometry != null)
3117 newTopFrame.setSize(geometry.getSize());
3120 AlignmentPanel bottomPanel = (AlignmentPanel) bottomPanels.get(i);
3121 AlignFrame newBottomFrame = new AlignFrame(bottomPanel);
3122 newBottomFrame.setSize(oldBottomFrame.getSize());
3123 newBottomFrame.setVisible(true);
3124 geometry = ((AlignViewport) bottomPanel.getAlignViewport())
3125 .getExplodedGeometry();
3126 if (geometry != null)
3128 newBottomFrame.setSize(geometry.getSize());
3131 topPanel.av.setGatherViewsHere(false);
3132 bottomPanel.av.setGatherViewsHere(false);
3133 JInternalFrame splitFrame = new SplitFrame(newTopFrame,
3135 if (geometry != null)
3137 splitFrame.setLocation(geometry.getLocation());
3139 Desktop.addInternalFrame(splitFrame, sf.getTitle(), -1, -1);
3143 * Clear references to the panels (now relocated in the new SplitFrames) before
3144 * closing the old SplitFrame.
3147 bottomPanels.clear();
3152 * Gather expanded split frames, sharing the same pairs of sequence set ids,
3153 * back into the given SplitFrame as additional views. Note that the gathered
3154 * frames may themselves have multiple views.
3158 public void gatherViews(GSplitFrame source)
3161 * special handling of explodedGeometry for a view within a SplitFrame: - it
3162 * holds the (x, y) position of the enclosing SplitFrame, and the (width,
3163 * height) of the AlignFrame component
3165 AlignFrame myTopFrame = (AlignFrame) source.getTopFrame();
3166 AlignFrame myBottomFrame = (AlignFrame) source.getBottomFrame();
3167 myTopFrame.viewport.setExplodedGeometry(new Rectangle(source.getX(),
3168 source.getY(), myTopFrame.getWidth(), myTopFrame.getHeight()));
3169 myBottomFrame.viewport
3170 .setExplodedGeometry(new Rectangle(source.getX(), source.getY(),
3171 myBottomFrame.getWidth(), myBottomFrame.getHeight()));
3172 myTopFrame.viewport.setGatherViewsHere(true);
3173 myBottomFrame.viewport.setGatherViewsHere(true);
3174 String topViewId = myTopFrame.viewport.getSequenceSetId();
3175 String bottomViewId = myBottomFrame.viewport.getSequenceSetId();
3177 JInternalFrame[] frames = desktop.getAllFrames();
3178 for (JInternalFrame frame : frames)
3180 if (frame instanceof SplitFrame && frame != source)
3182 SplitFrame sf = (SplitFrame) frame;
3183 AlignFrame topFrame = (AlignFrame) sf.getTopFrame();
3184 AlignFrame bottomFrame = (AlignFrame) sf.getBottomFrame();
3185 boolean gatherThis = false;
3186 for (int a = 0; a < topFrame.alignPanels.size(); a++)
3188 AlignmentPanel topPanel = topFrame.alignPanels.get(a);
3189 AlignmentPanel bottomPanel = bottomFrame.alignPanels.get(a);
3190 if (topViewId.equals(topPanel.av.getSequenceSetId())
3191 && bottomViewId.equals(bottomPanel.av.getSequenceSetId()))
3194 topPanel.av.setGatherViewsHere(false);
3195 bottomPanel.av.setGatherViewsHere(false);
3196 topPanel.av.setExplodedGeometry(
3197 new Rectangle(sf.getLocation(), topFrame.getSize()));
3198 bottomPanel.av.setExplodedGeometry(
3199 new Rectangle(sf.getLocation(), bottomFrame.getSize()));
3200 myTopFrame.addAlignmentPanel(topPanel, false);
3201 myBottomFrame.addAlignmentPanel(bottomPanel, false);
3207 topFrame.getAlignPanels().clear();
3208 bottomFrame.getAlignPanels().clear();
3215 * The dust settles...give focus to the tab we did this from.
3217 myTopFrame.setDisplayedView(myTopFrame.alignPanel);
3220 public static groovy.ui.Console getGroovyConsole()
3222 return groovyConsole;
3226 * handles the payload of a drag and drop event.
3228 * TODO refactor to desktop utilities class
3231 * - Data source strings extracted from the drop event
3233 * - protocol for each data source extracted from the drop event
3237 * - the payload from the drop event
3240 public static void transferFromDropTarget(List<Object> files,
3241 List<DataSourceType> protocols, DropTargetDropEvent evt,
3242 Transferable t) throws Exception
3245 DataFlavor uriListFlavor = new DataFlavor(
3246 "text/uri-list;class=java.lang.String"), urlFlavour = null;
3249 urlFlavour = new DataFlavor(
3250 "application/x-java-url; class=java.net.URL");
3251 } catch (ClassNotFoundException cfe)
3253 jalview.bin.Console.debug("Couldn't instantiate the URL dataflavor.",
3257 if (urlFlavour != null && t.isDataFlavorSupported(urlFlavour))
3262 java.net.URL url = (URL) t.getTransferData(urlFlavour);
3263 // nb: java 8 osx bug https://bugs.openjdk.java.net/browse/JDK-8156099
3264 // means url may be null.
3267 protocols.add(DataSourceType.URL);
3268 files.add(url.toString());
3269 jalview.bin.Console.debug("Drop handled as URL dataflavor "
3270 + files.get(files.size() - 1));
3275 if (Platform.isAMacAndNotJS())
3278 "Please ignore plist error - occurs due to problem with java 8 on OSX");
3281 } catch (Throwable ex)
3283 jalview.bin.Console.debug("URL drop handler failed.", ex);
3286 if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
3288 // Works on Windows and MacOSX
3289 jalview.bin.Console.debug("Drop handled as javaFileListFlavor");
3290 for (Object file : (List) t
3291 .getTransferData(DataFlavor.javaFileListFlavor))
3294 protocols.add(DataSourceType.FILE);
3299 // Unix like behaviour
3300 boolean added = false;
3302 if (t.isDataFlavorSupported(uriListFlavor))
3304 jalview.bin.Console.debug("Drop handled as uriListFlavor");
3305 // This is used by Unix drag system
3306 data = (String) t.getTransferData(uriListFlavor);
3310 // fallback to text: workaround - on OSX where there's a JVM bug
3312 .debug("standard URIListFlavor failed. Trying text");
3313 // try text fallback
3314 DataFlavor textDf = new DataFlavor(
3315 "text/plain;class=java.lang.String");
3316 if (t.isDataFlavorSupported(textDf))
3318 data = (String) t.getTransferData(textDf);
3321 jalview.bin.Console.debug("Plain text drop content returned "
3322 + (data == null ? "Null - failed" : data));
3327 while (protocols.size() < files.size())
3329 jalview.bin.Console.debug("Adding missing FILE protocol for "
3330 + files.get(protocols.size()));
3331 protocols.add(DataSourceType.FILE);
3333 for (java.util.StringTokenizer st = new java.util.StringTokenizer(
3334 data, "\r\n"); st.hasMoreTokens();)
3337 String s = st.nextToken();
3338 if (s.startsWith("#"))
3340 // the line is a comment (as per the RFC 2483)
3343 java.net.URI uri = new java.net.URI(s);
3344 if (uri.getScheme().toLowerCase(Locale.ROOT).startsWith("http"))
3346 protocols.add(DataSourceType.URL);
3347 files.add(uri.toString());
3351 // otherwise preserve old behaviour: catch all for file objects
3352 java.io.File file = new java.io.File(uri);
3353 protocols.add(DataSourceType.FILE);
3354 files.add(file.toString());
3359 if (jalview.bin.Console.isDebugEnabled())
3361 if (data == null || !added)
3364 if (t.getTransferDataFlavors() != null
3365 && t.getTransferDataFlavors().length > 0)
3367 jalview.bin.Console.debug(
3368 "Couldn't resolve drop data. Here are the supported flavors:");
3369 for (DataFlavor fl : t.getTransferDataFlavors())
3371 jalview.bin.Console.debug(
3372 "Supported transfer dataflavor: " + fl.toString());
3373 Object df = t.getTransferData(fl);
3376 jalview.bin.Console.debug("Retrieves: " + df);
3380 jalview.bin.Console.debug("Retrieved nothing");
3387 .debug("Couldn't resolve dataflavor for drop: "
3393 if (Platform.isWindowsAndNotJS())
3396 .debug("Scanning dropped content for Windows Link Files");
3398 // resolve any .lnk files in the file drop
3399 for (int f = 0; f < files.size(); f++)
3401 String source = files.get(f).toString().toLowerCase(Locale.ROOT);
3402 if (protocols.get(f).equals(DataSourceType.FILE)
3403 && (source.endsWith(".lnk") || source.endsWith(".url")
3404 || source.endsWith(".site")))
3408 Object obj = files.get(f);
3409 File lf = (obj instanceof File ? (File) obj
3410 : new File((String) obj));
3411 // process link file to get a URL
3412 jalview.bin.Console.debug("Found potential link file: " + lf);
3413 WindowsShortcut wscfile = new WindowsShortcut(lf);
3414 String fullname = wscfile.getRealFilename();
3415 protocols.set(f, FormatAdapter.checkProtocol(fullname));
3416 files.set(f, fullname);
3417 jalview.bin.Console.debug("Parsed real filename " + fullname
3418 + " to extract protocol: " + protocols.get(f));
3419 } catch (Exception ex)
3421 jalview.bin.Console.error(
3422 "Couldn't parse " + files.get(f) + " as a link file.",
3431 * Sets the Preferences property for experimental features to True or False
3432 * depending on the state of the controlling menu item
3435 protected void showExperimental_actionPerformed(boolean selected)
3437 Cache.setProperty(EXPERIMENTAL_FEATURES, Boolean.toString(selected));
3441 * Answers a (possibly empty) list of any structure viewer frames (currently
3442 * for either Jmol or Chimera) which are currently open. This may optionally
3443 * be restricted to viewers of a specified class, or viewers linked to a
3444 * specified alignment panel.
3447 * if not null, only return viewers linked to this panel
3448 * @param structureViewerClass
3449 * if not null, only return viewers of this class
3452 public List<StructureViewerBase> getStructureViewers(
3453 AlignmentPanel apanel,
3454 Class<? extends StructureViewerBase> structureViewerClass)
3456 List<StructureViewerBase> result = new ArrayList<>();
3457 JInternalFrame[] frames = Desktop.instance.getAllFrames();
3459 for (JInternalFrame frame : frames)
3461 if (frame instanceof StructureViewerBase)
3463 if (structureViewerClass == null
3464 || structureViewerClass.isInstance(frame))
3467 || ((StructureViewerBase) frame).isLinkedWith(apanel))
3469 result.add((StructureViewerBase) frame);
3477 public static final String debugScaleMessage = "Desktop graphics transform scale=";
3479 private static boolean debugScaleMessageDone = false;
3481 public static void debugScaleMessage(Graphics g)
3483 if (debugScaleMessageDone)
3487 // output used by tests to check HiDPI scaling settings in action
3490 Graphics2D gg = (Graphics2D) g;
3493 AffineTransform t = gg.getTransform();
3494 double scaleX = t.getScaleX();
3495 double scaleY = t.getScaleY();
3496 jalview.bin.Console.debug(debugScaleMessage + scaleX + " (X)");
3497 jalview.bin.Console.debug(debugScaleMessage + scaleY + " (Y)");
3498 debugScaleMessageDone = true;
3502 jalview.bin.Console.debug("Desktop graphics null");
3504 } catch (Exception e)
3506 jalview.bin.Console.debug(Cache.getStackTraceString(e));