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 = 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 private boolean instanceOnly;
235 class MyDesktopManager implements DesktopManager
238 private DesktopManager delegate;
240 public MyDesktopManager(DesktopManager delegate)
242 this.delegate = delegate;
246 public void activateFrame(JInternalFrame f)
250 delegate.activateFrame(f);
251 } catch (NullPointerException npe)
253 Point p = getMousePosition();
254 showPasteMenu(p.x, p.y);
259 public void beginDraggingFrame(JComponent f)
261 delegate.beginDraggingFrame(f);
265 public void beginResizingFrame(JComponent f, int direction)
267 delegate.beginResizingFrame(f, direction);
271 public void closeFrame(JInternalFrame f)
273 delegate.closeFrame(f);
277 public void deactivateFrame(JInternalFrame f)
279 delegate.deactivateFrame(f);
283 public void deiconifyFrame(JInternalFrame f)
285 delegate.deiconifyFrame(f);
289 public void dragFrame(JComponent f, int newX, int newY)
295 delegate.dragFrame(f, newX, newY);
299 public void endDraggingFrame(JComponent f)
301 delegate.endDraggingFrame(f);
302 desktopPane.repaint();
306 public void endResizingFrame(JComponent f)
308 delegate.endResizingFrame(f);
309 desktopPane.repaint();
313 public void iconifyFrame(JInternalFrame f)
315 delegate.iconifyFrame(f);
319 public void maximizeFrame(JInternalFrame f)
321 delegate.maximizeFrame(f);
325 public void minimizeFrame(JInternalFrame f)
327 delegate.minimizeFrame(f);
331 public void openFrame(JInternalFrame f)
333 delegate.openFrame(f);
337 public void resizeFrame(JComponent f, int newX, int newY, int newWidth,
344 delegate.resizeFrame(f, newX, newY, newWidth, newHeight);
348 public void setBoundsForFrame(JComponent f, int newX, int newY,
349 int newWidth, int newHeight)
351 delegate.setBoundsForFrame(f, newX, newY, newWidth, newHeight);
354 // All other methods, simply delegate
358 public MyDesktopPane desktopPane;
361 * Answers an 'application scope' singleton instance of this class. Separate
362 * SwingJS 'applets' running in the same browser page will each have a
363 * distinct instance of Desktop.
367 public static Desktop getInstance()
369 return Jalview.isHeadlessMode() ? null
370 : (Desktop) ApplicationSingletonProvider
371 .getInstance(Desktop.class);
379 public Desktop(boolean forInstance)
387 * Private constructor enforces singleton pattern. It is called by reflection
388 * from ApplicationSingletonProvider.getInstance().
390 @SuppressWarnings("unused")
397 * A note to implementors. It is ESSENTIAL that any activities that might
398 * block are spawned off as threads rather than waited for during this
401 if (!Platform.isJS())
403 doVamsasClientCheck();
406 doConfigureStructurePrefs();
407 setTitle("Jalview " + jalview.bin.Cache.getProperty("VERSION"));
408 setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
409 boolean selmemusage = jalview.bin.Cache.getDefault("SHOW_MEMUSAGE",
411 boolean showjconsole = jalview.bin.Cache.getDefault("SHOW_JAVA_CONSOLE",
413 desktopPane = new MyDesktopPane(selmemusage);
415 showMemusage.setSelected(selmemusage);
416 desktopPane.setBackground(Color.white);
417 getContentPane().setLayout(new BorderLayout());
418 // alternate config - have scrollbars - see notes in JAL-153
419 // JScrollPane sp = new JScrollPane();
420 // sp.getViewport().setView(desktop);
421 // getContentPane().add(sp, BorderLayout.CENTER);
423 // BH 2018 - just an experiment to try unclipped JInternalFrames.
426 getRootPane().putClientProperty("swingjs.overflow.hidden", "false");
429 getContentPane().add(desktopPane, BorderLayout.CENTER);
430 desktopPane.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
432 // This line prevents Windows Look&Feel resizing all new windows to maximum
433 // if previous window was maximised
434 desktopPane.setDesktopManager(new MyDesktopManager(
435 (Platform.isWindowsAndNotJS() ? new DefaultDesktopManager()
436 : Platform.isAMacAndNotJS()
437 ? new AquaInternalFrameManager(
438 desktopPane.getDesktopManager())
439 : desktopPane.getDesktopManager())));
441 Rectangle dims = getLastKnownDimensions("");
448 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
449 int xPos = Math.max(5, (screenSize.width - 900) / 2);
450 int yPos = Math.max(5, (screenSize.height - 650) / 2);
451 setBounds(xPos, yPos, 900, 650);
454 if (!Platform.isJS())
462 jconsole = new Console(this, showjconsole);
463 // add essential build information
464 jconsole.setHeader("Jalview Version: "
465 + jalview.bin.Cache.getProperty("VERSION") + "\n"
466 + "Jalview Installation: "
467 + jalview.bin.Cache.getDefault("INSTALLATION", "unknown")
468 + "\n" + "Build Date: "
469 + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown") + "\n"
470 + "Java version: " + System.getProperty("java.version") + "\n"
471 + System.getProperty("os.arch") + " "
472 + System.getProperty("os.name") + " "
473 + System.getProperty("os.version"));
475 showConsole(showjconsole);
477 showNews.setVisible(false);
479 experimentalFeatures.setSelected(showExperimental());
481 getIdentifiersOrgData();
485 // Spawn a thread that shows the splashscreen
487 SwingUtilities.invokeLater(new Runnable()
496 // Thread off a new instance of the file chooser - this reduces the time
498 // takes to open it later on.
499 new Thread(new Runnable()
504 Cache.log.debug("Filechooser init thread started.");
505 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
506 JalviewFileChooser.forRead(Cache.getProperty("LAST_DIRECTORY"),
508 Cache.log.debug("Filechooser init thread finished.");
511 // Add the service change listener
512 changeSupport.addJalviewPropertyChangeListener("services",
513 new PropertyChangeListener()
517 public void propertyChange(PropertyChangeEvent evt)
519 Cache.log.debug("Firing service changed event for "
520 + evt.getNewValue());
521 JalviewServicesChanged(evt);
528 this.setDropTarget(new java.awt.dnd.DropTarget(desktopPane, this));
530 this.addWindowListener(new WindowAdapter()
533 public void windowClosing(WindowEvent evt)
540 this.addMouseListener(ma = new MouseAdapter()
543 public void mousePressed(MouseEvent evt)
545 if (evt.isPopupTrigger()) // Mac
547 showPasteMenu(evt.getX(), evt.getY());
552 public void mouseReleased(MouseEvent evt)
554 if (evt.isPopupTrigger()) // Windows
556 showPasteMenu(evt.getX(), evt.getY());
560 desktopPane.addMouseListener(ma);
561 } catch (Throwable t)
568 * Answers true if user preferences to enable experimental features is True
573 public boolean showExperimental()
575 String experimental = Cache.getDefault(EXPERIMENTAL_FEATURES,
576 Boolean.FALSE.toString());
577 return Boolean.valueOf(experimental).booleanValue();
580 public void doConfigureStructurePrefs()
582 // configure services
583 StructureSelectionManager ssm = StructureSelectionManager
584 .getStructureSelectionManager(this);
585 if (jalview.bin.Cache.getDefault(Preferences.ADD_SS_ANN, true))
587 ssm.setAddTempFacAnnot(jalview.bin.Cache
588 .getDefault(Preferences.ADD_TEMPFACT_ANN, true));
589 ssm.setProcessSecondaryStructure(jalview.bin.Cache
590 .getDefault(Preferences.STRUCT_FROM_PDB, true));
591 ssm.setSecStructServices(
592 jalview.bin.Cache.getDefault(Preferences.USE_RNAVIEW, true));
596 ssm.setAddTempFacAnnot(false);
597 ssm.setProcessSecondaryStructure(false);
598 ssm.setSecStructServices(false);
602 public void checkForNews()
604 final Desktop me = this;
605 // Thread off the news reader, in case there are connection problems.
606 new Thread(new Runnable()
611 Cache.log.debug("Starting news thread.");
612 jvnews = new BlogReader(me);
613 showNews.setVisible(true);
614 Cache.log.debug("Completed news thread.");
619 public void getIdentifiersOrgData()
621 // Thread off the identifiers fetcher
622 new Thread(new Runnable()
627 Cache.log.debug("Downloading data from identifiers.org");
628 // UrlDownloadClient client = new UrlDownloadClient();
631 UrlDownloadClient.download(IdOrgSettings.getUrl(),
632 IdOrgSettings.getDownloadLocation());
633 } catch (IOException e)
635 Cache.log.debug("Exception downloading identifiers.org data"
644 protected void showNews_actionPerformed(ActionEvent e)
646 showNews(showNews.isSelected());
649 protected void showNews(boolean visible)
651 Cache.log.debug((visible ? "Showing" : "Hiding") + " news.");
652 showNews.setSelected(visible);
653 if (visible && !jvnews.isVisible())
655 new Thread(new Runnable()
660 long now = System.currentTimeMillis();
662 MessageManager.getString("status.refreshing_news"), now);
663 jvnews.refreshNews();
664 setProgressBar(null, now);
672 * recover the last known dimensions for a jalview window
675 * - empty string is desktop, all other windows have unique prefix
676 * @return null or last known dimensions scaled to current geometry (if last
677 * window geom was known)
679 Rectangle getLastKnownDimensions(String windowName)
681 // TODO: lock aspect ratio for scaling desktop Bug #0058199
682 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
683 String x = jalview.bin.Cache.getProperty(windowName + "SCREEN_X");
684 String y = jalview.bin.Cache.getProperty(windowName + "SCREEN_Y");
685 String width = jalview.bin.Cache
686 .getProperty(windowName + "SCREEN_WIDTH");
687 String height = jalview.bin.Cache
688 .getProperty(windowName + "SCREEN_HEIGHT");
689 if ((x != null) && (y != null) && (width != null) && (height != null))
691 int ix = Integer.parseInt(x), iy = Integer.parseInt(y),
692 iw = Integer.parseInt(width), ih = Integer.parseInt(height);
693 if (jalview.bin.Cache.getProperty("SCREENGEOMETRY_WIDTH") != null)
695 // attempt #1 - try to cope with change in screen geometry - this
696 // version doesn't preserve original jv aspect ratio.
697 // take ratio of current screen size vs original screen size.
698 double sw = ((1f * screenSize.width) / (1f * Integer.parseInt(
699 jalview.bin.Cache.getProperty("SCREENGEOMETRY_WIDTH"))));
700 double sh = ((1f * screenSize.height) / (1f * Integer.parseInt(
701 jalview.bin.Cache.getProperty("SCREENGEOMETRY_HEIGHT"))));
702 // rescale the bounds depending upon the current screen geometry.
703 ix = (int) (ix * sw);
704 iw = (int) (iw * sw);
705 iy = (int) (iy * sh);
706 ih = (int) (ih * sh);
707 while (ix >= screenSize.width)
709 jalview.bin.Cache.log.debug(
710 "Window geometry location recall error: shifting horizontal to within screenbounds.");
711 ix -= screenSize.width;
713 while (iy >= screenSize.height)
715 jalview.bin.Cache.log.debug(
716 "Window geometry location recall error: shifting vertical to within screenbounds.");
717 iy -= screenSize.height;
719 jalview.bin.Cache.log.debug(
720 "Got last known dimensions for " + windowName + ": x:" + ix
721 + " y:" + iy + " width:" + iw + " height:" + ih);
723 // return dimensions for new instance
724 return new Rectangle(ix, iy, iw, ih);
729 private void doVamsasClientCheck()
731 if (Cache.vamsasJarsPresent())
733 setupVamsasDisconnectedGui();
734 VamsasMenu.setVisible(true);
735 final Desktop us = this;
736 VamsasMenu.addMenuListener(new MenuListener()
738 // this listener remembers when the menu was first selected, and
739 // doesn't rebuild the session list until it has been cleared and
741 boolean refresh = true;
744 public void menuCanceled(MenuEvent e)
750 public void menuDeselected(MenuEvent e)
756 public void menuSelected(MenuEvent e)
760 us.buildVamsasStMenu();
765 vamsasStart.setVisible(true);
769 protected void showPasteMenu(int x, int y)
771 JPopupMenu popup = new JPopupMenu();
772 JMenuItem item = new JMenuItem(
773 MessageManager.getString("label.paste_new_window"));
774 item.addActionListener(new ActionListener()
777 public void actionPerformed(ActionEvent evt)
784 popup.show(this, x, y);
791 Clipboard c = Toolkit.getDefaultToolkit().getSystemClipboard();
792 Transferable contents = c.getContents(this);
794 if (contents != null)
796 String file = (String) contents
797 .getTransferData(DataFlavor.stringFlavor);
799 FileFormatI format = new IdentifyFile().identify(file,
800 DataSourceType.PASTE);
802 new FileLoader(true).loadFile(file, DataSourceType.PASTE, format);
805 } catch (Exception ex)
808 "Unable to paste alignment from system clipboard:\n" + ex);
813 * Adds and opens the given frame to the desktop
824 public static synchronized void addInternalFrame(
825 final JInternalFrame frame, String title, int w, int h)
827 addInternalFrame(frame, title, true, w, h, true, false);
831 * Add an internal frame to the Jalview desktop
838 * When true, display frame immediately, otherwise, caller must call
839 * setVisible themselves.
845 public static synchronized void addInternalFrame(
846 final JInternalFrame frame, String title, boolean makeVisible,
849 addInternalFrame(frame, title, makeVisible, w, h, true, false);
853 * Add an internal frame to the Jalview desktop and make it visible
866 public static synchronized void addInternalFrame(
867 final JInternalFrame frame, String title, int w, int h,
870 addInternalFrame(frame, title, true, w, h, resizable, false);
874 * Add an internal frame to the Jalview desktop
881 * When true, display frame immediately, otherwise, caller must call
882 * setVisible themselves.
889 * @param ignoreMinSize
890 * Do not set the default minimum size for frame
892 public static synchronized void addInternalFrame(
893 final JInternalFrame frame, String title, boolean makeVisible,
894 int w, int h, boolean resizable, boolean ignoreMinSize)
898 // TODO: allow callers to determine X and Y position of frame (eg. via
900 // TODO: consider fixing method to update entries in the window submenu with
901 // the current window title
903 frame.setTitle(title);
904 if (w > 0 && (frame.getWidth() < 1 || frame.getHeight() < 1))
908 // THIS IS A PUBLIC STATIC METHOD, SO IT MAY BE CALLED EVEN IN
909 // A HEADLESS STATE WHEN NO DESKTOP EXISTS. MUST RETURN
910 // IF JALVIEW IS RUNNING HEADLESS
911 // ///////////////////////////////////////////////
912 if (Jalview.isHeadlessMode() || getInstance().instanceOnly)
921 frame.setMinimumSize(
922 new Dimension(DEFAULT_MIN_WIDTH, DEFAULT_MIN_HEIGHT));
924 // Set default dimension for Alignment Frame window.
925 // The Alignment Frame window could be added from a number of places,
927 // I did this here in order not to miss out on any Alignment frame.
928 if (frame instanceof AlignFrame.IFrame)
930 frame.setMinimumSize(new Dimension(ALIGN_FRAME_DEFAULT_MIN_WIDTH,
931 ALIGN_FRAME_DEFAULT_MIN_HEIGHT));
935 frame.setVisible(makeVisible);
936 frame.setClosable(true);
937 frame.setResizable(resizable);
938 frame.setMaximizable(resizable);
939 frame.setIconifiable(resizable);
940 frame.setOpaque(Platform.isJS());
942 if (frame.getX() < 1 && frame.getY() < 1)
944 frame.setLocation(xOffset * openFrameCount,
945 yOffset * ((openFrameCount - 1) % 10) + yOffset);
949 * add an entry for the new frame in the Window menu
950 * (and remove it when the frame is closed)
952 JMenuItem menuItem = new JMenuItem(title);
953 frame.addInternalFrameListener(new InternalFrameAdapter()
956 public void internalFrameActivated(InternalFrameEvent evt)
958 JInternalFrame itf = getDesktopPane().getSelectedFrame();
961 if (itf.getContentPane() instanceof AlignFrame)
963 Jalview.setCurrentAlignFrame((AlignFrame) itf.getContentPane());
970 public void internalFrameClosed(InternalFrameEvent evt)
972 PaintRefresher.RemoveComponent(frame);
975 * defensive check to prevent frames being
976 * added half off the window
978 if (openFrameCount > 0)
984 * ensure no reference to alignFrame retained by menu item listener
986 if (menuItem.getActionListeners().length > 0)
988 menuItem.removeActionListener(menuItem.getActionListeners()[0]);
990 getInstance().windowMenu.remove(menuItem);
994 menuItem.addActionListener(new ActionListener()
997 public void actionPerformed(ActionEvent e)
1001 frame.setSelected(true);
1002 frame.setIcon(false);
1003 } catch (java.beans.PropertyVetoException ex)
1005 // System.err.println(ex.toString());
1010 setKeyBindings(frame);
1012 getDesktopPane().add(frame);
1014 getInstance().windowMenu.add(menuItem);
1019 frame.setSelected(true);
1020 frame.requestFocus();
1021 } catch (java.beans.PropertyVetoException ve)
1023 } catch (java.lang.ClassCastException cex)
1026 "Squashed a possible GUI implementation error. If you can recreate this, please look at http://issues.jalview.org/browse/JAL-869",
1032 * Add key bindings to a JInternalFrame so that Ctrl-W and Cmd-W will close the
1037 private static void setKeyBindings(JInternalFrame frame)
1039 final Action closeAction = new AbstractAction()
1042 public void actionPerformed(ActionEvent e)
1049 * set up key bindings for Ctrl-W and Cmd-W, with the same (Close) action
1051 KeyStroke ctrlWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
1052 InputEvent.CTRL_DOWN_MASK);
1053 KeyStroke cmdWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
1054 Toolkit.getDefaultToolkit().getMenuShortcutKeyMask());
1056 InputMap inputMap = frame
1057 .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
1058 String ctrlW = ctrlWKey.toString();
1059 inputMap.put(ctrlWKey, ctrlW);
1060 inputMap.put(cmdWKey, ctrlW);
1062 ActionMap actionMap = frame.getActionMap();
1063 actionMap.put(ctrlW, closeAction);
1067 public void lostOwnership(Clipboard clipboard, Transferable contents)
1071 getInstance().jalviewClipboard = null;
1074 internalCopy = false;
1078 public void dragEnter(DropTargetDragEvent evt)
1083 public void dragExit(DropTargetEvent evt)
1088 public void dragOver(DropTargetDragEvent evt)
1093 public void dropActionChanged(DropTargetDragEvent evt)
1104 public void drop(DropTargetDropEvent evt)
1106 boolean success = true;
1107 // JAL-1552 - acceptDrop required before getTransferable call for
1108 // Java's Transferable for native dnd
1109 evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
1110 Transferable t = evt.getTransferable();
1111 List<Object> files = new ArrayList<>();
1112 List<DataSourceType> protocols = new ArrayList<>();
1116 Desktop.transferFromDropTarget(files, protocols, evt, t);
1117 } catch (Exception e)
1119 e.printStackTrace();
1127 for (int i = 0; i < files.size(); i++)
1129 // BH 2018 File or String
1130 Object file = files.get(i);
1131 String fileName = file.toString();
1132 DataSourceType protocol = (protocols == null)
1133 ? DataSourceType.FILE
1135 FileFormatI format = null;
1137 if (fileName.endsWith(".jar"))
1139 format = FileFormat.Jalview;
1144 format = new IdentifyFile().identify(file, protocol);
1146 if (file instanceof File)
1148 Platform.cacheFileData((File) file);
1150 new FileLoader().loadFile(null, file, protocol, format);
1153 } catch (Exception ex)
1158 evt.dropComplete(success); // need this to ensure input focus is properly
1159 // transfered to any new windows created
1169 public void inputLocalFileMenuItem_actionPerformed(AlignViewport viewport)
1171 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
1172 JalviewFileChooser chooser = JalviewFileChooser
1173 .forRead(Cache.getProperty("LAST_DIRECTORY"), fileFormat, true);
1175 chooser.setFileView(new JalviewFileView());
1176 chooser.setDialogTitle(
1177 MessageManager.getString("label.open_local_file"));
1178 chooser.setToolTipText(MessageManager.getString("action.open"));
1180 chooser.setResponseHandler(0, new Runnable()
1185 File selectedFile = chooser.getSelectedFile();
1186 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1188 FileFormatI format = chooser.getSelectedFormat();
1191 * Call IdentifyFile to verify the file contains what its extension implies.
1192 * Skip this step for dynamically added file formats, because
1193 * IdentifyFile does not know how to recognise them.
1195 if (FileFormats.getInstance().isIdentifiable(format))
1199 format = new IdentifyFile().identify(selectedFile,
1200 DataSourceType.FILE);
1201 } catch (FileFormatException e)
1203 // format = null; //??
1207 new FileLoader().loadFile(viewport, selectedFile,
1208 DataSourceType.FILE, format);
1211 chooser.showOpenDialog(this);
1215 * Shows a dialog for input of a URL at which to retrieve alignment data
1220 public void inputURLMenuItem_actionPerformed(AlignViewport viewport)
1222 // This construct allows us to have a wider textfield
1224 JLabel label = new JLabel(
1225 MessageManager.getString("label.input_file_url"));
1227 JPanel panel = new JPanel(new GridLayout(2, 1));
1231 * the URL to fetch is
1232 * Java: an editable combobox with history
1233 * JS: (pending JAL-3038) a plain text field
1236 String urlBase = "http://www.";
1237 if (Platform.isJS())
1239 history = new JTextField(urlBase, 35);
1248 JComboBox<String> asCombo = new JComboBox<>();
1249 asCombo.setPreferredSize(new Dimension(400, 20));
1250 asCombo.setEditable(true);
1251 asCombo.addItem(urlBase);
1252 String historyItems = Cache.getProperty("RECENT_URL");
1253 if (historyItems != null)
1255 for (String token : historyItems.split("\\t"))
1257 asCombo.addItem(token);
1264 Object[] options = new Object[] { MessageManager.getString("action.ok"),
1265 MessageManager.getString("action.cancel") };
1266 Runnable action = new Runnable()
1271 @SuppressWarnings("unchecked")
1272 String url = (history instanceof JTextField
1273 ? ((JTextField) history).getText()
1274 : ((JComboBox<String>) history).getSelectedItem()
1277 if (url.toLowerCase().endsWith(".jar"))
1279 if (viewport != null)
1281 new FileLoader().loadFile(viewport, url, DataSourceType.URL,
1282 FileFormat.Jalview);
1286 new FileLoader().loadFile(url, DataSourceType.URL,
1287 FileFormat.Jalview);
1292 FileFormatI format = null;
1295 format = new IdentifyFile().identify(url, DataSourceType.URL);
1296 } catch (FileFormatException e)
1298 // TODO revise error handling, distinguish between
1299 // URL not found and response not valid
1304 String msg = MessageManager
1305 .formatMessage("label.couldnt_locate", url);
1306 JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
1308 MessageManager.getString("label.url_not_found"),
1309 JvOptionPane.WARNING_MESSAGE);
1314 if (viewport != null)
1316 new FileLoader().loadFile(viewport, url, DataSourceType.URL,
1321 new FileLoader().loadFile(url, DataSourceType.URL, format);
1326 String dialogOption = MessageManager
1327 .getString("label.input_alignment_from_url");
1328 JvOptionPane.newOptionDialog(getDesktopPane())
1329 .setResponseHandler(0, action)
1330 .showInternalDialog(panel, dialogOption,
1331 JvOptionPane.YES_NO_CANCEL_OPTION,
1332 JvOptionPane.PLAIN_MESSAGE, null, options,
1333 MessageManager.getString("action.ok"));
1337 * Opens the CutAndPaste window for the user to paste an alignment in to
1340 * - if not null, the pasted alignment is added to the current
1341 * alignment; if null, to a new alignment window
1344 public void inputTextboxMenuItem_actionPerformed(
1345 AlignmentViewPanel viewPanel)
1347 CutAndPasteTransfer cap = new CutAndPasteTransfer();
1348 cap.setForInput(viewPanel);
1349 Desktop.addInternalFrame(cap,
1350 MessageManager.getString("label.cut_paste_alignmen_file"), true,
1360 Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
1361 jalview.bin.Cache.setProperty("SCREENGEOMETRY_WIDTH",
1363 jalview.bin.Cache.setProperty("SCREENGEOMETRY_HEIGHT",
1364 screen.height + "");
1365 storeLastKnownDimensions("", new Rectangle(getBounds().x, getBounds().y,
1366 getWidth(), getHeight()));
1368 if (jconsole != null)
1370 storeLastKnownDimensions("JAVA_CONSOLE_", jconsole.getBounds());
1371 jconsole.stopConsole();
1375 storeLastKnownDimensions("JALVIEW_RSS_WINDOW_", jvnews.getBounds());
1378 if (dialogExecutor != null)
1380 dialogExecutor.shutdownNow();
1382 closeAll_actionPerformed(null);
1384 if (groovyConsole != null)
1386 // suppress a possible repeat prompt to save script
1387 groovyConsole.setDirty(false);
1388 groovyConsole.exit();
1393 private void storeLastKnownDimensions(String string, Rectangle jc)
1395 jalview.bin.Cache.log.debug("Storing last known dimensions for "
1396 + string + ": x:" + jc.x + " y:" + jc.y + " width:" + jc.width
1397 + " height:" + jc.height);
1399 jalview.bin.Cache.setProperty(string + "SCREEN_X", jc.x + "");
1400 jalview.bin.Cache.setProperty(string + "SCREEN_Y", jc.y + "");
1401 jalview.bin.Cache.setProperty(string + "SCREEN_WIDTH", jc.width + "");
1402 jalview.bin.Cache.setProperty(string + "SCREEN_HEIGHT", jc.height + "");
1412 public void aboutMenuItem_actionPerformed(ActionEvent e)
1414 // StringBuffer message = getAboutMessage(false);
1415 // JvOptionPane.showInternalMessageDialog(Desktop.getDesktop(),
1417 // message.toString(), "About Jalview", JvOptionPane.INFORMATION_MESSAGE);
1418 new Thread(new Runnable()
1423 new SplashScreen(true);
1428 public StringBuffer getAboutMessage(boolean shortv)
1430 StringBuffer message = new StringBuffer();
1431 message.append("<html>");
1434 message.append("<h1><strong>Version: "
1435 + jalview.bin.Cache.getProperty("VERSION")
1436 + "</strong></h1>");
1437 message.append("<strong>Last Updated: <em>"
1438 + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown")
1439 + "</em></strong>");
1445 message.append("<strong>Version "
1446 + jalview.bin.Cache.getProperty("VERSION")
1447 + "; last updated: "
1448 + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown"));
1451 if (jalview.bin.Cache.getDefault("LATEST_VERSION", "Checking")
1452 .equals("Checking"))
1454 message.append("<br>...Checking latest version...</br>");
1456 else if (!jalview.bin.Cache.getDefault("LATEST_VERSION", "Checking")
1457 .equals(jalview.bin.Cache.getProperty("VERSION")))
1459 boolean red = false;
1460 if (jalview.bin.Cache.getProperty("VERSION").toLowerCase()
1461 .indexOf("automated build") == -1)
1464 // Displayed when code version and jnlp version do not match and code
1465 // version is not a development build
1466 message.append("<div style=\"color: #FF0000;font-style: bold;\">");
1469 message.append("<br>!! Version "
1470 + jalview.bin.Cache.getDefault("LATEST_VERSION",
1472 + " is available for download from "
1473 + jalview.bin.Cache.getDefault("www.jalview.org",
1474 "http://www.jalview.org")
1478 message.append("</div>");
1481 message.append("<br>Authors: " + jalview.bin.Cache.getDefault(
1483 "The Jalview Authors (See AUTHORS file for current list)")
1484 + "<br><br>Development managed by The Barton Group, University of Dundee, Scotland, UK.<br>"
1485 + "<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"
1486 + "<br><br>If you use Jalview, please cite:"
1487 + "<br>Waterhouse, A.M., Procter, J.B., Martin, D.M.A, Clamp, M. and Barton, G. J. (2009)"
1488 + "<br>Jalview Version 2 - a multiple sequence alignment editor and analysis workbench"
1489 + "<br>Bioinformatics doi: 10.1093/bioinformatics/btp033"
1495 * Action on requesting Help documentation
1498 public void documentationMenuItem_actionPerformed()
1502 if (Platform.isJS())
1504 BrowserLauncher.openURL("http://www.jalview.org/help.html");
1513 Help.showHelpWindow();
1515 } catch (Exception ex)
1517 System.err.println("Error opening help: " + ex.getMessage());
1522 public void closeAll_actionPerformed(ActionEvent e)
1524 if (desktopPane == null)
1528 // TODO show a progress bar while closing?
1529 JInternalFrame[] frames = desktopPane.getAllFrames();
1530 for (int i = 0; i < frames.length; i++)
1534 frames[i].setClosed(true);
1535 } catch (java.beans.PropertyVetoException ex)
1539 Jalview.setCurrentAlignFrame(null);
1540 System.out.println("ALL CLOSED");
1541 if (v_client != null)
1543 // TODO clear binding to vamsas document objects on close_all
1547 * reset state of singleton objects as appropriate (clear down session state
1548 * when all windows are closed)
1550 getStructureSelectionManager().resetAll();
1554 public void raiseRelated_actionPerformed(ActionEvent e)
1556 reorderAssociatedWindows(false, false);
1560 public void minimizeAssociated_actionPerformed(ActionEvent e)
1562 reorderAssociatedWindows(true, false);
1565 void closeAssociatedWindows()
1567 reorderAssociatedWindows(false, true);
1573 * @seejalview.jbgui.GDesktop#garbageCollect_actionPerformed(java.awt.event.
1577 protected void garbageCollect_actionPerformed(ActionEvent e)
1579 // We simply collect the garbage
1580 jalview.bin.Cache.log.debug("Collecting garbage...");
1582 jalview.bin.Cache.log.debug("Finished garbage collection.");
1589 * jalview.jbgui.GDesktop#showMemusage_actionPerformed(java.awt.event.ActionEvent
1593 protected void showMemusage_actionPerformed(ActionEvent e)
1595 getDesktopPane().showMemoryUsage(showMemusage.isSelected());
1602 * jalview.jbgui.GDesktop#showConsole_actionPerformed(java.awt.event.ActionEvent
1606 protected void showConsole_actionPerformed(ActionEvent e)
1608 showConsole(showConsole.isSelected());
1611 Console jconsole = null;
1614 * control whether the java console is visible or not
1618 void showConsole(boolean selected)
1620 // TODO: decide if we should update properties file
1621 if (jconsole != null) // BH 2018
1623 showConsole.setSelected(selected);
1624 Cache.setProperty("SHOW_JAVA_CONSOLE",
1625 Boolean.valueOf(selected).toString());
1626 jconsole.setVisible(selected);
1630 void reorderAssociatedWindows(boolean minimize, boolean close)
1632 JInternalFrame[] frames = getDesktopPane().getAllFrames();
1633 if (frames == null || frames.length < 1)
1638 AlignmentViewport source = null, target = null;
1639 if (frames[0] instanceof AlignFrame.IFrame)
1641 source = ((AlignFrame.IFrame) frames[0]).alignframe.getCurrentView();
1643 else if (frames[0] instanceof TreePanel)
1645 source = ((TreePanel) frames[0]).getViewPort();
1647 else if (frames[0] instanceof PCAPanel)
1649 source = ((PCAPanel) frames[0]).av;
1651 else if (frames[0].getContentPane() instanceof PairwiseAlignPanel)
1653 source = ((PairwiseAlignPanel) frames[0].getContentPane()).av;
1658 for (int i = 0; i < frames.length; i++)
1661 if (frames[i] == null)
1665 if (frames[i] instanceof AlignFrame.IFrame)
1667 target = ((AlignFrame.IFrame) frames[i]).alignframe
1670 else if (frames[i] instanceof TreePanel)
1672 target = ((TreePanel) frames[i]).getViewPort();
1674 else if (frames[i] instanceof PCAPanel)
1676 target = ((PCAPanel) frames[i]).av;
1678 else if (frames[i].getContentPane() instanceof PairwiseAlignPanel)
1680 target = ((PairwiseAlignPanel) frames[i].getContentPane()).av;
1683 if (source == target)
1689 frames[i].setClosed(true);
1693 frames[i].setIcon(minimize);
1696 frames[i].toFront();
1700 } catch (java.beans.PropertyVetoException ex)
1715 protected void preferences_actionPerformed(ActionEvent e)
1721 * Prompts the user to choose a file and then saves the Jalview state as a
1722 * Jalview project file
1725 public void saveState_actionPerformed()
1727 saveState_actionPerformed(false);
1730 public void saveState_actionPerformed(boolean saveAs)
1732 java.io.File projectFile = getProjectFile();
1733 // autoSave indicates we already have a file and don't need to ask
1734 boolean autoSave = projectFile != null && !saveAs
1735 && BackupFiles.getEnabled();
1737 // System.out.println("autoSave="+autoSave+", projectFile='"+projectFile+"',
1738 // saveAs="+saveAs+", Backups
1739 // "+(BackupFiles.getEnabled()?"enabled":"disabled"));
1741 boolean approveSave = false;
1744 JalviewFileChooser chooser = new JalviewFileChooser("jvp",
1747 chooser.setFileView(new JalviewFileView());
1748 chooser.setDialogTitle(MessageManager.getString("label.save_state"));
1750 int value = chooser.showSaveDialog(this);
1752 if (value == JalviewFileChooser.APPROVE_OPTION)
1754 projectFile = chooser.getSelectedFile();
1755 setProjectFile(projectFile);
1760 if (approveSave || autoSave)
1762 final Desktop me = this;
1763 final java.io.File chosenFile = projectFile;
1764 new Thread(new Runnable()
1769 // TODO: refactor to Jalview desktop session controller action.
1770 setProgressBar(MessageManager.formatMessage(
1771 "label.saving_jalview_project", new Object[]
1772 { chosenFile.getName() }), chosenFile.hashCode());
1773 jalview.bin.Cache.setProperty("LAST_DIRECTORY",
1774 chosenFile.getParent());
1775 // TODO catch and handle errors for savestate
1776 // TODO prevent user from messing with the Desktop whilst we're saving
1779 boolean doBackup = BackupFiles.getEnabled();
1780 BackupFiles backupfiles = doBackup ? new BackupFiles(chosenFile) : null;
1782 new Jalview2XML().saveState(doBackup ? backupfiles.getTempFile() : chosenFile);
1786 backupfiles.setWriteSuccess(true);
1787 backupfiles.rollBackupsAndRenameTempFile();
1789 } catch (OutOfMemoryError oom)
1791 new OOMWarning("Whilst saving current state to "
1792 + chosenFile.getName(), oom);
1793 } catch (Exception ex)
1795 Cache.log.error("Problems whilst trying to save to "
1796 + chosenFile.getName(), ex);
1797 JvOptionPane.showMessageDialog(me,
1798 MessageManager.formatMessage(
1799 "label.error_whilst_saving_current_state_to",
1801 { chosenFile.getName() }),
1802 MessageManager.getString("label.couldnt_save_project"),
1803 JvOptionPane.WARNING_MESSAGE);
1805 setProgressBar(null, chosenFile.hashCode());
1812 public void saveAsState_actionPerformed(ActionEvent e)
1814 saveState_actionPerformed(true);
1817 protected void setProjectFile(File choice)
1819 this.projectFile = choice;
1822 public File getProjectFile()
1824 return this.projectFile;
1828 * Shows a file chooser dialog and tries to read in the selected file as a
1832 public void loadState_actionPerformed()
1834 final String[] suffix = new String[] { "jvp", "jar" };
1835 final String[] desc = new String[] { "Jalview Project",
1836 "Jalview Project (old)" };
1837 JalviewFileChooser chooser = new JalviewFileChooser(
1838 Cache.getProperty("LAST_DIRECTORY"), suffix, desc,
1839 "Jalview Project", true, true); // last two booleans: allFiles,
1841 chooser.setFileView(new JalviewFileView());
1842 chooser.setDialogTitle(MessageManager.getString("label.restore_state"));
1843 chooser.setResponseHandler(0, new Runnable()
1848 File selectedFile = chooser.getSelectedFile();
1849 setProjectFile(selectedFile);
1850 String choice = selectedFile.getAbsolutePath();
1851 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1852 new Thread(new Runnable()
1859 new Jalview2XML().loadJalviewAlign(choice);
1860 } catch (OutOfMemoryError oom)
1862 new OOMWarning("Whilst loading project from " + choice, oom);
1863 } catch (Exception ex)
1866 "Problems whilst loading project from " + choice, ex);
1867 JvOptionPane.showMessageDialog(Desktop.getDesktopPane(),
1868 MessageManager.formatMessage(
1869 "label.error_whilst_loading_project_from",
1872 MessageManager.getString("label.couldnt_load_project"),
1873 JvOptionPane.WARNING_MESSAGE);
1880 chooser.showOpenDialog(this);
1884 public void inputSequence_actionPerformed(ActionEvent e)
1886 new SequenceFetcher(this);
1889 JPanel progressPanel;
1891 ArrayList<JPanel> fileLoadingPanels = new ArrayList<>();
1893 public void startLoading(final Object fileName)
1895 if (fileLoadingCount == 0)
1897 fileLoadingPanels.add(addProgressPanel(MessageManager
1898 .formatMessage("label.loading_file", new Object[]
1904 private JPanel addProgressPanel(String string)
1906 if (progressPanel == null)
1908 progressPanel = new JPanel(new GridLayout(1, 1));
1909 totalProgressCount = 0;
1910 getContentPane().add(progressPanel, BorderLayout.SOUTH);
1912 JPanel thisprogress = new JPanel(new BorderLayout(10, 5));
1913 JProgressBar progressBar = new JProgressBar();
1914 progressBar.setIndeterminate(true);
1916 thisprogress.add(new JLabel(string), BorderLayout.WEST);
1918 thisprogress.add(progressBar, BorderLayout.CENTER);
1919 progressPanel.add(thisprogress);
1920 ((GridLayout) progressPanel.getLayout()).setRows(
1921 ((GridLayout) progressPanel.getLayout()).getRows() + 1);
1922 ++totalProgressCount;
1924 return thisprogress;
1927 int totalProgressCount = 0;
1929 private void removeProgressPanel(JPanel progbar)
1931 if (progressPanel != null)
1933 synchronized (progressPanel)
1935 progressPanel.remove(progbar);
1936 GridLayout gl = (GridLayout) progressPanel.getLayout();
1937 gl.setRows(gl.getRows() - 1);
1938 if (--totalProgressCount < 1)
1940 this.getContentPane().remove(progressPanel);
1941 progressPanel = null;
1948 public void stopLoading()
1951 if (fileLoadingCount < 1)
1953 while (fileLoadingPanels.size() > 0)
1955 removeProgressPanel(fileLoadingPanels.remove(0));
1957 fileLoadingPanels.clear();
1958 fileLoadingCount = 0;
1963 public static int getViewCount(String alignmentId)
1965 AlignmentViewport[] aps = getViewports(alignmentId);
1966 return (aps == null) ? 0 : aps.length;
1971 * @param alignmentId
1972 * - if null, all sets are returned
1973 * @return all AlignmentPanels concerning the alignmentId sequence set
1975 public static AlignmentPanel[] getAlignmentPanels(String alignmentId)
1977 if (Desktop.getDesktopPane() == null)
1979 // no frames created and in headless mode
1980 // TODO: verify that frames are recoverable when in headless mode
1983 List<AlignmentPanel> aps = new ArrayList<>();
1984 AlignFrame[] frames = getAlignFrames();
1989 for (AlignFrame af : frames)
1991 for (AlignmentPanel ap : af.alignPanels)
1993 if (alignmentId == null
1994 || alignmentId.equals(ap.av.getSequenceSetId()))
2000 if (aps.size() == 0)
2004 AlignmentPanel[] vap = aps.toArray(new AlignmentPanel[aps.size()]);
2009 * get all the viewports on an alignment.
2011 * @param sequenceSetId
2012 * unique alignment id (may be null - all viewports returned in that
2014 * @return all viewports on the alignment bound to sequenceSetId
2016 public static AlignmentViewport[] getViewports(String sequenceSetId)
2018 List<AlignmentViewport> viewp = new ArrayList<>();
2019 if (getDesktopPane() != null)
2021 AlignFrame[] frames = Desktop.getAlignFrames();
2023 for (AlignFrame afr : frames)
2025 if (sequenceSetId == null || afr.getViewport().getSequenceSetId()
2026 .equals(sequenceSetId))
2028 if (afr.alignPanels != null)
2030 for (AlignmentPanel ap : afr.alignPanels)
2032 if (sequenceSetId == null
2033 || sequenceSetId.equals(ap.av.getSequenceSetId()))
2041 viewp.add(afr.getViewport());
2045 if (viewp.size() > 0)
2047 return viewp.toArray(new AlignmentViewport[viewp.size()]);
2054 * Explode the views in the given frame into separate AlignFrame
2058 public static void explodeViews(AlignFrame af)
2060 int size = af.alignPanels.size();
2066 for (int i = 0; i < size; i++)
2068 AlignmentPanel ap = af.alignPanels.get(i);
2069 AlignFrame newaf = new AlignFrame(ap);
2072 * Restore the view's last exploded frame geometry if known. Multiple
2073 * views from one exploded frame share and restore the same (frame)
2074 * position and size.
2076 Rectangle geometry = ap.av.getExplodedGeometry();
2077 if (geometry != null)
2079 newaf.setBounds(geometry);
2082 ap.av.setGatherViewsHere(false);
2084 addInternalFrame(newaf, af.getTitle(),
2085 AlignFrame.DEFAULT_WIDTH,
2086 AlignFrame.DEFAULT_HEIGHT);
2089 af.alignPanels.clear();
2090 af.closeMenuItem_actionPerformed(true);
2095 * Gather expanded views (separate AlignFrame's) with the same sequence set
2096 * identifier back in to this frame as additional views, and close the expanded
2097 * views. Note the expanded frames may themselves have multiple views. We take
2102 public void gatherViews(AlignFrame source)
2104 source.viewport.setGatherViewsHere(true);
2105 source.viewport.setExplodedGeometry(source.getBounds());
2106 JInternalFrame[] frames = getAllFrames();
2107 String viewId = source.viewport.getSequenceSetId();
2109 for (int t = 0; t < frames.length; t++)
2111 if (frames[t] instanceof AlignFrame.IFrame
2112 && ((AlignFrame.IFrame) frames[t]).alignframe != source)
2114 AlignFrame af = ((AlignFrame.IFrame) frames[t]).alignframe;
2115 boolean gatherThis = false;
2116 for (int a = 0; a < af.alignPanels.size(); a++)
2118 AlignmentPanel ap = af.alignPanels.get(a);
2119 if (viewId.equals(ap.av.getSequenceSetId()))
2122 ap.av.setGatherViewsHere(false);
2123 ap.av.setExplodedGeometry(af.getBounds());
2124 source.addAlignmentPanel(ap, false);
2130 af.alignPanels.clear();
2131 af.closeMenuItem_actionPerformed(true);
2138 jalview.gui.VamsasApplication v_client = null;
2141 public void vamsasImport_actionPerformed(ActionEvent e)
2143 // TODO: JAL-3048 not needed for Jalview-JS
2145 if (v_client == null)
2147 // Load and try to start a session.
2148 JalviewFileChooser chooser = new JalviewFileChooser(
2149 jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
2151 chooser.setFileView(new JalviewFileView());
2152 chooser.setDialogTitle(
2153 MessageManager.getString("label.open_saved_vamsas_session"));
2154 chooser.setToolTipText(MessageManager.getString(
2155 "label.select_vamsas_session_opened_as_new_vamsas_session"));
2157 int value = chooser.showOpenDialog(this);
2159 if (value == JalviewFileChooser.APPROVE_OPTION)
2161 String fle = chooser.getSelectedFile().toString();
2162 if (!vamsasImport(chooser.getSelectedFile()))
2164 JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
2165 MessageManager.formatMessage(
2166 "label.couldnt_import_as_vamsas_session",
2170 .getString("label.vamsas_document_import_failed"),
2171 JvOptionPane.ERROR_MESSAGE);
2177 jalview.bin.Cache.log.error(
2178 "Implementation error - load session from a running session is not supported.");
2183 * import file into a new vamsas session (uses jalview.gui.VamsasApplication)
2186 * @return true if import was a success and a session was started.
2188 public boolean vamsasImport(URL url)
2190 // TODO: create progress bar
2191 if (v_client != null)
2194 jalview.bin.Cache.log.error(
2195 "Implementation error - load session from a running session is not supported.");
2201 // copy the URL content to a temporary local file
2202 // TODO: be a bit cleverer here with nio (?!)
2203 File file = File.createTempFile("vdocfromurl", ".vdj");
2204 FileOutputStream fos = new FileOutputStream(file);
2205 BufferedInputStream bis = new BufferedInputStream(url.openStream());
2206 byte[] buffer = new byte[2048];
2208 while ((ln = bis.read(buffer)) > -1)
2210 fos.write(buffer, 0, ln);
2214 v_client = new jalview.gui.VamsasApplication(this, file,
2215 url.toExternalForm());
2216 } catch (Exception ex)
2218 jalview.bin.Cache.log.error(
2219 "Failed to create new vamsas session from contents of URL "
2224 setupVamsasConnectedGui();
2225 v_client.initial_update(); // TODO: thread ?
2226 return v_client.inSession();
2230 * import file into a new vamsas session (uses jalview.gui.VamsasApplication)
2233 * @return true if import was a success and a session was started.
2235 public boolean vamsasImport(File file)
2237 if (v_client != null)
2240 jalview.bin.Cache.log.error(
2241 "Implementation error - load session from a running session is not supported.");
2245 setProgressBar(MessageManager.formatMessage(
2246 "status.importing_vamsas_session_from", new Object[]
2247 { file.getName() }), file.hashCode());
2250 v_client = new jalview.gui.VamsasApplication(this, file, null);
2251 } catch (Exception ex)
2253 setProgressBar(MessageManager.formatMessage(
2254 "status.importing_vamsas_session_from", new Object[]
2255 { file.getName() }), file.hashCode());
2256 jalview.bin.Cache.log.error(
2257 "New vamsas session from existing session file failed:", ex);
2260 setupVamsasConnectedGui();
2261 v_client.initial_update(); // TODO: thread ?
2262 setProgressBar(MessageManager.formatMessage(
2263 "status.importing_vamsas_session_from", new Object[]
2264 { file.getName() }), file.hashCode());
2265 return v_client.inSession();
2268 public boolean joinVamsasSession(String mysesid)
2270 if (v_client != null)
2272 throw new Error(MessageManager
2273 .getString("error.try_join_vamsas_session_another"));
2275 if (mysesid == null)
2278 MessageManager.getString("error.invalid_vamsas_session_id"));
2280 v_client = new VamsasApplication(this, mysesid);
2281 setupVamsasConnectedGui();
2282 v_client.initial_update();
2283 return (v_client.inSession());
2287 public void vamsasStart_actionPerformed(ActionEvent e)
2289 if (v_client == null)
2292 // we just start a default session for moment.
2294 * JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache.
2295 * getProperty("LAST_DIRECTORY"));
2297 * chooser.setFileView(new JalviewFileView());
2298 * chooser.setDialogTitle("Load Vamsas file");
2299 * chooser.setToolTipText("Import");
2301 * int value = chooser.showOpenDialog(this);
2303 * if (value == JalviewFileChooser.APPROVE_OPTION) { v_client = new
2304 * jalview.gui.VamsasApplication(this, chooser.getSelectedFile());
2306 v_client = new VamsasApplication(this);
2307 setupVamsasConnectedGui();
2308 v_client.initial_update(); // TODO: thread ?
2312 // store current data in session.
2313 v_client.push_update(); // TODO: thread
2317 protected void setupVamsasConnectedGui()
2319 vamsasStart.setText(MessageManager.getString("label.session_update"));
2320 vamsasSave.setVisible(true);
2321 vamsasStop.setVisible(true);
2322 vamsasImport.setVisible(false); // Document import to existing session is
2323 // not possible for vamsas-client-1.0.
2326 protected void setupVamsasDisconnectedGui()
2328 vamsasSave.setVisible(false);
2329 vamsasStop.setVisible(false);
2330 vamsasImport.setVisible(true);
2332 .setText(MessageManager.getString("label.new_vamsas_session"));
2336 public void vamsasStop_actionPerformed(ActionEvent e)
2338 if (v_client != null)
2340 v_client.end_session();
2342 setupVamsasDisconnectedGui();
2346 protected void buildVamsasStMenu()
2348 if (v_client == null)
2350 String[] sess = null;
2353 sess = VamsasApplication.getSessionList();
2354 } catch (Exception e)
2356 jalview.bin.Cache.log.warn("Problem getting current sessions list.",
2362 jalview.bin.Cache.log.debug(
2363 "Got current sessions list: " + sess.length + " entries.");
2364 VamsasStMenu.removeAll();
2365 for (int i = 0; i < sess.length; i++)
2367 JMenuItem sessit = new JMenuItem();
2368 sessit.setText(sess[i]);
2369 sessit.setToolTipText(MessageManager
2370 .formatMessage("label.connect_to_session", new Object[]
2372 final Desktop dsktp = this;
2373 final String mysesid = sess[i];
2374 sessit.addActionListener(new ActionListener()
2378 public void actionPerformed(ActionEvent e)
2380 if (dsktp.v_client == null)
2382 Thread rthr = new Thread(new Runnable()
2388 dsktp.v_client = new VamsasApplication(dsktp, mysesid);
2389 dsktp.setupVamsasConnectedGui();
2390 dsktp.v_client.initial_update();
2398 VamsasStMenu.add(sessit);
2400 // don't show an empty menu.
2401 VamsasStMenu.setVisible(sess.length > 0);
2406 jalview.bin.Cache.log.debug("No current vamsas sessions.");
2407 VamsasStMenu.removeAll();
2408 VamsasStMenu.setVisible(false);
2413 // Not interested in the content. Just hide ourselves.
2414 VamsasStMenu.setVisible(false);
2419 public void vamsasSave_actionPerformed(ActionEvent e)
2421 // TODO: JAL-3048 not needed for Jalview-JS
2423 if (v_client != null)
2425 // TODO: VAMSAS DOCUMENT EXTENSION is VDJ
2426 JalviewFileChooser chooser = new JalviewFileChooser("vdj",
2429 chooser.setFileView(new JalviewFileView());
2430 chooser.setDialogTitle(MessageManager
2431 .getString("label.save_vamsas_document_archive"));
2433 int value = chooser.showSaveDialog(this);
2435 if (value == JalviewFileChooser.APPROVE_OPTION)
2437 java.io.File choice = chooser.getSelectedFile();
2438 JPanel progpanel = addProgressPanel(MessageManager
2439 .formatMessage("label.saving_vamsas_doc", new Object[]
2440 { choice.getName() }));
2441 Cache.setProperty("LAST_DIRECTORY", choice.getParent());
2442 String warnmsg = null;
2443 String warnttl = null;
2446 v_client.vclient.storeDocument(choice);
2449 warnttl = "Serious Problem saving Vamsas Document";
2450 warnmsg = ex.toString();
2451 jalview.bin.Cache.log
2452 .error("Error Whilst saving document to " + choice, ex);
2454 } catch (Exception ex)
2456 warnttl = "Problem saving Vamsas Document.";
2457 warnmsg = ex.toString();
2458 jalview.bin.Cache.log.warn(
2459 "Exception Whilst saving document to " + choice, ex);
2462 removeProgressPanel(progpanel);
2463 if (warnmsg != null)
2465 JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
2467 warnmsg, warnttl, JvOptionPane.ERROR_MESSAGE);
2473 JPanel vamUpdate = null;
2476 * hide vamsas user gui bits when a vamsas document event is being handled.
2479 * true to hide gui, false to reveal gui
2481 public void setVamsasUpdate(boolean b)
2483 Cache.log.debug("Setting gui for Vamsas update "
2484 + (b ? "in progress" : "finished"));
2486 if (vamUpdate != null)
2488 this.removeProgressPanel(vamUpdate);
2492 vamUpdate = this.addProgressPanel(
2493 MessageManager.getString("label.updating_vamsas_session"));
2495 vamsasStart.setVisible(!b);
2496 vamsasStop.setVisible(!b);
2497 vamsasSave.setVisible(!b);
2500 public JInternalFrame[] getAllFrames()
2502 return desktopPane.getAllFrames();
2506 * Checks the given url to see if it gives a response indicating that the user
2507 * should be informed of a new questionnaire.
2511 public void checkForQuestionnaire(String url)
2513 UserQuestionnaireCheck jvq = new UserQuestionnaireCheck(url);
2514 // javax.swing.SwingUtilities.invokeLater(jvq);
2515 new Thread(jvq).start();
2518 public void checkURLLinks()
2520 // Thread off the URL link checker
2521 addDialogThread(new Runnable()
2526 if (Cache.getDefault("CHECKURLLINKS", true))
2528 // check what the actual links are - if it's just the default don't
2529 // bother with the warning
2530 List<String> links = Preferences.sequenceUrlLinks
2533 // only need to check links if there is one with a
2534 // SEQUENCE_ID which is not the default EMBL_EBI link
2535 ListIterator<String> li = links.listIterator();
2536 boolean check = false;
2537 List<JLabel> urls = new ArrayList<>();
2538 while (li.hasNext())
2540 String link = li.next();
2541 if (link.contains(UrlConstants.SEQUENCE_ID)
2542 && !UrlConstants.isDefaultString(link))
2545 int barPos = link.indexOf("|");
2546 String urlMsg = barPos == -1 ? link
2547 : link.substring(0, barPos) + ": "
2548 + link.substring(barPos + 1);
2549 urls.add(new JLabel(urlMsg));
2557 // ask user to check in case URL links use old style tokens
2558 // ($SEQUENCE_ID$ for sequence id _or_ accession id)
2559 JPanel msgPanel = new JPanel();
2560 msgPanel.setLayout(new BoxLayout(msgPanel, BoxLayout.PAGE_AXIS));
2561 msgPanel.add(Box.createVerticalGlue());
2562 JLabel msg = new JLabel(MessageManager
2563 .getString("label.SEQUENCE_ID_for_DB_ACCESSION1"));
2564 JLabel msg2 = new JLabel(MessageManager
2565 .getString("label.SEQUENCE_ID_for_DB_ACCESSION2"));
2567 for (JLabel url : urls)
2573 final JCheckBox jcb = new JCheckBox(
2574 MessageManager.getString("label.do_not_display_again"));
2575 jcb.addActionListener(new ActionListener()
2578 public void actionPerformed(ActionEvent e)
2580 // update Cache settings for "don't show this again"
2581 boolean showWarningAgain = !jcb.isSelected();
2582 Cache.setProperty("CHECKURLLINKS",
2583 Boolean.valueOf(showWarningAgain).toString());
2588 JvOptionPane.showMessageDialog(desktopPane, msgPanel,
2590 .getString("label.SEQUENCE_ID_no_longer_used"),
2591 JvOptionPane.WARNING_MESSAGE);
2598 * Proxy class for JDesktopPane which optionally displays the current memory
2599 * usage and highlights the desktop area with a red bar if free memory runs low.
2603 public class MyDesktopPane extends JDesktopPane
2606 private static final float ONE_MB = 1048576f;
2608 boolean showMemoryUsage = false;
2612 java.text.NumberFormat df;
2614 float maxMemory, allocatedMemory, freeMemory, totalFreeMemory,
2617 public MyDesktopPane(boolean showMemoryUsage)
2619 showMemoryUsage(showMemoryUsage);
2622 public void showMemoryUsage(boolean showMemory)
2624 this.showMemoryUsage = showMemory;
2627 Thread worker = new Thread(this);
2633 public boolean isShowMemoryUsage()
2635 return showMemoryUsage;
2641 df = java.text.NumberFormat.getNumberInstance();
2642 df.setMaximumFractionDigits(2);
2643 runtime = Runtime.getRuntime();
2645 while (showMemoryUsage)
2649 maxMemory = runtime.maxMemory() / ONE_MB;
2650 allocatedMemory = runtime.totalMemory() / ONE_MB;
2651 freeMemory = runtime.freeMemory() / ONE_MB;
2652 totalFreeMemory = freeMemory + (maxMemory - allocatedMemory);
2654 percentUsage = (totalFreeMemory / maxMemory) * 100;
2656 // if (percentUsage < 20)
2658 // border1 = BorderFactory.createMatteBorder(12, 12, 12, 12,
2660 // instance.set.setBorder(border1);
2663 // sleep after showing usage
2665 } catch (Exception ex)
2667 ex.printStackTrace();
2673 public void paintComponent(Graphics g)
2675 if (showMemoryUsage && g != null && df != null)
2677 if (percentUsage < 20)
2679 g.setColor(Color.red);
2681 FontMetrics fm = g.getFontMetrics();
2684 g.drawString(MessageManager.formatMessage("label.memory_stats",
2686 { df.format(totalFreeMemory), df.format(maxMemory),
2687 df.format(percentUsage) }),
2688 10, getHeight() - fm.getHeight());
2696 * Accessor method to quickly get all the AlignmentFrames loaded.
2698 * @return an array of AlignFrame, or null if none found
2700 public static AlignFrame[] getAlignFrames()
2702 if (Jalview.isHeadlessMode())
2704 // Desktop.getDesktop() is null in headless mode
2705 return new AlignFrame[] { Jalview.getCurrentAlignFrame() };
2708 JInternalFrame[] frames = Desktop.getDesktopPane().getAllFrames();
2714 List<AlignFrame> avp = new ArrayList<>();
2716 for (int i = frames.length - 1; i > -1; i--)
2718 if (frames[i].getContentPane() instanceof AlignFrame)
2720 avp.add((AlignFrame) frames[i].getContentPane());
2722 else if (frames[i] instanceof SplitFrame)
2725 * Also check for a split frame containing an AlignFrame
2727 GSplitFrame sf = (GSplitFrame) frames[i];
2728 if (sf.getTopFrame() instanceof AlignFrame)
2730 avp.add((AlignFrame) sf.getTopFrame());
2732 if (sf.getBottomFrame() instanceof AlignFrame)
2734 avp.add((AlignFrame) sf.getBottomFrame());
2738 if (avp.size() == 0)
2742 AlignFrame afs[] = avp.toArray(new AlignFrame[avp.size()]);
2747 * Returns an array of any AppJmol frames in the Desktop (or null if none).
2751 public GStructureViewer[] getJmols()
2753 JInternalFrame[] frames = Desktop.getDesktopPane().getAllFrames();
2759 List<GStructureViewer> avp = new ArrayList<>();
2761 for (int i = frames.length - 1; i > -1; i--)
2763 if (frames[i] instanceof AppJmol)
2765 GStructureViewer af = (GStructureViewer) frames[i];
2769 if (avp.size() == 0)
2773 GStructureViewer afs[] = avp.toArray(new GStructureViewer[avp.size()]);
2778 * Add Groovy Support to Jalview
2781 public void groovyShell_actionPerformed()
2785 openGroovyConsole();
2786 } catch (Exception ex)
2788 jalview.bin.Cache.log.error("Groovy Shell Creation failed.", ex);
2789 JvOptionPane.showInternalMessageDialog(desktopPane,
2791 MessageManager.getString("label.couldnt_create_groovy_shell"),
2792 MessageManager.getString("label.groovy_support_failed"),
2793 JvOptionPane.ERROR_MESSAGE);
2798 * Open the Groovy console
2800 private void openGroovyConsole()
2802 if (groovyConsole == null)
2804 groovyConsole = new groovy.ui.Console();
2805 groovyConsole.setVariable("Jalview", this);
2806 groovyConsole.run();
2809 * We allow only one console at a time, so that AlignFrame menu option
2810 * 'Calculate | Run Groovy script' is unambiguous.
2811 * Disable 'Groovy Console', and enable 'Run script', when the console is
2812 * opened, and the reverse when it is closed
2814 Window window = (Window) groovyConsole.getFrame();
2815 window.addWindowListener(new WindowAdapter()
2818 public void windowClosed(WindowEvent e)
2821 * rebind CMD-Q from Groovy Console to Jalview Quit
2824 enableExecuteGroovy(false);
2830 * show Groovy console window (after close and reopen)
2832 ((Window) groovyConsole.getFrame()).setVisible(true);
2835 * if we got this far, enable 'Run Groovy' in AlignFrame menus
2836 * and disable opening a second console
2838 enableExecuteGroovy(true);
2842 * Bind Ctrl/Cmd-Q to Quit - for reset as Groovy Console takes over this binding
2845 protected void addQuitHandler()
2847 getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
2848 .put(KeyStroke.getKeyStroke(KeyEvent.VK_Q,
2849 Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()),
2851 getRootPane().getActionMap().put("Quit", new AbstractAction()
2854 public void actionPerformed(ActionEvent e)
2862 * Enable or disable 'Run Groovy script' in AlignFrame calculate menus
2865 * true if Groovy console is open
2867 public void enableExecuteGroovy(boolean enabled)
2870 * disable opening a second Groovy console
2871 * (or re-enable when the console is closed)
2873 groovyShell.setEnabled(!enabled);
2875 AlignFrame[] alignFrames = getAlignFrames();
2876 if (alignFrames != null)
2878 for (AlignFrame af : alignFrames)
2880 af.setGroovyEnabled(enabled);
2886 * Progress bars managed by the IProgressIndicator method.
2888 private Hashtable<Long, JPanel> progressBars;
2890 private Hashtable<Long, IProgressIndicatorHandler> progressBarHandlers;
2895 * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
2898 public void setProgressBar(String message, long id)
2900 Platform.timeCheck("Desktop " + message, Platform.TIME_MARK);
2902 if (progressBars == null)
2904 progressBars = new Hashtable<>();
2905 progressBarHandlers = new Hashtable<>();
2908 if (progressBars.get(new Long(id)) != null)
2910 JPanel panel = progressBars.remove(new Long(id));
2911 if (progressBarHandlers.contains(new Long(id)))
2913 progressBarHandlers.remove(new Long(id));
2915 removeProgressPanel(panel);
2919 progressBars.put(new Long(id), addProgressPanel(message));
2926 * @see jalview.gui.IProgressIndicator#registerHandler(long,
2927 * jalview.gui.IProgressIndicatorHandler)
2930 public void registerHandler(final long id,
2931 final IProgressIndicatorHandler handler)
2933 if (progressBarHandlers == null
2934 || !progressBars.containsKey(new Long(id)))
2936 throw new Error(MessageManager.getString(
2937 "error.call_setprogressbar_before_registering_handler"));
2939 progressBarHandlers.put(new Long(id), handler);
2940 final JPanel progressPanel = progressBars.get(new Long(id));
2941 if (handler.canCancel())
2943 JButton cancel = new JButton(
2944 MessageManager.getString("action.cancel"));
2945 final IProgressIndicator us = this;
2946 cancel.addActionListener(new ActionListener()
2950 public void actionPerformed(ActionEvent e)
2952 handler.cancelActivity(id);
2953 us.setProgressBar(MessageManager
2954 .formatMessage("label.cancelled_params", new Object[]
2955 { ((JLabel) progressPanel.getComponent(0)).getText() }),
2959 progressPanel.add(cancel, BorderLayout.EAST);
2965 * @return true if any progress bars are still active
2968 public boolean operationInProgress()
2970 if (progressBars != null && progressBars.size() > 0)
2978 * This will return the first AlignFrame holding the given viewport instance. It
2979 * will break if there are more than one AlignFrames viewing a particular av.
2982 * @return alignFrame for viewport
2984 public static AlignFrame getAlignFrameFor(AlignViewportI viewport)
2986 if (getDesktopPane() != null)
2988 AlignmentPanel[] aps = getAlignmentPanels(
2989 viewport.getSequenceSetId());
2990 for (int panel = 0; aps != null && panel < aps.length; panel++)
2992 if (aps[panel] != null && aps[panel].av == viewport)
2994 return aps[panel].alignFrame;
3001 public VamsasApplication getVamsasApplication()
3008 * flag set if jalview GUI is being operated programmatically
3010 private boolean inBatchMode = false;
3013 * check if jalview GUI is being operated programmatically
3015 * @return inBatchMode
3017 public boolean isInBatchMode()
3023 * set flag if jalview GUI is being operated programmatically
3025 * @param inBatchMode
3027 public void setInBatchMode(boolean inBatchMode)
3029 this.inBatchMode = inBatchMode;
3032 public void startServiceDiscovery()
3034 startServiceDiscovery(false);
3037 public void startServiceDiscovery(boolean blocking)
3039 boolean alive = true;
3040 Thread t0 = null, t1 = null, t2 = null;
3041 // JAL-940 - JALVIEW 1 services are now being EOLed as of JABA 2.1 release
3044 // todo: changesupport handlers need to be transferred
3045 if (discoverer == null)
3047 discoverer = Discoverer.getInstance();
3048 // register PCS handler for getDesktop().
3049 discoverer.addPropertyChangeListener(changeSupport);
3051 // JAL-940 - disabled JWS1 service configuration - always start discoverer
3052 // until we phase out completely
3053 (t0 = new Thread(discoverer)).start();
3056 if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
3058 t2 = jalview.ws.jws2.Jws2Discoverer.getInstance()
3059 .startDiscoverer(changeSupport);
3063 // TODO: do rest service discovery
3072 } catch (Exception e)
3075 alive = (t1 != null && t1.isAlive()) || (t2 != null && t2.isAlive())
3076 || (t3 != null && t3.isAlive())
3077 || (t0 != null && t0.isAlive());
3083 * called to check if the service discovery process completed successfully.
3087 protected void JalviewServicesChanged(PropertyChangeEvent evt)
3089 if (evt.getNewValue() == null || evt.getNewValue() instanceof Vector)
3091 final String ermsg = jalview.ws.jws2.Jws2Discoverer.getInstance()
3092 .getErrorMessages();
3095 if (Cache.getDefault("SHOW_WSDISCOVERY_ERRORS", true))
3097 if (serviceChangedDialog == null)
3099 // only run if we aren't already displaying one of these.
3100 addDialogThread(serviceChangedDialog = new Runnable()
3107 * JalviewDialog jd =new JalviewDialog() {
3109 * @Override protected void cancelPressed() { // TODO
3110 * Auto-generated method stub
3112 * }@Override protected void okPressed() { // TODO
3113 * Auto-generated method stub
3115 * }@Override protected void raiseClosed() { // TODO
3116 * Auto-generated method stub
3118 * } }; jd.initDialogFrame(new
3119 * JLabel("<html><table width=\"450\"><tr><td>" + ermsg +
3120 * "<br/>It may be that you have invalid JABA URLs in your web service preferences,"
3121 * + " or mis-configured HTTP proxy settings.<br/>" +
3122 * "Check the <em>Connections</em> and <em>Web services</em> tab of the"
3124 * " Tools->Preferences dialog box to change them.</td></tr></table></html>"
3125 * ), true, true, "Web Service Configuration Problem", 450,
3128 * jd.waitForInput();
3130 JvOptionPane.showConfirmDialog(Desktop.getDesktopPane(),
3131 new JLabel("<html><table width=\"450\"><tr><td>"
3132 + ermsg + "</td></tr></table>"
3133 + "<p>It may be that you have invalid JABA URLs<br/>in your web service preferences,"
3134 + "<br>or as a command-line argument, or mis-configured HTTP proxy settings.</p>"
3135 + "<p>Check the <em>Connections</em> and <em>Web services</em> tab<br/>of the"
3136 + " Tools->Preferences dialog box to change them.</p></html>"),
3137 "Web Service Configuration Problem",
3138 JvOptionPane.DEFAULT_OPTION,
3139 JvOptionPane.ERROR_MESSAGE);
3140 serviceChangedDialog = null;
3149 "Errors reported by JABA discovery service. Check web services preferences.\n"
3156 Runnable serviceChangedDialog = null;
3159 * start a thread to open a URL in the configured browser. Pops up a warning
3160 * dialog to the user if there is an exception when calling out to the browser
3165 public static void showUrl(final String url)
3167 showUrl(url, getInstance());
3171 * Like showUrl but allows progress handler to be specified
3175 * (null) or object implementing IProgressIndicator
3177 public static void showUrl(final String url,
3178 final IProgressIndicator progress)
3180 new Thread(new Runnable()
3187 if (progress != null)
3189 progress.setProgressBar(MessageManager
3190 .formatMessage("status.opening_params", new Object[]
3191 { url }), this.hashCode());
3193 BrowserLauncher.openURL(url);
3194 } catch (Exception ex)
3196 JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
3198 .getString("label.web_browser_not_found_unix"),
3199 MessageManager.getString("label.web_browser_not_found"),
3200 JvOptionPane.WARNING_MESSAGE);
3202 ex.printStackTrace();
3204 if (progress != null)
3206 progress.setProgressBar(null, this.hashCode());
3212 private WsParamSetManager wsparamManager = null;
3214 public static ParamManager getUserParameterStore()
3216 Desktop d = getInstance();
3217 if (d.wsparamManager == null)
3219 d.wsparamManager = new WsParamSetManager();
3221 return d.wsparamManager;
3225 * static hyperlink handler proxy method for use by Jalview's internal windows
3229 public static void hyperlinkUpdate(HyperlinkEvent e)
3231 if (e.getEventType() == EventType.ACTIVATED)
3236 url = e.getURL().toString();
3237 Desktop.showUrl(url);
3238 } catch (Exception x)
3242 if (Cache.log != null)
3244 Cache.log.error("Couldn't handle string " + url + " as a URL.");
3249 "Couldn't handle string " + url + " as a URL.");
3252 // ignore any exceptions due to dud links.
3259 * single thread that handles display of dialogs to user.
3261 ExecutorService dialogExecutor = Executors.newSingleThreadExecutor();
3264 * flag indicating if dialogExecutor should try to acquire a permit
3266 volatile boolean dialogPause = true;
3271 java.util.concurrent.Semaphore block = new Semaphore(0);
3273 private groovy.ui.Console groovyConsole;
3275 public StructureViewer lastTargetedView;
3278 * add another dialog thread to the queue
3282 public void addDialogThread(final Runnable prompter)
3284 dialogExecutor.submit(new Runnable()
3294 } catch (InterruptedException x)
3304 SwingUtilities.invokeAndWait(prompter);
3305 } catch (Exception q)
3307 Cache.log.warn("Unexpected Exception in dialog thread.", q);
3313 public void startDialogQueue()
3315 // set the flag so we don't pause waiting for another permit and semaphore
3316 // the current task to begin
3317 dialogPause = false;
3322 * Outputs an image of the desktop to file in EPS format, after prompting the
3323 * user for choice of Text or Lineart character rendering (unless a preference
3324 * has been set). The file name is generated as
3327 * Jalview_snapshot_nnnnn.eps where nnnnn is the current timestamp in milliseconds
3331 protected void snapShotWindow_actionPerformed(ActionEvent e)
3333 // currently the menu option to do this is not shown
3336 int width = getWidth();
3337 int height = getHeight();
3339 "Jalview_snapshot_" + System.currentTimeMillis() + ".eps");
3340 ImageWriterI writer = new ImageWriterI()
3343 public void exportImage(Graphics g) throws Exception
3346 Cache.log.info("Successfully written snapshot to file "
3347 + of.getAbsolutePath());
3350 String title = "View of desktop";
3351 ImageExporter exporter = new ImageExporter(writer, null, TYPE.EPS,
3353 exporter.doExport(of, this, width, height, title);
3357 * Explode the views in the given SplitFrame into separate SplitFrame windows.
3358 * This respects (remembers) any previous 'exploded geometry' i.e. the size and
3359 * location last time the view was expanded (if any). However it does not
3360 * remember the split pane divider location - this is set to match the
3361 * 'exploding' frame.
3365 public void explodeViews(SplitFrame sf)
3367 AlignFrame oldTopFrame = (AlignFrame) sf.getTopFrame();
3368 AlignFrame oldBottomFrame = (AlignFrame) sf.getBottomFrame();
3369 List<? extends AlignmentViewPanel> topPanels = oldTopFrame
3371 List<? extends AlignmentViewPanel> bottomPanels = oldBottomFrame
3373 int viewCount = topPanels.size();
3380 * Processing in reverse order works, forwards order leaves the first panels
3381 * not visible. I don't know why!
3383 for (int i = viewCount - 1; i >= 0; i--)
3386 * Make new top and bottom frames. These take over the respective
3387 * AlignmentPanel objects, including their AlignmentViewports, so the
3388 * cdna/protein relationships between the viewports is carried over to the
3391 * explodedGeometry holds the (x, y) position of the previously exploded
3392 * SplitFrame, and the (width, height) of the AlignFrame component
3394 AlignmentPanel topPanel = (AlignmentPanel) topPanels.get(i);
3395 AlignFrame newTopFrame = new AlignFrame(topPanel);
3396 newTopFrame.setSize(oldTopFrame.getSize());
3397 newTopFrame.setVisible(true);
3398 Rectangle geometry = ((AlignViewport) topPanel.getAlignViewport())
3399 .getExplodedGeometry();
3400 if (geometry != null)
3402 newTopFrame.setSize(geometry.getSize());
3405 AlignmentPanel bottomPanel = (AlignmentPanel) bottomPanels.get(i);
3406 AlignFrame newBottomFrame = new AlignFrame(bottomPanel);
3407 newBottomFrame.setSize(oldBottomFrame.getSize());
3408 newBottomFrame.setVisible(true);
3409 geometry = ((AlignViewport) bottomPanel.getAlignViewport())
3410 .getExplodedGeometry();
3411 if (geometry != null)
3413 newBottomFrame.setSize(geometry.getSize());
3416 topPanel.av.setGatherViewsHere(false);
3417 bottomPanel.av.setGatherViewsHere(false);
3418 JInternalFrame splitFrame = new SplitFrame(newTopFrame,
3420 if (geometry != null)
3422 splitFrame.setLocation(geometry.getLocation());
3424 Desktop.addInternalFrame(splitFrame, sf.getTitle(), -1, -1);
3428 * Clear references to the panels (now relocated in the new SplitFrames)
3429 * before closing the old SplitFrame.
3432 bottomPanels.clear();
3437 * Gather expanded split frames, sharing the same pairs of sequence set ids,
3438 * back into the given SplitFrame as additional views. Note that the gathered
3439 * frames may themselves have multiple views.
3443 public void gatherViews(GSplitFrame source)
3446 * special handling of explodedGeometry for a view within a SplitFrame: - it
3447 * holds the (x, y) position of the enclosing SplitFrame, and the (width,
3448 * height) of the AlignFrame component
3450 AlignFrame myTopFrame = (AlignFrame) source.getTopFrame();
3451 AlignFrame myBottomFrame = (AlignFrame) source.getBottomFrame();
3452 myTopFrame.viewport.setExplodedGeometry(new Rectangle(source.getX(),
3453 source.getY(), myTopFrame.getWidth(), myTopFrame.getHeight()));
3454 myBottomFrame.viewport
3455 .setExplodedGeometry(new Rectangle(source.getX(), source.getY(),
3456 myBottomFrame.getWidth(), myBottomFrame.getHeight()));
3457 myTopFrame.viewport.setGatherViewsHere(true);
3458 myBottomFrame.viewport.setGatherViewsHere(true);
3459 String topViewId = myTopFrame.viewport.getSequenceSetId();
3460 String bottomViewId = myBottomFrame.viewport.getSequenceSetId();
3462 JInternalFrame[] frames = desktopPane.getAllFrames();
3463 for (JInternalFrame frame : frames)
3465 if (frame instanceof SplitFrame && frame != source)
3467 SplitFrame sf = (SplitFrame) frame;
3468 AlignFrame topFrame = (AlignFrame) sf.getTopFrame();
3469 AlignFrame bottomFrame = (AlignFrame) sf.getBottomFrame();
3470 boolean gatherThis = false;
3471 for (int a = 0; a < topFrame.alignPanels.size(); a++)
3473 AlignmentPanel topPanel = topFrame.alignPanels.get(a);
3474 AlignmentPanel bottomPanel = bottomFrame.alignPanels.get(a);
3475 if (topViewId.equals(topPanel.av.getSequenceSetId())
3476 && bottomViewId.equals(bottomPanel.av.getSequenceSetId()))
3479 topPanel.av.setGatherViewsHere(false);
3480 bottomPanel.av.setGatherViewsHere(false);
3481 topPanel.av.setExplodedGeometry(
3482 new Rectangle(sf.getLocation(), topFrame.getSize()));
3483 bottomPanel.av.setExplodedGeometry(
3484 new Rectangle(sf.getLocation(), bottomFrame.getSize()));
3485 myTopFrame.addAlignmentPanel(topPanel, false);
3486 myBottomFrame.addAlignmentPanel(bottomPanel, false);
3492 topFrame.getAlignPanels().clear();
3493 bottomFrame.getAlignPanels().clear();
3500 * The dust settles...give focus to the tab we did this from.
3502 myTopFrame.setDisplayedView(myTopFrame.alignPanel);
3505 public static groovy.ui.Console getGroovyConsole()
3507 Desktop desktop = getInstance();
3508 return desktop == null ? null : desktop.groovyConsole;
3512 * handles the payload of a drag and drop event.
3514 * TODO refactor to desktop utilities class
3517 * - Data source strings extracted from the drop event
3519 * - protocol for each data source extracted from the drop event
3523 * - the payload from the drop event
3526 @SuppressWarnings("unchecked")
3527 public static void transferFromDropTarget(List<Object> files,
3528 List<DataSourceType> protocols, DropTargetDropEvent evt,
3529 Transferable t) throws Exception
3532 // BH 2018 changed List<String> to List<Object> to allow for File from SwingJS
3534 // DataFlavor[] flavors = t.getTransferDataFlavors();
3535 // for (int i = 0; i < flavors.length; i++) {
3536 // if (flavors[i].isFlavorJavaFileListType()) {
3537 // evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
3538 // List<File> list = (List<File>) t.getTransferData(flavors[i]);
3539 // for (int j = 0; j < list.size(); j++) {
3540 // File file = (File) list.get(j);
3541 // byte[] data = getDroppedFileBytes(file);
3542 // fileName.setText(file.getName() + " - " + data.length + " " +
3543 // evt.getLocation());
3544 // JTextArea target = (JTextArea) ((DropTarget) evt.getSource()).getComponent();
3545 // target.setText(new String(data));
3547 // dtde.dropComplete(true);
3552 DataFlavor uriListFlavor = new DataFlavor(
3553 "text/uri-list;class=java.lang.String"), urlFlavour = null;
3556 urlFlavour = new DataFlavor(
3557 "application/x-java-url; class=java.net.URL");
3558 } catch (ClassNotFoundException cfe)
3560 Cache.log.debug("Couldn't instantiate the URL dataflavor.", cfe);
3563 if (urlFlavour != null && t.isDataFlavorSupported(urlFlavour))
3568 java.net.URL url = (URL) t.getTransferData(urlFlavour);
3569 // nb: java 8 osx bug https://bugs.openjdk.java.net/browse/JDK-8156099
3570 // means url may be null.
3573 protocols.add(DataSourceType.URL);
3574 files.add(url.toString());
3575 Cache.log.debug("Drop handled as URL dataflavor "
3576 + files.get(files.size() - 1));
3581 if (Platform.isAMacAndNotJS())
3584 "Please ignore plist error - occurs due to problem with java 8 on OSX");
3587 } catch (Throwable ex)
3589 Cache.log.debug("URL drop handler failed.", ex);
3592 if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
3594 // Works on Windows and MacOSX
3595 Cache.log.debug("Drop handled as javaFileListFlavor");
3596 for (Object file : (List<Object>) t
3597 .getTransferData(DataFlavor.javaFileListFlavor))
3600 protocols.add(DataSourceType.FILE);
3605 // Unix like behaviour
3606 boolean added = false;
3608 if (t.isDataFlavorSupported(uriListFlavor))
3610 Cache.log.debug("Drop handled as uriListFlavor");
3611 // This is used by Unix drag system
3612 data = (String) t.getTransferData(uriListFlavor);
3616 // fallback to text: workaround - on OSX where there's a JVM bug
3617 Cache.log.debug("standard URIListFlavor failed. Trying text");
3618 // try text fallback
3619 DataFlavor textDf = new DataFlavor(
3620 "text/plain;class=java.lang.String");
3621 if (t.isDataFlavorSupported(textDf))
3623 data = (String) t.getTransferData(textDf);
3626 Cache.log.debug("Plain text drop content returned "
3627 + (data == null ? "Null - failed" : data));
3632 while (protocols.size() < files.size())
3634 Cache.log.debug("Adding missing FILE protocol for "
3635 + files.get(protocols.size()));
3636 protocols.add(DataSourceType.FILE);
3638 for (java.util.StringTokenizer st = new java.util.StringTokenizer(
3639 data, "\r\n"); st.hasMoreTokens();)
3642 String s = st.nextToken();
3643 if (s.startsWith("#"))
3645 // the line is a comment (as per the RFC 2483)
3648 java.net.URI uri = new java.net.URI(s);
3649 if (uri.getScheme().toLowerCase().startsWith("http"))
3651 protocols.add(DataSourceType.URL);
3652 files.add(uri.toString());
3656 // otherwise preserve old behaviour: catch all for file objects
3657 java.io.File file = new java.io.File(uri);
3658 protocols.add(DataSourceType.FILE);
3659 files.add(file.toString());
3664 if (Cache.log.isDebugEnabled())
3666 if (data == null || !added)
3669 if (t.getTransferDataFlavors() != null
3670 && t.getTransferDataFlavors().length > 0)
3673 "Couldn't resolve drop data. Here are the supported flavors:");
3674 for (DataFlavor fl : t.getTransferDataFlavors())
3677 "Supported transfer dataflavor: " + fl.toString());
3678 Object df = t.getTransferData(fl);
3681 Cache.log.debug("Retrieves: " + df);
3685 Cache.log.debug("Retrieved nothing");
3691 Cache.log.debug("Couldn't resolve dataflavor for drop: "
3697 if (Platform.isWindowsAndNotJS())
3699 Cache.log.debug("Scanning dropped content for Windows Link Files");
3701 // resolve any .lnk files in the file drop
3702 for (int f = 0; f < files.size(); f++)
3704 String source = files.get(f).toString().toLowerCase();
3705 if (protocols.get(f).equals(DataSourceType.FILE)
3706 && (source.endsWith(".lnk") || source.endsWith(".url")
3707 || source.endsWith(".site")))
3711 Object obj = files.get(f);
3712 File lf = (obj instanceof File ? (File) obj
3713 : new File((String) obj));
3714 // process link file to get a URL
3715 Cache.log.debug("Found potential link file: " + lf);
3716 WindowsShortcut wscfile = new WindowsShortcut(lf);
3717 String fullname = wscfile.getRealFilename();
3718 protocols.set(f, FormatAdapter.checkProtocol(fullname));
3719 files.set(f, fullname);
3720 Cache.log.debug("Parsed real filename " + fullname
3721 + " to extract protocol: " + protocols.get(f));
3722 } catch (Exception ex)
3725 "Couldn't parse " + files.get(f) + " as a link file.",
3734 * Sets the Preferences property for experimental features to True or False
3735 * depending on the state of the controlling menu item
3738 protected void showExperimental_actionPerformed(boolean selected)
3740 Cache.setProperty(EXPERIMENTAL_FEATURES, Boolean.toString(selected));
3744 * Answers a (possibly empty) list of any structure viewer frames (currently for
3745 * either Jmol or Chimera) which are currently open. This may optionally be
3746 * restricted to viewers of a specified class, or viewers linked to a specified
3750 * if not null, only return viewers linked to this panel
3751 * @param structureViewerClass
3752 * if not null, only return viewers of this class
3755 public List<StructureViewerBase> getStructureViewers(
3756 AlignmentPanel apanel,
3757 Class<? extends StructureViewerBase> structureViewerClass)
3759 List<StructureViewerBase> result = new ArrayList<>();
3760 JInternalFrame[] frames = getAllFrames();
3762 for (JInternalFrame frame : frames)
3764 if (frame instanceof StructureViewerBase)
3766 if (structureViewerClass == null
3767 || structureViewerClass.isInstance(frame))
3770 || ((StructureViewerBase) frame).isLinkedWith(apanel))
3772 result.add((StructureViewerBase) frame);
3780 public static void addInternalFrame(AlignFrame af, String title,
3781 int width, int height)
3783 addInternalFrame(af.getIFrame(), title, width, height);