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 // TODO: JAL-3048 Dialog runner refactoring here
1653 int value = chooser.showSaveDialog(this);
1655 if (value == JalviewFileChooser.APPROVE_OPTION)
1657 final Desktop me = this;
1658 final java.io.File choice = chooser.getSelectedFile();
1659 setProjectFile(choice);
1661 new Thread(new Runnable()
1666 // TODO: refactor to Jalview desktop session controller action.
1667 setProgressBar(MessageManager.formatMessage(
1668 "label.saving_jalview_project", new Object[]
1669 { choice.getName() }), choice.hashCode());
1670 jalview.bin.Cache.setProperty("LAST_DIRECTORY",
1671 choice.getParent());
1672 // TODO catch and handle errors for savestate
1673 // TODO prevent user from messing with the Desktop whilst we're saving
1678 new Jalview2XML().saveState(choice);
1682 new jalview.project.Jalview2XML().saveState(choice);
1684 } catch (OutOfMemoryError oom)
1687 "Whilst saving current state to " + choice.getName(),
1689 } catch (Exception ex)
1692 "Problems whilst trying to save to " + choice.getName(),
1694 JvOptionPane.showMessageDialog(me,
1695 MessageManager.formatMessage(
1696 "label.error_whilst_saving_current_state_to",
1698 { choice.getName() }),
1699 MessageManager.getString("label.couldnt_save_project"),
1700 JvOptionPane.WARNING_MESSAGE);
1702 setProgressBar(null, choice.hashCode());
1708 private void setProjectFile(File choice)
1710 this.projectFile = choice;
1713 public File getProjectFile()
1715 return this.projectFile;
1719 * Prompts the user to choose a file and loads in as a Jalview project file
1722 public void loadState_actionPerformed(boolean asCastor)
1724 // TODO: GET RID OF .JVX BEFORE RELEASE JIM!
1725 final String[] suffix = asCastor ? new String[] { "jvp", "jar" }
1728 final String[] desc = asCastor
1730 { "Jalview Project", "Jalview Project (old)" }
1732 { "Jalview Project" };
1733 JalviewFileChooser chooser = new JalviewFileChooser(
1734 Cache.getProperty("LAST_DIRECTORY"), suffix,
1737 chooser.setFileView(new JalviewFileView());
1738 chooser.setDialogTitle(MessageManager.getString("label.restore_state"));
1740 int value = chooser.showOpenDialog(this);
1742 if (value == JalviewFileChooser.APPROVE_OPTION)
1744 final File selectedFile = chooser.getSelectedFile();
1745 setProjectFile(selectedFile);
1746 final String choice = selectedFile.getAbsolutePath();
1747 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1748 new Thread(new Runnable()
1753 setProgressBar(MessageManager.formatMessage(
1754 "label.loading_jalview_project", new Object[]
1755 { choice }), choice.hashCode());
1760 new Jalview2XML().loadJalviewAlign(choice);
1764 new jalview.project.Jalview2XML().loadJalviewAlign(choice);
1766 } catch (OutOfMemoryError oom)
1768 new OOMWarning("Whilst loading project from " + choice, oom);
1769 } catch (Exception ex)
1772 "Problems whilst loading project from " + choice, ex);
1773 JvOptionPane.showMessageDialog(Desktop.desktop,
1774 MessageManager.formatMessage(
1775 "label.error_whilst_loading_project_from",
1778 MessageManager.getString("label.couldnt_load_project"),
1779 JvOptionPane.WARNING_MESSAGE);
1781 setProgressBar(null, choice.hashCode());
1788 public void inputSequence_actionPerformed(ActionEvent e)
1790 new SequenceFetcher(this);
1793 JPanel progressPanel;
1795 ArrayList<JPanel> fileLoadingPanels = new ArrayList<>();
1797 public void startLoading(final Object fileName)
1799 if (fileLoadingCount == 0)
1801 fileLoadingPanels.add(addProgressPanel(MessageManager
1802 .formatMessage("label.loading_file", new Object[]
1808 private JPanel addProgressPanel(String string)
1810 if (progressPanel == null)
1812 progressPanel = new JPanel(new GridLayout(1, 1));
1813 totalProgressCount = 0;
1814 instance.getContentPane().add(progressPanel, BorderLayout.SOUTH);
1816 JPanel thisprogress = new JPanel(new BorderLayout(10, 5));
1817 JProgressBar progressBar = new JProgressBar();
1818 progressBar.setIndeterminate(true);
1820 thisprogress.add(new JLabel(string), BorderLayout.WEST);
1822 thisprogress.add(progressBar, BorderLayout.CENTER);
1823 progressPanel.add(thisprogress);
1824 ((GridLayout) progressPanel.getLayout()).setRows(
1825 ((GridLayout) progressPanel.getLayout()).getRows() + 1);
1826 ++totalProgressCount;
1827 instance.validate();
1828 return thisprogress;
1831 int totalProgressCount = 0;
1833 private void removeProgressPanel(JPanel progbar)
1835 if (progressPanel != null)
1837 synchronized (progressPanel)
1839 progressPanel.remove(progbar);
1840 GridLayout gl = (GridLayout) progressPanel.getLayout();
1841 gl.setRows(gl.getRows() - 1);
1842 if (--totalProgressCount < 1)
1844 this.getContentPane().remove(progressPanel);
1845 progressPanel = null;
1852 public void stopLoading()
1855 if (fileLoadingCount < 1)
1857 while (fileLoadingPanels.size() > 0)
1859 removeProgressPanel(fileLoadingPanels.remove(0));
1861 fileLoadingPanels.clear();
1862 fileLoadingCount = 0;
1867 public static int getViewCount(String alignmentId)
1869 AlignmentViewport[] aps = getViewports(alignmentId);
1870 return (aps == null) ? 0 : aps.length;
1875 * @param alignmentId
1876 * - if null, all sets are returned
1877 * @return all AlignmentPanels concerning the alignmentId sequence set
1879 public static AlignmentPanel[] getAlignmentPanels(String alignmentId)
1881 if (Desktop.desktop == null)
1883 // no frames created and in headless mode
1884 // TODO: verify that frames are recoverable when in headless mode
1887 List<AlignmentPanel> aps = new ArrayList<>();
1888 AlignFrame[] frames = getAlignFrames();
1893 for (AlignFrame af : frames)
1895 for (AlignmentPanel ap : af.alignPanels)
1897 if (alignmentId == null
1898 || alignmentId.equals(ap.av.getSequenceSetId()))
1904 if (aps.size() == 0)
1908 AlignmentPanel[] vap = aps.toArray(new AlignmentPanel[aps.size()]);
1913 * get all the viewports on an alignment.
1915 * @param sequenceSetId
1916 * unique alignment id (may be null - all viewports returned in that
1918 * @return all viewports on the alignment bound to sequenceSetId
1920 public static AlignmentViewport[] getViewports(String sequenceSetId)
1922 List<AlignmentViewport> viewp = new ArrayList<>();
1923 if (desktop != null)
1925 AlignFrame[] frames = Desktop.getAlignFrames();
1927 for (AlignFrame afr : frames)
1929 if (sequenceSetId == null || afr.getViewport().getSequenceSetId()
1930 .equals(sequenceSetId))
1932 if (afr.alignPanels != null)
1934 for (AlignmentPanel ap : afr.alignPanels)
1936 if (sequenceSetId == null
1937 || sequenceSetId.equals(ap.av.getSequenceSetId()))
1945 viewp.add(afr.getViewport());
1949 if (viewp.size() > 0)
1951 return viewp.toArray(new AlignmentViewport[viewp.size()]);
1958 * Explode the views in the given frame into separate AlignFrame
1962 public static void explodeViews(AlignFrame af)
1964 int size = af.alignPanels.size();
1970 for (int i = 0; i < size; i++)
1972 AlignmentPanel ap = af.alignPanels.get(i);
1973 AlignFrame newaf = new AlignFrame(ap);
1976 * Restore the view's last exploded frame geometry if known. Multiple
1977 * views from one exploded frame share and restore the same (frame)
1978 * position and size.
1980 Rectangle geometry = ap.av.getExplodedGeometry();
1981 if (geometry != null)
1983 newaf.setBounds(geometry);
1986 ap.av.setGatherViewsHere(false);
1988 addInternalFrame(newaf, af.getTitle(), AlignFrame.DEFAULT_WIDTH,
1989 AlignFrame.DEFAULT_HEIGHT);
1992 af.alignPanels.clear();
1993 af.closeMenuItem_actionPerformed(true);
1998 * Gather expanded views (separate AlignFrame's) with the same sequence set
1999 * identifier back in to this frame as additional views, and close the expanded
2000 * views. Note the expanded frames may themselves have multiple views. We take
2005 public void gatherViews(AlignFrame source)
2007 source.viewport.setGatherViewsHere(true);
2008 source.viewport.setExplodedGeometry(source.getBounds());
2009 JInternalFrame[] frames = desktop.getAllFrames();
2010 String viewId = source.viewport.getSequenceSetId();
2012 for (int t = 0; t < frames.length; t++)
2014 if (frames[t] instanceof AlignFrame && frames[t] != source)
2016 AlignFrame af = (AlignFrame) frames[t];
2017 boolean gatherThis = false;
2018 for (int a = 0; a < af.alignPanels.size(); a++)
2020 AlignmentPanel ap = af.alignPanels.get(a);
2021 if (viewId.equals(ap.av.getSequenceSetId()))
2024 ap.av.setGatherViewsHere(false);
2025 ap.av.setExplodedGeometry(af.getBounds());
2026 source.addAlignmentPanel(ap, false);
2032 af.alignPanels.clear();
2033 af.closeMenuItem_actionPerformed(true);
2040 jalview.gui.VamsasApplication v_client = null;
2043 public void vamsasImport_actionPerformed(ActionEvent e)
2045 // TODO: JAL-3048 not needed for Jalview-JS
2047 if (v_client == null)
2049 // Load and try to start a session.
2050 JalviewFileChooser chooser = new JalviewFileChooser(
2051 jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
2053 chooser.setFileView(new JalviewFileView());
2054 chooser.setDialogTitle(
2055 MessageManager.getString("label.open_saved_vamsas_session"));
2056 chooser.setToolTipText(MessageManager.getString(
2057 "label.select_vamsas_session_opened_as_new_vamsas_session"));
2059 int value = chooser.showOpenDialog(this);
2061 if (value == JalviewFileChooser.APPROVE_OPTION)
2063 String fle = chooser.getSelectedFile().toString();
2064 if (!vamsasImport(chooser.getSelectedFile()))
2066 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
2067 MessageManager.formatMessage(
2068 "label.couldnt_import_as_vamsas_session",
2072 .getString("label.vamsas_document_import_failed"),
2073 JvOptionPane.ERROR_MESSAGE);
2079 jalview.bin.Cache.log.error(
2080 "Implementation error - load session from a running session is not supported.");
2085 * import file into a new vamsas session (uses jalview.gui.VamsasApplication)
2088 * @return true if import was a success and a session was started.
2090 public boolean vamsasImport(URL url)
2092 // TODO: create progress bar
2093 if (v_client != null)
2096 jalview.bin.Cache.log.error(
2097 "Implementation error - load session from a running session is not supported.");
2103 // copy the URL content to a temporary local file
2104 // TODO: be a bit cleverer here with nio (?!)
2105 File file = File.createTempFile("vdocfromurl", ".vdj");
2106 FileOutputStream fos = new FileOutputStream(file);
2107 BufferedInputStream bis = new BufferedInputStream(url.openStream());
2108 byte[] buffer = new byte[2048];
2110 while ((ln = bis.read(buffer)) > -1)
2112 fos.write(buffer, 0, ln);
2116 v_client = new jalview.gui.VamsasApplication(this, file,
2117 url.toExternalForm());
2118 } catch (Exception ex)
2120 jalview.bin.Cache.log.error(
2121 "Failed to create new vamsas session from contents of URL "
2126 setupVamsasConnectedGui();
2127 v_client.initial_update(); // TODO: thread ?
2128 return v_client.inSession();
2132 * import file into a new vamsas session (uses jalview.gui.VamsasApplication)
2135 * @return true if import was a success and a session was started.
2137 public boolean vamsasImport(File file)
2139 if (v_client != null)
2142 jalview.bin.Cache.log.error(
2143 "Implementation error - load session from a running session is not supported.");
2147 setProgressBar(MessageManager.formatMessage(
2148 "status.importing_vamsas_session_from", new Object[]
2149 { file.getName() }), file.hashCode());
2152 v_client = new jalview.gui.VamsasApplication(this, file, null);
2153 } catch (Exception ex)
2155 setProgressBar(MessageManager.formatMessage(
2156 "status.importing_vamsas_session_from", new Object[]
2157 { file.getName() }), file.hashCode());
2158 jalview.bin.Cache.log.error(
2159 "New vamsas session from existing session file failed:", ex);
2162 setupVamsasConnectedGui();
2163 v_client.initial_update(); // TODO: thread ?
2164 setProgressBar(MessageManager.formatMessage(
2165 "status.importing_vamsas_session_from", new Object[]
2166 { file.getName() }), file.hashCode());
2167 return v_client.inSession();
2170 public boolean joinVamsasSession(String mysesid)
2172 if (v_client != null)
2174 throw new Error(MessageManager
2175 .getString("error.try_join_vamsas_session_another"));
2177 if (mysesid == null)
2180 MessageManager.getString("error.invalid_vamsas_session_id"));
2182 v_client = new VamsasApplication(this, mysesid);
2183 setupVamsasConnectedGui();
2184 v_client.initial_update();
2185 return (v_client.inSession());
2189 public void vamsasStart_actionPerformed(ActionEvent e)
2191 if (v_client == null)
2194 // we just start a default session for moment.
2196 * JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache.
2197 * getProperty("LAST_DIRECTORY"));
2199 * chooser.setFileView(new JalviewFileView());
2200 * chooser.setDialogTitle("Load Vamsas file");
2201 * chooser.setToolTipText("Import");
2203 * int value = chooser.showOpenDialog(this);
2205 * if (value == JalviewFileChooser.APPROVE_OPTION) { v_client = new
2206 * jalview.gui.VamsasApplication(this, chooser.getSelectedFile());
2208 v_client = new VamsasApplication(this);
2209 setupVamsasConnectedGui();
2210 v_client.initial_update(); // TODO: thread ?
2214 // store current data in session.
2215 v_client.push_update(); // TODO: thread
2219 protected void setupVamsasConnectedGui()
2221 vamsasStart.setText(MessageManager.getString("label.session_update"));
2222 vamsasSave.setVisible(true);
2223 vamsasStop.setVisible(true);
2224 vamsasImport.setVisible(false); // Document import to existing session is
2225 // not possible for vamsas-client-1.0.
2228 protected void setupVamsasDisconnectedGui()
2230 vamsasSave.setVisible(false);
2231 vamsasStop.setVisible(false);
2232 vamsasImport.setVisible(true);
2234 .setText(MessageManager.getString("label.new_vamsas_session"));
2238 public void vamsasStop_actionPerformed(ActionEvent e)
2240 if (v_client != null)
2242 v_client.end_session();
2244 setupVamsasDisconnectedGui();
2248 protected void buildVamsasStMenu()
2250 if (v_client == null)
2252 String[] sess = null;
2255 sess = VamsasApplication.getSessionList();
2256 } catch (Exception e)
2258 jalview.bin.Cache.log.warn("Problem getting current sessions list.",
2264 jalview.bin.Cache.log.debug(
2265 "Got current sessions list: " + sess.length + " entries.");
2266 VamsasStMenu.removeAll();
2267 for (int i = 0; i < sess.length; i++)
2269 JMenuItem sessit = new JMenuItem();
2270 sessit.setText(sess[i]);
2271 sessit.setToolTipText(MessageManager
2272 .formatMessage("label.connect_to_session", new Object[]
2274 final Desktop dsktp = this;
2275 final String mysesid = sess[i];
2276 sessit.addActionListener(new ActionListener()
2280 public void actionPerformed(ActionEvent e)
2282 if (dsktp.v_client == null)
2284 Thread rthr = new Thread(new Runnable()
2290 dsktp.v_client = new VamsasApplication(dsktp, mysesid);
2291 dsktp.setupVamsasConnectedGui();
2292 dsktp.v_client.initial_update();
2300 VamsasStMenu.add(sessit);
2302 // don't show an empty menu.
2303 VamsasStMenu.setVisible(sess.length > 0);
2308 jalview.bin.Cache.log.debug("No current vamsas sessions.");
2309 VamsasStMenu.removeAll();
2310 VamsasStMenu.setVisible(false);
2315 // Not interested in the content. Just hide ourselves.
2316 VamsasStMenu.setVisible(false);
2321 public void vamsasSave_actionPerformed(ActionEvent e)
2323 // TODO: JAL-3048 not needed for Jalview-JS
2325 if (v_client != null)
2327 // TODO: VAMSAS DOCUMENT EXTENSION is VDJ
2328 JalviewFileChooser chooser = new JalviewFileChooser("vdj",
2331 chooser.setFileView(new JalviewFileView());
2332 chooser.setDialogTitle(MessageManager
2333 .getString("label.save_vamsas_document_archive"));
2335 int value = chooser.showSaveDialog(this);
2337 if (value == JalviewFileChooser.APPROVE_OPTION)
2339 java.io.File choice = chooser.getSelectedFile();
2340 JPanel progpanel = addProgressPanel(MessageManager
2341 .formatMessage("label.saving_vamsas_doc", new Object[]
2342 { choice.getName() }));
2343 Cache.setProperty("LAST_DIRECTORY", choice.getParent());
2344 String warnmsg = null;
2345 String warnttl = null;
2348 v_client.vclient.storeDocument(choice);
2351 warnttl = "Serious Problem saving Vamsas Document";
2352 warnmsg = ex.toString();
2353 jalview.bin.Cache.log
2354 .error("Error Whilst saving document to " + choice, ex);
2356 } catch (Exception ex)
2358 warnttl = "Problem saving Vamsas Document.";
2359 warnmsg = ex.toString();
2360 jalview.bin.Cache.log.warn(
2361 "Exception Whilst saving document to " + choice, ex);
2364 removeProgressPanel(progpanel);
2365 if (warnmsg != null)
2367 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
2369 warnmsg, warnttl, JvOptionPane.ERROR_MESSAGE);
2375 JPanel vamUpdate = null;
2378 * hide vamsas user gui bits when a vamsas document event is being handled.
2381 * true to hide gui, false to reveal gui
2383 public void setVamsasUpdate(boolean b)
2385 Cache.log.debug("Setting gui for Vamsas update "
2386 + (b ? "in progress" : "finished"));
2388 if (vamUpdate != null)
2390 this.removeProgressPanel(vamUpdate);
2394 vamUpdate = this.addProgressPanel(
2395 MessageManager.getString("label.updating_vamsas_session"));
2397 vamsasStart.setVisible(!b);
2398 vamsasStop.setVisible(!b);
2399 vamsasSave.setVisible(!b);
2402 public JInternalFrame[] getAllFrames()
2404 return desktop.getAllFrames();
2408 * Checks the given url to see if it gives a response indicating that the user
2409 * should be informed of a new questionnaire.
2413 public void checkForQuestionnaire(String url)
2415 UserQuestionnaireCheck jvq = new UserQuestionnaireCheck(url);
2416 // javax.swing.SwingUtilities.invokeLater(jvq);
2417 new Thread(jvq).start();
2420 public void checkURLLinks()
2422 // Thread off the URL link checker
2423 addDialogThread(new Runnable()
2428 if (Cache.getDefault("CHECKURLLINKS", true))
2430 // check what the actual links are - if it's just the default don't
2431 // bother with the warning
2432 List<String> links = Preferences.sequenceUrlLinks
2435 // only need to check links if there is one with a
2436 // SEQUENCE_ID which is not the default EMBL_EBI link
2437 ListIterator<String> li = links.listIterator();
2438 boolean check = false;
2439 List<JLabel> urls = new ArrayList<>();
2440 while (li.hasNext())
2442 String link = li.next();
2443 if (link.contains(SEQUENCE_ID)
2444 && !UrlConstants.isDefaultString(link))
2447 int barPos = link.indexOf("|");
2448 String urlMsg = barPos == -1 ? link
2449 : link.substring(0, barPos) + ": "
2450 + link.substring(barPos + 1);
2451 urls.add(new JLabel(urlMsg));
2459 // ask user to check in case URL links use old style tokens
2460 // ($SEQUENCE_ID$ for sequence id _or_ accession id)
2461 JPanel msgPanel = new JPanel();
2462 msgPanel.setLayout(new BoxLayout(msgPanel, BoxLayout.PAGE_AXIS));
2463 msgPanel.add(Box.createVerticalGlue());
2464 JLabel msg = new JLabel(MessageManager
2465 .getString("label.SEQUENCE_ID_for_DB_ACCESSION1"));
2466 JLabel msg2 = new JLabel(MessageManager
2467 .getString("label.SEQUENCE_ID_for_DB_ACCESSION2"));
2469 for (JLabel url : urls)
2475 final JCheckBox jcb = new JCheckBox(
2476 MessageManager.getString("label.do_not_display_again"));
2477 jcb.addActionListener(new ActionListener()
2480 public void actionPerformed(ActionEvent e)
2482 // update Cache settings for "don't show this again"
2483 boolean showWarningAgain = !jcb.isSelected();
2484 Cache.setProperty("CHECKURLLINKS",
2485 Boolean.valueOf(showWarningAgain).toString());
2490 JvOptionPane.showMessageDialog(Desktop.desktop, msgPanel,
2492 .getString("label.SEQUENCE_ID_no_longer_used"),
2493 JvOptionPane.WARNING_MESSAGE);
2500 * Proxy class for JDesktopPane which optionally displays the current memory
2501 * usage and highlights the desktop area with a red bar if free memory runs low.
2505 public class MyDesktopPane extends JDesktopPane
2508 private static final float ONE_MB = 1048576f;
2510 boolean showMemoryUsage = false;
2514 java.text.NumberFormat df;
2516 float maxMemory, allocatedMemory, freeMemory, totalFreeMemory,
2519 public MyDesktopPane(boolean showMemoryUsage)
2521 showMemoryUsage(showMemoryUsage);
2524 public void showMemoryUsage(boolean showMemory)
2526 this.showMemoryUsage = showMemory;
2529 Thread worker = new Thread(this);
2535 public boolean isShowMemoryUsage()
2537 return showMemoryUsage;
2543 df = java.text.NumberFormat.getNumberInstance();
2544 df.setMaximumFractionDigits(2);
2545 runtime = Runtime.getRuntime();
2547 while (showMemoryUsage)
2551 maxMemory = runtime.maxMemory() / ONE_MB;
2552 allocatedMemory = runtime.totalMemory() / ONE_MB;
2553 freeMemory = runtime.freeMemory() / ONE_MB;
2554 totalFreeMemory = freeMemory + (maxMemory - allocatedMemory);
2556 percentUsage = (totalFreeMemory / maxMemory) * 100;
2558 // if (percentUsage < 20)
2560 // border1 = BorderFactory.createMatteBorder(12, 12, 12, 12,
2562 // instance.set.setBorder(border1);
2565 // sleep after showing usage
2567 } catch (Exception ex)
2569 ex.printStackTrace();
2575 public void paintComponent(Graphics g)
2577 if (showMemoryUsage && g != null && df != null)
2579 if (percentUsage < 20)
2581 g.setColor(Color.red);
2583 FontMetrics fm = g.getFontMetrics();
2586 g.drawString(MessageManager.formatMessage("label.memory_stats",
2588 { df.format(totalFreeMemory), df.format(maxMemory),
2589 df.format(percentUsage) }),
2590 10, getHeight() - fm.getHeight());
2597 * Accessor method to quickly get all the AlignmentFrames loaded.
2599 * @return an array of AlignFrame, or null if none found
2601 public static AlignFrame[] getAlignFrames()
2603 if (Jalview.isHeadlessMode())
2605 // Desktop.desktop is null in headless mode
2606 return new AlignFrame[] { Jalview.currentAlignFrame };
2609 JInternalFrame[] frames = Desktop.desktop.getAllFrames();
2615 List<AlignFrame> avp = new ArrayList<>();
2617 for (int i = frames.length - 1; i > -1; i--)
2619 if (frames[i] instanceof AlignFrame)
2621 avp.add((AlignFrame) frames[i]);
2623 else if (frames[i] instanceof SplitFrame)
2626 * Also check for a split frame containing an AlignFrame
2628 GSplitFrame sf = (GSplitFrame) frames[i];
2629 if (sf.getTopFrame() instanceof AlignFrame)
2631 avp.add((AlignFrame) sf.getTopFrame());
2633 if (sf.getBottomFrame() instanceof AlignFrame)
2635 avp.add((AlignFrame) sf.getBottomFrame());
2639 if (avp.size() == 0)
2643 AlignFrame afs[] = avp.toArray(new AlignFrame[avp.size()]);
2648 * Returns an array of any AppJmol frames in the Desktop (or null if none).
2652 public GStructureViewer[] getJmols()
2654 JInternalFrame[] frames = Desktop.desktop.getAllFrames();
2660 List<GStructureViewer> avp = new ArrayList<>();
2662 for (int i = frames.length - 1; i > -1; i--)
2664 if (frames[i] instanceof AppJmol)
2666 GStructureViewer af = (GStructureViewer) frames[i];
2670 if (avp.size() == 0)
2674 GStructureViewer afs[] = avp.toArray(new GStructureViewer[avp.size()]);
2679 * Add Groovy Support to Jalview
2682 public void groovyShell_actionPerformed()
2686 openGroovyConsole();
2687 } catch (Exception ex)
2689 jalview.bin.Cache.log.error("Groovy Shell Creation failed.", ex);
2690 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
2692 MessageManager.getString("label.couldnt_create_groovy_shell"),
2693 MessageManager.getString("label.groovy_support_failed"),
2694 JvOptionPane.ERROR_MESSAGE);
2699 * Open the Groovy console
2701 void openGroovyConsole()
2703 if (groovyConsole == null)
2705 groovyConsole = new groovy.ui.Console();
2706 groovyConsole.setVariable("Jalview", this);
2707 groovyConsole.run();
2710 * We allow only one console at a time, so that AlignFrame menu option
2711 * 'Calculate | Run Groovy script' is unambiguous.
2712 * Disable 'Groovy Console', and enable 'Run script', when the console is
2713 * opened, and the reverse when it is closed
2715 Window window = (Window) groovyConsole.getFrame();
2716 window.addWindowListener(new WindowAdapter()
2719 public void windowClosed(WindowEvent e)
2722 * rebind CMD-Q from Groovy Console to Jalview Quit
2725 enableExecuteGroovy(false);
2731 * show Groovy console window (after close and reopen)
2733 ((Window) groovyConsole.getFrame()).setVisible(true);
2736 * if we got this far, enable 'Run Groovy' in AlignFrame menus
2737 * and disable opening a second console
2739 enableExecuteGroovy(true);
2743 * Bind Ctrl/Cmd-Q to Quit - for reset as Groovy Console takes over this binding
2746 protected void addQuitHandler()
2748 getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
2749 .put(KeyStroke.getKeyStroke(KeyEvent.VK_Q,
2750 Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()),
2752 getRootPane().getActionMap().put("Quit", new AbstractAction()
2755 public void actionPerformed(ActionEvent e)
2763 * Enable or disable 'Run Groovy script' in AlignFrame calculate menus
2766 * true if Groovy console is open
2768 public void enableExecuteGroovy(boolean enabled)
2771 * disable opening a second Groovy console
2772 * (or re-enable when the console is closed)
2774 groovyShell.setEnabled(!enabled);
2776 AlignFrame[] alignFrames = getAlignFrames();
2777 if (alignFrames != null)
2779 for (AlignFrame af : alignFrames)
2781 af.setGroovyEnabled(enabled);
2787 * Progress bars managed by the IProgressIndicator method.
2789 private Hashtable<Long, JPanel> progressBars;
2791 private Hashtable<Long, IProgressIndicatorHandler> progressBarHandlers;
2796 * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
2799 public void setProgressBar(String message, long id)
2801 if (progressBars == null)
2803 progressBars = new Hashtable<>();
2804 progressBarHandlers = new Hashtable<>();
2807 if (progressBars.get(new Long(id)) != null)
2809 JPanel panel = progressBars.remove(new Long(id));
2810 if (progressBarHandlers.contains(new Long(id)))
2812 progressBarHandlers.remove(new Long(id));
2814 removeProgressPanel(panel);
2818 progressBars.put(new Long(id), addProgressPanel(message));
2825 * @see jalview.gui.IProgressIndicator#registerHandler(long,
2826 * jalview.gui.IProgressIndicatorHandler)
2829 public void registerHandler(final long id,
2830 final IProgressIndicatorHandler handler)
2832 if (progressBarHandlers == null
2833 || !progressBars.containsKey(new Long(id)))
2835 throw new Error(MessageManager.getString(
2836 "error.call_setprogressbar_before_registering_handler"));
2838 progressBarHandlers.put(new Long(id), handler);
2839 final JPanel progressPanel = progressBars.get(new Long(id));
2840 if (handler.canCancel())
2842 JButton cancel = new JButton(
2843 MessageManager.getString("action.cancel"));
2844 final IProgressIndicator us = this;
2845 cancel.addActionListener(new ActionListener()
2849 public void actionPerformed(ActionEvent e)
2851 handler.cancelActivity(id);
2852 us.setProgressBar(MessageManager
2853 .formatMessage("label.cancelled_params", new Object[]
2854 { ((JLabel) progressPanel.getComponent(0)).getText() }),
2858 progressPanel.add(cancel, BorderLayout.EAST);
2864 * @return true if any progress bars are still active
2867 public boolean operationInProgress()
2869 if (progressBars != null && progressBars.size() > 0)
2877 * This will return the first AlignFrame holding the given viewport instance. It
2878 * will break if there are more than one AlignFrames viewing a particular av.
2881 * @return alignFrame for viewport
2883 public static AlignFrame getAlignFrameFor(AlignViewportI viewport)
2885 if (desktop != null)
2887 AlignmentPanel[] aps = getAlignmentPanels(
2888 viewport.getSequenceSetId());
2889 for (int panel = 0; aps != null && panel < aps.length; panel++)
2891 if (aps[panel] != null && aps[panel].av == viewport)
2893 return aps[panel].alignFrame;
2900 public VamsasApplication getVamsasApplication()
2907 * flag set if jalview GUI is being operated programmatically
2909 private boolean inBatchMode = false;
2912 * check if jalview GUI is being operated programmatically
2914 * @return inBatchMode
2916 public boolean isInBatchMode()
2922 * set flag if jalview GUI is being operated programmatically
2924 * @param inBatchMode
2926 public void setInBatchMode(boolean inBatchMode)
2928 this.inBatchMode = inBatchMode;
2931 public void startServiceDiscovery()
2933 startServiceDiscovery(false);
2936 public void startServiceDiscovery(boolean blocking)
2938 boolean alive = true;
2939 Thread t0 = null, t1 = null, t2 = null;
2940 // JAL-940 - JALVIEW 1 services are now being EOLed as of JABA 2.1 release
2943 // todo: changesupport handlers need to be transferred
2944 if (discoverer == null)
2946 discoverer = new jalview.ws.jws1.Discoverer();
2947 // register PCS handler for desktop.
2948 discoverer.addPropertyChangeListener(changeSupport);
2950 // JAL-940 - disabled JWS1 service configuration - always start discoverer
2951 // until we phase out completely
2952 (t0 = new Thread(discoverer)).start();
2955 if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
2957 t2 = jalview.ws.jws2.Jws2Discoverer.getDiscoverer()
2958 .startDiscoverer(changeSupport);
2962 // TODO: do rest service discovery
2971 } catch (Exception e)
2974 alive = (t1 != null && t1.isAlive()) || (t2 != null && t2.isAlive())
2975 || (t3 != null && t3.isAlive())
2976 || (t0 != null && t0.isAlive());
2982 * called to check if the service discovery process completed successfully.
2986 protected void JalviewServicesChanged(PropertyChangeEvent evt)
2988 if (evt.getNewValue() == null || evt.getNewValue() instanceof Vector)
2990 final String ermsg = jalview.ws.jws2.Jws2Discoverer.getDiscoverer()
2991 .getErrorMessages();
2994 if (Cache.getDefault("SHOW_WSDISCOVERY_ERRORS", true))
2996 if (serviceChangedDialog == null)
2998 // only run if we aren't already displaying one of these.
2999 addDialogThread(serviceChangedDialog = new Runnable()
3006 * JalviewDialog jd =new JalviewDialog() {
3008 * @Override protected void cancelPressed() { // TODO
3009 * Auto-generated method stub
3011 * }@Override protected void okPressed() { // TODO
3012 * Auto-generated method stub
3014 * }@Override protected void raiseClosed() { // TODO
3015 * Auto-generated method stub
3017 * } }; jd.initDialogFrame(new
3018 * JLabel("<html><table width=\"450\"><tr><td>" + ermsg +
3019 * "<br/>It may be that you have invalid JABA URLs in your web service preferences,"
3020 * + " or mis-configured HTTP proxy settings.<br/>" +
3021 * "Check the <em>Connections</em> and <em>Web services</em> tab of the"
3023 * " Tools->Preferences dialog box to change them.</td></tr></table></html>"
3024 * ), true, true, "Web Service Configuration Problem", 450,
3027 * jd.waitForInput();
3029 JvOptionPane.showConfirmDialog(Desktop.desktop,
3030 new JLabel("<html><table width=\"450\"><tr><td>"
3031 + ermsg + "</td></tr></table>"
3032 + "<p>It may be that you have invalid JABA URLs<br/>in your web service preferences,"
3033 + "<br>or as a command-line argument, or mis-configured HTTP proxy settings.</p>"
3034 + "<p>Check the <em>Connections</em> and <em>Web services</em> tab<br/>of the"
3035 + " Tools->Preferences dialog box to change them.</p></html>"),
3036 "Web Service Configuration Problem",
3037 JvOptionPane.DEFAULT_OPTION,
3038 JvOptionPane.ERROR_MESSAGE);
3039 serviceChangedDialog = null;
3048 "Errors reported by JABA discovery service. Check web services preferences.\n"
3055 private Runnable serviceChangedDialog = null;
3058 * start a thread to open a URL in the configured browser. Pops up a warning
3059 * dialog to the user if there is an exception when calling out to the browser
3064 public static void showUrl(final String url)
3066 showUrl(url, Desktop.instance);
3070 * Like showUrl but allows progress handler to be specified
3074 * (null) or object implementing IProgressIndicator
3076 public static void showUrl(final String url,
3077 final IProgressIndicator progress)
3079 new Thread(new Runnable()
3086 if (progress != null)
3088 progress.setProgressBar(MessageManager
3089 .formatMessage("status.opening_params", new Object[]
3090 { url }), this.hashCode());
3092 jalview.util.BrowserLauncher.openURL(url);
3093 } catch (Exception ex)
3095 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
3097 .getString("label.web_browser_not_found_unix"),
3098 MessageManager.getString("label.web_browser_not_found"),
3099 JvOptionPane.WARNING_MESSAGE);
3101 ex.printStackTrace();
3103 if (progress != null)
3105 progress.setProgressBar(null, this.hashCode());
3111 public static WsParamSetManager wsparamManager = null;
3113 public static ParamManager getUserParameterStore()
3115 if (wsparamManager == null)
3117 wsparamManager = new WsParamSetManager();
3119 return wsparamManager;
3123 * static hyperlink handler proxy method for use by Jalview's internal windows
3127 public static void hyperlinkUpdate(HyperlinkEvent e)
3129 if (e.getEventType() == EventType.ACTIVATED)
3134 url = e.getURL().toString();
3135 Desktop.showUrl(url);
3136 } catch (Exception x)
3140 if (Cache.log != null)
3142 Cache.log.error("Couldn't handle string " + url + " as a URL.");
3147 "Couldn't handle string " + url + " as a URL.");
3150 // ignore any exceptions due to dud links.
3157 * single thread that handles display of dialogs to user.
3159 ExecutorService dialogExecutor = Executors.newSingleThreadExecutor();
3162 * flag indicating if dialogExecutor should try to acquire a permit
3164 private volatile boolean dialogPause = true;
3169 private java.util.concurrent.Semaphore block = new Semaphore(0);
3171 private static groovy.ui.Console groovyConsole;
3174 * add another dialog thread to the queue
3178 public void addDialogThread(final Runnable prompter)
3180 dialogExecutor.submit(new Runnable()
3190 } catch (InterruptedException x)
3195 if (instance == null)
3201 SwingUtilities.invokeAndWait(prompter);
3202 } catch (Exception q)
3204 Cache.log.warn("Unexpected Exception in dialog thread.", q);
3210 public void startDialogQueue()
3212 // set the flag so we don't pause waiting for another permit and semaphore
3213 // the current task to begin
3214 dialogPause = false;
3219 * Outputs an image of the desktop to file in EPS format, after prompting the
3220 * user for choice of Text or Lineart character rendering (unless a preference
3221 * has been set). The file name is generated as
3224 * Jalview_snapshot_nnnnn.eps where nnnnn is the current timestamp in milliseconds
3228 protected void snapShotWindow_actionPerformed(ActionEvent e)
3230 // currently the menu option to do this is not shown
3233 int width = getWidth();
3234 int height = getHeight();
3236 "Jalview_snapshot_" + System.currentTimeMillis() + ".eps");
3237 ImageWriterI writer = new ImageWriterI()
3240 public void exportImage(Graphics g) throws Exception
3243 Cache.log.info("Successfully written snapshot to file "
3244 + of.getAbsolutePath());
3247 String title = "View of desktop";
3248 ImageExporter exporter = new ImageExporter(writer, null, TYPE.EPS,
3250 exporter.doExport(of, this, width, height, title);
3254 * Explode the views in the given SplitFrame into separate SplitFrame windows.
3255 * This respects (remembers) any previous 'exploded geometry' i.e. the size and
3256 * location last time the view was expanded (if any). However it does not
3257 * remember the split pane divider location - this is set to match the
3258 * 'exploding' frame.
3262 public void explodeViews(SplitFrame sf)
3264 AlignFrame oldTopFrame = (AlignFrame) sf.getTopFrame();
3265 AlignFrame oldBottomFrame = (AlignFrame) sf.getBottomFrame();
3266 List<? extends AlignmentViewPanel> topPanels = oldTopFrame
3268 List<? extends AlignmentViewPanel> bottomPanels = oldBottomFrame
3270 int viewCount = topPanels.size();
3277 * Processing in reverse order works, forwards order leaves the first panels
3278 * not visible. I don't know why!
3280 for (int i = viewCount - 1; i >= 0; i--)
3283 * Make new top and bottom frames. These take over the respective
3284 * AlignmentPanel objects, including their AlignmentViewports, so the
3285 * cdna/protein relationships between the viewports is carried over to the
3288 * explodedGeometry holds the (x, y) position of the previously exploded
3289 * SplitFrame, and the (width, height) of the AlignFrame component
3291 AlignmentPanel topPanel = (AlignmentPanel) topPanels.get(i);
3292 AlignFrame newTopFrame = new AlignFrame(topPanel);
3293 newTopFrame.setSize(oldTopFrame.getSize());
3294 newTopFrame.setVisible(true);
3295 Rectangle geometry = ((AlignViewport) topPanel.getAlignViewport())
3296 .getExplodedGeometry();
3297 if (geometry != null)
3299 newTopFrame.setSize(geometry.getSize());
3302 AlignmentPanel bottomPanel = (AlignmentPanel) bottomPanels.get(i);
3303 AlignFrame newBottomFrame = new AlignFrame(bottomPanel);
3304 newBottomFrame.setSize(oldBottomFrame.getSize());
3305 newBottomFrame.setVisible(true);
3306 geometry = ((AlignViewport) bottomPanel.getAlignViewport())
3307 .getExplodedGeometry();
3308 if (geometry != null)
3310 newBottomFrame.setSize(geometry.getSize());
3313 topPanel.av.setGatherViewsHere(false);
3314 bottomPanel.av.setGatherViewsHere(false);
3315 JInternalFrame splitFrame = new SplitFrame(newTopFrame,
3317 if (geometry != null)
3319 splitFrame.setLocation(geometry.getLocation());
3321 Desktop.addInternalFrame(splitFrame, sf.getTitle(), -1, -1);
3325 * Clear references to the panels (now relocated in the new SplitFrames)
3326 * before closing the old SplitFrame.
3329 bottomPanels.clear();
3334 * Gather expanded split frames, sharing the same pairs of sequence set ids,
3335 * back into the given SplitFrame as additional views. Note that the gathered
3336 * frames may themselves have multiple views.
3340 public void gatherViews(GSplitFrame source)
3343 * special handling of explodedGeometry for a view within a SplitFrame: - it
3344 * holds the (x, y) position of the enclosing SplitFrame, and the (width,
3345 * height) of the AlignFrame component
3347 AlignFrame myTopFrame = (AlignFrame) source.getTopFrame();
3348 AlignFrame myBottomFrame = (AlignFrame) source.getBottomFrame();
3349 myTopFrame.viewport.setExplodedGeometry(new Rectangle(source.getX(),
3350 source.getY(), myTopFrame.getWidth(), myTopFrame.getHeight()));
3351 myBottomFrame.viewport
3352 .setExplodedGeometry(new Rectangle(source.getX(), source.getY(),
3353 myBottomFrame.getWidth(), myBottomFrame.getHeight()));
3354 myTopFrame.viewport.setGatherViewsHere(true);
3355 myBottomFrame.viewport.setGatherViewsHere(true);
3356 String topViewId = myTopFrame.viewport.getSequenceSetId();
3357 String bottomViewId = myBottomFrame.viewport.getSequenceSetId();
3359 JInternalFrame[] frames = desktop.getAllFrames();
3360 for (JInternalFrame frame : frames)
3362 if (frame instanceof SplitFrame && frame != source)
3364 SplitFrame sf = (SplitFrame) frame;
3365 AlignFrame topFrame = (AlignFrame) sf.getTopFrame();
3366 AlignFrame bottomFrame = (AlignFrame) sf.getBottomFrame();
3367 boolean gatherThis = false;
3368 for (int a = 0; a < topFrame.alignPanels.size(); a++)
3370 AlignmentPanel topPanel = topFrame.alignPanels.get(a);
3371 AlignmentPanel bottomPanel = bottomFrame.alignPanels.get(a);
3372 if (topViewId.equals(topPanel.av.getSequenceSetId())
3373 && bottomViewId.equals(bottomPanel.av.getSequenceSetId()))
3376 topPanel.av.setGatherViewsHere(false);
3377 bottomPanel.av.setGatherViewsHere(false);
3378 topPanel.av.setExplodedGeometry(
3379 new Rectangle(sf.getLocation(), topFrame.getSize()));
3380 bottomPanel.av.setExplodedGeometry(
3381 new Rectangle(sf.getLocation(), bottomFrame.getSize()));
3382 myTopFrame.addAlignmentPanel(topPanel, false);
3383 myBottomFrame.addAlignmentPanel(bottomPanel, false);
3389 topFrame.getAlignPanels().clear();
3390 bottomFrame.getAlignPanels().clear();
3397 * The dust settles...give focus to the tab we did this from.
3399 myTopFrame.setDisplayedView(myTopFrame.alignPanel);
3402 public static groovy.ui.Console getGroovyConsole()
3404 return groovyConsole;
3408 * handles the payload of a drag and drop event.
3410 * TODO refactor to desktop utilities class
3413 * - Data source strings extracted from the drop event
3415 * - protocol for each data source extracted from the drop event
3419 * - the payload from the drop event
3422 public static void transferFromDropTarget(List<Object> files,
3423 List<DataSourceType> protocols, DropTargetDropEvent evt,
3424 Transferable t) throws Exception
3427 // BH 2018 changed List<String> to List<Object> to allow for File from SwingJS
3429 // DataFlavor[] flavors = t.getTransferDataFlavors();
3430 // for (int i = 0; i < flavors.length; i++) {
3431 // if (flavors[i].isFlavorJavaFileListType()) {
3432 // evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
3433 // List<File> list = (List<File>) t.getTransferData(flavors[i]);
3434 // for (int j = 0; j < list.size(); j++) {
3435 // File file = (File) list.get(j);
3436 // byte[] data = getDroppedFileBytes(file);
3437 // fileName.setText(file.getName() + " - " + data.length + " " +
3438 // evt.getLocation());
3439 // JTextArea target = (JTextArea) ((DropTarget) evt.getSource()).getComponent();
3440 // target.setText(new String(data));
3442 // dtde.dropComplete(true);
3447 DataFlavor uriListFlavor = new DataFlavor(
3448 "text/uri-list;class=java.lang.String"), urlFlavour = null;
3451 urlFlavour = new DataFlavor(
3452 "application/x-java-url; class=java.net.URL");
3453 } catch (ClassNotFoundException cfe)
3455 Cache.log.debug("Couldn't instantiate the URL dataflavor.", cfe);
3458 if (urlFlavour != null && t.isDataFlavorSupported(urlFlavour))
3463 java.net.URL url = (URL) t.getTransferData(urlFlavour);
3464 // nb: java 8 osx bug https://bugs.openjdk.java.net/browse/JDK-8156099
3465 // means url may be null.
3468 protocols.add(DataSourceType.URL);
3469 files.add(url.toString());
3470 Cache.log.debug("Drop handled as URL dataflavor "
3471 + files.get(files.size() - 1));
3476 if (Platform.isAMac())
3479 "Please ignore plist error - occurs due to problem with java 8 on OSX");
3483 } catch (Throwable ex)
3485 Cache.log.debug("URL drop handler failed.", ex);
3488 if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
3490 // Works on Windows and MacOSX
3491 Cache.log.debug("Drop handled as javaFileListFlavor");
3492 for (Object file : (List) t
3493 .getTransferData(DataFlavor.javaFileListFlavor))
3496 protocols.add(DataSourceType.FILE);
3501 // Unix like behaviour
3502 boolean added = false;
3504 if (t.isDataFlavorSupported(uriListFlavor))
3506 Cache.log.debug("Drop handled as uriListFlavor");
3507 // This is used by Unix drag system
3508 data = (String) t.getTransferData(uriListFlavor);
3512 // fallback to text: workaround - on OSX where there's a JVM bug
3513 Cache.log.debug("standard URIListFlavor failed. Trying text");
3514 // try text fallback
3515 DataFlavor textDf = new DataFlavor(
3516 "text/plain;class=java.lang.String");
3517 if (t.isDataFlavorSupported(textDf))
3519 data = (String) t.getTransferData(textDf);
3522 Cache.log.debug("Plain text drop content returned "
3523 + (data == null ? "Null - failed" : data));
3528 while (protocols.size() < files.size())
3530 Cache.log.debug("Adding missing FILE protocol for "
3531 + files.get(protocols.size()));
3532 protocols.add(DataSourceType.FILE);
3534 for (java.util.StringTokenizer st = new java.util.StringTokenizer(
3535 data, "\r\n"); st.hasMoreTokens();)
3538 String s = st.nextToken();
3539 if (s.startsWith("#"))
3541 // the line is a comment (as per the RFC 2483)
3544 java.net.URI uri = new java.net.URI(s);
3545 if (uri.getScheme().toLowerCase().startsWith("http"))
3547 protocols.add(DataSourceType.URL);
3548 files.add(uri.toString());
3552 // otherwise preserve old behaviour: catch all for file objects
3553 java.io.File file = new java.io.File(uri);
3554 protocols.add(DataSourceType.FILE);
3555 files.add(file.toString());
3560 if (Cache.log.isDebugEnabled())
3562 if (data == null || !added)
3565 if (t.getTransferDataFlavors() != null
3566 && t.getTransferDataFlavors().length > 0)
3569 "Couldn't resolve drop data. Here are the supported flavors:");
3570 for (DataFlavor fl : t.getTransferDataFlavors())
3573 "Supported transfer dataflavor: " + fl.toString());
3574 Object df = t.getTransferData(fl);
3577 Cache.log.debug("Retrieves: " + df);
3581 Cache.log.debug("Retrieved nothing");
3587 Cache.log.debug("Couldn't resolve dataflavor for drop: "
3593 if (Platform.isWindows())
3596 Cache.log.debug("Scanning dropped content for Windows Link Files");
3598 // resolve any .lnk files in the file drop
3599 for (int f = 0; f < files.size(); f++)
3601 String source = files.get(f).toString().toLowerCase();
3602 if (protocols.get(f).equals(DataSourceType.FILE)
3603 && (source.endsWith(".lnk") || source.endsWith(".url")
3604 || source.endsWith(".site")))
3608 Object obj = files.get(f);
3609 File lf = (obj instanceof File ? (File) obj
3610 : new File((String) obj));
3611 // process link file to get a URL
3612 Cache.log.debug("Found potential link file: " + lf);
3613 WindowsShortcut wscfile = new WindowsShortcut(lf);
3614 String fullname = wscfile.getRealFilename();
3615 protocols.set(f, FormatAdapter.checkProtocol(fullname));
3616 files.set(f, fullname);
3617 Cache.log.debug("Parsed real filename " + fullname
3618 + " to extract protocol: " + protocols.get(f));
3619 } catch (Exception ex)
3622 "Couldn't parse " + files.get(f) + " as a link file.",
3631 * Sets the Preferences property for experimental features to True or False
3632 * depending on the state of the controlling menu item
3635 protected void showExperimental_actionPerformed(boolean selected)
3637 Cache.setProperty(EXPERIMENTAL_FEATURES, Boolean.toString(selected));
3641 * Answers a (possibly empty) list of any structure viewer frames (currently for
3642 * either Jmol or Chimera) which are currently open. This may optionally be
3643 * restricted to viewers of a specified class, or viewers linked to a specified
3647 * if not null, only return viewers linked to this panel
3648 * @param structureViewerClass
3649 * if not null, only return viewers of this class
3652 public List<StructureViewerBase> getStructureViewers(
3653 AlignmentPanel apanel,
3654 Class<? extends StructureViewerBase> structureViewerClass)
3656 List<StructureViewerBase> result = new ArrayList<>();
3657 JInternalFrame[] frames = Desktop.instance.getAllFrames();
3659 for (JInternalFrame frame : frames)
3661 if (frame instanceof StructureViewerBase)
3663 if (structureViewerClass == null
3664 || structureViewerClass.isInstance(frame))
3667 || ((StructureViewerBase) frame).isLinkedWith(apanel))
3669 result.add((StructureViewerBase) frame);