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 jalview.api.AlignViewportI;
24 import jalview.api.AlignmentViewPanel;
25 import jalview.api.StructureSelectionManagerProvider;
26 import jalview.bin.ApplicationSingletonProvider;
27 import jalview.bin.ApplicationSingletonProvider.ApplicationSingletonI;
28 import jalview.bin.Cache;
29 import jalview.bin.Jalview;
30 import jalview.gui.ImageExporter.ImageWriterI;
31 import jalview.io.BackupFiles;
32 import jalview.io.DataSourceType;
33 import jalview.io.FileFormat;
34 import jalview.io.FileFormatException;
35 import jalview.io.FileFormatI;
36 import jalview.io.FileFormats;
37 import jalview.io.FileLoader;
38 import jalview.io.FormatAdapter;
39 import jalview.io.IdentifyFile;
40 import jalview.io.JalviewFileChooser;
41 import jalview.io.JalviewFileView;
42 import jalview.jbgui.GDesktop;
43 import jalview.jbgui.GSplitFrame;
44 import jalview.jbgui.GStructureViewer;
45 import jalview.project.Jalview2XML;
46 import jalview.structure.StructureSelectionManager;
47 import jalview.urls.IdOrgSettings;
48 import jalview.util.BrowserLauncher;
49 import jalview.util.ImageMaker.TYPE;
50 import jalview.util.MessageManager;
51 import jalview.util.Platform;
52 import jalview.util.UrlConstants;
53 import jalview.viewmodel.AlignmentViewport;
54 import jalview.ws.jws1.Discoverer;
55 import jalview.ws.params.ParamManager;
56 import jalview.ws.utils.UrlDownloadClient;
58 import java.awt.BorderLayout;
59 import java.awt.Color;
60 import java.awt.Dimension;
61 import java.awt.FontMetrics;
62 import java.awt.Graphics;
63 import java.awt.GridLayout;
64 import java.awt.Point;
65 import java.awt.Rectangle;
66 import java.awt.Toolkit;
67 import java.awt.Window;
68 import java.awt.datatransfer.Clipboard;
69 import java.awt.datatransfer.ClipboardOwner;
70 import java.awt.datatransfer.DataFlavor;
71 import java.awt.datatransfer.Transferable;
72 import java.awt.dnd.DnDConstants;
73 import java.awt.dnd.DropTargetDragEvent;
74 import java.awt.dnd.DropTargetDropEvent;
75 import java.awt.dnd.DropTargetEvent;
76 import java.awt.dnd.DropTargetListener;
77 import java.awt.event.ActionEvent;
78 import java.awt.event.ActionListener;
79 import java.awt.event.InputEvent;
80 import java.awt.event.KeyEvent;
81 import java.awt.event.MouseAdapter;
82 import java.awt.event.MouseEvent;
83 import java.awt.event.WindowAdapter;
84 import java.awt.event.WindowEvent;
85 import java.beans.PropertyChangeEvent;
86 import java.beans.PropertyChangeListener;
87 import java.io.BufferedInputStream;
89 import java.io.FileOutputStream;
90 import java.io.IOException;
92 import java.util.ArrayList;
93 import java.util.Hashtable;
94 import java.util.List;
95 import java.util.ListIterator;
96 import java.util.Vector;
97 import java.util.concurrent.ExecutorService;
98 import java.util.concurrent.Executors;
99 import java.util.concurrent.Semaphore;
101 import javax.swing.AbstractAction;
102 import javax.swing.Action;
103 import javax.swing.ActionMap;
104 import javax.swing.Box;
105 import javax.swing.BoxLayout;
106 import javax.swing.DefaultDesktopManager;
107 import javax.swing.DesktopManager;
108 import javax.swing.InputMap;
109 import javax.swing.JButton;
110 import javax.swing.JCheckBox;
111 import javax.swing.JComboBox;
112 import javax.swing.JComponent;
113 import javax.swing.JDesktopPane;
114 import javax.swing.JFrame;
115 import javax.swing.JInternalFrame;
116 import javax.swing.JLabel;
117 import javax.swing.JMenuItem;
118 import javax.swing.JPanel;
119 import javax.swing.JPopupMenu;
120 import javax.swing.JProgressBar;
121 import javax.swing.JTextField;
122 import javax.swing.KeyStroke;
123 import javax.swing.SwingUtilities;
124 import javax.swing.event.HyperlinkEvent;
125 import javax.swing.event.HyperlinkEvent.EventType;
126 import javax.swing.event.InternalFrameAdapter;
127 import javax.swing.event.InternalFrameEvent;
128 import javax.swing.event.MenuEvent;
129 import javax.swing.event.MenuListener;
131 import org.stackoverflowusers.file.WindowsShortcut;
138 * @version $Revision: 1.155 $
140 @SuppressWarnings("serial")
141 public class Desktop extends GDesktop
142 implements DropTargetListener, ClipboardOwner, IProgressIndicator,
143 StructureSelectionManagerProvider, ApplicationSingletonI
146 private final static int DEFAULT_MIN_WIDTH = 300;
148 private final static int DEFAULT_MIN_HEIGHT = 250;
150 private final static int ALIGN_FRAME_DEFAULT_MIN_WIDTH = 600;
152 private final static int ALIGN_FRAME_DEFAULT_MIN_HEIGHT = 70;
154 private final static String EXPERIMENTAL_FEATURES = "EXPERIMENTAL_FEATURES";
156 private JalviewChangeSupport changeSupport = new JalviewChangeSupport();
159 * news reader - null if it was never started.
161 BlogReader jvnews = null;
163 private File projectFile;
167 * @see jalview.gui.JalviewChangeSupport#addJalviewPropertyChangeListener(java.beans.PropertyChangeListener)
169 public void addJalviewPropertyChangeListener(
170 PropertyChangeListener listener)
172 changeSupport.addJalviewPropertyChangeListener(listener);
176 * @param propertyName
178 * @see jalview.gui.JalviewChangeSupport#addJalviewPropertyChangeListener(java.lang.String,
179 * java.beans.PropertyChangeListener)
181 public void addJalviewPropertyChangeListener(String propertyName,
182 PropertyChangeListener listener)
184 changeSupport.addJalviewPropertyChangeListener(propertyName, listener);
188 * @param propertyName
190 * @see jalview.gui.JalviewChangeSupport#removeJalviewPropertyChangeListener(java.lang.String,
191 * java.beans.PropertyChangeListener)
193 public void removeJalviewPropertyChangeListener(String propertyName,
194 PropertyChangeListener listener)
196 changeSupport.removeJalviewPropertyChangeListener(propertyName,
200 public static MyDesktopPane getDesktopPane()
202 Desktop desktop = Desktop.getInstance();
203 return desktop == null ? null : desktop.desktopPane;
206 public static StructureSelectionManager getStructureSelectionManager()
208 return StructureSelectionManager
209 .getStructureSelectionManager(getInstance());
212 static int openFrameCount = 0;
214 static final int xOffset = 30;
216 static final int yOffset = 30;
218 public Discoverer discoverer;
220 public Object[] jalviewClipboard;
222 public boolean internalCopy = false;
224 private static int fileLoadingCount = 0;
226 public JInternalFrame conservationSlider;
228 public JInternalFrame PIDSlider;
230 class MyDesktopManager implements DesktopManager
233 private DesktopManager delegate;
235 public MyDesktopManager(DesktopManager delegate)
237 this.delegate = delegate;
241 public void activateFrame(JInternalFrame f)
245 delegate.activateFrame(f);
246 } catch (NullPointerException npe)
248 Point p = getMousePosition();
249 showPasteMenu(p.x, p.y);
254 public void beginDraggingFrame(JComponent f)
256 delegate.beginDraggingFrame(f);
260 public void beginResizingFrame(JComponent f, int direction)
262 delegate.beginResizingFrame(f, direction);
266 public void closeFrame(JInternalFrame f)
268 delegate.closeFrame(f);
272 public void deactivateFrame(JInternalFrame f)
274 delegate.deactivateFrame(f);
278 public void deiconifyFrame(JInternalFrame f)
280 delegate.deiconifyFrame(f);
284 public void dragFrame(JComponent f, int newX, int newY)
290 delegate.dragFrame(f, newX, newY);
294 public void endDraggingFrame(JComponent f)
296 delegate.endDraggingFrame(f);
297 desktopPane.repaint();
301 public void endResizingFrame(JComponent f)
303 delegate.endResizingFrame(f);
304 desktopPane.repaint();
308 public void iconifyFrame(JInternalFrame f)
310 delegate.iconifyFrame(f);
314 public void maximizeFrame(JInternalFrame f)
316 delegate.maximizeFrame(f);
320 public void minimizeFrame(JInternalFrame f)
322 delegate.minimizeFrame(f);
326 public void openFrame(JInternalFrame f)
328 delegate.openFrame(f);
332 public void resizeFrame(JComponent f, int newX, int newY, int newWidth,
339 delegate.resizeFrame(f, newX, newY, newWidth, newHeight);
343 public void setBoundsForFrame(JComponent f, int newX, int newY,
344 int newWidth, int newHeight)
346 delegate.setBoundsForFrame(f, newX, newY, newWidth, newHeight);
349 // All other methods, simply delegate
353 public MyDesktopPane desktopPane;
356 * Answers an 'application scope' singleton instance of this class. Separate
357 * SwingJS 'applets' running in the same browser page will each have a
358 * distinct instance of Desktop.
362 public static Desktop getInstance()
364 return Jalview.isHeadlessMode() ? null
365 : (Desktop) ApplicationSingletonProvider
366 .getInstance(Desktop.class);
370 * Private constructor enforces singleton pattern. It is called by reflection
371 * from ApplicationSingletonProvider.getInstance().
379 * A note to implementors. It is ESSENTIAL that any activities that might
380 * block are spawned off as threads rather than waited for during this
383 if (!Platform.isJS())
385 doVamsasClientCheck();
388 doConfigureStructurePrefs();
389 setTitle("Jalview " + jalview.bin.Cache.getProperty("VERSION"));
390 setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
391 boolean selmemusage = jalview.bin.Cache.getDefault("SHOW_MEMUSAGE",
393 boolean showjconsole = jalview.bin.Cache
394 .getDefault("SHOW_JAVA_CONSOLE", false);
395 desktopPane = new MyDesktopPane(selmemusage);
397 showMemusage.setSelected(selmemusage);
398 desktopPane.setBackground(Color.white);
399 getContentPane().setLayout(new BorderLayout());
400 // alternate config - have scrollbars - see notes in JAL-153
401 // JScrollPane sp = new JScrollPane();
402 // sp.getViewport().setView(desktop);
403 // getContentPane().add(sp, BorderLayout.CENTER);
405 // BH 2018 - just an experiment to try unclipped JInternalFrames.
408 getRootPane().putClientProperty("swingjs.overflow.hidden", "false");
411 getContentPane().add(desktopPane, BorderLayout.CENTER);
412 desktopPane.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
414 // This line prevents Windows Look&Feel resizing all new windows to
416 // if previous window was maximised
417 desktopPane.setDesktopManager(new MyDesktopManager(
418 (Platform.isWindowsAndNotJS() ? new DefaultDesktopManager()
419 : Platform.isAMacAndNotJS()
420 ? new AquaInternalFrameManager(
421 desktopPane.getDesktopManager())
422 : desktopPane.getDesktopManager())));
424 Rectangle dims = getLastKnownDimensions("");
431 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
432 int xPos = Math.max(5, (screenSize.width - 900) / 2);
433 int yPos = Math.max(5, (screenSize.height - 650) / 2);
434 setBounds(xPos, yPos, 900, 650);
437 // Note that this next syntax, checking for Platform.isJS and also
438 // escaping the code using @j2sIgnore, serves two purposes. It gives
439 // us an easily findable tag, Platform.isJS(), to places in the code where
440 // there is something different about the SwingJS implementation. Second,
441 // it deletes the unneeded Java-only code form the JavaScript version
442 // completely (@j2sIgnore), since it will never be used there.
444 if (!Platform.isJS())
452 jconsole = new Console(this, showjconsole);
453 // add essential build information
454 jconsole.setHeader("Jalview Version: "
455 + jalview.bin.Cache.getProperty("VERSION") + "\n"
456 + "Jalview Installation: "
457 + jalview.bin.Cache.getDefault("INSTALLATION", "unknown")
458 + "\n" + "Build Date: "
459 + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown")
460 + "\n" + "Java version: "
461 + System.getProperty("java.version") + "\n"
462 + System.getProperty("os.arch") + " "
463 + System.getProperty("os.name") + " "
464 + System.getProperty("os.version"));
466 showConsole(showjconsole);
468 showNews.setVisible(false);
470 experimentalFeatures.setSelected(showExperimental());
472 getIdentifiersOrgData();
476 // Spawn a thread that shows the splashscreen
478 SwingUtilities.invokeLater(new Runnable()
487 // Thread off a new instance of the file chooser - this reduces the time
489 // takes to open it later on.
490 new Thread(new Runnable()
495 Cache.log.debug("Filechooser init thread started.");
496 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
497 JalviewFileChooser.forRead(Cache.getProperty("LAST_DIRECTORY"),
499 Cache.log.debug("Filechooser init thread finished.");
502 // Add the service change listener
503 changeSupport.addJalviewPropertyChangeListener("services",
504 new PropertyChangeListener()
508 public void propertyChange(PropertyChangeEvent evt)
510 Cache.log.debug("Firing service changed event for "
511 + evt.getNewValue());
512 JalviewServicesChanged(evt);
519 this.setDropTarget(new java.awt.dnd.DropTarget(desktopPane, this));
521 this.addWindowListener(new WindowAdapter()
524 public void windowClosing(WindowEvent evt)
531 this.addMouseListener(ma = new MouseAdapter()
534 public void mousePressed(MouseEvent evt)
536 if (evt.isPopupTrigger()) // Mac
538 showPasteMenu(evt.getX(), evt.getY());
543 public void mouseReleased(MouseEvent evt)
545 if (evt.isPopupTrigger()) // Windows
547 showPasteMenu(evt.getX(), evt.getY());
551 desktopPane.addMouseListener(ma);
552 } catch (Throwable t)
559 * Answers true if user preferences to enable experimental features is True
564 public boolean showExperimental()
566 String experimental = Cache.getDefault(EXPERIMENTAL_FEATURES,
567 Boolean.FALSE.toString());
568 return Boolean.valueOf(experimental).booleanValue();
571 public void doConfigureStructurePrefs()
573 // configure services
574 StructureSelectionManager ssm = StructureSelectionManager
575 .getStructureSelectionManager(this);
576 if (jalview.bin.Cache.getDefault(Preferences.ADD_SS_ANN, true))
578 ssm.setAddTempFacAnnot(jalview.bin.Cache
579 .getDefault(Preferences.ADD_TEMPFACT_ANN, true));
580 ssm.setProcessSecondaryStructure(jalview.bin.Cache
581 .getDefault(Preferences.STRUCT_FROM_PDB, true));
582 ssm.setSecStructServices(
583 jalview.bin.Cache.getDefault(Preferences.USE_RNAVIEW, true));
587 ssm.setAddTempFacAnnot(false);
588 ssm.setProcessSecondaryStructure(false);
589 ssm.setSecStructServices(false);
593 public void checkForNews()
595 final Desktop me = this;
596 // Thread off the news reader, in case there are connection problems.
597 new Thread(new Runnable()
602 Cache.log.debug("Starting news thread.");
603 jvnews = new BlogReader(me);
604 showNews.setVisible(true);
605 Cache.log.debug("Completed news thread.");
610 public void getIdentifiersOrgData()
612 // Thread off the identifiers fetcher
613 new Thread(new Runnable()
618 Cache.log.debug("Downloading data from identifiers.org");
619 // UrlDownloadClient client = new UrlDownloadClient();
622 UrlDownloadClient.download(IdOrgSettings.getUrl(),
623 IdOrgSettings.getDownloadLocation());
624 } catch (IOException e)
626 Cache.log.debug("Exception downloading identifiers.org data"
635 protected void showNews_actionPerformed(ActionEvent e)
637 showNews(showNews.isSelected());
640 protected void showNews(boolean visible)
642 Cache.log.debug((visible ? "Showing" : "Hiding") + " news.");
643 showNews.setSelected(visible);
644 if (visible && !jvnews.isVisible())
646 new Thread(new Runnable()
651 long now = System.currentTimeMillis();
653 MessageManager.getString("status.refreshing_news"), now);
654 jvnews.refreshNews();
655 setProgressBar(null, now);
663 * recover the last known dimensions for a jalview window
666 * - empty string is desktop, all other windows have unique prefix
667 * @return null or last known dimensions scaled to current geometry (if last
668 * window geom was known)
670 Rectangle getLastKnownDimensions(String windowName)
672 // TODO: lock aspect ratio for scaling desktop Bug #0058199
673 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
674 String x = jalview.bin.Cache.getProperty(windowName + "SCREEN_X");
675 String y = jalview.bin.Cache.getProperty(windowName + "SCREEN_Y");
676 String width = jalview.bin.Cache
677 .getProperty(windowName + "SCREEN_WIDTH");
678 String height = jalview.bin.Cache
679 .getProperty(windowName + "SCREEN_HEIGHT");
680 if ((x != null) && (y != null) && (width != null) && (height != null))
682 int ix = Integer.parseInt(x), iy = Integer.parseInt(y),
683 iw = Integer.parseInt(width), ih = Integer.parseInt(height);
684 if (jalview.bin.Cache.getProperty("SCREENGEOMETRY_WIDTH") != null)
686 // attempt #1 - try to cope with change in screen geometry - this
687 // version doesn't preserve original jv aspect ratio.
688 // take ratio of current screen size vs original screen size.
689 double sw = ((1f * screenSize.width) / (1f * Integer.parseInt(
690 jalview.bin.Cache.getProperty("SCREENGEOMETRY_WIDTH"))));
691 double sh = ((1f * screenSize.height) / (1f * Integer.parseInt(
692 jalview.bin.Cache.getProperty("SCREENGEOMETRY_HEIGHT"))));
693 // rescale the bounds depending upon the current screen geometry.
694 ix = (int) (ix * sw);
695 iw = (int) (iw * sw);
696 iy = (int) (iy * sh);
697 ih = (int) (ih * sh);
698 while (ix >= screenSize.width)
700 jalview.bin.Cache.log.debug(
701 "Window geometry location recall error: shifting horizontal to within screenbounds.");
702 ix -= screenSize.width;
704 while (iy >= screenSize.height)
706 jalview.bin.Cache.log.debug(
707 "Window geometry location recall error: shifting vertical to within screenbounds.");
708 iy -= screenSize.height;
710 jalview.bin.Cache.log.debug(
711 "Got last known dimensions for " + windowName + ": x:" + ix
712 + " y:" + iy + " width:" + iw + " height:" + ih);
714 // return dimensions for new instance
715 return new Rectangle(ix, iy, iw, ih);
720 private void doVamsasClientCheck()
722 if (Cache.vamsasJarsPresent())
724 setupVamsasDisconnectedGui();
725 VamsasMenu.setVisible(true);
726 final Desktop us = this;
727 VamsasMenu.addMenuListener(new MenuListener()
729 // this listener remembers when the menu was first selected, and
730 // doesn't rebuild the session list until it has been cleared and
732 boolean refresh = true;
735 public void menuCanceled(MenuEvent e)
741 public void menuDeselected(MenuEvent e)
747 public void menuSelected(MenuEvent e)
751 us.buildVamsasStMenu();
756 vamsasStart.setVisible(true);
760 protected void showPasteMenu(int x, int y)
762 JPopupMenu popup = new JPopupMenu();
763 JMenuItem item = new JMenuItem(
764 MessageManager.getString("label.paste_new_window"));
765 item.addActionListener(new ActionListener()
768 public void actionPerformed(ActionEvent evt)
775 popup.show(this, x, y);
782 Clipboard c = Toolkit.getDefaultToolkit().getSystemClipboard();
783 Transferable contents = c.getContents(this);
785 if (contents != null)
787 String file = (String) contents
788 .getTransferData(DataFlavor.stringFlavor);
790 FileFormatI format = new IdentifyFile().identify(file,
791 DataSourceType.PASTE);
793 new FileLoader().loadFile(file, DataSourceType.PASTE, format);
796 } catch (Exception ex)
799 "Unable to paste alignment from system clipboard:\n" + ex);
804 * Adds and opens the given frame to the desktop
815 public static synchronized void addInternalFrame(
816 final JInternalFrame frame, String title, int w, int h)
818 addInternalFrame(frame, title, true, w, h, true, false);
822 * Add an internal frame to the Jalview desktop
829 * When true, display frame immediately, otherwise, caller must call
830 * setVisible themselves.
836 public static synchronized void addInternalFrame(
837 final JInternalFrame frame, String title, boolean makeVisible,
840 addInternalFrame(frame, title, makeVisible, w, h, true, false);
844 * Add an internal frame to the Jalview desktop and make it visible
857 public static synchronized void addInternalFrame(
858 final JInternalFrame frame, String title, int w, int h,
861 addInternalFrame(frame, title, true, w, h, resizable, false);
865 * Add an internal frame to the Jalview desktop
872 * When true, display frame immediately, otherwise, caller must call
873 * setVisible themselves.
880 * @param ignoreMinSize
881 * Do not set the default minimum size for frame
883 public static synchronized void addInternalFrame(
884 final JInternalFrame frame, String title, boolean makeVisible,
885 int w, int h, boolean resizable, boolean ignoreMinSize)
889 // TODO: allow callers to determine X and Y position of frame (eg. via
891 // TODO: consider fixing method to update entries in the window submenu with
892 // the current window title
894 frame.setTitle(title);
895 if (w > 0 && (frame.getWidth() < 1 || frame.getHeight() < 1))
899 // THIS IS A PUBLIC STATIC METHOD, SO IT MAY BE CALLED EVEN IN
900 // A HEADLESS STATE WHEN NO DESKTOP EXISTS. MUST RETURN
901 // IF JALVIEW IS RUNNING HEADLESS
902 // ///////////////////////////////////////////////
903 if (Jalview.isHeadlessMode())
912 frame.setMinimumSize(
913 new Dimension(DEFAULT_MIN_WIDTH, DEFAULT_MIN_HEIGHT));
915 // Set default dimension for Alignment Frame window.
916 // The Alignment Frame window could be added from a number of places,
918 // I did this here in order not to miss out on any Alignment frame.
919 if (frame instanceof AlignFrame)
921 frame.setMinimumSize(new Dimension(ALIGN_FRAME_DEFAULT_MIN_WIDTH,
922 ALIGN_FRAME_DEFAULT_MIN_HEIGHT));
926 frame.setVisible(makeVisible);
927 frame.setClosable(true);
928 frame.setResizable(resizable);
929 frame.setMaximizable(resizable);
930 frame.setIconifiable(resizable);
931 frame.setOpaque(Platform.isJS());
933 boolean isEmbedded = (Platform.getDimIfEmbedded(frame, -1, -1) != null);
934 if (!isEmbedded && frame.getX() < 1 && frame.getY() < 1)
936 frame.setLocation(xOffset * openFrameCount,
937 yOffset * ((openFrameCount - 1) % 10) + yOffset);
941 * add an entry for the new frame in the Window menu
942 * (and remove it when the frame is closed)
944 JMenuItem menuItem = new JMenuItem(title);
945 frame.addInternalFrameListener(new InternalFrameAdapter()
948 public void internalFrameActivated(InternalFrameEvent evt)
950 JInternalFrame itf = getDesktopPane().getSelectedFrame();
953 if (itf instanceof AlignFrame)
955 Jalview.setCurrentAlignFrame((AlignFrame) itf);
962 public void internalFrameClosed(InternalFrameEvent evt)
964 PaintRefresher.RemoveComponent(frame);
967 * defensive check to prevent frames being
968 * added half off the window
970 if (openFrameCount > 0)
976 * ensure no reference to alignFrame retained by menu item listener
978 if (menuItem.getActionListeners().length > 0)
980 menuItem.removeActionListener(menuItem.getActionListeners()[0]);
982 Desktop.getInstance().windowMenu.remove(menuItem);
986 menuItem.addActionListener(new ActionListener()
989 public void actionPerformed(ActionEvent e)
993 frame.setSelected(true);
994 frame.setIcon(false);
995 } catch (java.beans.PropertyVetoException ex)
997 // System.err.println(ex.toString());
1002 setKeyBindings(frame);
1004 getDesktopPane().add(frame);
1006 Desktop.getInstance().windowMenu.add(menuItem);
1011 frame.setSelected(true);
1012 frame.requestFocus();
1013 } catch (java.beans.PropertyVetoException ve)
1015 } catch (java.lang.ClassCastException cex)
1018 "Squashed a possible GUI implementation error. If you can recreate this, please look at http://issues.jalview.org/browse/JAL-869",
1024 * Add key bindings to a JInternalFrame so that Ctrl-W and Cmd-W will close the
1029 private static void setKeyBindings(JInternalFrame frame)
1031 final Action closeAction = new AbstractAction()
1034 public void actionPerformed(ActionEvent e)
1041 * set up key bindings for Ctrl-W and Cmd-W, with the same (Close) action
1043 KeyStroke ctrlWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
1044 InputEvent.CTRL_DOWN_MASK);
1045 KeyStroke cmdWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
1046 Toolkit.getDefaultToolkit().getMenuShortcutKeyMask());
1048 InputMap inputMap = frame
1049 .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
1050 String ctrlW = ctrlWKey.toString();
1051 inputMap.put(ctrlWKey, ctrlW);
1052 inputMap.put(cmdWKey, ctrlW);
1054 ActionMap actionMap = frame.getActionMap();
1055 actionMap.put(ctrlW, closeAction);
1059 public void lostOwnership(Clipboard clipboard, Transferable contents)
1063 Desktop.getInstance().jalviewClipboard = null;
1066 internalCopy = false;
1070 public void dragEnter(DropTargetDragEvent evt)
1075 public void dragExit(DropTargetEvent evt)
1080 public void dragOver(DropTargetDragEvent evt)
1085 public void dropActionChanged(DropTargetDragEvent evt)
1096 public void drop(DropTargetDropEvent evt)
1098 boolean success = true;
1099 // JAL-1552 - acceptDrop required before getTransferable call for
1100 // Java's Transferable for native dnd
1101 evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
1102 Transferable t = evt.getTransferable();
1103 List<Object> files = new ArrayList<>();
1104 List<DataSourceType> protocols = new ArrayList<>();
1108 Desktop.transferFromDropTarget(files, protocols, evt, t);
1109 } catch (Exception e)
1111 e.printStackTrace();
1119 for (int i = 0; i < files.size(); i++)
1121 // BH 2018 File or String
1122 Object file = files.get(i);
1123 String fileName = file.toString();
1124 DataSourceType protocol = (protocols == null)
1125 ? DataSourceType.FILE
1127 FileFormatI format = null;
1129 if (fileName.endsWith(".jar"))
1131 format = FileFormat.Jalview;
1136 format = new IdentifyFile().identify(file, protocol);
1138 if (file instanceof File)
1140 Platform.cacheFileData((File) file);
1142 new FileLoader().loadFile(null, file, protocol, format);
1145 } catch (Exception ex)
1150 evt.dropComplete(success); // need this to ensure input focus is properly
1151 // transfered to any new windows created
1161 public void inputLocalFileMenuItem_actionPerformed(AlignViewport viewport)
1163 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
1164 JalviewFileChooser chooser = JalviewFileChooser
1165 .forRead(Cache.getProperty("LAST_DIRECTORY"), fileFormat, true);
1167 chooser.setFileView(new JalviewFileView());
1168 chooser.setDialogTitle(
1169 MessageManager.getString("label.open_local_file"));
1170 chooser.setToolTipText(MessageManager.getString("action.open"));
1172 chooser.setResponseHandler(0, new Runnable()
1177 File selectedFile = chooser.getSelectedFile();
1178 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1180 FileFormatI format = chooser.getSelectedFormat();
1183 * Call IdentifyFile to verify the file contains what its extension implies.
1184 * Skip this step for dynamically added file formats, because
1185 * IdentifyFile does not know how to recognise them.
1187 if (FileFormats.getInstance().isIdentifiable(format))
1191 format = new IdentifyFile().identify(selectedFile,
1192 DataSourceType.FILE);
1193 } catch (FileFormatException e)
1195 // format = null; //??
1199 new FileLoader().loadFile(viewport, selectedFile,
1200 DataSourceType.FILE, format);
1203 chooser.showOpenDialog(this);
1207 * Shows a dialog for input of a URL at which to retrieve alignment data
1212 public void inputURLMenuItem_actionPerformed(AlignViewport viewport)
1214 // This construct allows us to have a wider textfield
1216 JLabel label = new JLabel(
1217 MessageManager.getString("label.input_file_url"));
1219 JPanel panel = new JPanel(new GridLayout(2, 1));
1223 * the URL to fetch is
1224 * Java: an editable combobox with history
1225 * JS: (pending JAL-3038) a plain text field
1228 String urlBase = "http://www.";
1229 if (Platform.isJS())
1231 history = new JTextField(urlBase, 35);
1240 JComboBox<String> asCombo = new JComboBox<>();
1241 asCombo.setPreferredSize(new Dimension(400, 20));
1242 asCombo.setEditable(true);
1243 asCombo.addItem(urlBase);
1244 String historyItems = Cache.getProperty("RECENT_URL");
1245 if (historyItems != null)
1247 for (String token : historyItems.split("\\t"))
1249 asCombo.addItem(token);
1256 Object[] options = new Object[] { MessageManager.getString("action.ok"),
1257 MessageManager.getString("action.cancel") };
1258 Runnable action = new Runnable()
1263 @SuppressWarnings("unchecked")
1264 String url = (history instanceof JTextField
1265 ? ((JTextField) history).getText()
1266 : ((JComboBox<String>) history).getSelectedItem()
1269 if (url.toLowerCase().endsWith(".jar"))
1271 if (viewport != null)
1273 new FileLoader().loadFile(viewport, url, DataSourceType.URL,
1274 FileFormat.Jalview);
1278 new FileLoader().loadFile(url, DataSourceType.URL,
1279 FileFormat.Jalview);
1284 FileFormatI format = null;
1287 format = new IdentifyFile().identify(url, DataSourceType.URL);
1288 } catch (FileFormatException e)
1290 // TODO revise error handling, distinguish between
1291 // URL not found and response not valid
1296 String msg = MessageManager
1297 .formatMessage("label.couldnt_locate", url);
1298 JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
1300 MessageManager.getString("label.url_not_found"),
1301 JvOptionPane.WARNING_MESSAGE);
1306 if (viewport != null)
1308 new FileLoader().loadFile(viewport, url, DataSourceType.URL,
1313 new FileLoader().loadFile(url, DataSourceType.URL, format);
1318 String dialogOption = MessageManager
1319 .getString("label.input_alignment_from_url");
1320 JvOptionPane.newOptionDialog(getDesktopPane())
1321 .setResponseHandler(0, action)
1322 .showInternalDialog(panel, dialogOption,
1323 JvOptionPane.YES_NO_CANCEL_OPTION,
1324 JvOptionPane.PLAIN_MESSAGE, null, options,
1325 MessageManager.getString("action.ok"));
1329 * Opens the CutAndPaste window for the user to paste an alignment in to
1332 * - if not null, the pasted alignment is added to the current
1333 * alignment; if null, to a new alignment window
1336 public void inputTextboxMenuItem_actionPerformed(
1337 AlignmentViewPanel viewPanel)
1339 CutAndPasteTransfer cap = new CutAndPasteTransfer();
1340 cap.setForInput(viewPanel);
1341 Desktop.addInternalFrame(cap,
1342 MessageManager.getString("label.cut_paste_alignmen_file"), true,
1352 Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
1353 jalview.bin.Cache.setProperty("SCREENGEOMETRY_WIDTH",
1355 jalview.bin.Cache.setProperty("SCREENGEOMETRY_HEIGHT",
1356 screen.height + "");
1357 storeLastKnownDimensions("", new Rectangle(getBounds().x, getBounds().y,
1358 getWidth(), getHeight()));
1360 if (jconsole != null)
1362 storeLastKnownDimensions("JAVA_CONSOLE_", jconsole.getBounds());
1363 jconsole.stopConsole();
1367 storeLastKnownDimensions("JALVIEW_RSS_WINDOW_", jvnews.getBounds());
1370 if (dialogExecutor != null)
1372 dialogExecutor.shutdownNow();
1374 closeAll_actionPerformed(null);
1376 if (groovyConsole != null)
1378 // suppress a possible repeat prompt to save script
1379 groovyConsole.setDirty(false);
1380 groovyConsole.exit();
1385 private void storeLastKnownDimensions(String string, Rectangle jc)
1387 jalview.bin.Cache.log.debug("Storing last known dimensions for "
1388 + string + ": x:" + jc.x + " y:" + jc.y + " width:" + jc.width
1389 + " height:" + jc.height);
1391 jalview.bin.Cache.setProperty(string + "SCREEN_X", jc.x + "");
1392 jalview.bin.Cache.setProperty(string + "SCREEN_Y", jc.y + "");
1393 jalview.bin.Cache.setProperty(string + "SCREEN_WIDTH", jc.width + "");
1394 jalview.bin.Cache.setProperty(string + "SCREEN_HEIGHT", jc.height + "");
1404 public void aboutMenuItem_actionPerformed(ActionEvent e)
1406 // StringBuffer message = getAboutMessage(false);
1407 // JvOptionPane.showInternalMessageDialog(Desktop.getDesktop(),
1409 // message.toString(), "About Jalview", JvOptionPane.INFORMATION_MESSAGE);
1410 new Thread(new Runnable()
1415 new SplashScreen(true);
1420 public StringBuffer getAboutMessage(boolean shortv)
1422 StringBuffer message = new StringBuffer();
1423 message.append("<html>");
1426 message.append("<h1><strong>Version: "
1427 + jalview.bin.Cache.getProperty("VERSION")
1428 + "</strong></h1>");
1429 message.append("<strong>Last Updated: <em>"
1430 + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown")
1431 + "</em></strong>");
1437 message.append("<strong>Version "
1438 + jalview.bin.Cache.getProperty("VERSION")
1439 + "; last updated: "
1440 + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown"));
1443 if (jalview.bin.Cache.getDefault("LATEST_VERSION", "Checking")
1444 .equals("Checking"))
1446 message.append("<br>...Checking latest version...</br>");
1448 else if (!jalview.bin.Cache.getDefault("LATEST_VERSION", "Checking")
1449 .equals(jalview.bin.Cache.getProperty("VERSION")))
1451 boolean red = false;
1452 if (jalview.bin.Cache.getProperty("VERSION").toLowerCase()
1453 .indexOf("automated build") == -1)
1456 // Displayed when code version and jnlp version do not match and code
1457 // version is not a development build
1458 message.append("<div style=\"color: #FF0000;font-style: bold;\">");
1461 message.append("<br>!! Version "
1462 + jalview.bin.Cache.getDefault("LATEST_VERSION",
1464 + " is available for download from "
1465 + jalview.bin.Cache.getDefault("www.jalview.org",
1466 "http://www.jalview.org")
1470 message.append("</div>");
1473 message.append("<br>Authors: " + jalview.bin.Cache.getDefault(
1475 "The Jalview Authors (See AUTHORS file for current list)")
1476 + "<br><br>Development managed by The Barton Group, University of Dundee, Scotland, UK.<br>"
1477 + "<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"
1478 + "<br><br>If you use Jalview, please cite:"
1479 + "<br>Waterhouse, A.M., Procter, J.B., Martin, D.M.A, Clamp, M. and Barton, G. J. (2009)"
1480 + "<br>Jalview Version 2 - a multiple sequence alignment editor and analysis workbench"
1481 + "<br>Bioinformatics doi: 10.1093/bioinformatics/btp033"
1487 * Action on requesting Help documentation
1490 public void documentationMenuItem_actionPerformed()
1494 if (Platform.isJS())
1496 BrowserLauncher.openURL("http://www.jalview.org/help.html");
1505 Help.showHelpWindow();
1507 } catch (Exception ex)
1509 System.err.println("Error opening help: " + ex.getMessage());
1514 public void closeAll_actionPerformed(ActionEvent e)
1516 if (desktopPane == null)
1520 // TODO show a progress bar while closing?
1521 JInternalFrame[] frames = desktopPane.getAllFrames();
1522 for (int i = 0; i < frames.length; i++)
1526 frames[i].setClosed(true);
1527 } catch (java.beans.PropertyVetoException ex)
1531 Jalview.setCurrentAlignFrame(null);
1532 System.out.println("ALL CLOSED");
1533 if (v_client != null)
1535 // TODO clear binding to vamsas document objects on close_all
1539 * reset state of singleton objects as appropriate (clear down session state
1540 * when all windows are closed)
1542 getStructureSelectionManager().resetAll();
1546 public void raiseRelated_actionPerformed(ActionEvent e)
1548 reorderAssociatedWindows(false, false);
1552 public void minimizeAssociated_actionPerformed(ActionEvent e)
1554 reorderAssociatedWindows(true, false);
1557 void closeAssociatedWindows()
1559 reorderAssociatedWindows(false, true);
1565 * @seejalview.jbgui.GDesktop#garbageCollect_actionPerformed(java.awt.event.
1569 protected void garbageCollect_actionPerformed(ActionEvent e)
1571 // We simply collect the garbage
1572 jalview.bin.Cache.log.debug("Collecting garbage...");
1574 jalview.bin.Cache.log.debug("Finished garbage collection.");
1581 * jalview.jbgui.GDesktop#showMemusage_actionPerformed(java.awt.event.ActionEvent
1585 protected void showMemusage_actionPerformed(ActionEvent e)
1587 getDesktopPane().showMemoryUsage(showMemusage.isSelected());
1594 * jalview.jbgui.GDesktop#showConsole_actionPerformed(java.awt.event.ActionEvent
1598 protected void showConsole_actionPerformed(ActionEvent e)
1600 showConsole(showConsole.isSelected());
1603 Console jconsole = null;
1606 * control whether the java console is visible or not
1610 void showConsole(boolean selected)
1612 // TODO: decide if we should update properties file
1613 if (jconsole != null) // BH 2018
1615 showConsole.setSelected(selected);
1616 Cache.setProperty("SHOW_JAVA_CONSOLE",
1617 Boolean.valueOf(selected).toString());
1618 jconsole.setVisible(selected);
1622 void reorderAssociatedWindows(boolean minimize, boolean close)
1624 JInternalFrame[] frames = getDesktopPane().getAllFrames();
1625 if (frames == null || frames.length < 1)
1630 AlignmentViewport source = null, target = null;
1631 if (frames[0] instanceof AlignFrame)
1633 source = ((AlignFrame) frames[0]).getCurrentView();
1635 else if (frames[0] instanceof TreePanel)
1637 source = ((TreePanel) frames[0]).getViewPort();
1639 else if (frames[0] instanceof PCAPanel)
1641 source = ((PCAPanel) frames[0]).av;
1643 else if (frames[0].getContentPane() instanceof PairwiseAlignPanel)
1645 source = ((PairwiseAlignPanel) frames[0].getContentPane()).av;
1650 for (int i = 0; i < frames.length; i++)
1653 if (frames[i] == null)
1657 if (frames[i] instanceof AlignFrame)
1659 target = ((AlignFrame) frames[i]).getCurrentView();
1661 else if (frames[i] instanceof TreePanel)
1663 target = ((TreePanel) frames[i]).getViewPort();
1665 else if (frames[i] instanceof PCAPanel)
1667 target = ((PCAPanel) frames[i]).av;
1669 else if (frames[i].getContentPane() instanceof PairwiseAlignPanel)
1671 target = ((PairwiseAlignPanel) frames[i].getContentPane()).av;
1674 if (source == target)
1680 frames[i].setClosed(true);
1684 frames[i].setIcon(minimize);
1687 frames[i].toFront();
1691 } catch (java.beans.PropertyVetoException ex)
1706 protected void preferences_actionPerformed(ActionEvent e)
1712 * Prompts the user to choose a file and then saves the Jalview state as a
1713 * Jalview project file
1716 public void saveState_actionPerformed()
1718 saveState_actionPerformed(false);
1721 public void saveState_actionPerformed(boolean saveAs)
1723 java.io.File projectFile = getProjectFile();
1724 // autoSave indicates we already have a file and don't need to ask
1725 boolean autoSave = projectFile != null && !saveAs
1726 && BackupFiles.getEnabled();
1728 // System.out.println("autoSave="+autoSave+", projectFile='"+projectFile+"',
1729 // saveAs="+saveAs+", Backups
1730 // "+(BackupFiles.getEnabled()?"enabled":"disabled"));
1732 boolean approveSave = false;
1735 JalviewFileChooser chooser = new JalviewFileChooser("jvp",
1738 chooser.setFileView(new JalviewFileView());
1739 chooser.setDialogTitle(MessageManager.getString("label.save_state"));
1741 int value = chooser.showSaveDialog(this);
1743 if (value == JalviewFileChooser.APPROVE_OPTION)
1745 projectFile = chooser.getSelectedFile();
1746 setProjectFile(projectFile);
1751 if (approveSave || autoSave)
1753 final Desktop me = this;
1754 final java.io.File chosenFile = projectFile;
1755 new Thread(new Runnable()
1760 // TODO: refactor to Jalview desktop session controller action.
1761 setProgressBar(MessageManager.formatMessage(
1762 "label.saving_jalview_project", new Object[]
1763 { chosenFile.getName() }), chosenFile.hashCode());
1764 jalview.bin.Cache.setProperty("LAST_DIRECTORY",
1765 chosenFile.getParent());
1766 // TODO catch and handle errors for savestate
1767 // TODO prevent user from messing with the Desktop whilst we're saving
1770 boolean doBackup = BackupFiles.getEnabled();
1771 BackupFiles backupfiles = doBackup ? new BackupFiles(chosenFile) : null;
1773 new Jalview2XML().saveState(doBackup ? backupfiles.getTempFile() : chosenFile);
1777 backupfiles.setWriteSuccess(true);
1778 backupfiles.rollBackupsAndRenameTempFile();
1780 } catch (OutOfMemoryError oom)
1782 new OOMWarning("Whilst saving current state to "
1783 + chosenFile.getName(), oom);
1784 } catch (Exception ex)
1786 Cache.log.error("Problems whilst trying to save to "
1787 + chosenFile.getName(), ex);
1788 JvOptionPane.showMessageDialog(me,
1789 MessageManager.formatMessage(
1790 "label.error_whilst_saving_current_state_to",
1792 { chosenFile.getName() }),
1793 MessageManager.getString("label.couldnt_save_project"),
1794 JvOptionPane.WARNING_MESSAGE);
1796 setProgressBar(null, chosenFile.hashCode());
1803 public void saveAsState_actionPerformed(ActionEvent e)
1805 saveState_actionPerformed(true);
1808 protected void setProjectFile(File choice)
1810 this.projectFile = choice;
1813 public File getProjectFile()
1815 return this.projectFile;
1819 * Shows a file chooser dialog and tries to read in the selected file as a
1823 public void loadState_actionPerformed()
1825 final String[] suffix = new String[] { "jvp", "jar" };
1826 final String[] desc = new String[] { "Jalview Project",
1827 "Jalview Project (old)" };
1828 JalviewFileChooser chooser = new JalviewFileChooser(
1829 Cache.getProperty("LAST_DIRECTORY"), suffix, desc,
1830 "Jalview Project", true, true); // last two booleans: allFiles,
1832 chooser.setFileView(new JalviewFileView());
1833 chooser.setDialogTitle(MessageManager.getString("label.restore_state"));
1834 chooser.setResponseHandler(0, new Runnable()
1839 File selectedFile = chooser.getSelectedFile();
1840 setProjectFile(selectedFile);
1841 String choice = selectedFile.getAbsolutePath();
1842 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1843 new Thread(new Runnable()
1850 new Jalview2XML().loadJalviewAlign(selectedFile);
1851 } catch (OutOfMemoryError oom)
1853 new OOMWarning("Whilst loading project from " + choice, oom);
1854 } catch (Exception ex)
1857 "Problems whilst loading project from " + choice, ex);
1858 JvOptionPane.showMessageDialog(Desktop.getDesktopPane(),
1859 MessageManager.formatMessage(
1860 "label.error_whilst_loading_project_from",
1863 MessageManager.getString("label.couldnt_load_project"),
1864 JvOptionPane.WARNING_MESSAGE);
1871 chooser.showOpenDialog(this);
1875 public void inputSequence_actionPerformed(ActionEvent e)
1877 new SequenceFetcher(this);
1880 JPanel progressPanel;
1882 ArrayList<JPanel> fileLoadingPanels = new ArrayList<>();
1884 public void startLoading(final Object fileName)
1886 if (fileLoadingCount == 0)
1888 fileLoadingPanels.add(addProgressPanel(MessageManager
1889 .formatMessage("label.loading_file", new Object[]
1895 private JPanel addProgressPanel(String string)
1897 if (progressPanel == null)
1899 progressPanel = new JPanel(new GridLayout(1, 1));
1900 totalProgressCount = 0;
1901 getContentPane().add(progressPanel, BorderLayout.SOUTH);
1903 JPanel thisprogress = new JPanel(new BorderLayout(10, 5));
1904 JProgressBar progressBar = new JProgressBar();
1905 progressBar.setIndeterminate(true);
1907 thisprogress.add(new JLabel(string), BorderLayout.WEST);
1909 thisprogress.add(progressBar, BorderLayout.CENTER);
1910 progressPanel.add(thisprogress);
1911 ((GridLayout) progressPanel.getLayout()).setRows(
1912 ((GridLayout) progressPanel.getLayout()).getRows() + 1);
1913 ++totalProgressCount;
1915 return thisprogress;
1918 int totalProgressCount = 0;
1920 private void removeProgressPanel(JPanel progbar)
1922 if (progressPanel != null)
1924 synchronized (progressPanel)
1926 progressPanel.remove(progbar);
1927 GridLayout gl = (GridLayout) progressPanel.getLayout();
1928 gl.setRows(gl.getRows() - 1);
1929 if (--totalProgressCount < 1)
1931 this.getContentPane().remove(progressPanel);
1932 progressPanel = null;
1939 public void stopLoading()
1942 if (fileLoadingCount < 1)
1944 while (fileLoadingPanels.size() > 0)
1946 removeProgressPanel(fileLoadingPanels.remove(0));
1948 fileLoadingPanels.clear();
1949 fileLoadingCount = 0;
1954 public static int getViewCount(String alignmentId)
1956 AlignmentViewport[] aps = getViewports(alignmentId);
1957 return (aps == null) ? 0 : aps.length;
1962 * @param alignmentId
1963 * - if null, all sets are returned
1964 * @return all AlignmentPanels concerning the alignmentId sequence set
1966 public static AlignmentPanel[] getAlignmentPanels(String alignmentId)
1968 if (Desktop.getDesktopPane() == null)
1970 // no frames created and in headless mode
1971 // TODO: verify that frames are recoverable when in headless mode
1974 List<AlignmentPanel> aps = new ArrayList<>();
1975 AlignFrame[] frames = getAlignFrames();
1980 for (AlignFrame af : frames)
1982 for (AlignmentPanel ap : af.alignPanels)
1984 if (alignmentId == null
1985 || alignmentId.equals(ap.av.getSequenceSetId()))
1991 if (aps.size() == 0)
1995 AlignmentPanel[] vap = aps.toArray(new AlignmentPanel[aps.size()]);
2000 * get all the viewports on an alignment.
2002 * @param sequenceSetId
2003 * unique alignment id (may be null - all viewports returned in that
2005 * @return all viewports on the alignment bound to sequenceSetId
2007 public static AlignmentViewport[] getViewports(String sequenceSetId)
2009 List<AlignmentViewport> viewp = new ArrayList<>();
2010 if (getDesktopPane() != null)
2012 AlignFrame[] frames = Desktop.getAlignFrames();
2014 for (AlignFrame afr : frames)
2016 if (sequenceSetId == null || afr.getViewport().getSequenceSetId()
2017 .equals(sequenceSetId))
2019 if (afr.alignPanels != null)
2021 for (AlignmentPanel ap : afr.alignPanels)
2023 if (sequenceSetId == null
2024 || sequenceSetId.equals(ap.av.getSequenceSetId()))
2032 viewp.add(afr.getViewport());
2036 if (viewp.size() > 0)
2038 return viewp.toArray(new AlignmentViewport[viewp.size()]);
2045 * Explode the views in the given frame into separate AlignFrame
2049 public static void explodeViews(AlignFrame af)
2051 int size = af.alignPanels.size();
2057 for (int i = 0; i < size; i++)
2059 AlignmentPanel ap = af.alignPanels.get(i);
2060 AlignFrame newaf = new AlignFrame(ap);
2063 * Restore the view's last exploded frame geometry if known. Multiple
2064 * views from one exploded frame share and restore the same (frame)
2065 * position and size.
2067 Rectangle geometry = ap.av.getExplodedGeometry();
2068 if (geometry != null)
2070 newaf.setBounds(geometry);
2073 ap.av.setGatherViewsHere(false);
2075 addInternalFrame(newaf, af.getTitle(), AlignFrame.DEFAULT_WIDTH,
2076 AlignFrame.DEFAULT_HEIGHT);
2079 af.alignPanels.clear();
2080 af.closeMenuItem_actionPerformed(true);
2085 * Gather expanded views (separate AlignFrame's) with the same sequence set
2086 * identifier back in to this frame as additional views, and close the expanded
2087 * views. Note the expanded frames may themselves have multiple views. We take
2092 public void gatherViews(AlignFrame source)
2094 source.viewport.setGatherViewsHere(true);
2095 source.viewport.setExplodedGeometry(source.getBounds());
2096 JInternalFrame[] frames = getAllFrames();
2097 String viewId = source.viewport.getSequenceSetId();
2099 for (int t = 0; t < frames.length; t++)
2101 if (frames[t] instanceof AlignFrame && frames[t] != source)
2103 AlignFrame af = (AlignFrame) frames[t];
2104 boolean gatherThis = false;
2105 for (int a = 0; a < af.alignPanels.size(); a++)
2107 AlignmentPanel ap = af.alignPanels.get(a);
2108 if (viewId.equals(ap.av.getSequenceSetId()))
2111 ap.av.setGatherViewsHere(false);
2112 ap.av.setExplodedGeometry(af.getBounds());
2113 source.addAlignmentPanel(ap, false);
2119 af.alignPanels.clear();
2120 af.closeMenuItem_actionPerformed(true);
2127 jalview.gui.VamsasApplication v_client = null;
2130 public void vamsasImport_actionPerformed(ActionEvent e)
2132 // TODO: JAL-3048 not needed for Jalview-JS
2134 if (v_client == null)
2136 // Load and try to start a session.
2137 JalviewFileChooser chooser = new JalviewFileChooser(
2138 jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
2140 chooser.setFileView(new JalviewFileView());
2141 chooser.setDialogTitle(
2142 MessageManager.getString("label.open_saved_vamsas_session"));
2143 chooser.setToolTipText(MessageManager.getString(
2144 "label.select_vamsas_session_opened_as_new_vamsas_session"));
2146 int value = chooser.showOpenDialog(this);
2148 if (value == JalviewFileChooser.APPROVE_OPTION)
2150 String fle = chooser.getSelectedFile().toString();
2151 if (!vamsasImport(chooser.getSelectedFile()))
2153 JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
2154 MessageManager.formatMessage(
2155 "label.couldnt_import_as_vamsas_session",
2159 .getString("label.vamsas_document_import_failed"),
2160 JvOptionPane.ERROR_MESSAGE);
2166 jalview.bin.Cache.log.error(
2167 "Implementation error - load session from a running session is not supported.");
2172 * import file into a new vamsas session (uses jalview.gui.VamsasApplication)
2175 * @return true if import was a success and a session was started.
2177 public boolean vamsasImport(URL url)
2179 // TODO: create progress bar
2180 if (v_client != null)
2183 jalview.bin.Cache.log.error(
2184 "Implementation error - load session from a running session is not supported.");
2190 // copy the URL content to a temporary local file
2191 // TODO: be a bit cleverer here with nio (?!)
2192 File file = File.createTempFile("vdocfromurl", ".vdj");
2193 FileOutputStream fos = new FileOutputStream(file);
2194 BufferedInputStream bis = new BufferedInputStream(url.openStream());
2195 byte[] buffer = new byte[2048];
2197 while ((ln = bis.read(buffer)) > -1)
2199 fos.write(buffer, 0, ln);
2203 v_client = new jalview.gui.VamsasApplication(this, file,
2204 url.toExternalForm());
2205 } catch (Exception ex)
2207 jalview.bin.Cache.log.error(
2208 "Failed to create new vamsas session from contents of URL "
2213 setupVamsasConnectedGui();
2214 v_client.initial_update(); // TODO: thread ?
2215 return v_client.inSession();
2219 * import file into a new vamsas session (uses jalview.gui.VamsasApplication)
2222 * @return true if import was a success and a session was started.
2224 public boolean vamsasImport(File file)
2226 if (v_client != null)
2229 jalview.bin.Cache.log.error(
2230 "Implementation error - load session from a running session is not supported.");
2234 setProgressBar(MessageManager.formatMessage(
2235 "status.importing_vamsas_session_from", new Object[]
2236 { file.getName() }), file.hashCode());
2239 v_client = new jalview.gui.VamsasApplication(this, file, null);
2240 } catch (Exception ex)
2242 setProgressBar(MessageManager.formatMessage(
2243 "status.importing_vamsas_session_from", new Object[]
2244 { file.getName() }), file.hashCode());
2245 jalview.bin.Cache.log.error(
2246 "New vamsas session from existing session file failed:", ex);
2249 setupVamsasConnectedGui();
2250 v_client.initial_update(); // TODO: thread ?
2251 setProgressBar(MessageManager.formatMessage(
2252 "status.importing_vamsas_session_from", new Object[]
2253 { file.getName() }), file.hashCode());
2254 return v_client.inSession();
2257 public boolean joinVamsasSession(String mysesid)
2259 if (v_client != null)
2261 throw new Error(MessageManager
2262 .getString("error.try_join_vamsas_session_another"));
2264 if (mysesid == null)
2267 MessageManager.getString("error.invalid_vamsas_session_id"));
2269 v_client = new VamsasApplication(this, mysesid);
2270 setupVamsasConnectedGui();
2271 v_client.initial_update();
2272 return (v_client.inSession());
2276 public void vamsasStart_actionPerformed(ActionEvent e)
2278 if (v_client == null)
2281 // we just start a default session for moment.
2283 * JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache.
2284 * getProperty("LAST_DIRECTORY"));
2286 * chooser.setFileView(new JalviewFileView());
2287 * chooser.setDialogTitle("Load Vamsas file");
2288 * chooser.setToolTipText("Import");
2290 * int value = chooser.showOpenDialog(this);
2292 * if (value == JalviewFileChooser.APPROVE_OPTION) { v_client = new
2293 * jalview.gui.VamsasApplication(this, chooser.getSelectedFile());
2295 v_client = new VamsasApplication(this);
2296 setupVamsasConnectedGui();
2297 v_client.initial_update(); // TODO: thread ?
2301 // store current data in session.
2302 v_client.push_update(); // TODO: thread
2306 protected void setupVamsasConnectedGui()
2308 vamsasStart.setText(MessageManager.getString("label.session_update"));
2309 vamsasSave.setVisible(true);
2310 vamsasStop.setVisible(true);
2311 vamsasImport.setVisible(false); // Document import to existing session is
2312 // not possible for vamsas-client-1.0.
2315 protected void setupVamsasDisconnectedGui()
2317 vamsasSave.setVisible(false);
2318 vamsasStop.setVisible(false);
2319 vamsasImport.setVisible(true);
2321 .setText(MessageManager.getString("label.new_vamsas_session"));
2325 public void vamsasStop_actionPerformed(ActionEvent e)
2327 if (v_client != null)
2329 v_client.end_session();
2331 setupVamsasDisconnectedGui();
2335 protected void buildVamsasStMenu()
2337 if (v_client == null)
2339 String[] sess = null;
2342 sess = VamsasApplication.getSessionList();
2343 } catch (Exception e)
2345 jalview.bin.Cache.log.warn("Problem getting current sessions list.",
2351 jalview.bin.Cache.log.debug(
2352 "Got current sessions list: " + sess.length + " entries.");
2353 VamsasStMenu.removeAll();
2354 for (int i = 0; i < sess.length; i++)
2356 JMenuItem sessit = new JMenuItem();
2357 sessit.setText(sess[i]);
2358 sessit.setToolTipText(MessageManager
2359 .formatMessage("label.connect_to_session", new Object[]
2361 final Desktop dsktp = this;
2362 final String mysesid = sess[i];
2363 sessit.addActionListener(new ActionListener()
2367 public void actionPerformed(ActionEvent e)
2369 if (dsktp.v_client == null)
2371 Thread rthr = new Thread(new Runnable()
2377 dsktp.v_client = new VamsasApplication(dsktp, mysesid);
2378 dsktp.setupVamsasConnectedGui();
2379 dsktp.v_client.initial_update();
2387 VamsasStMenu.add(sessit);
2389 // don't show an empty menu.
2390 VamsasStMenu.setVisible(sess.length > 0);
2395 jalview.bin.Cache.log.debug("No current vamsas sessions.");
2396 VamsasStMenu.removeAll();
2397 VamsasStMenu.setVisible(false);
2402 // Not interested in the content. Just hide ourselves.
2403 VamsasStMenu.setVisible(false);
2408 public void vamsasSave_actionPerformed(ActionEvent e)
2410 // TODO: JAL-3048 not needed for Jalview-JS
2412 if (v_client != null)
2414 // TODO: VAMSAS DOCUMENT EXTENSION is VDJ
2415 JalviewFileChooser chooser = new JalviewFileChooser("vdj",
2418 chooser.setFileView(new JalviewFileView());
2419 chooser.setDialogTitle(MessageManager
2420 .getString("label.save_vamsas_document_archive"));
2422 int value = chooser.showSaveDialog(this);
2424 if (value == JalviewFileChooser.APPROVE_OPTION)
2426 java.io.File choice = chooser.getSelectedFile();
2427 JPanel progpanel = addProgressPanel(MessageManager
2428 .formatMessage("label.saving_vamsas_doc", new Object[]
2429 { choice.getName() }));
2430 Cache.setProperty("LAST_DIRECTORY", choice.getParent());
2431 String warnmsg = null;
2432 String warnttl = null;
2435 v_client.vclient.storeDocument(choice);
2438 warnttl = "Serious Problem saving Vamsas Document";
2439 warnmsg = ex.toString();
2440 jalview.bin.Cache.log
2441 .error("Error Whilst saving document to " + choice, ex);
2443 } catch (Exception ex)
2445 warnttl = "Problem saving Vamsas Document.";
2446 warnmsg = ex.toString();
2447 jalview.bin.Cache.log.warn(
2448 "Exception Whilst saving document to " + choice, ex);
2451 removeProgressPanel(progpanel);
2452 if (warnmsg != null)
2454 JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
2456 warnmsg, warnttl, JvOptionPane.ERROR_MESSAGE);
2462 JPanel vamUpdate = null;
2465 * hide vamsas user gui bits when a vamsas document event is being handled.
2468 * true to hide gui, false to reveal gui
2470 public void setVamsasUpdate(boolean b)
2472 Cache.log.debug("Setting gui for Vamsas update "
2473 + (b ? "in progress" : "finished"));
2475 if (vamUpdate != null)
2477 this.removeProgressPanel(vamUpdate);
2481 vamUpdate = this.addProgressPanel(
2482 MessageManager.getString("label.updating_vamsas_session"));
2484 vamsasStart.setVisible(!b);
2485 vamsasStop.setVisible(!b);
2486 vamsasSave.setVisible(!b);
2489 public JInternalFrame[] getAllFrames()
2491 return desktopPane.getAllFrames();
2495 * Checks the given url to see if it gives a response indicating that the user
2496 * should be informed of a new questionnaire.
2500 public void checkForQuestionnaire(String url)
2502 UserQuestionnaireCheck jvq = new UserQuestionnaireCheck(url);
2503 // javax.swing.SwingUtilities.invokeLater(jvq);
2504 new Thread(jvq).start();
2507 public void checkURLLinks()
2509 // Thread off the URL link checker
2510 addDialogThread(new Runnable()
2515 if (Cache.getDefault("CHECKURLLINKS", true))
2517 // check what the actual links are - if it's just the default don't
2518 // bother with the warning
2519 List<String> links = Preferences.sequenceUrlLinks
2522 // only need to check links if there is one with a
2523 // SEQUENCE_ID which is not the default EMBL_EBI link
2524 ListIterator<String> li = links.listIterator();
2525 boolean check = false;
2526 List<JLabel> urls = new ArrayList<>();
2527 while (li.hasNext())
2529 String link = li.next();
2530 if (link.contains(UrlConstants.SEQUENCE_ID)
2531 && !UrlConstants.isDefaultString(link))
2534 int barPos = link.indexOf("|");
2535 String urlMsg = barPos == -1 ? link
2536 : link.substring(0, barPos) + ": "
2537 + link.substring(barPos + 1);
2538 urls.add(new JLabel(urlMsg));
2546 // ask user to check in case URL links use old style tokens
2547 // ($SEQUENCE_ID$ for sequence id _or_ accession id)
2548 JPanel msgPanel = new JPanel();
2549 msgPanel.setLayout(new BoxLayout(msgPanel, BoxLayout.PAGE_AXIS));
2550 msgPanel.add(Box.createVerticalGlue());
2551 JLabel msg = new JLabel(MessageManager
2552 .getString("label.SEQUENCE_ID_for_DB_ACCESSION1"));
2553 JLabel msg2 = new JLabel(MessageManager
2554 .getString("label.SEQUENCE_ID_for_DB_ACCESSION2"));
2556 for (JLabel url : urls)
2562 final JCheckBox jcb = new JCheckBox(
2563 MessageManager.getString("label.do_not_display_again"));
2564 jcb.addActionListener(new ActionListener()
2567 public void actionPerformed(ActionEvent e)
2569 // update Cache settings for "don't show this again"
2570 boolean showWarningAgain = !jcb.isSelected();
2571 Cache.setProperty("CHECKURLLINKS",
2572 Boolean.valueOf(showWarningAgain).toString());
2577 JvOptionPane.showMessageDialog(desktopPane, msgPanel,
2579 .getString("label.SEQUENCE_ID_no_longer_used"),
2580 JvOptionPane.WARNING_MESSAGE);
2587 * Proxy class for JDesktopPane which optionally displays the current memory
2588 * usage and highlights the desktop area with a red bar if free memory runs low.
2592 public class MyDesktopPane extends JDesktopPane
2595 private static final float ONE_MB = 1048576f;
2597 boolean showMemoryUsage = false;
2601 java.text.NumberFormat df;
2603 float maxMemory, allocatedMemory, freeMemory, totalFreeMemory,
2606 public MyDesktopPane(boolean showMemoryUsage)
2608 showMemoryUsage(showMemoryUsage);
2611 public void showMemoryUsage(boolean showMemory)
2613 this.showMemoryUsage = showMemory;
2616 Thread worker = new Thread(this);
2622 public boolean isShowMemoryUsage()
2624 return showMemoryUsage;
2630 df = java.text.NumberFormat.getNumberInstance();
2631 df.setMaximumFractionDigits(2);
2632 runtime = Runtime.getRuntime();
2634 while (showMemoryUsage)
2638 maxMemory = runtime.maxMemory() / ONE_MB;
2639 allocatedMemory = runtime.totalMemory() / ONE_MB;
2640 freeMemory = runtime.freeMemory() / ONE_MB;
2641 totalFreeMemory = freeMemory + (maxMemory - allocatedMemory);
2643 percentUsage = (totalFreeMemory / maxMemory) * 100;
2645 // if (percentUsage < 20)
2647 // border1 = BorderFactory.createMatteBorder(12, 12, 12, 12,
2649 // instance.set.setBorder(border1);
2652 // sleep after showing usage
2654 } catch (Exception ex)
2656 ex.printStackTrace();
2662 public void paintComponent(Graphics g)
2664 if (showMemoryUsage && g != null && df != null)
2666 if (percentUsage < 20)
2668 g.setColor(Color.red);
2670 FontMetrics fm = g.getFontMetrics();
2673 g.drawString(MessageManager.formatMessage("label.memory_stats",
2675 { df.format(totalFreeMemory), df.format(maxMemory),
2676 df.format(percentUsage) }),
2677 10, getHeight() - fm.getHeight());
2684 * Accessor method to quickly get all the AlignmentFrames loaded.
2686 * @return an array of AlignFrame, or null if none found
2688 public static AlignFrame[] getAlignFrames()
2690 if (Jalview.isHeadlessMode())
2692 // Desktop.getDesktop() is null in headless mode
2693 return new AlignFrame[] { Jalview.getCurrentAlignFrame() };
2696 JInternalFrame[] frames = Desktop.getDesktopPane().getAllFrames();
2702 List<AlignFrame> avp = new ArrayList<>();
2704 for (int i = frames.length - 1; i > -1; i--)
2706 if (frames[i] instanceof AlignFrame)
2708 avp.add((AlignFrame) frames[i]);
2710 else if (frames[i] instanceof SplitFrame)
2713 * Also check for a split frame containing an AlignFrame
2715 GSplitFrame sf = (GSplitFrame) frames[i];
2716 if (sf.getTopFrame() instanceof AlignFrame)
2718 avp.add((AlignFrame) sf.getTopFrame());
2720 if (sf.getBottomFrame() instanceof AlignFrame)
2722 avp.add((AlignFrame) sf.getBottomFrame());
2726 if (avp.size() == 0)
2730 AlignFrame afs[] = avp.toArray(new AlignFrame[avp.size()]);
2735 * Returns an array of any AppJmol frames in the Desktop (or null if none).
2739 public GStructureViewer[] getJmols()
2741 JInternalFrame[] frames = Desktop.getDesktopPane().getAllFrames();
2747 List<GStructureViewer> avp = new ArrayList<>();
2749 for (int i = frames.length - 1; i > -1; i--)
2751 if (frames[i] instanceof AppJmol)
2753 GStructureViewer af = (GStructureViewer) frames[i];
2757 if (avp.size() == 0)
2761 GStructureViewer afs[] = avp.toArray(new GStructureViewer[avp.size()]);
2766 * Add Groovy Support to Jalview
2769 public void groovyShell_actionPerformed()
2773 openGroovyConsole();
2774 } catch (Exception ex)
2776 jalview.bin.Cache.log.error("Groovy Shell Creation failed.", ex);
2777 JvOptionPane.showInternalMessageDialog(desktopPane,
2779 MessageManager.getString("label.couldnt_create_groovy_shell"),
2780 MessageManager.getString("label.groovy_support_failed"),
2781 JvOptionPane.ERROR_MESSAGE);
2786 * Open the Groovy console
2788 private void openGroovyConsole()
2790 if (groovyConsole == null)
2792 groovyConsole = new groovy.ui.Console();
2793 groovyConsole.setVariable("Jalview", this);
2794 groovyConsole.run();
2797 * We allow only one console at a time, so that AlignFrame menu option
2798 * 'Calculate | Run Groovy script' is unambiguous.
2799 * Disable 'Groovy Console', and enable 'Run script', when the console is
2800 * opened, and the reverse when it is closed
2802 Window window = (Window) groovyConsole.getFrame();
2803 window.addWindowListener(new WindowAdapter()
2806 public void windowClosed(WindowEvent e)
2809 * rebind CMD-Q from Groovy Console to Jalview Quit
2812 enableExecuteGroovy(false);
2818 * show Groovy console window (after close and reopen)
2820 ((Window) groovyConsole.getFrame()).setVisible(true);
2823 * if we got this far, enable 'Run Groovy' in AlignFrame menus
2824 * and disable opening a second console
2826 enableExecuteGroovy(true);
2830 * Bind Ctrl/Cmd-Q to Quit - for reset as Groovy Console takes over this binding
2833 protected void addQuitHandler()
2835 getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
2836 .put(KeyStroke.getKeyStroke(KeyEvent.VK_Q,
2837 Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()),
2839 getRootPane().getActionMap().put("Quit", new AbstractAction()
2842 public void actionPerformed(ActionEvent e)
2850 * Enable or disable 'Run Groovy script' in AlignFrame calculate menus
2853 * true if Groovy console is open
2855 public void enableExecuteGroovy(boolean enabled)
2858 * disable opening a second Groovy console
2859 * (or re-enable when the console is closed)
2861 groovyShell.setEnabled(!enabled);
2863 AlignFrame[] alignFrames = getAlignFrames();
2864 if (alignFrames != null)
2866 for (AlignFrame af : alignFrames)
2868 af.setGroovyEnabled(enabled);
2874 * Progress bars managed by the IProgressIndicator method.
2876 private Hashtable<Long, JPanel> progressBars;
2878 private Hashtable<Long, IProgressIndicatorHandler> progressBarHandlers;
2883 * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
2886 public void setProgressBar(String message, long id)
2888 Platform.timeCheck("Desktop " + message, Platform.TIME_MARK);
2890 if (progressBars == null)
2892 progressBars = new Hashtable<>();
2893 progressBarHandlers = new Hashtable<>();
2896 if (progressBars.get(new Long(id)) != null)
2898 JPanel panel = progressBars.remove(new Long(id));
2899 if (progressBarHandlers.contains(new Long(id)))
2901 progressBarHandlers.remove(new Long(id));
2903 removeProgressPanel(panel);
2907 progressBars.put(new Long(id), addProgressPanel(message));
2914 * @see jalview.gui.IProgressIndicator#registerHandler(long,
2915 * jalview.gui.IProgressIndicatorHandler)
2918 public void registerHandler(final long id,
2919 final IProgressIndicatorHandler handler)
2921 if (progressBarHandlers == null
2922 || !progressBars.containsKey(new Long(id)))
2924 throw new Error(MessageManager.getString(
2925 "error.call_setprogressbar_before_registering_handler"));
2927 progressBarHandlers.put(new Long(id), handler);
2928 final JPanel progressPanel = progressBars.get(new Long(id));
2929 if (handler.canCancel())
2931 JButton cancel = new JButton(
2932 MessageManager.getString("action.cancel"));
2933 final IProgressIndicator us = this;
2934 cancel.addActionListener(new ActionListener()
2938 public void actionPerformed(ActionEvent e)
2940 handler.cancelActivity(id);
2941 us.setProgressBar(MessageManager
2942 .formatMessage("label.cancelled_params", new Object[]
2943 { ((JLabel) progressPanel.getComponent(0)).getText() }),
2947 progressPanel.add(cancel, BorderLayout.EAST);
2953 * @return true if any progress bars are still active
2956 public boolean operationInProgress()
2958 if (progressBars != null && progressBars.size() > 0)
2966 * This will return the first AlignFrame holding the given viewport instance. It
2967 * will break if there are more than one AlignFrames viewing a particular av.
2970 * @return alignFrame for viewport
2972 public static AlignFrame getAlignFrameFor(AlignViewportI viewport)
2974 if (getDesktopPane() != null)
2976 AlignmentPanel[] aps = getAlignmentPanels(
2977 viewport.getSequenceSetId());
2978 for (int panel = 0; aps != null && panel < aps.length; panel++)
2980 if (aps[panel] != null && aps[panel].av == viewport)
2982 return aps[panel].alignFrame;
2989 public VamsasApplication getVamsasApplication()
2996 * flag set if jalview GUI is being operated programmatically
2998 private boolean inBatchMode = false;
3001 * check if jalview GUI is being operated programmatically
3003 * @return inBatchMode
3005 public boolean isInBatchMode()
3011 * set flag if jalview GUI is being operated programmatically
3013 * @param inBatchMode
3015 public void setInBatchMode(boolean inBatchMode)
3017 this.inBatchMode = inBatchMode;
3020 public void startServiceDiscovery()
3022 startServiceDiscovery(false);
3025 public void startServiceDiscovery(boolean blocking)
3027 boolean alive = true;
3028 Thread t0 = null, t1 = null, t2 = null;
3029 // JAL-940 - JALVIEW 1 services are now being EOLed as of JABA 2.1 release
3032 // todo: changesupport handlers need to be transferred
3033 if (discoverer == null)
3035 discoverer = Discoverer.getInstance();
3036 // register PCS handler for getDesktop().
3037 discoverer.addPropertyChangeListener(changeSupport);
3039 // JAL-940 - disabled JWS1 service configuration - always start discoverer
3040 // until we phase out completely
3041 (t0 = new Thread(discoverer)).start();
3044 if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
3046 t2 = jalview.ws.jws2.Jws2Discoverer.getInstance()
3047 .startDiscoverer(changeSupport);
3051 // TODO: do rest service discovery
3060 } catch (Exception e)
3063 alive = (t1 != null && t1.isAlive()) || (t2 != null && t2.isAlive())
3064 || (t3 != null && t3.isAlive())
3065 || (t0 != null && t0.isAlive());
3071 * called to check if the service discovery process completed successfully.
3075 protected void JalviewServicesChanged(PropertyChangeEvent evt)
3077 if (evt.getNewValue() == null || evt.getNewValue() instanceof Vector)
3079 final String ermsg = jalview.ws.jws2.Jws2Discoverer.getInstance()
3080 .getErrorMessages();
3083 if (Cache.getDefault("SHOW_WSDISCOVERY_ERRORS", true))
3085 if (serviceChangedDialog == null)
3087 // only run if we aren't already displaying one of these.
3088 addDialogThread(serviceChangedDialog = new Runnable()
3095 * JalviewDialog jd =new JalviewDialog() {
3097 * @Override protected void cancelPressed() { // TODO
3098 * Auto-generated method stub
3100 * }@Override protected void okPressed() { // TODO
3101 * Auto-generated method stub
3103 * }@Override protected void raiseClosed() { // TODO
3104 * Auto-generated method stub
3106 * } }; jd.initDialogFrame(new
3107 * JLabel("<html><table width=\"450\"><tr><td>" + ermsg +
3108 * "<br/>It may be that you have invalid JABA URLs in your web service preferences,"
3109 * + " or mis-configured HTTP proxy settings.<br/>" +
3110 * "Check the <em>Connections</em> and <em>Web services</em> tab of the"
3112 * " Tools->Preferences dialog box to change them.</td></tr></table></html>"
3113 * ), true, true, "Web Service Configuration Problem", 450,
3116 * jd.waitForInput();
3118 JvOptionPane.showConfirmDialog(Desktop.getDesktopPane(),
3119 new JLabel("<html><table width=\"450\"><tr><td>"
3120 + ermsg + "</td></tr></table>"
3121 + "<p>It may be that you have invalid JABA URLs<br/>in your web service preferences,"
3122 + "<br>or as a command-line argument, or mis-configured HTTP proxy settings.</p>"
3123 + "<p>Check the <em>Connections</em> and <em>Web services</em> tab<br/>of the"
3124 + " Tools->Preferences dialog box to change them.</p></html>"),
3125 "Web Service Configuration Problem",
3126 JvOptionPane.DEFAULT_OPTION,
3127 JvOptionPane.ERROR_MESSAGE);
3128 serviceChangedDialog = null;
3137 "Errors reported by JABA discovery service. Check web services preferences.\n"
3144 Runnable serviceChangedDialog = null;
3147 * start a thread to open a URL in the configured browser. Pops up a warning
3148 * dialog to the user if there is an exception when calling out to the browser
3153 public static void showUrl(final String url)
3155 showUrl(url, Desktop.getInstance());
3159 * Like showUrl but allows progress handler to be specified
3163 * (null) or object implementing IProgressIndicator
3165 public static void showUrl(final String url,
3166 final IProgressIndicator progress)
3168 new Thread(new Runnable()
3175 if (progress != null)
3177 progress.setProgressBar(MessageManager
3178 .formatMessage("status.opening_params", new Object[]
3179 { url }), this.hashCode());
3181 BrowserLauncher.openURL(url);
3182 } catch (Exception ex)
3184 JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
3186 .getString("label.web_browser_not_found_unix"),
3187 MessageManager.getString("label.web_browser_not_found"),
3188 JvOptionPane.WARNING_MESSAGE);
3190 ex.printStackTrace();
3192 if (progress != null)
3194 progress.setProgressBar(null, this.hashCode());
3200 private WsParamSetManager wsparamManager = null;
3202 public static ParamManager getUserParameterStore()
3204 Desktop d = Desktop.getInstance();
3205 if (d.wsparamManager == null)
3207 d.wsparamManager = new WsParamSetManager();
3209 return d.wsparamManager;
3213 * static hyperlink handler proxy method for use by Jalview's internal windows
3217 public static void hyperlinkUpdate(HyperlinkEvent e)
3219 if (e.getEventType() == EventType.ACTIVATED)
3224 url = e.getURL().toString();
3225 Desktop.showUrl(url);
3226 } catch (Exception x)
3230 if (Cache.log != null)
3232 Cache.log.error("Couldn't handle string " + url + " as a URL.");
3237 "Couldn't handle string " + url + " as a URL.");
3240 // ignore any exceptions due to dud links.
3247 * single thread that handles display of dialogs to user.
3249 ExecutorService dialogExecutor = Executors.newSingleThreadExecutor();
3252 * flag indicating if dialogExecutor should try to acquire a permit
3254 volatile boolean dialogPause = true;
3259 java.util.concurrent.Semaphore block = new Semaphore(0);
3261 private groovy.ui.Console groovyConsole;
3263 public StructureViewer lastTargetedView;
3266 * add another dialog thread to the queue
3270 public void addDialogThread(final Runnable prompter)
3272 dialogExecutor.submit(new Runnable()
3282 } catch (InterruptedException x)
3286 if (Jalview.isHeadlessMode())
3292 SwingUtilities.invokeAndWait(prompter);
3293 } catch (Exception q)
3295 Cache.log.warn("Unexpected Exception in dialog thread.", q);
3301 public void startDialogQueue()
3303 // set the flag so we don't pause waiting for another permit and semaphore
3304 // the current task to begin
3305 dialogPause = false;
3310 * Outputs an image of the desktop to file in EPS format, after prompting the
3311 * user for choice of Text or Lineart character rendering (unless a preference
3312 * has been set). The file name is generated as
3315 * Jalview_snapshot_nnnnn.eps where nnnnn is the current timestamp in milliseconds
3319 protected void snapShotWindow_actionPerformed(ActionEvent e)
3321 // currently the menu option to do this is not shown
3324 int width = getWidth();
3325 int height = getHeight();
3327 "Jalview_snapshot_" + System.currentTimeMillis() + ".eps");
3328 ImageWriterI writer = new ImageWriterI()
3331 public void exportImage(Graphics g) throws Exception
3334 Cache.log.info("Successfully written snapshot to file "
3335 + of.getAbsolutePath());
3338 String title = "View of desktop";
3339 ImageExporter exporter = new ImageExporter(writer, null, TYPE.EPS,
3341 exporter.doExport(of, this, width, height, title);
3345 * Explode the views in the given SplitFrame into separate SplitFrame windows.
3346 * This respects (remembers) any previous 'exploded geometry' i.e. the size and
3347 * location last time the view was expanded (if any). However it does not
3348 * remember the split pane divider location - this is set to match the
3349 * 'exploding' frame.
3353 public void explodeViews(SplitFrame sf)
3355 AlignFrame oldTopFrame = (AlignFrame) sf.getTopFrame();
3356 AlignFrame oldBottomFrame = (AlignFrame) sf.getBottomFrame();
3357 List<? extends AlignmentViewPanel> topPanels = oldTopFrame
3359 List<? extends AlignmentViewPanel> bottomPanels = oldBottomFrame
3361 int viewCount = topPanels.size();
3368 * Processing in reverse order works, forwards order leaves the first panels
3369 * not visible. I don't know why!
3371 for (int i = viewCount - 1; i >= 0; i--)
3374 * Make new top and bottom frames. These take over the respective
3375 * AlignmentPanel objects, including their AlignmentViewports, so the
3376 * cdna/protein relationships between the viewports is carried over to the
3379 * explodedGeometry holds the (x, y) position of the previously exploded
3380 * SplitFrame, and the (width, height) of the AlignFrame component
3382 AlignmentPanel topPanel = (AlignmentPanel) topPanels.get(i);
3383 AlignFrame newTopFrame = new AlignFrame(topPanel);
3384 newTopFrame.setSize(oldTopFrame.getSize());
3385 newTopFrame.setVisible(true);
3386 Rectangle geometry = ((AlignViewport) topPanel.getAlignViewport())
3387 .getExplodedGeometry();
3388 if (geometry != null)
3390 newTopFrame.setSize(geometry.getSize());
3393 AlignmentPanel bottomPanel = (AlignmentPanel) bottomPanels.get(i);
3394 AlignFrame newBottomFrame = new AlignFrame(bottomPanel);
3395 newBottomFrame.setSize(oldBottomFrame.getSize());
3396 newBottomFrame.setVisible(true);
3397 geometry = ((AlignViewport) bottomPanel.getAlignViewport())
3398 .getExplodedGeometry();
3399 if (geometry != null)
3401 newBottomFrame.setSize(geometry.getSize());
3404 topPanel.av.setGatherViewsHere(false);
3405 bottomPanel.av.setGatherViewsHere(false);
3406 JInternalFrame splitFrame = new SplitFrame(newTopFrame,
3408 if (geometry != null)
3410 splitFrame.setLocation(geometry.getLocation());
3412 Desktop.addInternalFrame(splitFrame, sf.getTitle(), -1, -1);
3416 * Clear references to the panels (now relocated in the new SplitFrames)
3417 * before closing the old SplitFrame.
3420 bottomPanels.clear();
3425 * Gather expanded split frames, sharing the same pairs of sequence set ids,
3426 * back into the given SplitFrame as additional views. Note that the gathered
3427 * frames may themselves have multiple views.
3431 public void gatherViews(GSplitFrame source)
3434 * special handling of explodedGeometry for a view within a SplitFrame: - it
3435 * holds the (x, y) position of the enclosing SplitFrame, and the (width,
3436 * height) of the AlignFrame component
3438 AlignFrame myTopFrame = (AlignFrame) source.getTopFrame();
3439 AlignFrame myBottomFrame = (AlignFrame) source.getBottomFrame();
3440 myTopFrame.viewport.setExplodedGeometry(new Rectangle(source.getX(),
3441 source.getY(), myTopFrame.getWidth(), myTopFrame.getHeight()));
3442 myBottomFrame.viewport
3443 .setExplodedGeometry(new Rectangle(source.getX(), source.getY(),
3444 myBottomFrame.getWidth(), myBottomFrame.getHeight()));
3445 myTopFrame.viewport.setGatherViewsHere(true);
3446 myBottomFrame.viewport.setGatherViewsHere(true);
3447 String topViewId = myTopFrame.viewport.getSequenceSetId();
3448 String bottomViewId = myBottomFrame.viewport.getSequenceSetId();
3450 JInternalFrame[] frames = desktopPane.getAllFrames();
3451 for (JInternalFrame frame : frames)
3453 if (frame instanceof SplitFrame && frame != source)
3455 SplitFrame sf = (SplitFrame) frame;
3456 AlignFrame topFrame = (AlignFrame) sf.getTopFrame();
3457 AlignFrame bottomFrame = (AlignFrame) sf.getBottomFrame();
3458 boolean gatherThis = false;
3459 for (int a = 0; a < topFrame.alignPanels.size(); a++)
3461 AlignmentPanel topPanel = topFrame.alignPanels.get(a);
3462 AlignmentPanel bottomPanel = bottomFrame.alignPanels.get(a);
3463 if (topViewId.equals(topPanel.av.getSequenceSetId())
3464 && bottomViewId.equals(bottomPanel.av.getSequenceSetId()))
3467 topPanel.av.setGatherViewsHere(false);
3468 bottomPanel.av.setGatherViewsHere(false);
3469 topPanel.av.setExplodedGeometry(
3470 new Rectangle(sf.getLocation(), topFrame.getSize()));
3471 bottomPanel.av.setExplodedGeometry(
3472 new Rectangle(sf.getLocation(), bottomFrame.getSize()));
3473 myTopFrame.addAlignmentPanel(topPanel, false);
3474 myBottomFrame.addAlignmentPanel(bottomPanel, false);
3480 topFrame.getAlignPanels().clear();
3481 bottomFrame.getAlignPanels().clear();
3488 * The dust settles...give focus to the tab we did this from.
3490 myTopFrame.setDisplayedView(myTopFrame.alignPanel);
3493 public static groovy.ui.Console getGroovyConsole()
3495 Desktop desktop = Desktop.getInstance();
3496 return desktop == null ? null : desktop.groovyConsole;
3500 * handles the payload of a drag and drop event.
3502 * TODO refactor to desktop utilities class
3505 * - Data source strings extracted from the drop event
3507 * - protocol for each data source extracted from the drop event
3511 * - the payload from the drop event
3514 @SuppressWarnings("unchecked")
3515 public static void transferFromDropTarget(List<Object> files,
3516 List<DataSourceType> protocols, DropTargetDropEvent evt,
3517 Transferable t) throws Exception
3520 // BH 2018 changed List<String> to List<Object> to allow for File from SwingJS
3522 // DataFlavor[] flavors = t.getTransferDataFlavors();
3523 // for (int i = 0; i < flavors.length; i++) {
3524 // if (flavors[i].isFlavorJavaFileListType()) {
3525 // evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
3526 // List<File> list = (List<File>) t.getTransferData(flavors[i]);
3527 // for (int j = 0; j < list.size(); j++) {
3528 // File file = (File) list.get(j);
3529 // byte[] data = getDroppedFileBytes(file);
3530 // fileName.setText(file.getName() + " - " + data.length + " " +
3531 // evt.getLocation());
3532 // JTextArea target = (JTextArea) ((DropTarget) evt.getSource()).getComponent();
3533 // target.setText(new String(data));
3535 // dtde.dropComplete(true);
3540 DataFlavor uriListFlavor = new DataFlavor(
3541 "text/uri-list;class=java.lang.String"), urlFlavour = null;
3544 urlFlavour = new DataFlavor(
3545 "application/x-java-url; class=java.net.URL");
3546 } catch (ClassNotFoundException cfe)
3548 Cache.log.debug("Couldn't instantiate the URL dataflavor.", cfe);
3551 if (urlFlavour != null && t.isDataFlavorSupported(urlFlavour))
3556 java.net.URL url = (URL) t.getTransferData(urlFlavour);
3557 // nb: java 8 osx bug https://bugs.openjdk.java.net/browse/JDK-8156099
3558 // means url may be null.
3561 protocols.add(DataSourceType.URL);
3562 files.add(url.toString());
3563 Cache.log.debug("Drop handled as URL dataflavor "
3564 + files.get(files.size() - 1));
3569 if (Platform.isAMacAndNotJS())
3572 "Please ignore plist error - occurs due to problem with java 8 on OSX");
3575 } catch (Throwable ex)
3577 Cache.log.debug("URL drop handler failed.", ex);
3580 if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
3582 // Works on Windows and MacOSX
3583 Cache.log.debug("Drop handled as javaFileListFlavor");
3584 for (Object file : (List<Object>) t
3585 .getTransferData(DataFlavor.javaFileListFlavor))
3588 protocols.add(DataSourceType.FILE);
3593 // Unix like behaviour
3594 boolean added = false;
3596 if (t.isDataFlavorSupported(uriListFlavor))
3598 Cache.log.debug("Drop handled as uriListFlavor");
3599 // This is used by Unix drag system
3600 data = (String) t.getTransferData(uriListFlavor);
3604 // fallback to text: workaround - on OSX where there's a JVM bug
3605 Cache.log.debug("standard URIListFlavor failed. Trying text");
3606 // try text fallback
3607 DataFlavor textDf = new DataFlavor(
3608 "text/plain;class=java.lang.String");
3609 if (t.isDataFlavorSupported(textDf))
3611 data = (String) t.getTransferData(textDf);
3614 Cache.log.debug("Plain text drop content returned "
3615 + (data == null ? "Null - failed" : data));
3620 while (protocols.size() < files.size())
3622 Cache.log.debug("Adding missing FILE protocol for "
3623 + files.get(protocols.size()));
3624 protocols.add(DataSourceType.FILE);
3626 for (java.util.StringTokenizer st = new java.util.StringTokenizer(
3627 data, "\r\n"); st.hasMoreTokens();)
3630 String s = st.nextToken();
3631 if (s.startsWith("#"))
3633 // the line is a comment (as per the RFC 2483)
3636 java.net.URI uri = new java.net.URI(s);
3637 if (uri.getScheme().toLowerCase().startsWith("http"))
3639 protocols.add(DataSourceType.URL);
3640 files.add(uri.toString());
3644 // otherwise preserve old behaviour: catch all for file objects
3645 java.io.File file = new java.io.File(uri);
3646 protocols.add(DataSourceType.FILE);
3647 files.add(file.toString());
3652 if (Cache.log.isDebugEnabled())
3654 if (data == null || !added)
3657 if (t.getTransferDataFlavors() != null
3658 && t.getTransferDataFlavors().length > 0)
3661 "Couldn't resolve drop data. Here are the supported flavors:");
3662 for (DataFlavor fl : t.getTransferDataFlavors())
3665 "Supported transfer dataflavor: " + fl.toString());
3666 Object df = t.getTransferData(fl);
3669 Cache.log.debug("Retrieves: " + df);
3673 Cache.log.debug("Retrieved nothing");
3679 Cache.log.debug("Couldn't resolve dataflavor for drop: "
3685 if (Platform.isWindowsAndNotJS())
3687 Cache.log.debug("Scanning dropped content for Windows Link Files");
3689 // resolve any .lnk files in the file drop
3690 for (int f = 0; f < files.size(); f++)
3692 String source = files.get(f).toString().toLowerCase();
3693 if (protocols.get(f).equals(DataSourceType.FILE)
3694 && (source.endsWith(".lnk") || source.endsWith(".url")
3695 || source.endsWith(".site")))
3699 Object obj = files.get(f);
3700 File lf = (obj instanceof File ? (File) obj
3701 : new File((String) obj));
3702 // process link file to get a URL
3703 Cache.log.debug("Found potential link file: " + lf);
3704 WindowsShortcut wscfile = new WindowsShortcut(lf);
3705 String fullname = wscfile.getRealFilename();
3706 protocols.set(f, FormatAdapter.checkProtocol(fullname));
3707 files.set(f, fullname);
3708 Cache.log.debug("Parsed real filename " + fullname
3709 + " to extract protocol: " + protocols.get(f));
3710 } catch (Exception ex)
3713 "Couldn't parse " + files.get(f) + " as a link file.",
3722 * Sets the Preferences property for experimental features to True or False
3723 * depending on the state of the controlling menu item
3726 protected void showExperimental_actionPerformed(boolean selected)
3728 Cache.setProperty(EXPERIMENTAL_FEATURES, Boolean.toString(selected));
3732 * Answers a (possibly empty) list of any structure viewer frames (currently for
3733 * either Jmol or Chimera) which are currently open. This may optionally be
3734 * restricted to viewers of a specified class, or viewers linked to a specified
3738 * if not null, only return viewers linked to this panel
3739 * @param structureViewerClass
3740 * if not null, only return viewers of this class
3743 public List<StructureViewerBase> getStructureViewers(
3744 AlignmentPanel apanel,
3745 Class<? extends StructureViewerBase> structureViewerClass)
3747 List<StructureViewerBase> result = new ArrayList<>();
3748 JInternalFrame[] frames = getAllFrames();
3750 for (JInternalFrame frame : frames)
3752 if (frame instanceof StructureViewerBase)
3754 if (structureViewerClass == null
3755 || structureViewerClass.isInstance(frame))
3758 || ((StructureViewerBase) frame).isLinkedWith(apanel))
3760 result.add((StructureViewerBase) frame);