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 boolean doBackup = BackupFiles.getEnabled();
1709 BackupFiles backupfiles = doBackup ? new BackupFiles(chosenFile) : null;
1711 new Jalview2XML().saveState(doBackup ? backupfiles.getTempFile() : chosenFile);
1715 backupfiles.setWriteSuccess(true);
1716 backupfiles.rollBackupsAndRenameTempFile();
1718 } catch (OutOfMemoryError oom)
1720 new OOMWarning("Whilst saving current state to "
1721 + chosenFile.getName(), oom);
1722 } catch (Exception ex)
1724 Cache.log.error("Problems whilst trying to save to "
1725 + chosenFile.getName(), ex);
1726 JvOptionPane.showMessageDialog(me,
1727 MessageManager.formatMessage(
1728 "label.error_whilst_saving_current_state_to",
1730 { chosenFile.getName() }),
1731 MessageManager.getString("label.couldnt_save_project"),
1732 JvOptionPane.WARNING_MESSAGE);
1734 setProgressBar(null, chosenFile.hashCode());
1741 public void saveAsState_actionPerformed(ActionEvent e)
1743 saveState_actionPerformed(true);
1746 private void setProjectFile(File choice)
1748 this.projectFile = choice;
1751 public File getProjectFile()
1753 return this.projectFile;
1757 * Shows a file chooser dialog and tries to read in the selected file as a
1761 public void loadState_actionPerformed()
1763 final String[] suffix = new String[] { "jvp", "jar" };
1764 final String[] desc = new String[] { "Jalview Project",
1765 "Jalview Project (old)" };
1766 JalviewFileChooser chooser = new JalviewFileChooser(
1767 Cache.getProperty("LAST_DIRECTORY"), suffix, desc,
1768 "Jalview Project", true, true); // last two booleans: allFiles,
1770 chooser.setFileView(new JalviewFileView());
1771 chooser.setDialogTitle(MessageManager.getString("label.restore_state"));
1772 chooser.setResponseHandler(0, new Runnable()
1777 File selectedFile = chooser.getSelectedFile();
1778 setProjectFile(selectedFile);
1779 String choice = selectedFile.getAbsolutePath();
1780 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1781 new Thread(new Runnable()
1788 new Jalview2XML().loadJalviewAlign(choice);
1789 } catch (OutOfMemoryError oom)
1791 new OOMWarning("Whilst loading project from " + choice, oom);
1792 } catch (Exception ex)
1795 "Problems whilst loading project from " + choice, ex);
1796 JvOptionPane.showMessageDialog(Desktop.desktop,
1797 MessageManager.formatMessage(
1798 "label.error_whilst_loading_project_from",
1801 MessageManager.getString("label.couldnt_load_project"),
1802 JvOptionPane.WARNING_MESSAGE);
1809 chooser.showOpenDialog(this);
1813 public void inputSequence_actionPerformed(ActionEvent e)
1815 new SequenceFetcher(this);
1818 JPanel progressPanel;
1820 ArrayList<JPanel> fileLoadingPanels = new ArrayList<>();
1822 public void startLoading(final Object fileName)
1824 if (fileLoadingCount == 0)
1826 fileLoadingPanels.add(addProgressPanel(MessageManager
1827 .formatMessage("label.loading_file", new Object[]
1833 private JPanel addProgressPanel(String string)
1835 if (progressPanel == null)
1837 progressPanel = new JPanel(new GridLayout(1, 1));
1838 totalProgressCount = 0;
1839 instance.getContentPane().add(progressPanel, BorderLayout.SOUTH);
1841 JPanel thisprogress = new JPanel(new BorderLayout(10, 5));
1842 JProgressBar progressBar = new JProgressBar();
1843 progressBar.setIndeterminate(true);
1845 thisprogress.add(new JLabel(string), BorderLayout.WEST);
1847 thisprogress.add(progressBar, BorderLayout.CENTER);
1848 progressPanel.add(thisprogress);
1849 ((GridLayout) progressPanel.getLayout()).setRows(
1850 ((GridLayout) progressPanel.getLayout()).getRows() + 1);
1851 ++totalProgressCount;
1852 instance.validate();
1853 return thisprogress;
1856 int totalProgressCount = 0;
1858 private void removeProgressPanel(JPanel progbar)
1860 if (progressPanel != null)
1862 synchronized (progressPanel)
1864 progressPanel.remove(progbar);
1865 GridLayout gl = (GridLayout) progressPanel.getLayout();
1866 gl.setRows(gl.getRows() - 1);
1867 if (--totalProgressCount < 1)
1869 this.getContentPane().remove(progressPanel);
1870 progressPanel = null;
1877 public void stopLoading()
1880 if (fileLoadingCount < 1)
1882 while (fileLoadingPanels.size() > 0)
1884 removeProgressPanel(fileLoadingPanels.remove(0));
1886 fileLoadingPanels.clear();
1887 fileLoadingCount = 0;
1892 public static int getViewCount(String alignmentId)
1894 AlignmentViewport[] aps = getViewports(alignmentId);
1895 return (aps == null) ? 0 : aps.length;
1900 * @param alignmentId
1901 * - if null, all sets are returned
1902 * @return all AlignmentPanels concerning the alignmentId sequence set
1904 public static AlignmentPanel[] getAlignmentPanels(String alignmentId)
1906 if (Desktop.desktop == null)
1908 // no frames created and in headless mode
1909 // TODO: verify that frames are recoverable when in headless mode
1912 List<AlignmentPanel> aps = new ArrayList<>();
1913 AlignFrame[] frames = getAlignFrames();
1918 for (AlignFrame af : frames)
1920 for (AlignmentPanel ap : af.alignPanels)
1922 if (alignmentId == null
1923 || alignmentId.equals(ap.av.getSequenceSetId()))
1929 if (aps.size() == 0)
1933 AlignmentPanel[] vap = aps.toArray(new AlignmentPanel[aps.size()]);
1938 * get all the viewports on an alignment.
1940 * @param sequenceSetId
1941 * unique alignment id (may be null - all viewports returned in that
1943 * @return all viewports on the alignment bound to sequenceSetId
1945 public static AlignmentViewport[] getViewports(String sequenceSetId)
1947 List<AlignmentViewport> viewp = new ArrayList<>();
1948 if (desktop != null)
1950 AlignFrame[] frames = Desktop.getAlignFrames();
1952 for (AlignFrame afr : frames)
1954 if (sequenceSetId == null || afr.getViewport().getSequenceSetId()
1955 .equals(sequenceSetId))
1957 if (afr.alignPanels != null)
1959 for (AlignmentPanel ap : afr.alignPanels)
1961 if (sequenceSetId == null
1962 || sequenceSetId.equals(ap.av.getSequenceSetId()))
1970 viewp.add(afr.getViewport());
1974 if (viewp.size() > 0)
1976 return viewp.toArray(new AlignmentViewport[viewp.size()]);
1983 * Explode the views in the given frame into separate AlignFrame
1987 public static void explodeViews(AlignFrame af)
1989 int size = af.alignPanels.size();
1995 for (int i = 0; i < size; i++)
1997 AlignmentPanel ap = af.alignPanels.get(i);
1998 AlignFrame newaf = new AlignFrame(ap);
2001 * Restore the view's last exploded frame geometry if known. Multiple
2002 * views from one exploded frame share and restore the same (frame)
2003 * position and size.
2005 Rectangle geometry = ap.av.getExplodedGeometry();
2006 if (geometry != null)
2008 newaf.setBounds(geometry);
2011 ap.av.setGatherViewsHere(false);
2013 addInternalFrame(newaf, af.getTitle(), AlignFrame.DEFAULT_WIDTH,
2014 AlignFrame.DEFAULT_HEIGHT);
2017 af.alignPanels.clear();
2018 af.closeMenuItem_actionPerformed(true);
2023 * Gather expanded views (separate AlignFrame's) with the same sequence set
2024 * identifier back in to this frame as additional views, and close the expanded
2025 * views. Note the expanded frames may themselves have multiple views. We take
2030 public void gatherViews(AlignFrame source)
2032 source.viewport.setGatherViewsHere(true);
2033 source.viewport.setExplodedGeometry(source.getBounds());
2034 JInternalFrame[] frames = desktop.getAllFrames();
2035 String viewId = source.viewport.getSequenceSetId();
2037 for (int t = 0; t < frames.length; t++)
2039 if (frames[t] instanceof AlignFrame && frames[t] != source)
2041 AlignFrame af = (AlignFrame) frames[t];
2042 boolean gatherThis = false;
2043 for (int a = 0; a < af.alignPanels.size(); a++)
2045 AlignmentPanel ap = af.alignPanels.get(a);
2046 if (viewId.equals(ap.av.getSequenceSetId()))
2049 ap.av.setGatherViewsHere(false);
2050 ap.av.setExplodedGeometry(af.getBounds());
2051 source.addAlignmentPanel(ap, false);
2057 af.alignPanels.clear();
2058 af.closeMenuItem_actionPerformed(true);
2065 jalview.gui.VamsasApplication v_client = null;
2068 public void vamsasImport_actionPerformed(ActionEvent e)
2070 // TODO: JAL-3048 not needed for Jalview-JS
2072 if (v_client == null)
2074 // Load and try to start a session.
2075 JalviewFileChooser chooser = new JalviewFileChooser(
2076 jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
2078 chooser.setFileView(new JalviewFileView());
2079 chooser.setDialogTitle(
2080 MessageManager.getString("label.open_saved_vamsas_session"));
2081 chooser.setToolTipText(MessageManager.getString(
2082 "label.select_vamsas_session_opened_as_new_vamsas_session"));
2084 int value = chooser.showOpenDialog(this);
2086 if (value == JalviewFileChooser.APPROVE_OPTION)
2088 String fle = chooser.getSelectedFile().toString();
2089 if (!vamsasImport(chooser.getSelectedFile()))
2091 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
2092 MessageManager.formatMessage(
2093 "label.couldnt_import_as_vamsas_session",
2097 .getString("label.vamsas_document_import_failed"),
2098 JvOptionPane.ERROR_MESSAGE);
2104 jalview.bin.Cache.log.error(
2105 "Implementation error - load session from a running session is not supported.");
2110 * import file into a new vamsas session (uses jalview.gui.VamsasApplication)
2113 * @return true if import was a success and a session was started.
2115 public boolean vamsasImport(URL url)
2117 // TODO: create progress bar
2118 if (v_client != null)
2121 jalview.bin.Cache.log.error(
2122 "Implementation error - load session from a running session is not supported.");
2128 // copy the URL content to a temporary local file
2129 // TODO: be a bit cleverer here with nio (?!)
2130 File file = File.createTempFile("vdocfromurl", ".vdj");
2131 FileOutputStream fos = new FileOutputStream(file);
2132 BufferedInputStream bis = new BufferedInputStream(url.openStream());
2133 byte[] buffer = new byte[2048];
2135 while ((ln = bis.read(buffer)) > -1)
2137 fos.write(buffer, 0, ln);
2141 v_client = new jalview.gui.VamsasApplication(this, file,
2142 url.toExternalForm());
2143 } catch (Exception ex)
2145 jalview.bin.Cache.log.error(
2146 "Failed to create new vamsas session from contents of URL "
2151 setupVamsasConnectedGui();
2152 v_client.initial_update(); // TODO: thread ?
2153 return v_client.inSession();
2157 * import file into a new vamsas session (uses jalview.gui.VamsasApplication)
2160 * @return true if import was a success and a session was started.
2162 public boolean vamsasImport(File file)
2164 if (v_client != null)
2167 jalview.bin.Cache.log.error(
2168 "Implementation error - load session from a running session is not supported.");
2172 setProgressBar(MessageManager.formatMessage(
2173 "status.importing_vamsas_session_from", new Object[]
2174 { file.getName() }), file.hashCode());
2177 v_client = new jalview.gui.VamsasApplication(this, file, null);
2178 } catch (Exception ex)
2180 setProgressBar(MessageManager.formatMessage(
2181 "status.importing_vamsas_session_from", new Object[]
2182 { file.getName() }), file.hashCode());
2183 jalview.bin.Cache.log.error(
2184 "New vamsas session from existing session file failed:", ex);
2187 setupVamsasConnectedGui();
2188 v_client.initial_update(); // TODO: thread ?
2189 setProgressBar(MessageManager.formatMessage(
2190 "status.importing_vamsas_session_from", new Object[]
2191 { file.getName() }), file.hashCode());
2192 return v_client.inSession();
2195 public boolean joinVamsasSession(String mysesid)
2197 if (v_client != null)
2199 throw new Error(MessageManager
2200 .getString("error.try_join_vamsas_session_another"));
2202 if (mysesid == null)
2205 MessageManager.getString("error.invalid_vamsas_session_id"));
2207 v_client = new VamsasApplication(this, mysesid);
2208 setupVamsasConnectedGui();
2209 v_client.initial_update();
2210 return (v_client.inSession());
2214 public void vamsasStart_actionPerformed(ActionEvent e)
2216 if (v_client == null)
2219 // we just start a default session for moment.
2221 * JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache.
2222 * getProperty("LAST_DIRECTORY"));
2224 * chooser.setFileView(new JalviewFileView());
2225 * chooser.setDialogTitle("Load Vamsas file");
2226 * chooser.setToolTipText("Import");
2228 * int value = chooser.showOpenDialog(this);
2230 * if (value == JalviewFileChooser.APPROVE_OPTION) { v_client = new
2231 * jalview.gui.VamsasApplication(this, chooser.getSelectedFile());
2233 v_client = new VamsasApplication(this);
2234 setupVamsasConnectedGui();
2235 v_client.initial_update(); // TODO: thread ?
2239 // store current data in session.
2240 v_client.push_update(); // TODO: thread
2244 protected void setupVamsasConnectedGui()
2246 vamsasStart.setText(MessageManager.getString("label.session_update"));
2247 vamsasSave.setVisible(true);
2248 vamsasStop.setVisible(true);
2249 vamsasImport.setVisible(false); // Document import to existing session is
2250 // not possible for vamsas-client-1.0.
2253 protected void setupVamsasDisconnectedGui()
2255 vamsasSave.setVisible(false);
2256 vamsasStop.setVisible(false);
2257 vamsasImport.setVisible(true);
2259 .setText(MessageManager.getString("label.new_vamsas_session"));
2263 public void vamsasStop_actionPerformed(ActionEvent e)
2265 if (v_client != null)
2267 v_client.end_session();
2269 setupVamsasDisconnectedGui();
2273 protected void buildVamsasStMenu()
2275 if (v_client == null)
2277 String[] sess = null;
2280 sess = VamsasApplication.getSessionList();
2281 } catch (Exception e)
2283 jalview.bin.Cache.log.warn("Problem getting current sessions list.",
2289 jalview.bin.Cache.log.debug(
2290 "Got current sessions list: " + sess.length + " entries.");
2291 VamsasStMenu.removeAll();
2292 for (int i = 0; i < sess.length; i++)
2294 JMenuItem sessit = new JMenuItem();
2295 sessit.setText(sess[i]);
2296 sessit.setToolTipText(MessageManager
2297 .formatMessage("label.connect_to_session", new Object[]
2299 final Desktop dsktp = this;
2300 final String mysesid = sess[i];
2301 sessit.addActionListener(new ActionListener()
2305 public void actionPerformed(ActionEvent e)
2307 if (dsktp.v_client == null)
2309 Thread rthr = new Thread(new Runnable()
2315 dsktp.v_client = new VamsasApplication(dsktp, mysesid);
2316 dsktp.setupVamsasConnectedGui();
2317 dsktp.v_client.initial_update();
2325 VamsasStMenu.add(sessit);
2327 // don't show an empty menu.
2328 VamsasStMenu.setVisible(sess.length > 0);
2333 jalview.bin.Cache.log.debug("No current vamsas sessions.");
2334 VamsasStMenu.removeAll();
2335 VamsasStMenu.setVisible(false);
2340 // Not interested in the content. Just hide ourselves.
2341 VamsasStMenu.setVisible(false);
2346 public void vamsasSave_actionPerformed(ActionEvent e)
2348 // TODO: JAL-3048 not needed for Jalview-JS
2350 if (v_client != null)
2352 // TODO: VAMSAS DOCUMENT EXTENSION is VDJ
2353 JalviewFileChooser chooser = new JalviewFileChooser("vdj",
2356 chooser.setFileView(new JalviewFileView());
2357 chooser.setDialogTitle(MessageManager
2358 .getString("label.save_vamsas_document_archive"));
2360 int value = chooser.showSaveDialog(this);
2362 if (value == JalviewFileChooser.APPROVE_OPTION)
2364 java.io.File choice = chooser.getSelectedFile();
2365 JPanel progpanel = addProgressPanel(MessageManager
2366 .formatMessage("label.saving_vamsas_doc", new Object[]
2367 { choice.getName() }));
2368 Cache.setProperty("LAST_DIRECTORY", choice.getParent());
2369 String warnmsg = null;
2370 String warnttl = null;
2373 v_client.vclient.storeDocument(choice);
2376 warnttl = "Serious Problem saving Vamsas Document";
2377 warnmsg = ex.toString();
2378 jalview.bin.Cache.log
2379 .error("Error Whilst saving document to " + choice, ex);
2381 } catch (Exception ex)
2383 warnttl = "Problem saving Vamsas Document.";
2384 warnmsg = ex.toString();
2385 jalview.bin.Cache.log.warn(
2386 "Exception Whilst saving document to " + choice, ex);
2389 removeProgressPanel(progpanel);
2390 if (warnmsg != null)
2392 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
2394 warnmsg, warnttl, JvOptionPane.ERROR_MESSAGE);
2400 JPanel vamUpdate = null;
2403 * hide vamsas user gui bits when a vamsas document event is being handled.
2406 * true to hide gui, false to reveal gui
2408 public void setVamsasUpdate(boolean b)
2410 Cache.log.debug("Setting gui for Vamsas update "
2411 + (b ? "in progress" : "finished"));
2413 if (vamUpdate != null)
2415 this.removeProgressPanel(vamUpdate);
2419 vamUpdate = this.addProgressPanel(
2420 MessageManager.getString("label.updating_vamsas_session"));
2422 vamsasStart.setVisible(!b);
2423 vamsasStop.setVisible(!b);
2424 vamsasSave.setVisible(!b);
2427 public JInternalFrame[] getAllFrames()
2429 return desktop.getAllFrames();
2433 * Checks the given url to see if it gives a response indicating that the user
2434 * should be informed of a new questionnaire.
2438 public void checkForQuestionnaire(String url)
2440 UserQuestionnaireCheck jvq = new UserQuestionnaireCheck(url);
2441 // javax.swing.SwingUtilities.invokeLater(jvq);
2442 new Thread(jvq).start();
2445 public void checkURLLinks()
2447 // Thread off the URL link checker
2448 addDialogThread(new Runnable()
2453 if (Cache.getDefault("CHECKURLLINKS", true))
2455 // check what the actual links are - if it's just the default don't
2456 // bother with the warning
2457 List<String> links = Preferences.sequenceUrlLinks
2460 // only need to check links if there is one with a
2461 // SEQUENCE_ID which is not the default EMBL_EBI link
2462 ListIterator<String> li = links.listIterator();
2463 boolean check = false;
2464 List<JLabel> urls = new ArrayList<>();
2465 while (li.hasNext())
2467 String link = li.next();
2468 if (link.contains(SEQUENCE_ID)
2469 && !UrlConstants.isDefaultString(link))
2472 int barPos = link.indexOf("|");
2473 String urlMsg = barPos == -1 ? link
2474 : link.substring(0, barPos) + ": "
2475 + link.substring(barPos + 1);
2476 urls.add(new JLabel(urlMsg));
2484 // ask user to check in case URL links use old style tokens
2485 // ($SEQUENCE_ID$ for sequence id _or_ accession id)
2486 JPanel msgPanel = new JPanel();
2487 msgPanel.setLayout(new BoxLayout(msgPanel, BoxLayout.PAGE_AXIS));
2488 msgPanel.add(Box.createVerticalGlue());
2489 JLabel msg = new JLabel(MessageManager
2490 .getString("label.SEQUENCE_ID_for_DB_ACCESSION1"));
2491 JLabel msg2 = new JLabel(MessageManager
2492 .getString("label.SEQUENCE_ID_for_DB_ACCESSION2"));
2494 for (JLabel url : urls)
2500 final JCheckBox jcb = new JCheckBox(
2501 MessageManager.getString("label.do_not_display_again"));
2502 jcb.addActionListener(new ActionListener()
2505 public void actionPerformed(ActionEvent e)
2507 // update Cache settings for "don't show this again"
2508 boolean showWarningAgain = !jcb.isSelected();
2509 Cache.setProperty("CHECKURLLINKS",
2510 Boolean.valueOf(showWarningAgain).toString());
2515 JvOptionPane.showMessageDialog(Desktop.desktop, msgPanel,
2517 .getString("label.SEQUENCE_ID_no_longer_used"),
2518 JvOptionPane.WARNING_MESSAGE);
2525 * Proxy class for JDesktopPane which optionally displays the current memory
2526 * usage and highlights the desktop area with a red bar if free memory runs low.
2530 public class MyDesktopPane extends JDesktopPane
2533 private static final float ONE_MB = 1048576f;
2535 boolean showMemoryUsage = false;
2539 java.text.NumberFormat df;
2541 float maxMemory, allocatedMemory, freeMemory, totalFreeMemory,
2544 public MyDesktopPane(boolean showMemoryUsage)
2546 showMemoryUsage(showMemoryUsage);
2549 public void showMemoryUsage(boolean showMemory)
2551 this.showMemoryUsage = showMemory;
2554 Thread worker = new Thread(this);
2560 public boolean isShowMemoryUsage()
2562 return showMemoryUsage;
2568 df = java.text.NumberFormat.getNumberInstance();
2569 df.setMaximumFractionDigits(2);
2570 runtime = Runtime.getRuntime();
2572 while (showMemoryUsage)
2576 maxMemory = runtime.maxMemory() / ONE_MB;
2577 allocatedMemory = runtime.totalMemory() / ONE_MB;
2578 freeMemory = runtime.freeMemory() / ONE_MB;
2579 totalFreeMemory = freeMemory + (maxMemory - allocatedMemory);
2581 percentUsage = (totalFreeMemory / maxMemory) * 100;
2583 // if (percentUsage < 20)
2585 // border1 = BorderFactory.createMatteBorder(12, 12, 12, 12,
2587 // instance.set.setBorder(border1);
2590 // sleep after showing usage
2592 } catch (Exception ex)
2594 ex.printStackTrace();
2600 public void paintComponent(Graphics g)
2602 if (showMemoryUsage && g != null && df != null)
2604 if (percentUsage < 20)
2606 g.setColor(Color.red);
2608 FontMetrics fm = g.getFontMetrics();
2611 g.drawString(MessageManager.formatMessage("label.memory_stats",
2613 { df.format(totalFreeMemory), df.format(maxMemory),
2614 df.format(percentUsage) }),
2615 10, getHeight() - fm.getHeight());
2622 * Accessor method to quickly get all the AlignmentFrames loaded.
2624 * @return an array of AlignFrame, or null if none found
2626 public static AlignFrame[] getAlignFrames()
2628 if (Jalview.isHeadlessMode())
2630 // Desktop.desktop is null in headless mode
2631 return new AlignFrame[] { Jalview.currentAlignFrame };
2634 JInternalFrame[] frames = Desktop.desktop.getAllFrames();
2640 List<AlignFrame> avp = new ArrayList<>();
2642 for (int i = frames.length - 1; i > -1; i--)
2644 if (frames[i] instanceof AlignFrame)
2646 avp.add((AlignFrame) frames[i]);
2648 else if (frames[i] instanceof SplitFrame)
2651 * Also check for a split frame containing an AlignFrame
2653 GSplitFrame sf = (GSplitFrame) frames[i];
2654 if (sf.getTopFrame() instanceof AlignFrame)
2656 avp.add((AlignFrame) sf.getTopFrame());
2658 if (sf.getBottomFrame() instanceof AlignFrame)
2660 avp.add((AlignFrame) sf.getBottomFrame());
2664 if (avp.size() == 0)
2668 AlignFrame afs[] = avp.toArray(new AlignFrame[avp.size()]);
2673 * Returns an array of any AppJmol frames in the Desktop (or null if none).
2677 public GStructureViewer[] getJmols()
2679 JInternalFrame[] frames = Desktop.desktop.getAllFrames();
2685 List<GStructureViewer> avp = new ArrayList<>();
2687 for (int i = frames.length - 1; i > -1; i--)
2689 if (frames[i] instanceof AppJmol)
2691 GStructureViewer af = (GStructureViewer) frames[i];
2695 if (avp.size() == 0)
2699 GStructureViewer afs[] = avp.toArray(new GStructureViewer[avp.size()]);
2704 * Add Groovy Support to Jalview
2707 public void groovyShell_actionPerformed()
2711 openGroovyConsole();
2712 } catch (Exception ex)
2714 jalview.bin.Cache.log.error("Groovy Shell Creation failed.", ex);
2715 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
2717 MessageManager.getString("label.couldnt_create_groovy_shell"),
2718 MessageManager.getString("label.groovy_support_failed"),
2719 JvOptionPane.ERROR_MESSAGE);
2724 * Open the Groovy console
2726 void openGroovyConsole()
2728 if (groovyConsole == null)
2730 groovyConsole = new groovy.ui.Console();
2731 groovyConsole.setVariable("Jalview", this);
2732 groovyConsole.run();
2735 * We allow only one console at a time, so that AlignFrame menu option
2736 * 'Calculate | Run Groovy script' is unambiguous.
2737 * Disable 'Groovy Console', and enable 'Run script', when the console is
2738 * opened, and the reverse when it is closed
2740 Window window = (Window) groovyConsole.getFrame();
2741 window.addWindowListener(new WindowAdapter()
2744 public void windowClosed(WindowEvent e)
2747 * rebind CMD-Q from Groovy Console to Jalview Quit
2750 enableExecuteGroovy(false);
2756 * show Groovy console window (after close and reopen)
2758 ((Window) groovyConsole.getFrame()).setVisible(true);
2761 * if we got this far, enable 'Run Groovy' in AlignFrame menus
2762 * and disable opening a second console
2764 enableExecuteGroovy(true);
2768 * Bind Ctrl/Cmd-Q to Quit - for reset as Groovy Console takes over this binding
2771 protected void addQuitHandler()
2773 getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
2774 .put(KeyStroke.getKeyStroke(KeyEvent.VK_Q,
2775 Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()),
2777 getRootPane().getActionMap().put("Quit", new AbstractAction()
2780 public void actionPerformed(ActionEvent e)
2788 * Enable or disable 'Run Groovy script' in AlignFrame calculate menus
2791 * true if Groovy console is open
2793 public void enableExecuteGroovy(boolean enabled)
2796 * disable opening a second Groovy console
2797 * (or re-enable when the console is closed)
2799 groovyShell.setEnabled(!enabled);
2801 AlignFrame[] alignFrames = getAlignFrames();
2802 if (alignFrames != null)
2804 for (AlignFrame af : alignFrames)
2806 af.setGroovyEnabled(enabled);
2812 * Progress bars managed by the IProgressIndicator method.
2814 private Hashtable<Long, JPanel> progressBars;
2816 private Hashtable<Long, IProgressIndicatorHandler> progressBarHandlers;
2821 * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
2824 public void setProgressBar(String message, long id)
2826 Platform.timeCheck("Desktop " + message, Platform.TIME_MARK);
2828 if (progressBars == null)
2830 progressBars = new Hashtable<>();
2831 progressBarHandlers = new Hashtable<>();
2834 if (progressBars.get(new Long(id)) != null)
2836 JPanel panel = progressBars.remove(new Long(id));
2837 if (progressBarHandlers.contains(new Long(id)))
2839 progressBarHandlers.remove(new Long(id));
2841 removeProgressPanel(panel);
2845 progressBars.put(new Long(id), addProgressPanel(message));
2852 * @see jalview.gui.IProgressIndicator#registerHandler(long,
2853 * jalview.gui.IProgressIndicatorHandler)
2856 public void registerHandler(final long id,
2857 final IProgressIndicatorHandler handler)
2859 if (progressBarHandlers == null
2860 || !progressBars.containsKey(new Long(id)))
2862 throw new Error(MessageManager.getString(
2863 "error.call_setprogressbar_before_registering_handler"));
2865 progressBarHandlers.put(new Long(id), handler);
2866 final JPanel progressPanel = progressBars.get(new Long(id));
2867 if (handler.canCancel())
2869 JButton cancel = new JButton(
2870 MessageManager.getString("action.cancel"));
2871 final IProgressIndicator us = this;
2872 cancel.addActionListener(new ActionListener()
2876 public void actionPerformed(ActionEvent e)
2878 handler.cancelActivity(id);
2879 us.setProgressBar(MessageManager
2880 .formatMessage("label.cancelled_params", new Object[]
2881 { ((JLabel) progressPanel.getComponent(0)).getText() }),
2885 progressPanel.add(cancel, BorderLayout.EAST);
2891 * @return true if any progress bars are still active
2894 public boolean operationInProgress()
2896 if (progressBars != null && progressBars.size() > 0)
2904 * This will return the first AlignFrame holding the given viewport instance. It
2905 * will break if there are more than one AlignFrames viewing a particular av.
2908 * @return alignFrame for viewport
2910 public static AlignFrame getAlignFrameFor(AlignViewportI viewport)
2912 if (desktop != null)
2914 AlignmentPanel[] aps = getAlignmentPanels(
2915 viewport.getSequenceSetId());
2916 for (int panel = 0; aps != null && panel < aps.length; panel++)
2918 if (aps[panel] != null && aps[panel].av == viewport)
2920 return aps[panel].alignFrame;
2927 public VamsasApplication getVamsasApplication()
2934 * flag set if jalview GUI is being operated programmatically
2936 private boolean inBatchMode = false;
2939 * check if jalview GUI is being operated programmatically
2941 * @return inBatchMode
2943 public boolean isInBatchMode()
2949 * set flag if jalview GUI is being operated programmatically
2951 * @param inBatchMode
2953 public void setInBatchMode(boolean inBatchMode)
2955 this.inBatchMode = inBatchMode;
2958 public void startServiceDiscovery()
2960 startServiceDiscovery(false);
2963 public void startServiceDiscovery(boolean blocking)
2965 boolean alive = true;
2966 Thread t0 = null, t1 = null, t2 = null;
2967 // JAL-940 - JALVIEW 1 services are now being EOLed as of JABA 2.1 release
2970 // todo: changesupport handlers need to be transferred
2971 if (discoverer == null)
2973 discoverer = new jalview.ws.jws1.Discoverer();
2974 // register PCS handler for desktop.
2975 discoverer.addPropertyChangeListener(changeSupport);
2977 // JAL-940 - disabled JWS1 service configuration - always start discoverer
2978 // until we phase out completely
2979 (t0 = new Thread(discoverer)).start();
2982 if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
2984 t2 = jalview.ws.jws2.Jws2Discoverer.getDiscoverer()
2985 .startDiscoverer(changeSupport);
2989 // TODO: do rest service discovery
2998 } catch (Exception e)
3001 alive = (t1 != null && t1.isAlive()) || (t2 != null && t2.isAlive())
3002 || (t3 != null && t3.isAlive())
3003 || (t0 != null && t0.isAlive());
3009 * called to check if the service discovery process completed successfully.
3013 protected void JalviewServicesChanged(PropertyChangeEvent evt)
3015 if (evt.getNewValue() == null || evt.getNewValue() instanceof Vector)
3017 final String ermsg = jalview.ws.jws2.Jws2Discoverer.getDiscoverer()
3018 .getErrorMessages();
3021 if (Cache.getDefault("SHOW_WSDISCOVERY_ERRORS", true))
3023 if (serviceChangedDialog == null)
3025 // only run if we aren't already displaying one of these.
3026 addDialogThread(serviceChangedDialog = new Runnable()
3033 * JalviewDialog jd =new JalviewDialog() {
3035 * @Override protected void cancelPressed() { // TODO
3036 * Auto-generated method stub
3038 * }@Override protected void okPressed() { // TODO
3039 * Auto-generated method stub
3041 * }@Override protected void raiseClosed() { // TODO
3042 * Auto-generated method stub
3044 * } }; jd.initDialogFrame(new
3045 * JLabel("<html><table width=\"450\"><tr><td>" + ermsg +
3046 * "<br/>It may be that you have invalid JABA URLs in your web service preferences,"
3047 * + " or mis-configured HTTP proxy settings.<br/>" +
3048 * "Check the <em>Connections</em> and <em>Web services</em> tab of the"
3050 * " Tools->Preferences dialog box to change them.</td></tr></table></html>"
3051 * ), true, true, "Web Service Configuration Problem", 450,
3054 * jd.waitForInput();
3056 JvOptionPane.showConfirmDialog(Desktop.desktop,
3057 new JLabel("<html><table width=\"450\"><tr><td>"
3058 + ermsg + "</td></tr></table>"
3059 + "<p>It may be that you have invalid JABA URLs<br/>in your web service preferences,"
3060 + "<br>or as a command-line argument, or mis-configured HTTP proxy settings.</p>"
3061 + "<p>Check the <em>Connections</em> and <em>Web services</em> tab<br/>of the"
3062 + " Tools->Preferences dialog box to change them.</p></html>"),
3063 "Web Service Configuration Problem",
3064 JvOptionPane.DEFAULT_OPTION,
3065 JvOptionPane.ERROR_MESSAGE);
3066 serviceChangedDialog = null;
3075 "Errors reported by JABA discovery service. Check web services preferences.\n"
3082 private Runnable serviceChangedDialog = null;
3085 * start a thread to open a URL in the configured browser. Pops up a warning
3086 * dialog to the user if there is an exception when calling out to the browser
3091 public static void showUrl(final String url)
3093 showUrl(url, Desktop.instance);
3097 * Like showUrl but allows progress handler to be specified
3101 * (null) or object implementing IProgressIndicator
3103 public static void showUrl(final String url,
3104 final IProgressIndicator progress)
3106 new Thread(new Runnable()
3113 if (progress != null)
3115 progress.setProgressBar(MessageManager
3116 .formatMessage("status.opening_params", new Object[]
3117 { url }), this.hashCode());
3119 jalview.util.BrowserLauncher.openURL(url);
3120 } catch (Exception ex)
3122 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
3124 .getString("label.web_browser_not_found_unix"),
3125 MessageManager.getString("label.web_browser_not_found"),
3126 JvOptionPane.WARNING_MESSAGE);
3128 ex.printStackTrace();
3130 if (progress != null)
3132 progress.setProgressBar(null, this.hashCode());
3138 public static WsParamSetManager wsparamManager = null;
3140 public static ParamManager getUserParameterStore()
3142 if (wsparamManager == null)
3144 wsparamManager = new WsParamSetManager();
3146 return wsparamManager;
3150 * static hyperlink handler proxy method for use by Jalview's internal windows
3154 public static void hyperlinkUpdate(HyperlinkEvent e)
3156 if (e.getEventType() == EventType.ACTIVATED)
3161 url = e.getURL().toString();
3162 Desktop.showUrl(url);
3163 } catch (Exception x)
3167 if (Cache.log != null)
3169 Cache.log.error("Couldn't handle string " + url + " as a URL.");
3174 "Couldn't handle string " + url + " as a URL.");
3177 // ignore any exceptions due to dud links.
3184 * single thread that handles display of dialogs to user.
3186 ExecutorService dialogExecutor = Executors.newSingleThreadExecutor();
3189 * flag indicating if dialogExecutor should try to acquire a permit
3191 private volatile boolean dialogPause = true;
3196 private java.util.concurrent.Semaphore block = new Semaphore(0);
3198 private static groovy.ui.Console groovyConsole;
3201 * add another dialog thread to the queue
3205 public void addDialogThread(final Runnable prompter)
3207 dialogExecutor.submit(new Runnable()
3217 } catch (InterruptedException x)
3222 if (instance == null)
3228 SwingUtilities.invokeAndWait(prompter);
3229 } catch (Exception q)
3231 Cache.log.warn("Unexpected Exception in dialog thread.", q);
3237 public void startDialogQueue()
3239 // set the flag so we don't pause waiting for another permit and semaphore
3240 // the current task to begin
3241 dialogPause = false;
3246 * Outputs an image of the desktop to file in EPS format, after prompting the
3247 * user for choice of Text or Lineart character rendering (unless a preference
3248 * has been set). The file name is generated as
3251 * Jalview_snapshot_nnnnn.eps where nnnnn is the current timestamp in milliseconds
3255 protected void snapShotWindow_actionPerformed(ActionEvent e)
3257 // currently the menu option to do this is not shown
3260 int width = getWidth();
3261 int height = getHeight();
3263 "Jalview_snapshot_" + System.currentTimeMillis() + ".eps");
3264 ImageWriterI writer = new ImageWriterI()
3267 public void exportImage(Graphics g) throws Exception
3270 Cache.log.info("Successfully written snapshot to file "
3271 + of.getAbsolutePath());
3274 String title = "View of desktop";
3275 ImageExporter exporter = new ImageExporter(writer, null, TYPE.EPS,
3277 exporter.doExport(of, this, width, height, title);
3281 * Explode the views in the given SplitFrame into separate SplitFrame windows.
3282 * This respects (remembers) any previous 'exploded geometry' i.e. the size and
3283 * location last time the view was expanded (if any). However it does not
3284 * remember the split pane divider location - this is set to match the
3285 * 'exploding' frame.
3289 public void explodeViews(SplitFrame sf)
3291 AlignFrame oldTopFrame = (AlignFrame) sf.getTopFrame();
3292 AlignFrame oldBottomFrame = (AlignFrame) sf.getBottomFrame();
3293 List<? extends AlignmentViewPanel> topPanels = oldTopFrame
3295 List<? extends AlignmentViewPanel> bottomPanels = oldBottomFrame
3297 int viewCount = topPanels.size();
3304 * Processing in reverse order works, forwards order leaves the first panels
3305 * not visible. I don't know why!
3307 for (int i = viewCount - 1; i >= 0; i--)
3310 * Make new top and bottom frames. These take over the respective
3311 * AlignmentPanel objects, including their AlignmentViewports, so the
3312 * cdna/protein relationships between the viewports is carried over to the
3315 * explodedGeometry holds the (x, y) position of the previously exploded
3316 * SplitFrame, and the (width, height) of the AlignFrame component
3318 AlignmentPanel topPanel = (AlignmentPanel) topPanels.get(i);
3319 AlignFrame newTopFrame = new AlignFrame(topPanel);
3320 newTopFrame.setSize(oldTopFrame.getSize());
3321 newTopFrame.setVisible(true);
3322 Rectangle geometry = ((AlignViewport) topPanel.getAlignViewport())
3323 .getExplodedGeometry();
3324 if (geometry != null)
3326 newTopFrame.setSize(geometry.getSize());
3329 AlignmentPanel bottomPanel = (AlignmentPanel) bottomPanels.get(i);
3330 AlignFrame newBottomFrame = new AlignFrame(bottomPanel);
3331 newBottomFrame.setSize(oldBottomFrame.getSize());
3332 newBottomFrame.setVisible(true);
3333 geometry = ((AlignViewport) bottomPanel.getAlignViewport())
3334 .getExplodedGeometry();
3335 if (geometry != null)
3337 newBottomFrame.setSize(geometry.getSize());
3340 topPanel.av.setGatherViewsHere(false);
3341 bottomPanel.av.setGatherViewsHere(false);
3342 JInternalFrame splitFrame = new SplitFrame(newTopFrame,
3344 if (geometry != null)
3346 splitFrame.setLocation(geometry.getLocation());
3348 Desktop.addInternalFrame(splitFrame, sf.getTitle(), -1, -1);
3352 * Clear references to the panels (now relocated in the new SplitFrames)
3353 * before closing the old SplitFrame.
3356 bottomPanels.clear();
3361 * Gather expanded split frames, sharing the same pairs of sequence set ids,
3362 * back into the given SplitFrame as additional views. Note that the gathered
3363 * frames may themselves have multiple views.
3367 public void gatherViews(GSplitFrame source)
3370 * special handling of explodedGeometry for a view within a SplitFrame: - it
3371 * holds the (x, y) position of the enclosing SplitFrame, and the (width,
3372 * height) of the AlignFrame component
3374 AlignFrame myTopFrame = (AlignFrame) source.getTopFrame();
3375 AlignFrame myBottomFrame = (AlignFrame) source.getBottomFrame();
3376 myTopFrame.viewport.setExplodedGeometry(new Rectangle(source.getX(),
3377 source.getY(), myTopFrame.getWidth(), myTopFrame.getHeight()));
3378 myBottomFrame.viewport
3379 .setExplodedGeometry(new Rectangle(source.getX(), source.getY(),
3380 myBottomFrame.getWidth(), myBottomFrame.getHeight()));
3381 myTopFrame.viewport.setGatherViewsHere(true);
3382 myBottomFrame.viewport.setGatherViewsHere(true);
3383 String topViewId = myTopFrame.viewport.getSequenceSetId();
3384 String bottomViewId = myBottomFrame.viewport.getSequenceSetId();
3386 JInternalFrame[] frames = desktop.getAllFrames();
3387 for (JInternalFrame frame : frames)
3389 if (frame instanceof SplitFrame && frame != source)
3391 SplitFrame sf = (SplitFrame) frame;
3392 AlignFrame topFrame = (AlignFrame) sf.getTopFrame();
3393 AlignFrame bottomFrame = (AlignFrame) sf.getBottomFrame();
3394 boolean gatherThis = false;
3395 for (int a = 0; a < topFrame.alignPanels.size(); a++)
3397 AlignmentPanel topPanel = topFrame.alignPanels.get(a);
3398 AlignmentPanel bottomPanel = bottomFrame.alignPanels.get(a);
3399 if (topViewId.equals(topPanel.av.getSequenceSetId())
3400 && bottomViewId.equals(bottomPanel.av.getSequenceSetId()))
3403 topPanel.av.setGatherViewsHere(false);
3404 bottomPanel.av.setGatherViewsHere(false);
3405 topPanel.av.setExplodedGeometry(
3406 new Rectangle(sf.getLocation(), topFrame.getSize()));
3407 bottomPanel.av.setExplodedGeometry(
3408 new Rectangle(sf.getLocation(), bottomFrame.getSize()));
3409 myTopFrame.addAlignmentPanel(topPanel, false);
3410 myBottomFrame.addAlignmentPanel(bottomPanel, false);
3416 topFrame.getAlignPanels().clear();
3417 bottomFrame.getAlignPanels().clear();
3424 * The dust settles...give focus to the tab we did this from.
3426 myTopFrame.setDisplayedView(myTopFrame.alignPanel);
3429 public static groovy.ui.Console getGroovyConsole()
3431 return groovyConsole;
3435 * handles the payload of a drag and drop event.
3437 * TODO refactor to desktop utilities class
3440 * - Data source strings extracted from the drop event
3442 * - protocol for each data source extracted from the drop event
3446 * - the payload from the drop event
3449 public static void transferFromDropTarget(List<Object> files,
3450 List<DataSourceType> protocols, DropTargetDropEvent evt,
3451 Transferable t) throws Exception
3454 // BH 2018 changed List<String> to List<Object> to allow for File from SwingJS
3456 // DataFlavor[] flavors = t.getTransferDataFlavors();
3457 // for (int i = 0; i < flavors.length; i++) {
3458 // if (flavors[i].isFlavorJavaFileListType()) {
3459 // evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
3460 // List<File> list = (List<File>) t.getTransferData(flavors[i]);
3461 // for (int j = 0; j < list.size(); j++) {
3462 // File file = (File) list.get(j);
3463 // byte[] data = getDroppedFileBytes(file);
3464 // fileName.setText(file.getName() + " - " + data.length + " " +
3465 // evt.getLocation());
3466 // JTextArea target = (JTextArea) ((DropTarget) evt.getSource()).getComponent();
3467 // target.setText(new String(data));
3469 // dtde.dropComplete(true);
3474 DataFlavor uriListFlavor = new DataFlavor(
3475 "text/uri-list;class=java.lang.String"), urlFlavour = null;
3478 urlFlavour = new DataFlavor(
3479 "application/x-java-url; class=java.net.URL");
3480 } catch (ClassNotFoundException cfe)
3482 Cache.log.debug("Couldn't instantiate the URL dataflavor.", cfe);
3485 if (urlFlavour != null && t.isDataFlavorSupported(urlFlavour))
3490 java.net.URL url = (URL) t.getTransferData(urlFlavour);
3491 // nb: java 8 osx bug https://bugs.openjdk.java.net/browse/JDK-8156099
3492 // means url may be null.
3495 protocols.add(DataSourceType.URL);
3496 files.add(url.toString());
3497 Cache.log.debug("Drop handled as URL dataflavor "
3498 + files.get(files.size() - 1));
3503 if (Platform.isAMacAndNotJS())
3506 "Please ignore plist error - occurs due to problem with java 8 on OSX");
3510 } catch (Throwable ex)
3512 Cache.log.debug("URL drop handler failed.", ex);
3515 if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
3517 // Works on Windows and MacOSX
3518 Cache.log.debug("Drop handled as javaFileListFlavor");
3519 for (Object file : (List) t
3520 .getTransferData(DataFlavor.javaFileListFlavor))
3523 protocols.add(DataSourceType.FILE);
3528 // Unix like behaviour
3529 boolean added = false;
3531 if (t.isDataFlavorSupported(uriListFlavor))
3533 Cache.log.debug("Drop handled as uriListFlavor");
3534 // This is used by Unix drag system
3535 data = (String) t.getTransferData(uriListFlavor);
3539 // fallback to text: workaround - on OSX where there's a JVM bug
3540 Cache.log.debug("standard URIListFlavor failed. Trying text");
3541 // try text fallback
3542 DataFlavor textDf = new DataFlavor(
3543 "text/plain;class=java.lang.String");
3544 if (t.isDataFlavorSupported(textDf))
3546 data = (String) t.getTransferData(textDf);
3549 Cache.log.debug("Plain text drop content returned "
3550 + (data == null ? "Null - failed" : data));
3555 while (protocols.size() < files.size())
3557 Cache.log.debug("Adding missing FILE protocol for "
3558 + files.get(protocols.size()));
3559 protocols.add(DataSourceType.FILE);
3561 for (java.util.StringTokenizer st = new java.util.StringTokenizer(
3562 data, "\r\n"); st.hasMoreTokens();)
3565 String s = st.nextToken();
3566 if (s.startsWith("#"))
3568 // the line is a comment (as per the RFC 2483)
3571 java.net.URI uri = new java.net.URI(s);
3572 if (uri.getScheme().toLowerCase().startsWith("http"))
3574 protocols.add(DataSourceType.URL);
3575 files.add(uri.toString());
3579 // otherwise preserve old behaviour: catch all for file objects
3580 java.io.File file = new java.io.File(uri);
3581 protocols.add(DataSourceType.FILE);
3582 files.add(file.toString());
3587 if (Cache.log.isDebugEnabled())
3589 if (data == null || !added)
3592 if (t.getTransferDataFlavors() != null
3593 && t.getTransferDataFlavors().length > 0)
3596 "Couldn't resolve drop data. Here are the supported flavors:");
3597 for (DataFlavor fl : t.getTransferDataFlavors())
3600 "Supported transfer dataflavor: " + fl.toString());
3601 Object df = t.getTransferData(fl);
3604 Cache.log.debug("Retrieves: " + df);
3608 Cache.log.debug("Retrieved nothing");
3614 Cache.log.debug("Couldn't resolve dataflavor for drop: "
3620 if (Platform.isWindowsAndNotJS())
3622 Cache.log.debug("Scanning dropped content for Windows Link Files");
3624 // resolve any .lnk files in the file drop
3625 for (int f = 0; f < files.size(); f++)
3627 String source = files.get(f).toString().toLowerCase();
3628 if (protocols.get(f).equals(DataSourceType.FILE)
3629 && (source.endsWith(".lnk") || source.endsWith(".url")
3630 || source.endsWith(".site")))
3634 Object obj = files.get(f);
3635 File lf = (obj instanceof File ? (File) obj
3636 : new File((String) obj));
3637 // process link file to get a URL
3638 Cache.log.debug("Found potential link file: " + lf);
3639 WindowsShortcut wscfile = new WindowsShortcut(lf);
3640 String fullname = wscfile.getRealFilename();
3641 protocols.set(f, FormatAdapter.checkProtocol(fullname));
3642 files.set(f, fullname);
3643 Cache.log.debug("Parsed real filename " + fullname
3644 + " to extract protocol: " + protocols.get(f));
3645 } catch (Exception ex)
3648 "Couldn't parse " + files.get(f) + " as a link file.",
3657 * Sets the Preferences property for experimental features to True or False
3658 * depending on the state of the controlling menu item
3661 protected void showExperimental_actionPerformed(boolean selected)
3663 Cache.setProperty(EXPERIMENTAL_FEATURES, Boolean.toString(selected));
3667 * Answers a (possibly empty) list of any structure viewer frames (currently for
3668 * either Jmol or Chimera) which are currently open. This may optionally be
3669 * restricted to viewers of a specified class, or viewers linked to a specified
3673 * if not null, only return viewers linked to this panel
3674 * @param structureViewerClass
3675 * if not null, only return viewers of this class
3678 public List<StructureViewerBase> getStructureViewers(
3679 AlignmentPanel apanel,
3680 Class<? extends StructureViewerBase> structureViewerClass)
3682 List<StructureViewerBase> result = new ArrayList<>();
3683 JInternalFrame[] frames = Desktop.instance.getAllFrames();
3685 for (JInternalFrame frame : frames)
3687 if (frame instanceof StructureViewerBase)
3689 if (structureViewerClass == null
3690 || structureViewerClass.isInstance(frame))
3693 || ((StructureViewerBase) frame).isLinkedWith(apanel))
3695 result.add((StructureViewerBase) frame);