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);
369 showMemusage.setSelected(selmemusage);
370 desktop.setBackground(Color.white);
371 getContentPane().setLayout(new BorderLayout());
372 // alternate config - have scrollbars - see notes in JAL-153
373 // JScrollPane sp = new JScrollPane();
374 // sp.getViewport().setView(desktop);
375 // getContentPane().add(sp, BorderLayout.CENTER);
377 // BH 2018 - just an experiment to try unclipped JInternalFrames.
378 // Must set for all three to be active:
381 getRootPane().putClientProperty("swingjs.overflow.hidden", "false");
382 ((JComponent) getContentPane()).putClientProperty("swingjs.overflow.hidden", "false");
383 desktop.putClientProperty("swingjs.overflow.hidden", "false");
386 getContentPane().add(desktop, BorderLayout.CENTER);
387 desktop.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
389 // This line prevents Windows Look&Feel resizing all new windows to maximum
390 // if previous window was maximised
391 desktop.setDesktopManager(new MyDesktopManager(
392 (Platform.isWindows() ? new DefaultDesktopManager()
394 ? new AquaInternalFrameManager(
395 desktop.getDesktopManager())
396 : desktop.getDesktopManager())));
398 Rectangle dims = getLastKnownDimensions("");
405 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
406 int xPos = Math.max(5, (screenSize.width - 900) / 2);
407 int yPos = Math.max(5, (screenSize.height - 650) / 2);
408 setBounds(xPos, yPos, 900, 650);
411 boolean doFullLoad = /** @j2sNative ! */true;
415 jconsole = new Console(this, showjconsole);
416 // add essential build information
417 jconsole.setHeader("Jalview Version: "
418 + jalview.bin.Cache.getProperty("VERSION") + "\n"
419 + "Jalview Installation: "
420 + jalview.bin.Cache.getDefault("INSTALLATION", "unknown")
421 + "\n" + "Build Date: "
422 + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown") + "\n"
423 + "Java version: " + System.getProperty("java.version") + "\n"
424 + System.getProperty("os.arch") + " "
425 + System.getProperty("os.name") + " "
426 + System.getProperty("os.version"));
428 showConsole(showjconsole);
430 showNews.setVisible(false);
432 experimentalFeatures.setSelected(showExperimental());
434 getIdentifiersOrgData();
438 // Spawn a thread that shows the splashscreen
440 SwingUtilities.invokeLater(new Runnable()
449 // Thread off a new instance of the file chooser - this reduces the time it
450 // takes to open it later on.
451 new Thread(new Runnable()
456 Cache.log.debug("Filechooser init thread started.");
457 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
458 JalviewFileChooser.forRead(Cache.getProperty("LAST_DIRECTORY"),
460 Cache.log.debug("Filechooser init thread finished.");
463 // Add the service change listener
464 changeSupport.addJalviewPropertyChangeListener("services",
465 new PropertyChangeListener()
469 public void propertyChange(PropertyChangeEvent evt)
471 Cache.log.debug("Firing service changed event for "
472 + evt.getNewValue());
473 JalviewServicesChanged(evt);
480 this.setDropTarget(new java.awt.dnd.DropTarget(desktop, this));
482 this.addWindowListener(new WindowAdapter()
485 public void windowClosing(WindowEvent evt)
492 this.addMouseListener(ma = new MouseAdapter()
495 public void mousePressed(MouseEvent evt)
497 if (evt.isPopupTrigger()) // Mac
499 showPasteMenu(evt.getX(), evt.getY());
504 public void mouseReleased(MouseEvent evt)
506 if (evt.isPopupTrigger()) // Windows
508 showPasteMenu(evt.getX(), evt.getY());
512 desktop.addMouseListener(ma);
517 * Answers true if user preferences to enable experimental features is True
522 public boolean showExperimental()
524 String experimental = Cache.getDefault(EXPERIMENTAL_FEATURES,
525 Boolean.FALSE.toString());
526 return Boolean.valueOf(experimental).booleanValue();
529 public void doConfigureStructurePrefs()
531 // configure services
532 StructureSelectionManager ssm = StructureSelectionManager
533 .getStructureSelectionManager(this);
534 if (jalview.bin.Cache.getDefault(Preferences.ADD_SS_ANN, true))
536 ssm.setAddTempFacAnnot(jalview.bin.Cache
537 .getDefault(Preferences.ADD_TEMPFACT_ANN, true));
538 ssm.setProcessSecondaryStructure(jalview.bin.Cache
539 .getDefault(Preferences.STRUCT_FROM_PDB, true));
540 ssm.setSecStructServices(
541 jalview.bin.Cache.getDefault(Preferences.USE_RNAVIEW, true));
545 ssm.setAddTempFacAnnot(false);
546 ssm.setProcessSecondaryStructure(false);
547 ssm.setSecStructServices(false);
551 public void checkForNews()
553 final Desktop me = this;
554 // Thread off the news reader, in case there are connection problems.
555 new Thread(new Runnable()
560 Cache.log.debug("Starting news thread.");
561 jvnews = new BlogReader(me);
562 showNews.setVisible(true);
563 Cache.log.debug("Completed news thread.");
568 public void getIdentifiersOrgData()
570 // Thread off the identifiers fetcher
571 new Thread(new Runnable()
576 Cache.log.debug("Downloading data from identifiers.org");
577 UrlDownloadClient client = new UrlDownloadClient();
580 client.download(IdOrgSettings.getUrl(),
581 IdOrgSettings.getDownloadLocation());
582 } catch (IOException e)
584 Cache.log.debug("Exception downloading identifiers.org data"
593 protected void showNews_actionPerformed(ActionEvent e)
595 showNews(showNews.isSelected());
598 void showNews(boolean visible)
600 Cache.log.debug((visible ? "Showing" : "Hiding") + " news.");
601 showNews.setSelected(visible);
602 if (visible && !jvnews.isVisible())
604 new Thread(new Runnable()
609 long now = System.currentTimeMillis();
610 Desktop.instance.setProgressBar(
611 MessageManager.getString("status.refreshing_news"), now);
612 jvnews.refreshNews();
613 Desktop.instance.setProgressBar(null, now);
621 * recover the last known dimensions for a jalview window
624 * - empty string is desktop, all other windows have unique prefix
625 * @return null or last known dimensions scaled to current geometry (if last
626 * window geom was known)
628 Rectangle getLastKnownDimensions(String windowName)
630 // TODO: lock aspect ratio for scaling desktop Bug #0058199
631 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
632 String x = jalview.bin.Cache.getProperty(windowName + "SCREEN_X");
633 String y = jalview.bin.Cache.getProperty(windowName + "SCREEN_Y");
634 String width = jalview.bin.Cache
635 .getProperty(windowName + "SCREEN_WIDTH");
636 String height = jalview.bin.Cache
637 .getProperty(windowName + "SCREEN_HEIGHT");
638 if ((x != null) && (y != null) && (width != null) && (height != null))
640 int ix = Integer.parseInt(x), iy = Integer.parseInt(y),
641 iw = Integer.parseInt(width), ih = Integer.parseInt(height);
642 if (jalview.bin.Cache.getProperty("SCREENGEOMETRY_WIDTH") != null)
644 // attempt #1 - try to cope with change in screen geometry - this
645 // version doesn't preserve original jv aspect ratio.
646 // take ratio of current screen size vs original screen size.
647 double sw = ((1f * screenSize.width) / (1f * Integer.parseInt(
648 jalview.bin.Cache.getProperty("SCREENGEOMETRY_WIDTH"))));
649 double sh = ((1f * screenSize.height) / (1f * Integer.parseInt(
650 jalview.bin.Cache.getProperty("SCREENGEOMETRY_HEIGHT"))));
651 // rescale the bounds depending upon the current screen geometry.
652 ix = (int) (ix * sw);
653 iw = (int) (iw * sw);
654 iy = (int) (iy * sh);
655 ih = (int) (ih * sh);
656 while (ix >= screenSize.width)
658 jalview.bin.Cache.log.debug(
659 "Window geometry location recall error: shifting horizontal to within screenbounds.");
660 ix -= screenSize.width;
662 while (iy >= screenSize.height)
664 jalview.bin.Cache.log.debug(
665 "Window geometry location recall error: shifting vertical to within screenbounds.");
666 iy -= screenSize.height;
668 jalview.bin.Cache.log.debug(
669 "Got last known dimensions for " + windowName + ": x:" + ix
670 + " y:" + iy + " width:" + iw + " height:" + ih);
672 // return dimensions for new instance
673 return new Rectangle(ix, iy, iw, ih);
678 private void doVamsasClientCheck()
680 if (Cache.vamsasJarsPresent())
682 setupVamsasDisconnectedGui();
683 VamsasMenu.setVisible(true);
684 final Desktop us = this;
685 VamsasMenu.addMenuListener(new MenuListener()
687 // this listener remembers when the menu was first selected, and
688 // doesn't rebuild the session list until it has been cleared and
690 boolean refresh = true;
693 public void menuCanceled(MenuEvent e)
699 public void menuDeselected(MenuEvent e)
705 public void menuSelected(MenuEvent e)
709 us.buildVamsasStMenu();
714 vamsasStart.setVisible(true);
718 void showPasteMenu(int x, int y)
720 JPopupMenu popup = new JPopupMenu();
721 JMenuItem item = new JMenuItem(
722 MessageManager.getString("label.paste_new_window"));
723 item.addActionListener(new ActionListener()
726 public void actionPerformed(ActionEvent evt)
733 popup.show(this, x, y);
740 Clipboard c = Toolkit.getDefaultToolkit().getSystemClipboard();
741 Transferable contents = c.getContents(this);
743 if (contents != null)
745 String file = (String) contents
746 .getTransferData(DataFlavor.stringFlavor);
748 FileFormatI format = new IdentifyFile().identify(file,
749 DataSourceType.PASTE);
751 new FileLoader().LoadFile(file, DataSourceType.PASTE, format);
754 } catch (Exception ex)
757 "Unable to paste alignment from system clipboard:\n" + ex);
762 * Adds and opens the given frame to the desktop
773 public static synchronized void addInternalFrame(
774 final JInternalFrame frame, String title, int w, int h)
776 addInternalFrame(frame, title, true, w, h, true, false);
780 * Add an internal frame to the Jalview desktop
787 * When true, display frame immediately, otherwise, caller must call
788 * setVisible themselves.
794 public static synchronized void addInternalFrame(
795 final JInternalFrame frame, String title, boolean makeVisible,
798 addInternalFrame(frame, title, makeVisible, w, h, true, false);
802 * Add an internal frame to the Jalview desktop and make it visible
815 public static synchronized void addInternalFrame(
816 final JInternalFrame frame, String title, int w, int h,
819 addInternalFrame(frame, title, true, w, h, resizable, false);
823 * Add an internal frame to the Jalview desktop
830 * When true, display frame immediately, otherwise, caller must call
831 * setVisible themselves.
838 * @param ignoreMinSize
839 * Do not set the default minimum size for frame
841 public static synchronized void addInternalFrame(
842 final JInternalFrame frame, String title, boolean makeVisible,
843 int w, int h, boolean resizable, boolean ignoreMinSize)
846 // TODO: allow callers to determine X and Y position of frame (eg. via
848 // TODO: consider fixing method to update entries in the window submenu with
849 // the current window title
851 frame.setTitle(title);
852 if (frame.getWidth() < 1 || frame.getHeight() < 1)
856 // THIS IS A PUBLIC STATIC METHOD, SO IT MAY BE CALLED EVEN IN
857 // A HEADLESS STATE WHEN NO DESKTOP EXISTS. MUST RETURN
858 // IF JALVIEW IS RUNNING HEADLESS
859 // ///////////////////////////////////////////////
860 if (instance == null || (System.getProperty("java.awt.headless") != null
861 && System.getProperty("java.awt.headless").equals("true")))
870 frame.setMinimumSize(
871 new Dimension(DEFAULT_MIN_WIDTH, DEFAULT_MIN_HEIGHT));
873 // Set default dimension for Alignment Frame window.
874 // The Alignment Frame window could be added from a number of places,
876 // I did this here in order not to miss out on any Alignment frame.
877 if (frame instanceof AlignFrame)
879 frame.setMinimumSize(new Dimension(ALIGN_FRAME_DEFAULT_MIN_WIDTH,
880 ALIGN_FRAME_DEFAULT_MIN_HEIGHT));
884 frame.setVisible(makeVisible);
885 frame.setClosable(true);
886 frame.setResizable(resizable);
887 frame.setMaximizable(resizable);
888 frame.setIconifiable(resizable);
889 frame.setOpaque(/** @j2sNative true || */
892 if (frame.getX() < 1 && frame.getY() < 1)
894 frame.setLocation(xOffset * openFrameCount,
895 yOffset * ((openFrameCount - 1) % 10) + yOffset);
899 * add an entry for the new frame in the Window menu
900 * (and remove it when the frame is closed)
902 final JMenuItem menuItem = new JMenuItem(title);
903 frame.addInternalFrameListener(new InternalFrameAdapter()
906 public void internalFrameActivated(InternalFrameEvent evt)
908 JInternalFrame itf = desktop.getSelectedFrame();
911 if (itf instanceof AlignFrame)
913 Jalview.setCurrentAlignFrame((AlignFrame) itf);
920 public void internalFrameClosed(InternalFrameEvent evt)
922 PaintRefresher.RemoveComponent(frame);
925 * defensive check to prevent frames being
926 * added half off the window
928 if (openFrameCount > 0)
934 * ensure no reference to alignFrame retained by menu item listener
936 if (menuItem.getActionListeners().length > 0)
938 menuItem.removeActionListener(menuItem.getActionListeners()[0]);
940 windowMenu.remove(menuItem);
944 menuItem.addActionListener(new ActionListener()
947 public void actionPerformed(ActionEvent e)
951 frame.setSelected(true);
952 frame.setIcon(false);
953 } catch (java.beans.PropertyVetoException ex)
955 // System.err.println(ex.toString());
960 setKeyBindings(frame);
964 windowMenu.add(menuItem);
969 frame.setSelected(true);
970 frame.requestFocus();
971 } catch (java.beans.PropertyVetoException ve)
973 } catch (java.lang.ClassCastException cex)
976 "Squashed a possible GUI implementation error. If you can recreate this, please look at http://issues.jalview.org/browse/JAL-869",
982 * Add key bindings to a JInternalFrame so that Ctrl-W and Cmd-W will close the
987 private static void setKeyBindings(JInternalFrame frame)
989 @SuppressWarnings("serial")
990 final Action closeAction = new AbstractAction()
993 public void actionPerformed(ActionEvent e)
1000 * set up key bindings for Ctrl-W and Cmd-W, with the same (Close) action
1002 KeyStroke ctrlWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
1003 InputEvent.CTRL_DOWN_MASK);
1004 KeyStroke cmdWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
1005 Toolkit.getDefaultToolkit().getMenuShortcutKeyMask());
1007 InputMap inputMap = frame
1008 .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
1009 String ctrlW = ctrlWKey.toString();
1010 inputMap.put(ctrlWKey, ctrlW);
1011 inputMap.put(cmdWKey, ctrlW);
1013 ActionMap actionMap = frame.getActionMap();
1014 actionMap.put(ctrlW, closeAction);
1018 public void lostOwnership(Clipboard clipboard, Transferable contents)
1022 Desktop.jalviewClipboard = null;
1025 internalCopy = false;
1029 public void dragEnter(DropTargetDragEvent evt)
1034 public void dragExit(DropTargetEvent evt)
1039 public void dragOver(DropTargetDragEvent evt)
1044 public void dropActionChanged(DropTargetDragEvent evt)
1055 public void drop(DropTargetDropEvent evt)
1057 boolean success = true;
1058 // JAL-1552 - acceptDrop required before getTransferable call for
1059 // Java's Transferable for native dnd
1060 evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
1061 Transferable t = evt.getTransferable();
1062 List<Object> files = new ArrayList<>();
1063 List<DataSourceType> protocols = new ArrayList<>();
1067 Desktop.transferFromDropTarget(files, protocols, evt, t);
1068 } catch (Exception e)
1070 e.printStackTrace();
1078 for (int i = 0; i < files.size(); i++)
1080 // BH 2018 File or String
1081 Object file = files.get(i);
1082 String fileName = file.toString();
1083 DataSourceType protocol = (protocols == null)
1084 ? DataSourceType.FILE
1086 FileFormatI format = null;
1088 if (fileName.endsWith(".jar"))
1090 format = FileFormat.Jalview;
1095 format = new IdentifyFile().identify(file, protocol);
1098 new FileLoader().LoadFile(null, file, protocol, format);
1101 } catch (Exception ex)
1106 evt.dropComplete(success); // need this to ensure input focus is properly
1107 // transfered to any new windows created
1117 public void inputLocalFileMenuItem_actionPerformed(AlignViewport viewport)
1119 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
1120 JalviewFileChooser chooser = JalviewFileChooser
1121 .forRead(Cache.getProperty("LAST_DIRECTORY"), fileFormat);
1123 chooser.setFileView(new JalviewFileView());
1124 chooser.setDialogTitle(
1125 MessageManager.getString("label.open_local_file"));
1126 chooser.setToolTipText(MessageManager.getString("action.open"));
1128 chooser.response(new RunResponse(JalviewFileChooser.APPROVE_OPTION)
1134 File selectedFile = chooser.getSelectedFile();
1135 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1137 FileFormatI format = chooser.getSelectedFormat();
1140 * Call IdentifyFile to verify the file contains what its extension implies.
1141 * Skip this step for dynamically added file formats, because
1142 * IdentifyFile does not know how to recognise them.
1144 if (FileFormats.getInstance().isIdentifiable(format))
1148 format = new IdentifyFile().identify(selectedFile,
1149 DataSourceType.FILE);
1150 } catch (FileFormatException e)
1152 // format = null; //??
1156 new FileLoader().LoadFile(viewport, selectedFile,
1157 DataSourceType.FILE, format);
1159 }).showOpenDialog(this);
1163 * Shows a dialog for input of a URL at which to retrieve alignment data
1168 public void inputURLMenuItem_actionPerformed(AlignViewport viewport)
1170 // This construct allows us to have a wider textfield
1172 JLabel label = new JLabel(
1173 MessageManager.getString("label.input_file_url"));
1175 JPanel panel = new JPanel(new GridLayout(2, 1));
1179 * the URL to fetch is
1180 * Java: an editable combobox with history
1181 * JS: (pending JAL-3038) a plain text field
1184 String urlBase = "http://www.";
1187 history = new JTextField(urlBase, 35);
1191 JComboBox<String> asCombo = new JComboBox<>();
1192 asCombo.setPreferredSize(new Dimension(400, 20));
1193 asCombo.setEditable(true);
1194 asCombo.addItem(urlBase);
1195 String historyItems = Cache.getProperty("RECENT_URL");
1196 if (historyItems != null)
1198 for (String token : historyItems.split("\\t"))
1200 asCombo.addItem(token);
1207 Object[] options = new Object[] { MessageManager.getString("action.ok"),
1208 MessageManager.getString("action.cancel") };
1209 RunResponse action = new RunResponse(JvOptionPane.OK_OPTION) {
1213 String url = Jalview.isJS() ? ((JTextField) history).getText()
1214 : ((JComboBox<String>) history).getSelectedItem()
1217 if (url.toLowerCase().endsWith(".jar"))
1219 if (viewport != null)
1221 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1222 FileFormat.Jalview);
1226 new FileLoader().LoadFile(url, DataSourceType.URL,
1227 FileFormat.Jalview);
1232 FileFormatI format = null;
1235 format = new IdentifyFile().identify(url, DataSourceType.URL);
1236 } catch (FileFormatException e)
1238 // TODO revise error handling, distinguish between
1239 // URL not found and response not valid
1244 String msg = MessageManager.formatMessage("label.couldnt_locate", url);
1245 JvOptionPane.showInternalMessageDialog(Desktop.desktop, msg,
1246 MessageManager.getString("label.url_not_found"),
1247 JvOptionPane.WARNING_MESSAGE);
1252 if (viewport != null)
1254 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1259 new FileLoader().LoadFile(url, DataSourceType.URL, format);
1263 String dialogOption = MessageManager
1264 .getString("label.input_alignment_from_url");
1265 JvOptionPane.newOptionDialog(desktop).response(action)
1266 .showInternalDialog(panel, dialogOption,
1267 JvOptionPane.YES_NO_CANCEL_OPTION,
1268 JvOptionPane.PLAIN_MESSAGE, null, options,
1269 MessageManager.getString("action.ok"));
1273 * Opens the CutAndPaste window for the user to paste an alignment in to
1276 * - if not null, the pasted alignment is added to the current
1277 * alignment; if null, to a new alignment window
1280 public void inputTextboxMenuItem_actionPerformed(
1281 AlignmentViewPanel viewPanel)
1283 CutAndPasteTransfer cap = new CutAndPasteTransfer();
1284 cap.setForInput(viewPanel);
1285 Desktop.addInternalFrame(cap,
1286 MessageManager.getString("label.cut_paste_alignmen_file"), true,
1296 Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
1297 jalview.bin.Cache.setProperty("SCREENGEOMETRY_WIDTH",
1299 jalview.bin.Cache.setProperty("SCREENGEOMETRY_HEIGHT",
1300 screen.height + "");
1301 storeLastKnownDimensions("", new Rectangle(getBounds().x, getBounds().y,
1302 getWidth(), getHeight()));
1304 if (jconsole != null)
1306 storeLastKnownDimensions("JAVA_CONSOLE_", jconsole.getBounds());
1307 jconsole.stopConsole();
1311 storeLastKnownDimensions("JALVIEW_RSS_WINDOW_", jvnews.getBounds());
1314 if (dialogExecutor != null)
1316 dialogExecutor.shutdownNow();
1318 closeAll_actionPerformed(null);
1320 if (groovyConsole != null)
1322 // suppress a possible repeat prompt to save script
1323 groovyConsole.setDirty(false);
1324 groovyConsole.exit();
1329 private void storeLastKnownDimensions(String string, Rectangle jc)
1331 jalview.bin.Cache.log.debug("Storing last known dimensions for "
1332 + string + ": x:" + jc.x + " y:" + jc.y + " width:" + jc.width
1333 + " height:" + jc.height);
1335 jalview.bin.Cache.setProperty(string + "SCREEN_X", jc.x + "");
1336 jalview.bin.Cache.setProperty(string + "SCREEN_Y", jc.y + "");
1337 jalview.bin.Cache.setProperty(string + "SCREEN_WIDTH", jc.width + "");
1338 jalview.bin.Cache.setProperty(string + "SCREEN_HEIGHT", jc.height + "");
1348 public void aboutMenuItem_actionPerformed(ActionEvent e)
1350 // StringBuffer message = getAboutMessage(false);
1351 // JvOptionPane.showInternalMessageDialog(Desktop.desktop,
1353 // message.toString(), "About Jalview", JvOptionPane.INFORMATION_MESSAGE);
1354 new Thread(new Runnable()
1359 new SplashScreen(true);
1364 public StringBuffer getAboutMessage(boolean shortv)
1366 StringBuffer message = new StringBuffer();
1367 message.append("<html>");
1370 message.append("<h1><strong>Version: "
1371 + jalview.bin.Cache.getProperty("VERSION")
1372 + "</strong></h1>");
1373 message.append("<strong>Last Updated: <em>"
1374 + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown")
1375 + "</em></strong>");
1381 message.append("<strong>Version "
1382 + jalview.bin.Cache.getProperty("VERSION")
1383 + "; last updated: "
1384 + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown"));
1387 if (jalview.bin.Cache.getDefault("LATEST_VERSION", "Checking")
1388 .equals("Checking"))
1390 message.append("<br>...Checking latest version...</br>");
1392 else if (!jalview.bin.Cache.getDefault("LATEST_VERSION", "Checking")
1393 .equals(jalview.bin.Cache.getProperty("VERSION")))
1395 boolean red = false;
1396 if (jalview.bin.Cache.getProperty("VERSION").toLowerCase()
1397 .indexOf("automated build") == -1)
1400 // Displayed when code version and jnlp version do not match and code
1401 // version is not a development build
1402 message.append("<div style=\"color: #FF0000;font-style: bold;\">");
1405 message.append("<br>!! Version "
1406 + jalview.bin.Cache.getDefault("LATEST_VERSION",
1408 + " is available for download from "
1409 + jalview.bin.Cache.getDefault("www.jalview.org",
1410 "http://www.jalview.org")
1414 message.append("</div>");
1417 message.append("<br>Authors: " + jalview.bin.Cache.getDefault(
1419 "The Jalview Authors (See AUTHORS file for current list)")
1420 + "<br><br>Development managed by The Barton Group, University of Dundee, Scotland, UK.<br>"
1421 + "<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"
1422 + "<br><br>If you use Jalview, please cite:"
1423 + "<br>Waterhouse, A.M., Procter, J.B., Martin, D.M.A, Clamp, M. and Barton, G. J. (2009)"
1424 + "<br>Jalview Version 2 - a multiple sequence alignment editor and analysis workbench"
1425 + "<br>Bioinformatics doi: 10.1093/bioinformatics/btp033"
1431 * Action on requesting Help documentation
1434 public void documentationMenuItem_actionPerformed()
1440 BrowserLauncher.openURL("http://www.jalview.org/help.html");
1444 Help.showHelpWindow();
1446 } catch (Exception ex)
1448 System.err.println("Error opening help: " + ex.getMessage());
1453 public void closeAll_actionPerformed(ActionEvent e)
1455 // TODO show a progress bar while closing?
1456 JInternalFrame[] frames = desktop.getAllFrames();
1457 for (int i = 0; i < frames.length; i++)
1461 frames[i].setClosed(true);
1462 } catch (java.beans.PropertyVetoException ex)
1466 Jalview.setCurrentAlignFrame(null);
1467 System.out.println("ALL CLOSED");
1468 if (v_client != null)
1470 // TODO clear binding to vamsas document objects on close_all
1474 * reset state of singleton objects as appropriate (clear down session state
1475 * when all windows are closed)
1477 StructureSelectionManager ssm = StructureSelectionManager
1478 .getStructureSelectionManager(this);
1486 public void raiseRelated_actionPerformed(ActionEvent e)
1488 reorderAssociatedWindows(false, false);
1492 public void minimizeAssociated_actionPerformed(ActionEvent e)
1494 reorderAssociatedWindows(true, false);
1497 void closeAssociatedWindows()
1499 reorderAssociatedWindows(false, true);
1505 * @seejalview.jbgui.GDesktop#garbageCollect_actionPerformed(java.awt.event.
1509 protected void garbageCollect_actionPerformed(ActionEvent e)
1511 // We simply collect the garbage
1512 jalview.bin.Cache.log.debug("Collecting garbage...");
1514 jalview.bin.Cache.log.debug("Finished garbage collection.");
1521 * jalview.jbgui.GDesktop#showMemusage_actionPerformed(java.awt.event.ActionEvent
1525 protected void showMemusage_actionPerformed(ActionEvent e)
1527 desktop.showMemoryUsage(showMemusage.isSelected());
1534 * jalview.jbgui.GDesktop#showConsole_actionPerformed(java.awt.event.ActionEvent
1538 protected void showConsole_actionPerformed(ActionEvent e)
1540 showConsole(showConsole.isSelected());
1543 Console jconsole = null;
1546 * control whether the java console is visible or not
1550 void showConsole(boolean selected)
1552 // TODO: decide if we should update properties file
1553 if (jconsole != null) // BH 2018
1555 showConsole.setSelected(selected);
1556 Cache.setProperty("SHOW_JAVA_CONSOLE",
1557 Boolean.valueOf(selected).toString());
1558 jconsole.setVisible(selected);
1562 void reorderAssociatedWindows(boolean minimize, boolean close)
1564 JInternalFrame[] frames = desktop.getAllFrames();
1565 if (frames == null || frames.length < 1)
1570 AlignmentViewport source = null, target = null;
1571 if (frames[0] instanceof AlignFrame)
1573 source = ((AlignFrame) frames[0]).getCurrentView();
1575 else if (frames[0] instanceof TreePanel)
1577 source = ((TreePanel) frames[0]).getViewPort();
1579 else if (frames[0] instanceof PCAPanel)
1581 source = ((PCAPanel) frames[0]).av;
1583 else if (frames[0].getContentPane() instanceof PairwiseAlignPanel)
1585 source = ((PairwiseAlignPanel) frames[0].getContentPane()).av;
1590 for (int i = 0; i < frames.length; i++)
1593 if (frames[i] == null)
1597 if (frames[i] instanceof AlignFrame)
1599 target = ((AlignFrame) frames[i]).getCurrentView();
1601 else if (frames[i] instanceof TreePanel)
1603 target = ((TreePanel) frames[i]).getViewPort();
1605 else if (frames[i] instanceof PCAPanel)
1607 target = ((PCAPanel) frames[i]).av;
1609 else if (frames[i].getContentPane() instanceof PairwiseAlignPanel)
1611 target = ((PairwiseAlignPanel) frames[i].getContentPane()).av;
1614 if (source == target)
1620 frames[i].setClosed(true);
1624 frames[i].setIcon(minimize);
1627 frames[i].toFront();
1631 } catch (java.beans.PropertyVetoException ex)
1646 protected void preferences_actionPerformed(ActionEvent e)
1652 * Prompts the user to choose a file and then saves the Jalview state as a
1653 * Jalview project file
1656 public void saveState_actionPerformed(boolean asCastor)
1658 JalviewFileChooser chooser = new JalviewFileChooser(
1659 asCastor ? "jvp" : "jvx",
1662 chooser.setFileView(new JalviewFileView());
1663 chooser.setDialogTitle(MessageManager.getString("label.save_state"));
1664 int option = chooser.showSaveDialog(this);
1665 if (option == JalviewFileChooser.APPROVE_OPTION)
1667 File choice = chooser.getSelectedFile();
1668 setProjectFile(choice);
1670 new Thread(new Runnable()
1675 // TODO: refactor to Jalview desktop session controller action.
1676 setProgressBar(MessageManager.formatMessage(
1677 "label.saving_jalview_project", new Object[]
1678 { choice.getName() }), choice.hashCode());
1679 jalview.bin.Cache.setProperty("LAST_DIRECTORY",
1680 choice.getParent());
1681 // TODO catch and handle errors for savestate
1682 // TODO prevent user from messing with the Desktop whilst we're saving
1687 new Jalview2XML().saveState(choice);
1691 new jalview.project.Jalview2XML().saveState(choice);
1693 } catch (OutOfMemoryError oom)
1696 "Whilst saving current state to " + choice.getName(),
1698 } catch (Exception ex)
1701 "Problems whilst trying to save to " + choice.getName(),
1703 JvOptionPane.showMessageDialog(Desktop.this,
1704 MessageManager.formatMessage(
1705 "label.error_whilst_saving_current_state_to",
1707 { choice.getName() }),
1708 MessageManager.getString("label.couldnt_save_project"),
1709 JvOptionPane.WARNING_MESSAGE);
1711 setProgressBar(null, choice.hashCode());
1717 void setProjectFile(File choice)
1719 this.projectFile = choice;
1722 public File getProjectFile()
1724 return this.projectFile;
1728 * Prompts the user to choose a file and loads in as a Jalview project file
1731 public void loadState_actionPerformed(boolean asCastor)
1733 // TODO: GET RID OF .JVX BEFORE RELEASE JIM!
1734 final String[] suffix = asCastor ? new String[] { "jvp", "jar" }
1737 final String[] desc = asCastor
1739 { "Jalview Project", "Jalview Project (old)" }
1741 { "Jalview Project" };
1742 JalviewFileChooser chooser = new JalviewFileChooser(
1743 Cache.getProperty("LAST_DIRECTORY"), suffix,
1746 chooser.setFileView(new JalviewFileView());
1747 chooser.setDialogTitle(MessageManager.getString("label.restore_state"));
1748 chooser.response(new RunResponse(JalviewFileChooser.APPROVE_OPTION)
1753 File selectedFile = chooser.getSelectedFile();
1754 setProjectFile(selectedFile);
1755 final String choice = selectedFile.getAbsolutePath();
1756 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1757 new Thread(new Runnable()
1765 new Jalview2XML().loadJalviewAlign(choice);
1769 new jalview.project.Jalview2XML().loadJalviewAlign(choice);
1771 } catch (OutOfMemoryError oom)
1773 new OOMWarning("Whilst loading project from " + choice, oom);
1774 } catch (Exception ex)
1777 "Problems whilst loading project from " + choice, ex);
1778 JvOptionPane.showMessageDialog(Desktop.desktop,
1779 MessageManager.formatMessage(
1780 "label.error_whilst_loading_project_from",
1783 MessageManager.getString("label.couldnt_load_project"),
1784 JvOptionPane.WARNING_MESSAGE);
1791 chooser.showOpenDialog(this);
1795 public void inputSequence_actionPerformed(ActionEvent e)
1797 new SequenceFetcher(this);
1800 JPanel progressPanel;
1802 ArrayList<JPanel> fileLoadingPanels = new ArrayList<>();
1804 public void startLoading(final Object fileName)
1806 if (fileLoadingCount == 0)
1808 fileLoadingPanels.add(addProgressPanel(MessageManager
1809 .formatMessage("label.loading_file", new Object[]
1815 private JPanel addProgressPanel(String string)
1817 if (progressPanel == null)
1819 progressPanel = new JPanel(new GridLayout(1, 1));
1820 totalProgressCount = 0;
1821 instance.getContentPane().add(progressPanel, BorderLayout.SOUTH);
1823 JPanel thisprogress = new JPanel(new BorderLayout(10, 5));
1824 JProgressBar progressBar = new JProgressBar();
1825 progressBar.setIndeterminate(true);
1827 thisprogress.add(new JLabel(string), BorderLayout.WEST);
1829 thisprogress.add(progressBar, BorderLayout.CENTER);
1830 progressPanel.add(thisprogress);
1831 ((GridLayout) progressPanel.getLayout()).setRows(
1832 ((GridLayout) progressPanel.getLayout()).getRows() + 1);
1833 ++totalProgressCount;
1834 instance.validate();
1835 return thisprogress;
1838 int totalProgressCount = 0;
1840 private void removeProgressPanel(JPanel progbar)
1842 if (progressPanel != null)
1844 synchronized (progressPanel)
1846 progressPanel.remove(progbar);
1847 GridLayout gl = (GridLayout) progressPanel.getLayout();
1848 gl.setRows(gl.getRows() - 1);
1849 if (--totalProgressCount < 1)
1851 this.getContentPane().remove(progressPanel);
1852 progressPanel = null;
1859 public void stopLoading()
1862 if (fileLoadingCount < 1)
1864 while (fileLoadingPanels.size() > 0)
1866 removeProgressPanel(fileLoadingPanels.remove(0));
1868 fileLoadingPanels.clear();
1869 fileLoadingCount = 0;
1874 public static int getViewCount(String alignmentId)
1876 AlignmentViewport[] aps = getViewports(alignmentId);
1877 return (aps == null) ? 0 : aps.length;
1882 * @param alignmentId
1883 * - if null, all sets are returned
1884 * @return all AlignmentPanels concerning the alignmentId sequence set
1886 public static AlignmentPanel[] getAlignmentPanels(String alignmentId)
1888 if (Desktop.desktop == null)
1890 // no frames created and in headless mode
1891 // TODO: verify that frames are recoverable when in headless mode
1894 List<AlignmentPanel> aps = new ArrayList<>();
1895 AlignFrame[] frames = getAlignFrames();
1900 for (AlignFrame af : frames)
1902 for (AlignmentPanel ap : af.alignPanels)
1904 if (alignmentId == null
1905 || alignmentId.equals(ap.av.getSequenceSetId()))
1911 if (aps.size() == 0)
1915 AlignmentPanel[] vap = aps.toArray(new AlignmentPanel[aps.size()]);
1920 * get all the viewports on an alignment.
1922 * @param sequenceSetId
1923 * unique alignment id (may be null - all viewports returned in that
1925 * @return all viewports on the alignment bound to sequenceSetId
1927 public static AlignmentViewport[] getViewports(String sequenceSetId)
1929 List<AlignmentViewport> viewp = new ArrayList<>();
1930 if (desktop != null)
1932 AlignFrame[] frames = Desktop.getAlignFrames();
1934 for (AlignFrame afr : frames)
1936 if (sequenceSetId == null || afr.getViewport().getSequenceSetId()
1937 .equals(sequenceSetId))
1939 if (afr.alignPanels != null)
1941 for (AlignmentPanel ap : afr.alignPanels)
1943 if (sequenceSetId == null
1944 || sequenceSetId.equals(ap.av.getSequenceSetId()))
1952 viewp.add(afr.getViewport());
1956 if (viewp.size() > 0)
1958 return viewp.toArray(new AlignmentViewport[viewp.size()]);
1965 * Explode the views in the given frame into separate AlignFrame
1969 public static void explodeViews(AlignFrame af)
1971 int size = af.alignPanels.size();
1977 for (int i = 0; i < size; i++)
1979 AlignmentPanel ap = af.alignPanels.get(i);
1980 AlignFrame newaf = new AlignFrame(ap);
1983 * Restore the view's last exploded frame geometry if known. Multiple
1984 * views from one exploded frame share and restore the same (frame)
1985 * position and size.
1987 Rectangle geometry = ap.av.getExplodedGeometry();
1988 if (geometry != null)
1990 newaf.setBounds(geometry);
1993 ap.av.setGatherViewsHere(false);
1995 addInternalFrame(newaf, af.getTitle(), AlignFrame.DEFAULT_WIDTH,
1996 AlignFrame.DEFAULT_HEIGHT);
1999 af.alignPanels.clear();
2000 af.closeMenuItem_actionPerformed(true);
2005 * Gather expanded views (separate AlignFrame's) with the same sequence set
2006 * identifier back in to this frame as additional views, and close the expanded
2007 * views. Note the expanded frames may themselves have multiple views. We take
2012 public void gatherViews(AlignFrame source)
2014 source.viewport.setGatherViewsHere(true);
2015 source.viewport.setExplodedGeometry(source.getBounds());
2016 JInternalFrame[] frames = desktop.getAllFrames();
2017 String viewId = source.viewport.getSequenceSetId();
2019 for (int t = 0; t < frames.length; t++)
2021 if (frames[t] instanceof AlignFrame && frames[t] != source)
2023 AlignFrame af = (AlignFrame) frames[t];
2024 boolean gatherThis = false;
2025 for (int a = 0; a < af.alignPanels.size(); a++)
2027 AlignmentPanel ap = af.alignPanels.get(a);
2028 if (viewId.equals(ap.av.getSequenceSetId()))
2031 ap.av.setGatherViewsHere(false);
2032 ap.av.setExplodedGeometry(af.getBounds());
2033 source.addAlignmentPanel(ap, false);
2039 af.alignPanels.clear();
2040 af.closeMenuItem_actionPerformed(true);
2047 jalview.gui.VamsasApplication v_client = null;
2050 public void vamsasImport_actionPerformed(ActionEvent e)
2052 // TODO: JAL-3048 not needed for Jalview-JS
2054 if (v_client == null)
2056 // Load and try to start a session.
2057 JalviewFileChooser chooser = new JalviewFileChooser(
2058 jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
2060 chooser.setFileView(new JalviewFileView());
2061 chooser.setDialogTitle(
2062 MessageManager.getString("label.open_saved_vamsas_session"));
2063 chooser.setToolTipText(MessageManager.getString(
2064 "label.select_vamsas_session_opened_as_new_vamsas_session"));
2066 int value = chooser.showOpenDialog(this);
2068 if (value == JalviewFileChooser.APPROVE_OPTION)
2070 String fle = chooser.getSelectedFile().toString();
2071 if (!vamsasImport(chooser.getSelectedFile()))
2073 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
2074 MessageManager.formatMessage(
2075 "label.couldnt_import_as_vamsas_session",
2079 .getString("label.vamsas_document_import_failed"),
2080 JvOptionPane.ERROR_MESSAGE);
2086 jalview.bin.Cache.log.error(
2087 "Implementation error - load session from a running session is not supported.");
2092 * import file into a new vamsas session (uses jalview.gui.VamsasApplication)
2095 * @return true if import was a success and a session was started.
2097 public boolean vamsasImport(URL url)
2099 // TODO: create progress bar
2100 if (v_client != null)
2103 jalview.bin.Cache.log.error(
2104 "Implementation error - load session from a running session is not supported.");
2110 // copy the URL content to a temporary local file
2111 // TODO: be a bit cleverer here with nio (?!)
2112 File file = File.createTempFile("vdocfromurl", ".vdj");
2113 FileOutputStream fos = new FileOutputStream(file);
2114 BufferedInputStream bis = new BufferedInputStream(url.openStream());
2115 byte[] buffer = new byte[2048];
2117 while ((ln = bis.read(buffer)) > -1)
2119 fos.write(buffer, 0, ln);
2123 v_client = new jalview.gui.VamsasApplication(this, file,
2124 url.toExternalForm());
2125 } catch (Exception ex)
2127 jalview.bin.Cache.log.error(
2128 "Failed to create new vamsas session from contents of URL "
2133 setupVamsasConnectedGui();
2134 v_client.initial_update(); // TODO: thread ?
2135 return v_client.inSession();
2139 * import file into a new vamsas session (uses jalview.gui.VamsasApplication)
2142 * @return true if import was a success and a session was started.
2144 public boolean vamsasImport(File file)
2146 if (v_client != null)
2149 jalview.bin.Cache.log.error(
2150 "Implementation error - load session from a running session is not supported.");
2154 setProgressBar(MessageManager.formatMessage(
2155 "status.importing_vamsas_session_from", new Object[]
2156 { file.getName() }), file.hashCode());
2159 v_client = new jalview.gui.VamsasApplication(this, file, null);
2160 } catch (Exception ex)
2162 setProgressBar(MessageManager.formatMessage(
2163 "status.importing_vamsas_session_from", new Object[]
2164 { file.getName() }), file.hashCode());
2165 jalview.bin.Cache.log.error(
2166 "New vamsas session from existing session file failed:", ex);
2169 setupVamsasConnectedGui();
2170 v_client.initial_update(); // TODO: thread ?
2171 setProgressBar(MessageManager.formatMessage(
2172 "status.importing_vamsas_session_from", new Object[]
2173 { file.getName() }), file.hashCode());
2174 return v_client.inSession();
2177 public boolean joinVamsasSession(String mysesid)
2179 if (v_client != null)
2181 throw new Error(MessageManager
2182 .getString("error.try_join_vamsas_session_another"));
2184 if (mysesid == null)
2187 MessageManager.getString("error.invalid_vamsas_session_id"));
2189 v_client = new VamsasApplication(this, mysesid);
2190 setupVamsasConnectedGui();
2191 v_client.initial_update();
2192 return (v_client.inSession());
2196 public void vamsasStart_actionPerformed(ActionEvent e)
2198 if (v_client == null)
2201 // we just start a default session for moment.
2203 * JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache.
2204 * getProperty("LAST_DIRECTORY"));
2206 * chooser.setFileView(new JalviewFileView());
2207 * chooser.setDialogTitle("Load Vamsas file");
2208 * chooser.setToolTipText("Import");
2210 * int value = chooser.showOpenDialog(this);
2212 * if (value == JalviewFileChooser.APPROVE_OPTION) { v_client = new
2213 * jalview.gui.VamsasApplication(this, chooser.getSelectedFile());
2215 v_client = new VamsasApplication(this);
2216 setupVamsasConnectedGui();
2217 v_client.initial_update(); // TODO: thread ?
2221 // store current data in session.
2222 v_client.push_update(); // TODO: thread
2226 protected void setupVamsasConnectedGui()
2228 vamsasStart.setText(MessageManager.getString("label.session_update"));
2229 vamsasSave.setVisible(true);
2230 vamsasStop.setVisible(true);
2231 vamsasImport.setVisible(false); // Document import to existing session is
2232 // not possible for vamsas-client-1.0.
2235 protected void setupVamsasDisconnectedGui()
2237 vamsasSave.setVisible(false);
2238 vamsasStop.setVisible(false);
2239 vamsasImport.setVisible(true);
2241 .setText(MessageManager.getString("label.new_vamsas_session"));
2245 public void vamsasStop_actionPerformed(ActionEvent e)
2247 if (v_client != null)
2249 v_client.end_session();
2251 setupVamsasDisconnectedGui();
2255 protected void buildVamsasStMenu()
2257 if (v_client == null)
2259 String[] sess = null;
2262 sess = VamsasApplication.getSessionList();
2263 } catch (Exception e)
2265 jalview.bin.Cache.log.warn("Problem getting current sessions list.",
2271 jalview.bin.Cache.log.debug(
2272 "Got current sessions list: " + sess.length + " entries.");
2273 VamsasStMenu.removeAll();
2274 for (int i = 0; i < sess.length; i++)
2276 JMenuItem sessit = new JMenuItem();
2277 sessit.setText(sess[i]);
2278 sessit.setToolTipText(MessageManager
2279 .formatMessage("label.connect_to_session", new Object[]
2281 final Desktop dsktp = this;
2282 final String mysesid = sess[i];
2283 sessit.addActionListener(new ActionListener()
2287 public void actionPerformed(ActionEvent e)
2289 if (dsktp.v_client == null)
2291 Thread rthr = new Thread(new Runnable()
2297 dsktp.v_client = new VamsasApplication(dsktp, mysesid);
2298 dsktp.setupVamsasConnectedGui();
2299 dsktp.v_client.initial_update();
2307 VamsasStMenu.add(sessit);
2309 // don't show an empty menu.
2310 VamsasStMenu.setVisible(sess.length > 0);
2315 jalview.bin.Cache.log.debug("No current vamsas sessions.");
2316 VamsasStMenu.removeAll();
2317 VamsasStMenu.setVisible(false);
2322 // Not interested in the content. Just hide ourselves.
2323 VamsasStMenu.setVisible(false);
2328 public void vamsasSave_actionPerformed(ActionEvent e)
2330 // TODO: JAL-3048 not needed for Jalview-JS
2332 if (v_client != null)
2334 // TODO: VAMSAS DOCUMENT EXTENSION is VDJ
2335 JalviewFileChooser chooser = new JalviewFileChooser("vdj",
2338 chooser.setFileView(new JalviewFileView());
2339 chooser.setDialogTitle(MessageManager
2340 .getString("label.save_vamsas_document_archive"));
2342 int value = chooser.showSaveDialog(this);
2344 if (value == JalviewFileChooser.APPROVE_OPTION)
2346 java.io.File choice = chooser.getSelectedFile();
2347 JPanel progpanel = addProgressPanel(MessageManager
2348 .formatMessage("label.saving_vamsas_doc", new Object[]
2349 { choice.getName() }));
2350 Cache.setProperty("LAST_DIRECTORY", choice.getParent());
2351 String warnmsg = null;
2352 String warnttl = null;
2355 v_client.vclient.storeDocument(choice);
2358 warnttl = "Serious Problem saving Vamsas Document";
2359 warnmsg = ex.toString();
2360 jalview.bin.Cache.log
2361 .error("Error Whilst saving document to " + choice, ex);
2363 } catch (Exception ex)
2365 warnttl = "Problem saving Vamsas Document.";
2366 warnmsg = ex.toString();
2367 jalview.bin.Cache.log.warn(
2368 "Exception Whilst saving document to " + choice, ex);
2371 removeProgressPanel(progpanel);
2372 if (warnmsg != null)
2374 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
2376 warnmsg, warnttl, JvOptionPane.ERROR_MESSAGE);
2382 JPanel vamUpdate = null;
2385 * hide vamsas user gui bits when a vamsas document event is being handled.
2388 * true to hide gui, false to reveal gui
2390 public void setVamsasUpdate(boolean b)
2392 Cache.log.debug("Setting gui for Vamsas update "
2393 + (b ? "in progress" : "finished"));
2395 if (vamUpdate != null)
2397 this.removeProgressPanel(vamUpdate);
2401 vamUpdate = this.addProgressPanel(
2402 MessageManager.getString("label.updating_vamsas_session"));
2404 vamsasStart.setVisible(!b);
2405 vamsasStop.setVisible(!b);
2406 vamsasSave.setVisible(!b);
2409 public JInternalFrame[] getAllFrames()
2411 return desktop.getAllFrames();
2415 * Checks the given url to see if it gives a response indicating that the user
2416 * should be informed of a new questionnaire.
2420 public void checkForQuestionnaire(String url)
2422 UserQuestionnaireCheck jvq = new UserQuestionnaireCheck(url);
2423 // javax.swing.SwingUtilities.invokeLater(jvq);
2424 new Thread(jvq).start();
2427 public void checkURLLinks()
2429 // Thread off the URL link checker
2430 addDialogThread(new Runnable()
2435 if (Cache.getDefault("CHECKURLLINKS", true))
2437 // check what the actual links are - if it's just the default don't
2438 // bother with the warning
2439 List<String> links = Preferences.sequenceUrlLinks
2442 // only need to check links if there is one with a
2443 // SEQUENCE_ID which is not the default EMBL_EBI link
2444 ListIterator<String> li = links.listIterator();
2445 boolean check = false;
2446 List<JLabel> urls = new ArrayList<>();
2447 while (li.hasNext())
2449 String link = li.next();
2450 if (link.contains(SEQUENCE_ID)
2451 && !UrlConstants.isDefaultString(link))
2454 int barPos = link.indexOf("|");
2455 String urlMsg = barPos == -1 ? link
2456 : link.substring(0, barPos) + ": "
2457 + link.substring(barPos + 1);
2458 urls.add(new JLabel(urlMsg));
2466 // ask user to check in case URL links use old style tokens
2467 // ($SEQUENCE_ID$ for sequence id _or_ accession id)
2468 JPanel msgPanel = new JPanel();
2469 msgPanel.setLayout(new BoxLayout(msgPanel, BoxLayout.PAGE_AXIS));
2470 msgPanel.add(Box.createVerticalGlue());
2471 JLabel msg = new JLabel(MessageManager
2472 .getString("label.SEQUENCE_ID_for_DB_ACCESSION1"));
2473 JLabel msg2 = new JLabel(MessageManager
2474 .getString("label.SEQUENCE_ID_for_DB_ACCESSION2"));
2476 for (JLabel url : urls)
2482 final JCheckBox jcb = new JCheckBox(
2483 MessageManager.getString("label.do_not_display_again"));
2484 jcb.addActionListener(new ActionListener()
2487 public void actionPerformed(ActionEvent e)
2489 // update Cache settings for "don't show this again"
2490 boolean showWarningAgain = !jcb.isSelected();
2491 Cache.setProperty("CHECKURLLINKS",
2492 Boolean.valueOf(showWarningAgain).toString());
2497 JvOptionPane.showMessageDialog(Desktop.desktop, msgPanel,
2499 .getString("label.SEQUENCE_ID_no_longer_used"),
2500 JvOptionPane.WARNING_MESSAGE);
2507 * Proxy class for JDesktopPane which optionally displays the current memory
2508 * usage and highlights the desktop area with a red bar if free memory runs low.
2512 public class MyDesktopPane extends JDesktopPane
2515 private static final float ONE_MB = 1048576f;
2517 boolean showMemoryUsage = false;
2521 java.text.NumberFormat df;
2523 float maxMemory, allocatedMemory, freeMemory, totalFreeMemory,
2526 public MyDesktopPane(boolean showMemoryUsage)
2528 showMemoryUsage(showMemoryUsage);
2531 public void showMemoryUsage(boolean showMemory)
2533 this.showMemoryUsage = showMemory;
2536 Thread worker = new Thread(this);
2542 public boolean isShowMemoryUsage()
2544 return showMemoryUsage;
2550 df = java.text.NumberFormat.getNumberInstance();
2551 df.setMaximumFractionDigits(2);
2552 runtime = Runtime.getRuntime();
2554 while (showMemoryUsage)
2558 maxMemory = runtime.maxMemory() / ONE_MB;
2559 allocatedMemory = runtime.totalMemory() / ONE_MB;
2560 freeMemory = runtime.freeMemory() / ONE_MB;
2561 totalFreeMemory = freeMemory + (maxMemory - allocatedMemory);
2563 percentUsage = (totalFreeMemory / maxMemory) * 100;
2565 // if (percentUsage < 20)
2567 // border1 = BorderFactory.createMatteBorder(12, 12, 12, 12,
2569 // instance.set.setBorder(border1);
2572 // sleep after showing usage
2574 } catch (Exception ex)
2576 ex.printStackTrace();
2582 public void paintComponent(Graphics g)
2584 if (showMemoryUsage && g != null && df != null)
2586 if (percentUsage < 20)
2588 g.setColor(Color.red);
2590 FontMetrics fm = g.getFontMetrics();
2593 g.drawString(MessageManager.formatMessage("label.memory_stats",
2595 { df.format(totalFreeMemory), df.format(maxMemory),
2596 df.format(percentUsage) }),
2597 10, getHeight() - fm.getHeight());
2604 * Accessor method to quickly get all the AlignmentFrames loaded.
2606 * @return an array of AlignFrame, or null if none found
2608 public static AlignFrame[] getAlignFrames()
2610 if (Jalview.isHeadlessMode())
2612 // Desktop.desktop is null in headless mode
2613 return new AlignFrame[] { Jalview.currentAlignFrame };
2616 JInternalFrame[] frames = Desktop.desktop.getAllFrames();
2622 List<AlignFrame> avp = new ArrayList<>();
2624 for (int i = frames.length - 1; i > -1; i--)
2626 if (frames[i] instanceof AlignFrame)
2628 avp.add((AlignFrame) frames[i]);
2630 else if (frames[i] instanceof SplitFrame)
2633 * Also check for a split frame containing an AlignFrame
2635 GSplitFrame sf = (GSplitFrame) frames[i];
2636 if (sf.getTopFrame() instanceof AlignFrame)
2638 avp.add((AlignFrame) sf.getTopFrame());
2640 if (sf.getBottomFrame() instanceof AlignFrame)
2642 avp.add((AlignFrame) sf.getBottomFrame());
2646 if (avp.size() == 0)
2650 AlignFrame afs[] = avp.toArray(new AlignFrame[avp.size()]);
2655 * Returns an array of any AppJmol frames in the Desktop (or null if none).
2659 public GStructureViewer[] getJmols()
2661 JInternalFrame[] frames = Desktop.desktop.getAllFrames();
2667 List<GStructureViewer> avp = new ArrayList<>();
2669 for (int i = frames.length - 1; i > -1; i--)
2671 if (frames[i] instanceof AppJmol)
2673 GStructureViewer af = (GStructureViewer) frames[i];
2677 if (avp.size() == 0)
2681 GStructureViewer afs[] = avp.toArray(new GStructureViewer[avp.size()]);
2686 * Add Groovy Support to Jalview
2689 public void groovyShell_actionPerformed()
2693 openGroovyConsole();
2694 } catch (Exception ex)
2696 jalview.bin.Cache.log.error("Groovy Shell Creation failed.", ex);
2697 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
2699 MessageManager.getString("label.couldnt_create_groovy_shell"),
2700 MessageManager.getString("label.groovy_support_failed"),
2701 JvOptionPane.ERROR_MESSAGE);
2706 * Open the Groovy console
2708 void openGroovyConsole()
2710 if (groovyConsole == null)
2712 groovyConsole = new groovy.ui.Console();
2713 groovyConsole.setVariable("Jalview", this);
2714 groovyConsole.run();
2717 * We allow only one console at a time, so that AlignFrame menu option
2718 * 'Calculate | Run Groovy script' is unambiguous.
2719 * Disable 'Groovy Console', and enable 'Run script', when the console is
2720 * opened, and the reverse when it is closed
2722 Window window = (Window) groovyConsole.getFrame();
2723 window.addWindowListener(new WindowAdapter()
2726 public void windowClosed(WindowEvent e)
2729 * rebind CMD-Q from Groovy Console to Jalview Quit
2732 enableExecuteGroovy(false);
2738 * show Groovy console window (after close and reopen)
2740 ((Window) groovyConsole.getFrame()).setVisible(true);
2743 * if we got this far, enable 'Run Groovy' in AlignFrame menus
2744 * and disable opening a second console
2746 enableExecuteGroovy(true);
2750 * Bind Ctrl/Cmd-Q to Quit - for reset as Groovy Console takes over this binding
2753 protected void addQuitHandler()
2755 getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
2756 .put(KeyStroke.getKeyStroke(KeyEvent.VK_Q,
2757 Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()),
2759 getRootPane().getActionMap().put("Quit", new AbstractAction()
2762 public void actionPerformed(ActionEvent e)
2770 * Enable or disable 'Run Groovy script' in AlignFrame calculate menus
2773 * true if Groovy console is open
2775 public void enableExecuteGroovy(boolean enabled)
2778 * disable opening a second Groovy console
2779 * (or re-enable when the console is closed)
2781 groovyShell.setEnabled(!enabled);
2783 AlignFrame[] alignFrames = getAlignFrames();
2784 if (alignFrames != null)
2786 for (AlignFrame af : alignFrames)
2788 af.setGroovyEnabled(enabled);
2794 * Progress bars managed by the IProgressIndicator method.
2796 private Hashtable<Long, JPanel> progressBars;
2798 private Hashtable<Long, IProgressIndicatorHandler> progressBarHandlers;
2803 * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
2806 public void setProgressBar(String message, long id)
2808 if (progressBars == null)
2810 progressBars = new Hashtable<>();
2811 progressBarHandlers = new Hashtable<>();
2814 if (progressBars.get(new Long(id)) != null)
2816 JPanel panel = progressBars.remove(new Long(id));
2817 if (progressBarHandlers.contains(new Long(id)))
2819 progressBarHandlers.remove(new Long(id));
2821 removeProgressPanel(panel);
2825 progressBars.put(new Long(id), addProgressPanel(message));
2832 * @see jalview.gui.IProgressIndicator#registerHandler(long,
2833 * jalview.gui.IProgressIndicatorHandler)
2836 public void registerHandler(final long id,
2837 final IProgressIndicatorHandler handler)
2839 if (progressBarHandlers == null
2840 || !progressBars.containsKey(new Long(id)))
2842 throw new Error(MessageManager.getString(
2843 "error.call_setprogressbar_before_registering_handler"));
2845 progressBarHandlers.put(new Long(id), handler);
2846 final JPanel progressPanel = progressBars.get(new Long(id));
2847 if (handler.canCancel())
2849 JButton cancel = new JButton(
2850 MessageManager.getString("action.cancel"));
2851 final IProgressIndicator us = this;
2852 cancel.addActionListener(new ActionListener()
2856 public void actionPerformed(ActionEvent e)
2858 handler.cancelActivity(id);
2859 us.setProgressBar(MessageManager
2860 .formatMessage("label.cancelled_params", new Object[]
2861 { ((JLabel) progressPanel.getComponent(0)).getText() }),
2865 progressPanel.add(cancel, BorderLayout.EAST);
2871 * @return true if any progress bars are still active
2874 public boolean operationInProgress()
2876 if (progressBars != null && progressBars.size() > 0)
2884 * This will return the first AlignFrame holding the given viewport instance. It
2885 * will break if there are more than one AlignFrames viewing a particular av.
2888 * @return alignFrame for viewport
2890 public static AlignFrame getAlignFrameFor(AlignViewportI viewport)
2892 if (desktop != null)
2894 AlignmentPanel[] aps = getAlignmentPanels(
2895 viewport.getSequenceSetId());
2896 for (int panel = 0; aps != null && panel < aps.length; panel++)
2898 if (aps[panel] != null && aps[panel].av == viewport)
2900 return aps[panel].alignFrame;
2907 public VamsasApplication getVamsasApplication()
2914 * flag set if jalview GUI is being operated programmatically
2916 private boolean inBatchMode = false;
2919 * check if jalview GUI is being operated programmatically
2921 * @return inBatchMode
2923 public boolean isInBatchMode()
2929 * set flag if jalview GUI is being operated programmatically
2931 * @param inBatchMode
2933 public void setInBatchMode(boolean inBatchMode)
2935 this.inBatchMode = inBatchMode;
2938 public void startServiceDiscovery()
2940 startServiceDiscovery(false);
2943 public void startServiceDiscovery(boolean blocking)
2945 boolean alive = true;
2946 Thread t0 = null, t1 = null, t2 = null;
2947 // JAL-940 - JALVIEW 1 services are now being EOLed as of JABA 2.1 release
2950 // todo: changesupport handlers need to be transferred
2951 if (discoverer == null)
2953 discoverer = new jalview.ws.jws1.Discoverer();
2954 // register PCS handler for desktop.
2955 discoverer.addPropertyChangeListener(changeSupport);
2957 // JAL-940 - disabled JWS1 service configuration - always start discoverer
2958 // until we phase out completely
2959 (t0 = new Thread(discoverer)).start();
2962 if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
2964 t2 = jalview.ws.jws2.Jws2Discoverer.getDiscoverer()
2965 .startDiscoverer(changeSupport);
2969 // TODO: do rest service discovery
2978 } catch (Exception e)
2981 alive = (t1 != null && t1.isAlive()) || (t2 != null && t2.isAlive())
2982 || (t3 != null && t3.isAlive())
2983 || (t0 != null && t0.isAlive());
2989 * called to check if the service discovery process completed successfully.
2993 protected void JalviewServicesChanged(PropertyChangeEvent evt)
2995 if (evt.getNewValue() == null || evt.getNewValue() instanceof Vector)
2997 final String ermsg = jalview.ws.jws2.Jws2Discoverer.getDiscoverer()
2998 .getErrorMessages();
3001 if (Cache.getDefault("SHOW_WSDISCOVERY_ERRORS", true))
3003 if (serviceChangedDialog == null)
3005 // only run if we aren't already displaying one of these.
3006 addDialogThread(serviceChangedDialog = new Runnable()
3013 * JalviewDialog jd =new JalviewDialog() {
3015 * @Override protected void cancelPressed() { // TODO
3016 * Auto-generated method stub
3018 * }@Override protected void okPressed() { // TODO
3019 * Auto-generated method stub
3021 * }@Override protected void raiseClosed() { // TODO
3022 * Auto-generated method stub
3024 * } }; jd.initDialogFrame(new
3025 * JLabel("<html><table width=\"450\"><tr><td>" + ermsg +
3026 * "<br/>It may be that you have invalid JABA URLs in your web service preferences,"
3027 * + " or mis-configured HTTP proxy settings.<br/>" +
3028 * "Check the <em>Connections</em> and <em>Web services</em> tab of the"
3030 * " Tools->Preferences dialog box to change them.</td></tr></table></html>"
3031 * ), true, true, "Web Service Configuration Problem", 450,
3034 * jd.waitForInput();
3036 JvOptionPane.showConfirmDialog(Desktop.desktop,
3037 new JLabel("<html><table width=\"450\"><tr><td>"
3038 + ermsg + "</td></tr></table>"
3039 + "<p>It may be that you have invalid JABA URLs<br/>in your web service preferences,"
3040 + "<br>or as a command-line argument, or mis-configured HTTP proxy settings.</p>"
3041 + "<p>Check the <em>Connections</em> and <em>Web services</em> tab<br/>of the"
3042 + " Tools->Preferences dialog box to change them.</p></html>"),
3043 "Web Service Configuration Problem",
3044 JvOptionPane.DEFAULT_OPTION,
3045 JvOptionPane.ERROR_MESSAGE);
3046 serviceChangedDialog = null;
3055 "Errors reported by JABA discovery service. Check web services preferences.\n"
3062 private Runnable serviceChangedDialog = null;
3065 * start a thread to open a URL in the configured browser. Pops up a warning
3066 * dialog to the user if there is an exception when calling out to the browser
3071 public static void showUrl(final String url)
3073 showUrl(url, Desktop.instance);
3077 * Like showUrl but allows progress handler to be specified
3081 * (null) or object implementing IProgressIndicator
3083 public static void showUrl(final String url,
3084 final IProgressIndicator progress)
3086 new Thread(new Runnable()
3093 if (progress != null)
3095 progress.setProgressBar(MessageManager
3096 .formatMessage("status.opening_params", new Object[]
3097 { url }), this.hashCode());
3099 jalview.util.BrowserLauncher.openURL(url);
3100 } catch (Exception ex)
3102 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
3104 .getString("label.web_browser_not_found_unix"),
3105 MessageManager.getString("label.web_browser_not_found"),
3106 JvOptionPane.WARNING_MESSAGE);
3108 ex.printStackTrace();
3110 if (progress != null)
3112 progress.setProgressBar(null, this.hashCode());
3118 public static WsParamSetManager wsparamManager = null;
3120 public static ParamManager getUserParameterStore()
3122 if (wsparamManager == null)
3124 wsparamManager = new WsParamSetManager();
3126 return wsparamManager;
3130 * static hyperlink handler proxy method for use by Jalview's internal windows
3134 public static void hyperlinkUpdate(HyperlinkEvent e)
3136 if (e.getEventType() == EventType.ACTIVATED)
3141 url = e.getURL().toString();
3142 Desktop.showUrl(url);
3143 } catch (Exception x)
3147 if (Cache.log != null)
3149 Cache.log.error("Couldn't handle string " + url + " as a URL.");
3154 "Couldn't handle string " + url + " as a URL.");
3157 // ignore any exceptions due to dud links.
3164 * single thread that handles display of dialogs to user.
3166 ExecutorService dialogExecutor = Executors.newSingleThreadExecutor();
3169 * flag indicating if dialogExecutor should try to acquire a permit
3171 private volatile boolean dialogPause = true;
3176 private java.util.concurrent.Semaphore block = new Semaphore(0);
3178 private static groovy.ui.Console groovyConsole;
3181 * add another dialog thread to the queue
3185 public void addDialogThread(final Runnable prompter)
3187 dialogExecutor.submit(new Runnable()
3197 } catch (InterruptedException x)
3202 if (instance == null)
3208 SwingUtilities.invokeAndWait(prompter);
3209 } catch (Exception q)
3211 Cache.log.warn("Unexpected Exception in dialog thread.", q);
3217 public void startDialogQueue()
3219 // set the flag so we don't pause waiting for another permit and semaphore
3220 // the current task to begin
3221 dialogPause = false;
3226 * Outputs an image of the desktop to file in EPS format, after prompting the
3227 * user for choice of Text or Lineart character rendering (unless a preference
3228 * has been set). The file name is generated as
3231 * Jalview_snapshot_nnnnn.eps where nnnnn is the current timestamp in milliseconds
3235 protected void snapShotWindow_actionPerformed(ActionEvent e)
3237 // currently the menu option to do this is not shown
3240 int width = getWidth();
3241 int height = getHeight();
3243 "Jalview_snapshot_" + System.currentTimeMillis() + ".eps");
3244 ImageWriterI writer = new ImageWriterI()
3247 public void exportImage(Graphics g) throws Exception
3250 Cache.log.info("Successfully written snapshot to file "
3251 + of.getAbsolutePath());
3254 String title = "View of desktop";
3255 ImageExporter exporter = new ImageExporter(writer, null, TYPE.EPS,
3257 exporter.doExport(of, this, width, height, title);
3261 * Explode the views in the given SplitFrame into separate SplitFrame windows.
3262 * This respects (remembers) any previous 'exploded geometry' i.e. the size and
3263 * location last time the view was expanded (if any). However it does not
3264 * remember the split pane divider location - this is set to match the
3265 * 'exploding' frame.
3269 public void explodeViews(SplitFrame sf)
3271 AlignFrame oldTopFrame = (AlignFrame) sf.getTopFrame();
3272 AlignFrame oldBottomFrame = (AlignFrame) sf.getBottomFrame();
3273 List<? extends AlignmentViewPanel> topPanels = oldTopFrame
3275 List<? extends AlignmentViewPanel> bottomPanels = oldBottomFrame
3277 int viewCount = topPanels.size();
3284 * Processing in reverse order works, forwards order leaves the first panels
3285 * not visible. I don't know why!
3287 for (int i = viewCount - 1; i >= 0; i--)
3290 * Make new top and bottom frames. These take over the respective
3291 * AlignmentPanel objects, including their AlignmentViewports, so the
3292 * cdna/protein relationships between the viewports is carried over to the
3295 * explodedGeometry holds the (x, y) position of the previously exploded
3296 * SplitFrame, and the (width, height) of the AlignFrame component
3298 AlignmentPanel topPanel = (AlignmentPanel) topPanels.get(i);
3299 AlignFrame newTopFrame = new AlignFrame(topPanel);
3300 newTopFrame.setSize(oldTopFrame.getSize());
3301 newTopFrame.setVisible(true);
3302 Rectangle geometry = ((AlignViewport) topPanel.getAlignViewport())
3303 .getExplodedGeometry();
3304 if (geometry != null)
3306 newTopFrame.setSize(geometry.getSize());
3309 AlignmentPanel bottomPanel = (AlignmentPanel) bottomPanels.get(i);
3310 AlignFrame newBottomFrame = new AlignFrame(bottomPanel);
3311 newBottomFrame.setSize(oldBottomFrame.getSize());
3312 newBottomFrame.setVisible(true);
3313 geometry = ((AlignViewport) bottomPanel.getAlignViewport())
3314 .getExplodedGeometry();
3315 if (geometry != null)
3317 newBottomFrame.setSize(geometry.getSize());
3320 topPanel.av.setGatherViewsHere(false);
3321 bottomPanel.av.setGatherViewsHere(false);
3322 JInternalFrame splitFrame = new SplitFrame(newTopFrame,
3324 if (geometry != null)
3326 splitFrame.setLocation(geometry.getLocation());
3328 Desktop.addInternalFrame(splitFrame, sf.getTitle(), -1, -1);
3332 * Clear references to the panels (now relocated in the new SplitFrames)
3333 * before closing the old SplitFrame.
3336 bottomPanels.clear();
3341 * Gather expanded split frames, sharing the same pairs of sequence set ids,
3342 * back into the given SplitFrame as additional views. Note that the gathered
3343 * frames may themselves have multiple views.
3347 public void gatherViews(GSplitFrame source)
3350 * special handling of explodedGeometry for a view within a SplitFrame: - it
3351 * holds the (x, y) position of the enclosing SplitFrame, and the (width,
3352 * height) of the AlignFrame component
3354 AlignFrame myTopFrame = (AlignFrame) source.getTopFrame();
3355 AlignFrame myBottomFrame = (AlignFrame) source.getBottomFrame();
3356 myTopFrame.viewport.setExplodedGeometry(new Rectangle(source.getX(),
3357 source.getY(), myTopFrame.getWidth(), myTopFrame.getHeight()));
3358 myBottomFrame.viewport
3359 .setExplodedGeometry(new Rectangle(source.getX(), source.getY(),
3360 myBottomFrame.getWidth(), myBottomFrame.getHeight()));
3361 myTopFrame.viewport.setGatherViewsHere(true);
3362 myBottomFrame.viewport.setGatherViewsHere(true);
3363 String topViewId = myTopFrame.viewport.getSequenceSetId();
3364 String bottomViewId = myBottomFrame.viewport.getSequenceSetId();
3366 JInternalFrame[] frames = desktop.getAllFrames();
3367 for (JInternalFrame frame : frames)
3369 if (frame instanceof SplitFrame && frame != source)
3371 SplitFrame sf = (SplitFrame) frame;
3372 AlignFrame topFrame = (AlignFrame) sf.getTopFrame();
3373 AlignFrame bottomFrame = (AlignFrame) sf.getBottomFrame();
3374 boolean gatherThis = false;
3375 for (int a = 0; a < topFrame.alignPanels.size(); a++)
3377 AlignmentPanel topPanel = topFrame.alignPanels.get(a);
3378 AlignmentPanel bottomPanel = bottomFrame.alignPanels.get(a);
3379 if (topViewId.equals(topPanel.av.getSequenceSetId())
3380 && bottomViewId.equals(bottomPanel.av.getSequenceSetId()))
3383 topPanel.av.setGatherViewsHere(false);
3384 bottomPanel.av.setGatherViewsHere(false);
3385 topPanel.av.setExplodedGeometry(
3386 new Rectangle(sf.getLocation(), topFrame.getSize()));
3387 bottomPanel.av.setExplodedGeometry(
3388 new Rectangle(sf.getLocation(), bottomFrame.getSize()));
3389 myTopFrame.addAlignmentPanel(topPanel, false);
3390 myBottomFrame.addAlignmentPanel(bottomPanel, false);
3396 topFrame.getAlignPanels().clear();
3397 bottomFrame.getAlignPanels().clear();
3404 * The dust settles...give focus to the tab we did this from.
3406 myTopFrame.setDisplayedView(myTopFrame.alignPanel);
3409 public static groovy.ui.Console getGroovyConsole()
3411 return groovyConsole;
3415 * handles the payload of a drag and drop event.
3417 * TODO refactor to desktop utilities class
3420 * - Data source strings extracted from the drop event
3422 * - protocol for each data source extracted from the drop event
3426 * - the payload from the drop event
3429 public static void transferFromDropTarget(List<Object> files,
3430 List<DataSourceType> protocols, DropTargetDropEvent evt,
3431 Transferable t) throws Exception
3434 // BH 2018 changed List<String> to List<Object> to allow for File from SwingJS
3436 // DataFlavor[] flavors = t.getTransferDataFlavors();
3437 // for (int i = 0; i < flavors.length; i++) {
3438 // if (flavors[i].isFlavorJavaFileListType()) {
3439 // evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
3440 // List<File> list = (List<File>) t.getTransferData(flavors[i]);
3441 // for (int j = 0; j < list.size(); j++) {
3442 // File file = (File) list.get(j);
3443 // byte[] data = getDroppedFileBytes(file);
3444 // fileName.setText(file.getName() + " - " + data.length + " " +
3445 // evt.getLocation());
3446 // JTextArea target = (JTextArea) ((DropTarget) evt.getSource()).getComponent();
3447 // target.setText(new String(data));
3449 // dtde.dropComplete(true);
3454 DataFlavor uriListFlavor = new DataFlavor(
3455 "text/uri-list;class=java.lang.String"), urlFlavour = null;
3458 urlFlavour = new DataFlavor(
3459 "application/x-java-url; class=java.net.URL");
3460 } catch (ClassNotFoundException cfe)
3462 Cache.log.debug("Couldn't instantiate the URL dataflavor.", cfe);
3465 if (urlFlavour != null && t.isDataFlavorSupported(urlFlavour))
3470 java.net.URL url = (URL) t.getTransferData(urlFlavour);
3471 // nb: java 8 osx bug https://bugs.openjdk.java.net/browse/JDK-8156099
3472 // means url may be null.
3475 protocols.add(DataSourceType.URL);
3476 files.add(url.toString());
3477 Cache.log.debug("Drop handled as URL dataflavor "
3478 + files.get(files.size() - 1));
3483 if (Platform.isAMac())
3486 "Please ignore plist error - occurs due to problem with java 8 on OSX");
3490 } catch (Throwable ex)
3492 Cache.log.debug("URL drop handler failed.", ex);
3495 if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
3497 // Works on Windows and MacOSX
3498 Cache.log.debug("Drop handled as javaFileListFlavor");
3499 for (Object file : (List) t
3500 .getTransferData(DataFlavor.javaFileListFlavor))
3503 protocols.add(DataSourceType.FILE);
3508 // Unix like behaviour
3509 boolean added = false;
3511 if (t.isDataFlavorSupported(uriListFlavor))
3513 Cache.log.debug("Drop handled as uriListFlavor");
3514 // This is used by Unix drag system
3515 data = (String) t.getTransferData(uriListFlavor);
3519 // fallback to text: workaround - on OSX where there's a JVM bug
3520 Cache.log.debug("standard URIListFlavor failed. Trying text");
3521 // try text fallback
3522 DataFlavor textDf = new DataFlavor(
3523 "text/plain;class=java.lang.String");
3524 if (t.isDataFlavorSupported(textDf))
3526 data = (String) t.getTransferData(textDf);
3529 Cache.log.debug("Plain text drop content returned "
3530 + (data == null ? "Null - failed" : data));
3535 while (protocols.size() < files.size())
3537 Cache.log.debug("Adding missing FILE protocol for "
3538 + files.get(protocols.size()));
3539 protocols.add(DataSourceType.FILE);
3541 for (java.util.StringTokenizer st = new java.util.StringTokenizer(
3542 data, "\r\n"); st.hasMoreTokens();)
3545 String s = st.nextToken();
3546 if (s.startsWith("#"))
3548 // the line is a comment (as per the RFC 2483)
3551 java.net.URI uri = new java.net.URI(s);
3552 if (uri.getScheme().toLowerCase().startsWith("http"))
3554 protocols.add(DataSourceType.URL);
3555 files.add(uri.toString());
3559 // otherwise preserve old behaviour: catch all for file objects
3560 java.io.File file = new java.io.File(uri);
3561 protocols.add(DataSourceType.FILE);
3562 files.add(file.toString());
3567 if (Cache.log.isDebugEnabled())
3569 if (data == null || !added)
3572 if (t.getTransferDataFlavors() != null
3573 && t.getTransferDataFlavors().length > 0)
3576 "Couldn't resolve drop data. Here are the supported flavors:");
3577 for (DataFlavor fl : t.getTransferDataFlavors())
3580 "Supported transfer dataflavor: " + fl.toString());
3581 Object df = t.getTransferData(fl);
3584 Cache.log.debug("Retrieves: " + df);
3588 Cache.log.debug("Retrieved nothing");
3594 Cache.log.debug("Couldn't resolve dataflavor for drop: "
3600 if (Platform.isWindows())
3603 Cache.log.debug("Scanning dropped content for Windows Link Files");
3605 // resolve any .lnk files in the file drop
3606 for (int f = 0; f < files.size(); f++)
3608 String source = files.get(f).toString().toLowerCase();
3609 if (protocols.get(f).equals(DataSourceType.FILE)
3610 && (source.endsWith(".lnk") || source.endsWith(".url")
3611 || source.endsWith(".site")))
3615 Object obj = files.get(f);
3616 File lf = (obj instanceof File ? (File) obj
3617 : new File((String) obj));
3618 // process link file to get a URL
3619 Cache.log.debug("Found potential link file: " + lf);
3620 WindowsShortcut wscfile = new WindowsShortcut(lf);
3621 String fullname = wscfile.getRealFilename();
3622 protocols.set(f, FormatAdapter.checkProtocol(fullname));
3623 files.set(f, fullname);
3624 Cache.log.debug("Parsed real filename " + fullname
3625 + " to extract protocol: " + protocols.get(f));
3626 } catch (Exception ex)
3629 "Couldn't parse " + files.get(f) + " as a link file.",
3638 * Sets the Preferences property for experimental features to True or False
3639 * depending on the state of the controlling menu item
3642 protected void showExperimental_actionPerformed(boolean selected)
3644 Cache.setProperty(EXPERIMENTAL_FEATURES, Boolean.toString(selected));
3648 * Answers a (possibly empty) list of any structure viewer frames (currently for
3649 * either Jmol or Chimera) which are currently open. This may optionally be
3650 * restricted to viewers of a specified class, or viewers linked to a specified
3654 * if not null, only return viewers linked to this panel
3655 * @param structureViewerClass
3656 * if not null, only return viewers of this class
3659 public List<StructureViewerBase> getStructureViewers(
3660 AlignmentPanel apanel,
3661 Class<? extends StructureViewerBase> structureViewerClass)
3663 List<StructureViewerBase> result = new ArrayList<>();
3664 JInternalFrame[] frames = Desktop.instance.getAllFrames();
3666 for (JInternalFrame frame : frames)
3668 if (frame instanceof StructureViewerBase)
3670 if (structureViewerClass == null
3671 || structureViewerClass.isInstance(frame))
3674 || ((StructureViewerBase) frame).isLinkedWith(apanel))
3676 result.add((StructureViewerBase) frame);