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.
353 if (!Platform.isJS())
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 = !Platform.isJS();
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(Platform.isJS()); // BH this should not be necessary
887 if (frame.getX() < 1 && frame.getY() < 1)
889 frame.setLocation(xOffset * openFrameCount,
890 yOffset * ((openFrameCount - 1) % 10) + yOffset);
894 * add an entry for the new frame in the Window menu
895 * (and remove it when the frame is closed)
897 final JMenuItem menuItem = new JMenuItem(title);
898 frame.addInternalFrameListener(new InternalFrameAdapter()
901 public void internalFrameActivated(InternalFrameEvent evt)
903 JInternalFrame itf = desktop.getSelectedFrame();
906 if (itf instanceof AlignFrame)
908 Jalview.setCurrentAlignFrame((AlignFrame) itf);
915 public void internalFrameClosed(InternalFrameEvent evt)
917 PaintRefresher.RemoveComponent(frame);
920 * defensive check to prevent frames being
921 * added half off the window
923 if (openFrameCount > 0)
929 * ensure no reference to alignFrame retained by menu item listener
931 if (menuItem.getActionListeners().length > 0)
933 menuItem.removeActionListener(menuItem.getActionListeners()[0]);
935 windowMenu.remove(menuItem);
939 menuItem.addActionListener(new ActionListener()
942 public void actionPerformed(ActionEvent e)
946 frame.setSelected(true);
947 frame.setIcon(false);
948 } catch (java.beans.PropertyVetoException ex)
950 // System.err.println(ex.toString());
955 setKeyBindings(frame);
959 windowMenu.add(menuItem);
964 frame.setSelected(true);
965 frame.requestFocus();
966 } catch (java.beans.PropertyVetoException ve)
968 } catch (java.lang.ClassCastException cex)
971 "Squashed a possible GUI implementation error. If you can recreate this, please look at http://issues.jalview.org/browse/JAL-869",
977 * Add key bindings to a JInternalFrame so that Ctrl-W and Cmd-W will close the
982 private static void setKeyBindings(JInternalFrame frame)
984 @SuppressWarnings("serial")
985 final Action closeAction = new AbstractAction()
988 public void actionPerformed(ActionEvent e)
995 * set up key bindings for Ctrl-W and Cmd-W, with the same (Close) action
997 KeyStroke ctrlWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
998 InputEvent.CTRL_DOWN_MASK);
999 KeyStroke cmdWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
1000 Toolkit.getDefaultToolkit().getMenuShortcutKeyMask());
1002 InputMap inputMap = frame
1003 .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
1004 String ctrlW = ctrlWKey.toString();
1005 inputMap.put(ctrlWKey, ctrlW);
1006 inputMap.put(cmdWKey, ctrlW);
1008 ActionMap actionMap = frame.getActionMap();
1009 actionMap.put(ctrlW, closeAction);
1013 public void lostOwnership(Clipboard clipboard, Transferable contents)
1017 Desktop.jalviewClipboard = null;
1020 internalCopy = false;
1024 public void dragEnter(DropTargetDragEvent evt)
1029 public void dragExit(DropTargetEvent evt)
1034 public void dragOver(DropTargetDragEvent evt)
1039 public void dropActionChanged(DropTargetDragEvent evt)
1050 public void drop(DropTargetDropEvent evt)
1052 boolean success = true;
1053 // JAL-1552 - acceptDrop required before getTransferable call for
1054 // Java's Transferable for native dnd
1055 evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
1056 Transferable t = evt.getTransferable();
1057 List<Object> files = new ArrayList<>();
1058 List<DataSourceType> protocols = new ArrayList<>();
1062 Desktop.transferFromDropTarget(files, protocols, evt, t);
1063 } catch (Exception e)
1065 e.printStackTrace();
1073 for (int i = 0; i < files.size(); i++)
1075 // BH 2018 File or String
1076 Object file = files.get(i);
1077 String fileName = file.toString();
1078 DataSourceType protocol = (protocols == null)
1079 ? DataSourceType.FILE
1081 FileFormatI format = null;
1083 if (fileName.endsWith(".jar"))
1085 format = FileFormat.Jalview;
1090 format = new IdentifyFile().identify(file, protocol);
1093 new FileLoader().LoadFile(null, file, protocol, format);
1096 } catch (Exception ex)
1101 evt.dropComplete(success); // need this to ensure input focus is properly
1102 // transfered to any new windows created
1112 public void inputLocalFileMenuItem_actionPerformed(AlignViewport viewport)
1114 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
1115 JalviewFileChooser chooser = JalviewFileChooser
1116 .forRead(Cache.getProperty("LAST_DIRECTORY"), fileFormat);
1118 chooser.setFileView(new JalviewFileView());
1119 chooser.setDialogTitle(
1120 MessageManager.getString("label.open_local_file"));
1121 chooser.setToolTipText(MessageManager.getString("action.open"));
1123 chooser.setResponseHandler(0, new Runnable()
1128 File selectedFile = chooser.getSelectedFile();
1129 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1131 FileFormatI format = chooser.getSelectedFormat();
1134 * Call IdentifyFile to verify the file contains what its extension implies.
1135 * Skip this step for dynamically added file formats, because
1136 * IdentifyFile does not know how to recognise them.
1138 if (FileFormats.getInstance().isIdentifiable(format))
1142 format = new IdentifyFile().identify(selectedFile,
1143 DataSourceType.FILE);
1144 } catch (FileFormatException e)
1146 // format = null; //??
1150 new FileLoader().LoadFile(viewport, selectedFile,
1151 DataSourceType.FILE, format);
1154 chooser.showOpenDialog(this);
1158 * Shows a dialog for input of a URL at which to retrieve alignment data
1163 public void inputURLMenuItem_actionPerformed(AlignViewport viewport)
1165 // This construct allows us to have a wider textfield
1167 JLabel label = new JLabel(
1168 MessageManager.getString("label.input_file_url"));
1170 JPanel panel = new JPanel(new GridLayout(2, 1));
1174 * the URL to fetch is
1175 * Java: an editable combobox with history
1176 * JS: (pending JAL-3038) a plain text field
1179 String urlBase = "http://www.";
1180 if (Platform.isJS())
1182 history = new JTextField(urlBase, 35);
1186 JComboBox<String> asCombo = new JComboBox<>();
1187 asCombo.setPreferredSize(new Dimension(400, 20));
1188 asCombo.setEditable(true);
1189 asCombo.addItem(urlBase);
1190 String historyItems = Cache.getProperty("RECENT_URL");
1191 if (historyItems != null)
1193 for (String token : historyItems.split("\\t"))
1195 asCombo.addItem(token);
1202 Object[] options = new Object[] { MessageManager.getString("action.ok"),
1203 MessageManager.getString("action.cancel") };
1204 Runnable action = new Runnable() {
1208 String url = Platform.isJS() ? ((JTextField) history).getText()
1209 : ((JComboBox<String>) history).getSelectedItem()
1212 if (url.toLowerCase().endsWith(".jar"))
1214 if (viewport != null)
1216 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1217 FileFormat.Jalview);
1221 new FileLoader().LoadFile(url, DataSourceType.URL,
1222 FileFormat.Jalview);
1227 FileFormatI format = null;
1230 format = new IdentifyFile().identify(url, DataSourceType.URL);
1231 } catch (FileFormatException e)
1233 // TODO revise error handling, distinguish between
1234 // URL not found and response not valid
1239 String msg = MessageManager.formatMessage("label.couldnt_locate", url);
1240 JvOptionPane.showInternalMessageDialog(Desktop.desktop, msg,
1241 MessageManager.getString("label.url_not_found"),
1242 JvOptionPane.WARNING_MESSAGE);
1247 if (viewport != null)
1249 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1254 new FileLoader().LoadFile(url, DataSourceType.URL, format);
1258 String dialogOption = MessageManager
1259 .getString("label.input_alignment_from_url");
1260 JvOptionPane.newOptionDialog(desktop).setResponseHandler(0, action)
1261 .showInternalDialog(panel, dialogOption,
1262 JvOptionPane.YES_NO_CANCEL_OPTION,
1263 JvOptionPane.PLAIN_MESSAGE, null, options,
1264 MessageManager.getString("action.ok"));
1268 * Opens the CutAndPaste window for the user to paste an alignment in to
1271 * - if not null, the pasted alignment is added to the current
1272 * alignment; if null, to a new alignment window
1275 public void inputTextboxMenuItem_actionPerformed(
1276 AlignmentViewPanel viewPanel)
1278 CutAndPasteTransfer cap = new CutAndPasteTransfer();
1279 cap.setForInput(viewPanel);
1280 Desktop.addInternalFrame(cap,
1281 MessageManager.getString("label.cut_paste_alignmen_file"), true,
1291 Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
1292 jalview.bin.Cache.setProperty("SCREENGEOMETRY_WIDTH",
1294 jalview.bin.Cache.setProperty("SCREENGEOMETRY_HEIGHT",
1295 screen.height + "");
1296 storeLastKnownDimensions("", new Rectangle(getBounds().x, getBounds().y,
1297 getWidth(), getHeight()));
1299 if (jconsole != null)
1301 storeLastKnownDimensions("JAVA_CONSOLE_", jconsole.getBounds());
1302 jconsole.stopConsole();
1306 storeLastKnownDimensions("JALVIEW_RSS_WINDOW_", jvnews.getBounds());
1309 if (dialogExecutor != null)
1311 dialogExecutor.shutdownNow();
1313 closeAll_actionPerformed(null);
1315 if (groovyConsole != null)
1317 // suppress a possible repeat prompt to save script
1318 groovyConsole.setDirty(false);
1319 groovyConsole.exit();
1324 private void storeLastKnownDimensions(String string, Rectangle jc)
1326 jalview.bin.Cache.log.debug("Storing last known dimensions for "
1327 + string + ": x:" + jc.x + " y:" + jc.y + " width:" + jc.width
1328 + " height:" + jc.height);
1330 jalview.bin.Cache.setProperty(string + "SCREEN_X", jc.x + "");
1331 jalview.bin.Cache.setProperty(string + "SCREEN_Y", jc.y + "");
1332 jalview.bin.Cache.setProperty(string + "SCREEN_WIDTH", jc.width + "");
1333 jalview.bin.Cache.setProperty(string + "SCREEN_HEIGHT", jc.height + "");
1343 public void aboutMenuItem_actionPerformed(ActionEvent e)
1345 // StringBuffer message = getAboutMessage(false);
1346 // JvOptionPane.showInternalMessageDialog(Desktop.desktop,
1348 // message.toString(), "About Jalview", JvOptionPane.INFORMATION_MESSAGE);
1349 new Thread(new Runnable()
1354 new SplashScreen(true);
1359 public StringBuffer getAboutMessage(boolean shortv)
1361 StringBuffer message = new StringBuffer();
1362 message.append("<html>");
1365 message.append("<h1><strong>Version: "
1366 + jalview.bin.Cache.getProperty("VERSION")
1367 + "</strong></h1>");
1368 message.append("<strong>Last Updated: <em>"
1369 + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown")
1370 + "</em></strong>");
1376 message.append("<strong>Version "
1377 + jalview.bin.Cache.getProperty("VERSION")
1378 + "; last updated: "
1379 + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown"));
1382 if (jalview.bin.Cache.getDefault("LATEST_VERSION", "Checking")
1383 .equals("Checking"))
1385 message.append("<br>...Checking latest version...</br>");
1387 else if (!jalview.bin.Cache.getDefault("LATEST_VERSION", "Checking")
1388 .equals(jalview.bin.Cache.getProperty("VERSION")))
1390 boolean red = false;
1391 if (jalview.bin.Cache.getProperty("VERSION").toLowerCase()
1392 .indexOf("automated build") == -1)
1395 // Displayed when code version and jnlp version do not match and code
1396 // version is not a development build
1397 message.append("<div style=\"color: #FF0000;font-style: bold;\">");
1400 message.append("<br>!! Version "
1401 + jalview.bin.Cache.getDefault("LATEST_VERSION",
1403 + " is available for download from "
1404 + jalview.bin.Cache.getDefault("www.jalview.org",
1405 "http://www.jalview.org")
1409 message.append("</div>");
1412 message.append("<br>Authors: " + jalview.bin.Cache.getDefault(
1414 "The Jalview Authors (See AUTHORS file for current list)")
1415 + "<br><br>Development managed by The Barton Group, University of Dundee, Scotland, UK.<br>"
1416 + "<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"
1417 + "<br><br>If you use Jalview, please cite:"
1418 + "<br>Waterhouse, A.M., Procter, J.B., Martin, D.M.A, Clamp, M. and Barton, G. J. (2009)"
1419 + "<br>Jalview Version 2 - a multiple sequence alignment editor and analysis workbench"
1420 + "<br>Bioinformatics doi: 10.1093/bioinformatics/btp033"
1426 * Action on requesting Help documentation
1429 public void documentationMenuItem_actionPerformed()
1433 if (Platform.isJS())
1435 BrowserLauncher.openURL("http://www.jalview.org/help.html");
1439 Help.showHelpWindow();
1441 } catch (Exception ex)
1443 System.err.println("Error opening help: " + ex.getMessage());
1448 public void closeAll_actionPerformed(ActionEvent e)
1450 // TODO show a progress bar while closing?
1451 JInternalFrame[] frames = desktop.getAllFrames();
1452 for (int i = 0; i < frames.length; i++)
1456 frames[i].setClosed(true);
1457 } catch (java.beans.PropertyVetoException ex)
1461 Jalview.setCurrentAlignFrame(null);
1462 System.out.println("ALL CLOSED");
1463 if (v_client != null)
1465 // TODO clear binding to vamsas document objects on close_all
1469 * reset state of singleton objects as appropriate (clear down session state
1470 * when all windows are closed)
1472 StructureSelectionManager ssm = StructureSelectionManager
1473 .getStructureSelectionManager(this);
1481 public void raiseRelated_actionPerformed(ActionEvent e)
1483 reorderAssociatedWindows(false, false);
1487 public void minimizeAssociated_actionPerformed(ActionEvent e)
1489 reorderAssociatedWindows(true, false);
1492 void closeAssociatedWindows()
1494 reorderAssociatedWindows(false, true);
1500 * @seejalview.jbgui.GDesktop#garbageCollect_actionPerformed(java.awt.event.
1504 protected void garbageCollect_actionPerformed(ActionEvent e)
1506 // We simply collect the garbage
1507 jalview.bin.Cache.log.debug("Collecting garbage...");
1509 jalview.bin.Cache.log.debug("Finished garbage collection.");
1516 * jalview.jbgui.GDesktop#showMemusage_actionPerformed(java.awt.event.ActionEvent
1520 protected void showMemusage_actionPerformed(ActionEvent e)
1522 desktop.showMemoryUsage(showMemusage.isSelected());
1529 * jalview.jbgui.GDesktop#showConsole_actionPerformed(java.awt.event.ActionEvent
1533 protected void showConsole_actionPerformed(ActionEvent e)
1535 showConsole(showConsole.isSelected());
1538 Console jconsole = null;
1541 * control whether the java console is visible or not
1545 void showConsole(boolean selected)
1547 // TODO: decide if we should update properties file
1548 if (jconsole != null) // BH 2018
1550 showConsole.setSelected(selected);
1551 Cache.setProperty("SHOW_JAVA_CONSOLE",
1552 Boolean.valueOf(selected).toString());
1553 jconsole.setVisible(selected);
1557 void reorderAssociatedWindows(boolean minimize, boolean close)
1559 JInternalFrame[] frames = desktop.getAllFrames();
1560 if (frames == null || frames.length < 1)
1565 AlignmentViewport source = null, target = null;
1566 if (frames[0] instanceof AlignFrame)
1568 source = ((AlignFrame) frames[0]).getCurrentView();
1570 else if (frames[0] instanceof TreePanel)
1572 source = ((TreePanel) frames[0]).getViewPort();
1574 else if (frames[0] instanceof PCAPanel)
1576 source = ((PCAPanel) frames[0]).av;
1578 else if (frames[0].getContentPane() instanceof PairwiseAlignPanel)
1580 source = ((PairwiseAlignPanel) frames[0].getContentPane()).av;
1585 for (int i = 0; i < frames.length; i++)
1588 if (frames[i] == null)
1592 if (frames[i] instanceof AlignFrame)
1594 target = ((AlignFrame) frames[i]).getCurrentView();
1596 else if (frames[i] instanceof TreePanel)
1598 target = ((TreePanel) frames[i]).getViewPort();
1600 else if (frames[i] instanceof PCAPanel)
1602 target = ((PCAPanel) frames[i]).av;
1604 else if (frames[i].getContentPane() instanceof PairwiseAlignPanel)
1606 target = ((PairwiseAlignPanel) frames[i].getContentPane()).av;
1609 if (source == target)
1615 frames[i].setClosed(true);
1619 frames[i].setIcon(minimize);
1622 frames[i].toFront();
1626 } catch (java.beans.PropertyVetoException ex)
1641 protected void preferences_actionPerformed(ActionEvent e)
1647 * Prompts the user to choose a file and then saves the Jalview state as a
1648 * Jalview project file
1651 public void saveState_actionPerformed(boolean asCastor)
1653 JalviewFileChooser chooser = new JalviewFileChooser(
1654 asCastor ? "jvp" : "jvx",
1657 chooser.setFileView(new JalviewFileView());
1658 chooser.setDialogTitle(MessageManager.getString("label.save_state"));
1659 int option = chooser.showSaveDialog(this);
1660 if (option == JalviewFileChooser.APPROVE_OPTION)
1662 File choice = chooser.getSelectedFile();
1663 setProjectFile(choice);
1665 new Thread(new Runnable()
1670 // TODO: refactor to Jalview desktop session controller action.
1671 setProgressBar(MessageManager.formatMessage(
1672 "label.saving_jalview_project", new Object[]
1673 { choice.getName() }), choice.hashCode());
1674 jalview.bin.Cache.setProperty("LAST_DIRECTORY",
1675 choice.getParent());
1676 // TODO catch and handle errors for savestate
1677 // TODO prevent user from messing with the Desktop whilst we're saving
1682 new Jalview2XML().saveState(choice);
1686 new jalview.project.Jalview2XML().saveState(choice);
1688 } catch (OutOfMemoryError oom)
1691 "Whilst saving current state to " + choice.getName(),
1693 } catch (Exception ex)
1696 "Problems whilst trying to save to " + choice.getName(),
1698 JvOptionPane.showMessageDialog(Desktop.this,
1699 MessageManager.formatMessage(
1700 "label.error_whilst_saving_current_state_to",
1702 { choice.getName() }),
1703 MessageManager.getString("label.couldnt_save_project"),
1704 JvOptionPane.WARNING_MESSAGE);
1706 setProgressBar(null, choice.hashCode());
1712 void setProjectFile(File choice)
1714 this.projectFile = choice;
1717 public File getProjectFile()
1719 return this.projectFile;
1723 * Prompts the user to choose a file and loads in as a Jalview project file
1726 public void loadState_actionPerformed(boolean asCastor)
1728 // TODO: GET RID OF .JVX BEFORE RELEASE JIM!
1729 final String[] suffix = asCastor ? new String[] { "jvp", "jar" }
1732 final String[] desc = asCastor
1734 { "Jalview Project", "Jalview Project (old)" }
1736 { "Jalview Project" };
1737 JalviewFileChooser chooser = new JalviewFileChooser(
1738 Cache.getProperty("LAST_DIRECTORY"), suffix,
1741 chooser.setFileView(new JalviewFileView());
1742 chooser.setDialogTitle(MessageManager.getString("label.restore_state"));
1743 chooser.setResponseHandler(0, new Runnable()
1748 File selectedFile = chooser.getSelectedFile();
1749 setProjectFile(selectedFile);
1750 String choice = selectedFile.getAbsolutePath();
1751 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1752 new Thread(new Runnable()
1760 new Jalview2XML().loadJalviewAlign(choice);
1764 new jalview.project.Jalview2XML().loadJalviewAlign(selectedFile);
1766 } catch (OutOfMemoryError oom)
1768 new OOMWarning("Whilst loading project from " + choice, oom);
1769 } catch (Exception ex)
1772 "Problems whilst loading project from " + choice, ex);
1773 JvOptionPane.showMessageDialog(Desktop.desktop,
1774 MessageManager.formatMessage(
1775 "label.error_whilst_loading_project_from",
1778 MessageManager.getString("label.couldnt_load_project"),
1779 JvOptionPane.WARNING_MESSAGE);
1786 chooser.showOpenDialog(this);
1790 public void inputSequence_actionPerformed(ActionEvent e)
1792 new SequenceFetcher(this);
1795 JPanel progressPanel;
1797 ArrayList<JPanel> fileLoadingPanels = new ArrayList<>();
1799 public void startLoading(final Object fileName)
1801 if (fileLoadingCount == 0)
1803 fileLoadingPanels.add(addProgressPanel(MessageManager
1804 .formatMessage("label.loading_file", new Object[]
1810 private JPanel addProgressPanel(String string)
1812 if (progressPanel == null)
1814 progressPanel = new JPanel(new GridLayout(1, 1));
1815 totalProgressCount = 0;
1816 instance.getContentPane().add(progressPanel, BorderLayout.SOUTH);
1818 JPanel thisprogress = new JPanel(new BorderLayout(10, 5));
1819 JProgressBar progressBar = new JProgressBar();
1820 progressBar.setIndeterminate(true);
1822 thisprogress.add(new JLabel(string), BorderLayout.WEST);
1824 thisprogress.add(progressBar, BorderLayout.CENTER);
1825 progressPanel.add(thisprogress);
1826 ((GridLayout) progressPanel.getLayout()).setRows(
1827 ((GridLayout) progressPanel.getLayout()).getRows() + 1);
1828 ++totalProgressCount;
1829 instance.validate();
1830 return thisprogress;
1833 int totalProgressCount = 0;
1835 private void removeProgressPanel(JPanel progbar)
1837 if (progressPanel != null)
1839 synchronized (progressPanel)
1841 progressPanel.remove(progbar);
1842 GridLayout gl = (GridLayout) progressPanel.getLayout();
1843 gl.setRows(gl.getRows() - 1);
1844 if (--totalProgressCount < 1)
1846 this.getContentPane().remove(progressPanel);
1847 progressPanel = null;
1854 public void stopLoading()
1857 if (fileLoadingCount < 1)
1859 while (fileLoadingPanels.size() > 0)
1861 removeProgressPanel(fileLoadingPanels.remove(0));
1863 fileLoadingPanels.clear();
1864 fileLoadingCount = 0;
1869 public static int getViewCount(String alignmentId)
1871 AlignmentViewport[] aps = getViewports(alignmentId);
1872 return (aps == null) ? 0 : aps.length;
1877 * @param alignmentId
1878 * - if null, all sets are returned
1879 * @return all AlignmentPanels concerning the alignmentId sequence set
1881 public static AlignmentPanel[] getAlignmentPanels(String alignmentId)
1883 if (Desktop.desktop == null)
1885 // no frames created and in headless mode
1886 // TODO: verify that frames are recoverable when in headless mode
1889 List<AlignmentPanel> aps = new ArrayList<>();
1890 AlignFrame[] frames = getAlignFrames();
1895 for (AlignFrame af : frames)
1897 for (AlignmentPanel ap : af.alignPanels)
1899 if (alignmentId == null
1900 || alignmentId.equals(ap.av.getSequenceSetId()))
1906 if (aps.size() == 0)
1910 AlignmentPanel[] vap = aps.toArray(new AlignmentPanel[aps.size()]);
1915 * get all the viewports on an alignment.
1917 * @param sequenceSetId
1918 * unique alignment id (may be null - all viewports returned in that
1920 * @return all viewports on the alignment bound to sequenceSetId
1922 public static AlignmentViewport[] getViewports(String sequenceSetId)
1924 List<AlignmentViewport> viewp = new ArrayList<>();
1925 if (desktop != null)
1927 AlignFrame[] frames = Desktop.getAlignFrames();
1929 for (AlignFrame afr : frames)
1931 if (sequenceSetId == null || afr.getViewport().getSequenceSetId()
1932 .equals(sequenceSetId))
1934 if (afr.alignPanels != null)
1936 for (AlignmentPanel ap : afr.alignPanels)
1938 if (sequenceSetId == null
1939 || sequenceSetId.equals(ap.av.getSequenceSetId()))
1947 viewp.add(afr.getViewport());
1951 if (viewp.size() > 0)
1953 return viewp.toArray(new AlignmentViewport[viewp.size()]);
1960 * Explode the views in the given frame into separate AlignFrame
1964 public static void explodeViews(AlignFrame af)
1966 int size = af.alignPanels.size();
1972 for (int i = 0; i < size; i++)
1974 AlignmentPanel ap = af.alignPanels.get(i);
1975 AlignFrame newaf = new AlignFrame(ap);
1978 * Restore the view's last exploded frame geometry if known. Multiple
1979 * views from one exploded frame share and restore the same (frame)
1980 * position and size.
1982 Rectangle geometry = ap.av.getExplodedGeometry();
1983 if (geometry != null)
1985 newaf.setBounds(geometry);
1988 ap.av.setGatherViewsHere(false);
1990 addInternalFrame(newaf, af.getTitle(), AlignFrame.DEFAULT_WIDTH,
1991 AlignFrame.DEFAULT_HEIGHT);
1994 af.alignPanels.clear();
1995 af.closeMenuItem_actionPerformed(true);
2000 * Gather expanded views (separate AlignFrame's) with the same sequence set
2001 * identifier back in to this frame as additional views, and close the expanded
2002 * views. Note the expanded frames may themselves have multiple views. We take
2007 public void gatherViews(AlignFrame source)
2009 source.viewport.setGatherViewsHere(true);
2010 source.viewport.setExplodedGeometry(source.getBounds());
2011 JInternalFrame[] frames = desktop.getAllFrames();
2012 String viewId = source.viewport.getSequenceSetId();
2014 for (int t = 0; t < frames.length; t++)
2016 if (frames[t] instanceof AlignFrame && frames[t] != source)
2018 AlignFrame af = (AlignFrame) frames[t];
2019 boolean gatherThis = false;
2020 for (int a = 0; a < af.alignPanels.size(); a++)
2022 AlignmentPanel ap = af.alignPanels.get(a);
2023 if (viewId.equals(ap.av.getSequenceSetId()))
2026 ap.av.setGatherViewsHere(false);
2027 ap.av.setExplodedGeometry(af.getBounds());
2028 source.addAlignmentPanel(ap, false);
2034 af.alignPanels.clear();
2035 af.closeMenuItem_actionPerformed(true);
2042 jalview.gui.VamsasApplication v_client = null;
2045 public void vamsasImport_actionPerformed(ActionEvent e)
2047 // TODO: JAL-3048 not needed for Jalview-JS
2049 if (v_client == null)
2051 // Load and try to start a session.
2052 JalviewFileChooser chooser = new JalviewFileChooser(
2053 jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
2055 chooser.setFileView(new JalviewFileView());
2056 chooser.setDialogTitle(
2057 MessageManager.getString("label.open_saved_vamsas_session"));
2058 chooser.setToolTipText(MessageManager.getString(
2059 "label.select_vamsas_session_opened_as_new_vamsas_session"));
2061 int value = chooser.showOpenDialog(this);
2063 if (value == JalviewFileChooser.APPROVE_OPTION)
2065 String fle = chooser.getSelectedFile().toString();
2066 if (!vamsasImport(chooser.getSelectedFile()))
2068 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
2069 MessageManager.formatMessage(
2070 "label.couldnt_import_as_vamsas_session",
2074 .getString("label.vamsas_document_import_failed"),
2075 JvOptionPane.ERROR_MESSAGE);
2081 jalview.bin.Cache.log.error(
2082 "Implementation error - load session from a running session is not supported.");
2087 * import file into a new vamsas session (uses jalview.gui.VamsasApplication)
2090 * @return true if import was a success and a session was started.
2092 public boolean vamsasImport(URL url)
2094 // TODO: create progress bar
2095 if (v_client != null)
2098 jalview.bin.Cache.log.error(
2099 "Implementation error - load session from a running session is not supported.");
2105 // copy the URL content to a temporary local file
2106 // TODO: be a bit cleverer here with nio (?!)
2107 File file = File.createTempFile("vdocfromurl", ".vdj");
2108 FileOutputStream fos = new FileOutputStream(file);
2109 BufferedInputStream bis = new BufferedInputStream(url.openStream());
2110 byte[] buffer = new byte[2048];
2112 while ((ln = bis.read(buffer)) > -1)
2114 fos.write(buffer, 0, ln);
2118 v_client = new jalview.gui.VamsasApplication(this, file,
2119 url.toExternalForm());
2120 } catch (Exception ex)
2122 jalview.bin.Cache.log.error(
2123 "Failed to create new vamsas session from contents of URL "
2128 setupVamsasConnectedGui();
2129 v_client.initial_update(); // TODO: thread ?
2130 return v_client.inSession();
2134 * import file into a new vamsas session (uses jalview.gui.VamsasApplication)
2137 * @return true if import was a success and a session was started.
2139 public boolean vamsasImport(File file)
2141 if (v_client != null)
2144 jalview.bin.Cache.log.error(
2145 "Implementation error - load session from a running session is not supported.");
2149 setProgressBar(MessageManager.formatMessage(
2150 "status.importing_vamsas_session_from", new Object[]
2151 { file.getName() }), file.hashCode());
2154 v_client = new jalview.gui.VamsasApplication(this, file, null);
2155 } catch (Exception ex)
2157 setProgressBar(MessageManager.formatMessage(
2158 "status.importing_vamsas_session_from", new Object[]
2159 { file.getName() }), file.hashCode());
2160 jalview.bin.Cache.log.error(
2161 "New vamsas session from existing session file failed:", ex);
2164 setupVamsasConnectedGui();
2165 v_client.initial_update(); // TODO: thread ?
2166 setProgressBar(MessageManager.formatMessage(
2167 "status.importing_vamsas_session_from", new Object[]
2168 { file.getName() }), file.hashCode());
2169 return v_client.inSession();
2172 public boolean joinVamsasSession(String mysesid)
2174 if (v_client != null)
2176 throw new Error(MessageManager
2177 .getString("error.try_join_vamsas_session_another"));
2179 if (mysesid == null)
2182 MessageManager.getString("error.invalid_vamsas_session_id"));
2184 v_client = new VamsasApplication(this, mysesid);
2185 setupVamsasConnectedGui();
2186 v_client.initial_update();
2187 return (v_client.inSession());
2191 public void vamsasStart_actionPerformed(ActionEvent e)
2193 if (v_client == null)
2196 // we just start a default session for moment.
2198 * JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache.
2199 * getProperty("LAST_DIRECTORY"));
2201 * chooser.setFileView(new JalviewFileView());
2202 * chooser.setDialogTitle("Load Vamsas file");
2203 * chooser.setToolTipText("Import");
2205 * int value = chooser.showOpenDialog(this);
2207 * if (value == JalviewFileChooser.APPROVE_OPTION) { v_client = new
2208 * jalview.gui.VamsasApplication(this, chooser.getSelectedFile());
2210 v_client = new VamsasApplication(this);
2211 setupVamsasConnectedGui();
2212 v_client.initial_update(); // TODO: thread ?
2216 // store current data in session.
2217 v_client.push_update(); // TODO: thread
2221 protected void setupVamsasConnectedGui()
2223 vamsasStart.setText(MessageManager.getString("label.session_update"));
2224 vamsasSave.setVisible(true);
2225 vamsasStop.setVisible(true);
2226 vamsasImport.setVisible(false); // Document import to existing session is
2227 // not possible for vamsas-client-1.0.
2230 protected void setupVamsasDisconnectedGui()
2232 vamsasSave.setVisible(false);
2233 vamsasStop.setVisible(false);
2234 vamsasImport.setVisible(true);
2236 .setText(MessageManager.getString("label.new_vamsas_session"));
2240 public void vamsasStop_actionPerformed(ActionEvent e)
2242 if (v_client != null)
2244 v_client.end_session();
2246 setupVamsasDisconnectedGui();
2250 protected void buildVamsasStMenu()
2252 if (v_client == null)
2254 String[] sess = null;
2257 sess = VamsasApplication.getSessionList();
2258 } catch (Exception e)
2260 jalview.bin.Cache.log.warn("Problem getting current sessions list.",
2266 jalview.bin.Cache.log.debug(
2267 "Got current sessions list: " + sess.length + " entries.");
2268 VamsasStMenu.removeAll();
2269 for (int i = 0; i < sess.length; i++)
2271 JMenuItem sessit = new JMenuItem();
2272 sessit.setText(sess[i]);
2273 sessit.setToolTipText(MessageManager
2274 .formatMessage("label.connect_to_session", new Object[]
2276 final Desktop dsktp = this;
2277 final String mysesid = sess[i];
2278 sessit.addActionListener(new ActionListener()
2282 public void actionPerformed(ActionEvent e)
2284 if (dsktp.v_client == null)
2286 Thread rthr = new Thread(new Runnable()
2292 dsktp.v_client = new VamsasApplication(dsktp, mysesid);
2293 dsktp.setupVamsasConnectedGui();
2294 dsktp.v_client.initial_update();
2302 VamsasStMenu.add(sessit);
2304 // don't show an empty menu.
2305 VamsasStMenu.setVisible(sess.length > 0);
2310 jalview.bin.Cache.log.debug("No current vamsas sessions.");
2311 VamsasStMenu.removeAll();
2312 VamsasStMenu.setVisible(false);
2317 // Not interested in the content. Just hide ourselves.
2318 VamsasStMenu.setVisible(false);
2323 public void vamsasSave_actionPerformed(ActionEvent e)
2325 // TODO: JAL-3048 not needed for Jalview-JS
2327 if (v_client != null)
2329 // TODO: VAMSAS DOCUMENT EXTENSION is VDJ
2330 JalviewFileChooser chooser = new JalviewFileChooser("vdj",
2333 chooser.setFileView(new JalviewFileView());
2334 chooser.setDialogTitle(MessageManager
2335 .getString("label.save_vamsas_document_archive"));
2337 int value = chooser.showSaveDialog(this);
2339 if (value == JalviewFileChooser.APPROVE_OPTION)
2341 java.io.File choice = chooser.getSelectedFile();
2342 JPanel progpanel = addProgressPanel(MessageManager
2343 .formatMessage("label.saving_vamsas_doc", new Object[]
2344 { choice.getName() }));
2345 Cache.setProperty("LAST_DIRECTORY", choice.getParent());
2346 String warnmsg = null;
2347 String warnttl = null;
2350 v_client.vclient.storeDocument(choice);
2353 warnttl = "Serious Problem saving Vamsas Document";
2354 warnmsg = ex.toString();
2355 jalview.bin.Cache.log
2356 .error("Error Whilst saving document to " + choice, ex);
2358 } catch (Exception ex)
2360 warnttl = "Problem saving Vamsas Document.";
2361 warnmsg = ex.toString();
2362 jalview.bin.Cache.log.warn(
2363 "Exception Whilst saving document to " + choice, ex);
2366 removeProgressPanel(progpanel);
2367 if (warnmsg != null)
2369 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
2371 warnmsg, warnttl, JvOptionPane.ERROR_MESSAGE);
2377 JPanel vamUpdate = null;
2380 * hide vamsas user gui bits when a vamsas document event is being handled.
2383 * true to hide gui, false to reveal gui
2385 public void setVamsasUpdate(boolean b)
2387 Cache.log.debug("Setting gui for Vamsas update "
2388 + (b ? "in progress" : "finished"));
2390 if (vamUpdate != null)
2392 this.removeProgressPanel(vamUpdate);
2396 vamUpdate = this.addProgressPanel(
2397 MessageManager.getString("label.updating_vamsas_session"));
2399 vamsasStart.setVisible(!b);
2400 vamsasStop.setVisible(!b);
2401 vamsasSave.setVisible(!b);
2404 public JInternalFrame[] getAllFrames()
2406 return desktop.getAllFrames();
2410 * Checks the given url to see if it gives a response indicating that the user
2411 * should be informed of a new questionnaire.
2415 public void checkForQuestionnaire(String url)
2417 UserQuestionnaireCheck jvq = new UserQuestionnaireCheck(url);
2418 // javax.swing.SwingUtilities.invokeLater(jvq);
2419 new Thread(jvq).start();
2422 public void checkURLLinks()
2424 // Thread off the URL link checker
2425 addDialogThread(new Runnable()
2430 if (Cache.getDefault("CHECKURLLINKS", true))
2432 // check what the actual links are - if it's just the default don't
2433 // bother with the warning
2434 List<String> links = Preferences.sequenceUrlLinks
2437 // only need to check links if there is one with a
2438 // SEQUENCE_ID which is not the default EMBL_EBI link
2439 ListIterator<String> li = links.listIterator();
2440 boolean check = false;
2441 List<JLabel> urls = new ArrayList<>();
2442 while (li.hasNext())
2444 String link = li.next();
2445 if (link.contains(SEQUENCE_ID)
2446 && !UrlConstants.isDefaultString(link))
2449 int barPos = link.indexOf("|");
2450 String urlMsg = barPos == -1 ? link
2451 : link.substring(0, barPos) + ": "
2452 + link.substring(barPos + 1);
2453 urls.add(new JLabel(urlMsg));
2461 // ask user to check in case URL links use old style tokens
2462 // ($SEQUENCE_ID$ for sequence id _or_ accession id)
2463 JPanel msgPanel = new JPanel();
2464 msgPanel.setLayout(new BoxLayout(msgPanel, BoxLayout.PAGE_AXIS));
2465 msgPanel.add(Box.createVerticalGlue());
2466 JLabel msg = new JLabel(MessageManager
2467 .getString("label.SEQUENCE_ID_for_DB_ACCESSION1"));
2468 JLabel msg2 = new JLabel(MessageManager
2469 .getString("label.SEQUENCE_ID_for_DB_ACCESSION2"));
2471 for (JLabel url : urls)
2477 final JCheckBox jcb = new JCheckBox(
2478 MessageManager.getString("label.do_not_display_again"));
2479 jcb.addActionListener(new ActionListener()
2482 public void actionPerformed(ActionEvent e)
2484 // update Cache settings for "don't show this again"
2485 boolean showWarningAgain = !jcb.isSelected();
2486 Cache.setProperty("CHECKURLLINKS",
2487 Boolean.valueOf(showWarningAgain).toString());
2492 JvOptionPane.showMessageDialog(Desktop.desktop, msgPanel,
2494 .getString("label.SEQUENCE_ID_no_longer_used"),
2495 JvOptionPane.WARNING_MESSAGE);
2502 * Proxy class for JDesktopPane which optionally displays the current memory
2503 * usage and highlights the desktop area with a red bar if free memory runs low.
2507 public class MyDesktopPane extends JDesktopPane
2510 private static final float ONE_MB = 1048576f;
2512 boolean showMemoryUsage = false;
2516 java.text.NumberFormat df;
2518 float maxMemory, allocatedMemory, freeMemory, totalFreeMemory,
2521 public MyDesktopPane(boolean showMemoryUsage)
2523 showMemoryUsage(showMemoryUsage);
2526 public void showMemoryUsage(boolean showMemory)
2528 this.showMemoryUsage = showMemory;
2531 Thread worker = new Thread(this);
2537 public boolean isShowMemoryUsage()
2539 return showMemoryUsage;
2545 df = java.text.NumberFormat.getNumberInstance();
2546 df.setMaximumFractionDigits(2);
2547 runtime = Runtime.getRuntime();
2549 while (showMemoryUsage)
2553 maxMemory = runtime.maxMemory() / ONE_MB;
2554 allocatedMemory = runtime.totalMemory() / ONE_MB;
2555 freeMemory = runtime.freeMemory() / ONE_MB;
2556 totalFreeMemory = freeMemory + (maxMemory - allocatedMemory);
2558 percentUsage = (totalFreeMemory / maxMemory) * 100;
2560 // if (percentUsage < 20)
2562 // border1 = BorderFactory.createMatteBorder(12, 12, 12, 12,
2564 // instance.set.setBorder(border1);
2567 // sleep after showing usage
2569 } catch (Exception ex)
2571 ex.printStackTrace();
2577 public void paintComponent(Graphics g)
2579 if (showMemoryUsage && g != null && df != null)
2581 if (percentUsage < 20)
2583 g.setColor(Color.red);
2585 FontMetrics fm = g.getFontMetrics();
2588 g.drawString(MessageManager.formatMessage("label.memory_stats",
2590 { df.format(totalFreeMemory), df.format(maxMemory),
2591 df.format(percentUsage) }),
2592 10, getHeight() - fm.getHeight());
2599 * Accessor method to quickly get all the AlignmentFrames loaded.
2601 * @return an array of AlignFrame, or null if none found
2603 public static AlignFrame[] getAlignFrames()
2605 if (Jalview.isHeadlessMode())
2607 // Desktop.desktop is null in headless mode
2608 return new AlignFrame[] { Jalview.currentAlignFrame };
2611 JInternalFrame[] frames = Desktop.desktop.getAllFrames();
2617 List<AlignFrame> avp = new ArrayList<>();
2619 for (int i = frames.length - 1; i > -1; i--)
2621 if (frames[i] instanceof AlignFrame)
2623 avp.add((AlignFrame) frames[i]);
2625 else if (frames[i] instanceof SplitFrame)
2628 * Also check for a split frame containing an AlignFrame
2630 GSplitFrame sf = (GSplitFrame) frames[i];
2631 if (sf.getTopFrame() instanceof AlignFrame)
2633 avp.add((AlignFrame) sf.getTopFrame());
2635 if (sf.getBottomFrame() instanceof AlignFrame)
2637 avp.add((AlignFrame) sf.getBottomFrame());
2641 if (avp.size() == 0)
2645 AlignFrame afs[] = avp.toArray(new AlignFrame[avp.size()]);
2650 * Returns an array of any AppJmol frames in the Desktop (or null if none).
2654 public GStructureViewer[] getJmols()
2656 JInternalFrame[] frames = Desktop.desktop.getAllFrames();
2662 List<GStructureViewer> avp = new ArrayList<>();
2664 for (int i = frames.length - 1; i > -1; i--)
2666 if (frames[i] instanceof AppJmol)
2668 GStructureViewer af = (GStructureViewer) frames[i];
2672 if (avp.size() == 0)
2676 GStructureViewer afs[] = avp.toArray(new GStructureViewer[avp.size()]);
2681 * Add Groovy Support to Jalview
2684 public void groovyShell_actionPerformed()
2688 openGroovyConsole();
2689 } catch (Exception ex)
2691 jalview.bin.Cache.log.error("Groovy Shell Creation failed.", ex);
2692 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
2694 MessageManager.getString("label.couldnt_create_groovy_shell"),
2695 MessageManager.getString("label.groovy_support_failed"),
2696 JvOptionPane.ERROR_MESSAGE);
2701 * Open the Groovy console
2703 void openGroovyConsole()
2705 if (groovyConsole == null)
2707 groovyConsole = new groovy.ui.Console();
2708 groovyConsole.setVariable("Jalview", this);
2709 groovyConsole.run();
2712 * We allow only one console at a time, so that AlignFrame menu option
2713 * 'Calculate | Run Groovy script' is unambiguous.
2714 * Disable 'Groovy Console', and enable 'Run script', when the console is
2715 * opened, and the reverse when it is closed
2717 Window window = (Window) groovyConsole.getFrame();
2718 window.addWindowListener(new WindowAdapter()
2721 public void windowClosed(WindowEvent e)
2724 * rebind CMD-Q from Groovy Console to Jalview Quit
2727 enableExecuteGroovy(false);
2733 * show Groovy console window (after close and reopen)
2735 ((Window) groovyConsole.getFrame()).setVisible(true);
2738 * if we got this far, enable 'Run Groovy' in AlignFrame menus
2739 * and disable opening a second console
2741 enableExecuteGroovy(true);
2745 * Bind Ctrl/Cmd-Q to Quit - for reset as Groovy Console takes over this binding
2748 protected void addQuitHandler()
2750 getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
2751 .put(KeyStroke.getKeyStroke(KeyEvent.VK_Q,
2752 Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()),
2754 getRootPane().getActionMap().put("Quit", new AbstractAction()
2757 public void actionPerformed(ActionEvent e)
2765 * Enable or disable 'Run Groovy script' in AlignFrame calculate menus
2768 * true if Groovy console is open
2770 public void enableExecuteGroovy(boolean enabled)
2773 * disable opening a second Groovy console
2774 * (or re-enable when the console is closed)
2776 groovyShell.setEnabled(!enabled);
2778 AlignFrame[] alignFrames = getAlignFrames();
2779 if (alignFrames != null)
2781 for (AlignFrame af : alignFrames)
2783 af.setGroovyEnabled(enabled);
2789 * Progress bars managed by the IProgressIndicator method.
2791 private Hashtable<Long, JPanel> progressBars;
2793 private Hashtable<Long, IProgressIndicatorHandler> progressBarHandlers;
2798 * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
2801 public void setProgressBar(String message, long id)
2803 if (progressBars == null)
2805 progressBars = new Hashtable<>();
2806 progressBarHandlers = new Hashtable<>();
2809 if (progressBars.get(new Long(id)) != null)
2811 JPanel panel = progressBars.remove(new Long(id));
2812 if (progressBarHandlers.contains(new Long(id)))
2814 progressBarHandlers.remove(new Long(id));
2816 removeProgressPanel(panel);
2820 progressBars.put(new Long(id), addProgressPanel(message));
2827 * @see jalview.gui.IProgressIndicator#registerHandler(long,
2828 * jalview.gui.IProgressIndicatorHandler)
2831 public void registerHandler(final long id,
2832 final IProgressIndicatorHandler handler)
2834 if (progressBarHandlers == null
2835 || !progressBars.containsKey(new Long(id)))
2837 throw new Error(MessageManager.getString(
2838 "error.call_setprogressbar_before_registering_handler"));
2840 progressBarHandlers.put(new Long(id), handler);
2841 final JPanel progressPanel = progressBars.get(new Long(id));
2842 if (handler.canCancel())
2844 JButton cancel = new JButton(
2845 MessageManager.getString("action.cancel"));
2846 final IProgressIndicator us = this;
2847 cancel.addActionListener(new ActionListener()
2851 public void actionPerformed(ActionEvent e)
2853 handler.cancelActivity(id);
2854 us.setProgressBar(MessageManager
2855 .formatMessage("label.cancelled_params", new Object[]
2856 { ((JLabel) progressPanel.getComponent(0)).getText() }),
2860 progressPanel.add(cancel, BorderLayout.EAST);
2866 * @return true if any progress bars are still active
2869 public boolean operationInProgress()
2871 if (progressBars != null && progressBars.size() > 0)
2879 * This will return the first AlignFrame holding the given viewport instance. It
2880 * will break if there are more than one AlignFrames viewing a particular av.
2883 * @return alignFrame for viewport
2885 public static AlignFrame getAlignFrameFor(AlignViewportI viewport)
2887 if (desktop != null)
2889 AlignmentPanel[] aps = getAlignmentPanels(
2890 viewport.getSequenceSetId());
2891 for (int panel = 0; aps != null && panel < aps.length; panel++)
2893 if (aps[panel] != null && aps[panel].av == viewport)
2895 return aps[panel].alignFrame;
2902 public VamsasApplication getVamsasApplication()
2909 * flag set if jalview GUI is being operated programmatically
2911 private boolean inBatchMode = false;
2914 * check if jalview GUI is being operated programmatically
2916 * @return inBatchMode
2918 public boolean isInBatchMode()
2924 * set flag if jalview GUI is being operated programmatically
2926 * @param inBatchMode
2928 public void setInBatchMode(boolean inBatchMode)
2930 this.inBatchMode = inBatchMode;
2933 public void startServiceDiscovery()
2935 startServiceDiscovery(false);
2938 public void startServiceDiscovery(boolean blocking)
2940 boolean alive = true;
2941 Thread t0 = null, t1 = null, t2 = null;
2942 // JAL-940 - JALVIEW 1 services are now being EOLed as of JABA 2.1 release
2945 // todo: changesupport handlers need to be transferred
2946 if (discoverer == null)
2948 discoverer = new jalview.ws.jws1.Discoverer();
2949 // register PCS handler for desktop.
2950 discoverer.addPropertyChangeListener(changeSupport);
2952 // JAL-940 - disabled JWS1 service configuration - always start discoverer
2953 // until we phase out completely
2954 (t0 = new Thread(discoverer)).start();
2957 if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
2959 t2 = jalview.ws.jws2.Jws2Discoverer.getDiscoverer()
2960 .startDiscoverer(changeSupport);
2964 // TODO: do rest service discovery
2973 } catch (Exception e)
2976 alive = (t1 != null && t1.isAlive()) || (t2 != null && t2.isAlive())
2977 || (t3 != null && t3.isAlive())
2978 || (t0 != null && t0.isAlive());
2984 * called to check if the service discovery process completed successfully.
2988 protected void JalviewServicesChanged(PropertyChangeEvent evt)
2990 if (evt.getNewValue() == null || evt.getNewValue() instanceof Vector)
2992 final String ermsg = jalview.ws.jws2.Jws2Discoverer.getDiscoverer()
2993 .getErrorMessages();
2996 if (Cache.getDefault("SHOW_WSDISCOVERY_ERRORS", true))
2998 if (serviceChangedDialog == null)
3000 // only run if we aren't already displaying one of these.
3001 addDialogThread(serviceChangedDialog = new Runnable()
3008 * JalviewDialog jd =new JalviewDialog() {
3010 * @Override protected void cancelPressed() { // TODO
3011 * Auto-generated method stub
3013 * }@Override protected void okPressed() { // TODO
3014 * Auto-generated method stub
3016 * }@Override protected void raiseClosed() { // TODO
3017 * Auto-generated method stub
3019 * } }; jd.initDialogFrame(new
3020 * JLabel("<html><table width=\"450\"><tr><td>" + ermsg +
3021 * "<br/>It may be that you have invalid JABA URLs in your web service preferences,"
3022 * + " or mis-configured HTTP proxy settings.<br/>" +
3023 * "Check the <em>Connections</em> and <em>Web services</em> tab of the"
3025 * " Tools->Preferences dialog box to change them.</td></tr></table></html>"
3026 * ), true, true, "Web Service Configuration Problem", 450,
3029 * jd.waitForInput();
3031 JvOptionPane.showConfirmDialog(Desktop.desktop,
3032 new JLabel("<html><table width=\"450\"><tr><td>"
3033 + ermsg + "</td></tr></table>"
3034 + "<p>It may be that you have invalid JABA URLs<br/>in your web service preferences,"
3035 + "<br>or as a command-line argument, or mis-configured HTTP proxy settings.</p>"
3036 + "<p>Check the <em>Connections</em> and <em>Web services</em> tab<br/>of the"
3037 + " Tools->Preferences dialog box to change them.</p></html>"),
3038 "Web Service Configuration Problem",
3039 JvOptionPane.DEFAULT_OPTION,
3040 JvOptionPane.ERROR_MESSAGE);
3041 serviceChangedDialog = null;
3050 "Errors reported by JABA discovery service. Check web services preferences.\n"
3057 private Runnable serviceChangedDialog = null;
3060 * start a thread to open a URL in the configured browser. Pops up a warning
3061 * dialog to the user if there is an exception when calling out to the browser
3066 public static void showUrl(final String url)
3068 showUrl(url, Desktop.instance);
3072 * Like showUrl but allows progress handler to be specified
3076 * (null) or object implementing IProgressIndicator
3078 public static void showUrl(final String url,
3079 final IProgressIndicator progress)
3081 new Thread(new Runnable()
3088 if (progress != null)
3090 progress.setProgressBar(MessageManager
3091 .formatMessage("status.opening_params", new Object[]
3092 { url }), this.hashCode());
3094 jalview.util.BrowserLauncher.openURL(url);
3095 } catch (Exception ex)
3097 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
3099 .getString("label.web_browser_not_found_unix"),
3100 MessageManager.getString("label.web_browser_not_found"),
3101 JvOptionPane.WARNING_MESSAGE);
3103 ex.printStackTrace();
3105 if (progress != null)
3107 progress.setProgressBar(null, this.hashCode());
3113 public static WsParamSetManager wsparamManager = null;
3115 public static ParamManager getUserParameterStore()
3117 if (wsparamManager == null)
3119 wsparamManager = new WsParamSetManager();
3121 return wsparamManager;
3125 * static hyperlink handler proxy method for use by Jalview's internal windows
3129 public static void hyperlinkUpdate(HyperlinkEvent e)
3131 if (e.getEventType() == EventType.ACTIVATED)
3136 url = e.getURL().toString();
3137 Desktop.showUrl(url);
3138 } catch (Exception x)
3142 if (Cache.log != null)
3144 Cache.log.error("Couldn't handle string " + url + " as a URL.");
3149 "Couldn't handle string " + url + " as a URL.");
3152 // ignore any exceptions due to dud links.
3159 * single thread that handles display of dialogs to user.
3161 ExecutorService dialogExecutor = Executors.newSingleThreadExecutor();
3164 * flag indicating if dialogExecutor should try to acquire a permit
3166 private volatile boolean dialogPause = true;
3171 private java.util.concurrent.Semaphore block = new Semaphore(0);
3173 private static groovy.ui.Console groovyConsole;
3176 * add another dialog thread to the queue
3180 public void addDialogThread(final Runnable prompter)
3182 dialogExecutor.submit(new Runnable()
3192 } catch (InterruptedException x)
3197 if (instance == null)
3203 SwingUtilities.invokeAndWait(prompter);
3204 } catch (Exception q)
3206 Cache.log.warn("Unexpected Exception in dialog thread.", q);
3212 public void startDialogQueue()
3214 // set the flag so we don't pause waiting for another permit and semaphore
3215 // the current task to begin
3216 dialogPause = false;
3221 * Outputs an image of the desktop to file in EPS format, after prompting the
3222 * user for choice of Text or Lineart character rendering (unless a preference
3223 * has been set). The file name is generated as
3226 * Jalview_snapshot_nnnnn.eps where nnnnn is the current timestamp in milliseconds
3230 protected void snapShotWindow_actionPerformed(ActionEvent e)
3232 // currently the menu option to do this is not shown
3235 int width = getWidth();
3236 int height = getHeight();
3238 "Jalview_snapshot_" + System.currentTimeMillis() + ".eps");
3239 ImageWriterI writer = new ImageWriterI()
3242 public void exportImage(Graphics g) throws Exception
3245 Cache.log.info("Successfully written snapshot to file "
3246 + of.getAbsolutePath());
3249 String title = "View of desktop";
3250 ImageExporter exporter = new ImageExporter(writer, null, TYPE.EPS,
3252 exporter.doExport(of, this, width, height, title);
3256 * Explode the views in the given SplitFrame into separate SplitFrame windows.
3257 * This respects (remembers) any previous 'exploded geometry' i.e. the size and
3258 * location last time the view was expanded (if any). However it does not
3259 * remember the split pane divider location - this is set to match the
3260 * 'exploding' frame.
3264 public void explodeViews(SplitFrame sf)
3266 AlignFrame oldTopFrame = (AlignFrame) sf.getTopFrame();
3267 AlignFrame oldBottomFrame = (AlignFrame) sf.getBottomFrame();
3268 List<? extends AlignmentViewPanel> topPanels = oldTopFrame
3270 List<? extends AlignmentViewPanel> bottomPanels = oldBottomFrame
3272 int viewCount = topPanels.size();
3279 * Processing in reverse order works, forwards order leaves the first panels
3280 * not visible. I don't know why!
3282 for (int i = viewCount - 1; i >= 0; i--)
3285 * Make new top and bottom frames. These take over the respective
3286 * AlignmentPanel objects, including their AlignmentViewports, so the
3287 * cdna/protein relationships between the viewports is carried over to the
3290 * explodedGeometry holds the (x, y) position of the previously exploded
3291 * SplitFrame, and the (width, height) of the AlignFrame component
3293 AlignmentPanel topPanel = (AlignmentPanel) topPanels.get(i);
3294 AlignFrame newTopFrame = new AlignFrame(topPanel);
3295 newTopFrame.setSize(oldTopFrame.getSize());
3296 newTopFrame.setVisible(true);
3297 Rectangle geometry = ((AlignViewport) topPanel.getAlignViewport())
3298 .getExplodedGeometry();
3299 if (geometry != null)
3301 newTopFrame.setSize(geometry.getSize());
3304 AlignmentPanel bottomPanel = (AlignmentPanel) bottomPanels.get(i);
3305 AlignFrame newBottomFrame = new AlignFrame(bottomPanel);
3306 newBottomFrame.setSize(oldBottomFrame.getSize());
3307 newBottomFrame.setVisible(true);
3308 geometry = ((AlignViewport) bottomPanel.getAlignViewport())
3309 .getExplodedGeometry();
3310 if (geometry != null)
3312 newBottomFrame.setSize(geometry.getSize());
3315 topPanel.av.setGatherViewsHere(false);
3316 bottomPanel.av.setGatherViewsHere(false);
3317 JInternalFrame splitFrame = new SplitFrame(newTopFrame,
3319 if (geometry != null)
3321 splitFrame.setLocation(geometry.getLocation());
3323 Desktop.addInternalFrame(splitFrame, sf.getTitle(), -1, -1);
3327 * Clear references to the panels (now relocated in the new SplitFrames)
3328 * before closing the old SplitFrame.
3331 bottomPanels.clear();
3336 * Gather expanded split frames, sharing the same pairs of sequence set ids,
3337 * back into the given SplitFrame as additional views. Note that the gathered
3338 * frames may themselves have multiple views.
3342 public void gatherViews(GSplitFrame source)
3345 * special handling of explodedGeometry for a view within a SplitFrame: - it
3346 * holds the (x, y) position of the enclosing SplitFrame, and the (width,
3347 * height) of the AlignFrame component
3349 AlignFrame myTopFrame = (AlignFrame) source.getTopFrame();
3350 AlignFrame myBottomFrame = (AlignFrame) source.getBottomFrame();
3351 myTopFrame.viewport.setExplodedGeometry(new Rectangle(source.getX(),
3352 source.getY(), myTopFrame.getWidth(), myTopFrame.getHeight()));
3353 myBottomFrame.viewport
3354 .setExplodedGeometry(new Rectangle(source.getX(), source.getY(),
3355 myBottomFrame.getWidth(), myBottomFrame.getHeight()));
3356 myTopFrame.viewport.setGatherViewsHere(true);
3357 myBottomFrame.viewport.setGatherViewsHere(true);
3358 String topViewId = myTopFrame.viewport.getSequenceSetId();
3359 String bottomViewId = myBottomFrame.viewport.getSequenceSetId();
3361 JInternalFrame[] frames = desktop.getAllFrames();
3362 for (JInternalFrame frame : frames)
3364 if (frame instanceof SplitFrame && frame != source)
3366 SplitFrame sf = (SplitFrame) frame;
3367 AlignFrame topFrame = (AlignFrame) sf.getTopFrame();
3368 AlignFrame bottomFrame = (AlignFrame) sf.getBottomFrame();
3369 boolean gatherThis = false;
3370 for (int a = 0; a < topFrame.alignPanels.size(); a++)
3372 AlignmentPanel topPanel = topFrame.alignPanels.get(a);
3373 AlignmentPanel bottomPanel = bottomFrame.alignPanels.get(a);
3374 if (topViewId.equals(topPanel.av.getSequenceSetId())
3375 && bottomViewId.equals(bottomPanel.av.getSequenceSetId()))
3378 topPanel.av.setGatherViewsHere(false);
3379 bottomPanel.av.setGatherViewsHere(false);
3380 topPanel.av.setExplodedGeometry(
3381 new Rectangle(sf.getLocation(), topFrame.getSize()));
3382 bottomPanel.av.setExplodedGeometry(
3383 new Rectangle(sf.getLocation(), bottomFrame.getSize()));
3384 myTopFrame.addAlignmentPanel(topPanel, false);
3385 myBottomFrame.addAlignmentPanel(bottomPanel, false);
3391 topFrame.getAlignPanels().clear();
3392 bottomFrame.getAlignPanels().clear();
3399 * The dust settles...give focus to the tab we did this from.
3401 myTopFrame.setDisplayedView(myTopFrame.alignPanel);
3404 public static groovy.ui.Console getGroovyConsole()
3406 return groovyConsole;
3410 * handles the payload of a drag and drop event.
3412 * TODO refactor to desktop utilities class
3415 * - Data source strings extracted from the drop event
3417 * - protocol for each data source extracted from the drop event
3421 * - the payload from the drop event
3424 public static void transferFromDropTarget(List<Object> files,
3425 List<DataSourceType> protocols, DropTargetDropEvent evt,
3426 Transferable t) throws Exception
3429 // BH 2018 changed List<String> to List<Object> to allow for File from SwingJS
3431 // DataFlavor[] flavors = t.getTransferDataFlavors();
3432 // for (int i = 0; i < flavors.length; i++) {
3433 // if (flavors[i].isFlavorJavaFileListType()) {
3434 // evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
3435 // List<File> list = (List<File>) t.getTransferData(flavors[i]);
3436 // for (int j = 0; j < list.size(); j++) {
3437 // File file = (File) list.get(j);
3438 // byte[] data = getDroppedFileBytes(file);
3439 // fileName.setText(file.getName() + " - " + data.length + " " +
3440 // evt.getLocation());
3441 // JTextArea target = (JTextArea) ((DropTarget) evt.getSource()).getComponent();
3442 // target.setText(new String(data));
3444 // dtde.dropComplete(true);
3449 DataFlavor uriListFlavor = new DataFlavor(
3450 "text/uri-list;class=java.lang.String"), urlFlavour = null;
3453 urlFlavour = new DataFlavor(
3454 "application/x-java-url; class=java.net.URL");
3455 } catch (ClassNotFoundException cfe)
3457 Cache.log.debug("Couldn't instantiate the URL dataflavor.", cfe);
3460 if (urlFlavour != null && t.isDataFlavorSupported(urlFlavour))
3465 java.net.URL url = (URL) t.getTransferData(urlFlavour);
3466 // nb: java 8 osx bug https://bugs.openjdk.java.net/browse/JDK-8156099
3467 // means url may be null.
3470 protocols.add(DataSourceType.URL);
3471 files.add(url.toString());
3472 Cache.log.debug("Drop handled as URL dataflavor "
3473 + files.get(files.size() - 1));
3478 if (Platform.isAMacAndNotJS())
3481 "Please ignore plist error - occurs due to problem with java 8 on OSX");
3485 } catch (Throwable ex)
3487 Cache.log.debug("URL drop handler failed.", ex);
3490 if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
3492 // Works on Windows and MacOSX
3493 Cache.log.debug("Drop handled as javaFileListFlavor");
3494 for (Object file : (List) t
3495 .getTransferData(DataFlavor.javaFileListFlavor))
3498 protocols.add(DataSourceType.FILE);
3503 // Unix like behaviour
3504 boolean added = false;
3506 if (t.isDataFlavorSupported(uriListFlavor))
3508 Cache.log.debug("Drop handled as uriListFlavor");
3509 // This is used by Unix drag system
3510 data = (String) t.getTransferData(uriListFlavor);
3514 // fallback to text: workaround - on OSX where there's a JVM bug
3515 Cache.log.debug("standard URIListFlavor failed. Trying text");
3516 // try text fallback
3517 DataFlavor textDf = new DataFlavor(
3518 "text/plain;class=java.lang.String");
3519 if (t.isDataFlavorSupported(textDf))
3521 data = (String) t.getTransferData(textDf);
3524 Cache.log.debug("Plain text drop content returned "
3525 + (data == null ? "Null - failed" : data));
3530 while (protocols.size() < files.size())
3532 Cache.log.debug("Adding missing FILE protocol for "
3533 + files.get(protocols.size()));
3534 protocols.add(DataSourceType.FILE);
3536 for (java.util.StringTokenizer st = new java.util.StringTokenizer(
3537 data, "\r\n"); st.hasMoreTokens();)
3540 String s = st.nextToken();
3541 if (s.startsWith("#"))
3543 // the line is a comment (as per the RFC 2483)
3546 java.net.URI uri = new java.net.URI(s);
3547 if (uri.getScheme().toLowerCase().startsWith("http"))
3549 protocols.add(DataSourceType.URL);
3550 files.add(uri.toString());
3554 // otherwise preserve old behaviour: catch all for file objects
3555 java.io.File file = new java.io.File(uri);
3556 protocols.add(DataSourceType.FILE);
3557 files.add(file.toString());
3562 if (Cache.log.isDebugEnabled())
3564 if (data == null || !added)
3567 if (t.getTransferDataFlavors() != null
3568 && t.getTransferDataFlavors().length > 0)
3571 "Couldn't resolve drop data. Here are the supported flavors:");
3572 for (DataFlavor fl : t.getTransferDataFlavors())
3575 "Supported transfer dataflavor: " + fl.toString());
3576 Object df = t.getTransferData(fl);
3579 Cache.log.debug("Retrieves: " + df);
3583 Cache.log.debug("Retrieved nothing");
3589 Cache.log.debug("Couldn't resolve dataflavor for drop: "
3595 if (Platform.isWindowsAndNotJS())
3597 Cache.log.debug("Scanning dropped content for Windows Link Files");
3599 // resolve any .lnk files in the file drop
3600 for (int f = 0; f < files.size(); f++)
3602 String source = files.get(f).toString().toLowerCase();
3603 if (protocols.get(f).equals(DataSourceType.FILE)
3604 && (source.endsWith(".lnk") || source.endsWith(".url")
3605 || source.endsWith(".site")))
3609 Object obj = files.get(f);
3610 File lf = (obj instanceof File ? (File) obj
3611 : new File((String) obj));
3612 // process link file to get a URL
3613 Cache.log.debug("Found potential link file: " + lf);
3614 WindowsShortcut wscfile = new WindowsShortcut(lf);
3615 String fullname = wscfile.getRealFilename();
3616 protocols.set(f, FormatAdapter.checkProtocol(fullname));
3617 files.set(f, fullname);
3618 Cache.log.debug("Parsed real filename " + fullname
3619 + " to extract protocol: " + protocols.get(f));
3620 } catch (Exception ex)
3623 "Couldn't parse " + files.get(f) + " as a link file.",
3632 * Sets the Preferences property for experimental features to True or False
3633 * depending on the state of the controlling menu item
3636 protected void showExperimental_actionPerformed(boolean selected)
3638 Cache.setProperty(EXPERIMENTAL_FEATURES, Boolean.toString(selected));
3642 * Answers a (possibly empty) list of any structure viewer frames (currently for
3643 * either Jmol or Chimera) which are currently open. This may optionally be
3644 * restricted to viewers of a specified class, or viewers linked to a specified
3648 * if not null, only return viewers linked to this panel
3649 * @param structureViewerClass
3650 * if not null, only return viewers of this class
3653 public List<StructureViewerBase> getStructureViewers(
3654 AlignmentPanel apanel,
3655 Class<? extends StructureViewerBase> structureViewerClass)
3657 List<StructureViewerBase> result = new ArrayList<>();
3658 JInternalFrame[] frames = Desktop.instance.getAllFrames();
3660 for (JInternalFrame frame : frames)
3662 if (frame instanceof StructureViewerBase)
3664 if (structureViewerClass == null
3665 || structureViewerClass.isInstance(frame))
3668 || ((StructureViewerBase) frame).isLinkedWith(apanel))
3670 result.add((StructureViewerBase) frame);