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 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()
391 Desktop d = (Desktop) ApplicationSingletonProvider
392 .getInstance(Desktop.class);
393 d.instanceOnly = true;
398 * Private constructor enforces singleton pattern. It is called by reflection
399 * from ApplicationSingletonProvider.getInstance().
401 @SuppressWarnings("unused")
408 * A note to implementors. It is ESSENTIAL that any activities that might
409 * block are spawned off as threads rather than waited for during this
412 if (!Platform.isJS())
414 doVamsasClientCheck();
417 doConfigureStructurePrefs();
418 setTitle("Jalview " + jalview.bin.Cache.getProperty("VERSION"));
419 setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
420 boolean selmemusage = jalview.bin.Cache.getDefault("SHOW_MEMUSAGE",
422 boolean showjconsole = jalview.bin.Cache
423 .getDefault("SHOW_JAVA_CONSOLE", false);
424 desktopPane = new MyDesktopPane(selmemusage);
426 showMemusage.setSelected(selmemusage);
427 desktopPane.setBackground(Color.white);
428 getContentPane().setLayout(new BorderLayout());
429 // alternate config - have scrollbars - see notes in JAL-153
430 // JScrollPane sp = new JScrollPane();
431 // sp.getViewport().setView(desktop);
432 // getContentPane().add(sp, BorderLayout.CENTER);
434 // BH 2018 - just an experiment to try unclipped JInternalFrames.
437 getRootPane().putClientProperty("swingjs.overflow.hidden", "false");
440 getContentPane().add(desktopPane, BorderLayout.CENTER);
441 desktopPane.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
443 // This line prevents Windows Look&Feel resizing all new windows to
445 // if previous window was maximised
446 desktopPane.setDesktopManager(new MyDesktopManager(
447 (Platform.isWindowsAndNotJS() ? new DefaultDesktopManager()
448 : Platform.isAMacAndNotJS()
449 ? new AquaInternalFrameManager(
450 desktopPane.getDesktopManager())
451 : desktopPane.getDesktopManager())));
453 Rectangle dims = getLastKnownDimensions("");
460 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
461 int xPos = Math.max(5, (screenSize.width - 900) / 2);
462 int yPos = Math.max(5, (screenSize.height - 650) / 2);
463 setBounds(xPos, yPos, 900, 650);
466 // Note that this next syntax, checking for Platform.isJS and also
467 // escaping the code using @j2sIgnore, serves two purposes. It gives
468 // us an easily findable tag, Platform.isJS(), to places in the code where
469 // there is something different about the SwingJS implementation. Second,
470 // it deletes the unneeded Java-only code form the JavaScript version
471 // completely (@j2sIgnore), since it will never be used there.
473 if (!Platform.isJS())
481 jconsole = new Console(this, showjconsole);
482 // add essential build information
483 jconsole.setHeader("Jalview Version: "
484 + jalview.bin.Cache.getProperty("VERSION") + "\n"
485 + "Jalview Installation: "
486 + jalview.bin.Cache.getDefault("INSTALLATION", "unknown")
487 + "\n" + "Build Date: "
488 + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown")
489 + "\n" + "Java version: "
490 + System.getProperty("java.version") + "\n"
491 + System.getProperty("os.arch") + " "
492 + System.getProperty("os.name") + " "
493 + System.getProperty("os.version"));
495 showConsole(showjconsole);
497 showNews.setVisible(false);
499 experimentalFeatures.setSelected(showExperimental());
501 getIdentifiersOrgData();
505 // Spawn a thread that shows the splashscreen
507 SwingUtilities.invokeLater(new Runnable()
516 // Thread off a new instance of the file chooser - this reduces the time
518 // takes to open it later on.
519 new Thread(new Runnable()
524 Cache.log.debug("Filechooser init thread started.");
525 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
526 JalviewFileChooser.forRead(Cache.getProperty("LAST_DIRECTORY"),
528 Cache.log.debug("Filechooser init thread finished.");
531 // Add the service change listener
532 changeSupport.addJalviewPropertyChangeListener("services",
533 new PropertyChangeListener()
537 public void propertyChange(PropertyChangeEvent evt)
539 Cache.log.debug("Firing service changed event for "
540 + evt.getNewValue());
541 JalviewServicesChanged(evt);
548 this.setDropTarget(new java.awt.dnd.DropTarget(desktopPane, this));
550 this.addWindowListener(new WindowAdapter()
553 public void windowClosing(WindowEvent evt)
560 this.addMouseListener(ma = new MouseAdapter()
563 public void mousePressed(MouseEvent evt)
565 if (evt.isPopupTrigger()) // Mac
567 showPasteMenu(evt.getX(), evt.getY());
572 public void mouseReleased(MouseEvent evt)
574 if (evt.isPopupTrigger()) // Windows
576 showPasteMenu(evt.getX(), evt.getY());
580 desktopPane.addMouseListener(ma);
581 } catch (Throwable t)
588 * Answers true if user preferences to enable experimental features is True
593 public boolean showExperimental()
595 String experimental = Cache.getDefault(EXPERIMENTAL_FEATURES,
596 Boolean.FALSE.toString());
597 return Boolean.valueOf(experimental).booleanValue();
600 public void doConfigureStructurePrefs()
602 // configure services
603 StructureSelectionManager ssm = StructureSelectionManager
604 .getStructureSelectionManager(this);
605 if (jalview.bin.Cache.getDefault(Preferences.ADD_SS_ANN, true))
607 ssm.setAddTempFacAnnot(jalview.bin.Cache
608 .getDefault(Preferences.ADD_TEMPFACT_ANN, true));
609 ssm.setProcessSecondaryStructure(jalview.bin.Cache
610 .getDefault(Preferences.STRUCT_FROM_PDB, true));
611 ssm.setSecStructServices(
612 jalview.bin.Cache.getDefault(Preferences.USE_RNAVIEW, true));
616 ssm.setAddTempFacAnnot(false);
617 ssm.setProcessSecondaryStructure(false);
618 ssm.setSecStructServices(false);
622 public void checkForNews()
624 final Desktop me = this;
625 // Thread off the news reader, in case there are connection problems.
626 new Thread(new Runnable()
631 Cache.log.debug("Starting news thread.");
632 jvnews = new BlogReader(me);
633 showNews.setVisible(true);
634 Cache.log.debug("Completed news thread.");
639 public void getIdentifiersOrgData()
641 // Thread off the identifiers fetcher
642 new Thread(new Runnable()
647 Cache.log.debug("Downloading data from identifiers.org");
648 // UrlDownloadClient client = new UrlDownloadClient();
651 UrlDownloadClient.download(IdOrgSettings.getUrl(),
652 IdOrgSettings.getDownloadLocation());
653 } catch (IOException e)
655 Cache.log.debug("Exception downloading identifiers.org data"
664 protected void showNews_actionPerformed(ActionEvent e)
666 showNews(showNews.isSelected());
669 protected void showNews(boolean visible)
671 Cache.log.debug((visible ? "Showing" : "Hiding") + " news.");
672 showNews.setSelected(visible);
673 if (visible && !jvnews.isVisible())
675 new Thread(new Runnable()
680 long now = System.currentTimeMillis();
682 MessageManager.getString("status.refreshing_news"), now);
683 jvnews.refreshNews();
684 setProgressBar(null, now);
692 * recover the last known dimensions for a jalview window
695 * - empty string is desktop, all other windows have unique prefix
696 * @return null or last known dimensions scaled to current geometry (if last
697 * window geom was known)
699 Rectangle getLastKnownDimensions(String windowName)
701 // TODO: lock aspect ratio for scaling desktop Bug #0058199
702 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
703 String x = jalview.bin.Cache.getProperty(windowName + "SCREEN_X");
704 String y = jalview.bin.Cache.getProperty(windowName + "SCREEN_Y");
705 String width = jalview.bin.Cache
706 .getProperty(windowName + "SCREEN_WIDTH");
707 String height = jalview.bin.Cache
708 .getProperty(windowName + "SCREEN_HEIGHT");
709 if ((x != null) && (y != null) && (width != null) && (height != null))
711 int ix = Integer.parseInt(x), iy = Integer.parseInt(y),
712 iw = Integer.parseInt(width), ih = Integer.parseInt(height);
713 if (jalview.bin.Cache.getProperty("SCREENGEOMETRY_WIDTH") != null)
715 // attempt #1 - try to cope with change in screen geometry - this
716 // version doesn't preserve original jv aspect ratio.
717 // take ratio of current screen size vs original screen size.
718 double sw = ((1f * screenSize.width) / (1f * Integer.parseInt(
719 jalview.bin.Cache.getProperty("SCREENGEOMETRY_WIDTH"))));
720 double sh = ((1f * screenSize.height) / (1f * Integer.parseInt(
721 jalview.bin.Cache.getProperty("SCREENGEOMETRY_HEIGHT"))));
722 // rescale the bounds depending upon the current screen geometry.
723 ix = (int) (ix * sw);
724 iw = (int) (iw * sw);
725 iy = (int) (iy * sh);
726 ih = (int) (ih * sh);
727 while (ix >= screenSize.width)
729 jalview.bin.Cache.log.debug(
730 "Window geometry location recall error: shifting horizontal to within screenbounds.");
731 ix -= screenSize.width;
733 while (iy >= screenSize.height)
735 jalview.bin.Cache.log.debug(
736 "Window geometry location recall error: shifting vertical to within screenbounds.");
737 iy -= screenSize.height;
739 jalview.bin.Cache.log.debug(
740 "Got last known dimensions for " + windowName + ": x:" + ix
741 + " y:" + iy + " width:" + iw + " height:" + ih);
743 // return dimensions for new instance
744 return new Rectangle(ix, iy, iw, ih);
749 private void doVamsasClientCheck()
751 if (Cache.vamsasJarsPresent())
753 setupVamsasDisconnectedGui();
754 VamsasMenu.setVisible(true);
755 final Desktop us = this;
756 VamsasMenu.addMenuListener(new MenuListener()
758 // this listener remembers when the menu was first selected, and
759 // doesn't rebuild the session list until it has been cleared and
761 boolean refresh = true;
764 public void menuCanceled(MenuEvent e)
770 public void menuDeselected(MenuEvent e)
776 public void menuSelected(MenuEvent e)
780 us.buildVamsasStMenu();
785 vamsasStart.setVisible(true);
789 protected void showPasteMenu(int x, int y)
791 JPopupMenu popup = new JPopupMenu();
792 JMenuItem item = new JMenuItem(
793 MessageManager.getString("label.paste_new_window"));
794 item.addActionListener(new ActionListener()
797 public void actionPerformed(ActionEvent evt)
804 popup.show(this, x, y);
811 Clipboard c = Toolkit.getDefaultToolkit().getSystemClipboard();
812 Transferable contents = c.getContents(this);
814 if (contents != null)
816 String file = (String) contents
817 .getTransferData(DataFlavor.stringFlavor);
819 FileFormatI format = new IdentifyFile().identify(file,
820 DataSourceType.PASTE);
822 new FileLoader().loadFile(file, DataSourceType.PASTE, format);
825 } catch (Exception ex)
828 "Unable to paste alignment from system clipboard:\n" + ex);
833 * Adds and opens the given frame to the desktop
844 public static synchronized void addInternalFrame(
845 final JInternalFrame frame, String title, int w, int h)
847 addInternalFrame(frame, title, true, w, h, true, false);
851 * Add an internal frame to the Jalview desktop
858 * When true, display frame immediately, otherwise, caller must call
859 * setVisible themselves.
865 public static synchronized void addInternalFrame(
866 final JInternalFrame frame, String title, boolean makeVisible,
869 addInternalFrame(frame, title, makeVisible, w, h, true, false);
873 * Add an internal frame to the Jalview desktop and make it visible
886 public static synchronized void addInternalFrame(
887 final JInternalFrame frame, String title, int w, int h,
890 addInternalFrame(frame, title, true, w, h, resizable, false);
894 * Add an internal frame to the Jalview desktop
901 * When true, display frame immediately, otherwise, caller must call
902 * setVisible themselves.
909 * @param ignoreMinSize
910 * Do not set the default minimum size for frame
912 public static synchronized void addInternalFrame(
913 final JInternalFrame frame, String title, boolean makeVisible,
914 int w, int h, boolean resizable, boolean ignoreMinSize)
918 // TODO: allow callers to determine X and Y position of frame (eg. via
920 // TODO: consider fixing method to update entries in the window submenu with
921 // the current window title
923 frame.setTitle(title);
924 if (w > 0 && (frame.getWidth() < 1 || frame.getHeight() < 1))
928 // THIS IS A PUBLIC STATIC METHOD, SO IT MAY BE CALLED EVEN IN
929 // A HEADLESS STATE WHEN NO DESKTOP EXISTS. MUST RETURN
930 // IF JALVIEW IS RUNNING HEADLESS OR IN INSTANCE-ONLY (testNG) MODE
931 // ///////////////////////////////////////////////
932 if (Jalview.isHeadlessMode() || Desktop.getInstance().instanceOnly)
941 frame.setMinimumSize(
942 new Dimension(DEFAULT_MIN_WIDTH, DEFAULT_MIN_HEIGHT));
944 // Set default dimension for Alignment Frame window.
945 // The Alignment Frame window could be added from a number of places,
947 // I did this here in order not to miss out on any Alignment frame.
948 if (frame instanceof AlignFrame)
950 frame.setMinimumSize(new Dimension(ALIGN_FRAME_DEFAULT_MIN_WIDTH,
951 ALIGN_FRAME_DEFAULT_MIN_HEIGHT));
955 frame.setVisible(makeVisible);
956 frame.setClosable(true);
957 frame.setResizable(resizable);
958 frame.setMaximizable(resizable);
959 frame.setIconifiable(resizable);
960 frame.setOpaque(Platform.isJS());
962 boolean isEmbedded = (Platform.getDimIfEmbedded(frame, -1, -1) != null);
963 if (!isEmbedded && frame.getX() < 1 && frame.getY() < 1)
965 frame.setLocation(xOffset * openFrameCount,
966 yOffset * ((openFrameCount - 1) % 10) + yOffset);
970 * add an entry for the new frame in the Window menu
971 * (and remove it when the frame is closed)
973 JMenuItem menuItem = new JMenuItem(title);
974 frame.addInternalFrameListener(new InternalFrameAdapter()
977 public void internalFrameActivated(InternalFrameEvent evt)
979 JInternalFrame itf = getDesktopPane().getSelectedFrame();
982 if (itf instanceof AlignFrame)
984 Jalview.setCurrentAlignFrame((AlignFrame) itf);
991 public void internalFrameClosed(InternalFrameEvent evt)
993 PaintRefresher.RemoveComponent(frame);
996 * defensive check to prevent frames being
997 * added half off the window
999 if (openFrameCount > 0)
1005 * ensure no reference to alignFrame retained by menu item listener
1007 if (menuItem.getActionListeners().length > 0)
1009 menuItem.removeActionListener(menuItem.getActionListeners()[0]);
1011 Desktop.getInstance().windowMenu.remove(menuItem);
1015 menuItem.addActionListener(new ActionListener()
1018 public void actionPerformed(ActionEvent e)
1022 frame.setSelected(true);
1023 frame.setIcon(false);
1024 } catch (java.beans.PropertyVetoException ex)
1026 // System.err.println(ex.toString());
1031 setKeyBindings(frame);
1033 getDesktopPane().add(frame);
1035 Desktop.getInstance().windowMenu.add(menuItem);
1040 frame.setSelected(true);
1041 frame.requestFocus();
1042 } catch (java.beans.PropertyVetoException ve)
1044 } catch (java.lang.ClassCastException cex)
1047 "Squashed a possible GUI implementation error. If you can recreate this, please look at http://issues.jalview.org/browse/JAL-869",
1053 * Add key bindings to a JInternalFrame so that Ctrl-W and Cmd-W will close the
1058 private static void setKeyBindings(JInternalFrame frame)
1060 final Action closeAction = new AbstractAction()
1063 public void actionPerformed(ActionEvent e)
1070 * set up key bindings for Ctrl-W and Cmd-W, with the same (Close) action
1072 KeyStroke ctrlWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
1073 InputEvent.CTRL_DOWN_MASK);
1074 KeyStroke cmdWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
1075 Toolkit.getDefaultToolkit().getMenuShortcutKeyMask());
1077 InputMap inputMap = frame
1078 .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
1079 String ctrlW = ctrlWKey.toString();
1080 inputMap.put(ctrlWKey, ctrlW);
1081 inputMap.put(cmdWKey, ctrlW);
1083 ActionMap actionMap = frame.getActionMap();
1084 actionMap.put(ctrlW, closeAction);
1088 public void lostOwnership(Clipboard clipboard, Transferable contents)
1092 Desktop.getInstance().jalviewClipboard = null;
1095 internalCopy = false;
1099 public void dragEnter(DropTargetDragEvent evt)
1104 public void dragExit(DropTargetEvent evt)
1109 public void dragOver(DropTargetDragEvent evt)
1114 public void dropActionChanged(DropTargetDragEvent evt)
1125 public void drop(DropTargetDropEvent evt)
1127 boolean success = true;
1128 // JAL-1552 - acceptDrop required before getTransferable call for
1129 // Java's Transferable for native dnd
1130 evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
1131 Transferable t = evt.getTransferable();
1132 List<Object> files = new ArrayList<>();
1133 List<DataSourceType> protocols = new ArrayList<>();
1137 Desktop.transferFromDropTarget(files, protocols, evt, t);
1138 } catch (Exception e)
1140 e.printStackTrace();
1148 for (int i = 0; i < files.size(); i++)
1150 // BH 2018 File or String
1151 Object file = files.get(i);
1152 String fileName = file.toString();
1153 DataSourceType protocol = (protocols == null)
1154 ? DataSourceType.FILE
1156 FileFormatI format = null;
1158 if (fileName.endsWith(".jar"))
1160 format = FileFormat.Jalview;
1165 format = new IdentifyFile().identify(file, protocol);
1167 if (file instanceof File)
1169 Platform.cacheFileData((File) file);
1171 new FileLoader().loadFile(null, file, protocol, format);
1174 } catch (Exception ex)
1179 evt.dropComplete(success); // need this to ensure input focus is properly
1180 // transfered to any new windows created
1190 public void inputLocalFileMenuItem_actionPerformed(AlignViewport viewport)
1192 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
1193 JalviewFileChooser chooser = JalviewFileChooser
1194 .forRead(Cache.getProperty("LAST_DIRECTORY"), fileFormat, true);
1196 chooser.setFileView(new JalviewFileView());
1197 chooser.setDialogTitle(
1198 MessageManager.getString("label.open_local_file"));
1199 chooser.setToolTipText(MessageManager.getString("action.open"));
1201 chooser.setResponseHandler(0, new Runnable()
1206 File selectedFile = chooser.getSelectedFile();
1207 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1209 FileFormatI format = chooser.getSelectedFormat();
1212 * Call IdentifyFile to verify the file contains what its extension implies.
1213 * Skip this step for dynamically added file formats, because
1214 * IdentifyFile does not know how to recognise them.
1216 if (FileFormats.getInstance().isIdentifiable(format))
1220 format = new IdentifyFile().identify(selectedFile,
1221 DataSourceType.FILE);
1222 } catch (FileFormatException e)
1224 // format = null; //??
1228 new FileLoader().loadFile(viewport, selectedFile,
1229 DataSourceType.FILE, format);
1232 chooser.showOpenDialog(this);
1236 * Shows a dialog for input of a URL at which to retrieve alignment data
1241 public void inputURLMenuItem_actionPerformed(AlignViewport viewport)
1243 // This construct allows us to have a wider textfield
1245 JLabel label = new JLabel(
1246 MessageManager.getString("label.input_file_url"));
1248 JPanel panel = new JPanel(new GridLayout(2, 1));
1252 * the URL to fetch is
1253 * Java: an editable combobox with history
1254 * JS: (pending JAL-3038) a plain text field
1257 String urlBase = "http://www.";
1258 if (Platform.isJS())
1260 history = new JTextField(urlBase, 35);
1269 JComboBox<String> asCombo = new JComboBox<>();
1270 asCombo.setPreferredSize(new Dimension(400, 20));
1271 asCombo.setEditable(true);
1272 asCombo.addItem(urlBase);
1273 String historyItems = Cache.getProperty("RECENT_URL");
1274 if (historyItems != null)
1276 for (String token : historyItems.split("\\t"))
1278 asCombo.addItem(token);
1285 Object[] options = new Object[] { MessageManager.getString("action.ok"),
1286 MessageManager.getString("action.cancel") };
1287 Runnable action = new Runnable()
1292 @SuppressWarnings("unchecked")
1293 String url = (history instanceof JTextField
1294 ? ((JTextField) history).getText()
1295 : ((JComboBox<String>) history).getSelectedItem()
1298 if (url.toLowerCase().endsWith(".jar"))
1300 if (viewport != null)
1302 new FileLoader().loadFile(viewport, url, DataSourceType.URL,
1303 FileFormat.Jalview);
1307 new FileLoader().loadFile(url, DataSourceType.URL,
1308 FileFormat.Jalview);
1313 FileFormatI format = null;
1316 format = new IdentifyFile().identify(url, DataSourceType.URL);
1317 } catch (FileFormatException e)
1319 // TODO revise error handling, distinguish between
1320 // URL not found and response not valid
1325 String msg = MessageManager
1326 .formatMessage("label.couldnt_locate", url);
1327 JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
1329 MessageManager.getString("label.url_not_found"),
1330 JvOptionPane.WARNING_MESSAGE);
1335 if (viewport != null)
1337 new FileLoader().loadFile(viewport, url, DataSourceType.URL,
1342 new FileLoader().loadFile(url, DataSourceType.URL, format);
1347 String dialogOption = MessageManager
1348 .getString("label.input_alignment_from_url");
1349 JvOptionPane.newOptionDialog(getDesktopPane())
1350 .setResponseHandler(0, action)
1351 .showInternalDialog(panel, dialogOption,
1352 JvOptionPane.YES_NO_CANCEL_OPTION,
1353 JvOptionPane.PLAIN_MESSAGE, null, options,
1354 MessageManager.getString("action.ok"));
1358 * Opens the CutAndPaste window for the user to paste an alignment in to
1361 * - if not null, the pasted alignment is added to the current
1362 * alignment; if null, to a new alignment window
1365 public void inputTextboxMenuItem_actionPerformed(
1366 AlignmentViewPanel viewPanel)
1368 CutAndPasteTransfer cap = new CutAndPasteTransfer();
1369 cap.setForInput(viewPanel);
1370 Desktop.addInternalFrame(cap,
1371 MessageManager.getString("label.cut_paste_alignmen_file"), true,
1381 Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
1382 jalview.bin.Cache.setProperty("SCREENGEOMETRY_WIDTH",
1384 jalview.bin.Cache.setProperty("SCREENGEOMETRY_HEIGHT",
1385 screen.height + "");
1386 storeLastKnownDimensions("", new Rectangle(getBounds().x, getBounds().y,
1387 getWidth(), getHeight()));
1389 if (jconsole != null)
1391 storeLastKnownDimensions("JAVA_CONSOLE_", jconsole.getBounds());
1392 jconsole.stopConsole();
1396 storeLastKnownDimensions("JALVIEW_RSS_WINDOW_", jvnews.getBounds());
1399 if (dialogExecutor != null)
1401 dialogExecutor.shutdownNow();
1403 closeAll_actionPerformed(null);
1405 if (groovyConsole != null)
1407 // suppress a possible repeat prompt to save script
1408 groovyConsole.setDirty(false);
1409 groovyConsole.exit();
1414 private void storeLastKnownDimensions(String string, Rectangle jc)
1416 jalview.bin.Cache.log.debug("Storing last known dimensions for "
1417 + string + ": x:" + jc.x + " y:" + jc.y + " width:" + jc.width
1418 + " height:" + jc.height);
1420 jalview.bin.Cache.setProperty(string + "SCREEN_X", jc.x + "");
1421 jalview.bin.Cache.setProperty(string + "SCREEN_Y", jc.y + "");
1422 jalview.bin.Cache.setProperty(string + "SCREEN_WIDTH", jc.width + "");
1423 jalview.bin.Cache.setProperty(string + "SCREEN_HEIGHT", jc.height + "");
1433 public void aboutMenuItem_actionPerformed(ActionEvent e)
1435 // StringBuffer message = getAboutMessage(false);
1436 // JvOptionPane.showInternalMessageDialog(Desktop.getDesktop(),
1438 // message.toString(), "About Jalview", JvOptionPane.INFORMATION_MESSAGE);
1439 new Thread(new Runnable()
1444 new SplashScreen(true);
1449 public StringBuffer getAboutMessage(boolean shortv)
1451 StringBuffer message = new StringBuffer();
1452 message.append("<html>");
1455 message.append("<h1><strong>Version: "
1456 + jalview.bin.Cache.getProperty("VERSION")
1457 + "</strong></h1>");
1458 message.append("<strong>Last Updated: <em>"
1459 + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown")
1460 + "</em></strong>");
1466 message.append("<strong>Version "
1467 + jalview.bin.Cache.getProperty("VERSION")
1468 + "; last updated: "
1469 + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown"));
1472 if (jalview.bin.Cache.getDefault("LATEST_VERSION", "Checking")
1473 .equals("Checking"))
1475 message.append("<br>...Checking latest version...</br>");
1477 else if (!jalview.bin.Cache.getDefault("LATEST_VERSION", "Checking")
1478 .equals(jalview.bin.Cache.getProperty("VERSION")))
1480 boolean red = false;
1481 if (jalview.bin.Cache.getProperty("VERSION").toLowerCase()
1482 .indexOf("automated build") == -1)
1485 // Displayed when code version and jnlp version do not match and code
1486 // version is not a development build
1487 message.append("<div style=\"color: #FF0000;font-style: bold;\">");
1490 message.append("<br>!! Version "
1491 + jalview.bin.Cache.getDefault("LATEST_VERSION",
1493 + " is available for download from "
1494 + jalview.bin.Cache.getDefault("www.jalview.org",
1495 "http://www.jalview.org")
1499 message.append("</div>");
1502 message.append("<br>Authors: " + jalview.bin.Cache.getDefault(
1504 "The Jalview Authors (See AUTHORS file for current list)")
1505 + "<br><br>Development managed by The Barton Group, University of Dundee, Scotland, UK.<br>"
1506 + "<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"
1507 + "<br><br>If you use Jalview, please cite:"
1508 + "<br>Waterhouse, A.M., Procter, J.B., Martin, D.M.A, Clamp, M. and Barton, G. J. (2009)"
1509 + "<br>Jalview Version 2 - a multiple sequence alignment editor and analysis workbench"
1510 + "<br>Bioinformatics doi: 10.1093/bioinformatics/btp033"
1516 * Action on requesting Help documentation
1519 public void documentationMenuItem_actionPerformed()
1523 if (Platform.isJS())
1525 BrowserLauncher.openURL("http://www.jalview.org/help.html");
1534 Help.showHelpWindow();
1536 } catch (Exception ex)
1538 System.err.println("Error opening help: " + ex.getMessage());
1543 public void closeAll_actionPerformed(ActionEvent e)
1545 if (desktopPane == null)
1549 // TODO show a progress bar while closing?
1550 JInternalFrame[] frames = desktopPane.getAllFrames();
1551 for (int i = 0; i < frames.length; i++)
1555 frames[i].setClosed(true);
1556 } catch (java.beans.PropertyVetoException ex)
1560 Jalview.setCurrentAlignFrame(null);
1561 System.out.println("ALL CLOSED");
1562 if (v_client != null)
1564 // TODO clear binding to vamsas document objects on close_all
1568 * reset state of singleton objects as appropriate (clear down session state
1569 * when all windows are closed)
1571 getStructureSelectionManager().resetAll();
1575 public void raiseRelated_actionPerformed(ActionEvent e)
1577 reorderAssociatedWindows(false, false);
1581 public void minimizeAssociated_actionPerformed(ActionEvent e)
1583 reorderAssociatedWindows(true, false);
1586 void closeAssociatedWindows()
1588 reorderAssociatedWindows(false, true);
1594 * @seejalview.jbgui.GDesktop#garbageCollect_actionPerformed(java.awt.event.
1598 protected void garbageCollect_actionPerformed(ActionEvent e)
1600 // We simply collect the garbage
1601 jalview.bin.Cache.log.debug("Collecting garbage...");
1603 jalview.bin.Cache.log.debug("Finished garbage collection.");
1610 * jalview.jbgui.GDesktop#showMemusage_actionPerformed(java.awt.event.ActionEvent
1614 protected void showMemusage_actionPerformed(ActionEvent e)
1616 getDesktopPane().showMemoryUsage(showMemusage.isSelected());
1623 * jalview.jbgui.GDesktop#showConsole_actionPerformed(java.awt.event.ActionEvent
1627 protected void showConsole_actionPerformed(ActionEvent e)
1629 showConsole(showConsole.isSelected());
1632 Console jconsole = null;
1635 * control whether the java console is visible or not
1639 void showConsole(boolean selected)
1641 // TODO: decide if we should update properties file
1642 if (jconsole != null) // BH 2018
1644 showConsole.setSelected(selected);
1645 Cache.setProperty("SHOW_JAVA_CONSOLE",
1646 Boolean.valueOf(selected).toString());
1647 jconsole.setVisible(selected);
1651 void reorderAssociatedWindows(boolean minimize, boolean close)
1653 JInternalFrame[] frames = getDesktopPane().getAllFrames();
1654 if (frames == null || frames.length < 1)
1659 AlignmentViewport source = null, target = null;
1660 if (frames[0] instanceof AlignFrame)
1662 source = ((AlignFrame) frames[0]).getCurrentView();
1664 else if (frames[0] instanceof TreePanel)
1666 source = ((TreePanel) frames[0]).getViewPort();
1668 else if (frames[0] instanceof PCAPanel)
1670 source = ((PCAPanel) frames[0]).av;
1672 else if (frames[0].getContentPane() instanceof PairwiseAlignPanel)
1674 source = ((PairwiseAlignPanel) frames[0].getContentPane()).av;
1679 for (int i = 0; i < frames.length; i++)
1682 if (frames[i] == null)
1686 if (frames[i] instanceof AlignFrame)
1688 target = ((AlignFrame) frames[i]).getCurrentView();
1690 else if (frames[i] instanceof TreePanel)
1692 target = ((TreePanel) frames[i]).getViewPort();
1694 else if (frames[i] instanceof PCAPanel)
1696 target = ((PCAPanel) frames[i]).av;
1698 else if (frames[i].getContentPane() instanceof PairwiseAlignPanel)
1700 target = ((PairwiseAlignPanel) frames[i].getContentPane()).av;
1703 if (source == target)
1709 frames[i].setClosed(true);
1713 frames[i].setIcon(minimize);
1716 frames[i].toFront();
1720 } catch (java.beans.PropertyVetoException ex)
1735 protected void preferences_actionPerformed(ActionEvent e)
1741 * Prompts the user to choose a file and then saves the Jalview state as a
1742 * Jalview project file
1745 public void saveState_actionPerformed()
1747 saveState_actionPerformed(false);
1750 public void saveState_actionPerformed(boolean saveAs)
1752 java.io.File projectFile = getProjectFile();
1753 // autoSave indicates we already have a file and don't need to ask
1754 boolean autoSave = projectFile != null && !saveAs
1755 && BackupFiles.getEnabled();
1757 // System.out.println("autoSave="+autoSave+", projectFile='"+projectFile+"',
1758 // saveAs="+saveAs+", Backups
1759 // "+(BackupFiles.getEnabled()?"enabled":"disabled"));
1761 boolean approveSave = false;
1764 JalviewFileChooser chooser = new JalviewFileChooser("jvp",
1767 chooser.setFileView(new JalviewFileView());
1768 chooser.setDialogTitle(MessageManager.getString("label.save_state"));
1770 int value = chooser.showSaveDialog(this);
1772 if (value == JalviewFileChooser.APPROVE_OPTION)
1774 projectFile = chooser.getSelectedFile();
1775 setProjectFile(projectFile);
1780 if (approveSave || autoSave)
1782 final Desktop me = this;
1783 final java.io.File chosenFile = projectFile;
1784 new Thread(new Runnable()
1789 // TODO: refactor to Jalview desktop session controller action.
1790 setProgressBar(MessageManager.formatMessage(
1791 "label.saving_jalview_project", new Object[]
1792 { chosenFile.getName() }), chosenFile.hashCode());
1793 jalview.bin.Cache.setProperty("LAST_DIRECTORY",
1794 chosenFile.getParent());
1795 // TODO catch and handle errors for savestate
1796 // TODO prevent user from messing with the Desktop whilst we're saving
1799 boolean doBackup = BackupFiles.getEnabled();
1800 BackupFiles backupfiles = doBackup ? new BackupFiles(chosenFile) : null;
1802 new Jalview2XML().saveState(doBackup ? backupfiles.getTempFile() : chosenFile);
1806 backupfiles.setWriteSuccess(true);
1807 backupfiles.rollBackupsAndRenameTempFile();
1809 } catch (OutOfMemoryError oom)
1811 new OOMWarning("Whilst saving current state to "
1812 + chosenFile.getName(), oom);
1813 } catch (Exception ex)
1815 Cache.log.error("Problems whilst trying to save to "
1816 + chosenFile.getName(), ex);
1817 JvOptionPane.showMessageDialog(me,
1818 MessageManager.formatMessage(
1819 "label.error_whilst_saving_current_state_to",
1821 { chosenFile.getName() }),
1822 MessageManager.getString("label.couldnt_save_project"),
1823 JvOptionPane.WARNING_MESSAGE);
1825 setProgressBar(null, chosenFile.hashCode());
1832 public void saveAsState_actionPerformed(ActionEvent e)
1834 saveState_actionPerformed(true);
1837 protected void setProjectFile(File choice)
1839 this.projectFile = choice;
1842 public File getProjectFile()
1844 return this.projectFile;
1848 * Shows a file chooser dialog and tries to read in the selected file as a
1852 public void loadState_actionPerformed()
1854 final String[] suffix = new String[] { "jvp", "jar" };
1855 final String[] desc = new String[] { "Jalview Project",
1856 "Jalview Project (old)" };
1857 JalviewFileChooser chooser = new JalviewFileChooser(
1858 Cache.getProperty("LAST_DIRECTORY"), suffix, desc,
1859 "Jalview Project", true, true); // last two booleans: allFiles,
1861 chooser.setFileView(new JalviewFileView());
1862 chooser.setDialogTitle(MessageManager.getString("label.restore_state"));
1863 chooser.setResponseHandler(0, new Runnable()
1868 File selectedFile = chooser.getSelectedFile();
1869 setProjectFile(selectedFile);
1870 String choice = selectedFile.getAbsolutePath();
1871 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1872 new Thread(new Runnable()
1879 new Jalview2XML().loadJalviewAlign(choice);
1880 } catch (OutOfMemoryError oom)
1882 new OOMWarning("Whilst loading project from " + choice, oom);
1883 } catch (Exception ex)
1886 "Problems whilst loading project from " + choice, ex);
1887 JvOptionPane.showMessageDialog(Desktop.getDesktopPane(),
1888 MessageManager.formatMessage(
1889 "label.error_whilst_loading_project_from",
1892 MessageManager.getString("label.couldnt_load_project"),
1893 JvOptionPane.WARNING_MESSAGE);
1900 chooser.showOpenDialog(this);
1904 public void inputSequence_actionPerformed(ActionEvent e)
1906 new SequenceFetcher(this);
1909 JPanel progressPanel;
1911 ArrayList<JPanel> fileLoadingPanels = new ArrayList<>();
1913 public void startLoading(final Object fileName)
1915 if (fileLoadingCount == 0)
1917 fileLoadingPanels.add(addProgressPanel(MessageManager
1918 .formatMessage("label.loading_file", new Object[]
1924 private JPanel addProgressPanel(String string)
1926 if (progressPanel == null)
1928 progressPanel = new JPanel(new GridLayout(1, 1));
1929 totalProgressCount = 0;
1930 getContentPane().add(progressPanel, BorderLayout.SOUTH);
1932 JPanel thisprogress = new JPanel(new BorderLayout(10, 5));
1933 JProgressBar progressBar = new JProgressBar();
1934 progressBar.setIndeterminate(true);
1936 thisprogress.add(new JLabel(string), BorderLayout.WEST);
1938 thisprogress.add(progressBar, BorderLayout.CENTER);
1939 progressPanel.add(thisprogress);
1940 ((GridLayout) progressPanel.getLayout()).setRows(
1941 ((GridLayout) progressPanel.getLayout()).getRows() + 1);
1942 ++totalProgressCount;
1944 return thisprogress;
1947 int totalProgressCount = 0;
1949 private void removeProgressPanel(JPanel progbar)
1951 if (progressPanel != null)
1953 synchronized (progressPanel)
1955 progressPanel.remove(progbar);
1956 GridLayout gl = (GridLayout) progressPanel.getLayout();
1957 gl.setRows(gl.getRows() - 1);
1958 if (--totalProgressCount < 1)
1960 this.getContentPane().remove(progressPanel);
1961 progressPanel = null;
1968 public void stopLoading()
1971 if (fileLoadingCount < 1)
1973 while (fileLoadingPanels.size() > 0)
1975 removeProgressPanel(fileLoadingPanels.remove(0));
1977 fileLoadingPanels.clear();
1978 fileLoadingCount = 0;
1983 public static int getViewCount(String alignmentId)
1985 AlignmentViewport[] aps = getViewports(alignmentId);
1986 return (aps == null) ? 0 : aps.length;
1991 * @param alignmentId
1992 * - if null, all sets are returned
1993 * @return all AlignmentPanels concerning the alignmentId sequence set
1995 public static AlignmentPanel[] getAlignmentPanels(String alignmentId)
1997 if (Desktop.getDesktopPane() == null)
1999 // no frames created and in headless mode
2000 // TODO: verify that frames are recoverable when in headless mode
2003 List<AlignmentPanel> aps = new ArrayList<>();
2004 AlignFrame[] frames = getAlignFrames();
2009 for (AlignFrame af : frames)
2011 for (AlignmentPanel ap : af.alignPanels)
2013 if (alignmentId == null
2014 || alignmentId.equals(ap.av.getSequenceSetId()))
2020 if (aps.size() == 0)
2024 AlignmentPanel[] vap = aps.toArray(new AlignmentPanel[aps.size()]);
2029 * get all the viewports on an alignment.
2031 * @param sequenceSetId
2032 * unique alignment id (may be null - all viewports returned in that
2034 * @return all viewports on the alignment bound to sequenceSetId
2036 public static AlignmentViewport[] getViewports(String sequenceSetId)
2038 List<AlignmentViewport> viewp = new ArrayList<>();
2039 if (getDesktopPane() != null)
2041 AlignFrame[] frames = Desktop.getAlignFrames();
2043 for (AlignFrame afr : frames)
2045 if (sequenceSetId == null || afr.getViewport().getSequenceSetId()
2046 .equals(sequenceSetId))
2048 if (afr.alignPanels != null)
2050 for (AlignmentPanel ap : afr.alignPanels)
2052 if (sequenceSetId == null
2053 || sequenceSetId.equals(ap.av.getSequenceSetId()))
2061 viewp.add(afr.getViewport());
2065 if (viewp.size() > 0)
2067 return viewp.toArray(new AlignmentViewport[viewp.size()]);
2074 * Explode the views in the given frame into separate AlignFrame
2078 public static void explodeViews(AlignFrame af)
2080 int size = af.alignPanels.size();
2086 for (int i = 0; i < size; i++)
2088 AlignmentPanel ap = af.alignPanels.get(i);
2089 AlignFrame newaf = new AlignFrame(ap);
2092 * Restore the view's last exploded frame geometry if known. Multiple
2093 * views from one exploded frame share and restore the same (frame)
2094 * position and size.
2096 Rectangle geometry = ap.av.getExplodedGeometry();
2097 if (geometry != null)
2099 newaf.setBounds(geometry);
2102 ap.av.setGatherViewsHere(false);
2104 addInternalFrame(newaf, af.getTitle(), AlignFrame.DEFAULT_WIDTH,
2105 AlignFrame.DEFAULT_HEIGHT);
2108 af.alignPanels.clear();
2109 af.closeMenuItem_actionPerformed(true);
2114 * Gather expanded views (separate AlignFrame's) with the same sequence set
2115 * identifier back in to this frame as additional views, and close the expanded
2116 * views. Note the expanded frames may themselves have multiple views. We take
2121 public void gatherViews(AlignFrame source)
2123 source.viewport.setGatherViewsHere(true);
2124 source.viewport.setExplodedGeometry(source.getBounds());
2125 JInternalFrame[] frames = getAllFrames();
2126 String viewId = source.viewport.getSequenceSetId();
2128 for (int t = 0; t < frames.length; t++)
2130 if (frames[t] instanceof AlignFrame && frames[t] != source)
2132 AlignFrame af = (AlignFrame) frames[t];
2133 boolean gatherThis = false;
2134 for (int a = 0; a < af.alignPanels.size(); a++)
2136 AlignmentPanel ap = af.alignPanels.get(a);
2137 if (viewId.equals(ap.av.getSequenceSetId()))
2140 ap.av.setGatherViewsHere(false);
2141 ap.av.setExplodedGeometry(af.getBounds());
2142 source.addAlignmentPanel(ap, false);
2148 af.alignPanels.clear();
2149 af.closeMenuItem_actionPerformed(true);
2156 jalview.gui.VamsasApplication v_client = null;
2159 public void vamsasImport_actionPerformed(ActionEvent e)
2161 // TODO: JAL-3048 not needed for Jalview-JS
2163 if (v_client == null)
2165 // Load and try to start a session.
2166 JalviewFileChooser chooser = new JalviewFileChooser(
2167 jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
2169 chooser.setFileView(new JalviewFileView());
2170 chooser.setDialogTitle(
2171 MessageManager.getString("label.open_saved_vamsas_session"));
2172 chooser.setToolTipText(MessageManager.getString(
2173 "label.select_vamsas_session_opened_as_new_vamsas_session"));
2175 int value = chooser.showOpenDialog(this);
2177 if (value == JalviewFileChooser.APPROVE_OPTION)
2179 String fle = chooser.getSelectedFile().toString();
2180 if (!vamsasImport(chooser.getSelectedFile()))
2182 JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
2183 MessageManager.formatMessage(
2184 "label.couldnt_import_as_vamsas_session",
2188 .getString("label.vamsas_document_import_failed"),
2189 JvOptionPane.ERROR_MESSAGE);
2195 jalview.bin.Cache.log.error(
2196 "Implementation error - load session from a running session is not supported.");
2201 * import file into a new vamsas session (uses jalview.gui.VamsasApplication)
2204 * @return true if import was a success and a session was started.
2206 public boolean vamsasImport(URL url)
2208 // TODO: create progress bar
2209 if (v_client != null)
2212 jalview.bin.Cache.log.error(
2213 "Implementation error - load session from a running session is not supported.");
2219 // copy the URL content to a temporary local file
2220 // TODO: be a bit cleverer here with nio (?!)
2221 File file = File.createTempFile("vdocfromurl", ".vdj");
2222 FileOutputStream fos = new FileOutputStream(file);
2223 BufferedInputStream bis = new BufferedInputStream(url.openStream());
2224 byte[] buffer = new byte[2048];
2226 while ((ln = bis.read(buffer)) > -1)
2228 fos.write(buffer, 0, ln);
2232 v_client = new jalview.gui.VamsasApplication(this, file,
2233 url.toExternalForm());
2234 } catch (Exception ex)
2236 jalview.bin.Cache.log.error(
2237 "Failed to create new vamsas session from contents of URL "
2242 setupVamsasConnectedGui();
2243 v_client.initial_update(); // TODO: thread ?
2244 return v_client.inSession();
2248 * import file into a new vamsas session (uses jalview.gui.VamsasApplication)
2251 * @return true if import was a success and a session was started.
2253 public boolean vamsasImport(File file)
2255 if (v_client != null)
2258 jalview.bin.Cache.log.error(
2259 "Implementation error - load session from a running session is not supported.");
2263 setProgressBar(MessageManager.formatMessage(
2264 "status.importing_vamsas_session_from", new Object[]
2265 { file.getName() }), file.hashCode());
2268 v_client = new jalview.gui.VamsasApplication(this, file, null);
2269 } catch (Exception ex)
2271 setProgressBar(MessageManager.formatMessage(
2272 "status.importing_vamsas_session_from", new Object[]
2273 { file.getName() }), file.hashCode());
2274 jalview.bin.Cache.log.error(
2275 "New vamsas session from existing session file failed:", ex);
2278 setupVamsasConnectedGui();
2279 v_client.initial_update(); // TODO: thread ?
2280 setProgressBar(MessageManager.formatMessage(
2281 "status.importing_vamsas_session_from", new Object[]
2282 { file.getName() }), file.hashCode());
2283 return v_client.inSession();
2286 public boolean joinVamsasSession(String mysesid)
2288 if (v_client != null)
2290 throw new Error(MessageManager
2291 .getString("error.try_join_vamsas_session_another"));
2293 if (mysesid == null)
2296 MessageManager.getString("error.invalid_vamsas_session_id"));
2298 v_client = new VamsasApplication(this, mysesid);
2299 setupVamsasConnectedGui();
2300 v_client.initial_update();
2301 return (v_client.inSession());
2305 public void vamsasStart_actionPerformed(ActionEvent e)
2307 if (v_client == null)
2310 // we just start a default session for moment.
2312 * JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache.
2313 * getProperty("LAST_DIRECTORY"));
2315 * chooser.setFileView(new JalviewFileView());
2316 * chooser.setDialogTitle("Load Vamsas file");
2317 * chooser.setToolTipText("Import");
2319 * int value = chooser.showOpenDialog(this);
2321 * if (value == JalviewFileChooser.APPROVE_OPTION) { v_client = new
2322 * jalview.gui.VamsasApplication(this, chooser.getSelectedFile());
2324 v_client = new VamsasApplication(this);
2325 setupVamsasConnectedGui();
2326 v_client.initial_update(); // TODO: thread ?
2330 // store current data in session.
2331 v_client.push_update(); // TODO: thread
2335 protected void setupVamsasConnectedGui()
2337 vamsasStart.setText(MessageManager.getString("label.session_update"));
2338 vamsasSave.setVisible(true);
2339 vamsasStop.setVisible(true);
2340 vamsasImport.setVisible(false); // Document import to existing session is
2341 // not possible for vamsas-client-1.0.
2344 protected void setupVamsasDisconnectedGui()
2346 vamsasSave.setVisible(false);
2347 vamsasStop.setVisible(false);
2348 vamsasImport.setVisible(true);
2350 .setText(MessageManager.getString("label.new_vamsas_session"));
2354 public void vamsasStop_actionPerformed(ActionEvent e)
2356 if (v_client != null)
2358 v_client.end_session();
2360 setupVamsasDisconnectedGui();
2364 protected void buildVamsasStMenu()
2366 if (v_client == null)
2368 String[] sess = null;
2371 sess = VamsasApplication.getSessionList();
2372 } catch (Exception e)
2374 jalview.bin.Cache.log.warn("Problem getting current sessions list.",
2380 jalview.bin.Cache.log.debug(
2381 "Got current sessions list: " + sess.length + " entries.");
2382 VamsasStMenu.removeAll();
2383 for (int i = 0; i < sess.length; i++)
2385 JMenuItem sessit = new JMenuItem();
2386 sessit.setText(sess[i]);
2387 sessit.setToolTipText(MessageManager
2388 .formatMessage("label.connect_to_session", new Object[]
2390 final Desktop dsktp = this;
2391 final String mysesid = sess[i];
2392 sessit.addActionListener(new ActionListener()
2396 public void actionPerformed(ActionEvent e)
2398 if (dsktp.v_client == null)
2400 Thread rthr = new Thread(new Runnable()
2406 dsktp.v_client = new VamsasApplication(dsktp, mysesid);
2407 dsktp.setupVamsasConnectedGui();
2408 dsktp.v_client.initial_update();
2416 VamsasStMenu.add(sessit);
2418 // don't show an empty menu.
2419 VamsasStMenu.setVisible(sess.length > 0);
2424 jalview.bin.Cache.log.debug("No current vamsas sessions.");
2425 VamsasStMenu.removeAll();
2426 VamsasStMenu.setVisible(false);
2431 // Not interested in the content. Just hide ourselves.
2432 VamsasStMenu.setVisible(false);
2437 public void vamsasSave_actionPerformed(ActionEvent e)
2439 // TODO: JAL-3048 not needed for Jalview-JS
2441 if (v_client != null)
2443 // TODO: VAMSAS DOCUMENT EXTENSION is VDJ
2444 JalviewFileChooser chooser = new JalviewFileChooser("vdj",
2447 chooser.setFileView(new JalviewFileView());
2448 chooser.setDialogTitle(MessageManager
2449 .getString("label.save_vamsas_document_archive"));
2451 int value = chooser.showSaveDialog(this);
2453 if (value == JalviewFileChooser.APPROVE_OPTION)
2455 java.io.File choice = chooser.getSelectedFile();
2456 JPanel progpanel = addProgressPanel(MessageManager
2457 .formatMessage("label.saving_vamsas_doc", new Object[]
2458 { choice.getName() }));
2459 Cache.setProperty("LAST_DIRECTORY", choice.getParent());
2460 String warnmsg = null;
2461 String warnttl = null;
2464 v_client.vclient.storeDocument(choice);
2467 warnttl = "Serious Problem saving Vamsas Document";
2468 warnmsg = ex.toString();
2469 jalview.bin.Cache.log
2470 .error("Error Whilst saving document to " + choice, ex);
2472 } catch (Exception ex)
2474 warnttl = "Problem saving Vamsas Document.";
2475 warnmsg = ex.toString();
2476 jalview.bin.Cache.log.warn(
2477 "Exception Whilst saving document to " + choice, ex);
2480 removeProgressPanel(progpanel);
2481 if (warnmsg != null)
2483 JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
2485 warnmsg, warnttl, JvOptionPane.ERROR_MESSAGE);
2491 JPanel vamUpdate = null;
2494 * hide vamsas user gui bits when a vamsas document event is being handled.
2497 * true to hide gui, false to reveal gui
2499 public void setVamsasUpdate(boolean b)
2501 Cache.log.debug("Setting gui for Vamsas update "
2502 + (b ? "in progress" : "finished"));
2504 if (vamUpdate != null)
2506 this.removeProgressPanel(vamUpdate);
2510 vamUpdate = this.addProgressPanel(
2511 MessageManager.getString("label.updating_vamsas_session"));
2513 vamsasStart.setVisible(!b);
2514 vamsasStop.setVisible(!b);
2515 vamsasSave.setVisible(!b);
2518 public JInternalFrame[] getAllFrames()
2520 return desktopPane.getAllFrames();
2524 * Checks the given url to see if it gives a response indicating that the user
2525 * should be informed of a new questionnaire.
2529 public void checkForQuestionnaire(String url)
2531 UserQuestionnaireCheck jvq = new UserQuestionnaireCheck(url);
2532 // javax.swing.SwingUtilities.invokeLater(jvq);
2533 new Thread(jvq).start();
2536 public void checkURLLinks()
2538 // Thread off the URL link checker
2539 addDialogThread(new Runnable()
2544 if (Cache.getDefault("CHECKURLLINKS", true))
2546 // check what the actual links are - if it's just the default don't
2547 // bother with the warning
2548 List<String> links = Preferences.sequenceUrlLinks
2551 // only need to check links if there is one with a
2552 // SEQUENCE_ID which is not the default EMBL_EBI link
2553 ListIterator<String> li = links.listIterator();
2554 boolean check = false;
2555 List<JLabel> urls = new ArrayList<>();
2556 while (li.hasNext())
2558 String link = li.next();
2559 if (link.contains(UrlConstants.SEQUENCE_ID)
2560 && !UrlConstants.isDefaultString(link))
2563 int barPos = link.indexOf("|");
2564 String urlMsg = barPos == -1 ? link
2565 : link.substring(0, barPos) + ": "
2566 + link.substring(barPos + 1);
2567 urls.add(new JLabel(urlMsg));
2575 // ask user to check in case URL links use old style tokens
2576 // ($SEQUENCE_ID$ for sequence id _or_ accession id)
2577 JPanel msgPanel = new JPanel();
2578 msgPanel.setLayout(new BoxLayout(msgPanel, BoxLayout.PAGE_AXIS));
2579 msgPanel.add(Box.createVerticalGlue());
2580 JLabel msg = new JLabel(MessageManager
2581 .getString("label.SEQUENCE_ID_for_DB_ACCESSION1"));
2582 JLabel msg2 = new JLabel(MessageManager
2583 .getString("label.SEQUENCE_ID_for_DB_ACCESSION2"));
2585 for (JLabel url : urls)
2591 final JCheckBox jcb = new JCheckBox(
2592 MessageManager.getString("label.do_not_display_again"));
2593 jcb.addActionListener(new ActionListener()
2596 public void actionPerformed(ActionEvent e)
2598 // update Cache settings for "don't show this again"
2599 boolean showWarningAgain = !jcb.isSelected();
2600 Cache.setProperty("CHECKURLLINKS",
2601 Boolean.valueOf(showWarningAgain).toString());
2606 JvOptionPane.showMessageDialog(desktopPane, msgPanel,
2608 .getString("label.SEQUENCE_ID_no_longer_used"),
2609 JvOptionPane.WARNING_MESSAGE);
2616 * Proxy class for JDesktopPane which optionally displays the current memory
2617 * usage and highlights the desktop area with a red bar if free memory runs low.
2621 public class MyDesktopPane extends JDesktopPane
2624 private static final float ONE_MB = 1048576f;
2626 boolean showMemoryUsage = false;
2630 java.text.NumberFormat df;
2632 float maxMemory, allocatedMemory, freeMemory, totalFreeMemory,
2635 public MyDesktopPane(boolean showMemoryUsage)
2637 showMemoryUsage(showMemoryUsage);
2640 public void showMemoryUsage(boolean showMemory)
2642 this.showMemoryUsage = showMemory;
2645 Thread worker = new Thread(this);
2651 public boolean isShowMemoryUsage()
2653 return showMemoryUsage;
2659 df = java.text.NumberFormat.getNumberInstance();
2660 df.setMaximumFractionDigits(2);
2661 runtime = Runtime.getRuntime();
2663 while (showMemoryUsage)
2667 maxMemory = runtime.maxMemory() / ONE_MB;
2668 allocatedMemory = runtime.totalMemory() / ONE_MB;
2669 freeMemory = runtime.freeMemory() / ONE_MB;
2670 totalFreeMemory = freeMemory + (maxMemory - allocatedMemory);
2672 percentUsage = (totalFreeMemory / maxMemory) * 100;
2674 // if (percentUsage < 20)
2676 // border1 = BorderFactory.createMatteBorder(12, 12, 12, 12,
2678 // instance.set.setBorder(border1);
2681 // sleep after showing usage
2683 } catch (Exception ex)
2685 ex.printStackTrace();
2691 public void paintComponent(Graphics g)
2693 if (showMemoryUsage && g != null && df != null)
2695 if (percentUsage < 20)
2697 g.setColor(Color.red);
2699 FontMetrics fm = g.getFontMetrics();
2702 g.drawString(MessageManager.formatMessage("label.memory_stats",
2704 { df.format(totalFreeMemory), df.format(maxMemory),
2705 df.format(percentUsage) }),
2706 10, getHeight() - fm.getHeight());
2713 * Accessor method to quickly get all the AlignmentFrames loaded.
2715 * @return an array of AlignFrame, or null if none found
2717 public static AlignFrame[] getAlignFrames()
2719 if (Jalview.isHeadlessMode())
2721 // Desktop.getDesktop() is null in headless mode
2722 return new AlignFrame[] { Jalview.getCurrentAlignFrame() };
2725 JInternalFrame[] frames = Desktop.getDesktopPane().getAllFrames();
2731 List<AlignFrame> avp = new ArrayList<>();
2733 for (int i = frames.length - 1; i > -1; i--)
2735 if (frames[i] instanceof AlignFrame)
2737 avp.add((AlignFrame) frames[i]);
2739 else if (frames[i] instanceof SplitFrame)
2742 * Also check for a split frame containing an AlignFrame
2744 GSplitFrame sf = (GSplitFrame) frames[i];
2745 if (sf.getTopFrame() instanceof AlignFrame)
2747 avp.add((AlignFrame) sf.getTopFrame());
2749 if (sf.getBottomFrame() instanceof AlignFrame)
2751 avp.add((AlignFrame) sf.getBottomFrame());
2755 if (avp.size() == 0)
2759 AlignFrame afs[] = avp.toArray(new AlignFrame[avp.size()]);
2764 * Returns an array of any AppJmol frames in the Desktop (or null if none).
2768 public GStructureViewer[] getJmols()
2770 JInternalFrame[] frames = Desktop.getDesktopPane().getAllFrames();
2776 List<GStructureViewer> avp = new ArrayList<>();
2778 for (int i = frames.length - 1; i > -1; i--)
2780 if (frames[i] instanceof AppJmol)
2782 GStructureViewer af = (GStructureViewer) frames[i];
2786 if (avp.size() == 0)
2790 GStructureViewer afs[] = avp.toArray(new GStructureViewer[avp.size()]);
2795 * Add Groovy Support to Jalview
2798 public void groovyShell_actionPerformed()
2802 openGroovyConsole();
2803 } catch (Exception ex)
2805 jalview.bin.Cache.log.error("Groovy Shell Creation failed.", ex);
2806 JvOptionPane.showInternalMessageDialog(desktopPane,
2808 MessageManager.getString("label.couldnt_create_groovy_shell"),
2809 MessageManager.getString("label.groovy_support_failed"),
2810 JvOptionPane.ERROR_MESSAGE);
2815 * Open the Groovy console
2817 private void openGroovyConsole()
2819 if (groovyConsole == null)
2821 groovyConsole = new groovy.ui.Console();
2822 groovyConsole.setVariable("Jalview", this);
2823 groovyConsole.run();
2826 * We allow only one console at a time, so that AlignFrame menu option
2827 * 'Calculate | Run Groovy script' is unambiguous.
2828 * Disable 'Groovy Console', and enable 'Run script', when the console is
2829 * opened, and the reverse when it is closed
2831 Window window = (Window) groovyConsole.getFrame();
2832 window.addWindowListener(new WindowAdapter()
2835 public void windowClosed(WindowEvent e)
2838 * rebind CMD-Q from Groovy Console to Jalview Quit
2841 enableExecuteGroovy(false);
2847 * show Groovy console window (after close and reopen)
2849 ((Window) groovyConsole.getFrame()).setVisible(true);
2852 * if we got this far, enable 'Run Groovy' in AlignFrame menus
2853 * and disable opening a second console
2855 enableExecuteGroovy(true);
2859 * Bind Ctrl/Cmd-Q to Quit - for reset as Groovy Console takes over this binding
2862 protected void addQuitHandler()
2864 getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
2865 .put(KeyStroke.getKeyStroke(KeyEvent.VK_Q,
2866 Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()),
2868 getRootPane().getActionMap().put("Quit", new AbstractAction()
2871 public void actionPerformed(ActionEvent e)
2879 * Enable or disable 'Run Groovy script' in AlignFrame calculate menus
2882 * true if Groovy console is open
2884 public void enableExecuteGroovy(boolean enabled)
2887 * disable opening a second Groovy console
2888 * (or re-enable when the console is closed)
2890 groovyShell.setEnabled(!enabled);
2892 AlignFrame[] alignFrames = getAlignFrames();
2893 if (alignFrames != null)
2895 for (AlignFrame af : alignFrames)
2897 af.setGroovyEnabled(enabled);
2903 * Progress bars managed by the IProgressIndicator method.
2905 private Hashtable<Long, JPanel> progressBars;
2907 private Hashtable<Long, IProgressIndicatorHandler> progressBarHandlers;
2912 * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
2915 public void setProgressBar(String message, long id)
2917 Platform.timeCheck("Desktop " + message, Platform.TIME_MARK);
2919 if (progressBars == null)
2921 progressBars = new Hashtable<>();
2922 progressBarHandlers = new Hashtable<>();
2925 if (progressBars.get(new Long(id)) != null)
2927 JPanel panel = progressBars.remove(new Long(id));
2928 if (progressBarHandlers.contains(new Long(id)))
2930 progressBarHandlers.remove(new Long(id));
2932 removeProgressPanel(panel);
2936 progressBars.put(new Long(id), addProgressPanel(message));
2943 * @see jalview.gui.IProgressIndicator#registerHandler(long,
2944 * jalview.gui.IProgressIndicatorHandler)
2947 public void registerHandler(final long id,
2948 final IProgressIndicatorHandler handler)
2950 if (progressBarHandlers == null
2951 || !progressBars.containsKey(new Long(id)))
2953 throw new Error(MessageManager.getString(
2954 "error.call_setprogressbar_before_registering_handler"));
2956 progressBarHandlers.put(new Long(id), handler);
2957 final JPanel progressPanel = progressBars.get(new Long(id));
2958 if (handler.canCancel())
2960 JButton cancel = new JButton(
2961 MessageManager.getString("action.cancel"));
2962 final IProgressIndicator us = this;
2963 cancel.addActionListener(new ActionListener()
2967 public void actionPerformed(ActionEvent e)
2969 handler.cancelActivity(id);
2970 us.setProgressBar(MessageManager
2971 .formatMessage("label.cancelled_params", new Object[]
2972 { ((JLabel) progressPanel.getComponent(0)).getText() }),
2976 progressPanel.add(cancel, BorderLayout.EAST);
2982 * @return true if any progress bars are still active
2985 public boolean operationInProgress()
2987 if (progressBars != null && progressBars.size() > 0)
2995 * This will return the first AlignFrame holding the given viewport instance. It
2996 * will break if there are more than one AlignFrames viewing a particular av.
2999 * @return alignFrame for viewport
3001 public static AlignFrame getAlignFrameFor(AlignViewportI viewport)
3003 if (getDesktopPane() != null)
3005 AlignmentPanel[] aps = getAlignmentPanels(
3006 viewport.getSequenceSetId());
3007 for (int panel = 0; aps != null && panel < aps.length; panel++)
3009 if (aps[panel] != null && aps[panel].av == viewport)
3011 return aps[panel].alignFrame;
3018 public VamsasApplication getVamsasApplication()
3025 * flag set if jalview GUI is being operated programmatically
3027 private boolean inBatchMode = false;
3030 * check if jalview GUI is being operated programmatically
3032 * @return inBatchMode
3034 public boolean isInBatchMode()
3040 * set flag if jalview GUI is being operated programmatically
3042 * @param inBatchMode
3044 public void setInBatchMode(boolean inBatchMode)
3046 this.inBatchMode = inBatchMode;
3049 public void startServiceDiscovery()
3051 startServiceDiscovery(false);
3054 public void startServiceDiscovery(boolean blocking)
3056 boolean alive = true;
3057 Thread t0 = null, t1 = null, t2 = null;
3058 // JAL-940 - JALVIEW 1 services are now being EOLed as of JABA 2.1 release
3061 // todo: changesupport handlers need to be transferred
3062 if (discoverer == null)
3064 discoverer = Discoverer.getInstance();
3065 // register PCS handler for getDesktop().
3066 discoverer.addPropertyChangeListener(changeSupport);
3068 // JAL-940 - disabled JWS1 service configuration - always start discoverer
3069 // until we phase out completely
3070 (t0 = new Thread(discoverer)).start();
3073 if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
3075 t2 = jalview.ws.jws2.Jws2Discoverer.getInstance()
3076 .startDiscoverer(changeSupport);
3080 // TODO: do rest service discovery
3089 } catch (Exception e)
3092 alive = (t1 != null && t1.isAlive()) || (t2 != null && t2.isAlive())
3093 || (t3 != null && t3.isAlive())
3094 || (t0 != null && t0.isAlive());
3100 * called to check if the service discovery process completed successfully.
3104 protected void JalviewServicesChanged(PropertyChangeEvent evt)
3106 if (evt.getNewValue() == null || evt.getNewValue() instanceof Vector)
3108 final String ermsg = jalview.ws.jws2.Jws2Discoverer.getInstance()
3109 .getErrorMessages();
3112 if (Cache.getDefault("SHOW_WSDISCOVERY_ERRORS", true))
3114 if (serviceChangedDialog == null)
3116 // only run if we aren't already displaying one of these.
3117 addDialogThread(serviceChangedDialog = new Runnable()
3124 * JalviewDialog jd =new JalviewDialog() {
3126 * @Override protected void cancelPressed() { // TODO
3127 * Auto-generated method stub
3129 * }@Override protected void okPressed() { // TODO
3130 * Auto-generated method stub
3132 * }@Override protected void raiseClosed() { // TODO
3133 * Auto-generated method stub
3135 * } }; jd.initDialogFrame(new
3136 * JLabel("<html><table width=\"450\"><tr><td>" + ermsg +
3137 * "<br/>It may be that you have invalid JABA URLs in your web service preferences,"
3138 * + " or mis-configured HTTP proxy settings.<br/>" +
3139 * "Check the <em>Connections</em> and <em>Web services</em> tab of the"
3141 * " Tools->Preferences dialog box to change them.</td></tr></table></html>"
3142 * ), true, true, "Web Service Configuration Problem", 450,
3145 * jd.waitForInput();
3147 JvOptionPane.showConfirmDialog(Desktop.getDesktopPane(),
3148 new JLabel("<html><table width=\"450\"><tr><td>"
3149 + ermsg + "</td></tr></table>"
3150 + "<p>It may be that you have invalid JABA URLs<br/>in your web service preferences,"
3151 + "<br>or as a command-line argument, or mis-configured HTTP proxy settings.</p>"
3152 + "<p>Check the <em>Connections</em> and <em>Web services</em> tab<br/>of the"
3153 + " Tools->Preferences dialog box to change them.</p></html>"),
3154 "Web Service Configuration Problem",
3155 JvOptionPane.DEFAULT_OPTION,
3156 JvOptionPane.ERROR_MESSAGE);
3157 serviceChangedDialog = null;
3166 "Errors reported by JABA discovery service. Check web services preferences.\n"
3173 Runnable serviceChangedDialog = null;
3176 * start a thread to open a URL in the configured browser. Pops up a warning
3177 * dialog to the user if there is an exception when calling out to the browser
3182 public static void showUrl(final String url)
3184 showUrl(url, Desktop.getInstance());
3188 * Like showUrl but allows progress handler to be specified
3192 * (null) or object implementing IProgressIndicator
3194 public static void showUrl(final String url,
3195 final IProgressIndicator progress)
3197 new Thread(new Runnable()
3204 if (progress != null)
3206 progress.setProgressBar(MessageManager
3207 .formatMessage("status.opening_params", new Object[]
3208 { url }), this.hashCode());
3210 BrowserLauncher.openURL(url);
3211 } catch (Exception ex)
3213 JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
3215 .getString("label.web_browser_not_found_unix"),
3216 MessageManager.getString("label.web_browser_not_found"),
3217 JvOptionPane.WARNING_MESSAGE);
3219 ex.printStackTrace();
3221 if (progress != null)
3223 progress.setProgressBar(null, this.hashCode());
3229 private WsParamSetManager wsparamManager = null;
3231 public static ParamManager getUserParameterStore()
3233 Desktop d = Desktop.getInstance();
3234 if (d.wsparamManager == null)
3236 d.wsparamManager = new WsParamSetManager();
3238 return d.wsparamManager;
3242 * static hyperlink handler proxy method for use by Jalview's internal windows
3246 public static void hyperlinkUpdate(HyperlinkEvent e)
3248 if (e.getEventType() == EventType.ACTIVATED)
3253 url = e.getURL().toString();
3254 Desktop.showUrl(url);
3255 } catch (Exception x)
3259 if (Cache.log != null)
3261 Cache.log.error("Couldn't handle string " + url + " as a URL.");
3266 "Couldn't handle string " + url + " as a URL.");
3269 // ignore any exceptions due to dud links.
3276 * single thread that handles display of dialogs to user.
3278 ExecutorService dialogExecutor = Executors.newSingleThreadExecutor();
3281 * flag indicating if dialogExecutor should try to acquire a permit
3283 volatile boolean dialogPause = true;
3288 java.util.concurrent.Semaphore block = new Semaphore(0);
3290 private groovy.ui.Console groovyConsole;
3292 public StructureViewer lastTargetedView;
3295 * add another dialog thread to the queue
3299 public void addDialogThread(final Runnable prompter)
3301 dialogExecutor.submit(new Runnable()
3311 } catch (InterruptedException x)
3315 if (instanceOnly || Jalview.isHeadlessMode())
3321 SwingUtilities.invokeAndWait(prompter);
3322 } catch (Exception q)
3324 Cache.log.warn("Unexpected Exception in dialog thread.", q);
3330 public void startDialogQueue()
3332 // set the flag so we don't pause waiting for another permit and semaphore
3333 // the current task to begin
3334 dialogPause = false;
3339 * Outputs an image of the desktop to file in EPS format, after prompting the
3340 * user for choice of Text or Lineart character rendering (unless a preference
3341 * has been set). The file name is generated as
3344 * Jalview_snapshot_nnnnn.eps where nnnnn is the current timestamp in milliseconds
3348 protected void snapShotWindow_actionPerformed(ActionEvent e)
3350 // currently the menu option to do this is not shown
3353 int width = getWidth();
3354 int height = getHeight();
3356 "Jalview_snapshot_" + System.currentTimeMillis() + ".eps");
3357 ImageWriterI writer = new ImageWriterI()
3360 public void exportImage(Graphics g) throws Exception
3363 Cache.log.info("Successfully written snapshot to file "
3364 + of.getAbsolutePath());
3367 String title = "View of desktop";
3368 ImageExporter exporter = new ImageExporter(writer, null, TYPE.EPS,
3370 exporter.doExport(of, this, width, height, title);
3374 * Explode the views in the given SplitFrame into separate SplitFrame windows.
3375 * This respects (remembers) any previous 'exploded geometry' i.e. the size and
3376 * location last time the view was expanded (if any). However it does not
3377 * remember the split pane divider location - this is set to match the
3378 * 'exploding' frame.
3382 public void explodeViews(SplitFrame sf)
3384 AlignFrame oldTopFrame = (AlignFrame) sf.getTopFrame();
3385 AlignFrame oldBottomFrame = (AlignFrame) sf.getBottomFrame();
3386 List<? extends AlignmentViewPanel> topPanels = oldTopFrame
3388 List<? extends AlignmentViewPanel> bottomPanels = oldBottomFrame
3390 int viewCount = topPanels.size();
3397 * Processing in reverse order works, forwards order leaves the first panels
3398 * not visible. I don't know why!
3400 for (int i = viewCount - 1; i >= 0; i--)
3403 * Make new top and bottom frames. These take over the respective
3404 * AlignmentPanel objects, including their AlignmentViewports, so the
3405 * cdna/protein relationships between the viewports is carried over to the
3408 * explodedGeometry holds the (x, y) position of the previously exploded
3409 * SplitFrame, and the (width, height) of the AlignFrame component
3411 AlignmentPanel topPanel = (AlignmentPanel) topPanels.get(i);
3412 AlignFrame newTopFrame = new AlignFrame(topPanel);
3413 newTopFrame.setSize(oldTopFrame.getSize());
3414 newTopFrame.setVisible(true);
3415 Rectangle geometry = ((AlignViewport) topPanel.getAlignViewport())
3416 .getExplodedGeometry();
3417 if (geometry != null)
3419 newTopFrame.setSize(geometry.getSize());
3422 AlignmentPanel bottomPanel = (AlignmentPanel) bottomPanels.get(i);
3423 AlignFrame newBottomFrame = new AlignFrame(bottomPanel);
3424 newBottomFrame.setSize(oldBottomFrame.getSize());
3425 newBottomFrame.setVisible(true);
3426 geometry = ((AlignViewport) bottomPanel.getAlignViewport())
3427 .getExplodedGeometry();
3428 if (geometry != null)
3430 newBottomFrame.setSize(geometry.getSize());
3433 topPanel.av.setGatherViewsHere(false);
3434 bottomPanel.av.setGatherViewsHere(false);
3435 JInternalFrame splitFrame = new SplitFrame(newTopFrame,
3437 if (geometry != null)
3439 splitFrame.setLocation(geometry.getLocation());
3441 Desktop.addInternalFrame(splitFrame, sf.getTitle(), -1, -1);
3445 * Clear references to the panels (now relocated in the new SplitFrames)
3446 * before closing the old SplitFrame.
3449 bottomPanels.clear();
3454 * Gather expanded split frames, sharing the same pairs of sequence set ids,
3455 * back into the given SplitFrame as additional views. Note that the gathered
3456 * frames may themselves have multiple views.
3460 public void gatherViews(GSplitFrame source)
3463 * special handling of explodedGeometry for a view within a SplitFrame: - it
3464 * holds the (x, y) position of the enclosing SplitFrame, and the (width,
3465 * height) of the AlignFrame component
3467 AlignFrame myTopFrame = (AlignFrame) source.getTopFrame();
3468 AlignFrame myBottomFrame = (AlignFrame) source.getBottomFrame();
3469 myTopFrame.viewport.setExplodedGeometry(new Rectangle(source.getX(),
3470 source.getY(), myTopFrame.getWidth(), myTopFrame.getHeight()));
3471 myBottomFrame.viewport
3472 .setExplodedGeometry(new Rectangle(source.getX(), source.getY(),
3473 myBottomFrame.getWidth(), myBottomFrame.getHeight()));
3474 myTopFrame.viewport.setGatherViewsHere(true);
3475 myBottomFrame.viewport.setGatherViewsHere(true);
3476 String topViewId = myTopFrame.viewport.getSequenceSetId();
3477 String bottomViewId = myBottomFrame.viewport.getSequenceSetId();
3479 JInternalFrame[] frames = desktopPane.getAllFrames();
3480 for (JInternalFrame frame : frames)
3482 if (frame instanceof SplitFrame && frame != source)
3484 SplitFrame sf = (SplitFrame) frame;
3485 AlignFrame topFrame = (AlignFrame) sf.getTopFrame();
3486 AlignFrame bottomFrame = (AlignFrame) sf.getBottomFrame();
3487 boolean gatherThis = false;
3488 for (int a = 0; a < topFrame.alignPanels.size(); a++)
3490 AlignmentPanel topPanel = topFrame.alignPanels.get(a);
3491 AlignmentPanel bottomPanel = bottomFrame.alignPanels.get(a);
3492 if (topViewId.equals(topPanel.av.getSequenceSetId())
3493 && bottomViewId.equals(bottomPanel.av.getSequenceSetId()))
3496 topPanel.av.setGatherViewsHere(false);
3497 bottomPanel.av.setGatherViewsHere(false);
3498 topPanel.av.setExplodedGeometry(
3499 new Rectangle(sf.getLocation(), topFrame.getSize()));
3500 bottomPanel.av.setExplodedGeometry(
3501 new Rectangle(sf.getLocation(), bottomFrame.getSize()));
3502 myTopFrame.addAlignmentPanel(topPanel, false);
3503 myBottomFrame.addAlignmentPanel(bottomPanel, false);
3509 topFrame.getAlignPanels().clear();
3510 bottomFrame.getAlignPanels().clear();
3517 * The dust settles...give focus to the tab we did this from.
3519 myTopFrame.setDisplayedView(myTopFrame.alignPanel);
3522 public static groovy.ui.Console getGroovyConsole()
3524 Desktop desktop = Desktop.getInstance();
3525 return desktop == null ? null : desktop.groovyConsole;
3529 * handles the payload of a drag and drop event.
3531 * TODO refactor to desktop utilities class
3534 * - Data source strings extracted from the drop event
3536 * - protocol for each data source extracted from the drop event
3540 * - the payload from the drop event
3543 @SuppressWarnings("unchecked")
3544 public static void transferFromDropTarget(List<Object> files,
3545 List<DataSourceType> protocols, DropTargetDropEvent evt,
3546 Transferable t) throws Exception
3549 // BH 2018 changed List<String> to List<Object> to allow for File from SwingJS
3551 // DataFlavor[] flavors = t.getTransferDataFlavors();
3552 // for (int i = 0; i < flavors.length; i++) {
3553 // if (flavors[i].isFlavorJavaFileListType()) {
3554 // evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
3555 // List<File> list = (List<File>) t.getTransferData(flavors[i]);
3556 // for (int j = 0; j < list.size(); j++) {
3557 // File file = (File) list.get(j);
3558 // byte[] data = getDroppedFileBytes(file);
3559 // fileName.setText(file.getName() + " - " + data.length + " " +
3560 // evt.getLocation());
3561 // JTextArea target = (JTextArea) ((DropTarget) evt.getSource()).getComponent();
3562 // target.setText(new String(data));
3564 // dtde.dropComplete(true);
3569 DataFlavor uriListFlavor = new DataFlavor(
3570 "text/uri-list;class=java.lang.String"), urlFlavour = null;
3573 urlFlavour = new DataFlavor(
3574 "application/x-java-url; class=java.net.URL");
3575 } catch (ClassNotFoundException cfe)
3577 Cache.log.debug("Couldn't instantiate the URL dataflavor.", cfe);
3580 if (urlFlavour != null && t.isDataFlavorSupported(urlFlavour))
3585 java.net.URL url = (URL) t.getTransferData(urlFlavour);
3586 // nb: java 8 osx bug https://bugs.openjdk.java.net/browse/JDK-8156099
3587 // means url may be null.
3590 protocols.add(DataSourceType.URL);
3591 files.add(url.toString());
3592 Cache.log.debug("Drop handled as URL dataflavor "
3593 + files.get(files.size() - 1));
3598 if (Platform.isAMacAndNotJS())
3601 "Please ignore plist error - occurs due to problem with java 8 on OSX");
3604 } catch (Throwable ex)
3606 Cache.log.debug("URL drop handler failed.", ex);
3609 if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
3611 // Works on Windows and MacOSX
3612 Cache.log.debug("Drop handled as javaFileListFlavor");
3613 for (Object file : (List<Object>) t
3614 .getTransferData(DataFlavor.javaFileListFlavor))
3617 protocols.add(DataSourceType.FILE);
3622 // Unix like behaviour
3623 boolean added = false;
3625 if (t.isDataFlavorSupported(uriListFlavor))
3627 Cache.log.debug("Drop handled as uriListFlavor");
3628 // This is used by Unix drag system
3629 data = (String) t.getTransferData(uriListFlavor);
3633 // fallback to text: workaround - on OSX where there's a JVM bug
3634 Cache.log.debug("standard URIListFlavor failed. Trying text");
3635 // try text fallback
3636 DataFlavor textDf = new DataFlavor(
3637 "text/plain;class=java.lang.String");
3638 if (t.isDataFlavorSupported(textDf))
3640 data = (String) t.getTransferData(textDf);
3643 Cache.log.debug("Plain text drop content returned "
3644 + (data == null ? "Null - failed" : data));
3649 while (protocols.size() < files.size())
3651 Cache.log.debug("Adding missing FILE protocol for "
3652 + files.get(protocols.size()));
3653 protocols.add(DataSourceType.FILE);
3655 for (java.util.StringTokenizer st = new java.util.StringTokenizer(
3656 data, "\r\n"); st.hasMoreTokens();)
3659 String s = st.nextToken();
3660 if (s.startsWith("#"))
3662 // the line is a comment (as per the RFC 2483)
3665 java.net.URI uri = new java.net.URI(s);
3666 if (uri.getScheme().toLowerCase().startsWith("http"))
3668 protocols.add(DataSourceType.URL);
3669 files.add(uri.toString());
3673 // otherwise preserve old behaviour: catch all for file objects
3674 java.io.File file = new java.io.File(uri);
3675 protocols.add(DataSourceType.FILE);
3676 files.add(file.toString());
3681 if (Cache.log.isDebugEnabled())
3683 if (data == null || !added)
3686 if (t.getTransferDataFlavors() != null
3687 && t.getTransferDataFlavors().length > 0)
3690 "Couldn't resolve drop data. Here are the supported flavors:");
3691 for (DataFlavor fl : t.getTransferDataFlavors())
3694 "Supported transfer dataflavor: " + fl.toString());
3695 Object df = t.getTransferData(fl);
3698 Cache.log.debug("Retrieves: " + df);
3702 Cache.log.debug("Retrieved nothing");
3708 Cache.log.debug("Couldn't resolve dataflavor for drop: "
3714 if (Platform.isWindowsAndNotJS())
3716 Cache.log.debug("Scanning dropped content for Windows Link Files");
3718 // resolve any .lnk files in the file drop
3719 for (int f = 0; f < files.size(); f++)
3721 String source = files.get(f).toString().toLowerCase();
3722 if (protocols.get(f).equals(DataSourceType.FILE)
3723 && (source.endsWith(".lnk") || source.endsWith(".url")
3724 || source.endsWith(".site")))
3728 Object obj = files.get(f);
3729 File lf = (obj instanceof File ? (File) obj
3730 : new File((String) obj));
3731 // process link file to get a URL
3732 Cache.log.debug("Found potential link file: " + lf);
3733 WindowsShortcut wscfile = new WindowsShortcut(lf);
3734 String fullname = wscfile.getRealFilename();
3735 protocols.set(f, FormatAdapter.checkProtocol(fullname));
3736 files.set(f, fullname);
3737 Cache.log.debug("Parsed real filename " + fullname
3738 + " to extract protocol: " + protocols.get(f));
3739 } catch (Exception ex)
3742 "Couldn't parse " + files.get(f) + " as a link file.",
3751 * Sets the Preferences property for experimental features to True or False
3752 * depending on the state of the controlling menu item
3755 protected void showExperimental_actionPerformed(boolean selected)
3757 Cache.setProperty(EXPERIMENTAL_FEATURES, Boolean.toString(selected));
3761 * Answers a (possibly empty) list of any structure viewer frames (currently for
3762 * either Jmol or Chimera) which are currently open. This may optionally be
3763 * restricted to viewers of a specified class, or viewers linked to a specified
3767 * if not null, only return viewers linked to this panel
3768 * @param structureViewerClass
3769 * if not null, only return viewers of this class
3772 public List<StructureViewerBase> getStructureViewers(
3773 AlignmentPanel apanel,
3774 Class<? extends StructureViewerBase> structureViewerClass)
3776 List<StructureViewerBase> result = new ArrayList<>();
3777 JInternalFrame[] frames = getAllFrames();
3779 for (JInternalFrame frame : frames)
3781 if (frame instanceof StructureViewerBase)
3783 if (structureViewerClass == null
3784 || structureViewerClass.isInstance(frame))
3787 || ((StructureViewerBase) frame).isLinkedWith(apanel))
3789 result.add((StructureViewerBase) frame);