2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
5 * This file is part of Jalview.
7 * Jalview is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation, either version 3
10 * of the License, or (at your option) any later version.
12 * Jalview is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19 * The Jalview Authors are detailed in the 'AUTHORS' file.
23 import jalview.api.AlignViewportI;
24 import jalview.api.AlignmentViewPanel;
25 import jalview.api.StructureSelectionManagerProvider;
26 import jalview.bin.ApplicationSingletonProvider;
27 import jalview.bin.ApplicationSingletonProvider.ApplicationSingletonI;
28 import jalview.bin.Cache;
29 import jalview.bin.Jalview;
30 import jalview.gui.ImageExporter.ImageWriterI;
31 import jalview.io.BackupFiles;
32 import jalview.io.DataSourceType;
33 import jalview.io.FileFormat;
34 import jalview.io.FileFormatException;
35 import jalview.io.FileFormatI;
36 import jalview.io.FileFormats;
37 import jalview.io.FileLoader;
38 import jalview.io.FormatAdapter;
39 import jalview.io.IdentifyFile;
40 import jalview.io.JalviewFileChooser;
41 import jalview.io.JalviewFileView;
42 import jalview.jbgui.GDesktop;
43 import jalview.jbgui.GSplitFrame;
44 import jalview.jbgui.GStructureViewer;
45 import jalview.project.Jalview2XML;
46 import jalview.structure.StructureSelectionManager;
47 import jalview.urls.IdOrgSettings;
48 import jalview.util.BrowserLauncher;
49 import jalview.util.ImageMaker.TYPE;
50 import jalview.util.MessageManager;
51 import jalview.util.Platform;
52 import jalview.util.UrlConstants;
53 import jalview.viewmodel.AlignmentViewport;
54 import jalview.ws.jws1.Discoverer;
55 import jalview.ws.params.ParamManager;
56 import jalview.ws.utils.UrlDownloadClient;
58 import java.awt.BorderLayout;
59 import java.awt.Color;
60 import java.awt.Dimension;
61 import java.awt.FontMetrics;
62 import java.awt.Graphics;
63 import java.awt.GridLayout;
64 import java.awt.Point;
65 import java.awt.Rectangle;
66 import java.awt.Toolkit;
67 import java.awt.Window;
68 import java.awt.datatransfer.Clipboard;
69 import java.awt.datatransfer.ClipboardOwner;
70 import java.awt.datatransfer.DataFlavor;
71 import java.awt.datatransfer.Transferable;
72 import java.awt.dnd.DnDConstants;
73 import java.awt.dnd.DropTargetDragEvent;
74 import java.awt.dnd.DropTargetDropEvent;
75 import java.awt.dnd.DropTargetEvent;
76 import java.awt.dnd.DropTargetListener;
77 import java.awt.event.ActionEvent;
78 import java.awt.event.ActionListener;
79 import java.awt.event.InputEvent;
80 import java.awt.event.KeyEvent;
81 import java.awt.event.MouseAdapter;
82 import java.awt.event.MouseEvent;
83 import java.awt.event.WindowAdapter;
84 import java.awt.event.WindowEvent;
85 import java.beans.PropertyChangeEvent;
86 import java.beans.PropertyChangeListener;
87 import java.io.BufferedInputStream;
89 import java.io.FileOutputStream;
90 import java.io.IOException;
92 import java.util.ArrayList;
93 import java.util.Hashtable;
94 import java.util.List;
95 import java.util.ListIterator;
96 import java.util.Vector;
97 import java.util.concurrent.ExecutorService;
98 import java.util.concurrent.Executors;
99 import java.util.concurrent.Semaphore;
101 import javax.swing.AbstractAction;
102 import javax.swing.Action;
103 import javax.swing.ActionMap;
104 import javax.swing.Box;
105 import javax.swing.BoxLayout;
106 import javax.swing.DefaultDesktopManager;
107 import javax.swing.DesktopManager;
108 import javax.swing.InputMap;
109 import javax.swing.JButton;
110 import javax.swing.JCheckBox;
111 import javax.swing.JComboBox;
112 import javax.swing.JComponent;
113 import javax.swing.JDesktopPane;
114 import javax.swing.JFrame;
115 import javax.swing.JInternalFrame;
116 import javax.swing.JLabel;
117 import javax.swing.JMenuItem;
118 import javax.swing.JPanel;
119 import javax.swing.JPopupMenu;
120 import javax.swing.JProgressBar;
121 import javax.swing.JTextField;
122 import javax.swing.KeyStroke;
123 import javax.swing.SwingUtilities;
124 import javax.swing.event.HyperlinkEvent;
125 import javax.swing.event.HyperlinkEvent.EventType;
126 import javax.swing.event.InternalFrameAdapter;
127 import javax.swing.event.InternalFrameEvent;
128 import javax.swing.event.MenuEvent;
129 import javax.swing.event.MenuListener;
131 import org.stackoverflowusers.file.WindowsShortcut;
138 * @version $Revision: 1.155 $
140 @SuppressWarnings("serial")
141 public class Desktop extends GDesktop
142 implements DropTargetListener, ClipboardOwner, IProgressIndicator,
143 StructureSelectionManagerProvider, ApplicationSingletonI
146 private final static int DEFAULT_MIN_WIDTH = 300;
148 private final static int DEFAULT_MIN_HEIGHT = 250;
150 private final static int ALIGN_FRAME_DEFAULT_MIN_WIDTH = 600;
152 private final static int ALIGN_FRAME_DEFAULT_MIN_HEIGHT = 70;
154 private final static String EXPERIMENTAL_FEATURES = "EXPERIMENTAL_FEATURES";
156 private JalviewChangeSupport changeSupport = new JalviewChangeSupport();
159 * news reader - null if it was never started.
161 BlogReader jvnews = null;
163 private File projectFile;
167 * @see jalview.gui.JalviewChangeSupport#addJalviewPropertyChangeListener(java.beans.PropertyChangeListener)
169 public void addJalviewPropertyChangeListener(
170 PropertyChangeListener listener)
172 changeSupport.addJalviewPropertyChangeListener(listener);
176 * @param propertyName
178 * @see jalview.gui.JalviewChangeSupport#addJalviewPropertyChangeListener(java.lang.String,
179 * java.beans.PropertyChangeListener)
181 public void addJalviewPropertyChangeListener(String propertyName,
182 PropertyChangeListener listener)
184 changeSupport.addJalviewPropertyChangeListener(propertyName, listener);
188 * @param propertyName
190 * @see jalview.gui.JalviewChangeSupport#removeJalviewPropertyChangeListener(java.lang.String,
191 * java.beans.PropertyChangeListener)
193 public void removeJalviewPropertyChangeListener(String propertyName,
194 PropertyChangeListener listener)
196 changeSupport.removeJalviewPropertyChangeListener(propertyName,
200 public static MyDesktopPane getDesktopPane()
202 Desktop desktop = Desktop.getInstance();
203 return desktop == null ? null : desktop.desktopPane;
206 public static StructureSelectionManager getStructureSelectionManager()
208 return StructureSelectionManager
209 .getStructureSelectionManager(getInstance());
212 static int openFrameCount = 0;
214 static final int xOffset = 30;
216 static final int yOffset = 30;
218 public Discoverer discoverer;
220 public Object[] jalviewClipboard;
222 public boolean internalCopy = false;
224 private static int fileLoadingCount = 0;
226 public JInternalFrame conservationSlider;
228 public JInternalFrame PIDSlider;
231 * just an instance (for testng, probably); no actual frames
233 * This flag, when set true, allows a headless-like operation, with a Desktop
234 * object but no actual frames. The issue has to do with the mess-up of the
235 * Windows JInternalFrame implementation, which surreptitiously and
236 * unforgivingly accesses a native peer class, preventing headless operation.
238 * It is set by invoking the Desktop(true) constructor.
240 * It is possible that we can remove this option now, since headless mode is
241 * finally working on Windows through careful attention to not creating any
242 * JInternalFrame objects when in that mode.
245 private boolean instanceOnly;
247 class MyDesktopManager implements DesktopManager
250 private DesktopManager delegate;
252 public MyDesktopManager(DesktopManager delegate)
254 this.delegate = delegate;
258 public void activateFrame(JInternalFrame f)
262 delegate.activateFrame(f);
263 } catch (NullPointerException npe)
265 Point p = getMousePosition();
266 showPasteMenu(p.x, p.y);
271 public void beginDraggingFrame(JComponent f)
273 delegate.beginDraggingFrame(f);
277 public void beginResizingFrame(JComponent f, int direction)
279 delegate.beginResizingFrame(f, direction);
283 public void closeFrame(JInternalFrame f)
285 delegate.closeFrame(f);
289 public void deactivateFrame(JInternalFrame f)
291 delegate.deactivateFrame(f);
295 public void deiconifyFrame(JInternalFrame f)
297 delegate.deiconifyFrame(f);
301 public void dragFrame(JComponent f, int newX, int newY)
307 delegate.dragFrame(f, newX, newY);
311 public void endDraggingFrame(JComponent f)
313 delegate.endDraggingFrame(f);
314 desktopPane.repaint();
318 public void endResizingFrame(JComponent f)
320 delegate.endResizingFrame(f);
321 desktopPane.repaint();
325 public void iconifyFrame(JInternalFrame f)
327 delegate.iconifyFrame(f);
331 public void maximizeFrame(JInternalFrame f)
333 delegate.maximizeFrame(f);
337 public void minimizeFrame(JInternalFrame f)
339 delegate.minimizeFrame(f);
343 public void openFrame(JInternalFrame f)
345 delegate.openFrame(f);
349 public void resizeFrame(JComponent f, int newX, int newY, int newWidth,
356 delegate.resizeFrame(f, newX, newY, newWidth, newHeight);
360 public void setBoundsForFrame(JComponent f, int newX, int newY,
361 int newWidth, int newHeight)
363 delegate.setBoundsForFrame(f, newX, newY, newWidth, newHeight);
366 // All other methods, simply delegate
370 public MyDesktopPane desktopPane;
373 * Answers an 'application scope' singleton instance of this class. Separate
374 * SwingJS 'applets' running in the same browser page will each have a
375 * distinct instance of Desktop.
379 public static Desktop getInstance()
381 return Jalview.isHeadlessMode() ? null
382 : (Desktop) ApplicationSingletonProvider
383 .getInstance(Desktop.class);
387 * For testing purposes, this constructor can be utilized to allow the creation
388 * of a singleton Desktop instance without the formation of frames. The Cache is
389 * initialized, but that is all.
391 * It is not currently used.
395 public Desktop(boolean forInstance)
403 * Private constructor enforces singleton pattern. It is called by reflection
404 * from ApplicationSingletonProvider.getInstance().
406 @SuppressWarnings("unused")
413 * A note to implementors. It is ESSENTIAL that any activities that might
414 * block are spawned off as threads rather than waited for during this
417 if (!Platform.isJS())
419 doVamsasClientCheck();
422 doConfigureStructurePrefs();
423 setTitle("Jalview " + jalview.bin.Cache.getProperty("VERSION"));
424 setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
425 boolean selmemusage = jalview.bin.Cache.getDefault("SHOW_MEMUSAGE",
427 boolean showjconsole = jalview.bin.Cache
428 .getDefault("SHOW_JAVA_CONSOLE", false);
429 desktopPane = new MyDesktopPane(selmemusage);
431 showMemusage.setSelected(selmemusage);
432 desktopPane.setBackground(Color.white);
433 getContentPane().setLayout(new BorderLayout());
434 // alternate config - have scrollbars - see notes in JAL-153
435 // JScrollPane sp = new JScrollPane();
436 // sp.getViewport().setView(desktop);
437 // getContentPane().add(sp, BorderLayout.CENTER);
439 // BH 2018 - just an experiment to try unclipped JInternalFrames.
442 getRootPane().putClientProperty("swingjs.overflow.hidden", "false");
445 getContentPane().add(desktopPane, BorderLayout.CENTER);
446 desktopPane.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
448 // This line prevents Windows Look&Feel resizing all new windows to
450 // if previous window was maximised
451 desktopPane.setDesktopManager(new MyDesktopManager(
452 (Platform.isWindowsAndNotJS() ? new DefaultDesktopManager()
453 : Platform.isAMacAndNotJS()
454 ? new AquaInternalFrameManager(
455 desktopPane.getDesktopManager())
456 : desktopPane.getDesktopManager())));
458 Rectangle dims = getLastKnownDimensions("");
465 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
466 int xPos = Math.max(5, (screenSize.width - 900) / 2);
467 int yPos = Math.max(5, (screenSize.height - 650) / 2);
468 setBounds(xPos, yPos, 900, 650);
471 // Note that this next syntax, checking for Platform.isJS and also
472 // escaping the code using @j2sIgnore, serves two purposes. It gives
473 // us an easily findable tag, Platform.isJS(), to places in the code where
474 // there is something different about the SwingJS implementation. Second,
475 // it deletes the unneeded Java-only code form the JavaScript version
476 // completely (@j2sIgnore), since it will never be used there.
478 if (!Platform.isJS())
486 jconsole = new Console(this, showjconsole);
487 // add essential build information
488 jconsole.setHeader("Jalview Version: "
489 + jalview.bin.Cache.getProperty("VERSION") + "\n"
490 + "Jalview Installation: "
491 + jalview.bin.Cache.getDefault("INSTALLATION", "unknown")
492 + "\n" + "Build Date: "
493 + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown")
494 + "\n" + "Java version: "
495 + System.getProperty("java.version") + "\n"
496 + System.getProperty("os.arch") + " "
497 + System.getProperty("os.name") + " "
498 + System.getProperty("os.version"));
500 showConsole(showjconsole);
502 showNews.setVisible(false);
504 experimentalFeatures.setSelected(showExperimental());
506 getIdentifiersOrgData();
510 // Spawn a thread that shows the splashscreen
512 SwingUtilities.invokeLater(new Runnable()
521 // Thread off a new instance of the file chooser - this reduces the time
523 // takes to open it later on.
524 new Thread(new Runnable()
529 Cache.log.debug("Filechooser init thread started.");
530 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
531 JalviewFileChooser.forRead(Cache.getProperty("LAST_DIRECTORY"),
533 Cache.log.debug("Filechooser init thread finished.");
536 // Add the service change listener
537 changeSupport.addJalviewPropertyChangeListener("services",
538 new PropertyChangeListener()
542 public void propertyChange(PropertyChangeEvent evt)
544 Cache.log.debug("Firing service changed event for "
545 + evt.getNewValue());
546 JalviewServicesChanged(evt);
553 this.setDropTarget(new java.awt.dnd.DropTarget(desktopPane, this));
555 this.addWindowListener(new WindowAdapter()
558 public void windowClosing(WindowEvent evt)
565 this.addMouseListener(ma = new MouseAdapter()
568 public void mousePressed(MouseEvent evt)
570 if (evt.isPopupTrigger()) // Mac
572 showPasteMenu(evt.getX(), evt.getY());
577 public void mouseReleased(MouseEvent evt)
579 if (evt.isPopupTrigger()) // Windows
581 showPasteMenu(evt.getX(), evt.getY());
585 desktopPane.addMouseListener(ma);
586 } catch (Throwable t)
593 * Answers true if user preferences to enable experimental features is True
598 public boolean showExperimental()
600 String experimental = Cache.getDefault(EXPERIMENTAL_FEATURES,
601 Boolean.FALSE.toString());
602 return Boolean.valueOf(experimental).booleanValue();
605 public void doConfigureStructurePrefs()
607 // configure services
608 StructureSelectionManager ssm = StructureSelectionManager
609 .getStructureSelectionManager(this);
610 if (jalview.bin.Cache.getDefault(Preferences.ADD_SS_ANN, true))
612 ssm.setAddTempFacAnnot(jalview.bin.Cache
613 .getDefault(Preferences.ADD_TEMPFACT_ANN, true));
614 ssm.setProcessSecondaryStructure(jalview.bin.Cache
615 .getDefault(Preferences.STRUCT_FROM_PDB, true));
616 ssm.setSecStructServices(
617 jalview.bin.Cache.getDefault(Preferences.USE_RNAVIEW, true));
621 ssm.setAddTempFacAnnot(false);
622 ssm.setProcessSecondaryStructure(false);
623 ssm.setSecStructServices(false);
627 public void checkForNews()
629 final Desktop me = this;
630 // Thread off the news reader, in case there are connection problems.
631 new Thread(new Runnable()
636 Cache.log.debug("Starting news thread.");
637 jvnews = new BlogReader(me);
638 showNews.setVisible(true);
639 Cache.log.debug("Completed news thread.");
644 public void getIdentifiersOrgData()
646 // Thread off the identifiers fetcher
647 new Thread(new Runnable()
652 Cache.log.debug("Downloading data from identifiers.org");
653 // UrlDownloadClient client = new UrlDownloadClient();
656 UrlDownloadClient.download(IdOrgSettings.getUrl(),
657 IdOrgSettings.getDownloadLocation());
658 } catch (IOException e)
660 Cache.log.debug("Exception downloading identifiers.org data"
669 protected void showNews_actionPerformed(ActionEvent e)
671 showNews(showNews.isSelected());
674 protected void showNews(boolean visible)
676 Cache.log.debug((visible ? "Showing" : "Hiding") + " news.");
677 showNews.setSelected(visible);
678 if (visible && !jvnews.isVisible())
680 new Thread(new Runnable()
685 long now = System.currentTimeMillis();
687 MessageManager.getString("status.refreshing_news"), now);
688 jvnews.refreshNews();
689 setProgressBar(null, now);
697 * recover the last known dimensions for a jalview window
700 * - empty string is desktop, all other windows have unique prefix
701 * @return null or last known dimensions scaled to current geometry (if last
702 * window geom was known)
704 Rectangle getLastKnownDimensions(String windowName)
706 // TODO: lock aspect ratio for scaling desktop Bug #0058199
707 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
708 String x = jalview.bin.Cache.getProperty(windowName + "SCREEN_X");
709 String y = jalview.bin.Cache.getProperty(windowName + "SCREEN_Y");
710 String width = jalview.bin.Cache
711 .getProperty(windowName + "SCREEN_WIDTH");
712 String height = jalview.bin.Cache
713 .getProperty(windowName + "SCREEN_HEIGHT");
714 if ((x != null) && (y != null) && (width != null) && (height != null))
716 int ix = Integer.parseInt(x), iy = Integer.parseInt(y),
717 iw = Integer.parseInt(width), ih = Integer.parseInt(height);
718 if (jalview.bin.Cache.getProperty("SCREENGEOMETRY_WIDTH") != null)
720 // attempt #1 - try to cope with change in screen geometry - this
721 // version doesn't preserve original jv aspect ratio.
722 // take ratio of current screen size vs original screen size.
723 double sw = ((1f * screenSize.width) / (1f * Integer.parseInt(
724 jalview.bin.Cache.getProperty("SCREENGEOMETRY_WIDTH"))));
725 double sh = ((1f * screenSize.height) / (1f * Integer.parseInt(
726 jalview.bin.Cache.getProperty("SCREENGEOMETRY_HEIGHT"))));
727 // rescale the bounds depending upon the current screen geometry.
728 ix = (int) (ix * sw);
729 iw = (int) (iw * sw);
730 iy = (int) (iy * sh);
731 ih = (int) (ih * sh);
732 while (ix >= screenSize.width)
734 jalview.bin.Cache.log.debug(
735 "Window geometry location recall error: shifting horizontal to within screenbounds.");
736 ix -= screenSize.width;
738 while (iy >= screenSize.height)
740 jalview.bin.Cache.log.debug(
741 "Window geometry location recall error: shifting vertical to within screenbounds.");
742 iy -= screenSize.height;
744 jalview.bin.Cache.log.debug(
745 "Got last known dimensions for " + windowName + ": x:" + ix
746 + " y:" + iy + " width:" + iw + " height:" + ih);
748 // return dimensions for new instance
749 return new Rectangle(ix, iy, iw, ih);
754 private void doVamsasClientCheck()
756 if (Cache.vamsasJarsPresent())
758 setupVamsasDisconnectedGui();
759 VamsasMenu.setVisible(true);
760 final Desktop us = this;
761 VamsasMenu.addMenuListener(new MenuListener()
763 // this listener remembers when the menu was first selected, and
764 // doesn't rebuild the session list until it has been cleared and
766 boolean refresh = true;
769 public void menuCanceled(MenuEvent e)
775 public void menuDeselected(MenuEvent e)
781 public void menuSelected(MenuEvent e)
785 us.buildVamsasStMenu();
790 vamsasStart.setVisible(true);
794 protected void showPasteMenu(int x, int y)
796 JPopupMenu popup = new JPopupMenu();
797 JMenuItem item = new JMenuItem(
798 MessageManager.getString("label.paste_new_window"));
799 item.addActionListener(new ActionListener()
802 public void actionPerformed(ActionEvent evt)
809 popup.show(this, x, y);
816 Clipboard c = Toolkit.getDefaultToolkit().getSystemClipboard();
817 Transferable contents = c.getContents(this);
819 if (contents != null)
821 String file = (String) contents
822 .getTransferData(DataFlavor.stringFlavor);
824 FileFormatI format = new IdentifyFile().identify(file,
825 DataSourceType.PASTE);
827 new FileLoader().loadFile(file, DataSourceType.PASTE, format);
830 } catch (Exception ex)
833 "Unable to paste alignment from system clipboard:\n" + ex);
838 * Adds and opens the given frame to the desktop
849 public static synchronized void addInternalFrame(
850 final JInternalFrame frame, String title, int w, int h)
852 addInternalFrame(frame, title, true, w, h, true, false);
856 * Add an internal frame to the Jalview desktop
863 * When true, display frame immediately, otherwise, caller must call
864 * setVisible themselves.
870 public static synchronized void addInternalFrame(
871 final JInternalFrame frame, String title, boolean makeVisible,
874 addInternalFrame(frame, title, makeVisible, w, h, true, false);
878 * Add an internal frame to the Jalview desktop and make it visible
891 public static synchronized void addInternalFrame(
892 final JInternalFrame frame, String title, int w, int h,
895 addInternalFrame(frame, title, true, w, h, resizable, false);
899 * Add an internal frame to the Jalview desktop
906 * When true, display frame immediately, otherwise, caller must call
907 * setVisible themselves.
914 * @param ignoreMinSize
915 * Do not set the default minimum size for frame
917 public static synchronized void addInternalFrame(
918 final JInternalFrame frame, String title, boolean makeVisible,
919 int w, int h, boolean resizable, boolean ignoreMinSize)
923 // TODO: allow callers to determine X and Y position of frame (eg. via
925 // TODO: consider fixing method to update entries in the window submenu with
926 // the current window title
928 frame.setTitle(title);
929 if (w > 0 && (frame.getWidth() < 1 || frame.getHeight() < 1))
933 // THIS IS A PUBLIC STATIC METHOD, SO IT MAY BE CALLED EVEN IN
934 // A HEADLESS STATE WHEN NO DESKTOP EXISTS. MUST RETURN
935 // IF JALVIEW IS RUNNING HEADLESS
936 // ///////////////////////////////////////////////
937 if (Jalview.isHeadlessMode() || Desktop.getInstance().instanceOnly)
946 frame.setMinimumSize(
947 new Dimension(DEFAULT_MIN_WIDTH, DEFAULT_MIN_HEIGHT));
949 // Set default dimension for Alignment Frame window.
950 // The Alignment Frame window could be added from a number of places,
952 // I did this here in order not to miss out on any Alignment frame.
953 if (frame instanceof AlignFrame)
955 frame.setMinimumSize(new Dimension(ALIGN_FRAME_DEFAULT_MIN_WIDTH,
956 ALIGN_FRAME_DEFAULT_MIN_HEIGHT));
960 frame.setVisible(makeVisible);
961 frame.setClosable(true);
962 frame.setResizable(resizable);
963 frame.setMaximizable(resizable);
964 frame.setIconifiable(resizable);
965 frame.setOpaque(Platform.isJS());
967 boolean isEmbedded = (Platform.getDimIfEmbedded(frame, -1, -1) != null);
968 if (!isEmbedded && frame.getX() < 1 && frame.getY() < 1)
970 frame.setLocation(xOffset * openFrameCount,
971 yOffset * ((openFrameCount - 1) % 10) + yOffset);
975 * add an entry for the new frame in the Window menu
976 * (and remove it when the frame is closed)
978 JMenuItem menuItem = new JMenuItem(title);
979 frame.addInternalFrameListener(new InternalFrameAdapter()
982 public void internalFrameActivated(InternalFrameEvent evt)
984 JInternalFrame itf = getDesktopPane().getSelectedFrame();
987 if (itf instanceof AlignFrame)
989 Jalview.setCurrentAlignFrame((AlignFrame) itf);
996 public void internalFrameClosed(InternalFrameEvent evt)
998 PaintRefresher.RemoveComponent(frame);
1001 * defensive check to prevent frames being
1002 * added half off the window
1004 if (openFrameCount > 0)
1010 * ensure no reference to alignFrame retained by menu item listener
1012 if (menuItem.getActionListeners().length > 0)
1014 menuItem.removeActionListener(menuItem.getActionListeners()[0]);
1016 Desktop.getInstance().windowMenu.remove(menuItem);
1020 menuItem.addActionListener(new ActionListener()
1023 public void actionPerformed(ActionEvent e)
1027 frame.setSelected(true);
1028 frame.setIcon(false);
1029 } catch (java.beans.PropertyVetoException ex)
1031 // System.err.println(ex.toString());
1036 setKeyBindings(frame);
1038 getDesktopPane().add(frame);
1040 Desktop.getInstance().windowMenu.add(menuItem);
1045 frame.setSelected(true);
1046 frame.requestFocus();
1047 } catch (java.beans.PropertyVetoException ve)
1049 } catch (java.lang.ClassCastException cex)
1052 "Squashed a possible GUI implementation error. If you can recreate this, please look at http://issues.jalview.org/browse/JAL-869",
1058 * Add key bindings to a JInternalFrame so that Ctrl-W and Cmd-W will close the
1063 private static void setKeyBindings(JInternalFrame frame)
1065 final Action closeAction = new AbstractAction()
1068 public void actionPerformed(ActionEvent e)
1075 * set up key bindings for Ctrl-W and Cmd-W, with the same (Close) action
1077 KeyStroke ctrlWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
1078 InputEvent.CTRL_DOWN_MASK);
1079 KeyStroke cmdWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
1080 Toolkit.getDefaultToolkit().getMenuShortcutKeyMask());
1082 InputMap inputMap = frame
1083 .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
1084 String ctrlW = ctrlWKey.toString();
1085 inputMap.put(ctrlWKey, ctrlW);
1086 inputMap.put(cmdWKey, ctrlW);
1088 ActionMap actionMap = frame.getActionMap();
1089 actionMap.put(ctrlW, closeAction);
1093 public void lostOwnership(Clipboard clipboard, Transferable contents)
1097 Desktop.getInstance().jalviewClipboard = null;
1100 internalCopy = false;
1104 public void dragEnter(DropTargetDragEvent evt)
1109 public void dragExit(DropTargetEvent evt)
1114 public void dragOver(DropTargetDragEvent evt)
1119 public void dropActionChanged(DropTargetDragEvent evt)
1130 public void drop(DropTargetDropEvent evt)
1132 boolean success = true;
1133 // JAL-1552 - acceptDrop required before getTransferable call for
1134 // Java's Transferable for native dnd
1135 evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
1136 Transferable t = evt.getTransferable();
1137 List<Object> files = new ArrayList<>();
1138 List<DataSourceType> protocols = new ArrayList<>();
1142 Desktop.transferFromDropTarget(files, protocols, evt, t);
1143 } catch (Exception e)
1145 e.printStackTrace();
1153 for (int i = 0; i < files.size(); i++)
1155 // BH 2018 File or String
1156 Object file = files.get(i);
1157 String fileName = file.toString();
1158 DataSourceType protocol = (protocols == null)
1159 ? DataSourceType.FILE
1161 FileFormatI format = null;
1163 if (fileName.endsWith(".jar"))
1165 format = FileFormat.Jalview;
1170 format = new IdentifyFile().identify(file, protocol);
1172 if (file instanceof File)
1174 Platform.cacheFileData((File) file);
1176 new FileLoader().loadFile(null, file, protocol, format);
1179 } catch (Exception ex)
1184 evt.dropComplete(success); // need this to ensure input focus is properly
1185 // transfered to any new windows created
1195 public void inputLocalFileMenuItem_actionPerformed(AlignViewport viewport)
1197 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
1198 JalviewFileChooser chooser = JalviewFileChooser
1199 .forRead(Cache.getProperty("LAST_DIRECTORY"), fileFormat, true);
1201 chooser.setFileView(new JalviewFileView());
1202 chooser.setDialogTitle(
1203 MessageManager.getString("label.open_local_file"));
1204 chooser.setToolTipText(MessageManager.getString("action.open"));
1206 chooser.setResponseHandler(0, new Runnable()
1211 File selectedFile = chooser.getSelectedFile();
1212 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1214 FileFormatI format = chooser.getSelectedFormat();
1217 * Call IdentifyFile to verify the file contains what its extension implies.
1218 * Skip this step for dynamically added file formats, because
1219 * IdentifyFile does not know how to recognise them.
1221 if (FileFormats.getInstance().isIdentifiable(format))
1225 format = new IdentifyFile().identify(selectedFile,
1226 DataSourceType.FILE);
1227 } catch (FileFormatException e)
1229 // format = null; //??
1233 new FileLoader().loadFile(viewport, selectedFile,
1234 DataSourceType.FILE, format);
1237 chooser.showOpenDialog(this);
1241 * Shows a dialog for input of a URL at which to retrieve alignment data
1246 public void inputURLMenuItem_actionPerformed(AlignViewport viewport)
1248 // This construct allows us to have a wider textfield
1250 JLabel label = new JLabel(
1251 MessageManager.getString("label.input_file_url"));
1253 JPanel panel = new JPanel(new GridLayout(2, 1));
1257 * the URL to fetch is
1258 * Java: an editable combobox with history
1259 * JS: (pending JAL-3038) a plain text field
1262 String urlBase = "http://www.";
1263 if (Platform.isJS())
1265 history = new JTextField(urlBase, 35);
1274 JComboBox<String> asCombo = new JComboBox<>();
1275 asCombo.setPreferredSize(new Dimension(400, 20));
1276 asCombo.setEditable(true);
1277 asCombo.addItem(urlBase);
1278 String historyItems = Cache.getProperty("RECENT_URL");
1279 if (historyItems != null)
1281 for (String token : historyItems.split("\\t"))
1283 asCombo.addItem(token);
1290 Object[] options = new Object[] { MessageManager.getString("action.ok"),
1291 MessageManager.getString("action.cancel") };
1292 Runnable action = new Runnable()
1297 @SuppressWarnings("unchecked")
1298 String url = (history instanceof JTextField
1299 ? ((JTextField) history).getText()
1300 : ((JComboBox<String>) history).getSelectedItem()
1303 if (url.toLowerCase().endsWith(".jar"))
1305 if (viewport != null)
1307 new FileLoader().loadFile(viewport, url, DataSourceType.URL,
1308 FileFormat.Jalview);
1312 new FileLoader().loadFile(url, DataSourceType.URL,
1313 FileFormat.Jalview);
1318 FileFormatI format = null;
1321 format = new IdentifyFile().identify(url, DataSourceType.URL);
1322 } catch (FileFormatException e)
1324 // TODO revise error handling, distinguish between
1325 // URL not found and response not valid
1330 String msg = MessageManager
1331 .formatMessage("label.couldnt_locate", url);
1332 JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
1334 MessageManager.getString("label.url_not_found"),
1335 JvOptionPane.WARNING_MESSAGE);
1340 if (viewport != null)
1342 new FileLoader().loadFile(viewport, url, DataSourceType.URL,
1347 new FileLoader().loadFile(url, DataSourceType.URL, format);
1352 String dialogOption = MessageManager
1353 .getString("label.input_alignment_from_url");
1354 JvOptionPane.newOptionDialog(getDesktopPane())
1355 .setResponseHandler(0, action)
1356 .showInternalDialog(panel, dialogOption,
1357 JvOptionPane.YES_NO_CANCEL_OPTION,
1358 JvOptionPane.PLAIN_MESSAGE, null, options,
1359 MessageManager.getString("action.ok"));
1363 * Opens the CutAndPaste window for the user to paste an alignment in to
1366 * - if not null, the pasted alignment is added to the current
1367 * alignment; if null, to a new alignment window
1370 public void inputTextboxMenuItem_actionPerformed(
1371 AlignmentViewPanel viewPanel)
1373 CutAndPasteTransfer cap = new CutAndPasteTransfer();
1374 cap.setForInput(viewPanel);
1375 Desktop.addInternalFrame(cap,
1376 MessageManager.getString("label.cut_paste_alignmen_file"), true,
1386 Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
1387 jalview.bin.Cache.setProperty("SCREENGEOMETRY_WIDTH",
1389 jalview.bin.Cache.setProperty("SCREENGEOMETRY_HEIGHT",
1390 screen.height + "");
1391 storeLastKnownDimensions("", new Rectangle(getBounds().x, getBounds().y,
1392 getWidth(), getHeight()));
1394 if (jconsole != null)
1396 storeLastKnownDimensions("JAVA_CONSOLE_", jconsole.getBounds());
1397 jconsole.stopConsole();
1401 storeLastKnownDimensions("JALVIEW_RSS_WINDOW_", jvnews.getBounds());
1404 if (dialogExecutor != null)
1406 dialogExecutor.shutdownNow();
1408 closeAll_actionPerformed(null);
1410 if (groovyConsole != null)
1412 // suppress a possible repeat prompt to save script
1413 groovyConsole.setDirty(false);
1414 groovyConsole.exit();
1419 private void storeLastKnownDimensions(String string, Rectangle jc)
1421 jalview.bin.Cache.log.debug("Storing last known dimensions for "
1422 + string + ": x:" + jc.x + " y:" + jc.y + " width:" + jc.width
1423 + " height:" + jc.height);
1425 jalview.bin.Cache.setProperty(string + "SCREEN_X", jc.x + "");
1426 jalview.bin.Cache.setProperty(string + "SCREEN_Y", jc.y + "");
1427 jalview.bin.Cache.setProperty(string + "SCREEN_WIDTH", jc.width + "");
1428 jalview.bin.Cache.setProperty(string + "SCREEN_HEIGHT", jc.height + "");
1438 public void aboutMenuItem_actionPerformed(ActionEvent e)
1440 // StringBuffer message = getAboutMessage(false);
1441 // JvOptionPane.showInternalMessageDialog(Desktop.getDesktop(),
1443 // message.toString(), "About Jalview", JvOptionPane.INFORMATION_MESSAGE);
1444 new Thread(new Runnable()
1449 new SplashScreen(true);
1454 public StringBuffer getAboutMessage(boolean shortv)
1456 StringBuffer message = new StringBuffer();
1457 message.append("<html>");
1460 message.append("<h1><strong>Version: "
1461 + jalview.bin.Cache.getProperty("VERSION")
1462 + "</strong></h1>");
1463 message.append("<strong>Last Updated: <em>"
1464 + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown")
1465 + "</em></strong>");
1471 message.append("<strong>Version "
1472 + jalview.bin.Cache.getProperty("VERSION")
1473 + "; last updated: "
1474 + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown"));
1477 if (jalview.bin.Cache.getDefault("LATEST_VERSION", "Checking")
1478 .equals("Checking"))
1480 message.append("<br>...Checking latest version...</br>");
1482 else if (!jalview.bin.Cache.getDefault("LATEST_VERSION", "Checking")
1483 .equals(jalview.bin.Cache.getProperty("VERSION")))
1485 boolean red = false;
1486 if (jalview.bin.Cache.getProperty("VERSION").toLowerCase()
1487 .indexOf("automated build") == -1)
1490 // Displayed when code version and jnlp version do not match and code
1491 // version is not a development build
1492 message.append("<div style=\"color: #FF0000;font-style: bold;\">");
1495 message.append("<br>!! Version "
1496 + jalview.bin.Cache.getDefault("LATEST_VERSION",
1498 + " is available for download from "
1499 + jalview.bin.Cache.getDefault("www.jalview.org",
1500 "http://www.jalview.org")
1504 message.append("</div>");
1507 message.append("<br>Authors: " + jalview.bin.Cache.getDefault(
1509 "The Jalview Authors (See AUTHORS file for current list)")
1510 + "<br><br>Development managed by The Barton Group, University of Dundee, Scotland, UK.<br>"
1511 + "<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"
1512 + "<br><br>If you use Jalview, please cite:"
1513 + "<br>Waterhouse, A.M., Procter, J.B., Martin, D.M.A, Clamp, M. and Barton, G. J. (2009)"
1514 + "<br>Jalview Version 2 - a multiple sequence alignment editor and analysis workbench"
1515 + "<br>Bioinformatics doi: 10.1093/bioinformatics/btp033"
1521 * Action on requesting Help documentation
1524 public void documentationMenuItem_actionPerformed()
1528 if (Platform.isJS())
1530 BrowserLauncher.openURL("http://www.jalview.org/help.html");
1539 Help.showHelpWindow();
1541 } catch (Exception ex)
1543 System.err.println("Error opening help: " + ex.getMessage());
1548 public void closeAll_actionPerformed(ActionEvent e)
1550 if (desktopPane == null)
1554 // TODO show a progress bar while closing?
1555 JInternalFrame[] frames = desktopPane.getAllFrames();
1556 for (int i = 0; i < frames.length; i++)
1560 frames[i].setClosed(true);
1561 } catch (java.beans.PropertyVetoException ex)
1565 Jalview.setCurrentAlignFrame(null);
1566 System.out.println("ALL CLOSED");
1567 if (v_client != null)
1569 // TODO clear binding to vamsas document objects on close_all
1573 * reset state of singleton objects as appropriate (clear down session state
1574 * when all windows are closed)
1576 getStructureSelectionManager().resetAll();
1580 public void raiseRelated_actionPerformed(ActionEvent e)
1582 reorderAssociatedWindows(false, false);
1586 public void minimizeAssociated_actionPerformed(ActionEvent e)
1588 reorderAssociatedWindows(true, false);
1591 void closeAssociatedWindows()
1593 reorderAssociatedWindows(false, true);
1599 * @seejalview.jbgui.GDesktop#garbageCollect_actionPerformed(java.awt.event.
1603 protected void garbageCollect_actionPerformed(ActionEvent e)
1605 // We simply collect the garbage
1606 jalview.bin.Cache.log.debug("Collecting garbage...");
1608 jalview.bin.Cache.log.debug("Finished garbage collection.");
1615 * jalview.jbgui.GDesktop#showMemusage_actionPerformed(java.awt.event.ActionEvent
1619 protected void showMemusage_actionPerformed(ActionEvent e)
1621 getDesktopPane().showMemoryUsage(showMemusage.isSelected());
1628 * jalview.jbgui.GDesktop#showConsole_actionPerformed(java.awt.event.ActionEvent
1632 protected void showConsole_actionPerformed(ActionEvent e)
1634 showConsole(showConsole.isSelected());
1637 Console jconsole = null;
1640 * control whether the java console is visible or not
1644 void showConsole(boolean selected)
1646 // TODO: decide if we should update properties file
1647 if (jconsole != null) // BH 2018
1649 showConsole.setSelected(selected);
1650 Cache.setProperty("SHOW_JAVA_CONSOLE",
1651 Boolean.valueOf(selected).toString());
1652 jconsole.setVisible(selected);
1656 void reorderAssociatedWindows(boolean minimize, boolean close)
1658 JInternalFrame[] frames = getDesktopPane().getAllFrames();
1659 if (frames == null || frames.length < 1)
1664 AlignmentViewport source = null, target = null;
1665 if (frames[0] instanceof AlignFrame)
1667 source = ((AlignFrame) frames[0]).getCurrentView();
1669 else if (frames[0] instanceof TreePanel)
1671 source = ((TreePanel) frames[0]).getViewPort();
1673 else if (frames[0] instanceof PCAPanel)
1675 source = ((PCAPanel) frames[0]).av;
1677 else if (frames[0].getContentPane() instanceof PairwiseAlignPanel)
1679 source = ((PairwiseAlignPanel) frames[0].getContentPane()).av;
1684 for (int i = 0; i < frames.length; i++)
1687 if (frames[i] == null)
1691 if (frames[i] instanceof AlignFrame)
1693 target = ((AlignFrame) frames[i]).getCurrentView();
1695 else if (frames[i] instanceof TreePanel)
1697 target = ((TreePanel) frames[i]).getViewPort();
1699 else if (frames[i] instanceof PCAPanel)
1701 target = ((PCAPanel) frames[i]).av;
1703 else if (frames[i].getContentPane() instanceof PairwiseAlignPanel)
1705 target = ((PairwiseAlignPanel) frames[i].getContentPane()).av;
1708 if (source == target)
1714 frames[i].setClosed(true);
1718 frames[i].setIcon(minimize);
1721 frames[i].toFront();
1725 } catch (java.beans.PropertyVetoException ex)
1740 protected void preferences_actionPerformed(ActionEvent e)
1746 * Prompts the user to choose a file and then saves the Jalview state as a
1747 * Jalview project file
1750 public void saveState_actionPerformed()
1752 saveState_actionPerformed(false);
1755 public void saveState_actionPerformed(boolean saveAs)
1757 java.io.File projectFile = getProjectFile();
1758 // autoSave indicates we already have a file and don't need to ask
1759 boolean autoSave = projectFile != null && !saveAs
1760 && BackupFiles.getEnabled();
1762 // System.out.println("autoSave="+autoSave+", projectFile='"+projectFile+"',
1763 // saveAs="+saveAs+", Backups
1764 // "+(BackupFiles.getEnabled()?"enabled":"disabled"));
1766 boolean approveSave = false;
1769 JalviewFileChooser chooser = new JalviewFileChooser("jvp",
1772 chooser.setFileView(new JalviewFileView());
1773 chooser.setDialogTitle(MessageManager.getString("label.save_state"));
1775 int value = chooser.showSaveDialog(this);
1777 if (value == JalviewFileChooser.APPROVE_OPTION)
1779 projectFile = chooser.getSelectedFile();
1780 setProjectFile(projectFile);
1785 if (approveSave || autoSave)
1787 final Desktop me = this;
1788 final java.io.File chosenFile = projectFile;
1789 new Thread(new Runnable()
1794 // TODO: refactor to Jalview desktop session controller action.
1795 setProgressBar(MessageManager.formatMessage(
1796 "label.saving_jalview_project", new Object[]
1797 { chosenFile.getName() }), chosenFile.hashCode());
1798 jalview.bin.Cache.setProperty("LAST_DIRECTORY",
1799 chosenFile.getParent());
1800 // TODO catch and handle errors for savestate
1801 // TODO prevent user from messing with the Desktop whilst we're saving
1804 boolean doBackup = BackupFiles.getEnabled();
1805 BackupFiles backupfiles = doBackup ? new BackupFiles(chosenFile) : null;
1807 new Jalview2XML().saveState(doBackup ? backupfiles.getTempFile() : chosenFile);
1811 backupfiles.setWriteSuccess(true);
1812 backupfiles.rollBackupsAndRenameTempFile();
1814 } catch (OutOfMemoryError oom)
1816 new OOMWarning("Whilst saving current state to "
1817 + chosenFile.getName(), oom);
1818 } catch (Exception ex)
1820 Cache.log.error("Problems whilst trying to save to "
1821 + chosenFile.getName(), ex);
1822 JvOptionPane.showMessageDialog(me,
1823 MessageManager.formatMessage(
1824 "label.error_whilst_saving_current_state_to",
1826 { chosenFile.getName() }),
1827 MessageManager.getString("label.couldnt_save_project"),
1828 JvOptionPane.WARNING_MESSAGE);
1830 setProgressBar(null, chosenFile.hashCode());
1837 public void saveAsState_actionPerformed(ActionEvent e)
1839 saveState_actionPerformed(true);
1842 protected void setProjectFile(File choice)
1844 this.projectFile = choice;
1847 public File getProjectFile()
1849 return this.projectFile;
1853 * Shows a file chooser dialog and tries to read in the selected file as a
1857 public void loadState_actionPerformed()
1859 final String[] suffix = new String[] { "jvp", "jar" };
1860 final String[] desc = new String[] { "Jalview Project",
1861 "Jalview Project (old)" };
1862 JalviewFileChooser chooser = new JalviewFileChooser(
1863 Cache.getProperty("LAST_DIRECTORY"), suffix, desc,
1864 "Jalview Project", true, true); // last two booleans: allFiles,
1866 chooser.setFileView(new JalviewFileView());
1867 chooser.setDialogTitle(MessageManager.getString("label.restore_state"));
1868 chooser.setResponseHandler(0, new Runnable()
1873 File selectedFile = chooser.getSelectedFile();
1874 setProjectFile(selectedFile);
1875 String choice = selectedFile.getAbsolutePath();
1876 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1877 new Thread(new Runnable()
1884 new Jalview2XML().loadJalviewAlign(choice);
1885 } catch (OutOfMemoryError oom)
1887 new OOMWarning("Whilst loading project from " + choice, oom);
1888 } catch (Exception ex)
1891 "Problems whilst loading project from " + choice, ex);
1892 JvOptionPane.showMessageDialog(Desktop.getDesktopPane(),
1893 MessageManager.formatMessage(
1894 "label.error_whilst_loading_project_from",
1897 MessageManager.getString("label.couldnt_load_project"),
1898 JvOptionPane.WARNING_MESSAGE);
1905 chooser.showOpenDialog(this);
1909 public void inputSequence_actionPerformed(ActionEvent e)
1911 new SequenceFetcher(this);
1914 JPanel progressPanel;
1916 ArrayList<JPanel> fileLoadingPanels = new ArrayList<>();
1918 public void startLoading(final Object fileName)
1920 if (fileLoadingCount == 0)
1922 fileLoadingPanels.add(addProgressPanel(MessageManager
1923 .formatMessage("label.loading_file", new Object[]
1929 private JPanel addProgressPanel(String string)
1931 if (progressPanel == null)
1933 progressPanel = new JPanel(new GridLayout(1, 1));
1934 totalProgressCount = 0;
1935 getContentPane().add(progressPanel, BorderLayout.SOUTH);
1937 JPanel thisprogress = new JPanel(new BorderLayout(10, 5));
1938 JProgressBar progressBar = new JProgressBar();
1939 progressBar.setIndeterminate(true);
1941 thisprogress.add(new JLabel(string), BorderLayout.WEST);
1943 thisprogress.add(progressBar, BorderLayout.CENTER);
1944 progressPanel.add(thisprogress);
1945 ((GridLayout) progressPanel.getLayout()).setRows(
1946 ((GridLayout) progressPanel.getLayout()).getRows() + 1);
1947 ++totalProgressCount;
1949 return thisprogress;
1952 int totalProgressCount = 0;
1954 private void removeProgressPanel(JPanel progbar)
1956 if (progressPanel != null)
1958 synchronized (progressPanel)
1960 progressPanel.remove(progbar);
1961 GridLayout gl = (GridLayout) progressPanel.getLayout();
1962 gl.setRows(gl.getRows() - 1);
1963 if (--totalProgressCount < 1)
1965 this.getContentPane().remove(progressPanel);
1966 progressPanel = null;
1973 public void stopLoading()
1976 if (fileLoadingCount < 1)
1978 while (fileLoadingPanels.size() > 0)
1980 removeProgressPanel(fileLoadingPanels.remove(0));
1982 fileLoadingPanels.clear();
1983 fileLoadingCount = 0;
1988 public static int getViewCount(String alignmentId)
1990 AlignmentViewport[] aps = getViewports(alignmentId);
1991 return (aps == null) ? 0 : aps.length;
1996 * @param alignmentId
1997 * - if null, all sets are returned
1998 * @return all AlignmentPanels concerning the alignmentId sequence set
2000 public static AlignmentPanel[] getAlignmentPanels(String alignmentId)
2002 if (Desktop.getDesktopPane() == null)
2004 // no frames created and in headless mode
2005 // TODO: verify that frames are recoverable when in headless mode
2008 List<AlignmentPanel> aps = new ArrayList<>();
2009 AlignFrame[] frames = getAlignFrames();
2014 for (AlignFrame af : frames)
2016 for (AlignmentPanel ap : af.alignPanels)
2018 if (alignmentId == null
2019 || alignmentId.equals(ap.av.getSequenceSetId()))
2025 if (aps.size() == 0)
2029 AlignmentPanel[] vap = aps.toArray(new AlignmentPanel[aps.size()]);
2034 * get all the viewports on an alignment.
2036 * @param sequenceSetId
2037 * unique alignment id (may be null - all viewports returned in that
2039 * @return all viewports on the alignment bound to sequenceSetId
2041 public static AlignmentViewport[] getViewports(String sequenceSetId)
2043 List<AlignmentViewport> viewp = new ArrayList<>();
2044 if (getDesktopPane() != null)
2046 AlignFrame[] frames = Desktop.getAlignFrames();
2048 for (AlignFrame afr : frames)
2050 if (sequenceSetId == null || afr.getViewport().getSequenceSetId()
2051 .equals(sequenceSetId))
2053 if (afr.alignPanels != null)
2055 for (AlignmentPanel ap : afr.alignPanels)
2057 if (sequenceSetId == null
2058 || sequenceSetId.equals(ap.av.getSequenceSetId()))
2066 viewp.add(afr.getViewport());
2070 if (viewp.size() > 0)
2072 return viewp.toArray(new AlignmentViewport[viewp.size()]);
2079 * Explode the views in the given frame into separate AlignFrame
2083 public static void explodeViews(AlignFrame af)
2085 int size = af.alignPanels.size();
2091 for (int i = 0; i < size; i++)
2093 AlignmentPanel ap = af.alignPanels.get(i);
2094 AlignFrame newaf = new AlignFrame(ap);
2097 * Restore the view's last exploded frame geometry if known. Multiple
2098 * views from one exploded frame share and restore the same (frame)
2099 * position and size.
2101 Rectangle geometry = ap.av.getExplodedGeometry();
2102 if (geometry != null)
2104 newaf.setBounds(geometry);
2107 ap.av.setGatherViewsHere(false);
2109 addInternalFrame(newaf, af.getTitle(), AlignFrame.DEFAULT_WIDTH,
2110 AlignFrame.DEFAULT_HEIGHT);
2113 af.alignPanels.clear();
2114 af.closeMenuItem_actionPerformed(true);
2119 * Gather expanded views (separate AlignFrame's) with the same sequence set
2120 * identifier back in to this frame as additional views, and close the expanded
2121 * views. Note the expanded frames may themselves have multiple views. We take
2126 public void gatherViews(AlignFrame source)
2128 source.viewport.setGatherViewsHere(true);
2129 source.viewport.setExplodedGeometry(source.getBounds());
2130 JInternalFrame[] frames = getAllFrames();
2131 String viewId = source.viewport.getSequenceSetId();
2133 for (int t = 0; t < frames.length; t++)
2135 if (frames[t] instanceof AlignFrame && frames[t] != source)
2137 AlignFrame af = (AlignFrame) frames[t];
2138 boolean gatherThis = false;
2139 for (int a = 0; a < af.alignPanels.size(); a++)
2141 AlignmentPanel ap = af.alignPanels.get(a);
2142 if (viewId.equals(ap.av.getSequenceSetId()))
2145 ap.av.setGatherViewsHere(false);
2146 ap.av.setExplodedGeometry(af.getBounds());
2147 source.addAlignmentPanel(ap, false);
2153 af.alignPanels.clear();
2154 af.closeMenuItem_actionPerformed(true);
2161 jalview.gui.VamsasApplication v_client = null;
2164 public void vamsasImport_actionPerformed(ActionEvent e)
2166 // TODO: JAL-3048 not needed for Jalview-JS
2168 if (v_client == null)
2170 // Load and try to start a session.
2171 JalviewFileChooser chooser = new JalviewFileChooser(
2172 jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
2174 chooser.setFileView(new JalviewFileView());
2175 chooser.setDialogTitle(
2176 MessageManager.getString("label.open_saved_vamsas_session"));
2177 chooser.setToolTipText(MessageManager.getString(
2178 "label.select_vamsas_session_opened_as_new_vamsas_session"));
2180 int value = chooser.showOpenDialog(this);
2182 if (value == JalviewFileChooser.APPROVE_OPTION)
2184 String fle = chooser.getSelectedFile().toString();
2185 if (!vamsasImport(chooser.getSelectedFile()))
2187 JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
2188 MessageManager.formatMessage(
2189 "label.couldnt_import_as_vamsas_session",
2193 .getString("label.vamsas_document_import_failed"),
2194 JvOptionPane.ERROR_MESSAGE);
2200 jalview.bin.Cache.log.error(
2201 "Implementation error - load session from a running session is not supported.");
2206 * import file into a new vamsas session (uses jalview.gui.VamsasApplication)
2209 * @return true if import was a success and a session was started.
2211 public boolean vamsasImport(URL url)
2213 // TODO: create progress bar
2214 if (v_client != null)
2217 jalview.bin.Cache.log.error(
2218 "Implementation error - load session from a running session is not supported.");
2224 // copy the URL content to a temporary local file
2225 // TODO: be a bit cleverer here with nio (?!)
2226 File file = File.createTempFile("vdocfromurl", ".vdj");
2227 FileOutputStream fos = new FileOutputStream(file);
2228 BufferedInputStream bis = new BufferedInputStream(url.openStream());
2229 byte[] buffer = new byte[2048];
2231 while ((ln = bis.read(buffer)) > -1)
2233 fos.write(buffer, 0, ln);
2237 v_client = new jalview.gui.VamsasApplication(this, file,
2238 url.toExternalForm());
2239 } catch (Exception ex)
2241 jalview.bin.Cache.log.error(
2242 "Failed to create new vamsas session from contents of URL "
2247 setupVamsasConnectedGui();
2248 v_client.initial_update(); // TODO: thread ?
2249 return v_client.inSession();
2253 * import file into a new vamsas session (uses jalview.gui.VamsasApplication)
2256 * @return true if import was a success and a session was started.
2258 public boolean vamsasImport(File file)
2260 if (v_client != null)
2263 jalview.bin.Cache.log.error(
2264 "Implementation error - load session from a running session is not supported.");
2268 setProgressBar(MessageManager.formatMessage(
2269 "status.importing_vamsas_session_from", new Object[]
2270 { file.getName() }), file.hashCode());
2273 v_client = new jalview.gui.VamsasApplication(this, file, null);
2274 } catch (Exception ex)
2276 setProgressBar(MessageManager.formatMessage(
2277 "status.importing_vamsas_session_from", new Object[]
2278 { file.getName() }), file.hashCode());
2279 jalview.bin.Cache.log.error(
2280 "New vamsas session from existing session file failed:", ex);
2283 setupVamsasConnectedGui();
2284 v_client.initial_update(); // TODO: thread ?
2285 setProgressBar(MessageManager.formatMessage(
2286 "status.importing_vamsas_session_from", new Object[]
2287 { file.getName() }), file.hashCode());
2288 return v_client.inSession();
2291 public boolean joinVamsasSession(String mysesid)
2293 if (v_client != null)
2295 throw new Error(MessageManager
2296 .getString("error.try_join_vamsas_session_another"));
2298 if (mysesid == null)
2301 MessageManager.getString("error.invalid_vamsas_session_id"));
2303 v_client = new VamsasApplication(this, mysesid);
2304 setupVamsasConnectedGui();
2305 v_client.initial_update();
2306 return (v_client.inSession());
2310 public void vamsasStart_actionPerformed(ActionEvent e)
2312 if (v_client == null)
2315 // we just start a default session for moment.
2317 * JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache.
2318 * getProperty("LAST_DIRECTORY"));
2320 * chooser.setFileView(new JalviewFileView());
2321 * chooser.setDialogTitle("Load Vamsas file");
2322 * chooser.setToolTipText("Import");
2324 * int value = chooser.showOpenDialog(this);
2326 * if (value == JalviewFileChooser.APPROVE_OPTION) { v_client = new
2327 * jalview.gui.VamsasApplication(this, chooser.getSelectedFile());
2329 v_client = new VamsasApplication(this);
2330 setupVamsasConnectedGui();
2331 v_client.initial_update(); // TODO: thread ?
2335 // store current data in session.
2336 v_client.push_update(); // TODO: thread
2340 protected void setupVamsasConnectedGui()
2342 vamsasStart.setText(MessageManager.getString("label.session_update"));
2343 vamsasSave.setVisible(true);
2344 vamsasStop.setVisible(true);
2345 vamsasImport.setVisible(false); // Document import to existing session is
2346 // not possible for vamsas-client-1.0.
2349 protected void setupVamsasDisconnectedGui()
2351 vamsasSave.setVisible(false);
2352 vamsasStop.setVisible(false);
2353 vamsasImport.setVisible(true);
2355 .setText(MessageManager.getString("label.new_vamsas_session"));
2359 public void vamsasStop_actionPerformed(ActionEvent e)
2361 if (v_client != null)
2363 v_client.end_session();
2365 setupVamsasDisconnectedGui();
2369 protected void buildVamsasStMenu()
2371 if (v_client == null)
2373 String[] sess = null;
2376 sess = VamsasApplication.getSessionList();
2377 } catch (Exception e)
2379 jalview.bin.Cache.log.warn("Problem getting current sessions list.",
2385 jalview.bin.Cache.log.debug(
2386 "Got current sessions list: " + sess.length + " entries.");
2387 VamsasStMenu.removeAll();
2388 for (int i = 0; i < sess.length; i++)
2390 JMenuItem sessit = new JMenuItem();
2391 sessit.setText(sess[i]);
2392 sessit.setToolTipText(MessageManager
2393 .formatMessage("label.connect_to_session", new Object[]
2395 final Desktop dsktp = this;
2396 final String mysesid = sess[i];
2397 sessit.addActionListener(new ActionListener()
2401 public void actionPerformed(ActionEvent e)
2403 if (dsktp.v_client == null)
2405 Thread rthr = new Thread(new Runnable()
2411 dsktp.v_client = new VamsasApplication(dsktp, mysesid);
2412 dsktp.setupVamsasConnectedGui();
2413 dsktp.v_client.initial_update();
2421 VamsasStMenu.add(sessit);
2423 // don't show an empty menu.
2424 VamsasStMenu.setVisible(sess.length > 0);
2429 jalview.bin.Cache.log.debug("No current vamsas sessions.");
2430 VamsasStMenu.removeAll();
2431 VamsasStMenu.setVisible(false);
2436 // Not interested in the content. Just hide ourselves.
2437 VamsasStMenu.setVisible(false);
2442 public void vamsasSave_actionPerformed(ActionEvent e)
2444 // TODO: JAL-3048 not needed for Jalview-JS
2446 if (v_client != null)
2448 // TODO: VAMSAS DOCUMENT EXTENSION is VDJ
2449 JalviewFileChooser chooser = new JalviewFileChooser("vdj",
2452 chooser.setFileView(new JalviewFileView());
2453 chooser.setDialogTitle(MessageManager
2454 .getString("label.save_vamsas_document_archive"));
2456 int value = chooser.showSaveDialog(this);
2458 if (value == JalviewFileChooser.APPROVE_OPTION)
2460 java.io.File choice = chooser.getSelectedFile();
2461 JPanel progpanel = addProgressPanel(MessageManager
2462 .formatMessage("label.saving_vamsas_doc", new Object[]
2463 { choice.getName() }));
2464 Cache.setProperty("LAST_DIRECTORY", choice.getParent());
2465 String warnmsg = null;
2466 String warnttl = null;
2469 v_client.vclient.storeDocument(choice);
2472 warnttl = "Serious Problem saving Vamsas Document";
2473 warnmsg = ex.toString();
2474 jalview.bin.Cache.log
2475 .error("Error Whilst saving document to " + choice, ex);
2477 } catch (Exception ex)
2479 warnttl = "Problem saving Vamsas Document.";
2480 warnmsg = ex.toString();
2481 jalview.bin.Cache.log.warn(
2482 "Exception Whilst saving document to " + choice, ex);
2485 removeProgressPanel(progpanel);
2486 if (warnmsg != null)
2488 JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
2490 warnmsg, warnttl, JvOptionPane.ERROR_MESSAGE);
2496 JPanel vamUpdate = null;
2499 * hide vamsas user gui bits when a vamsas document event is being handled.
2502 * true to hide gui, false to reveal gui
2504 public void setVamsasUpdate(boolean b)
2506 Cache.log.debug("Setting gui for Vamsas update "
2507 + (b ? "in progress" : "finished"));
2509 if (vamUpdate != null)
2511 this.removeProgressPanel(vamUpdate);
2515 vamUpdate = this.addProgressPanel(
2516 MessageManager.getString("label.updating_vamsas_session"));
2518 vamsasStart.setVisible(!b);
2519 vamsasStop.setVisible(!b);
2520 vamsasSave.setVisible(!b);
2523 public JInternalFrame[] getAllFrames()
2525 return desktopPane.getAllFrames();
2529 * Checks the given url to see if it gives a response indicating that the user
2530 * should be informed of a new questionnaire.
2534 public void checkForQuestionnaire(String url)
2536 UserQuestionnaireCheck jvq = new UserQuestionnaireCheck(url);
2537 // javax.swing.SwingUtilities.invokeLater(jvq);
2538 new Thread(jvq).start();
2541 public void checkURLLinks()
2543 // Thread off the URL link checker
2544 addDialogThread(new Runnable()
2549 if (Cache.getDefault("CHECKURLLINKS", true))
2551 // check what the actual links are - if it's just the default don't
2552 // bother with the warning
2553 List<String> links = Preferences.sequenceUrlLinks
2556 // only need to check links if there is one with a
2557 // SEQUENCE_ID which is not the default EMBL_EBI link
2558 ListIterator<String> li = links.listIterator();
2559 boolean check = false;
2560 List<JLabel> urls = new ArrayList<>();
2561 while (li.hasNext())
2563 String link = li.next();
2564 if (link.contains(UrlConstants.SEQUENCE_ID)
2565 && !UrlConstants.isDefaultString(link))
2568 int barPos = link.indexOf("|");
2569 String urlMsg = barPos == -1 ? link
2570 : link.substring(0, barPos) + ": "
2571 + link.substring(barPos + 1);
2572 urls.add(new JLabel(urlMsg));
2580 // ask user to check in case URL links use old style tokens
2581 // ($SEQUENCE_ID$ for sequence id _or_ accession id)
2582 JPanel msgPanel = new JPanel();
2583 msgPanel.setLayout(new BoxLayout(msgPanel, BoxLayout.PAGE_AXIS));
2584 msgPanel.add(Box.createVerticalGlue());
2585 JLabel msg = new JLabel(MessageManager
2586 .getString("label.SEQUENCE_ID_for_DB_ACCESSION1"));
2587 JLabel msg2 = new JLabel(MessageManager
2588 .getString("label.SEQUENCE_ID_for_DB_ACCESSION2"));
2590 for (JLabel url : urls)
2596 final JCheckBox jcb = new JCheckBox(
2597 MessageManager.getString("label.do_not_display_again"));
2598 jcb.addActionListener(new ActionListener()
2601 public void actionPerformed(ActionEvent e)
2603 // update Cache settings for "don't show this again"
2604 boolean showWarningAgain = !jcb.isSelected();
2605 Cache.setProperty("CHECKURLLINKS",
2606 Boolean.valueOf(showWarningAgain).toString());
2611 JvOptionPane.showMessageDialog(desktopPane, msgPanel,
2613 .getString("label.SEQUENCE_ID_no_longer_used"),
2614 JvOptionPane.WARNING_MESSAGE);
2621 * Proxy class for JDesktopPane which optionally displays the current memory
2622 * usage and highlights the desktop area with a red bar if free memory runs low.
2626 public class MyDesktopPane extends JDesktopPane
2629 private static final float ONE_MB = 1048576f;
2631 boolean showMemoryUsage = false;
2635 java.text.NumberFormat df;
2637 float maxMemory, allocatedMemory, freeMemory, totalFreeMemory,
2640 public MyDesktopPane(boolean showMemoryUsage)
2642 showMemoryUsage(showMemoryUsage);
2645 public void showMemoryUsage(boolean showMemory)
2647 this.showMemoryUsage = showMemory;
2650 Thread worker = new Thread(this);
2656 public boolean isShowMemoryUsage()
2658 return showMemoryUsage;
2664 df = java.text.NumberFormat.getNumberInstance();
2665 df.setMaximumFractionDigits(2);
2666 runtime = Runtime.getRuntime();
2668 while (showMemoryUsage)
2672 maxMemory = runtime.maxMemory() / ONE_MB;
2673 allocatedMemory = runtime.totalMemory() / ONE_MB;
2674 freeMemory = runtime.freeMemory() / ONE_MB;
2675 totalFreeMemory = freeMemory + (maxMemory - allocatedMemory);
2677 percentUsage = (totalFreeMemory / maxMemory) * 100;
2679 // if (percentUsage < 20)
2681 // border1 = BorderFactory.createMatteBorder(12, 12, 12, 12,
2683 // instance.set.setBorder(border1);
2686 // sleep after showing usage
2688 } catch (Exception ex)
2690 ex.printStackTrace();
2696 public void paintComponent(Graphics g)
2698 if (showMemoryUsage && g != null && df != null)
2700 if (percentUsage < 20)
2702 g.setColor(Color.red);
2704 FontMetrics fm = g.getFontMetrics();
2707 g.drawString(MessageManager.formatMessage("label.memory_stats",
2709 { df.format(totalFreeMemory), df.format(maxMemory),
2710 df.format(percentUsage) }),
2711 10, getHeight() - fm.getHeight());
2718 * Accessor method to quickly get all the AlignmentFrames loaded.
2720 * @return an array of AlignFrame, or null if none found
2722 public static AlignFrame[] getAlignFrames()
2724 if (Jalview.isHeadlessMode())
2726 // Desktop.getDesktop() is null in headless mode
2727 return new AlignFrame[] { Jalview.getCurrentAlignFrame() };
2730 JInternalFrame[] frames = Desktop.getDesktopPane().getAllFrames();
2736 List<AlignFrame> avp = new ArrayList<>();
2738 for (int i = frames.length - 1; i > -1; i--)
2740 if (frames[i] instanceof AlignFrame)
2742 avp.add((AlignFrame) frames[i]);
2744 else if (frames[i] instanceof SplitFrame)
2747 * Also check for a split frame containing an AlignFrame
2749 GSplitFrame sf = (GSplitFrame) frames[i];
2750 if (sf.getTopFrame() instanceof AlignFrame)
2752 avp.add((AlignFrame) sf.getTopFrame());
2754 if (sf.getBottomFrame() instanceof AlignFrame)
2756 avp.add((AlignFrame) sf.getBottomFrame());
2760 if (avp.size() == 0)
2764 AlignFrame afs[] = avp.toArray(new AlignFrame[avp.size()]);
2769 * Returns an array of any AppJmol frames in the Desktop (or null if none).
2773 public GStructureViewer[] getJmols()
2775 JInternalFrame[] frames = Desktop.getDesktopPane().getAllFrames();
2781 List<GStructureViewer> avp = new ArrayList<>();
2783 for (int i = frames.length - 1; i > -1; i--)
2785 if (frames[i] instanceof AppJmol)
2787 GStructureViewer af = (GStructureViewer) frames[i];
2791 if (avp.size() == 0)
2795 GStructureViewer afs[] = avp.toArray(new GStructureViewer[avp.size()]);
2800 * Add Groovy Support to Jalview
2803 public void groovyShell_actionPerformed()
2807 openGroovyConsole();
2808 } catch (Exception ex)
2810 jalview.bin.Cache.log.error("Groovy Shell Creation failed.", ex);
2811 JvOptionPane.showInternalMessageDialog(desktopPane,
2813 MessageManager.getString("label.couldnt_create_groovy_shell"),
2814 MessageManager.getString("label.groovy_support_failed"),
2815 JvOptionPane.ERROR_MESSAGE);
2820 * Open the Groovy console
2822 private void openGroovyConsole()
2824 if (groovyConsole == null)
2826 groovyConsole = new groovy.ui.Console();
2827 groovyConsole.setVariable("Jalview", this);
2828 groovyConsole.run();
2831 * We allow only one console at a time, so that AlignFrame menu option
2832 * 'Calculate | Run Groovy script' is unambiguous.
2833 * Disable 'Groovy Console', and enable 'Run script', when the console is
2834 * opened, and the reverse when it is closed
2836 Window window = (Window) groovyConsole.getFrame();
2837 window.addWindowListener(new WindowAdapter()
2840 public void windowClosed(WindowEvent e)
2843 * rebind CMD-Q from Groovy Console to Jalview Quit
2846 enableExecuteGroovy(false);
2852 * show Groovy console window (after close and reopen)
2854 ((Window) groovyConsole.getFrame()).setVisible(true);
2857 * if we got this far, enable 'Run Groovy' in AlignFrame menus
2858 * and disable opening a second console
2860 enableExecuteGroovy(true);
2864 * Bind Ctrl/Cmd-Q to Quit - for reset as Groovy Console takes over this binding
2867 protected void addQuitHandler()
2869 getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
2870 .put(KeyStroke.getKeyStroke(KeyEvent.VK_Q,
2871 Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()),
2873 getRootPane().getActionMap().put("Quit", new AbstractAction()
2876 public void actionPerformed(ActionEvent e)
2884 * Enable or disable 'Run Groovy script' in AlignFrame calculate menus
2887 * true if Groovy console is open
2889 public void enableExecuteGroovy(boolean enabled)
2892 * disable opening a second Groovy console
2893 * (or re-enable when the console is closed)
2895 groovyShell.setEnabled(!enabled);
2897 AlignFrame[] alignFrames = getAlignFrames();
2898 if (alignFrames != null)
2900 for (AlignFrame af : alignFrames)
2902 af.setGroovyEnabled(enabled);
2908 * Progress bars managed by the IProgressIndicator method.
2910 private Hashtable<Long, JPanel> progressBars;
2912 private Hashtable<Long, IProgressIndicatorHandler> progressBarHandlers;
2917 * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
2920 public void setProgressBar(String message, long id)
2922 Platform.timeCheck("Desktop " + message, Platform.TIME_MARK);
2924 if (progressBars == null)
2926 progressBars = new Hashtable<>();
2927 progressBarHandlers = new Hashtable<>();
2930 if (progressBars.get(new Long(id)) != null)
2932 JPanel panel = progressBars.remove(new Long(id));
2933 if (progressBarHandlers.contains(new Long(id)))
2935 progressBarHandlers.remove(new Long(id));
2937 removeProgressPanel(panel);
2941 progressBars.put(new Long(id), addProgressPanel(message));
2948 * @see jalview.gui.IProgressIndicator#registerHandler(long,
2949 * jalview.gui.IProgressIndicatorHandler)
2952 public void registerHandler(final long id,
2953 final IProgressIndicatorHandler handler)
2955 if (progressBarHandlers == null
2956 || !progressBars.containsKey(new Long(id)))
2958 throw new Error(MessageManager.getString(
2959 "error.call_setprogressbar_before_registering_handler"));
2961 progressBarHandlers.put(new Long(id), handler);
2962 final JPanel progressPanel = progressBars.get(new Long(id));
2963 if (handler.canCancel())
2965 JButton cancel = new JButton(
2966 MessageManager.getString("action.cancel"));
2967 final IProgressIndicator us = this;
2968 cancel.addActionListener(new ActionListener()
2972 public void actionPerformed(ActionEvent e)
2974 handler.cancelActivity(id);
2975 us.setProgressBar(MessageManager
2976 .formatMessage("label.cancelled_params", new Object[]
2977 { ((JLabel) progressPanel.getComponent(0)).getText() }),
2981 progressPanel.add(cancel, BorderLayout.EAST);
2987 * @return true if any progress bars are still active
2990 public boolean operationInProgress()
2992 if (progressBars != null && progressBars.size() > 0)
3000 * This will return the first AlignFrame holding the given viewport instance. It
3001 * will break if there are more than one AlignFrames viewing a particular av.
3004 * @return alignFrame for viewport
3006 public static AlignFrame getAlignFrameFor(AlignViewportI viewport)
3008 if (getDesktopPane() != null)
3010 AlignmentPanel[] aps = getAlignmentPanels(
3011 viewport.getSequenceSetId());
3012 for (int panel = 0; aps != null && panel < aps.length; panel++)
3014 if (aps[panel] != null && aps[panel].av == viewport)
3016 return aps[panel].alignFrame;
3023 public VamsasApplication getVamsasApplication()
3030 * flag set if jalview GUI is being operated programmatically
3032 private boolean inBatchMode = false;
3035 * check if jalview GUI is being operated programmatically
3037 * @return inBatchMode
3039 public boolean isInBatchMode()
3045 * set flag if jalview GUI is being operated programmatically
3047 * @param inBatchMode
3049 public void setInBatchMode(boolean inBatchMode)
3051 this.inBatchMode = inBatchMode;
3054 public void startServiceDiscovery()
3056 startServiceDiscovery(false);
3059 public void startServiceDiscovery(boolean blocking)
3061 boolean alive = true;
3062 Thread t0 = null, t1 = null, t2 = null;
3063 // JAL-940 - JALVIEW 1 services are now being EOLed as of JABA 2.1 release
3066 // todo: changesupport handlers need to be transferred
3067 if (discoverer == null)
3069 discoverer = Discoverer.getInstance();
3070 // register PCS handler for getDesktop().
3071 discoverer.addPropertyChangeListener(changeSupport);
3073 // JAL-940 - disabled JWS1 service configuration - always start discoverer
3074 // until we phase out completely
3075 (t0 = new Thread(discoverer)).start();
3078 if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
3080 t2 = jalview.ws.jws2.Jws2Discoverer.getInstance()
3081 .startDiscoverer(changeSupport);
3085 // TODO: do rest service discovery
3094 } catch (Exception e)
3097 alive = (t1 != null && t1.isAlive()) || (t2 != null && t2.isAlive())
3098 || (t3 != null && t3.isAlive())
3099 || (t0 != null && t0.isAlive());
3105 * called to check if the service discovery process completed successfully.
3109 protected void JalviewServicesChanged(PropertyChangeEvent evt)
3111 if (evt.getNewValue() == null || evt.getNewValue() instanceof Vector)
3113 final String ermsg = jalview.ws.jws2.Jws2Discoverer.getInstance()
3114 .getErrorMessages();
3117 if (Cache.getDefault("SHOW_WSDISCOVERY_ERRORS", true))
3119 if (serviceChangedDialog == null)
3121 // only run if we aren't already displaying one of these.
3122 addDialogThread(serviceChangedDialog = new Runnable()
3129 * JalviewDialog jd =new JalviewDialog() {
3131 * @Override protected void cancelPressed() { // TODO
3132 * Auto-generated method stub
3134 * }@Override protected void okPressed() { // TODO
3135 * Auto-generated method stub
3137 * }@Override protected void raiseClosed() { // TODO
3138 * Auto-generated method stub
3140 * } }; jd.initDialogFrame(new
3141 * JLabel("<html><table width=\"450\"><tr><td>" + ermsg +
3142 * "<br/>It may be that you have invalid JABA URLs in your web service preferences,"
3143 * + " or mis-configured HTTP proxy settings.<br/>" +
3144 * "Check the <em>Connections</em> and <em>Web services</em> tab of the"
3146 * " Tools->Preferences dialog box to change them.</td></tr></table></html>"
3147 * ), true, true, "Web Service Configuration Problem", 450,
3150 * jd.waitForInput();
3152 JvOptionPane.showConfirmDialog(Desktop.getDesktopPane(),
3153 new JLabel("<html><table width=\"450\"><tr><td>"
3154 + ermsg + "</td></tr></table>"
3155 + "<p>It may be that you have invalid JABA URLs<br/>in your web service preferences,"
3156 + "<br>or as a command-line argument, or mis-configured HTTP proxy settings.</p>"
3157 + "<p>Check the <em>Connections</em> and <em>Web services</em> tab<br/>of the"
3158 + " Tools->Preferences dialog box to change them.</p></html>"),
3159 "Web Service Configuration Problem",
3160 JvOptionPane.DEFAULT_OPTION,
3161 JvOptionPane.ERROR_MESSAGE);
3162 serviceChangedDialog = null;
3171 "Errors reported by JABA discovery service. Check web services preferences.\n"
3178 Runnable serviceChangedDialog = null;
3181 * start a thread to open a URL in the configured browser. Pops up a warning
3182 * dialog to the user if there is an exception when calling out to the browser
3187 public static void showUrl(final String url)
3189 showUrl(url, Desktop.getInstance());
3193 * Like showUrl but allows progress handler to be specified
3197 * (null) or object implementing IProgressIndicator
3199 public static void showUrl(final String url,
3200 final IProgressIndicator progress)
3202 new Thread(new Runnable()
3209 if (progress != null)
3211 progress.setProgressBar(MessageManager
3212 .formatMessage("status.opening_params", new Object[]
3213 { url }), this.hashCode());
3215 BrowserLauncher.openURL(url);
3216 } catch (Exception ex)
3218 JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
3220 .getString("label.web_browser_not_found_unix"),
3221 MessageManager.getString("label.web_browser_not_found"),
3222 JvOptionPane.WARNING_MESSAGE);
3224 ex.printStackTrace();
3226 if (progress != null)
3228 progress.setProgressBar(null, this.hashCode());
3234 private WsParamSetManager wsparamManager = null;
3236 public static ParamManager getUserParameterStore()
3238 Desktop d = Desktop.getInstance();
3239 if (d.wsparamManager == null)
3241 d.wsparamManager = new WsParamSetManager();
3243 return d.wsparamManager;
3247 * static hyperlink handler proxy method for use by Jalview's internal windows
3251 public static void hyperlinkUpdate(HyperlinkEvent e)
3253 if (e.getEventType() == EventType.ACTIVATED)
3258 url = e.getURL().toString();
3259 Desktop.showUrl(url);
3260 } catch (Exception x)
3264 if (Cache.log != null)
3266 Cache.log.error("Couldn't handle string " + url + " as a URL.");
3271 "Couldn't handle string " + url + " as a URL.");
3274 // ignore any exceptions due to dud links.
3281 * single thread that handles display of dialogs to user.
3283 ExecutorService dialogExecutor = Executors.newSingleThreadExecutor();
3286 * flag indicating if dialogExecutor should try to acquire a permit
3288 volatile boolean dialogPause = true;
3293 java.util.concurrent.Semaphore block = new Semaphore(0);
3295 private groovy.ui.Console groovyConsole;
3297 public StructureViewer lastTargetedView;
3300 * add another dialog thread to the queue
3304 public void addDialogThread(final Runnable prompter)
3306 dialogExecutor.submit(new Runnable()
3316 } catch (InterruptedException x)
3326 SwingUtilities.invokeAndWait(prompter);
3327 } catch (Exception q)
3329 Cache.log.warn("Unexpected Exception in dialog thread.", q);
3335 public void startDialogQueue()
3337 // set the flag so we don't pause waiting for another permit and semaphore
3338 // the current task to begin
3339 dialogPause = false;
3344 * Outputs an image of the desktop to file in EPS format, after prompting the
3345 * user for choice of Text or Lineart character rendering (unless a preference
3346 * has been set). The file name is generated as
3349 * Jalview_snapshot_nnnnn.eps where nnnnn is the current timestamp in milliseconds
3353 protected void snapShotWindow_actionPerformed(ActionEvent e)
3355 // currently the menu option to do this is not shown
3358 int width = getWidth();
3359 int height = getHeight();
3361 "Jalview_snapshot_" + System.currentTimeMillis() + ".eps");
3362 ImageWriterI writer = new ImageWriterI()
3365 public void exportImage(Graphics g) throws Exception
3368 Cache.log.info("Successfully written snapshot to file "
3369 + of.getAbsolutePath());
3372 String title = "View of desktop";
3373 ImageExporter exporter = new ImageExporter(writer, null, TYPE.EPS,
3375 exporter.doExport(of, this, width, height, title);
3379 * Explode the views in the given SplitFrame into separate SplitFrame windows.
3380 * This respects (remembers) any previous 'exploded geometry' i.e. the size and
3381 * location last time the view was expanded (if any). However it does not
3382 * remember the split pane divider location - this is set to match the
3383 * 'exploding' frame.
3387 public void explodeViews(SplitFrame sf)
3389 AlignFrame oldTopFrame = (AlignFrame) sf.getTopFrame();
3390 AlignFrame oldBottomFrame = (AlignFrame) sf.getBottomFrame();
3391 List<? extends AlignmentViewPanel> topPanels = oldTopFrame
3393 List<? extends AlignmentViewPanel> bottomPanels = oldBottomFrame
3395 int viewCount = topPanels.size();
3402 * Processing in reverse order works, forwards order leaves the first panels
3403 * not visible. I don't know why!
3405 for (int i = viewCount - 1; i >= 0; i--)
3408 * Make new top and bottom frames. These take over the respective
3409 * AlignmentPanel objects, including their AlignmentViewports, so the
3410 * cdna/protein relationships between the viewports is carried over to the
3413 * explodedGeometry holds the (x, y) position of the previously exploded
3414 * SplitFrame, and the (width, height) of the AlignFrame component
3416 AlignmentPanel topPanel = (AlignmentPanel) topPanels.get(i);
3417 AlignFrame newTopFrame = new AlignFrame(topPanel);
3418 newTopFrame.setSize(oldTopFrame.getSize());
3419 newTopFrame.setVisible(true);
3420 Rectangle geometry = ((AlignViewport) topPanel.getAlignViewport())
3421 .getExplodedGeometry();
3422 if (geometry != null)
3424 newTopFrame.setSize(geometry.getSize());
3427 AlignmentPanel bottomPanel = (AlignmentPanel) bottomPanels.get(i);
3428 AlignFrame newBottomFrame = new AlignFrame(bottomPanel);
3429 newBottomFrame.setSize(oldBottomFrame.getSize());
3430 newBottomFrame.setVisible(true);
3431 geometry = ((AlignViewport) bottomPanel.getAlignViewport())
3432 .getExplodedGeometry();
3433 if (geometry != null)
3435 newBottomFrame.setSize(geometry.getSize());
3438 topPanel.av.setGatherViewsHere(false);
3439 bottomPanel.av.setGatherViewsHere(false);
3440 JInternalFrame splitFrame = new SplitFrame(newTopFrame,
3442 if (geometry != null)
3444 splitFrame.setLocation(geometry.getLocation());
3446 Desktop.addInternalFrame(splitFrame, sf.getTitle(), -1, -1);
3450 * Clear references to the panels (now relocated in the new SplitFrames)
3451 * before closing the old SplitFrame.
3454 bottomPanels.clear();
3459 * Gather expanded split frames, sharing the same pairs of sequence set ids,
3460 * back into the given SplitFrame as additional views. Note that the gathered
3461 * frames may themselves have multiple views.
3465 public void gatherViews(GSplitFrame source)
3468 * special handling of explodedGeometry for a view within a SplitFrame: - it
3469 * holds the (x, y) position of the enclosing SplitFrame, and the (width,
3470 * height) of the AlignFrame component
3472 AlignFrame myTopFrame = (AlignFrame) source.getTopFrame();
3473 AlignFrame myBottomFrame = (AlignFrame) source.getBottomFrame();
3474 myTopFrame.viewport.setExplodedGeometry(new Rectangle(source.getX(),
3475 source.getY(), myTopFrame.getWidth(), myTopFrame.getHeight()));
3476 myBottomFrame.viewport
3477 .setExplodedGeometry(new Rectangle(source.getX(), source.getY(),
3478 myBottomFrame.getWidth(), myBottomFrame.getHeight()));
3479 myTopFrame.viewport.setGatherViewsHere(true);
3480 myBottomFrame.viewport.setGatherViewsHere(true);
3481 String topViewId = myTopFrame.viewport.getSequenceSetId();
3482 String bottomViewId = myBottomFrame.viewport.getSequenceSetId();
3484 JInternalFrame[] frames = desktopPane.getAllFrames();
3485 for (JInternalFrame frame : frames)
3487 if (frame instanceof SplitFrame && frame != source)
3489 SplitFrame sf = (SplitFrame) frame;
3490 AlignFrame topFrame = (AlignFrame) sf.getTopFrame();
3491 AlignFrame bottomFrame = (AlignFrame) sf.getBottomFrame();
3492 boolean gatherThis = false;
3493 for (int a = 0; a < topFrame.alignPanels.size(); a++)
3495 AlignmentPanel topPanel = topFrame.alignPanels.get(a);
3496 AlignmentPanel bottomPanel = bottomFrame.alignPanels.get(a);
3497 if (topViewId.equals(topPanel.av.getSequenceSetId())
3498 && bottomViewId.equals(bottomPanel.av.getSequenceSetId()))
3501 topPanel.av.setGatherViewsHere(false);
3502 bottomPanel.av.setGatherViewsHere(false);
3503 topPanel.av.setExplodedGeometry(
3504 new Rectangle(sf.getLocation(), topFrame.getSize()));
3505 bottomPanel.av.setExplodedGeometry(
3506 new Rectangle(sf.getLocation(), bottomFrame.getSize()));
3507 myTopFrame.addAlignmentPanel(topPanel, false);
3508 myBottomFrame.addAlignmentPanel(bottomPanel, false);
3514 topFrame.getAlignPanels().clear();
3515 bottomFrame.getAlignPanels().clear();
3522 * The dust settles...give focus to the tab we did this from.
3524 myTopFrame.setDisplayedView(myTopFrame.alignPanel);
3527 public static groovy.ui.Console getGroovyConsole()
3529 Desktop desktop = Desktop.getInstance();
3530 return desktop == null ? null : desktop.groovyConsole;
3534 * handles the payload of a drag and drop event.
3536 * TODO refactor to desktop utilities class
3539 * - Data source strings extracted from the drop event
3541 * - protocol for each data source extracted from the drop event
3545 * - the payload from the drop event
3548 @SuppressWarnings("unchecked")
3549 public static void transferFromDropTarget(List<Object> files,
3550 List<DataSourceType> protocols, DropTargetDropEvent evt,
3551 Transferable t) throws Exception
3554 // BH 2018 changed List<String> to List<Object> to allow for File from SwingJS
3556 // DataFlavor[] flavors = t.getTransferDataFlavors();
3557 // for (int i = 0; i < flavors.length; i++) {
3558 // if (flavors[i].isFlavorJavaFileListType()) {
3559 // evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
3560 // List<File> list = (List<File>) t.getTransferData(flavors[i]);
3561 // for (int j = 0; j < list.size(); j++) {
3562 // File file = (File) list.get(j);
3563 // byte[] data = getDroppedFileBytes(file);
3564 // fileName.setText(file.getName() + " - " + data.length + " " +
3565 // evt.getLocation());
3566 // JTextArea target = (JTextArea) ((DropTarget) evt.getSource()).getComponent();
3567 // target.setText(new String(data));
3569 // dtde.dropComplete(true);
3574 DataFlavor uriListFlavor = new DataFlavor(
3575 "text/uri-list;class=java.lang.String"), urlFlavour = null;
3578 urlFlavour = new DataFlavor(
3579 "application/x-java-url; class=java.net.URL");
3580 } catch (ClassNotFoundException cfe)
3582 Cache.log.debug("Couldn't instantiate the URL dataflavor.", cfe);
3585 if (urlFlavour != null && t.isDataFlavorSupported(urlFlavour))
3590 java.net.URL url = (URL) t.getTransferData(urlFlavour);
3591 // nb: java 8 osx bug https://bugs.openjdk.java.net/browse/JDK-8156099
3592 // means url may be null.
3595 protocols.add(DataSourceType.URL);
3596 files.add(url.toString());
3597 Cache.log.debug("Drop handled as URL dataflavor "
3598 + files.get(files.size() - 1));
3603 if (Platform.isAMacAndNotJS())
3606 "Please ignore plist error - occurs due to problem with java 8 on OSX");
3609 } catch (Throwable ex)
3611 Cache.log.debug("URL drop handler failed.", ex);
3614 if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
3616 // Works on Windows and MacOSX
3617 Cache.log.debug("Drop handled as javaFileListFlavor");
3618 for (Object file : (List<Object>) t
3619 .getTransferData(DataFlavor.javaFileListFlavor))
3622 protocols.add(DataSourceType.FILE);
3627 // Unix like behaviour
3628 boolean added = false;
3630 if (t.isDataFlavorSupported(uriListFlavor))
3632 Cache.log.debug("Drop handled as uriListFlavor");
3633 // This is used by Unix drag system
3634 data = (String) t.getTransferData(uriListFlavor);
3638 // fallback to text: workaround - on OSX where there's a JVM bug
3639 Cache.log.debug("standard URIListFlavor failed. Trying text");
3640 // try text fallback
3641 DataFlavor textDf = new DataFlavor(
3642 "text/plain;class=java.lang.String");
3643 if (t.isDataFlavorSupported(textDf))
3645 data = (String) t.getTransferData(textDf);
3648 Cache.log.debug("Plain text drop content returned "
3649 + (data == null ? "Null - failed" : data));
3654 while (protocols.size() < files.size())
3656 Cache.log.debug("Adding missing FILE protocol for "
3657 + files.get(protocols.size()));
3658 protocols.add(DataSourceType.FILE);
3660 for (java.util.StringTokenizer st = new java.util.StringTokenizer(
3661 data, "\r\n"); st.hasMoreTokens();)
3664 String s = st.nextToken();
3665 if (s.startsWith("#"))
3667 // the line is a comment (as per the RFC 2483)
3670 java.net.URI uri = new java.net.URI(s);
3671 if (uri.getScheme().toLowerCase().startsWith("http"))
3673 protocols.add(DataSourceType.URL);
3674 files.add(uri.toString());
3678 // otherwise preserve old behaviour: catch all for file objects
3679 java.io.File file = new java.io.File(uri);
3680 protocols.add(DataSourceType.FILE);
3681 files.add(file.toString());
3686 if (Cache.log.isDebugEnabled())
3688 if (data == null || !added)
3691 if (t.getTransferDataFlavors() != null
3692 && t.getTransferDataFlavors().length > 0)
3695 "Couldn't resolve drop data. Here are the supported flavors:");
3696 for (DataFlavor fl : t.getTransferDataFlavors())
3699 "Supported transfer dataflavor: " + fl.toString());
3700 Object df = t.getTransferData(fl);
3703 Cache.log.debug("Retrieves: " + df);
3707 Cache.log.debug("Retrieved nothing");
3713 Cache.log.debug("Couldn't resolve dataflavor for drop: "
3719 if (Platform.isWindowsAndNotJS())
3721 Cache.log.debug("Scanning dropped content for Windows Link Files");
3723 // resolve any .lnk files in the file drop
3724 for (int f = 0; f < files.size(); f++)
3726 String source = files.get(f).toString().toLowerCase();
3727 if (protocols.get(f).equals(DataSourceType.FILE)
3728 && (source.endsWith(".lnk") || source.endsWith(".url")
3729 || source.endsWith(".site")))
3733 Object obj = files.get(f);
3734 File lf = (obj instanceof File ? (File) obj
3735 : new File((String) obj));
3736 // process link file to get a URL
3737 Cache.log.debug("Found potential link file: " + lf);
3738 WindowsShortcut wscfile = new WindowsShortcut(lf);
3739 String fullname = wscfile.getRealFilename();
3740 protocols.set(f, FormatAdapter.checkProtocol(fullname));
3741 files.set(f, fullname);
3742 Cache.log.debug("Parsed real filename " + fullname
3743 + " to extract protocol: " + protocols.get(f));
3744 } catch (Exception ex)
3747 "Couldn't parse " + files.get(f) + " as a link file.",
3756 * Sets the Preferences property for experimental features to True or False
3757 * depending on the state of the controlling menu item
3760 protected void showExperimental_actionPerformed(boolean selected)
3762 Cache.setProperty(EXPERIMENTAL_FEATURES, Boolean.toString(selected));
3766 * Answers a (possibly empty) list of any structure viewer frames (currently for
3767 * either Jmol or Chimera) which are currently open. This may optionally be
3768 * restricted to viewers of a specified class, or viewers linked to a specified
3772 * if not null, only return viewers linked to this panel
3773 * @param structureViewerClass
3774 * if not null, only return viewers of this class
3777 public List<StructureViewerBase> getStructureViewers(
3778 AlignmentPanel apanel,
3779 Class<? extends StructureViewerBase> structureViewerClass)
3781 List<StructureViewerBase> result = new ArrayList<>();
3782 JInternalFrame[] frames = getAllFrames();
3784 for (JInternalFrame frame : frames)
3786 if (frame instanceof StructureViewerBase)
3788 if (structureViewerClass == null
3789 || structureViewerClass.isInstance(frame))
3792 || ((StructureViewerBase) frame).isLinkedWith(apanel))
3794 result.add((StructureViewerBase) frame);