2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
5 * This file is part of Jalview.
7 * Jalview is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation, either version 3
10 * of the License, or (at your option) any later version.
12 * Jalview is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19 * The Jalview Authors are detailed in the 'AUTHORS' file.
23 import jalview.api.AlignViewportI;
24 import jalview.api.AlignmentViewPanel;
25 import jalview.api.StructureSelectionManagerProvider;
26 import jalview.bin.ApplicationSingletonProvider;
27 import jalview.bin.ApplicationSingletonProvider.ApplicationSingletonI;
28 import jalview.bin.Cache;
29 import jalview.bin.Jalview;
30 import jalview.gui.ImageExporter.ImageWriterI;
31 import jalview.io.BackupFiles;
32 import jalview.io.DataSourceType;
33 import jalview.io.FileFormat;
34 import jalview.io.FileFormatException;
35 import jalview.io.FileFormatI;
36 import jalview.io.FileFormats;
37 import jalview.io.FileLoader;
38 import jalview.io.FormatAdapter;
39 import jalview.io.IdentifyFile;
40 import jalview.io.JalviewFileChooser;
41 import jalview.io.JalviewFileView;
42 import jalview.jbgui.GDesktop;
43 import jalview.jbgui.GSplitFrame;
44 import jalview.jbgui.GStructureViewer;
45 import jalview.project.Jalview2XML;
46 import jalview.structure.StructureSelectionManager;
47 import jalview.urls.IdOrgSettings;
48 import jalview.util.BrowserLauncher;
49 import jalview.util.ImageMaker.TYPE;
50 import jalview.util.MessageManager;
51 import jalview.util.Platform;
52 import jalview.util.UrlConstants;
53 import jalview.viewmodel.AlignmentViewport;
54 import jalview.ws.jws1.Discoverer;
55 import jalview.ws.params.ParamManager;
56 import jalview.ws.utils.UrlDownloadClient;
58 import java.awt.BorderLayout;
59 import java.awt.Color;
60 import java.awt.Dimension;
61 import java.awt.FontMetrics;
62 import java.awt.Graphics;
63 import java.awt.GridLayout;
64 import java.awt.Point;
65 import java.awt.Rectangle;
66 import java.awt.Toolkit;
67 import java.awt.Window;
68 import java.awt.datatransfer.Clipboard;
69 import java.awt.datatransfer.ClipboardOwner;
70 import java.awt.datatransfer.DataFlavor;
71 import java.awt.datatransfer.Transferable;
72 import java.awt.dnd.DnDConstants;
73 import java.awt.dnd.DropTargetDragEvent;
74 import java.awt.dnd.DropTargetDropEvent;
75 import java.awt.dnd.DropTargetEvent;
76 import java.awt.dnd.DropTargetListener;
77 import java.awt.event.ActionEvent;
78 import java.awt.event.ActionListener;
79 import java.awt.event.InputEvent;
80 import java.awt.event.KeyEvent;
81 import java.awt.event.MouseAdapter;
82 import java.awt.event.MouseEvent;
83 import java.awt.event.WindowAdapter;
84 import java.awt.event.WindowEvent;
85 import java.beans.PropertyChangeEvent;
86 import java.beans.PropertyChangeListener;
87 import java.io.BufferedInputStream;
89 import java.io.FileOutputStream;
90 import java.io.IOException;
92 import java.util.ArrayList;
93 import java.util.Hashtable;
94 import java.util.List;
95 import java.util.ListIterator;
96 import java.util.Vector;
97 import java.util.concurrent.ExecutorService;
98 import java.util.concurrent.Executors;
99 import java.util.concurrent.Semaphore;
101 import javax.swing.AbstractAction;
102 import javax.swing.Action;
103 import javax.swing.ActionMap;
104 import javax.swing.Box;
105 import javax.swing.BoxLayout;
106 import javax.swing.DefaultDesktopManager;
107 import javax.swing.DesktopManager;
108 import javax.swing.InputMap;
109 import javax.swing.JButton;
110 import javax.swing.JCheckBox;
111 import javax.swing.JComboBox;
112 import javax.swing.JComponent;
113 import javax.swing.JDesktopPane;
114 import javax.swing.JFrame;
115 import javax.swing.JInternalFrame;
116 import javax.swing.JLabel;
117 import javax.swing.JMenuItem;
118 import javax.swing.JPanel;
119 import javax.swing.JPopupMenu;
120 import javax.swing.JProgressBar;
121 import javax.swing.JTextField;
122 import javax.swing.KeyStroke;
123 import javax.swing.SwingUtilities;
124 import javax.swing.event.HyperlinkEvent;
125 import javax.swing.event.HyperlinkEvent.EventType;
126 import javax.swing.event.InternalFrameAdapter;
127 import javax.swing.event.InternalFrameEvent;
128 import javax.swing.event.MenuEvent;
129 import javax.swing.event.MenuListener;
131 import org.stackoverflowusers.file.WindowsShortcut;
138 * @version $Revision: 1.155 $
140 @SuppressWarnings("serial")
141 public class Desktop extends GDesktop
142 implements DropTargetListener, ClipboardOwner, IProgressIndicator,
143 StructureSelectionManagerProvider, ApplicationSingletonI
146 private final static int DEFAULT_MIN_WIDTH = 300;
148 private final static int DEFAULT_MIN_HEIGHT = 250;
150 private final static int ALIGN_FRAME_DEFAULT_MIN_WIDTH = 600;
152 private final static int ALIGN_FRAME_DEFAULT_MIN_HEIGHT = 70;
154 private final static String EXPERIMENTAL_FEATURES = "EXPERIMENTAL_FEATURES";
156 private JalviewChangeSupport changeSupport = new JalviewChangeSupport();
159 * news reader - null if it was never started.
161 BlogReader jvnews = null;
163 private File projectFile;
167 * @see jalview.gui.JalviewChangeSupport#addJalviewPropertyChangeListener(java.beans.PropertyChangeListener)
169 public void addJalviewPropertyChangeListener(
170 PropertyChangeListener listener)
172 changeSupport.addJalviewPropertyChangeListener(listener);
176 * @param propertyName
178 * @see jalview.gui.JalviewChangeSupport#addJalviewPropertyChangeListener(java.lang.String,
179 * java.beans.PropertyChangeListener)
181 public void addJalviewPropertyChangeListener(String propertyName,
182 PropertyChangeListener listener)
184 changeSupport.addJalviewPropertyChangeListener(propertyName, listener);
188 * @param propertyName
190 * @see jalview.gui.JalviewChangeSupport#removeJalviewPropertyChangeListener(java.lang.String,
191 * java.beans.PropertyChangeListener)
193 public void removeJalviewPropertyChangeListener(String propertyName,
194 PropertyChangeListener listener)
196 changeSupport.removeJalviewPropertyChangeListener(propertyName,
200 public static MyDesktopPane getDesktopPane()
202 Desktop desktop = Desktop.getInstance();
203 return desktop == null ? null : desktop.desktopPane;
206 public static StructureSelectionManager getStructureSelectionManager()
208 return StructureSelectionManager
209 .getStructureSelectionManager(getInstance());
212 static int openFrameCount = 0;
214 static final int xOffset = 30;
216 static final int yOffset = 30;
218 public Discoverer discoverer;
220 public Object[] jalviewClipboard;
222 public boolean internalCopy = false;
224 private static int fileLoadingCount = 0;
226 public JInternalFrame conservationSlider;
228 public JInternalFrame PIDSlider;
231 * just an instance (for testng, probably); no actual frames
233 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().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 (Desktop.getInstance().instanceOnly || Jalview.isHeadlessMode())
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)
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 instanceof AlignFrame)
963 Jalview.setCurrentAlignFrame((AlignFrame) itf);
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 Desktop.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 Desktop.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 Desktop.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)
1641 source = ((AlignFrame) frames[0]).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)
1667 target = ((AlignFrame) frames[i]).getCurrentView();
1669 else if (frames[i] instanceof TreePanel)
1671 target = ((TreePanel) frames[i]).getViewPort();
1673 else if (frames[i] instanceof PCAPanel)
1675 target = ((PCAPanel) frames[i]).av;
1677 else if (frames[i].getContentPane() instanceof PairwiseAlignPanel)
1679 target = ((PairwiseAlignPanel) frames[i].getContentPane()).av;
1682 if (source == target)
1688 frames[i].setClosed(true);
1692 frames[i].setIcon(minimize);
1695 frames[i].toFront();
1699 } catch (java.beans.PropertyVetoException ex)
1714 protected void preferences_actionPerformed(ActionEvent e)
1720 * Prompts the user to choose a file and then saves the Jalview state as a
1721 * Jalview project file
1724 public void saveState_actionPerformed()
1726 saveState_actionPerformed(false);
1729 public void saveState_actionPerformed(boolean saveAs)
1731 java.io.File projectFile = getProjectFile();
1732 // autoSave indicates we already have a file and don't need to ask
1733 boolean autoSave = projectFile != null && !saveAs
1734 && BackupFiles.getEnabled();
1736 // System.out.println("autoSave="+autoSave+", projectFile='"+projectFile+"',
1737 // saveAs="+saveAs+", Backups
1738 // "+(BackupFiles.getEnabled()?"enabled":"disabled"));
1740 boolean approveSave = false;
1743 JalviewFileChooser chooser = new JalviewFileChooser("jvp",
1746 chooser.setFileView(new JalviewFileView());
1747 chooser.setDialogTitle(MessageManager.getString("label.save_state"));
1749 int value = chooser.showSaveDialog(this);
1751 if (value == JalviewFileChooser.APPROVE_OPTION)
1753 projectFile = chooser.getSelectedFile();
1754 setProjectFile(projectFile);
1759 if (approveSave || autoSave)
1761 final Desktop me = this;
1762 final java.io.File chosenFile = projectFile;
1763 new Thread(new Runnable()
1768 // TODO: refactor to Jalview desktop session controller action.
1769 setProgressBar(MessageManager.formatMessage(
1770 "label.saving_jalview_project", new Object[]
1771 { chosenFile.getName() }), chosenFile.hashCode());
1772 jalview.bin.Cache.setProperty("LAST_DIRECTORY",
1773 chosenFile.getParent());
1774 // TODO catch and handle errors for savestate
1775 // TODO prevent user from messing with the Desktop whilst we're saving
1778 boolean doBackup = BackupFiles.getEnabled();
1779 BackupFiles backupfiles = doBackup ? new BackupFiles(chosenFile) : null;
1781 new Jalview2XML().saveState(doBackup ? backupfiles.getTempFile() : chosenFile);
1785 backupfiles.setWriteSuccess(true);
1786 backupfiles.rollBackupsAndRenameTempFile();
1788 } catch (OutOfMemoryError oom)
1790 new OOMWarning("Whilst saving current state to "
1791 + chosenFile.getName(), oom);
1792 } catch (Exception ex)
1794 Cache.log.error("Problems whilst trying to save to "
1795 + chosenFile.getName(), ex);
1796 JvOptionPane.showMessageDialog(me,
1797 MessageManager.formatMessage(
1798 "label.error_whilst_saving_current_state_to",
1800 { chosenFile.getName() }),
1801 MessageManager.getString("label.couldnt_save_project"),
1802 JvOptionPane.WARNING_MESSAGE);
1804 setProgressBar(null, chosenFile.hashCode());
1811 public void saveAsState_actionPerformed(ActionEvent e)
1813 saveState_actionPerformed(true);
1816 protected void setProjectFile(File choice)
1818 this.projectFile = choice;
1821 public File getProjectFile()
1823 return this.projectFile;
1827 * Shows a file chooser dialog and tries to read in the selected file as a
1831 public void loadState_actionPerformed()
1833 final String[] suffix = new String[] { "jvp", "jar" };
1834 final String[] desc = new String[] { "Jalview Project",
1835 "Jalview Project (old)" };
1836 JalviewFileChooser chooser = new JalviewFileChooser(
1837 Cache.getProperty("LAST_DIRECTORY"), suffix, desc,
1838 "Jalview Project", true, true); // last two booleans: allFiles,
1840 chooser.setFileView(new JalviewFileView());
1841 chooser.setDialogTitle(MessageManager.getString("label.restore_state"));
1842 chooser.setResponseHandler(0, new Runnable()
1847 File selectedFile = chooser.getSelectedFile();
1848 setProjectFile(selectedFile);
1849 String choice = selectedFile.getAbsolutePath();
1850 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1851 new Thread(new Runnable()
1858 new Jalview2XML().loadJalviewAlign(choice);
1859 } catch (OutOfMemoryError oom)
1861 new OOMWarning("Whilst loading project from " + choice, oom);
1862 } catch (Exception ex)
1865 "Problems whilst loading project from " + choice, ex);
1866 JvOptionPane.showMessageDialog(Desktop.getDesktopPane(),
1867 MessageManager.formatMessage(
1868 "label.error_whilst_loading_project_from",
1871 MessageManager.getString("label.couldnt_load_project"),
1872 JvOptionPane.WARNING_MESSAGE);
1879 chooser.showOpenDialog(this);
1883 public void inputSequence_actionPerformed(ActionEvent e)
1885 new SequenceFetcher(this);
1888 JPanel progressPanel;
1890 ArrayList<JPanel> fileLoadingPanels = new ArrayList<>();
1892 public void startLoading(final Object fileName)
1894 if (fileLoadingCount == 0)
1896 fileLoadingPanels.add(addProgressPanel(MessageManager
1897 .formatMessage("label.loading_file", new Object[]
1903 private JPanel addProgressPanel(String string)
1905 if (progressPanel == null)
1907 progressPanel = new JPanel(new GridLayout(1, 1));
1908 totalProgressCount = 0;
1909 getContentPane().add(progressPanel, BorderLayout.SOUTH);
1911 JPanel thisprogress = new JPanel(new BorderLayout(10, 5));
1912 JProgressBar progressBar = new JProgressBar();
1913 progressBar.setIndeterminate(true);
1915 thisprogress.add(new JLabel(string), BorderLayout.WEST);
1917 thisprogress.add(progressBar, BorderLayout.CENTER);
1918 progressPanel.add(thisprogress);
1919 ((GridLayout) progressPanel.getLayout()).setRows(
1920 ((GridLayout) progressPanel.getLayout()).getRows() + 1);
1921 ++totalProgressCount;
1923 return thisprogress;
1926 int totalProgressCount = 0;
1928 private void removeProgressPanel(JPanel progbar)
1930 if (progressPanel != null)
1932 synchronized (progressPanel)
1934 progressPanel.remove(progbar);
1935 GridLayout gl = (GridLayout) progressPanel.getLayout();
1936 gl.setRows(gl.getRows() - 1);
1937 if (--totalProgressCount < 1)
1939 this.getContentPane().remove(progressPanel);
1940 progressPanel = null;
1947 public void stopLoading()
1950 if (fileLoadingCount < 1)
1952 while (fileLoadingPanels.size() > 0)
1954 removeProgressPanel(fileLoadingPanels.remove(0));
1956 fileLoadingPanels.clear();
1957 fileLoadingCount = 0;
1962 public static int getViewCount(String alignmentId)
1964 AlignmentViewport[] aps = getViewports(alignmentId);
1965 return (aps == null) ? 0 : aps.length;
1970 * @param alignmentId
1971 * - if null, all sets are returned
1972 * @return all AlignmentPanels concerning the alignmentId sequence set
1974 public static AlignmentPanel[] getAlignmentPanels(String alignmentId)
1976 if (Desktop.getDesktopPane() == null)
1978 // no frames created and in headless mode
1979 // TODO: verify that frames are recoverable when in headless mode
1982 List<AlignmentPanel> aps = new ArrayList<>();
1983 AlignFrame[] frames = getAlignFrames();
1988 for (AlignFrame af : frames)
1990 for (AlignmentPanel ap : af.alignPanels)
1992 if (alignmentId == null
1993 || alignmentId.equals(ap.av.getSequenceSetId()))
1999 if (aps.size() == 0)
2003 AlignmentPanel[] vap = aps.toArray(new AlignmentPanel[aps.size()]);
2008 * get all the viewports on an alignment.
2010 * @param sequenceSetId
2011 * unique alignment id (may be null - all viewports returned in that
2013 * @return all viewports on the alignment bound to sequenceSetId
2015 public static AlignmentViewport[] getViewports(String sequenceSetId)
2017 List<AlignmentViewport> viewp = new ArrayList<>();
2018 if (getDesktopPane() != null)
2020 AlignFrame[] frames = Desktop.getAlignFrames();
2022 for (AlignFrame afr : frames)
2024 if (sequenceSetId == null || afr.getViewport().getSequenceSetId()
2025 .equals(sequenceSetId))
2027 if (afr.alignPanels != null)
2029 for (AlignmentPanel ap : afr.alignPanels)
2031 if (sequenceSetId == null
2032 || sequenceSetId.equals(ap.av.getSequenceSetId()))
2040 viewp.add(afr.getViewport());
2044 if (viewp.size() > 0)
2046 return viewp.toArray(new AlignmentViewport[viewp.size()]);
2053 * Explode the views in the given frame into separate AlignFrame
2057 public static void explodeViews(AlignFrame af)
2059 int size = af.alignPanels.size();
2065 for (int i = 0; i < size; i++)
2067 AlignmentPanel ap = af.alignPanels.get(i);
2068 AlignFrame newaf = new AlignFrame(ap);
2071 * Restore the view's last exploded frame geometry if known. Multiple
2072 * views from one exploded frame share and restore the same (frame)
2073 * position and size.
2075 Rectangle geometry = ap.av.getExplodedGeometry();
2076 if (geometry != null)
2078 newaf.setBounds(geometry);
2081 ap.av.setGatherViewsHere(false);
2083 addInternalFrame(newaf, af.getTitle(), AlignFrame.DEFAULT_WIDTH,
2084 AlignFrame.DEFAULT_HEIGHT);
2087 af.alignPanels.clear();
2088 af.closeMenuItem_actionPerformed(true);
2093 * Gather expanded views (separate AlignFrame's) with the same sequence set
2094 * identifier back in to this frame as additional views, and close the expanded
2095 * views. Note the expanded frames may themselves have multiple views. We take
2100 public void gatherViews(AlignFrame source)
2102 source.viewport.setGatherViewsHere(true);
2103 source.viewport.setExplodedGeometry(source.getBounds());
2104 JInternalFrame[] frames = getAllFrames();
2105 String viewId = source.viewport.getSequenceSetId();
2107 for (int t = 0; t < frames.length; t++)
2109 if (frames[t] instanceof AlignFrame && frames[t] != source)
2111 AlignFrame af = (AlignFrame) frames[t];
2112 boolean gatherThis = false;
2113 for (int a = 0; a < af.alignPanels.size(); a++)
2115 AlignmentPanel ap = af.alignPanels.get(a);
2116 if (viewId.equals(ap.av.getSequenceSetId()))
2119 ap.av.setGatherViewsHere(false);
2120 ap.av.setExplodedGeometry(af.getBounds());
2121 source.addAlignmentPanel(ap, false);
2127 af.alignPanels.clear();
2128 af.closeMenuItem_actionPerformed(true);
2135 jalview.gui.VamsasApplication v_client = null;
2138 public void vamsasImport_actionPerformed(ActionEvent e)
2140 // TODO: JAL-3048 not needed for Jalview-JS
2142 if (v_client == null)
2144 // Load and try to start a session.
2145 JalviewFileChooser chooser = new JalviewFileChooser(
2146 jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
2148 chooser.setFileView(new JalviewFileView());
2149 chooser.setDialogTitle(
2150 MessageManager.getString("label.open_saved_vamsas_session"));
2151 chooser.setToolTipText(MessageManager.getString(
2152 "label.select_vamsas_session_opened_as_new_vamsas_session"));
2154 int value = chooser.showOpenDialog(this);
2156 if (value == JalviewFileChooser.APPROVE_OPTION)
2158 String fle = chooser.getSelectedFile().toString();
2159 if (!vamsasImport(chooser.getSelectedFile()))
2161 JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
2162 MessageManager.formatMessage(
2163 "label.couldnt_import_as_vamsas_session",
2167 .getString("label.vamsas_document_import_failed"),
2168 JvOptionPane.ERROR_MESSAGE);
2174 jalview.bin.Cache.log.error(
2175 "Implementation error - load session from a running session is not supported.");
2180 * import file into a new vamsas session (uses jalview.gui.VamsasApplication)
2183 * @return true if import was a success and a session was started.
2185 public boolean vamsasImport(URL url)
2187 // TODO: create progress bar
2188 if (v_client != null)
2191 jalview.bin.Cache.log.error(
2192 "Implementation error - load session from a running session is not supported.");
2198 // copy the URL content to a temporary local file
2199 // TODO: be a bit cleverer here with nio (?!)
2200 File file = File.createTempFile("vdocfromurl", ".vdj");
2201 FileOutputStream fos = new FileOutputStream(file);
2202 BufferedInputStream bis = new BufferedInputStream(url.openStream());
2203 byte[] buffer = new byte[2048];
2205 while ((ln = bis.read(buffer)) > -1)
2207 fos.write(buffer, 0, ln);
2211 v_client = new jalview.gui.VamsasApplication(this, file,
2212 url.toExternalForm());
2213 } catch (Exception ex)
2215 jalview.bin.Cache.log.error(
2216 "Failed to create new vamsas session from contents of URL "
2221 setupVamsasConnectedGui();
2222 v_client.initial_update(); // TODO: thread ?
2223 return v_client.inSession();
2227 * import file into a new vamsas session (uses jalview.gui.VamsasApplication)
2230 * @return true if import was a success and a session was started.
2232 public boolean vamsasImport(File file)
2234 if (v_client != null)
2237 jalview.bin.Cache.log.error(
2238 "Implementation error - load session from a running session is not supported.");
2242 setProgressBar(MessageManager.formatMessage(
2243 "status.importing_vamsas_session_from", new Object[]
2244 { file.getName() }), file.hashCode());
2247 v_client = new jalview.gui.VamsasApplication(this, file, null);
2248 } catch (Exception ex)
2250 setProgressBar(MessageManager.formatMessage(
2251 "status.importing_vamsas_session_from", new Object[]
2252 { file.getName() }), file.hashCode());
2253 jalview.bin.Cache.log.error(
2254 "New vamsas session from existing session file failed:", ex);
2257 setupVamsasConnectedGui();
2258 v_client.initial_update(); // TODO: thread ?
2259 setProgressBar(MessageManager.formatMessage(
2260 "status.importing_vamsas_session_from", new Object[]
2261 { file.getName() }), file.hashCode());
2262 return v_client.inSession();
2265 public boolean joinVamsasSession(String mysesid)
2267 if (v_client != null)
2269 throw new Error(MessageManager
2270 .getString("error.try_join_vamsas_session_another"));
2272 if (mysesid == null)
2275 MessageManager.getString("error.invalid_vamsas_session_id"));
2277 v_client = new VamsasApplication(this, mysesid);
2278 setupVamsasConnectedGui();
2279 v_client.initial_update();
2280 return (v_client.inSession());
2284 public void vamsasStart_actionPerformed(ActionEvent e)
2286 if (v_client == null)
2289 // we just start a default session for moment.
2291 * JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache.
2292 * getProperty("LAST_DIRECTORY"));
2294 * chooser.setFileView(new JalviewFileView());
2295 * chooser.setDialogTitle("Load Vamsas file");
2296 * chooser.setToolTipText("Import");
2298 * int value = chooser.showOpenDialog(this);
2300 * if (value == JalviewFileChooser.APPROVE_OPTION) { v_client = new
2301 * jalview.gui.VamsasApplication(this, chooser.getSelectedFile());
2303 v_client = new VamsasApplication(this);
2304 setupVamsasConnectedGui();
2305 v_client.initial_update(); // TODO: thread ?
2309 // store current data in session.
2310 v_client.push_update(); // TODO: thread
2314 protected void setupVamsasConnectedGui()
2316 vamsasStart.setText(MessageManager.getString("label.session_update"));
2317 vamsasSave.setVisible(true);
2318 vamsasStop.setVisible(true);
2319 vamsasImport.setVisible(false); // Document import to existing session is
2320 // not possible for vamsas-client-1.0.
2323 protected void setupVamsasDisconnectedGui()
2325 vamsasSave.setVisible(false);
2326 vamsasStop.setVisible(false);
2327 vamsasImport.setVisible(true);
2329 .setText(MessageManager.getString("label.new_vamsas_session"));
2333 public void vamsasStop_actionPerformed(ActionEvent e)
2335 if (v_client != null)
2337 v_client.end_session();
2339 setupVamsasDisconnectedGui();
2343 protected void buildVamsasStMenu()
2345 if (v_client == null)
2347 String[] sess = null;
2350 sess = VamsasApplication.getSessionList();
2351 } catch (Exception e)
2353 jalview.bin.Cache.log.warn("Problem getting current sessions list.",
2359 jalview.bin.Cache.log.debug(
2360 "Got current sessions list: " + sess.length + " entries.");
2361 VamsasStMenu.removeAll();
2362 for (int i = 0; i < sess.length; i++)
2364 JMenuItem sessit = new JMenuItem();
2365 sessit.setText(sess[i]);
2366 sessit.setToolTipText(MessageManager
2367 .formatMessage("label.connect_to_session", new Object[]
2369 final Desktop dsktp = this;
2370 final String mysesid = sess[i];
2371 sessit.addActionListener(new ActionListener()
2375 public void actionPerformed(ActionEvent e)
2377 if (dsktp.v_client == null)
2379 Thread rthr = new Thread(new Runnable()
2385 dsktp.v_client = new VamsasApplication(dsktp, mysesid);
2386 dsktp.setupVamsasConnectedGui();
2387 dsktp.v_client.initial_update();
2395 VamsasStMenu.add(sessit);
2397 // don't show an empty menu.
2398 VamsasStMenu.setVisible(sess.length > 0);
2403 jalview.bin.Cache.log.debug("No current vamsas sessions.");
2404 VamsasStMenu.removeAll();
2405 VamsasStMenu.setVisible(false);
2410 // Not interested in the content. Just hide ourselves.
2411 VamsasStMenu.setVisible(false);
2416 public void vamsasSave_actionPerformed(ActionEvent e)
2418 // TODO: JAL-3048 not needed for Jalview-JS
2420 if (v_client != null)
2422 // TODO: VAMSAS DOCUMENT EXTENSION is VDJ
2423 JalviewFileChooser chooser = new JalviewFileChooser("vdj",
2426 chooser.setFileView(new JalviewFileView());
2427 chooser.setDialogTitle(MessageManager
2428 .getString("label.save_vamsas_document_archive"));
2430 int value = chooser.showSaveDialog(this);
2432 if (value == JalviewFileChooser.APPROVE_OPTION)
2434 java.io.File choice = chooser.getSelectedFile();
2435 JPanel progpanel = addProgressPanel(MessageManager
2436 .formatMessage("label.saving_vamsas_doc", new Object[]
2437 { choice.getName() }));
2438 Cache.setProperty("LAST_DIRECTORY", choice.getParent());
2439 String warnmsg = null;
2440 String warnttl = null;
2443 v_client.vclient.storeDocument(choice);
2446 warnttl = "Serious Problem saving Vamsas Document";
2447 warnmsg = ex.toString();
2448 jalview.bin.Cache.log
2449 .error("Error Whilst saving document to " + choice, ex);
2451 } catch (Exception ex)
2453 warnttl = "Problem saving Vamsas Document.";
2454 warnmsg = ex.toString();
2455 jalview.bin.Cache.log.warn(
2456 "Exception Whilst saving document to " + choice, ex);
2459 removeProgressPanel(progpanel);
2460 if (warnmsg != null)
2462 JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
2464 warnmsg, warnttl, JvOptionPane.ERROR_MESSAGE);
2470 JPanel vamUpdate = null;
2473 * hide vamsas user gui bits when a vamsas document event is being handled.
2476 * true to hide gui, false to reveal gui
2478 public void setVamsasUpdate(boolean b)
2480 Cache.log.debug("Setting gui for Vamsas update "
2481 + (b ? "in progress" : "finished"));
2483 if (vamUpdate != null)
2485 this.removeProgressPanel(vamUpdate);
2489 vamUpdate = this.addProgressPanel(
2490 MessageManager.getString("label.updating_vamsas_session"));
2492 vamsasStart.setVisible(!b);
2493 vamsasStop.setVisible(!b);
2494 vamsasSave.setVisible(!b);
2497 public JInternalFrame[] getAllFrames()
2499 return desktopPane.getAllFrames();
2503 * Checks the given url to see if it gives a response indicating that the user
2504 * should be informed of a new questionnaire.
2508 public void checkForQuestionnaire(String url)
2510 UserQuestionnaireCheck jvq = new UserQuestionnaireCheck(url);
2511 // javax.swing.SwingUtilities.invokeLater(jvq);
2512 new Thread(jvq).start();
2515 public void checkURLLinks()
2517 // Thread off the URL link checker
2518 addDialogThread(new Runnable()
2523 if (Cache.getDefault("CHECKURLLINKS", true))
2525 // check what the actual links are - if it's just the default don't
2526 // bother with the warning
2527 List<String> links = Preferences.sequenceUrlLinks
2530 // only need to check links if there is one with a
2531 // SEQUENCE_ID which is not the default EMBL_EBI link
2532 ListIterator<String> li = links.listIterator();
2533 boolean check = false;
2534 List<JLabel> urls = new ArrayList<>();
2535 while (li.hasNext())
2537 String link = li.next();
2538 if (link.contains(UrlConstants.SEQUENCE_ID)
2539 && !UrlConstants.isDefaultString(link))
2542 int barPos = link.indexOf("|");
2543 String urlMsg = barPos == -1 ? link
2544 : link.substring(0, barPos) + ": "
2545 + link.substring(barPos + 1);
2546 urls.add(new JLabel(urlMsg));
2554 // ask user to check in case URL links use old style tokens
2555 // ($SEQUENCE_ID$ for sequence id _or_ accession id)
2556 JPanel msgPanel = new JPanel();
2557 msgPanel.setLayout(new BoxLayout(msgPanel, BoxLayout.PAGE_AXIS));
2558 msgPanel.add(Box.createVerticalGlue());
2559 JLabel msg = new JLabel(MessageManager
2560 .getString("label.SEQUENCE_ID_for_DB_ACCESSION1"));
2561 JLabel msg2 = new JLabel(MessageManager
2562 .getString("label.SEQUENCE_ID_for_DB_ACCESSION2"));
2564 for (JLabel url : urls)
2570 final JCheckBox jcb = new JCheckBox(
2571 MessageManager.getString("label.do_not_display_again"));
2572 jcb.addActionListener(new ActionListener()
2575 public void actionPerformed(ActionEvent e)
2577 // update Cache settings for "don't show this again"
2578 boolean showWarningAgain = !jcb.isSelected();
2579 Cache.setProperty("CHECKURLLINKS",
2580 Boolean.valueOf(showWarningAgain).toString());
2585 JvOptionPane.showMessageDialog(desktopPane, msgPanel,
2587 .getString("label.SEQUENCE_ID_no_longer_used"),
2588 JvOptionPane.WARNING_MESSAGE);
2595 * Proxy class for JDesktopPane which optionally displays the current memory
2596 * usage and highlights the desktop area with a red bar if free memory runs low.
2600 public class MyDesktopPane extends JDesktopPane
2603 private static final float ONE_MB = 1048576f;
2605 boolean showMemoryUsage = false;
2609 java.text.NumberFormat df;
2611 float maxMemory, allocatedMemory, freeMemory, totalFreeMemory,
2614 public MyDesktopPane(boolean showMemoryUsage)
2616 showMemoryUsage(showMemoryUsage);
2619 public void showMemoryUsage(boolean showMemory)
2621 this.showMemoryUsage = showMemory;
2624 Thread worker = new Thread(this);
2630 public boolean isShowMemoryUsage()
2632 return showMemoryUsage;
2638 df = java.text.NumberFormat.getNumberInstance();
2639 df.setMaximumFractionDigits(2);
2640 runtime = Runtime.getRuntime();
2642 while (showMemoryUsage)
2646 maxMemory = runtime.maxMemory() / ONE_MB;
2647 allocatedMemory = runtime.totalMemory() / ONE_MB;
2648 freeMemory = runtime.freeMemory() / ONE_MB;
2649 totalFreeMemory = freeMemory + (maxMemory - allocatedMemory);
2651 percentUsage = (totalFreeMemory / maxMemory) * 100;
2653 // if (percentUsage < 20)
2655 // border1 = BorderFactory.createMatteBorder(12, 12, 12, 12,
2657 // instance.set.setBorder(border1);
2660 // sleep after showing usage
2662 } catch (Exception ex)
2664 ex.printStackTrace();
2670 public void paintComponent(Graphics g)
2672 if (showMemoryUsage && g != null && df != null)
2674 if (percentUsage < 20)
2676 g.setColor(Color.red);
2678 FontMetrics fm = g.getFontMetrics();
2681 g.drawString(MessageManager.formatMessage("label.memory_stats",
2683 { df.format(totalFreeMemory), df.format(maxMemory),
2684 df.format(percentUsage) }),
2685 10, getHeight() - fm.getHeight());
2692 * Accessor method to quickly get all the AlignmentFrames loaded.
2694 * @return an array of AlignFrame, or null if none found
2696 public static AlignFrame[] getAlignFrames()
2698 if (Jalview.isHeadlessMode())
2700 // Desktop.getDesktop() is null in headless mode
2701 return new AlignFrame[] { Jalview.getCurrentAlignFrame() };
2704 JInternalFrame[] frames = Desktop.getDesktopPane().getAllFrames();
2710 List<AlignFrame> avp = new ArrayList<>();
2712 for (int i = frames.length - 1; i > -1; i--)
2714 if (frames[i] instanceof AlignFrame)
2716 avp.add((AlignFrame) frames[i]);
2718 else if (frames[i] instanceof SplitFrame)
2721 * Also check for a split frame containing an AlignFrame
2723 GSplitFrame sf = (GSplitFrame) frames[i];
2724 if (sf.getTopFrame() instanceof AlignFrame)
2726 avp.add((AlignFrame) sf.getTopFrame());
2728 if (sf.getBottomFrame() instanceof AlignFrame)
2730 avp.add((AlignFrame) sf.getBottomFrame());
2734 if (avp.size() == 0)
2738 AlignFrame afs[] = avp.toArray(new AlignFrame[avp.size()]);
2743 * Returns an array of any AppJmol frames in the Desktop (or null if none).
2747 public GStructureViewer[] getJmols()
2749 JInternalFrame[] frames = Desktop.getDesktopPane().getAllFrames();
2755 List<GStructureViewer> avp = new ArrayList<>();
2757 for (int i = frames.length - 1; i > -1; i--)
2759 if (frames[i] instanceof AppJmol)
2761 GStructureViewer af = (GStructureViewer) frames[i];
2765 if (avp.size() == 0)
2769 GStructureViewer afs[] = avp.toArray(new GStructureViewer[avp.size()]);
2774 * Add Groovy Support to Jalview
2777 public void groovyShell_actionPerformed()
2781 openGroovyConsole();
2782 } catch (Exception ex)
2784 jalview.bin.Cache.log.error("Groovy Shell Creation failed.", ex);
2785 JvOptionPane.showInternalMessageDialog(desktopPane,
2787 MessageManager.getString("label.couldnt_create_groovy_shell"),
2788 MessageManager.getString("label.groovy_support_failed"),
2789 JvOptionPane.ERROR_MESSAGE);
2794 * Open the Groovy console
2796 private void openGroovyConsole()
2798 if (groovyConsole == null)
2800 groovyConsole = new groovy.ui.Console();
2801 groovyConsole.setVariable("Jalview", this);
2802 groovyConsole.run();
2805 * We allow only one console at a time, so that AlignFrame menu option
2806 * 'Calculate | Run Groovy script' is unambiguous.
2807 * Disable 'Groovy Console', and enable 'Run script', when the console is
2808 * opened, and the reverse when it is closed
2810 Window window = (Window) groovyConsole.getFrame();
2811 window.addWindowListener(new WindowAdapter()
2814 public void windowClosed(WindowEvent e)
2817 * rebind CMD-Q from Groovy Console to Jalview Quit
2820 enableExecuteGroovy(false);
2826 * show Groovy console window (after close and reopen)
2828 ((Window) groovyConsole.getFrame()).setVisible(true);
2831 * if we got this far, enable 'Run Groovy' in AlignFrame menus
2832 * and disable opening a second console
2834 enableExecuteGroovy(true);
2838 * Bind Ctrl/Cmd-Q to Quit - for reset as Groovy Console takes over this binding
2841 protected void addQuitHandler()
2843 getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
2844 .put(KeyStroke.getKeyStroke(KeyEvent.VK_Q,
2845 Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()),
2847 getRootPane().getActionMap().put("Quit", new AbstractAction()
2850 public void actionPerformed(ActionEvent e)
2858 * Enable or disable 'Run Groovy script' in AlignFrame calculate menus
2861 * true if Groovy console is open
2863 public void enableExecuteGroovy(boolean enabled)
2866 * disable opening a second Groovy console
2867 * (or re-enable when the console is closed)
2869 groovyShell.setEnabled(!enabled);
2871 AlignFrame[] alignFrames = getAlignFrames();
2872 if (alignFrames != null)
2874 for (AlignFrame af : alignFrames)
2876 af.setGroovyEnabled(enabled);
2882 * Progress bars managed by the IProgressIndicator method.
2884 private Hashtable<Long, JPanel> progressBars;
2886 private Hashtable<Long, IProgressIndicatorHandler> progressBarHandlers;
2891 * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
2894 public void setProgressBar(String message, long id)
2896 Platform.timeCheck("Desktop " + message, Platform.TIME_MARK);
2898 if (progressBars == null)
2900 progressBars = new Hashtable<>();
2901 progressBarHandlers = new Hashtable<>();
2904 if (progressBars.get(new Long(id)) != null)
2906 JPanel panel = progressBars.remove(new Long(id));
2907 if (progressBarHandlers.contains(new Long(id)))
2909 progressBarHandlers.remove(new Long(id));
2911 removeProgressPanel(panel);
2915 progressBars.put(new Long(id), addProgressPanel(message));
2922 * @see jalview.gui.IProgressIndicator#registerHandler(long,
2923 * jalview.gui.IProgressIndicatorHandler)
2926 public void registerHandler(final long id,
2927 final IProgressIndicatorHandler handler)
2929 if (progressBarHandlers == null
2930 || !progressBars.containsKey(new Long(id)))
2932 throw new Error(MessageManager.getString(
2933 "error.call_setprogressbar_before_registering_handler"));
2935 progressBarHandlers.put(new Long(id), handler);
2936 final JPanel progressPanel = progressBars.get(new Long(id));
2937 if (handler.canCancel())
2939 JButton cancel = new JButton(
2940 MessageManager.getString("action.cancel"));
2941 final IProgressIndicator us = this;
2942 cancel.addActionListener(new ActionListener()
2946 public void actionPerformed(ActionEvent e)
2948 handler.cancelActivity(id);
2949 us.setProgressBar(MessageManager
2950 .formatMessage("label.cancelled_params", new Object[]
2951 { ((JLabel) progressPanel.getComponent(0)).getText() }),
2955 progressPanel.add(cancel, BorderLayout.EAST);
2961 * @return true if any progress bars are still active
2964 public boolean operationInProgress()
2966 if (progressBars != null && progressBars.size() > 0)
2974 * This will return the first AlignFrame holding the given viewport instance. It
2975 * will break if there are more than one AlignFrames viewing a particular av.
2978 * @return alignFrame for viewport
2980 public static AlignFrame getAlignFrameFor(AlignViewportI viewport)
2982 if (getDesktopPane() != null)
2984 AlignmentPanel[] aps = getAlignmentPanels(
2985 viewport.getSequenceSetId());
2986 for (int panel = 0; aps != null && panel < aps.length; panel++)
2988 if (aps[panel] != null && aps[panel].av == viewport)
2990 return aps[panel].alignFrame;
2997 public VamsasApplication getVamsasApplication()
3004 * flag set if jalview GUI is being operated programmatically
3006 private boolean inBatchMode = false;
3009 * check if jalview GUI is being operated programmatically
3011 * @return inBatchMode
3013 public boolean isInBatchMode()
3019 * set flag if jalview GUI is being operated programmatically
3021 * @param inBatchMode
3023 public void setInBatchMode(boolean inBatchMode)
3025 this.inBatchMode = inBatchMode;
3028 public void startServiceDiscovery()
3030 startServiceDiscovery(false);
3033 public void startServiceDiscovery(boolean blocking)
3035 boolean alive = true;
3036 Thread t0 = null, t1 = null, t2 = null;
3037 // JAL-940 - JALVIEW 1 services are now being EOLed as of JABA 2.1 release
3040 // todo: changesupport handlers need to be transferred
3041 if (discoverer == null)
3043 discoverer = Discoverer.getInstance();
3044 // register PCS handler for getDesktop().
3045 discoverer.addPropertyChangeListener(changeSupport);
3047 // JAL-940 - disabled JWS1 service configuration - always start discoverer
3048 // until we phase out completely
3049 (t0 = new Thread(discoverer)).start();
3052 if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
3054 t2 = jalview.ws.jws2.Jws2Discoverer.getInstance()
3055 .startDiscoverer(changeSupport);
3059 // TODO: do rest service discovery
3068 } catch (Exception e)
3071 alive = (t1 != null && t1.isAlive()) || (t2 != null && t2.isAlive())
3072 || (t3 != null && t3.isAlive())
3073 || (t0 != null && t0.isAlive());
3079 * called to check if the service discovery process completed successfully.
3083 protected void JalviewServicesChanged(PropertyChangeEvent evt)
3085 if (evt.getNewValue() == null || evt.getNewValue() instanceof Vector)
3087 final String ermsg = jalview.ws.jws2.Jws2Discoverer.getInstance()
3088 .getErrorMessages();
3091 if (Cache.getDefault("SHOW_WSDISCOVERY_ERRORS", true))
3093 if (serviceChangedDialog == null)
3095 // only run if we aren't already displaying one of these.
3096 addDialogThread(serviceChangedDialog = new Runnable()
3103 * JalviewDialog jd =new JalviewDialog() {
3105 * @Override protected void cancelPressed() { // TODO
3106 * Auto-generated method stub
3108 * }@Override protected void okPressed() { // TODO
3109 * Auto-generated method stub
3111 * }@Override protected void raiseClosed() { // TODO
3112 * Auto-generated method stub
3114 * } }; jd.initDialogFrame(new
3115 * JLabel("<html><table width=\"450\"><tr><td>" + ermsg +
3116 * "<br/>It may be that you have invalid JABA URLs in your web service preferences,"
3117 * + " or mis-configured HTTP proxy settings.<br/>" +
3118 * "Check the <em>Connections</em> and <em>Web services</em> tab of the"
3120 * " Tools->Preferences dialog box to change them.</td></tr></table></html>"
3121 * ), true, true, "Web Service Configuration Problem", 450,
3124 * jd.waitForInput();
3126 JvOptionPane.showConfirmDialog(Desktop.getDesktopPane(),
3127 new JLabel("<html><table width=\"450\"><tr><td>"
3128 + ermsg + "</td></tr></table>"
3129 + "<p>It may be that you have invalid JABA URLs<br/>in your web service preferences,"
3130 + "<br>or as a command-line argument, or mis-configured HTTP proxy settings.</p>"
3131 + "<p>Check the <em>Connections</em> and <em>Web services</em> tab<br/>of the"
3132 + " Tools->Preferences dialog box to change them.</p></html>"),
3133 "Web Service Configuration Problem",
3134 JvOptionPane.DEFAULT_OPTION,
3135 JvOptionPane.ERROR_MESSAGE);
3136 serviceChangedDialog = null;
3145 "Errors reported by JABA discovery service. Check web services preferences.\n"
3152 Runnable serviceChangedDialog = null;
3155 * start a thread to open a URL in the configured browser. Pops up a warning
3156 * dialog to the user if there is an exception when calling out to the browser
3161 public static void showUrl(final String url)
3163 showUrl(url, Desktop.getInstance());
3167 * Like showUrl but allows progress handler to be specified
3171 * (null) or object implementing IProgressIndicator
3173 public static void showUrl(final String url,
3174 final IProgressIndicator progress)
3176 new Thread(new Runnable()
3183 if (progress != null)
3185 progress.setProgressBar(MessageManager
3186 .formatMessage("status.opening_params", new Object[]
3187 { url }), this.hashCode());
3189 BrowserLauncher.openURL(url);
3190 } catch (Exception ex)
3192 JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
3194 .getString("label.web_browser_not_found_unix"),
3195 MessageManager.getString("label.web_browser_not_found"),
3196 JvOptionPane.WARNING_MESSAGE);
3198 ex.printStackTrace();
3200 if (progress != null)
3202 progress.setProgressBar(null, this.hashCode());
3208 private WsParamSetManager wsparamManager = null;
3210 public static ParamManager getUserParameterStore()
3212 Desktop d = Desktop.getInstance();
3213 if (d.wsparamManager == null)
3215 d.wsparamManager = new WsParamSetManager();
3217 return d.wsparamManager;
3221 * static hyperlink handler proxy method for use by Jalview's internal windows
3225 public static void hyperlinkUpdate(HyperlinkEvent e)
3227 if (e.getEventType() == EventType.ACTIVATED)
3232 url = e.getURL().toString();
3233 Desktop.showUrl(url);
3234 } catch (Exception x)
3238 if (Cache.log != null)
3240 Cache.log.error("Couldn't handle string " + url + " as a URL.");
3245 "Couldn't handle string " + url + " as a URL.");
3248 // ignore any exceptions due to dud links.
3255 * single thread that handles display of dialogs to user.
3257 ExecutorService dialogExecutor = Executors.newSingleThreadExecutor();
3260 * flag indicating if dialogExecutor should try to acquire a permit
3262 volatile boolean dialogPause = true;
3267 java.util.concurrent.Semaphore block = new Semaphore(0);
3269 private groovy.ui.Console groovyConsole;
3271 public StructureViewer lastTargetedView;
3274 * add another dialog thread to the queue
3278 public void addDialogThread(final Runnable prompter)
3280 dialogExecutor.submit(new Runnable()
3290 } catch (InterruptedException x)
3300 SwingUtilities.invokeAndWait(prompter);
3301 } catch (Exception q)
3303 Cache.log.warn("Unexpected Exception in dialog thread.", q);
3309 public void startDialogQueue()
3311 // set the flag so we don't pause waiting for another permit and semaphore
3312 // the current task to begin
3313 dialogPause = false;
3318 * Outputs an image of the desktop to file in EPS format, after prompting the
3319 * user for choice of Text or Lineart character rendering (unless a preference
3320 * has been set). The file name is generated as
3323 * Jalview_snapshot_nnnnn.eps where nnnnn is the current timestamp in milliseconds
3327 protected void snapShotWindow_actionPerformed(ActionEvent e)
3329 // currently the menu option to do this is not shown
3332 int width = getWidth();
3333 int height = getHeight();
3335 "Jalview_snapshot_" + System.currentTimeMillis() + ".eps");
3336 ImageWriterI writer = new ImageWriterI()
3339 public void exportImage(Graphics g) throws Exception
3342 Cache.log.info("Successfully written snapshot to file "
3343 + of.getAbsolutePath());
3346 String title = "View of desktop";
3347 ImageExporter exporter = new ImageExporter(writer, null, TYPE.EPS,
3349 exporter.doExport(of, this, width, height, title);
3353 * Explode the views in the given SplitFrame into separate SplitFrame windows.
3354 * This respects (remembers) any previous 'exploded geometry' i.e. the size and
3355 * location last time the view was expanded (if any). However it does not
3356 * remember the split pane divider location - this is set to match the
3357 * 'exploding' frame.
3361 public void explodeViews(SplitFrame sf)
3363 AlignFrame oldTopFrame = (AlignFrame) sf.getTopFrame();
3364 AlignFrame oldBottomFrame = (AlignFrame) sf.getBottomFrame();
3365 List<? extends AlignmentViewPanel> topPanels = oldTopFrame
3367 List<? extends AlignmentViewPanel> bottomPanels = oldBottomFrame
3369 int viewCount = topPanels.size();
3376 * Processing in reverse order works, forwards order leaves the first panels
3377 * not visible. I don't know why!
3379 for (int i = viewCount - 1; i >= 0; i--)
3382 * Make new top and bottom frames. These take over the respective
3383 * AlignmentPanel objects, including their AlignmentViewports, so the
3384 * cdna/protein relationships between the viewports is carried over to the
3387 * explodedGeometry holds the (x, y) position of the previously exploded
3388 * SplitFrame, and the (width, height) of the AlignFrame component
3390 AlignmentPanel topPanel = (AlignmentPanel) topPanels.get(i);
3391 AlignFrame newTopFrame = new AlignFrame(topPanel);
3392 newTopFrame.setSize(oldTopFrame.getSize());
3393 newTopFrame.setVisible(true);
3394 Rectangle geometry = ((AlignViewport) topPanel.getAlignViewport())
3395 .getExplodedGeometry();
3396 if (geometry != null)
3398 newTopFrame.setSize(geometry.getSize());
3401 AlignmentPanel bottomPanel = (AlignmentPanel) bottomPanels.get(i);
3402 AlignFrame newBottomFrame = new AlignFrame(bottomPanel);
3403 newBottomFrame.setSize(oldBottomFrame.getSize());
3404 newBottomFrame.setVisible(true);
3405 geometry = ((AlignViewport) bottomPanel.getAlignViewport())
3406 .getExplodedGeometry();
3407 if (geometry != null)
3409 newBottomFrame.setSize(geometry.getSize());
3412 topPanel.av.setGatherViewsHere(false);
3413 bottomPanel.av.setGatherViewsHere(false);
3414 JInternalFrame splitFrame = new SplitFrame(newTopFrame,
3416 if (geometry != null)
3418 splitFrame.setLocation(geometry.getLocation());
3420 Desktop.addInternalFrame(splitFrame, sf.getTitle(), -1, -1);
3424 * Clear references to the panels (now relocated in the new SplitFrames)
3425 * before closing the old SplitFrame.
3428 bottomPanels.clear();
3433 * Gather expanded split frames, sharing the same pairs of sequence set ids,
3434 * back into the given SplitFrame as additional views. Note that the gathered
3435 * frames may themselves have multiple views.
3439 public void gatherViews(GSplitFrame source)
3442 * special handling of explodedGeometry for a view within a SplitFrame: - it
3443 * holds the (x, y) position of the enclosing SplitFrame, and the (width,
3444 * height) of the AlignFrame component
3446 AlignFrame myTopFrame = (AlignFrame) source.getTopFrame();
3447 AlignFrame myBottomFrame = (AlignFrame) source.getBottomFrame();
3448 myTopFrame.viewport.setExplodedGeometry(new Rectangle(source.getX(),
3449 source.getY(), myTopFrame.getWidth(), myTopFrame.getHeight()));
3450 myBottomFrame.viewport
3451 .setExplodedGeometry(new Rectangle(source.getX(), source.getY(),
3452 myBottomFrame.getWidth(), myBottomFrame.getHeight()));
3453 myTopFrame.viewport.setGatherViewsHere(true);
3454 myBottomFrame.viewport.setGatherViewsHere(true);
3455 String topViewId = myTopFrame.viewport.getSequenceSetId();
3456 String bottomViewId = myBottomFrame.viewport.getSequenceSetId();
3458 JInternalFrame[] frames = desktopPane.getAllFrames();
3459 for (JInternalFrame frame : frames)
3461 if (frame instanceof SplitFrame && frame != source)
3463 SplitFrame sf = (SplitFrame) frame;
3464 AlignFrame topFrame = (AlignFrame) sf.getTopFrame();
3465 AlignFrame bottomFrame = (AlignFrame) sf.getBottomFrame();
3466 boolean gatherThis = false;
3467 for (int a = 0; a < topFrame.alignPanels.size(); a++)
3469 AlignmentPanel topPanel = topFrame.alignPanels.get(a);
3470 AlignmentPanel bottomPanel = bottomFrame.alignPanels.get(a);
3471 if (topViewId.equals(topPanel.av.getSequenceSetId())
3472 && bottomViewId.equals(bottomPanel.av.getSequenceSetId()))
3475 topPanel.av.setGatherViewsHere(false);
3476 bottomPanel.av.setGatherViewsHere(false);
3477 topPanel.av.setExplodedGeometry(
3478 new Rectangle(sf.getLocation(), topFrame.getSize()));
3479 bottomPanel.av.setExplodedGeometry(
3480 new Rectangle(sf.getLocation(), bottomFrame.getSize()));
3481 myTopFrame.addAlignmentPanel(topPanel, false);
3482 myBottomFrame.addAlignmentPanel(bottomPanel, false);
3488 topFrame.getAlignPanels().clear();
3489 bottomFrame.getAlignPanels().clear();
3496 * The dust settles...give focus to the tab we did this from.
3498 myTopFrame.setDisplayedView(myTopFrame.alignPanel);
3501 public static groovy.ui.Console getGroovyConsole()
3503 Desktop desktop = Desktop.getInstance();
3504 return desktop == null ? null : desktop.groovyConsole;
3508 * handles the payload of a drag and drop event.
3510 * TODO refactor to desktop utilities class
3513 * - Data source strings extracted from the drop event
3515 * - protocol for each data source extracted from the drop event
3519 * - the payload from the drop event
3522 @SuppressWarnings("unchecked")
3523 public static void transferFromDropTarget(List<Object> files,
3524 List<DataSourceType> protocols, DropTargetDropEvent evt,
3525 Transferable t) throws Exception
3528 // BH 2018 changed List<String> to List<Object> to allow for File from SwingJS
3530 // DataFlavor[] flavors = t.getTransferDataFlavors();
3531 // for (int i = 0; i < flavors.length; i++) {
3532 // if (flavors[i].isFlavorJavaFileListType()) {
3533 // evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
3534 // List<File> list = (List<File>) t.getTransferData(flavors[i]);
3535 // for (int j = 0; j < list.size(); j++) {
3536 // File file = (File) list.get(j);
3537 // byte[] data = getDroppedFileBytes(file);
3538 // fileName.setText(file.getName() + " - " + data.length + " " +
3539 // evt.getLocation());
3540 // JTextArea target = (JTextArea) ((DropTarget) evt.getSource()).getComponent();
3541 // target.setText(new String(data));
3543 // dtde.dropComplete(true);
3548 DataFlavor uriListFlavor = new DataFlavor(
3549 "text/uri-list;class=java.lang.String"), urlFlavour = null;
3552 urlFlavour = new DataFlavor(
3553 "application/x-java-url; class=java.net.URL");
3554 } catch (ClassNotFoundException cfe)
3556 Cache.log.debug("Couldn't instantiate the URL dataflavor.", cfe);
3559 if (urlFlavour != null && t.isDataFlavorSupported(urlFlavour))
3564 java.net.URL url = (URL) t.getTransferData(urlFlavour);
3565 // nb: java 8 osx bug https://bugs.openjdk.java.net/browse/JDK-8156099
3566 // means url may be null.
3569 protocols.add(DataSourceType.URL);
3570 files.add(url.toString());
3571 Cache.log.debug("Drop handled as URL dataflavor "
3572 + files.get(files.size() - 1));
3577 if (Platform.isAMacAndNotJS())
3580 "Please ignore plist error - occurs due to problem with java 8 on OSX");
3583 } catch (Throwable ex)
3585 Cache.log.debug("URL drop handler failed.", ex);
3588 if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
3590 // Works on Windows and MacOSX
3591 Cache.log.debug("Drop handled as javaFileListFlavor");
3592 for (Object file : (List<Object>) t
3593 .getTransferData(DataFlavor.javaFileListFlavor))
3596 protocols.add(DataSourceType.FILE);
3601 // Unix like behaviour
3602 boolean added = false;
3604 if (t.isDataFlavorSupported(uriListFlavor))
3606 Cache.log.debug("Drop handled as uriListFlavor");
3607 // This is used by Unix drag system
3608 data = (String) t.getTransferData(uriListFlavor);
3612 // fallback to text: workaround - on OSX where there's a JVM bug
3613 Cache.log.debug("standard URIListFlavor failed. Trying text");
3614 // try text fallback
3615 DataFlavor textDf = new DataFlavor(
3616 "text/plain;class=java.lang.String");
3617 if (t.isDataFlavorSupported(textDf))
3619 data = (String) t.getTransferData(textDf);
3622 Cache.log.debug("Plain text drop content returned "
3623 + (data == null ? "Null - failed" : data));
3628 while (protocols.size() < files.size())
3630 Cache.log.debug("Adding missing FILE protocol for "
3631 + files.get(protocols.size()));
3632 protocols.add(DataSourceType.FILE);
3634 for (java.util.StringTokenizer st = new java.util.StringTokenizer(
3635 data, "\r\n"); st.hasMoreTokens();)
3638 String s = st.nextToken();
3639 if (s.startsWith("#"))
3641 // the line is a comment (as per the RFC 2483)
3644 java.net.URI uri = new java.net.URI(s);
3645 if (uri.getScheme().toLowerCase().startsWith("http"))
3647 protocols.add(DataSourceType.URL);
3648 files.add(uri.toString());
3652 // otherwise preserve old behaviour: catch all for file objects
3653 java.io.File file = new java.io.File(uri);
3654 protocols.add(DataSourceType.FILE);
3655 files.add(file.toString());
3660 if (Cache.log.isDebugEnabled())
3662 if (data == null || !added)
3665 if (t.getTransferDataFlavors() != null
3666 && t.getTransferDataFlavors().length > 0)
3669 "Couldn't resolve drop data. Here are the supported flavors:");
3670 for (DataFlavor fl : t.getTransferDataFlavors())
3673 "Supported transfer dataflavor: " + fl.toString());
3674 Object df = t.getTransferData(fl);
3677 Cache.log.debug("Retrieves: " + df);
3681 Cache.log.debug("Retrieved nothing");
3687 Cache.log.debug("Couldn't resolve dataflavor for drop: "
3693 if (Platform.isWindowsAndNotJS())
3695 Cache.log.debug("Scanning dropped content for Windows Link Files");
3697 // resolve any .lnk files in the file drop
3698 for (int f = 0; f < files.size(); f++)
3700 String source = files.get(f).toString().toLowerCase();
3701 if (protocols.get(f).equals(DataSourceType.FILE)
3702 && (source.endsWith(".lnk") || source.endsWith(".url")
3703 || source.endsWith(".site")))
3707 Object obj = files.get(f);
3708 File lf = (obj instanceof File ? (File) obj
3709 : new File((String) obj));
3710 // process link file to get a URL
3711 Cache.log.debug("Found potential link file: " + lf);
3712 WindowsShortcut wscfile = new WindowsShortcut(lf);
3713 String fullname = wscfile.getRealFilename();
3714 protocols.set(f, FormatAdapter.checkProtocol(fullname));
3715 files.set(f, fullname);
3716 Cache.log.debug("Parsed real filename " + fullname
3717 + " to extract protocol: " + protocols.get(f));
3718 } catch (Exception ex)
3721 "Couldn't parse " + files.get(f) + " as a link file.",
3730 * Sets the Preferences property for experimental features to True or False
3731 * depending on the state of the controlling menu item
3734 protected void showExperimental_actionPerformed(boolean selected)
3736 Cache.setProperty(EXPERIMENTAL_FEATURES, Boolean.toString(selected));
3740 * Answers a (possibly empty) list of any structure viewer frames (currently for
3741 * either Jmol or Chimera) which are currently open. This may optionally be
3742 * restricted to viewers of a specified class, or viewers linked to a specified
3746 * if not null, only return viewers linked to this panel
3747 * @param structureViewerClass
3748 * if not null, only return viewers of this class
3751 public List<StructureViewerBase> getStructureViewers(
3752 AlignmentPanel apanel,
3753 Class<? extends StructureViewerBase> structureViewerClass)
3755 List<StructureViewerBase> result = new ArrayList<>();
3756 JInternalFrame[] frames = getAllFrames();
3758 for (JInternalFrame frame : frames)
3760 if (frame instanceof StructureViewerBase)
3762 if (structureViewerClass == null
3763 || structureViewerClass.isInstance(frame))
3766 || ((StructureViewerBase) frame).isLinkedWith(apanel))
3768 result.add((StructureViewerBase) frame);