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;
230 class MyDesktopManager implements DesktopManager
233 private DesktopManager delegate;
235 public MyDesktopManager(DesktopManager delegate)
237 this.delegate = delegate;
241 public void activateFrame(JInternalFrame f)
245 delegate.activateFrame(f);
246 } catch (NullPointerException npe)
248 Point p = getMousePosition();
249 showPasteMenu(p.x, p.y);
254 public void beginDraggingFrame(JComponent f)
256 delegate.beginDraggingFrame(f);
260 public void beginResizingFrame(JComponent f, int direction)
262 delegate.beginResizingFrame(f, direction);
266 public void closeFrame(JInternalFrame f)
268 delegate.closeFrame(f);
272 public void deactivateFrame(JInternalFrame f)
274 delegate.deactivateFrame(f);
278 public void deiconifyFrame(JInternalFrame f)
280 delegate.deiconifyFrame(f);
284 public void dragFrame(JComponent f, int newX, int newY)
290 delegate.dragFrame(f, newX, newY);
294 public void endDraggingFrame(JComponent f)
296 delegate.endDraggingFrame(f);
297 desktopPane.repaint();
301 public void endResizingFrame(JComponent f)
303 delegate.endResizingFrame(f);
304 desktopPane.repaint();
308 public void iconifyFrame(JInternalFrame f)
310 delegate.iconifyFrame(f);
314 public void maximizeFrame(JInternalFrame f)
316 delegate.maximizeFrame(f);
320 public void minimizeFrame(JInternalFrame f)
322 delegate.minimizeFrame(f);
326 public void openFrame(JInternalFrame f)
328 delegate.openFrame(f);
332 public void resizeFrame(JComponent f, int newX, int newY, int newWidth,
339 delegate.resizeFrame(f, newX, newY, newWidth, newHeight);
343 public void setBoundsForFrame(JComponent f, int newX, int newY,
344 int newWidth, int newHeight)
346 delegate.setBoundsForFrame(f, newX, newY, newWidth, newHeight);
349 // All other methods, simply delegate
353 public MyDesktopPane desktopPane;
356 * Answers an 'application scope' singleton instance of this class. Separate
357 * SwingJS 'applets' running in the same browser page will each have a
358 * distinct instance of Desktop.
362 public static Desktop getInstance()
364 return Jalview.isHeadlessMode() ? null
365 : (Desktop) ApplicationSingletonProvider
366 .getInstance(Desktop.class);
370 * Private constructor enforces singleton pattern. It is called by reflection
371 * from ApplicationSingletonProvider.getInstance().
379 * A note to implementors. It is ESSENTIAL that any activities that might
380 * block are spawned off as threads rather than waited for during this
383 if (!Platform.isJS())
385 doVamsasClientCheck();
388 doConfigureStructurePrefs();
389 setTitle("Jalview " + jalview.bin.Cache.getProperty("VERSION"));
390 setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
391 boolean selmemusage = jalview.bin.Cache.getDefault("SHOW_MEMUSAGE",
393 boolean showjconsole = jalview.bin.Cache
394 .getDefault("SHOW_JAVA_CONSOLE", false);
395 desktopPane = new MyDesktopPane(selmemusage);
397 showMemusage.setSelected(selmemusage);
398 desktopPane.setBackground(Color.white);
399 getContentPane().setLayout(new BorderLayout());
400 // alternate config - have scrollbars - see notes in JAL-153
401 // JScrollPane sp = new JScrollPane();
402 // sp.getViewport().setView(desktop);
403 // getContentPane().add(sp, BorderLayout.CENTER);
405 // BH 2018 - just an experiment to try unclipped JInternalFrames.
408 getRootPane().putClientProperty("swingjs.overflow.hidden", "false");
411 getContentPane().add(desktopPane, BorderLayout.CENTER);
412 desktopPane.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
414 // This line prevents Windows Look&Feel resizing all new windows to
416 // if previous window was maximised
417 desktopPane.setDesktopManager(new MyDesktopManager(
418 (Platform.isWindowsAndNotJS() ? new DefaultDesktopManager()
419 : Platform.isAMacAndNotJS()
420 ? new AquaInternalFrameManager(
421 desktopPane.getDesktopManager())
422 : desktopPane.getDesktopManager())));
424 Rectangle dims = getLastKnownDimensions("");
431 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
432 int xPos = Math.max(5, (screenSize.width - 900) / 2);
433 int yPos = Math.max(5, (screenSize.height - 650) / 2);
434 setBounds(xPos, yPos, 900, 650);
437 // Note that this next syntax, checking for Platform.isJS and also
438 // escaping the code using @j2sIgnore, serves two purposes. It gives
439 // us an easily findable tag, Platform.isJS(), to places in the code where
440 // there is something different about the SwingJS implementation. Second,
441 // it deletes the unneeded Java-only code form the JavaScript version
442 // completely (@j2sIgnore), since it will never be used there.
444 if (!Platform.isJS() && !Jalview.isSynchronous())
452 jconsole = new Console(this, showjconsole);
453 // add essential build information
454 jconsole.setHeader("Jalview Version: "
455 + jalview.bin.Cache.getProperty("VERSION") + "\n"
456 + "Jalview Installation: "
457 + jalview.bin.Cache.getDefault("INSTALLATION", "unknown")
458 + "\n" + "Build Date: "
459 + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown")
460 + "\n" + "Java version: "
461 + System.getProperty("java.version") + "\n"
462 + System.getProperty("os.arch") + " "
463 + System.getProperty("os.name") + " "
464 + System.getProperty("os.version"));
466 showConsole(showjconsole);
468 showNews.setVisible(false);
470 experimentalFeatures.setSelected(showExperimental());
472 getIdentifiersOrgData();
476 // Spawn a thread that shows the splashscreen
478 SwingUtilities.invokeLater(new Runnable()
487 // Thread off a new instance of the file chooser - this reduces the time
489 // takes to open it later on.
490 new Thread(new Runnable()
495 Cache.log.debug("Filechooser init thread started.");
496 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
497 JalviewFileChooser.forRead(Cache.getProperty("LAST_DIRECTORY"),
499 Cache.log.debug("Filechooser init thread finished.");
502 // Add the service change listener
503 changeSupport.addJalviewPropertyChangeListener("services",
504 new PropertyChangeListener()
508 public void propertyChange(PropertyChangeEvent evt)
510 Cache.log.debug("Firing service changed event for "
511 + evt.getNewValue());
512 JalviewServicesChanged(evt);
519 if (!Jalview.isSynchronous())
521 this.setDropTarget(new java.awt.dnd.DropTarget(desktopPane, this));
523 this.addWindowListener(new WindowAdapter()
526 public void windowClosing(WindowEvent evt)
533 this.addMouseListener(ma = new MouseAdapter()
536 public void mousePressed(MouseEvent evt)
538 if (evt.isPopupTrigger()) // Mac
540 showPasteMenu(evt.getX(), evt.getY());
545 public void mouseReleased(MouseEvent evt)
547 if (evt.isPopupTrigger()) // Windows
549 showPasteMenu(evt.getX(), evt.getY());
553 desktopPane.addMouseListener(ma);
555 } catch (Throwable t)
562 * Answers true if user preferences to enable experimental features is True
567 public boolean showExperimental()
569 String experimental = Cache.getDefault(EXPERIMENTAL_FEATURES,
570 Boolean.FALSE.toString());
571 return Boolean.valueOf(experimental).booleanValue();
574 public void doConfigureStructurePrefs()
576 // configure services
577 StructureSelectionManager ssm = StructureSelectionManager
578 .getStructureSelectionManager(this);
579 if (jalview.bin.Cache.getDefault(Preferences.ADD_SS_ANN, true))
581 ssm.setAddTempFacAnnot(jalview.bin.Cache
582 .getDefault(Preferences.ADD_TEMPFACT_ANN, true));
583 ssm.setProcessSecondaryStructure(jalview.bin.Cache
584 .getDefault(Preferences.STRUCT_FROM_PDB, true));
585 ssm.setSecStructServices(
586 jalview.bin.Cache.getDefault(Preferences.USE_RNAVIEW, true));
590 ssm.setAddTempFacAnnot(false);
591 ssm.setProcessSecondaryStructure(false);
592 ssm.setSecStructServices(false);
596 public void checkForNews()
598 final Desktop me = this;
599 // Thread off the news reader, in case there are connection problems.
600 new Thread(new Runnable()
605 Cache.log.debug("Starting news thread.");
606 jvnews = new BlogReader(me);
607 showNews.setVisible(true);
608 Cache.log.debug("Completed news thread.");
613 public void getIdentifiersOrgData()
615 // Thread off the identifiers fetcher
616 new Thread(new Runnable()
621 Cache.log.debug("Downloading data from identifiers.org");
622 // UrlDownloadClient client = new UrlDownloadClient();
625 UrlDownloadClient.download(IdOrgSettings.getUrl(),
626 IdOrgSettings.getDownloadLocation());
627 } catch (IOException e)
629 Cache.log.debug("Exception downloading identifiers.org data"
638 protected void showNews_actionPerformed(ActionEvent e)
640 showNews(showNews.isSelected());
643 protected void showNews(boolean visible)
645 Cache.log.debug((visible ? "Showing" : "Hiding") + " news.");
646 showNews.setSelected(visible);
647 if (visible && !jvnews.isVisible())
649 new Thread(new Runnable()
654 long now = System.currentTimeMillis();
656 MessageManager.getString("status.refreshing_news"), now);
657 jvnews.refreshNews();
658 setProgressBar(null, now);
666 * recover the last known dimensions for a jalview window
669 * - empty string is desktop, all other windows have unique prefix
670 * @return null or last known dimensions scaled to current geometry (if last
671 * window geom was known)
673 Rectangle getLastKnownDimensions(String windowName)
675 // TODO: lock aspect ratio for scaling desktop Bug #0058199
676 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
677 String x = jalview.bin.Cache.getProperty(windowName + "SCREEN_X");
678 String y = jalview.bin.Cache.getProperty(windowName + "SCREEN_Y");
679 String width = jalview.bin.Cache
680 .getProperty(windowName + "SCREEN_WIDTH");
681 String height = jalview.bin.Cache
682 .getProperty(windowName + "SCREEN_HEIGHT");
683 if ((x != null) && (y != null) && (width != null) && (height != null))
685 int ix = Integer.parseInt(x), iy = Integer.parseInt(y),
686 iw = Integer.parseInt(width), ih = Integer.parseInt(height);
687 if (jalview.bin.Cache.getProperty("SCREENGEOMETRY_WIDTH") != null)
689 // attempt #1 - try to cope with change in screen geometry - this
690 // version doesn't preserve original jv aspect ratio.
691 // take ratio of current screen size vs original screen size.
692 double sw = ((1f * screenSize.width) / (1f * Integer.parseInt(
693 jalview.bin.Cache.getProperty("SCREENGEOMETRY_WIDTH"))));
694 double sh = ((1f * screenSize.height) / (1f * Integer.parseInt(
695 jalview.bin.Cache.getProperty("SCREENGEOMETRY_HEIGHT"))));
696 // rescale the bounds depending upon the current screen geometry.
697 ix = (int) (ix * sw);
698 iw = (int) (iw * sw);
699 iy = (int) (iy * sh);
700 ih = (int) (ih * sh);
701 while (ix >= screenSize.width)
703 jalview.bin.Cache.log.debug(
704 "Window geometry location recall error: shifting horizontal to within screenbounds.");
705 ix -= screenSize.width;
707 while (iy >= screenSize.height)
709 jalview.bin.Cache.log.debug(
710 "Window geometry location recall error: shifting vertical to within screenbounds.");
711 iy -= screenSize.height;
713 jalview.bin.Cache.log.debug(
714 "Got last known dimensions for " + windowName + ": x:" + ix
715 + " y:" + iy + " width:" + iw + " height:" + ih);
717 // return dimensions for new instance
718 return new Rectangle(ix, iy, iw, ih);
723 private void doVamsasClientCheck()
725 if (Cache.vamsasJarsPresent())
727 setupVamsasDisconnectedGui();
728 VamsasMenu.setVisible(true);
729 final Desktop us = this;
730 VamsasMenu.addMenuListener(new MenuListener()
732 // this listener remembers when the menu was first selected, and
733 // doesn't rebuild the session list until it has been cleared and
735 boolean refresh = true;
738 public void menuCanceled(MenuEvent e)
744 public void menuDeselected(MenuEvent e)
750 public void menuSelected(MenuEvent e)
754 us.buildVamsasStMenu();
759 vamsasStart.setVisible(true);
763 protected void showPasteMenu(int x, int y)
765 JPopupMenu popup = new JPopupMenu();
766 JMenuItem item = new JMenuItem(
767 MessageManager.getString("label.paste_new_window"));
768 item.addActionListener(new ActionListener()
771 public void actionPerformed(ActionEvent evt)
778 popup.show(this, x, y);
785 Clipboard c = Toolkit.getDefaultToolkit().getSystemClipboard();
786 Transferable contents = c.getContents(this);
788 if (contents != null)
790 String file = (String) contents
791 .getTransferData(DataFlavor.stringFlavor);
793 FileFormatI format = new IdentifyFile().identify(file,
794 DataSourceType.PASTE);
796 new FileLoader().loadFile(file, DataSourceType.PASTE, format);
799 } catch (Exception ex)
802 "Unable to paste alignment from system clipboard:\n" + ex);
807 * Adds and opens the given frame to the desktop
818 public static synchronized void addInternalFrame(
819 final JInternalFrame frame, String title, int w, int h)
821 addInternalFrame(frame, title, true, w, h, true, false);
825 * Add an internal frame to the Jalview desktop
832 * When true, display frame immediately, otherwise, caller must call
833 * setVisible themselves.
839 public static synchronized void addInternalFrame(
840 final JInternalFrame frame, String title, boolean makeVisible,
843 addInternalFrame(frame, title, makeVisible, w, h, true, false);
847 * Add an internal frame to the Jalview desktop and make it visible
860 public static synchronized void addInternalFrame(
861 final JInternalFrame frame, String title, int w, int h,
864 addInternalFrame(frame, title, true, w, h, resizable, false);
868 * Add an internal frame to the Jalview desktop
875 * When true, display frame immediately, otherwise, caller must call
876 * setVisible themselves.
883 * @param ignoreMinSize
884 * Do not set the default minimum size for frame
886 public static synchronized void addInternalFrame(
887 final JInternalFrame frame, String title, boolean makeVisible,
888 int w, int h, boolean resizable, boolean ignoreMinSize)
892 // TODO: allow callers to determine X and Y position of frame (eg. via
894 // TODO: consider fixing method to update entries in the window submenu with
895 // the current window title
897 frame.setTitle(title);
898 if (w > 0 && (frame.getWidth() < 1 || frame.getHeight() < 1))
902 // THIS IS A PUBLIC STATIC METHOD, SO IT MAY BE CALLED EVEN IN
903 // A HEADLESS STATE WHEN NO DESKTOP EXISTS. MUST RETURN
904 // IF JALVIEW IS RUNNING HEADLESS
905 // ///////////////////////////////////////////////
906 if (Jalview.isHeadlessMode())
915 frame.setMinimumSize(
916 new Dimension(DEFAULT_MIN_WIDTH, DEFAULT_MIN_HEIGHT));
918 // Set default dimension for Alignment Frame window.
919 // The Alignment Frame window could be added from a number of places,
921 // I did this here in order not to miss out on any Alignment frame.
922 if (frame instanceof AlignFrame)
924 frame.setMinimumSize(new Dimension(ALIGN_FRAME_DEFAULT_MIN_WIDTH,
925 ALIGN_FRAME_DEFAULT_MIN_HEIGHT));
929 frame.setVisible(makeVisible);
930 frame.setClosable(true);
931 frame.setResizable(resizable);
932 frame.setMaximizable(resizable);
933 frame.setIconifiable(resizable);
934 frame.setOpaque(Platform.isJS());
936 boolean isEmbedded = (Platform.getDimIfEmbedded(frame, -1, -1) != null);
937 if (!isEmbedded && frame.getX() < 1 && frame.getY() < 1)
939 frame.setLocation(xOffset * openFrameCount,
940 yOffset * ((openFrameCount - 1) % 10) + yOffset);
944 * add an entry for the new frame in the Window menu
945 * (and remove it when the frame is closed)
947 JMenuItem menuItem = new JMenuItem(title);
948 frame.addInternalFrameListener(new InternalFrameAdapter()
951 public void internalFrameActivated(InternalFrameEvent evt)
953 JInternalFrame itf = getDesktopPane().getSelectedFrame();
956 if (itf instanceof AlignFrame)
958 Jalview.setCurrentAlignFrame((AlignFrame) itf);
965 public void internalFrameClosed(InternalFrameEvent evt)
967 PaintRefresher.RemoveComponent(frame);
970 * defensive check to prevent frames being
971 * added half off the window
973 if (openFrameCount > 0)
979 * ensure no reference to alignFrame retained by menu item listener
981 if (menuItem.getActionListeners().length > 0)
983 menuItem.removeActionListener(menuItem.getActionListeners()[0]);
985 Desktop.getInstance().windowMenu.remove(menuItem);
989 menuItem.addActionListener(new ActionListener()
992 public void actionPerformed(ActionEvent e)
996 frame.setSelected(true);
997 frame.setIcon(false);
998 } catch (java.beans.PropertyVetoException ex)
1000 // System.err.println(ex.toString());
1005 setKeyBindings(frame);
1007 getDesktopPane().add(frame);
1009 Desktop.getInstance().windowMenu.add(menuItem);
1014 frame.setSelected(true);
1015 frame.requestFocus();
1016 } catch (java.beans.PropertyVetoException ve)
1018 } catch (java.lang.ClassCastException cex)
1021 "Squashed a possible GUI implementation error. If you can recreate this, please look at http://issues.jalview.org/browse/JAL-869",
1027 * Add key bindings to a JInternalFrame so that Ctrl-W and Cmd-W will close the
1032 private static void setKeyBindings(JInternalFrame frame)
1034 final Action closeAction = new AbstractAction()
1037 public void actionPerformed(ActionEvent e)
1044 * set up key bindings for Ctrl-W and Cmd-W, with the same (Close) action
1046 KeyStroke ctrlWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
1047 InputEvent.CTRL_DOWN_MASK);
1048 KeyStroke cmdWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
1049 Toolkit.getDefaultToolkit().getMenuShortcutKeyMask());
1051 InputMap inputMap = frame
1052 .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
1053 String ctrlW = ctrlWKey.toString();
1054 inputMap.put(ctrlWKey, ctrlW);
1055 inputMap.put(cmdWKey, ctrlW);
1057 ActionMap actionMap = frame.getActionMap();
1058 actionMap.put(ctrlW, closeAction);
1062 public void lostOwnership(Clipboard clipboard, Transferable contents)
1066 Desktop.getInstance().jalviewClipboard = null;
1069 internalCopy = false;
1073 public void dragEnter(DropTargetDragEvent evt)
1078 public void dragExit(DropTargetEvent evt)
1083 public void dragOver(DropTargetDragEvent evt)
1088 public void dropActionChanged(DropTargetDragEvent evt)
1099 public void drop(DropTargetDropEvent evt)
1101 boolean success = true;
1102 // JAL-1552 - acceptDrop required before getTransferable call for
1103 // Java's Transferable for native dnd
1104 evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
1105 Transferable t = evt.getTransferable();
1106 List<Object> files = new ArrayList<>();
1107 List<DataSourceType> protocols = new ArrayList<>();
1111 Desktop.transferFromDropTarget(files, protocols, evt, t);
1112 } catch (Exception e)
1114 e.printStackTrace();
1122 for (int i = 0; i < files.size(); i++)
1124 // BH 2018 File or String
1125 Object file = files.get(i);
1126 String fileName = file.toString();
1127 DataSourceType protocol = (protocols == null)
1128 ? DataSourceType.FILE
1130 FileFormatI format = null;
1132 if (fileName.endsWith(".jar"))
1134 format = FileFormat.Jalview;
1139 format = new IdentifyFile().identify(file, protocol);
1141 if (file instanceof File)
1143 Platform.cacheFileData((File) file);
1145 new FileLoader().loadFile(null, file, protocol, format);
1148 } catch (Exception ex)
1153 evt.dropComplete(success); // need this to ensure input focus is properly
1154 // transfered to any new windows created
1164 public void inputLocalFileMenuItem_actionPerformed(AlignViewport viewport)
1166 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
1167 JalviewFileChooser chooser = JalviewFileChooser
1168 .forRead(Cache.getProperty("LAST_DIRECTORY"), fileFormat, true);
1170 chooser.setFileView(new JalviewFileView());
1171 chooser.setDialogTitle(
1172 MessageManager.getString("label.open_local_file"));
1173 chooser.setToolTipText(MessageManager.getString("action.open"));
1175 chooser.setResponseHandler(0, new Runnable()
1180 File selectedFile = chooser.getSelectedFile();
1181 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1183 FileFormatI format = chooser.getSelectedFormat();
1186 * Call IdentifyFile to verify the file contains what its extension implies.
1187 * Skip this step for dynamically added file formats, because
1188 * IdentifyFile does not know how to recognise them.
1190 if (FileFormats.getInstance().isIdentifiable(format))
1194 format = new IdentifyFile().identify(selectedFile,
1195 DataSourceType.FILE);
1196 } catch (FileFormatException e)
1198 // format = null; //??
1202 new FileLoader().loadFile(viewport, selectedFile,
1203 DataSourceType.FILE, format);
1206 chooser.showOpenDialog(this);
1210 * Shows a dialog for input of a URL at which to retrieve alignment data
1215 public void inputURLMenuItem_actionPerformed(AlignViewport viewport)
1217 // This construct allows us to have a wider textfield
1219 JLabel label = new JLabel(
1220 MessageManager.getString("label.input_file_url"));
1222 JPanel panel = new JPanel(new GridLayout(2, 1));
1226 * the URL to fetch is
1227 * Java: an editable combobox with history
1228 * JS: (pending JAL-3038) a plain text field
1231 String urlBase = "http://www.";
1232 if (Platform.isJS())
1234 history = new JTextField(urlBase, 35);
1243 JComboBox<String> asCombo = new JComboBox<>();
1244 asCombo.setPreferredSize(new Dimension(400, 20));
1245 asCombo.setEditable(true);
1246 asCombo.addItem(urlBase);
1247 String historyItems = Cache.getProperty("RECENT_URL");
1248 if (historyItems != null)
1250 for (String token : historyItems.split("\\t"))
1252 asCombo.addItem(token);
1259 Object[] options = new Object[] { MessageManager.getString("action.ok"),
1260 MessageManager.getString("action.cancel") };
1261 Runnable action = new Runnable()
1266 @SuppressWarnings("unchecked")
1267 String url = (history instanceof JTextField
1268 ? ((JTextField) history).getText()
1269 : ((JComboBox<String>) history).getSelectedItem()
1272 if (url.toLowerCase().endsWith(".jar"))
1274 if (viewport != null)
1276 new FileLoader().loadFile(viewport, url, DataSourceType.URL,
1277 FileFormat.Jalview);
1281 new FileLoader().loadFile(url, DataSourceType.URL,
1282 FileFormat.Jalview);
1287 FileFormatI format = null;
1290 format = new IdentifyFile().identify(url, DataSourceType.URL);
1291 } catch (FileFormatException e)
1293 // TODO revise error handling, distinguish between
1294 // URL not found and response not valid
1299 String msg = MessageManager
1300 .formatMessage("label.couldnt_locate", url);
1301 JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
1303 MessageManager.getString("label.url_not_found"),
1304 JvOptionPane.WARNING_MESSAGE);
1309 if (viewport != null)
1311 new FileLoader().loadFile(viewport, url, DataSourceType.URL,
1316 new FileLoader().loadFile(url, DataSourceType.URL, format);
1321 String dialogOption = MessageManager
1322 .getString("label.input_alignment_from_url");
1323 JvOptionPane.newOptionDialog(getDesktopPane())
1324 .setResponseHandler(0, action)
1325 .showInternalDialog(panel, dialogOption,
1326 JvOptionPane.YES_NO_CANCEL_OPTION,
1327 JvOptionPane.PLAIN_MESSAGE, null, options,
1328 MessageManager.getString("action.ok"));
1332 * Opens the CutAndPaste window for the user to paste an alignment in to
1335 * - if not null, the pasted alignment is added to the current
1336 * alignment; if null, to a new alignment window
1339 public void inputTextboxMenuItem_actionPerformed(
1340 AlignmentViewPanel viewPanel)
1342 CutAndPasteTransfer cap = new CutAndPasteTransfer();
1343 cap.setForInput(viewPanel);
1344 Desktop.addInternalFrame(cap,
1345 MessageManager.getString("label.cut_paste_alignmen_file"), true,
1355 Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
1356 jalview.bin.Cache.setProperty("SCREENGEOMETRY_WIDTH",
1358 jalview.bin.Cache.setProperty("SCREENGEOMETRY_HEIGHT",
1359 screen.height + "");
1360 storeLastKnownDimensions("", new Rectangle(getBounds().x, getBounds().y,
1361 getWidth(), getHeight()));
1363 if (jconsole != null)
1365 storeLastKnownDimensions("JAVA_CONSOLE_", jconsole.getBounds());
1366 jconsole.stopConsole();
1370 storeLastKnownDimensions("JALVIEW_RSS_WINDOW_", jvnews.getBounds());
1373 if (dialogExecutor != null)
1375 dialogExecutor.shutdownNow();
1377 closeAll_actionPerformed(null);
1379 if (groovyConsole != null)
1381 // suppress a possible repeat prompt to save script
1382 groovyConsole.setDirty(false);
1383 groovyConsole.exit();
1388 private void storeLastKnownDimensions(String string, Rectangle jc)
1390 jalview.bin.Cache.log.debug("Storing last known dimensions for "
1391 + string + ": x:" + jc.x + " y:" + jc.y + " width:" + jc.width
1392 + " height:" + jc.height);
1394 jalview.bin.Cache.setProperty(string + "SCREEN_X", jc.x + "");
1395 jalview.bin.Cache.setProperty(string + "SCREEN_Y", jc.y + "");
1396 jalview.bin.Cache.setProperty(string + "SCREEN_WIDTH", jc.width + "");
1397 jalview.bin.Cache.setProperty(string + "SCREEN_HEIGHT", jc.height + "");
1407 public void aboutMenuItem_actionPerformed(ActionEvent e)
1409 // StringBuffer message = getAboutMessage(false);
1410 // JvOptionPane.showInternalMessageDialog(Desktop.getDesktop(),
1412 // message.toString(), "About Jalview", JvOptionPane.INFORMATION_MESSAGE);
1413 new Thread(new Runnable()
1418 new SplashScreen(true);
1423 public StringBuffer getAboutMessage(boolean shortv)
1425 StringBuffer message = new StringBuffer();
1426 message.append("<html>");
1429 message.append("<h1><strong>Version: "
1430 + jalview.bin.Cache.getProperty("VERSION")
1431 + "</strong></h1>");
1432 message.append("<strong>Last Updated: <em>"
1433 + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown")
1434 + "</em></strong>");
1440 message.append("<strong>Version "
1441 + jalview.bin.Cache.getProperty("VERSION")
1442 + "; last updated: "
1443 + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown"));
1446 if (jalview.bin.Cache.getDefault("LATEST_VERSION", "Checking")
1447 .equals("Checking"))
1449 message.append("<br>...Checking latest version...</br>");
1451 else if (!jalview.bin.Cache.getDefault("LATEST_VERSION", "Checking")
1452 .equals(jalview.bin.Cache.getProperty("VERSION")))
1454 boolean red = false;
1455 if (jalview.bin.Cache.getProperty("VERSION").toLowerCase()
1456 .indexOf("automated build") == -1)
1459 // Displayed when code version and jnlp version do not match and code
1460 // version is not a development build
1461 message.append("<div style=\"color: #FF0000;font-style: bold;\">");
1464 message.append("<br>!! Version "
1465 + jalview.bin.Cache.getDefault("LATEST_VERSION",
1467 + " is available for download from "
1468 + jalview.bin.Cache.getDefault("www.jalview.org",
1469 "http://www.jalview.org")
1473 message.append("</div>");
1476 message.append("<br>Authors: " + jalview.bin.Cache.getDefault(
1478 "The Jalview Authors (See AUTHORS file for current list)")
1479 + "<br><br>Development managed by The Barton Group, University of Dundee, Scotland, UK.<br>"
1480 + "<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"
1481 + "<br><br>If you use Jalview, please cite:"
1482 + "<br>Waterhouse, A.M., Procter, J.B., Martin, D.M.A, Clamp, M. and Barton, G. J. (2009)"
1483 + "<br>Jalview Version 2 - a multiple sequence alignment editor and analysis workbench"
1484 + "<br>Bioinformatics doi: 10.1093/bioinformatics/btp033"
1490 * Action on requesting Help documentation
1493 public void documentationMenuItem_actionPerformed()
1497 if (Platform.isJS())
1499 BrowserLauncher.openURL("http://www.jalview.org/help.html");
1508 Help.showHelpWindow();
1510 } catch (Exception ex)
1512 System.err.println("Error opening help: " + ex.getMessage());
1517 public void closeAll_actionPerformed(ActionEvent e)
1519 if (desktopPane == null)
1523 // TODO show a progress bar while closing?
1524 JInternalFrame[] frames = desktopPane.getAllFrames();
1525 for (int i = 0; i < frames.length; i++)
1529 frames[i].setClosed(true);
1530 } catch (java.beans.PropertyVetoException ex)
1534 Jalview.setCurrentAlignFrame(null);
1535 System.out.println("ALL CLOSED");
1536 if (v_client != null)
1538 // TODO clear binding to vamsas document objects on close_all
1542 * reset state of singleton objects as appropriate (clear down session state
1543 * when all windows are closed)
1545 getStructureSelectionManager().resetAll();
1549 public void raiseRelated_actionPerformed(ActionEvent e)
1551 reorderAssociatedWindows(false, false);
1555 public void minimizeAssociated_actionPerformed(ActionEvent e)
1557 reorderAssociatedWindows(true, false);
1560 void closeAssociatedWindows()
1562 reorderAssociatedWindows(false, true);
1568 * @seejalview.jbgui.GDesktop#garbageCollect_actionPerformed(java.awt.event.
1572 protected void garbageCollect_actionPerformed(ActionEvent e)
1574 // We simply collect the garbage
1575 jalview.bin.Cache.log.debug("Collecting garbage...");
1577 jalview.bin.Cache.log.debug("Finished garbage collection.");
1584 * jalview.jbgui.GDesktop#showMemusage_actionPerformed(java.awt.event.ActionEvent
1588 protected void showMemusage_actionPerformed(ActionEvent e)
1590 getDesktopPane().showMemoryUsage(showMemusage.isSelected());
1597 * jalview.jbgui.GDesktop#showConsole_actionPerformed(java.awt.event.ActionEvent
1601 protected void showConsole_actionPerformed(ActionEvent e)
1603 showConsole(showConsole.isSelected());
1606 Console jconsole = null;
1609 * control whether the java console is visible or not
1613 void showConsole(boolean selected)
1615 // TODO: decide if we should update properties file
1616 if (jconsole != null) // BH 2018
1618 showConsole.setSelected(selected);
1619 Cache.setProperty("SHOW_JAVA_CONSOLE",
1620 Boolean.valueOf(selected).toString());
1621 jconsole.setVisible(selected);
1625 void reorderAssociatedWindows(boolean minimize, boolean close)
1627 JInternalFrame[] frames = getDesktopPane().getAllFrames();
1628 if (frames == null || frames.length < 1)
1633 AlignmentViewport source = null, target = null;
1634 if (frames[0] instanceof AlignFrame)
1636 source = ((AlignFrame) frames[0]).getCurrentView();
1638 else if (frames[0] instanceof TreePanel)
1640 source = ((TreePanel) frames[0]).getViewPort();
1642 else if (frames[0] instanceof PCAPanel)
1644 source = ((PCAPanel) frames[0]).av;
1646 else if (frames[0].getContentPane() instanceof PairwiseAlignPanel)
1648 source = ((PairwiseAlignPanel) frames[0].getContentPane()).av;
1653 for (int i = 0; i < frames.length; i++)
1656 if (frames[i] == null)
1660 if (frames[i] instanceof AlignFrame)
1662 target = ((AlignFrame) frames[i]).getCurrentView();
1664 else if (frames[i] instanceof TreePanel)
1666 target = ((TreePanel) frames[i]).getViewPort();
1668 else if (frames[i] instanceof PCAPanel)
1670 target = ((PCAPanel) frames[i]).av;
1672 else if (frames[i].getContentPane() instanceof PairwiseAlignPanel)
1674 target = ((PairwiseAlignPanel) frames[i].getContentPane()).av;
1677 if (source == target)
1683 frames[i].setClosed(true);
1687 frames[i].setIcon(minimize);
1690 frames[i].toFront();
1694 } catch (java.beans.PropertyVetoException ex)
1709 protected void preferences_actionPerformed(ActionEvent e)
1715 * Prompts the user to choose a file and then saves the Jalview state as a
1716 * Jalview project file
1719 public void saveState_actionPerformed()
1721 saveState_actionPerformed(false);
1724 public void saveState_actionPerformed(boolean saveAs)
1726 java.io.File projectFile = getProjectFile();
1727 // autoSave indicates we already have a file and don't need to ask
1728 boolean autoSave = projectFile != null && !saveAs
1729 && BackupFiles.getEnabled();
1731 // System.out.println("autoSave="+autoSave+", projectFile='"+projectFile+"',
1732 // saveAs="+saveAs+", Backups
1733 // "+(BackupFiles.getEnabled()?"enabled":"disabled"));
1735 boolean approveSave = false;
1738 JalviewFileChooser chooser = new JalviewFileChooser("jvp",
1741 chooser.setFileView(new JalviewFileView());
1742 chooser.setDialogTitle(MessageManager.getString("label.save_state"));
1744 int value = chooser.showSaveDialog(this);
1746 if (value == JalviewFileChooser.APPROVE_OPTION)
1748 projectFile = chooser.getSelectedFile();
1749 setProjectFile(projectFile);
1754 if (approveSave || autoSave)
1756 final Desktop me = this;
1757 final java.io.File chosenFile = projectFile;
1758 new Thread(new Runnable()
1763 // TODO: refactor to Jalview desktop session controller action.
1764 setProgressBar(MessageManager.formatMessage(
1765 "label.saving_jalview_project", new Object[]
1766 { chosenFile.getName() }), chosenFile.hashCode());
1767 jalview.bin.Cache.setProperty("LAST_DIRECTORY",
1768 chosenFile.getParent());
1769 // TODO catch and handle errors for savestate
1770 // TODO prevent user from messing with the Desktop whilst we're saving
1773 boolean doBackup = BackupFiles.getEnabled();
1774 BackupFiles backupfiles = doBackup ? new BackupFiles(chosenFile) : null;
1776 new Jalview2XML().saveState(doBackup ? backupfiles.getTempFile() : chosenFile);
1780 backupfiles.setWriteSuccess(true);
1781 backupfiles.rollBackupsAndRenameTempFile();
1783 } catch (OutOfMemoryError oom)
1785 new OOMWarning("Whilst saving current state to "
1786 + chosenFile.getName(), oom);
1787 } catch (Exception ex)
1789 Cache.log.error("Problems whilst trying to save to "
1790 + chosenFile.getName(), ex);
1791 JvOptionPane.showMessageDialog(me,
1792 MessageManager.formatMessage(
1793 "label.error_whilst_saving_current_state_to",
1795 { chosenFile.getName() }),
1796 MessageManager.getString("label.couldnt_save_project"),
1797 JvOptionPane.WARNING_MESSAGE);
1799 setProgressBar(null, chosenFile.hashCode());
1806 public void saveAsState_actionPerformed(ActionEvent e)
1808 saveState_actionPerformed(true);
1811 protected void setProjectFile(File choice)
1813 this.projectFile = choice;
1816 public File getProjectFile()
1818 return this.projectFile;
1822 * Shows a file chooser dialog and tries to read in the selected file as a
1826 public void loadState_actionPerformed()
1828 final String[] suffix = new String[] { "jvp", "jar" };
1829 final String[] desc = new String[] { "Jalview Project",
1830 "Jalview Project (old)" };
1831 JalviewFileChooser chooser = new JalviewFileChooser(
1832 Cache.getProperty("LAST_DIRECTORY"), suffix, desc,
1833 "Jalview Project", true, true); // last two booleans: allFiles,
1835 chooser.setFileView(new JalviewFileView());
1836 chooser.setDialogTitle(MessageManager.getString("label.restore_state"));
1837 chooser.setResponseHandler(0, new Runnable()
1842 File selectedFile = chooser.getSelectedFile();
1843 setProjectFile(selectedFile);
1844 String choice = selectedFile.getAbsolutePath();
1845 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1846 new Thread(new Runnable()
1853 new Jalview2XML().loadJalviewAlign(selectedFile);
1854 } catch (OutOfMemoryError oom)
1856 new OOMWarning("Whilst loading project from " + choice, oom);
1857 } catch (Exception ex)
1860 "Problems whilst loading project from " + choice, ex);
1861 JvOptionPane.showMessageDialog(Desktop.getDesktopPane(),
1862 MessageManager.formatMessage(
1863 "label.error_whilst_loading_project_from",
1866 MessageManager.getString("label.couldnt_load_project"),
1867 JvOptionPane.WARNING_MESSAGE);
1874 chooser.showOpenDialog(this);
1878 public void inputSequence_actionPerformed(ActionEvent e)
1880 new SequenceFetcher(this);
1883 JPanel progressPanel;
1885 ArrayList<JPanel> fileLoadingPanels = new ArrayList<>();
1887 public void startLoading(final Object fileName)
1889 if (fileLoadingCount == 0)
1891 fileLoadingPanels.add(addProgressPanel(MessageManager
1892 .formatMessage("label.loading_file", new Object[]
1898 private JPanel addProgressPanel(String string)
1900 if (progressPanel == null)
1902 progressPanel = new JPanel(new GridLayout(1, 1));
1903 totalProgressCount = 0;
1904 getContentPane().add(progressPanel, BorderLayout.SOUTH);
1906 JPanel thisprogress = new JPanel(new BorderLayout(10, 5));
1907 JProgressBar progressBar = new JProgressBar();
1908 progressBar.setIndeterminate(true);
1910 thisprogress.add(new JLabel(string), BorderLayout.WEST);
1912 thisprogress.add(progressBar, BorderLayout.CENTER);
1913 progressPanel.add(thisprogress);
1914 ((GridLayout) progressPanel.getLayout()).setRows(
1915 ((GridLayout) progressPanel.getLayout()).getRows() + 1);
1916 ++totalProgressCount;
1918 return thisprogress;
1921 int totalProgressCount = 0;
1923 private void removeProgressPanel(JPanel progbar)
1925 if (progressPanel != null)
1927 synchronized (progressPanel)
1929 progressPanel.remove(progbar);
1930 GridLayout gl = (GridLayout) progressPanel.getLayout();
1931 gl.setRows(gl.getRows() - 1);
1932 if (--totalProgressCount < 1)
1934 this.getContentPane().remove(progressPanel);
1935 progressPanel = null;
1942 public void stopLoading()
1945 if (fileLoadingCount < 1)
1947 while (fileLoadingPanels.size() > 0)
1949 removeProgressPanel(fileLoadingPanels.remove(0));
1951 fileLoadingPanels.clear();
1952 fileLoadingCount = 0;
1957 public static int getViewCount(String alignmentId)
1959 AlignmentViewport[] aps = getViewports(alignmentId);
1960 return (aps == null) ? 0 : aps.length;
1965 * @param alignmentId
1966 * - if null, all sets are returned
1967 * @return all AlignmentPanels concerning the alignmentId sequence set
1969 public static AlignmentPanel[] getAlignmentPanels(String alignmentId)
1971 if (Desktop.getDesktopPane() == null)
1973 // no frames created and in headless mode
1974 // TODO: verify that frames are recoverable when in headless mode
1977 List<AlignmentPanel> aps = new ArrayList<>();
1978 AlignFrame[] frames = getAlignFrames();
1983 for (AlignFrame af : frames)
1985 for (AlignmentPanel ap : af.alignPanels)
1987 if (alignmentId == null
1988 || alignmentId.equals(ap.av.getSequenceSetId()))
1994 if (aps.size() == 0)
1998 AlignmentPanel[] vap = aps.toArray(new AlignmentPanel[aps.size()]);
2003 * get all the viewports on an alignment.
2005 * @param sequenceSetId
2006 * unique alignment id (may be null - all viewports returned in that
2008 * @return all viewports on the alignment bound to sequenceSetId
2010 public static AlignmentViewport[] getViewports(String sequenceSetId)
2012 List<AlignmentViewport> viewp = new ArrayList<>();
2013 if (getDesktopPane() != null)
2015 AlignFrame[] frames = Desktop.getAlignFrames();
2017 for (AlignFrame afr : frames)
2019 if (sequenceSetId == null || afr.getViewport().getSequenceSetId()
2020 .equals(sequenceSetId))
2022 if (afr.alignPanels != null)
2024 for (AlignmentPanel ap : afr.alignPanels)
2026 if (sequenceSetId == null
2027 || sequenceSetId.equals(ap.av.getSequenceSetId()))
2035 viewp.add(afr.getViewport());
2039 if (viewp.size() > 0)
2041 return viewp.toArray(new AlignmentViewport[viewp.size()]);
2048 * Explode the views in the given frame into separate AlignFrame
2052 public static void explodeViews(AlignFrame af)
2054 int size = af.alignPanels.size();
2060 for (int i = 0; i < size; i++)
2062 AlignmentPanel ap = af.alignPanels.get(i);
2063 AlignFrame newaf = new AlignFrame(ap);
2066 * Restore the view's last exploded frame geometry if known. Multiple
2067 * views from one exploded frame share and restore the same (frame)
2068 * position and size.
2070 Rectangle geometry = ap.av.getExplodedGeometry();
2071 if (geometry != null)
2073 newaf.setBounds(geometry);
2076 ap.av.setGatherViewsHere(false);
2078 addInternalFrame(newaf, af.getTitle(), AlignFrame.DEFAULT_WIDTH,
2079 AlignFrame.DEFAULT_HEIGHT);
2082 af.alignPanels.clear();
2083 af.closeMenuItem_actionPerformed(true);
2088 * Gather expanded views (separate AlignFrame's) with the same sequence set
2089 * identifier back in to this frame as additional views, and close the expanded
2090 * views. Note the expanded frames may themselves have multiple views. We take
2095 public void gatherViews(AlignFrame source)
2097 source.viewport.setGatherViewsHere(true);
2098 source.viewport.setExplodedGeometry(source.getBounds());
2099 JInternalFrame[] frames = getAllFrames();
2100 String viewId = source.viewport.getSequenceSetId();
2102 for (int t = 0; t < frames.length; t++)
2104 if (frames[t] instanceof AlignFrame && frames[t] != source)
2106 AlignFrame af = (AlignFrame) frames[t];
2107 boolean gatherThis = false;
2108 for (int a = 0; a < af.alignPanels.size(); a++)
2110 AlignmentPanel ap = af.alignPanels.get(a);
2111 if (viewId.equals(ap.av.getSequenceSetId()))
2114 ap.av.setGatherViewsHere(false);
2115 ap.av.setExplodedGeometry(af.getBounds());
2116 source.addAlignmentPanel(ap, false);
2122 af.alignPanels.clear();
2123 af.closeMenuItem_actionPerformed(true);
2130 jalview.gui.VamsasApplication v_client = null;
2133 public void vamsasImport_actionPerformed(ActionEvent e)
2135 // TODO: JAL-3048 not needed for Jalview-JS
2137 if (v_client == null)
2139 // Load and try to start a session.
2140 JalviewFileChooser chooser = new JalviewFileChooser(
2141 jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
2143 chooser.setFileView(new JalviewFileView());
2144 chooser.setDialogTitle(
2145 MessageManager.getString("label.open_saved_vamsas_session"));
2146 chooser.setToolTipText(MessageManager.getString(
2147 "label.select_vamsas_session_opened_as_new_vamsas_session"));
2149 int value = chooser.showOpenDialog(this);
2151 if (value == JalviewFileChooser.APPROVE_OPTION)
2153 String fle = chooser.getSelectedFile().toString();
2154 if (!vamsasImport(chooser.getSelectedFile()))
2156 JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
2157 MessageManager.formatMessage(
2158 "label.couldnt_import_as_vamsas_session",
2162 .getString("label.vamsas_document_import_failed"),
2163 JvOptionPane.ERROR_MESSAGE);
2169 jalview.bin.Cache.log.error(
2170 "Implementation error - load session from a running session is not supported.");
2175 * import file into a new vamsas session (uses jalview.gui.VamsasApplication)
2178 * @return true if import was a success and a session was started.
2180 public boolean vamsasImport(URL url)
2182 // TODO: create progress bar
2183 if (v_client != null)
2186 jalview.bin.Cache.log.error(
2187 "Implementation error - load session from a running session is not supported.");
2193 // copy the URL content to a temporary local file
2194 // TODO: be a bit cleverer here with nio (?!)
2195 File file = File.createTempFile("vdocfromurl", ".vdj");
2196 FileOutputStream fos = new FileOutputStream(file);
2197 BufferedInputStream bis = new BufferedInputStream(url.openStream());
2198 byte[] buffer = new byte[2048];
2200 while ((ln = bis.read(buffer)) > -1)
2202 fos.write(buffer, 0, ln);
2206 v_client = new jalview.gui.VamsasApplication(this, file,
2207 url.toExternalForm());
2208 } catch (Exception ex)
2210 jalview.bin.Cache.log.error(
2211 "Failed to create new vamsas session from contents of URL "
2216 setupVamsasConnectedGui();
2217 v_client.initial_update(); // TODO: thread ?
2218 return v_client.inSession();
2222 * import file into a new vamsas session (uses jalview.gui.VamsasApplication)
2225 * @return true if import was a success and a session was started.
2227 public boolean vamsasImport(File file)
2229 if (v_client != null)
2232 jalview.bin.Cache.log.error(
2233 "Implementation error - load session from a running session is not supported.");
2237 setProgressBar(MessageManager.formatMessage(
2238 "status.importing_vamsas_session_from", new Object[]
2239 { file.getName() }), file.hashCode());
2242 v_client = new jalview.gui.VamsasApplication(this, file, null);
2243 } catch (Exception ex)
2245 setProgressBar(MessageManager.formatMessage(
2246 "status.importing_vamsas_session_from", new Object[]
2247 { file.getName() }), file.hashCode());
2248 jalview.bin.Cache.log.error(
2249 "New vamsas session from existing session file failed:", ex);
2252 setupVamsasConnectedGui();
2253 v_client.initial_update(); // TODO: thread ?
2254 setProgressBar(MessageManager.formatMessage(
2255 "status.importing_vamsas_session_from", new Object[]
2256 { file.getName() }), file.hashCode());
2257 return v_client.inSession();
2260 public boolean joinVamsasSession(String mysesid)
2262 if (v_client != null)
2264 throw new Error(MessageManager
2265 .getString("error.try_join_vamsas_session_another"));
2267 if (mysesid == null)
2270 MessageManager.getString("error.invalid_vamsas_session_id"));
2272 v_client = new VamsasApplication(this, mysesid);
2273 setupVamsasConnectedGui();
2274 v_client.initial_update();
2275 return (v_client.inSession());
2279 public void vamsasStart_actionPerformed(ActionEvent e)
2281 if (v_client == null)
2284 // we just start a default session for moment.
2286 * JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache.
2287 * getProperty("LAST_DIRECTORY"));
2289 * chooser.setFileView(new JalviewFileView());
2290 * chooser.setDialogTitle("Load Vamsas file");
2291 * chooser.setToolTipText("Import");
2293 * int value = chooser.showOpenDialog(this);
2295 * if (value == JalviewFileChooser.APPROVE_OPTION) { v_client = new
2296 * jalview.gui.VamsasApplication(this, chooser.getSelectedFile());
2298 v_client = new VamsasApplication(this);
2299 setupVamsasConnectedGui();
2300 v_client.initial_update(); // TODO: thread ?
2304 // store current data in session.
2305 v_client.push_update(); // TODO: thread
2309 protected void setupVamsasConnectedGui()
2311 vamsasStart.setText(MessageManager.getString("label.session_update"));
2312 vamsasSave.setVisible(true);
2313 vamsasStop.setVisible(true);
2314 vamsasImport.setVisible(false); // Document import to existing session is
2315 // not possible for vamsas-client-1.0.
2318 protected void setupVamsasDisconnectedGui()
2320 vamsasSave.setVisible(false);
2321 vamsasStop.setVisible(false);
2322 vamsasImport.setVisible(true);
2324 .setText(MessageManager.getString("label.new_vamsas_session"));
2328 public void vamsasStop_actionPerformed(ActionEvent e)
2330 if (v_client != null)
2332 v_client.end_session();
2334 setupVamsasDisconnectedGui();
2338 protected void buildVamsasStMenu()
2340 if (v_client == null)
2342 String[] sess = null;
2345 sess = VamsasApplication.getSessionList();
2346 } catch (Exception e)
2348 jalview.bin.Cache.log.warn("Problem getting current sessions list.",
2354 jalview.bin.Cache.log.debug(
2355 "Got current sessions list: " + sess.length + " entries.");
2356 VamsasStMenu.removeAll();
2357 for (int i = 0; i < sess.length; i++)
2359 JMenuItem sessit = new JMenuItem();
2360 sessit.setText(sess[i]);
2361 sessit.setToolTipText(MessageManager
2362 .formatMessage("label.connect_to_session", new Object[]
2364 final Desktop dsktp = this;
2365 final String mysesid = sess[i];
2366 sessit.addActionListener(new ActionListener()
2370 public void actionPerformed(ActionEvent e)
2372 if (dsktp.v_client == null)
2374 Thread rthr = new Thread(new Runnable()
2380 dsktp.v_client = new VamsasApplication(dsktp, mysesid);
2381 dsktp.setupVamsasConnectedGui();
2382 dsktp.v_client.initial_update();
2390 VamsasStMenu.add(sessit);
2392 // don't show an empty menu.
2393 VamsasStMenu.setVisible(sess.length > 0);
2398 jalview.bin.Cache.log.debug("No current vamsas sessions.");
2399 VamsasStMenu.removeAll();
2400 VamsasStMenu.setVisible(false);
2405 // Not interested in the content. Just hide ourselves.
2406 VamsasStMenu.setVisible(false);
2411 public void vamsasSave_actionPerformed(ActionEvent e)
2413 // TODO: JAL-3048 not needed for Jalview-JS
2415 if (v_client != null)
2417 // TODO: VAMSAS DOCUMENT EXTENSION is VDJ
2418 JalviewFileChooser chooser = new JalviewFileChooser("vdj",
2421 chooser.setFileView(new JalviewFileView());
2422 chooser.setDialogTitle(MessageManager
2423 .getString("label.save_vamsas_document_archive"));
2425 int value = chooser.showSaveDialog(this);
2427 if (value == JalviewFileChooser.APPROVE_OPTION)
2429 java.io.File choice = chooser.getSelectedFile();
2430 JPanel progpanel = addProgressPanel(MessageManager
2431 .formatMessage("label.saving_vamsas_doc", new Object[]
2432 { choice.getName() }));
2433 Cache.setProperty("LAST_DIRECTORY", choice.getParent());
2434 String warnmsg = null;
2435 String warnttl = null;
2438 v_client.vclient.storeDocument(choice);
2441 warnttl = "Serious Problem saving Vamsas Document";
2442 warnmsg = ex.toString();
2443 jalview.bin.Cache.log
2444 .error("Error Whilst saving document to " + choice, ex);
2446 } catch (Exception ex)
2448 warnttl = "Problem saving Vamsas Document.";
2449 warnmsg = ex.toString();
2450 jalview.bin.Cache.log.warn(
2451 "Exception Whilst saving document to " + choice, ex);
2454 removeProgressPanel(progpanel);
2455 if (warnmsg != null)
2457 JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
2459 warnmsg, warnttl, JvOptionPane.ERROR_MESSAGE);
2465 JPanel vamUpdate = null;
2468 * hide vamsas user gui bits when a vamsas document event is being handled.
2471 * true to hide gui, false to reveal gui
2473 public void setVamsasUpdate(boolean b)
2475 Cache.log.debug("Setting gui for Vamsas update "
2476 + (b ? "in progress" : "finished"));
2478 if (vamUpdate != null)
2480 this.removeProgressPanel(vamUpdate);
2484 vamUpdate = this.addProgressPanel(
2485 MessageManager.getString("label.updating_vamsas_session"));
2487 vamsasStart.setVisible(!b);
2488 vamsasStop.setVisible(!b);
2489 vamsasSave.setVisible(!b);
2492 public JInternalFrame[] getAllFrames()
2494 return desktopPane.getAllFrames();
2498 * Checks the given url to see if it gives a response indicating that the user
2499 * should be informed of a new questionnaire.
2503 public void checkForQuestionnaire(String url)
2505 UserQuestionnaireCheck jvq = new UserQuestionnaireCheck(url);
2506 // javax.swing.SwingUtilities.invokeLater(jvq);
2507 new Thread(jvq).start();
2510 public void checkURLLinks()
2512 // Thread off the URL link checker
2513 addDialogThread(new Runnable()
2518 if (Cache.getDefault("CHECKURLLINKS", true))
2520 // check what the actual links are - if it's just the default don't
2521 // bother with the warning
2522 List<String> links = Preferences.sequenceUrlLinks
2525 // only need to check links if there is one with a
2526 // SEQUENCE_ID which is not the default EMBL_EBI link
2527 ListIterator<String> li = links.listIterator();
2528 boolean check = false;
2529 List<JLabel> urls = new ArrayList<>();
2530 while (li.hasNext())
2532 String link = li.next();
2533 if (link.contains(UrlConstants.SEQUENCE_ID)
2534 && !UrlConstants.isDefaultString(link))
2537 int barPos = link.indexOf("|");
2538 String urlMsg = barPos == -1 ? link
2539 : link.substring(0, barPos) + ": "
2540 + link.substring(barPos + 1);
2541 urls.add(new JLabel(urlMsg));
2549 // ask user to check in case URL links use old style tokens
2550 // ($SEQUENCE_ID$ for sequence id _or_ accession id)
2551 JPanel msgPanel = new JPanel();
2552 msgPanel.setLayout(new BoxLayout(msgPanel, BoxLayout.PAGE_AXIS));
2553 msgPanel.add(Box.createVerticalGlue());
2554 JLabel msg = new JLabel(MessageManager
2555 .getString("label.SEQUENCE_ID_for_DB_ACCESSION1"));
2556 JLabel msg2 = new JLabel(MessageManager
2557 .getString("label.SEQUENCE_ID_for_DB_ACCESSION2"));
2559 for (JLabel url : urls)
2565 final JCheckBox jcb = new JCheckBox(
2566 MessageManager.getString("label.do_not_display_again"));
2567 jcb.addActionListener(new ActionListener()
2570 public void actionPerformed(ActionEvent e)
2572 // update Cache settings for "don't show this again"
2573 boolean showWarningAgain = !jcb.isSelected();
2574 Cache.setProperty("CHECKURLLINKS",
2575 Boolean.valueOf(showWarningAgain).toString());
2580 JvOptionPane.showMessageDialog(desktopPane, msgPanel,
2582 .getString("label.SEQUENCE_ID_no_longer_used"),
2583 JvOptionPane.WARNING_MESSAGE);
2590 * Proxy class for JDesktopPane which optionally displays the current memory
2591 * usage and highlights the desktop area with a red bar if free memory runs low.
2595 public class MyDesktopPane extends JDesktopPane
2598 private static final float ONE_MB = 1048576f;
2600 boolean showMemoryUsage = false;
2604 java.text.NumberFormat df;
2606 float maxMemory, allocatedMemory, freeMemory, totalFreeMemory,
2609 public MyDesktopPane(boolean showMemoryUsage)
2611 showMemoryUsage(showMemoryUsage);
2614 public void showMemoryUsage(boolean showMemory)
2616 this.showMemoryUsage = showMemory;
2619 Thread worker = new Thread(this);
2625 public boolean isShowMemoryUsage()
2627 return showMemoryUsage;
2633 df = java.text.NumberFormat.getNumberInstance();
2634 df.setMaximumFractionDigits(2);
2635 runtime = Runtime.getRuntime();
2637 while (showMemoryUsage)
2641 maxMemory = runtime.maxMemory() / ONE_MB;
2642 allocatedMemory = runtime.totalMemory() / ONE_MB;
2643 freeMemory = runtime.freeMemory() / ONE_MB;
2644 totalFreeMemory = freeMemory + (maxMemory - allocatedMemory);
2646 percentUsage = (totalFreeMemory / maxMemory) * 100;
2648 // if (percentUsage < 20)
2650 // border1 = BorderFactory.createMatteBorder(12, 12, 12, 12,
2652 // instance.set.setBorder(border1);
2655 // sleep after showing usage
2657 } catch (Exception ex)
2659 ex.printStackTrace();
2665 public void paintComponent(Graphics g)
2667 if (showMemoryUsage && g != null && df != null)
2669 if (percentUsage < 20)
2671 g.setColor(Color.red);
2673 FontMetrics fm = g.getFontMetrics();
2676 g.drawString(MessageManager.formatMessage("label.memory_stats",
2678 { df.format(totalFreeMemory), df.format(maxMemory),
2679 df.format(percentUsage) }),
2680 10, getHeight() - fm.getHeight());
2687 * Accessor method to quickly get all the AlignmentFrames loaded.
2689 * @return an array of AlignFrame, or null if none found
2691 public static AlignFrame[] getAlignFrames()
2693 if (Jalview.isHeadlessMode())
2695 // Desktop.getDesktop() is null in headless mode
2696 return new AlignFrame[] { Jalview.getCurrentAlignFrame() };
2699 JInternalFrame[] frames = Desktop.getDesktopPane().getAllFrames();
2705 List<AlignFrame> avp = new ArrayList<>();
2707 for (int i = frames.length - 1; i > -1; i--)
2709 if (frames[i] instanceof AlignFrame)
2711 avp.add((AlignFrame) frames[i]);
2713 else if (frames[i] instanceof SplitFrame)
2716 * Also check for a split frame containing an AlignFrame
2718 GSplitFrame sf = (GSplitFrame) frames[i];
2719 if (sf.getTopFrame() instanceof AlignFrame)
2721 avp.add((AlignFrame) sf.getTopFrame());
2723 if (sf.getBottomFrame() instanceof AlignFrame)
2725 avp.add((AlignFrame) sf.getBottomFrame());
2729 if (avp.size() == 0)
2733 AlignFrame afs[] = avp.toArray(new AlignFrame[avp.size()]);
2738 * Returns an array of any AppJmol frames in the Desktop (or null if none).
2742 public GStructureViewer[] getJmols()
2744 JInternalFrame[] frames = Desktop.getDesktopPane().getAllFrames();
2750 List<GStructureViewer> avp = new ArrayList<>();
2752 for (int i = frames.length - 1; i > -1; i--)
2754 if (frames[i] instanceof AppJmol)
2756 GStructureViewer af = (GStructureViewer) frames[i];
2760 if (avp.size() == 0)
2764 GStructureViewer afs[] = avp.toArray(new GStructureViewer[avp.size()]);
2769 * Add Groovy Support to Jalview
2772 public void groovyShell_actionPerformed()
2776 openGroovyConsole();
2777 } catch (Exception ex)
2779 jalview.bin.Cache.log.error("Groovy Shell Creation failed.", ex);
2780 JvOptionPane.showInternalMessageDialog(desktopPane,
2782 MessageManager.getString("label.couldnt_create_groovy_shell"),
2783 MessageManager.getString("label.groovy_support_failed"),
2784 JvOptionPane.ERROR_MESSAGE);
2789 * Open the Groovy console
2791 private void openGroovyConsole()
2793 if (groovyConsole == null)
2795 groovyConsole = new groovy.ui.Console();
2796 groovyConsole.setVariable("Jalview", this);
2797 groovyConsole.run();
2800 * We allow only one console at a time, so that AlignFrame menu option
2801 * 'Calculate | Run Groovy script' is unambiguous.
2802 * Disable 'Groovy Console', and enable 'Run script', when the console is
2803 * opened, and the reverse when it is closed
2805 Window window = (Window) groovyConsole.getFrame();
2806 window.addWindowListener(new WindowAdapter()
2809 public void windowClosed(WindowEvent e)
2812 * rebind CMD-Q from Groovy Console to Jalview Quit
2815 enableExecuteGroovy(false);
2821 * show Groovy console window (after close and reopen)
2823 ((Window) groovyConsole.getFrame()).setVisible(true);
2826 * if we got this far, enable 'Run Groovy' in AlignFrame menus
2827 * and disable opening a second console
2829 enableExecuteGroovy(true);
2833 * Bind Ctrl/Cmd-Q to Quit - for reset as Groovy Console takes over this binding
2836 protected void addQuitHandler()
2838 getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
2839 .put(KeyStroke.getKeyStroke(KeyEvent.VK_Q,
2840 Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()),
2842 getRootPane().getActionMap().put("Quit", new AbstractAction()
2845 public void actionPerformed(ActionEvent e)
2853 * Enable or disable 'Run Groovy script' in AlignFrame calculate menus
2856 * true if Groovy console is open
2858 public void enableExecuteGroovy(boolean enabled)
2861 * disable opening a second Groovy console
2862 * (or re-enable when the console is closed)
2864 groovyShell.setEnabled(!enabled);
2866 AlignFrame[] alignFrames = getAlignFrames();
2867 if (alignFrames != null)
2869 for (AlignFrame af : alignFrames)
2871 af.setGroovyEnabled(enabled);
2877 * Progress bars managed by the IProgressIndicator method.
2879 private Hashtable<Long, JPanel> progressBars;
2881 private Hashtable<Long, IProgressIndicatorHandler> progressBarHandlers;
2886 * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
2889 public void setProgressBar(String message, long id)
2891 Platform.timeCheck("Desktop " + message, Platform.TIME_MARK);
2893 if (progressBars == null)
2895 progressBars = new Hashtable<>();
2896 progressBarHandlers = new Hashtable<>();
2899 if (progressBars.get(new Long(id)) != null)
2901 JPanel panel = progressBars.remove(new Long(id));
2902 if (progressBarHandlers.contains(new Long(id)))
2904 progressBarHandlers.remove(new Long(id));
2906 removeProgressPanel(panel);
2910 progressBars.put(new Long(id), addProgressPanel(message));
2917 * @see jalview.gui.IProgressIndicator#registerHandler(long,
2918 * jalview.gui.IProgressIndicatorHandler)
2921 public void registerHandler(final long id,
2922 final IProgressIndicatorHandler handler)
2924 if (progressBarHandlers == null
2925 || !progressBars.containsKey(new Long(id)))
2927 throw new Error(MessageManager.getString(
2928 "error.call_setprogressbar_before_registering_handler"));
2930 progressBarHandlers.put(new Long(id), handler);
2931 final JPanel progressPanel = progressBars.get(new Long(id));
2932 if (handler.canCancel())
2934 JButton cancel = new JButton(
2935 MessageManager.getString("action.cancel"));
2936 final IProgressIndicator us = this;
2937 cancel.addActionListener(new ActionListener()
2941 public void actionPerformed(ActionEvent e)
2943 handler.cancelActivity(id);
2944 us.setProgressBar(MessageManager
2945 .formatMessage("label.cancelled_params", new Object[]
2946 { ((JLabel) progressPanel.getComponent(0)).getText() }),
2950 progressPanel.add(cancel, BorderLayout.EAST);
2956 * @return true if any progress bars are still active
2959 public boolean operationInProgress()
2961 if (progressBars != null && progressBars.size() > 0)
2969 * This will return the first AlignFrame holding the given viewport instance. It
2970 * will break if there are more than one AlignFrames viewing a particular av.
2973 * @return alignFrame for viewport
2975 public static AlignFrame getAlignFrameFor(AlignViewportI viewport)
2977 if (getDesktopPane() != null)
2979 AlignmentPanel[] aps = getAlignmentPanels(
2980 viewport.getSequenceSetId());
2981 for (int panel = 0; aps != null && panel < aps.length; panel++)
2983 if (aps[panel] != null && aps[panel].av == viewport)
2985 return aps[panel].alignFrame;
2992 public VamsasApplication getVamsasApplication()
2999 * flag set if jalview GUI is being operated programmatically
3001 private boolean inBatchMode = false;
3004 * check if jalview GUI is being operated programmatically
3006 * @return inBatchMode
3008 public boolean isInBatchMode()
3014 * set flag if jalview GUI is being operated programmatically
3016 * @param inBatchMode
3018 public void setInBatchMode(boolean inBatchMode)
3020 this.inBatchMode = inBatchMode;
3023 public void startServiceDiscovery()
3025 startServiceDiscovery(false);
3028 public void startServiceDiscovery(boolean blocking)
3030 boolean alive = true;
3031 Thread t0 = null, t1 = null, t2 = null;
3032 // JAL-940 - JALVIEW 1 services are now being EOLed as of JABA 2.1 release
3035 // todo: changesupport handlers need to be transferred
3036 if (discoverer == null)
3038 discoverer = Discoverer.getInstance();
3039 // register PCS handler for getDesktop().
3040 discoverer.addPropertyChangeListener(changeSupport);
3042 // JAL-940 - disabled JWS1 service configuration - always start discoverer
3043 // until we phase out completely
3044 (t0 = new Thread(discoverer)).start();
3047 if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
3049 t2 = jalview.ws.jws2.Jws2Discoverer.getInstance()
3050 .startDiscoverer(changeSupport);
3054 // TODO: do rest service discovery
3063 } catch (Exception e)
3066 alive = (t1 != null && t1.isAlive()) || (t2 != null && t2.isAlive())
3067 || (t3 != null && t3.isAlive())
3068 || (t0 != null && t0.isAlive());
3074 * called to check if the service discovery process completed successfully.
3078 protected void JalviewServicesChanged(PropertyChangeEvent evt)
3080 if (evt.getNewValue() == null || evt.getNewValue() instanceof Vector)
3082 final String ermsg = jalview.ws.jws2.Jws2Discoverer.getInstance()
3083 .getErrorMessages();
3086 if (Cache.getDefault("SHOW_WSDISCOVERY_ERRORS", true))
3088 if (serviceChangedDialog == null)
3090 // only run if we aren't already displaying one of these.
3091 addDialogThread(serviceChangedDialog = new Runnable()
3098 * JalviewDialog jd =new JalviewDialog() {
3100 * @Override protected void cancelPressed() { // TODO
3101 * Auto-generated method stub
3103 * }@Override protected void okPressed() { // TODO
3104 * Auto-generated method stub
3106 * }@Override protected void raiseClosed() { // TODO
3107 * Auto-generated method stub
3109 * } }; jd.initDialogFrame(new
3110 * JLabel("<html><table width=\"450\"><tr><td>" + ermsg +
3111 * "<br/>It may be that you have invalid JABA URLs in your web service preferences,"
3112 * + " or mis-configured HTTP proxy settings.<br/>" +
3113 * "Check the <em>Connections</em> and <em>Web services</em> tab of the"
3115 * " Tools->Preferences dialog box to change them.</td></tr></table></html>"
3116 * ), true, true, "Web Service Configuration Problem", 450,
3119 * jd.waitForInput();
3121 JvOptionPane.showConfirmDialog(Desktop.getDesktopPane(),
3122 new JLabel("<html><table width=\"450\"><tr><td>"
3123 + ermsg + "</td></tr></table>"
3124 + "<p>It may be that you have invalid JABA URLs<br/>in your web service preferences,"
3125 + "<br>or as a command-line argument, or mis-configured HTTP proxy settings.</p>"
3126 + "<p>Check the <em>Connections</em> and <em>Web services</em> tab<br/>of the"
3127 + " Tools->Preferences dialog box to change them.</p></html>"),
3128 "Web Service Configuration Problem",
3129 JvOptionPane.DEFAULT_OPTION,
3130 JvOptionPane.ERROR_MESSAGE);
3131 serviceChangedDialog = null;
3140 "Errors reported by JABA discovery service. Check web services preferences.\n"
3147 Runnable serviceChangedDialog = null;
3150 * start a thread to open a URL in the configured browser. Pops up a warning
3151 * dialog to the user if there is an exception when calling out to the browser
3156 public static void showUrl(final String url)
3158 showUrl(url, Desktop.getInstance());
3162 * Like showUrl but allows progress handler to be specified
3166 * (null) or object implementing IProgressIndicator
3168 public static void showUrl(final String url,
3169 final IProgressIndicator progress)
3171 new Thread(new Runnable()
3178 if (progress != null)
3180 progress.setProgressBar(MessageManager
3181 .formatMessage("status.opening_params", new Object[]
3182 { url }), this.hashCode());
3184 BrowserLauncher.openURL(url);
3185 } catch (Exception ex)
3187 JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
3189 .getString("label.web_browser_not_found_unix"),
3190 MessageManager.getString("label.web_browser_not_found"),
3191 JvOptionPane.WARNING_MESSAGE);
3193 ex.printStackTrace();
3195 if (progress != null)
3197 progress.setProgressBar(null, this.hashCode());
3203 private WsParamSetManager wsparamManager = null;
3205 public static ParamManager getUserParameterStore()
3207 Desktop d = Desktop.getInstance();
3208 if (d.wsparamManager == null)
3210 d.wsparamManager = new WsParamSetManager();
3212 return d.wsparamManager;
3216 * static hyperlink handler proxy method for use by Jalview's internal windows
3220 public static void hyperlinkUpdate(HyperlinkEvent e)
3222 if (e.getEventType() == EventType.ACTIVATED)
3227 url = e.getURL().toString();
3228 Desktop.showUrl(url);
3229 } catch (Exception x)
3233 if (Cache.log != null)
3235 Cache.log.error("Couldn't handle string " + url + " as a URL.");
3240 "Couldn't handle string " + url + " as a URL.");
3243 // ignore any exceptions due to dud links.
3250 * single thread that handles display of dialogs to user.
3252 ExecutorService dialogExecutor = Executors.newSingleThreadExecutor();
3255 * flag indicating if dialogExecutor should try to acquire a permit
3257 volatile boolean dialogPause = true;
3262 java.util.concurrent.Semaphore block = new Semaphore(0);
3264 private groovy.ui.Console groovyConsole;
3266 public StructureViewer lastTargetedView;
3269 * add another dialog thread to the queue
3273 public void addDialogThread(final Runnable prompter)
3275 dialogExecutor.submit(new Runnable()
3285 } catch (InterruptedException x)
3289 if (Jalview.isHeadlessMode())
3295 SwingUtilities.invokeAndWait(prompter);
3296 } catch (Exception q)
3298 Cache.log.warn("Unexpected Exception in dialog thread.", q);
3304 public void startDialogQueue()
3306 // set the flag so we don't pause waiting for another permit and semaphore
3307 // the current task to begin
3308 dialogPause = false;
3313 * Outputs an image of the desktop to file in EPS format, after prompting the
3314 * user for choice of Text or Lineart character rendering (unless a preference
3315 * has been set). The file name is generated as
3318 * Jalview_snapshot_nnnnn.eps where nnnnn is the current timestamp in milliseconds
3322 protected void snapShotWindow_actionPerformed(ActionEvent e)
3324 // currently the menu option to do this is not shown
3327 int width = getWidth();
3328 int height = getHeight();
3330 "Jalview_snapshot_" + System.currentTimeMillis() + ".eps");
3331 ImageWriterI writer = new ImageWriterI()
3334 public void exportImage(Graphics g) throws Exception
3337 Cache.log.info("Successfully written snapshot to file "
3338 + of.getAbsolutePath());
3341 String title = "View of desktop";
3342 ImageExporter exporter = new ImageExporter(writer, null, TYPE.EPS,
3344 exporter.doExport(of, this, width, height, title);
3348 * Explode the views in the given SplitFrame into separate SplitFrame windows.
3349 * This respects (remembers) any previous 'exploded geometry' i.e. the size and
3350 * location last time the view was expanded (if any). However it does not
3351 * remember the split pane divider location - this is set to match the
3352 * 'exploding' frame.
3356 public void explodeViews(SplitFrame sf)
3358 AlignFrame oldTopFrame = (AlignFrame) sf.getTopFrame();
3359 AlignFrame oldBottomFrame = (AlignFrame) sf.getBottomFrame();
3360 List<? extends AlignmentViewPanel> topPanels = oldTopFrame
3362 List<? extends AlignmentViewPanel> bottomPanels = oldBottomFrame
3364 int viewCount = topPanels.size();
3371 * Processing in reverse order works, forwards order leaves the first panels
3372 * not visible. I don't know why!
3374 for (int i = viewCount - 1; i >= 0; i--)
3377 * Make new top and bottom frames. These take over the respective
3378 * AlignmentPanel objects, including their AlignmentViewports, so the
3379 * cdna/protein relationships between the viewports is carried over to the
3382 * explodedGeometry holds the (x, y) position of the previously exploded
3383 * SplitFrame, and the (width, height) of the AlignFrame component
3385 AlignmentPanel topPanel = (AlignmentPanel) topPanels.get(i);
3386 AlignFrame newTopFrame = new AlignFrame(topPanel);
3387 newTopFrame.setSize(oldTopFrame.getSize());
3388 newTopFrame.setVisible(true);
3389 Rectangle geometry = ((AlignViewport) topPanel.getAlignViewport())
3390 .getExplodedGeometry();
3391 if (geometry != null)
3393 newTopFrame.setSize(geometry.getSize());
3396 AlignmentPanel bottomPanel = (AlignmentPanel) bottomPanels.get(i);
3397 AlignFrame newBottomFrame = new AlignFrame(bottomPanel);
3398 newBottomFrame.setSize(oldBottomFrame.getSize());
3399 newBottomFrame.setVisible(true);
3400 geometry = ((AlignViewport) bottomPanel.getAlignViewport())
3401 .getExplodedGeometry();
3402 if (geometry != null)
3404 newBottomFrame.setSize(geometry.getSize());
3407 topPanel.av.setGatherViewsHere(false);
3408 bottomPanel.av.setGatherViewsHere(false);
3409 JInternalFrame splitFrame = new SplitFrame(newTopFrame,
3411 if (geometry != null)
3413 splitFrame.setLocation(geometry.getLocation());
3415 Desktop.addInternalFrame(splitFrame, sf.getTitle(), -1, -1);
3419 * Clear references to the panels (now relocated in the new SplitFrames)
3420 * before closing the old SplitFrame.
3423 bottomPanels.clear();
3428 * Gather expanded split frames, sharing the same pairs of sequence set ids,
3429 * back into the given SplitFrame as additional views. Note that the gathered
3430 * frames may themselves have multiple views.
3434 public void gatherViews(GSplitFrame source)
3437 * special handling of explodedGeometry for a view within a SplitFrame: - it
3438 * holds the (x, y) position of the enclosing SplitFrame, and the (width,
3439 * height) of the AlignFrame component
3441 AlignFrame myTopFrame = (AlignFrame) source.getTopFrame();
3442 AlignFrame myBottomFrame = (AlignFrame) source.getBottomFrame();
3443 myTopFrame.viewport.setExplodedGeometry(new Rectangle(source.getX(),
3444 source.getY(), myTopFrame.getWidth(), myTopFrame.getHeight()));
3445 myBottomFrame.viewport
3446 .setExplodedGeometry(new Rectangle(source.getX(), source.getY(),
3447 myBottomFrame.getWidth(), myBottomFrame.getHeight()));
3448 myTopFrame.viewport.setGatherViewsHere(true);
3449 myBottomFrame.viewport.setGatherViewsHere(true);
3450 String topViewId = myTopFrame.viewport.getSequenceSetId();
3451 String bottomViewId = myBottomFrame.viewport.getSequenceSetId();
3453 JInternalFrame[] frames = desktopPane.getAllFrames();
3454 for (JInternalFrame frame : frames)
3456 if (frame instanceof SplitFrame && frame != source)
3458 SplitFrame sf = (SplitFrame) frame;
3459 AlignFrame topFrame = (AlignFrame) sf.getTopFrame();
3460 AlignFrame bottomFrame = (AlignFrame) sf.getBottomFrame();
3461 boolean gatherThis = false;
3462 for (int a = 0; a < topFrame.alignPanels.size(); a++)
3464 AlignmentPanel topPanel = topFrame.alignPanels.get(a);
3465 AlignmentPanel bottomPanel = bottomFrame.alignPanels.get(a);
3466 if (topViewId.equals(topPanel.av.getSequenceSetId())
3467 && bottomViewId.equals(bottomPanel.av.getSequenceSetId()))
3470 topPanel.av.setGatherViewsHere(false);
3471 bottomPanel.av.setGatherViewsHere(false);
3472 topPanel.av.setExplodedGeometry(
3473 new Rectangle(sf.getLocation(), topFrame.getSize()));
3474 bottomPanel.av.setExplodedGeometry(
3475 new Rectangle(sf.getLocation(), bottomFrame.getSize()));
3476 myTopFrame.addAlignmentPanel(topPanel, false);
3477 myBottomFrame.addAlignmentPanel(bottomPanel, false);
3483 topFrame.getAlignPanels().clear();
3484 bottomFrame.getAlignPanels().clear();
3491 * The dust settles...give focus to the tab we did this from.
3493 myTopFrame.setDisplayedView(myTopFrame.alignPanel);
3496 public static groovy.ui.Console getGroovyConsole()
3498 Desktop desktop = Desktop.getInstance();
3499 return desktop == null ? null : desktop.groovyConsole;
3503 * handles the payload of a drag and drop event.
3505 * TODO refactor to desktop utilities class
3508 * - Data source strings extracted from the drop event
3510 * - protocol for each data source extracted from the drop event
3514 * - the payload from the drop event
3517 @SuppressWarnings("unchecked")
3518 public static void transferFromDropTarget(List<Object> files,
3519 List<DataSourceType> protocols, DropTargetDropEvent evt,
3520 Transferable t) throws Exception
3523 // BH 2018 changed List<String> to List<Object> to allow for File from SwingJS
3525 // DataFlavor[] flavors = t.getTransferDataFlavors();
3526 // for (int i = 0; i < flavors.length; i++) {
3527 // if (flavors[i].isFlavorJavaFileListType()) {
3528 // evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
3529 // List<File> list = (List<File>) t.getTransferData(flavors[i]);
3530 // for (int j = 0; j < list.size(); j++) {
3531 // File file = (File) list.get(j);
3532 // byte[] data = getDroppedFileBytes(file);
3533 // fileName.setText(file.getName() + " - " + data.length + " " +
3534 // evt.getLocation());
3535 // JTextArea target = (JTextArea) ((DropTarget) evt.getSource()).getComponent();
3536 // target.setText(new String(data));
3538 // dtde.dropComplete(true);
3543 DataFlavor uriListFlavor = new DataFlavor(
3544 "text/uri-list;class=java.lang.String"), urlFlavour = null;
3547 urlFlavour = new DataFlavor(
3548 "application/x-java-url; class=java.net.URL");
3549 } catch (ClassNotFoundException cfe)
3551 Cache.log.debug("Couldn't instantiate the URL dataflavor.", cfe);
3554 if (urlFlavour != null && t.isDataFlavorSupported(urlFlavour))
3559 java.net.URL url = (URL) t.getTransferData(urlFlavour);
3560 // nb: java 8 osx bug https://bugs.openjdk.java.net/browse/JDK-8156099
3561 // means url may be null.
3564 protocols.add(DataSourceType.URL);
3565 files.add(url.toString());
3566 Cache.log.debug("Drop handled as URL dataflavor "
3567 + files.get(files.size() - 1));
3572 if (Platform.isAMacAndNotJS())
3575 "Please ignore plist error - occurs due to problem with java 8 on OSX");
3578 } catch (Throwable ex)
3580 Cache.log.debug("URL drop handler failed.", ex);
3583 if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
3585 // Works on Windows and MacOSX
3586 Cache.log.debug("Drop handled as javaFileListFlavor");
3587 for (Object file : (List<Object>) t
3588 .getTransferData(DataFlavor.javaFileListFlavor))
3591 protocols.add(DataSourceType.FILE);
3596 // Unix like behaviour
3597 boolean added = false;
3599 if (t.isDataFlavorSupported(uriListFlavor))
3601 Cache.log.debug("Drop handled as uriListFlavor");
3602 // This is used by Unix drag system
3603 data = (String) t.getTransferData(uriListFlavor);
3607 // fallback to text: workaround - on OSX where there's a JVM bug
3608 Cache.log.debug("standard URIListFlavor failed. Trying text");
3609 // try text fallback
3610 DataFlavor textDf = new DataFlavor(
3611 "text/plain;class=java.lang.String");
3612 if (t.isDataFlavorSupported(textDf))
3614 data = (String) t.getTransferData(textDf);
3617 Cache.log.debug("Plain text drop content returned "
3618 + (data == null ? "Null - failed" : data));
3623 while (protocols.size() < files.size())
3625 Cache.log.debug("Adding missing FILE protocol for "
3626 + files.get(protocols.size()));
3627 protocols.add(DataSourceType.FILE);
3629 for (java.util.StringTokenizer st = new java.util.StringTokenizer(
3630 data, "\r\n"); st.hasMoreTokens();)
3633 String s = st.nextToken();
3634 if (s.startsWith("#"))
3636 // the line is a comment (as per the RFC 2483)
3639 java.net.URI uri = new java.net.URI(s);
3640 if (uri.getScheme().toLowerCase().startsWith("http"))
3642 protocols.add(DataSourceType.URL);
3643 files.add(uri.toString());
3647 // otherwise preserve old behaviour: catch all for file objects
3648 java.io.File file = new java.io.File(uri);
3649 protocols.add(DataSourceType.FILE);
3650 files.add(file.toString());
3655 if (Cache.log.isDebugEnabled())
3657 if (data == null || !added)
3660 if (t.getTransferDataFlavors() != null
3661 && t.getTransferDataFlavors().length > 0)
3664 "Couldn't resolve drop data. Here are the supported flavors:");
3665 for (DataFlavor fl : t.getTransferDataFlavors())
3668 "Supported transfer dataflavor: " + fl.toString());
3669 Object df = t.getTransferData(fl);
3672 Cache.log.debug("Retrieves: " + df);
3676 Cache.log.debug("Retrieved nothing");
3682 Cache.log.debug("Couldn't resolve dataflavor for drop: "
3688 if (Platform.isWindowsAndNotJS())
3690 Cache.log.debug("Scanning dropped content for Windows Link Files");
3692 // resolve any .lnk files in the file drop
3693 for (int f = 0; f < files.size(); f++)
3695 String source = files.get(f).toString().toLowerCase();
3696 if (protocols.get(f).equals(DataSourceType.FILE)
3697 && (source.endsWith(".lnk") || source.endsWith(".url")
3698 || source.endsWith(".site")))
3702 Object obj = files.get(f);
3703 File lf = (obj instanceof File ? (File) obj
3704 : new File((String) obj));
3705 // process link file to get a URL
3706 Cache.log.debug("Found potential link file: " + lf);
3707 WindowsShortcut wscfile = new WindowsShortcut(lf);
3708 String fullname = wscfile.getRealFilename();
3709 protocols.set(f, FormatAdapter.checkProtocol(fullname));
3710 files.set(f, fullname);
3711 Cache.log.debug("Parsed real filename " + fullname
3712 + " to extract protocol: " + protocols.get(f));
3713 } catch (Exception ex)
3716 "Couldn't parse " + files.get(f) + " as a link file.",
3725 * Sets the Preferences property for experimental features to True or False
3726 * depending on the state of the controlling menu item
3729 protected void showExperimental_actionPerformed(boolean selected)
3731 Cache.setProperty(EXPERIMENTAL_FEATURES, Boolean.toString(selected));
3735 * Answers a (possibly empty) list of any structure viewer frames (currently for
3736 * either Jmol or Chimera) which are currently open. This may optionally be
3737 * restricted to viewers of a specified class, or viewers linked to a specified
3741 * if not null, only return viewers linked to this panel
3742 * @param structureViewerClass
3743 * if not null, only return viewers of this class
3746 public List<StructureViewerBase> getStructureViewers(
3747 AlignmentPanel apanel,
3748 Class<? extends StructureViewerBase> structureViewerClass)
3750 List<StructureViewerBase> result = new ArrayList<>();
3751 JInternalFrame[] frames = getAllFrames();
3753 for (JInternalFrame frame : frames)
3755 if (frame instanceof StructureViewerBase)
3757 if (structureViewerClass == null
3758 || structureViewerClass.isInstance(frame))
3761 || ((StructureViewerBase) frame).isLinkedWith(apanel))
3763 result.add((StructureViewerBase) frame);