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 jalview.api.AlignViewportI;
24 import jalview.api.AlignmentViewPanel;
25 import jalview.api.StructureSelectionManagerProvider;
26 import jalview.bin.ApplicationSingletonProvider;
27 import jalview.bin.ApplicationSingletonProvider.ApplicationSingletonI;
28 import jalview.bin.Cache;
29 import jalview.bin.Jalview;
30 import jalview.gui.ImageExporter.ImageWriterI;
31 import jalview.io.BackupFiles;
32 import jalview.io.DataSourceType;
33 import jalview.io.FileFormat;
34 import jalview.io.FileFormatException;
35 import jalview.io.FileFormatI;
36 import jalview.io.FileFormats;
37 import jalview.io.FileLoader;
38 import jalview.io.FormatAdapter;
39 import jalview.io.IdentifyFile;
40 import jalview.io.JalviewFileChooser;
41 import jalview.io.JalviewFileView;
42 import jalview.jbgui.GDesktop;
43 import jalview.jbgui.GSplitFrame;
44 import jalview.jbgui.GStructureViewer;
45 import jalview.project.Jalview2XML;
46 import jalview.structure.StructureSelectionManager;
47 import jalview.urls.IdOrgSettings;
48 import jalview.util.BrowserLauncher;
49 import jalview.util.ImageMaker.TYPE;
50 import jalview.util.MessageManager;
51 import jalview.util.Platform;
52 import jalview.util.UrlConstants;
53 import jalview.viewmodel.AlignmentViewport;
54 import jalview.ws.jws1.Discoverer;
55 import jalview.ws.params.ParamManager;
56 import jalview.ws.utils.UrlDownloadClient;
58 import java.awt.BorderLayout;
59 import java.awt.Color;
60 import java.awt.Dimension;
61 import java.awt.FontMetrics;
62 import java.awt.Graphics;
63 import java.awt.GridLayout;
64 import java.awt.Point;
65 import java.awt.Rectangle;
66 import java.awt.Toolkit;
67 import java.awt.Window;
68 import java.awt.datatransfer.Clipboard;
69 import java.awt.datatransfer.ClipboardOwner;
70 import java.awt.datatransfer.DataFlavor;
71 import java.awt.datatransfer.Transferable;
72 import java.awt.dnd.DnDConstants;
73 import java.awt.dnd.DropTargetDragEvent;
74 import java.awt.dnd.DropTargetDropEvent;
75 import java.awt.dnd.DropTargetEvent;
76 import java.awt.dnd.DropTargetListener;
77 import java.awt.event.ActionEvent;
78 import java.awt.event.ActionListener;
79 import java.awt.event.InputEvent;
80 import java.awt.event.KeyEvent;
81 import java.awt.event.MouseAdapter;
82 import java.awt.event.MouseEvent;
83 import java.awt.event.WindowAdapter;
84 import java.awt.event.WindowEvent;
85 import java.beans.PropertyChangeEvent;
86 import java.beans.PropertyChangeListener;
87 import java.io.BufferedInputStream;
89 import java.io.FileOutputStream;
90 import java.io.IOException;
92 import java.util.ArrayList;
93 import java.util.Hashtable;
94 import java.util.List;
95 import java.util.ListIterator;
96 import java.util.Vector;
97 import java.util.concurrent.ExecutorService;
98 import java.util.concurrent.Executors;
99 import java.util.concurrent.Semaphore;
101 import javax.swing.AbstractAction;
102 import javax.swing.Action;
103 import javax.swing.ActionMap;
104 import javax.swing.Box;
105 import javax.swing.BoxLayout;
106 import javax.swing.DefaultDesktopManager;
107 import javax.swing.DesktopManager;
108 import javax.swing.InputMap;
109 import javax.swing.JButton;
110 import javax.swing.JCheckBox;
111 import javax.swing.JComboBox;
112 import javax.swing.JComponent;
113 import javax.swing.JDesktopPane;
114 import javax.swing.JFrame;
115 import javax.swing.JInternalFrame;
116 import javax.swing.JLabel;
117 import javax.swing.JMenuItem;
118 import javax.swing.JPanel;
119 import javax.swing.JPopupMenu;
120 import javax.swing.JProgressBar;
121 import javax.swing.JTextField;
122 import javax.swing.KeyStroke;
123 import javax.swing.SwingUtilities;
124 import javax.swing.event.HyperlinkEvent;
125 import javax.swing.event.HyperlinkEvent.EventType;
126 import javax.swing.event.InternalFrameAdapter;
127 import javax.swing.event.InternalFrameEvent;
128 import javax.swing.event.MenuEvent;
129 import javax.swing.event.MenuListener;
131 import org.stackoverflowusers.file.WindowsShortcut;
138 * @version $Revision: 1.155 $
140 @SuppressWarnings("serial")
141 public class Desktop extends GDesktop
142 implements DropTargetListener, ClipboardOwner, IProgressIndicator,
143 StructureSelectionManagerProvider, ApplicationSingletonI
146 private final static int DEFAULT_MIN_WIDTH = 300;
148 private final static int DEFAULT_MIN_HEIGHT = 250;
150 private final static int ALIGN_FRAME_DEFAULT_MIN_WIDTH = 600;
152 private final static int ALIGN_FRAME_DEFAULT_MIN_HEIGHT = 70;
154 private final static String EXPERIMENTAL_FEATURES = "EXPERIMENTAL_FEATURES";
156 private JalviewChangeSupport changeSupport = new JalviewChangeSupport();
159 * news reader - null if it was never started.
161 BlogReader jvnews = null;
163 private File projectFile;
167 * @see jalview.gui.JalviewChangeSupport#addJalviewPropertyChangeListener(java.beans.PropertyChangeListener)
169 public void addJalviewPropertyChangeListener(
170 PropertyChangeListener listener)
172 changeSupport.addJalviewPropertyChangeListener(listener);
176 * @param propertyName
178 * @see jalview.gui.JalviewChangeSupport#addJalviewPropertyChangeListener(java.lang.String,
179 * java.beans.PropertyChangeListener)
181 public void addJalviewPropertyChangeListener(String propertyName,
182 PropertyChangeListener listener)
184 changeSupport.addJalviewPropertyChangeListener(propertyName, listener);
188 * @param propertyName
190 * @see jalview.gui.JalviewChangeSupport#removeJalviewPropertyChangeListener(java.lang.String,
191 * java.beans.PropertyChangeListener)
193 public void removeJalviewPropertyChangeListener(String propertyName,
194 PropertyChangeListener listener)
196 changeSupport.removeJalviewPropertyChangeListener(propertyName,
200 public static MyDesktopPane getDesktopPane()
202 Desktop desktop = Desktop.getInstance();
203 return desktop == null ? null : desktop.desktopPane;
206 public static StructureSelectionManager getStructureSelectionManager()
208 return StructureSelectionManager
209 .getStructureSelectionManager(getInstance());
212 static int openFrameCount = 0;
214 static final int xOffset = 30;
216 static final int yOffset = 30;
218 public Discoverer discoverer;
220 public Object[] jalviewClipboard;
222 public boolean internalCopy = false;
224 private static int fileLoadingCount = 0;
226 public JInternalFrame conservationSlider;
228 public JInternalFrame PIDSlider;
231 * just an instance (for testng, probably); no actual frames
233 * This flag, when set true, allows a headless-like operation, with a Desktop
234 * object but no actual frames. The issue has to do with the mess-up of the
235 * Windows JInternalFrame implementation, which surreptitiously and
236 * unforgivingly accesses a native peer class, preventing headless operation.
238 * It is set by invoking the Desktop(true) constructor.
240 * It is possible that we can remove this option now, since headless mode is
241 * finally working on Windows through careful attention to not creating any
242 * JInternalFrame objects when in that mode.
245 boolean instanceOnly;
247 class MyDesktopManager implements DesktopManager
250 private DesktopManager delegate;
252 public MyDesktopManager(DesktopManager delegate)
254 this.delegate = delegate;
258 public void activateFrame(JInternalFrame f)
262 delegate.activateFrame(f);
263 } catch (NullPointerException npe)
265 Point p = getMousePosition();
266 showPasteMenu(p.x, p.y);
271 public void beginDraggingFrame(JComponent f)
273 delegate.beginDraggingFrame(f);
277 public void beginResizingFrame(JComponent f, int direction)
279 delegate.beginResizingFrame(f, direction);
283 public void closeFrame(JInternalFrame f)
285 delegate.closeFrame(f);
289 public void deactivateFrame(JInternalFrame f)
291 delegate.deactivateFrame(f);
295 public void deiconifyFrame(JInternalFrame f)
297 delegate.deiconifyFrame(f);
301 public void dragFrame(JComponent f, int newX, int newY)
307 delegate.dragFrame(f, newX, newY);
311 public void endDraggingFrame(JComponent f)
313 delegate.endDraggingFrame(f);
314 desktopPane.repaint();
318 public void endResizingFrame(JComponent f)
320 delegate.endResizingFrame(f);
321 desktopPane.repaint();
325 public void iconifyFrame(JInternalFrame f)
327 delegate.iconifyFrame(f);
331 public void maximizeFrame(JInternalFrame f)
333 delegate.maximizeFrame(f);
337 public void minimizeFrame(JInternalFrame f)
339 delegate.minimizeFrame(f);
343 public void openFrame(JInternalFrame f)
345 delegate.openFrame(f);
349 public void resizeFrame(JComponent f, int newX, int newY, int newWidth,
356 delegate.resizeFrame(f, newX, newY, newWidth, newHeight);
360 public void setBoundsForFrame(JComponent f, int newX, int newY,
361 int newWidth, int newHeight)
363 delegate.setBoundsForFrame(f, newX, newY, newWidth, newHeight);
366 // All other methods, simply delegate
370 public MyDesktopPane desktopPane;
373 * Answers an 'application scope' singleton instance of this class. Separate
374 * SwingJS 'applets' running in the same browser page will each have a
375 * distinct instance of Desktop.
379 public static Desktop getInstance()
381 return Jalview.isHeadlessMode() ? null
382 : (Desktop) ApplicationSingletonProvider
383 .getInstance(Desktop.class);
387 * For TestNG, this constructor can be utilized to allow the creation of a
388 * singleton Desktop instance without the formation of frames and, especially,
389 * not involving dialogs. Cache.log is also initialized for some tests that
390 * require it despite there being no Desktop.
394 public Desktop(boolean forInstance)
396 ApplicationSingletonProvider.setInstance(Desktop.class, this);
402 * Private constructor enforces singleton pattern. It is called by reflection
403 * from ApplicationSingletonProvider.getInstance().
405 @SuppressWarnings("unused")
412 * A note to implementors. It is ESSENTIAL that any activities that might
413 * block are spawned off as threads rather than waited for during this
416 if (!Platform.isJS())
418 doVamsasClientCheck();
421 doConfigureStructurePrefs();
422 setTitle("Jalview " + jalview.bin.Cache.getProperty("VERSION"));
423 setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
424 boolean selmemusage = jalview.bin.Cache.getDefault("SHOW_MEMUSAGE",
426 boolean showjconsole = jalview.bin.Cache
427 .getDefault("SHOW_JAVA_CONSOLE", false);
428 desktopPane = new MyDesktopPane(selmemusage);
430 showMemusage.setSelected(selmemusage);
431 desktopPane.setBackground(Color.white);
432 getContentPane().setLayout(new BorderLayout());
433 // alternate config - have scrollbars - see notes in JAL-153
434 // JScrollPane sp = new JScrollPane();
435 // sp.getViewport().setView(desktop);
436 // getContentPane().add(sp, BorderLayout.CENTER);
438 // BH 2018 - just an experiment to try unclipped JInternalFrames.
441 getRootPane().putClientProperty("swingjs.overflow.hidden", "false");
444 getContentPane().add(desktopPane, BorderLayout.CENTER);
445 desktopPane.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
447 // This line prevents Windows Look&Feel resizing all new windows to
449 // if previous window was maximised
450 desktopPane.setDesktopManager(new MyDesktopManager(
451 (Platform.isWindowsAndNotJS() ? new DefaultDesktopManager()
452 : Platform.isAMacAndNotJS()
453 ? new AquaInternalFrameManager(
454 desktopPane.getDesktopManager())
455 : desktopPane.getDesktopManager())));
457 Rectangle dims = getLastKnownDimensions("");
464 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
465 int xPos = Math.max(5, (screenSize.width - 900) / 2);
466 int yPos = Math.max(5, (screenSize.height - 650) / 2);
467 setBounds(xPos, yPos, 900, 650);
470 // Note that this next syntax, checking for Platform.isJS and also
471 // escaping the code using @j2sIgnore, serves two purposes. It gives
472 // us an easily findable tag, Platform.isJS(), to places in the code where
473 // there is something different about the SwingJS implementation. Second,
474 // it deletes the unneeded Java-only code form the JavaScript version
475 // completely (@j2sIgnore), since it will never be used there.
477 if (!Platform.isJS())
485 jconsole = new Console(this, showjconsole);
486 // add essential build information
487 jconsole.setHeader("Jalview Version: "
488 + jalview.bin.Cache.getProperty("VERSION") + "\n"
489 + "Jalview Installation: "
490 + jalview.bin.Cache.getDefault("INSTALLATION", "unknown")
491 + "\n" + "Build Date: "
492 + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown")
493 + "\n" + "Java version: "
494 + System.getProperty("java.version") + "\n"
495 + System.getProperty("os.arch") + " "
496 + System.getProperty("os.name") + " "
497 + System.getProperty("os.version"));
499 showConsole(showjconsole);
501 showNews.setVisible(false);
503 experimentalFeatures.setSelected(showExperimental());
505 getIdentifiersOrgData();
509 // Spawn a thread that shows the splashscreen
511 SwingUtilities.invokeLater(new Runnable()
520 // Thread off a new instance of the file chooser - this reduces the time
522 // takes to open it later on.
523 new Thread(new Runnable()
528 Cache.log.debug("Filechooser init thread started.");
529 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
530 JalviewFileChooser.forRead(Cache.getProperty("LAST_DIRECTORY"),
532 Cache.log.debug("Filechooser init thread finished.");
535 // Add the service change listener
536 changeSupport.addJalviewPropertyChangeListener("services",
537 new PropertyChangeListener()
541 public void propertyChange(PropertyChangeEvent evt)
543 Cache.log.debug("Firing service changed event for "
544 + evt.getNewValue());
545 JalviewServicesChanged(evt);
552 this.setDropTarget(new java.awt.dnd.DropTarget(desktopPane, this));
554 this.addWindowListener(new WindowAdapter()
557 public void windowClosing(WindowEvent evt)
564 this.addMouseListener(ma = new MouseAdapter()
567 public void mousePressed(MouseEvent evt)
569 if (evt.isPopupTrigger()) // Mac
571 showPasteMenu(evt.getX(), evt.getY());
576 public void mouseReleased(MouseEvent evt)
578 if (evt.isPopupTrigger()) // Windows
580 showPasteMenu(evt.getX(), evt.getY());
584 desktopPane.addMouseListener(ma);
585 } catch (Throwable t)
592 * Answers true if user preferences to enable experimental features is True
597 public boolean showExperimental()
599 String experimental = Cache.getDefault(EXPERIMENTAL_FEATURES,
600 Boolean.FALSE.toString());
601 return Boolean.valueOf(experimental).booleanValue();
604 public void doConfigureStructurePrefs()
606 // configure services
607 StructureSelectionManager ssm = StructureSelectionManager
608 .getStructureSelectionManager(this);
609 if (jalview.bin.Cache.getDefault(Preferences.ADD_SS_ANN, true))
611 ssm.setAddTempFacAnnot(jalview.bin.Cache
612 .getDefault(Preferences.ADD_TEMPFACT_ANN, true));
613 ssm.setProcessSecondaryStructure(jalview.bin.Cache
614 .getDefault(Preferences.STRUCT_FROM_PDB, true));
615 ssm.setSecStructServices(
616 jalview.bin.Cache.getDefault(Preferences.USE_RNAVIEW, true));
620 ssm.setAddTempFacAnnot(false);
621 ssm.setProcessSecondaryStructure(false);
622 ssm.setSecStructServices(false);
626 public void checkForNews()
628 final Desktop me = this;
629 // Thread off the news reader, in case there are connection problems.
630 new Thread(new Runnable()
635 Cache.log.debug("Starting news thread.");
636 jvnews = new BlogReader(me);
637 showNews.setVisible(true);
638 Cache.log.debug("Completed news thread.");
643 public void getIdentifiersOrgData()
645 // Thread off the identifiers fetcher
646 new Thread(new Runnable()
651 Cache.log.debug("Downloading data from identifiers.org");
652 // UrlDownloadClient client = new UrlDownloadClient();
655 UrlDownloadClient.download(IdOrgSettings.getUrl(),
656 IdOrgSettings.getDownloadLocation());
657 } catch (IOException e)
659 Cache.log.debug("Exception downloading identifiers.org data"
668 protected void showNews_actionPerformed(ActionEvent e)
670 showNews(showNews.isSelected());
673 protected void showNews(boolean visible)
675 Cache.log.debug((visible ? "Showing" : "Hiding") + " news.");
676 showNews.setSelected(visible);
677 if (visible && !jvnews.isVisible())
679 new Thread(new Runnable()
684 long now = System.currentTimeMillis();
686 MessageManager.getString("status.refreshing_news"), now);
687 jvnews.refreshNews();
688 setProgressBar(null, now);
696 * recover the last known dimensions for a jalview window
699 * - empty string is desktop, all other windows have unique prefix
700 * @return null or last known dimensions scaled to current geometry (if last
701 * window geom was known)
703 Rectangle getLastKnownDimensions(String windowName)
705 // TODO: lock aspect ratio for scaling desktop Bug #0058199
706 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
707 String x = jalview.bin.Cache.getProperty(windowName + "SCREEN_X");
708 String y = jalview.bin.Cache.getProperty(windowName + "SCREEN_Y");
709 String width = jalview.bin.Cache
710 .getProperty(windowName + "SCREEN_WIDTH");
711 String height = jalview.bin.Cache
712 .getProperty(windowName + "SCREEN_HEIGHT");
713 if ((x != null) && (y != null) && (width != null) && (height != null))
715 int ix = Integer.parseInt(x), iy = Integer.parseInt(y),
716 iw = Integer.parseInt(width), ih = Integer.parseInt(height);
717 if (jalview.bin.Cache.getProperty("SCREENGEOMETRY_WIDTH") != null)
719 // attempt #1 - try to cope with change in screen geometry - this
720 // version doesn't preserve original jv aspect ratio.
721 // take ratio of current screen size vs original screen size.
722 double sw = ((1f * screenSize.width) / (1f * Integer.parseInt(
723 jalview.bin.Cache.getProperty("SCREENGEOMETRY_WIDTH"))));
724 double sh = ((1f * screenSize.height) / (1f * Integer.parseInt(
725 jalview.bin.Cache.getProperty("SCREENGEOMETRY_HEIGHT"))));
726 // rescale the bounds depending upon the current screen geometry.
727 ix = (int) (ix * sw);
728 iw = (int) (iw * sw);
729 iy = (int) (iy * sh);
730 ih = (int) (ih * sh);
731 while (ix >= screenSize.width)
733 jalview.bin.Cache.log.debug(
734 "Window geometry location recall error: shifting horizontal to within screenbounds.");
735 ix -= screenSize.width;
737 while (iy >= screenSize.height)
739 jalview.bin.Cache.log.debug(
740 "Window geometry location recall error: shifting vertical to within screenbounds.");
741 iy -= screenSize.height;
743 jalview.bin.Cache.log.debug(
744 "Got last known dimensions for " + windowName + ": x:" + ix
745 + " y:" + iy + " width:" + iw + " height:" + ih);
747 // return dimensions for new instance
748 return new Rectangle(ix, iy, iw, ih);
753 private void doVamsasClientCheck()
755 if (Cache.vamsasJarsPresent())
757 setupVamsasDisconnectedGui();
758 VamsasMenu.setVisible(true);
759 final Desktop us = this;
760 VamsasMenu.addMenuListener(new MenuListener()
762 // this listener remembers when the menu was first selected, and
763 // doesn't rebuild the session list until it has been cleared and
765 boolean refresh = true;
768 public void menuCanceled(MenuEvent e)
774 public void menuDeselected(MenuEvent e)
780 public void menuSelected(MenuEvent e)
784 us.buildVamsasStMenu();
789 vamsasStart.setVisible(true);
793 protected void showPasteMenu(int x, int y)
795 JPopupMenu popup = new JPopupMenu();
796 JMenuItem item = new JMenuItem(
797 MessageManager.getString("label.paste_new_window"));
798 item.addActionListener(new ActionListener()
801 public void actionPerformed(ActionEvent evt)
808 popup.show(this, x, y);
815 Clipboard c = Toolkit.getDefaultToolkit().getSystemClipboard();
816 Transferable contents = c.getContents(this);
818 if (contents != null)
820 String file = (String) contents
821 .getTransferData(DataFlavor.stringFlavor);
823 FileFormatI format = new IdentifyFile().identify(file,
824 DataSourceType.PASTE);
826 new FileLoader().loadFile(file, DataSourceType.PASTE, format);
829 } catch (Exception ex)
832 "Unable to paste alignment from system clipboard:\n" + ex);
837 * Adds and opens the given frame to the desktop
848 public static synchronized void addInternalFrame(
849 final JInternalFrame frame, String title, int w, int h)
851 addInternalFrame(frame, title, true, w, h, true, false);
855 * Add an internal frame to the Jalview desktop
862 * When true, display frame immediately, otherwise, caller must call
863 * setVisible themselves.
869 public static synchronized void addInternalFrame(
870 final JInternalFrame frame, String title, boolean makeVisible,
873 addInternalFrame(frame, title, makeVisible, w, h, true, false);
877 * Add an internal frame to the Jalview desktop and make it visible
890 public static synchronized void addInternalFrame(
891 final JInternalFrame frame, String title, int w, int h,
894 addInternalFrame(frame, title, true, w, h, resizable, false);
898 * Add an internal frame to the Jalview desktop
905 * When true, display frame immediately, otherwise, caller must call
906 * setVisible themselves.
913 * @param ignoreMinSize
914 * Do not set the default minimum size for frame
916 public static synchronized void addInternalFrame(
917 final JInternalFrame frame, String title, boolean makeVisible,
918 int w, int h, boolean resizable, boolean ignoreMinSize)
922 // TODO: allow callers to determine X and Y position of frame (eg. via
924 // TODO: consider fixing method to update entries in the window submenu with
925 // the current window title
927 frame.setTitle(title);
928 if (w > 0 && (frame.getWidth() < 1 || frame.getHeight() < 1))
932 // THIS IS A PUBLIC STATIC METHOD, SO IT MAY BE CALLED EVEN IN
933 // A HEADLESS STATE WHEN NO DESKTOP EXISTS. MUST RETURN
934 // IF JALVIEW IS RUNNING HEADLESS
935 // ///////////////////////////////////////////////
936 if (Jalview.isHeadlessMode() || Desktop.getInstance().instanceOnly)
945 frame.setMinimumSize(
946 new Dimension(DEFAULT_MIN_WIDTH, DEFAULT_MIN_HEIGHT));
948 // Set default dimension for Alignment Frame window.
949 // The Alignment Frame window could be added from a number of places,
951 // I did this here in order not to miss out on any Alignment frame.
952 if (frame instanceof AlignFrame)
954 frame.setMinimumSize(new Dimension(ALIGN_FRAME_DEFAULT_MIN_WIDTH,
955 ALIGN_FRAME_DEFAULT_MIN_HEIGHT));
959 frame.setVisible(makeVisible);
960 frame.setClosable(true);
961 frame.setResizable(resizable);
962 frame.setMaximizable(resizable);
963 frame.setIconifiable(resizable);
964 frame.setOpaque(Platform.isJS());
966 boolean isEmbedded = (Platform.getDimIfEmbedded(frame, -1, -1) != null);
967 if (!isEmbedded && frame.getX() < 1 && frame.getY() < 1)
969 frame.setLocation(xOffset * openFrameCount,
970 yOffset * ((openFrameCount - 1) % 10) + yOffset);
974 * add an entry for the new frame in the Window menu
975 * (and remove it when the frame is closed)
977 JMenuItem menuItem = new JMenuItem(title);
978 frame.addInternalFrameListener(new InternalFrameAdapter()
981 public void internalFrameActivated(InternalFrameEvent evt)
983 JInternalFrame itf = getDesktopPane().getSelectedFrame();
986 if (itf instanceof AlignFrame)
988 Jalview.setCurrentAlignFrame((AlignFrame) itf);
995 public void internalFrameClosed(InternalFrameEvent evt)
997 PaintRefresher.RemoveComponent(frame);
1000 * defensive check to prevent frames being
1001 * added half off the window
1003 if (openFrameCount > 0)
1009 * ensure no reference to alignFrame retained by menu item listener
1011 if (menuItem.getActionListeners().length > 0)
1013 menuItem.removeActionListener(menuItem.getActionListeners()[0]);
1015 Desktop.getInstance().windowMenu.remove(menuItem);
1019 menuItem.addActionListener(new ActionListener()
1022 public void actionPerformed(ActionEvent e)
1026 frame.setSelected(true);
1027 frame.setIcon(false);
1028 } catch (java.beans.PropertyVetoException ex)
1030 // System.err.println(ex.toString());
1035 setKeyBindings(frame);
1037 getDesktopPane().add(frame);
1039 Desktop.getInstance().windowMenu.add(menuItem);
1044 frame.setSelected(true);
1045 frame.requestFocus();
1046 } catch (java.beans.PropertyVetoException ve)
1048 } catch (java.lang.ClassCastException cex)
1051 "Squashed a possible GUI implementation error. If you can recreate this, please look at http://issues.jalview.org/browse/JAL-869",
1057 * Add key bindings to a JInternalFrame so that Ctrl-W and Cmd-W will close the
1062 private static void setKeyBindings(JInternalFrame frame)
1064 final Action closeAction = new AbstractAction()
1067 public void actionPerformed(ActionEvent e)
1074 * set up key bindings for Ctrl-W and Cmd-W, with the same (Close) action
1076 KeyStroke ctrlWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
1077 InputEvent.CTRL_DOWN_MASK);
1078 KeyStroke cmdWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
1079 Toolkit.getDefaultToolkit().getMenuShortcutKeyMask());
1081 InputMap inputMap = frame
1082 .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
1083 String ctrlW = ctrlWKey.toString();
1084 inputMap.put(ctrlWKey, ctrlW);
1085 inputMap.put(cmdWKey, ctrlW);
1087 ActionMap actionMap = frame.getActionMap();
1088 actionMap.put(ctrlW, closeAction);
1092 public void lostOwnership(Clipboard clipboard, Transferable contents)
1096 Desktop.getInstance().jalviewClipboard = null;
1099 internalCopy = false;
1103 public void dragEnter(DropTargetDragEvent evt)
1108 public void dragExit(DropTargetEvent evt)
1113 public void dragOver(DropTargetDragEvent evt)
1118 public void dropActionChanged(DropTargetDragEvent evt)
1129 public void drop(DropTargetDropEvent evt)
1131 boolean success = true;
1132 // JAL-1552 - acceptDrop required before getTransferable call for
1133 // Java's Transferable for native dnd
1134 evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
1135 Transferable t = evt.getTransferable();
1136 List<Object> files = new ArrayList<>();
1137 List<DataSourceType> protocols = new ArrayList<>();
1141 Desktop.transferFromDropTarget(files, protocols, evt, t);
1142 } catch (Exception e)
1144 e.printStackTrace();
1152 for (int i = 0; i < files.size(); i++)
1154 // BH 2018 File or String
1155 Object file = files.get(i);
1156 String fileName = file.toString();
1157 DataSourceType protocol = (protocols == null)
1158 ? DataSourceType.FILE
1160 FileFormatI format = null;
1162 if (fileName.endsWith(".jar"))
1164 format = FileFormat.Jalview;
1169 format = new IdentifyFile().identify(file, protocol);
1171 if (file instanceof File)
1173 Platform.cacheFileData((File) file);
1175 new FileLoader().loadFile(null, file, protocol, format);
1178 } catch (Exception ex)
1183 evt.dropComplete(success); // need this to ensure input focus is properly
1184 // transfered to any new windows created
1194 public void inputLocalFileMenuItem_actionPerformed(AlignViewport viewport)
1196 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
1197 JalviewFileChooser chooser = JalviewFileChooser
1198 .forRead(Cache.getProperty("LAST_DIRECTORY"), fileFormat, true);
1200 chooser.setFileView(new JalviewFileView());
1201 chooser.setDialogTitle(
1202 MessageManager.getString("label.open_local_file"));
1203 chooser.setToolTipText(MessageManager.getString("action.open"));
1205 chooser.setResponseHandler(0, new Runnable()
1210 File selectedFile = chooser.getSelectedFile();
1211 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1213 FileFormatI format = chooser.getSelectedFormat();
1216 * Call IdentifyFile to verify the file contains what its extension implies.
1217 * Skip this step for dynamically added file formats, because
1218 * IdentifyFile does not know how to recognise them.
1220 if (FileFormats.getInstance().isIdentifiable(format))
1224 format = new IdentifyFile().identify(selectedFile,
1225 DataSourceType.FILE);
1226 } catch (FileFormatException e)
1228 // format = null; //??
1232 new FileLoader().loadFile(viewport, selectedFile,
1233 DataSourceType.FILE, format);
1236 chooser.showOpenDialog(this);
1240 * Shows a dialog for input of a URL at which to retrieve alignment data
1245 public void inputURLMenuItem_actionPerformed(AlignViewport viewport)
1247 // This construct allows us to have a wider textfield
1249 JLabel label = new JLabel(
1250 MessageManager.getString("label.input_file_url"));
1252 JPanel panel = new JPanel(new GridLayout(2, 1));
1256 * the URL to fetch is
1257 * Java: an editable combobox with history
1258 * JS: (pending JAL-3038) a plain text field
1261 String urlBase = "http://www.";
1262 if (Platform.isJS())
1264 history = new JTextField(urlBase, 35);
1273 JComboBox<String> asCombo = new JComboBox<>();
1274 asCombo.setPreferredSize(new Dimension(400, 20));
1275 asCombo.setEditable(true);
1276 asCombo.addItem(urlBase);
1277 String historyItems = Cache.getProperty("RECENT_URL");
1278 if (historyItems != null)
1280 for (String token : historyItems.split("\\t"))
1282 asCombo.addItem(token);
1289 Object[] options = new Object[] { MessageManager.getString("action.ok"),
1290 MessageManager.getString("action.cancel") };
1291 Runnable action = new Runnable()
1296 @SuppressWarnings("unchecked")
1297 String url = (history instanceof JTextField
1298 ? ((JTextField) history).getText()
1299 : ((JComboBox<String>) history).getSelectedItem()
1302 if (url.toLowerCase().endsWith(".jar"))
1304 if (viewport != null)
1306 new FileLoader().loadFile(viewport, url, DataSourceType.URL,
1307 FileFormat.Jalview);
1311 new FileLoader().loadFile(url, DataSourceType.URL,
1312 FileFormat.Jalview);
1317 FileFormatI format = null;
1320 format = new IdentifyFile().identify(url, DataSourceType.URL);
1321 } catch (FileFormatException e)
1323 // TODO revise error handling, distinguish between
1324 // URL not found and response not valid
1329 String msg = MessageManager
1330 .formatMessage("label.couldnt_locate", url);
1331 JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
1333 MessageManager.getString("label.url_not_found"),
1334 JvOptionPane.WARNING_MESSAGE);
1339 if (viewport != null)
1341 new FileLoader().loadFile(viewport, url, DataSourceType.URL,
1346 new FileLoader().loadFile(url, DataSourceType.URL, format);
1351 String dialogOption = MessageManager
1352 .getString("label.input_alignment_from_url");
1353 JvOptionPane.newOptionDialog(getDesktopPane())
1354 .setResponseHandler(0, action)
1355 .showInternalDialog(panel, dialogOption,
1356 JvOptionPane.YES_NO_CANCEL_OPTION,
1357 JvOptionPane.PLAIN_MESSAGE, null, options,
1358 MessageManager.getString("action.ok"));
1362 * Opens the CutAndPaste window for the user to paste an alignment in to
1365 * - if not null, the pasted alignment is added to the current
1366 * alignment; if null, to a new alignment window
1369 public void inputTextboxMenuItem_actionPerformed(
1370 AlignmentViewPanel viewPanel)
1372 CutAndPasteTransfer cap = new CutAndPasteTransfer();
1373 cap.setForInput(viewPanel);
1374 Desktop.addInternalFrame(cap,
1375 MessageManager.getString("label.cut_paste_alignmen_file"), true,
1385 Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
1386 jalview.bin.Cache.setProperty("SCREENGEOMETRY_WIDTH",
1388 jalview.bin.Cache.setProperty("SCREENGEOMETRY_HEIGHT",
1389 screen.height + "");
1390 storeLastKnownDimensions("", new Rectangle(getBounds().x, getBounds().y,
1391 getWidth(), getHeight()));
1393 if (jconsole != null)
1395 storeLastKnownDimensions("JAVA_CONSOLE_", jconsole.getBounds());
1396 jconsole.stopConsole();
1400 storeLastKnownDimensions("JALVIEW_RSS_WINDOW_", jvnews.getBounds());
1403 if (dialogExecutor != null)
1405 dialogExecutor.shutdownNow();
1407 closeAll_actionPerformed(null);
1409 if (groovyConsole != null)
1411 // suppress a possible repeat prompt to save script
1412 groovyConsole.setDirty(false);
1413 groovyConsole.exit();
1418 private void storeLastKnownDimensions(String string, Rectangle jc)
1420 jalview.bin.Cache.log.debug("Storing last known dimensions for "
1421 + string + ": x:" + jc.x + " y:" + jc.y + " width:" + jc.width
1422 + " height:" + jc.height);
1424 jalview.bin.Cache.setProperty(string + "SCREEN_X", jc.x + "");
1425 jalview.bin.Cache.setProperty(string + "SCREEN_Y", jc.y + "");
1426 jalview.bin.Cache.setProperty(string + "SCREEN_WIDTH", jc.width + "");
1427 jalview.bin.Cache.setProperty(string + "SCREEN_HEIGHT", jc.height + "");
1437 public void aboutMenuItem_actionPerformed(ActionEvent e)
1439 // StringBuffer message = getAboutMessage(false);
1440 // JvOptionPane.showInternalMessageDialog(Desktop.getDesktop(),
1442 // message.toString(), "About Jalview", JvOptionPane.INFORMATION_MESSAGE);
1443 new Thread(new Runnable()
1448 new SplashScreen(true);
1453 public StringBuffer getAboutMessage(boolean shortv)
1455 StringBuffer message = new StringBuffer();
1456 message.append("<html>");
1459 message.append("<h1><strong>Version: "
1460 + jalview.bin.Cache.getProperty("VERSION")
1461 + "</strong></h1>");
1462 message.append("<strong>Last Updated: <em>"
1463 + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown")
1464 + "</em></strong>");
1470 message.append("<strong>Version "
1471 + jalview.bin.Cache.getProperty("VERSION")
1472 + "; last updated: "
1473 + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown"));
1476 if (jalview.bin.Cache.getDefault("LATEST_VERSION", "Checking")
1477 .equals("Checking"))
1479 message.append("<br>...Checking latest version...</br>");
1481 else if (!jalview.bin.Cache.getDefault("LATEST_VERSION", "Checking")
1482 .equals(jalview.bin.Cache.getProperty("VERSION")))
1484 boolean red = false;
1485 if (jalview.bin.Cache.getProperty("VERSION").toLowerCase()
1486 .indexOf("automated build") == -1)
1489 // Displayed when code version and jnlp version do not match and code
1490 // version is not a development build
1491 message.append("<div style=\"color: #FF0000;font-style: bold;\">");
1494 message.append("<br>!! Version "
1495 + jalview.bin.Cache.getDefault("LATEST_VERSION",
1497 + " is available for download from "
1498 + jalview.bin.Cache.getDefault("www.jalview.org",
1499 "http://www.jalview.org")
1503 message.append("</div>");
1506 message.append("<br>Authors: " + jalview.bin.Cache.getDefault(
1508 "The Jalview Authors (See AUTHORS file for current list)")
1509 + "<br><br>Development managed by The Barton Group, University of Dundee, Scotland, UK.<br>"
1510 + "<br><br>For help, see the FAQ at <a href=\"http://www.jalview.org/faq\">www.jalview.org/faq</a> and/or join the jalview-discuss@jalview.org mailing list"
1511 + "<br><br>If you use Jalview, please cite:"
1512 + "<br>Waterhouse, A.M., Procter, J.B., Martin, D.M.A, Clamp, M. and Barton, G. J. (2009)"
1513 + "<br>Jalview Version 2 - a multiple sequence alignment editor and analysis workbench"
1514 + "<br>Bioinformatics doi: 10.1093/bioinformatics/btp033"
1520 * Action on requesting Help documentation
1523 public void documentationMenuItem_actionPerformed()
1527 if (Platform.isJS())
1529 BrowserLauncher.openURL("http://www.jalview.org/help.html");
1538 Help.showHelpWindow();
1540 } catch (Exception ex)
1542 System.err.println("Error opening help: " + ex.getMessage());
1547 public void closeAll_actionPerformed(ActionEvent e)
1549 if (desktopPane == null)
1553 // TODO show a progress bar while closing?
1554 JInternalFrame[] frames = desktopPane.getAllFrames();
1555 for (int i = 0; i < frames.length; i++)
1559 frames[i].setClosed(true);
1560 } catch (java.beans.PropertyVetoException ex)
1564 Jalview.setCurrentAlignFrame(null);
1565 System.out.println("ALL CLOSED");
1566 if (v_client != null)
1568 // TODO clear binding to vamsas document objects on close_all
1572 * reset state of singleton objects as appropriate (clear down session state
1573 * when all windows are closed)
1575 getStructureSelectionManager().resetAll();
1579 public void raiseRelated_actionPerformed(ActionEvent e)
1581 reorderAssociatedWindows(false, false);
1585 public void minimizeAssociated_actionPerformed(ActionEvent e)
1587 reorderAssociatedWindows(true, false);
1590 void closeAssociatedWindows()
1592 reorderAssociatedWindows(false, true);
1598 * @seejalview.jbgui.GDesktop#garbageCollect_actionPerformed(java.awt.event.
1602 protected void garbageCollect_actionPerformed(ActionEvent e)
1604 // We simply collect the garbage
1605 jalview.bin.Cache.log.debug("Collecting garbage...");
1607 jalview.bin.Cache.log.debug("Finished garbage collection.");
1614 * jalview.jbgui.GDesktop#showMemusage_actionPerformed(java.awt.event.ActionEvent
1618 protected void showMemusage_actionPerformed(ActionEvent e)
1620 getDesktopPane().showMemoryUsage(showMemusage.isSelected());
1627 * jalview.jbgui.GDesktop#showConsole_actionPerformed(java.awt.event.ActionEvent
1631 protected void showConsole_actionPerformed(ActionEvent e)
1633 showConsole(showConsole.isSelected());
1636 Console jconsole = null;
1639 * control whether the java console is visible or not
1643 void showConsole(boolean selected)
1645 // TODO: decide if we should update properties file
1646 if (jconsole != null) // BH 2018
1648 showConsole.setSelected(selected);
1649 Cache.setProperty("SHOW_JAVA_CONSOLE",
1650 Boolean.valueOf(selected).toString());
1651 jconsole.setVisible(selected);
1655 void reorderAssociatedWindows(boolean minimize, boolean close)
1657 JInternalFrame[] frames = getDesktopPane().getAllFrames();
1658 if (frames == null || frames.length < 1)
1663 AlignmentViewport source = null, target = null;
1664 if (frames[0] instanceof AlignFrame)
1666 source = ((AlignFrame) frames[0]).getCurrentView();
1668 else if (frames[0] instanceof TreePanel)
1670 source = ((TreePanel) frames[0]).getViewPort();
1672 else if (frames[0] instanceof PCAPanel)
1674 source = ((PCAPanel) frames[0]).av;
1676 else if (frames[0].getContentPane() instanceof PairwiseAlignPanel)
1678 source = ((PairwiseAlignPanel) frames[0].getContentPane()).av;
1683 for (int i = 0; i < frames.length; i++)
1686 if (frames[i] == null)
1690 if (frames[i] instanceof AlignFrame)
1692 target = ((AlignFrame) frames[i]).getCurrentView();
1694 else if (frames[i] instanceof TreePanel)
1696 target = ((TreePanel) frames[i]).getViewPort();
1698 else if (frames[i] instanceof PCAPanel)
1700 target = ((PCAPanel) frames[i]).av;
1702 else if (frames[i].getContentPane() instanceof PairwiseAlignPanel)
1704 target = ((PairwiseAlignPanel) frames[i].getContentPane()).av;
1707 if (source == target)
1713 frames[i].setClosed(true);
1717 frames[i].setIcon(minimize);
1720 frames[i].toFront();
1724 } catch (java.beans.PropertyVetoException ex)
1739 protected void preferences_actionPerformed(ActionEvent e)
1745 * Prompts the user to choose a file and then saves the Jalview state as a
1746 * Jalview project file
1749 public void saveState_actionPerformed()
1751 saveState_actionPerformed(false);
1754 public void saveState_actionPerformed(boolean saveAs)
1756 java.io.File projectFile = getProjectFile();
1757 // autoSave indicates we already have a file and don't need to ask
1758 boolean autoSave = projectFile != null && !saveAs
1759 && BackupFiles.getEnabled();
1761 // System.out.println("autoSave="+autoSave+", projectFile='"+projectFile+"',
1762 // saveAs="+saveAs+", Backups
1763 // "+(BackupFiles.getEnabled()?"enabled":"disabled"));
1765 boolean approveSave = false;
1768 JalviewFileChooser chooser = new JalviewFileChooser("jvp",
1771 chooser.setFileView(new JalviewFileView());
1772 chooser.setDialogTitle(MessageManager.getString("label.save_state"));
1774 int value = chooser.showSaveDialog(this);
1776 if (value == JalviewFileChooser.APPROVE_OPTION)
1778 projectFile = chooser.getSelectedFile();
1779 setProjectFile(projectFile);
1784 if (approveSave || autoSave)
1786 final Desktop me = this;
1787 final java.io.File chosenFile = projectFile;
1788 new Thread(new Runnable()
1793 // TODO: refactor to Jalview desktop session controller action.
1794 setProgressBar(MessageManager.formatMessage(
1795 "label.saving_jalview_project", new Object[]
1796 { chosenFile.getName() }), chosenFile.hashCode());
1797 jalview.bin.Cache.setProperty("LAST_DIRECTORY",
1798 chosenFile.getParent());
1799 // TODO catch and handle errors for savestate
1800 // TODO prevent user from messing with the Desktop whilst we're saving
1803 boolean doBackup = BackupFiles.getEnabled();
1804 BackupFiles backupfiles = doBackup ? new BackupFiles(chosenFile) : null;
1806 new Jalview2XML().saveState(doBackup ? backupfiles.getTempFile() : chosenFile);
1810 backupfiles.setWriteSuccess(true);
1811 backupfiles.rollBackupsAndRenameTempFile();
1813 } catch (OutOfMemoryError oom)
1815 new OOMWarning("Whilst saving current state to "
1816 + chosenFile.getName(), oom);
1817 } catch (Exception ex)
1819 Cache.log.error("Problems whilst trying to save to "
1820 + chosenFile.getName(), ex);
1821 JvOptionPane.showMessageDialog(me,
1822 MessageManager.formatMessage(
1823 "label.error_whilst_saving_current_state_to",
1825 { chosenFile.getName() }),
1826 MessageManager.getString("label.couldnt_save_project"),
1827 JvOptionPane.WARNING_MESSAGE);
1829 setProgressBar(null, chosenFile.hashCode());
1836 public void saveAsState_actionPerformed(ActionEvent e)
1838 saveState_actionPerformed(true);
1841 protected void setProjectFile(File choice)
1843 this.projectFile = choice;
1846 public File getProjectFile()
1848 return this.projectFile;
1852 * Shows a file chooser dialog and tries to read in the selected file as a
1856 public void loadState_actionPerformed()
1858 final String[] suffix = new String[] { "jvp", "jar" };
1859 final String[] desc = new String[] { "Jalview Project",
1860 "Jalview Project (old)" };
1861 JalviewFileChooser chooser = new JalviewFileChooser(
1862 Cache.getProperty("LAST_DIRECTORY"), suffix, desc,
1863 "Jalview Project", true, true); // last two booleans: allFiles,
1865 chooser.setFileView(new JalviewFileView());
1866 chooser.setDialogTitle(MessageManager.getString("label.restore_state"));
1867 chooser.setResponseHandler(0, new Runnable()
1872 File selectedFile = chooser.getSelectedFile();
1873 setProjectFile(selectedFile);
1874 String choice = selectedFile.getAbsolutePath();
1875 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1876 new Thread(new Runnable()
1883 new Jalview2XML().loadJalviewAlign(choice);
1884 } catch (OutOfMemoryError oom)
1886 new OOMWarning("Whilst loading project from " + choice, oom);
1887 } catch (Exception ex)
1890 "Problems whilst loading project from " + choice, ex);
1891 JvOptionPane.showMessageDialog(Desktop.getDesktopPane(),
1892 MessageManager.formatMessage(
1893 "label.error_whilst_loading_project_from",
1896 MessageManager.getString("label.couldnt_load_project"),
1897 JvOptionPane.WARNING_MESSAGE);
1904 chooser.showOpenDialog(this);
1908 public void inputSequence_actionPerformed(ActionEvent e)
1910 new SequenceFetcher(this);
1913 JPanel progressPanel;
1915 ArrayList<JPanel> fileLoadingPanels = new ArrayList<>();
1917 public void startLoading(final Object fileName)
1919 if (fileLoadingCount == 0)
1921 fileLoadingPanels.add(addProgressPanel(MessageManager
1922 .formatMessage("label.loading_file", new Object[]
1928 private JPanel addProgressPanel(String string)
1930 if (progressPanel == null)
1932 progressPanel = new JPanel(new GridLayout(1, 1));
1933 totalProgressCount = 0;
1934 getContentPane().add(progressPanel, BorderLayout.SOUTH);
1936 JPanel thisprogress = new JPanel(new BorderLayout(10, 5));
1937 JProgressBar progressBar = new JProgressBar();
1938 progressBar.setIndeterminate(true);
1940 thisprogress.add(new JLabel(string), BorderLayout.WEST);
1942 thisprogress.add(progressBar, BorderLayout.CENTER);
1943 progressPanel.add(thisprogress);
1944 ((GridLayout) progressPanel.getLayout()).setRows(
1945 ((GridLayout) progressPanel.getLayout()).getRows() + 1);
1946 ++totalProgressCount;
1948 return thisprogress;
1951 int totalProgressCount = 0;
1953 private void removeProgressPanel(JPanel progbar)
1955 if (progressPanel != null)
1957 synchronized (progressPanel)
1959 progressPanel.remove(progbar);
1960 GridLayout gl = (GridLayout) progressPanel.getLayout();
1961 gl.setRows(gl.getRows() - 1);
1962 if (--totalProgressCount < 1)
1964 this.getContentPane().remove(progressPanel);
1965 progressPanel = null;
1972 public void stopLoading()
1975 if (fileLoadingCount < 1)
1977 while (fileLoadingPanels.size() > 0)
1979 removeProgressPanel(fileLoadingPanels.remove(0));
1981 fileLoadingPanels.clear();
1982 fileLoadingCount = 0;
1987 public static int getViewCount(String alignmentId)
1989 AlignmentViewport[] aps = getViewports(alignmentId);
1990 return (aps == null) ? 0 : aps.length;
1995 * @param alignmentId
1996 * - if null, all sets are returned
1997 * @return all AlignmentPanels concerning the alignmentId sequence set
1999 public static AlignmentPanel[] getAlignmentPanels(String alignmentId)
2001 if (Desktop.getDesktopPane() == null)
2003 // no frames created and in headless mode
2004 // TODO: verify that frames are recoverable when in headless mode
2007 List<AlignmentPanel> aps = new ArrayList<>();
2008 AlignFrame[] frames = getAlignFrames();
2013 for (AlignFrame af : frames)
2015 for (AlignmentPanel ap : af.alignPanels)
2017 if (alignmentId == null
2018 || alignmentId.equals(ap.av.getSequenceSetId()))
2024 if (aps.size() == 0)
2028 AlignmentPanel[] vap = aps.toArray(new AlignmentPanel[aps.size()]);
2033 * get all the viewports on an alignment.
2035 * @param sequenceSetId
2036 * unique alignment id (may be null - all viewports returned in that
2038 * @return all viewports on the alignment bound to sequenceSetId
2040 public static AlignmentViewport[] getViewports(String sequenceSetId)
2042 List<AlignmentViewport> viewp = new ArrayList<>();
2043 if (getDesktopPane() != null)
2045 AlignFrame[] frames = Desktop.getAlignFrames();
2047 for (AlignFrame afr : frames)
2049 if (sequenceSetId == null || afr.getViewport().getSequenceSetId()
2050 .equals(sequenceSetId))
2052 if (afr.alignPanels != null)
2054 for (AlignmentPanel ap : afr.alignPanels)
2056 if (sequenceSetId == null
2057 || sequenceSetId.equals(ap.av.getSequenceSetId()))
2065 viewp.add(afr.getViewport());
2069 if (viewp.size() > 0)
2071 return viewp.toArray(new AlignmentViewport[viewp.size()]);
2078 * Explode the views in the given frame into separate AlignFrame
2082 public static void explodeViews(AlignFrame af)
2084 int size = af.alignPanels.size();
2090 for (int i = 0; i < size; i++)
2092 AlignmentPanel ap = af.alignPanels.get(i);
2093 AlignFrame newaf = new AlignFrame(ap);
2096 * Restore the view's last exploded frame geometry if known. Multiple
2097 * views from one exploded frame share and restore the same (frame)
2098 * position and size.
2100 Rectangle geometry = ap.av.getExplodedGeometry();
2101 if (geometry != null)
2103 newaf.setBounds(geometry);
2106 ap.av.setGatherViewsHere(false);
2108 addInternalFrame(newaf, af.getTitle(), AlignFrame.DEFAULT_WIDTH,
2109 AlignFrame.DEFAULT_HEIGHT);
2112 af.alignPanels.clear();
2113 af.closeMenuItem_actionPerformed(true);
2118 * Gather expanded views (separate AlignFrame's) with the same sequence set
2119 * identifier back in to this frame as additional views, and close the expanded
2120 * views. Note the expanded frames may themselves have multiple views. We take
2125 public void gatherViews(AlignFrame source)
2127 source.viewport.setGatherViewsHere(true);
2128 source.viewport.setExplodedGeometry(source.getBounds());
2129 JInternalFrame[] frames = getAllFrames();
2130 String viewId = source.viewport.getSequenceSetId();
2132 for (int t = 0; t < frames.length; t++)
2134 if (frames[t] instanceof AlignFrame && frames[t] != source)
2136 AlignFrame af = (AlignFrame) frames[t];
2137 boolean gatherThis = false;
2138 for (int a = 0; a < af.alignPanels.size(); a++)
2140 AlignmentPanel ap = af.alignPanels.get(a);
2141 if (viewId.equals(ap.av.getSequenceSetId()))
2144 ap.av.setGatherViewsHere(false);
2145 ap.av.setExplodedGeometry(af.getBounds());
2146 source.addAlignmentPanel(ap, false);
2152 af.alignPanels.clear();
2153 af.closeMenuItem_actionPerformed(true);
2160 jalview.gui.VamsasApplication v_client = null;
2163 public void vamsasImport_actionPerformed(ActionEvent e)
2165 // TODO: JAL-3048 not needed for Jalview-JS
2167 if (v_client == null)
2169 // Load and try to start a session.
2170 JalviewFileChooser chooser = new JalviewFileChooser(
2171 jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
2173 chooser.setFileView(new JalviewFileView());
2174 chooser.setDialogTitle(
2175 MessageManager.getString("label.open_saved_vamsas_session"));
2176 chooser.setToolTipText(MessageManager.getString(
2177 "label.select_vamsas_session_opened_as_new_vamsas_session"));
2179 int value = chooser.showOpenDialog(this);
2181 if (value == JalviewFileChooser.APPROVE_OPTION)
2183 String fle = chooser.getSelectedFile().toString();
2184 if (!vamsasImport(chooser.getSelectedFile()))
2186 JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
2187 MessageManager.formatMessage(
2188 "label.couldnt_import_as_vamsas_session",
2192 .getString("label.vamsas_document_import_failed"),
2193 JvOptionPane.ERROR_MESSAGE);
2199 jalview.bin.Cache.log.error(
2200 "Implementation error - load session from a running session is not supported.");
2205 * import file into a new vamsas session (uses jalview.gui.VamsasApplication)
2208 * @return true if import was a success and a session was started.
2210 public boolean vamsasImport(URL url)
2212 // TODO: create progress bar
2213 if (v_client != null)
2216 jalview.bin.Cache.log.error(
2217 "Implementation error - load session from a running session is not supported.");
2223 // copy the URL content to a temporary local file
2224 // TODO: be a bit cleverer here with nio (?!)
2225 File file = File.createTempFile("vdocfromurl", ".vdj");
2226 FileOutputStream fos = new FileOutputStream(file);
2227 BufferedInputStream bis = new BufferedInputStream(url.openStream());
2228 byte[] buffer = new byte[2048];
2230 while ((ln = bis.read(buffer)) > -1)
2232 fos.write(buffer, 0, ln);
2236 v_client = new jalview.gui.VamsasApplication(this, file,
2237 url.toExternalForm());
2238 } catch (Exception ex)
2240 jalview.bin.Cache.log.error(
2241 "Failed to create new vamsas session from contents of URL "
2246 setupVamsasConnectedGui();
2247 v_client.initial_update(); // TODO: thread ?
2248 return v_client.inSession();
2252 * import file into a new vamsas session (uses jalview.gui.VamsasApplication)
2255 * @return true if import was a success and a session was started.
2257 public boolean vamsasImport(File file)
2259 if (v_client != null)
2262 jalview.bin.Cache.log.error(
2263 "Implementation error - load session from a running session is not supported.");
2267 setProgressBar(MessageManager.formatMessage(
2268 "status.importing_vamsas_session_from", new Object[]
2269 { file.getName() }), file.hashCode());
2272 v_client = new jalview.gui.VamsasApplication(this, file, null);
2273 } catch (Exception ex)
2275 setProgressBar(MessageManager.formatMessage(
2276 "status.importing_vamsas_session_from", new Object[]
2277 { file.getName() }), file.hashCode());
2278 jalview.bin.Cache.log.error(
2279 "New vamsas session from existing session file failed:", ex);
2282 setupVamsasConnectedGui();
2283 v_client.initial_update(); // TODO: thread ?
2284 setProgressBar(MessageManager.formatMessage(
2285 "status.importing_vamsas_session_from", new Object[]
2286 { file.getName() }), file.hashCode());
2287 return v_client.inSession();
2290 public boolean joinVamsasSession(String mysesid)
2292 if (v_client != null)
2294 throw new Error(MessageManager
2295 .getString("error.try_join_vamsas_session_another"));
2297 if (mysesid == null)
2300 MessageManager.getString("error.invalid_vamsas_session_id"));
2302 v_client = new VamsasApplication(this, mysesid);
2303 setupVamsasConnectedGui();
2304 v_client.initial_update();
2305 return (v_client.inSession());
2309 public void vamsasStart_actionPerformed(ActionEvent e)
2311 if (v_client == null)
2314 // we just start a default session for moment.
2316 * JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache.
2317 * getProperty("LAST_DIRECTORY"));
2319 * chooser.setFileView(new JalviewFileView());
2320 * chooser.setDialogTitle("Load Vamsas file");
2321 * chooser.setToolTipText("Import");
2323 * int value = chooser.showOpenDialog(this);
2325 * if (value == JalviewFileChooser.APPROVE_OPTION) { v_client = new
2326 * jalview.gui.VamsasApplication(this, chooser.getSelectedFile());
2328 v_client = new VamsasApplication(this);
2329 setupVamsasConnectedGui();
2330 v_client.initial_update(); // TODO: thread ?
2334 // store current data in session.
2335 v_client.push_update(); // TODO: thread
2339 protected void setupVamsasConnectedGui()
2341 vamsasStart.setText(MessageManager.getString("label.session_update"));
2342 vamsasSave.setVisible(true);
2343 vamsasStop.setVisible(true);
2344 vamsasImport.setVisible(false); // Document import to existing session is
2345 // not possible for vamsas-client-1.0.
2348 protected void setupVamsasDisconnectedGui()
2350 vamsasSave.setVisible(false);
2351 vamsasStop.setVisible(false);
2352 vamsasImport.setVisible(true);
2354 .setText(MessageManager.getString("label.new_vamsas_session"));
2358 public void vamsasStop_actionPerformed(ActionEvent e)
2360 if (v_client != null)
2362 v_client.end_session();
2364 setupVamsasDisconnectedGui();
2368 protected void buildVamsasStMenu()
2370 if (v_client == null)
2372 String[] sess = null;
2375 sess = VamsasApplication.getSessionList();
2376 } catch (Exception e)
2378 jalview.bin.Cache.log.warn("Problem getting current sessions list.",
2384 jalview.bin.Cache.log.debug(
2385 "Got current sessions list: " + sess.length + " entries.");
2386 VamsasStMenu.removeAll();
2387 for (int i = 0; i < sess.length; i++)
2389 JMenuItem sessit = new JMenuItem();
2390 sessit.setText(sess[i]);
2391 sessit.setToolTipText(MessageManager
2392 .formatMessage("label.connect_to_session", new Object[]
2394 final Desktop dsktp = this;
2395 final String mysesid = sess[i];
2396 sessit.addActionListener(new ActionListener()
2400 public void actionPerformed(ActionEvent e)
2402 if (dsktp.v_client == null)
2404 Thread rthr = new Thread(new Runnable()
2410 dsktp.v_client = new VamsasApplication(dsktp, mysesid);
2411 dsktp.setupVamsasConnectedGui();
2412 dsktp.v_client.initial_update();
2420 VamsasStMenu.add(sessit);
2422 // don't show an empty menu.
2423 VamsasStMenu.setVisible(sess.length > 0);
2428 jalview.bin.Cache.log.debug("No current vamsas sessions.");
2429 VamsasStMenu.removeAll();
2430 VamsasStMenu.setVisible(false);
2435 // Not interested in the content. Just hide ourselves.
2436 VamsasStMenu.setVisible(false);
2441 public void vamsasSave_actionPerformed(ActionEvent e)
2443 // TODO: JAL-3048 not needed for Jalview-JS
2445 if (v_client != null)
2447 // TODO: VAMSAS DOCUMENT EXTENSION is VDJ
2448 JalviewFileChooser chooser = new JalviewFileChooser("vdj",
2451 chooser.setFileView(new JalviewFileView());
2452 chooser.setDialogTitle(MessageManager
2453 .getString("label.save_vamsas_document_archive"));
2455 int value = chooser.showSaveDialog(this);
2457 if (value == JalviewFileChooser.APPROVE_OPTION)
2459 java.io.File choice = chooser.getSelectedFile();
2460 JPanel progpanel = addProgressPanel(MessageManager
2461 .formatMessage("label.saving_vamsas_doc", new Object[]
2462 { choice.getName() }));
2463 Cache.setProperty("LAST_DIRECTORY", choice.getParent());
2464 String warnmsg = null;
2465 String warnttl = null;
2468 v_client.vclient.storeDocument(choice);
2471 warnttl = "Serious Problem saving Vamsas Document";
2472 warnmsg = ex.toString();
2473 jalview.bin.Cache.log
2474 .error("Error Whilst saving document to " + choice, ex);
2476 } catch (Exception ex)
2478 warnttl = "Problem saving Vamsas Document.";
2479 warnmsg = ex.toString();
2480 jalview.bin.Cache.log.warn(
2481 "Exception Whilst saving document to " + choice, ex);
2484 removeProgressPanel(progpanel);
2485 if (warnmsg != null)
2487 JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
2489 warnmsg, warnttl, JvOptionPane.ERROR_MESSAGE);
2495 JPanel vamUpdate = null;
2498 * hide vamsas user gui bits when a vamsas document event is being handled.
2501 * true to hide gui, false to reveal gui
2503 public void setVamsasUpdate(boolean b)
2505 Cache.log.debug("Setting gui for Vamsas update "
2506 + (b ? "in progress" : "finished"));
2508 if (vamUpdate != null)
2510 this.removeProgressPanel(vamUpdate);
2514 vamUpdate = this.addProgressPanel(
2515 MessageManager.getString("label.updating_vamsas_session"));
2517 vamsasStart.setVisible(!b);
2518 vamsasStop.setVisible(!b);
2519 vamsasSave.setVisible(!b);
2522 public JInternalFrame[] getAllFrames()
2524 return desktopPane.getAllFrames();
2528 * Checks the given url to see if it gives a response indicating that the user
2529 * should be informed of a new questionnaire.
2533 public void checkForQuestionnaire(String url)
2535 UserQuestionnaireCheck jvq = new UserQuestionnaireCheck(url);
2536 // javax.swing.SwingUtilities.invokeLater(jvq);
2537 new Thread(jvq).start();
2540 public void checkURLLinks()
2542 // Thread off the URL link checker
2543 addDialogThread(new Runnable()
2548 if (Cache.getDefault("CHECKURLLINKS", true))
2550 // check what the actual links are - if it's just the default don't
2551 // bother with the warning
2552 List<String> links = Preferences.sequenceUrlLinks
2555 // only need to check links if there is one with a
2556 // SEQUENCE_ID which is not the default EMBL_EBI link
2557 ListIterator<String> li = links.listIterator();
2558 boolean check = false;
2559 List<JLabel> urls = new ArrayList<>();
2560 while (li.hasNext())
2562 String link = li.next();
2563 if (link.contains(UrlConstants.SEQUENCE_ID)
2564 && !UrlConstants.isDefaultString(link))
2567 int barPos = link.indexOf("|");
2568 String urlMsg = barPos == -1 ? link
2569 : link.substring(0, barPos) + ": "
2570 + link.substring(barPos + 1);
2571 urls.add(new JLabel(urlMsg));
2579 // ask user to check in case URL links use old style tokens
2580 // ($SEQUENCE_ID$ for sequence id _or_ accession id)
2581 JPanel msgPanel = new JPanel();
2582 msgPanel.setLayout(new BoxLayout(msgPanel, BoxLayout.PAGE_AXIS));
2583 msgPanel.add(Box.createVerticalGlue());
2584 JLabel msg = new JLabel(MessageManager
2585 .getString("label.SEQUENCE_ID_for_DB_ACCESSION1"));
2586 JLabel msg2 = new JLabel(MessageManager
2587 .getString("label.SEQUENCE_ID_for_DB_ACCESSION2"));
2589 for (JLabel url : urls)
2595 final JCheckBox jcb = new JCheckBox(
2596 MessageManager.getString("label.do_not_display_again"));
2597 jcb.addActionListener(new ActionListener()
2600 public void actionPerformed(ActionEvent e)
2602 // update Cache settings for "don't show this again"
2603 boolean showWarningAgain = !jcb.isSelected();
2604 Cache.setProperty("CHECKURLLINKS",
2605 Boolean.valueOf(showWarningAgain).toString());
2610 JvOptionPane.showMessageDialog(desktopPane, msgPanel,
2612 .getString("label.SEQUENCE_ID_no_longer_used"),
2613 JvOptionPane.WARNING_MESSAGE);
2620 * Proxy class for JDesktopPane which optionally displays the current memory
2621 * usage and highlights the desktop area with a red bar if free memory runs low.
2625 public class MyDesktopPane extends JDesktopPane
2628 private static final float ONE_MB = 1048576f;
2630 boolean showMemoryUsage = false;
2634 java.text.NumberFormat df;
2636 float maxMemory, allocatedMemory, freeMemory, totalFreeMemory,
2639 public MyDesktopPane(boolean showMemoryUsage)
2641 showMemoryUsage(showMemoryUsage);
2644 public void showMemoryUsage(boolean showMemory)
2646 this.showMemoryUsage = showMemory;
2649 Thread worker = new Thread(this);
2655 public boolean isShowMemoryUsage()
2657 return showMemoryUsage;
2663 df = java.text.NumberFormat.getNumberInstance();
2664 df.setMaximumFractionDigits(2);
2665 runtime = Runtime.getRuntime();
2667 while (showMemoryUsage)
2671 maxMemory = runtime.maxMemory() / ONE_MB;
2672 allocatedMemory = runtime.totalMemory() / ONE_MB;
2673 freeMemory = runtime.freeMemory() / ONE_MB;
2674 totalFreeMemory = freeMemory + (maxMemory - allocatedMemory);
2676 percentUsage = (totalFreeMemory / maxMemory) * 100;
2678 // if (percentUsage < 20)
2680 // border1 = BorderFactory.createMatteBorder(12, 12, 12, 12,
2682 // instance.set.setBorder(border1);
2685 // sleep after showing usage
2687 } catch (Exception ex)
2689 ex.printStackTrace();
2695 public void paintComponent(Graphics g)
2697 if (showMemoryUsage && g != null && df != null)
2699 if (percentUsage < 20)
2701 g.setColor(Color.red);
2703 FontMetrics fm = g.getFontMetrics();
2706 g.drawString(MessageManager.formatMessage("label.memory_stats",
2708 { df.format(totalFreeMemory), df.format(maxMemory),
2709 df.format(percentUsage) }),
2710 10, getHeight() - fm.getHeight());
2717 * Accessor method to quickly get all the AlignmentFrames loaded.
2719 * @return an array of AlignFrame, or null if none found
2721 public static AlignFrame[] getAlignFrames()
2723 if (Jalview.isHeadlessMode())
2725 // Desktop.getDesktop() is null in headless mode
2726 return new AlignFrame[] { Jalview.getCurrentAlignFrame() };
2729 JInternalFrame[] frames = Desktop.getDesktopPane().getAllFrames();
2735 List<AlignFrame> avp = new ArrayList<>();
2737 for (int i = frames.length - 1; i > -1; i--)
2739 if (frames[i] instanceof AlignFrame)
2741 avp.add((AlignFrame) frames[i]);
2743 else if (frames[i] instanceof SplitFrame)
2746 * Also check for a split frame containing an AlignFrame
2748 GSplitFrame sf = (GSplitFrame) frames[i];
2749 if (sf.getTopFrame() instanceof AlignFrame)
2751 avp.add((AlignFrame) sf.getTopFrame());
2753 if (sf.getBottomFrame() instanceof AlignFrame)
2755 avp.add((AlignFrame) sf.getBottomFrame());
2759 if (avp.size() == 0)
2763 AlignFrame afs[] = avp.toArray(new AlignFrame[avp.size()]);
2768 * Returns an array of any AppJmol frames in the Desktop (or null if none).
2772 public GStructureViewer[] getJmols()
2774 JInternalFrame[] frames = Desktop.getDesktopPane().getAllFrames();
2780 List<GStructureViewer> avp = new ArrayList<>();
2782 for (int i = frames.length - 1; i > -1; i--)
2784 if (frames[i] instanceof AppJmol)
2786 GStructureViewer af = (GStructureViewer) frames[i];
2790 if (avp.size() == 0)
2794 GStructureViewer afs[] = avp.toArray(new GStructureViewer[avp.size()]);
2799 * Add Groovy Support to Jalview
2802 public void groovyShell_actionPerformed()
2806 openGroovyConsole();
2807 } catch (Exception ex)
2809 jalview.bin.Cache.log.error("Groovy Shell Creation failed.", ex);
2810 JvOptionPane.showInternalMessageDialog(desktopPane,
2812 MessageManager.getString("label.couldnt_create_groovy_shell"),
2813 MessageManager.getString("label.groovy_support_failed"),
2814 JvOptionPane.ERROR_MESSAGE);
2819 * Open the Groovy console
2821 private void openGroovyConsole()
2823 if (groovyConsole == null)
2825 groovyConsole = new groovy.ui.Console();
2826 groovyConsole.setVariable("Jalview", this);
2827 groovyConsole.run();
2830 * We allow only one console at a time, so that AlignFrame menu option
2831 * 'Calculate | Run Groovy script' is unambiguous.
2832 * Disable 'Groovy Console', and enable 'Run script', when the console is
2833 * opened, and the reverse when it is closed
2835 Window window = (Window) groovyConsole.getFrame();
2836 window.addWindowListener(new WindowAdapter()
2839 public void windowClosed(WindowEvent e)
2842 * rebind CMD-Q from Groovy Console to Jalview Quit
2845 enableExecuteGroovy(false);
2851 * show Groovy console window (after close and reopen)
2853 ((Window) groovyConsole.getFrame()).setVisible(true);
2856 * if we got this far, enable 'Run Groovy' in AlignFrame menus
2857 * and disable opening a second console
2859 enableExecuteGroovy(true);
2863 * Bind Ctrl/Cmd-Q to Quit - for reset as Groovy Console takes over this binding
2866 protected void addQuitHandler()
2868 getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
2869 .put(KeyStroke.getKeyStroke(KeyEvent.VK_Q,
2870 Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()),
2872 getRootPane().getActionMap().put("Quit", new AbstractAction()
2875 public void actionPerformed(ActionEvent e)
2883 * Enable or disable 'Run Groovy script' in AlignFrame calculate menus
2886 * true if Groovy console is open
2888 public void enableExecuteGroovy(boolean enabled)
2891 * disable opening a second Groovy console
2892 * (or re-enable when the console is closed)
2894 groovyShell.setEnabled(!enabled);
2896 AlignFrame[] alignFrames = getAlignFrames();
2897 if (alignFrames != null)
2899 for (AlignFrame af : alignFrames)
2901 af.setGroovyEnabled(enabled);
2907 * Progress bars managed by the IProgressIndicator method.
2909 private Hashtable<Long, JPanel> progressBars;
2911 private Hashtable<Long, IProgressIndicatorHandler> progressBarHandlers;
2916 * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
2919 public void setProgressBar(String message, long id)
2921 Platform.timeCheck("Desktop " + message, Platform.TIME_MARK);
2923 if (progressBars == null)
2925 progressBars = new Hashtable<>();
2926 progressBarHandlers = new Hashtable<>();
2929 if (progressBars.get(new Long(id)) != null)
2931 JPanel panel = progressBars.remove(new Long(id));
2932 if (progressBarHandlers.contains(new Long(id)))
2934 progressBarHandlers.remove(new Long(id));
2936 removeProgressPanel(panel);
2940 progressBars.put(new Long(id), addProgressPanel(message));
2947 * @see jalview.gui.IProgressIndicator#registerHandler(long,
2948 * jalview.gui.IProgressIndicatorHandler)
2951 public void registerHandler(final long id,
2952 final IProgressIndicatorHandler handler)
2954 if (progressBarHandlers == null
2955 || !progressBars.containsKey(new Long(id)))
2957 throw new Error(MessageManager.getString(
2958 "error.call_setprogressbar_before_registering_handler"));
2960 progressBarHandlers.put(new Long(id), handler);
2961 final JPanel progressPanel = progressBars.get(new Long(id));
2962 if (handler.canCancel())
2964 JButton cancel = new JButton(
2965 MessageManager.getString("action.cancel"));
2966 final IProgressIndicator us = this;
2967 cancel.addActionListener(new ActionListener()
2971 public void actionPerformed(ActionEvent e)
2973 handler.cancelActivity(id);
2974 us.setProgressBar(MessageManager
2975 .formatMessage("label.cancelled_params", new Object[]
2976 { ((JLabel) progressPanel.getComponent(0)).getText() }),
2980 progressPanel.add(cancel, BorderLayout.EAST);
2986 * @return true if any progress bars are still active
2989 public boolean operationInProgress()
2991 if (progressBars != null && progressBars.size() > 0)
2999 * This will return the first AlignFrame holding the given viewport instance. It
3000 * will break if there are more than one AlignFrames viewing a particular av.
3003 * @return alignFrame for viewport
3005 public static AlignFrame getAlignFrameFor(AlignViewportI viewport)
3007 if (getDesktopPane() != null)
3009 AlignmentPanel[] aps = getAlignmentPanels(
3010 viewport.getSequenceSetId());
3011 for (int panel = 0; aps != null && panel < aps.length; panel++)
3013 if (aps[panel] != null && aps[panel].av == viewport)
3015 return aps[panel].alignFrame;
3022 public VamsasApplication getVamsasApplication()
3029 * flag set if jalview GUI is being operated programmatically
3031 private boolean inBatchMode = false;
3034 * check if jalview GUI is being operated programmatically
3036 * @return inBatchMode
3038 public boolean isInBatchMode()
3044 * set flag if jalview GUI is being operated programmatically
3046 * @param inBatchMode
3048 public void setInBatchMode(boolean inBatchMode)
3050 this.inBatchMode = inBatchMode;
3053 public void startServiceDiscovery()
3055 startServiceDiscovery(false);
3058 public void startServiceDiscovery(boolean blocking)
3060 boolean alive = true;
3061 Thread t0 = null, t1 = null, t2 = null;
3062 // JAL-940 - JALVIEW 1 services are now being EOLed as of JABA 2.1 release
3065 // todo: changesupport handlers need to be transferred
3066 if (discoverer == null)
3068 discoverer = Discoverer.getInstance();
3069 // register PCS handler for getDesktop().
3070 discoverer.addPropertyChangeListener(changeSupport);
3072 // JAL-940 - disabled JWS1 service configuration - always start discoverer
3073 // until we phase out completely
3074 (t0 = new Thread(discoverer)).start();
3077 if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
3079 t2 = jalview.ws.jws2.Jws2Discoverer.getInstance()
3080 .startDiscoverer(changeSupport);
3084 // TODO: do rest service discovery
3093 } catch (Exception e)
3096 alive = (t1 != null && t1.isAlive()) || (t2 != null && t2.isAlive())
3097 || (t3 != null && t3.isAlive())
3098 || (t0 != null && t0.isAlive());
3104 * called to check if the service discovery process completed successfully.
3108 protected void JalviewServicesChanged(PropertyChangeEvent evt)
3110 if (evt.getNewValue() == null || evt.getNewValue() instanceof Vector)
3112 final String ermsg = jalview.ws.jws2.Jws2Discoverer.getInstance()
3113 .getErrorMessages();
3116 if (Cache.getDefault("SHOW_WSDISCOVERY_ERRORS", true))
3118 if (serviceChangedDialog == null)
3120 // only run if we aren't already displaying one of these.
3121 addDialogThread(serviceChangedDialog = new Runnable()
3128 * JalviewDialog jd =new JalviewDialog() {
3130 * @Override protected void cancelPressed() { // TODO
3131 * Auto-generated method stub
3133 * }@Override protected void okPressed() { // TODO
3134 * Auto-generated method stub
3136 * }@Override protected void raiseClosed() { // TODO
3137 * Auto-generated method stub
3139 * } }; jd.initDialogFrame(new
3140 * JLabel("<html><table width=\"450\"><tr><td>" + ermsg +
3141 * "<br/>It may be that you have invalid JABA URLs in your web service preferences,"
3142 * + " or mis-configured HTTP proxy settings.<br/>" +
3143 * "Check the <em>Connections</em> and <em>Web services</em> tab of the"
3145 * " Tools->Preferences dialog box to change them.</td></tr></table></html>"
3146 * ), true, true, "Web Service Configuration Problem", 450,
3149 * jd.waitForInput();
3151 JvOptionPane.showConfirmDialog(Desktop.getDesktopPane(),
3152 new JLabel("<html><table width=\"450\"><tr><td>"
3153 + ermsg + "</td></tr></table>"
3154 + "<p>It may be that you have invalid JABA URLs<br/>in your web service preferences,"
3155 + "<br>or as a command-line argument, or mis-configured HTTP proxy settings.</p>"
3156 + "<p>Check the <em>Connections</em> and <em>Web services</em> tab<br/>of the"
3157 + " Tools->Preferences dialog box to change them.</p></html>"),
3158 "Web Service Configuration Problem",
3159 JvOptionPane.DEFAULT_OPTION,
3160 JvOptionPane.ERROR_MESSAGE);
3161 serviceChangedDialog = null;
3170 "Errors reported by JABA discovery service. Check web services preferences.\n"
3177 Runnable serviceChangedDialog = null;
3180 * start a thread to open a URL in the configured browser. Pops up a warning
3181 * dialog to the user if there is an exception when calling out to the browser
3186 public static void showUrl(final String url)
3188 showUrl(url, Desktop.getInstance());
3192 * Like showUrl but allows progress handler to be specified
3196 * (null) or object implementing IProgressIndicator
3198 public static void showUrl(final String url,
3199 final IProgressIndicator progress)
3201 new Thread(new Runnable()
3208 if (progress != null)
3210 progress.setProgressBar(MessageManager
3211 .formatMessage("status.opening_params", new Object[]
3212 { url }), this.hashCode());
3214 BrowserLauncher.openURL(url);
3215 } catch (Exception ex)
3217 JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
3219 .getString("label.web_browser_not_found_unix"),
3220 MessageManager.getString("label.web_browser_not_found"),
3221 JvOptionPane.WARNING_MESSAGE);
3223 ex.printStackTrace();
3225 if (progress != null)
3227 progress.setProgressBar(null, this.hashCode());
3233 private WsParamSetManager wsparamManager = null;
3235 public static ParamManager getUserParameterStore()
3237 Desktop d = Desktop.getInstance();
3238 if (d.wsparamManager == null)
3240 d.wsparamManager = new WsParamSetManager();
3242 return d.wsparamManager;
3246 * static hyperlink handler proxy method for use by Jalview's internal windows
3250 public static void hyperlinkUpdate(HyperlinkEvent e)
3252 if (e.getEventType() == EventType.ACTIVATED)
3257 url = e.getURL().toString();
3258 Desktop.showUrl(url);
3259 } catch (Exception x)
3263 if (Cache.log != null)
3265 Cache.log.error("Couldn't handle string " + url + " as a URL.");
3270 "Couldn't handle string " + url + " as a URL.");
3273 // ignore any exceptions due to dud links.
3280 * single thread that handles display of dialogs to user.
3282 ExecutorService dialogExecutor = Executors.newSingleThreadExecutor();
3285 * flag indicating if dialogExecutor should try to acquire a permit
3287 volatile boolean dialogPause = true;
3292 java.util.concurrent.Semaphore block = new Semaphore(0);
3294 private groovy.ui.Console groovyConsole;
3296 public StructureViewer lastTargetedView;
3299 * add another dialog thread to the queue
3303 public void addDialogThread(final Runnable prompter)
3305 dialogExecutor.submit(new Runnable()
3315 } catch (InterruptedException x)
3319 System.out.println("Desktop headless or instanceonly" + instanceOnly
3320 + " " + Jalview.isHeadlessMode());
3321 if (instanceOnly || Jalview.isHeadlessMode())
3327 SwingUtilities.invokeAndWait(prompter);
3328 } catch (Exception q)
3330 Cache.log.warn("Unexpected Exception in dialog thread.", q);
3336 public void startDialogQueue()
3338 // set the flag so we don't pause waiting for another permit and semaphore
3339 // the current task to begin
3340 dialogPause = false;
3345 * Outputs an image of the desktop to file in EPS format, after prompting the
3346 * user for choice of Text or Lineart character rendering (unless a preference
3347 * has been set). The file name is generated as
3350 * Jalview_snapshot_nnnnn.eps where nnnnn is the current timestamp in milliseconds
3354 protected void snapShotWindow_actionPerformed(ActionEvent e)
3356 // currently the menu option to do this is not shown
3359 int width = getWidth();
3360 int height = getHeight();
3362 "Jalview_snapshot_" + System.currentTimeMillis() + ".eps");
3363 ImageWriterI writer = new ImageWriterI()
3366 public void exportImage(Graphics g) throws Exception
3369 Cache.log.info("Successfully written snapshot to file "
3370 + of.getAbsolutePath());
3373 String title = "View of desktop";
3374 ImageExporter exporter = new ImageExporter(writer, null, TYPE.EPS,
3376 exporter.doExport(of, this, width, height, title);
3380 * Explode the views in the given SplitFrame into separate SplitFrame windows.
3381 * This respects (remembers) any previous 'exploded geometry' i.e. the size and
3382 * location last time the view was expanded (if any). However it does not
3383 * remember the split pane divider location - this is set to match the
3384 * 'exploding' frame.
3388 public void explodeViews(SplitFrame sf)
3390 AlignFrame oldTopFrame = (AlignFrame) sf.getTopFrame();
3391 AlignFrame oldBottomFrame = (AlignFrame) sf.getBottomFrame();
3392 List<? extends AlignmentViewPanel> topPanels = oldTopFrame
3394 List<? extends AlignmentViewPanel> bottomPanels = oldBottomFrame
3396 int viewCount = topPanels.size();
3403 * Processing in reverse order works, forwards order leaves the first panels
3404 * not visible. I don't know why!
3406 for (int i = viewCount - 1; i >= 0; i--)
3409 * Make new top and bottom frames. These take over the respective
3410 * AlignmentPanel objects, including their AlignmentViewports, so the
3411 * cdna/protein relationships between the viewports is carried over to the
3414 * explodedGeometry holds the (x, y) position of the previously exploded
3415 * SplitFrame, and the (width, height) of the AlignFrame component
3417 AlignmentPanel topPanel = (AlignmentPanel) topPanels.get(i);
3418 AlignFrame newTopFrame = new AlignFrame(topPanel);
3419 newTopFrame.setSize(oldTopFrame.getSize());
3420 newTopFrame.setVisible(true);
3421 Rectangle geometry = ((AlignViewport) topPanel.getAlignViewport())
3422 .getExplodedGeometry();
3423 if (geometry != null)
3425 newTopFrame.setSize(geometry.getSize());
3428 AlignmentPanel bottomPanel = (AlignmentPanel) bottomPanels.get(i);
3429 AlignFrame newBottomFrame = new AlignFrame(bottomPanel);
3430 newBottomFrame.setSize(oldBottomFrame.getSize());
3431 newBottomFrame.setVisible(true);
3432 geometry = ((AlignViewport) bottomPanel.getAlignViewport())
3433 .getExplodedGeometry();
3434 if (geometry != null)
3436 newBottomFrame.setSize(geometry.getSize());
3439 topPanel.av.setGatherViewsHere(false);
3440 bottomPanel.av.setGatherViewsHere(false);
3441 JInternalFrame splitFrame = new SplitFrame(newTopFrame,
3443 if (geometry != null)
3445 splitFrame.setLocation(geometry.getLocation());
3447 Desktop.addInternalFrame(splitFrame, sf.getTitle(), -1, -1);
3451 * Clear references to the panels (now relocated in the new SplitFrames)
3452 * before closing the old SplitFrame.
3455 bottomPanels.clear();
3460 * Gather expanded split frames, sharing the same pairs of sequence set ids,
3461 * back into the given SplitFrame as additional views. Note that the gathered
3462 * frames may themselves have multiple views.
3466 public void gatherViews(GSplitFrame source)
3469 * special handling of explodedGeometry for a view within a SplitFrame: - it
3470 * holds the (x, y) position of the enclosing SplitFrame, and the (width,
3471 * height) of the AlignFrame component
3473 AlignFrame myTopFrame = (AlignFrame) source.getTopFrame();
3474 AlignFrame myBottomFrame = (AlignFrame) source.getBottomFrame();
3475 myTopFrame.viewport.setExplodedGeometry(new Rectangle(source.getX(),
3476 source.getY(), myTopFrame.getWidth(), myTopFrame.getHeight()));
3477 myBottomFrame.viewport
3478 .setExplodedGeometry(new Rectangle(source.getX(), source.getY(),
3479 myBottomFrame.getWidth(), myBottomFrame.getHeight()));
3480 myTopFrame.viewport.setGatherViewsHere(true);
3481 myBottomFrame.viewport.setGatherViewsHere(true);
3482 String topViewId = myTopFrame.viewport.getSequenceSetId();
3483 String bottomViewId = myBottomFrame.viewport.getSequenceSetId();
3485 JInternalFrame[] frames = desktopPane.getAllFrames();
3486 for (JInternalFrame frame : frames)
3488 if (frame instanceof SplitFrame && frame != source)
3490 SplitFrame sf = (SplitFrame) frame;
3491 AlignFrame topFrame = (AlignFrame) sf.getTopFrame();
3492 AlignFrame bottomFrame = (AlignFrame) sf.getBottomFrame();
3493 boolean gatherThis = false;
3494 for (int a = 0; a < topFrame.alignPanels.size(); a++)
3496 AlignmentPanel topPanel = topFrame.alignPanels.get(a);
3497 AlignmentPanel bottomPanel = bottomFrame.alignPanels.get(a);
3498 if (topViewId.equals(topPanel.av.getSequenceSetId())
3499 && bottomViewId.equals(bottomPanel.av.getSequenceSetId()))
3502 topPanel.av.setGatherViewsHere(false);
3503 bottomPanel.av.setGatherViewsHere(false);
3504 topPanel.av.setExplodedGeometry(
3505 new Rectangle(sf.getLocation(), topFrame.getSize()));
3506 bottomPanel.av.setExplodedGeometry(
3507 new Rectangle(sf.getLocation(), bottomFrame.getSize()));
3508 myTopFrame.addAlignmentPanel(topPanel, false);
3509 myBottomFrame.addAlignmentPanel(bottomPanel, false);
3515 topFrame.getAlignPanels().clear();
3516 bottomFrame.getAlignPanels().clear();
3523 * The dust settles...give focus to the tab we did this from.
3525 myTopFrame.setDisplayedView(myTopFrame.alignPanel);
3528 public static groovy.ui.Console getGroovyConsole()
3530 Desktop desktop = Desktop.getInstance();
3531 return desktop == null ? null : desktop.groovyConsole;
3535 * handles the payload of a drag and drop event.
3537 * TODO refactor to desktop utilities class
3540 * - Data source strings extracted from the drop event
3542 * - protocol for each data source extracted from the drop event
3546 * - the payload from the drop event
3549 @SuppressWarnings("unchecked")
3550 public static void transferFromDropTarget(List<Object> files,
3551 List<DataSourceType> protocols, DropTargetDropEvent evt,
3552 Transferable t) throws Exception
3555 // BH 2018 changed List<String> to List<Object> to allow for File from SwingJS
3557 // DataFlavor[] flavors = t.getTransferDataFlavors();
3558 // for (int i = 0; i < flavors.length; i++) {
3559 // if (flavors[i].isFlavorJavaFileListType()) {
3560 // evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
3561 // List<File> list = (List<File>) t.getTransferData(flavors[i]);
3562 // for (int j = 0; j < list.size(); j++) {
3563 // File file = (File) list.get(j);
3564 // byte[] data = getDroppedFileBytes(file);
3565 // fileName.setText(file.getName() + " - " + data.length + " " +
3566 // evt.getLocation());
3567 // JTextArea target = (JTextArea) ((DropTarget) evt.getSource()).getComponent();
3568 // target.setText(new String(data));
3570 // dtde.dropComplete(true);
3575 DataFlavor uriListFlavor = new DataFlavor(
3576 "text/uri-list;class=java.lang.String"), urlFlavour = null;
3579 urlFlavour = new DataFlavor(
3580 "application/x-java-url; class=java.net.URL");
3581 } catch (ClassNotFoundException cfe)
3583 Cache.log.debug("Couldn't instantiate the URL dataflavor.", cfe);
3586 if (urlFlavour != null && t.isDataFlavorSupported(urlFlavour))
3591 java.net.URL url = (URL) t.getTransferData(urlFlavour);
3592 // nb: java 8 osx bug https://bugs.openjdk.java.net/browse/JDK-8156099
3593 // means url may be null.
3596 protocols.add(DataSourceType.URL);
3597 files.add(url.toString());
3598 Cache.log.debug("Drop handled as URL dataflavor "
3599 + files.get(files.size() - 1));
3604 if (Platform.isAMacAndNotJS())
3607 "Please ignore plist error - occurs due to problem with java 8 on OSX");
3610 } catch (Throwable ex)
3612 Cache.log.debug("URL drop handler failed.", ex);
3615 if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
3617 // Works on Windows and MacOSX
3618 Cache.log.debug("Drop handled as javaFileListFlavor");
3619 for (Object file : (List<Object>) t
3620 .getTransferData(DataFlavor.javaFileListFlavor))
3623 protocols.add(DataSourceType.FILE);
3628 // Unix like behaviour
3629 boolean added = false;
3631 if (t.isDataFlavorSupported(uriListFlavor))
3633 Cache.log.debug("Drop handled as uriListFlavor");
3634 // This is used by Unix drag system
3635 data = (String) t.getTransferData(uriListFlavor);
3639 // fallback to text: workaround - on OSX where there's a JVM bug
3640 Cache.log.debug("standard URIListFlavor failed. Trying text");
3641 // try text fallback
3642 DataFlavor textDf = new DataFlavor(
3643 "text/plain;class=java.lang.String");
3644 if (t.isDataFlavorSupported(textDf))
3646 data = (String) t.getTransferData(textDf);
3649 Cache.log.debug("Plain text drop content returned "
3650 + (data == null ? "Null - failed" : data));
3655 while (protocols.size() < files.size())
3657 Cache.log.debug("Adding missing FILE protocol for "
3658 + files.get(protocols.size()));
3659 protocols.add(DataSourceType.FILE);
3661 for (java.util.StringTokenizer st = new java.util.StringTokenizer(
3662 data, "\r\n"); st.hasMoreTokens();)
3665 String s = st.nextToken();
3666 if (s.startsWith("#"))
3668 // the line is a comment (as per the RFC 2483)
3671 java.net.URI uri = new java.net.URI(s);
3672 if (uri.getScheme().toLowerCase().startsWith("http"))
3674 protocols.add(DataSourceType.URL);
3675 files.add(uri.toString());
3679 // otherwise preserve old behaviour: catch all for file objects
3680 java.io.File file = new java.io.File(uri);
3681 protocols.add(DataSourceType.FILE);
3682 files.add(file.toString());
3687 if (Cache.log.isDebugEnabled())
3689 if (data == null || !added)
3692 if (t.getTransferDataFlavors() != null
3693 && t.getTransferDataFlavors().length > 0)
3696 "Couldn't resolve drop data. Here are the supported flavors:");
3697 for (DataFlavor fl : t.getTransferDataFlavors())
3700 "Supported transfer dataflavor: " + fl.toString());
3701 Object df = t.getTransferData(fl);
3704 Cache.log.debug("Retrieves: " + df);
3708 Cache.log.debug("Retrieved nothing");
3714 Cache.log.debug("Couldn't resolve dataflavor for drop: "
3720 if (Platform.isWindowsAndNotJS())
3722 Cache.log.debug("Scanning dropped content for Windows Link Files");
3724 // resolve any .lnk files in the file drop
3725 for (int f = 0; f < files.size(); f++)
3727 String source = files.get(f).toString().toLowerCase();
3728 if (protocols.get(f).equals(DataSourceType.FILE)
3729 && (source.endsWith(".lnk") || source.endsWith(".url")
3730 || source.endsWith(".site")))
3734 Object obj = files.get(f);
3735 File lf = (obj instanceof File ? (File) obj
3736 : new File((String) obj));
3737 // process link file to get a URL
3738 Cache.log.debug("Found potential link file: " + lf);
3739 WindowsShortcut wscfile = new WindowsShortcut(lf);
3740 String fullname = wscfile.getRealFilename();
3741 protocols.set(f, FormatAdapter.checkProtocol(fullname));
3742 files.set(f, fullname);
3743 Cache.log.debug("Parsed real filename " + fullname
3744 + " to extract protocol: " + protocols.get(f));
3745 } catch (Exception ex)
3748 "Couldn't parse " + files.get(f) + " as a link file.",
3757 * Sets the Preferences property for experimental features to True or False
3758 * depending on the state of the controlling menu item
3761 protected void showExperimental_actionPerformed(boolean selected)
3763 Cache.setProperty(EXPERIMENTAL_FEATURES, Boolean.toString(selected));
3767 * Answers a (possibly empty) list of any structure viewer frames (currently for
3768 * either Jmol or Chimera) which are currently open. This may optionally be
3769 * restricted to viewers of a specified class, or viewers linked to a specified
3773 * if not null, only return viewers linked to this panel
3774 * @param structureViewerClass
3775 * if not null, only return viewers of this class
3778 public List<StructureViewerBase> getStructureViewers(
3779 AlignmentPanel apanel,
3780 Class<? extends StructureViewerBase> structureViewerClass)
3782 List<StructureViewerBase> result = new ArrayList<>();
3783 JInternalFrame[] frames = getAllFrames();
3785 for (JInternalFrame frame : frames)
3787 if (frame instanceof StructureViewerBase)
3789 if (structureViewerClass == null
3790 || structureViewerClass.isInstance(frame))
3793 || ((StructureViewerBase) frame).isLinkedWith(apanel))
3795 result.add((StructureViewerBase) frame);