2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
5 * This file is part of Jalview.
7 * Jalview is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation, either version 3
10 * of the License, or (at your option) any later version.
12 * Jalview is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19 * The Jalview Authors are detailed in the 'AUTHORS' file.
23 import static jalview.util.UrlConstants.SEQUENCE_ID;
25 import jalview.api.AlignViewportI;
26 import jalview.api.AlignmentViewPanel;
27 import jalview.bin.Cache;
28 import jalview.bin.Jalview;
29 import jalview.gui.ImageExporter.ImageWriterI;
30 import jalview.io.DataSourceType;
31 import jalview.io.FileFormat;
32 import jalview.io.FileFormatException;
33 import jalview.io.FileFormatI;
34 import jalview.io.FileFormats;
35 import jalview.io.FileLoader;
36 import jalview.io.FormatAdapter;
37 import jalview.io.IdentifyFile;
38 import jalview.io.JalviewFileChooser;
39 import jalview.io.JalviewFileView;
40 import jalview.jbgui.GSplitFrame;
41 import jalview.jbgui.GStructureViewer;
42 import jalview.structure.StructureSelectionManager;
43 import jalview.urls.IdOrgSettings;
44 import jalview.util.BrowserLauncher;
45 import jalview.util.ImageMaker.TYPE;
46 import jalview.util.MessageManager;
47 import jalview.util.Platform;
48 import jalview.util.UrlConstants;
49 import jalview.util.dialogrunner.RunResponse;
50 import jalview.viewmodel.AlignmentViewport;
51 import jalview.ws.params.ParamManager;
52 import jalview.ws.utils.UrlDownloadClient;
54 import java.awt.BorderLayout;
55 import java.awt.Color;
56 import java.awt.Dimension;
57 import java.awt.FontMetrics;
58 import java.awt.Graphics;
59 import java.awt.GridLayout;
60 import java.awt.Point;
61 import java.awt.Rectangle;
62 import java.awt.Toolkit;
63 import java.awt.Window;
64 import java.awt.datatransfer.Clipboard;
65 import java.awt.datatransfer.ClipboardOwner;
66 import java.awt.datatransfer.DataFlavor;
67 import java.awt.datatransfer.Transferable;
68 import java.awt.dnd.DnDConstants;
69 import java.awt.dnd.DropTargetDragEvent;
70 import java.awt.dnd.DropTargetDropEvent;
71 import java.awt.dnd.DropTargetEvent;
72 import java.awt.dnd.DropTargetListener;
73 import java.awt.event.ActionEvent;
74 import java.awt.event.ActionListener;
75 import java.awt.event.InputEvent;
76 import java.awt.event.KeyEvent;
77 import java.awt.event.MouseAdapter;
78 import java.awt.event.MouseEvent;
79 import java.awt.event.WindowAdapter;
80 import java.awt.event.WindowEvent;
81 import java.beans.PropertyChangeEvent;
82 import java.beans.PropertyChangeListener;
83 import java.io.BufferedInputStream;
85 import java.io.FileOutputStream;
86 import java.io.IOException;
88 import java.util.ArrayList;
89 import java.util.Hashtable;
90 import java.util.List;
91 import java.util.ListIterator;
92 import java.util.StringTokenizer;
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.
350 * A note to implementors. It is ESSENTIAL that any activities that might block
351 * are spawned off as threads rather than waited for during this constructor.
356 doVamsasClientCheck();
359 doConfigureStructurePrefs();
360 setTitle("Jalview " + jalview.bin.Cache.getProperty("VERSION"));
361 setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
362 boolean selmemusage = jalview.bin.Cache.getDefault("SHOW_MEMUSAGE",
364 boolean showjconsole = jalview.bin.Cache.getDefault("SHOW_JAVA_CONSOLE",
366 desktop = new MyDesktopPane(selmemusage);
367 showMemusage.setSelected(selmemusage);
368 desktop.setBackground(Color.white);
369 getContentPane().setLayout(new BorderLayout());
370 // alternate config - have scrollbars - see notes in JAL-153
371 // JScrollPane sp = new JScrollPane();
372 // sp.getViewport().setView(desktop);
373 // getContentPane().add(sp, BorderLayout.CENTER);
374 getContentPane().add(desktop, BorderLayout.CENTER);
375 desktop.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
377 // This line prevents Windows Look&Feel resizing all new windows to maximum
378 // if previous window was maximised
379 desktop.setDesktopManager(new MyDesktopManager(
380 (Platform.isWindows() ? new DefaultDesktopManager()
382 ? new AquaInternalFrameManager(
383 desktop.getDesktopManager())
384 : desktop.getDesktopManager())));
386 Rectangle dims = getLastKnownDimensions("");
393 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
394 int xPos = Math.max(5, (screenSize.width - 900) / 2);
395 int yPos = Math.max(5, (screenSize.height - 650) / 2);
396 setBounds(xPos, yPos, 900, 650);
399 boolean doFullLoad = /** @j2sNative ! */true;
403 jconsole = new Console(this, showjconsole);
404 // add essential build information
405 jconsole.setHeader("Jalview Version: "
406 + jalview.bin.Cache.getProperty("VERSION") + "\n"
407 + "Jalview Installation: "
408 + jalview.bin.Cache.getDefault("INSTALLATION", "unknown")
409 + "\n" + "Build Date: "
410 + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown") + "\n"
411 + "Java version: " + System.getProperty("java.version") + "\n"
412 + System.getProperty("os.arch") + " "
413 + System.getProperty("os.name") + " "
414 + System.getProperty("os.version"));
416 showConsole(showjconsole);
418 showNews.setVisible(false);
420 experimentalFeatures.setSelected(showExperimental());
422 getIdentifiersOrgData();
426 // Spawn a thread that shows the splashscreen
428 SwingUtilities.invokeLater(new Runnable()
437 // Thread off a new instance of the file chooser - this reduces the time it
438 // takes to open it later on.
439 new Thread(new Runnable()
444 Cache.log.debug("Filechooser init thread started.");
445 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
446 JalviewFileChooser.forRead(Cache.getProperty("LAST_DIRECTORY"),
448 Cache.log.debug("Filechooser init thread finished.");
451 // Add the service change listener
452 changeSupport.addJalviewPropertyChangeListener("services",
453 new PropertyChangeListener()
457 public void propertyChange(PropertyChangeEvent evt)
459 Cache.log.debug("Firing service changed event for "
460 + evt.getNewValue());
461 JalviewServicesChanged(evt);
468 this.setDropTarget(new java.awt.dnd.DropTarget(desktop, this));
470 this.addWindowListener(new WindowAdapter()
473 public void windowClosing(WindowEvent evt)
480 this.addMouseListener(ma = new MouseAdapter()
483 public void mousePressed(MouseEvent evt)
485 if (evt.isPopupTrigger()) // Mac
487 showPasteMenu(evt.getX(), evt.getY());
492 public void mouseReleased(MouseEvent evt)
494 if (evt.isPopupTrigger()) // Windows
496 showPasteMenu(evt.getX(), evt.getY());
500 desktop.addMouseListener(ma);
505 * Answers true if user preferences to enable experimental features is True
510 public boolean showExperimental()
512 String experimental = Cache.getDefault(EXPERIMENTAL_FEATURES,
513 Boolean.FALSE.toString());
514 return Boolean.valueOf(experimental).booleanValue();
517 public void doConfigureStructurePrefs()
519 // configure services
520 StructureSelectionManager ssm = StructureSelectionManager
521 .getStructureSelectionManager(this);
522 if (jalview.bin.Cache.getDefault(Preferences.ADD_SS_ANN, true))
524 ssm.setAddTempFacAnnot(jalview.bin.Cache
525 .getDefault(Preferences.ADD_TEMPFACT_ANN, true));
526 ssm.setProcessSecondaryStructure(jalview.bin.Cache
527 .getDefault(Preferences.STRUCT_FROM_PDB, true));
528 ssm.setSecStructServices(
529 jalview.bin.Cache.getDefault(Preferences.USE_RNAVIEW, true));
533 ssm.setAddTempFacAnnot(false);
534 ssm.setProcessSecondaryStructure(false);
535 ssm.setSecStructServices(false);
539 public void checkForNews()
541 final Desktop me = this;
542 // Thread off the news reader, in case there are connection problems.
543 new Thread(new Runnable()
548 Cache.log.debug("Starting news thread.");
549 jvnews = new BlogReader(me);
550 showNews.setVisible(true);
551 Cache.log.debug("Completed news thread.");
556 public void getIdentifiersOrgData()
558 // Thread off the identifiers fetcher
559 new Thread(new Runnable()
564 Cache.log.debug("Downloading data from identifiers.org");
565 UrlDownloadClient client = new UrlDownloadClient();
568 client.download(IdOrgSettings.getUrl(),
569 IdOrgSettings.getDownloadLocation());
570 } catch (IOException e)
572 Cache.log.debug("Exception downloading identifiers.org data"
581 protected void showNews_actionPerformed(ActionEvent e)
583 showNews(showNews.isSelected());
586 void showNews(boolean visible)
588 Cache.log.debug((visible ? "Showing" : "Hiding") + " news.");
589 showNews.setSelected(visible);
590 if (visible && !jvnews.isVisible())
592 new Thread(new Runnable()
597 long now = System.currentTimeMillis();
598 Desktop.instance.setProgressBar(
599 MessageManager.getString("status.refreshing_news"), now);
600 jvnews.refreshNews();
601 Desktop.instance.setProgressBar(null, now);
609 * recover the last known dimensions for a jalview window
612 * - empty string is desktop, all other windows have unique prefix
613 * @return null or last known dimensions scaled to current geometry (if last
614 * window geom was known)
616 Rectangle getLastKnownDimensions(String windowName)
618 // TODO: lock aspect ratio for scaling desktop Bug #0058199
619 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
620 String x = jalview.bin.Cache.getProperty(windowName + "SCREEN_X");
621 String y = jalview.bin.Cache.getProperty(windowName + "SCREEN_Y");
622 String width = jalview.bin.Cache
623 .getProperty(windowName + "SCREEN_WIDTH");
624 String height = jalview.bin.Cache
625 .getProperty(windowName + "SCREEN_HEIGHT");
626 if ((x != null) && (y != null) && (width != null) && (height != null))
628 int ix = Integer.parseInt(x), iy = Integer.parseInt(y),
629 iw = Integer.parseInt(width), ih = Integer.parseInt(height);
630 if (jalview.bin.Cache.getProperty("SCREENGEOMETRY_WIDTH") != null)
632 // attempt #1 - try to cope with change in screen geometry - this
633 // version doesn't preserve original jv aspect ratio.
634 // take ratio of current screen size vs original screen size.
635 double sw = ((1f * screenSize.width) / (1f * Integer.parseInt(
636 jalview.bin.Cache.getProperty("SCREENGEOMETRY_WIDTH"))));
637 double sh = ((1f * screenSize.height) / (1f * Integer.parseInt(
638 jalview.bin.Cache.getProperty("SCREENGEOMETRY_HEIGHT"))));
639 // rescale the bounds depending upon the current screen geometry.
640 ix = (int) (ix * sw);
641 iw = (int) (iw * sw);
642 iy = (int) (iy * sh);
643 ih = (int) (ih * sh);
644 while (ix >= screenSize.width)
646 jalview.bin.Cache.log.debug(
647 "Window geometry location recall error: shifting horizontal to within screenbounds.");
648 ix -= screenSize.width;
650 while (iy >= screenSize.height)
652 jalview.bin.Cache.log.debug(
653 "Window geometry location recall error: shifting vertical to within screenbounds.");
654 iy -= screenSize.height;
656 jalview.bin.Cache.log.debug(
657 "Got last known dimensions for " + windowName + ": x:" + ix
658 + " y:" + iy + " width:" + iw + " height:" + ih);
660 // return dimensions for new instance
661 return new Rectangle(ix, iy, iw, ih);
666 private void doVamsasClientCheck()
668 if (Cache.vamsasJarsPresent())
670 setupVamsasDisconnectedGui();
671 VamsasMenu.setVisible(true);
672 final Desktop us = this;
673 VamsasMenu.addMenuListener(new MenuListener()
675 // this listener remembers when the menu was first selected, and
676 // doesn't rebuild the session list until it has been cleared and
678 boolean refresh = true;
681 public void menuCanceled(MenuEvent e)
687 public void menuDeselected(MenuEvent e)
693 public void menuSelected(MenuEvent e)
697 us.buildVamsasStMenu();
702 vamsasStart.setVisible(true);
706 void showPasteMenu(int x, int y)
708 JPopupMenu popup = new JPopupMenu();
709 JMenuItem item = new JMenuItem(
710 MessageManager.getString("label.paste_new_window"));
711 item.addActionListener(new ActionListener()
714 public void actionPerformed(ActionEvent evt)
721 popup.show(this, x, y);
728 Clipboard c = Toolkit.getDefaultToolkit().getSystemClipboard();
729 Transferable contents = c.getContents(this);
731 if (contents != null)
733 String file = (String) contents
734 .getTransferData(DataFlavor.stringFlavor);
736 FileFormatI format = new IdentifyFile().identify(file,
737 DataSourceType.PASTE);
739 new FileLoader().LoadFile(file, DataSourceType.PASTE, format);
742 } catch (Exception ex)
745 "Unable to paste alignment from system clipboard:\n" + ex);
750 * Adds and opens the given frame to the desktop
761 public static synchronized void addInternalFrame(
762 final JInternalFrame frame, String title, int w, int h)
764 addInternalFrame(frame, title, true, w, h, true, false);
768 * Add an internal frame to the Jalview desktop
775 * When true, display frame immediately, otherwise, caller must call
776 * setVisible themselves.
782 public static synchronized void addInternalFrame(
783 final JInternalFrame frame, String title, boolean makeVisible,
786 addInternalFrame(frame, title, makeVisible, w, h, true, false);
790 * Add an internal frame to the Jalview desktop and make it visible
803 public static synchronized void addInternalFrame(
804 final JInternalFrame frame, String title, int w, int h,
807 addInternalFrame(frame, title, true, w, h, resizable, false);
811 * Add an internal frame to the Jalview desktop
818 * When true, display frame immediately, otherwise, caller must call
819 * setVisible themselves.
826 * @param ignoreMinSize
827 * Do not set the default minimum size for frame
829 public static synchronized void addInternalFrame(
830 final JInternalFrame frame, String title, boolean makeVisible,
831 int w, int h, boolean resizable, boolean ignoreMinSize)
834 // TODO: allow callers to determine X and Y position of frame (eg. via
836 // TODO: consider fixing method to update entries in the window submenu with
837 // the current window title
839 frame.setTitle(title);
840 if (frame.getWidth() < 1 || frame.getHeight() < 1)
844 // THIS IS A PUBLIC STATIC METHOD, SO IT MAY BE CALLED EVEN IN
845 // A HEADLESS STATE WHEN NO DESKTOP EXISTS. MUST RETURN
846 // IF JALVIEW IS RUNNING HEADLESS
847 // ///////////////////////////////////////////////
848 if (instance == null || (System.getProperty("java.awt.headless") != null
849 && System.getProperty("java.awt.headless").equals("true")))
858 frame.setMinimumSize(
859 new Dimension(DEFAULT_MIN_WIDTH, DEFAULT_MIN_HEIGHT));
861 // Set default dimension for Alignment Frame window.
862 // The Alignment Frame window could be added from a number of places,
864 // I did this here in order not to miss out on any Alignment frame.
865 if (frame instanceof AlignFrame)
867 frame.setMinimumSize(new Dimension(ALIGN_FRAME_DEFAULT_MIN_WIDTH,
868 ALIGN_FRAME_DEFAULT_MIN_HEIGHT));
872 frame.setVisible(makeVisible);
873 frame.setClosable(true);
874 frame.setResizable(resizable);
875 frame.setMaximizable(resizable);
876 frame.setIconifiable(resizable);
877 frame.setOpaque(/** @j2sNative true || */
880 if (frame.getX() < 1 && frame.getY() < 1)
882 frame.setLocation(xOffset * openFrameCount,
883 yOffset * ((openFrameCount - 1) % 10) + yOffset);
887 * add an entry for the new frame in the Window menu
888 * (and remove it when the frame is closed)
890 final JMenuItem menuItem = new JMenuItem(title);
891 frame.addInternalFrameListener(new InternalFrameAdapter()
894 public void internalFrameActivated(InternalFrameEvent evt)
896 JInternalFrame itf = desktop.getSelectedFrame();
899 if (itf instanceof AlignFrame)
901 Jalview.setCurrentAlignFrame((AlignFrame) itf);
908 public void internalFrameClosed(InternalFrameEvent evt)
910 PaintRefresher.RemoveComponent(frame);
913 * defensive check to prevent frames being
914 * added half off the window
916 if (openFrameCount > 0)
922 * ensure no reference to alignFrame retained by menu item listener
924 if (menuItem.getActionListeners().length > 0)
926 menuItem.removeActionListener(menuItem.getActionListeners()[0]);
928 windowMenu.remove(menuItem);
932 menuItem.addActionListener(new ActionListener()
935 public void actionPerformed(ActionEvent e)
939 frame.setSelected(true);
940 frame.setIcon(false);
941 } catch (java.beans.PropertyVetoException ex)
943 // System.err.println(ex.toString());
948 setKeyBindings(frame);
952 windowMenu.add(menuItem);
957 frame.setSelected(true);
958 frame.requestFocus();
959 } catch (java.beans.PropertyVetoException ve)
961 } catch (java.lang.ClassCastException cex)
964 "Squashed a possible GUI implementation error. If you can recreate this, please look at http://issues.jalview.org/browse/JAL-869",
970 * Add key bindings to a JInternalFrame so that Ctrl-W and Cmd-W will close the
975 private static void setKeyBindings(JInternalFrame frame)
977 @SuppressWarnings("serial")
978 final Action closeAction = new AbstractAction()
981 public void actionPerformed(ActionEvent e)
988 * set up key bindings for Ctrl-W and Cmd-W, with the same (Close) action
990 KeyStroke ctrlWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
991 InputEvent.CTRL_DOWN_MASK);
992 KeyStroke cmdWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
993 Toolkit.getDefaultToolkit().getMenuShortcutKeyMask());
995 InputMap inputMap = frame
996 .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
997 String ctrlW = ctrlWKey.toString();
998 inputMap.put(ctrlWKey, ctrlW);
999 inputMap.put(cmdWKey, ctrlW);
1001 ActionMap actionMap = frame.getActionMap();
1002 actionMap.put(ctrlW, closeAction);
1006 public void lostOwnership(Clipboard clipboard, Transferable contents)
1010 Desktop.jalviewClipboard = null;
1013 internalCopy = false;
1017 public void dragEnter(DropTargetDragEvent evt)
1022 public void dragExit(DropTargetEvent evt)
1027 public void dragOver(DropTargetDragEvent evt)
1032 public void dropActionChanged(DropTargetDragEvent evt)
1043 public void drop(DropTargetDropEvent evt)
1045 boolean success = true;
1046 // JAL-1552 - acceptDrop required before getTransferable call for
1047 // Java's Transferable for native dnd
1048 evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
1049 Transferable t = evt.getTransferable();
1050 List<Object> files = new ArrayList<>();
1051 List<DataSourceType> protocols = new ArrayList<>();
1055 Desktop.transferFromDropTarget(files, protocols, evt, t);
1056 } catch (Exception e)
1058 e.printStackTrace();
1066 for (int i = 0; i < files.size(); i++)
1068 // BH 2018 File or String
1069 Object file = files.get(i);
1070 String fileName = file.toString();
1071 DataSourceType protocol = (protocols == null)
1072 ? DataSourceType.FILE
1074 FileFormatI format = null;
1076 if (fileName.endsWith(".jar"))
1078 format = FileFormat.Jalview;
1083 format = new IdentifyFile().identify(file, protocol);
1086 new FileLoader().LoadFile(null, file, protocol, format);
1089 } catch (Exception ex)
1094 evt.dropComplete(success); // need this to ensure input focus is properly
1095 // transfered to any new windows created
1105 public void inputLocalFileMenuItem_actionPerformed(AlignViewport viewport)
1107 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
1108 JalviewFileChooser chooser = JalviewFileChooser
1109 .forRead(Cache.getProperty("LAST_DIRECTORY"), fileFormat);
1111 chooser.setFileView(new JalviewFileView());
1112 chooser.setDialogTitle(
1113 MessageManager.getString("label.open_local_file"));
1114 chooser.setToolTipText(MessageManager.getString("action.open"));
1116 chooser.response(new RunResponse(JalviewFileChooser.APPROVE_OPTION)
1122 File selectedFile = chooser.getSelectedFile();
1123 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1125 FileFormatI format = chooser.getSelectedFormat();
1128 * Call IdentifyFile to verify the file contains what its extension implies.
1129 * Skip this step for dynamically added file formats, because
1130 * IdentifyFile does not know how to recognise them.
1132 if (FileFormats.getInstance().isIdentifiable(format))
1136 format = new IdentifyFile().identify(selectedFile,
1137 DataSourceType.FILE);
1138 } catch (FileFormatException e)
1140 // format = null; //??
1144 new FileLoader().LoadFile(viewport, selectedFile,
1145 DataSourceType.FILE, format);
1147 }).openDialog(this);
1151 * Shows a dialog for input of a URL at which to retrieve alignment data
1156 public void inputURLMenuItem_actionPerformed(AlignViewport viewport)
1158 // This construct allows us to have a wider textfield
1160 JLabel label = new JLabel(
1161 MessageManager.getString("label.input_file_url"));
1163 JPanel panel = new JPanel(new GridLayout(2, 1));
1167 * the URL to fetch is
1168 * Java: an editable combobox with history
1169 * JS: (pending JAL-3038) a plain text field
1172 String urlBase = "http://www.";
1175 history = new JTextField(urlBase, 35);
1179 JComboBox<String> asCombo = new JComboBox<>();
1180 asCombo.setPreferredSize(new Dimension(400, 20));
1181 asCombo.setEditable(true);
1182 asCombo.addItem(urlBase);
1183 String historyItems = Cache.getProperty("RECENT_URL");
1184 if (historyItems != null)
1186 for (String token : historyItems.split("\\t"))
1188 asCombo.addItem(token);
1195 Object[] options = new Object[] { MessageManager.getString("action.ok"),
1196 MessageManager.getString("action.cancel") };
1197 RunResponse action = new RunResponse(JvOptionPane.OK_OPTION) {
1201 String url = Jalview.isJS() ? ((JTextField) history).getText()
1202 : ((JComboBox<String>) history).getSelectedItem()
1205 if (url.toLowerCase().endsWith(".jar"))
1207 if (viewport != null)
1209 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1210 FileFormat.Jalview);
1214 new FileLoader().LoadFile(url, DataSourceType.URL,
1215 FileFormat.Jalview);
1220 FileFormatI format = null;
1223 format = new IdentifyFile().identify(url, DataSourceType.URL);
1224 } catch (FileFormatException e)
1226 // TODO revise error handling, distinguish between
1227 // URL not found and response not valid
1232 String msg = MessageManager.formatMessage("label.couldnt_locate", url);
1233 JvOptionPane.showInternalMessageDialog(Desktop.desktop, msg,
1234 MessageManager.getString("label.url_not_found"),
1235 JvOptionPane.WARNING_MESSAGE);
1240 if (viewport != null)
1242 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1247 new FileLoader().LoadFile(url, DataSourceType.URL, format);
1251 String dialogOption = MessageManager
1252 .getString("label.input_alignment_from_url");
1253 JvOptionPane.newOptionDialog(desktop).response(action)
1254 .showInternalDialog(panel, dialogOption,
1255 JvOptionPane.YES_NO_CANCEL_OPTION,
1256 JvOptionPane.PLAIN_MESSAGE, null, options,
1257 MessageManager.getString("action.ok"));
1261 * Opens the CutAndPaste window for the user to paste an alignment in to
1264 * - if not null, the pasted alignment is added to the current
1265 * alignment; if null, to a new alignment window
1268 public void inputTextboxMenuItem_actionPerformed(
1269 AlignmentViewPanel viewPanel)
1271 CutAndPasteTransfer cap = new CutAndPasteTransfer();
1272 cap.setForInput(viewPanel);
1273 Desktop.addInternalFrame(cap,
1274 MessageManager.getString("label.cut_paste_alignmen_file"), true,
1284 Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
1285 jalview.bin.Cache.setProperty("SCREENGEOMETRY_WIDTH",
1287 jalview.bin.Cache.setProperty("SCREENGEOMETRY_HEIGHT",
1288 screen.height + "");
1289 storeLastKnownDimensions("", new Rectangle(getBounds().x, getBounds().y,
1290 getWidth(), getHeight()));
1292 if (jconsole != null)
1294 storeLastKnownDimensions("JAVA_CONSOLE_", jconsole.getBounds());
1295 jconsole.stopConsole();
1299 storeLastKnownDimensions("JALVIEW_RSS_WINDOW_", jvnews.getBounds());
1302 if (dialogExecutor != null)
1304 dialogExecutor.shutdownNow();
1306 closeAll_actionPerformed(null);
1308 if (groovyConsole != null)
1310 // suppress a possible repeat prompt to save script
1311 groovyConsole.setDirty(false);
1312 groovyConsole.exit();
1317 private void storeLastKnownDimensions(String string, Rectangle jc)
1319 jalview.bin.Cache.log.debug("Storing last known dimensions for "
1320 + string + ": x:" + jc.x + " y:" + jc.y + " width:" + jc.width
1321 + " height:" + jc.height);
1323 jalview.bin.Cache.setProperty(string + "SCREEN_X", jc.x + "");
1324 jalview.bin.Cache.setProperty(string + "SCREEN_Y", jc.y + "");
1325 jalview.bin.Cache.setProperty(string + "SCREEN_WIDTH", jc.width + "");
1326 jalview.bin.Cache.setProperty(string + "SCREEN_HEIGHT", jc.height + "");
1336 public void aboutMenuItem_actionPerformed(ActionEvent e)
1338 // StringBuffer message = getAboutMessage(false);
1339 // JvOptionPane.showInternalMessageDialog(Desktop.desktop,
1341 // message.toString(), "About Jalview", JvOptionPane.INFORMATION_MESSAGE);
1342 new Thread(new Runnable()
1347 new SplashScreen(true);
1352 public StringBuffer getAboutMessage(boolean shortv)
1354 StringBuffer message = new StringBuffer();
1355 message.append("<html>");
1358 message.append("<h1><strong>Version: "
1359 + jalview.bin.Cache.getProperty("VERSION")
1360 + "</strong></h1>");
1361 message.append("<strong>Last Updated: <em>"
1362 + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown")
1363 + "</em></strong>");
1369 message.append("<strong>Version "
1370 + jalview.bin.Cache.getProperty("VERSION")
1371 + "; last updated: "
1372 + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown"));
1375 if (jalview.bin.Cache.getDefault("LATEST_VERSION", "Checking")
1376 .equals("Checking"))
1378 message.append("<br>...Checking latest version...</br>");
1380 else if (!jalview.bin.Cache.getDefault("LATEST_VERSION", "Checking")
1381 .equals(jalview.bin.Cache.getProperty("VERSION")))
1383 boolean red = false;
1384 if (jalview.bin.Cache.getProperty("VERSION").toLowerCase()
1385 .indexOf("automated build") == -1)
1388 // Displayed when code version and jnlp version do not match and code
1389 // version is not a development build
1390 message.append("<div style=\"color: #FF0000;font-style: bold;\">");
1393 message.append("<br>!! Version "
1394 + jalview.bin.Cache.getDefault("LATEST_VERSION",
1396 + " is available for download from "
1397 + jalview.bin.Cache.getDefault("www.jalview.org",
1398 "http://www.jalview.org")
1402 message.append("</div>");
1405 message.append("<br>Authors: " + jalview.bin.Cache.getDefault(
1407 "The Jalview Authors (See AUTHORS file for current list)")
1408 + "<br><br>Development managed by The Barton Group, University of Dundee, Scotland, UK.<br>"
1409 + "<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"
1410 + "<br><br>If you use Jalview, please cite:"
1411 + "<br>Waterhouse, A.M., Procter, J.B., Martin, D.M.A, Clamp, M. and Barton, G. J. (2009)"
1412 + "<br>Jalview Version 2 - a multiple sequence alignment editor and analysis workbench"
1413 + "<br>Bioinformatics doi: 10.1093/bioinformatics/btp033"
1419 * Action on requesting Help documentation
1422 public void documentationMenuItem_actionPerformed()
1428 BrowserLauncher.openURL("http://www.jalview.org/help.html");
1432 Help.showHelpWindow();
1434 } catch (Exception ex)
1436 System.err.println("Error opening help: " + ex.getMessage());
1441 public void closeAll_actionPerformed(ActionEvent e)
1443 // TODO show a progress bar while closing?
1444 JInternalFrame[] frames = desktop.getAllFrames();
1445 for (int i = 0; i < frames.length; i++)
1449 frames[i].setClosed(true);
1450 } catch (java.beans.PropertyVetoException ex)
1454 Jalview.setCurrentAlignFrame(null);
1455 System.out.println("ALL CLOSED");
1456 if (v_client != null)
1458 // TODO clear binding to vamsas document objects on close_all
1462 * reset state of singleton objects as appropriate (clear down session state
1463 * when all windows are closed)
1465 StructureSelectionManager ssm = StructureSelectionManager
1466 .getStructureSelectionManager(this);
1474 public void raiseRelated_actionPerformed(ActionEvent e)
1476 reorderAssociatedWindows(false, false);
1480 public void minimizeAssociated_actionPerformed(ActionEvent e)
1482 reorderAssociatedWindows(true, false);
1485 void closeAssociatedWindows()
1487 reorderAssociatedWindows(false, true);
1493 * @seejalview.jbgui.GDesktop#garbageCollect_actionPerformed(java.awt.event.
1497 protected void garbageCollect_actionPerformed(ActionEvent e)
1499 // We simply collect the garbage
1500 jalview.bin.Cache.log.debug("Collecting garbage...");
1502 jalview.bin.Cache.log.debug("Finished garbage collection.");
1509 * jalview.jbgui.GDesktop#showMemusage_actionPerformed(java.awt.event.ActionEvent
1513 protected void showMemusage_actionPerformed(ActionEvent e)
1515 desktop.showMemoryUsage(showMemusage.isSelected());
1522 * jalview.jbgui.GDesktop#showConsole_actionPerformed(java.awt.event.ActionEvent
1526 protected void showConsole_actionPerformed(ActionEvent e)
1528 showConsole(showConsole.isSelected());
1531 Console jconsole = null;
1534 * control whether the java console is visible or not
1538 void showConsole(boolean selected)
1540 // TODO: decide if we should update properties file
1541 if (jconsole != null) // BH 2018
1543 showConsole.setSelected(selected);
1544 Cache.setProperty("SHOW_JAVA_CONSOLE",
1545 Boolean.valueOf(selected).toString());
1546 jconsole.setVisible(selected);
1550 void reorderAssociatedWindows(boolean minimize, boolean close)
1552 JInternalFrame[] frames = desktop.getAllFrames();
1553 if (frames == null || frames.length < 1)
1558 AlignmentViewport source = null, target = null;
1559 if (frames[0] instanceof AlignFrame)
1561 source = ((AlignFrame) frames[0]).getCurrentView();
1563 else if (frames[0] instanceof TreePanel)
1565 source = ((TreePanel) frames[0]).getViewPort();
1567 else if (frames[0] instanceof PCAPanel)
1569 source = ((PCAPanel) frames[0]).av;
1571 else if (frames[0].getContentPane() instanceof PairwiseAlignPanel)
1573 source = ((PairwiseAlignPanel) frames[0].getContentPane()).av;
1578 for (int i = 0; i < frames.length; i++)
1581 if (frames[i] == null)
1585 if (frames[i] instanceof AlignFrame)
1587 target = ((AlignFrame) frames[i]).getCurrentView();
1589 else if (frames[i] instanceof TreePanel)
1591 target = ((TreePanel) frames[i]).getViewPort();
1593 else if (frames[i] instanceof PCAPanel)
1595 target = ((PCAPanel) frames[i]).av;
1597 else if (frames[i].getContentPane() instanceof PairwiseAlignPanel)
1599 target = ((PairwiseAlignPanel) frames[i].getContentPane()).av;
1602 if (source == target)
1608 frames[i].setClosed(true);
1612 frames[i].setIcon(minimize);
1615 frames[i].toFront();
1619 } catch (java.beans.PropertyVetoException ex)
1634 protected void preferences_actionPerformed(ActionEvent e)
1640 * Prompts the user to choose a file and then saves the Jalview state as a
1641 * Jalview project file
1644 public void saveState_actionPerformed(boolean asCastor)
1646 JalviewFileChooser chooser = new JalviewFileChooser(
1647 asCastor ? "jvp" : "jvx",
1650 chooser.setFileView(new JalviewFileView());
1651 chooser.setDialogTitle(MessageManager.getString("label.save_state"));
1652 int option = chooser.showSaveDialog(this);
1653 if (option == JalviewFileChooser.APPROVE_OPTION)
1655 File choice = chooser.getSelectedFile();
1656 setProjectFile(choice);
1658 new Thread(new Runnable()
1663 // TODO: refactor to Jalview desktop session controller action.
1664 setProgressBar(MessageManager.formatMessage(
1665 "label.saving_jalview_project", new Object[]
1666 { choice.getName() }), choice.hashCode());
1667 jalview.bin.Cache.setProperty("LAST_DIRECTORY",
1668 choice.getParent());
1669 // TODO catch and handle errors for savestate
1670 // TODO prevent user from messing with the Desktop whilst we're saving
1675 new Jalview2XML().saveState(choice);
1679 new jalview.project.Jalview2XML().saveState(choice);
1681 } catch (OutOfMemoryError oom)
1684 "Whilst saving current state to " + choice.getName(),
1686 } catch (Exception ex)
1689 "Problems whilst trying to save to " + choice.getName(),
1691 JvOptionPane.showMessageDialog(Desktop.this,
1692 MessageManager.formatMessage(
1693 "label.error_whilst_saving_current_state_to",
1695 { choice.getName() }),
1696 MessageManager.getString("label.couldnt_save_project"),
1697 JvOptionPane.WARNING_MESSAGE);
1699 setProgressBar(null, choice.hashCode());
1705 void setProjectFile(File choice)
1707 this.projectFile = choice;
1710 public File getProjectFile()
1712 return this.projectFile;
1716 * Prompts the user to choose a file and loads in as a Jalview project file
1719 public void loadState_actionPerformed(boolean asCastor)
1721 // TODO: GET RID OF .JVX BEFORE RELEASE JIM!
1722 final String[] suffix = asCastor ? new String[] { "jvp", "jar" }
1725 final String[] desc = asCastor
1727 { "Jalview Project", "Jalview Project (old)" }
1729 { "Jalview Project" };
1730 JalviewFileChooser chooser = new JalviewFileChooser(
1731 Cache.getProperty("LAST_DIRECTORY"), suffix,
1734 chooser.setFileView(new JalviewFileView());
1735 chooser.setDialogTitle(MessageManager.getString("label.restore_state"));
1736 chooser.response(new RunResponse(JalviewFileChooser.APPROVE_OPTION)
1741 File selectedFile = chooser.getSelectedFile();
1742 setProjectFile(selectedFile);
1743 final String choice = selectedFile.getAbsolutePath();
1744 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1745 new Thread(new Runnable()
1753 new Jalview2XML().loadJalviewAlign(choice);
1757 new jalview.project.Jalview2XML().loadJalviewAlign(choice);
1759 } catch (OutOfMemoryError oom)
1761 new OOMWarning("Whilst loading project from " + choice, oom);
1762 } catch (Exception ex)
1765 "Problems whilst loading project from " + choice, ex);
1766 JvOptionPane.showMessageDialog(Desktop.desktop,
1767 MessageManager.formatMessage(
1768 "label.error_whilst_loading_project_from",
1771 MessageManager.getString("label.couldnt_load_project"),
1772 JvOptionPane.WARNING_MESSAGE);
1779 chooser.showOpenDialog(this);
1783 public void inputSequence_actionPerformed(ActionEvent e)
1785 new SequenceFetcher(this);
1788 JPanel progressPanel;
1790 ArrayList<JPanel> fileLoadingPanels = new ArrayList<>();
1792 public void startLoading(final Object fileName)
1794 if (fileLoadingCount == 0)
1796 fileLoadingPanels.add(addProgressPanel(MessageManager
1797 .formatMessage("label.loading_file", new Object[]
1803 private JPanel addProgressPanel(String string)
1805 if (progressPanel == null)
1807 progressPanel = new JPanel(new GridLayout(1, 1));
1808 totalProgressCount = 0;
1809 instance.getContentPane().add(progressPanel, BorderLayout.SOUTH);
1811 JPanel thisprogress = new JPanel(new BorderLayout(10, 5));
1812 JProgressBar progressBar = new JProgressBar();
1813 progressBar.setIndeterminate(true);
1815 thisprogress.add(new JLabel(string), BorderLayout.WEST);
1817 thisprogress.add(progressBar, BorderLayout.CENTER);
1818 progressPanel.add(thisprogress);
1819 ((GridLayout) progressPanel.getLayout()).setRows(
1820 ((GridLayout) progressPanel.getLayout()).getRows() + 1);
1821 ++totalProgressCount;
1822 instance.validate();
1823 return thisprogress;
1826 int totalProgressCount = 0;
1828 private void removeProgressPanel(JPanel progbar)
1830 if (progressPanel != null)
1832 synchronized (progressPanel)
1834 progressPanel.remove(progbar);
1835 GridLayout gl = (GridLayout) progressPanel.getLayout();
1836 gl.setRows(gl.getRows() - 1);
1837 if (--totalProgressCount < 1)
1839 this.getContentPane().remove(progressPanel);
1840 progressPanel = null;
1847 public void stopLoading()
1850 if (fileLoadingCount < 1)
1852 while (fileLoadingPanels.size() > 0)
1854 removeProgressPanel(fileLoadingPanels.remove(0));
1856 fileLoadingPanels.clear();
1857 fileLoadingCount = 0;
1862 public static int getViewCount(String alignmentId)
1864 AlignmentViewport[] aps = getViewports(alignmentId);
1865 return (aps == null) ? 0 : aps.length;
1870 * @param alignmentId
1871 * - if null, all sets are returned
1872 * @return all AlignmentPanels concerning the alignmentId sequence set
1874 public static AlignmentPanel[] getAlignmentPanels(String alignmentId)
1876 if (Desktop.desktop == null)
1878 // no frames created and in headless mode
1879 // TODO: verify that frames are recoverable when in headless mode
1882 List<AlignmentPanel> aps = new ArrayList<>();
1883 AlignFrame[] frames = getAlignFrames();
1888 for (AlignFrame af : frames)
1890 for (AlignmentPanel ap : af.alignPanels)
1892 if (alignmentId == null
1893 || alignmentId.equals(ap.av.getSequenceSetId()))
1899 if (aps.size() == 0)
1903 AlignmentPanel[] vap = aps.toArray(new AlignmentPanel[aps.size()]);
1908 * get all the viewports on an alignment.
1910 * @param sequenceSetId
1911 * unique alignment id (may be null - all viewports returned in that
1913 * @return all viewports on the alignment bound to sequenceSetId
1915 public static AlignmentViewport[] getViewports(String sequenceSetId)
1917 List<AlignmentViewport> viewp = new ArrayList<>();
1918 if (desktop != null)
1920 AlignFrame[] frames = Desktop.getAlignFrames();
1922 for (AlignFrame afr : frames)
1924 if (sequenceSetId == null || afr.getViewport().getSequenceSetId()
1925 .equals(sequenceSetId))
1927 if (afr.alignPanels != null)
1929 for (AlignmentPanel ap : afr.alignPanels)
1931 if (sequenceSetId == null
1932 || sequenceSetId.equals(ap.av.getSequenceSetId()))
1940 viewp.add(afr.getViewport());
1944 if (viewp.size() > 0)
1946 return viewp.toArray(new AlignmentViewport[viewp.size()]);
1953 * Explode the views in the given frame into separate AlignFrame
1957 public static void explodeViews(AlignFrame af)
1959 int size = af.alignPanels.size();
1965 for (int i = 0; i < size; i++)
1967 AlignmentPanel ap = af.alignPanels.get(i);
1968 AlignFrame newaf = new AlignFrame(ap);
1971 * Restore the view's last exploded frame geometry if known. Multiple
1972 * views from one exploded frame share and restore the same (frame)
1973 * position and size.
1975 Rectangle geometry = ap.av.getExplodedGeometry();
1976 if (geometry != null)
1978 newaf.setBounds(geometry);
1981 ap.av.setGatherViewsHere(false);
1983 addInternalFrame(newaf, af.getTitle(), AlignFrame.DEFAULT_WIDTH,
1984 AlignFrame.DEFAULT_HEIGHT);
1987 af.alignPanels.clear();
1988 af.closeMenuItem_actionPerformed(true);
1993 * Gather expanded views (separate AlignFrame's) with the same sequence set
1994 * identifier back in to this frame as additional views, and close the expanded
1995 * views. Note the expanded frames may themselves have multiple views. We take
2000 public void gatherViews(AlignFrame source)
2002 source.viewport.setGatherViewsHere(true);
2003 source.viewport.setExplodedGeometry(source.getBounds());
2004 JInternalFrame[] frames = desktop.getAllFrames();
2005 String viewId = source.viewport.getSequenceSetId();
2007 for (int t = 0; t < frames.length; t++)
2009 if (frames[t] instanceof AlignFrame && frames[t] != source)
2011 AlignFrame af = (AlignFrame) frames[t];
2012 boolean gatherThis = false;
2013 for (int a = 0; a < af.alignPanels.size(); a++)
2015 AlignmentPanel ap = af.alignPanels.get(a);
2016 if (viewId.equals(ap.av.getSequenceSetId()))
2019 ap.av.setGatherViewsHere(false);
2020 ap.av.setExplodedGeometry(af.getBounds());
2021 source.addAlignmentPanel(ap, false);
2027 af.alignPanels.clear();
2028 af.closeMenuItem_actionPerformed(true);
2035 jalview.gui.VamsasApplication v_client = null;
2038 public void vamsasImport_actionPerformed(ActionEvent e)
2040 // TODO: JAL-3048 not needed for Jalview-JS
2042 if (v_client == null)
2044 // Load and try to start a session.
2045 JalviewFileChooser chooser = new JalviewFileChooser(
2046 jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
2048 chooser.setFileView(new JalviewFileView());
2049 chooser.setDialogTitle(
2050 MessageManager.getString("label.open_saved_vamsas_session"));
2051 chooser.setToolTipText(MessageManager.getString(
2052 "label.select_vamsas_session_opened_as_new_vamsas_session"));
2054 int value = chooser.showOpenDialog(this);
2056 if (value == JalviewFileChooser.APPROVE_OPTION)
2058 String fle = chooser.getSelectedFile().toString();
2059 if (!vamsasImport(chooser.getSelectedFile()))
2061 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
2062 MessageManager.formatMessage(
2063 "label.couldnt_import_as_vamsas_session",
2067 .getString("label.vamsas_document_import_failed"),
2068 JvOptionPane.ERROR_MESSAGE);
2074 jalview.bin.Cache.log.error(
2075 "Implementation error - load session from a running session is not supported.");
2080 * import file into a new vamsas session (uses jalview.gui.VamsasApplication)
2083 * @return true if import was a success and a session was started.
2085 public boolean vamsasImport(URL url)
2087 // TODO: create progress bar
2088 if (v_client != null)
2091 jalview.bin.Cache.log.error(
2092 "Implementation error - load session from a running session is not supported.");
2098 // copy the URL content to a temporary local file
2099 // TODO: be a bit cleverer here with nio (?!)
2100 File file = File.createTempFile("vdocfromurl", ".vdj");
2101 FileOutputStream fos = new FileOutputStream(file);
2102 BufferedInputStream bis = new BufferedInputStream(url.openStream());
2103 byte[] buffer = new byte[2048];
2105 while ((ln = bis.read(buffer)) > -1)
2107 fos.write(buffer, 0, ln);
2111 v_client = new jalview.gui.VamsasApplication(this, file,
2112 url.toExternalForm());
2113 } catch (Exception ex)
2115 jalview.bin.Cache.log.error(
2116 "Failed to create new vamsas session from contents of URL "
2121 setupVamsasConnectedGui();
2122 v_client.initial_update(); // TODO: thread ?
2123 return v_client.inSession();
2127 * import file into a new vamsas session (uses jalview.gui.VamsasApplication)
2130 * @return true if import was a success and a session was started.
2132 public boolean vamsasImport(File file)
2134 if (v_client != null)
2137 jalview.bin.Cache.log.error(
2138 "Implementation error - load session from a running session is not supported.");
2142 setProgressBar(MessageManager.formatMessage(
2143 "status.importing_vamsas_session_from", new Object[]
2144 { file.getName() }), file.hashCode());
2147 v_client = new jalview.gui.VamsasApplication(this, file, null);
2148 } catch (Exception ex)
2150 setProgressBar(MessageManager.formatMessage(
2151 "status.importing_vamsas_session_from", new Object[]
2152 { file.getName() }), file.hashCode());
2153 jalview.bin.Cache.log.error(
2154 "New vamsas session from existing session file failed:", ex);
2157 setupVamsasConnectedGui();
2158 v_client.initial_update(); // TODO: thread ?
2159 setProgressBar(MessageManager.formatMessage(
2160 "status.importing_vamsas_session_from", new Object[]
2161 { file.getName() }), file.hashCode());
2162 return v_client.inSession();
2165 public boolean joinVamsasSession(String mysesid)
2167 if (v_client != null)
2169 throw new Error(MessageManager
2170 .getString("error.try_join_vamsas_session_another"));
2172 if (mysesid == null)
2175 MessageManager.getString("error.invalid_vamsas_session_id"));
2177 v_client = new VamsasApplication(this, mysesid);
2178 setupVamsasConnectedGui();
2179 v_client.initial_update();
2180 return (v_client.inSession());
2184 public void vamsasStart_actionPerformed(ActionEvent e)
2186 if (v_client == null)
2189 // we just start a default session for moment.
2191 * JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache.
2192 * getProperty("LAST_DIRECTORY"));
2194 * chooser.setFileView(new JalviewFileView());
2195 * chooser.setDialogTitle("Load Vamsas file");
2196 * chooser.setToolTipText("Import");
2198 * int value = chooser.showOpenDialog(this);
2200 * if (value == JalviewFileChooser.APPROVE_OPTION) { v_client = new
2201 * jalview.gui.VamsasApplication(this, chooser.getSelectedFile());
2203 v_client = new VamsasApplication(this);
2204 setupVamsasConnectedGui();
2205 v_client.initial_update(); // TODO: thread ?
2209 // store current data in session.
2210 v_client.push_update(); // TODO: thread
2214 protected void setupVamsasConnectedGui()
2216 vamsasStart.setText(MessageManager.getString("label.session_update"));
2217 vamsasSave.setVisible(true);
2218 vamsasStop.setVisible(true);
2219 vamsasImport.setVisible(false); // Document import to existing session is
2220 // not possible for vamsas-client-1.0.
2223 protected void setupVamsasDisconnectedGui()
2225 vamsasSave.setVisible(false);
2226 vamsasStop.setVisible(false);
2227 vamsasImport.setVisible(true);
2229 .setText(MessageManager.getString("label.new_vamsas_session"));
2233 public void vamsasStop_actionPerformed(ActionEvent e)
2235 if (v_client != null)
2237 v_client.end_session();
2239 setupVamsasDisconnectedGui();
2243 protected void buildVamsasStMenu()
2245 if (v_client == null)
2247 String[] sess = null;
2250 sess = VamsasApplication.getSessionList();
2251 } catch (Exception e)
2253 jalview.bin.Cache.log.warn("Problem getting current sessions list.",
2259 jalview.bin.Cache.log.debug(
2260 "Got current sessions list: " + sess.length + " entries.");
2261 VamsasStMenu.removeAll();
2262 for (int i = 0; i < sess.length; i++)
2264 JMenuItem sessit = new JMenuItem();
2265 sessit.setText(sess[i]);
2266 sessit.setToolTipText(MessageManager
2267 .formatMessage("label.connect_to_session", new Object[]
2269 final Desktop dsktp = this;
2270 final String mysesid = sess[i];
2271 sessit.addActionListener(new ActionListener()
2275 public void actionPerformed(ActionEvent e)
2277 if (dsktp.v_client == null)
2279 Thread rthr = new Thread(new Runnable()
2285 dsktp.v_client = new VamsasApplication(dsktp, mysesid);
2286 dsktp.setupVamsasConnectedGui();
2287 dsktp.v_client.initial_update();
2295 VamsasStMenu.add(sessit);
2297 // don't show an empty menu.
2298 VamsasStMenu.setVisible(sess.length > 0);
2303 jalview.bin.Cache.log.debug("No current vamsas sessions.");
2304 VamsasStMenu.removeAll();
2305 VamsasStMenu.setVisible(false);
2310 // Not interested in the content. Just hide ourselves.
2311 VamsasStMenu.setVisible(false);
2316 public void vamsasSave_actionPerformed(ActionEvent e)
2318 // TODO: JAL-3048 not needed for Jalview-JS
2320 if (v_client != null)
2322 // TODO: VAMSAS DOCUMENT EXTENSION is VDJ
2323 JalviewFileChooser chooser = new JalviewFileChooser("vdj",
2326 chooser.setFileView(new JalviewFileView());
2327 chooser.setDialogTitle(MessageManager
2328 .getString("label.save_vamsas_document_archive"));
2330 int value = chooser.showSaveDialog(this);
2332 if (value == JalviewFileChooser.APPROVE_OPTION)
2334 java.io.File choice = chooser.getSelectedFile();
2335 JPanel progpanel = addProgressPanel(MessageManager
2336 .formatMessage("label.saving_vamsas_doc", new Object[]
2337 { choice.getName() }));
2338 Cache.setProperty("LAST_DIRECTORY", choice.getParent());
2339 String warnmsg = null;
2340 String warnttl = null;
2343 v_client.vclient.storeDocument(choice);
2346 warnttl = "Serious Problem saving Vamsas Document";
2347 warnmsg = ex.toString();
2348 jalview.bin.Cache.log
2349 .error("Error Whilst saving document to " + choice, ex);
2351 } catch (Exception ex)
2353 warnttl = "Problem saving Vamsas Document.";
2354 warnmsg = ex.toString();
2355 jalview.bin.Cache.log.warn(
2356 "Exception Whilst saving document to " + choice, ex);
2359 removeProgressPanel(progpanel);
2360 if (warnmsg != null)
2362 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
2364 warnmsg, warnttl, JvOptionPane.ERROR_MESSAGE);
2370 JPanel vamUpdate = null;
2373 * hide vamsas user gui bits when a vamsas document event is being handled.
2376 * true to hide gui, false to reveal gui
2378 public void setVamsasUpdate(boolean b)
2380 Cache.log.debug("Setting gui for Vamsas update "
2381 + (b ? "in progress" : "finished"));
2383 if (vamUpdate != null)
2385 this.removeProgressPanel(vamUpdate);
2389 vamUpdate = this.addProgressPanel(
2390 MessageManager.getString("label.updating_vamsas_session"));
2392 vamsasStart.setVisible(!b);
2393 vamsasStop.setVisible(!b);
2394 vamsasSave.setVisible(!b);
2397 public JInternalFrame[] getAllFrames()
2399 return desktop.getAllFrames();
2403 * Checks the given url to see if it gives a response indicating that the user
2404 * should be informed of a new questionnaire.
2408 public void checkForQuestionnaire(String url)
2410 UserQuestionnaireCheck jvq = new UserQuestionnaireCheck(url);
2411 // javax.swing.SwingUtilities.invokeLater(jvq);
2412 new Thread(jvq).start();
2415 public void checkURLLinks()
2417 // Thread off the URL link checker
2418 addDialogThread(new Runnable()
2423 if (Cache.getDefault("CHECKURLLINKS", true))
2425 // check what the actual links are - if it's just the default don't
2426 // bother with the warning
2427 List<String> links = Preferences.sequenceUrlLinks
2430 // only need to check links if there is one with a
2431 // SEQUENCE_ID which is not the default EMBL_EBI link
2432 ListIterator<String> li = links.listIterator();
2433 boolean check = false;
2434 List<JLabel> urls = new ArrayList<>();
2435 while (li.hasNext())
2437 String link = li.next();
2438 if (link.contains(SEQUENCE_ID)
2439 && !UrlConstants.isDefaultString(link))
2442 int barPos = link.indexOf("|");
2443 String urlMsg = barPos == -1 ? link
2444 : link.substring(0, barPos) + ": "
2445 + link.substring(barPos + 1);
2446 urls.add(new JLabel(urlMsg));
2454 // ask user to check in case URL links use old style tokens
2455 // ($SEQUENCE_ID$ for sequence id _or_ accession id)
2456 JPanel msgPanel = new JPanel();
2457 msgPanel.setLayout(new BoxLayout(msgPanel, BoxLayout.PAGE_AXIS));
2458 msgPanel.add(Box.createVerticalGlue());
2459 JLabel msg = new JLabel(MessageManager
2460 .getString("label.SEQUENCE_ID_for_DB_ACCESSION1"));
2461 JLabel msg2 = new JLabel(MessageManager
2462 .getString("label.SEQUENCE_ID_for_DB_ACCESSION2"));
2464 for (JLabel url : urls)
2470 final JCheckBox jcb = new JCheckBox(
2471 MessageManager.getString("label.do_not_display_again"));
2472 jcb.addActionListener(new ActionListener()
2475 public void actionPerformed(ActionEvent e)
2477 // update Cache settings for "don't show this again"
2478 boolean showWarningAgain = !jcb.isSelected();
2479 Cache.setProperty("CHECKURLLINKS",
2480 Boolean.valueOf(showWarningAgain).toString());
2485 JvOptionPane.showMessageDialog(Desktop.desktop, msgPanel,
2487 .getString("label.SEQUENCE_ID_no_longer_used"),
2488 JvOptionPane.WARNING_MESSAGE);
2495 * Proxy class for JDesktopPane which optionally displays the current memory
2496 * usage and highlights the desktop area with a red bar if free memory runs low.
2500 public class MyDesktopPane extends JDesktopPane
2503 private static final float ONE_MB = 1048576f;
2505 boolean showMemoryUsage = false;
2509 java.text.NumberFormat df;
2511 float maxMemory, allocatedMemory, freeMemory, totalFreeMemory,
2514 public MyDesktopPane(boolean showMemoryUsage)
2516 showMemoryUsage(showMemoryUsage);
2519 public void showMemoryUsage(boolean showMemory)
2521 this.showMemoryUsage = showMemory;
2524 Thread worker = new Thread(this);
2530 public boolean isShowMemoryUsage()
2532 return showMemoryUsage;
2538 df = java.text.NumberFormat.getNumberInstance();
2539 df.setMaximumFractionDigits(2);
2540 runtime = Runtime.getRuntime();
2542 while (showMemoryUsage)
2546 maxMemory = runtime.maxMemory() / ONE_MB;
2547 allocatedMemory = runtime.totalMemory() / ONE_MB;
2548 freeMemory = runtime.freeMemory() / ONE_MB;
2549 totalFreeMemory = freeMemory + (maxMemory - allocatedMemory);
2551 percentUsage = (totalFreeMemory / maxMemory) * 100;
2553 // if (percentUsage < 20)
2555 // border1 = BorderFactory.createMatteBorder(12, 12, 12, 12,
2557 // instance.set.setBorder(border1);
2560 // sleep after showing usage
2562 } catch (Exception ex)
2564 ex.printStackTrace();
2570 public void paintComponent(Graphics g)
2572 if (showMemoryUsage && g != null && df != null)
2574 if (percentUsage < 20)
2576 g.setColor(Color.red);
2578 FontMetrics fm = g.getFontMetrics();
2581 g.drawString(MessageManager.formatMessage("label.memory_stats",
2583 { df.format(totalFreeMemory), df.format(maxMemory),
2584 df.format(percentUsage) }),
2585 10, getHeight() - fm.getHeight());
2592 * Accessor method to quickly get all the AlignmentFrames loaded.
2594 * @return an array of AlignFrame, or null if none found
2596 public static AlignFrame[] getAlignFrames()
2598 if (Jalview.isHeadlessMode())
2600 // Desktop.desktop is null in headless mode
2601 return new AlignFrame[] { Jalview.currentAlignFrame };
2604 JInternalFrame[] frames = Desktop.desktop.getAllFrames();
2610 List<AlignFrame> avp = new ArrayList<>();
2612 for (int i = frames.length - 1; i > -1; i--)
2614 if (frames[i] instanceof AlignFrame)
2616 avp.add((AlignFrame) frames[i]);
2618 else if (frames[i] instanceof SplitFrame)
2621 * Also check for a split frame containing an AlignFrame
2623 GSplitFrame sf = (GSplitFrame) frames[i];
2624 if (sf.getTopFrame() instanceof AlignFrame)
2626 avp.add((AlignFrame) sf.getTopFrame());
2628 if (sf.getBottomFrame() instanceof AlignFrame)
2630 avp.add((AlignFrame) sf.getBottomFrame());
2634 if (avp.size() == 0)
2638 AlignFrame afs[] = avp.toArray(new AlignFrame[avp.size()]);
2643 * Returns an array of any AppJmol frames in the Desktop (or null if none).
2647 public GStructureViewer[] getJmols()
2649 JInternalFrame[] frames = Desktop.desktop.getAllFrames();
2655 List<GStructureViewer> avp = new ArrayList<>();
2657 for (int i = frames.length - 1; i > -1; i--)
2659 if (frames[i] instanceof AppJmol)
2661 GStructureViewer af = (GStructureViewer) frames[i];
2665 if (avp.size() == 0)
2669 GStructureViewer afs[] = avp.toArray(new GStructureViewer[avp.size()]);
2674 * Add Groovy Support to Jalview
2677 public void groovyShell_actionPerformed()
2681 openGroovyConsole();
2682 } catch (Exception ex)
2684 jalview.bin.Cache.log.error("Groovy Shell Creation failed.", ex);
2685 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
2687 MessageManager.getString("label.couldnt_create_groovy_shell"),
2688 MessageManager.getString("label.groovy_support_failed"),
2689 JvOptionPane.ERROR_MESSAGE);
2694 * Open the Groovy console
2696 void openGroovyConsole()
2698 if (groovyConsole == null)
2700 groovyConsole = new groovy.ui.Console();
2701 groovyConsole.setVariable("Jalview", this);
2702 groovyConsole.run();
2705 * We allow only one console at a time, so that AlignFrame menu option
2706 * 'Calculate | Run Groovy script' is unambiguous.
2707 * Disable 'Groovy Console', and enable 'Run script', when the console is
2708 * opened, and the reverse when it is closed
2710 Window window = (Window) groovyConsole.getFrame();
2711 window.addWindowListener(new WindowAdapter()
2714 public void windowClosed(WindowEvent e)
2717 * rebind CMD-Q from Groovy Console to Jalview Quit
2720 enableExecuteGroovy(false);
2726 * show Groovy console window (after close and reopen)
2728 ((Window) groovyConsole.getFrame()).setVisible(true);
2731 * if we got this far, enable 'Run Groovy' in AlignFrame menus
2732 * and disable opening a second console
2734 enableExecuteGroovy(true);
2738 * Bind Ctrl/Cmd-Q to Quit - for reset as Groovy Console takes over this binding
2741 protected void addQuitHandler()
2743 getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
2744 .put(KeyStroke.getKeyStroke(KeyEvent.VK_Q,
2745 Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()),
2747 getRootPane().getActionMap().put("Quit", new AbstractAction()
2750 public void actionPerformed(ActionEvent e)
2758 * Enable or disable 'Run Groovy script' in AlignFrame calculate menus
2761 * true if Groovy console is open
2763 public void enableExecuteGroovy(boolean enabled)
2766 * disable opening a second Groovy console
2767 * (or re-enable when the console is closed)
2769 groovyShell.setEnabled(!enabled);
2771 AlignFrame[] alignFrames = getAlignFrames();
2772 if (alignFrames != null)
2774 for (AlignFrame af : alignFrames)
2776 af.setGroovyEnabled(enabled);
2782 * Progress bars managed by the IProgressIndicator method.
2784 private Hashtable<Long, JPanel> progressBars;
2786 private Hashtable<Long, IProgressIndicatorHandler> progressBarHandlers;
2791 * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
2794 public void setProgressBar(String message, long id)
2796 if (progressBars == null)
2798 progressBars = new Hashtable<>();
2799 progressBarHandlers = new Hashtable<>();
2802 if (progressBars.get(new Long(id)) != null)
2804 JPanel panel = progressBars.remove(new Long(id));
2805 if (progressBarHandlers.contains(new Long(id)))
2807 progressBarHandlers.remove(new Long(id));
2809 removeProgressPanel(panel);
2813 progressBars.put(new Long(id), addProgressPanel(message));
2820 * @see jalview.gui.IProgressIndicator#registerHandler(long,
2821 * jalview.gui.IProgressIndicatorHandler)
2824 public void registerHandler(final long id,
2825 final IProgressIndicatorHandler handler)
2827 if (progressBarHandlers == null
2828 || !progressBars.containsKey(new Long(id)))
2830 throw new Error(MessageManager.getString(
2831 "error.call_setprogressbar_before_registering_handler"));
2833 progressBarHandlers.put(new Long(id), handler);
2834 final JPanel progressPanel = progressBars.get(new Long(id));
2835 if (handler.canCancel())
2837 JButton cancel = new JButton(
2838 MessageManager.getString("action.cancel"));
2839 final IProgressIndicator us = this;
2840 cancel.addActionListener(new ActionListener()
2844 public void actionPerformed(ActionEvent e)
2846 handler.cancelActivity(id);
2847 us.setProgressBar(MessageManager
2848 .formatMessage("label.cancelled_params", new Object[]
2849 { ((JLabel) progressPanel.getComponent(0)).getText() }),
2853 progressPanel.add(cancel, BorderLayout.EAST);
2859 * @return true if any progress bars are still active
2862 public boolean operationInProgress()
2864 if (progressBars != null && progressBars.size() > 0)
2872 * This will return the first AlignFrame holding the given viewport instance. It
2873 * will break if there are more than one AlignFrames viewing a particular av.
2876 * @return alignFrame for viewport
2878 public static AlignFrame getAlignFrameFor(AlignViewportI viewport)
2880 if (desktop != null)
2882 AlignmentPanel[] aps = getAlignmentPanels(
2883 viewport.getSequenceSetId());
2884 for (int panel = 0; aps != null && panel < aps.length; panel++)
2886 if (aps[panel] != null && aps[panel].av == viewport)
2888 return aps[panel].alignFrame;
2895 public VamsasApplication getVamsasApplication()
2902 * flag set if jalview GUI is being operated programmatically
2904 private boolean inBatchMode = false;
2907 * check if jalview GUI is being operated programmatically
2909 * @return inBatchMode
2911 public boolean isInBatchMode()
2917 * set flag if jalview GUI is being operated programmatically
2919 * @param inBatchMode
2921 public void setInBatchMode(boolean inBatchMode)
2923 this.inBatchMode = inBatchMode;
2926 public void startServiceDiscovery()
2928 startServiceDiscovery(false);
2931 public void startServiceDiscovery(boolean blocking)
2933 boolean alive = true;
2934 Thread t0 = null, t1 = null, t2 = null;
2935 // JAL-940 - JALVIEW 1 services are now being EOLed as of JABA 2.1 release
2938 // todo: changesupport handlers need to be transferred
2939 if (discoverer == null)
2941 discoverer = new jalview.ws.jws1.Discoverer();
2942 // register PCS handler for desktop.
2943 discoverer.addPropertyChangeListener(changeSupport);
2945 // JAL-940 - disabled JWS1 service configuration - always start discoverer
2946 // until we phase out completely
2947 (t0 = new Thread(discoverer)).start();
2950 if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
2952 t2 = jalview.ws.jws2.Jws2Discoverer.getDiscoverer()
2953 .startDiscoverer(changeSupport);
2957 // TODO: do rest service discovery
2966 } catch (Exception e)
2969 alive = (t1 != null && t1.isAlive()) || (t2 != null && t2.isAlive())
2970 || (t3 != null && t3.isAlive())
2971 || (t0 != null && t0.isAlive());
2977 * called to check if the service discovery process completed successfully.
2981 protected void JalviewServicesChanged(PropertyChangeEvent evt)
2983 if (evt.getNewValue() == null || evt.getNewValue() instanceof Vector)
2985 final String ermsg = jalview.ws.jws2.Jws2Discoverer.getDiscoverer()
2986 .getErrorMessages();
2989 if (Cache.getDefault("SHOW_WSDISCOVERY_ERRORS", true))
2991 if (serviceChangedDialog == null)
2993 // only run if we aren't already displaying one of these.
2994 addDialogThread(serviceChangedDialog = new Runnable()
3001 * JalviewDialog jd =new JalviewDialog() {
3003 * @Override protected void cancelPressed() { // TODO
3004 * Auto-generated method stub
3006 * }@Override protected void okPressed() { // TODO
3007 * Auto-generated method stub
3009 * }@Override protected void raiseClosed() { // TODO
3010 * Auto-generated method stub
3012 * } }; jd.initDialogFrame(new
3013 * JLabel("<html><table width=\"450\"><tr><td>" + ermsg +
3014 * "<br/>It may be that you have invalid JABA URLs in your web service preferences,"
3015 * + " or mis-configured HTTP proxy settings.<br/>" +
3016 * "Check the <em>Connections</em> and <em>Web services</em> tab of the"
3018 * " Tools->Preferences dialog box to change them.</td></tr></table></html>"
3019 * ), true, true, "Web Service Configuration Problem", 450,
3022 * jd.waitForInput();
3024 JvOptionPane.showConfirmDialog(Desktop.desktop,
3025 new JLabel("<html><table width=\"450\"><tr><td>"
3026 + ermsg + "</td></tr></table>"
3027 + "<p>It may be that you have invalid JABA URLs<br/>in your web service preferences,"
3028 + "<br>or as a command-line argument, or mis-configured HTTP proxy settings.</p>"
3029 + "<p>Check the <em>Connections</em> and <em>Web services</em> tab<br/>of the"
3030 + " Tools->Preferences dialog box to change them.</p></html>"),
3031 "Web Service Configuration Problem",
3032 JvOptionPane.DEFAULT_OPTION,
3033 JvOptionPane.ERROR_MESSAGE);
3034 serviceChangedDialog = null;
3043 "Errors reported by JABA discovery service. Check web services preferences.\n"
3050 private Runnable serviceChangedDialog = null;
3053 * start a thread to open a URL in the configured browser. Pops up a warning
3054 * dialog to the user if there is an exception when calling out to the browser
3059 public static void showUrl(final String url)
3061 showUrl(url, Desktop.instance);
3065 * Like showUrl but allows progress handler to be specified
3069 * (null) or object implementing IProgressIndicator
3071 public static void showUrl(final String url,
3072 final IProgressIndicator progress)
3074 new Thread(new Runnable()
3081 if (progress != null)
3083 progress.setProgressBar(MessageManager
3084 .formatMessage("status.opening_params", new Object[]
3085 { url }), this.hashCode());
3087 jalview.util.BrowserLauncher.openURL(url);
3088 } catch (Exception ex)
3090 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
3092 .getString("label.web_browser_not_found_unix"),
3093 MessageManager.getString("label.web_browser_not_found"),
3094 JvOptionPane.WARNING_MESSAGE);
3096 ex.printStackTrace();
3098 if (progress != null)
3100 progress.setProgressBar(null, this.hashCode());
3106 public static WsParamSetManager wsparamManager = null;
3108 public static ParamManager getUserParameterStore()
3110 if (wsparamManager == null)
3112 wsparamManager = new WsParamSetManager();
3114 return wsparamManager;
3118 * static hyperlink handler proxy method for use by Jalview's internal windows
3122 public static void hyperlinkUpdate(HyperlinkEvent e)
3124 if (e.getEventType() == EventType.ACTIVATED)
3129 url = e.getURL().toString();
3130 Desktop.showUrl(url);
3131 } catch (Exception x)
3135 if (Cache.log != null)
3137 Cache.log.error("Couldn't handle string " + url + " as a URL.");
3142 "Couldn't handle string " + url + " as a URL.");
3145 // ignore any exceptions due to dud links.
3152 * single thread that handles display of dialogs to user.
3154 ExecutorService dialogExecutor = Executors.newSingleThreadExecutor();
3157 * flag indicating if dialogExecutor should try to acquire a permit
3159 private volatile boolean dialogPause = true;
3164 private java.util.concurrent.Semaphore block = new Semaphore(0);
3166 private static groovy.ui.Console groovyConsole;
3169 * add another dialog thread to the queue
3173 public void addDialogThread(final Runnable prompter)
3175 dialogExecutor.submit(new Runnable()
3185 } catch (InterruptedException x)
3190 if (instance == null)
3196 SwingUtilities.invokeAndWait(prompter);
3197 } catch (Exception q)
3199 Cache.log.warn("Unexpected Exception in dialog thread.", q);
3205 public void startDialogQueue()
3207 // set the flag so we don't pause waiting for another permit and semaphore
3208 // the current task to begin
3209 dialogPause = false;
3214 * Outputs an image of the desktop to file in EPS format, after prompting the
3215 * user for choice of Text or Lineart character rendering (unless a preference
3216 * has been set). The file name is generated as
3219 * Jalview_snapshot_nnnnn.eps where nnnnn is the current timestamp in milliseconds
3223 protected void snapShotWindow_actionPerformed(ActionEvent e)
3225 // currently the menu option to do this is not shown
3228 int width = getWidth();
3229 int height = getHeight();
3231 "Jalview_snapshot_" + System.currentTimeMillis() + ".eps");
3232 ImageWriterI writer = new ImageWriterI()
3235 public void exportImage(Graphics g) throws Exception
3238 Cache.log.info("Successfully written snapshot to file "
3239 + of.getAbsolutePath());
3242 String title = "View of desktop";
3243 ImageExporter exporter = new ImageExporter(writer, null, TYPE.EPS,
3245 exporter.doExport(of, this, width, height, title);
3249 * Explode the views in the given SplitFrame into separate SplitFrame windows.
3250 * This respects (remembers) any previous 'exploded geometry' i.e. the size and
3251 * location last time the view was expanded (if any). However it does not
3252 * remember the split pane divider location - this is set to match the
3253 * 'exploding' frame.
3257 public void explodeViews(SplitFrame sf)
3259 AlignFrame oldTopFrame = (AlignFrame) sf.getTopFrame();
3260 AlignFrame oldBottomFrame = (AlignFrame) sf.getBottomFrame();
3261 List<? extends AlignmentViewPanel> topPanels = oldTopFrame
3263 List<? extends AlignmentViewPanel> bottomPanels = oldBottomFrame
3265 int viewCount = topPanels.size();
3272 * Processing in reverse order works, forwards order leaves the first panels
3273 * not visible. I don't know why!
3275 for (int i = viewCount - 1; i >= 0; i--)
3278 * Make new top and bottom frames. These take over the respective
3279 * AlignmentPanel objects, including their AlignmentViewports, so the
3280 * cdna/protein relationships between the viewports is carried over to the
3283 * explodedGeometry holds the (x, y) position of the previously exploded
3284 * SplitFrame, and the (width, height) of the AlignFrame component
3286 AlignmentPanel topPanel = (AlignmentPanel) topPanels.get(i);
3287 AlignFrame newTopFrame = new AlignFrame(topPanel);
3288 newTopFrame.setSize(oldTopFrame.getSize());
3289 newTopFrame.setVisible(true);
3290 Rectangle geometry = ((AlignViewport) topPanel.getAlignViewport())
3291 .getExplodedGeometry();
3292 if (geometry != null)
3294 newTopFrame.setSize(geometry.getSize());
3297 AlignmentPanel bottomPanel = (AlignmentPanel) bottomPanels.get(i);
3298 AlignFrame newBottomFrame = new AlignFrame(bottomPanel);
3299 newBottomFrame.setSize(oldBottomFrame.getSize());
3300 newBottomFrame.setVisible(true);
3301 geometry = ((AlignViewport) bottomPanel.getAlignViewport())
3302 .getExplodedGeometry();
3303 if (geometry != null)
3305 newBottomFrame.setSize(geometry.getSize());
3308 topPanel.av.setGatherViewsHere(false);
3309 bottomPanel.av.setGatherViewsHere(false);
3310 JInternalFrame splitFrame = new SplitFrame(newTopFrame,
3312 if (geometry != null)
3314 splitFrame.setLocation(geometry.getLocation());
3316 Desktop.addInternalFrame(splitFrame, sf.getTitle(), -1, -1);
3320 * Clear references to the panels (now relocated in the new SplitFrames)
3321 * before closing the old SplitFrame.
3324 bottomPanels.clear();
3329 * Gather expanded split frames, sharing the same pairs of sequence set ids,
3330 * back into the given SplitFrame as additional views. Note that the gathered
3331 * frames may themselves have multiple views.
3335 public void gatherViews(GSplitFrame source)
3338 * special handling of explodedGeometry for a view within a SplitFrame: - it
3339 * holds the (x, y) position of the enclosing SplitFrame, and the (width,
3340 * height) of the AlignFrame component
3342 AlignFrame myTopFrame = (AlignFrame) source.getTopFrame();
3343 AlignFrame myBottomFrame = (AlignFrame) source.getBottomFrame();
3344 myTopFrame.viewport.setExplodedGeometry(new Rectangle(source.getX(),
3345 source.getY(), myTopFrame.getWidth(), myTopFrame.getHeight()));
3346 myBottomFrame.viewport
3347 .setExplodedGeometry(new Rectangle(source.getX(), source.getY(),
3348 myBottomFrame.getWidth(), myBottomFrame.getHeight()));
3349 myTopFrame.viewport.setGatherViewsHere(true);
3350 myBottomFrame.viewport.setGatherViewsHere(true);
3351 String topViewId = myTopFrame.viewport.getSequenceSetId();
3352 String bottomViewId = myBottomFrame.viewport.getSequenceSetId();
3354 JInternalFrame[] frames = desktop.getAllFrames();
3355 for (JInternalFrame frame : frames)
3357 if (frame instanceof SplitFrame && frame != source)
3359 SplitFrame sf = (SplitFrame) frame;
3360 AlignFrame topFrame = (AlignFrame) sf.getTopFrame();
3361 AlignFrame bottomFrame = (AlignFrame) sf.getBottomFrame();
3362 boolean gatherThis = false;
3363 for (int a = 0; a < topFrame.alignPanels.size(); a++)
3365 AlignmentPanel topPanel = topFrame.alignPanels.get(a);
3366 AlignmentPanel bottomPanel = bottomFrame.alignPanels.get(a);
3367 if (topViewId.equals(topPanel.av.getSequenceSetId())
3368 && bottomViewId.equals(bottomPanel.av.getSequenceSetId()))
3371 topPanel.av.setGatherViewsHere(false);
3372 bottomPanel.av.setGatherViewsHere(false);
3373 topPanel.av.setExplodedGeometry(
3374 new Rectangle(sf.getLocation(), topFrame.getSize()));
3375 bottomPanel.av.setExplodedGeometry(
3376 new Rectangle(sf.getLocation(), bottomFrame.getSize()));
3377 myTopFrame.addAlignmentPanel(topPanel, false);
3378 myBottomFrame.addAlignmentPanel(bottomPanel, false);
3384 topFrame.getAlignPanels().clear();
3385 bottomFrame.getAlignPanels().clear();
3392 * The dust settles...give focus to the tab we did this from.
3394 myTopFrame.setDisplayedView(myTopFrame.alignPanel);
3397 public static groovy.ui.Console getGroovyConsole()
3399 return groovyConsole;
3403 * handles the payload of a drag and drop event.
3405 * TODO refactor to desktop utilities class
3408 * - Data source strings extracted from the drop event
3410 * - protocol for each data source extracted from the drop event
3414 * - the payload from the drop event
3417 public static void transferFromDropTarget(List<Object> files,
3418 List<DataSourceType> protocols, DropTargetDropEvent evt,
3419 Transferable t) throws Exception
3422 // BH 2018 changed List<String> to List<Object> to allow for File from SwingJS
3424 // DataFlavor[] flavors = t.getTransferDataFlavors();
3425 // for (int i = 0; i < flavors.length; i++) {
3426 // if (flavors[i].isFlavorJavaFileListType()) {
3427 // evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
3428 // List<File> list = (List<File>) t.getTransferData(flavors[i]);
3429 // for (int j = 0; j < list.size(); j++) {
3430 // File file = (File) list.get(j);
3431 // byte[] data = getDroppedFileBytes(file);
3432 // fileName.setText(file.getName() + " - " + data.length + " " +
3433 // evt.getLocation());
3434 // JTextArea target = (JTextArea) ((DropTarget) evt.getSource()).getComponent();
3435 // target.setText(new String(data));
3437 // dtde.dropComplete(true);
3442 DataFlavor uriListFlavor = new DataFlavor(
3443 "text/uri-list;class=java.lang.String"), urlFlavour = null;
3446 urlFlavour = new DataFlavor(
3447 "application/x-java-url; class=java.net.URL");
3448 } catch (ClassNotFoundException cfe)
3450 Cache.log.debug("Couldn't instantiate the URL dataflavor.", cfe);
3453 if (urlFlavour != null && t.isDataFlavorSupported(urlFlavour))
3458 java.net.URL url = (URL) t.getTransferData(urlFlavour);
3459 // nb: java 8 osx bug https://bugs.openjdk.java.net/browse/JDK-8156099
3460 // means url may be null.
3463 protocols.add(DataSourceType.URL);
3464 files.add(url.toString());
3465 Cache.log.debug("Drop handled as URL dataflavor "
3466 + files.get(files.size() - 1));
3471 if (Platform.isAMac())
3474 "Please ignore plist error - occurs due to problem with java 8 on OSX");
3478 } catch (Throwable ex)
3480 Cache.log.debug("URL drop handler failed.", ex);
3483 if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
3485 // Works on Windows and MacOSX
3486 Cache.log.debug("Drop handled as javaFileListFlavor");
3487 for (Object file : (List) t
3488 .getTransferData(DataFlavor.javaFileListFlavor))
3491 protocols.add(DataSourceType.FILE);
3496 // Unix like behaviour
3497 boolean added = false;
3499 if (t.isDataFlavorSupported(uriListFlavor))
3501 Cache.log.debug("Drop handled as uriListFlavor");
3502 // This is used by Unix drag system
3503 data = (String) t.getTransferData(uriListFlavor);
3507 // fallback to text: workaround - on OSX where there's a JVM bug
3508 Cache.log.debug("standard URIListFlavor failed. Trying text");
3509 // try text fallback
3510 DataFlavor textDf = new DataFlavor(
3511 "text/plain;class=java.lang.String");
3512 if (t.isDataFlavorSupported(textDf))
3514 data = (String) t.getTransferData(textDf);
3517 Cache.log.debug("Plain text drop content returned "
3518 + (data == null ? "Null - failed" : data));
3523 while (protocols.size() < files.size())
3525 Cache.log.debug("Adding missing FILE protocol for "
3526 + files.get(protocols.size()));
3527 protocols.add(DataSourceType.FILE);
3529 for (java.util.StringTokenizer st = new java.util.StringTokenizer(
3530 data, "\r\n"); st.hasMoreTokens();)
3533 String s = st.nextToken();
3534 if (s.startsWith("#"))
3536 // the line is a comment (as per the RFC 2483)
3539 java.net.URI uri = new java.net.URI(s);
3540 if (uri.getScheme().toLowerCase().startsWith("http"))
3542 protocols.add(DataSourceType.URL);
3543 files.add(uri.toString());
3547 // otherwise preserve old behaviour: catch all for file objects
3548 java.io.File file = new java.io.File(uri);
3549 protocols.add(DataSourceType.FILE);
3550 files.add(file.toString());
3555 if (Cache.log.isDebugEnabled())
3557 if (data == null || !added)
3560 if (t.getTransferDataFlavors() != null
3561 && t.getTransferDataFlavors().length > 0)
3564 "Couldn't resolve drop data. Here are the supported flavors:");
3565 for (DataFlavor fl : t.getTransferDataFlavors())
3568 "Supported transfer dataflavor: " + fl.toString());
3569 Object df = t.getTransferData(fl);
3572 Cache.log.debug("Retrieves: " + df);
3576 Cache.log.debug("Retrieved nothing");
3582 Cache.log.debug("Couldn't resolve dataflavor for drop: "
3588 if (Platform.isWindows())
3591 Cache.log.debug("Scanning dropped content for Windows Link Files");
3593 // resolve any .lnk files in the file drop
3594 for (int f = 0; f < files.size(); f++)
3596 String source = files.get(f).toString().toLowerCase();
3597 if (protocols.get(f).equals(DataSourceType.FILE)
3598 && (source.endsWith(".lnk") || source.endsWith(".url")
3599 || source.endsWith(".site")))
3603 Object obj = files.get(f);
3604 File lf = (obj instanceof File ? (File) obj
3605 : new File((String) obj));
3606 // process link file to get a URL
3607 Cache.log.debug("Found potential link file: " + lf);
3608 WindowsShortcut wscfile = new WindowsShortcut(lf);
3609 String fullname = wscfile.getRealFilename();
3610 protocols.set(f, FormatAdapter.checkProtocol(fullname));
3611 files.set(f, fullname);
3612 Cache.log.debug("Parsed real filename " + fullname
3613 + " to extract protocol: " + protocols.get(f));
3614 } catch (Exception ex)
3617 "Couldn't parse " + files.get(f) + " as a link file.",
3626 * Sets the Preferences property for experimental features to True or False
3627 * depending on the state of the controlling menu item
3630 protected void showExperimental_actionPerformed(boolean selected)
3632 Cache.setProperty(EXPERIMENTAL_FEATURES, Boolean.toString(selected));
3636 * Answers a (possibly empty) list of any structure viewer frames (currently for
3637 * either Jmol or Chimera) which are currently open. This may optionally be
3638 * restricted to viewers of a specified class, or viewers linked to a specified
3642 * if not null, only return viewers linked to this panel
3643 * @param structureViewerClass
3644 * if not null, only return viewers of this class
3647 public List<StructureViewerBase> getStructureViewers(
3648 AlignmentPanel apanel,
3649 Class<? extends StructureViewerBase> structureViewerClass)
3651 List<StructureViewerBase> result = new ArrayList<>();
3652 JInternalFrame[] frames = Desktop.instance.getAllFrames();
3654 for (JInternalFrame frame : frames)
3656 if (frame instanceof StructureViewerBase)
3658 if (structureViewerClass == null
3659 || structureViewerClass.isInstance(frame))
3662 || ((StructureViewerBase) frame).isLinkedWith(apanel))
3664 result.add((StructureViewerBase) frame);