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 Desktop.getInstanceOnly().
240 static 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 with the creation of frames, but no addition to
384 * the Desktop object, as in headless mode. Dialogs are not initiated.
385 * Cache.log is also initialized for some tests that require it despite there
389 public static Desktop getInstanceOnly()
392 return getInstance();
396 * Private constructor enforces singleton pattern. It is called by reflection
397 * from ApplicationSingletonProvider.getInstance().
405 * A note to implementors. It is ESSENTIAL that any activities that might
406 * block are spawned off as threads rather than waited for during this
409 if (!Platform.isJS())
411 doVamsasClientCheck();
414 doConfigureStructurePrefs();
415 setTitle("Jalview " + jalview.bin.Cache.getProperty("VERSION"));
416 setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
417 boolean selmemusage = jalview.bin.Cache.getDefault("SHOW_MEMUSAGE",
419 boolean showjconsole = jalview.bin.Cache
420 .getDefault("SHOW_JAVA_CONSOLE", false);
421 desktopPane = new MyDesktopPane(selmemusage);
423 showMemusage.setSelected(selmemusage);
424 desktopPane.setBackground(Color.white);
425 getContentPane().setLayout(new BorderLayout());
426 // alternate config - have scrollbars - see notes in JAL-153
427 // JScrollPane sp = new JScrollPane();
428 // sp.getViewport().setView(desktop);
429 // getContentPane().add(sp, BorderLayout.CENTER);
431 // BH 2018 - just an experiment to try unclipped JInternalFrames.
434 getRootPane().putClientProperty("swingjs.overflow.hidden", "false");
437 getContentPane().add(desktopPane, BorderLayout.CENTER);
438 desktopPane.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
440 // This line prevents Windows Look&Feel resizing all new windows to
442 // if previous window was maximised
443 desktopPane.setDesktopManager(new MyDesktopManager(
444 (Platform.isWindowsAndNotJS() ? new DefaultDesktopManager()
445 : Platform.isAMacAndNotJS()
446 ? new AquaInternalFrameManager(
447 desktopPane.getDesktopManager())
448 : desktopPane.getDesktopManager())));
450 Rectangle dims = getLastKnownDimensions("");
457 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
458 int xPos = Math.max(5, (screenSize.width - 900) / 2);
459 int yPos = Math.max(5, (screenSize.height - 650) / 2);
460 setBounds(xPos, yPos, 900, 650);
463 // Note that this next syntax, checking for Platform.isJS and also
464 // escaping the code using @j2sIgnore, serves two purposes. It gives
465 // us an easily findable tag, Platform.isJS(), to places in the code where
466 // there is something different about the SwingJS implementation. Second,
467 // it deletes the unneeded Java-only code form the JavaScript version
468 // completely (@j2sIgnore), since it will never be used there.
470 if (!Platform.isJS())
478 jconsole = new Console(this, showjconsole);
479 // add essential build information
480 jconsole.setHeader("Jalview Version: "
481 + jalview.bin.Cache.getProperty("VERSION") + "\n"
482 + "Jalview Installation: "
483 + jalview.bin.Cache.getDefault("INSTALLATION", "unknown")
484 + "\n" + "Build Date: "
485 + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown")
486 + "\n" + "Java version: "
487 + System.getProperty("java.version") + "\n"
488 + System.getProperty("os.arch") + " "
489 + System.getProperty("os.name") + " "
490 + System.getProperty("os.version"));
492 showConsole(showjconsole);
494 showNews.setVisible(false);
496 experimentalFeatures.setSelected(showExperimental());
498 getIdentifiersOrgData();
502 // Spawn a thread that shows the splashscreen
504 SwingUtilities.invokeLater(new Runnable()
513 // Thread off a new instance of the file chooser - this reduces the time
515 // takes to open it later on.
516 new Thread(new Runnable()
521 Cache.log.debug("Filechooser init thread started.");
522 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
523 JalviewFileChooser.forRead(Cache.getProperty("LAST_DIRECTORY"),
525 Cache.log.debug("Filechooser init thread finished.");
528 // Add the service change listener
529 changeSupport.addJalviewPropertyChangeListener("services",
530 new PropertyChangeListener()
534 public void propertyChange(PropertyChangeEvent evt)
536 Cache.log.debug("Firing service changed event for "
537 + evt.getNewValue());
538 JalviewServicesChanged(evt);
545 this.setDropTarget(new java.awt.dnd.DropTarget(desktopPane, this));
547 this.addWindowListener(new WindowAdapter()
550 public void windowClosing(WindowEvent evt)
557 this.addMouseListener(ma = new MouseAdapter()
560 public void mousePressed(MouseEvent evt)
562 if (evt.isPopupTrigger()) // Mac
564 showPasteMenu(evt.getX(), evt.getY());
569 public void mouseReleased(MouseEvent evt)
571 if (evt.isPopupTrigger()) // Windows
573 showPasteMenu(evt.getX(), evt.getY());
577 desktopPane.addMouseListener(ma);
578 } catch (Throwable t)
585 * Answers true if user preferences to enable experimental features is True
590 public boolean showExperimental()
592 String experimental = Cache.getDefault(EXPERIMENTAL_FEATURES,
593 Boolean.FALSE.toString());
594 return Boolean.valueOf(experimental).booleanValue();
597 public void doConfigureStructurePrefs()
599 // configure services
600 StructureSelectionManager ssm = StructureSelectionManager
601 .getStructureSelectionManager(this);
602 if (jalview.bin.Cache.getDefault(Preferences.ADD_SS_ANN, true))
604 ssm.setAddTempFacAnnot(jalview.bin.Cache
605 .getDefault(Preferences.ADD_TEMPFACT_ANN, true));
606 ssm.setProcessSecondaryStructure(jalview.bin.Cache
607 .getDefault(Preferences.STRUCT_FROM_PDB, true));
608 ssm.setSecStructServices(
609 jalview.bin.Cache.getDefault(Preferences.USE_RNAVIEW, true));
613 ssm.setAddTempFacAnnot(false);
614 ssm.setProcessSecondaryStructure(false);
615 ssm.setSecStructServices(false);
619 public void checkForNews()
621 final Desktop me = this;
622 // Thread off the news reader, in case there are connection problems.
623 new Thread(new Runnable()
628 Cache.log.debug("Starting news thread.");
629 jvnews = new BlogReader(me);
630 showNews.setVisible(true);
631 Cache.log.debug("Completed news thread.");
636 public void getIdentifiersOrgData()
638 // Thread off the identifiers fetcher
639 new Thread(new Runnable()
644 Cache.log.debug("Downloading data from identifiers.org");
645 // UrlDownloadClient client = new UrlDownloadClient();
648 UrlDownloadClient.download(IdOrgSettings.getUrl(),
649 IdOrgSettings.getDownloadLocation());
650 } catch (IOException e)
652 Cache.log.debug("Exception downloading identifiers.org data"
661 protected void showNews_actionPerformed(ActionEvent e)
663 showNews(showNews.isSelected());
666 protected void showNews(boolean visible)
668 Cache.log.debug((visible ? "Showing" : "Hiding") + " news.");
669 showNews.setSelected(visible);
670 if (visible && !jvnews.isVisible())
672 new Thread(new Runnable()
677 long now = System.currentTimeMillis();
679 MessageManager.getString("status.refreshing_news"), now);
680 jvnews.refreshNews();
681 setProgressBar(null, now);
689 * recover the last known dimensions for a jalview window
692 * - empty string is desktop, all other windows have unique prefix
693 * @return null or last known dimensions scaled to current geometry (if last
694 * window geom was known)
696 Rectangle getLastKnownDimensions(String windowName)
698 // TODO: lock aspect ratio for scaling desktop Bug #0058199
699 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
700 String x = jalview.bin.Cache.getProperty(windowName + "SCREEN_X");
701 String y = jalview.bin.Cache.getProperty(windowName + "SCREEN_Y");
702 String width = jalview.bin.Cache
703 .getProperty(windowName + "SCREEN_WIDTH");
704 String height = jalview.bin.Cache
705 .getProperty(windowName + "SCREEN_HEIGHT");
706 if ((x != null) && (y != null) && (width != null) && (height != null))
708 int ix = Integer.parseInt(x), iy = Integer.parseInt(y),
709 iw = Integer.parseInt(width), ih = Integer.parseInt(height);
710 if (jalview.bin.Cache.getProperty("SCREENGEOMETRY_WIDTH") != null)
712 // attempt #1 - try to cope with change in screen geometry - this
713 // version doesn't preserve original jv aspect ratio.
714 // take ratio of current screen size vs original screen size.
715 double sw = ((1f * screenSize.width) / (1f * Integer.parseInt(
716 jalview.bin.Cache.getProperty("SCREENGEOMETRY_WIDTH"))));
717 double sh = ((1f * screenSize.height) / (1f * Integer.parseInt(
718 jalview.bin.Cache.getProperty("SCREENGEOMETRY_HEIGHT"))));
719 // rescale the bounds depending upon the current screen geometry.
720 ix = (int) (ix * sw);
721 iw = (int) (iw * sw);
722 iy = (int) (iy * sh);
723 ih = (int) (ih * sh);
724 while (ix >= screenSize.width)
726 jalview.bin.Cache.log.debug(
727 "Window geometry location recall error: shifting horizontal to within screenbounds.");
728 ix -= screenSize.width;
730 while (iy >= screenSize.height)
732 jalview.bin.Cache.log.debug(
733 "Window geometry location recall error: shifting vertical to within screenbounds.");
734 iy -= screenSize.height;
736 jalview.bin.Cache.log.debug(
737 "Got last known dimensions for " + windowName + ": x:" + ix
738 + " y:" + iy + " width:" + iw + " height:" + ih);
740 // return dimensions for new instance
741 return new Rectangle(ix, iy, iw, ih);
746 private void doVamsasClientCheck()
748 if (Cache.vamsasJarsPresent())
750 setupVamsasDisconnectedGui();
751 VamsasMenu.setVisible(true);
752 final Desktop us = this;
753 VamsasMenu.addMenuListener(new MenuListener()
755 // this listener remembers when the menu was first selected, and
756 // doesn't rebuild the session list until it has been cleared and
758 boolean refresh = true;
761 public void menuCanceled(MenuEvent e)
767 public void menuDeselected(MenuEvent e)
773 public void menuSelected(MenuEvent e)
777 us.buildVamsasStMenu();
782 vamsasStart.setVisible(true);
786 protected void showPasteMenu(int x, int y)
788 JPopupMenu popup = new JPopupMenu();
789 JMenuItem item = new JMenuItem(
790 MessageManager.getString("label.paste_new_window"));
791 item.addActionListener(new ActionListener()
794 public void actionPerformed(ActionEvent evt)
801 popup.show(this, x, y);
808 Clipboard c = Toolkit.getDefaultToolkit().getSystemClipboard();
809 Transferable contents = c.getContents(this);
811 if (contents != null)
813 String file = (String) contents
814 .getTransferData(DataFlavor.stringFlavor);
816 FileFormatI format = new IdentifyFile().identify(file,
817 DataSourceType.PASTE);
819 new FileLoader().loadFile(file, DataSourceType.PASTE, format);
822 } catch (Exception ex)
825 "Unable to paste alignment from system clipboard:\n" + ex);
830 * Adds and opens the given frame to the desktop
841 public static synchronized void addInternalFrame(
842 final JInternalFrame frame, String title, int w, int h)
844 addInternalFrame(frame, title, true, w, h, true, false);
848 * Add an internal frame to the Jalview desktop
855 * When true, display frame immediately, otherwise, caller must call
856 * setVisible themselves.
862 public static synchronized void addInternalFrame(
863 final JInternalFrame frame, String title, boolean makeVisible,
866 addInternalFrame(frame, title, makeVisible, w, h, true, false);
870 * Add an internal frame to the Jalview desktop and make it visible
883 public static synchronized void addInternalFrame(
884 final JInternalFrame frame, String title, int w, int h,
887 addInternalFrame(frame, title, true, w, h, resizable, false);
891 * Add an internal frame to the Jalview desktop
898 * When true, display frame immediately, otherwise, caller must call
899 * setVisible themselves.
906 * @param ignoreMinSize
907 * Do not set the default minimum size for frame
909 public static synchronized void addInternalFrame(
910 final JInternalFrame frame, String title, boolean makeVisible,
911 int w, int h, boolean resizable, boolean ignoreMinSize)
915 // TODO: allow callers to determine X and Y position of frame (eg. via
917 // TODO: consider fixing method to update entries in the window submenu with
918 // the current window title
920 frame.setTitle(title);
921 if (w > 0 && (frame.getWidth() < 1 || frame.getHeight() < 1))
925 // THIS IS A PUBLIC STATIC METHOD, SO IT MAY BE CALLED EVEN IN
926 // A HEADLESS STATE WHEN NO DESKTOP EXISTS. MUST RETURN
927 // IF JALVIEW IS RUNNING HEADLESS OR IN INSTANCE-ONLY (testNG) MODE
928 // ///////////////////////////////////////////////
929 if (Jalview.isHeadlessMode() || Desktop.instanceOnly)
938 frame.setMinimumSize(
939 new Dimension(DEFAULT_MIN_WIDTH, DEFAULT_MIN_HEIGHT));
941 // Set default dimension for Alignment Frame window.
942 // The Alignment Frame window could be added from a number of places,
944 // I did this here in order not to miss out on any Alignment frame.
945 if (frame instanceof AlignFrame)
947 frame.setMinimumSize(new Dimension(ALIGN_FRAME_DEFAULT_MIN_WIDTH,
948 ALIGN_FRAME_DEFAULT_MIN_HEIGHT));
952 frame.setVisible(makeVisible);
953 frame.setClosable(true);
954 frame.setResizable(resizable);
955 frame.setMaximizable(resizable);
956 frame.setIconifiable(resizable);
957 frame.setOpaque(Platform.isJS());
959 boolean isEmbedded = (Platform.getDimIfEmbedded(frame, -1, -1) != null);
960 if (!isEmbedded && frame.getX() < 1 && frame.getY() < 1)
962 frame.setLocation(xOffset * openFrameCount,
963 yOffset * ((openFrameCount - 1) % 10) + yOffset);
967 * add an entry for the new frame in the Window menu
968 * (and remove it when the frame is closed)
970 JMenuItem menuItem = new JMenuItem(title);
971 frame.addInternalFrameListener(new InternalFrameAdapter()
974 public void internalFrameActivated(InternalFrameEvent evt)
976 JInternalFrame itf = getDesktopPane().getSelectedFrame();
979 if (itf instanceof AlignFrame)
981 Jalview.setCurrentAlignFrame((AlignFrame) itf);
988 public void internalFrameClosed(InternalFrameEvent evt)
990 PaintRefresher.RemoveComponent(frame);
993 * defensive check to prevent frames being
994 * added half off the window
996 if (openFrameCount > 0)
1002 * ensure no reference to alignFrame retained by menu item listener
1004 if (menuItem.getActionListeners().length > 0)
1006 menuItem.removeActionListener(menuItem.getActionListeners()[0]);
1008 Desktop.getInstance().windowMenu.remove(menuItem);
1012 menuItem.addActionListener(new ActionListener()
1015 public void actionPerformed(ActionEvent e)
1019 frame.setSelected(true);
1020 frame.setIcon(false);
1021 } catch (java.beans.PropertyVetoException ex)
1023 // System.err.println(ex.toString());
1028 setKeyBindings(frame);
1030 getDesktopPane().add(frame);
1032 Desktop.getInstance().windowMenu.add(menuItem);
1037 frame.setSelected(true);
1038 frame.requestFocus();
1039 } catch (java.beans.PropertyVetoException ve)
1041 } catch (java.lang.ClassCastException cex)
1044 "Squashed a possible GUI implementation error. If you can recreate this, please look at http://issues.jalview.org/browse/JAL-869",
1050 * Add key bindings to a JInternalFrame so that Ctrl-W and Cmd-W will close the
1055 private static void setKeyBindings(JInternalFrame frame)
1057 final Action closeAction = new AbstractAction()
1060 public void actionPerformed(ActionEvent e)
1067 * set up key bindings for Ctrl-W and Cmd-W, with the same (Close) action
1069 KeyStroke ctrlWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
1070 InputEvent.CTRL_DOWN_MASK);
1071 KeyStroke cmdWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
1072 Toolkit.getDefaultToolkit().getMenuShortcutKeyMask());
1074 InputMap inputMap = frame
1075 .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
1076 String ctrlW = ctrlWKey.toString();
1077 inputMap.put(ctrlWKey, ctrlW);
1078 inputMap.put(cmdWKey, ctrlW);
1080 ActionMap actionMap = frame.getActionMap();
1081 actionMap.put(ctrlW, closeAction);
1085 public void lostOwnership(Clipboard clipboard, Transferable contents)
1089 Desktop.getInstance().jalviewClipboard = null;
1092 internalCopy = false;
1096 public void dragEnter(DropTargetDragEvent evt)
1101 public void dragExit(DropTargetEvent evt)
1106 public void dragOver(DropTargetDragEvent evt)
1111 public void dropActionChanged(DropTargetDragEvent evt)
1122 public void drop(DropTargetDropEvent evt)
1124 boolean success = true;
1125 // JAL-1552 - acceptDrop required before getTransferable call for
1126 // Java's Transferable for native dnd
1127 evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
1128 Transferable t = evt.getTransferable();
1129 List<Object> files = new ArrayList<>();
1130 List<DataSourceType> protocols = new ArrayList<>();
1134 Desktop.transferFromDropTarget(files, protocols, evt, t);
1135 } catch (Exception e)
1137 e.printStackTrace();
1145 for (int i = 0; i < files.size(); i++)
1147 // BH 2018 File or String
1148 Object file = files.get(i);
1149 String fileName = file.toString();
1150 DataSourceType protocol = (protocols == null)
1151 ? DataSourceType.FILE
1153 FileFormatI format = null;
1155 if (fileName.endsWith(".jar"))
1157 format = FileFormat.Jalview;
1162 format = new IdentifyFile().identify(file, protocol);
1164 if (file instanceof File)
1166 Platform.cacheFileData((File) file);
1168 new FileLoader().loadFile(null, file, protocol, format);
1171 } catch (Exception ex)
1176 evt.dropComplete(success); // need this to ensure input focus is properly
1177 // transfered to any new windows created
1187 public void inputLocalFileMenuItem_actionPerformed(AlignViewport viewport)
1189 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
1190 JalviewFileChooser chooser = JalviewFileChooser
1191 .forRead(Cache.getProperty("LAST_DIRECTORY"), fileFormat, true);
1193 chooser.setFileView(new JalviewFileView());
1194 chooser.setDialogTitle(
1195 MessageManager.getString("label.open_local_file"));
1196 chooser.setToolTipText(MessageManager.getString("action.open"));
1198 chooser.setResponseHandler(0, new Runnable()
1203 File selectedFile = chooser.getSelectedFile();
1204 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1206 FileFormatI format = chooser.getSelectedFormat();
1209 * Call IdentifyFile to verify the file contains what its extension implies.
1210 * Skip this step for dynamically added file formats, because
1211 * IdentifyFile does not know how to recognise them.
1213 if (FileFormats.getInstance().isIdentifiable(format))
1217 format = new IdentifyFile().identify(selectedFile,
1218 DataSourceType.FILE);
1219 } catch (FileFormatException e)
1221 // format = null; //??
1225 new FileLoader().loadFile(viewport, selectedFile,
1226 DataSourceType.FILE, format);
1229 chooser.showOpenDialog(this);
1233 * Shows a dialog for input of a URL at which to retrieve alignment data
1238 public void inputURLMenuItem_actionPerformed(AlignViewport viewport)
1240 // This construct allows us to have a wider textfield
1242 JLabel label = new JLabel(
1243 MessageManager.getString("label.input_file_url"));
1245 JPanel panel = new JPanel(new GridLayout(2, 1));
1249 * the URL to fetch is
1250 * Java: an editable combobox with history
1251 * JS: (pending JAL-3038) a plain text field
1254 String urlBase = "http://www.";
1255 if (Platform.isJS())
1257 history = new JTextField(urlBase, 35);
1266 JComboBox<String> asCombo = new JComboBox<>();
1267 asCombo.setPreferredSize(new Dimension(400, 20));
1268 asCombo.setEditable(true);
1269 asCombo.addItem(urlBase);
1270 String historyItems = Cache.getProperty("RECENT_URL");
1271 if (historyItems != null)
1273 for (String token : historyItems.split("\\t"))
1275 asCombo.addItem(token);
1282 Object[] options = new Object[] { MessageManager.getString("action.ok"),
1283 MessageManager.getString("action.cancel") };
1284 Runnable action = new Runnable()
1289 @SuppressWarnings("unchecked")
1290 String url = (history instanceof JTextField
1291 ? ((JTextField) history).getText()
1292 : ((JComboBox<String>) history).getSelectedItem()
1295 if (url.toLowerCase().endsWith(".jar"))
1297 if (viewport != null)
1299 new FileLoader().loadFile(viewport, url, DataSourceType.URL,
1300 FileFormat.Jalview);
1304 new FileLoader().loadFile(url, DataSourceType.URL,
1305 FileFormat.Jalview);
1310 FileFormatI format = null;
1313 format = new IdentifyFile().identify(url, DataSourceType.URL);
1314 } catch (FileFormatException e)
1316 // TODO revise error handling, distinguish between
1317 // URL not found and response not valid
1322 String msg = MessageManager
1323 .formatMessage("label.couldnt_locate", url);
1324 JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
1326 MessageManager.getString("label.url_not_found"),
1327 JvOptionPane.WARNING_MESSAGE);
1332 if (viewport != null)
1334 new FileLoader().loadFile(viewport, url, DataSourceType.URL,
1339 new FileLoader().loadFile(url, DataSourceType.URL, format);
1344 String dialogOption = MessageManager
1345 .getString("label.input_alignment_from_url");
1346 JvOptionPane.newOptionDialog(getDesktopPane())
1347 .setResponseHandler(0, action)
1348 .showInternalDialog(panel, dialogOption,
1349 JvOptionPane.YES_NO_CANCEL_OPTION,
1350 JvOptionPane.PLAIN_MESSAGE, null, options,
1351 MessageManager.getString("action.ok"));
1355 * Opens the CutAndPaste window for the user to paste an alignment in to
1358 * - if not null, the pasted alignment is added to the current
1359 * alignment; if null, to a new alignment window
1362 public void inputTextboxMenuItem_actionPerformed(
1363 AlignmentViewPanel viewPanel)
1365 CutAndPasteTransfer cap = new CutAndPasteTransfer();
1366 cap.setForInput(viewPanel);
1367 Desktop.addInternalFrame(cap,
1368 MessageManager.getString("label.cut_paste_alignmen_file"), true,
1378 Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
1379 jalview.bin.Cache.setProperty("SCREENGEOMETRY_WIDTH",
1381 jalview.bin.Cache.setProperty("SCREENGEOMETRY_HEIGHT",
1382 screen.height + "");
1383 storeLastKnownDimensions("", new Rectangle(getBounds().x, getBounds().y,
1384 getWidth(), getHeight()));
1386 if (jconsole != null)
1388 storeLastKnownDimensions("JAVA_CONSOLE_", jconsole.getBounds());
1389 jconsole.stopConsole();
1393 storeLastKnownDimensions("JALVIEW_RSS_WINDOW_", jvnews.getBounds());
1396 if (dialogExecutor != null)
1398 dialogExecutor.shutdownNow();
1400 closeAll_actionPerformed(null);
1402 if (groovyConsole != null)
1404 // suppress a possible repeat prompt to save script
1405 groovyConsole.setDirty(false);
1406 groovyConsole.exit();
1411 private void storeLastKnownDimensions(String string, Rectangle jc)
1413 jalview.bin.Cache.log.debug("Storing last known dimensions for "
1414 + string + ": x:" + jc.x + " y:" + jc.y + " width:" + jc.width
1415 + " height:" + jc.height);
1417 jalview.bin.Cache.setProperty(string + "SCREEN_X", jc.x + "");
1418 jalview.bin.Cache.setProperty(string + "SCREEN_Y", jc.y + "");
1419 jalview.bin.Cache.setProperty(string + "SCREEN_WIDTH", jc.width + "");
1420 jalview.bin.Cache.setProperty(string + "SCREEN_HEIGHT", jc.height + "");
1430 public void aboutMenuItem_actionPerformed(ActionEvent e)
1432 // StringBuffer message = getAboutMessage(false);
1433 // JvOptionPane.showInternalMessageDialog(Desktop.getDesktop(),
1435 // message.toString(), "About Jalview", JvOptionPane.INFORMATION_MESSAGE);
1436 new Thread(new Runnable()
1441 new SplashScreen(true);
1446 public StringBuffer getAboutMessage(boolean shortv)
1448 StringBuffer message = new StringBuffer();
1449 message.append("<html>");
1452 message.append("<h1><strong>Version: "
1453 + jalview.bin.Cache.getProperty("VERSION")
1454 + "</strong></h1>");
1455 message.append("<strong>Last Updated: <em>"
1456 + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown")
1457 + "</em></strong>");
1463 message.append("<strong>Version "
1464 + jalview.bin.Cache.getProperty("VERSION")
1465 + "; last updated: "
1466 + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown"));
1469 if (jalview.bin.Cache.getDefault("LATEST_VERSION", "Checking")
1470 .equals("Checking"))
1472 message.append("<br>...Checking latest version...</br>");
1474 else if (!jalview.bin.Cache.getDefault("LATEST_VERSION", "Checking")
1475 .equals(jalview.bin.Cache.getProperty("VERSION")))
1477 boolean red = false;
1478 if (jalview.bin.Cache.getProperty("VERSION").toLowerCase()
1479 .indexOf("automated build") == -1)
1482 // Displayed when code version and jnlp version do not match and code
1483 // version is not a development build
1484 message.append("<div style=\"color: #FF0000;font-style: bold;\">");
1487 message.append("<br>!! Version "
1488 + jalview.bin.Cache.getDefault("LATEST_VERSION",
1490 + " is available for download from "
1491 + jalview.bin.Cache.getDefault("www.jalview.org",
1492 "http://www.jalview.org")
1496 message.append("</div>");
1499 message.append("<br>Authors: " + jalview.bin.Cache.getDefault(
1501 "The Jalview Authors (See AUTHORS file for current list)")
1502 + "<br><br>Development managed by The Barton Group, University of Dundee, Scotland, UK.<br>"
1503 + "<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"
1504 + "<br><br>If you use Jalview, please cite:"
1505 + "<br>Waterhouse, A.M., Procter, J.B., Martin, D.M.A, Clamp, M. and Barton, G. J. (2009)"
1506 + "<br>Jalview Version 2 - a multiple sequence alignment editor and analysis workbench"
1507 + "<br>Bioinformatics doi: 10.1093/bioinformatics/btp033"
1513 * Action on requesting Help documentation
1516 public void documentationMenuItem_actionPerformed()
1520 if (Platform.isJS())
1522 BrowserLauncher.openURL("http://www.jalview.org/help.html");
1531 Help.showHelpWindow();
1533 } catch (Exception ex)
1535 System.err.println("Error opening help: " + ex.getMessage());
1540 public void closeAll_actionPerformed(ActionEvent e)
1542 if (desktopPane == null)
1546 // TODO show a progress bar while closing?
1547 JInternalFrame[] frames = desktopPane.getAllFrames();
1548 for (int i = 0; i < frames.length; i++)
1552 frames[i].setClosed(true);
1553 } catch (java.beans.PropertyVetoException ex)
1557 Jalview.setCurrentAlignFrame(null);
1558 System.out.println("ALL CLOSED");
1559 if (v_client != null)
1561 // TODO clear binding to vamsas document objects on close_all
1565 * reset state of singleton objects as appropriate (clear down session state
1566 * when all windows are closed)
1568 getStructureSelectionManager().resetAll();
1572 public void raiseRelated_actionPerformed(ActionEvent e)
1574 reorderAssociatedWindows(false, false);
1578 public void minimizeAssociated_actionPerformed(ActionEvent e)
1580 reorderAssociatedWindows(true, false);
1583 void closeAssociatedWindows()
1585 reorderAssociatedWindows(false, true);
1591 * @seejalview.jbgui.GDesktop#garbageCollect_actionPerformed(java.awt.event.
1595 protected void garbageCollect_actionPerformed(ActionEvent e)
1597 // We simply collect the garbage
1598 jalview.bin.Cache.log.debug("Collecting garbage...");
1600 jalview.bin.Cache.log.debug("Finished garbage collection.");
1607 * jalview.jbgui.GDesktop#showMemusage_actionPerformed(java.awt.event.ActionEvent
1611 protected void showMemusage_actionPerformed(ActionEvent e)
1613 getDesktopPane().showMemoryUsage(showMemusage.isSelected());
1620 * jalview.jbgui.GDesktop#showConsole_actionPerformed(java.awt.event.ActionEvent
1624 protected void showConsole_actionPerformed(ActionEvent e)
1626 showConsole(showConsole.isSelected());
1629 Console jconsole = null;
1632 * control whether the java console is visible or not
1636 void showConsole(boolean selected)
1638 // TODO: decide if we should update properties file
1639 if (jconsole != null) // BH 2018
1641 showConsole.setSelected(selected);
1642 Cache.setProperty("SHOW_JAVA_CONSOLE",
1643 Boolean.valueOf(selected).toString());
1644 jconsole.setVisible(selected);
1648 void reorderAssociatedWindows(boolean minimize, boolean close)
1650 JInternalFrame[] frames = getDesktopPane().getAllFrames();
1651 if (frames == null || frames.length < 1)
1656 AlignmentViewport source = null, target = null;
1657 if (frames[0] instanceof AlignFrame)
1659 source = ((AlignFrame) frames[0]).getCurrentView();
1661 else if (frames[0] instanceof TreePanel)
1663 source = ((TreePanel) frames[0]).getViewPort();
1665 else if (frames[0] instanceof PCAPanel)
1667 source = ((PCAPanel) frames[0]).av;
1669 else if (frames[0].getContentPane() instanceof PairwiseAlignPanel)
1671 source = ((PairwiseAlignPanel) frames[0].getContentPane()).av;
1676 for (int i = 0; i < frames.length; i++)
1679 if (frames[i] == null)
1683 if (frames[i] instanceof AlignFrame)
1685 target = ((AlignFrame) frames[i]).getCurrentView();
1687 else if (frames[i] instanceof TreePanel)
1689 target = ((TreePanel) frames[i]).getViewPort();
1691 else if (frames[i] instanceof PCAPanel)
1693 target = ((PCAPanel) frames[i]).av;
1695 else if (frames[i].getContentPane() instanceof PairwiseAlignPanel)
1697 target = ((PairwiseAlignPanel) frames[i].getContentPane()).av;
1700 if (source == target)
1706 frames[i].setClosed(true);
1710 frames[i].setIcon(minimize);
1713 frames[i].toFront();
1717 } catch (java.beans.PropertyVetoException ex)
1732 protected void preferences_actionPerformed(ActionEvent e)
1738 * Prompts the user to choose a file and then saves the Jalview state as a
1739 * Jalview project file
1742 public void saveState_actionPerformed()
1744 saveState_actionPerformed(false);
1747 public void saveState_actionPerformed(boolean saveAs)
1749 java.io.File projectFile = getProjectFile();
1750 // autoSave indicates we already have a file and don't need to ask
1751 boolean autoSave = projectFile != null && !saveAs
1752 && BackupFiles.getEnabled();
1754 // System.out.println("autoSave="+autoSave+", projectFile='"+projectFile+"',
1755 // saveAs="+saveAs+", Backups
1756 // "+(BackupFiles.getEnabled()?"enabled":"disabled"));
1758 boolean approveSave = false;
1761 JalviewFileChooser chooser = new JalviewFileChooser("jvp",
1764 chooser.setFileView(new JalviewFileView());
1765 chooser.setDialogTitle(MessageManager.getString("label.save_state"));
1767 int value = chooser.showSaveDialog(this);
1769 if (value == JalviewFileChooser.APPROVE_OPTION)
1771 projectFile = chooser.getSelectedFile();
1772 setProjectFile(projectFile);
1777 if (approveSave || autoSave)
1779 final Desktop me = this;
1780 final java.io.File chosenFile = projectFile;
1781 new Thread(new Runnable()
1786 // TODO: refactor to Jalview desktop session controller action.
1787 setProgressBar(MessageManager.formatMessage(
1788 "label.saving_jalview_project", new Object[]
1789 { chosenFile.getName() }), chosenFile.hashCode());
1790 jalview.bin.Cache.setProperty("LAST_DIRECTORY",
1791 chosenFile.getParent());
1792 // TODO catch and handle errors for savestate
1793 // TODO prevent user from messing with the Desktop whilst we're saving
1796 boolean doBackup = BackupFiles.getEnabled();
1797 BackupFiles backupfiles = doBackup ? new BackupFiles(chosenFile) : null;
1799 new Jalview2XML().saveState(doBackup ? backupfiles.getTempFile() : chosenFile);
1803 backupfiles.setWriteSuccess(true);
1804 backupfiles.rollBackupsAndRenameTempFile();
1806 } catch (OutOfMemoryError oom)
1808 new OOMWarning("Whilst saving current state to "
1809 + chosenFile.getName(), oom);
1810 } catch (Exception ex)
1812 Cache.log.error("Problems whilst trying to save to "
1813 + chosenFile.getName(), ex);
1814 JvOptionPane.showMessageDialog(me,
1815 MessageManager.formatMessage(
1816 "label.error_whilst_saving_current_state_to",
1818 { chosenFile.getName() }),
1819 MessageManager.getString("label.couldnt_save_project"),
1820 JvOptionPane.WARNING_MESSAGE);
1822 setProgressBar(null, chosenFile.hashCode());
1829 public void saveAsState_actionPerformed(ActionEvent e)
1831 saveState_actionPerformed(true);
1834 protected void setProjectFile(File choice)
1836 this.projectFile = choice;
1839 public File getProjectFile()
1841 return this.projectFile;
1845 * Shows a file chooser dialog and tries to read in the selected file as a
1849 public void loadState_actionPerformed()
1851 final String[] suffix = new String[] { "jvp", "jar" };
1852 final String[] desc = new String[] { "Jalview Project",
1853 "Jalview Project (old)" };
1854 JalviewFileChooser chooser = new JalviewFileChooser(
1855 Cache.getProperty("LAST_DIRECTORY"), suffix, desc,
1856 "Jalview Project", true, true); // last two booleans: allFiles,
1858 chooser.setFileView(new JalviewFileView());
1859 chooser.setDialogTitle(MessageManager.getString("label.restore_state"));
1860 chooser.setResponseHandler(0, new Runnable()
1865 File selectedFile = chooser.getSelectedFile();
1866 setProjectFile(selectedFile);
1867 String choice = selectedFile.getAbsolutePath();
1868 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1869 new Thread(new Runnable()
1876 new Jalview2XML().loadJalviewAlign(selectedFile);
1877 } catch (OutOfMemoryError oom)
1879 new OOMWarning("Whilst loading project from " + choice, oom);
1880 } catch (Exception ex)
1883 "Problems whilst loading project from " + choice, ex);
1884 JvOptionPane.showMessageDialog(Desktop.getDesktopPane(),
1885 MessageManager.formatMessage(
1886 "label.error_whilst_loading_project_from",
1889 MessageManager.getString("label.couldnt_load_project"),
1890 JvOptionPane.WARNING_MESSAGE);
1897 chooser.showOpenDialog(this);
1901 public void inputSequence_actionPerformed(ActionEvent e)
1903 new SequenceFetcher(this);
1906 JPanel progressPanel;
1908 ArrayList<JPanel> fileLoadingPanels = new ArrayList<>();
1910 public void startLoading(final Object fileName)
1912 if (fileLoadingCount == 0)
1914 fileLoadingPanels.add(addProgressPanel(MessageManager
1915 .formatMessage("label.loading_file", new Object[]
1921 private JPanel addProgressPanel(String string)
1923 if (progressPanel == null)
1925 progressPanel = new JPanel(new GridLayout(1, 1));
1926 totalProgressCount = 0;
1927 getContentPane().add(progressPanel, BorderLayout.SOUTH);
1929 JPanel thisprogress = new JPanel(new BorderLayout(10, 5));
1930 JProgressBar progressBar = new JProgressBar();
1931 progressBar.setIndeterminate(true);
1933 thisprogress.add(new JLabel(string), BorderLayout.WEST);
1935 thisprogress.add(progressBar, BorderLayout.CENTER);
1936 progressPanel.add(thisprogress);
1937 ((GridLayout) progressPanel.getLayout()).setRows(
1938 ((GridLayout) progressPanel.getLayout()).getRows() + 1);
1939 ++totalProgressCount;
1941 return thisprogress;
1944 int totalProgressCount = 0;
1946 private void removeProgressPanel(JPanel progbar)
1948 if (progressPanel != null)
1950 synchronized (progressPanel)
1952 progressPanel.remove(progbar);
1953 GridLayout gl = (GridLayout) progressPanel.getLayout();
1954 gl.setRows(gl.getRows() - 1);
1955 if (--totalProgressCount < 1)
1957 this.getContentPane().remove(progressPanel);
1958 progressPanel = null;
1965 public void stopLoading()
1968 if (fileLoadingCount < 1)
1970 while (fileLoadingPanels.size() > 0)
1972 removeProgressPanel(fileLoadingPanels.remove(0));
1974 fileLoadingPanels.clear();
1975 fileLoadingCount = 0;
1980 public static int getViewCount(String alignmentId)
1982 AlignmentViewport[] aps = getViewports(alignmentId);
1983 return (aps == null) ? 0 : aps.length;
1988 * @param alignmentId
1989 * - if null, all sets are returned
1990 * @return all AlignmentPanels concerning the alignmentId sequence set
1992 public static AlignmentPanel[] getAlignmentPanels(String alignmentId)
1994 if (Desktop.getDesktopPane() == null)
1996 // no frames created and in headless mode
1997 // TODO: verify that frames are recoverable when in headless mode
2000 List<AlignmentPanel> aps = new ArrayList<>();
2001 AlignFrame[] frames = getAlignFrames();
2006 for (AlignFrame af : frames)
2008 for (AlignmentPanel ap : af.alignPanels)
2010 if (alignmentId == null
2011 || alignmentId.equals(ap.av.getSequenceSetId()))
2017 if (aps.size() == 0)
2021 AlignmentPanel[] vap = aps.toArray(new AlignmentPanel[aps.size()]);
2026 * get all the viewports on an alignment.
2028 * @param sequenceSetId
2029 * unique alignment id (may be null - all viewports returned in that
2031 * @return all viewports on the alignment bound to sequenceSetId
2033 public static AlignmentViewport[] getViewports(String sequenceSetId)
2035 List<AlignmentViewport> viewp = new ArrayList<>();
2036 if (getDesktopPane() != null)
2038 AlignFrame[] frames = Desktop.getAlignFrames();
2040 for (AlignFrame afr : frames)
2042 if (sequenceSetId == null || afr.getViewport().getSequenceSetId()
2043 .equals(sequenceSetId))
2045 if (afr.alignPanels != null)
2047 for (AlignmentPanel ap : afr.alignPanels)
2049 if (sequenceSetId == null
2050 || sequenceSetId.equals(ap.av.getSequenceSetId()))
2058 viewp.add(afr.getViewport());
2062 if (viewp.size() > 0)
2064 return viewp.toArray(new AlignmentViewport[viewp.size()]);
2071 * Explode the views in the given frame into separate AlignFrame
2075 public static void explodeViews(AlignFrame af)
2077 int size = af.alignPanels.size();
2083 for (int i = 0; i < size; i++)
2085 AlignmentPanel ap = af.alignPanels.get(i);
2086 AlignFrame newaf = new AlignFrame(ap);
2089 * Restore the view's last exploded frame geometry if known. Multiple
2090 * views from one exploded frame share and restore the same (frame)
2091 * position and size.
2093 Rectangle geometry = ap.av.getExplodedGeometry();
2094 if (geometry != null)
2096 newaf.setBounds(geometry);
2099 ap.av.setGatherViewsHere(false);
2101 addInternalFrame(newaf, af.getTitle(), AlignFrame.DEFAULT_WIDTH,
2102 AlignFrame.DEFAULT_HEIGHT);
2105 af.alignPanels.clear();
2106 af.closeMenuItem_actionPerformed(true);
2111 * Gather expanded views (separate AlignFrame's) with the same sequence set
2112 * identifier back in to this frame as additional views, and close the expanded
2113 * views. Note the expanded frames may themselves have multiple views. We take
2118 public void gatherViews(AlignFrame source)
2120 source.viewport.setGatherViewsHere(true);
2121 source.viewport.setExplodedGeometry(source.getBounds());
2122 JInternalFrame[] frames = getAllFrames();
2123 String viewId = source.viewport.getSequenceSetId();
2125 for (int t = 0; t < frames.length; t++)
2127 if (frames[t] instanceof AlignFrame && frames[t] != source)
2129 AlignFrame af = (AlignFrame) frames[t];
2130 boolean gatherThis = false;
2131 for (int a = 0; a < af.alignPanels.size(); a++)
2133 AlignmentPanel ap = af.alignPanels.get(a);
2134 if (viewId.equals(ap.av.getSequenceSetId()))
2137 ap.av.setGatherViewsHere(false);
2138 ap.av.setExplodedGeometry(af.getBounds());
2139 source.addAlignmentPanel(ap, false);
2145 af.alignPanels.clear();
2146 af.closeMenuItem_actionPerformed(true);
2153 jalview.gui.VamsasApplication v_client = null;
2156 public void vamsasImport_actionPerformed(ActionEvent e)
2158 // TODO: JAL-3048 not needed for Jalview-JS
2160 if (v_client == null)
2162 // Load and try to start a session.
2163 JalviewFileChooser chooser = new JalviewFileChooser(
2164 jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
2166 chooser.setFileView(new JalviewFileView());
2167 chooser.setDialogTitle(
2168 MessageManager.getString("label.open_saved_vamsas_session"));
2169 chooser.setToolTipText(MessageManager.getString(
2170 "label.select_vamsas_session_opened_as_new_vamsas_session"));
2172 int value = chooser.showOpenDialog(this);
2174 if (value == JalviewFileChooser.APPROVE_OPTION)
2176 String fle = chooser.getSelectedFile().toString();
2177 if (!vamsasImport(chooser.getSelectedFile()))
2179 JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
2180 MessageManager.formatMessage(
2181 "label.couldnt_import_as_vamsas_session",
2185 .getString("label.vamsas_document_import_failed"),
2186 JvOptionPane.ERROR_MESSAGE);
2192 jalview.bin.Cache.log.error(
2193 "Implementation error - load session from a running session is not supported.");
2198 * import file into a new vamsas session (uses jalview.gui.VamsasApplication)
2201 * @return true if import was a success and a session was started.
2203 public boolean vamsasImport(URL url)
2205 // TODO: create progress bar
2206 if (v_client != null)
2209 jalview.bin.Cache.log.error(
2210 "Implementation error - load session from a running session is not supported.");
2216 // copy the URL content to a temporary local file
2217 // TODO: be a bit cleverer here with nio (?!)
2218 File file = File.createTempFile("vdocfromurl", ".vdj");
2219 FileOutputStream fos = new FileOutputStream(file);
2220 BufferedInputStream bis = new BufferedInputStream(url.openStream());
2221 byte[] buffer = new byte[2048];
2223 while ((ln = bis.read(buffer)) > -1)
2225 fos.write(buffer, 0, ln);
2229 v_client = new jalview.gui.VamsasApplication(this, file,
2230 url.toExternalForm());
2231 } catch (Exception ex)
2233 jalview.bin.Cache.log.error(
2234 "Failed to create new vamsas session from contents of URL "
2239 setupVamsasConnectedGui();
2240 v_client.initial_update(); // TODO: thread ?
2241 return v_client.inSession();
2245 * import file into a new vamsas session (uses jalview.gui.VamsasApplication)
2248 * @return true if import was a success and a session was started.
2250 public boolean vamsasImport(File file)
2252 if (v_client != null)
2255 jalview.bin.Cache.log.error(
2256 "Implementation error - load session from a running session is not supported.");
2260 setProgressBar(MessageManager.formatMessage(
2261 "status.importing_vamsas_session_from", new Object[]
2262 { file.getName() }), file.hashCode());
2265 v_client = new jalview.gui.VamsasApplication(this, file, null);
2266 } catch (Exception ex)
2268 setProgressBar(MessageManager.formatMessage(
2269 "status.importing_vamsas_session_from", new Object[]
2270 { file.getName() }), file.hashCode());
2271 jalview.bin.Cache.log.error(
2272 "New vamsas session from existing session file failed:", ex);
2275 setupVamsasConnectedGui();
2276 v_client.initial_update(); // TODO: thread ?
2277 setProgressBar(MessageManager.formatMessage(
2278 "status.importing_vamsas_session_from", new Object[]
2279 { file.getName() }), file.hashCode());
2280 return v_client.inSession();
2283 public boolean joinVamsasSession(String mysesid)
2285 if (v_client != null)
2287 throw new Error(MessageManager
2288 .getString("error.try_join_vamsas_session_another"));
2290 if (mysesid == null)
2293 MessageManager.getString("error.invalid_vamsas_session_id"));
2295 v_client = new VamsasApplication(this, mysesid);
2296 setupVamsasConnectedGui();
2297 v_client.initial_update();
2298 return (v_client.inSession());
2302 public void vamsasStart_actionPerformed(ActionEvent e)
2304 if (v_client == null)
2307 // we just start a default session for moment.
2309 * JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache.
2310 * getProperty("LAST_DIRECTORY"));
2312 * chooser.setFileView(new JalviewFileView());
2313 * chooser.setDialogTitle("Load Vamsas file");
2314 * chooser.setToolTipText("Import");
2316 * int value = chooser.showOpenDialog(this);
2318 * if (value == JalviewFileChooser.APPROVE_OPTION) { v_client = new
2319 * jalview.gui.VamsasApplication(this, chooser.getSelectedFile());
2321 v_client = new VamsasApplication(this);
2322 setupVamsasConnectedGui();
2323 v_client.initial_update(); // TODO: thread ?
2327 // store current data in session.
2328 v_client.push_update(); // TODO: thread
2332 protected void setupVamsasConnectedGui()
2334 vamsasStart.setText(MessageManager.getString("label.session_update"));
2335 vamsasSave.setVisible(true);
2336 vamsasStop.setVisible(true);
2337 vamsasImport.setVisible(false); // Document import to existing session is
2338 // not possible for vamsas-client-1.0.
2341 protected void setupVamsasDisconnectedGui()
2343 vamsasSave.setVisible(false);
2344 vamsasStop.setVisible(false);
2345 vamsasImport.setVisible(true);
2347 .setText(MessageManager.getString("label.new_vamsas_session"));
2351 public void vamsasStop_actionPerformed(ActionEvent e)
2353 if (v_client != null)
2355 v_client.end_session();
2357 setupVamsasDisconnectedGui();
2361 protected void buildVamsasStMenu()
2363 if (v_client == null)
2365 String[] sess = null;
2368 sess = VamsasApplication.getSessionList();
2369 } catch (Exception e)
2371 jalview.bin.Cache.log.warn("Problem getting current sessions list.",
2377 jalview.bin.Cache.log.debug(
2378 "Got current sessions list: " + sess.length + " entries.");
2379 VamsasStMenu.removeAll();
2380 for (int i = 0; i < sess.length; i++)
2382 JMenuItem sessit = new JMenuItem();
2383 sessit.setText(sess[i]);
2384 sessit.setToolTipText(MessageManager
2385 .formatMessage("label.connect_to_session", new Object[]
2387 final Desktop dsktp = this;
2388 final String mysesid = sess[i];
2389 sessit.addActionListener(new ActionListener()
2393 public void actionPerformed(ActionEvent e)
2395 if (dsktp.v_client == null)
2397 Thread rthr = new Thread(new Runnable()
2403 dsktp.v_client = new VamsasApplication(dsktp, mysesid);
2404 dsktp.setupVamsasConnectedGui();
2405 dsktp.v_client.initial_update();
2413 VamsasStMenu.add(sessit);
2415 // don't show an empty menu.
2416 VamsasStMenu.setVisible(sess.length > 0);
2421 jalview.bin.Cache.log.debug("No current vamsas sessions.");
2422 VamsasStMenu.removeAll();
2423 VamsasStMenu.setVisible(false);
2428 // Not interested in the content. Just hide ourselves.
2429 VamsasStMenu.setVisible(false);
2434 public void vamsasSave_actionPerformed(ActionEvent e)
2436 // TODO: JAL-3048 not needed for Jalview-JS
2438 if (v_client != null)
2440 // TODO: VAMSAS DOCUMENT EXTENSION is VDJ
2441 JalviewFileChooser chooser = new JalviewFileChooser("vdj",
2444 chooser.setFileView(new JalviewFileView());
2445 chooser.setDialogTitle(MessageManager
2446 .getString("label.save_vamsas_document_archive"));
2448 int value = chooser.showSaveDialog(this);
2450 if (value == JalviewFileChooser.APPROVE_OPTION)
2452 java.io.File choice = chooser.getSelectedFile();
2453 JPanel progpanel = addProgressPanel(MessageManager
2454 .formatMessage("label.saving_vamsas_doc", new Object[]
2455 { choice.getName() }));
2456 Cache.setProperty("LAST_DIRECTORY", choice.getParent());
2457 String warnmsg = null;
2458 String warnttl = null;
2461 v_client.vclient.storeDocument(choice);
2464 warnttl = "Serious Problem saving Vamsas Document";
2465 warnmsg = ex.toString();
2466 jalview.bin.Cache.log
2467 .error("Error Whilst saving document to " + choice, ex);
2469 } catch (Exception ex)
2471 warnttl = "Problem saving Vamsas Document.";
2472 warnmsg = ex.toString();
2473 jalview.bin.Cache.log.warn(
2474 "Exception Whilst saving document to " + choice, ex);
2477 removeProgressPanel(progpanel);
2478 if (warnmsg != null)
2480 JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
2482 warnmsg, warnttl, JvOptionPane.ERROR_MESSAGE);
2488 JPanel vamUpdate = null;
2491 * hide vamsas user gui bits when a vamsas document event is being handled.
2494 * true to hide gui, false to reveal gui
2496 public void setVamsasUpdate(boolean b)
2498 Cache.log.debug("Setting gui for Vamsas update "
2499 + (b ? "in progress" : "finished"));
2501 if (vamUpdate != null)
2503 this.removeProgressPanel(vamUpdate);
2507 vamUpdate = this.addProgressPanel(
2508 MessageManager.getString("label.updating_vamsas_session"));
2510 vamsasStart.setVisible(!b);
2511 vamsasStop.setVisible(!b);
2512 vamsasSave.setVisible(!b);
2515 public JInternalFrame[] getAllFrames()
2517 return desktopPane.getAllFrames();
2521 * Checks the given url to see if it gives a response indicating that the user
2522 * should be informed of a new questionnaire.
2526 public void checkForQuestionnaire(String url)
2528 UserQuestionnaireCheck jvq = new UserQuestionnaireCheck(url);
2529 // javax.swing.SwingUtilities.invokeLater(jvq);
2530 new Thread(jvq).start();
2533 public void checkURLLinks()
2535 // Thread off the URL link checker
2536 addDialogThread(new Runnable()
2541 if (Cache.getDefault("CHECKURLLINKS", true))
2543 // check what the actual links are - if it's just the default don't
2544 // bother with the warning
2545 List<String> links = Preferences.sequenceUrlLinks
2548 // only need to check links if there is one with a
2549 // SEQUENCE_ID which is not the default EMBL_EBI link
2550 ListIterator<String> li = links.listIterator();
2551 boolean check = false;
2552 List<JLabel> urls = new ArrayList<>();
2553 while (li.hasNext())
2555 String link = li.next();
2556 if (link.contains(UrlConstants.SEQUENCE_ID)
2557 && !UrlConstants.isDefaultString(link))
2560 int barPos = link.indexOf("|");
2561 String urlMsg = barPos == -1 ? link
2562 : link.substring(0, barPos) + ": "
2563 + link.substring(barPos + 1);
2564 urls.add(new JLabel(urlMsg));
2572 // ask user to check in case URL links use old style tokens
2573 // ($SEQUENCE_ID$ for sequence id _or_ accession id)
2574 JPanel msgPanel = new JPanel();
2575 msgPanel.setLayout(new BoxLayout(msgPanel, BoxLayout.PAGE_AXIS));
2576 msgPanel.add(Box.createVerticalGlue());
2577 JLabel msg = new JLabel(MessageManager
2578 .getString("label.SEQUENCE_ID_for_DB_ACCESSION1"));
2579 JLabel msg2 = new JLabel(MessageManager
2580 .getString("label.SEQUENCE_ID_for_DB_ACCESSION2"));
2582 for (JLabel url : urls)
2588 final JCheckBox jcb = new JCheckBox(
2589 MessageManager.getString("label.do_not_display_again"));
2590 jcb.addActionListener(new ActionListener()
2593 public void actionPerformed(ActionEvent e)
2595 // update Cache settings for "don't show this again"
2596 boolean showWarningAgain = !jcb.isSelected();
2597 Cache.setProperty("CHECKURLLINKS",
2598 Boolean.valueOf(showWarningAgain).toString());
2603 JvOptionPane.showMessageDialog(desktopPane, msgPanel,
2605 .getString("label.SEQUENCE_ID_no_longer_used"),
2606 JvOptionPane.WARNING_MESSAGE);
2613 * Proxy class for JDesktopPane which optionally displays the current memory
2614 * usage and highlights the desktop area with a red bar if free memory runs low.
2618 public class MyDesktopPane extends JDesktopPane
2621 private static final float ONE_MB = 1048576f;
2623 boolean showMemoryUsage = false;
2627 java.text.NumberFormat df;
2629 float maxMemory, allocatedMemory, freeMemory, totalFreeMemory,
2632 public MyDesktopPane(boolean showMemoryUsage)
2634 showMemoryUsage(showMemoryUsage);
2637 public void showMemoryUsage(boolean showMemory)
2639 this.showMemoryUsage = showMemory;
2642 Thread worker = new Thread(this);
2648 public boolean isShowMemoryUsage()
2650 return showMemoryUsage;
2656 df = java.text.NumberFormat.getNumberInstance();
2657 df.setMaximumFractionDigits(2);
2658 runtime = Runtime.getRuntime();
2660 while (showMemoryUsage)
2664 maxMemory = runtime.maxMemory() / ONE_MB;
2665 allocatedMemory = runtime.totalMemory() / ONE_MB;
2666 freeMemory = runtime.freeMemory() / ONE_MB;
2667 totalFreeMemory = freeMemory + (maxMemory - allocatedMemory);
2669 percentUsage = (totalFreeMemory / maxMemory) * 100;
2671 // if (percentUsage < 20)
2673 // border1 = BorderFactory.createMatteBorder(12, 12, 12, 12,
2675 // instance.set.setBorder(border1);
2678 // sleep after showing usage
2680 } catch (Exception ex)
2682 ex.printStackTrace();
2688 public void paintComponent(Graphics g)
2690 if (showMemoryUsage && g != null && df != null)
2692 if (percentUsage < 20)
2694 g.setColor(Color.red);
2696 FontMetrics fm = g.getFontMetrics();
2699 g.drawString(MessageManager.formatMessage("label.memory_stats",
2701 { df.format(totalFreeMemory), df.format(maxMemory),
2702 df.format(percentUsage) }),
2703 10, getHeight() - fm.getHeight());
2710 * Accessor method to quickly get all the AlignmentFrames loaded.
2712 * @return an array of AlignFrame, or null if none found
2714 public static AlignFrame[] getAlignFrames()
2716 if (Jalview.isHeadlessMode())
2718 // Desktop.getDesktop() is null in headless mode
2719 return new AlignFrame[] { Jalview.getCurrentAlignFrame() };
2722 JInternalFrame[] frames = Desktop.getDesktopPane().getAllFrames();
2728 List<AlignFrame> avp = new ArrayList<>();
2730 for (int i = frames.length - 1; i > -1; i--)
2732 if (frames[i] instanceof AlignFrame)
2734 avp.add((AlignFrame) frames[i]);
2736 else if (frames[i] instanceof SplitFrame)
2739 * Also check for a split frame containing an AlignFrame
2741 GSplitFrame sf = (GSplitFrame) frames[i];
2742 if (sf.getTopFrame() instanceof AlignFrame)
2744 avp.add((AlignFrame) sf.getTopFrame());
2746 if (sf.getBottomFrame() instanceof AlignFrame)
2748 avp.add((AlignFrame) sf.getBottomFrame());
2752 if (avp.size() == 0)
2756 AlignFrame afs[] = avp.toArray(new AlignFrame[avp.size()]);
2761 * Returns an array of any AppJmol frames in the Desktop (or null if none).
2765 public GStructureViewer[] getJmols()
2767 JInternalFrame[] frames = Desktop.getDesktopPane().getAllFrames();
2773 List<GStructureViewer> avp = new ArrayList<>();
2775 for (int i = frames.length - 1; i > -1; i--)
2777 if (frames[i] instanceof AppJmol)
2779 GStructureViewer af = (GStructureViewer) frames[i];
2783 if (avp.size() == 0)
2787 GStructureViewer afs[] = avp.toArray(new GStructureViewer[avp.size()]);
2792 * Add Groovy Support to Jalview
2795 public void groovyShell_actionPerformed()
2799 openGroovyConsole();
2800 } catch (Exception ex)
2802 jalview.bin.Cache.log.error("Groovy Shell Creation failed.", ex);
2803 JvOptionPane.showInternalMessageDialog(desktopPane,
2805 MessageManager.getString("label.couldnt_create_groovy_shell"),
2806 MessageManager.getString("label.groovy_support_failed"),
2807 JvOptionPane.ERROR_MESSAGE);
2812 * Open the Groovy console
2814 private void openGroovyConsole()
2816 if (groovyConsole == null)
2818 groovyConsole = new groovy.ui.Console();
2819 groovyConsole.setVariable("Jalview", this);
2820 groovyConsole.run();
2823 * We allow only one console at a time, so that AlignFrame menu option
2824 * 'Calculate | Run Groovy script' is unambiguous.
2825 * Disable 'Groovy Console', and enable 'Run script', when the console is
2826 * opened, and the reverse when it is closed
2828 Window window = (Window) groovyConsole.getFrame();
2829 window.addWindowListener(new WindowAdapter()
2832 public void windowClosed(WindowEvent e)
2835 * rebind CMD-Q from Groovy Console to Jalview Quit
2838 enableExecuteGroovy(false);
2844 * show Groovy console window (after close and reopen)
2846 ((Window) groovyConsole.getFrame()).setVisible(true);
2849 * if we got this far, enable 'Run Groovy' in AlignFrame menus
2850 * and disable opening a second console
2852 enableExecuteGroovy(true);
2856 * Bind Ctrl/Cmd-Q to Quit - for reset as Groovy Console takes over this binding
2859 protected void addQuitHandler()
2861 getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
2862 .put(KeyStroke.getKeyStroke(KeyEvent.VK_Q,
2863 Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()),
2865 getRootPane().getActionMap().put("Quit", new AbstractAction()
2868 public void actionPerformed(ActionEvent e)
2876 * Enable or disable 'Run Groovy script' in AlignFrame calculate menus
2879 * true if Groovy console is open
2881 public void enableExecuteGroovy(boolean enabled)
2884 * disable opening a second Groovy console
2885 * (or re-enable when the console is closed)
2887 groovyShell.setEnabled(!enabled);
2889 AlignFrame[] alignFrames = getAlignFrames();
2890 if (alignFrames != null)
2892 for (AlignFrame af : alignFrames)
2894 af.setGroovyEnabled(enabled);
2900 * Progress bars managed by the IProgressIndicator method.
2902 private Hashtable<Long, JPanel> progressBars;
2904 private Hashtable<Long, IProgressIndicatorHandler> progressBarHandlers;
2909 * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
2912 public void setProgressBar(String message, long id)
2914 Platform.timeCheck("Desktop " + message, Platform.TIME_MARK);
2916 if (progressBars == null)
2918 progressBars = new Hashtable<>();
2919 progressBarHandlers = new Hashtable<>();
2922 if (progressBars.get(new Long(id)) != null)
2924 JPanel panel = progressBars.remove(new Long(id));
2925 if (progressBarHandlers.contains(new Long(id)))
2927 progressBarHandlers.remove(new Long(id));
2929 removeProgressPanel(panel);
2933 progressBars.put(new Long(id), addProgressPanel(message));
2940 * @see jalview.gui.IProgressIndicator#registerHandler(long,
2941 * jalview.gui.IProgressIndicatorHandler)
2944 public void registerHandler(final long id,
2945 final IProgressIndicatorHandler handler)
2947 if (progressBarHandlers == null
2948 || !progressBars.containsKey(new Long(id)))
2950 throw new Error(MessageManager.getString(
2951 "error.call_setprogressbar_before_registering_handler"));
2953 progressBarHandlers.put(new Long(id), handler);
2954 final JPanel progressPanel = progressBars.get(new Long(id));
2955 if (handler.canCancel())
2957 JButton cancel = new JButton(
2958 MessageManager.getString("action.cancel"));
2959 final IProgressIndicator us = this;
2960 cancel.addActionListener(new ActionListener()
2964 public void actionPerformed(ActionEvent e)
2966 handler.cancelActivity(id);
2967 us.setProgressBar(MessageManager
2968 .formatMessage("label.cancelled_params", new Object[]
2969 { ((JLabel) progressPanel.getComponent(0)).getText() }),
2973 progressPanel.add(cancel, BorderLayout.EAST);
2979 * @return true if any progress bars are still active
2982 public boolean operationInProgress()
2984 if (progressBars != null && progressBars.size() > 0)
2992 * This will return the first AlignFrame holding the given viewport instance. It
2993 * will break if there are more than one AlignFrames viewing a particular av.
2996 * @return alignFrame for viewport
2998 public static AlignFrame getAlignFrameFor(AlignViewportI viewport)
3000 if (getDesktopPane() != null)
3002 AlignmentPanel[] aps = getAlignmentPanels(
3003 viewport.getSequenceSetId());
3004 for (int panel = 0; aps != null && panel < aps.length; panel++)
3006 if (aps[panel] != null && aps[panel].av == viewport)
3008 return aps[panel].alignFrame;
3015 public VamsasApplication getVamsasApplication()
3022 * flag set if jalview GUI is being operated programmatically
3024 private boolean inBatchMode = false;
3027 * check if jalview GUI is being operated programmatically
3029 * @return inBatchMode
3031 public boolean isInBatchMode()
3037 * set flag if jalview GUI is being operated programmatically
3039 * @param inBatchMode
3041 public void setInBatchMode(boolean inBatchMode)
3043 this.inBatchMode = inBatchMode;
3046 public void startServiceDiscovery()
3048 startServiceDiscovery(false);
3051 public void startServiceDiscovery(boolean blocking)
3053 boolean alive = true;
3054 Thread t0 = null, t1 = null, t2 = null;
3055 // JAL-940 - JALVIEW 1 services are now being EOLed as of JABA 2.1 release
3058 // todo: changesupport handlers need to be transferred
3059 if (discoverer == null)
3061 discoverer = Discoverer.getInstance();
3062 // register PCS handler for getDesktop().
3063 discoverer.addPropertyChangeListener(changeSupport);
3065 // JAL-940 - disabled JWS1 service configuration - always start discoverer
3066 // until we phase out completely
3067 (t0 = new Thread(discoverer)).start();
3070 if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
3072 t2 = jalview.ws.jws2.Jws2Discoverer.getInstance()
3073 .startDiscoverer(changeSupport);
3077 // TODO: do rest service discovery
3086 } catch (Exception e)
3089 alive = (t1 != null && t1.isAlive()) || (t2 != null && t2.isAlive())
3090 || (t3 != null && t3.isAlive())
3091 || (t0 != null && t0.isAlive());
3097 * called to check if the service discovery process completed successfully.
3101 protected void JalviewServicesChanged(PropertyChangeEvent evt)
3103 if (evt.getNewValue() == null || evt.getNewValue() instanceof Vector)
3105 final String ermsg = jalview.ws.jws2.Jws2Discoverer.getInstance()
3106 .getErrorMessages();
3109 if (Cache.getDefault("SHOW_WSDISCOVERY_ERRORS", true))
3111 if (serviceChangedDialog == null)
3113 // only run if we aren't already displaying one of these.
3114 addDialogThread(serviceChangedDialog = new Runnable()
3121 * JalviewDialog jd =new JalviewDialog() {
3123 * @Override protected void cancelPressed() { // TODO
3124 * Auto-generated method stub
3126 * }@Override protected void okPressed() { // TODO
3127 * Auto-generated method stub
3129 * }@Override protected void raiseClosed() { // TODO
3130 * Auto-generated method stub
3132 * } }; jd.initDialogFrame(new
3133 * JLabel("<html><table width=\"450\"><tr><td>" + ermsg +
3134 * "<br/>It may be that you have invalid JABA URLs in your web service preferences,"
3135 * + " or mis-configured HTTP proxy settings.<br/>" +
3136 * "Check the <em>Connections</em> and <em>Web services</em> tab of the"
3138 * " Tools->Preferences dialog box to change them.</td></tr></table></html>"
3139 * ), true, true, "Web Service Configuration Problem", 450,
3142 * jd.waitForInput();
3144 JvOptionPane.showConfirmDialog(Desktop.getDesktopPane(),
3145 new JLabel("<html><table width=\"450\"><tr><td>"
3146 + ermsg + "</td></tr></table>"
3147 + "<p>It may be that you have invalid JABA URLs<br/>in your web service preferences,"
3148 + "<br>or as a command-line argument, or mis-configured HTTP proxy settings.</p>"
3149 + "<p>Check the <em>Connections</em> and <em>Web services</em> tab<br/>of the"
3150 + " Tools->Preferences dialog box to change them.</p></html>"),
3151 "Web Service Configuration Problem",
3152 JvOptionPane.DEFAULT_OPTION,
3153 JvOptionPane.ERROR_MESSAGE);
3154 serviceChangedDialog = null;
3163 "Errors reported by JABA discovery service. Check web services preferences.\n"
3170 Runnable serviceChangedDialog = null;
3173 * start a thread to open a URL in the configured browser. Pops up a warning
3174 * dialog to the user if there is an exception when calling out to the browser
3179 public static void showUrl(final String url)
3181 showUrl(url, Desktop.getInstance());
3185 * Like showUrl but allows progress handler to be specified
3189 * (null) or object implementing IProgressIndicator
3191 public static void showUrl(final String url,
3192 final IProgressIndicator progress)
3194 new Thread(new Runnable()
3201 if (progress != null)
3203 progress.setProgressBar(MessageManager
3204 .formatMessage("status.opening_params", new Object[]
3205 { url }), this.hashCode());
3207 BrowserLauncher.openURL(url);
3208 } catch (Exception ex)
3210 JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
3212 .getString("label.web_browser_not_found_unix"),
3213 MessageManager.getString("label.web_browser_not_found"),
3214 JvOptionPane.WARNING_MESSAGE);
3216 ex.printStackTrace();
3218 if (progress != null)
3220 progress.setProgressBar(null, this.hashCode());
3226 private WsParamSetManager wsparamManager = null;
3228 public static ParamManager getUserParameterStore()
3230 Desktop d = Desktop.getInstance();
3231 if (d.wsparamManager == null)
3233 d.wsparamManager = new WsParamSetManager();
3235 return d.wsparamManager;
3239 * static hyperlink handler proxy method for use by Jalview's internal windows
3243 public static void hyperlinkUpdate(HyperlinkEvent e)
3245 if (e.getEventType() == EventType.ACTIVATED)
3250 url = e.getURL().toString();
3251 Desktop.showUrl(url);
3252 } catch (Exception x)
3256 if (Cache.log != null)
3258 Cache.log.error("Couldn't handle string " + url + " as a URL.");
3263 "Couldn't handle string " + url + " as a URL.");
3266 // ignore any exceptions due to dud links.
3273 * single thread that handles display of dialogs to user.
3275 ExecutorService dialogExecutor = Executors.newSingleThreadExecutor();
3278 * flag indicating if dialogExecutor should try to acquire a permit
3280 volatile boolean dialogPause = true;
3285 java.util.concurrent.Semaphore block = new Semaphore(0);
3287 private groovy.ui.Console groovyConsole;
3289 public StructureViewer lastTargetedView;
3292 * add another dialog thread to the queue
3296 public void addDialogThread(final Runnable prompter)
3298 dialogExecutor.submit(new Runnable()
3308 } catch (InterruptedException x)
3312 if (instanceOnly || Jalview.isHeadlessMode())
3318 SwingUtilities.invokeAndWait(prompter);
3319 } catch (Exception q)
3321 Cache.log.warn("Unexpected Exception in dialog thread.", q);
3327 public void startDialogQueue()
3329 // set the flag so we don't pause waiting for another permit and semaphore
3330 // the current task to begin
3331 dialogPause = false;
3336 * Outputs an image of the desktop to file in EPS format, after prompting the
3337 * user for choice of Text or Lineart character rendering (unless a preference
3338 * has been set). The file name is generated as
3341 * Jalview_snapshot_nnnnn.eps where nnnnn is the current timestamp in milliseconds
3345 protected void snapShotWindow_actionPerformed(ActionEvent e)
3347 // currently the menu option to do this is not shown
3350 int width = getWidth();
3351 int height = getHeight();
3353 "Jalview_snapshot_" + System.currentTimeMillis() + ".eps");
3354 ImageWriterI writer = new ImageWriterI()
3357 public void exportImage(Graphics g) throws Exception
3360 Cache.log.info("Successfully written snapshot to file "
3361 + of.getAbsolutePath());
3364 String title = "View of desktop";
3365 ImageExporter exporter = new ImageExporter(writer, null, TYPE.EPS,
3367 exporter.doExport(of, this, width, height, title);
3371 * Explode the views in the given SplitFrame into separate SplitFrame windows.
3372 * This respects (remembers) any previous 'exploded geometry' i.e. the size and
3373 * location last time the view was expanded (if any). However it does not
3374 * remember the split pane divider location - this is set to match the
3375 * 'exploding' frame.
3379 public void explodeViews(SplitFrame sf)
3381 AlignFrame oldTopFrame = (AlignFrame) sf.getTopFrame();
3382 AlignFrame oldBottomFrame = (AlignFrame) sf.getBottomFrame();
3383 List<? extends AlignmentViewPanel> topPanels = oldTopFrame
3385 List<? extends AlignmentViewPanel> bottomPanels = oldBottomFrame
3387 int viewCount = topPanels.size();
3394 * Processing in reverse order works, forwards order leaves the first panels
3395 * not visible. I don't know why!
3397 for (int i = viewCount - 1; i >= 0; i--)
3400 * Make new top and bottom frames. These take over the respective
3401 * AlignmentPanel objects, including their AlignmentViewports, so the
3402 * cdna/protein relationships between the viewports is carried over to the
3405 * explodedGeometry holds the (x, y) position of the previously exploded
3406 * SplitFrame, and the (width, height) of the AlignFrame component
3408 AlignmentPanel topPanel = (AlignmentPanel) topPanels.get(i);
3409 AlignFrame newTopFrame = new AlignFrame(topPanel);
3410 newTopFrame.setSize(oldTopFrame.getSize());
3411 newTopFrame.setVisible(true);
3412 Rectangle geometry = ((AlignViewport) topPanel.getAlignViewport())
3413 .getExplodedGeometry();
3414 if (geometry != null)
3416 newTopFrame.setSize(geometry.getSize());
3419 AlignmentPanel bottomPanel = (AlignmentPanel) bottomPanels.get(i);
3420 AlignFrame newBottomFrame = new AlignFrame(bottomPanel);
3421 newBottomFrame.setSize(oldBottomFrame.getSize());
3422 newBottomFrame.setVisible(true);
3423 geometry = ((AlignViewport) bottomPanel.getAlignViewport())
3424 .getExplodedGeometry();
3425 if (geometry != null)
3427 newBottomFrame.setSize(geometry.getSize());
3430 topPanel.av.setGatherViewsHere(false);
3431 bottomPanel.av.setGatherViewsHere(false);
3432 JInternalFrame splitFrame = new SplitFrame(newTopFrame,
3434 if (geometry != null)
3436 splitFrame.setLocation(geometry.getLocation());
3438 Desktop.addInternalFrame(splitFrame, sf.getTitle(), -1, -1);
3442 * Clear references to the panels (now relocated in the new SplitFrames)
3443 * before closing the old SplitFrame.
3446 bottomPanels.clear();
3451 * Gather expanded split frames, sharing the same pairs of sequence set ids,
3452 * back into the given SplitFrame as additional views. Note that the gathered
3453 * frames may themselves have multiple views.
3457 public void gatherViews(GSplitFrame source)
3460 * special handling of explodedGeometry for a view within a SplitFrame: - it
3461 * holds the (x, y) position of the enclosing SplitFrame, and the (width,
3462 * height) of the AlignFrame component
3464 AlignFrame myTopFrame = (AlignFrame) source.getTopFrame();
3465 AlignFrame myBottomFrame = (AlignFrame) source.getBottomFrame();
3466 myTopFrame.viewport.setExplodedGeometry(new Rectangle(source.getX(),
3467 source.getY(), myTopFrame.getWidth(), myTopFrame.getHeight()));
3468 myBottomFrame.viewport
3469 .setExplodedGeometry(new Rectangle(source.getX(), source.getY(),
3470 myBottomFrame.getWidth(), myBottomFrame.getHeight()));
3471 myTopFrame.viewport.setGatherViewsHere(true);
3472 myBottomFrame.viewport.setGatherViewsHere(true);
3473 String topViewId = myTopFrame.viewport.getSequenceSetId();
3474 String bottomViewId = myBottomFrame.viewport.getSequenceSetId();
3476 JInternalFrame[] frames = desktopPane.getAllFrames();
3477 for (JInternalFrame frame : frames)
3479 if (frame instanceof SplitFrame && frame != source)
3481 SplitFrame sf = (SplitFrame) frame;
3482 AlignFrame topFrame = (AlignFrame) sf.getTopFrame();
3483 AlignFrame bottomFrame = (AlignFrame) sf.getBottomFrame();
3484 boolean gatherThis = false;
3485 for (int a = 0; a < topFrame.alignPanels.size(); a++)
3487 AlignmentPanel topPanel = topFrame.alignPanels.get(a);
3488 AlignmentPanel bottomPanel = bottomFrame.alignPanels.get(a);
3489 if (topViewId.equals(topPanel.av.getSequenceSetId())
3490 && bottomViewId.equals(bottomPanel.av.getSequenceSetId()))
3493 topPanel.av.setGatherViewsHere(false);
3494 bottomPanel.av.setGatherViewsHere(false);
3495 topPanel.av.setExplodedGeometry(
3496 new Rectangle(sf.getLocation(), topFrame.getSize()));
3497 bottomPanel.av.setExplodedGeometry(
3498 new Rectangle(sf.getLocation(), bottomFrame.getSize()));
3499 myTopFrame.addAlignmentPanel(topPanel, false);
3500 myBottomFrame.addAlignmentPanel(bottomPanel, false);
3506 topFrame.getAlignPanels().clear();
3507 bottomFrame.getAlignPanels().clear();
3514 * The dust settles...give focus to the tab we did this from.
3516 myTopFrame.setDisplayedView(myTopFrame.alignPanel);
3519 public static groovy.ui.Console getGroovyConsole()
3521 Desktop desktop = Desktop.getInstance();
3522 return desktop == null ? null : desktop.groovyConsole;
3526 * handles the payload of a drag and drop event.
3528 * TODO refactor to desktop utilities class
3531 * - Data source strings extracted from the drop event
3533 * - protocol for each data source extracted from the drop event
3537 * - the payload from the drop event
3540 @SuppressWarnings("unchecked")
3541 public static void transferFromDropTarget(List<Object> files,
3542 List<DataSourceType> protocols, DropTargetDropEvent evt,
3543 Transferable t) throws Exception
3546 // BH 2018 changed List<String> to List<Object> to allow for File from SwingJS
3548 // DataFlavor[] flavors = t.getTransferDataFlavors();
3549 // for (int i = 0; i < flavors.length; i++) {
3550 // if (flavors[i].isFlavorJavaFileListType()) {
3551 // evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
3552 // List<File> list = (List<File>) t.getTransferData(flavors[i]);
3553 // for (int j = 0; j < list.size(); j++) {
3554 // File file = (File) list.get(j);
3555 // byte[] data = getDroppedFileBytes(file);
3556 // fileName.setText(file.getName() + " - " + data.length + " " +
3557 // evt.getLocation());
3558 // JTextArea target = (JTextArea) ((DropTarget) evt.getSource()).getComponent();
3559 // target.setText(new String(data));
3561 // dtde.dropComplete(true);
3566 DataFlavor uriListFlavor = new DataFlavor(
3567 "text/uri-list;class=java.lang.String"), urlFlavour = null;
3570 urlFlavour = new DataFlavor(
3571 "application/x-java-url; class=java.net.URL");
3572 } catch (ClassNotFoundException cfe)
3574 Cache.log.debug("Couldn't instantiate the URL dataflavor.", cfe);
3577 if (urlFlavour != null && t.isDataFlavorSupported(urlFlavour))
3582 java.net.URL url = (URL) t.getTransferData(urlFlavour);
3583 // nb: java 8 osx bug https://bugs.openjdk.java.net/browse/JDK-8156099
3584 // means url may be null.
3587 protocols.add(DataSourceType.URL);
3588 files.add(url.toString());
3589 Cache.log.debug("Drop handled as URL dataflavor "
3590 + files.get(files.size() - 1));
3595 if (Platform.isAMacAndNotJS())
3598 "Please ignore plist error - occurs due to problem with java 8 on OSX");
3601 } catch (Throwable ex)
3603 Cache.log.debug("URL drop handler failed.", ex);
3606 if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
3608 // Works on Windows and MacOSX
3609 Cache.log.debug("Drop handled as javaFileListFlavor");
3610 for (Object file : (List<Object>) t
3611 .getTransferData(DataFlavor.javaFileListFlavor))
3614 protocols.add(DataSourceType.FILE);
3619 // Unix like behaviour
3620 boolean added = false;
3622 if (t.isDataFlavorSupported(uriListFlavor))
3624 Cache.log.debug("Drop handled as uriListFlavor");
3625 // This is used by Unix drag system
3626 data = (String) t.getTransferData(uriListFlavor);
3630 // fallback to text: workaround - on OSX where there's a JVM bug
3631 Cache.log.debug("standard URIListFlavor failed. Trying text");
3632 // try text fallback
3633 DataFlavor textDf = new DataFlavor(
3634 "text/plain;class=java.lang.String");
3635 if (t.isDataFlavorSupported(textDf))
3637 data = (String) t.getTransferData(textDf);
3640 Cache.log.debug("Plain text drop content returned "
3641 + (data == null ? "Null - failed" : data));
3646 while (protocols.size() < files.size())
3648 Cache.log.debug("Adding missing FILE protocol for "
3649 + files.get(protocols.size()));
3650 protocols.add(DataSourceType.FILE);
3652 for (java.util.StringTokenizer st = new java.util.StringTokenizer(
3653 data, "\r\n"); st.hasMoreTokens();)
3656 String s = st.nextToken();
3657 if (s.startsWith("#"))
3659 // the line is a comment (as per the RFC 2483)
3662 java.net.URI uri = new java.net.URI(s);
3663 if (uri.getScheme().toLowerCase().startsWith("http"))
3665 protocols.add(DataSourceType.URL);
3666 files.add(uri.toString());
3670 // otherwise preserve old behaviour: catch all for file objects
3671 java.io.File file = new java.io.File(uri);
3672 protocols.add(DataSourceType.FILE);
3673 files.add(file.toString());
3678 if (Cache.log.isDebugEnabled())
3680 if (data == null || !added)
3683 if (t.getTransferDataFlavors() != null
3684 && t.getTransferDataFlavors().length > 0)
3687 "Couldn't resolve drop data. Here are the supported flavors:");
3688 for (DataFlavor fl : t.getTransferDataFlavors())
3691 "Supported transfer dataflavor: " + fl.toString());
3692 Object df = t.getTransferData(fl);
3695 Cache.log.debug("Retrieves: " + df);
3699 Cache.log.debug("Retrieved nothing");
3705 Cache.log.debug("Couldn't resolve dataflavor for drop: "
3711 if (Platform.isWindowsAndNotJS())
3713 Cache.log.debug("Scanning dropped content for Windows Link Files");
3715 // resolve any .lnk files in the file drop
3716 for (int f = 0; f < files.size(); f++)
3718 String source = files.get(f).toString().toLowerCase();
3719 if (protocols.get(f).equals(DataSourceType.FILE)
3720 && (source.endsWith(".lnk") || source.endsWith(".url")
3721 || source.endsWith(".site")))
3725 Object obj = files.get(f);
3726 File lf = (obj instanceof File ? (File) obj
3727 : new File((String) obj));
3728 // process link file to get a URL
3729 Cache.log.debug("Found potential link file: " + lf);
3730 WindowsShortcut wscfile = new WindowsShortcut(lf);
3731 String fullname = wscfile.getRealFilename();
3732 protocols.set(f, FormatAdapter.checkProtocol(fullname));
3733 files.set(f, fullname);
3734 Cache.log.debug("Parsed real filename " + fullname
3735 + " to extract protocol: " + protocols.get(f));
3736 } catch (Exception ex)
3739 "Couldn't parse " + files.get(f) + " as a link file.",
3748 * Sets the Preferences property for experimental features to True or False
3749 * depending on the state of the controlling menu item
3752 protected void showExperimental_actionPerformed(boolean selected)
3754 Cache.setProperty(EXPERIMENTAL_FEATURES, Boolean.toString(selected));
3758 * Answers a (possibly empty) list of any structure viewer frames (currently for
3759 * either Jmol or Chimera) which are currently open. This may optionally be
3760 * restricted to viewers of a specified class, or viewers linked to a specified
3764 * if not null, only return viewers linked to this panel
3765 * @param structureViewerClass
3766 * if not null, only return viewers of this class
3769 public List<StructureViewerBase> getStructureViewers(
3770 AlignmentPanel apanel,
3771 Class<? extends StructureViewerBase> structureViewerClass)
3773 List<StructureViewerBase> result = new ArrayList<>();
3774 JInternalFrame[] frames = getAllFrames();
3776 for (JInternalFrame frame : frames)
3778 if (frame instanceof StructureViewerBase)
3780 if (structureViewerClass == null
3781 || structureViewerClass.isInstance(frame))
3784 || ((StructureViewerBase) frame).isLinkedWith(apanel))
3786 result.add((StructureViewerBase) frame);