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 static jalview.util.UrlConstants.SEQUENCE_ID;
25 import jalview.api.AlignViewportI;
26 import jalview.api.AlignmentViewPanel;
27 import jalview.bin.Cache;
28 import jalview.bin.Jalview;
29 import jalview.gui.ImageExporter.ImageWriterI;
30 import jalview.io.DataSourceType;
31 import jalview.io.FileFormat;
32 import jalview.io.FileFormatException;
33 import jalview.io.FileFormatI;
34 import jalview.io.FileFormats;
35 import jalview.io.FileLoader;
36 import jalview.io.FormatAdapter;
37 import jalview.io.IdentifyFile;
38 import jalview.io.JalviewFileChooser;
39 import jalview.io.JalviewFileView;
40 import jalview.jbgui.GSplitFrame;
41 import jalview.jbgui.GStructureViewer;
42 import jalview.structure.StructureSelectionManager;
43 import jalview.urls.IdOrgSettings;
44 import jalview.util.BrowserLauncher;
45 import jalview.util.ImageMaker.TYPE;
46 import jalview.util.MessageManager;
47 import jalview.util.Platform;
48 import jalview.util.UrlConstants;
49 import jalview.viewmodel.AlignmentViewport;
50 import jalview.ws.params.ParamManager;
51 import jalview.ws.utils.UrlDownloadClient;
53 import java.awt.BorderLayout;
54 import java.awt.Color;
55 import java.awt.Dimension;
56 import java.awt.FontMetrics;
57 import java.awt.Graphics;
58 import java.awt.GridLayout;
59 import java.awt.Point;
60 import java.awt.Rectangle;
61 import java.awt.Toolkit;
62 import java.awt.Window;
63 import java.awt.datatransfer.Clipboard;
64 import java.awt.datatransfer.ClipboardOwner;
65 import java.awt.datatransfer.DataFlavor;
66 import java.awt.datatransfer.Transferable;
67 import java.awt.dnd.DnDConstants;
68 import java.awt.dnd.DropTargetDragEvent;
69 import java.awt.dnd.DropTargetDropEvent;
70 import java.awt.dnd.DropTargetEvent;
71 import java.awt.dnd.DropTargetListener;
72 import java.awt.event.ActionEvent;
73 import java.awt.event.ActionListener;
74 import java.awt.event.InputEvent;
75 import java.awt.event.KeyEvent;
76 import java.awt.event.MouseAdapter;
77 import java.awt.event.MouseEvent;
78 import java.awt.event.WindowAdapter;
79 import java.awt.event.WindowEvent;
80 import java.beans.PropertyChangeEvent;
81 import java.beans.PropertyChangeListener;
82 import java.io.BufferedInputStream;
84 import java.io.FileOutputStream;
85 import java.io.IOException;
87 import java.util.ArrayList;
88 import java.util.Hashtable;
89 import java.util.List;
90 import java.util.ListIterator;
91 import java.util.Vector;
92 import java.util.concurrent.ExecutorService;
93 import java.util.concurrent.Executors;
94 import java.util.concurrent.Semaphore;
96 import javax.swing.AbstractAction;
97 import javax.swing.Action;
98 import javax.swing.ActionMap;
99 import javax.swing.Box;
100 import javax.swing.BoxLayout;
101 import javax.swing.DefaultDesktopManager;
102 import javax.swing.DesktopManager;
103 import javax.swing.InputMap;
104 import javax.swing.JButton;
105 import javax.swing.JCheckBox;
106 import javax.swing.JComboBox;
107 import javax.swing.JComponent;
108 import javax.swing.JDesktopPane;
109 import javax.swing.JFrame;
110 import javax.swing.JInternalFrame;
111 import javax.swing.JLabel;
112 import javax.swing.JMenuItem;
113 import javax.swing.JPanel;
114 import javax.swing.JPopupMenu;
115 import javax.swing.JProgressBar;
116 import javax.swing.JTextField;
117 import javax.swing.KeyStroke;
118 import javax.swing.SwingUtilities;
119 import javax.swing.event.HyperlinkEvent;
120 import javax.swing.event.HyperlinkEvent.EventType;
121 import javax.swing.event.InternalFrameAdapter;
122 import javax.swing.event.InternalFrameEvent;
123 import javax.swing.event.MenuEvent;
124 import javax.swing.event.MenuListener;
126 import org.stackoverflowusers.file.WindowsShortcut;
133 * @version $Revision: 1.155 $
135 public class Desktop extends jalview.jbgui.GDesktop
136 implements DropTargetListener, ClipboardOwner, IProgressIndicator,
137 jalview.api.StructureSelectionManagerProvider
139 private static int DEFAULT_MIN_WIDTH = 300;
141 private static int DEFAULT_MIN_HEIGHT = 250;
143 private static int ALIGN_FRAME_DEFAULT_MIN_WIDTH = 600;
145 private static int ALIGN_FRAME_DEFAULT_MIN_HEIGHT = 70;
147 private static final String EXPERIMENTAL_FEATURES = "EXPERIMENTAL_FEATURES";
149 private JalviewChangeSupport changeSupport = new JalviewChangeSupport();
152 * news reader - null if it was never started.
154 private BlogReader jvnews = null;
156 private File projectFile;
160 * @see jalview.gui.JalviewChangeSupport#addJalviewPropertyChangeListener(java.beans.PropertyChangeListener)
162 public void addJalviewPropertyChangeListener(
163 PropertyChangeListener listener)
165 changeSupport.addJalviewPropertyChangeListener(listener);
169 * @param propertyName
171 * @see jalview.gui.JalviewChangeSupport#addJalviewPropertyChangeListener(java.lang.String,
172 * java.beans.PropertyChangeListener)
174 public void addJalviewPropertyChangeListener(String propertyName,
175 PropertyChangeListener listener)
177 changeSupport.addJalviewPropertyChangeListener(propertyName, listener);
181 * @param propertyName
183 * @see jalview.gui.JalviewChangeSupport#removeJalviewPropertyChangeListener(java.lang.String,
184 * java.beans.PropertyChangeListener)
186 public void removeJalviewPropertyChangeListener(String propertyName,
187 PropertyChangeListener listener)
189 changeSupport.removeJalviewPropertyChangeListener(propertyName,
193 /** Singleton Desktop instance */
194 public static Desktop instance;
196 public static MyDesktopPane desktop;
198 public static MyDesktopPane getDesktop()
200 // BH 2018 could use currentThread() here as a reference to a
201 // Hashtable<Thread, MyDesktopPane> in JavaScript
205 static int openFrameCount = 0;
207 static final int xOffset = 30;
209 static final int yOffset = 30;
211 public static jalview.ws.jws1.Discoverer discoverer;
213 public static Object[] jalviewClipboard;
215 public static boolean internalCopy = false;
217 static int fileLoadingCount = 0;
219 class MyDesktopManager implements DesktopManager
222 private DesktopManager delegate;
224 public MyDesktopManager(DesktopManager delegate)
226 this.delegate = delegate;
230 public void activateFrame(JInternalFrame f)
234 delegate.activateFrame(f);
235 } catch (NullPointerException npe)
237 Point p = getMousePosition();
238 instance.showPasteMenu(p.x, p.y);
243 public void beginDraggingFrame(JComponent f)
245 delegate.beginDraggingFrame(f);
249 public void beginResizingFrame(JComponent f, int direction)
251 delegate.beginResizingFrame(f, direction);
255 public void closeFrame(JInternalFrame f)
257 delegate.closeFrame(f);
261 public void deactivateFrame(JInternalFrame f)
263 delegate.deactivateFrame(f);
267 public void deiconifyFrame(JInternalFrame f)
269 delegate.deiconifyFrame(f);
273 public void dragFrame(JComponent f, int newX, int newY)
279 delegate.dragFrame(f, newX, newY);
283 public void endDraggingFrame(JComponent f)
285 delegate.endDraggingFrame(f);
290 public void endResizingFrame(JComponent f)
292 delegate.endResizingFrame(f);
297 public void iconifyFrame(JInternalFrame f)
299 delegate.iconifyFrame(f);
303 public void maximizeFrame(JInternalFrame f)
305 delegate.maximizeFrame(f);
309 public void minimizeFrame(JInternalFrame f)
311 delegate.minimizeFrame(f);
315 public void openFrame(JInternalFrame f)
317 delegate.openFrame(f);
321 public void resizeFrame(JComponent f, int newX, int newY, int newWidth,
328 delegate.resizeFrame(f, newX, newY, newWidth, newHeight);
332 public void setBoundsForFrame(JComponent f, int newX, int newY,
333 int newWidth, int newHeight)
335 delegate.setBoundsForFrame(f, newX, newY, newWidth, newHeight);
338 // All other methods, simply delegate
343 * Creates a new Desktop object.
349 * A note to implementors. It is ESSENTIAL that any activities that might block
350 * are spawned off as threads rather than waited for during this constructor.
355 doVamsasClientCheck();
358 doConfigureStructurePrefs();
359 setTitle("Jalview " + jalview.bin.Cache.getProperty("VERSION"));
360 setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
361 boolean selmemusage = jalview.bin.Cache.getDefault("SHOW_MEMUSAGE",
363 boolean showjconsole = jalview.bin.Cache.getDefault("SHOW_JAVA_CONSOLE",
365 desktop = new MyDesktopPane(selmemusage);
368 showMemusage.setSelected(selmemusage);
369 desktop.setBackground(Color.white);
370 getContentPane().setLayout(new BorderLayout());
371 // alternate config - have scrollbars - see notes in JAL-153
372 // JScrollPane sp = new JScrollPane();
373 // sp.getViewport().setView(desktop);
374 // getContentPane().add(sp, BorderLayout.CENTER);
376 // BH 2018 - just an experiment to try unclipped JInternalFrames.
379 getRootPane().putClientProperty("swingjs.overflow.hidden", "false");
382 getContentPane().add(desktop, BorderLayout.CENTER);
383 desktop.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
385 // This line prevents Windows Look&Feel resizing all new windows to maximum
386 // if previous window was maximised
387 desktop.setDesktopManager(new MyDesktopManager(
388 (Platform.isWindowsAndNotJS() ? new DefaultDesktopManager()
389 : Platform.isAMacAndNotJS()
390 ? new AquaInternalFrameManager(
391 desktop.getDesktopManager())
392 : desktop.getDesktopManager())));
394 Rectangle dims = getLastKnownDimensions("");
401 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
402 int xPos = Math.max(5, (screenSize.width - 900) / 2);
403 int yPos = Math.max(5, (screenSize.height - 650) / 2);
404 setBounds(xPos, yPos, 900, 650);
407 boolean doFullLoad = /** @j2sNative ! */true;
411 jconsole = new Console(this, showjconsole);
412 // add essential build information
413 jconsole.setHeader("Jalview Version: "
414 + jalview.bin.Cache.getProperty("VERSION") + "\n"
415 + "Jalview Installation: "
416 + jalview.bin.Cache.getDefault("INSTALLATION", "unknown")
417 + "\n" + "Build Date: "
418 + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown") + "\n"
419 + "Java version: " + System.getProperty("java.version") + "\n"
420 + System.getProperty("os.arch") + " "
421 + System.getProperty("os.name") + " "
422 + System.getProperty("os.version"));
424 showConsole(showjconsole);
426 showNews.setVisible(false);
428 experimentalFeatures.setSelected(showExperimental());
430 getIdentifiersOrgData();
434 // Spawn a thread that shows the splashscreen
436 SwingUtilities.invokeLater(new Runnable()
445 // Thread off a new instance of the file chooser - this reduces the time it
446 // takes to open it later on.
447 new Thread(new Runnable()
452 Cache.log.debug("Filechooser init thread started.");
453 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
454 JalviewFileChooser.forRead(Cache.getProperty("LAST_DIRECTORY"),
456 Cache.log.debug("Filechooser init thread finished.");
459 // Add the service change listener
460 changeSupport.addJalviewPropertyChangeListener("services",
461 new PropertyChangeListener()
465 public void propertyChange(PropertyChangeEvent evt)
467 Cache.log.debug("Firing service changed event for "
468 + evt.getNewValue());
469 JalviewServicesChanged(evt);
476 this.setDropTarget(new java.awt.dnd.DropTarget(desktop, this));
478 this.addWindowListener(new WindowAdapter()
481 public void windowClosing(WindowEvent evt)
488 this.addMouseListener(ma = new MouseAdapter()
491 public void mousePressed(MouseEvent evt)
493 if (evt.isPopupTrigger()) // Mac
495 showPasteMenu(evt.getX(), evt.getY());
500 public void mouseReleased(MouseEvent evt)
502 if (evt.isPopupTrigger()) // Windows
504 showPasteMenu(evt.getX(), evt.getY());
508 desktop.addMouseListener(ma);
513 * Answers true if user preferences to enable experimental features is True
518 public boolean showExperimental()
520 String experimental = Cache.getDefault(EXPERIMENTAL_FEATURES,
521 Boolean.FALSE.toString());
522 return Boolean.valueOf(experimental).booleanValue();
525 public void doConfigureStructurePrefs()
527 // configure services
528 StructureSelectionManager ssm = StructureSelectionManager
529 .getStructureSelectionManager(this);
530 if (jalview.bin.Cache.getDefault(Preferences.ADD_SS_ANN, true))
532 ssm.setAddTempFacAnnot(jalview.bin.Cache
533 .getDefault(Preferences.ADD_TEMPFACT_ANN, true));
534 ssm.setProcessSecondaryStructure(jalview.bin.Cache
535 .getDefault(Preferences.STRUCT_FROM_PDB, true));
536 ssm.setSecStructServices(
537 jalview.bin.Cache.getDefault(Preferences.USE_RNAVIEW, true));
541 ssm.setAddTempFacAnnot(false);
542 ssm.setProcessSecondaryStructure(false);
543 ssm.setSecStructServices(false);
547 public void checkForNews()
549 final Desktop me = this;
550 // Thread off the news reader, in case there are connection problems.
551 new Thread(new Runnable()
556 Cache.log.debug("Starting news thread.");
557 jvnews = new BlogReader(me);
558 showNews.setVisible(true);
559 Cache.log.debug("Completed news thread.");
564 public void getIdentifiersOrgData()
566 // Thread off the identifiers fetcher
567 new Thread(new Runnable()
572 Cache.log.debug("Downloading data from identifiers.org");
573 UrlDownloadClient client = new UrlDownloadClient();
576 client.download(IdOrgSettings.getUrl(),
577 IdOrgSettings.getDownloadLocation());
578 } catch (IOException e)
580 Cache.log.debug("Exception downloading identifiers.org data"
589 protected void showNews_actionPerformed(ActionEvent e)
591 showNews(showNews.isSelected());
594 void showNews(boolean visible)
596 Cache.log.debug((visible ? "Showing" : "Hiding") + " news.");
597 showNews.setSelected(visible);
598 if (visible && !jvnews.isVisible())
600 new Thread(new Runnable()
605 long now = System.currentTimeMillis();
606 Desktop.instance.setProgressBar(
607 MessageManager.getString("status.refreshing_news"), now);
608 jvnews.refreshNews();
609 Desktop.instance.setProgressBar(null, now);
617 * recover the last known dimensions for a jalview window
620 * - empty string is desktop, all other windows have unique prefix
621 * @return null or last known dimensions scaled to current geometry (if last
622 * window geom was known)
624 Rectangle getLastKnownDimensions(String windowName)
626 // TODO: lock aspect ratio for scaling desktop Bug #0058199
627 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
628 String x = jalview.bin.Cache.getProperty(windowName + "SCREEN_X");
629 String y = jalview.bin.Cache.getProperty(windowName + "SCREEN_Y");
630 String width = jalview.bin.Cache
631 .getProperty(windowName + "SCREEN_WIDTH");
632 String height = jalview.bin.Cache
633 .getProperty(windowName + "SCREEN_HEIGHT");
634 if ((x != null) && (y != null) && (width != null) && (height != null))
636 int ix = Integer.parseInt(x), iy = Integer.parseInt(y),
637 iw = Integer.parseInt(width), ih = Integer.parseInt(height);
638 if (jalview.bin.Cache.getProperty("SCREENGEOMETRY_WIDTH") != null)
640 // attempt #1 - try to cope with change in screen geometry - this
641 // version doesn't preserve original jv aspect ratio.
642 // take ratio of current screen size vs original screen size.
643 double sw = ((1f * screenSize.width) / (1f * Integer.parseInt(
644 jalview.bin.Cache.getProperty("SCREENGEOMETRY_WIDTH"))));
645 double sh = ((1f * screenSize.height) / (1f * Integer.parseInt(
646 jalview.bin.Cache.getProperty("SCREENGEOMETRY_HEIGHT"))));
647 // rescale the bounds depending upon the current screen geometry.
648 ix = (int) (ix * sw);
649 iw = (int) (iw * sw);
650 iy = (int) (iy * sh);
651 ih = (int) (ih * sh);
652 while (ix >= screenSize.width)
654 jalview.bin.Cache.log.debug(
655 "Window geometry location recall error: shifting horizontal to within screenbounds.");
656 ix -= screenSize.width;
658 while (iy >= screenSize.height)
660 jalview.bin.Cache.log.debug(
661 "Window geometry location recall error: shifting vertical to within screenbounds.");
662 iy -= screenSize.height;
664 jalview.bin.Cache.log.debug(
665 "Got last known dimensions for " + windowName + ": x:" + ix
666 + " y:" + iy + " width:" + iw + " height:" + ih);
668 // return dimensions for new instance
669 return new Rectangle(ix, iy, iw, ih);
674 private void doVamsasClientCheck()
676 if (Cache.vamsasJarsPresent())
678 setupVamsasDisconnectedGui();
679 VamsasMenu.setVisible(true);
680 final Desktop us = this;
681 VamsasMenu.addMenuListener(new MenuListener()
683 // this listener remembers when the menu was first selected, and
684 // doesn't rebuild the session list until it has been cleared and
686 boolean refresh = true;
689 public void menuCanceled(MenuEvent e)
695 public void menuDeselected(MenuEvent e)
701 public void menuSelected(MenuEvent e)
705 us.buildVamsasStMenu();
710 vamsasStart.setVisible(true);
714 void showPasteMenu(int x, int y)
716 JPopupMenu popup = new JPopupMenu();
717 JMenuItem item = new JMenuItem(
718 MessageManager.getString("label.paste_new_window"));
719 item.addActionListener(new ActionListener()
722 public void actionPerformed(ActionEvent evt)
729 popup.show(this, x, y);
736 Clipboard c = Toolkit.getDefaultToolkit().getSystemClipboard();
737 Transferable contents = c.getContents(this);
739 if (contents != null)
741 String file = (String) contents
742 .getTransferData(DataFlavor.stringFlavor);
744 FileFormatI format = new IdentifyFile().identify(file,
745 DataSourceType.PASTE);
747 new FileLoader().LoadFile(file, DataSourceType.PASTE, format);
750 } catch (Exception ex)
753 "Unable to paste alignment from system clipboard:\n" + ex);
758 * Adds and opens the given frame to the desktop
769 public static synchronized void addInternalFrame(
770 final JInternalFrame frame, String title, int w, int h)
772 addInternalFrame(frame, title, true, w, h, true, false);
776 * Add an internal frame to the Jalview desktop
783 * When true, display frame immediately, otherwise, caller must call
784 * setVisible themselves.
790 public static synchronized void addInternalFrame(
791 final JInternalFrame frame, String title, boolean makeVisible,
794 addInternalFrame(frame, title, makeVisible, w, h, true, false);
798 * Add an internal frame to the Jalview desktop and make it visible
811 public static synchronized void addInternalFrame(
812 final JInternalFrame frame, String title, int w, int h,
815 addInternalFrame(frame, title, true, w, h, resizable, false);
819 * Add an internal frame to the Jalview desktop
826 * When true, display frame immediately, otherwise, caller must call
827 * setVisible themselves.
834 * @param ignoreMinSize
835 * Do not set the default minimum size for frame
837 public static synchronized void addInternalFrame(
838 final JInternalFrame frame, String title, boolean makeVisible,
839 int w, int h, boolean resizable, boolean ignoreMinSize)
842 // TODO: allow callers to determine X and Y position of frame (eg. via
844 // TODO: consider fixing method to update entries in the window submenu with
845 // the current window title
847 frame.setTitle(title);
848 if (frame.getWidth() < 1 || frame.getHeight() < 1)
852 // THIS IS A PUBLIC STATIC METHOD, SO IT MAY BE CALLED EVEN IN
853 // A HEADLESS STATE WHEN NO DESKTOP EXISTS. MUST RETURN
854 // IF JALVIEW IS RUNNING HEADLESS
855 // ///////////////////////////////////////////////
856 if (instance == null || (System.getProperty("java.awt.headless") != null
857 && System.getProperty("java.awt.headless").equals("true")))
866 frame.setMinimumSize(
867 new Dimension(DEFAULT_MIN_WIDTH, DEFAULT_MIN_HEIGHT));
869 // Set default dimension for Alignment Frame window.
870 // The Alignment Frame window could be added from a number of places,
872 // I did this here in order not to miss out on any Alignment frame.
873 if (frame instanceof AlignFrame)
875 frame.setMinimumSize(new Dimension(ALIGN_FRAME_DEFAULT_MIN_WIDTH,
876 ALIGN_FRAME_DEFAULT_MIN_HEIGHT));
880 frame.setVisible(makeVisible);
881 frame.setClosable(true);
882 frame.setResizable(resizable);
883 frame.setMaximizable(resizable);
884 frame.setIconifiable(resizable);
885 frame.setOpaque(/** @j2sNative true || */
888 if (frame.getX() < 1 && frame.getY() < 1)
890 frame.setLocation(xOffset * openFrameCount,
891 yOffset * ((openFrameCount - 1) % 10) + yOffset);
895 * add an entry for the new frame in the Window menu
896 * (and remove it when the frame is closed)
898 final JMenuItem menuItem = new JMenuItem(title);
899 frame.addInternalFrameListener(new InternalFrameAdapter()
902 public void internalFrameActivated(InternalFrameEvent evt)
904 JInternalFrame itf = desktop.getSelectedFrame();
907 if (itf instanceof AlignFrame)
909 Jalview.setCurrentAlignFrame((AlignFrame) itf);
916 public void internalFrameClosed(InternalFrameEvent evt)
918 PaintRefresher.RemoveComponent(frame);
921 * defensive check to prevent frames being
922 * added half off the window
924 if (openFrameCount > 0)
930 * ensure no reference to alignFrame retained by menu item listener
932 if (menuItem.getActionListeners().length > 0)
934 menuItem.removeActionListener(menuItem.getActionListeners()[0]);
936 windowMenu.remove(menuItem);
940 menuItem.addActionListener(new ActionListener()
943 public void actionPerformed(ActionEvent e)
947 frame.setSelected(true);
948 frame.setIcon(false);
949 } catch (java.beans.PropertyVetoException ex)
951 // System.err.println(ex.toString());
956 setKeyBindings(frame);
960 windowMenu.add(menuItem);
965 frame.setSelected(true);
966 frame.requestFocus();
967 } catch (java.beans.PropertyVetoException ve)
969 } catch (java.lang.ClassCastException cex)
972 "Squashed a possible GUI implementation error. If you can recreate this, please look at http://issues.jalview.org/browse/JAL-869",
978 * Add key bindings to a JInternalFrame so that Ctrl-W and Cmd-W will close the
983 private static void setKeyBindings(JInternalFrame frame)
985 @SuppressWarnings("serial")
986 final Action closeAction = new AbstractAction()
989 public void actionPerformed(ActionEvent e)
996 * set up key bindings for Ctrl-W and Cmd-W, with the same (Close) action
998 KeyStroke ctrlWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
999 InputEvent.CTRL_DOWN_MASK);
1000 KeyStroke cmdWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
1001 Toolkit.getDefaultToolkit().getMenuShortcutKeyMask());
1003 InputMap inputMap = frame
1004 .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
1005 String ctrlW = ctrlWKey.toString();
1006 inputMap.put(ctrlWKey, ctrlW);
1007 inputMap.put(cmdWKey, ctrlW);
1009 ActionMap actionMap = frame.getActionMap();
1010 actionMap.put(ctrlW, closeAction);
1014 public void lostOwnership(Clipboard clipboard, Transferable contents)
1018 Desktop.jalviewClipboard = null;
1021 internalCopy = false;
1025 public void dragEnter(DropTargetDragEvent evt)
1030 public void dragExit(DropTargetEvent evt)
1035 public void dragOver(DropTargetDragEvent evt)
1040 public void dropActionChanged(DropTargetDragEvent evt)
1051 public void drop(DropTargetDropEvent evt)
1053 boolean success = true;
1054 // JAL-1552 - acceptDrop required before getTransferable call for
1055 // Java's Transferable for native dnd
1056 evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
1057 Transferable t = evt.getTransferable();
1058 List<Object> files = new ArrayList<>();
1059 List<DataSourceType> protocols = new ArrayList<>();
1063 Desktop.transferFromDropTarget(files, protocols, evt, t);
1064 } catch (Exception e)
1066 e.printStackTrace();
1074 for (int i = 0; i < files.size(); i++)
1076 // BH 2018 File or String
1077 Object file = files.get(i);
1078 String fileName = file.toString();
1079 DataSourceType protocol = (protocols == null)
1080 ? DataSourceType.FILE
1082 FileFormatI format = null;
1084 if (fileName.endsWith(".jar"))
1086 format = FileFormat.Jalview;
1091 format = new IdentifyFile().identify(file, protocol);
1094 new FileLoader().LoadFile(null, file, protocol, format);
1097 } catch (Exception ex)
1102 evt.dropComplete(success); // need this to ensure input focus is properly
1103 // transfered to any new windows created
1113 public void inputLocalFileMenuItem_actionPerformed(AlignViewport viewport)
1115 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
1116 JalviewFileChooser chooser = JalviewFileChooser
1117 .forRead(Cache.getProperty("LAST_DIRECTORY"), fileFormat);
1119 chooser.setFileView(new JalviewFileView());
1120 chooser.setDialogTitle(
1121 MessageManager.getString("label.open_local_file"));
1122 chooser.setToolTipText(MessageManager.getString("action.open"));
1124 chooser.setResponseHandler(0, new Runnable()
1129 File selectedFile = chooser.getSelectedFile();
1130 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1132 FileFormatI format = chooser.getSelectedFormat();
1135 * Call IdentifyFile to verify the file contains what its extension implies.
1136 * Skip this step for dynamically added file formats, because
1137 * IdentifyFile does not know how to recognise them.
1139 if (FileFormats.getInstance().isIdentifiable(format))
1143 format = new IdentifyFile().identify(selectedFile,
1144 DataSourceType.FILE);
1145 } catch (FileFormatException e)
1147 // format = null; //??
1151 new FileLoader().LoadFile(viewport, selectedFile,
1152 DataSourceType.FILE, format);
1155 chooser.showOpenDialog(this);
1159 * Shows a dialog for input of a URL at which to retrieve alignment data
1164 public void inputURLMenuItem_actionPerformed(AlignViewport viewport)
1166 // This construct allows us to have a wider textfield
1168 JLabel label = new JLabel(
1169 MessageManager.getString("label.input_file_url"));
1171 JPanel panel = new JPanel(new GridLayout(2, 1));
1175 * the URL to fetch is
1176 * Java: an editable combobox with history
1177 * JS: (pending JAL-3038) a plain text field
1180 String urlBase = "http://www.";
1183 history = new JTextField(urlBase, 35);
1187 JComboBox<String> asCombo = new JComboBox<>();
1188 asCombo.setPreferredSize(new Dimension(400, 20));
1189 asCombo.setEditable(true);
1190 asCombo.addItem(urlBase);
1191 String historyItems = Cache.getProperty("RECENT_URL");
1192 if (historyItems != null)
1194 for (String token : historyItems.split("\\t"))
1196 asCombo.addItem(token);
1203 Object[] options = new Object[] { MessageManager.getString("action.ok"),
1204 MessageManager.getString("action.cancel") };
1205 Runnable action = new Runnable() {
1209 String url = Jalview.isJS() ? ((JTextField) history).getText()
1210 : ((JComboBox<String>) history).getSelectedItem()
1213 if (url.toLowerCase().endsWith(".jar"))
1215 if (viewport != null)
1217 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1218 FileFormat.Jalview);
1222 new FileLoader().LoadFile(url, DataSourceType.URL,
1223 FileFormat.Jalview);
1228 FileFormatI format = null;
1231 format = new IdentifyFile().identify(url, DataSourceType.URL);
1232 } catch (FileFormatException e)
1234 // TODO revise error handling, distinguish between
1235 // URL not found and response not valid
1240 String msg = MessageManager.formatMessage("label.couldnt_locate", url);
1241 JvOptionPane.showInternalMessageDialog(Desktop.desktop, msg,
1242 MessageManager.getString("label.url_not_found"),
1243 JvOptionPane.WARNING_MESSAGE);
1248 if (viewport != null)
1250 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1255 new FileLoader().LoadFile(url, DataSourceType.URL, format);
1259 String dialogOption = MessageManager
1260 .getString("label.input_alignment_from_url");
1261 JvOptionPane.newOptionDialog(desktop).setResponseHandler(0, action)
1262 .showInternalDialog(panel, dialogOption,
1263 JvOptionPane.YES_NO_CANCEL_OPTION,
1264 JvOptionPane.PLAIN_MESSAGE, null, options,
1265 MessageManager.getString("action.ok"));
1269 * Opens the CutAndPaste window for the user to paste an alignment in to
1272 * - if not null, the pasted alignment is added to the current
1273 * alignment; if null, to a new alignment window
1276 public void inputTextboxMenuItem_actionPerformed(
1277 AlignmentViewPanel viewPanel)
1279 CutAndPasteTransfer cap = new CutAndPasteTransfer();
1280 cap.setForInput(viewPanel);
1281 Desktop.addInternalFrame(cap,
1282 MessageManager.getString("label.cut_paste_alignmen_file"), true,
1292 Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
1293 jalview.bin.Cache.setProperty("SCREENGEOMETRY_WIDTH",
1295 jalview.bin.Cache.setProperty("SCREENGEOMETRY_HEIGHT",
1296 screen.height + "");
1297 storeLastKnownDimensions("", new Rectangle(getBounds().x, getBounds().y,
1298 getWidth(), getHeight()));
1300 if (jconsole != null)
1302 storeLastKnownDimensions("JAVA_CONSOLE_", jconsole.getBounds());
1303 jconsole.stopConsole();
1307 storeLastKnownDimensions("JALVIEW_RSS_WINDOW_", jvnews.getBounds());
1310 if (dialogExecutor != null)
1312 dialogExecutor.shutdownNow();
1314 closeAll_actionPerformed(null);
1316 if (groovyConsole != null)
1318 // suppress a possible repeat prompt to save script
1319 groovyConsole.setDirty(false);
1320 groovyConsole.exit();
1325 private void storeLastKnownDimensions(String string, Rectangle jc)
1327 jalview.bin.Cache.log.debug("Storing last known dimensions for "
1328 + string + ": x:" + jc.x + " y:" + jc.y + " width:" + jc.width
1329 + " height:" + jc.height);
1331 jalview.bin.Cache.setProperty(string + "SCREEN_X", jc.x + "");
1332 jalview.bin.Cache.setProperty(string + "SCREEN_Y", jc.y + "");
1333 jalview.bin.Cache.setProperty(string + "SCREEN_WIDTH", jc.width + "");
1334 jalview.bin.Cache.setProperty(string + "SCREEN_HEIGHT", jc.height + "");
1344 public void aboutMenuItem_actionPerformed(ActionEvent e)
1346 // StringBuffer message = getAboutMessage(false);
1347 // JvOptionPane.showInternalMessageDialog(Desktop.desktop,
1349 // message.toString(), "About Jalview", JvOptionPane.INFORMATION_MESSAGE);
1350 new Thread(new Runnable()
1355 new SplashScreen(true);
1360 public StringBuffer getAboutMessage(boolean shortv)
1362 StringBuffer message = new StringBuffer();
1363 message.append("<html>");
1366 message.append("<h1><strong>Version: "
1367 + jalview.bin.Cache.getProperty("VERSION")
1368 + "</strong></h1>");
1369 message.append("<strong>Last Updated: <em>"
1370 + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown")
1371 + "</em></strong>");
1377 message.append("<strong>Version "
1378 + jalview.bin.Cache.getProperty("VERSION")
1379 + "; last updated: "
1380 + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown"));
1383 if (jalview.bin.Cache.getDefault("LATEST_VERSION", "Checking")
1384 .equals("Checking"))
1386 message.append("<br>...Checking latest version...</br>");
1388 else if (!jalview.bin.Cache.getDefault("LATEST_VERSION", "Checking")
1389 .equals(jalview.bin.Cache.getProperty("VERSION")))
1391 boolean red = false;
1392 if (jalview.bin.Cache.getProperty("VERSION").toLowerCase()
1393 .indexOf("automated build") == -1)
1396 // Displayed when code version and jnlp version do not match and code
1397 // version is not a development build
1398 message.append("<div style=\"color: #FF0000;font-style: bold;\">");
1401 message.append("<br>!! Version "
1402 + jalview.bin.Cache.getDefault("LATEST_VERSION",
1404 + " is available for download from "
1405 + jalview.bin.Cache.getDefault("www.jalview.org",
1406 "http://www.jalview.org")
1410 message.append("</div>");
1413 message.append("<br>Authors: " + jalview.bin.Cache.getDefault(
1415 "The Jalview Authors (See AUTHORS file for current list)")
1416 + "<br><br>Development managed by The Barton Group, University of Dundee, Scotland, UK.<br>"
1417 + "<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"
1418 + "<br><br>If you use Jalview, please cite:"
1419 + "<br>Waterhouse, A.M., Procter, J.B., Martin, D.M.A, Clamp, M. and Barton, G. J. (2009)"
1420 + "<br>Jalview Version 2 - a multiple sequence alignment editor and analysis workbench"
1421 + "<br>Bioinformatics doi: 10.1093/bioinformatics/btp033"
1427 * Action on requesting Help documentation
1430 public void documentationMenuItem_actionPerformed()
1436 BrowserLauncher.openURL("http://www.jalview.org/help.html");
1440 Help.showHelpWindow();
1442 } catch (Exception ex)
1444 System.err.println("Error opening help: " + ex.getMessage());
1449 public void closeAll_actionPerformed(ActionEvent e)
1451 // TODO show a progress bar while closing?
1452 JInternalFrame[] frames = desktop.getAllFrames();
1453 for (int i = 0; i < frames.length; i++)
1457 frames[i].setClosed(true);
1458 } catch (java.beans.PropertyVetoException ex)
1462 Jalview.setCurrentAlignFrame(null);
1463 System.out.println("ALL CLOSED");
1464 if (v_client != null)
1466 // TODO clear binding to vamsas document objects on close_all
1470 * reset state of singleton objects as appropriate (clear down session state
1471 * when all windows are closed)
1473 StructureSelectionManager ssm = StructureSelectionManager
1474 .getStructureSelectionManager(this);
1482 public void raiseRelated_actionPerformed(ActionEvent e)
1484 reorderAssociatedWindows(false, false);
1488 public void minimizeAssociated_actionPerformed(ActionEvent e)
1490 reorderAssociatedWindows(true, false);
1493 void closeAssociatedWindows()
1495 reorderAssociatedWindows(false, true);
1501 * @seejalview.jbgui.GDesktop#garbageCollect_actionPerformed(java.awt.event.
1505 protected void garbageCollect_actionPerformed(ActionEvent e)
1507 // We simply collect the garbage
1508 jalview.bin.Cache.log.debug("Collecting garbage...");
1510 jalview.bin.Cache.log.debug("Finished garbage collection.");
1517 * jalview.jbgui.GDesktop#showMemusage_actionPerformed(java.awt.event.ActionEvent
1521 protected void showMemusage_actionPerformed(ActionEvent e)
1523 desktop.showMemoryUsage(showMemusage.isSelected());
1530 * jalview.jbgui.GDesktop#showConsole_actionPerformed(java.awt.event.ActionEvent
1534 protected void showConsole_actionPerformed(ActionEvent e)
1536 showConsole(showConsole.isSelected());
1539 Console jconsole = null;
1542 * control whether the java console is visible or not
1546 void showConsole(boolean selected)
1548 // TODO: decide if we should update properties file
1549 if (jconsole != null) // BH 2018
1551 showConsole.setSelected(selected);
1552 Cache.setProperty("SHOW_JAVA_CONSOLE",
1553 Boolean.valueOf(selected).toString());
1554 jconsole.setVisible(selected);
1558 void reorderAssociatedWindows(boolean minimize, boolean close)
1560 JInternalFrame[] frames = desktop.getAllFrames();
1561 if (frames == null || frames.length < 1)
1566 AlignmentViewport source = null, target = null;
1567 if (frames[0] instanceof AlignFrame)
1569 source = ((AlignFrame) frames[0]).getCurrentView();
1571 else if (frames[0] instanceof TreePanel)
1573 source = ((TreePanel) frames[0]).getViewPort();
1575 else if (frames[0] instanceof PCAPanel)
1577 source = ((PCAPanel) frames[0]).av;
1579 else if (frames[0].getContentPane() instanceof PairwiseAlignPanel)
1581 source = ((PairwiseAlignPanel) frames[0].getContentPane()).av;
1586 for (int i = 0; i < frames.length; i++)
1589 if (frames[i] == null)
1593 if (frames[i] instanceof AlignFrame)
1595 target = ((AlignFrame) frames[i]).getCurrentView();
1597 else if (frames[i] instanceof TreePanel)
1599 target = ((TreePanel) frames[i]).getViewPort();
1601 else if (frames[i] instanceof PCAPanel)
1603 target = ((PCAPanel) frames[i]).av;
1605 else if (frames[i].getContentPane() instanceof PairwiseAlignPanel)
1607 target = ((PairwiseAlignPanel) frames[i].getContentPane()).av;
1610 if (source == target)
1616 frames[i].setClosed(true);
1620 frames[i].setIcon(minimize);
1623 frames[i].toFront();
1627 } catch (java.beans.PropertyVetoException ex)
1642 protected void preferences_actionPerformed(ActionEvent e)
1648 * Prompts the user to choose a file and then saves the Jalview state as a
1649 * Jalview project file
1652 public void saveState_actionPerformed(boolean asCastor)
1654 JalviewFileChooser chooser = new JalviewFileChooser(
1655 asCastor ? "jvp" : "jvx",
1658 chooser.setFileView(new JalviewFileView());
1659 chooser.setDialogTitle(MessageManager.getString("label.save_state"));
1660 int option = chooser.showSaveDialog(this);
1661 if (option == JalviewFileChooser.APPROVE_OPTION)
1663 File choice = chooser.getSelectedFile();
1664 setProjectFile(choice);
1666 new Thread(new Runnable()
1671 // TODO: refactor to Jalview desktop session controller action.
1672 setProgressBar(MessageManager.formatMessage(
1673 "label.saving_jalview_project", new Object[]
1674 { choice.getName() }), choice.hashCode());
1675 jalview.bin.Cache.setProperty("LAST_DIRECTORY",
1676 choice.getParent());
1677 // TODO catch and handle errors for savestate
1678 // TODO prevent user from messing with the Desktop whilst we're saving
1683 new Jalview2XML().saveState(choice);
1687 new jalview.project.Jalview2XML().saveState(choice);
1689 } catch (OutOfMemoryError oom)
1692 "Whilst saving current state to " + choice.getName(),
1694 } catch (Exception ex)
1697 "Problems whilst trying to save to " + choice.getName(),
1699 JvOptionPane.showMessageDialog(Desktop.this,
1700 MessageManager.formatMessage(
1701 "label.error_whilst_saving_current_state_to",
1703 { choice.getName() }),
1704 MessageManager.getString("label.couldnt_save_project"),
1705 JvOptionPane.WARNING_MESSAGE);
1707 setProgressBar(null, choice.hashCode());
1713 void setProjectFile(File choice)
1715 this.projectFile = choice;
1718 public File getProjectFile()
1720 return this.projectFile;
1724 * Prompts the user to choose a file and loads in as a Jalview project file
1727 public void loadState_actionPerformed(boolean asCastor)
1729 // TODO: GET RID OF .JVX BEFORE RELEASE JIM!
1730 final String[] suffix = asCastor ? new String[] { "jvp", "jar" }
1733 final String[] desc = asCastor
1735 { "Jalview Project", "Jalview Project (old)" }
1737 { "Jalview Project" };
1738 JalviewFileChooser chooser = new JalviewFileChooser(
1739 Cache.getProperty("LAST_DIRECTORY"), suffix,
1742 chooser.setFileView(new JalviewFileView());
1743 chooser.setDialogTitle(MessageManager.getString("label.restore_state"));
1744 chooser.setResponseHandler(0, new Runnable()
1749 File selectedFile = chooser.getSelectedFile();
1750 setProjectFile(selectedFile);
1751 final String choice = selectedFile.getAbsolutePath();
1752 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1753 new Thread(new Runnable()
1761 new Jalview2XML().loadJalviewAlign(choice);
1765 new jalview.project.Jalview2XML().loadJalviewAlign(choice);
1767 } catch (OutOfMemoryError oom)
1769 new OOMWarning("Whilst loading project from " + choice, oom);
1770 } catch (Exception ex)
1773 "Problems whilst loading project from " + choice, ex);
1774 JvOptionPane.showMessageDialog(Desktop.desktop,
1775 MessageManager.formatMessage(
1776 "label.error_whilst_loading_project_from",
1779 MessageManager.getString("label.couldnt_load_project"),
1780 JvOptionPane.WARNING_MESSAGE);
1787 chooser.showOpenDialog(this);
1791 public void inputSequence_actionPerformed(ActionEvent e)
1793 new SequenceFetcher(this);
1796 JPanel progressPanel;
1798 ArrayList<JPanel> fileLoadingPanels = new ArrayList<>();
1800 public void startLoading(final Object fileName)
1802 if (fileLoadingCount == 0)
1804 fileLoadingPanels.add(addProgressPanel(MessageManager
1805 .formatMessage("label.loading_file", new Object[]
1811 private JPanel addProgressPanel(String string)
1813 if (progressPanel == null)
1815 progressPanel = new JPanel(new GridLayout(1, 1));
1816 totalProgressCount = 0;
1817 instance.getContentPane().add(progressPanel, BorderLayout.SOUTH);
1819 JPanel thisprogress = new JPanel(new BorderLayout(10, 5));
1820 JProgressBar progressBar = new JProgressBar();
1821 progressBar.setIndeterminate(true);
1823 thisprogress.add(new JLabel(string), BorderLayout.WEST);
1825 thisprogress.add(progressBar, BorderLayout.CENTER);
1826 progressPanel.add(thisprogress);
1827 ((GridLayout) progressPanel.getLayout()).setRows(
1828 ((GridLayout) progressPanel.getLayout()).getRows() + 1);
1829 ++totalProgressCount;
1830 instance.validate();
1831 return thisprogress;
1834 int totalProgressCount = 0;
1836 private void removeProgressPanel(JPanel progbar)
1838 if (progressPanel != null)
1840 synchronized (progressPanel)
1842 progressPanel.remove(progbar);
1843 GridLayout gl = (GridLayout) progressPanel.getLayout();
1844 gl.setRows(gl.getRows() - 1);
1845 if (--totalProgressCount < 1)
1847 this.getContentPane().remove(progressPanel);
1848 progressPanel = null;
1855 public void stopLoading()
1858 if (fileLoadingCount < 1)
1860 while (fileLoadingPanels.size() > 0)
1862 removeProgressPanel(fileLoadingPanels.remove(0));
1864 fileLoadingPanels.clear();
1865 fileLoadingCount = 0;
1870 public static int getViewCount(String alignmentId)
1872 AlignmentViewport[] aps = getViewports(alignmentId);
1873 return (aps == null) ? 0 : aps.length;
1878 * @param alignmentId
1879 * - if null, all sets are returned
1880 * @return all AlignmentPanels concerning the alignmentId sequence set
1882 public static AlignmentPanel[] getAlignmentPanels(String alignmentId)
1884 if (Desktop.desktop == null)
1886 // no frames created and in headless mode
1887 // TODO: verify that frames are recoverable when in headless mode
1890 List<AlignmentPanel> aps = new ArrayList<>();
1891 AlignFrame[] frames = getAlignFrames();
1896 for (AlignFrame af : frames)
1898 for (AlignmentPanel ap : af.alignPanels)
1900 if (alignmentId == null
1901 || alignmentId.equals(ap.av.getSequenceSetId()))
1907 if (aps.size() == 0)
1911 AlignmentPanel[] vap = aps.toArray(new AlignmentPanel[aps.size()]);
1916 * get all the viewports on an alignment.
1918 * @param sequenceSetId
1919 * unique alignment id (may be null - all viewports returned in that
1921 * @return all viewports on the alignment bound to sequenceSetId
1923 public static AlignmentViewport[] getViewports(String sequenceSetId)
1925 List<AlignmentViewport> viewp = new ArrayList<>();
1926 if (desktop != null)
1928 AlignFrame[] frames = Desktop.getAlignFrames();
1930 for (AlignFrame afr : frames)
1932 if (sequenceSetId == null || afr.getViewport().getSequenceSetId()
1933 .equals(sequenceSetId))
1935 if (afr.alignPanels != null)
1937 for (AlignmentPanel ap : afr.alignPanels)
1939 if (sequenceSetId == null
1940 || sequenceSetId.equals(ap.av.getSequenceSetId()))
1948 viewp.add(afr.getViewport());
1952 if (viewp.size() > 0)
1954 return viewp.toArray(new AlignmentViewport[viewp.size()]);
1961 * Explode the views in the given frame into separate AlignFrame
1965 public static void explodeViews(AlignFrame af)
1967 int size = af.alignPanels.size();
1973 for (int i = 0; i < size; i++)
1975 AlignmentPanel ap = af.alignPanels.get(i);
1976 AlignFrame newaf = new AlignFrame(ap);
1979 * Restore the view's last exploded frame geometry if known. Multiple
1980 * views from one exploded frame share and restore the same (frame)
1981 * position and size.
1983 Rectangle geometry = ap.av.getExplodedGeometry();
1984 if (geometry != null)
1986 newaf.setBounds(geometry);
1989 ap.av.setGatherViewsHere(false);
1991 addInternalFrame(newaf, af.getTitle(), AlignFrame.DEFAULT_WIDTH,
1992 AlignFrame.DEFAULT_HEIGHT);
1995 af.alignPanels.clear();
1996 af.closeMenuItem_actionPerformed(true);
2001 * Gather expanded views (separate AlignFrame's) with the same sequence set
2002 * identifier back in to this frame as additional views, and close the expanded
2003 * views. Note the expanded frames may themselves have multiple views. We take
2008 public void gatherViews(AlignFrame source)
2010 source.viewport.setGatherViewsHere(true);
2011 source.viewport.setExplodedGeometry(source.getBounds());
2012 JInternalFrame[] frames = desktop.getAllFrames();
2013 String viewId = source.viewport.getSequenceSetId();
2015 for (int t = 0; t < frames.length; t++)
2017 if (frames[t] instanceof AlignFrame && frames[t] != source)
2019 AlignFrame af = (AlignFrame) frames[t];
2020 boolean gatherThis = false;
2021 for (int a = 0; a < af.alignPanels.size(); a++)
2023 AlignmentPanel ap = af.alignPanels.get(a);
2024 if (viewId.equals(ap.av.getSequenceSetId()))
2027 ap.av.setGatherViewsHere(false);
2028 ap.av.setExplodedGeometry(af.getBounds());
2029 source.addAlignmentPanel(ap, false);
2035 af.alignPanels.clear();
2036 af.closeMenuItem_actionPerformed(true);
2043 jalview.gui.VamsasApplication v_client = null;
2046 public void vamsasImport_actionPerformed(ActionEvent e)
2048 // TODO: JAL-3048 not needed for Jalview-JS
2050 if (v_client == null)
2052 // Load and try to start a session.
2053 JalviewFileChooser chooser = new JalviewFileChooser(
2054 jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
2056 chooser.setFileView(new JalviewFileView());
2057 chooser.setDialogTitle(
2058 MessageManager.getString("label.open_saved_vamsas_session"));
2059 chooser.setToolTipText(MessageManager.getString(
2060 "label.select_vamsas_session_opened_as_new_vamsas_session"));
2062 int value = chooser.showOpenDialog(this);
2064 if (value == JalviewFileChooser.APPROVE_OPTION)
2066 String fle = chooser.getSelectedFile().toString();
2067 if (!vamsasImport(chooser.getSelectedFile()))
2069 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
2070 MessageManager.formatMessage(
2071 "label.couldnt_import_as_vamsas_session",
2075 .getString("label.vamsas_document_import_failed"),
2076 JvOptionPane.ERROR_MESSAGE);
2082 jalview.bin.Cache.log.error(
2083 "Implementation error - load session from a running session is not supported.");
2088 * import file into a new vamsas session (uses jalview.gui.VamsasApplication)
2091 * @return true if import was a success and a session was started.
2093 public boolean vamsasImport(URL url)
2095 // TODO: create progress bar
2096 if (v_client != null)
2099 jalview.bin.Cache.log.error(
2100 "Implementation error - load session from a running session is not supported.");
2106 // copy the URL content to a temporary local file
2107 // TODO: be a bit cleverer here with nio (?!)
2108 File file = File.createTempFile("vdocfromurl", ".vdj");
2109 FileOutputStream fos = new FileOutputStream(file);
2110 BufferedInputStream bis = new BufferedInputStream(url.openStream());
2111 byte[] buffer = new byte[2048];
2113 while ((ln = bis.read(buffer)) > -1)
2115 fos.write(buffer, 0, ln);
2119 v_client = new jalview.gui.VamsasApplication(this, file,
2120 url.toExternalForm());
2121 } catch (Exception ex)
2123 jalview.bin.Cache.log.error(
2124 "Failed to create new vamsas session from contents of URL "
2129 setupVamsasConnectedGui();
2130 v_client.initial_update(); // TODO: thread ?
2131 return v_client.inSession();
2135 * import file into a new vamsas session (uses jalview.gui.VamsasApplication)
2138 * @return true if import was a success and a session was started.
2140 public boolean vamsasImport(File file)
2142 if (v_client != null)
2145 jalview.bin.Cache.log.error(
2146 "Implementation error - load session from a running session is not supported.");
2150 setProgressBar(MessageManager.formatMessage(
2151 "status.importing_vamsas_session_from", new Object[]
2152 { file.getName() }), file.hashCode());
2155 v_client = new jalview.gui.VamsasApplication(this, file, null);
2156 } catch (Exception ex)
2158 setProgressBar(MessageManager.formatMessage(
2159 "status.importing_vamsas_session_from", new Object[]
2160 { file.getName() }), file.hashCode());
2161 jalview.bin.Cache.log.error(
2162 "New vamsas session from existing session file failed:", ex);
2165 setupVamsasConnectedGui();
2166 v_client.initial_update(); // TODO: thread ?
2167 setProgressBar(MessageManager.formatMessage(
2168 "status.importing_vamsas_session_from", new Object[]
2169 { file.getName() }), file.hashCode());
2170 return v_client.inSession();
2173 public boolean joinVamsasSession(String mysesid)
2175 if (v_client != null)
2177 throw new Error(MessageManager
2178 .getString("error.try_join_vamsas_session_another"));
2180 if (mysesid == null)
2183 MessageManager.getString("error.invalid_vamsas_session_id"));
2185 v_client = new VamsasApplication(this, mysesid);
2186 setupVamsasConnectedGui();
2187 v_client.initial_update();
2188 return (v_client.inSession());
2192 public void vamsasStart_actionPerformed(ActionEvent e)
2194 if (v_client == null)
2197 // we just start a default session for moment.
2199 * JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache.
2200 * getProperty("LAST_DIRECTORY"));
2202 * chooser.setFileView(new JalviewFileView());
2203 * chooser.setDialogTitle("Load Vamsas file");
2204 * chooser.setToolTipText("Import");
2206 * int value = chooser.showOpenDialog(this);
2208 * if (value == JalviewFileChooser.APPROVE_OPTION) { v_client = new
2209 * jalview.gui.VamsasApplication(this, chooser.getSelectedFile());
2211 v_client = new VamsasApplication(this);
2212 setupVamsasConnectedGui();
2213 v_client.initial_update(); // TODO: thread ?
2217 // store current data in session.
2218 v_client.push_update(); // TODO: thread
2222 protected void setupVamsasConnectedGui()
2224 vamsasStart.setText(MessageManager.getString("label.session_update"));
2225 vamsasSave.setVisible(true);
2226 vamsasStop.setVisible(true);
2227 vamsasImport.setVisible(false); // Document import to existing session is
2228 // not possible for vamsas-client-1.0.
2231 protected void setupVamsasDisconnectedGui()
2233 vamsasSave.setVisible(false);
2234 vamsasStop.setVisible(false);
2235 vamsasImport.setVisible(true);
2237 .setText(MessageManager.getString("label.new_vamsas_session"));
2241 public void vamsasStop_actionPerformed(ActionEvent e)
2243 if (v_client != null)
2245 v_client.end_session();
2247 setupVamsasDisconnectedGui();
2251 protected void buildVamsasStMenu()
2253 if (v_client == null)
2255 String[] sess = null;
2258 sess = VamsasApplication.getSessionList();
2259 } catch (Exception e)
2261 jalview.bin.Cache.log.warn("Problem getting current sessions list.",
2267 jalview.bin.Cache.log.debug(
2268 "Got current sessions list: " + sess.length + " entries.");
2269 VamsasStMenu.removeAll();
2270 for (int i = 0; i < sess.length; i++)
2272 JMenuItem sessit = new JMenuItem();
2273 sessit.setText(sess[i]);
2274 sessit.setToolTipText(MessageManager
2275 .formatMessage("label.connect_to_session", new Object[]
2277 final Desktop dsktp = this;
2278 final String mysesid = sess[i];
2279 sessit.addActionListener(new ActionListener()
2283 public void actionPerformed(ActionEvent e)
2285 if (dsktp.v_client == null)
2287 Thread rthr = new Thread(new Runnable()
2293 dsktp.v_client = new VamsasApplication(dsktp, mysesid);
2294 dsktp.setupVamsasConnectedGui();
2295 dsktp.v_client.initial_update();
2303 VamsasStMenu.add(sessit);
2305 // don't show an empty menu.
2306 VamsasStMenu.setVisible(sess.length > 0);
2311 jalview.bin.Cache.log.debug("No current vamsas sessions.");
2312 VamsasStMenu.removeAll();
2313 VamsasStMenu.setVisible(false);
2318 // Not interested in the content. Just hide ourselves.
2319 VamsasStMenu.setVisible(false);
2324 public void vamsasSave_actionPerformed(ActionEvent e)
2326 // TODO: JAL-3048 not needed for Jalview-JS
2328 if (v_client != null)
2330 // TODO: VAMSAS DOCUMENT EXTENSION is VDJ
2331 JalviewFileChooser chooser = new JalviewFileChooser("vdj",
2334 chooser.setFileView(new JalviewFileView());
2335 chooser.setDialogTitle(MessageManager
2336 .getString("label.save_vamsas_document_archive"));
2338 int value = chooser.showSaveDialog(this);
2340 if (value == JalviewFileChooser.APPROVE_OPTION)
2342 java.io.File choice = chooser.getSelectedFile();
2343 JPanel progpanel = addProgressPanel(MessageManager
2344 .formatMessage("label.saving_vamsas_doc", new Object[]
2345 { choice.getName() }));
2346 Cache.setProperty("LAST_DIRECTORY", choice.getParent());
2347 String warnmsg = null;
2348 String warnttl = null;
2351 v_client.vclient.storeDocument(choice);
2354 warnttl = "Serious Problem saving Vamsas Document";
2355 warnmsg = ex.toString();
2356 jalview.bin.Cache.log
2357 .error("Error Whilst saving document to " + choice, ex);
2359 } catch (Exception ex)
2361 warnttl = "Problem saving Vamsas Document.";
2362 warnmsg = ex.toString();
2363 jalview.bin.Cache.log.warn(
2364 "Exception Whilst saving document to " + choice, ex);
2367 removeProgressPanel(progpanel);
2368 if (warnmsg != null)
2370 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
2372 warnmsg, warnttl, JvOptionPane.ERROR_MESSAGE);
2378 JPanel vamUpdate = null;
2381 * hide vamsas user gui bits when a vamsas document event is being handled.
2384 * true to hide gui, false to reveal gui
2386 public void setVamsasUpdate(boolean b)
2388 Cache.log.debug("Setting gui for Vamsas update "
2389 + (b ? "in progress" : "finished"));
2391 if (vamUpdate != null)
2393 this.removeProgressPanel(vamUpdate);
2397 vamUpdate = this.addProgressPanel(
2398 MessageManager.getString("label.updating_vamsas_session"));
2400 vamsasStart.setVisible(!b);
2401 vamsasStop.setVisible(!b);
2402 vamsasSave.setVisible(!b);
2405 public JInternalFrame[] getAllFrames()
2407 return desktop.getAllFrames();
2411 * Checks the given url to see if it gives a response indicating that the user
2412 * should be informed of a new questionnaire.
2416 public void checkForQuestionnaire(String url)
2418 UserQuestionnaireCheck jvq = new UserQuestionnaireCheck(url);
2419 // javax.swing.SwingUtilities.invokeLater(jvq);
2420 new Thread(jvq).start();
2423 public void checkURLLinks()
2425 // Thread off the URL link checker
2426 addDialogThread(new Runnable()
2431 if (Cache.getDefault("CHECKURLLINKS", true))
2433 // check what the actual links are - if it's just the default don't
2434 // bother with the warning
2435 List<String> links = Preferences.sequenceUrlLinks
2438 // only need to check links if there is one with a
2439 // SEQUENCE_ID which is not the default EMBL_EBI link
2440 ListIterator<String> li = links.listIterator();
2441 boolean check = false;
2442 List<JLabel> urls = new ArrayList<>();
2443 while (li.hasNext())
2445 String link = li.next();
2446 if (link.contains(SEQUENCE_ID)
2447 && !UrlConstants.isDefaultString(link))
2450 int barPos = link.indexOf("|");
2451 String urlMsg = barPos == -1 ? link
2452 : link.substring(0, barPos) + ": "
2453 + link.substring(barPos + 1);
2454 urls.add(new JLabel(urlMsg));
2462 // ask user to check in case URL links use old style tokens
2463 // ($SEQUENCE_ID$ for sequence id _or_ accession id)
2464 JPanel msgPanel = new JPanel();
2465 msgPanel.setLayout(new BoxLayout(msgPanel, BoxLayout.PAGE_AXIS));
2466 msgPanel.add(Box.createVerticalGlue());
2467 JLabel msg = new JLabel(MessageManager
2468 .getString("label.SEQUENCE_ID_for_DB_ACCESSION1"));
2469 JLabel msg2 = new JLabel(MessageManager
2470 .getString("label.SEQUENCE_ID_for_DB_ACCESSION2"));
2472 for (JLabel url : urls)
2478 final JCheckBox jcb = new JCheckBox(
2479 MessageManager.getString("label.do_not_display_again"));
2480 jcb.addActionListener(new ActionListener()
2483 public void actionPerformed(ActionEvent e)
2485 // update Cache settings for "don't show this again"
2486 boolean showWarningAgain = !jcb.isSelected();
2487 Cache.setProperty("CHECKURLLINKS",
2488 Boolean.valueOf(showWarningAgain).toString());
2493 JvOptionPane.showMessageDialog(Desktop.desktop, msgPanel,
2495 .getString("label.SEQUENCE_ID_no_longer_used"),
2496 JvOptionPane.WARNING_MESSAGE);
2503 * Proxy class for JDesktopPane which optionally displays the current memory
2504 * usage and highlights the desktop area with a red bar if free memory runs low.
2508 public class MyDesktopPane extends JDesktopPane
2511 private static final float ONE_MB = 1048576f;
2513 boolean showMemoryUsage = false;
2517 java.text.NumberFormat df;
2519 float maxMemory, allocatedMemory, freeMemory, totalFreeMemory,
2522 public MyDesktopPane(boolean showMemoryUsage)
2524 showMemoryUsage(showMemoryUsage);
2527 public void showMemoryUsage(boolean showMemory)
2529 this.showMemoryUsage = showMemory;
2532 Thread worker = new Thread(this);
2538 public boolean isShowMemoryUsage()
2540 return showMemoryUsage;
2546 df = java.text.NumberFormat.getNumberInstance();
2547 df.setMaximumFractionDigits(2);
2548 runtime = Runtime.getRuntime();
2550 while (showMemoryUsage)
2554 maxMemory = runtime.maxMemory() / ONE_MB;
2555 allocatedMemory = runtime.totalMemory() / ONE_MB;
2556 freeMemory = runtime.freeMemory() / ONE_MB;
2557 totalFreeMemory = freeMemory + (maxMemory - allocatedMemory);
2559 percentUsage = (totalFreeMemory / maxMemory) * 100;
2561 // if (percentUsage < 20)
2563 // border1 = BorderFactory.createMatteBorder(12, 12, 12, 12,
2565 // instance.set.setBorder(border1);
2568 // sleep after showing usage
2570 } catch (Exception ex)
2572 ex.printStackTrace();
2578 public void paintComponent(Graphics g)
2580 if (showMemoryUsage && g != null && df != null)
2582 if (percentUsage < 20)
2584 g.setColor(Color.red);
2586 FontMetrics fm = g.getFontMetrics();
2589 g.drawString(MessageManager.formatMessage("label.memory_stats",
2591 { df.format(totalFreeMemory), df.format(maxMemory),
2592 df.format(percentUsage) }),
2593 10, getHeight() - fm.getHeight());
2600 * Accessor method to quickly get all the AlignmentFrames loaded.
2602 * @return an array of AlignFrame, or null if none found
2604 public static AlignFrame[] getAlignFrames()
2606 if (Jalview.isHeadlessMode())
2608 // Desktop.desktop is null in headless mode
2609 return new AlignFrame[] { Jalview.currentAlignFrame };
2612 JInternalFrame[] frames = Desktop.desktop.getAllFrames();
2618 List<AlignFrame> avp = new ArrayList<>();
2620 for (int i = frames.length - 1; i > -1; i--)
2622 if (frames[i] instanceof AlignFrame)
2624 avp.add((AlignFrame) frames[i]);
2626 else if (frames[i] instanceof SplitFrame)
2629 * Also check for a split frame containing an AlignFrame
2631 GSplitFrame sf = (GSplitFrame) frames[i];
2632 if (sf.getTopFrame() instanceof AlignFrame)
2634 avp.add((AlignFrame) sf.getTopFrame());
2636 if (sf.getBottomFrame() instanceof AlignFrame)
2638 avp.add((AlignFrame) sf.getBottomFrame());
2642 if (avp.size() == 0)
2646 AlignFrame afs[] = avp.toArray(new AlignFrame[avp.size()]);
2651 * Returns an array of any AppJmol frames in the Desktop (or null if none).
2655 public GStructureViewer[] getJmols()
2657 JInternalFrame[] frames = Desktop.desktop.getAllFrames();
2663 List<GStructureViewer> avp = new ArrayList<>();
2665 for (int i = frames.length - 1; i > -1; i--)
2667 if (frames[i] instanceof AppJmol)
2669 GStructureViewer af = (GStructureViewer) frames[i];
2673 if (avp.size() == 0)
2677 GStructureViewer afs[] = avp.toArray(new GStructureViewer[avp.size()]);
2682 * Add Groovy Support to Jalview
2685 public void groovyShell_actionPerformed()
2689 openGroovyConsole();
2690 } catch (Exception ex)
2692 jalview.bin.Cache.log.error("Groovy Shell Creation failed.", ex);
2693 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
2695 MessageManager.getString("label.couldnt_create_groovy_shell"),
2696 MessageManager.getString("label.groovy_support_failed"),
2697 JvOptionPane.ERROR_MESSAGE);
2702 * Open the Groovy console
2704 void openGroovyConsole()
2706 if (groovyConsole == null)
2708 groovyConsole = new groovy.ui.Console();
2709 groovyConsole.setVariable("Jalview", this);
2710 groovyConsole.run();
2713 * We allow only one console at a time, so that AlignFrame menu option
2714 * 'Calculate | Run Groovy script' is unambiguous.
2715 * Disable 'Groovy Console', and enable 'Run script', when the console is
2716 * opened, and the reverse when it is closed
2718 Window window = (Window) groovyConsole.getFrame();
2719 window.addWindowListener(new WindowAdapter()
2722 public void windowClosed(WindowEvent e)
2725 * rebind CMD-Q from Groovy Console to Jalview Quit
2728 enableExecuteGroovy(false);
2734 * show Groovy console window (after close and reopen)
2736 ((Window) groovyConsole.getFrame()).setVisible(true);
2739 * if we got this far, enable 'Run Groovy' in AlignFrame menus
2740 * and disable opening a second console
2742 enableExecuteGroovy(true);
2746 * Bind Ctrl/Cmd-Q to Quit - for reset as Groovy Console takes over this binding
2749 protected void addQuitHandler()
2751 getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
2752 .put(KeyStroke.getKeyStroke(KeyEvent.VK_Q,
2753 Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()),
2755 getRootPane().getActionMap().put("Quit", new AbstractAction()
2758 public void actionPerformed(ActionEvent e)
2766 * Enable or disable 'Run Groovy script' in AlignFrame calculate menus
2769 * true if Groovy console is open
2771 public void enableExecuteGroovy(boolean enabled)
2774 * disable opening a second Groovy console
2775 * (or re-enable when the console is closed)
2777 groovyShell.setEnabled(!enabled);
2779 AlignFrame[] alignFrames = getAlignFrames();
2780 if (alignFrames != null)
2782 for (AlignFrame af : alignFrames)
2784 af.setGroovyEnabled(enabled);
2790 * Progress bars managed by the IProgressIndicator method.
2792 private Hashtable<Long, JPanel> progressBars;
2794 private Hashtable<Long, IProgressIndicatorHandler> progressBarHandlers;
2799 * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
2802 public void setProgressBar(String message, long id)
2804 if (progressBars == null)
2806 progressBars = new Hashtable<>();
2807 progressBarHandlers = new Hashtable<>();
2810 if (progressBars.get(new Long(id)) != null)
2812 JPanel panel = progressBars.remove(new Long(id));
2813 if (progressBarHandlers.contains(new Long(id)))
2815 progressBarHandlers.remove(new Long(id));
2817 removeProgressPanel(panel);
2821 progressBars.put(new Long(id), addProgressPanel(message));
2828 * @see jalview.gui.IProgressIndicator#registerHandler(long,
2829 * jalview.gui.IProgressIndicatorHandler)
2832 public void registerHandler(final long id,
2833 final IProgressIndicatorHandler handler)
2835 if (progressBarHandlers == null
2836 || !progressBars.containsKey(new Long(id)))
2838 throw new Error(MessageManager.getString(
2839 "error.call_setprogressbar_before_registering_handler"));
2841 progressBarHandlers.put(new Long(id), handler);
2842 final JPanel progressPanel = progressBars.get(new Long(id));
2843 if (handler.canCancel())
2845 JButton cancel = new JButton(
2846 MessageManager.getString("action.cancel"));
2847 final IProgressIndicator us = this;
2848 cancel.addActionListener(new ActionListener()
2852 public void actionPerformed(ActionEvent e)
2854 handler.cancelActivity(id);
2855 us.setProgressBar(MessageManager
2856 .formatMessage("label.cancelled_params", new Object[]
2857 { ((JLabel) progressPanel.getComponent(0)).getText() }),
2861 progressPanel.add(cancel, BorderLayout.EAST);
2867 * @return true if any progress bars are still active
2870 public boolean operationInProgress()
2872 if (progressBars != null && progressBars.size() > 0)
2880 * This will return the first AlignFrame holding the given viewport instance. It
2881 * will break if there are more than one AlignFrames viewing a particular av.
2884 * @return alignFrame for viewport
2886 public static AlignFrame getAlignFrameFor(AlignViewportI viewport)
2888 if (desktop != null)
2890 AlignmentPanel[] aps = getAlignmentPanels(
2891 viewport.getSequenceSetId());
2892 for (int panel = 0; aps != null && panel < aps.length; panel++)
2894 if (aps[panel] != null && aps[panel].av == viewport)
2896 return aps[panel].alignFrame;
2903 public VamsasApplication getVamsasApplication()
2910 * flag set if jalview GUI is being operated programmatically
2912 private boolean inBatchMode = false;
2915 * check if jalview GUI is being operated programmatically
2917 * @return inBatchMode
2919 public boolean isInBatchMode()
2925 * set flag if jalview GUI is being operated programmatically
2927 * @param inBatchMode
2929 public void setInBatchMode(boolean inBatchMode)
2931 this.inBatchMode = inBatchMode;
2934 public void startServiceDiscovery()
2936 startServiceDiscovery(false);
2939 public void startServiceDiscovery(boolean blocking)
2941 boolean alive = true;
2942 Thread t0 = null, t1 = null, t2 = null;
2943 // JAL-940 - JALVIEW 1 services are now being EOLed as of JABA 2.1 release
2946 // todo: changesupport handlers need to be transferred
2947 if (discoverer == null)
2949 discoverer = new jalview.ws.jws1.Discoverer();
2950 // register PCS handler for desktop.
2951 discoverer.addPropertyChangeListener(changeSupport);
2953 // JAL-940 - disabled JWS1 service configuration - always start discoverer
2954 // until we phase out completely
2955 (t0 = new Thread(discoverer)).start();
2958 if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
2960 t2 = jalview.ws.jws2.Jws2Discoverer.getDiscoverer()
2961 .startDiscoverer(changeSupport);
2965 // TODO: do rest service discovery
2974 } catch (Exception e)
2977 alive = (t1 != null && t1.isAlive()) || (t2 != null && t2.isAlive())
2978 || (t3 != null && t3.isAlive())
2979 || (t0 != null && t0.isAlive());
2985 * called to check if the service discovery process completed successfully.
2989 protected void JalviewServicesChanged(PropertyChangeEvent evt)
2991 if (evt.getNewValue() == null || evt.getNewValue() instanceof Vector)
2993 final String ermsg = jalview.ws.jws2.Jws2Discoverer.getDiscoverer()
2994 .getErrorMessages();
2997 if (Cache.getDefault("SHOW_WSDISCOVERY_ERRORS", true))
2999 if (serviceChangedDialog == null)
3001 // only run if we aren't already displaying one of these.
3002 addDialogThread(serviceChangedDialog = new Runnable()
3009 * JalviewDialog jd =new JalviewDialog() {
3011 * @Override protected void cancelPressed() { // TODO
3012 * Auto-generated method stub
3014 * }@Override protected void okPressed() { // TODO
3015 * Auto-generated method stub
3017 * }@Override protected void raiseClosed() { // TODO
3018 * Auto-generated method stub
3020 * } }; jd.initDialogFrame(new
3021 * JLabel("<html><table width=\"450\"><tr><td>" + ermsg +
3022 * "<br/>It may be that you have invalid JABA URLs in your web service preferences,"
3023 * + " or mis-configured HTTP proxy settings.<br/>" +
3024 * "Check the <em>Connections</em> and <em>Web services</em> tab of the"
3026 * " Tools->Preferences dialog box to change them.</td></tr></table></html>"
3027 * ), true, true, "Web Service Configuration Problem", 450,
3030 * jd.waitForInput();
3032 JvOptionPane.showConfirmDialog(Desktop.desktop,
3033 new JLabel("<html><table width=\"450\"><tr><td>"
3034 + ermsg + "</td></tr></table>"
3035 + "<p>It may be that you have invalid JABA URLs<br/>in your web service preferences,"
3036 + "<br>or as a command-line argument, or mis-configured HTTP proxy settings.</p>"
3037 + "<p>Check the <em>Connections</em> and <em>Web services</em> tab<br/>of the"
3038 + " Tools->Preferences dialog box to change them.</p></html>"),
3039 "Web Service Configuration Problem",
3040 JvOptionPane.DEFAULT_OPTION,
3041 JvOptionPane.ERROR_MESSAGE);
3042 serviceChangedDialog = null;
3051 "Errors reported by JABA discovery service. Check web services preferences.\n"
3058 private Runnable serviceChangedDialog = null;
3061 * start a thread to open a URL in the configured browser. Pops up a warning
3062 * dialog to the user if there is an exception when calling out to the browser
3067 public static void showUrl(final String url)
3069 showUrl(url, Desktop.instance);
3073 * Like showUrl but allows progress handler to be specified
3077 * (null) or object implementing IProgressIndicator
3079 public static void showUrl(final String url,
3080 final IProgressIndicator progress)
3082 new Thread(new Runnable()
3089 if (progress != null)
3091 progress.setProgressBar(MessageManager
3092 .formatMessage("status.opening_params", new Object[]
3093 { url }), this.hashCode());
3095 jalview.util.BrowserLauncher.openURL(url);
3096 } catch (Exception ex)
3098 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
3100 .getString("label.web_browser_not_found_unix"),
3101 MessageManager.getString("label.web_browser_not_found"),
3102 JvOptionPane.WARNING_MESSAGE);
3104 ex.printStackTrace();
3106 if (progress != null)
3108 progress.setProgressBar(null, this.hashCode());
3114 public static WsParamSetManager wsparamManager = null;
3116 public static ParamManager getUserParameterStore()
3118 if (wsparamManager == null)
3120 wsparamManager = new WsParamSetManager();
3122 return wsparamManager;
3126 * static hyperlink handler proxy method for use by Jalview's internal windows
3130 public static void hyperlinkUpdate(HyperlinkEvent e)
3132 if (e.getEventType() == EventType.ACTIVATED)
3137 url = e.getURL().toString();
3138 Desktop.showUrl(url);
3139 } catch (Exception x)
3143 if (Cache.log != null)
3145 Cache.log.error("Couldn't handle string " + url + " as a URL.");
3150 "Couldn't handle string " + url + " as a URL.");
3153 // ignore any exceptions due to dud links.
3160 * single thread that handles display of dialogs to user.
3162 ExecutorService dialogExecutor = Executors.newSingleThreadExecutor();
3165 * flag indicating if dialogExecutor should try to acquire a permit
3167 private volatile boolean dialogPause = true;
3172 private java.util.concurrent.Semaphore block = new Semaphore(0);
3174 private static groovy.ui.Console groovyConsole;
3177 * add another dialog thread to the queue
3181 public void addDialogThread(final Runnable prompter)
3183 dialogExecutor.submit(new Runnable()
3193 } catch (InterruptedException x)
3198 if (instance == null)
3204 SwingUtilities.invokeAndWait(prompter);
3205 } catch (Exception q)
3207 Cache.log.warn("Unexpected Exception in dialog thread.", q);
3213 public void startDialogQueue()
3215 // set the flag so we don't pause waiting for another permit and semaphore
3216 // the current task to begin
3217 dialogPause = false;
3222 * Outputs an image of the desktop to file in EPS format, after prompting the
3223 * user for choice of Text or Lineart character rendering (unless a preference
3224 * has been set). The file name is generated as
3227 * Jalview_snapshot_nnnnn.eps where nnnnn is the current timestamp in milliseconds
3231 protected void snapShotWindow_actionPerformed(ActionEvent e)
3233 // currently the menu option to do this is not shown
3236 int width = getWidth();
3237 int height = getHeight();
3239 "Jalview_snapshot_" + System.currentTimeMillis() + ".eps");
3240 ImageWriterI writer = new ImageWriterI()
3243 public void exportImage(Graphics g) throws Exception
3246 Cache.log.info("Successfully written snapshot to file "
3247 + of.getAbsolutePath());
3250 String title = "View of desktop";
3251 ImageExporter exporter = new ImageExporter(writer, null, TYPE.EPS,
3253 exporter.doExport(of, this, width, height, title);
3257 * Explode the views in the given SplitFrame into separate SplitFrame windows.
3258 * This respects (remembers) any previous 'exploded geometry' i.e. the size and
3259 * location last time the view was expanded (if any). However it does not
3260 * remember the split pane divider location - this is set to match the
3261 * 'exploding' frame.
3265 public void explodeViews(SplitFrame sf)
3267 AlignFrame oldTopFrame = (AlignFrame) sf.getTopFrame();
3268 AlignFrame oldBottomFrame = (AlignFrame) sf.getBottomFrame();
3269 List<? extends AlignmentViewPanel> topPanels = oldTopFrame
3271 List<? extends AlignmentViewPanel> bottomPanels = oldBottomFrame
3273 int viewCount = topPanels.size();
3280 * Processing in reverse order works, forwards order leaves the first panels
3281 * not visible. I don't know why!
3283 for (int i = viewCount - 1; i >= 0; i--)
3286 * Make new top and bottom frames. These take over the respective
3287 * AlignmentPanel objects, including their AlignmentViewports, so the
3288 * cdna/protein relationships between the viewports is carried over to the
3291 * explodedGeometry holds the (x, y) position of the previously exploded
3292 * SplitFrame, and the (width, height) of the AlignFrame component
3294 AlignmentPanel topPanel = (AlignmentPanel) topPanels.get(i);
3295 AlignFrame newTopFrame = new AlignFrame(topPanel);
3296 newTopFrame.setSize(oldTopFrame.getSize());
3297 newTopFrame.setVisible(true);
3298 Rectangle geometry = ((AlignViewport) topPanel.getAlignViewport())
3299 .getExplodedGeometry();
3300 if (geometry != null)
3302 newTopFrame.setSize(geometry.getSize());
3305 AlignmentPanel bottomPanel = (AlignmentPanel) bottomPanels.get(i);
3306 AlignFrame newBottomFrame = new AlignFrame(bottomPanel);
3307 newBottomFrame.setSize(oldBottomFrame.getSize());
3308 newBottomFrame.setVisible(true);
3309 geometry = ((AlignViewport) bottomPanel.getAlignViewport())
3310 .getExplodedGeometry();
3311 if (geometry != null)
3313 newBottomFrame.setSize(geometry.getSize());
3316 topPanel.av.setGatherViewsHere(false);
3317 bottomPanel.av.setGatherViewsHere(false);
3318 JInternalFrame splitFrame = new SplitFrame(newTopFrame,
3320 if (geometry != null)
3322 splitFrame.setLocation(geometry.getLocation());
3324 Desktop.addInternalFrame(splitFrame, sf.getTitle(), -1, -1);
3328 * Clear references to the panels (now relocated in the new SplitFrames)
3329 * before closing the old SplitFrame.
3332 bottomPanels.clear();
3337 * Gather expanded split frames, sharing the same pairs of sequence set ids,
3338 * back into the given SplitFrame as additional views. Note that the gathered
3339 * frames may themselves have multiple views.
3343 public void gatherViews(GSplitFrame source)
3346 * special handling of explodedGeometry for a view within a SplitFrame: - it
3347 * holds the (x, y) position of the enclosing SplitFrame, and the (width,
3348 * height) of the AlignFrame component
3350 AlignFrame myTopFrame = (AlignFrame) source.getTopFrame();
3351 AlignFrame myBottomFrame = (AlignFrame) source.getBottomFrame();
3352 myTopFrame.viewport.setExplodedGeometry(new Rectangle(source.getX(),
3353 source.getY(), myTopFrame.getWidth(), myTopFrame.getHeight()));
3354 myBottomFrame.viewport
3355 .setExplodedGeometry(new Rectangle(source.getX(), source.getY(),
3356 myBottomFrame.getWidth(), myBottomFrame.getHeight()));
3357 myTopFrame.viewport.setGatherViewsHere(true);
3358 myBottomFrame.viewport.setGatherViewsHere(true);
3359 String topViewId = myTopFrame.viewport.getSequenceSetId();
3360 String bottomViewId = myBottomFrame.viewport.getSequenceSetId();
3362 JInternalFrame[] frames = desktop.getAllFrames();
3363 for (JInternalFrame frame : frames)
3365 if (frame instanceof SplitFrame && frame != source)
3367 SplitFrame sf = (SplitFrame) frame;
3368 AlignFrame topFrame = (AlignFrame) sf.getTopFrame();
3369 AlignFrame bottomFrame = (AlignFrame) sf.getBottomFrame();
3370 boolean gatherThis = false;
3371 for (int a = 0; a < topFrame.alignPanels.size(); a++)
3373 AlignmentPanel topPanel = topFrame.alignPanels.get(a);
3374 AlignmentPanel bottomPanel = bottomFrame.alignPanels.get(a);
3375 if (topViewId.equals(topPanel.av.getSequenceSetId())
3376 && bottomViewId.equals(bottomPanel.av.getSequenceSetId()))
3379 topPanel.av.setGatherViewsHere(false);
3380 bottomPanel.av.setGatherViewsHere(false);
3381 topPanel.av.setExplodedGeometry(
3382 new Rectangle(sf.getLocation(), topFrame.getSize()));
3383 bottomPanel.av.setExplodedGeometry(
3384 new Rectangle(sf.getLocation(), bottomFrame.getSize()));
3385 myTopFrame.addAlignmentPanel(topPanel, false);
3386 myBottomFrame.addAlignmentPanel(bottomPanel, false);
3392 topFrame.getAlignPanels().clear();
3393 bottomFrame.getAlignPanels().clear();
3400 * The dust settles...give focus to the tab we did this from.
3402 myTopFrame.setDisplayedView(myTopFrame.alignPanel);
3405 public static groovy.ui.Console getGroovyConsole()
3407 return groovyConsole;
3411 * handles the payload of a drag and drop event.
3413 * TODO refactor to desktop utilities class
3416 * - Data source strings extracted from the drop event
3418 * - protocol for each data source extracted from the drop event
3422 * - the payload from the drop event
3425 public static void transferFromDropTarget(List<Object> files,
3426 List<DataSourceType> protocols, DropTargetDropEvent evt,
3427 Transferable t) throws Exception
3430 // BH 2018 changed List<String> to List<Object> to allow for File from SwingJS
3432 // DataFlavor[] flavors = t.getTransferDataFlavors();
3433 // for (int i = 0; i < flavors.length; i++) {
3434 // if (flavors[i].isFlavorJavaFileListType()) {
3435 // evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
3436 // List<File> list = (List<File>) t.getTransferData(flavors[i]);
3437 // for (int j = 0; j < list.size(); j++) {
3438 // File file = (File) list.get(j);
3439 // byte[] data = getDroppedFileBytes(file);
3440 // fileName.setText(file.getName() + " - " + data.length + " " +
3441 // evt.getLocation());
3442 // JTextArea target = (JTextArea) ((DropTarget) evt.getSource()).getComponent();
3443 // target.setText(new String(data));
3445 // dtde.dropComplete(true);
3450 DataFlavor uriListFlavor = new DataFlavor(
3451 "text/uri-list;class=java.lang.String"), urlFlavour = null;
3454 urlFlavour = new DataFlavor(
3455 "application/x-java-url; class=java.net.URL");
3456 } catch (ClassNotFoundException cfe)
3458 Cache.log.debug("Couldn't instantiate the URL dataflavor.", cfe);
3461 if (urlFlavour != null && t.isDataFlavorSupported(urlFlavour))
3466 java.net.URL url = (URL) t.getTransferData(urlFlavour);
3467 // nb: java 8 osx bug https://bugs.openjdk.java.net/browse/JDK-8156099
3468 // means url may be null.
3471 protocols.add(DataSourceType.URL);
3472 files.add(url.toString());
3473 Cache.log.debug("Drop handled as URL dataflavor "
3474 + files.get(files.size() - 1));
3479 if (Platform.isAMacAndNotJS())
3482 "Please ignore plist error - occurs due to problem with java 8 on OSX");
3486 } catch (Throwable ex)
3488 Cache.log.debug("URL drop handler failed.", ex);
3491 if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
3493 // Works on Windows and MacOSX
3494 Cache.log.debug("Drop handled as javaFileListFlavor");
3495 for (Object file : (List) t
3496 .getTransferData(DataFlavor.javaFileListFlavor))
3499 protocols.add(DataSourceType.FILE);
3504 // Unix like behaviour
3505 boolean added = false;
3507 if (t.isDataFlavorSupported(uriListFlavor))
3509 Cache.log.debug("Drop handled as uriListFlavor");
3510 // This is used by Unix drag system
3511 data = (String) t.getTransferData(uriListFlavor);
3515 // fallback to text: workaround - on OSX where there's a JVM bug
3516 Cache.log.debug("standard URIListFlavor failed. Trying text");
3517 // try text fallback
3518 DataFlavor textDf = new DataFlavor(
3519 "text/plain;class=java.lang.String");
3520 if (t.isDataFlavorSupported(textDf))
3522 data = (String) t.getTransferData(textDf);
3525 Cache.log.debug("Plain text drop content returned "
3526 + (data == null ? "Null - failed" : data));
3531 while (protocols.size() < files.size())
3533 Cache.log.debug("Adding missing FILE protocol for "
3534 + files.get(protocols.size()));
3535 protocols.add(DataSourceType.FILE);
3537 for (java.util.StringTokenizer st = new java.util.StringTokenizer(
3538 data, "\r\n"); st.hasMoreTokens();)
3541 String s = st.nextToken();
3542 if (s.startsWith("#"))
3544 // the line is a comment (as per the RFC 2483)
3547 java.net.URI uri = new java.net.URI(s);
3548 if (uri.getScheme().toLowerCase().startsWith("http"))
3550 protocols.add(DataSourceType.URL);
3551 files.add(uri.toString());
3555 // otherwise preserve old behaviour: catch all for file objects
3556 java.io.File file = new java.io.File(uri);
3557 protocols.add(DataSourceType.FILE);
3558 files.add(file.toString());
3563 if (Cache.log.isDebugEnabled())
3565 if (data == null || !added)
3568 if (t.getTransferDataFlavors() != null
3569 && t.getTransferDataFlavors().length > 0)
3572 "Couldn't resolve drop data. Here are the supported flavors:");
3573 for (DataFlavor fl : t.getTransferDataFlavors())
3576 "Supported transfer dataflavor: " + fl.toString());
3577 Object df = t.getTransferData(fl);
3580 Cache.log.debug("Retrieves: " + df);
3584 Cache.log.debug("Retrieved nothing");
3590 Cache.log.debug("Couldn't resolve dataflavor for drop: "
3596 if (Platform.isWindowsAndNotJS())
3598 Cache.log.debug("Scanning dropped content for Windows Link Files");
3600 // resolve any .lnk files in the file drop
3601 for (int f = 0; f < files.size(); f++)
3603 String source = files.get(f).toString().toLowerCase();
3604 if (protocols.get(f).equals(DataSourceType.FILE)
3605 && (source.endsWith(".lnk") || source.endsWith(".url")
3606 || source.endsWith(".site")))
3610 Object obj = files.get(f);
3611 File lf = (obj instanceof File ? (File) obj
3612 : new File((String) obj));
3613 // process link file to get a URL
3614 Cache.log.debug("Found potential link file: " + lf);
3615 WindowsShortcut wscfile = new WindowsShortcut(lf);
3616 String fullname = wscfile.getRealFilename();
3617 protocols.set(f, FormatAdapter.checkProtocol(fullname));
3618 files.set(f, fullname);
3619 Cache.log.debug("Parsed real filename " + fullname
3620 + " to extract protocol: " + protocols.get(f));
3621 } catch (Exception ex)
3624 "Couldn't parse " + files.get(f) + " as a link file.",
3633 * Sets the Preferences property for experimental features to True or False
3634 * depending on the state of the controlling menu item
3637 protected void showExperimental_actionPerformed(boolean selected)
3639 Cache.setProperty(EXPERIMENTAL_FEATURES, Boolean.toString(selected));
3643 * Answers a (possibly empty) list of any structure viewer frames (currently for
3644 * either Jmol or Chimera) which are currently open. This may optionally be
3645 * restricted to viewers of a specified class, or viewers linked to a specified
3649 * if not null, only return viewers linked to this panel
3650 * @param structureViewerClass
3651 * if not null, only return viewers of this class
3654 public List<StructureViewerBase> getStructureViewers(
3655 AlignmentPanel apanel,
3656 Class<? extends StructureViewerBase> structureViewerClass)
3658 List<StructureViewerBase> result = new ArrayList<>();
3659 JInternalFrame[] frames = Desktop.instance.getAllFrames();
3661 for (JInternalFrame frame : frames)
3663 if (frame instanceof StructureViewerBase)
3665 if (structureViewerClass == null
3666 || structureViewerClass.isInstance(frame))
3669 || ((StructureViewerBase) frame).isLinkedWith(apanel))
3671 result.add((StructureViewerBase) frame);