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.io.DataSourceType;
30 import jalview.io.FileFormat;
31 import jalview.io.FileFormatException;
32 import jalview.io.FileFormatI;
33 import jalview.io.FileFormats;
34 import jalview.io.FileLoader;
35 import jalview.io.FormatAdapter;
36 import jalview.io.IdentifyFile;
37 import jalview.io.JalviewFileChooser;
38 import jalview.io.JalviewFileView;
39 import jalview.jbgui.GSplitFrame;
40 import jalview.jbgui.GStructureViewer;
41 import jalview.structure.StructureSelectionManager;
42 import jalview.urls.IdOrgSettings;
43 import jalview.util.ImageMaker;
44 import jalview.util.MessageManager;
45 import jalview.util.Platform;
46 import jalview.util.UrlConstants;
47 import jalview.util.dialogrunner.RunResponse;
48 import jalview.viewmodel.AlignmentViewport;
49 import jalview.ws.params.ParamManager;
50 import jalview.ws.utils.UrlDownloadClient;
52 import java.awt.BorderLayout;
53 import java.awt.Color;
54 import java.awt.Dimension;
55 import java.awt.FontMetrics;
56 import java.awt.Graphics;
57 import java.awt.GridLayout;
58 import java.awt.Point;
59 import java.awt.Rectangle;
60 import java.awt.Toolkit;
61 import java.awt.Window;
62 import java.awt.datatransfer.Clipboard;
63 import java.awt.datatransfer.ClipboardOwner;
64 import java.awt.datatransfer.DataFlavor;
65 import java.awt.datatransfer.Transferable;
66 import java.awt.dnd.DnDConstants;
67 import java.awt.dnd.DropTargetDragEvent;
68 import java.awt.dnd.DropTargetDropEvent;
69 import java.awt.dnd.DropTargetEvent;
70 import java.awt.dnd.DropTargetListener;
71 import java.awt.event.ActionEvent;
72 import java.awt.event.ActionListener;
73 import java.awt.event.InputEvent;
74 import java.awt.event.KeyEvent;
75 import java.awt.event.MouseAdapter;
76 import java.awt.event.MouseEvent;
77 import java.awt.event.WindowAdapter;
78 import java.awt.event.WindowEvent;
79 import java.beans.PropertyChangeEvent;
80 import java.beans.PropertyChangeListener;
81 import java.io.BufferedInputStream;
83 import java.io.FileOutputStream;
84 import java.io.IOException;
86 import java.util.ArrayList;
87 import java.util.Hashtable;
88 import java.util.List;
89 import java.util.ListIterator;
90 import java.util.StringTokenizer;
91 import java.util.Vector;
92 import java.util.concurrent.ExecutorService;
93 import java.util.concurrent.Executors;
94 import java.util.concurrent.Semaphore;
96 import javax.swing.AbstractAction;
97 import javax.swing.Action;
98 import javax.swing.ActionMap;
99 import javax.swing.Box;
100 import javax.swing.BoxLayout;
101 import javax.swing.DefaultDesktopManager;
102 import javax.swing.DesktopManager;
103 import javax.swing.InputMap;
104 import javax.swing.JButton;
105 import javax.swing.JCheckBox;
106 import javax.swing.JComboBox;
107 import javax.swing.JComponent;
108 import javax.swing.JDesktopPane;
109 import javax.swing.JFrame;
110 import javax.swing.JInternalFrame;
111 import javax.swing.JLabel;
112 import javax.swing.JMenuItem;
113 import javax.swing.JPanel;
114 import javax.swing.JPopupMenu;
115 import javax.swing.JProgressBar;
116 import javax.swing.KeyStroke;
117 import javax.swing.SwingUtilities;
118 import javax.swing.event.HyperlinkEvent;
119 import javax.swing.event.HyperlinkEvent.EventType;
120 import javax.swing.event.InternalFrameAdapter;
121 import javax.swing.event.InternalFrameEvent;
122 import javax.swing.event.MenuEvent;
123 import javax.swing.event.MenuListener;
125 import org.stackoverflowusers.file.WindowsShortcut;
132 * @version $Revision: 1.155 $
134 public class Desktop extends jalview.jbgui.GDesktop
135 implements DropTargetListener, ClipboardOwner, IProgressIndicator,
136 jalview.api.StructureSelectionManagerProvider
138 private static int DEFAULT_MIN_WIDTH = 300;
140 private static int DEFAULT_MIN_HEIGHT = 250;
142 private static int ALIGN_FRAME_DEFAULT_MIN_WIDTH = 600;
144 private static int ALIGN_FRAME_DEFAULT_MIN_HEIGHT = 70;
146 private static final String EXPERIMENTAL_FEATURES = "EXPERIMENTAL_FEATURES";
148 private JalviewChangeSupport changeSupport = new JalviewChangeSupport();
151 * news reader - null if it was never started.
153 private BlogReader jvnews = null;
155 private File projectFile;
159 * @see jalview.gui.JalviewChangeSupport#addJalviewPropertyChangeListener(java.beans.PropertyChangeListener)
161 public void addJalviewPropertyChangeListener(
162 PropertyChangeListener listener)
164 changeSupport.addJalviewPropertyChangeListener(listener);
168 * @param propertyName
170 * @see jalview.gui.JalviewChangeSupport#addJalviewPropertyChangeListener(java.lang.String,
171 * java.beans.PropertyChangeListener)
173 public void addJalviewPropertyChangeListener(String propertyName,
174 PropertyChangeListener listener)
176 changeSupport.addJalviewPropertyChangeListener(propertyName, listener);
180 * @param propertyName
182 * @see jalview.gui.JalviewChangeSupport#removeJalviewPropertyChangeListener(java.lang.String,
183 * java.beans.PropertyChangeListener)
185 public void removeJalviewPropertyChangeListener(String propertyName,
186 PropertyChangeListener listener)
188 changeSupport.removeJalviewPropertyChangeListener(propertyName,
192 /** Singleton Desktop instance */
193 public static Desktop instance;
195 public static MyDesktopPane desktop;
197 public static MyDesktopPane getDesktop()
199 // BH 2018 could use currentThread() here as a reference to a
200 // Hashtable<Thread, MyDesktopPane> in JavaScript
204 static int openFrameCount = 0;
206 static final int xOffset = 30;
208 static final int yOffset = 30;
210 public static jalview.ws.jws1.Discoverer discoverer;
212 public static Object[] jalviewClipboard;
214 public static boolean internalCopy = false;
216 static int fileLoadingCount = 0;
218 class MyDesktopManager implements DesktopManager
221 private DesktopManager delegate;
223 public MyDesktopManager(DesktopManager delegate)
225 this.delegate = delegate;
229 public void activateFrame(JInternalFrame f)
233 delegate.activateFrame(f);
234 } catch (NullPointerException npe)
236 Point p = getMousePosition();
237 instance.showPasteMenu(p.x, p.y);
242 public void beginDraggingFrame(JComponent f)
244 delegate.beginDraggingFrame(f);
248 public void beginResizingFrame(JComponent f, int direction)
250 delegate.beginResizingFrame(f, direction);
254 public void closeFrame(JInternalFrame f)
256 delegate.closeFrame(f);
260 public void deactivateFrame(JInternalFrame f)
262 delegate.deactivateFrame(f);
266 public void deiconifyFrame(JInternalFrame f)
268 delegate.deiconifyFrame(f);
272 public void dragFrame(JComponent f, int newX, int newY)
278 delegate.dragFrame(f, newX, newY);
282 public void endDraggingFrame(JComponent f)
284 delegate.endDraggingFrame(f);
289 public void endResizingFrame(JComponent f)
291 delegate.endResizingFrame(f);
296 public void iconifyFrame(JInternalFrame f)
298 delegate.iconifyFrame(f);
302 public void maximizeFrame(JInternalFrame f)
304 delegate.maximizeFrame(f);
308 public void minimizeFrame(JInternalFrame f)
310 delegate.minimizeFrame(f);
314 public void openFrame(JInternalFrame f)
316 delegate.openFrame(f);
320 public void resizeFrame(JComponent f, int newX, int newY, int newWidth,
327 delegate.resizeFrame(f, newX, newY, newWidth, newHeight);
331 public void setBoundsForFrame(JComponent f, int newX, int newY,
332 int newWidth, int newHeight)
334 delegate.setBoundsForFrame(f, newX, newY, newWidth, newHeight);
337 // All other methods, simply delegate
342 * Creates a new Desktop object.
347 * A note to implementors. It is ESSENTIAL that any activities that might block
348 * are spawned off as threads rather than waited for during this constructor.
351 doVamsasClientCheck();
353 doConfigureStructurePrefs();
354 setTitle("Jalview " + jalview.bin.Cache.getProperty("VERSION"));
355 setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
356 boolean selmemusage = jalview.bin.Cache.getDefault("SHOW_MEMUSAGE",
358 boolean showjconsole = jalview.bin.Cache.getDefault("SHOW_JAVA_CONSOLE",
360 desktop = new MyDesktopPane(selmemusage);
361 showMemusage.setSelected(selmemusage);
362 desktop.setBackground(Color.white);
363 getContentPane().setLayout(new BorderLayout());
364 // alternate config - have scrollbars - see notes in JAL-153
365 // JScrollPane sp = new JScrollPane();
366 // sp.getViewport().setView(desktop);
367 // getContentPane().add(sp, BorderLayout.CENTER);
368 getContentPane().add(desktop, BorderLayout.CENTER);
369 desktop.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
371 // This line prevents Windows Look&Feel resizing all new windows to maximum
372 // if previous window was maximised
373 desktop.setDesktopManager(new MyDesktopManager(
374 (Platform.isWindows() ? new DefaultDesktopManager()
376 ? new AquaInternalFrameManager(
377 desktop.getDesktopManager())
378 : desktop.getDesktopManager())));
380 Rectangle dims = getLastKnownDimensions("");
387 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
388 int xPos = Math.max(5, (screenSize.width - 900) / 2);
389 int yPos = Math.max(5, (screenSize.height - 650) / 2);
390 setBounds(xPos, yPos, 900, 650);
399 jconsole = new Console(this, showjconsole);
400 // add essential build information
401 jconsole.setHeader("Jalview Version: "
402 + jalview.bin.Cache.getProperty("VERSION") + "\n"
403 + "Jalview Installation: "
404 + jalview.bin.Cache.getDefault("INSTALLATION", "unknown")
405 + "\n" + "Build Date: "
406 + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown") + "\n"
407 + "Java version: " + System.getProperty("java.version") + "\n"
408 + System.getProperty("os.arch") + " "
409 + System.getProperty("os.name") + " "
410 + System.getProperty("os.version"));
412 showConsole(showjconsole);
414 showNews.setVisible(false);
416 experimentalFeatures.setSelected(showExperimental());
418 getIdentifiersOrgData();
422 // Spawn a thread that shows the splashscreen
424 SwingUtilities.invokeLater(new Runnable()
433 // Thread off a new instance of the file chooser - this reduces the time it
434 // takes to open it later on.
435 new Thread(new Runnable()
440 Cache.log.debug("Filechooser init thread started.");
441 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
442 JalviewFileChooser.forRead(Cache.getProperty("LAST_DIRECTORY"),
444 Cache.log.debug("Filechooser init thread finished.");
447 // Add the service change listener
448 changeSupport.addJalviewPropertyChangeListener("services",
449 new PropertyChangeListener()
453 public void propertyChange(PropertyChangeEvent evt)
455 Cache.log.debug("Firing service changed event for "
456 + evt.getNewValue());
457 JalviewServicesChanged(evt);
462 } // end BH 2018 ignore
464 this.setDropTarget(new java.awt.dnd.DropTarget(desktop, this));
466 this.addWindowListener(new WindowAdapter()
469 public void windowClosing(WindowEvent evt)
476 this.addMouseListener(ma = new MouseAdapter()
479 public void mousePressed(MouseEvent evt)
481 if (evt.isPopupTrigger()) // Mac
483 showPasteMenu(evt.getX(), evt.getY());
488 public void mouseReleased(MouseEvent evt)
490 if (evt.isPopupTrigger()) // Windows
492 showPasteMenu(evt.getX(), evt.getY());
496 desktop.addMouseListener(ma);
501 * Answers true if user preferences to enable experimental features is True
506 public boolean showExperimental()
508 String experimental = Cache.getDefault(EXPERIMENTAL_FEATURES,
509 Boolean.FALSE.toString());
510 return Boolean.valueOf(experimental).booleanValue();
513 public void doConfigureStructurePrefs()
515 // configure services
516 StructureSelectionManager ssm = StructureSelectionManager
517 .getStructureSelectionManager(this);
518 if (jalview.bin.Cache.getDefault(Preferences.ADD_SS_ANN, true))
520 ssm.setAddTempFacAnnot(jalview.bin.Cache
521 .getDefault(Preferences.ADD_TEMPFACT_ANN, true));
522 ssm.setProcessSecondaryStructure(jalview.bin.Cache
523 .getDefault(Preferences.STRUCT_FROM_PDB, true));
524 ssm.setSecStructServices(
525 jalview.bin.Cache.getDefault(Preferences.USE_RNAVIEW, true));
529 ssm.setAddTempFacAnnot(false);
530 ssm.setProcessSecondaryStructure(false);
531 ssm.setSecStructServices(false);
535 public void checkForNews()
544 final Desktop me = this;
545 // Thread off the news reader, in case there are connection problems.
546 addDialogThread(new Runnable()
551 Cache.log.debug("Starting news thread.");
553 jvnews = new BlogReader(me);
554 showNews.setVisible(true);
555 Cache.log.debug("Completed news thread.");
561 public void getIdentifiersOrgData()
563 // Thread off the identifiers fetcher
564 addDialogThread(new Runnable()
569 Cache.log.debug("Downloading data from identifiers.org");
570 UrlDownloadClient client = new UrlDownloadClient();
573 client.download(IdOrgSettings.getUrl(),
574 IdOrgSettings.getDownloadLocation());
575 } catch (IOException e)
577 Cache.log.debug("Exception downloading identifiers.org data"
585 protected void showNews_actionPerformed(ActionEvent e)
587 showNews(showNews.isSelected());
590 void showNews(boolean visible)
599 Cache.log.debug((visible ? "Showing" : "Hiding") + " news.");
600 showNews.setSelected(visible);
601 if (visible && !jvnews.isVisible())
603 new Thread(new Runnable()
608 long now = System.currentTimeMillis();
609 Desktop.instance.setProgressBar(
610 MessageManager.getString("status.refreshing_news"),
612 jvnews.refreshNews();
613 Desktop.instance.setProgressBar(null, now);
622 * recover the last known dimensions for a jalview window
625 * - empty string is desktop, all other windows have unique prefix
626 * @return null or last known dimensions scaled to current geometry (if last
627 * window geom was known)
629 Rectangle getLastKnownDimensions(String windowName)
631 // TODO: lock aspect ratio for scaling desktop Bug #0058199
632 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
633 String x = jalview.bin.Cache.getProperty(windowName + "SCREEN_X");
634 String y = jalview.bin.Cache.getProperty(windowName + "SCREEN_Y");
635 String width = jalview.bin.Cache
636 .getProperty(windowName + "SCREEN_WIDTH");
637 String height = jalview.bin.Cache
638 .getProperty(windowName + "SCREEN_HEIGHT");
639 if ((x != null) && (y != null) && (width != null) && (height != null))
641 int ix = Integer.parseInt(x), iy = Integer.parseInt(y),
642 iw = Integer.parseInt(width), ih = Integer.parseInt(height);
643 if (jalview.bin.Cache.getProperty("SCREENGEOMETRY_WIDTH") != null)
645 // attempt #1 - try to cope with change in screen geometry - this
646 // version doesn't preserve original jv aspect ratio.
647 // take ratio of current screen size vs original screen size.
648 double sw = ((1f * screenSize.width) / (1f * Integer.parseInt(
649 jalview.bin.Cache.getProperty("SCREENGEOMETRY_WIDTH"))));
650 double sh = ((1f * screenSize.height) / (1f * Integer.parseInt(
651 jalview.bin.Cache.getProperty("SCREENGEOMETRY_HEIGHT"))));
652 // rescale the bounds depending upon the current screen geometry.
653 ix = (int) (ix * sw);
654 iw = (int) (iw * sw);
655 iy = (int) (iy * sh);
656 ih = (int) (ih * sh);
657 while (ix >= screenSize.width)
659 jalview.bin.Cache.log.debug(
660 "Window geometry location recall error: shifting horizontal to within screenbounds.");
661 ix -= screenSize.width;
663 while (iy >= screenSize.height)
665 jalview.bin.Cache.log.debug(
666 "Window geometry location recall error: shifting vertical to within screenbounds.");
667 iy -= screenSize.height;
669 jalview.bin.Cache.log.debug(
670 "Got last known dimensions for " + windowName + ": x:" + ix
671 + " y:" + iy + " width:" + iw + " height:" + ih);
673 // return dimensions for new instance
674 return new Rectangle(ix, iy, iw, ih);
679 private void doVamsasClientCheck()
681 if (/** @j2sNative false && */ // BH 2018
682 jalview.bin.Cache.vamsasJarsPresent())
684 setupVamsasDisconnectedGui();
685 VamsasMenu.setVisible(true);
686 final Desktop us = this;
687 VamsasMenu.addMenuListener(new MenuListener()
689 // this listener remembers when the menu was first selected, and
690 // doesn't rebuild the session list until it has been cleared and
692 boolean refresh = true;
695 public void menuCanceled(MenuEvent e)
701 public void menuDeselected(MenuEvent e)
707 public void menuSelected(MenuEvent e)
711 us.buildVamsasStMenu();
716 vamsasStart.setVisible(true);
720 void showPasteMenu(int x, int y)
722 JPopupMenu popup = new JPopupMenu();
723 JMenuItem item = new JMenuItem(
724 MessageManager.getString("label.paste_new_window"));
725 item.addActionListener(new ActionListener()
728 public void actionPerformed(ActionEvent evt)
735 popup.show(this, x, y);
742 Clipboard c = Toolkit.getDefaultToolkit().getSystemClipboard();
743 Transferable contents = c.getContents(this);
745 if (contents != null)
747 String file = (String) contents
748 .getTransferData(DataFlavor.stringFlavor);
750 FileFormatI format = new IdentifyFile().identify(file,
751 DataSourceType.PASTE);
753 new FileLoader().LoadFile(file, DataSourceType.PASTE, format);
756 } catch (Exception ex)
759 "Unable to paste alignment from system clipboard:\n" + ex);
764 * Adds and opens the given frame to the desktop
775 public static synchronized void addInternalFrame(
776 final JInternalFrame frame, String title, int w, int h)
778 addInternalFrame(frame, title, true, w, h, true, false);
782 * Add an internal frame to the Jalview desktop
789 * When true, display frame immediately, otherwise, caller must call
790 * setVisible themselves.
796 public static synchronized void addInternalFrame(
797 final JInternalFrame frame, String title, boolean makeVisible,
800 addInternalFrame(frame, title, makeVisible, w, h, true, false);
804 * Add an internal frame to the Jalview desktop and make it visible
817 public static synchronized void addInternalFrame(
818 final JInternalFrame frame, String title, int w, int h,
821 addInternalFrame(frame, title, true, w, h, resizable, false);
825 * Add an internal frame to the Jalview desktop
832 * When true, display frame immediately, otherwise, caller must call
833 * setVisible themselves.
840 * @param ignoreMinSize
841 * Do not set the default minimum size for frame
843 public static synchronized void addInternalFrame(
844 final JInternalFrame frame, String title, boolean makeVisible,
845 int w, int h, boolean resizable, boolean ignoreMinSize)
848 // TODO: allow callers to determine X and Y position of frame (eg. via
850 // TODO: consider fixing method to update entries in the window submenu with
851 // the current window title
853 frame.setTitle(title);
854 if (frame.getWidth() < 1 || frame.getHeight() < 1)
858 // THIS IS A PUBLIC STATIC METHOD, SO IT MAY BE CALLED EVEN IN
859 // A HEADLESS STATE WHEN NO DESKTOP EXISTS. MUST RETURN
860 // IF JALVIEW IS RUNNING HEADLESS
861 // ///////////////////////////////////////////////
862 if (instance == null || (System.getProperty("java.awt.headless") != null
863 && System.getProperty("java.awt.headless").equals("true")))
872 frame.setMinimumSize(
873 new Dimension(DEFAULT_MIN_WIDTH, DEFAULT_MIN_HEIGHT));
875 // Set default dimension for Alignment Frame window.
876 // The Alignment Frame window could be added from a number of places,
878 // I did this here in order not to miss out on any Alignment frame.
879 if (frame instanceof AlignFrame)
881 frame.setMinimumSize(new Dimension(ALIGN_FRAME_DEFAULT_MIN_WIDTH,
882 ALIGN_FRAME_DEFAULT_MIN_HEIGHT));
886 frame.setVisible(makeVisible);
887 frame.setClosable(true);
888 frame.setResizable(resizable);
889 frame.setMaximizable(resizable);
890 frame.setIconifiable(resizable);
891 frame.setOpaque(/** @j2sNative true || */
894 if (frame.getX() < 1 && frame.getY() < 1)
896 frame.setLocation(xOffset * openFrameCount,
897 yOffset * ((openFrameCount - 1) % 10) + yOffset);
901 * add an entry for the new frame in the Window menu
902 * (and remove it when the frame is closed)
904 final JMenuItem menuItem = new JMenuItem(title);
905 frame.addInternalFrameListener(new InternalFrameAdapter()
908 public void internalFrameActivated(InternalFrameEvent evt)
910 JInternalFrame itf = desktop.getSelectedFrame();
913 if (itf instanceof AlignFrame)
915 Jalview.setCurrentAlignFrame((AlignFrame) itf);
922 public void internalFrameClosed(InternalFrameEvent evt)
924 PaintRefresher.RemoveComponent(frame);
927 * defensive check to prevent frames being
928 * added half off the window
930 if (openFrameCount > 0)
936 * ensure no reference to alignFrame retained by menu item listener
938 if (menuItem.getActionListeners().length > 0)
940 menuItem.removeActionListener(menuItem.getActionListeners()[0]);
942 windowMenu.remove(menuItem);
946 menuItem.addActionListener(new ActionListener()
949 public void actionPerformed(ActionEvent e)
953 frame.setSelected(true);
954 frame.setIcon(false);
955 } catch (java.beans.PropertyVetoException ex)
962 setKeyBindings(frame);
966 windowMenu.add(menuItem);
971 frame.setSelected(true);
972 frame.requestFocus();
973 } catch (java.beans.PropertyVetoException ve)
975 } catch (java.lang.ClassCastException cex)
978 "Squashed a possible GUI implementation error. If you can recreate this, please look at http://issues.jalview.org/browse/JAL-869",
984 * Add key bindings to a JInternalFrame so that Ctrl-W and Cmd-W will close the
989 private static void setKeyBindings(JInternalFrame frame)
991 @SuppressWarnings("serial")
992 final Action closeAction = new AbstractAction()
995 public void actionPerformed(ActionEvent e)
1002 * set up key bindings for Ctrl-W and Cmd-W, with the same (Close) action
1004 KeyStroke ctrlWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
1005 InputEvent.CTRL_DOWN_MASK);
1006 KeyStroke cmdWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
1007 Toolkit.getDefaultToolkit().getMenuShortcutKeyMask());
1009 InputMap inputMap = frame
1010 .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
1011 String ctrlW = ctrlWKey.toString();
1012 inputMap.put(ctrlWKey, ctrlW);
1013 inputMap.put(cmdWKey, ctrlW);
1015 ActionMap actionMap = frame.getActionMap();
1016 actionMap.put(ctrlW, closeAction);
1020 public void lostOwnership(Clipboard clipboard, Transferable contents)
1024 Desktop.jalviewClipboard = null;
1027 internalCopy = false;
1031 public void dragEnter(DropTargetDragEvent evt)
1036 public void dragExit(DropTargetEvent evt)
1041 public void dragOver(DropTargetDragEvent evt)
1046 public void dropActionChanged(DropTargetDragEvent evt)
1057 public void drop(DropTargetDropEvent evt)
1059 boolean success = true;
1060 // JAL-1552 - acceptDrop required before getTransferable call for
1061 // Java's Transferable for native dnd
1062 evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
1063 Transferable t = evt.getTransferable();
1064 List<Object> files = new ArrayList<>();
1065 List<DataSourceType> protocols = new ArrayList<>();
1069 Desktop.transferFromDropTarget(files, protocols, evt, t);
1070 } catch (Exception e)
1072 e.printStackTrace();
1080 for (int i = 0; i < files.size(); i++)
1082 // BH 2018 File or String
1083 Object file = files.get(i);
1084 String fileName = file.toString();
1085 DataSourceType protocol = (protocols == null)
1086 ? DataSourceType.FILE
1088 FileFormatI format = null;
1090 if (fileName.endsWith(".jar"))
1092 format = FileFormat.Jalview;
1097 format = new IdentifyFile().identify(file, protocol);
1100 new FileLoader().LoadFile(null, file, protocol, format);
1103 } catch (Exception ex)
1108 evt.dropComplete(success); // need this to ensure input focus is properly
1109 // transfered to any new windows created
1119 public void inputLocalFileMenuItem_actionPerformed(AlignViewport viewport)
1121 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
1122 JalviewFileChooser chooser = JalviewFileChooser
1123 .forRead(Cache.getProperty("LAST_DIRECTORY"), fileFormat);
1125 chooser.setFileView(new JalviewFileView());
1126 chooser.setDialogTitle(
1127 MessageManager.getString("label.open_local_file"));
1128 chooser.setToolTipText(MessageManager.getString("action.open"));
1130 chooser.response(new RunResponse(JalviewFileChooser.APPROVE_OPTION)
1136 File selectedFile = chooser.getSelectedFile();
1137 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1139 FileFormatI format = chooser.getSelectedFormat();
1142 * Call IdentifyFile to verify the file contains what its extension implies.
1143 * Skip this step for dynamically added file formats, because
1144 * IdentifyFile does not know how to recognise them.
1146 if (FileFormats.getInstance().isIdentifiable(format))
1150 format = new IdentifyFile().identify(selectedFile,
1151 DataSourceType.FILE);
1152 } catch (FileFormatException e)
1154 // format = null; //??
1158 new FileLoader().LoadFile(viewport, selectedFile,
1159 DataSourceType.FILE, format);
1161 }).openDialog(this);
1171 public void inputURLMenuItem_actionPerformed(AlignViewport viewport)
1173 // This construct allows us to have a wider textfield
1175 JLabel label = new JLabel(
1176 MessageManager.getString("label.input_file_url"));
1178 JComboBox history = new JComboBox();
1179 JPanel panel = new JPanel(new GridLayout(2, 1));
1182 history.setPreferredSize(new Dimension(400, 20));
1183 history.setEditable(true);
1184 history.addItem("http://www.");
1186 String historyItems = jalview.bin.Cache.getProperty("RECENT_URL");
1190 if (historyItems != null)
1192 st = new StringTokenizer(historyItems, "\t");
1194 while (st.hasMoreTokens())
1196 history.addItem(st.nextElement());
1200 // BH 2018 -- providing a callback for SwingJS
1201 // dialogOption is just a simple way to provide
1202 // context for the modal-like response.
1203 // The only requirement is that desktop implement
1204 // PropertyChangeListener, which is used already in Java
1205 // for changes in input value and such within the dialogs.
1207 String dialogOption = "label.input_alignment_from_url";
1208 desktop.dialogData = new Object[] { dialogOption, viewport, history };
1209 desktop.onDialogReturn(JvOptionPane.showInternalConfirmDialog(desktop,
1210 panel, MessageManager.getString(dialogOption),
1211 JvOptionPane.OK_CANCEL_OPTION));
1213 // no code may follow this, as SwingJS will not block
1214 // callback in JavaScript comes via a property change event,
1215 // thus going into desktop.onDialogReturn(int) just the same as
1221 * Opens the CutAndPaste window for the user to paste an alignment in to
1224 * - if not null, the pasted alignment is added to the current
1225 * alignment; if null, to a new alignment window
1228 public void inputTextboxMenuItem_actionPerformed(
1229 AlignmentViewPanel viewPanel)
1231 CutAndPasteTransfer cap = new CutAndPasteTransfer();
1232 cap.setForInput(viewPanel);
1233 Desktop.addInternalFrame(cap,
1234 MessageManager.getString("label.cut_paste_alignmen_file"), true,
1244 Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
1245 jalview.bin.Cache.setProperty("SCREENGEOMETRY_WIDTH",
1247 jalview.bin.Cache.setProperty("SCREENGEOMETRY_HEIGHT",
1248 screen.height + "");
1249 storeLastKnownDimensions("", new Rectangle(getBounds().x, getBounds().y,
1250 getWidth(), getHeight()));
1252 if (jconsole != null)
1254 storeLastKnownDimensions("JAVA_CONSOLE_", jconsole.getBounds());
1255 jconsole.stopConsole();
1259 storeLastKnownDimensions("JALVIEW_RSS_WINDOW_", jvnews.getBounds());
1262 if (dialogExecutor != null)
1264 dialogExecutor.shutdownNow();
1266 closeAll_actionPerformed(null);
1268 if (groovyConsole != null)
1270 // suppress a possible repeat prompt to save script
1271 groovyConsole.setDirty(false);
1272 groovyConsole.exit();
1277 private void storeLastKnownDimensions(String string, Rectangle jc)
1279 jalview.bin.Cache.log.debug("Storing last known dimensions for "
1280 + string + ": x:" + jc.x + " y:" + jc.y + " width:" + jc.width
1281 + " height:" + jc.height);
1283 jalview.bin.Cache.setProperty(string + "SCREEN_X", jc.x + "");
1284 jalview.bin.Cache.setProperty(string + "SCREEN_Y", jc.y + "");
1285 jalview.bin.Cache.setProperty(string + "SCREEN_WIDTH", jc.width + "");
1286 jalview.bin.Cache.setProperty(string + "SCREEN_HEIGHT", jc.height + "");
1296 public void aboutMenuItem_actionPerformed(ActionEvent e)
1298 // StringBuffer message = getAboutMessage(false);
1299 // JvOptionPane.showInternalMessageDialog(Desktop.desktop,
1301 // message.toString(), "About Jalview", JvOptionPane.INFORMATION_MESSAGE);
1302 new Thread(new Runnable()
1307 new SplashScreen(true);
1312 public StringBuffer getAboutMessage(boolean shortv)
1314 StringBuffer message = new StringBuffer();
1315 message.append("<html>");
1318 message.append("<h1><strong>Version: "
1319 + jalview.bin.Cache.getProperty("VERSION")
1320 + "</strong></h1>");
1321 message.append("<strong>Last Updated: <em>"
1322 + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown")
1323 + "</em></strong>");
1329 message.append("<strong>Version "
1330 + jalview.bin.Cache.getProperty("VERSION")
1331 + "; last updated: "
1332 + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown"));
1335 if (jalview.bin.Cache.getDefault("LATEST_VERSION", "Checking")
1336 .equals("Checking"))
1338 message.append("<br>...Checking latest version...</br>");
1340 else if (!jalview.bin.Cache.getDefault("LATEST_VERSION", "Checking")
1341 .equals(jalview.bin.Cache.getProperty("VERSION")))
1343 boolean red = false;
1344 if (jalview.bin.Cache.getProperty("VERSION").toLowerCase()
1345 .indexOf("automated build") == -1)
1348 // Displayed when code version and jnlp version do not match and code
1349 // version is not a development build
1350 message.append("<div style=\"color: #FF0000;font-style: bold;\">");
1353 message.append("<br>!! Version "
1354 + jalview.bin.Cache.getDefault("LATEST_VERSION",
1356 + " is available for download from "
1357 + jalview.bin.Cache.getDefault("www.jalview.org",
1358 "http://www.jalview.org")
1362 message.append("</div>");
1365 message.append("<br>Authors: " + jalview.bin.Cache.getDefault(
1367 "The Jalview Authors (See AUTHORS file for current list)")
1368 + "<br><br>Development managed by The Barton Group, University of Dundee, Scotland, UK.<br>"
1369 + "<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"
1370 + "<br><br>If you use Jalview, please cite:"
1371 + "<br>Waterhouse, A.M., Procter, J.B., Martin, D.M.A, Clamp, M. and Barton, G. J. (2009)"
1372 + "<br>Jalview Version 2 - a multiple sequence alignment editor and analysis workbench"
1373 + "<br>Bioinformatics doi: 10.1093/bioinformatics/btp033"
1385 public void documentationMenuItem_actionPerformed(ActionEvent e)
1389 Help.showHelpWindow();
1390 } catch (Exception ex)
1396 public void closeAll_actionPerformed(ActionEvent e)
1398 // TODO show a progress bar while closing?
1399 JInternalFrame[] frames = desktop.getAllFrames();
1400 for (int i = 0; i < frames.length; i++)
1404 frames[i].setClosed(true);
1405 } catch (java.beans.PropertyVetoException ex)
1409 Jalview.setCurrentAlignFrame(null);
1410 System.out.println("ALL CLOSED");
1411 if (v_client != null)
1413 // TODO clear binding to vamsas document objects on close_all
1417 * reset state of singleton objects as appropriate (clear down session state
1418 * when all windows are closed)
1420 StructureSelectionManager ssm = StructureSelectionManager
1421 .getStructureSelectionManager(this);
1429 public void raiseRelated_actionPerformed(ActionEvent e)
1431 reorderAssociatedWindows(false, false);
1435 public void minimizeAssociated_actionPerformed(ActionEvent e)
1437 reorderAssociatedWindows(true, false);
1440 void closeAssociatedWindows()
1442 reorderAssociatedWindows(false, true);
1448 * @seejalview.jbgui.GDesktop#garbageCollect_actionPerformed(java.awt.event.
1452 protected void garbageCollect_actionPerformed(ActionEvent e)
1454 // We simply collect the garbage
1455 jalview.bin.Cache.log.debug("Collecting garbage...");
1457 jalview.bin.Cache.log.debug("Finished garbage collection.");
1464 * jalview.jbgui.GDesktop#showMemusage_actionPerformed(java.awt.event.ActionEvent
1468 protected void showMemusage_actionPerformed(ActionEvent e)
1470 desktop.showMemoryUsage(showMemusage.isSelected());
1477 * jalview.jbgui.GDesktop#showConsole_actionPerformed(java.awt.event.ActionEvent
1481 protected void showConsole_actionPerformed(ActionEvent e)
1483 showConsole(showConsole.isSelected());
1486 Console jconsole = null;
1489 * control whether the java console is visible or not
1493 void showConsole(boolean selected)
1495 // TODO: decide if we should update properties file
1496 if (jconsole != null) // BH 2018
1498 showConsole.setSelected(selected);
1499 Cache.setProperty("SHOW_JAVA_CONSOLE",
1500 Boolean.valueOf(selected).toString());
1501 jconsole.setVisible(selected);
1505 void reorderAssociatedWindows(boolean minimize, boolean close)
1507 JInternalFrame[] frames = desktop.getAllFrames();
1508 if (frames == null || frames.length < 1)
1513 AlignmentViewport source = null, target = null;
1514 if (frames[0] instanceof AlignFrame)
1516 source = ((AlignFrame) frames[0]).getCurrentView();
1518 else if (frames[0] instanceof TreePanel)
1520 source = ((TreePanel) frames[0]).getViewPort();
1522 else if (frames[0] instanceof PCAPanel)
1524 source = ((PCAPanel) frames[0]).av;
1526 else if (frames[0].getContentPane() instanceof PairwiseAlignPanel)
1528 source = ((PairwiseAlignPanel) frames[0].getContentPane()).av;
1533 for (int i = 0; i < frames.length; i++)
1536 if (frames[i] == null)
1540 if (frames[i] instanceof AlignFrame)
1542 target = ((AlignFrame) frames[i]).getCurrentView();
1544 else if (frames[i] instanceof TreePanel)
1546 target = ((TreePanel) frames[i]).getViewPort();
1548 else if (frames[i] instanceof PCAPanel)
1550 target = ((PCAPanel) frames[i]).av;
1552 else if (frames[i].getContentPane() instanceof PairwiseAlignPanel)
1554 target = ((PairwiseAlignPanel) frames[i].getContentPane()).av;
1557 if (source == target)
1563 frames[i].setClosed(true);
1567 frames[i].setIcon(minimize);
1570 frames[i].toFront();
1574 } catch (java.beans.PropertyVetoException ex)
1589 protected void preferences_actionPerformed(ActionEvent e)
1601 public void saveState_actionPerformed(ActionEvent e)
1603 // TODO: JAL-3048 not needed for Jalview-JS
1605 JalviewFileChooser chooser = new JalviewFileChooser("jvp",
1608 chooser.setFileView(new JalviewFileView());
1609 chooser.setDialogTitle(MessageManager.getString("label.save_state"));
1611 int value = chooser.showSaveDialog(this);
1613 if (value == JalviewFileChooser.APPROVE_OPTION)
1615 final Desktop me = this;
1616 final java.io.File choice = chooser.getSelectedFile();
1617 setProjectFile(choice);
1619 new Thread(new Runnable()
1624 // TODO: refactor to Jalview desktop session controller action.
1625 setProgressBar(MessageManager.formatMessage(
1626 "label.saving_jalview_project", new Object[]
1627 { choice.getName() }), choice.hashCode());
1628 jalview.bin.Cache.setProperty("LAST_DIRECTORY",
1629 choice.getParent());
1630 // TODO catch and handle errors for savestate
1631 // TODO prevent user from messing with the Desktop whilst we're saving
1634 new Jalview2XML().saveState(choice);
1635 } catch (OutOfMemoryError oom)
1638 "Whilst saving current state to " + choice.getName(),
1640 } catch (Exception ex)
1643 "Problems whilst trying to save to " + choice.getName(),
1645 JvOptionPane.showMessageDialog(me,
1646 MessageManager.formatMessage(
1647 "label.error_whilst_saving_current_state_to",
1649 { choice.getName() }),
1650 MessageManager.getString("label.couldnt_save_project"),
1651 JvOptionPane.WARNING_MESSAGE);
1653 setProgressBar(null, choice.hashCode());
1659 private void setProjectFile(File choice)
1661 this.projectFile = choice;
1664 public File getProjectFile()
1666 return this.projectFile;
1676 public void loadState_actionPerformed(ActionEvent e)
1678 // TODO: JAL-3048 not needed for Jalview-JS
1680 JalviewFileChooser chooser = new JalviewFileChooser(
1681 Cache.getProperty("LAST_DIRECTORY"), new String[]
1684 { "Jalview Project", "Jalview Project (old)" },
1686 chooser.setFileView(new JalviewFileView());
1687 chooser.setDialogTitle(MessageManager.getString("label.restore_state"));
1689 int value = chooser.showOpenDialog(this);
1691 if (value == JalviewFileChooser.APPROVE_OPTION)
1693 final File selectedFile = chooser.getSelectedFile();
1694 setProjectFile(selectedFile);
1695 final String choice = selectedFile.getAbsolutePath();
1696 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1697 new Thread(new Runnable()
1702 setProgressBar(MessageManager.formatMessage(
1703 "label.loading_jalview_project", new Object[]
1704 { choice }), choice.hashCode());
1707 new Jalview2XML().loadJalviewAlign(choice);
1708 } catch (OutOfMemoryError oom)
1710 new OOMWarning("Whilst loading project from " + choice, oom);
1711 } catch (Exception ex)
1714 "Problems whilst loading project from " + choice, ex);
1715 JvOptionPane.showMessageDialog(Desktop.desktop,
1716 MessageManager.formatMessage(
1717 "label.error_whilst_loading_project_from",
1720 MessageManager.getString("label.couldnt_load_project"),
1721 JvOptionPane.WARNING_MESSAGE);
1723 setProgressBar(null, choice.hashCode());
1730 public void inputSequence_actionPerformed(ActionEvent e)
1732 new SequenceFetcher(this);
1735 JPanel progressPanel;
1737 ArrayList<JPanel> fileLoadingPanels = new ArrayList<>();
1739 public void startLoading(final Object fileName)
1741 if (fileLoadingCount == 0)
1743 fileLoadingPanels.add(addProgressPanel(MessageManager
1744 .formatMessage("label.loading_file", new Object[]
1750 private JPanel addProgressPanel(String string)
1752 if (progressPanel == null)
1754 progressPanel = new JPanel(new GridLayout(1, 1));
1755 totalProgressCount = 0;
1756 instance.getContentPane().add(progressPanel, BorderLayout.SOUTH);
1758 JPanel thisprogress = new JPanel(new BorderLayout(10, 5));
1759 JProgressBar progressBar = new JProgressBar();
1760 progressBar.setIndeterminate(true);
1762 thisprogress.add(new JLabel(string), BorderLayout.WEST);
1764 thisprogress.add(progressBar, BorderLayout.CENTER);
1765 progressPanel.add(thisprogress);
1766 ((GridLayout) progressPanel.getLayout()).setRows(
1767 ((GridLayout) progressPanel.getLayout()).getRows() + 1);
1768 ++totalProgressCount;
1769 instance.validate();
1770 return thisprogress;
1773 int totalProgressCount = 0;
1775 private void removeProgressPanel(JPanel progbar)
1777 if (progressPanel != null)
1779 synchronized (progressPanel)
1781 progressPanel.remove(progbar);
1782 GridLayout gl = (GridLayout) progressPanel.getLayout();
1783 gl.setRows(gl.getRows() - 1);
1784 if (--totalProgressCount < 1)
1786 this.getContentPane().remove(progressPanel);
1787 progressPanel = null;
1794 public void stopLoading()
1797 if (fileLoadingCount < 1)
1799 while (fileLoadingPanels.size() > 0)
1801 removeProgressPanel(fileLoadingPanels.remove(0));
1803 fileLoadingPanels.clear();
1804 fileLoadingCount = 0;
1809 public static int getViewCount(String alignmentId)
1811 AlignmentViewport[] aps = getViewports(alignmentId);
1812 return (aps == null) ? 0 : aps.length;
1817 * @param alignmentId
1818 * - if null, all sets are returned
1819 * @return all AlignmentPanels concerning the alignmentId sequence set
1821 public static AlignmentPanel[] getAlignmentPanels(String alignmentId)
1823 if (Desktop.desktop == null)
1825 // no frames created and in headless mode
1826 // TODO: verify that frames are recoverable when in headless mode
1829 List<AlignmentPanel> aps = new ArrayList<>();
1830 AlignFrame[] frames = getAlignFrames();
1835 for (AlignFrame af : frames)
1837 for (AlignmentPanel ap : af.alignPanels)
1839 if (alignmentId == null
1840 || alignmentId.equals(ap.av.getSequenceSetId()))
1846 if (aps.size() == 0)
1850 AlignmentPanel[] vap = aps.toArray(new AlignmentPanel[aps.size()]);
1855 * get all the viewports on an alignment.
1857 * @param sequenceSetId
1858 * unique alignment id (may be null - all viewports returned in that
1860 * @return all viewports on the alignment bound to sequenceSetId
1862 public static AlignmentViewport[] getViewports(String sequenceSetId)
1864 List<AlignmentViewport> viewp = new ArrayList<>();
1865 if (desktop != null)
1867 AlignFrame[] frames = Desktop.getAlignFrames();
1869 for (AlignFrame afr : frames)
1871 if (sequenceSetId == null || afr.getViewport().getSequenceSetId()
1872 .equals(sequenceSetId))
1874 if (afr.alignPanels != null)
1876 for (AlignmentPanel ap : afr.alignPanels)
1878 if (sequenceSetId == null
1879 || sequenceSetId.equals(ap.av.getSequenceSetId()))
1887 viewp.add(afr.getViewport());
1891 if (viewp.size() > 0)
1893 return viewp.toArray(new AlignmentViewport[viewp.size()]);
1900 * Explode the views in the given frame into separate AlignFrame
1904 public static void explodeViews(AlignFrame af)
1906 int size = af.alignPanels.size();
1912 for (int i = 0; i < size; i++)
1914 AlignmentPanel ap = af.alignPanels.get(i);
1915 AlignFrame newaf = new AlignFrame(ap);
1918 * Restore the view's last exploded frame geometry if known. Multiple
1919 * views from one exploded frame share and restore the same (frame)
1920 * position and size.
1922 Rectangle geometry = ap.av.getExplodedGeometry();
1923 if (geometry != null)
1925 newaf.setBounds(geometry);
1928 ap.av.setGatherViewsHere(false);
1930 addInternalFrame(newaf, af.getTitle(), AlignFrame.DEFAULT_WIDTH,
1931 AlignFrame.DEFAULT_HEIGHT);
1934 af.alignPanels.clear();
1935 af.closeMenuItem_actionPerformed(true);
1940 * Gather expanded views (separate AlignFrame's) with the same sequence set
1941 * identifier back in to this frame as additional views, and close the expanded
1942 * views. Note the expanded frames may themselves have multiple views. We take
1947 public void gatherViews(AlignFrame source)
1949 source.viewport.setGatherViewsHere(true);
1950 source.viewport.setExplodedGeometry(source.getBounds());
1951 JInternalFrame[] frames = desktop.getAllFrames();
1952 String viewId = source.viewport.getSequenceSetId();
1954 for (int t = 0; t < frames.length; t++)
1956 if (frames[t] instanceof AlignFrame && frames[t] != source)
1958 AlignFrame af = (AlignFrame) frames[t];
1959 boolean gatherThis = false;
1960 for (int a = 0; a < af.alignPanels.size(); a++)
1962 AlignmentPanel ap = af.alignPanels.get(a);
1963 if (viewId.equals(ap.av.getSequenceSetId()))
1966 ap.av.setGatherViewsHere(false);
1967 ap.av.setExplodedGeometry(af.getBounds());
1968 source.addAlignmentPanel(ap, false);
1974 af.alignPanels.clear();
1975 af.closeMenuItem_actionPerformed(true);
1982 jalview.gui.VamsasApplication v_client = null;
1985 public void vamsasImport_actionPerformed(ActionEvent e)
1987 // TODO: JAL-3048 not needed for Jalview-JS
1989 if (v_client == null)
1991 // Load and try to start a session.
1992 JalviewFileChooser chooser = new JalviewFileChooser(
1993 jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
1995 chooser.setFileView(new JalviewFileView());
1996 chooser.setDialogTitle(
1997 MessageManager.getString("label.open_saved_vamsas_session"));
1998 chooser.setToolTipText(MessageManager.getString(
1999 "label.select_vamsas_session_opened_as_new_vamsas_session"));
2001 int value = chooser.showOpenDialog(this);
2003 if (value == JalviewFileChooser.APPROVE_OPTION)
2005 String fle = chooser.getSelectedFile().toString();
2006 if (!vamsasImport(chooser.getSelectedFile()))
2008 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
2009 MessageManager.formatMessage(
2010 "label.couldnt_import_as_vamsas_session",
2014 .getString("label.vamsas_document_import_failed"),
2015 JvOptionPane.ERROR_MESSAGE);
2021 jalview.bin.Cache.log.error(
2022 "Implementation error - load session from a running session is not supported.");
2027 * import file into a new vamsas session (uses jalview.gui.VamsasApplication)
2030 * @return true if import was a success and a session was started.
2032 public boolean vamsasImport(URL url)
2034 // TODO: create progress bar
2035 if (v_client != null)
2038 jalview.bin.Cache.log.error(
2039 "Implementation error - load session from a running session is not supported.");
2045 // copy the URL content to a temporary local file
2046 // TODO: be a bit cleverer here with nio (?!)
2047 File file = File.createTempFile("vdocfromurl", ".vdj");
2048 FileOutputStream fos = new FileOutputStream(file);
2049 BufferedInputStream bis = new BufferedInputStream(url.openStream());
2050 byte[] buffer = new byte[2048];
2052 while ((ln = bis.read(buffer)) > -1)
2054 fos.write(buffer, 0, ln);
2058 v_client = new jalview.gui.VamsasApplication(this, file,
2059 url.toExternalForm());
2060 } catch (Exception ex)
2062 jalview.bin.Cache.log.error(
2063 "Failed to create new vamsas session from contents of URL "
2068 setupVamsasConnectedGui();
2069 v_client.initial_update(); // TODO: thread ?
2070 return v_client.inSession();
2074 * import file into a new vamsas session (uses jalview.gui.VamsasApplication)
2077 * @return true if import was a success and a session was started.
2079 public boolean vamsasImport(File file)
2081 if (v_client != null)
2084 jalview.bin.Cache.log.error(
2085 "Implementation error - load session from a running session is not supported.");
2089 setProgressBar(MessageManager.formatMessage(
2090 "status.importing_vamsas_session_from", new Object[]
2091 { file.getName() }), file.hashCode());
2094 v_client = new jalview.gui.VamsasApplication(this, file, null);
2095 } catch (Exception ex)
2097 setProgressBar(MessageManager.formatMessage(
2098 "status.importing_vamsas_session_from", new Object[]
2099 { file.getName() }), file.hashCode());
2100 jalview.bin.Cache.log.error(
2101 "New vamsas session from existing session file failed:", ex);
2104 setupVamsasConnectedGui();
2105 v_client.initial_update(); // TODO: thread ?
2106 setProgressBar(MessageManager.formatMessage(
2107 "status.importing_vamsas_session_from", new Object[]
2108 { file.getName() }), file.hashCode());
2109 return v_client.inSession();
2112 public boolean joinVamsasSession(String mysesid)
2114 if (v_client != null)
2116 throw new Error(MessageManager
2117 .getString("error.try_join_vamsas_session_another"));
2119 if (mysesid == null)
2122 MessageManager.getString("error.invalid_vamsas_session_id"));
2124 v_client = new VamsasApplication(this, mysesid);
2125 setupVamsasConnectedGui();
2126 v_client.initial_update();
2127 return (v_client.inSession());
2131 public void vamsasStart_actionPerformed(ActionEvent e)
2133 if (v_client == null)
2136 // we just start a default session for moment.
2138 * JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache.
2139 * getProperty("LAST_DIRECTORY"));
2141 * chooser.setFileView(new JalviewFileView());
2142 * chooser.setDialogTitle("Load Vamsas file");
2143 * chooser.setToolTipText("Import");
2145 * int value = chooser.showOpenDialog(this);
2147 * if (value == JalviewFileChooser.APPROVE_OPTION) { v_client = new
2148 * jalview.gui.VamsasApplication(this, chooser.getSelectedFile());
2150 v_client = new VamsasApplication(this);
2151 setupVamsasConnectedGui();
2152 v_client.initial_update(); // TODO: thread ?
2156 // store current data in session.
2157 v_client.push_update(); // TODO: thread
2161 protected void setupVamsasConnectedGui()
2163 vamsasStart.setText(MessageManager.getString("label.session_update"));
2164 vamsasSave.setVisible(true);
2165 vamsasStop.setVisible(true);
2166 vamsasImport.setVisible(false); // Document import to existing session is
2167 // not possible for vamsas-client-1.0.
2170 protected void setupVamsasDisconnectedGui()
2172 vamsasSave.setVisible(false);
2173 vamsasStop.setVisible(false);
2174 vamsasImport.setVisible(true);
2176 .setText(MessageManager.getString("label.new_vamsas_session"));
2180 public void vamsasStop_actionPerformed(ActionEvent e)
2182 if (v_client != null)
2184 v_client.end_session();
2186 setupVamsasDisconnectedGui();
2190 protected void buildVamsasStMenu()
2192 if (v_client == null)
2194 String[] sess = null;
2197 sess = VamsasApplication.getSessionList();
2198 } catch (Exception e)
2200 jalview.bin.Cache.log.warn("Problem getting current sessions list.",
2206 jalview.bin.Cache.log.debug(
2207 "Got current sessions list: " + sess.length + " entries.");
2208 VamsasStMenu.removeAll();
2209 for (int i = 0; i < sess.length; i++)
2211 JMenuItem sessit = new JMenuItem();
2212 sessit.setText(sess[i]);
2213 sessit.setToolTipText(MessageManager
2214 .formatMessage("label.connect_to_session", new Object[]
2216 final Desktop dsktp = this;
2217 final String mysesid = sess[i];
2218 sessit.addActionListener(new ActionListener()
2222 public void actionPerformed(ActionEvent e)
2224 if (dsktp.v_client == null)
2226 Thread rthr = new Thread(new Runnable()
2232 dsktp.v_client = new VamsasApplication(dsktp, mysesid);
2233 dsktp.setupVamsasConnectedGui();
2234 dsktp.v_client.initial_update();
2242 VamsasStMenu.add(sessit);
2244 // don't show an empty menu.
2245 VamsasStMenu.setVisible(sess.length > 0);
2250 jalview.bin.Cache.log.debug("No current vamsas sessions.");
2251 VamsasStMenu.removeAll();
2252 VamsasStMenu.setVisible(false);
2257 // Not interested in the content. Just hide ourselves.
2258 VamsasStMenu.setVisible(false);
2263 public void vamsasSave_actionPerformed(ActionEvent e)
2265 // TODO: JAL-3048 not needed for Jalview-JS
2267 if (v_client != null)
2269 // TODO: VAMSAS DOCUMENT EXTENSION is VDJ
2270 JalviewFileChooser chooser = new JalviewFileChooser("vdj",
2273 chooser.setFileView(new JalviewFileView());
2274 chooser.setDialogTitle(MessageManager
2275 .getString("label.save_vamsas_document_archive"));
2277 int value = chooser.showSaveDialog(this);
2279 if (value == JalviewFileChooser.APPROVE_OPTION)
2281 java.io.File choice = chooser.getSelectedFile();
2282 JPanel progpanel = addProgressPanel(MessageManager
2283 .formatMessage("label.saving_vamsas_doc", new Object[]
2284 { choice.getName() }));
2285 Cache.setProperty("LAST_DIRECTORY", choice.getParent());
2286 String warnmsg = null;
2287 String warnttl = null;
2290 v_client.vclient.storeDocument(choice);
2293 warnttl = "Serious Problem saving Vamsas Document";
2294 warnmsg = ex.toString();
2295 jalview.bin.Cache.log
2296 .error("Error Whilst saving document to " + choice, ex);
2298 } catch (Exception ex)
2300 warnttl = "Problem saving Vamsas Document.";
2301 warnmsg = ex.toString();
2302 jalview.bin.Cache.log.warn(
2303 "Exception Whilst saving document to " + choice, ex);
2306 removeProgressPanel(progpanel);
2307 if (warnmsg != null)
2309 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
2311 warnmsg, warnttl, JvOptionPane.ERROR_MESSAGE);
2317 JPanel vamUpdate = null;
2320 * hide vamsas user gui bits when a vamsas document event is being handled.
2323 * true to hide gui, false to reveal gui
2325 public void setVamsasUpdate(boolean b)
2327 Cache.log.debug("Setting gui for Vamsas update "
2328 + (b ? "in progress" : "finished"));
2330 if (vamUpdate != null)
2332 this.removeProgressPanel(vamUpdate);
2336 vamUpdate = this.addProgressPanel(
2337 MessageManager.getString("label.updating_vamsas_session"));
2339 vamsasStart.setVisible(!b);
2340 vamsasStop.setVisible(!b);
2341 vamsasSave.setVisible(!b);
2344 public JInternalFrame[] getAllFrames()
2346 return desktop.getAllFrames();
2350 * Checks the given url to see if it gives a response indicating that the user
2351 * should be informed of a new questionnaire.
2355 public void checkForQuestionnaire(String url)
2357 UserQuestionnaireCheck jvq = new UserQuestionnaireCheck(url);
2358 // javax.swing.SwingUtilities.invokeLater(jvq);
2359 new Thread(jvq).start();
2362 public void checkURLLinks()
2364 // Thread off the URL link checker
2365 addDialogThread(new Runnable()
2370 if (/** @j2sNative false && */ // BH 2018
2371 Cache.getDefault("CHECKURLLINKS", true))
2373 // check what the actual links are - if it's just the default don't
2374 // bother with the warning
2375 List<String> links = Preferences.sequenceUrlLinks
2378 // only need to check links if there is one with a
2379 // SEQUENCE_ID which is not the default EMBL_EBI link
2380 ListIterator<String> li = links.listIterator();
2381 boolean check = false;
2382 List<JLabel> urls = new ArrayList<>();
2383 while (li.hasNext())
2385 String link = li.next();
2386 if (link.contains(SEQUENCE_ID)
2387 && !UrlConstants.isDefaultString(link))
2390 int barPos = link.indexOf("|");
2391 String urlMsg = barPos == -1 ? link
2392 : link.substring(0, barPos) + ": "
2393 + link.substring(barPos + 1);
2394 urls.add(new JLabel(urlMsg));
2402 // ask user to check in case URL links use old style tokens
2403 // ($SEQUENCE_ID$ for sequence id _or_ accession id)
2404 JPanel msgPanel = new JPanel();
2405 msgPanel.setLayout(new BoxLayout(msgPanel, BoxLayout.PAGE_AXIS));
2406 msgPanel.add(Box.createVerticalGlue());
2407 JLabel msg = new JLabel(MessageManager
2408 .getString("label.SEQUENCE_ID_for_DB_ACCESSION1"));
2409 JLabel msg2 = new JLabel(MessageManager
2410 .getString("label.SEQUENCE_ID_for_DB_ACCESSION2"));
2412 for (JLabel url : urls)
2418 final JCheckBox jcb = new JCheckBox(
2419 MessageManager.getString("label.do_not_display_again"));
2420 jcb.addActionListener(new ActionListener()
2423 public void actionPerformed(ActionEvent e)
2425 // update Cache settings for "don't show this again"
2426 boolean showWarningAgain = !jcb.isSelected();
2427 Cache.setProperty("CHECKURLLINKS",
2428 Boolean.valueOf(showWarningAgain).toString());
2433 JvOptionPane.showMessageDialog(Desktop.desktop, msgPanel,
2435 .getString("label.SEQUENCE_ID_no_longer_used"),
2436 JvOptionPane.WARNING_MESSAGE);
2443 * Proxy class for JDesktopPane which optionally displays the current memory
2444 * usage and highlights the desktop area with a red bar if free memory runs low.
2448 public class MyDesktopPane extends JDesktopPane
2449 implements Runnable, PropertyChangeListener
2452 public Object[] dialogData;
2456 public void propertyChange(PropertyChangeEvent event)
2458 // TODO this is obsolete with JAL-3048 - delete?
2459 Object val = event.getNewValue();
2460 String name = event.getPropertyName();
2461 System.out.println(name);
2462 switch (event.getSource().getClass().getName())
2464 case "javax.swing.JOptionPane":
2468 onDialogReturn(val);
2471 if (val instanceof Integer)
2473 onDialogReturn(((Integer) val).intValue());
2477 onDialogReturn(val);
2482 case "javax.swing.JFileChooser":
2485 case "SelectedFile":
2486 // in JavaScript, this File object will have a _bytes property,
2487 // because the file data has already been loaded
2488 onDialogReturn(new Object[] { (File) val });
2493 System.out.println(event.getSource().getClass().getName() + " "
2494 + event.getPropertyName() + ": " + event.getNewValue());
2497 // JSCOmponent.DialogCaller interface
2498 void onDialogReturn(Object value)
2500 switch ((String) dialogData[0])
2502 case "SelectedFile":
2504 dialogData[0] = value;
2505 ((Runnable) dialogData[1]).run();
2511 // JSCOmponent.DialogCaller interface
2512 void onDialogReturn(int value)
2514 if (value != Math.floor(value))
2516 // in JavaScript, this will be NaN, oddly enough
2520 switch ((String) dialogData[0])
2523 dialogData[0] = Integer.valueOf(value);
2524 ((Runnable) dialogData[1]).run();
2526 case "label.input_alignment_from_url":
2527 // reconstruct the parameter data
2529 AlignViewport viewport = (AlignViewport) dialogData[1];
2530 JComboBox history = (JComboBox) dialogData[2];
2531 // the rest of this is unchangaed
2532 if (reply != JvOptionPane.OK_OPTION)
2537 String url = history.getSelectedItem().toString();
2539 if (url.toLowerCase().endsWith(".jar"))
2541 if (viewport != null)
2543 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
2544 FileFormat.Jalview);
2548 new FileLoader().LoadFile(url, DataSourceType.URL,
2549 FileFormat.Jalview);
2554 FileFormatI format = null;
2557 format = new IdentifyFile().identify(url, DataSourceType.URL);
2558 } catch (FileFormatException e)
2560 // TODO revise error handling, distinguish between
2561 // URL not found and response not valid
2566 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
2567 MessageManager.formatMessage("label.couldnt_locate",
2570 MessageManager.getString("label.url_not_found"),
2571 JvOptionPane.WARNING_MESSAGE);
2576 if (viewport != null)
2578 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
2583 new FileLoader().LoadFile(url, DataSourceType.URL, format);
2592 private static final float ONE_MB = 1048576f;
2594 boolean showMemoryUsage = false;
2598 java.text.NumberFormat df;
2600 float maxMemory, allocatedMemory, freeMemory, totalFreeMemory,
2603 public MyDesktopPane(boolean showMemoryUsage)
2605 showMemoryUsage(showMemoryUsage);
2608 public void showMemoryUsage(boolean showMemory)
2610 this.showMemoryUsage = showMemory;
2613 Thread worker = new Thread(this);
2619 public boolean isShowMemoryUsage()
2621 return showMemoryUsage;
2627 df = java.text.NumberFormat.getNumberInstance();
2628 df.setMaximumFractionDigits(2);
2629 runtime = Runtime.getRuntime();
2631 while (showMemoryUsage)
2635 maxMemory = runtime.maxMemory() / ONE_MB;
2636 allocatedMemory = runtime.totalMemory() / ONE_MB;
2637 freeMemory = runtime.freeMemory() / ONE_MB;
2638 totalFreeMemory = freeMemory + (maxMemory - allocatedMemory);
2640 percentUsage = (totalFreeMemory / maxMemory) * 100;
2642 // if (percentUsage < 20)
2644 // border1 = BorderFactory.createMatteBorder(12, 12, 12, 12,
2646 // instance.set.setBorder(border1);
2649 // sleep after showing usage
2651 } catch (Exception ex)
2653 ex.printStackTrace();
2659 public void paintComponent(Graphics g)
2661 if (showMemoryUsage && g != null && df != null)
2663 if (percentUsage < 20)
2665 g.setColor(Color.red);
2667 FontMetrics fm = g.getFontMetrics();
2670 g.drawString(MessageManager.formatMessage("label.memory_stats",
2672 { df.format(totalFreeMemory), df.format(maxMemory),
2673 df.format(percentUsage) }),
2674 10, getHeight() - fm.getHeight());
2681 * Accessor method to quickly get all the AlignmentFrames loaded.
2683 * @return an array of AlignFrame, or null if none found
2685 public static AlignFrame[] getAlignFrames()
2687 if (Jalview.isHeadlessMode())
2689 // Desktop.desktop is null in headless mode
2690 return new AlignFrame[] { Jalview.currentAlignFrame };
2693 JInternalFrame[] frames = Desktop.desktop.getAllFrames();
2699 List<AlignFrame> avp = new ArrayList<>();
2701 for (int i = frames.length - 1; i > -1; i--)
2703 if (frames[i] instanceof AlignFrame)
2705 avp.add((AlignFrame) frames[i]);
2707 else if (frames[i] instanceof SplitFrame)
2710 * Also check for a split frame containing an AlignFrame
2712 GSplitFrame sf = (GSplitFrame) frames[i];
2713 if (sf.getTopFrame() instanceof AlignFrame)
2715 avp.add((AlignFrame) sf.getTopFrame());
2717 if (sf.getBottomFrame() instanceof AlignFrame)
2719 avp.add((AlignFrame) sf.getBottomFrame());
2723 if (avp.size() == 0)
2727 AlignFrame afs[] = avp.toArray(new AlignFrame[avp.size()]);
2732 * Returns an array of any AppJmol frames in the Desktop (or null if none).
2736 public GStructureViewer[] getJmols()
2738 JInternalFrame[] frames = Desktop.desktop.getAllFrames();
2744 List<GStructureViewer> avp = new ArrayList<>();
2746 for (int i = frames.length - 1; i > -1; i--)
2748 if (frames[i] instanceof AppJmol)
2750 GStructureViewer af = (GStructureViewer) frames[i];
2754 if (avp.size() == 0)
2758 GStructureViewer afs[] = avp.toArray(new GStructureViewer[avp.size()]);
2763 * Add Groovy Support to Jalview
2766 public void groovyShell_actionPerformed()
2770 openGroovyConsole();
2771 } catch (Exception ex)
2773 jalview.bin.Cache.log.error("Groovy Shell Creation failed.", ex);
2774 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
2776 MessageManager.getString("label.couldnt_create_groovy_shell"),
2777 MessageManager.getString("label.groovy_support_failed"),
2778 JvOptionPane.ERROR_MESSAGE);
2783 * Open the Groovy console
2785 void openGroovyConsole()
2787 if (groovyConsole == null)
2789 groovyConsole = new groovy.ui.Console();
2790 groovyConsole.setVariable("Jalview", this);
2791 groovyConsole.run();
2794 * We allow only one console at a time, so that AlignFrame menu option
2795 * 'Calculate | Run Groovy script' is unambiguous.
2796 * Disable 'Groovy Console', and enable 'Run script', when the console is
2797 * opened, and the reverse when it is closed
2799 Window window = (Window) groovyConsole.getFrame();
2800 window.addWindowListener(new WindowAdapter()
2803 public void windowClosed(WindowEvent e)
2806 * rebind CMD-Q from Groovy Console to Jalview Quit
2809 enableExecuteGroovy(false);
2815 * show Groovy console window (after close and reopen)
2817 ((Window) groovyConsole.getFrame()).setVisible(true);
2820 * if we got this far, enable 'Run Groovy' in AlignFrame menus
2821 * and disable opening a second console
2823 enableExecuteGroovy(true);
2827 * Bind Ctrl/Cmd-Q to Quit - for reset as Groovy Console takes over this binding
2830 protected void addQuitHandler()
2832 getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
2833 .put(KeyStroke.getKeyStroke(KeyEvent.VK_Q,
2834 Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()),
2836 getRootPane().getActionMap().put("Quit", new AbstractAction()
2839 public void actionPerformed(ActionEvent e)
2847 * Enable or disable 'Run Groovy script' in AlignFrame calculate menus
2850 * true if Groovy console is open
2852 public void enableExecuteGroovy(boolean enabled)
2855 * disable opening a second Groovy console
2856 * (or re-enable when the console is closed)
2858 groovyShell.setEnabled(!enabled);
2860 AlignFrame[] alignFrames = getAlignFrames();
2861 if (alignFrames != null)
2863 for (AlignFrame af : alignFrames)
2865 af.setGroovyEnabled(enabled);
2871 * Progress bars managed by the IProgressIndicator method.
2873 private Hashtable<Long, JPanel> progressBars;
2875 private Hashtable<Long, IProgressIndicatorHandler> progressBarHandlers;
2880 * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
2883 public void setProgressBar(String message, long id)
2885 if (progressBars == null)
2887 progressBars = new Hashtable<>();
2888 progressBarHandlers = new Hashtable<>();
2891 if (progressBars.get(new Long(id)) != null)
2893 JPanel panel = progressBars.remove(new Long(id));
2894 if (progressBarHandlers.contains(new Long(id)))
2896 progressBarHandlers.remove(new Long(id));
2898 removeProgressPanel(panel);
2902 progressBars.put(new Long(id), addProgressPanel(message));
2909 * @see jalview.gui.IProgressIndicator#registerHandler(long,
2910 * jalview.gui.IProgressIndicatorHandler)
2913 public void registerHandler(final long id,
2914 final IProgressIndicatorHandler handler)
2916 if (progressBarHandlers == null
2917 || !progressBars.containsKey(new Long(id)))
2919 throw new Error(MessageManager.getString(
2920 "error.call_setprogressbar_before_registering_handler"));
2922 progressBarHandlers.put(new Long(id), handler);
2923 final JPanel progressPanel = progressBars.get(new Long(id));
2924 if (handler.canCancel())
2926 JButton cancel = new JButton(
2927 MessageManager.getString("action.cancel"));
2928 final IProgressIndicator us = this;
2929 cancel.addActionListener(new ActionListener()
2933 public void actionPerformed(ActionEvent e)
2935 handler.cancelActivity(id);
2936 us.setProgressBar(MessageManager
2937 .formatMessage("label.cancelled_params", new Object[]
2938 { ((JLabel) progressPanel.getComponent(0)).getText() }),
2942 progressPanel.add(cancel, BorderLayout.EAST);
2948 * @return true if any progress bars are still active
2951 public boolean operationInProgress()
2953 if (progressBars != null && progressBars.size() > 0)
2961 * This will return the first AlignFrame holding the given viewport instance. It
2962 * will break if there are more than one AlignFrames viewing a particular av.
2965 * @return alignFrame for viewport
2967 public static AlignFrame getAlignFrameFor(AlignViewportI viewport)
2969 if (desktop != null)
2971 AlignmentPanel[] aps = getAlignmentPanels(
2972 viewport.getSequenceSetId());
2973 for (int panel = 0; aps != null && panel < aps.length; panel++)
2975 if (aps[panel] != null && aps[panel].av == viewport)
2977 return aps[panel].alignFrame;
2984 public VamsasApplication getVamsasApplication()
2991 * flag set if jalview GUI is being operated programmatically
2993 private boolean inBatchMode = false;
2996 * check if jalview GUI is being operated programmatically
2998 * @return inBatchMode
3000 public boolean isInBatchMode()
3006 * set flag if jalview GUI is being operated programmatically
3008 * @param inBatchMode
3010 public void setInBatchMode(boolean inBatchMode)
3012 this.inBatchMode = inBatchMode;
3015 public void startServiceDiscovery()
3017 startServiceDiscovery(false);
3020 public void startServiceDiscovery(boolean blocking)
3022 boolean alive = true;
3023 Thread t0 = null, t1 = null, t2 = null;
3024 // JAL-940 - JALVIEW 1 services are now being EOLed as of JABA 2.1 release
3027 // todo: changesupport handlers need to be transferred
3028 if (discoverer == null)
3030 discoverer = new jalview.ws.jws1.Discoverer();
3031 // register PCS handler for desktop.
3032 discoverer.addPropertyChangeListener(changeSupport);
3034 // JAL-940 - disabled JWS1 service configuration - always start discoverer
3035 // until we phase out completely
3036 (t0 = new Thread(discoverer)).start();
3039 if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
3041 t2 = jalview.ws.jws2.Jws2Discoverer.getDiscoverer()
3042 .startDiscoverer(changeSupport);
3046 // TODO: do rest service discovery
3055 } catch (Exception e)
3058 alive = (t1 != null && t1.isAlive()) || (t2 != null && t2.isAlive())
3059 || (t3 != null && t3.isAlive())
3060 || (t0 != null && t0.isAlive());
3066 * called to check if the service discovery process completed successfully.
3070 protected void JalviewServicesChanged(PropertyChangeEvent evt)
3072 if (evt.getNewValue() == null || evt.getNewValue() instanceof Vector)
3074 final String ermsg = jalview.ws.jws2.Jws2Discoverer.getDiscoverer()
3075 .getErrorMessages();
3078 if (Cache.getDefault("SHOW_WSDISCOVERY_ERRORS", true))
3080 if (serviceChangedDialog == null)
3082 // only run if we aren't already displaying one of these.
3083 addDialogThread(serviceChangedDialog = new Runnable()
3090 * JalviewDialog jd =new JalviewDialog() {
3092 * @Override protected void cancelPressed() { // TODO
3093 * Auto-generated method stub
3095 * }@Override protected void okPressed() { // TODO
3096 * Auto-generated method stub
3098 * }@Override protected void raiseClosed() { // TODO
3099 * Auto-generated method stub
3101 * } }; jd.initDialogFrame(new
3102 * JLabel("<html><table width=\"450\"><tr><td>" + ermsg +
3103 * "<br/>It may be that you have invalid JABA URLs in your web service preferences,"
3104 * + " or mis-configured HTTP proxy settings.<br/>" +
3105 * "Check the <em>Connections</em> and <em>Web services</em> tab of the"
3107 * " Tools->Preferences dialog box to change them.</td></tr></table></html>"
3108 * ), true, true, "Web Service Configuration Problem", 450,
3111 * jd.waitForInput();
3113 JvOptionPane.showConfirmDialog(Desktop.desktop,
3114 new JLabel("<html><table width=\"450\"><tr><td>"
3115 + ermsg + "</td></tr></table>"
3116 + "<p>It may be that you have invalid JABA URLs<br/>in your web service preferences,"
3117 + "<br>or as a command-line argument, or mis-configured HTTP proxy settings.</p>"
3118 + "<p>Check the <em>Connections</em> and <em>Web services</em> tab<br/>of the"
3119 + " Tools->Preferences dialog box to change them.</p></html>"),
3120 "Web Service Configuration Problem",
3121 JvOptionPane.DEFAULT_OPTION,
3122 JvOptionPane.ERROR_MESSAGE);
3123 serviceChangedDialog = null;
3132 "Errors reported by JABA discovery service. Check web services preferences.\n"
3139 private Runnable serviceChangedDialog = null;
3142 * start a thread to open a URL in the configured browser. Pops up a warning
3143 * dialog to the user if there is an exception when calling out to the browser
3148 public static void showUrl(final String url)
3150 showUrl(url, Desktop.instance);
3154 * Like showUrl but allows progress handler to be specified
3158 * (null) or object implementing IProgressIndicator
3160 public static void showUrl(final String url,
3161 final IProgressIndicator progress)
3163 new Thread(new Runnable()
3170 if (progress != null)
3172 progress.setProgressBar(MessageManager
3173 .formatMessage("status.opening_params", new Object[]
3174 { url }), this.hashCode());
3176 jalview.util.BrowserLauncher.openURL(url);
3177 } catch (Exception ex)
3179 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
3181 .getString("label.web_browser_not_found_unix"),
3182 MessageManager.getString("label.web_browser_not_found"),
3183 JvOptionPane.WARNING_MESSAGE);
3185 ex.printStackTrace();
3187 if (progress != null)
3189 progress.setProgressBar(null, this.hashCode());
3195 public static WsParamSetManager wsparamManager = null;
3197 public static ParamManager getUserParameterStore()
3199 if (wsparamManager == null)
3201 wsparamManager = new WsParamSetManager();
3203 return wsparamManager;
3207 * static hyperlink handler proxy method for use by Jalview's internal windows
3211 public static void hyperlinkUpdate(HyperlinkEvent e)
3213 if (e.getEventType() == EventType.ACTIVATED)
3218 url = e.getURL().toString();
3219 Desktop.showUrl(url);
3220 } catch (Exception x)
3224 if (Cache.log != null)
3226 Cache.log.error("Couldn't handle string " + url + " as a URL.");
3231 "Couldn't handle string " + url + " as a URL.");
3234 // ignore any exceptions due to dud links.
3241 * single thread that handles display of dialogs to user.
3243 ExecutorService dialogExecutor = Executors.newSingleThreadExecutor();
3246 * flag indicating if dialogExecutor should try to acquire a permit
3248 private volatile boolean dialogPause = true;
3253 private java.util.concurrent.Semaphore block = new Semaphore(0);
3255 private static groovy.ui.Console groovyConsole;
3258 * add another dialog thread to the queue
3262 public void addDialogThread(final Runnable prompter)
3264 dialogExecutor.submit(new Runnable()
3274 } catch (InterruptedException x)
3279 if (instance == null)
3285 SwingUtilities.invokeAndWait(prompter);
3286 } catch (Exception q)
3288 Cache.log.warn("Unexpected Exception in dialog thread.", q);
3294 public void startDialogQueue()
3296 // set the flag so we don't pause waiting for another permit and semaphore
3297 // the current task to begin
3298 dialogPause = false;
3303 protected void snapShotWindow_actionPerformed(ActionEvent e)
3307 ImageMaker im = new jalview.util.ImageMaker(
3308 this, ImageMaker.TYPE.EPS, "View of Desktop", getWidth(),
3309 getHeight(), of = new File("Jalview_snapshot"
3310 + System.currentTimeMillis() + ".eps"),
3311 "View of desktop", null, 0, false);
3314 paintAll(im.getGraphics());
3316 } catch (Exception q)
3318 Cache.log.error("Couldn't write snapshot to " + of.getAbsolutePath(),
3322 Cache.log.info("Successfully written snapshot to file "
3323 + of.getAbsolutePath());
3327 * Explode the views in the given SplitFrame into separate SplitFrame windows.
3328 * This respects (remembers) any previous 'exploded geometry' i.e. the size and
3329 * location last time the view was expanded (if any). However it does not
3330 * remember the split pane divider location - this is set to match the
3331 * 'exploding' frame.
3335 public void explodeViews(SplitFrame sf)
3337 AlignFrame oldTopFrame = (AlignFrame) sf.getTopFrame();
3338 AlignFrame oldBottomFrame = (AlignFrame) sf.getBottomFrame();
3339 List<? extends AlignmentViewPanel> topPanels = oldTopFrame
3341 List<? extends AlignmentViewPanel> bottomPanels = oldBottomFrame
3343 int viewCount = topPanels.size();
3350 * Processing in reverse order works, forwards order leaves the first panels
3351 * not visible. I don't know why!
3353 for (int i = viewCount - 1; i >= 0; i--)
3356 * Make new top and bottom frames. These take over the respective
3357 * AlignmentPanel objects, including their AlignmentViewports, so the
3358 * cdna/protein relationships between the viewports is carried over to the
3361 * explodedGeometry holds the (x, y) position of the previously exploded
3362 * SplitFrame, and the (width, height) of the AlignFrame component
3364 AlignmentPanel topPanel = (AlignmentPanel) topPanels.get(i);
3365 AlignFrame newTopFrame = new AlignFrame(topPanel);
3366 newTopFrame.setSize(oldTopFrame.getSize());
3367 newTopFrame.setVisible(true);
3368 Rectangle geometry = ((AlignViewport) topPanel.getAlignViewport())
3369 .getExplodedGeometry();
3370 if (geometry != null)
3372 newTopFrame.setSize(geometry.getSize());
3375 AlignmentPanel bottomPanel = (AlignmentPanel) bottomPanels.get(i);
3376 AlignFrame newBottomFrame = new AlignFrame(bottomPanel);
3377 newBottomFrame.setSize(oldBottomFrame.getSize());
3378 newBottomFrame.setVisible(true);
3379 geometry = ((AlignViewport) bottomPanel.getAlignViewport())
3380 .getExplodedGeometry();
3381 if (geometry != null)
3383 newBottomFrame.setSize(geometry.getSize());
3386 topPanel.av.setGatherViewsHere(false);
3387 bottomPanel.av.setGatherViewsHere(false);
3388 JInternalFrame splitFrame = new SplitFrame(newTopFrame,
3390 if (geometry != null)
3392 splitFrame.setLocation(geometry.getLocation());
3394 Desktop.addInternalFrame(splitFrame, sf.getTitle(), -1, -1);
3398 * Clear references to the panels (now relocated in the new SplitFrames)
3399 * before closing the old SplitFrame.
3402 bottomPanels.clear();
3407 * Gather expanded split frames, sharing the same pairs of sequence set ids,
3408 * back into the given SplitFrame as additional views. Note that the gathered
3409 * frames may themselves have multiple views.
3413 public void gatherViews(GSplitFrame source)
3416 * special handling of explodedGeometry for a view within a SplitFrame: - it
3417 * holds the (x, y) position of the enclosing SplitFrame, and the (width,
3418 * height) of the AlignFrame component
3420 AlignFrame myTopFrame = (AlignFrame) source.getTopFrame();
3421 AlignFrame myBottomFrame = (AlignFrame) source.getBottomFrame();
3422 myTopFrame.viewport.setExplodedGeometry(new Rectangle(source.getX(),
3423 source.getY(), myTopFrame.getWidth(), myTopFrame.getHeight()));
3424 myBottomFrame.viewport
3425 .setExplodedGeometry(new Rectangle(source.getX(), source.getY(),
3426 myBottomFrame.getWidth(), myBottomFrame.getHeight()));
3427 myTopFrame.viewport.setGatherViewsHere(true);
3428 myBottomFrame.viewport.setGatherViewsHere(true);
3429 String topViewId = myTopFrame.viewport.getSequenceSetId();
3430 String bottomViewId = myBottomFrame.viewport.getSequenceSetId();
3432 JInternalFrame[] frames = desktop.getAllFrames();
3433 for (JInternalFrame frame : frames)
3435 if (frame instanceof SplitFrame && frame != source)
3437 SplitFrame sf = (SplitFrame) frame;
3438 AlignFrame topFrame = (AlignFrame) sf.getTopFrame();
3439 AlignFrame bottomFrame = (AlignFrame) sf.getBottomFrame();
3440 boolean gatherThis = false;
3441 for (int a = 0; a < topFrame.alignPanels.size(); a++)
3443 AlignmentPanel topPanel = topFrame.alignPanels.get(a);
3444 AlignmentPanel bottomPanel = bottomFrame.alignPanels.get(a);
3445 if (topViewId.equals(topPanel.av.getSequenceSetId())
3446 && bottomViewId.equals(bottomPanel.av.getSequenceSetId()))
3449 topPanel.av.setGatherViewsHere(false);
3450 bottomPanel.av.setGatherViewsHere(false);
3451 topPanel.av.setExplodedGeometry(
3452 new Rectangle(sf.getLocation(), topFrame.getSize()));
3453 bottomPanel.av.setExplodedGeometry(
3454 new Rectangle(sf.getLocation(), bottomFrame.getSize()));
3455 myTopFrame.addAlignmentPanel(topPanel, false);
3456 myBottomFrame.addAlignmentPanel(bottomPanel, false);
3462 topFrame.getAlignPanels().clear();
3463 bottomFrame.getAlignPanels().clear();
3470 * The dust settles...give focus to the tab we did this from.
3472 myTopFrame.setDisplayedView(myTopFrame.alignPanel);
3475 public static groovy.ui.Console getGroovyConsole()
3477 return groovyConsole;
3481 * handles the payload of a drag and drop event.
3483 * TODO refactor to desktop utilities class
3486 * - Data source strings extracted from the drop event
3488 * - protocol for each data source extracted from the drop event
3492 * - the payload from the drop event
3495 public static void transferFromDropTarget(List<Object> files,
3496 List<DataSourceType> protocols, DropTargetDropEvent evt,
3497 Transferable t) throws Exception
3500 // BH 2018 changed List<String> to List<Object> to allow for File from SwingJS
3502 // DataFlavor[] flavors = t.getTransferDataFlavors();
3503 // for (int i = 0; i < flavors.length; i++) {
3504 // if (flavors[i].isFlavorJavaFileListType()) {
3505 // evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
3506 // List<File> list = (List<File>) t.getTransferData(flavors[i]);
3507 // for (int j = 0; j < list.size(); j++) {
3508 // File file = (File) list.get(j);
3509 // byte[] data = getDroppedFileBytes(file);
3510 // fileName.setText(file.getName() + " - " + data.length + " " +
3511 // evt.getLocation());
3512 // JTextArea target = (JTextArea) ((DropTarget) evt.getSource()).getComponent();
3513 // target.setText(new String(data));
3515 // dtde.dropComplete(true);
3520 DataFlavor uriListFlavor = new DataFlavor(
3521 "text/uri-list;class=java.lang.String"), urlFlavour = null;
3524 urlFlavour = new DataFlavor(
3525 "application/x-java-url; class=java.net.URL");
3526 } catch (ClassNotFoundException cfe)
3528 Cache.log.debug("Couldn't instantiate the URL dataflavor.", cfe);
3531 if (urlFlavour != null && t.isDataFlavorSupported(urlFlavour))
3536 java.net.URL url = (URL) t.getTransferData(urlFlavour);
3537 // nb: java 8 osx bug https://bugs.openjdk.java.net/browse/JDK-8156099
3538 // means url may be null.
3541 protocols.add(DataSourceType.URL);
3542 files.add(url.toString());
3543 Cache.log.debug("Drop handled as URL dataflavor "
3544 + files.get(files.size() - 1));
3549 if (Platform.isAMac())
3552 "Please ignore plist error - occurs due to problem with java 8 on OSX");
3556 } catch (Throwable ex)
3558 Cache.log.debug("URL drop handler failed.", ex);
3561 if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
3563 // Works on Windows and MacOSX
3564 Cache.log.debug("Drop handled as javaFileListFlavor");
3565 for (Object file : (List) t
3566 .getTransferData(DataFlavor.javaFileListFlavor))
3569 protocols.add(DataSourceType.FILE);
3574 // Unix like behaviour
3575 boolean added = false;
3577 if (t.isDataFlavorSupported(uriListFlavor))
3579 Cache.log.debug("Drop handled as uriListFlavor");
3580 // This is used by Unix drag system
3581 data = (String) t.getTransferData(uriListFlavor);
3585 // fallback to text: workaround - on OSX where there's a JVM bug
3586 Cache.log.debug("standard URIListFlavor failed. Trying text");
3587 // try text fallback
3588 DataFlavor textDf = new DataFlavor(
3589 "text/plain;class=java.lang.String");
3590 if (t.isDataFlavorSupported(textDf))
3592 data = (String) t.getTransferData(textDf);
3595 Cache.log.debug("Plain text drop content returned "
3596 + (data == null ? "Null - failed" : data));
3601 while (protocols.size() < files.size())
3603 Cache.log.debug("Adding missing FILE protocol for "
3604 + files.get(protocols.size()));
3605 protocols.add(DataSourceType.FILE);
3607 for (java.util.StringTokenizer st = new java.util.StringTokenizer(
3608 data, "\r\n"); st.hasMoreTokens();)
3611 String s = st.nextToken();
3612 if (s.startsWith("#"))
3614 // the line is a comment (as per the RFC 2483)
3617 java.net.URI uri = new java.net.URI(s);
3618 if (uri.getScheme().toLowerCase().startsWith("http"))
3620 protocols.add(DataSourceType.URL);
3621 files.add(uri.toString());
3625 // otherwise preserve old behaviour: catch all for file objects
3626 java.io.File file = new java.io.File(uri);
3627 protocols.add(DataSourceType.FILE);
3628 files.add(file.toString());
3633 if (Cache.log.isDebugEnabled())
3635 if (data == null || !added)
3638 if (t.getTransferDataFlavors() != null
3639 && t.getTransferDataFlavors().length > 0)
3642 "Couldn't resolve drop data. Here are the supported flavors:");
3643 for (DataFlavor fl : t.getTransferDataFlavors())
3646 "Supported transfer dataflavor: " + fl.toString());
3647 Object df = t.getTransferData(fl);
3650 Cache.log.debug("Retrieves: " + df);
3654 Cache.log.debug("Retrieved nothing");
3660 Cache.log.debug("Couldn't resolve dataflavor for drop: "
3666 if (Platform.isWindows())
3669 Cache.log.debug("Scanning dropped content for Windows Link Files");
3671 // resolve any .lnk files in the file drop
3672 for (int f = 0; f < files.size(); f++)
3674 String source = files.get(f).toString().toLowerCase();
3675 if (protocols.get(f).equals(DataSourceType.FILE)
3676 && (source.endsWith(".lnk") || source.endsWith(".url")
3677 || source.endsWith(".site")))
3681 Object obj = files.get(f);
3682 File lf = (obj instanceof File ? (File) obj
3683 : new File((String) obj));
3684 // process link file to get a URL
3685 Cache.log.debug("Found potential link file: " + lf);
3686 WindowsShortcut wscfile = new WindowsShortcut(lf);
3687 String fullname = wscfile.getRealFilename();
3688 protocols.set(f, FormatAdapter.checkProtocol(fullname));
3689 files.set(f, fullname);
3690 Cache.log.debug("Parsed real filename " + fullname
3691 + " to extract protocol: " + protocols.get(f));
3692 } catch (Exception ex)
3695 "Couldn't parse " + files.get(f) + " as a link file.",
3704 * Sets the Preferences property for experimental features to True or False
3705 * depending on the state of the controlling menu item
3708 protected void showExperimental_actionPerformed(boolean selected)
3710 Cache.setProperty(EXPERIMENTAL_FEATURES, Boolean.toString(selected));
3714 * Answers a (possibly empty) list of any structure viewer frames (currently for
3715 * either Jmol or Chimera) which are currently open. This may optionally be
3716 * restricted to viewers of a specified class, or viewers linked to a specified
3720 * if not null, only return viewers linked to this panel
3721 * @param structureViewerClass
3722 * if not null, only return viewers of this class
3725 public List<StructureViewerBase> getStructureViewers(
3726 AlignmentPanel apanel,
3727 Class<? extends StructureViewerBase> structureViewerClass)
3729 List<StructureViewerBase> result = new ArrayList<>();
3730 JInternalFrame[] frames = Desktop.instance.getAllFrames();
3732 for (JInternalFrame frame : frames)
3734 if (frame instanceof StructureViewerBase)
3736 if (structureViewerClass == null
3737 || structureViewerClass.isInstance(frame))
3740 || ((StructureViewerBase) frame).isLinkedWith(apanel))
3742 result.add((StructureViewerBase) frame);