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 addDialogThread(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 addDialogThread(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"
580 protected void showNews_actionPerformed(ActionEvent e)
582 showNews(showNews.isSelected());
585 void showNews(boolean visible)
587 Cache.log.debug((visible ? "Showing" : "Hiding") + " news.");
588 showNews.setSelected(visible);
589 if (visible && !jvnews.isVisible())
591 new Thread(new Runnable()
596 long now = System.currentTimeMillis();
597 Desktop.instance.setProgressBar(
598 MessageManager.getString("status.refreshing_news"), now);
599 jvnews.refreshNews();
600 Desktop.instance.setProgressBar(null, now);
608 * recover the last known dimensions for a jalview window
611 * - empty string is desktop, all other windows have unique prefix
612 * @return null or last known dimensions scaled to current geometry (if last
613 * window geom was known)
615 Rectangle getLastKnownDimensions(String windowName)
617 // TODO: lock aspect ratio for scaling desktop Bug #0058199
618 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
619 String x = jalview.bin.Cache.getProperty(windowName + "SCREEN_X");
620 String y = jalview.bin.Cache.getProperty(windowName + "SCREEN_Y");
621 String width = jalview.bin.Cache
622 .getProperty(windowName + "SCREEN_WIDTH");
623 String height = jalview.bin.Cache
624 .getProperty(windowName + "SCREEN_HEIGHT");
625 if ((x != null) && (y != null) && (width != null) && (height != null))
627 int ix = Integer.parseInt(x), iy = Integer.parseInt(y),
628 iw = Integer.parseInt(width), ih = Integer.parseInt(height);
629 if (jalview.bin.Cache.getProperty("SCREENGEOMETRY_WIDTH") != null)
631 // attempt #1 - try to cope with change in screen geometry - this
632 // version doesn't preserve original jv aspect ratio.
633 // take ratio of current screen size vs original screen size.
634 double sw = ((1f * screenSize.width) / (1f * Integer.parseInt(
635 jalview.bin.Cache.getProperty("SCREENGEOMETRY_WIDTH"))));
636 double sh = ((1f * screenSize.height) / (1f * Integer.parseInt(
637 jalview.bin.Cache.getProperty("SCREENGEOMETRY_HEIGHT"))));
638 // rescale the bounds depending upon the current screen geometry.
639 ix = (int) (ix * sw);
640 iw = (int) (iw * sw);
641 iy = (int) (iy * sh);
642 ih = (int) (ih * sh);
643 while (ix >= screenSize.width)
645 jalview.bin.Cache.log.debug(
646 "Window geometry location recall error: shifting horizontal to within screenbounds.");
647 ix -= screenSize.width;
649 while (iy >= screenSize.height)
651 jalview.bin.Cache.log.debug(
652 "Window geometry location recall error: shifting vertical to within screenbounds.");
653 iy -= screenSize.height;
655 jalview.bin.Cache.log.debug(
656 "Got last known dimensions for " + windowName + ": x:" + ix
657 + " y:" + iy + " width:" + iw + " height:" + ih);
659 // return dimensions for new instance
660 return new Rectangle(ix, iy, iw, ih);
665 private void doVamsasClientCheck()
667 if (Cache.vamsasJarsPresent())
669 setupVamsasDisconnectedGui();
670 VamsasMenu.setVisible(true);
671 final Desktop us = this;
672 VamsasMenu.addMenuListener(new MenuListener()
674 // this listener remembers when the menu was first selected, and
675 // doesn't rebuild the session list until it has been cleared and
677 boolean refresh = true;
680 public void menuCanceled(MenuEvent e)
686 public void menuDeselected(MenuEvent e)
692 public void menuSelected(MenuEvent e)
696 us.buildVamsasStMenu();
701 vamsasStart.setVisible(true);
705 void showPasteMenu(int x, int y)
707 JPopupMenu popup = new JPopupMenu();
708 JMenuItem item = new JMenuItem(
709 MessageManager.getString("label.paste_new_window"));
710 item.addActionListener(new ActionListener()
713 public void actionPerformed(ActionEvent evt)
720 popup.show(this, x, y);
727 Clipboard c = Toolkit.getDefaultToolkit().getSystemClipboard();
728 Transferable contents = c.getContents(this);
730 if (contents != null)
732 String file = (String) contents
733 .getTransferData(DataFlavor.stringFlavor);
735 FileFormatI format = new IdentifyFile().identify(file,
736 DataSourceType.PASTE);
738 new FileLoader().LoadFile(file, DataSourceType.PASTE, format);
741 } catch (Exception ex)
744 "Unable to paste alignment from system clipboard:\n" + ex);
749 * Adds and opens the given frame to the desktop
760 public static synchronized void addInternalFrame(
761 final JInternalFrame frame, String title, int w, int h)
763 addInternalFrame(frame, title, true, w, h, true, false);
767 * Add an internal frame to the Jalview desktop
774 * When true, display frame immediately, otherwise, caller must call
775 * setVisible themselves.
781 public static synchronized void addInternalFrame(
782 final JInternalFrame frame, String title, boolean makeVisible,
785 addInternalFrame(frame, title, makeVisible, w, h, true, false);
789 * Add an internal frame to the Jalview desktop and make it visible
802 public static synchronized void addInternalFrame(
803 final JInternalFrame frame, String title, int w, int h,
806 addInternalFrame(frame, title, true, w, h, resizable, false);
810 * Add an internal frame to the Jalview desktop
817 * When true, display frame immediately, otherwise, caller must call
818 * setVisible themselves.
825 * @param ignoreMinSize
826 * Do not set the default minimum size for frame
828 public static synchronized void addInternalFrame(
829 final JInternalFrame frame, String title, boolean makeVisible,
830 int w, int h, boolean resizable, boolean ignoreMinSize)
833 // TODO: allow callers to determine X and Y position of frame (eg. via
835 // TODO: consider fixing method to update entries in the window submenu with
836 // the current window title
838 frame.setTitle(title);
839 if (frame.getWidth() < 1 || frame.getHeight() < 1)
843 // THIS IS A PUBLIC STATIC METHOD, SO IT MAY BE CALLED EVEN IN
844 // A HEADLESS STATE WHEN NO DESKTOP EXISTS. MUST RETURN
845 // IF JALVIEW IS RUNNING HEADLESS
846 // ///////////////////////////////////////////////
847 if (instance == null || (System.getProperty("java.awt.headless") != null
848 && System.getProperty("java.awt.headless").equals("true")))
857 frame.setMinimumSize(
858 new Dimension(DEFAULT_MIN_WIDTH, DEFAULT_MIN_HEIGHT));
860 // Set default dimension for Alignment Frame window.
861 // The Alignment Frame window could be added from a number of places,
863 // I did this here in order not to miss out on any Alignment frame.
864 if (frame instanceof AlignFrame)
866 frame.setMinimumSize(new Dimension(ALIGN_FRAME_DEFAULT_MIN_WIDTH,
867 ALIGN_FRAME_DEFAULT_MIN_HEIGHT));
871 frame.setVisible(makeVisible);
872 frame.setClosable(true);
873 frame.setResizable(resizable);
874 frame.setMaximizable(resizable);
875 frame.setIconifiable(resizable);
876 frame.setOpaque(/** @j2sNative true || */
879 if (frame.getX() < 1 && frame.getY() < 1)
881 frame.setLocation(xOffset * openFrameCount,
882 yOffset * ((openFrameCount - 1) % 10) + yOffset);
886 * add an entry for the new frame in the Window menu
887 * (and remove it when the frame is closed)
889 final JMenuItem menuItem = new JMenuItem(title);
890 frame.addInternalFrameListener(new InternalFrameAdapter()
893 public void internalFrameActivated(InternalFrameEvent evt)
895 JInternalFrame itf = desktop.getSelectedFrame();
898 if (itf instanceof AlignFrame)
900 Jalview.setCurrentAlignFrame((AlignFrame) itf);
907 public void internalFrameClosed(InternalFrameEvent evt)
909 PaintRefresher.RemoveComponent(frame);
912 * defensive check to prevent frames being
913 * added half off the window
915 if (openFrameCount > 0)
921 * ensure no reference to alignFrame retained by menu item listener
923 if (menuItem.getActionListeners().length > 0)
925 menuItem.removeActionListener(menuItem.getActionListeners()[0]);
927 windowMenu.remove(menuItem);
931 menuItem.addActionListener(new ActionListener()
934 public void actionPerformed(ActionEvent e)
938 frame.setSelected(true);
939 frame.setIcon(false);
940 } catch (java.beans.PropertyVetoException ex)
942 // System.err.println(ex.toString());
947 setKeyBindings(frame);
951 windowMenu.add(menuItem);
956 frame.setSelected(true);
957 frame.requestFocus();
958 } catch (java.beans.PropertyVetoException ve)
960 } catch (java.lang.ClassCastException cex)
963 "Squashed a possible GUI implementation error. If you can recreate this, please look at http://issues.jalview.org/browse/JAL-869",
969 * Add key bindings to a JInternalFrame so that Ctrl-W and Cmd-W will close the
974 private static void setKeyBindings(JInternalFrame frame)
976 @SuppressWarnings("serial")
977 final Action closeAction = new AbstractAction()
980 public void actionPerformed(ActionEvent e)
987 * set up key bindings for Ctrl-W and Cmd-W, with the same (Close) action
989 KeyStroke ctrlWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
990 InputEvent.CTRL_DOWN_MASK);
991 KeyStroke cmdWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
992 Toolkit.getDefaultToolkit().getMenuShortcutKeyMask());
994 InputMap inputMap = frame
995 .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
996 String ctrlW = ctrlWKey.toString();
997 inputMap.put(ctrlWKey, ctrlW);
998 inputMap.put(cmdWKey, ctrlW);
1000 ActionMap actionMap = frame.getActionMap();
1001 actionMap.put(ctrlW, closeAction);
1005 public void lostOwnership(Clipboard clipboard, Transferable contents)
1009 Desktop.jalviewClipboard = null;
1012 internalCopy = false;
1016 public void dragEnter(DropTargetDragEvent evt)
1021 public void dragExit(DropTargetEvent evt)
1026 public void dragOver(DropTargetDragEvent evt)
1031 public void dropActionChanged(DropTargetDragEvent evt)
1042 public void drop(DropTargetDropEvent evt)
1044 boolean success = true;
1045 // JAL-1552 - acceptDrop required before getTransferable call for
1046 // Java's Transferable for native dnd
1047 evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
1048 Transferable t = evt.getTransferable();
1049 List<Object> files = new ArrayList<>();
1050 List<DataSourceType> protocols = new ArrayList<>();
1054 Desktop.transferFromDropTarget(files, protocols, evt, t);
1055 } catch (Exception e)
1057 e.printStackTrace();
1065 for (int i = 0; i < files.size(); i++)
1067 // BH 2018 File or String
1068 Object file = files.get(i);
1069 String fileName = file.toString();
1070 DataSourceType protocol = (protocols == null)
1071 ? DataSourceType.FILE
1073 FileFormatI format = null;
1075 if (fileName.endsWith(".jar"))
1077 format = FileFormat.Jalview;
1082 format = new IdentifyFile().identify(file, protocol);
1085 new FileLoader().LoadFile(null, file, protocol, format);
1088 } catch (Exception ex)
1093 evt.dropComplete(success); // need this to ensure input focus is properly
1094 // transfered to any new windows created
1104 public void inputLocalFileMenuItem_actionPerformed(AlignViewport viewport)
1106 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
1107 JalviewFileChooser chooser = JalviewFileChooser
1108 .forRead(Cache.getProperty("LAST_DIRECTORY"), fileFormat);
1110 chooser.setFileView(new JalviewFileView());
1111 chooser.setDialogTitle(
1112 MessageManager.getString("label.open_local_file"));
1113 chooser.setToolTipText(MessageManager.getString("action.open"));
1115 chooser.response(new RunResponse(JalviewFileChooser.APPROVE_OPTION)
1121 File selectedFile = chooser.getSelectedFile();
1122 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1124 FileFormatI format = chooser.getSelectedFormat();
1127 * Call IdentifyFile to verify the file contains what its extension implies.
1128 * Skip this step for dynamically added file formats, because
1129 * IdentifyFile does not know how to recognise them.
1131 if (FileFormats.getInstance().isIdentifiable(format))
1135 format = new IdentifyFile().identify(selectedFile,
1136 DataSourceType.FILE);
1137 } catch (FileFormatException e)
1139 // format = null; //??
1143 new FileLoader().LoadFile(viewport, selectedFile,
1144 DataSourceType.FILE, format);
1146 }).openDialog(this);
1150 * Shows a dialog for input of a URL at which to retrieve alignment data
1155 public void inputURLMenuItem_actionPerformed(AlignViewport viewport)
1157 // This construct allows us to have a wider textfield
1159 JLabel label = new JLabel(
1160 MessageManager.getString("label.input_file_url"));
1162 JPanel panel = new JPanel(new GridLayout(2, 1));
1166 * the URL to fetch is
1167 * Java: an editable combobox with history
1168 * JS: (pending JAL-3038) a plain text field
1171 String urlBase = "http://www.";
1174 history = new JTextField(urlBase, 35);
1178 JComboBox<String> asCombo = new JComboBox<>();
1179 asCombo.setPreferredSize(new Dimension(400, 20));
1180 asCombo.setEditable(true);
1181 asCombo.addItem(urlBase);
1182 String historyItems = Cache.getProperty("RECENT_URL");
1183 if (historyItems != null)
1185 for (String token : historyItems.split("\\t"))
1187 asCombo.addItem(token);
1194 Object[] options = new Object[] { MessageManager.getString("action.ok"),
1195 MessageManager.getString("action.cancel") };
1196 RunResponse action = new RunResponse(JvOptionPane.OK_OPTION) {
1200 String url = Jalview.isJS() ? ((JTextField) history).getText()
1201 : ((JComboBox<String>) history).getSelectedItem()
1204 if (url.toLowerCase().endsWith(".jar"))
1206 if (viewport != null)
1208 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1209 FileFormat.Jalview);
1213 new FileLoader().LoadFile(url, DataSourceType.URL,
1214 FileFormat.Jalview);
1219 FileFormatI format = null;
1222 format = new IdentifyFile().identify(url, DataSourceType.URL);
1223 } catch (FileFormatException e)
1225 // TODO revise error handling, distinguish between
1226 // URL not found and response not valid
1231 String msg = MessageManager.formatMessage("label.couldnt_locate", url);
1232 JvOptionPane.showInternalMessageDialog(Desktop.desktop, msg,
1233 MessageManager.getString("label.url_not_found"),
1234 JvOptionPane.WARNING_MESSAGE);
1239 if (viewport != null)
1241 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1246 new FileLoader().LoadFile(url, DataSourceType.URL, format);
1250 String dialogOption = MessageManager
1251 .getString("label.input_alignment_from_url");
1252 JvOptionPane.newOptionDialog(desktop).response(action)
1253 .showInternalDialog(panel, dialogOption,
1254 JvOptionPane.YES_NO_CANCEL_OPTION,
1255 JvOptionPane.PLAIN_MESSAGE, null, options,
1256 MessageManager.getString("action.ok"));
1260 * Opens the CutAndPaste window for the user to paste an alignment in to
1263 * - if not null, the pasted alignment is added to the current
1264 * alignment; if null, to a new alignment window
1267 public void inputTextboxMenuItem_actionPerformed(
1268 AlignmentViewPanel viewPanel)
1270 CutAndPasteTransfer cap = new CutAndPasteTransfer();
1271 cap.setForInput(viewPanel);
1272 Desktop.addInternalFrame(cap,
1273 MessageManager.getString("label.cut_paste_alignmen_file"), true,
1283 Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
1284 jalview.bin.Cache.setProperty("SCREENGEOMETRY_WIDTH",
1286 jalview.bin.Cache.setProperty("SCREENGEOMETRY_HEIGHT",
1287 screen.height + "");
1288 storeLastKnownDimensions("", new Rectangle(getBounds().x, getBounds().y,
1289 getWidth(), getHeight()));
1291 if (jconsole != null)
1293 storeLastKnownDimensions("JAVA_CONSOLE_", jconsole.getBounds());
1294 jconsole.stopConsole();
1298 storeLastKnownDimensions("JALVIEW_RSS_WINDOW_", jvnews.getBounds());
1301 if (dialogExecutor != null)
1303 dialogExecutor.shutdownNow();
1305 closeAll_actionPerformed(null);
1307 if (groovyConsole != null)
1309 // suppress a possible repeat prompt to save script
1310 groovyConsole.setDirty(false);
1311 groovyConsole.exit();
1316 private void storeLastKnownDimensions(String string, Rectangle jc)
1318 jalview.bin.Cache.log.debug("Storing last known dimensions for "
1319 + string + ": x:" + jc.x + " y:" + jc.y + " width:" + jc.width
1320 + " height:" + jc.height);
1322 jalview.bin.Cache.setProperty(string + "SCREEN_X", jc.x + "");
1323 jalview.bin.Cache.setProperty(string + "SCREEN_Y", jc.y + "");
1324 jalview.bin.Cache.setProperty(string + "SCREEN_WIDTH", jc.width + "");
1325 jalview.bin.Cache.setProperty(string + "SCREEN_HEIGHT", jc.height + "");
1335 public void aboutMenuItem_actionPerformed(ActionEvent e)
1337 // StringBuffer message = getAboutMessage(false);
1338 // JvOptionPane.showInternalMessageDialog(Desktop.desktop,
1340 // message.toString(), "About Jalview", JvOptionPane.INFORMATION_MESSAGE);
1341 new Thread(new Runnable()
1346 new SplashScreen(true);
1351 public StringBuffer getAboutMessage(boolean shortv)
1353 StringBuffer message = new StringBuffer();
1354 message.append("<html>");
1357 message.append("<h1><strong>Version: "
1358 + jalview.bin.Cache.getProperty("VERSION")
1359 + "</strong></h1>");
1360 message.append("<strong>Last Updated: <em>"
1361 + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown")
1362 + "</em></strong>");
1368 message.append("<strong>Version "
1369 + jalview.bin.Cache.getProperty("VERSION")
1370 + "; last updated: "
1371 + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown"));
1374 if (jalview.bin.Cache.getDefault("LATEST_VERSION", "Checking")
1375 .equals("Checking"))
1377 message.append("<br>...Checking latest version...</br>");
1379 else if (!jalview.bin.Cache.getDefault("LATEST_VERSION", "Checking")
1380 .equals(jalview.bin.Cache.getProperty("VERSION")))
1382 boolean red = false;
1383 if (jalview.bin.Cache.getProperty("VERSION").toLowerCase()
1384 .indexOf("automated build") == -1)
1387 // Displayed when code version and jnlp version do not match and code
1388 // version is not a development build
1389 message.append("<div style=\"color: #FF0000;font-style: bold;\">");
1392 message.append("<br>!! Version "
1393 + jalview.bin.Cache.getDefault("LATEST_VERSION",
1395 + " is available for download from "
1396 + jalview.bin.Cache.getDefault("www.jalview.org",
1397 "http://www.jalview.org")
1401 message.append("</div>");
1404 message.append("<br>Authors: " + jalview.bin.Cache.getDefault(
1406 "The Jalview Authors (See AUTHORS file for current list)")
1407 + "<br><br>Development managed by The Barton Group, University of Dundee, Scotland, UK.<br>"
1408 + "<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"
1409 + "<br><br>If you use Jalview, please cite:"
1410 + "<br>Waterhouse, A.M., Procter, J.B., Martin, D.M.A, Clamp, M. and Barton, G. J. (2009)"
1411 + "<br>Jalview Version 2 - a multiple sequence alignment editor and analysis workbench"
1412 + "<br>Bioinformatics doi: 10.1093/bioinformatics/btp033"
1418 * Action on requesting Help documentation
1421 public void documentationMenuItem_actionPerformed()
1427 BrowserLauncher.openURL("http://www.jalview.org/help.html");
1431 Help.showHelpWindow();
1433 } catch (Exception ex)
1435 System.err.println("Error opening help: " + ex.getMessage());
1440 public void closeAll_actionPerformed(ActionEvent e)
1442 // TODO show a progress bar while closing?
1443 JInternalFrame[] frames = desktop.getAllFrames();
1444 for (int i = 0; i < frames.length; i++)
1448 frames[i].setClosed(true);
1449 } catch (java.beans.PropertyVetoException ex)
1453 Jalview.setCurrentAlignFrame(null);
1454 System.out.println("ALL CLOSED");
1455 if (v_client != null)
1457 // TODO clear binding to vamsas document objects on close_all
1461 * reset state of singleton objects as appropriate (clear down session state
1462 * when all windows are closed)
1464 StructureSelectionManager ssm = StructureSelectionManager
1465 .getStructureSelectionManager(this);
1473 public void raiseRelated_actionPerformed(ActionEvent e)
1475 reorderAssociatedWindows(false, false);
1479 public void minimizeAssociated_actionPerformed(ActionEvent e)
1481 reorderAssociatedWindows(true, false);
1484 void closeAssociatedWindows()
1486 reorderAssociatedWindows(false, true);
1492 * @seejalview.jbgui.GDesktop#garbageCollect_actionPerformed(java.awt.event.
1496 protected void garbageCollect_actionPerformed(ActionEvent e)
1498 // We simply collect the garbage
1499 jalview.bin.Cache.log.debug("Collecting garbage...");
1501 jalview.bin.Cache.log.debug("Finished garbage collection.");
1508 * jalview.jbgui.GDesktop#showMemusage_actionPerformed(java.awt.event.ActionEvent
1512 protected void showMemusage_actionPerformed(ActionEvent e)
1514 desktop.showMemoryUsage(showMemusage.isSelected());
1521 * jalview.jbgui.GDesktop#showConsole_actionPerformed(java.awt.event.ActionEvent
1525 protected void showConsole_actionPerformed(ActionEvent e)
1527 showConsole(showConsole.isSelected());
1530 Console jconsole = null;
1533 * control whether the java console is visible or not
1537 void showConsole(boolean selected)
1539 // TODO: decide if we should update properties file
1540 if (jconsole != null) // BH 2018
1542 showConsole.setSelected(selected);
1543 Cache.setProperty("SHOW_JAVA_CONSOLE",
1544 Boolean.valueOf(selected).toString());
1545 jconsole.setVisible(selected);
1549 void reorderAssociatedWindows(boolean minimize, boolean close)
1551 JInternalFrame[] frames = desktop.getAllFrames();
1552 if (frames == null || frames.length < 1)
1557 AlignmentViewport source = null, target = null;
1558 if (frames[0] instanceof AlignFrame)
1560 source = ((AlignFrame) frames[0]).getCurrentView();
1562 else if (frames[0] instanceof TreePanel)
1564 source = ((TreePanel) frames[0]).getViewPort();
1566 else if (frames[0] instanceof PCAPanel)
1568 source = ((PCAPanel) frames[0]).av;
1570 else if (frames[0].getContentPane() instanceof PairwiseAlignPanel)
1572 source = ((PairwiseAlignPanel) frames[0].getContentPane()).av;
1577 for (int i = 0; i < frames.length; i++)
1580 if (frames[i] == null)
1584 if (frames[i] instanceof AlignFrame)
1586 target = ((AlignFrame) frames[i]).getCurrentView();
1588 else if (frames[i] instanceof TreePanel)
1590 target = ((TreePanel) frames[i]).getViewPort();
1592 else if (frames[i] instanceof PCAPanel)
1594 target = ((PCAPanel) frames[i]).av;
1596 else if (frames[i].getContentPane() instanceof PairwiseAlignPanel)
1598 target = ((PairwiseAlignPanel) frames[i].getContentPane()).av;
1601 if (source == target)
1607 frames[i].setClosed(true);
1611 frames[i].setIcon(minimize);
1614 frames[i].toFront();
1618 } catch (java.beans.PropertyVetoException ex)
1633 protected void preferences_actionPerformed(ActionEvent e)
1639 * Prompts the user to choose a file and then saves the Jalview state as a
1640 * Jalview project file
1643 public void saveState_actionPerformed()
1645 JalviewFileChooser chooser = new JalviewFileChooser("jvp",
1648 chooser.setFileView(new JalviewFileView());
1649 chooser.setDialogTitle(MessageManager.getString("label.save_state"));
1651 int value = chooser.showSaveDialog(this);
1653 if (value == JalviewFileChooser.APPROVE_OPTION)
1655 final Desktop me = this;
1656 final java.io.File choice = chooser.getSelectedFile();
1657 setProjectFile(choice);
1659 new Thread(new Runnable()
1664 // TODO: refactor to Jalview desktop session controller action.
1665 setProgressBar(MessageManager.formatMessage(
1666 "label.saving_jalview_project", new Object[]
1667 { choice.getName() }), choice.hashCode());
1668 jalview.bin.Cache.setProperty("LAST_DIRECTORY",
1669 choice.getParent());
1670 // TODO catch and handle errors for savestate
1671 // TODO prevent user from messing with the Desktop whilst we're saving
1674 new Jalview2XML().saveState(choice);
1675 } catch (OutOfMemoryError oom)
1678 "Whilst saving current state to " + choice.getName(),
1680 } catch (Exception ex)
1683 "Problems whilst trying to save to " + choice.getName(),
1685 JvOptionPane.showMessageDialog(me,
1686 MessageManager.formatMessage(
1687 "label.error_whilst_saving_current_state_to",
1689 { choice.getName() }),
1690 MessageManager.getString("label.couldnt_save_project"),
1691 JvOptionPane.WARNING_MESSAGE);
1693 setProgressBar(null, choice.hashCode());
1699 private void setProjectFile(File choice)
1701 this.projectFile = choice;
1704 public File getProjectFile()
1706 return this.projectFile;
1710 * Prompts the user to choose a file and loads in as a Jalview project file
1713 public void loadState_actionPerformed()
1715 JalviewFileChooser chooser = new JalviewFileChooser(
1716 Cache.getProperty("LAST_DIRECTORY"), new String[]
1719 { "Jalview Project", "Jalview Project (old)" },
1721 chooser.setFileView(new JalviewFileView());
1722 chooser.setDialogTitle(MessageManager.getString("label.restore_state"));
1724 int value = chooser.showOpenDialog(this);
1726 if (value == JalviewFileChooser.APPROVE_OPTION)
1728 final File selectedFile = chooser.getSelectedFile();
1729 setProjectFile(selectedFile);
1730 final String choice = selectedFile.getAbsolutePath();
1731 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1732 new Thread(new Runnable()
1737 setProgressBar(MessageManager.formatMessage(
1738 "label.loading_jalview_project", new Object[]
1739 { choice }), choice.hashCode());
1742 new Jalview2XML().loadJalviewAlign(choice);
1743 } catch (OutOfMemoryError oom)
1745 new OOMWarning("Whilst loading project from " + choice, oom);
1746 } catch (Exception ex)
1749 "Problems whilst loading project from " + choice, ex);
1750 JvOptionPane.showMessageDialog(Desktop.desktop,
1751 MessageManager.formatMessage(
1752 "label.error_whilst_loading_project_from",
1755 MessageManager.getString("label.couldnt_load_project"),
1756 JvOptionPane.WARNING_MESSAGE);
1758 setProgressBar(null, choice.hashCode());
1765 public void inputSequence_actionPerformed(ActionEvent e)
1767 new SequenceFetcher(this);
1770 JPanel progressPanel;
1772 ArrayList<JPanel> fileLoadingPanels = new ArrayList<>();
1774 public void startLoading(final Object fileName)
1776 if (fileLoadingCount == 0)
1778 fileLoadingPanels.add(addProgressPanel(MessageManager
1779 .formatMessage("label.loading_file", new Object[]
1785 private JPanel addProgressPanel(String string)
1787 if (progressPanel == null)
1789 progressPanel = new JPanel(new GridLayout(1, 1));
1790 totalProgressCount = 0;
1791 instance.getContentPane().add(progressPanel, BorderLayout.SOUTH);
1793 JPanel thisprogress = new JPanel(new BorderLayout(10, 5));
1794 JProgressBar progressBar = new JProgressBar();
1795 progressBar.setIndeterminate(true);
1797 thisprogress.add(new JLabel(string), BorderLayout.WEST);
1799 thisprogress.add(progressBar, BorderLayout.CENTER);
1800 progressPanel.add(thisprogress);
1801 ((GridLayout) progressPanel.getLayout()).setRows(
1802 ((GridLayout) progressPanel.getLayout()).getRows() + 1);
1803 ++totalProgressCount;
1804 instance.validate();
1805 return thisprogress;
1808 int totalProgressCount = 0;
1810 private void removeProgressPanel(JPanel progbar)
1812 if (progressPanel != null)
1814 synchronized (progressPanel)
1816 progressPanel.remove(progbar);
1817 GridLayout gl = (GridLayout) progressPanel.getLayout();
1818 gl.setRows(gl.getRows() - 1);
1819 if (--totalProgressCount < 1)
1821 this.getContentPane().remove(progressPanel);
1822 progressPanel = null;
1829 public void stopLoading()
1832 if (fileLoadingCount < 1)
1834 while (fileLoadingPanels.size() > 0)
1836 removeProgressPanel(fileLoadingPanels.remove(0));
1838 fileLoadingPanels.clear();
1839 fileLoadingCount = 0;
1844 public static int getViewCount(String alignmentId)
1846 AlignmentViewport[] aps = getViewports(alignmentId);
1847 return (aps == null) ? 0 : aps.length;
1852 * @param alignmentId
1853 * - if null, all sets are returned
1854 * @return all AlignmentPanels concerning the alignmentId sequence set
1856 public static AlignmentPanel[] getAlignmentPanels(String alignmentId)
1858 if (Desktop.desktop == null)
1860 // no frames created and in headless mode
1861 // TODO: verify that frames are recoverable when in headless mode
1864 List<AlignmentPanel> aps = new ArrayList<>();
1865 AlignFrame[] frames = getAlignFrames();
1870 for (AlignFrame af : frames)
1872 for (AlignmentPanel ap : af.alignPanels)
1874 if (alignmentId == null
1875 || alignmentId.equals(ap.av.getSequenceSetId()))
1881 if (aps.size() == 0)
1885 AlignmentPanel[] vap = aps.toArray(new AlignmentPanel[aps.size()]);
1890 * get all the viewports on an alignment.
1892 * @param sequenceSetId
1893 * unique alignment id (may be null - all viewports returned in that
1895 * @return all viewports on the alignment bound to sequenceSetId
1897 public static AlignmentViewport[] getViewports(String sequenceSetId)
1899 List<AlignmentViewport> viewp = new ArrayList<>();
1900 if (desktop != null)
1902 AlignFrame[] frames = Desktop.getAlignFrames();
1904 for (AlignFrame afr : frames)
1906 if (sequenceSetId == null || afr.getViewport().getSequenceSetId()
1907 .equals(sequenceSetId))
1909 if (afr.alignPanels != null)
1911 for (AlignmentPanel ap : afr.alignPanels)
1913 if (sequenceSetId == null
1914 || sequenceSetId.equals(ap.av.getSequenceSetId()))
1922 viewp.add(afr.getViewport());
1926 if (viewp.size() > 0)
1928 return viewp.toArray(new AlignmentViewport[viewp.size()]);
1935 * Explode the views in the given frame into separate AlignFrame
1939 public static void explodeViews(AlignFrame af)
1941 int size = af.alignPanels.size();
1947 for (int i = 0; i < size; i++)
1949 AlignmentPanel ap = af.alignPanels.get(i);
1950 AlignFrame newaf = new AlignFrame(ap);
1953 * Restore the view's last exploded frame geometry if known. Multiple
1954 * views from one exploded frame share and restore the same (frame)
1955 * position and size.
1957 Rectangle geometry = ap.av.getExplodedGeometry();
1958 if (geometry != null)
1960 newaf.setBounds(geometry);
1963 ap.av.setGatherViewsHere(false);
1965 addInternalFrame(newaf, af.getTitle(), AlignFrame.DEFAULT_WIDTH,
1966 AlignFrame.DEFAULT_HEIGHT);
1969 af.alignPanels.clear();
1970 af.closeMenuItem_actionPerformed(true);
1975 * Gather expanded views (separate AlignFrame's) with the same sequence set
1976 * identifier back in to this frame as additional views, and close the expanded
1977 * views. Note the expanded frames may themselves have multiple views. We take
1982 public void gatherViews(AlignFrame source)
1984 source.viewport.setGatherViewsHere(true);
1985 source.viewport.setExplodedGeometry(source.getBounds());
1986 JInternalFrame[] frames = desktop.getAllFrames();
1987 String viewId = source.viewport.getSequenceSetId();
1989 for (int t = 0; t < frames.length; t++)
1991 if (frames[t] instanceof AlignFrame && frames[t] != source)
1993 AlignFrame af = (AlignFrame) frames[t];
1994 boolean gatherThis = false;
1995 for (int a = 0; a < af.alignPanels.size(); a++)
1997 AlignmentPanel ap = af.alignPanels.get(a);
1998 if (viewId.equals(ap.av.getSequenceSetId()))
2001 ap.av.setGatherViewsHere(false);
2002 ap.av.setExplodedGeometry(af.getBounds());
2003 source.addAlignmentPanel(ap, false);
2009 af.alignPanels.clear();
2010 af.closeMenuItem_actionPerformed(true);
2017 jalview.gui.VamsasApplication v_client = null;
2020 public void vamsasImport_actionPerformed(ActionEvent e)
2022 // TODO: JAL-3048 not needed for Jalview-JS
2024 if (v_client == null)
2026 // Load and try to start a session.
2027 JalviewFileChooser chooser = new JalviewFileChooser(
2028 jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
2030 chooser.setFileView(new JalviewFileView());
2031 chooser.setDialogTitle(
2032 MessageManager.getString("label.open_saved_vamsas_session"));
2033 chooser.setToolTipText(MessageManager.getString(
2034 "label.select_vamsas_session_opened_as_new_vamsas_session"));
2036 int value = chooser.showOpenDialog(this);
2038 if (value == JalviewFileChooser.APPROVE_OPTION)
2040 String fle = chooser.getSelectedFile().toString();
2041 if (!vamsasImport(chooser.getSelectedFile()))
2043 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
2044 MessageManager.formatMessage(
2045 "label.couldnt_import_as_vamsas_session",
2049 .getString("label.vamsas_document_import_failed"),
2050 JvOptionPane.ERROR_MESSAGE);
2056 jalview.bin.Cache.log.error(
2057 "Implementation error - load session from a running session is not supported.");
2062 * import file into a new vamsas session (uses jalview.gui.VamsasApplication)
2065 * @return true if import was a success and a session was started.
2067 public boolean vamsasImport(URL url)
2069 // TODO: create progress bar
2070 if (v_client != null)
2073 jalview.bin.Cache.log.error(
2074 "Implementation error - load session from a running session is not supported.");
2080 // copy the URL content to a temporary local file
2081 // TODO: be a bit cleverer here with nio (?!)
2082 File file = File.createTempFile("vdocfromurl", ".vdj");
2083 FileOutputStream fos = new FileOutputStream(file);
2084 BufferedInputStream bis = new BufferedInputStream(url.openStream());
2085 byte[] buffer = new byte[2048];
2087 while ((ln = bis.read(buffer)) > -1)
2089 fos.write(buffer, 0, ln);
2093 v_client = new jalview.gui.VamsasApplication(this, file,
2094 url.toExternalForm());
2095 } catch (Exception ex)
2097 jalview.bin.Cache.log.error(
2098 "Failed to create new vamsas session from contents of URL "
2103 setupVamsasConnectedGui();
2104 v_client.initial_update(); // TODO: thread ?
2105 return v_client.inSession();
2109 * import file into a new vamsas session (uses jalview.gui.VamsasApplication)
2112 * @return true if import was a success and a session was started.
2114 public boolean vamsasImport(File file)
2116 if (v_client != null)
2119 jalview.bin.Cache.log.error(
2120 "Implementation error - load session from a running session is not supported.");
2124 setProgressBar(MessageManager.formatMessage(
2125 "status.importing_vamsas_session_from", new Object[]
2126 { file.getName() }), file.hashCode());
2129 v_client = new jalview.gui.VamsasApplication(this, file, null);
2130 } catch (Exception ex)
2132 setProgressBar(MessageManager.formatMessage(
2133 "status.importing_vamsas_session_from", new Object[]
2134 { file.getName() }), file.hashCode());
2135 jalview.bin.Cache.log.error(
2136 "New vamsas session from existing session file failed:", ex);
2139 setupVamsasConnectedGui();
2140 v_client.initial_update(); // TODO: thread ?
2141 setProgressBar(MessageManager.formatMessage(
2142 "status.importing_vamsas_session_from", new Object[]
2143 { file.getName() }), file.hashCode());
2144 return v_client.inSession();
2147 public boolean joinVamsasSession(String mysesid)
2149 if (v_client != null)
2151 throw new Error(MessageManager
2152 .getString("error.try_join_vamsas_session_another"));
2154 if (mysesid == null)
2157 MessageManager.getString("error.invalid_vamsas_session_id"));
2159 v_client = new VamsasApplication(this, mysesid);
2160 setupVamsasConnectedGui();
2161 v_client.initial_update();
2162 return (v_client.inSession());
2166 public void vamsasStart_actionPerformed(ActionEvent e)
2168 if (v_client == null)
2171 // we just start a default session for moment.
2173 * JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache.
2174 * getProperty("LAST_DIRECTORY"));
2176 * chooser.setFileView(new JalviewFileView());
2177 * chooser.setDialogTitle("Load Vamsas file");
2178 * chooser.setToolTipText("Import");
2180 * int value = chooser.showOpenDialog(this);
2182 * if (value == JalviewFileChooser.APPROVE_OPTION) { v_client = new
2183 * jalview.gui.VamsasApplication(this, chooser.getSelectedFile());
2185 v_client = new VamsasApplication(this);
2186 setupVamsasConnectedGui();
2187 v_client.initial_update(); // TODO: thread ?
2191 // store current data in session.
2192 v_client.push_update(); // TODO: thread
2196 protected void setupVamsasConnectedGui()
2198 vamsasStart.setText(MessageManager.getString("label.session_update"));
2199 vamsasSave.setVisible(true);
2200 vamsasStop.setVisible(true);
2201 vamsasImport.setVisible(false); // Document import to existing session is
2202 // not possible for vamsas-client-1.0.
2205 protected void setupVamsasDisconnectedGui()
2207 vamsasSave.setVisible(false);
2208 vamsasStop.setVisible(false);
2209 vamsasImport.setVisible(true);
2211 .setText(MessageManager.getString("label.new_vamsas_session"));
2215 public void vamsasStop_actionPerformed(ActionEvent e)
2217 if (v_client != null)
2219 v_client.end_session();
2221 setupVamsasDisconnectedGui();
2225 protected void buildVamsasStMenu()
2227 if (v_client == null)
2229 String[] sess = null;
2232 sess = VamsasApplication.getSessionList();
2233 } catch (Exception e)
2235 jalview.bin.Cache.log.warn("Problem getting current sessions list.",
2241 jalview.bin.Cache.log.debug(
2242 "Got current sessions list: " + sess.length + " entries.");
2243 VamsasStMenu.removeAll();
2244 for (int i = 0; i < sess.length; i++)
2246 JMenuItem sessit = new JMenuItem();
2247 sessit.setText(sess[i]);
2248 sessit.setToolTipText(MessageManager
2249 .formatMessage("label.connect_to_session", new Object[]
2251 final Desktop dsktp = this;
2252 final String mysesid = sess[i];
2253 sessit.addActionListener(new ActionListener()
2257 public void actionPerformed(ActionEvent e)
2259 if (dsktp.v_client == null)
2261 Thread rthr = new Thread(new Runnable()
2267 dsktp.v_client = new VamsasApplication(dsktp, mysesid);
2268 dsktp.setupVamsasConnectedGui();
2269 dsktp.v_client.initial_update();
2277 VamsasStMenu.add(sessit);
2279 // don't show an empty menu.
2280 VamsasStMenu.setVisible(sess.length > 0);
2285 jalview.bin.Cache.log.debug("No current vamsas sessions.");
2286 VamsasStMenu.removeAll();
2287 VamsasStMenu.setVisible(false);
2292 // Not interested in the content. Just hide ourselves.
2293 VamsasStMenu.setVisible(false);
2298 public void vamsasSave_actionPerformed(ActionEvent e)
2300 // TODO: JAL-3048 not needed for Jalview-JS
2302 if (v_client != null)
2304 // TODO: VAMSAS DOCUMENT EXTENSION is VDJ
2305 JalviewFileChooser chooser = new JalviewFileChooser("vdj",
2308 chooser.setFileView(new JalviewFileView());
2309 chooser.setDialogTitle(MessageManager
2310 .getString("label.save_vamsas_document_archive"));
2312 int value = chooser.showSaveDialog(this);
2314 if (value == JalviewFileChooser.APPROVE_OPTION)
2316 java.io.File choice = chooser.getSelectedFile();
2317 JPanel progpanel = addProgressPanel(MessageManager
2318 .formatMessage("label.saving_vamsas_doc", new Object[]
2319 { choice.getName() }));
2320 Cache.setProperty("LAST_DIRECTORY", choice.getParent());
2321 String warnmsg = null;
2322 String warnttl = null;
2325 v_client.vclient.storeDocument(choice);
2328 warnttl = "Serious Problem saving Vamsas Document";
2329 warnmsg = ex.toString();
2330 jalview.bin.Cache.log
2331 .error("Error Whilst saving document to " + choice, ex);
2333 } catch (Exception ex)
2335 warnttl = "Problem saving Vamsas Document.";
2336 warnmsg = ex.toString();
2337 jalview.bin.Cache.log.warn(
2338 "Exception Whilst saving document to " + choice, ex);
2341 removeProgressPanel(progpanel);
2342 if (warnmsg != null)
2344 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
2346 warnmsg, warnttl, JvOptionPane.ERROR_MESSAGE);
2352 JPanel vamUpdate = null;
2355 * hide vamsas user gui bits when a vamsas document event is being handled.
2358 * true to hide gui, false to reveal gui
2360 public void setVamsasUpdate(boolean b)
2362 Cache.log.debug("Setting gui for Vamsas update "
2363 + (b ? "in progress" : "finished"));
2365 if (vamUpdate != null)
2367 this.removeProgressPanel(vamUpdate);
2371 vamUpdate = this.addProgressPanel(
2372 MessageManager.getString("label.updating_vamsas_session"));
2374 vamsasStart.setVisible(!b);
2375 vamsasStop.setVisible(!b);
2376 vamsasSave.setVisible(!b);
2379 public JInternalFrame[] getAllFrames()
2381 return desktop.getAllFrames();
2385 * Checks the given url to see if it gives a response indicating that the user
2386 * should be informed of a new questionnaire.
2390 public void checkForQuestionnaire(String url)
2392 UserQuestionnaireCheck jvq = new UserQuestionnaireCheck(url);
2393 // javax.swing.SwingUtilities.invokeLater(jvq);
2394 new Thread(jvq).start();
2397 public void checkURLLinks()
2399 // Thread off the URL link checker
2400 addDialogThread(new Runnable()
2405 if (Cache.getDefault("CHECKURLLINKS", true))
2407 // check what the actual links are - if it's just the default don't
2408 // bother with the warning
2409 List<String> links = Preferences.sequenceUrlLinks
2412 // only need to check links if there is one with a
2413 // SEQUENCE_ID which is not the default EMBL_EBI link
2414 ListIterator<String> li = links.listIterator();
2415 boolean check = false;
2416 List<JLabel> urls = new ArrayList<>();
2417 while (li.hasNext())
2419 String link = li.next();
2420 if (link.contains(SEQUENCE_ID)
2421 && !UrlConstants.isDefaultString(link))
2424 int barPos = link.indexOf("|");
2425 String urlMsg = barPos == -1 ? link
2426 : link.substring(0, barPos) + ": "
2427 + link.substring(barPos + 1);
2428 urls.add(new JLabel(urlMsg));
2436 // ask user to check in case URL links use old style tokens
2437 // ($SEQUENCE_ID$ for sequence id _or_ accession id)
2438 JPanel msgPanel = new JPanel();
2439 msgPanel.setLayout(new BoxLayout(msgPanel, BoxLayout.PAGE_AXIS));
2440 msgPanel.add(Box.createVerticalGlue());
2441 JLabel msg = new JLabel(MessageManager
2442 .getString("label.SEQUENCE_ID_for_DB_ACCESSION1"));
2443 JLabel msg2 = new JLabel(MessageManager
2444 .getString("label.SEQUENCE_ID_for_DB_ACCESSION2"));
2446 for (JLabel url : urls)
2452 final JCheckBox jcb = new JCheckBox(
2453 MessageManager.getString("label.do_not_display_again"));
2454 jcb.addActionListener(new ActionListener()
2457 public void actionPerformed(ActionEvent e)
2459 // update Cache settings for "don't show this again"
2460 boolean showWarningAgain = !jcb.isSelected();
2461 Cache.setProperty("CHECKURLLINKS",
2462 Boolean.valueOf(showWarningAgain).toString());
2467 JvOptionPane.showMessageDialog(Desktop.desktop, msgPanel,
2469 .getString("label.SEQUENCE_ID_no_longer_used"),
2470 JvOptionPane.WARNING_MESSAGE);
2477 * Proxy class for JDesktopPane which optionally displays the current memory
2478 * usage and highlights the desktop area with a red bar if free memory runs low.
2482 public class MyDesktopPane extends JDesktopPane
2485 private static final float ONE_MB = 1048576f;
2487 boolean showMemoryUsage = false;
2491 java.text.NumberFormat df;
2493 float maxMemory, allocatedMemory, freeMemory, totalFreeMemory,
2496 public MyDesktopPane(boolean showMemoryUsage)
2498 showMemoryUsage(showMemoryUsage);
2501 public void showMemoryUsage(boolean showMemory)
2503 this.showMemoryUsage = showMemory;
2506 Thread worker = new Thread(this);
2512 public boolean isShowMemoryUsage()
2514 return showMemoryUsage;
2520 df = java.text.NumberFormat.getNumberInstance();
2521 df.setMaximumFractionDigits(2);
2522 runtime = Runtime.getRuntime();
2524 while (showMemoryUsage)
2528 maxMemory = runtime.maxMemory() / ONE_MB;
2529 allocatedMemory = runtime.totalMemory() / ONE_MB;
2530 freeMemory = runtime.freeMemory() / ONE_MB;
2531 totalFreeMemory = freeMemory + (maxMemory - allocatedMemory);
2533 percentUsage = (totalFreeMemory / maxMemory) * 100;
2535 // if (percentUsage < 20)
2537 // border1 = BorderFactory.createMatteBorder(12, 12, 12, 12,
2539 // instance.set.setBorder(border1);
2542 // sleep after showing usage
2544 } catch (Exception ex)
2546 ex.printStackTrace();
2552 public void paintComponent(Graphics g)
2554 if (showMemoryUsage && g != null && df != null)
2556 if (percentUsage < 20)
2558 g.setColor(Color.red);
2560 FontMetrics fm = g.getFontMetrics();
2563 g.drawString(MessageManager.formatMessage("label.memory_stats",
2565 { df.format(totalFreeMemory), df.format(maxMemory),
2566 df.format(percentUsage) }),
2567 10, getHeight() - fm.getHeight());
2574 * Accessor method to quickly get all the AlignmentFrames loaded.
2576 * @return an array of AlignFrame, or null if none found
2578 public static AlignFrame[] getAlignFrames()
2580 if (Jalview.isHeadlessMode())
2582 // Desktop.desktop is null in headless mode
2583 return new AlignFrame[] { Jalview.currentAlignFrame };
2586 JInternalFrame[] frames = Desktop.desktop.getAllFrames();
2592 List<AlignFrame> avp = new ArrayList<>();
2594 for (int i = frames.length - 1; i > -1; i--)
2596 if (frames[i] instanceof AlignFrame)
2598 avp.add((AlignFrame) frames[i]);
2600 else if (frames[i] instanceof SplitFrame)
2603 * Also check for a split frame containing an AlignFrame
2605 GSplitFrame sf = (GSplitFrame) frames[i];
2606 if (sf.getTopFrame() instanceof AlignFrame)
2608 avp.add((AlignFrame) sf.getTopFrame());
2610 if (sf.getBottomFrame() instanceof AlignFrame)
2612 avp.add((AlignFrame) sf.getBottomFrame());
2616 if (avp.size() == 0)
2620 AlignFrame afs[] = avp.toArray(new AlignFrame[avp.size()]);
2625 * Returns an array of any AppJmol frames in the Desktop (or null if none).
2629 public GStructureViewer[] getJmols()
2631 JInternalFrame[] frames = Desktop.desktop.getAllFrames();
2637 List<GStructureViewer> avp = new ArrayList<>();
2639 for (int i = frames.length - 1; i > -1; i--)
2641 if (frames[i] instanceof AppJmol)
2643 GStructureViewer af = (GStructureViewer) frames[i];
2647 if (avp.size() == 0)
2651 GStructureViewer afs[] = avp.toArray(new GStructureViewer[avp.size()]);
2656 * Add Groovy Support to Jalview
2659 public void groovyShell_actionPerformed()
2663 openGroovyConsole();
2664 } catch (Exception ex)
2666 jalview.bin.Cache.log.error("Groovy Shell Creation failed.", ex);
2667 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
2669 MessageManager.getString("label.couldnt_create_groovy_shell"),
2670 MessageManager.getString("label.groovy_support_failed"),
2671 JvOptionPane.ERROR_MESSAGE);
2676 * Open the Groovy console
2678 void openGroovyConsole()
2680 if (groovyConsole == null)
2682 groovyConsole = new groovy.ui.Console();
2683 groovyConsole.setVariable("Jalview", this);
2684 groovyConsole.run();
2687 * We allow only one console at a time, so that AlignFrame menu option
2688 * 'Calculate | Run Groovy script' is unambiguous.
2689 * Disable 'Groovy Console', and enable 'Run script', when the console is
2690 * opened, and the reverse when it is closed
2692 Window window = (Window) groovyConsole.getFrame();
2693 window.addWindowListener(new WindowAdapter()
2696 public void windowClosed(WindowEvent e)
2699 * rebind CMD-Q from Groovy Console to Jalview Quit
2702 enableExecuteGroovy(false);
2708 * show Groovy console window (after close and reopen)
2710 ((Window) groovyConsole.getFrame()).setVisible(true);
2713 * if we got this far, enable 'Run Groovy' in AlignFrame menus
2714 * and disable opening a second console
2716 enableExecuteGroovy(true);
2720 * Bind Ctrl/Cmd-Q to Quit - for reset as Groovy Console takes over this binding
2723 protected void addQuitHandler()
2725 getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
2726 .put(KeyStroke.getKeyStroke(KeyEvent.VK_Q,
2727 Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()),
2729 getRootPane().getActionMap().put("Quit", new AbstractAction()
2732 public void actionPerformed(ActionEvent e)
2740 * Enable or disable 'Run Groovy script' in AlignFrame calculate menus
2743 * true if Groovy console is open
2745 public void enableExecuteGroovy(boolean enabled)
2748 * disable opening a second Groovy console
2749 * (or re-enable when the console is closed)
2751 groovyShell.setEnabled(!enabled);
2753 AlignFrame[] alignFrames = getAlignFrames();
2754 if (alignFrames != null)
2756 for (AlignFrame af : alignFrames)
2758 af.setGroovyEnabled(enabled);
2764 * Progress bars managed by the IProgressIndicator method.
2766 private Hashtable<Long, JPanel> progressBars;
2768 private Hashtable<Long, IProgressIndicatorHandler> progressBarHandlers;
2773 * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
2776 public void setProgressBar(String message, long id)
2778 if (progressBars == null)
2780 progressBars = new Hashtable<>();
2781 progressBarHandlers = new Hashtable<>();
2784 if (progressBars.get(new Long(id)) != null)
2786 JPanel panel = progressBars.remove(new Long(id));
2787 if (progressBarHandlers.contains(new Long(id)))
2789 progressBarHandlers.remove(new Long(id));
2791 removeProgressPanel(panel);
2795 progressBars.put(new Long(id), addProgressPanel(message));
2802 * @see jalview.gui.IProgressIndicator#registerHandler(long,
2803 * jalview.gui.IProgressIndicatorHandler)
2806 public void registerHandler(final long id,
2807 final IProgressIndicatorHandler handler)
2809 if (progressBarHandlers == null
2810 || !progressBars.containsKey(new Long(id)))
2812 throw new Error(MessageManager.getString(
2813 "error.call_setprogressbar_before_registering_handler"));
2815 progressBarHandlers.put(new Long(id), handler);
2816 final JPanel progressPanel = progressBars.get(new Long(id));
2817 if (handler.canCancel())
2819 JButton cancel = new JButton(
2820 MessageManager.getString("action.cancel"));
2821 final IProgressIndicator us = this;
2822 cancel.addActionListener(new ActionListener()
2826 public void actionPerformed(ActionEvent e)
2828 handler.cancelActivity(id);
2829 us.setProgressBar(MessageManager
2830 .formatMessage("label.cancelled_params", new Object[]
2831 { ((JLabel) progressPanel.getComponent(0)).getText() }),
2835 progressPanel.add(cancel, BorderLayout.EAST);
2841 * @return true if any progress bars are still active
2844 public boolean operationInProgress()
2846 if (progressBars != null && progressBars.size() > 0)
2854 * This will return the first AlignFrame holding the given viewport instance. It
2855 * will break if there are more than one AlignFrames viewing a particular av.
2858 * @return alignFrame for viewport
2860 public static AlignFrame getAlignFrameFor(AlignViewportI viewport)
2862 if (desktop != null)
2864 AlignmentPanel[] aps = getAlignmentPanels(
2865 viewport.getSequenceSetId());
2866 for (int panel = 0; aps != null && panel < aps.length; panel++)
2868 if (aps[panel] != null && aps[panel].av == viewport)
2870 return aps[panel].alignFrame;
2877 public VamsasApplication getVamsasApplication()
2884 * flag set if jalview GUI is being operated programmatically
2886 private boolean inBatchMode = false;
2889 * check if jalview GUI is being operated programmatically
2891 * @return inBatchMode
2893 public boolean isInBatchMode()
2899 * set flag if jalview GUI is being operated programmatically
2901 * @param inBatchMode
2903 public void setInBatchMode(boolean inBatchMode)
2905 this.inBatchMode = inBatchMode;
2908 public void startServiceDiscovery()
2910 startServiceDiscovery(false);
2913 public void startServiceDiscovery(boolean blocking)
2915 boolean alive = true;
2916 Thread t0 = null, t1 = null, t2 = null;
2917 // JAL-940 - JALVIEW 1 services are now being EOLed as of JABA 2.1 release
2920 // todo: changesupport handlers need to be transferred
2921 if (discoverer == null)
2923 discoverer = new jalview.ws.jws1.Discoverer();
2924 // register PCS handler for desktop.
2925 discoverer.addPropertyChangeListener(changeSupport);
2927 // JAL-940 - disabled JWS1 service configuration - always start discoverer
2928 // until we phase out completely
2929 (t0 = new Thread(discoverer)).start();
2932 if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
2934 t2 = jalview.ws.jws2.Jws2Discoverer.getDiscoverer()
2935 .startDiscoverer(changeSupport);
2939 // TODO: do rest service discovery
2948 } catch (Exception e)
2951 alive = (t1 != null && t1.isAlive()) || (t2 != null && t2.isAlive())
2952 || (t3 != null && t3.isAlive())
2953 || (t0 != null && t0.isAlive());
2959 * called to check if the service discovery process completed successfully.
2963 protected void JalviewServicesChanged(PropertyChangeEvent evt)
2965 if (evt.getNewValue() == null || evt.getNewValue() instanceof Vector)
2967 final String ermsg = jalview.ws.jws2.Jws2Discoverer.getDiscoverer()
2968 .getErrorMessages();
2971 if (Cache.getDefault("SHOW_WSDISCOVERY_ERRORS", true))
2973 if (serviceChangedDialog == null)
2975 // only run if we aren't already displaying one of these.
2976 addDialogThread(serviceChangedDialog = new Runnable()
2983 * JalviewDialog jd =new JalviewDialog() {
2985 * @Override protected void cancelPressed() { // TODO
2986 * Auto-generated method stub
2988 * }@Override protected void okPressed() { // TODO
2989 * Auto-generated method stub
2991 * }@Override protected void raiseClosed() { // TODO
2992 * Auto-generated method stub
2994 * } }; jd.initDialogFrame(new
2995 * JLabel("<html><table width=\"450\"><tr><td>" + ermsg +
2996 * "<br/>It may be that you have invalid JABA URLs in your web service preferences,"
2997 * + " or mis-configured HTTP proxy settings.<br/>" +
2998 * "Check the <em>Connections</em> and <em>Web services</em> tab of the"
3000 * " Tools->Preferences dialog box to change them.</td></tr></table></html>"
3001 * ), true, true, "Web Service Configuration Problem", 450,
3004 * jd.waitForInput();
3006 JvOptionPane.showConfirmDialog(Desktop.desktop,
3007 new JLabel("<html><table width=\"450\"><tr><td>"
3008 + ermsg + "</td></tr></table>"
3009 + "<p>It may be that you have invalid JABA URLs<br/>in your web service preferences,"
3010 + "<br>or as a command-line argument, or mis-configured HTTP proxy settings.</p>"
3011 + "<p>Check the <em>Connections</em> and <em>Web services</em> tab<br/>of the"
3012 + " Tools->Preferences dialog box to change them.</p></html>"),
3013 "Web Service Configuration Problem",
3014 JvOptionPane.DEFAULT_OPTION,
3015 JvOptionPane.ERROR_MESSAGE);
3016 serviceChangedDialog = null;
3025 "Errors reported by JABA discovery service. Check web services preferences.\n"
3032 private Runnable serviceChangedDialog = null;
3035 * start a thread to open a URL in the configured browser. Pops up a warning
3036 * dialog to the user if there is an exception when calling out to the browser
3041 public static void showUrl(final String url)
3043 showUrl(url, Desktop.instance);
3047 * Like showUrl but allows progress handler to be specified
3051 * (null) or object implementing IProgressIndicator
3053 public static void showUrl(final String url,
3054 final IProgressIndicator progress)
3056 new Thread(new Runnable()
3063 if (progress != null)
3065 progress.setProgressBar(MessageManager
3066 .formatMessage("status.opening_params", new Object[]
3067 { url }), this.hashCode());
3069 jalview.util.BrowserLauncher.openURL(url);
3070 } catch (Exception ex)
3072 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
3074 .getString("label.web_browser_not_found_unix"),
3075 MessageManager.getString("label.web_browser_not_found"),
3076 JvOptionPane.WARNING_MESSAGE);
3078 ex.printStackTrace();
3080 if (progress != null)
3082 progress.setProgressBar(null, this.hashCode());
3088 public static WsParamSetManager wsparamManager = null;
3090 public static ParamManager getUserParameterStore()
3092 if (wsparamManager == null)
3094 wsparamManager = new WsParamSetManager();
3096 return wsparamManager;
3100 * static hyperlink handler proxy method for use by Jalview's internal windows
3104 public static void hyperlinkUpdate(HyperlinkEvent e)
3106 if (e.getEventType() == EventType.ACTIVATED)
3111 url = e.getURL().toString();
3112 Desktop.showUrl(url);
3113 } catch (Exception x)
3117 if (Cache.log != null)
3119 Cache.log.error("Couldn't handle string " + url + " as a URL.");
3124 "Couldn't handle string " + url + " as a URL.");
3127 // ignore any exceptions due to dud links.
3134 * single thread that handles display of dialogs to user.
3136 ExecutorService dialogExecutor = Executors.newSingleThreadExecutor();
3139 * flag indicating if dialogExecutor should try to acquire a permit
3141 private volatile boolean dialogPause = true;
3146 private java.util.concurrent.Semaphore block = new Semaphore(0);
3148 private static groovy.ui.Console groovyConsole;
3151 * add another dialog thread to the queue
3155 public void addDialogThread(final Runnable prompter)
3157 dialogExecutor.submit(new Runnable()
3167 } catch (InterruptedException x)
3172 if (instance == null)
3178 SwingUtilities.invokeAndWait(prompter);
3179 } catch (Exception q)
3181 Cache.log.warn("Unexpected Exception in dialog thread.", q);
3187 public void startDialogQueue()
3189 // set the flag so we don't pause waiting for another permit and semaphore
3190 // the current task to begin
3191 dialogPause = false;
3196 * Outputs an image of the desktop to file in EPS format, after prompting the
3197 * user for choice of Text or Lineart character rendering (unless a preference
3198 * has been set). The file name is generated as
3201 * Jalview_snapshot_nnnnn.eps where nnnnn is the current timestamp in milliseconds
3205 protected void snapShotWindow_actionPerformed(ActionEvent e)
3207 // currently the menu option to do this is not shown
3210 int width = getWidth();
3211 int height = getHeight();
3213 "Jalview_snapshot_" + System.currentTimeMillis() + ".eps");
3214 ImageWriterI writer = new ImageWriterI()
3217 public void exportImage(Graphics g) throws Exception
3220 Cache.log.info("Successfully written snapshot to file "
3221 + of.getAbsolutePath());
3224 String title = "View of desktop";
3225 ImageExporter exporter = new ImageExporter(writer, null, TYPE.EPS,
3227 exporter.doExport(of, this, width, height, title);
3231 * Explode the views in the given SplitFrame into separate SplitFrame windows.
3232 * This respects (remembers) any previous 'exploded geometry' i.e. the size and
3233 * location last time the view was expanded (if any). However it does not
3234 * remember the split pane divider location - this is set to match the
3235 * 'exploding' frame.
3239 public void explodeViews(SplitFrame sf)
3241 AlignFrame oldTopFrame = (AlignFrame) sf.getTopFrame();
3242 AlignFrame oldBottomFrame = (AlignFrame) sf.getBottomFrame();
3243 List<? extends AlignmentViewPanel> topPanels = oldTopFrame
3245 List<? extends AlignmentViewPanel> bottomPanels = oldBottomFrame
3247 int viewCount = topPanels.size();
3254 * Processing in reverse order works, forwards order leaves the first panels
3255 * not visible. I don't know why!
3257 for (int i = viewCount - 1; i >= 0; i--)
3260 * Make new top and bottom frames. These take over the respective
3261 * AlignmentPanel objects, including their AlignmentViewports, so the
3262 * cdna/protein relationships between the viewports is carried over to the
3265 * explodedGeometry holds the (x, y) position of the previously exploded
3266 * SplitFrame, and the (width, height) of the AlignFrame component
3268 AlignmentPanel topPanel = (AlignmentPanel) topPanels.get(i);
3269 AlignFrame newTopFrame = new AlignFrame(topPanel);
3270 newTopFrame.setSize(oldTopFrame.getSize());
3271 newTopFrame.setVisible(true);
3272 Rectangle geometry = ((AlignViewport) topPanel.getAlignViewport())
3273 .getExplodedGeometry();
3274 if (geometry != null)
3276 newTopFrame.setSize(geometry.getSize());
3279 AlignmentPanel bottomPanel = (AlignmentPanel) bottomPanels.get(i);
3280 AlignFrame newBottomFrame = new AlignFrame(bottomPanel);
3281 newBottomFrame.setSize(oldBottomFrame.getSize());
3282 newBottomFrame.setVisible(true);
3283 geometry = ((AlignViewport) bottomPanel.getAlignViewport())
3284 .getExplodedGeometry();
3285 if (geometry != null)
3287 newBottomFrame.setSize(geometry.getSize());
3290 topPanel.av.setGatherViewsHere(false);
3291 bottomPanel.av.setGatherViewsHere(false);
3292 JInternalFrame splitFrame = new SplitFrame(newTopFrame,
3294 if (geometry != null)
3296 splitFrame.setLocation(geometry.getLocation());
3298 Desktop.addInternalFrame(splitFrame, sf.getTitle(), -1, -1);
3302 * Clear references to the panels (now relocated in the new SplitFrames)
3303 * before closing the old SplitFrame.
3306 bottomPanels.clear();
3311 * Gather expanded split frames, sharing the same pairs of sequence set ids,
3312 * back into the given SplitFrame as additional views. Note that the gathered
3313 * frames may themselves have multiple views.
3317 public void gatherViews(GSplitFrame source)
3320 * special handling of explodedGeometry for a view within a SplitFrame: - it
3321 * holds the (x, y) position of the enclosing SplitFrame, and the (width,
3322 * height) of the AlignFrame component
3324 AlignFrame myTopFrame = (AlignFrame) source.getTopFrame();
3325 AlignFrame myBottomFrame = (AlignFrame) source.getBottomFrame();
3326 myTopFrame.viewport.setExplodedGeometry(new Rectangle(source.getX(),
3327 source.getY(), myTopFrame.getWidth(), myTopFrame.getHeight()));
3328 myBottomFrame.viewport
3329 .setExplodedGeometry(new Rectangle(source.getX(), source.getY(),
3330 myBottomFrame.getWidth(), myBottomFrame.getHeight()));
3331 myTopFrame.viewport.setGatherViewsHere(true);
3332 myBottomFrame.viewport.setGatherViewsHere(true);
3333 String topViewId = myTopFrame.viewport.getSequenceSetId();
3334 String bottomViewId = myBottomFrame.viewport.getSequenceSetId();
3336 JInternalFrame[] frames = desktop.getAllFrames();
3337 for (JInternalFrame frame : frames)
3339 if (frame instanceof SplitFrame && frame != source)
3341 SplitFrame sf = (SplitFrame) frame;
3342 AlignFrame topFrame = (AlignFrame) sf.getTopFrame();
3343 AlignFrame bottomFrame = (AlignFrame) sf.getBottomFrame();
3344 boolean gatherThis = false;
3345 for (int a = 0; a < topFrame.alignPanels.size(); a++)
3347 AlignmentPanel topPanel = topFrame.alignPanels.get(a);
3348 AlignmentPanel bottomPanel = bottomFrame.alignPanels.get(a);
3349 if (topViewId.equals(topPanel.av.getSequenceSetId())
3350 && bottomViewId.equals(bottomPanel.av.getSequenceSetId()))
3353 topPanel.av.setGatherViewsHere(false);
3354 bottomPanel.av.setGatherViewsHere(false);
3355 topPanel.av.setExplodedGeometry(
3356 new Rectangle(sf.getLocation(), topFrame.getSize()));
3357 bottomPanel.av.setExplodedGeometry(
3358 new Rectangle(sf.getLocation(), bottomFrame.getSize()));
3359 myTopFrame.addAlignmentPanel(topPanel, false);
3360 myBottomFrame.addAlignmentPanel(bottomPanel, false);
3366 topFrame.getAlignPanels().clear();
3367 bottomFrame.getAlignPanels().clear();
3374 * The dust settles...give focus to the tab we did this from.
3376 myTopFrame.setDisplayedView(myTopFrame.alignPanel);
3379 public static groovy.ui.Console getGroovyConsole()
3381 return groovyConsole;
3385 * handles the payload of a drag and drop event.
3387 * TODO refactor to desktop utilities class
3390 * - Data source strings extracted from the drop event
3392 * - protocol for each data source extracted from the drop event
3396 * - the payload from the drop event
3399 public static void transferFromDropTarget(List<Object> files,
3400 List<DataSourceType> protocols, DropTargetDropEvent evt,
3401 Transferable t) throws Exception
3404 // BH 2018 changed List<String> to List<Object> to allow for File from SwingJS
3406 // DataFlavor[] flavors = t.getTransferDataFlavors();
3407 // for (int i = 0; i < flavors.length; i++) {
3408 // if (flavors[i].isFlavorJavaFileListType()) {
3409 // evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
3410 // List<File> list = (List<File>) t.getTransferData(flavors[i]);
3411 // for (int j = 0; j < list.size(); j++) {
3412 // File file = (File) list.get(j);
3413 // byte[] data = getDroppedFileBytes(file);
3414 // fileName.setText(file.getName() + " - " + data.length + " " +
3415 // evt.getLocation());
3416 // JTextArea target = (JTextArea) ((DropTarget) evt.getSource()).getComponent();
3417 // target.setText(new String(data));
3419 // dtde.dropComplete(true);
3424 DataFlavor uriListFlavor = new DataFlavor(
3425 "text/uri-list;class=java.lang.String"), urlFlavour = null;
3428 urlFlavour = new DataFlavor(
3429 "application/x-java-url; class=java.net.URL");
3430 } catch (ClassNotFoundException cfe)
3432 Cache.log.debug("Couldn't instantiate the URL dataflavor.", cfe);
3435 if (urlFlavour != null && t.isDataFlavorSupported(urlFlavour))
3440 java.net.URL url = (URL) t.getTransferData(urlFlavour);
3441 // nb: java 8 osx bug https://bugs.openjdk.java.net/browse/JDK-8156099
3442 // means url may be null.
3445 protocols.add(DataSourceType.URL);
3446 files.add(url.toString());
3447 Cache.log.debug("Drop handled as URL dataflavor "
3448 + files.get(files.size() - 1));
3453 if (Platform.isAMac())
3456 "Please ignore plist error - occurs due to problem with java 8 on OSX");
3460 } catch (Throwable ex)
3462 Cache.log.debug("URL drop handler failed.", ex);
3465 if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
3467 // Works on Windows and MacOSX
3468 Cache.log.debug("Drop handled as javaFileListFlavor");
3469 for (Object file : (List) t
3470 .getTransferData(DataFlavor.javaFileListFlavor))
3473 protocols.add(DataSourceType.FILE);
3478 // Unix like behaviour
3479 boolean added = false;
3481 if (t.isDataFlavorSupported(uriListFlavor))
3483 Cache.log.debug("Drop handled as uriListFlavor");
3484 // This is used by Unix drag system
3485 data = (String) t.getTransferData(uriListFlavor);
3489 // fallback to text: workaround - on OSX where there's a JVM bug
3490 Cache.log.debug("standard URIListFlavor failed. Trying text");
3491 // try text fallback
3492 DataFlavor textDf = new DataFlavor(
3493 "text/plain;class=java.lang.String");
3494 if (t.isDataFlavorSupported(textDf))
3496 data = (String) t.getTransferData(textDf);
3499 Cache.log.debug("Plain text drop content returned "
3500 + (data == null ? "Null - failed" : data));
3505 while (protocols.size() < files.size())
3507 Cache.log.debug("Adding missing FILE protocol for "
3508 + files.get(protocols.size()));
3509 protocols.add(DataSourceType.FILE);
3511 for (java.util.StringTokenizer st = new java.util.StringTokenizer(
3512 data, "\r\n"); st.hasMoreTokens();)
3515 String s = st.nextToken();
3516 if (s.startsWith("#"))
3518 // the line is a comment (as per the RFC 2483)
3521 java.net.URI uri = new java.net.URI(s);
3522 if (uri.getScheme().toLowerCase().startsWith("http"))
3524 protocols.add(DataSourceType.URL);
3525 files.add(uri.toString());
3529 // otherwise preserve old behaviour: catch all for file objects
3530 java.io.File file = new java.io.File(uri);
3531 protocols.add(DataSourceType.FILE);
3532 files.add(file.toString());
3537 if (Cache.log.isDebugEnabled())
3539 if (data == null || !added)
3542 if (t.getTransferDataFlavors() != null
3543 && t.getTransferDataFlavors().length > 0)
3546 "Couldn't resolve drop data. Here are the supported flavors:");
3547 for (DataFlavor fl : t.getTransferDataFlavors())
3550 "Supported transfer dataflavor: " + fl.toString());
3551 Object df = t.getTransferData(fl);
3554 Cache.log.debug("Retrieves: " + df);
3558 Cache.log.debug("Retrieved nothing");
3564 Cache.log.debug("Couldn't resolve dataflavor for drop: "
3570 if (Platform.isWindows())
3573 Cache.log.debug("Scanning dropped content for Windows Link Files");
3575 // resolve any .lnk files in the file drop
3576 for (int f = 0; f < files.size(); f++)
3578 String source = files.get(f).toString().toLowerCase();
3579 if (protocols.get(f).equals(DataSourceType.FILE)
3580 && (source.endsWith(".lnk") || source.endsWith(".url")
3581 || source.endsWith(".site")))
3585 Object obj = files.get(f);
3586 File lf = (obj instanceof File ? (File) obj
3587 : new File((String) obj));
3588 // process link file to get a URL
3589 Cache.log.debug("Found potential link file: " + lf);
3590 WindowsShortcut wscfile = new WindowsShortcut(lf);
3591 String fullname = wscfile.getRealFilename();
3592 protocols.set(f, FormatAdapter.checkProtocol(fullname));
3593 files.set(f, fullname);
3594 Cache.log.debug("Parsed real filename " + fullname
3595 + " to extract protocol: " + protocols.get(f));
3596 } catch (Exception ex)
3599 "Couldn't parse " + files.get(f) + " as a link file.",
3608 * Sets the Preferences property for experimental features to True or False
3609 * depending on the state of the controlling menu item
3612 protected void showExperimental_actionPerformed(boolean selected)
3614 Cache.setProperty(EXPERIMENTAL_FEATURES, Boolean.toString(selected));
3618 * Answers a (possibly empty) list of any structure viewer frames (currently for
3619 * either Jmol or Chimera) which are currently open. This may optionally be
3620 * restricted to viewers of a specified class, or viewers linked to a specified
3624 * if not null, only return viewers linked to this panel
3625 * @param structureViewerClass
3626 * if not null, only return viewers of this class
3629 public List<StructureViewerBase> getStructureViewers(
3630 AlignmentPanel apanel,
3631 Class<? extends StructureViewerBase> structureViewerClass)
3633 List<StructureViewerBase> result = new ArrayList<>();
3634 JInternalFrame[] frames = Desktop.instance.getAllFrames();
3636 for (JInternalFrame frame : frames)
3638 if (frame instanceof StructureViewerBase)
3640 if (structureViewerClass == null
3641 || structureViewerClass.isInstance(frame))
3644 || ((StructureViewerBase) frame).isLinkedWith(apanel))
3646 result.add((StructureViewerBase) frame);