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.BackupFiles;
31 import jalview.io.DataSourceType;
32 import jalview.io.FileFormat;
33 import jalview.io.FileFormatException;
34 import jalview.io.FileFormatI;
35 import jalview.io.FileFormats;
36 import jalview.io.FileLoader;
37 import jalview.io.FormatAdapter;
38 import jalview.io.IdentifyFile;
39 import jalview.io.JalviewFileChooser;
40 import jalview.io.JalviewFileView;
41 import jalview.jbgui.GSplitFrame;
42 import jalview.jbgui.GStructureViewer;
43 import jalview.project.Jalview2XML;
44 import jalview.structure.StructureSelectionManager;
45 import jalview.urls.IdOrgSettings;
46 import jalview.util.BrowserLauncher;
47 import jalview.util.ImageMaker.TYPE;
48 import jalview.util.MessageManager;
49 import jalview.util.Platform;
50 import jalview.util.UrlConstants;
51 import jalview.viewmodel.AlignmentViewport;
52 import jalview.ws.params.ParamManager;
53 import jalview.ws.utils.UrlDownloadClient;
55 import java.awt.BorderLayout;
56 import java.awt.Color;
57 import java.awt.Dimension;
58 import java.awt.FontMetrics;
59 import java.awt.Graphics;
60 import java.awt.GridLayout;
61 import java.awt.Point;
62 import java.awt.Rectangle;
63 import java.awt.Toolkit;
64 import java.awt.Window;
65 import java.awt.datatransfer.Clipboard;
66 import java.awt.datatransfer.ClipboardOwner;
67 import java.awt.datatransfer.DataFlavor;
68 import java.awt.datatransfer.Transferable;
69 import java.awt.dnd.DnDConstants;
70 import java.awt.dnd.DropTargetDragEvent;
71 import java.awt.dnd.DropTargetDropEvent;
72 import java.awt.dnd.DropTargetEvent;
73 import java.awt.dnd.DropTargetListener;
74 import java.awt.event.ActionEvent;
75 import java.awt.event.ActionListener;
76 import java.awt.event.InputEvent;
77 import java.awt.event.KeyEvent;
78 import java.awt.event.MouseAdapter;
79 import java.awt.event.MouseEvent;
80 import java.awt.event.WindowAdapter;
81 import java.awt.event.WindowEvent;
82 import java.beans.PropertyChangeEvent;
83 import java.beans.PropertyChangeListener;
84 import java.io.BufferedInputStream;
86 import java.io.FileOutputStream;
87 import java.io.IOException;
89 import java.util.ArrayList;
90 import java.util.Hashtable;
91 import java.util.List;
92 import java.util.ListIterator;
93 import java.util.Vector;
94 import java.util.concurrent.ExecutorService;
95 import java.util.concurrent.Executors;
96 import java.util.concurrent.Semaphore;
98 import javax.swing.AbstractAction;
99 import javax.swing.Action;
100 import javax.swing.ActionMap;
101 import javax.swing.Box;
102 import javax.swing.BoxLayout;
103 import javax.swing.DefaultDesktopManager;
104 import javax.swing.DesktopManager;
105 import javax.swing.InputMap;
106 import javax.swing.JButton;
107 import javax.swing.JCheckBox;
108 import javax.swing.JComboBox;
109 import javax.swing.JComponent;
110 import javax.swing.JDesktopPane;
111 import javax.swing.JFrame;
112 import javax.swing.JInternalFrame;
113 import javax.swing.JLabel;
114 import javax.swing.JMenuItem;
115 import javax.swing.JPanel;
116 import javax.swing.JPopupMenu;
117 import javax.swing.JProgressBar;
118 import javax.swing.JTextField;
119 import javax.swing.KeyStroke;
120 import javax.swing.SwingUtilities;
121 import javax.swing.event.HyperlinkEvent;
122 import javax.swing.event.HyperlinkEvent.EventType;
123 import javax.swing.event.InternalFrameAdapter;
124 import javax.swing.event.InternalFrameEvent;
125 import javax.swing.event.MenuEvent;
126 import javax.swing.event.MenuListener;
128 import org.stackoverflowusers.file.WindowsShortcut;
135 * @version $Revision: 1.155 $
137 public class Desktop extends jalview.jbgui.GDesktop
138 implements DropTargetListener, ClipboardOwner, IProgressIndicator,
139 jalview.api.StructureSelectionManagerProvider
141 private static int DEFAULT_MIN_WIDTH = 300;
143 private static int DEFAULT_MIN_HEIGHT = 250;
145 private static int ALIGN_FRAME_DEFAULT_MIN_WIDTH = 600;
147 private static int ALIGN_FRAME_DEFAULT_MIN_HEIGHT = 70;
149 private static final String EXPERIMENTAL_FEATURES = "EXPERIMENTAL_FEATURES";
151 private JalviewChangeSupport changeSupport = new JalviewChangeSupport();
154 * news reader - null if it was never started.
156 private BlogReader jvnews = null;
158 private File projectFile;
162 * @see jalview.gui.JalviewChangeSupport#addJalviewPropertyChangeListener(java.beans.PropertyChangeListener)
164 public void addJalviewPropertyChangeListener(
165 PropertyChangeListener listener)
167 changeSupport.addJalviewPropertyChangeListener(listener);
171 * @param propertyName
173 * @see jalview.gui.JalviewChangeSupport#addJalviewPropertyChangeListener(java.lang.String,
174 * java.beans.PropertyChangeListener)
176 public void addJalviewPropertyChangeListener(String propertyName,
177 PropertyChangeListener listener)
179 changeSupport.addJalviewPropertyChangeListener(propertyName, listener);
183 * @param propertyName
185 * @see jalview.gui.JalviewChangeSupport#removeJalviewPropertyChangeListener(java.lang.String,
186 * java.beans.PropertyChangeListener)
188 public void removeJalviewPropertyChangeListener(String propertyName,
189 PropertyChangeListener listener)
191 changeSupport.removeJalviewPropertyChangeListener(propertyName,
195 /** Singleton Desktop instance */
196 public static Desktop instance;
198 public static MyDesktopPane desktop;
200 public static MyDesktopPane getDesktop()
202 // BH 2018 could use currentThread() here as a reference to a
203 // Hashtable<Thread, MyDesktopPane> in JavaScript
207 static int openFrameCount = 0;
209 static final int xOffset = 30;
211 static final int yOffset = 30;
213 public static jalview.ws.jws1.Discoverer discoverer;
215 public static Object[] jalviewClipboard;
217 public static boolean internalCopy = false;
219 static int fileLoadingCount = 0;
221 class MyDesktopManager implements DesktopManager
224 private DesktopManager delegate;
226 public MyDesktopManager(DesktopManager delegate)
228 this.delegate = delegate;
232 public void activateFrame(JInternalFrame f)
236 delegate.activateFrame(f);
237 } catch (NullPointerException npe)
239 Point p = getMousePosition();
240 instance.showPasteMenu(p.x, p.y);
245 public void beginDraggingFrame(JComponent f)
247 delegate.beginDraggingFrame(f);
251 public void beginResizingFrame(JComponent f, int direction)
253 delegate.beginResizingFrame(f, direction);
257 public void closeFrame(JInternalFrame f)
259 delegate.closeFrame(f);
263 public void deactivateFrame(JInternalFrame f)
265 delegate.deactivateFrame(f);
269 public void deiconifyFrame(JInternalFrame f)
271 delegate.deiconifyFrame(f);
275 public void dragFrame(JComponent f, int newX, int newY)
281 delegate.dragFrame(f, newX, newY);
285 public void endDraggingFrame(JComponent f)
287 delegate.endDraggingFrame(f);
292 public void endResizingFrame(JComponent f)
294 delegate.endResizingFrame(f);
299 public void iconifyFrame(JInternalFrame f)
301 delegate.iconifyFrame(f);
305 public void maximizeFrame(JInternalFrame f)
307 delegate.maximizeFrame(f);
311 public void minimizeFrame(JInternalFrame f)
313 delegate.minimizeFrame(f);
317 public void openFrame(JInternalFrame f)
319 delegate.openFrame(f);
323 public void resizeFrame(JComponent f, int newX, int newY, int newWidth,
330 delegate.resizeFrame(f, newX, newY, newWidth, newHeight);
334 public void setBoundsForFrame(JComponent f, int newX, int newY,
335 int newWidth, int newHeight)
337 delegate.setBoundsForFrame(f, newX, newY, newWidth, newHeight);
340 // All other methods, simply delegate
345 * Creates a new Desktop object.
351 * A note to implementors. It is ESSENTIAL that any activities that might block
352 * are spawned off as threads rather than waited for during this constructor.
355 if (!Platform.isJS())
357 doVamsasClientCheck();
360 doConfigureStructurePrefs();
361 setTitle("Jalview " + jalview.bin.Cache.getProperty("VERSION"));
362 setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
363 boolean selmemusage = jalview.bin.Cache.getDefault("SHOW_MEMUSAGE",
365 boolean showjconsole = jalview.bin.Cache.getDefault("SHOW_JAVA_CONSOLE",
367 desktop = new MyDesktopPane(selmemusage);
370 showMemusage.setSelected(selmemusage);
371 desktop.setBackground(Color.white);
372 getContentPane().setLayout(new BorderLayout());
373 // alternate config - have scrollbars - see notes in JAL-153
374 // JScrollPane sp = new JScrollPane();
375 // sp.getViewport().setView(desktop);
376 // getContentPane().add(sp, BorderLayout.CENTER);
378 // BH 2018 - just an experiment to try unclipped JInternalFrames.
381 getRootPane().putClientProperty("swingjs.overflow.hidden", "false");
384 getContentPane().add(desktop, BorderLayout.CENTER);
385 desktop.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
387 // This line prevents Windows Look&Feel resizing all new windows to maximum
388 // if previous window was maximised
389 desktop.setDesktopManager(new MyDesktopManager(
390 (Platform.isWindowsAndNotJS() ? new DefaultDesktopManager()
391 : Platform.isAMacAndNotJS()
392 ? new AquaInternalFrameManager(
393 desktop.getDesktopManager())
394 : desktop.getDesktopManager())));
396 Rectangle dims = getLastKnownDimensions("");
403 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
404 int xPos = Math.max(5, (screenSize.width - 900) / 2);
405 int yPos = Math.max(5, (screenSize.height - 650) / 2);
406 setBounds(xPos, yPos, 900, 650);
409 boolean doFullLoad = /** @j2sNative ! */true;
413 jconsole = new Console(this, showjconsole);
414 // add essential build information
415 jconsole.setHeader("Jalview Version: "
416 + jalview.bin.Cache.getProperty("VERSION") + "\n"
417 + "Jalview Installation: "
418 + jalview.bin.Cache.getDefault("INSTALLATION", "unknown")
419 + "\n" + "Build Date: "
420 + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown") + "\n"
421 + "Java version: " + System.getProperty("java.version") + "\n"
422 + System.getProperty("os.arch") + " "
423 + System.getProperty("os.name") + " "
424 + System.getProperty("os.version"));
426 showConsole(showjconsole);
428 showNews.setVisible(false);
430 experimentalFeatures.setSelected(showExperimental());
432 getIdentifiersOrgData();
436 // Spawn a thread that shows the splashscreen
438 SwingUtilities.invokeLater(new Runnable()
447 // Thread off a new instance of the file chooser - this reduces the time it
448 // takes to open it later on.
449 new Thread(new Runnable()
454 Cache.log.debug("Filechooser init thread started.");
455 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
456 JalviewFileChooser.forRead(Cache.getProperty("LAST_DIRECTORY"),
458 Cache.log.debug("Filechooser init thread finished.");
461 // Add the service change listener
462 changeSupport.addJalviewPropertyChangeListener("services",
463 new PropertyChangeListener()
467 public void propertyChange(PropertyChangeEvent evt)
469 Cache.log.debug("Firing service changed event for "
470 + evt.getNewValue());
471 JalviewServicesChanged(evt);
478 this.setDropTarget(new java.awt.dnd.DropTarget(desktop, this));
480 this.addWindowListener(new WindowAdapter()
483 public void windowClosing(WindowEvent evt)
490 this.addMouseListener(ma = new MouseAdapter()
493 public void mousePressed(MouseEvent evt)
495 if (evt.isPopupTrigger()) // Mac
497 showPasteMenu(evt.getX(), evt.getY());
502 public void mouseReleased(MouseEvent evt)
504 if (evt.isPopupTrigger()) // Windows
506 showPasteMenu(evt.getX(), evt.getY());
510 desktop.addMouseListener(ma);
515 * Answers true if user preferences to enable experimental features is True
520 public boolean showExperimental()
522 String experimental = Cache.getDefault(EXPERIMENTAL_FEATURES,
523 Boolean.FALSE.toString());
524 return Boolean.valueOf(experimental).booleanValue();
527 public void doConfigureStructurePrefs()
529 // configure services
530 StructureSelectionManager ssm = StructureSelectionManager
531 .getStructureSelectionManager(this);
532 if (jalview.bin.Cache.getDefault(Preferences.ADD_SS_ANN, true))
534 ssm.setAddTempFacAnnot(jalview.bin.Cache
535 .getDefault(Preferences.ADD_TEMPFACT_ANN, true));
536 ssm.setProcessSecondaryStructure(jalview.bin.Cache
537 .getDefault(Preferences.STRUCT_FROM_PDB, true));
538 ssm.setSecStructServices(
539 jalview.bin.Cache.getDefault(Preferences.USE_RNAVIEW, true));
543 ssm.setAddTempFacAnnot(false);
544 ssm.setProcessSecondaryStructure(false);
545 ssm.setSecStructServices(false);
549 public void checkForNews()
551 final Desktop me = this;
552 // Thread off the news reader, in case there are connection problems.
553 new Thread(new Runnable()
558 Cache.log.debug("Starting news thread.");
559 jvnews = new BlogReader(me);
560 showNews.setVisible(true);
561 Cache.log.debug("Completed news thread.");
566 public void getIdentifiersOrgData()
568 // Thread off the identifiers fetcher
569 new Thread(new Runnable()
574 Cache.log.debug("Downloading data from identifiers.org");
575 UrlDownloadClient client = new UrlDownloadClient();
578 client.download(IdOrgSettings.getUrl(),
579 IdOrgSettings.getDownloadLocation());
580 } catch (IOException e)
582 Cache.log.debug("Exception downloading identifiers.org data"
591 protected void showNews_actionPerformed(ActionEvent e)
593 showNews(showNews.isSelected());
596 void showNews(boolean visible)
598 Cache.log.debug((visible ? "Showing" : "Hiding") + " news.");
599 showNews.setSelected(visible);
600 if (visible && !jvnews.isVisible())
602 new Thread(new Runnable()
607 long now = System.currentTimeMillis();
608 Desktop.instance.setProgressBar(
609 MessageManager.getString("status.refreshing_news"), now);
610 jvnews.refreshNews();
611 Desktop.instance.setProgressBar(null, now);
619 * recover the last known dimensions for a jalview window
622 * - empty string is desktop, all other windows have unique prefix
623 * @return null or last known dimensions scaled to current geometry (if last
624 * window geom was known)
626 Rectangle getLastKnownDimensions(String windowName)
628 // TODO: lock aspect ratio for scaling desktop Bug #0058199
629 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
630 String x = jalview.bin.Cache.getProperty(windowName + "SCREEN_X");
631 String y = jalview.bin.Cache.getProperty(windowName + "SCREEN_Y");
632 String width = jalview.bin.Cache
633 .getProperty(windowName + "SCREEN_WIDTH");
634 String height = jalview.bin.Cache
635 .getProperty(windowName + "SCREEN_HEIGHT");
636 if ((x != null) && (y != null) && (width != null) && (height != null))
638 int ix = Integer.parseInt(x), iy = Integer.parseInt(y),
639 iw = Integer.parseInt(width), ih = Integer.parseInt(height);
640 if (jalview.bin.Cache.getProperty("SCREENGEOMETRY_WIDTH") != null)
642 // attempt #1 - try to cope with change in screen geometry - this
643 // version doesn't preserve original jv aspect ratio.
644 // take ratio of current screen size vs original screen size.
645 double sw = ((1f * screenSize.width) / (1f * Integer.parseInt(
646 jalview.bin.Cache.getProperty("SCREENGEOMETRY_WIDTH"))));
647 double sh = ((1f * screenSize.height) / (1f * Integer.parseInt(
648 jalview.bin.Cache.getProperty("SCREENGEOMETRY_HEIGHT"))));
649 // rescale the bounds depending upon the current screen geometry.
650 ix = (int) (ix * sw);
651 iw = (int) (iw * sw);
652 iy = (int) (iy * sh);
653 ih = (int) (ih * sh);
654 while (ix >= screenSize.width)
656 jalview.bin.Cache.log.debug(
657 "Window geometry location recall error: shifting horizontal to within screenbounds.");
658 ix -= screenSize.width;
660 while (iy >= screenSize.height)
662 jalview.bin.Cache.log.debug(
663 "Window geometry location recall error: shifting vertical to within screenbounds.");
664 iy -= screenSize.height;
666 jalview.bin.Cache.log.debug(
667 "Got last known dimensions for " + windowName + ": x:" + ix
668 + " y:" + iy + " width:" + iw + " height:" + ih);
670 // return dimensions for new instance
671 return new Rectangle(ix, iy, iw, ih);
676 private void doVamsasClientCheck()
678 if (Cache.vamsasJarsPresent())
680 setupVamsasDisconnectedGui();
681 VamsasMenu.setVisible(true);
682 final Desktop us = this;
683 VamsasMenu.addMenuListener(new MenuListener()
685 // this listener remembers when the menu was first selected, and
686 // doesn't rebuild the session list until it has been cleared and
688 boolean refresh = true;
691 public void menuCanceled(MenuEvent e)
697 public void menuDeselected(MenuEvent e)
703 public void menuSelected(MenuEvent e)
707 us.buildVamsasStMenu();
712 vamsasStart.setVisible(true);
716 void showPasteMenu(int x, int y)
718 JPopupMenu popup = new JPopupMenu();
719 JMenuItem item = new JMenuItem(
720 MessageManager.getString("label.paste_new_window"));
721 item.addActionListener(new ActionListener()
724 public void actionPerformed(ActionEvent evt)
731 popup.show(this, x, y);
738 Clipboard c = Toolkit.getDefaultToolkit().getSystemClipboard();
739 Transferable contents = c.getContents(this);
741 if (contents != null)
743 String file = (String) contents
744 .getTransferData(DataFlavor.stringFlavor);
746 FileFormatI format = new IdentifyFile().identify(file,
747 DataSourceType.PASTE);
749 new FileLoader().LoadFile(file, DataSourceType.PASTE, format);
752 } catch (Exception ex)
755 "Unable to paste alignment from system clipboard:\n" + ex);
760 * Adds and opens the given frame to the desktop
771 public static synchronized void addInternalFrame(
772 final JInternalFrame frame, String title, int w, int h)
774 addInternalFrame(frame, title, true, w, h, true, false);
778 * Add an internal frame to the Jalview desktop
785 * When true, display frame immediately, otherwise, caller must call
786 * setVisible themselves.
792 public static synchronized void addInternalFrame(
793 final JInternalFrame frame, String title, boolean makeVisible,
796 addInternalFrame(frame, title, makeVisible, w, h, true, false);
800 * Add an internal frame to the Jalview desktop and make it visible
813 public static synchronized void addInternalFrame(
814 final JInternalFrame frame, String title, int w, int h,
817 addInternalFrame(frame, title, true, w, h, resizable, false);
821 * Add an internal frame to the Jalview desktop
828 * When true, display frame immediately, otherwise, caller must call
829 * setVisible themselves.
836 * @param ignoreMinSize
837 * Do not set the default minimum size for frame
839 public static synchronized void addInternalFrame(
840 final JInternalFrame frame, String title, boolean makeVisible,
841 int w, int h, boolean resizable, boolean ignoreMinSize)
844 // TODO: allow callers to determine X and Y position of frame (eg. via
846 // TODO: consider fixing method to update entries in the window submenu with
847 // the current window title
849 frame.setTitle(title);
850 if (frame.getWidth() < 1 || frame.getHeight() < 1)
854 // THIS IS A PUBLIC STATIC METHOD, SO IT MAY BE CALLED EVEN IN
855 // A HEADLESS STATE WHEN NO DESKTOP EXISTS. MUST RETURN
856 // IF JALVIEW IS RUNNING HEADLESS
857 // ///////////////////////////////////////////////
858 if (instance == null || (System.getProperty("java.awt.headless") != null
859 && System.getProperty("java.awt.headless").equals("true")))
868 frame.setMinimumSize(
869 new Dimension(DEFAULT_MIN_WIDTH, DEFAULT_MIN_HEIGHT));
871 // Set default dimension for Alignment Frame window.
872 // The Alignment Frame window could be added from a number of places,
874 // I did this here in order not to miss out on any Alignment frame.
875 if (frame instanceof AlignFrame)
877 frame.setMinimumSize(new Dimension(ALIGN_FRAME_DEFAULT_MIN_WIDTH,
878 ALIGN_FRAME_DEFAULT_MIN_HEIGHT));
882 frame.setVisible(makeVisible);
883 frame.setClosable(true);
884 frame.setResizable(resizable);
885 frame.setMaximizable(resizable);
886 frame.setIconifiable(resizable);
887 frame.setOpaque(/** @j2sNative true || */
890 if (frame.getX() < 1 && frame.getY() < 1)
892 frame.setLocation(xOffset * openFrameCount,
893 yOffset * ((openFrameCount - 1) % 10) + yOffset);
897 * add an entry for the new frame in the Window menu
898 * (and remove it when the frame is closed)
900 final JMenuItem menuItem = new JMenuItem(title);
901 frame.addInternalFrameListener(new InternalFrameAdapter()
904 public void internalFrameActivated(InternalFrameEvent evt)
906 JInternalFrame itf = desktop.getSelectedFrame();
909 if (itf instanceof AlignFrame)
911 Jalview.setCurrentAlignFrame((AlignFrame) itf);
918 public void internalFrameClosed(InternalFrameEvent evt)
920 PaintRefresher.RemoveComponent(frame);
923 * defensive check to prevent frames being
924 * added half off the window
926 if (openFrameCount > 0)
932 * ensure no reference to alignFrame retained by menu item listener
934 if (menuItem.getActionListeners().length > 0)
936 menuItem.removeActionListener(menuItem.getActionListeners()[0]);
938 windowMenu.remove(menuItem);
942 menuItem.addActionListener(new ActionListener()
945 public void actionPerformed(ActionEvent e)
949 frame.setSelected(true);
950 frame.setIcon(false);
951 } catch (java.beans.PropertyVetoException ex)
953 // System.err.println(ex.toString());
958 setKeyBindings(frame);
962 windowMenu.add(menuItem);
967 frame.setSelected(true);
968 frame.requestFocus();
969 } catch (java.beans.PropertyVetoException ve)
971 } catch (java.lang.ClassCastException cex)
974 "Squashed a possible GUI implementation error. If you can recreate this, please look at http://issues.jalview.org/browse/JAL-869",
980 * Add key bindings to a JInternalFrame so that Ctrl-W and Cmd-W will close the
985 private static void setKeyBindings(JInternalFrame frame)
987 @SuppressWarnings("serial")
988 final Action closeAction = new AbstractAction()
991 public void actionPerformed(ActionEvent e)
998 * set up key bindings for Ctrl-W and Cmd-W, with the same (Close) action
1000 KeyStroke ctrlWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
1001 InputEvent.CTRL_DOWN_MASK);
1002 KeyStroke cmdWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
1003 Toolkit.getDefaultToolkit().getMenuShortcutKeyMask());
1005 InputMap inputMap = frame
1006 .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
1007 String ctrlW = ctrlWKey.toString();
1008 inputMap.put(ctrlWKey, ctrlW);
1009 inputMap.put(cmdWKey, ctrlW);
1011 ActionMap actionMap = frame.getActionMap();
1012 actionMap.put(ctrlW, closeAction);
1016 public void lostOwnership(Clipboard clipboard, Transferable contents)
1020 Desktop.jalviewClipboard = null;
1023 internalCopy = false;
1027 public void dragEnter(DropTargetDragEvent evt)
1032 public void dragExit(DropTargetEvent evt)
1037 public void dragOver(DropTargetDragEvent evt)
1042 public void dropActionChanged(DropTargetDragEvent evt)
1053 public void drop(DropTargetDropEvent evt)
1055 boolean success = true;
1056 // JAL-1552 - acceptDrop required before getTransferable call for
1057 // Java's Transferable for native dnd
1058 evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
1059 Transferable t = evt.getTransferable();
1060 List<Object> files = new ArrayList<>();
1061 List<DataSourceType> protocols = new ArrayList<>();
1065 Desktop.transferFromDropTarget(files, protocols, evt, t);
1066 } catch (Exception e)
1068 e.printStackTrace();
1076 for (int i = 0; i < files.size(); i++)
1078 // BH 2018 File or String
1079 Object file = files.get(i);
1080 String fileName = file.toString();
1081 DataSourceType protocol = (protocols == null)
1082 ? DataSourceType.FILE
1084 FileFormatI format = null;
1086 if (fileName.endsWith(".jar"))
1088 format = FileFormat.Jalview;
1093 format = new IdentifyFile().identify(file, protocol);
1096 new FileLoader().LoadFile(null, file, protocol, format);
1099 } catch (Exception ex)
1104 evt.dropComplete(success); // need this to ensure input focus is properly
1105 // transfered to any new windows created
1115 public void inputLocalFileMenuItem_actionPerformed(AlignViewport viewport)
1117 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
1118 JalviewFileChooser chooser = JalviewFileChooser
1119 .forRead(Cache.getProperty("LAST_DIRECTORY"), fileFormat, true);
1121 chooser.setFileView(new JalviewFileView());
1122 chooser.setDialogTitle(
1123 MessageManager.getString("label.open_local_file"));
1124 chooser.setToolTipText(MessageManager.getString("action.open"));
1126 chooser.setResponseHandler(0, new Runnable()
1131 File selectedFile = chooser.getSelectedFile();
1132 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1134 FileFormatI format = chooser.getSelectedFormat();
1137 * Call IdentifyFile to verify the file contains what its extension implies.
1138 * Skip this step for dynamically added file formats, because
1139 * IdentifyFile does not know how to recognise them.
1141 if (FileFormats.getInstance().isIdentifiable(format))
1145 format = new IdentifyFile().identify(selectedFile,
1146 DataSourceType.FILE);
1147 } catch (FileFormatException e)
1149 // format = null; //??
1153 new FileLoader().LoadFile(viewport, selectedFile,
1154 DataSourceType.FILE, format);
1157 chooser.showOpenDialog(this);
1161 * Shows a dialog for input of a URL at which to retrieve alignment data
1166 public void inputURLMenuItem_actionPerformed(AlignViewport viewport)
1168 // This construct allows us to have a wider textfield
1170 JLabel label = new JLabel(
1171 MessageManager.getString("label.input_file_url"));
1173 JPanel panel = new JPanel(new GridLayout(2, 1));
1177 * the URL to fetch is
1178 * Java: an editable combobox with history
1179 * JS: (pending JAL-3038) a plain text field
1182 String urlBase = "http://www.";
1183 if (Platform.isJS())
1185 history = new JTextField(urlBase, 35);
1189 JComboBox<String> asCombo = new JComboBox<>();
1190 asCombo.setPreferredSize(new Dimension(400, 20));
1191 asCombo.setEditable(true);
1192 asCombo.addItem(urlBase);
1193 String historyItems = Cache.getProperty("RECENT_URL");
1194 if (historyItems != null)
1196 for (String token : historyItems.split("\\t"))
1198 asCombo.addItem(token);
1205 Object[] options = new Object[] { MessageManager.getString("action.ok"),
1206 MessageManager.getString("action.cancel") };
1207 Runnable action = new Runnable() {
1211 String url = Platform.isJS() ? ((JTextField) history).getText()
1212 : ((JComboBox<String>) history).getSelectedItem()
1215 if (url.toLowerCase().endsWith(".jar"))
1217 if (viewport != null)
1219 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1220 FileFormat.Jalview);
1224 new FileLoader().LoadFile(url, DataSourceType.URL,
1225 FileFormat.Jalview);
1230 FileFormatI format = null;
1233 format = new IdentifyFile().identify(url, DataSourceType.URL);
1234 } catch (FileFormatException e)
1236 // TODO revise error handling, distinguish between
1237 // URL not found and response not valid
1242 String msg = MessageManager.formatMessage("label.couldnt_locate", url);
1243 JvOptionPane.showInternalMessageDialog(Desktop.desktop, msg,
1244 MessageManager.getString("label.url_not_found"),
1245 JvOptionPane.WARNING_MESSAGE);
1250 if (viewport != null)
1252 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1257 new FileLoader().LoadFile(url, DataSourceType.URL, format);
1261 String dialogOption = MessageManager
1262 .getString("label.input_alignment_from_url");
1263 JvOptionPane.newOptionDialog(desktop).setResponseHandler(0, action)
1264 .showInternalDialog(panel, dialogOption,
1265 JvOptionPane.YES_NO_CANCEL_OPTION,
1266 JvOptionPane.PLAIN_MESSAGE, null, options,
1267 MessageManager.getString("action.ok"));
1271 * Opens the CutAndPaste window for the user to paste an alignment in to
1274 * - if not null, the pasted alignment is added to the current
1275 * alignment; if null, to a new alignment window
1278 public void inputTextboxMenuItem_actionPerformed(
1279 AlignmentViewPanel viewPanel)
1281 CutAndPasteTransfer cap = new CutAndPasteTransfer();
1282 cap.setForInput(viewPanel);
1283 Desktop.addInternalFrame(cap,
1284 MessageManager.getString("label.cut_paste_alignmen_file"), true,
1294 Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
1295 jalview.bin.Cache.setProperty("SCREENGEOMETRY_WIDTH",
1297 jalview.bin.Cache.setProperty("SCREENGEOMETRY_HEIGHT",
1298 screen.height + "");
1299 storeLastKnownDimensions("", new Rectangle(getBounds().x, getBounds().y,
1300 getWidth(), getHeight()));
1302 if (jconsole != null)
1304 storeLastKnownDimensions("JAVA_CONSOLE_", jconsole.getBounds());
1305 jconsole.stopConsole();
1309 storeLastKnownDimensions("JALVIEW_RSS_WINDOW_", jvnews.getBounds());
1312 if (dialogExecutor != null)
1314 dialogExecutor.shutdownNow();
1316 closeAll_actionPerformed(null);
1318 if (groovyConsole != null)
1320 // suppress a possible repeat prompt to save script
1321 groovyConsole.setDirty(false);
1322 groovyConsole.exit();
1327 private void storeLastKnownDimensions(String string, Rectangle jc)
1329 jalview.bin.Cache.log.debug("Storing last known dimensions for "
1330 + string + ": x:" + jc.x + " y:" + jc.y + " width:" + jc.width
1331 + " height:" + jc.height);
1333 jalview.bin.Cache.setProperty(string + "SCREEN_X", jc.x + "");
1334 jalview.bin.Cache.setProperty(string + "SCREEN_Y", jc.y + "");
1335 jalview.bin.Cache.setProperty(string + "SCREEN_WIDTH", jc.width + "");
1336 jalview.bin.Cache.setProperty(string + "SCREEN_HEIGHT", jc.height + "");
1346 public void aboutMenuItem_actionPerformed(ActionEvent e)
1348 // StringBuffer message = getAboutMessage(false);
1349 // JvOptionPane.showInternalMessageDialog(Desktop.desktop,
1351 // message.toString(), "About Jalview", JvOptionPane.INFORMATION_MESSAGE);
1352 new Thread(new Runnable()
1357 new SplashScreen(true);
1362 public StringBuffer getAboutMessage(boolean shortv)
1364 StringBuffer message = new StringBuffer();
1365 message.append("<html>");
1368 message.append("<h1><strong>Version: "
1369 + jalview.bin.Cache.getProperty("VERSION")
1370 + "</strong></h1>");
1371 message.append("<strong>Last Updated: <em>"
1372 + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown")
1373 + "</em></strong>");
1379 message.append("<strong>Version "
1380 + jalview.bin.Cache.getProperty("VERSION")
1381 + "; last updated: "
1382 + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown"));
1385 if (jalview.bin.Cache.getDefault("LATEST_VERSION", "Checking")
1386 .equals("Checking"))
1388 message.append("<br>...Checking latest version...</br>");
1390 else if (!jalview.bin.Cache.getDefault("LATEST_VERSION", "Checking")
1391 .equals(jalview.bin.Cache.getProperty("VERSION")))
1393 boolean red = false;
1394 if (jalview.bin.Cache.getProperty("VERSION").toLowerCase()
1395 .indexOf("automated build") == -1)
1398 // Displayed when code version and jnlp version do not match and code
1399 // version is not a development build
1400 message.append("<div style=\"color: #FF0000;font-style: bold;\">");
1403 message.append("<br>!! Version "
1404 + jalview.bin.Cache.getDefault("LATEST_VERSION",
1406 + " is available for download from "
1407 + jalview.bin.Cache.getDefault("www.jalview.org",
1408 "http://www.jalview.org")
1412 message.append("</div>");
1415 message.append("<br>Authors: " + jalview.bin.Cache.getDefault(
1417 "The Jalview Authors (See AUTHORS file for current list)")
1418 + "<br><br>Development managed by The Barton Group, University of Dundee, Scotland, UK.<br>"
1419 + "<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"
1420 + "<br><br>If you use Jalview, please cite:"
1421 + "<br>Waterhouse, A.M., Procter, J.B., Martin, D.M.A, Clamp, M. and Barton, G. J. (2009)"
1422 + "<br>Jalview Version 2 - a multiple sequence alignment editor and analysis workbench"
1423 + "<br>Bioinformatics doi: 10.1093/bioinformatics/btp033"
1429 * Action on requesting Help documentation
1432 public void documentationMenuItem_actionPerformed()
1436 if (Platform.isJS())
1438 BrowserLauncher.openURL("http://www.jalview.org/help.html");
1442 Help.showHelpWindow();
1444 } catch (Exception ex)
1446 System.err.println("Error opening help: " + ex.getMessage());
1451 public void closeAll_actionPerformed(ActionEvent e)
1453 // TODO show a progress bar while closing?
1454 JInternalFrame[] frames = desktop.getAllFrames();
1455 for (int i = 0; i < frames.length; i++)
1459 frames[i].setClosed(true);
1460 } catch (java.beans.PropertyVetoException ex)
1464 Jalview.setCurrentAlignFrame(null);
1465 System.out.println("ALL CLOSED");
1466 if (v_client != null)
1468 // TODO clear binding to vamsas document objects on close_all
1472 * reset state of singleton objects as appropriate (clear down session state
1473 * when all windows are closed)
1475 StructureSelectionManager ssm = StructureSelectionManager
1476 .getStructureSelectionManager(this);
1484 public void raiseRelated_actionPerformed(ActionEvent e)
1486 reorderAssociatedWindows(false, false);
1490 public void minimizeAssociated_actionPerformed(ActionEvent e)
1492 reorderAssociatedWindows(true, false);
1495 void closeAssociatedWindows()
1497 reorderAssociatedWindows(false, true);
1503 * @seejalview.jbgui.GDesktop#garbageCollect_actionPerformed(java.awt.event.
1507 protected void garbageCollect_actionPerformed(ActionEvent e)
1509 // We simply collect the garbage
1510 jalview.bin.Cache.log.debug("Collecting garbage...");
1512 jalview.bin.Cache.log.debug("Finished garbage collection.");
1519 * jalview.jbgui.GDesktop#showMemusage_actionPerformed(java.awt.event.ActionEvent
1523 protected void showMemusage_actionPerformed(ActionEvent e)
1525 desktop.showMemoryUsage(showMemusage.isSelected());
1532 * jalview.jbgui.GDesktop#showConsole_actionPerformed(java.awt.event.ActionEvent
1536 protected void showConsole_actionPerformed(ActionEvent e)
1538 showConsole(showConsole.isSelected());
1541 Console jconsole = null;
1544 * control whether the java console is visible or not
1548 void showConsole(boolean selected)
1550 // TODO: decide if we should update properties file
1551 if (jconsole != null) // BH 2018
1553 showConsole.setSelected(selected);
1554 Cache.setProperty("SHOW_JAVA_CONSOLE",
1555 Boolean.valueOf(selected).toString());
1556 jconsole.setVisible(selected);
1560 void reorderAssociatedWindows(boolean minimize, boolean close)
1562 JInternalFrame[] frames = desktop.getAllFrames();
1563 if (frames == null || frames.length < 1)
1568 AlignmentViewport source = null, target = null;
1569 if (frames[0] instanceof AlignFrame)
1571 source = ((AlignFrame) frames[0]).getCurrentView();
1573 else if (frames[0] instanceof TreePanel)
1575 source = ((TreePanel) frames[0]).getViewPort();
1577 else if (frames[0] instanceof PCAPanel)
1579 source = ((PCAPanel) frames[0]).av;
1581 else if (frames[0].getContentPane() instanceof PairwiseAlignPanel)
1583 source = ((PairwiseAlignPanel) frames[0].getContentPane()).av;
1588 for (int i = 0; i < frames.length; i++)
1591 if (frames[i] == null)
1595 if (frames[i] instanceof AlignFrame)
1597 target = ((AlignFrame) frames[i]).getCurrentView();
1599 else if (frames[i] instanceof TreePanel)
1601 target = ((TreePanel) frames[i]).getViewPort();
1603 else if (frames[i] instanceof PCAPanel)
1605 target = ((PCAPanel) frames[i]).av;
1607 else if (frames[i].getContentPane() instanceof PairwiseAlignPanel)
1609 target = ((PairwiseAlignPanel) frames[i].getContentPane()).av;
1612 if (source == target)
1618 frames[i].setClosed(true);
1622 frames[i].setIcon(minimize);
1625 frames[i].toFront();
1629 } catch (java.beans.PropertyVetoException ex)
1644 protected void preferences_actionPerformed(ActionEvent e)
1650 * Prompts the user to choose a file and then saves the Jalview state as a
1651 * Jalview project file
1654 public void saveState_actionPerformed()
1656 saveState_actionPerformed(false);
1659 public void saveState_actionPerformed(boolean saveAs)
1661 java.io.File projectFile = getProjectFile();
1662 // autoSave indicates we already have a file and don't need to ask
1663 boolean autoSave = projectFile != null && !saveAs
1664 && BackupFiles.getEnabled();
1666 // System.out.println("autoSave="+autoSave+", projectFile='"+projectFile+"',
1667 // saveAs="+saveAs+", Backups
1668 // "+(BackupFiles.getEnabled()?"enabled":"disabled"));
1670 boolean approveSave = false;
1673 JalviewFileChooser chooser = new JalviewFileChooser("jvp",
1676 chooser.setFileView(new JalviewFileView());
1677 chooser.setDialogTitle(MessageManager.getString("label.save_state"));
1679 int value = chooser.showSaveDialog(this);
1681 if (value == JalviewFileChooser.APPROVE_OPTION)
1683 projectFile = chooser.getSelectedFile();
1684 setProjectFile(projectFile);
1689 if (approveSave || autoSave)
1691 final Desktop me = this;
1692 final java.io.File chosenFile = projectFile;
1693 new Thread(new Runnable()
1698 // TODO: refactor to Jalview desktop session controller action.
1699 setProgressBar(MessageManager.formatMessage(
1700 "label.saving_jalview_project", new Object[]
1701 { chosenFile.getName() }), chosenFile.hashCode());
1702 jalview.bin.Cache.setProperty("LAST_DIRECTORY",
1703 chosenFile.getParent());
1704 // TODO catch and handle errors for savestate
1705 // TODO prevent user from messing with the Desktop whilst we're saving
1708 BackupFiles backupfiles = new BackupFiles(chosenFile);
1710 new Jalview2XML().saveState(backupfiles.getTempFile());
1712 backupfiles.setWriteSuccess(true);
1713 backupfiles.rollBackupsAndRenameTempFile();
1714 } catch (OutOfMemoryError oom)
1716 new OOMWarning("Whilst saving current state to "
1717 + chosenFile.getName(), oom);
1718 } catch (Exception ex)
1720 Cache.log.error("Problems whilst trying to save to "
1721 + chosenFile.getName(), ex);
1722 JvOptionPane.showMessageDialog(me,
1723 MessageManager.formatMessage(
1724 "label.error_whilst_saving_current_state_to",
1726 { chosenFile.getName() }),
1727 MessageManager.getString("label.couldnt_save_project"),
1728 JvOptionPane.WARNING_MESSAGE);
1730 setProgressBar(null, chosenFile.hashCode());
1737 public void saveAsState_actionPerformed(ActionEvent e)
1739 saveState_actionPerformed(true);
1742 private void setProjectFile(File choice)
1744 this.projectFile = choice;
1747 public File getProjectFile()
1749 return this.projectFile;
1753 * Shows a file chooser dialog and tries to read in the selected file as a
1757 public void loadState_actionPerformed()
1759 final String[] suffix = new String[] { "jvp", "jar" };
1760 final String[] desc = new String[] { "Jalview Project",
1761 "Jalview Project (old)" };
1762 JalviewFileChooser chooser = new JalviewFileChooser(
1763 Cache.getProperty("LAST_DIRECTORY"), suffix, desc,
1764 "Jalview Project", true, true); // last two booleans: allFiles,
1766 chooser.setFileView(new JalviewFileView());
1767 chooser.setDialogTitle(MessageManager.getString("label.restore_state"));
1768 chooser.setResponseHandler(0, new Runnable()
1773 File selectedFile = chooser.getSelectedFile();
1774 setProjectFile(selectedFile);
1775 String choice = selectedFile.getAbsolutePath();
1776 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1777 new Thread(new Runnable()
1784 new Jalview2XML().loadJalviewAlign(choice);
1785 } catch (OutOfMemoryError oom)
1787 new OOMWarning("Whilst loading project from " + choice, oom);
1788 } catch (Exception ex)
1791 "Problems whilst loading project from " + choice, ex);
1792 JvOptionPane.showMessageDialog(Desktop.desktop,
1793 MessageManager.formatMessage(
1794 "label.error_whilst_loading_project_from",
1797 MessageManager.getString("label.couldnt_load_project"),
1798 JvOptionPane.WARNING_MESSAGE);
1805 chooser.showOpenDialog(this);
1809 public void inputSequence_actionPerformed(ActionEvent e)
1811 new SequenceFetcher(this);
1814 JPanel progressPanel;
1816 ArrayList<JPanel> fileLoadingPanels = new ArrayList<>();
1818 public void startLoading(final Object fileName)
1820 if (fileLoadingCount == 0)
1822 fileLoadingPanels.add(addProgressPanel(MessageManager
1823 .formatMessage("label.loading_file", new Object[]
1829 private JPanel addProgressPanel(String string)
1831 if (progressPanel == null)
1833 progressPanel = new JPanel(new GridLayout(1, 1));
1834 totalProgressCount = 0;
1835 instance.getContentPane().add(progressPanel, BorderLayout.SOUTH);
1837 JPanel thisprogress = new JPanel(new BorderLayout(10, 5));
1838 JProgressBar progressBar = new JProgressBar();
1839 progressBar.setIndeterminate(true);
1841 thisprogress.add(new JLabel(string), BorderLayout.WEST);
1843 thisprogress.add(progressBar, BorderLayout.CENTER);
1844 progressPanel.add(thisprogress);
1845 ((GridLayout) progressPanel.getLayout()).setRows(
1846 ((GridLayout) progressPanel.getLayout()).getRows() + 1);
1847 ++totalProgressCount;
1848 instance.validate();
1849 return thisprogress;
1852 int totalProgressCount = 0;
1854 private void removeProgressPanel(JPanel progbar)
1856 if (progressPanel != null)
1858 synchronized (progressPanel)
1860 progressPanel.remove(progbar);
1861 GridLayout gl = (GridLayout) progressPanel.getLayout();
1862 gl.setRows(gl.getRows() - 1);
1863 if (--totalProgressCount < 1)
1865 this.getContentPane().remove(progressPanel);
1866 progressPanel = null;
1873 public void stopLoading()
1876 if (fileLoadingCount < 1)
1878 while (fileLoadingPanels.size() > 0)
1880 removeProgressPanel(fileLoadingPanels.remove(0));
1882 fileLoadingPanels.clear();
1883 fileLoadingCount = 0;
1888 public static int getViewCount(String alignmentId)
1890 AlignmentViewport[] aps = getViewports(alignmentId);
1891 return (aps == null) ? 0 : aps.length;
1896 * @param alignmentId
1897 * - if null, all sets are returned
1898 * @return all AlignmentPanels concerning the alignmentId sequence set
1900 public static AlignmentPanel[] getAlignmentPanels(String alignmentId)
1902 if (Desktop.desktop == null)
1904 // no frames created and in headless mode
1905 // TODO: verify that frames are recoverable when in headless mode
1908 List<AlignmentPanel> aps = new ArrayList<>();
1909 AlignFrame[] frames = getAlignFrames();
1914 for (AlignFrame af : frames)
1916 for (AlignmentPanel ap : af.alignPanels)
1918 if (alignmentId == null
1919 || alignmentId.equals(ap.av.getSequenceSetId()))
1925 if (aps.size() == 0)
1929 AlignmentPanel[] vap = aps.toArray(new AlignmentPanel[aps.size()]);
1934 * get all the viewports on an alignment.
1936 * @param sequenceSetId
1937 * unique alignment id (may be null - all viewports returned in that
1939 * @return all viewports on the alignment bound to sequenceSetId
1941 public static AlignmentViewport[] getViewports(String sequenceSetId)
1943 List<AlignmentViewport> viewp = new ArrayList<>();
1944 if (desktop != null)
1946 AlignFrame[] frames = Desktop.getAlignFrames();
1948 for (AlignFrame afr : frames)
1950 if (sequenceSetId == null || afr.getViewport().getSequenceSetId()
1951 .equals(sequenceSetId))
1953 if (afr.alignPanels != null)
1955 for (AlignmentPanel ap : afr.alignPanels)
1957 if (sequenceSetId == null
1958 || sequenceSetId.equals(ap.av.getSequenceSetId()))
1966 viewp.add(afr.getViewport());
1970 if (viewp.size() > 0)
1972 return viewp.toArray(new AlignmentViewport[viewp.size()]);
1979 * Explode the views in the given frame into separate AlignFrame
1983 public static void explodeViews(AlignFrame af)
1985 int size = af.alignPanels.size();
1991 for (int i = 0; i < size; i++)
1993 AlignmentPanel ap = af.alignPanels.get(i);
1994 AlignFrame newaf = new AlignFrame(ap);
1997 * Restore the view's last exploded frame geometry if known. Multiple
1998 * views from one exploded frame share and restore the same (frame)
1999 * position and size.
2001 Rectangle geometry = ap.av.getExplodedGeometry();
2002 if (geometry != null)
2004 newaf.setBounds(geometry);
2007 ap.av.setGatherViewsHere(false);
2009 addInternalFrame(newaf, af.getTitle(), AlignFrame.DEFAULT_WIDTH,
2010 AlignFrame.DEFAULT_HEIGHT);
2013 af.alignPanels.clear();
2014 af.closeMenuItem_actionPerformed(true);
2019 * Gather expanded views (separate AlignFrame's) with the same sequence set
2020 * identifier back in to this frame as additional views, and close the expanded
2021 * views. Note the expanded frames may themselves have multiple views. We take
2026 public void gatherViews(AlignFrame source)
2028 source.viewport.setGatherViewsHere(true);
2029 source.viewport.setExplodedGeometry(source.getBounds());
2030 JInternalFrame[] frames = desktop.getAllFrames();
2031 String viewId = source.viewport.getSequenceSetId();
2033 for (int t = 0; t < frames.length; t++)
2035 if (frames[t] instanceof AlignFrame && frames[t] != source)
2037 AlignFrame af = (AlignFrame) frames[t];
2038 boolean gatherThis = false;
2039 for (int a = 0; a < af.alignPanels.size(); a++)
2041 AlignmentPanel ap = af.alignPanels.get(a);
2042 if (viewId.equals(ap.av.getSequenceSetId()))
2045 ap.av.setGatherViewsHere(false);
2046 ap.av.setExplodedGeometry(af.getBounds());
2047 source.addAlignmentPanel(ap, false);
2053 af.alignPanels.clear();
2054 af.closeMenuItem_actionPerformed(true);
2061 jalview.gui.VamsasApplication v_client = null;
2064 public void vamsasImport_actionPerformed(ActionEvent e)
2066 // TODO: JAL-3048 not needed for Jalview-JS
2068 if (v_client == null)
2070 // Load and try to start a session.
2071 JalviewFileChooser chooser = new JalviewFileChooser(
2072 jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
2074 chooser.setFileView(new JalviewFileView());
2075 chooser.setDialogTitle(
2076 MessageManager.getString("label.open_saved_vamsas_session"));
2077 chooser.setToolTipText(MessageManager.getString(
2078 "label.select_vamsas_session_opened_as_new_vamsas_session"));
2080 int value = chooser.showOpenDialog(this);
2082 if (value == JalviewFileChooser.APPROVE_OPTION)
2084 String fle = chooser.getSelectedFile().toString();
2085 if (!vamsasImport(chooser.getSelectedFile()))
2087 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
2088 MessageManager.formatMessage(
2089 "label.couldnt_import_as_vamsas_session",
2093 .getString("label.vamsas_document_import_failed"),
2094 JvOptionPane.ERROR_MESSAGE);
2100 jalview.bin.Cache.log.error(
2101 "Implementation error - load session from a running session is not supported.");
2106 * import file into a new vamsas session (uses jalview.gui.VamsasApplication)
2109 * @return true if import was a success and a session was started.
2111 public boolean vamsasImport(URL url)
2113 // TODO: create progress bar
2114 if (v_client != null)
2117 jalview.bin.Cache.log.error(
2118 "Implementation error - load session from a running session is not supported.");
2124 // copy the URL content to a temporary local file
2125 // TODO: be a bit cleverer here with nio (?!)
2126 File file = File.createTempFile("vdocfromurl", ".vdj");
2127 FileOutputStream fos = new FileOutputStream(file);
2128 BufferedInputStream bis = new BufferedInputStream(url.openStream());
2129 byte[] buffer = new byte[2048];
2131 while ((ln = bis.read(buffer)) > -1)
2133 fos.write(buffer, 0, ln);
2137 v_client = new jalview.gui.VamsasApplication(this, file,
2138 url.toExternalForm());
2139 } catch (Exception ex)
2141 jalview.bin.Cache.log.error(
2142 "Failed to create new vamsas session from contents of URL "
2147 setupVamsasConnectedGui();
2148 v_client.initial_update(); // TODO: thread ?
2149 return v_client.inSession();
2153 * import file into a new vamsas session (uses jalview.gui.VamsasApplication)
2156 * @return true if import was a success and a session was started.
2158 public boolean vamsasImport(File file)
2160 if (v_client != null)
2163 jalview.bin.Cache.log.error(
2164 "Implementation error - load session from a running session is not supported.");
2168 setProgressBar(MessageManager.formatMessage(
2169 "status.importing_vamsas_session_from", new Object[]
2170 { file.getName() }), file.hashCode());
2173 v_client = new jalview.gui.VamsasApplication(this, file, null);
2174 } catch (Exception ex)
2176 setProgressBar(MessageManager.formatMessage(
2177 "status.importing_vamsas_session_from", new Object[]
2178 { file.getName() }), file.hashCode());
2179 jalview.bin.Cache.log.error(
2180 "New vamsas session from existing session file failed:", ex);
2183 setupVamsasConnectedGui();
2184 v_client.initial_update(); // TODO: thread ?
2185 setProgressBar(MessageManager.formatMessage(
2186 "status.importing_vamsas_session_from", new Object[]
2187 { file.getName() }), file.hashCode());
2188 return v_client.inSession();
2191 public boolean joinVamsasSession(String mysesid)
2193 if (v_client != null)
2195 throw new Error(MessageManager
2196 .getString("error.try_join_vamsas_session_another"));
2198 if (mysesid == null)
2201 MessageManager.getString("error.invalid_vamsas_session_id"));
2203 v_client = new VamsasApplication(this, mysesid);
2204 setupVamsasConnectedGui();
2205 v_client.initial_update();
2206 return (v_client.inSession());
2210 public void vamsasStart_actionPerformed(ActionEvent e)
2212 if (v_client == null)
2215 // we just start a default session for moment.
2217 * JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache.
2218 * getProperty("LAST_DIRECTORY"));
2220 * chooser.setFileView(new JalviewFileView());
2221 * chooser.setDialogTitle("Load Vamsas file");
2222 * chooser.setToolTipText("Import");
2224 * int value = chooser.showOpenDialog(this);
2226 * if (value == JalviewFileChooser.APPROVE_OPTION) { v_client = new
2227 * jalview.gui.VamsasApplication(this, chooser.getSelectedFile());
2229 v_client = new VamsasApplication(this);
2230 setupVamsasConnectedGui();
2231 v_client.initial_update(); // TODO: thread ?
2235 // store current data in session.
2236 v_client.push_update(); // TODO: thread
2240 protected void setupVamsasConnectedGui()
2242 vamsasStart.setText(MessageManager.getString("label.session_update"));
2243 vamsasSave.setVisible(true);
2244 vamsasStop.setVisible(true);
2245 vamsasImport.setVisible(false); // Document import to existing session is
2246 // not possible for vamsas-client-1.0.
2249 protected void setupVamsasDisconnectedGui()
2251 vamsasSave.setVisible(false);
2252 vamsasStop.setVisible(false);
2253 vamsasImport.setVisible(true);
2255 .setText(MessageManager.getString("label.new_vamsas_session"));
2259 public void vamsasStop_actionPerformed(ActionEvent e)
2261 if (v_client != null)
2263 v_client.end_session();
2265 setupVamsasDisconnectedGui();
2269 protected void buildVamsasStMenu()
2271 if (v_client == null)
2273 String[] sess = null;
2276 sess = VamsasApplication.getSessionList();
2277 } catch (Exception e)
2279 jalview.bin.Cache.log.warn("Problem getting current sessions list.",
2285 jalview.bin.Cache.log.debug(
2286 "Got current sessions list: " + sess.length + " entries.");
2287 VamsasStMenu.removeAll();
2288 for (int i = 0; i < sess.length; i++)
2290 JMenuItem sessit = new JMenuItem();
2291 sessit.setText(sess[i]);
2292 sessit.setToolTipText(MessageManager
2293 .formatMessage("label.connect_to_session", new Object[]
2295 final Desktop dsktp = this;
2296 final String mysesid = sess[i];
2297 sessit.addActionListener(new ActionListener()
2301 public void actionPerformed(ActionEvent e)
2303 if (dsktp.v_client == null)
2305 Thread rthr = new Thread(new Runnable()
2311 dsktp.v_client = new VamsasApplication(dsktp, mysesid);
2312 dsktp.setupVamsasConnectedGui();
2313 dsktp.v_client.initial_update();
2321 VamsasStMenu.add(sessit);
2323 // don't show an empty menu.
2324 VamsasStMenu.setVisible(sess.length > 0);
2329 jalview.bin.Cache.log.debug("No current vamsas sessions.");
2330 VamsasStMenu.removeAll();
2331 VamsasStMenu.setVisible(false);
2336 // Not interested in the content. Just hide ourselves.
2337 VamsasStMenu.setVisible(false);
2342 public void vamsasSave_actionPerformed(ActionEvent e)
2344 // TODO: JAL-3048 not needed for Jalview-JS
2346 if (v_client != null)
2348 // TODO: VAMSAS DOCUMENT EXTENSION is VDJ
2349 JalviewFileChooser chooser = new JalviewFileChooser("vdj",
2352 chooser.setFileView(new JalviewFileView());
2353 chooser.setDialogTitle(MessageManager
2354 .getString("label.save_vamsas_document_archive"));
2356 int value = chooser.showSaveDialog(this);
2358 if (value == JalviewFileChooser.APPROVE_OPTION)
2360 java.io.File choice = chooser.getSelectedFile();
2361 JPanel progpanel = addProgressPanel(MessageManager
2362 .formatMessage("label.saving_vamsas_doc", new Object[]
2363 { choice.getName() }));
2364 Cache.setProperty("LAST_DIRECTORY", choice.getParent());
2365 String warnmsg = null;
2366 String warnttl = null;
2369 v_client.vclient.storeDocument(choice);
2372 warnttl = "Serious Problem saving Vamsas Document";
2373 warnmsg = ex.toString();
2374 jalview.bin.Cache.log
2375 .error("Error Whilst saving document to " + choice, ex);
2377 } catch (Exception ex)
2379 warnttl = "Problem saving Vamsas Document.";
2380 warnmsg = ex.toString();
2381 jalview.bin.Cache.log.warn(
2382 "Exception Whilst saving document to " + choice, ex);
2385 removeProgressPanel(progpanel);
2386 if (warnmsg != null)
2388 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
2390 warnmsg, warnttl, JvOptionPane.ERROR_MESSAGE);
2396 JPanel vamUpdate = null;
2399 * hide vamsas user gui bits when a vamsas document event is being handled.
2402 * true to hide gui, false to reveal gui
2404 public void setVamsasUpdate(boolean b)
2406 Cache.log.debug("Setting gui for Vamsas update "
2407 + (b ? "in progress" : "finished"));
2409 if (vamUpdate != null)
2411 this.removeProgressPanel(vamUpdate);
2415 vamUpdate = this.addProgressPanel(
2416 MessageManager.getString("label.updating_vamsas_session"));
2418 vamsasStart.setVisible(!b);
2419 vamsasStop.setVisible(!b);
2420 vamsasSave.setVisible(!b);
2423 public JInternalFrame[] getAllFrames()
2425 return desktop.getAllFrames();
2429 * Checks the given url to see if it gives a response indicating that the user
2430 * should be informed of a new questionnaire.
2434 public void checkForQuestionnaire(String url)
2436 UserQuestionnaireCheck jvq = new UserQuestionnaireCheck(url);
2437 // javax.swing.SwingUtilities.invokeLater(jvq);
2438 new Thread(jvq).start();
2441 public void checkURLLinks()
2443 // Thread off the URL link checker
2444 addDialogThread(new Runnable()
2449 if (Cache.getDefault("CHECKURLLINKS", true))
2451 // check what the actual links are - if it's just the default don't
2452 // bother with the warning
2453 List<String> links = Preferences.sequenceUrlLinks
2456 // only need to check links if there is one with a
2457 // SEQUENCE_ID which is not the default EMBL_EBI link
2458 ListIterator<String> li = links.listIterator();
2459 boolean check = false;
2460 List<JLabel> urls = new ArrayList<>();
2461 while (li.hasNext())
2463 String link = li.next();
2464 if (link.contains(SEQUENCE_ID)
2465 && !UrlConstants.isDefaultString(link))
2468 int barPos = link.indexOf("|");
2469 String urlMsg = barPos == -1 ? link
2470 : link.substring(0, barPos) + ": "
2471 + link.substring(barPos + 1);
2472 urls.add(new JLabel(urlMsg));
2480 // ask user to check in case URL links use old style tokens
2481 // ($SEQUENCE_ID$ for sequence id _or_ accession id)
2482 JPanel msgPanel = new JPanel();
2483 msgPanel.setLayout(new BoxLayout(msgPanel, BoxLayout.PAGE_AXIS));
2484 msgPanel.add(Box.createVerticalGlue());
2485 JLabel msg = new JLabel(MessageManager
2486 .getString("label.SEQUENCE_ID_for_DB_ACCESSION1"));
2487 JLabel msg2 = new JLabel(MessageManager
2488 .getString("label.SEQUENCE_ID_for_DB_ACCESSION2"));
2490 for (JLabel url : urls)
2496 final JCheckBox jcb = new JCheckBox(
2497 MessageManager.getString("label.do_not_display_again"));
2498 jcb.addActionListener(new ActionListener()
2501 public void actionPerformed(ActionEvent e)
2503 // update Cache settings for "don't show this again"
2504 boolean showWarningAgain = !jcb.isSelected();
2505 Cache.setProperty("CHECKURLLINKS",
2506 Boolean.valueOf(showWarningAgain).toString());
2511 JvOptionPane.showMessageDialog(Desktop.desktop, msgPanel,
2513 .getString("label.SEQUENCE_ID_no_longer_used"),
2514 JvOptionPane.WARNING_MESSAGE);
2521 * Proxy class for JDesktopPane which optionally displays the current memory
2522 * usage and highlights the desktop area with a red bar if free memory runs low.
2526 public class MyDesktopPane extends JDesktopPane
2529 private static final float ONE_MB = 1048576f;
2531 boolean showMemoryUsage = false;
2535 java.text.NumberFormat df;
2537 float maxMemory, allocatedMemory, freeMemory, totalFreeMemory,
2540 public MyDesktopPane(boolean showMemoryUsage)
2542 showMemoryUsage(showMemoryUsage);
2545 public void showMemoryUsage(boolean showMemory)
2547 this.showMemoryUsage = showMemory;
2550 Thread worker = new Thread(this);
2556 public boolean isShowMemoryUsage()
2558 return showMemoryUsage;
2564 df = java.text.NumberFormat.getNumberInstance();
2565 df.setMaximumFractionDigits(2);
2566 runtime = Runtime.getRuntime();
2568 while (showMemoryUsage)
2572 maxMemory = runtime.maxMemory() / ONE_MB;
2573 allocatedMemory = runtime.totalMemory() / ONE_MB;
2574 freeMemory = runtime.freeMemory() / ONE_MB;
2575 totalFreeMemory = freeMemory + (maxMemory - allocatedMemory);
2577 percentUsage = (totalFreeMemory / maxMemory) * 100;
2579 // if (percentUsage < 20)
2581 // border1 = BorderFactory.createMatteBorder(12, 12, 12, 12,
2583 // instance.set.setBorder(border1);
2586 // sleep after showing usage
2588 } catch (Exception ex)
2590 ex.printStackTrace();
2596 public void paintComponent(Graphics g)
2598 if (showMemoryUsage && g != null && df != null)
2600 if (percentUsage < 20)
2602 g.setColor(Color.red);
2604 FontMetrics fm = g.getFontMetrics();
2607 g.drawString(MessageManager.formatMessage("label.memory_stats",
2609 { df.format(totalFreeMemory), df.format(maxMemory),
2610 df.format(percentUsage) }),
2611 10, getHeight() - fm.getHeight());
2618 * Accessor method to quickly get all the AlignmentFrames loaded.
2620 * @return an array of AlignFrame, or null if none found
2622 public static AlignFrame[] getAlignFrames()
2624 if (Jalview.isHeadlessMode())
2626 // Desktop.desktop is null in headless mode
2627 return new AlignFrame[] { Jalview.currentAlignFrame };
2630 JInternalFrame[] frames = Desktop.desktop.getAllFrames();
2636 List<AlignFrame> avp = new ArrayList<>();
2638 for (int i = frames.length - 1; i > -1; i--)
2640 if (frames[i] instanceof AlignFrame)
2642 avp.add((AlignFrame) frames[i]);
2644 else if (frames[i] instanceof SplitFrame)
2647 * Also check for a split frame containing an AlignFrame
2649 GSplitFrame sf = (GSplitFrame) frames[i];
2650 if (sf.getTopFrame() instanceof AlignFrame)
2652 avp.add((AlignFrame) sf.getTopFrame());
2654 if (sf.getBottomFrame() instanceof AlignFrame)
2656 avp.add((AlignFrame) sf.getBottomFrame());
2660 if (avp.size() == 0)
2664 AlignFrame afs[] = avp.toArray(new AlignFrame[avp.size()]);
2669 * Returns an array of any AppJmol frames in the Desktop (or null if none).
2673 public GStructureViewer[] getJmols()
2675 JInternalFrame[] frames = Desktop.desktop.getAllFrames();
2681 List<GStructureViewer> avp = new ArrayList<>();
2683 for (int i = frames.length - 1; i > -1; i--)
2685 if (frames[i] instanceof AppJmol)
2687 GStructureViewer af = (GStructureViewer) frames[i];
2691 if (avp.size() == 0)
2695 GStructureViewer afs[] = avp.toArray(new GStructureViewer[avp.size()]);
2700 * Add Groovy Support to Jalview
2703 public void groovyShell_actionPerformed()
2707 openGroovyConsole();
2708 } catch (Exception ex)
2710 jalview.bin.Cache.log.error("Groovy Shell Creation failed.", ex);
2711 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
2713 MessageManager.getString("label.couldnt_create_groovy_shell"),
2714 MessageManager.getString("label.groovy_support_failed"),
2715 JvOptionPane.ERROR_MESSAGE);
2720 * Open the Groovy console
2722 void openGroovyConsole()
2724 if (groovyConsole == null)
2726 groovyConsole = new groovy.ui.Console();
2727 groovyConsole.setVariable("Jalview", this);
2728 groovyConsole.run();
2731 * We allow only one console at a time, so that AlignFrame menu option
2732 * 'Calculate | Run Groovy script' is unambiguous.
2733 * Disable 'Groovy Console', and enable 'Run script', when the console is
2734 * opened, and the reverse when it is closed
2736 Window window = (Window) groovyConsole.getFrame();
2737 window.addWindowListener(new WindowAdapter()
2740 public void windowClosed(WindowEvent e)
2743 * rebind CMD-Q from Groovy Console to Jalview Quit
2746 enableExecuteGroovy(false);
2752 * show Groovy console window (after close and reopen)
2754 ((Window) groovyConsole.getFrame()).setVisible(true);
2757 * if we got this far, enable 'Run Groovy' in AlignFrame menus
2758 * and disable opening a second console
2760 enableExecuteGroovy(true);
2764 * Bind Ctrl/Cmd-Q to Quit - for reset as Groovy Console takes over this binding
2767 protected void addQuitHandler()
2769 getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
2770 .put(KeyStroke.getKeyStroke(KeyEvent.VK_Q,
2771 Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()),
2773 getRootPane().getActionMap().put("Quit", new AbstractAction()
2776 public void actionPerformed(ActionEvent e)
2784 * Enable or disable 'Run Groovy script' in AlignFrame calculate menus
2787 * true if Groovy console is open
2789 public void enableExecuteGroovy(boolean enabled)
2792 * disable opening a second Groovy console
2793 * (or re-enable when the console is closed)
2795 groovyShell.setEnabled(!enabled);
2797 AlignFrame[] alignFrames = getAlignFrames();
2798 if (alignFrames != null)
2800 for (AlignFrame af : alignFrames)
2802 af.setGroovyEnabled(enabled);
2808 * Progress bars managed by the IProgressIndicator method.
2810 private Hashtable<Long, JPanel> progressBars;
2812 private Hashtable<Long, IProgressIndicatorHandler> progressBarHandlers;
2817 * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
2820 public void setProgressBar(String message, long id)
2822 Platform.timeCheck("Desktop " + message, Platform.TIME_MARK);
2824 if (progressBars == null)
2826 progressBars = new Hashtable<>();
2827 progressBarHandlers = new Hashtable<>();
2830 if (progressBars.get(new Long(id)) != null)
2832 JPanel panel = progressBars.remove(new Long(id));
2833 if (progressBarHandlers.contains(new Long(id)))
2835 progressBarHandlers.remove(new Long(id));
2837 removeProgressPanel(panel);
2841 progressBars.put(new Long(id), addProgressPanel(message));
2848 * @see jalview.gui.IProgressIndicator#registerHandler(long,
2849 * jalview.gui.IProgressIndicatorHandler)
2852 public void registerHandler(final long id,
2853 final IProgressIndicatorHandler handler)
2855 if (progressBarHandlers == null
2856 || !progressBars.containsKey(new Long(id)))
2858 throw new Error(MessageManager.getString(
2859 "error.call_setprogressbar_before_registering_handler"));
2861 progressBarHandlers.put(new Long(id), handler);
2862 final JPanel progressPanel = progressBars.get(new Long(id));
2863 if (handler.canCancel())
2865 JButton cancel = new JButton(
2866 MessageManager.getString("action.cancel"));
2867 final IProgressIndicator us = this;
2868 cancel.addActionListener(new ActionListener()
2872 public void actionPerformed(ActionEvent e)
2874 handler.cancelActivity(id);
2875 us.setProgressBar(MessageManager
2876 .formatMessage("label.cancelled_params", new Object[]
2877 { ((JLabel) progressPanel.getComponent(0)).getText() }),
2881 progressPanel.add(cancel, BorderLayout.EAST);
2887 * @return true if any progress bars are still active
2890 public boolean operationInProgress()
2892 if (progressBars != null && progressBars.size() > 0)
2900 * This will return the first AlignFrame holding the given viewport instance. It
2901 * will break if there are more than one AlignFrames viewing a particular av.
2904 * @return alignFrame for viewport
2906 public static AlignFrame getAlignFrameFor(AlignViewportI viewport)
2908 if (desktop != null)
2910 AlignmentPanel[] aps = getAlignmentPanels(
2911 viewport.getSequenceSetId());
2912 for (int panel = 0; aps != null && panel < aps.length; panel++)
2914 if (aps[panel] != null && aps[panel].av == viewport)
2916 return aps[panel].alignFrame;
2923 public VamsasApplication getVamsasApplication()
2930 * flag set if jalview GUI is being operated programmatically
2932 private boolean inBatchMode = false;
2935 * check if jalview GUI is being operated programmatically
2937 * @return inBatchMode
2939 public boolean isInBatchMode()
2945 * set flag if jalview GUI is being operated programmatically
2947 * @param inBatchMode
2949 public void setInBatchMode(boolean inBatchMode)
2951 this.inBatchMode = inBatchMode;
2954 public void startServiceDiscovery()
2956 startServiceDiscovery(false);
2959 public void startServiceDiscovery(boolean blocking)
2961 boolean alive = true;
2962 Thread t0 = null, t1 = null, t2 = null;
2963 // JAL-940 - JALVIEW 1 services are now being EOLed as of JABA 2.1 release
2966 // todo: changesupport handlers need to be transferred
2967 if (discoverer == null)
2969 discoverer = new jalview.ws.jws1.Discoverer();
2970 // register PCS handler for desktop.
2971 discoverer.addPropertyChangeListener(changeSupport);
2973 // JAL-940 - disabled JWS1 service configuration - always start discoverer
2974 // until we phase out completely
2975 (t0 = new Thread(discoverer)).start();
2978 if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
2980 t2 = jalview.ws.jws2.Jws2Discoverer.getDiscoverer()
2981 .startDiscoverer(changeSupport);
2985 // TODO: do rest service discovery
2994 } catch (Exception e)
2997 alive = (t1 != null && t1.isAlive()) || (t2 != null && t2.isAlive())
2998 || (t3 != null && t3.isAlive())
2999 || (t0 != null && t0.isAlive());
3005 * called to check if the service discovery process completed successfully.
3009 protected void JalviewServicesChanged(PropertyChangeEvent evt)
3011 if (evt.getNewValue() == null || evt.getNewValue() instanceof Vector)
3013 final String ermsg = jalview.ws.jws2.Jws2Discoverer.getDiscoverer()
3014 .getErrorMessages();
3017 if (Cache.getDefault("SHOW_WSDISCOVERY_ERRORS", true))
3019 if (serviceChangedDialog == null)
3021 // only run if we aren't already displaying one of these.
3022 addDialogThread(serviceChangedDialog = new Runnable()
3029 * JalviewDialog jd =new JalviewDialog() {
3031 * @Override protected void cancelPressed() { // TODO
3032 * Auto-generated method stub
3034 * }@Override protected void okPressed() { // TODO
3035 * Auto-generated method stub
3037 * }@Override protected void raiseClosed() { // TODO
3038 * Auto-generated method stub
3040 * } }; jd.initDialogFrame(new
3041 * JLabel("<html><table width=\"450\"><tr><td>" + ermsg +
3042 * "<br/>It may be that you have invalid JABA URLs in your web service preferences,"
3043 * + " or mis-configured HTTP proxy settings.<br/>" +
3044 * "Check the <em>Connections</em> and <em>Web services</em> tab of the"
3046 * " Tools->Preferences dialog box to change them.</td></tr></table></html>"
3047 * ), true, true, "Web Service Configuration Problem", 450,
3050 * jd.waitForInput();
3052 JvOptionPane.showConfirmDialog(Desktop.desktop,
3053 new JLabel("<html><table width=\"450\"><tr><td>"
3054 + ermsg + "</td></tr></table>"
3055 + "<p>It may be that you have invalid JABA URLs<br/>in your web service preferences,"
3056 + "<br>or as a command-line argument, or mis-configured HTTP proxy settings.</p>"
3057 + "<p>Check the <em>Connections</em> and <em>Web services</em> tab<br/>of the"
3058 + " Tools->Preferences dialog box to change them.</p></html>"),
3059 "Web Service Configuration Problem",
3060 JvOptionPane.DEFAULT_OPTION,
3061 JvOptionPane.ERROR_MESSAGE);
3062 serviceChangedDialog = null;
3071 "Errors reported by JABA discovery service. Check web services preferences.\n"
3078 private Runnable serviceChangedDialog = null;
3081 * start a thread to open a URL in the configured browser. Pops up a warning
3082 * dialog to the user if there is an exception when calling out to the browser
3087 public static void showUrl(final String url)
3089 showUrl(url, Desktop.instance);
3093 * Like showUrl but allows progress handler to be specified
3097 * (null) or object implementing IProgressIndicator
3099 public static void showUrl(final String url,
3100 final IProgressIndicator progress)
3102 new Thread(new Runnable()
3109 if (progress != null)
3111 progress.setProgressBar(MessageManager
3112 .formatMessage("status.opening_params", new Object[]
3113 { url }), this.hashCode());
3115 jalview.util.BrowserLauncher.openURL(url);
3116 } catch (Exception ex)
3118 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
3120 .getString("label.web_browser_not_found_unix"),
3121 MessageManager.getString("label.web_browser_not_found"),
3122 JvOptionPane.WARNING_MESSAGE);
3124 ex.printStackTrace();
3126 if (progress != null)
3128 progress.setProgressBar(null, this.hashCode());
3134 public static WsParamSetManager wsparamManager = null;
3136 public static ParamManager getUserParameterStore()
3138 if (wsparamManager == null)
3140 wsparamManager = new WsParamSetManager();
3142 return wsparamManager;
3146 * static hyperlink handler proxy method for use by Jalview's internal windows
3150 public static void hyperlinkUpdate(HyperlinkEvent e)
3152 if (e.getEventType() == EventType.ACTIVATED)
3157 url = e.getURL().toString();
3158 Desktop.showUrl(url);
3159 } catch (Exception x)
3163 if (Cache.log != null)
3165 Cache.log.error("Couldn't handle string " + url + " as a URL.");
3170 "Couldn't handle string " + url + " as a URL.");
3173 // ignore any exceptions due to dud links.
3180 * single thread that handles display of dialogs to user.
3182 ExecutorService dialogExecutor = Executors.newSingleThreadExecutor();
3185 * flag indicating if dialogExecutor should try to acquire a permit
3187 private volatile boolean dialogPause = true;
3192 private java.util.concurrent.Semaphore block = new Semaphore(0);
3194 private static groovy.ui.Console groovyConsole;
3197 * add another dialog thread to the queue
3201 public void addDialogThread(final Runnable prompter)
3203 dialogExecutor.submit(new Runnable()
3213 } catch (InterruptedException x)
3218 if (instance == null)
3224 SwingUtilities.invokeAndWait(prompter);
3225 } catch (Exception q)
3227 Cache.log.warn("Unexpected Exception in dialog thread.", q);
3233 public void startDialogQueue()
3235 // set the flag so we don't pause waiting for another permit and semaphore
3236 // the current task to begin
3237 dialogPause = false;
3242 * Outputs an image of the desktop to file in EPS format, after prompting the
3243 * user for choice of Text or Lineart character rendering (unless a preference
3244 * has been set). The file name is generated as
3247 * Jalview_snapshot_nnnnn.eps where nnnnn is the current timestamp in milliseconds
3251 protected void snapShotWindow_actionPerformed(ActionEvent e)
3253 // currently the menu option to do this is not shown
3256 int width = getWidth();
3257 int height = getHeight();
3259 "Jalview_snapshot_" + System.currentTimeMillis() + ".eps");
3260 ImageWriterI writer = new ImageWriterI()
3263 public void exportImage(Graphics g) throws Exception
3266 Cache.log.info("Successfully written snapshot to file "
3267 + of.getAbsolutePath());
3270 String title = "View of desktop";
3271 ImageExporter exporter = new ImageExporter(writer, null, TYPE.EPS,
3273 exporter.doExport(of, this, width, height, title);
3277 * Explode the views in the given SplitFrame into separate SplitFrame windows.
3278 * This respects (remembers) any previous 'exploded geometry' i.e. the size and
3279 * location last time the view was expanded (if any). However it does not
3280 * remember the split pane divider location - this is set to match the
3281 * 'exploding' frame.
3285 public void explodeViews(SplitFrame sf)
3287 AlignFrame oldTopFrame = (AlignFrame) sf.getTopFrame();
3288 AlignFrame oldBottomFrame = (AlignFrame) sf.getBottomFrame();
3289 List<? extends AlignmentViewPanel> topPanels = oldTopFrame
3291 List<? extends AlignmentViewPanel> bottomPanels = oldBottomFrame
3293 int viewCount = topPanels.size();
3300 * Processing in reverse order works, forwards order leaves the first panels
3301 * not visible. I don't know why!
3303 for (int i = viewCount - 1; i >= 0; i--)
3306 * Make new top and bottom frames. These take over the respective
3307 * AlignmentPanel objects, including their AlignmentViewports, so the
3308 * cdna/protein relationships between the viewports is carried over to the
3311 * explodedGeometry holds the (x, y) position of the previously exploded
3312 * SplitFrame, and the (width, height) of the AlignFrame component
3314 AlignmentPanel topPanel = (AlignmentPanel) topPanels.get(i);
3315 AlignFrame newTopFrame = new AlignFrame(topPanel);
3316 newTopFrame.setSize(oldTopFrame.getSize());
3317 newTopFrame.setVisible(true);
3318 Rectangle geometry = ((AlignViewport) topPanel.getAlignViewport())
3319 .getExplodedGeometry();
3320 if (geometry != null)
3322 newTopFrame.setSize(geometry.getSize());
3325 AlignmentPanel bottomPanel = (AlignmentPanel) bottomPanels.get(i);
3326 AlignFrame newBottomFrame = new AlignFrame(bottomPanel);
3327 newBottomFrame.setSize(oldBottomFrame.getSize());
3328 newBottomFrame.setVisible(true);
3329 geometry = ((AlignViewport) bottomPanel.getAlignViewport())
3330 .getExplodedGeometry();
3331 if (geometry != null)
3333 newBottomFrame.setSize(geometry.getSize());
3336 topPanel.av.setGatherViewsHere(false);
3337 bottomPanel.av.setGatherViewsHere(false);
3338 JInternalFrame splitFrame = new SplitFrame(newTopFrame,
3340 if (geometry != null)
3342 splitFrame.setLocation(geometry.getLocation());
3344 Desktop.addInternalFrame(splitFrame, sf.getTitle(), -1, -1);
3348 * Clear references to the panels (now relocated in the new SplitFrames)
3349 * before closing the old SplitFrame.
3352 bottomPanels.clear();
3357 * Gather expanded split frames, sharing the same pairs of sequence set ids,
3358 * back into the given SplitFrame as additional views. Note that the gathered
3359 * frames may themselves have multiple views.
3363 public void gatherViews(GSplitFrame source)
3366 * special handling of explodedGeometry for a view within a SplitFrame: - it
3367 * holds the (x, y) position of the enclosing SplitFrame, and the (width,
3368 * height) of the AlignFrame component
3370 AlignFrame myTopFrame = (AlignFrame) source.getTopFrame();
3371 AlignFrame myBottomFrame = (AlignFrame) source.getBottomFrame();
3372 myTopFrame.viewport.setExplodedGeometry(new Rectangle(source.getX(),
3373 source.getY(), myTopFrame.getWidth(), myTopFrame.getHeight()));
3374 myBottomFrame.viewport
3375 .setExplodedGeometry(new Rectangle(source.getX(), source.getY(),
3376 myBottomFrame.getWidth(), myBottomFrame.getHeight()));
3377 myTopFrame.viewport.setGatherViewsHere(true);
3378 myBottomFrame.viewport.setGatherViewsHere(true);
3379 String topViewId = myTopFrame.viewport.getSequenceSetId();
3380 String bottomViewId = myBottomFrame.viewport.getSequenceSetId();
3382 JInternalFrame[] frames = desktop.getAllFrames();
3383 for (JInternalFrame frame : frames)
3385 if (frame instanceof SplitFrame && frame != source)
3387 SplitFrame sf = (SplitFrame) frame;
3388 AlignFrame topFrame = (AlignFrame) sf.getTopFrame();
3389 AlignFrame bottomFrame = (AlignFrame) sf.getBottomFrame();
3390 boolean gatherThis = false;
3391 for (int a = 0; a < topFrame.alignPanels.size(); a++)
3393 AlignmentPanel topPanel = topFrame.alignPanels.get(a);
3394 AlignmentPanel bottomPanel = bottomFrame.alignPanels.get(a);
3395 if (topViewId.equals(topPanel.av.getSequenceSetId())
3396 && bottomViewId.equals(bottomPanel.av.getSequenceSetId()))
3399 topPanel.av.setGatherViewsHere(false);
3400 bottomPanel.av.setGatherViewsHere(false);
3401 topPanel.av.setExplodedGeometry(
3402 new Rectangle(sf.getLocation(), topFrame.getSize()));
3403 bottomPanel.av.setExplodedGeometry(
3404 new Rectangle(sf.getLocation(), bottomFrame.getSize()));
3405 myTopFrame.addAlignmentPanel(topPanel, false);
3406 myBottomFrame.addAlignmentPanel(bottomPanel, false);
3412 topFrame.getAlignPanels().clear();
3413 bottomFrame.getAlignPanels().clear();
3420 * The dust settles...give focus to the tab we did this from.
3422 myTopFrame.setDisplayedView(myTopFrame.alignPanel);
3425 public static groovy.ui.Console getGroovyConsole()
3427 return groovyConsole;
3431 * handles the payload of a drag and drop event.
3433 * TODO refactor to desktop utilities class
3436 * - Data source strings extracted from the drop event
3438 * - protocol for each data source extracted from the drop event
3442 * - the payload from the drop event
3445 public static void transferFromDropTarget(List<Object> files,
3446 List<DataSourceType> protocols, DropTargetDropEvent evt,
3447 Transferable t) throws Exception
3450 // BH 2018 changed List<String> to List<Object> to allow for File from SwingJS
3452 // DataFlavor[] flavors = t.getTransferDataFlavors();
3453 // for (int i = 0; i < flavors.length; i++) {
3454 // if (flavors[i].isFlavorJavaFileListType()) {
3455 // evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
3456 // List<File> list = (List<File>) t.getTransferData(flavors[i]);
3457 // for (int j = 0; j < list.size(); j++) {
3458 // File file = (File) list.get(j);
3459 // byte[] data = getDroppedFileBytes(file);
3460 // fileName.setText(file.getName() + " - " + data.length + " " +
3461 // evt.getLocation());
3462 // JTextArea target = (JTextArea) ((DropTarget) evt.getSource()).getComponent();
3463 // target.setText(new String(data));
3465 // dtde.dropComplete(true);
3470 DataFlavor uriListFlavor = new DataFlavor(
3471 "text/uri-list;class=java.lang.String"), urlFlavour = null;
3474 urlFlavour = new DataFlavor(
3475 "application/x-java-url; class=java.net.URL");
3476 } catch (ClassNotFoundException cfe)
3478 Cache.log.debug("Couldn't instantiate the URL dataflavor.", cfe);
3481 if (urlFlavour != null && t.isDataFlavorSupported(urlFlavour))
3486 java.net.URL url = (URL) t.getTransferData(urlFlavour);
3487 // nb: java 8 osx bug https://bugs.openjdk.java.net/browse/JDK-8156099
3488 // means url may be null.
3491 protocols.add(DataSourceType.URL);
3492 files.add(url.toString());
3493 Cache.log.debug("Drop handled as URL dataflavor "
3494 + files.get(files.size() - 1));
3499 if (Platform.isAMacAndNotJS())
3502 "Please ignore plist error - occurs due to problem with java 8 on OSX");
3506 } catch (Throwable ex)
3508 Cache.log.debug("URL drop handler failed.", ex);
3511 if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
3513 // Works on Windows and MacOSX
3514 Cache.log.debug("Drop handled as javaFileListFlavor");
3515 for (Object file : (List) t
3516 .getTransferData(DataFlavor.javaFileListFlavor))
3519 protocols.add(DataSourceType.FILE);
3524 // Unix like behaviour
3525 boolean added = false;
3527 if (t.isDataFlavorSupported(uriListFlavor))
3529 Cache.log.debug("Drop handled as uriListFlavor");
3530 // This is used by Unix drag system
3531 data = (String) t.getTransferData(uriListFlavor);
3535 // fallback to text: workaround - on OSX where there's a JVM bug
3536 Cache.log.debug("standard URIListFlavor failed. Trying text");
3537 // try text fallback
3538 DataFlavor textDf = new DataFlavor(
3539 "text/plain;class=java.lang.String");
3540 if (t.isDataFlavorSupported(textDf))
3542 data = (String) t.getTransferData(textDf);
3545 Cache.log.debug("Plain text drop content returned "
3546 + (data == null ? "Null - failed" : data));
3551 while (protocols.size() < files.size())
3553 Cache.log.debug("Adding missing FILE protocol for "
3554 + files.get(protocols.size()));
3555 protocols.add(DataSourceType.FILE);
3557 for (java.util.StringTokenizer st = new java.util.StringTokenizer(
3558 data, "\r\n"); st.hasMoreTokens();)
3561 String s = st.nextToken();
3562 if (s.startsWith("#"))
3564 // the line is a comment (as per the RFC 2483)
3567 java.net.URI uri = new java.net.URI(s);
3568 if (uri.getScheme().toLowerCase().startsWith("http"))
3570 protocols.add(DataSourceType.URL);
3571 files.add(uri.toString());
3575 // otherwise preserve old behaviour: catch all for file objects
3576 java.io.File file = new java.io.File(uri);
3577 protocols.add(DataSourceType.FILE);
3578 files.add(file.toString());
3583 if (Cache.log.isDebugEnabled())
3585 if (data == null || !added)
3588 if (t.getTransferDataFlavors() != null
3589 && t.getTransferDataFlavors().length > 0)
3592 "Couldn't resolve drop data. Here are the supported flavors:");
3593 for (DataFlavor fl : t.getTransferDataFlavors())
3596 "Supported transfer dataflavor: " + fl.toString());
3597 Object df = t.getTransferData(fl);
3600 Cache.log.debug("Retrieves: " + df);
3604 Cache.log.debug("Retrieved nothing");
3610 Cache.log.debug("Couldn't resolve dataflavor for drop: "
3616 if (Platform.isWindowsAndNotJS())
3618 Cache.log.debug("Scanning dropped content for Windows Link Files");
3620 // resolve any .lnk files in the file drop
3621 for (int f = 0; f < files.size(); f++)
3623 String source = files.get(f).toString().toLowerCase();
3624 if (protocols.get(f).equals(DataSourceType.FILE)
3625 && (source.endsWith(".lnk") || source.endsWith(".url")
3626 || source.endsWith(".site")))
3630 Object obj = files.get(f);
3631 File lf = (obj instanceof File ? (File) obj
3632 : new File((String) obj));
3633 // process link file to get a URL
3634 Cache.log.debug("Found potential link file: " + lf);
3635 WindowsShortcut wscfile = new WindowsShortcut(lf);
3636 String fullname = wscfile.getRealFilename();
3637 protocols.set(f, FormatAdapter.checkProtocol(fullname));
3638 files.set(f, fullname);
3639 Cache.log.debug("Parsed real filename " + fullname
3640 + " to extract protocol: " + protocols.get(f));
3641 } catch (Exception ex)
3644 "Couldn't parse " + files.get(f) + " as a link file.",
3653 * Sets the Preferences property for experimental features to True or False
3654 * depending on the state of the controlling menu item
3657 protected void showExperimental_actionPerformed(boolean selected)
3659 Cache.setProperty(EXPERIMENTAL_FEATURES, Boolean.toString(selected));
3663 * Answers a (possibly empty) list of any structure viewer frames (currently for
3664 * either Jmol or Chimera) which are currently open. This may optionally be
3665 * restricted to viewers of a specified class, or viewers linked to a specified
3669 * if not null, only return viewers linked to this panel
3670 * @param structureViewerClass
3671 * if not null, only return viewers of this class
3674 public List<StructureViewerBase> getStructureViewers(
3675 AlignmentPanel apanel,
3676 Class<? extends StructureViewerBase> structureViewerClass)
3678 List<StructureViewerBase> result = new ArrayList<>();
3679 JInternalFrame[] frames = Desktop.instance.getAllFrames();
3681 for (JInternalFrame frame : frames)
3683 if (frame instanceof StructureViewerBase)
3685 if (structureViewerClass == null
3686 || structureViewerClass.isInstance(frame))
3689 || ((StructureViewerBase) frame).isLinkedWith(apanel))
3691 result.add((StructureViewerBase) frame);