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. Though not headless, this option disallows
235 * dialogs to run in test environments.
237 * It is set by invoking the Desktop(true) constructor.
240 boolean instanceOnly;
242 class MyDesktopManager implements DesktopManager
245 private DesktopManager delegate;
247 public MyDesktopManager(DesktopManager delegate)
249 this.delegate = delegate;
253 public void activateFrame(JInternalFrame f)
257 delegate.activateFrame(f);
258 } catch (NullPointerException npe)
260 Point p = getMousePosition();
261 showPasteMenu(p.x, p.y);
266 public void beginDraggingFrame(JComponent f)
268 delegate.beginDraggingFrame(f);
272 public void beginResizingFrame(JComponent f, int direction)
274 delegate.beginResizingFrame(f, direction);
278 public void closeFrame(JInternalFrame f)
280 delegate.closeFrame(f);
284 public void deactivateFrame(JInternalFrame f)
286 delegate.deactivateFrame(f);
290 public void deiconifyFrame(JInternalFrame f)
292 delegate.deiconifyFrame(f);
296 public void dragFrame(JComponent f, int newX, int newY)
302 delegate.dragFrame(f, newX, newY);
306 public void endDraggingFrame(JComponent f)
308 delegate.endDraggingFrame(f);
309 desktopPane.repaint();
313 public void endResizingFrame(JComponent f)
315 delegate.endResizingFrame(f);
316 desktopPane.repaint();
320 public void iconifyFrame(JInternalFrame f)
322 delegate.iconifyFrame(f);
326 public void maximizeFrame(JInternalFrame f)
328 delegate.maximizeFrame(f);
332 public void minimizeFrame(JInternalFrame f)
334 delegate.minimizeFrame(f);
338 public void openFrame(JInternalFrame f)
340 delegate.openFrame(f);
344 public void resizeFrame(JComponent f, int newX, int newY, int newWidth,
351 delegate.resizeFrame(f, newX, newY, newWidth, newHeight);
355 public void setBoundsForFrame(JComponent f, int newX, int newY,
356 int newWidth, int newHeight)
358 delegate.setBoundsForFrame(f, newX, newY, newWidth, newHeight);
361 // All other methods, simply delegate
365 public MyDesktopPane desktopPane;
368 * Answers an 'application scope' singleton instance of this class. Separate
369 * SwingJS 'applets' running in the same browser page will each have a
370 * distinct instance of Desktop.
374 public static Desktop getInstance()
376 return Jalview.isHeadlessMode() ? null
377 : (Desktop) ApplicationSingletonProvider
378 .getInstance(Desktop.class);
382 * For TestNG, this constructor can be utilized to allow the creation of a
383 * singleton Desktop instance without the formation of frames and, especially,
384 * not involving dialogs. Cache.log is also initialized for some tests that
385 * require it despite there being no Desktop.
389 public Desktop(boolean forInstance)
391 ApplicationSingletonProvider.setInstance(Desktop.class, this);
397 * Private constructor enforces singleton pattern. It is called by reflection
398 * from ApplicationSingletonProvider.getInstance().
400 @SuppressWarnings("unused")
407 * A note to implementors. It is ESSENTIAL that any activities that might
408 * block are spawned off as threads rather than waited for during this
411 if (!Platform.isJS())
413 doVamsasClientCheck();
416 doConfigureStructurePrefs();
417 setTitle("Jalview " + jalview.bin.Cache.getProperty("VERSION"));
418 setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
419 boolean selmemusage = jalview.bin.Cache.getDefault("SHOW_MEMUSAGE",
421 boolean showjconsole = jalview.bin.Cache
422 .getDefault("SHOW_JAVA_CONSOLE", false);
423 desktopPane = new MyDesktopPane(selmemusage);
425 showMemusage.setSelected(selmemusage);
426 desktopPane.setBackground(Color.white);
427 getContentPane().setLayout(new BorderLayout());
428 // alternate config - have scrollbars - see notes in JAL-153
429 // JScrollPane sp = new JScrollPane();
430 // sp.getViewport().setView(desktop);
431 // getContentPane().add(sp, BorderLayout.CENTER);
433 // BH 2018 - just an experiment to try unclipped JInternalFrames.
436 getRootPane().putClientProperty("swingjs.overflow.hidden", "false");
439 getContentPane().add(desktopPane, BorderLayout.CENTER);
440 desktopPane.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
442 // This line prevents Windows Look&Feel resizing all new windows to
444 // if previous window was maximised
445 desktopPane.setDesktopManager(new MyDesktopManager(
446 (Platform.isWindowsAndNotJS() ? new DefaultDesktopManager()
447 : Platform.isAMacAndNotJS()
448 ? new AquaInternalFrameManager(
449 desktopPane.getDesktopManager())
450 : desktopPane.getDesktopManager())));
452 Rectangle dims = getLastKnownDimensions("");
459 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
460 int xPos = Math.max(5, (screenSize.width - 900) / 2);
461 int yPos = Math.max(5, (screenSize.height - 650) / 2);
462 setBounds(xPos, yPos, 900, 650);
465 // Note that this next syntax, checking for Platform.isJS and also
466 // escaping the code using @j2sIgnore, serves two purposes. It gives
467 // us an easily findable tag, Platform.isJS(), to places in the code where
468 // there is something different about the SwingJS implementation. Second,
469 // it deletes the unneeded Java-only code form the JavaScript version
470 // completely (@j2sIgnore), since it will never be used there.
472 if (!Platform.isJS())
480 jconsole = new Console(this, showjconsole);
481 // add essential build information
482 jconsole.setHeader("Jalview Version: "
483 + jalview.bin.Cache.getProperty("VERSION") + "\n"
484 + "Jalview Installation: "
485 + jalview.bin.Cache.getDefault("INSTALLATION", "unknown")
486 + "\n" + "Build Date: "
487 + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown")
488 + "\n" + "Java version: "
489 + System.getProperty("java.version") + "\n"
490 + System.getProperty("os.arch") + " "
491 + System.getProperty("os.name") + " "
492 + System.getProperty("os.version"));
494 showConsole(showjconsole);
496 showNews.setVisible(false);
498 experimentalFeatures.setSelected(showExperimental());
500 getIdentifiersOrgData();
504 // Spawn a thread that shows the splashscreen
506 SwingUtilities.invokeLater(new Runnable()
515 // Thread off a new instance of the file chooser - this reduces the time
517 // takes to open it later on.
518 new Thread(new Runnable()
523 Cache.log.debug("Filechooser init thread started.");
524 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
525 JalviewFileChooser.forRead(Cache.getProperty("LAST_DIRECTORY"),
527 Cache.log.debug("Filechooser init thread finished.");
530 // Add the service change listener
531 changeSupport.addJalviewPropertyChangeListener("services",
532 new PropertyChangeListener()
536 public void propertyChange(PropertyChangeEvent evt)
538 Cache.log.debug("Firing service changed event for "
539 + evt.getNewValue());
540 JalviewServicesChanged(evt);
547 this.setDropTarget(new java.awt.dnd.DropTarget(desktopPane, this));
549 this.addWindowListener(new WindowAdapter()
552 public void windowClosing(WindowEvent evt)
559 this.addMouseListener(ma = new MouseAdapter()
562 public void mousePressed(MouseEvent evt)
564 if (evt.isPopupTrigger()) // Mac
566 showPasteMenu(evt.getX(), evt.getY());
571 public void mouseReleased(MouseEvent evt)
573 if (evt.isPopupTrigger()) // Windows
575 showPasteMenu(evt.getX(), evt.getY());
579 desktopPane.addMouseListener(ma);
580 } catch (Throwable t)
587 * Answers true if user preferences to enable experimental features is True
592 public boolean showExperimental()
594 String experimental = Cache.getDefault(EXPERIMENTAL_FEATURES,
595 Boolean.FALSE.toString());
596 return Boolean.valueOf(experimental).booleanValue();
599 public void doConfigureStructurePrefs()
601 // configure services
602 StructureSelectionManager ssm = StructureSelectionManager
603 .getStructureSelectionManager(this);
604 if (jalview.bin.Cache.getDefault(Preferences.ADD_SS_ANN, true))
606 ssm.setAddTempFacAnnot(jalview.bin.Cache
607 .getDefault(Preferences.ADD_TEMPFACT_ANN, true));
608 ssm.setProcessSecondaryStructure(jalview.bin.Cache
609 .getDefault(Preferences.STRUCT_FROM_PDB, true));
610 ssm.setSecStructServices(
611 jalview.bin.Cache.getDefault(Preferences.USE_RNAVIEW, true));
615 ssm.setAddTempFacAnnot(false);
616 ssm.setProcessSecondaryStructure(false);
617 ssm.setSecStructServices(false);
621 public void checkForNews()
623 final Desktop me = this;
624 // Thread off the news reader, in case there are connection problems.
625 new Thread(new Runnable()
630 Cache.log.debug("Starting news thread.");
631 jvnews = new BlogReader(me);
632 showNews.setVisible(true);
633 Cache.log.debug("Completed news thread.");
638 public void getIdentifiersOrgData()
640 // Thread off the identifiers fetcher
641 new Thread(new Runnable()
646 Cache.log.debug("Downloading data from identifiers.org");
647 // UrlDownloadClient client = new UrlDownloadClient();
650 UrlDownloadClient.download(IdOrgSettings.getUrl(),
651 IdOrgSettings.getDownloadLocation());
652 } catch (IOException e)
654 Cache.log.debug("Exception downloading identifiers.org data"
663 protected void showNews_actionPerformed(ActionEvent e)
665 showNews(showNews.isSelected());
668 protected void showNews(boolean visible)
670 Cache.log.debug((visible ? "Showing" : "Hiding") + " news.");
671 showNews.setSelected(visible);
672 if (visible && !jvnews.isVisible())
674 new Thread(new Runnable()
679 long now = System.currentTimeMillis();
681 MessageManager.getString("status.refreshing_news"), now);
682 jvnews.refreshNews();
683 setProgressBar(null, now);
691 * recover the last known dimensions for a jalview window
694 * - empty string is desktop, all other windows have unique prefix
695 * @return null or last known dimensions scaled to current geometry (if last
696 * window geom was known)
698 Rectangle getLastKnownDimensions(String windowName)
700 // TODO: lock aspect ratio for scaling desktop Bug #0058199
701 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
702 String x = jalview.bin.Cache.getProperty(windowName + "SCREEN_X");
703 String y = jalview.bin.Cache.getProperty(windowName + "SCREEN_Y");
704 String width = jalview.bin.Cache
705 .getProperty(windowName + "SCREEN_WIDTH");
706 String height = jalview.bin.Cache
707 .getProperty(windowName + "SCREEN_HEIGHT");
708 if ((x != null) && (y != null) && (width != null) && (height != null))
710 int ix = Integer.parseInt(x), iy = Integer.parseInt(y),
711 iw = Integer.parseInt(width), ih = Integer.parseInt(height);
712 if (jalview.bin.Cache.getProperty("SCREENGEOMETRY_WIDTH") != null)
714 // attempt #1 - try to cope with change in screen geometry - this
715 // version doesn't preserve original jv aspect ratio.
716 // take ratio of current screen size vs original screen size.
717 double sw = ((1f * screenSize.width) / (1f * Integer.parseInt(
718 jalview.bin.Cache.getProperty("SCREENGEOMETRY_WIDTH"))));
719 double sh = ((1f * screenSize.height) / (1f * Integer.parseInt(
720 jalview.bin.Cache.getProperty("SCREENGEOMETRY_HEIGHT"))));
721 // rescale the bounds depending upon the current screen geometry.
722 ix = (int) (ix * sw);
723 iw = (int) (iw * sw);
724 iy = (int) (iy * sh);
725 ih = (int) (ih * sh);
726 while (ix >= screenSize.width)
728 jalview.bin.Cache.log.debug(
729 "Window geometry location recall error: shifting horizontal to within screenbounds.");
730 ix -= screenSize.width;
732 while (iy >= screenSize.height)
734 jalview.bin.Cache.log.debug(
735 "Window geometry location recall error: shifting vertical to within screenbounds.");
736 iy -= screenSize.height;
738 jalview.bin.Cache.log.debug(
739 "Got last known dimensions for " + windowName + ": x:" + ix
740 + " y:" + iy + " width:" + iw + " height:" + ih);
742 // return dimensions for new instance
743 return new Rectangle(ix, iy, iw, ih);
748 private void doVamsasClientCheck()
750 if (Cache.vamsasJarsPresent())
752 setupVamsasDisconnectedGui();
753 VamsasMenu.setVisible(true);
754 final Desktop us = this;
755 VamsasMenu.addMenuListener(new MenuListener()
757 // this listener remembers when the menu was first selected, and
758 // doesn't rebuild the session list until it has been cleared and
760 boolean refresh = true;
763 public void menuCanceled(MenuEvent e)
769 public void menuDeselected(MenuEvent e)
775 public void menuSelected(MenuEvent e)
779 us.buildVamsasStMenu();
784 vamsasStart.setVisible(true);
788 protected void showPasteMenu(int x, int y)
790 JPopupMenu popup = new JPopupMenu();
791 JMenuItem item = new JMenuItem(
792 MessageManager.getString("label.paste_new_window"));
793 item.addActionListener(new ActionListener()
796 public void actionPerformed(ActionEvent evt)
803 popup.show(this, x, y);
810 Clipboard c = Toolkit.getDefaultToolkit().getSystemClipboard();
811 Transferable contents = c.getContents(this);
813 if (contents != null)
815 String file = (String) contents
816 .getTransferData(DataFlavor.stringFlavor);
818 FileFormatI format = new IdentifyFile().identify(file,
819 DataSourceType.PASTE);
821 new FileLoader().loadFile(file, DataSourceType.PASTE, format);
824 } catch (Exception ex)
827 "Unable to paste alignment from system clipboard:\n" + ex);
832 * Adds and opens the given frame to the desktop
843 public static synchronized void addInternalFrame(
844 final JInternalFrame frame, String title, int w, int h)
846 addInternalFrame(frame, title, true, w, h, true, false);
850 * Add an internal frame to the Jalview desktop
857 * When true, display frame immediately, otherwise, caller must call
858 * setVisible themselves.
864 public static synchronized void addInternalFrame(
865 final JInternalFrame frame, String title, boolean makeVisible,
868 addInternalFrame(frame, title, makeVisible, w, h, true, false);
872 * Add an internal frame to the Jalview desktop and make it visible
885 public static synchronized void addInternalFrame(
886 final JInternalFrame frame, String title, int w, int h,
889 addInternalFrame(frame, title, true, w, h, resizable, false);
893 * Add an internal frame to the Jalview desktop
900 * When true, display frame immediately, otherwise, caller must call
901 * setVisible themselves.
908 * @param ignoreMinSize
909 * Do not set the default minimum size for frame
911 public static synchronized void addInternalFrame(
912 final JInternalFrame frame, String title, boolean makeVisible,
913 int w, int h, boolean resizable, boolean ignoreMinSize)
917 // TODO: allow callers to determine X and Y position of frame (eg. via
919 // TODO: consider fixing method to update entries in the window submenu with
920 // the current window title
922 frame.setTitle(title);
923 if (w > 0 && (frame.getWidth() < 1 || frame.getHeight() < 1))
927 // THIS IS A PUBLIC STATIC METHOD, SO IT MAY BE CALLED EVEN IN
928 // A HEADLESS STATE WHEN NO DESKTOP EXISTS. MUST RETURN
929 // IF JALVIEW IS RUNNING HEADLESS
930 // ///////////////////////////////////////////////
931 if (Jalview.isHeadlessMode() || Desktop.getInstance().instanceOnly)
940 frame.setMinimumSize(
941 new Dimension(DEFAULT_MIN_WIDTH, DEFAULT_MIN_HEIGHT));
943 // Set default dimension for Alignment Frame window.
944 // The Alignment Frame window could be added from a number of places,
946 // I did this here in order not to miss out on any Alignment frame.
947 if (frame instanceof AlignFrame)
949 frame.setMinimumSize(new Dimension(ALIGN_FRAME_DEFAULT_MIN_WIDTH,
950 ALIGN_FRAME_DEFAULT_MIN_HEIGHT));
954 frame.setVisible(makeVisible);
955 frame.setClosable(true);
956 frame.setResizable(resizable);
957 frame.setMaximizable(resizable);
958 frame.setIconifiable(resizable);
959 frame.setOpaque(Platform.isJS());
961 boolean isEmbedded = (Platform.getDimIfEmbedded(frame, -1, -1) != null);
962 if (!isEmbedded && frame.getX() < 1 && frame.getY() < 1)
964 frame.setLocation(xOffset * openFrameCount,
965 yOffset * ((openFrameCount - 1) % 10) + yOffset);
969 * add an entry for the new frame in the Window menu
970 * (and remove it when the frame is closed)
972 JMenuItem menuItem = new JMenuItem(title);
973 frame.addInternalFrameListener(new InternalFrameAdapter()
976 public void internalFrameActivated(InternalFrameEvent evt)
978 JInternalFrame itf = getDesktopPane().getSelectedFrame();
981 if (itf instanceof AlignFrame)
983 Jalview.setCurrentAlignFrame((AlignFrame) itf);
990 public void internalFrameClosed(InternalFrameEvent evt)
992 PaintRefresher.RemoveComponent(frame);
995 * defensive check to prevent frames being
996 * added half off the window
998 if (openFrameCount > 0)
1004 * ensure no reference to alignFrame retained by menu item listener
1006 if (menuItem.getActionListeners().length > 0)
1008 menuItem.removeActionListener(menuItem.getActionListeners()[0]);
1010 Desktop.getInstance().windowMenu.remove(menuItem);
1014 menuItem.addActionListener(new ActionListener()
1017 public void actionPerformed(ActionEvent e)
1021 frame.setSelected(true);
1022 frame.setIcon(false);
1023 } catch (java.beans.PropertyVetoException ex)
1025 // System.err.println(ex.toString());
1030 setKeyBindings(frame);
1032 getDesktopPane().add(frame);
1034 Desktop.getInstance().windowMenu.add(menuItem);
1039 frame.setSelected(true);
1040 frame.requestFocus();
1041 } catch (java.beans.PropertyVetoException ve)
1043 } catch (java.lang.ClassCastException cex)
1046 "Squashed a possible GUI implementation error. If you can recreate this, please look at http://issues.jalview.org/browse/JAL-869",
1052 * Add key bindings to a JInternalFrame so that Ctrl-W and Cmd-W will close the
1057 private static void setKeyBindings(JInternalFrame frame)
1059 final Action closeAction = new AbstractAction()
1062 public void actionPerformed(ActionEvent e)
1069 * set up key bindings for Ctrl-W and Cmd-W, with the same (Close) action
1071 KeyStroke ctrlWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
1072 InputEvent.CTRL_DOWN_MASK);
1073 KeyStroke cmdWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
1074 Toolkit.getDefaultToolkit().getMenuShortcutKeyMask());
1076 InputMap inputMap = frame
1077 .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
1078 String ctrlW = ctrlWKey.toString();
1079 inputMap.put(ctrlWKey, ctrlW);
1080 inputMap.put(cmdWKey, ctrlW);
1082 ActionMap actionMap = frame.getActionMap();
1083 actionMap.put(ctrlW, closeAction);
1087 public void lostOwnership(Clipboard clipboard, Transferable contents)
1091 Desktop.getInstance().jalviewClipboard = null;
1094 internalCopy = false;
1098 public void dragEnter(DropTargetDragEvent evt)
1103 public void dragExit(DropTargetEvent evt)
1108 public void dragOver(DropTargetDragEvent evt)
1113 public void dropActionChanged(DropTargetDragEvent evt)
1124 public void drop(DropTargetDropEvent evt)
1126 boolean success = true;
1127 // JAL-1552 - acceptDrop required before getTransferable call for
1128 // Java's Transferable for native dnd
1129 evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
1130 Transferable t = evt.getTransferable();
1131 List<Object> files = new ArrayList<>();
1132 List<DataSourceType> protocols = new ArrayList<>();
1136 Desktop.transferFromDropTarget(files, protocols, evt, t);
1137 } catch (Exception e)
1139 e.printStackTrace();
1147 for (int i = 0; i < files.size(); i++)
1149 // BH 2018 File or String
1150 Object file = files.get(i);
1151 String fileName = file.toString();
1152 DataSourceType protocol = (protocols == null)
1153 ? DataSourceType.FILE
1155 FileFormatI format = null;
1157 if (fileName.endsWith(".jar"))
1159 format = FileFormat.Jalview;
1164 format = new IdentifyFile().identify(file, protocol);
1166 if (file instanceof File)
1168 Platform.cacheFileData((File) file);
1170 new FileLoader().loadFile(null, file, protocol, format);
1173 } catch (Exception ex)
1178 evt.dropComplete(success); // need this to ensure input focus is properly
1179 // transfered to any new windows created
1189 public void inputLocalFileMenuItem_actionPerformed(AlignViewport viewport)
1191 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
1192 JalviewFileChooser chooser = JalviewFileChooser
1193 .forRead(Cache.getProperty("LAST_DIRECTORY"), fileFormat, true);
1195 chooser.setFileView(new JalviewFileView());
1196 chooser.setDialogTitle(
1197 MessageManager.getString("label.open_local_file"));
1198 chooser.setToolTipText(MessageManager.getString("action.open"));
1200 chooser.setResponseHandler(0, new Runnable()
1205 File selectedFile = chooser.getSelectedFile();
1206 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1208 FileFormatI format = chooser.getSelectedFormat();
1211 * Call IdentifyFile to verify the file contains what its extension implies.
1212 * Skip this step for dynamically added file formats, because
1213 * IdentifyFile does not know how to recognise them.
1215 if (FileFormats.getInstance().isIdentifiable(format))
1219 format = new IdentifyFile().identify(selectedFile,
1220 DataSourceType.FILE);
1221 } catch (FileFormatException e)
1223 // format = null; //??
1227 new FileLoader().loadFile(viewport, selectedFile,
1228 DataSourceType.FILE, format);
1231 chooser.showOpenDialog(this);
1235 * Shows a dialog for input of a URL at which to retrieve alignment data
1240 public void inputURLMenuItem_actionPerformed(AlignViewport viewport)
1242 // This construct allows us to have a wider textfield
1244 JLabel label = new JLabel(
1245 MessageManager.getString("label.input_file_url"));
1247 JPanel panel = new JPanel(new GridLayout(2, 1));
1251 * the URL to fetch is
1252 * Java: an editable combobox with history
1253 * JS: (pending JAL-3038) a plain text field
1256 String urlBase = "http://www.";
1257 if (Platform.isJS())
1259 history = new JTextField(urlBase, 35);
1268 JComboBox<String> asCombo = new JComboBox<>();
1269 asCombo.setPreferredSize(new Dimension(400, 20));
1270 asCombo.setEditable(true);
1271 asCombo.addItem(urlBase);
1272 String historyItems = Cache.getProperty("RECENT_URL");
1273 if (historyItems != null)
1275 for (String token : historyItems.split("\\t"))
1277 asCombo.addItem(token);
1284 Object[] options = new Object[] { MessageManager.getString("action.ok"),
1285 MessageManager.getString("action.cancel") };
1286 Runnable action = new Runnable()
1291 @SuppressWarnings("unchecked")
1292 String url = (history instanceof JTextField
1293 ? ((JTextField) history).getText()
1294 : ((JComboBox<String>) history).getSelectedItem()
1297 if (url.toLowerCase().endsWith(".jar"))
1299 if (viewport != null)
1301 new FileLoader().loadFile(viewport, url, DataSourceType.URL,
1302 FileFormat.Jalview);
1306 new FileLoader().loadFile(url, DataSourceType.URL,
1307 FileFormat.Jalview);
1312 FileFormatI format = null;
1315 format = new IdentifyFile().identify(url, DataSourceType.URL);
1316 } catch (FileFormatException e)
1318 // TODO revise error handling, distinguish between
1319 // URL not found and response not valid
1324 String msg = MessageManager
1325 .formatMessage("label.couldnt_locate", url);
1326 JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
1328 MessageManager.getString("label.url_not_found"),
1329 JvOptionPane.WARNING_MESSAGE);
1334 if (viewport != null)
1336 new FileLoader().loadFile(viewport, url, DataSourceType.URL,
1341 new FileLoader().loadFile(url, DataSourceType.URL, format);
1346 String dialogOption = MessageManager
1347 .getString("label.input_alignment_from_url");
1348 JvOptionPane.newOptionDialog(getDesktopPane())
1349 .setResponseHandler(0, action)
1350 .showInternalDialog(panel, dialogOption,
1351 JvOptionPane.YES_NO_CANCEL_OPTION,
1352 JvOptionPane.PLAIN_MESSAGE, null, options,
1353 MessageManager.getString("action.ok"));
1357 * Opens the CutAndPaste window for the user to paste an alignment in to
1360 * - if not null, the pasted alignment is added to the current
1361 * alignment; if null, to a new alignment window
1364 public void inputTextboxMenuItem_actionPerformed(
1365 AlignmentViewPanel viewPanel)
1367 CutAndPasteTransfer cap = new CutAndPasteTransfer();
1368 cap.setForInput(viewPanel);
1369 Desktop.addInternalFrame(cap,
1370 MessageManager.getString("label.cut_paste_alignmen_file"), true,
1380 Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
1381 jalview.bin.Cache.setProperty("SCREENGEOMETRY_WIDTH",
1383 jalview.bin.Cache.setProperty("SCREENGEOMETRY_HEIGHT",
1384 screen.height + "");
1385 storeLastKnownDimensions("", new Rectangle(getBounds().x, getBounds().y,
1386 getWidth(), getHeight()));
1388 if (jconsole != null)
1390 storeLastKnownDimensions("JAVA_CONSOLE_", jconsole.getBounds());
1391 jconsole.stopConsole();
1395 storeLastKnownDimensions("JALVIEW_RSS_WINDOW_", jvnews.getBounds());
1398 if (dialogExecutor != null)
1400 dialogExecutor.shutdownNow();
1402 closeAll_actionPerformed(null);
1404 if (groovyConsole != null)
1406 // suppress a possible repeat prompt to save script
1407 groovyConsole.setDirty(false);
1408 groovyConsole.exit();
1413 private void storeLastKnownDimensions(String string, Rectangle jc)
1415 jalview.bin.Cache.log.debug("Storing last known dimensions for "
1416 + string + ": x:" + jc.x + " y:" + jc.y + " width:" + jc.width
1417 + " height:" + jc.height);
1419 jalview.bin.Cache.setProperty(string + "SCREEN_X", jc.x + "");
1420 jalview.bin.Cache.setProperty(string + "SCREEN_Y", jc.y + "");
1421 jalview.bin.Cache.setProperty(string + "SCREEN_WIDTH", jc.width + "");
1422 jalview.bin.Cache.setProperty(string + "SCREEN_HEIGHT", jc.height + "");
1432 public void aboutMenuItem_actionPerformed(ActionEvent e)
1434 // StringBuffer message = getAboutMessage(false);
1435 // JvOptionPane.showInternalMessageDialog(Desktop.getDesktop(),
1437 // message.toString(), "About Jalview", JvOptionPane.INFORMATION_MESSAGE);
1438 new Thread(new Runnable()
1443 new SplashScreen(true);
1448 public StringBuffer getAboutMessage(boolean shortv)
1450 StringBuffer message = new StringBuffer();
1451 message.append("<html>");
1454 message.append("<h1><strong>Version: "
1455 + jalview.bin.Cache.getProperty("VERSION")
1456 + "</strong></h1>");
1457 message.append("<strong>Last Updated: <em>"
1458 + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown")
1459 + "</em></strong>");
1465 message.append("<strong>Version "
1466 + jalview.bin.Cache.getProperty("VERSION")
1467 + "; last updated: "
1468 + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown"));
1471 if (jalview.bin.Cache.getDefault("LATEST_VERSION", "Checking")
1472 .equals("Checking"))
1474 message.append("<br>...Checking latest version...</br>");
1476 else if (!jalview.bin.Cache.getDefault("LATEST_VERSION", "Checking")
1477 .equals(jalview.bin.Cache.getProperty("VERSION")))
1479 boolean red = false;
1480 if (jalview.bin.Cache.getProperty("VERSION").toLowerCase()
1481 .indexOf("automated build") == -1)
1484 // Displayed when code version and jnlp version do not match and code
1485 // version is not a development build
1486 message.append("<div style=\"color: #FF0000;font-style: bold;\">");
1489 message.append("<br>!! Version "
1490 + jalview.bin.Cache.getDefault("LATEST_VERSION",
1492 + " is available for download from "
1493 + jalview.bin.Cache.getDefault("www.jalview.org",
1494 "http://www.jalview.org")
1498 message.append("</div>");
1501 message.append("<br>Authors: " + jalview.bin.Cache.getDefault(
1503 "The Jalview Authors (See AUTHORS file for current list)")
1504 + "<br><br>Development managed by The Barton Group, University of Dundee, Scotland, UK.<br>"
1505 + "<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"
1506 + "<br><br>If you use Jalview, please cite:"
1507 + "<br>Waterhouse, A.M., Procter, J.B., Martin, D.M.A, Clamp, M. and Barton, G. J. (2009)"
1508 + "<br>Jalview Version 2 - a multiple sequence alignment editor and analysis workbench"
1509 + "<br>Bioinformatics doi: 10.1093/bioinformatics/btp033"
1515 * Action on requesting Help documentation
1518 public void documentationMenuItem_actionPerformed()
1522 if (Platform.isJS())
1524 BrowserLauncher.openURL("http://www.jalview.org/help.html");
1533 Help.showHelpWindow();
1535 } catch (Exception ex)
1537 System.err.println("Error opening help: " + ex.getMessage());
1542 public void closeAll_actionPerformed(ActionEvent e)
1544 if (desktopPane == null)
1548 // TODO show a progress bar while closing?
1549 JInternalFrame[] frames = desktopPane.getAllFrames();
1550 for (int i = 0; i < frames.length; i++)
1554 frames[i].setClosed(true);
1555 } catch (java.beans.PropertyVetoException ex)
1559 Jalview.setCurrentAlignFrame(null);
1560 System.out.println("ALL CLOSED");
1561 if (v_client != null)
1563 // TODO clear binding to vamsas document objects on close_all
1567 * reset state of singleton objects as appropriate (clear down session state
1568 * when all windows are closed)
1570 getStructureSelectionManager().resetAll();
1574 public void raiseRelated_actionPerformed(ActionEvent e)
1576 reorderAssociatedWindows(false, false);
1580 public void minimizeAssociated_actionPerformed(ActionEvent e)
1582 reorderAssociatedWindows(true, false);
1585 void closeAssociatedWindows()
1587 reorderAssociatedWindows(false, true);
1593 * @seejalview.jbgui.GDesktop#garbageCollect_actionPerformed(java.awt.event.
1597 protected void garbageCollect_actionPerformed(ActionEvent e)
1599 // We simply collect the garbage
1600 jalview.bin.Cache.log.debug("Collecting garbage...");
1602 jalview.bin.Cache.log.debug("Finished garbage collection.");
1609 * jalview.jbgui.GDesktop#showMemusage_actionPerformed(java.awt.event.ActionEvent
1613 protected void showMemusage_actionPerformed(ActionEvent e)
1615 getDesktopPane().showMemoryUsage(showMemusage.isSelected());
1622 * jalview.jbgui.GDesktop#showConsole_actionPerformed(java.awt.event.ActionEvent
1626 protected void showConsole_actionPerformed(ActionEvent e)
1628 showConsole(showConsole.isSelected());
1631 Console jconsole = null;
1634 * control whether the java console is visible or not
1638 void showConsole(boolean selected)
1640 // TODO: decide if we should update properties file
1641 if (jconsole != null) // BH 2018
1643 showConsole.setSelected(selected);
1644 Cache.setProperty("SHOW_JAVA_CONSOLE",
1645 Boolean.valueOf(selected).toString());
1646 jconsole.setVisible(selected);
1650 void reorderAssociatedWindows(boolean minimize, boolean close)
1652 JInternalFrame[] frames = getDesktopPane().getAllFrames();
1653 if (frames == null || frames.length < 1)
1658 AlignmentViewport source = null, target = null;
1659 if (frames[0] instanceof AlignFrame)
1661 source = ((AlignFrame) frames[0]).getCurrentView();
1663 else if (frames[0] instanceof TreePanel)
1665 source = ((TreePanel) frames[0]).getViewPort();
1667 else if (frames[0] instanceof PCAPanel)
1669 source = ((PCAPanel) frames[0]).av;
1671 else if (frames[0].getContentPane() instanceof PairwiseAlignPanel)
1673 source = ((PairwiseAlignPanel) frames[0].getContentPane()).av;
1678 for (int i = 0; i < frames.length; i++)
1681 if (frames[i] == null)
1685 if (frames[i] instanceof AlignFrame)
1687 target = ((AlignFrame) frames[i]).getCurrentView();
1689 else if (frames[i] instanceof TreePanel)
1691 target = ((TreePanel) frames[i]).getViewPort();
1693 else if (frames[i] instanceof PCAPanel)
1695 target = ((PCAPanel) frames[i]).av;
1697 else if (frames[i].getContentPane() instanceof PairwiseAlignPanel)
1699 target = ((PairwiseAlignPanel) frames[i].getContentPane()).av;
1702 if (source == target)
1708 frames[i].setClosed(true);
1712 frames[i].setIcon(minimize);
1715 frames[i].toFront();
1719 } catch (java.beans.PropertyVetoException ex)
1734 protected void preferences_actionPerformed(ActionEvent e)
1740 * Prompts the user to choose a file and then saves the Jalview state as a
1741 * Jalview project file
1744 public void saveState_actionPerformed()
1746 saveState_actionPerformed(false);
1749 public void saveState_actionPerformed(boolean saveAs)
1751 java.io.File projectFile = getProjectFile();
1752 // autoSave indicates we already have a file and don't need to ask
1753 boolean autoSave = projectFile != null && !saveAs
1754 && BackupFiles.getEnabled();
1756 // System.out.println("autoSave="+autoSave+", projectFile='"+projectFile+"',
1757 // saveAs="+saveAs+", Backups
1758 // "+(BackupFiles.getEnabled()?"enabled":"disabled"));
1760 boolean approveSave = false;
1763 JalviewFileChooser chooser = new JalviewFileChooser("jvp",
1766 chooser.setFileView(new JalviewFileView());
1767 chooser.setDialogTitle(MessageManager.getString("label.save_state"));
1769 int value = chooser.showSaveDialog(this);
1771 if (value == JalviewFileChooser.APPROVE_OPTION)
1773 projectFile = chooser.getSelectedFile();
1774 setProjectFile(projectFile);
1779 if (approveSave || autoSave)
1781 final Desktop me = this;
1782 final java.io.File chosenFile = projectFile;
1783 new Thread(new Runnable()
1788 // TODO: refactor to Jalview desktop session controller action.
1789 setProgressBar(MessageManager.formatMessage(
1790 "label.saving_jalview_project", new Object[]
1791 { chosenFile.getName() }), chosenFile.hashCode());
1792 jalview.bin.Cache.setProperty("LAST_DIRECTORY",
1793 chosenFile.getParent());
1794 // TODO catch and handle errors for savestate
1795 // TODO prevent user from messing with the Desktop whilst we're saving
1798 boolean doBackup = BackupFiles.getEnabled();
1799 BackupFiles backupfiles = doBackup ? new BackupFiles(chosenFile) : null;
1801 new Jalview2XML().saveState(doBackup ? backupfiles.getTempFile() : chosenFile);
1805 backupfiles.setWriteSuccess(true);
1806 backupfiles.rollBackupsAndRenameTempFile();
1808 } catch (OutOfMemoryError oom)
1810 new OOMWarning("Whilst saving current state to "
1811 + chosenFile.getName(), oom);
1812 } catch (Exception ex)
1814 Cache.log.error("Problems whilst trying to save to "
1815 + chosenFile.getName(), ex);
1816 JvOptionPane.showMessageDialog(me,
1817 MessageManager.formatMessage(
1818 "label.error_whilst_saving_current_state_to",
1820 { chosenFile.getName() }),
1821 MessageManager.getString("label.couldnt_save_project"),
1822 JvOptionPane.WARNING_MESSAGE);
1824 setProgressBar(null, chosenFile.hashCode());
1831 public void saveAsState_actionPerformed(ActionEvent e)
1833 saveState_actionPerformed(true);
1836 protected void setProjectFile(File choice)
1838 this.projectFile = choice;
1841 public File getProjectFile()
1843 return this.projectFile;
1847 * Shows a file chooser dialog and tries to read in the selected file as a
1851 public void loadState_actionPerformed()
1853 final String[] suffix = new String[] { "jvp", "jar" };
1854 final String[] desc = new String[] { "Jalview Project",
1855 "Jalview Project (old)" };
1856 JalviewFileChooser chooser = new JalviewFileChooser(
1857 Cache.getProperty("LAST_DIRECTORY"), suffix, desc,
1858 "Jalview Project", true, true); // last two booleans: allFiles,
1860 chooser.setFileView(new JalviewFileView());
1861 chooser.setDialogTitle(MessageManager.getString("label.restore_state"));
1862 chooser.setResponseHandler(0, new Runnable()
1867 File selectedFile = chooser.getSelectedFile();
1868 setProjectFile(selectedFile);
1869 String choice = selectedFile.getAbsolutePath();
1870 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1871 new Thread(new Runnable()
1878 new Jalview2XML().loadJalviewAlign(choice);
1879 } catch (OutOfMemoryError oom)
1881 new OOMWarning("Whilst loading project from " + choice, oom);
1882 } catch (Exception ex)
1885 "Problems whilst loading project from " + choice, ex);
1886 JvOptionPane.showMessageDialog(Desktop.getDesktopPane(),
1887 MessageManager.formatMessage(
1888 "label.error_whilst_loading_project_from",
1891 MessageManager.getString("label.couldnt_load_project"),
1892 JvOptionPane.WARNING_MESSAGE);
1899 chooser.showOpenDialog(this);
1903 public void inputSequence_actionPerformed(ActionEvent e)
1905 new SequenceFetcher(this);
1908 JPanel progressPanel;
1910 ArrayList<JPanel> fileLoadingPanels = new ArrayList<>();
1912 public void startLoading(final Object fileName)
1914 if (fileLoadingCount == 0)
1916 fileLoadingPanels.add(addProgressPanel(MessageManager
1917 .formatMessage("label.loading_file", new Object[]
1923 private JPanel addProgressPanel(String string)
1925 if (progressPanel == null)
1927 progressPanel = new JPanel(new GridLayout(1, 1));
1928 totalProgressCount = 0;
1929 getContentPane().add(progressPanel, BorderLayout.SOUTH);
1931 JPanel thisprogress = new JPanel(new BorderLayout(10, 5));
1932 JProgressBar progressBar = new JProgressBar();
1933 progressBar.setIndeterminate(true);
1935 thisprogress.add(new JLabel(string), BorderLayout.WEST);
1937 thisprogress.add(progressBar, BorderLayout.CENTER);
1938 progressPanel.add(thisprogress);
1939 ((GridLayout) progressPanel.getLayout()).setRows(
1940 ((GridLayout) progressPanel.getLayout()).getRows() + 1);
1941 ++totalProgressCount;
1943 return thisprogress;
1946 int totalProgressCount = 0;
1948 private void removeProgressPanel(JPanel progbar)
1950 if (progressPanel != null)
1952 synchronized (progressPanel)
1954 progressPanel.remove(progbar);
1955 GridLayout gl = (GridLayout) progressPanel.getLayout();
1956 gl.setRows(gl.getRows() - 1);
1957 if (--totalProgressCount < 1)
1959 this.getContentPane().remove(progressPanel);
1960 progressPanel = null;
1967 public void stopLoading()
1970 if (fileLoadingCount < 1)
1972 while (fileLoadingPanels.size() > 0)
1974 removeProgressPanel(fileLoadingPanels.remove(0));
1976 fileLoadingPanels.clear();
1977 fileLoadingCount = 0;
1982 public static int getViewCount(String alignmentId)
1984 AlignmentViewport[] aps = getViewports(alignmentId);
1985 return (aps == null) ? 0 : aps.length;
1990 * @param alignmentId
1991 * - if null, all sets are returned
1992 * @return all AlignmentPanels concerning the alignmentId sequence set
1994 public static AlignmentPanel[] getAlignmentPanels(String alignmentId)
1996 if (Desktop.getDesktopPane() == null)
1998 // no frames created and in headless mode
1999 // TODO: verify that frames are recoverable when in headless mode
2002 List<AlignmentPanel> aps = new ArrayList<>();
2003 AlignFrame[] frames = getAlignFrames();
2008 for (AlignFrame af : frames)
2010 for (AlignmentPanel ap : af.alignPanels)
2012 if (alignmentId == null
2013 || alignmentId.equals(ap.av.getSequenceSetId()))
2019 if (aps.size() == 0)
2023 AlignmentPanel[] vap = aps.toArray(new AlignmentPanel[aps.size()]);
2028 * get all the viewports on an alignment.
2030 * @param sequenceSetId
2031 * unique alignment id (may be null - all viewports returned in that
2033 * @return all viewports on the alignment bound to sequenceSetId
2035 public static AlignmentViewport[] getViewports(String sequenceSetId)
2037 List<AlignmentViewport> viewp = new ArrayList<>();
2038 if (getDesktopPane() != null)
2040 AlignFrame[] frames = Desktop.getAlignFrames();
2042 for (AlignFrame afr : frames)
2044 if (sequenceSetId == null || afr.getViewport().getSequenceSetId()
2045 .equals(sequenceSetId))
2047 if (afr.alignPanels != null)
2049 for (AlignmentPanel ap : afr.alignPanels)
2051 if (sequenceSetId == null
2052 || sequenceSetId.equals(ap.av.getSequenceSetId()))
2060 viewp.add(afr.getViewport());
2064 if (viewp.size() > 0)
2066 return viewp.toArray(new AlignmentViewport[viewp.size()]);
2073 * Explode the views in the given frame into separate AlignFrame
2077 public static void explodeViews(AlignFrame af)
2079 int size = af.alignPanels.size();
2085 for (int i = 0; i < size; i++)
2087 AlignmentPanel ap = af.alignPanels.get(i);
2088 AlignFrame newaf = new AlignFrame(ap);
2091 * Restore the view's last exploded frame geometry if known. Multiple
2092 * views from one exploded frame share and restore the same (frame)
2093 * position and size.
2095 Rectangle geometry = ap.av.getExplodedGeometry();
2096 if (geometry != null)
2098 newaf.setBounds(geometry);
2101 ap.av.setGatherViewsHere(false);
2103 addInternalFrame(newaf, af.getTitle(), AlignFrame.DEFAULT_WIDTH,
2104 AlignFrame.DEFAULT_HEIGHT);
2107 af.alignPanels.clear();
2108 af.closeMenuItem_actionPerformed(true);
2113 * Gather expanded views (separate AlignFrame's) with the same sequence set
2114 * identifier back in to this frame as additional views, and close the expanded
2115 * views. Note the expanded frames may themselves have multiple views. We take
2120 public void gatherViews(AlignFrame source)
2122 source.viewport.setGatherViewsHere(true);
2123 source.viewport.setExplodedGeometry(source.getBounds());
2124 JInternalFrame[] frames = getAllFrames();
2125 String viewId = source.viewport.getSequenceSetId();
2127 for (int t = 0; t < frames.length; t++)
2129 if (frames[t] instanceof AlignFrame && frames[t] != source)
2131 AlignFrame af = (AlignFrame) frames[t];
2132 boolean gatherThis = false;
2133 for (int a = 0; a < af.alignPanels.size(); a++)
2135 AlignmentPanel ap = af.alignPanels.get(a);
2136 if (viewId.equals(ap.av.getSequenceSetId()))
2139 ap.av.setGatherViewsHere(false);
2140 ap.av.setExplodedGeometry(af.getBounds());
2141 source.addAlignmentPanel(ap, false);
2147 af.alignPanels.clear();
2148 af.closeMenuItem_actionPerformed(true);
2155 jalview.gui.VamsasApplication v_client = null;
2158 public void vamsasImport_actionPerformed(ActionEvent e)
2160 // TODO: JAL-3048 not needed for Jalview-JS
2162 if (v_client == null)
2164 // Load and try to start a session.
2165 JalviewFileChooser chooser = new JalviewFileChooser(
2166 jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
2168 chooser.setFileView(new JalviewFileView());
2169 chooser.setDialogTitle(
2170 MessageManager.getString("label.open_saved_vamsas_session"));
2171 chooser.setToolTipText(MessageManager.getString(
2172 "label.select_vamsas_session_opened_as_new_vamsas_session"));
2174 int value = chooser.showOpenDialog(this);
2176 if (value == JalviewFileChooser.APPROVE_OPTION)
2178 String fle = chooser.getSelectedFile().toString();
2179 if (!vamsasImport(chooser.getSelectedFile()))
2181 JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
2182 MessageManager.formatMessage(
2183 "label.couldnt_import_as_vamsas_session",
2187 .getString("label.vamsas_document_import_failed"),
2188 JvOptionPane.ERROR_MESSAGE);
2194 jalview.bin.Cache.log.error(
2195 "Implementation error - load session from a running session is not supported.");
2200 * import file into a new vamsas session (uses jalview.gui.VamsasApplication)
2203 * @return true if import was a success and a session was started.
2205 public boolean vamsasImport(URL url)
2207 // TODO: create progress bar
2208 if (v_client != null)
2211 jalview.bin.Cache.log.error(
2212 "Implementation error - load session from a running session is not supported.");
2218 // copy the URL content to a temporary local file
2219 // TODO: be a bit cleverer here with nio (?!)
2220 File file = File.createTempFile("vdocfromurl", ".vdj");
2221 FileOutputStream fos = new FileOutputStream(file);
2222 BufferedInputStream bis = new BufferedInputStream(url.openStream());
2223 byte[] buffer = new byte[2048];
2225 while ((ln = bis.read(buffer)) > -1)
2227 fos.write(buffer, 0, ln);
2231 v_client = new jalview.gui.VamsasApplication(this, file,
2232 url.toExternalForm());
2233 } catch (Exception ex)
2235 jalview.bin.Cache.log.error(
2236 "Failed to create new vamsas session from contents of URL "
2241 setupVamsasConnectedGui();
2242 v_client.initial_update(); // TODO: thread ?
2243 return v_client.inSession();
2247 * import file into a new vamsas session (uses jalview.gui.VamsasApplication)
2250 * @return true if import was a success and a session was started.
2252 public boolean vamsasImport(File file)
2254 if (v_client != null)
2257 jalview.bin.Cache.log.error(
2258 "Implementation error - load session from a running session is not supported.");
2262 setProgressBar(MessageManager.formatMessage(
2263 "status.importing_vamsas_session_from", new Object[]
2264 { file.getName() }), file.hashCode());
2267 v_client = new jalview.gui.VamsasApplication(this, file, null);
2268 } catch (Exception ex)
2270 setProgressBar(MessageManager.formatMessage(
2271 "status.importing_vamsas_session_from", new Object[]
2272 { file.getName() }), file.hashCode());
2273 jalview.bin.Cache.log.error(
2274 "New vamsas session from existing session file failed:", ex);
2277 setupVamsasConnectedGui();
2278 v_client.initial_update(); // TODO: thread ?
2279 setProgressBar(MessageManager.formatMessage(
2280 "status.importing_vamsas_session_from", new Object[]
2281 { file.getName() }), file.hashCode());
2282 return v_client.inSession();
2285 public boolean joinVamsasSession(String mysesid)
2287 if (v_client != null)
2289 throw new Error(MessageManager
2290 .getString("error.try_join_vamsas_session_another"));
2292 if (mysesid == null)
2295 MessageManager.getString("error.invalid_vamsas_session_id"));
2297 v_client = new VamsasApplication(this, mysesid);
2298 setupVamsasConnectedGui();
2299 v_client.initial_update();
2300 return (v_client.inSession());
2304 public void vamsasStart_actionPerformed(ActionEvent e)
2306 if (v_client == null)
2309 // we just start a default session for moment.
2311 * JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache.
2312 * getProperty("LAST_DIRECTORY"));
2314 * chooser.setFileView(new JalviewFileView());
2315 * chooser.setDialogTitle("Load Vamsas file");
2316 * chooser.setToolTipText("Import");
2318 * int value = chooser.showOpenDialog(this);
2320 * if (value == JalviewFileChooser.APPROVE_OPTION) { v_client = new
2321 * jalview.gui.VamsasApplication(this, chooser.getSelectedFile());
2323 v_client = new VamsasApplication(this);
2324 setupVamsasConnectedGui();
2325 v_client.initial_update(); // TODO: thread ?
2329 // store current data in session.
2330 v_client.push_update(); // TODO: thread
2334 protected void setupVamsasConnectedGui()
2336 vamsasStart.setText(MessageManager.getString("label.session_update"));
2337 vamsasSave.setVisible(true);
2338 vamsasStop.setVisible(true);
2339 vamsasImport.setVisible(false); // Document import to existing session is
2340 // not possible for vamsas-client-1.0.
2343 protected void setupVamsasDisconnectedGui()
2345 vamsasSave.setVisible(false);
2346 vamsasStop.setVisible(false);
2347 vamsasImport.setVisible(true);
2349 .setText(MessageManager.getString("label.new_vamsas_session"));
2353 public void vamsasStop_actionPerformed(ActionEvent e)
2355 if (v_client != null)
2357 v_client.end_session();
2359 setupVamsasDisconnectedGui();
2363 protected void buildVamsasStMenu()
2365 if (v_client == null)
2367 String[] sess = null;
2370 sess = VamsasApplication.getSessionList();
2371 } catch (Exception e)
2373 jalview.bin.Cache.log.warn("Problem getting current sessions list.",
2379 jalview.bin.Cache.log.debug(
2380 "Got current sessions list: " + sess.length + " entries.");
2381 VamsasStMenu.removeAll();
2382 for (int i = 0; i < sess.length; i++)
2384 JMenuItem sessit = new JMenuItem();
2385 sessit.setText(sess[i]);
2386 sessit.setToolTipText(MessageManager
2387 .formatMessage("label.connect_to_session", new Object[]
2389 final Desktop dsktp = this;
2390 final String mysesid = sess[i];
2391 sessit.addActionListener(new ActionListener()
2395 public void actionPerformed(ActionEvent e)
2397 if (dsktp.v_client == null)
2399 Thread rthr = new Thread(new Runnable()
2405 dsktp.v_client = new VamsasApplication(dsktp, mysesid);
2406 dsktp.setupVamsasConnectedGui();
2407 dsktp.v_client.initial_update();
2415 VamsasStMenu.add(sessit);
2417 // don't show an empty menu.
2418 VamsasStMenu.setVisible(sess.length > 0);
2423 jalview.bin.Cache.log.debug("No current vamsas sessions.");
2424 VamsasStMenu.removeAll();
2425 VamsasStMenu.setVisible(false);
2430 // Not interested in the content. Just hide ourselves.
2431 VamsasStMenu.setVisible(false);
2436 public void vamsasSave_actionPerformed(ActionEvent e)
2438 // TODO: JAL-3048 not needed for Jalview-JS
2440 if (v_client != null)
2442 // TODO: VAMSAS DOCUMENT EXTENSION is VDJ
2443 JalviewFileChooser chooser = new JalviewFileChooser("vdj",
2446 chooser.setFileView(new JalviewFileView());
2447 chooser.setDialogTitle(MessageManager
2448 .getString("label.save_vamsas_document_archive"));
2450 int value = chooser.showSaveDialog(this);
2452 if (value == JalviewFileChooser.APPROVE_OPTION)
2454 java.io.File choice = chooser.getSelectedFile();
2455 JPanel progpanel = addProgressPanel(MessageManager
2456 .formatMessage("label.saving_vamsas_doc", new Object[]
2457 { choice.getName() }));
2458 Cache.setProperty("LAST_DIRECTORY", choice.getParent());
2459 String warnmsg = null;
2460 String warnttl = null;
2463 v_client.vclient.storeDocument(choice);
2466 warnttl = "Serious Problem saving Vamsas Document";
2467 warnmsg = ex.toString();
2468 jalview.bin.Cache.log
2469 .error("Error Whilst saving document to " + choice, ex);
2471 } catch (Exception ex)
2473 warnttl = "Problem saving Vamsas Document.";
2474 warnmsg = ex.toString();
2475 jalview.bin.Cache.log.warn(
2476 "Exception Whilst saving document to " + choice, ex);
2479 removeProgressPanel(progpanel);
2480 if (warnmsg != null)
2482 JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
2484 warnmsg, warnttl, JvOptionPane.ERROR_MESSAGE);
2490 JPanel vamUpdate = null;
2493 * hide vamsas user gui bits when a vamsas document event is being handled.
2496 * true to hide gui, false to reveal gui
2498 public void setVamsasUpdate(boolean b)
2500 Cache.log.debug("Setting gui for Vamsas update "
2501 + (b ? "in progress" : "finished"));
2503 if (vamUpdate != null)
2505 this.removeProgressPanel(vamUpdate);
2509 vamUpdate = this.addProgressPanel(
2510 MessageManager.getString("label.updating_vamsas_session"));
2512 vamsasStart.setVisible(!b);
2513 vamsasStop.setVisible(!b);
2514 vamsasSave.setVisible(!b);
2517 public JInternalFrame[] getAllFrames()
2519 return desktopPane.getAllFrames();
2523 * Checks the given url to see if it gives a response indicating that the user
2524 * should be informed of a new questionnaire.
2528 public void checkForQuestionnaire(String url)
2530 UserQuestionnaireCheck jvq = new UserQuestionnaireCheck(url);
2531 // javax.swing.SwingUtilities.invokeLater(jvq);
2532 new Thread(jvq).start();
2535 public void checkURLLinks()
2537 // Thread off the URL link checker
2538 addDialogThread(new Runnable()
2543 if (Cache.getDefault("CHECKURLLINKS", true))
2545 // check what the actual links are - if it's just the default don't
2546 // bother with the warning
2547 List<String> links = Preferences.sequenceUrlLinks
2550 // only need to check links if there is one with a
2551 // SEQUENCE_ID which is not the default EMBL_EBI link
2552 ListIterator<String> li = links.listIterator();
2553 boolean check = false;
2554 List<JLabel> urls = new ArrayList<>();
2555 while (li.hasNext())
2557 String link = li.next();
2558 if (link.contains(UrlConstants.SEQUENCE_ID)
2559 && !UrlConstants.isDefaultString(link))
2562 int barPos = link.indexOf("|");
2563 String urlMsg = barPos == -1 ? link
2564 : link.substring(0, barPos) + ": "
2565 + link.substring(barPos + 1);
2566 urls.add(new JLabel(urlMsg));
2574 // ask user to check in case URL links use old style tokens
2575 // ($SEQUENCE_ID$ for sequence id _or_ accession id)
2576 JPanel msgPanel = new JPanel();
2577 msgPanel.setLayout(new BoxLayout(msgPanel, BoxLayout.PAGE_AXIS));
2578 msgPanel.add(Box.createVerticalGlue());
2579 JLabel msg = new JLabel(MessageManager
2580 .getString("label.SEQUENCE_ID_for_DB_ACCESSION1"));
2581 JLabel msg2 = new JLabel(MessageManager
2582 .getString("label.SEQUENCE_ID_for_DB_ACCESSION2"));
2584 for (JLabel url : urls)
2590 final JCheckBox jcb = new JCheckBox(
2591 MessageManager.getString("label.do_not_display_again"));
2592 jcb.addActionListener(new ActionListener()
2595 public void actionPerformed(ActionEvent e)
2597 // update Cache settings for "don't show this again"
2598 boolean showWarningAgain = !jcb.isSelected();
2599 Cache.setProperty("CHECKURLLINKS",
2600 Boolean.valueOf(showWarningAgain).toString());
2605 JvOptionPane.showMessageDialog(desktopPane, msgPanel,
2607 .getString("label.SEQUENCE_ID_no_longer_used"),
2608 JvOptionPane.WARNING_MESSAGE);
2615 * Proxy class for JDesktopPane which optionally displays the current memory
2616 * usage and highlights the desktop area with a red bar if free memory runs low.
2620 public class MyDesktopPane extends JDesktopPane
2623 private static final float ONE_MB = 1048576f;
2625 boolean showMemoryUsage = false;
2629 java.text.NumberFormat df;
2631 float maxMemory, allocatedMemory, freeMemory, totalFreeMemory,
2634 public MyDesktopPane(boolean showMemoryUsage)
2636 showMemoryUsage(showMemoryUsage);
2639 public void showMemoryUsage(boolean showMemory)
2641 this.showMemoryUsage = showMemory;
2644 Thread worker = new Thread(this);
2650 public boolean isShowMemoryUsage()
2652 return showMemoryUsage;
2658 df = java.text.NumberFormat.getNumberInstance();
2659 df.setMaximumFractionDigits(2);
2660 runtime = Runtime.getRuntime();
2662 while (showMemoryUsage)
2666 maxMemory = runtime.maxMemory() / ONE_MB;
2667 allocatedMemory = runtime.totalMemory() / ONE_MB;
2668 freeMemory = runtime.freeMemory() / ONE_MB;
2669 totalFreeMemory = freeMemory + (maxMemory - allocatedMemory);
2671 percentUsage = (totalFreeMemory / maxMemory) * 100;
2673 // if (percentUsage < 20)
2675 // border1 = BorderFactory.createMatteBorder(12, 12, 12, 12,
2677 // instance.set.setBorder(border1);
2680 // sleep after showing usage
2682 } catch (Exception ex)
2684 ex.printStackTrace();
2690 public void paintComponent(Graphics g)
2692 if (showMemoryUsage && g != null && df != null)
2694 if (percentUsage < 20)
2696 g.setColor(Color.red);
2698 FontMetrics fm = g.getFontMetrics();
2701 g.drawString(MessageManager.formatMessage("label.memory_stats",
2703 { df.format(totalFreeMemory), df.format(maxMemory),
2704 df.format(percentUsage) }),
2705 10, getHeight() - fm.getHeight());
2712 * Accessor method to quickly get all the AlignmentFrames loaded.
2714 * @return an array of AlignFrame, or null if none found
2716 public static AlignFrame[] getAlignFrames()
2718 if (Jalview.isHeadlessMode())
2720 // Desktop.getDesktop() is null in headless mode
2721 return new AlignFrame[] { Jalview.getCurrentAlignFrame() };
2724 JInternalFrame[] frames = Desktop.getDesktopPane().getAllFrames();
2730 List<AlignFrame> avp = new ArrayList<>();
2732 for (int i = frames.length - 1; i > -1; i--)
2734 if (frames[i] instanceof AlignFrame)
2736 avp.add((AlignFrame) frames[i]);
2738 else if (frames[i] instanceof SplitFrame)
2741 * Also check for a split frame containing an AlignFrame
2743 GSplitFrame sf = (GSplitFrame) frames[i];
2744 if (sf.getTopFrame() instanceof AlignFrame)
2746 avp.add((AlignFrame) sf.getTopFrame());
2748 if (sf.getBottomFrame() instanceof AlignFrame)
2750 avp.add((AlignFrame) sf.getBottomFrame());
2754 if (avp.size() == 0)
2758 AlignFrame afs[] = avp.toArray(new AlignFrame[avp.size()]);
2763 * Returns an array of any AppJmol frames in the Desktop (or null if none).
2767 public GStructureViewer[] getJmols()
2769 JInternalFrame[] frames = Desktop.getDesktopPane().getAllFrames();
2775 List<GStructureViewer> avp = new ArrayList<>();
2777 for (int i = frames.length - 1; i > -1; i--)
2779 if (frames[i] instanceof AppJmol)
2781 GStructureViewer af = (GStructureViewer) frames[i];
2785 if (avp.size() == 0)
2789 GStructureViewer afs[] = avp.toArray(new GStructureViewer[avp.size()]);
2794 * Add Groovy Support to Jalview
2797 public void groovyShell_actionPerformed()
2801 openGroovyConsole();
2802 } catch (Exception ex)
2804 jalview.bin.Cache.log.error("Groovy Shell Creation failed.", ex);
2805 JvOptionPane.showInternalMessageDialog(desktopPane,
2807 MessageManager.getString("label.couldnt_create_groovy_shell"),
2808 MessageManager.getString("label.groovy_support_failed"),
2809 JvOptionPane.ERROR_MESSAGE);
2814 * Open the Groovy console
2816 private void openGroovyConsole()
2818 if (groovyConsole == null)
2820 groovyConsole = new groovy.ui.Console();
2821 groovyConsole.setVariable("Jalview", this);
2822 groovyConsole.run();
2825 * We allow only one console at a time, so that AlignFrame menu option
2826 * 'Calculate | Run Groovy script' is unambiguous.
2827 * Disable 'Groovy Console', and enable 'Run script', when the console is
2828 * opened, and the reverse when it is closed
2830 Window window = (Window) groovyConsole.getFrame();
2831 window.addWindowListener(new WindowAdapter()
2834 public void windowClosed(WindowEvent e)
2837 * rebind CMD-Q from Groovy Console to Jalview Quit
2840 enableExecuteGroovy(false);
2846 * show Groovy console window (after close and reopen)
2848 ((Window) groovyConsole.getFrame()).setVisible(true);
2851 * if we got this far, enable 'Run Groovy' in AlignFrame menus
2852 * and disable opening a second console
2854 enableExecuteGroovy(true);
2858 * Bind Ctrl/Cmd-Q to Quit - for reset as Groovy Console takes over this binding
2861 protected void addQuitHandler()
2863 getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
2864 .put(KeyStroke.getKeyStroke(KeyEvent.VK_Q,
2865 Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()),
2867 getRootPane().getActionMap().put("Quit", new AbstractAction()
2870 public void actionPerformed(ActionEvent e)
2878 * Enable or disable 'Run Groovy script' in AlignFrame calculate menus
2881 * true if Groovy console is open
2883 public void enableExecuteGroovy(boolean enabled)
2886 * disable opening a second Groovy console
2887 * (or re-enable when the console is closed)
2889 groovyShell.setEnabled(!enabled);
2891 AlignFrame[] alignFrames = getAlignFrames();
2892 if (alignFrames != null)
2894 for (AlignFrame af : alignFrames)
2896 af.setGroovyEnabled(enabled);
2902 * Progress bars managed by the IProgressIndicator method.
2904 private Hashtable<Long, JPanel> progressBars;
2906 private Hashtable<Long, IProgressIndicatorHandler> progressBarHandlers;
2911 * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
2914 public void setProgressBar(String message, long id)
2916 Platform.timeCheck("Desktop " + message, Platform.TIME_MARK);
2918 if (progressBars == null)
2920 progressBars = new Hashtable<>();
2921 progressBarHandlers = new Hashtable<>();
2924 if (progressBars.get(new Long(id)) != null)
2926 JPanel panel = progressBars.remove(new Long(id));
2927 if (progressBarHandlers.contains(new Long(id)))
2929 progressBarHandlers.remove(new Long(id));
2931 removeProgressPanel(panel);
2935 progressBars.put(new Long(id), addProgressPanel(message));
2942 * @see jalview.gui.IProgressIndicator#registerHandler(long,
2943 * jalview.gui.IProgressIndicatorHandler)
2946 public void registerHandler(final long id,
2947 final IProgressIndicatorHandler handler)
2949 if (progressBarHandlers == null
2950 || !progressBars.containsKey(new Long(id)))
2952 throw new Error(MessageManager.getString(
2953 "error.call_setprogressbar_before_registering_handler"));
2955 progressBarHandlers.put(new Long(id), handler);
2956 final JPanel progressPanel = progressBars.get(new Long(id));
2957 if (handler.canCancel())
2959 JButton cancel = new JButton(
2960 MessageManager.getString("action.cancel"));
2961 final IProgressIndicator us = this;
2962 cancel.addActionListener(new ActionListener()
2966 public void actionPerformed(ActionEvent e)
2968 handler.cancelActivity(id);
2969 us.setProgressBar(MessageManager
2970 .formatMessage("label.cancelled_params", new Object[]
2971 { ((JLabel) progressPanel.getComponent(0)).getText() }),
2975 progressPanel.add(cancel, BorderLayout.EAST);
2981 * @return true if any progress bars are still active
2984 public boolean operationInProgress()
2986 if (progressBars != null && progressBars.size() > 0)
2994 * This will return the first AlignFrame holding the given viewport instance. It
2995 * will break if there are more than one AlignFrames viewing a particular av.
2998 * @return alignFrame for viewport
3000 public static AlignFrame getAlignFrameFor(AlignViewportI viewport)
3002 if (getDesktopPane() != null)
3004 AlignmentPanel[] aps = getAlignmentPanels(
3005 viewport.getSequenceSetId());
3006 for (int panel = 0; aps != null && panel < aps.length; panel++)
3008 if (aps[panel] != null && aps[panel].av == viewport)
3010 return aps[panel].alignFrame;
3017 public VamsasApplication getVamsasApplication()
3024 * flag set if jalview GUI is being operated programmatically
3026 private boolean inBatchMode = false;
3029 * check if jalview GUI is being operated programmatically
3031 * @return inBatchMode
3033 public boolean isInBatchMode()
3039 * set flag if jalview GUI is being operated programmatically
3041 * @param inBatchMode
3043 public void setInBatchMode(boolean inBatchMode)
3045 this.inBatchMode = inBatchMode;
3048 public void startServiceDiscovery()
3050 startServiceDiscovery(false);
3053 public void startServiceDiscovery(boolean blocking)
3055 boolean alive = true;
3056 Thread t0 = null, t1 = null, t2 = null;
3057 // JAL-940 - JALVIEW 1 services are now being EOLed as of JABA 2.1 release
3060 // todo: changesupport handlers need to be transferred
3061 if (discoverer == null)
3063 discoverer = Discoverer.getInstance();
3064 // register PCS handler for getDesktop().
3065 discoverer.addPropertyChangeListener(changeSupport);
3067 // JAL-940 - disabled JWS1 service configuration - always start discoverer
3068 // until we phase out completely
3069 (t0 = new Thread(discoverer)).start();
3072 if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
3074 t2 = jalview.ws.jws2.Jws2Discoverer.getInstance()
3075 .startDiscoverer(changeSupport);
3079 // TODO: do rest service discovery
3088 } catch (Exception e)
3091 alive = (t1 != null && t1.isAlive()) || (t2 != null && t2.isAlive())
3092 || (t3 != null && t3.isAlive())
3093 || (t0 != null && t0.isAlive());
3099 * called to check if the service discovery process completed successfully.
3103 protected void JalviewServicesChanged(PropertyChangeEvent evt)
3105 if (evt.getNewValue() == null || evt.getNewValue() instanceof Vector)
3107 final String ermsg = jalview.ws.jws2.Jws2Discoverer.getInstance()
3108 .getErrorMessages();
3111 if (Cache.getDefault("SHOW_WSDISCOVERY_ERRORS", true))
3113 if (serviceChangedDialog == null)
3115 // only run if we aren't already displaying one of these.
3116 addDialogThread(serviceChangedDialog = new Runnable()
3123 * JalviewDialog jd =new JalviewDialog() {
3125 * @Override protected void cancelPressed() { // TODO
3126 * Auto-generated method stub
3128 * }@Override protected void okPressed() { // TODO
3129 * Auto-generated method stub
3131 * }@Override protected void raiseClosed() { // TODO
3132 * Auto-generated method stub
3134 * } }; jd.initDialogFrame(new
3135 * JLabel("<html><table width=\"450\"><tr><td>" + ermsg +
3136 * "<br/>It may be that you have invalid JABA URLs in your web service preferences,"
3137 * + " or mis-configured HTTP proxy settings.<br/>" +
3138 * "Check the <em>Connections</em> and <em>Web services</em> tab of the"
3140 * " Tools->Preferences dialog box to change them.</td></tr></table></html>"
3141 * ), true, true, "Web Service Configuration Problem", 450,
3144 * jd.waitForInput();
3146 JvOptionPane.showConfirmDialog(Desktop.getDesktopPane(),
3147 new JLabel("<html><table width=\"450\"><tr><td>"
3148 + ermsg + "</td></tr></table>"
3149 + "<p>It may be that you have invalid JABA URLs<br/>in your web service preferences,"
3150 + "<br>or as a command-line argument, or mis-configured HTTP proxy settings.</p>"
3151 + "<p>Check the <em>Connections</em> and <em>Web services</em> tab<br/>of the"
3152 + " Tools->Preferences dialog box to change them.</p></html>"),
3153 "Web Service Configuration Problem",
3154 JvOptionPane.DEFAULT_OPTION,
3155 JvOptionPane.ERROR_MESSAGE);
3156 serviceChangedDialog = null;
3165 "Errors reported by JABA discovery service. Check web services preferences.\n"
3172 Runnable serviceChangedDialog = null;
3175 * start a thread to open a URL in the configured browser. Pops up a warning
3176 * dialog to the user if there is an exception when calling out to the browser
3181 public static void showUrl(final String url)
3183 showUrl(url, Desktop.getInstance());
3187 * Like showUrl but allows progress handler to be specified
3191 * (null) or object implementing IProgressIndicator
3193 public static void showUrl(final String url,
3194 final IProgressIndicator progress)
3196 new Thread(new Runnable()
3203 if (progress != null)
3205 progress.setProgressBar(MessageManager
3206 .formatMessage("status.opening_params", new Object[]
3207 { url }), this.hashCode());
3209 BrowserLauncher.openURL(url);
3210 } catch (Exception ex)
3212 JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
3214 .getString("label.web_browser_not_found_unix"),
3215 MessageManager.getString("label.web_browser_not_found"),
3216 JvOptionPane.WARNING_MESSAGE);
3218 ex.printStackTrace();
3220 if (progress != null)
3222 progress.setProgressBar(null, this.hashCode());
3228 private WsParamSetManager wsparamManager = null;
3230 public static ParamManager getUserParameterStore()
3232 Desktop d = Desktop.getInstance();
3233 if (d.wsparamManager == null)
3235 d.wsparamManager = new WsParamSetManager();
3237 return d.wsparamManager;
3241 * static hyperlink handler proxy method for use by Jalview's internal windows
3245 public static void hyperlinkUpdate(HyperlinkEvent e)
3247 if (e.getEventType() == EventType.ACTIVATED)
3252 url = e.getURL().toString();
3253 Desktop.showUrl(url);
3254 } catch (Exception x)
3258 if (Cache.log != null)
3260 Cache.log.error("Couldn't handle string " + url + " as a URL.");
3265 "Couldn't handle string " + url + " as a URL.");
3268 // ignore any exceptions due to dud links.
3275 * single thread that handles display of dialogs to user.
3277 ExecutorService dialogExecutor = Executors.newSingleThreadExecutor();
3280 * flag indicating if dialogExecutor should try to acquire a permit
3282 volatile boolean dialogPause = true;
3287 java.util.concurrent.Semaphore block = new Semaphore(0);
3289 private groovy.ui.Console groovyConsole;
3291 public StructureViewer lastTargetedView;
3294 * add another dialog thread to the queue
3298 public void addDialogThread(final Runnable prompter)
3300 dialogExecutor.submit(new Runnable()
3310 } catch (InterruptedException x)
3314 System.out.println("Desktop headless or instanceonly" + instanceOnly
3315 + " " + Jalview.isHeadlessMode());
3316 if (instanceOnly || Jalview.isHeadlessMode())
3322 SwingUtilities.invokeAndWait(prompter);
3323 } catch (Exception q)
3325 Cache.log.warn("Unexpected Exception in dialog thread.", q);
3331 public void startDialogQueue()
3333 // set the flag so we don't pause waiting for another permit and semaphore
3334 // the current task to begin
3335 dialogPause = false;
3340 * Outputs an image of the desktop to file in EPS format, after prompting the
3341 * user for choice of Text or Lineart character rendering (unless a preference
3342 * has been set). The file name is generated as
3345 * Jalview_snapshot_nnnnn.eps where nnnnn is the current timestamp in milliseconds
3349 protected void snapShotWindow_actionPerformed(ActionEvent e)
3351 // currently the menu option to do this is not shown
3354 int width = getWidth();
3355 int height = getHeight();
3357 "Jalview_snapshot_" + System.currentTimeMillis() + ".eps");
3358 ImageWriterI writer = new ImageWriterI()
3361 public void exportImage(Graphics g) throws Exception
3364 Cache.log.info("Successfully written snapshot to file "
3365 + of.getAbsolutePath());
3368 String title = "View of desktop";
3369 ImageExporter exporter = new ImageExporter(writer, null, TYPE.EPS,
3371 exporter.doExport(of, this, width, height, title);
3375 * Explode the views in the given SplitFrame into separate SplitFrame windows.
3376 * This respects (remembers) any previous 'exploded geometry' i.e. the size and
3377 * location last time the view was expanded (if any). However it does not
3378 * remember the split pane divider location - this is set to match the
3379 * 'exploding' frame.
3383 public void explodeViews(SplitFrame sf)
3385 AlignFrame oldTopFrame = (AlignFrame) sf.getTopFrame();
3386 AlignFrame oldBottomFrame = (AlignFrame) sf.getBottomFrame();
3387 List<? extends AlignmentViewPanel> topPanels = oldTopFrame
3389 List<? extends AlignmentViewPanel> bottomPanels = oldBottomFrame
3391 int viewCount = topPanels.size();
3398 * Processing in reverse order works, forwards order leaves the first panels
3399 * not visible. I don't know why!
3401 for (int i = viewCount - 1; i >= 0; i--)
3404 * Make new top and bottom frames. These take over the respective
3405 * AlignmentPanel objects, including their AlignmentViewports, so the
3406 * cdna/protein relationships between the viewports is carried over to the
3409 * explodedGeometry holds the (x, y) position of the previously exploded
3410 * SplitFrame, and the (width, height) of the AlignFrame component
3412 AlignmentPanel topPanel = (AlignmentPanel) topPanels.get(i);
3413 AlignFrame newTopFrame = new AlignFrame(topPanel);
3414 newTopFrame.setSize(oldTopFrame.getSize());
3415 newTopFrame.setVisible(true);
3416 Rectangle geometry = ((AlignViewport) topPanel.getAlignViewport())
3417 .getExplodedGeometry();
3418 if (geometry != null)
3420 newTopFrame.setSize(geometry.getSize());
3423 AlignmentPanel bottomPanel = (AlignmentPanel) bottomPanels.get(i);
3424 AlignFrame newBottomFrame = new AlignFrame(bottomPanel);
3425 newBottomFrame.setSize(oldBottomFrame.getSize());
3426 newBottomFrame.setVisible(true);
3427 geometry = ((AlignViewport) bottomPanel.getAlignViewport())
3428 .getExplodedGeometry();
3429 if (geometry != null)
3431 newBottomFrame.setSize(geometry.getSize());
3434 topPanel.av.setGatherViewsHere(false);
3435 bottomPanel.av.setGatherViewsHere(false);
3436 JInternalFrame splitFrame = new SplitFrame(newTopFrame,
3438 if (geometry != null)
3440 splitFrame.setLocation(geometry.getLocation());
3442 Desktop.addInternalFrame(splitFrame, sf.getTitle(), -1, -1);
3446 * Clear references to the panels (now relocated in the new SplitFrames)
3447 * before closing the old SplitFrame.
3450 bottomPanels.clear();
3455 * Gather expanded split frames, sharing the same pairs of sequence set ids,
3456 * back into the given SplitFrame as additional views. Note that the gathered
3457 * frames may themselves have multiple views.
3461 public void gatherViews(GSplitFrame source)
3464 * special handling of explodedGeometry for a view within a SplitFrame: - it
3465 * holds the (x, y) position of the enclosing SplitFrame, and the (width,
3466 * height) of the AlignFrame component
3468 AlignFrame myTopFrame = (AlignFrame) source.getTopFrame();
3469 AlignFrame myBottomFrame = (AlignFrame) source.getBottomFrame();
3470 myTopFrame.viewport.setExplodedGeometry(new Rectangle(source.getX(),
3471 source.getY(), myTopFrame.getWidth(), myTopFrame.getHeight()));
3472 myBottomFrame.viewport
3473 .setExplodedGeometry(new Rectangle(source.getX(), source.getY(),
3474 myBottomFrame.getWidth(), myBottomFrame.getHeight()));
3475 myTopFrame.viewport.setGatherViewsHere(true);
3476 myBottomFrame.viewport.setGatherViewsHere(true);
3477 String topViewId = myTopFrame.viewport.getSequenceSetId();
3478 String bottomViewId = myBottomFrame.viewport.getSequenceSetId();
3480 JInternalFrame[] frames = desktopPane.getAllFrames();
3481 for (JInternalFrame frame : frames)
3483 if (frame instanceof SplitFrame && frame != source)
3485 SplitFrame sf = (SplitFrame) frame;
3486 AlignFrame topFrame = (AlignFrame) sf.getTopFrame();
3487 AlignFrame bottomFrame = (AlignFrame) sf.getBottomFrame();
3488 boolean gatherThis = false;
3489 for (int a = 0; a < topFrame.alignPanels.size(); a++)
3491 AlignmentPanel topPanel = topFrame.alignPanels.get(a);
3492 AlignmentPanel bottomPanel = bottomFrame.alignPanels.get(a);
3493 if (topViewId.equals(topPanel.av.getSequenceSetId())
3494 && bottomViewId.equals(bottomPanel.av.getSequenceSetId()))
3497 topPanel.av.setGatherViewsHere(false);
3498 bottomPanel.av.setGatherViewsHere(false);
3499 topPanel.av.setExplodedGeometry(
3500 new Rectangle(sf.getLocation(), topFrame.getSize()));
3501 bottomPanel.av.setExplodedGeometry(
3502 new Rectangle(sf.getLocation(), bottomFrame.getSize()));
3503 myTopFrame.addAlignmentPanel(topPanel, false);
3504 myBottomFrame.addAlignmentPanel(bottomPanel, false);
3510 topFrame.getAlignPanels().clear();
3511 bottomFrame.getAlignPanels().clear();
3518 * The dust settles...give focus to the tab we did this from.
3520 myTopFrame.setDisplayedView(myTopFrame.alignPanel);
3523 public static groovy.ui.Console getGroovyConsole()
3525 Desktop desktop = Desktop.getInstance();
3526 return desktop == null ? null : desktop.groovyConsole;
3530 * handles the payload of a drag and drop event.
3532 * TODO refactor to desktop utilities class
3535 * - Data source strings extracted from the drop event
3537 * - protocol for each data source extracted from the drop event
3541 * - the payload from the drop event
3544 @SuppressWarnings("unchecked")
3545 public static void transferFromDropTarget(List<Object> files,
3546 List<DataSourceType> protocols, DropTargetDropEvent evt,
3547 Transferable t) throws Exception
3550 // BH 2018 changed List<String> to List<Object> to allow for File from SwingJS
3552 // DataFlavor[] flavors = t.getTransferDataFlavors();
3553 // for (int i = 0; i < flavors.length; i++) {
3554 // if (flavors[i].isFlavorJavaFileListType()) {
3555 // evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
3556 // List<File> list = (List<File>) t.getTransferData(flavors[i]);
3557 // for (int j = 0; j < list.size(); j++) {
3558 // File file = (File) list.get(j);
3559 // byte[] data = getDroppedFileBytes(file);
3560 // fileName.setText(file.getName() + " - " + data.length + " " +
3561 // evt.getLocation());
3562 // JTextArea target = (JTextArea) ((DropTarget) evt.getSource()).getComponent();
3563 // target.setText(new String(data));
3565 // dtde.dropComplete(true);
3570 DataFlavor uriListFlavor = new DataFlavor(
3571 "text/uri-list;class=java.lang.String"), urlFlavour = null;
3574 urlFlavour = new DataFlavor(
3575 "application/x-java-url; class=java.net.URL");
3576 } catch (ClassNotFoundException cfe)
3578 Cache.log.debug("Couldn't instantiate the URL dataflavor.", cfe);
3581 if (urlFlavour != null && t.isDataFlavorSupported(urlFlavour))
3586 java.net.URL url = (URL) t.getTransferData(urlFlavour);
3587 // nb: java 8 osx bug https://bugs.openjdk.java.net/browse/JDK-8156099
3588 // means url may be null.
3591 protocols.add(DataSourceType.URL);
3592 files.add(url.toString());
3593 Cache.log.debug("Drop handled as URL dataflavor "
3594 + files.get(files.size() - 1));
3599 if (Platform.isAMacAndNotJS())
3602 "Please ignore plist error - occurs due to problem with java 8 on OSX");
3605 } catch (Throwable ex)
3607 Cache.log.debug("URL drop handler failed.", ex);
3610 if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
3612 // Works on Windows and MacOSX
3613 Cache.log.debug("Drop handled as javaFileListFlavor");
3614 for (Object file : (List<Object>) t
3615 .getTransferData(DataFlavor.javaFileListFlavor))
3618 protocols.add(DataSourceType.FILE);
3623 // Unix like behaviour
3624 boolean added = false;
3626 if (t.isDataFlavorSupported(uriListFlavor))
3628 Cache.log.debug("Drop handled as uriListFlavor");
3629 // This is used by Unix drag system
3630 data = (String) t.getTransferData(uriListFlavor);
3634 // fallback to text: workaround - on OSX where there's a JVM bug
3635 Cache.log.debug("standard URIListFlavor failed. Trying text");
3636 // try text fallback
3637 DataFlavor textDf = new DataFlavor(
3638 "text/plain;class=java.lang.String");
3639 if (t.isDataFlavorSupported(textDf))
3641 data = (String) t.getTransferData(textDf);
3644 Cache.log.debug("Plain text drop content returned "
3645 + (data == null ? "Null - failed" : data));
3650 while (protocols.size() < files.size())
3652 Cache.log.debug("Adding missing FILE protocol for "
3653 + files.get(protocols.size()));
3654 protocols.add(DataSourceType.FILE);
3656 for (java.util.StringTokenizer st = new java.util.StringTokenizer(
3657 data, "\r\n"); st.hasMoreTokens();)
3660 String s = st.nextToken();
3661 if (s.startsWith("#"))
3663 // the line is a comment (as per the RFC 2483)
3666 java.net.URI uri = new java.net.URI(s);
3667 if (uri.getScheme().toLowerCase().startsWith("http"))
3669 protocols.add(DataSourceType.URL);
3670 files.add(uri.toString());
3674 // otherwise preserve old behaviour: catch all for file objects
3675 java.io.File file = new java.io.File(uri);
3676 protocols.add(DataSourceType.FILE);
3677 files.add(file.toString());
3682 if (Cache.log.isDebugEnabled())
3684 if (data == null || !added)
3687 if (t.getTransferDataFlavors() != null
3688 && t.getTransferDataFlavors().length > 0)
3691 "Couldn't resolve drop data. Here are the supported flavors:");
3692 for (DataFlavor fl : t.getTransferDataFlavors())
3695 "Supported transfer dataflavor: " + fl.toString());
3696 Object df = t.getTransferData(fl);
3699 Cache.log.debug("Retrieves: " + df);
3703 Cache.log.debug("Retrieved nothing");
3709 Cache.log.debug("Couldn't resolve dataflavor for drop: "
3715 if (Platform.isWindowsAndNotJS())
3717 Cache.log.debug("Scanning dropped content for Windows Link Files");
3719 // resolve any .lnk files in the file drop
3720 for (int f = 0; f < files.size(); f++)
3722 String source = files.get(f).toString().toLowerCase();
3723 if (protocols.get(f).equals(DataSourceType.FILE)
3724 && (source.endsWith(".lnk") || source.endsWith(".url")
3725 || source.endsWith(".site")))
3729 Object obj = files.get(f);
3730 File lf = (obj instanceof File ? (File) obj
3731 : new File((String) obj));
3732 // process link file to get a URL
3733 Cache.log.debug("Found potential link file: " + lf);
3734 WindowsShortcut wscfile = new WindowsShortcut(lf);
3735 String fullname = wscfile.getRealFilename();
3736 protocols.set(f, FormatAdapter.checkProtocol(fullname));
3737 files.set(f, fullname);
3738 Cache.log.debug("Parsed real filename " + fullname
3739 + " to extract protocol: " + protocols.get(f));
3740 } catch (Exception ex)
3743 "Couldn't parse " + files.get(f) + " as a link file.",
3752 * Sets the Preferences property for experimental features to True or False
3753 * depending on the state of the controlling menu item
3756 protected void showExperimental_actionPerformed(boolean selected)
3758 Cache.setProperty(EXPERIMENTAL_FEATURES, Boolean.toString(selected));
3762 * Answers a (possibly empty) list of any structure viewer frames (currently for
3763 * either Jmol or Chimera) which are currently open. This may optionally be
3764 * restricted to viewers of a specified class, or viewers linked to a specified
3768 * if not null, only return viewers linked to this panel
3769 * @param structureViewerClass
3770 * if not null, only return viewers of this class
3773 public List<StructureViewerBase> getStructureViewers(
3774 AlignmentPanel apanel,
3775 Class<? extends StructureViewerBase> structureViewerClass)
3777 List<StructureViewerBase> result = new ArrayList<>();
3778 JInternalFrame[] frames = getAllFrames();
3780 for (JInternalFrame frame : frames)
3782 if (frame instanceof StructureViewerBase)
3784 if (structureViewerClass == null
3785 || structureViewerClass.isInstance(frame))
3788 || ((StructureViewerBase) frame).isLinkedWith(apanel))
3790 result.add((StructureViewerBase) frame);