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();
474 if (Jalview.isInteractive())
476 // disabled for SeqCanvasTest
479 // Spawn a thread that shows the splashscreen
481 SwingUtilities.invokeLater(new Runnable()
490 // Thread off a new instance of the file chooser - this reduces the
493 // takes to open it later on.
494 new Thread(new Runnable()
499 Cache.log.debug("Filechooser init thread started.");
500 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
501 JalviewFileChooser.forRead(
502 Cache.getProperty("LAST_DIRECTORY"), fileFormat);
503 Cache.log.debug("Filechooser init thread finished.");
506 // Add the service change listener
507 changeSupport.addJalviewPropertyChangeListener("services",
508 new PropertyChangeListener()
512 public void propertyChange(PropertyChangeEvent evt)
514 Cache.log.debug("Firing service changed event for "
515 + evt.getNewValue());
516 JalviewServicesChanged(evt);
523 this.setDropTarget(new java.awt.dnd.DropTarget(desktopPane, this));
525 this.addWindowListener(new WindowAdapter()
528 public void windowClosing(WindowEvent evt)
535 this.addMouseListener(ma = new MouseAdapter()
538 public void mousePressed(MouseEvent evt)
540 if (evt.isPopupTrigger()) // Mac
542 showPasteMenu(evt.getX(), evt.getY());
547 public void mouseReleased(MouseEvent evt)
549 if (evt.isPopupTrigger()) // Windows
551 showPasteMenu(evt.getX(), evt.getY());
555 desktopPane.addMouseListener(ma);
557 } catch (Throwable t)
565 * Answers true if user preferences to enable experimental features is True
570 public boolean showExperimental()
572 String experimental = Cache.getDefault(EXPERIMENTAL_FEATURES,
573 Boolean.FALSE.toString());
574 return Boolean.valueOf(experimental).booleanValue();
577 public void doConfigureStructurePrefs()
579 // configure services
580 StructureSelectionManager ssm = StructureSelectionManager
581 .getStructureSelectionManager(this);
582 if (jalview.bin.Cache.getDefault(Preferences.ADD_SS_ANN, true))
584 ssm.setAddTempFacAnnot(jalview.bin.Cache
585 .getDefault(Preferences.ADD_TEMPFACT_ANN, true));
586 ssm.setProcessSecondaryStructure(jalview.bin.Cache
587 .getDefault(Preferences.STRUCT_FROM_PDB, true));
588 ssm.setSecStructServices(
589 jalview.bin.Cache.getDefault(Preferences.USE_RNAVIEW, true));
593 ssm.setAddTempFacAnnot(false);
594 ssm.setProcessSecondaryStructure(false);
595 ssm.setSecStructServices(false);
599 public void checkForNews()
601 final Desktop me = this;
602 // Thread off the news reader, in case there are connection problems.
603 new Thread(new Runnable()
608 Cache.log.debug("Starting news thread.");
609 jvnews = new BlogReader(me);
610 showNews.setVisible(true);
611 Cache.log.debug("Completed news thread.");
616 public void getIdentifiersOrgData()
618 // Thread off the identifiers fetcher
619 new Thread(new Runnable()
624 Cache.log.debug("Downloading data from identifiers.org");
625 // UrlDownloadClient client = new UrlDownloadClient();
628 UrlDownloadClient.download(IdOrgSettings.getUrl(),
629 IdOrgSettings.getDownloadLocation());
630 } catch (IOException e)
632 Cache.log.debug("Exception downloading identifiers.org data"
641 protected void showNews_actionPerformed(ActionEvent e)
643 showNews(showNews.isSelected());
646 protected void showNews(boolean visible)
648 Cache.log.debug((visible ? "Showing" : "Hiding") + " news.");
649 showNews.setSelected(visible);
650 if (visible && !jvnews.isVisible())
652 new Thread(new Runnable()
657 long now = System.currentTimeMillis();
659 MessageManager.getString("status.refreshing_news"), now);
660 jvnews.refreshNews();
661 setProgressBar(null, now);
669 * recover the last known dimensions for a jalview window
672 * - empty string is desktop, all other windows have unique prefix
673 * @return null or last known dimensions scaled to current geometry (if last
674 * window geom was known)
676 Rectangle getLastKnownDimensions(String windowName)
678 // TODO: lock aspect ratio for scaling desktop Bug #0058199
679 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
680 String x = jalview.bin.Cache.getProperty(windowName + "SCREEN_X");
681 String y = jalview.bin.Cache.getProperty(windowName + "SCREEN_Y");
682 String width = jalview.bin.Cache
683 .getProperty(windowName + "SCREEN_WIDTH");
684 String height = jalview.bin.Cache
685 .getProperty(windowName + "SCREEN_HEIGHT");
686 if ((x != null) && (y != null) && (width != null) && (height != null))
688 int ix = Integer.parseInt(x), iy = Integer.parseInt(y),
689 iw = Integer.parseInt(width), ih = Integer.parseInt(height);
690 if (jalview.bin.Cache.getProperty("SCREENGEOMETRY_WIDTH") != null)
692 // attempt #1 - try to cope with change in screen geometry - this
693 // version doesn't preserve original jv aspect ratio.
694 // take ratio of current screen size vs original screen size.
695 double sw = ((1f * screenSize.width) / (1f * Integer.parseInt(
696 jalview.bin.Cache.getProperty("SCREENGEOMETRY_WIDTH"))));
697 double sh = ((1f * screenSize.height) / (1f * Integer.parseInt(
698 jalview.bin.Cache.getProperty("SCREENGEOMETRY_HEIGHT"))));
699 // rescale the bounds depending upon the current screen geometry.
700 ix = (int) (ix * sw);
701 iw = (int) (iw * sw);
702 iy = (int) (iy * sh);
703 ih = (int) (ih * sh);
704 while (ix >= screenSize.width)
706 jalview.bin.Cache.log.debug(
707 "Window geometry location recall error: shifting horizontal to within screenbounds.");
708 ix -= screenSize.width;
710 while (iy >= screenSize.height)
712 jalview.bin.Cache.log.debug(
713 "Window geometry location recall error: shifting vertical to within screenbounds.");
714 iy -= screenSize.height;
716 jalview.bin.Cache.log.debug(
717 "Got last known dimensions for " + windowName + ": x:" + ix
718 + " y:" + iy + " width:" + iw + " height:" + ih);
720 // return dimensions for new instance
721 return new Rectangle(ix, iy, iw, ih);
726 private void doVamsasClientCheck()
728 if (Cache.vamsasJarsPresent())
730 setupVamsasDisconnectedGui();
731 VamsasMenu.setVisible(true);
732 final Desktop us = this;
733 VamsasMenu.addMenuListener(new MenuListener()
735 // this listener remembers when the menu was first selected, and
736 // doesn't rebuild the session list until it has been cleared and
738 boolean refresh = true;
741 public void menuCanceled(MenuEvent e)
747 public void menuDeselected(MenuEvent e)
753 public void menuSelected(MenuEvent e)
757 us.buildVamsasStMenu();
762 vamsasStart.setVisible(true);
766 protected void showPasteMenu(int x, int y)
768 JPopupMenu popup = new JPopupMenu();
769 JMenuItem item = new JMenuItem(
770 MessageManager.getString("label.paste_new_window"));
771 item.addActionListener(new ActionListener()
774 public void actionPerformed(ActionEvent evt)
781 popup.show(this, x, y);
788 Clipboard c = Toolkit.getDefaultToolkit().getSystemClipboard();
789 Transferable contents = c.getContents(this);
791 if (contents != null)
793 String file = (String) contents
794 .getTransferData(DataFlavor.stringFlavor);
796 FileFormatI format = new IdentifyFile().identify(file,
797 DataSourceType.PASTE);
799 new FileLoader().loadFile(file, DataSourceType.PASTE, format);
802 } catch (Exception ex)
805 "Unable to paste alignment from system clipboard:\n" + ex);
810 * Adds and opens the given frame to the desktop
821 public static synchronized void addInternalFrame(
822 final JInternalFrame frame, String title, int w, int h)
824 addInternalFrame(frame, title, true, w, h, true, false);
828 * Add an internal frame to the Jalview desktop
835 * When true, display frame immediately, otherwise, caller must call
836 * setVisible themselves.
842 public static synchronized void addInternalFrame(
843 final JInternalFrame frame, String title, boolean makeVisible,
846 addInternalFrame(frame, title, makeVisible, w, h, true, false);
850 * Add an internal frame to the Jalview desktop and make it visible
863 public static synchronized void addInternalFrame(
864 final JInternalFrame frame, String title, int w, int h,
867 addInternalFrame(frame, title, true, w, h, resizable, false);
871 * Add an internal frame to the Jalview desktop
878 * When true, display frame immediately, otherwise, caller must call
879 * setVisible themselves.
886 * @param ignoreMinSize
887 * Do not set the default minimum size for frame
889 public static synchronized void addInternalFrame(
890 final JInternalFrame frame, String title, boolean makeVisible,
891 int w, int h, boolean resizable, boolean ignoreMinSize)
895 // TODO: allow callers to determine X and Y position of frame (eg. via
897 // TODO: consider fixing method to update entries in the window submenu with
898 // the current window title
900 frame.setTitle(title);
901 if (w > 0 && (frame.getWidth() < 1 || frame.getHeight() < 1))
905 // THIS IS A PUBLIC STATIC METHOD, SO IT MAY BE CALLED EVEN IN
906 // A HEADLESS STATE WHEN NO DESKTOP EXISTS. MUST RETURN
907 // IF JALVIEW IS RUNNING HEADLESS
908 // ///////////////////////////////////////////////
909 if (Jalview.isHeadlessMode())
918 frame.setMinimumSize(
919 new Dimension(DEFAULT_MIN_WIDTH, DEFAULT_MIN_HEIGHT));
921 // Set default dimension for Alignment Frame window.
922 // The Alignment Frame window could be added from a number of places,
924 // I did this here in order not to miss out on any Alignment frame.
925 if (frame instanceof AlignFrame)
927 frame.setMinimumSize(new Dimension(ALIGN_FRAME_DEFAULT_MIN_WIDTH,
928 ALIGN_FRAME_DEFAULT_MIN_HEIGHT));
932 frame.setVisible(makeVisible);
933 frame.setClosable(true);
934 frame.setResizable(resizable);
935 frame.setMaximizable(resizable);
936 frame.setIconifiable(resizable);
937 frame.setOpaque(Platform.isJS());
939 boolean isEmbedded = (Platform.getDimIfEmbedded(frame, -1, -1) != null);
940 if (!isEmbedded && frame.getX() < 1 && frame.getY() < 1)
942 frame.setLocation(xOffset * openFrameCount,
943 yOffset * ((openFrameCount - 1) % 10) + yOffset);
947 * add an entry for the new frame in the Window menu
948 * (and remove it when the frame is closed)
950 JMenuItem menuItem = new JMenuItem(title);
951 frame.addInternalFrameListener(new InternalFrameAdapter()
954 public void internalFrameActivated(InternalFrameEvent evt)
956 JInternalFrame itf = getDesktopPane().getSelectedFrame();
959 if (itf instanceof AlignFrame)
961 Jalview.setCurrentAlignFrame((AlignFrame) itf);
968 public void internalFrameClosed(InternalFrameEvent evt)
970 PaintRefresher.RemoveComponent(frame);
973 * defensive check to prevent frames being
974 * added half off the window
976 if (openFrameCount > 0)
982 * ensure no reference to alignFrame retained by menu item listener
984 if (menuItem.getActionListeners().length > 0)
986 menuItem.removeActionListener(menuItem.getActionListeners()[0]);
988 Desktop.getInstance().windowMenu.remove(menuItem);
992 menuItem.addActionListener(new ActionListener()
995 public void actionPerformed(ActionEvent e)
999 frame.setSelected(true);
1000 frame.setIcon(false);
1001 } catch (java.beans.PropertyVetoException ex)
1003 // System.err.println(ex.toString());
1008 setKeyBindings(frame);
1010 getDesktopPane().add(frame);
1012 Desktop.getInstance().windowMenu.add(menuItem);
1017 frame.setSelected(true);
1018 frame.requestFocus();
1019 } catch (java.beans.PropertyVetoException ve)
1021 } catch (java.lang.ClassCastException cex)
1024 "Squashed a possible GUI implementation error. If you can recreate this, please look at http://issues.jalview.org/browse/JAL-869",
1030 * Add key bindings to a JInternalFrame so that Ctrl-W and Cmd-W will close the
1035 private static void setKeyBindings(JInternalFrame frame)
1037 final Action closeAction = new AbstractAction()
1040 public void actionPerformed(ActionEvent e)
1047 * set up key bindings for Ctrl-W and Cmd-W, with the same (Close) action
1049 KeyStroke ctrlWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
1050 InputEvent.CTRL_DOWN_MASK);
1051 KeyStroke cmdWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
1052 Toolkit.getDefaultToolkit().getMenuShortcutKeyMask());
1054 InputMap inputMap = frame
1055 .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
1056 String ctrlW = ctrlWKey.toString();
1057 inputMap.put(ctrlWKey, ctrlW);
1058 inputMap.put(cmdWKey, ctrlW);
1060 ActionMap actionMap = frame.getActionMap();
1061 actionMap.put(ctrlW, closeAction);
1065 public void lostOwnership(Clipboard clipboard, Transferable contents)
1069 Desktop.getInstance().jalviewClipboard = null;
1072 internalCopy = false;
1076 public void dragEnter(DropTargetDragEvent evt)
1081 public void dragExit(DropTargetEvent evt)
1086 public void dragOver(DropTargetDragEvent evt)
1091 public void dropActionChanged(DropTargetDragEvent evt)
1102 public void drop(DropTargetDropEvent evt)
1104 boolean success = true;
1105 // JAL-1552 - acceptDrop required before getTransferable call for
1106 // Java's Transferable for native dnd
1107 evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
1108 Transferable t = evt.getTransferable();
1109 List<Object> files = new ArrayList<>();
1110 List<DataSourceType> protocols = new ArrayList<>();
1114 Desktop.transferFromDropTarget(files, protocols, evt, t);
1115 } catch (Exception e)
1117 e.printStackTrace();
1125 for (int i = 0; i < files.size(); i++)
1127 // BH 2018 File or String
1128 Object file = files.get(i);
1129 String fileName = file.toString();
1130 DataSourceType protocol = (protocols == null)
1131 ? DataSourceType.FILE
1133 FileFormatI format = null;
1135 if (fileName.endsWith(".jar"))
1137 format = FileFormat.Jalview;
1142 format = new IdentifyFile().identify(file, protocol);
1144 if (file instanceof File)
1146 Platform.cacheFileData((File) file);
1148 new FileLoader().loadFile(null, file, protocol, format);
1151 } catch (Exception ex)
1156 evt.dropComplete(success); // need this to ensure input focus is properly
1157 // transfered to any new windows created
1167 public void inputLocalFileMenuItem_actionPerformed(AlignViewport viewport)
1169 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
1170 JalviewFileChooser chooser = JalviewFileChooser
1171 .forRead(Cache.getProperty("LAST_DIRECTORY"), fileFormat, true);
1173 chooser.setFileView(new JalviewFileView());
1174 chooser.setDialogTitle(
1175 MessageManager.getString("label.open_local_file"));
1176 chooser.setToolTipText(MessageManager.getString("action.open"));
1178 chooser.setResponseHandler(0, new Runnable()
1183 File selectedFile = chooser.getSelectedFile();
1184 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1186 FileFormatI format = chooser.getSelectedFormat();
1189 * Call IdentifyFile to verify the file contains what its extension implies.
1190 * Skip this step for dynamically added file formats, because
1191 * IdentifyFile does not know how to recognise them.
1193 if (FileFormats.getInstance().isIdentifiable(format))
1197 format = new IdentifyFile().identify(selectedFile,
1198 DataSourceType.FILE);
1199 } catch (FileFormatException e)
1201 // format = null; //??
1205 new FileLoader().loadFile(viewport, selectedFile,
1206 DataSourceType.FILE, format);
1209 chooser.showOpenDialog(this);
1213 * Shows a dialog for input of a URL at which to retrieve alignment data
1218 public void inputURLMenuItem_actionPerformed(AlignViewport viewport)
1220 // This construct allows us to have a wider textfield
1222 JLabel label = new JLabel(
1223 MessageManager.getString("label.input_file_url"));
1225 JPanel panel = new JPanel(new GridLayout(2, 1));
1229 * the URL to fetch is
1230 * Java: an editable combobox with history
1231 * JS: (pending JAL-3038) a plain text field
1234 String urlBase = "http://www.";
1235 if (Platform.isJS())
1237 history = new JTextField(urlBase, 35);
1246 JComboBox<String> asCombo = new JComboBox<>();
1247 asCombo.setPreferredSize(new Dimension(400, 20));
1248 asCombo.setEditable(true);
1249 asCombo.addItem(urlBase);
1250 String historyItems = Cache.getProperty("RECENT_URL");
1251 if (historyItems != null)
1253 for (String token : historyItems.split("\\t"))
1255 asCombo.addItem(token);
1262 Object[] options = new Object[] { MessageManager.getString("action.ok"),
1263 MessageManager.getString("action.cancel") };
1264 Runnable action = new Runnable()
1269 @SuppressWarnings("unchecked")
1270 String url = (history instanceof JTextField
1271 ? ((JTextField) history).getText()
1272 : ((JComboBox<String>) history).getSelectedItem()
1275 if (url.toLowerCase().endsWith(".jar"))
1277 if (viewport != null)
1279 new FileLoader().loadFile(viewport, url, DataSourceType.URL,
1280 FileFormat.Jalview);
1284 new FileLoader().loadFile(url, DataSourceType.URL,
1285 FileFormat.Jalview);
1290 FileFormatI format = null;
1293 format = new IdentifyFile().identify(url, DataSourceType.URL);
1294 } catch (FileFormatException e)
1296 // TODO revise error handling, distinguish between
1297 // URL not found and response not valid
1302 String msg = MessageManager
1303 .formatMessage("label.couldnt_locate", url);
1304 JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
1306 MessageManager.getString("label.url_not_found"),
1307 JvOptionPane.WARNING_MESSAGE);
1312 if (viewport != null)
1314 new FileLoader().loadFile(viewport, url, DataSourceType.URL,
1319 new FileLoader().loadFile(url, DataSourceType.URL, format);
1324 String dialogOption = MessageManager
1325 .getString("label.input_alignment_from_url");
1326 JvOptionPane.newOptionDialog(getDesktopPane())
1327 .setResponseHandler(0, action)
1328 .showInternalDialog(panel, dialogOption,
1329 JvOptionPane.YES_NO_CANCEL_OPTION,
1330 JvOptionPane.PLAIN_MESSAGE, null, options,
1331 MessageManager.getString("action.ok"));
1335 * Opens the CutAndPaste window for the user to paste an alignment in to
1338 * - if not null, the pasted alignment is added to the current
1339 * alignment; if null, to a new alignment window
1342 public void inputTextboxMenuItem_actionPerformed(
1343 AlignmentViewPanel viewPanel)
1345 CutAndPasteTransfer cap = new CutAndPasteTransfer();
1346 cap.setForInput(viewPanel);
1347 Desktop.addInternalFrame(cap,
1348 MessageManager.getString("label.cut_paste_alignmen_file"), true,
1358 Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
1359 jalview.bin.Cache.setProperty("SCREENGEOMETRY_WIDTH",
1361 jalview.bin.Cache.setProperty("SCREENGEOMETRY_HEIGHT",
1362 screen.height + "");
1363 storeLastKnownDimensions("", new Rectangle(getBounds().x, getBounds().y,
1364 getWidth(), getHeight()));
1366 if (jconsole != null)
1368 storeLastKnownDimensions("JAVA_CONSOLE_", jconsole.getBounds());
1369 jconsole.stopConsole();
1373 storeLastKnownDimensions("JALVIEW_RSS_WINDOW_", jvnews.getBounds());
1376 if (dialogExecutor != null)
1378 dialogExecutor.shutdownNow();
1380 closeAll_actionPerformed(null);
1382 if (groovyConsole != null)
1384 // suppress a possible repeat prompt to save script
1385 groovyConsole.setDirty(false);
1386 groovyConsole.exit();
1391 private void storeLastKnownDimensions(String string, Rectangle jc)
1393 jalview.bin.Cache.log.debug("Storing last known dimensions for "
1394 + string + ": x:" + jc.x + " y:" + jc.y + " width:" + jc.width
1395 + " height:" + jc.height);
1397 jalview.bin.Cache.setProperty(string + "SCREEN_X", jc.x + "");
1398 jalview.bin.Cache.setProperty(string + "SCREEN_Y", jc.y + "");
1399 jalview.bin.Cache.setProperty(string + "SCREEN_WIDTH", jc.width + "");
1400 jalview.bin.Cache.setProperty(string + "SCREEN_HEIGHT", jc.height + "");
1410 public void aboutMenuItem_actionPerformed(ActionEvent e)
1412 // StringBuffer message = getAboutMessage(false);
1413 // JvOptionPane.showInternalMessageDialog(Desktop.getDesktop(),
1415 // message.toString(), "About Jalview", JvOptionPane.INFORMATION_MESSAGE);
1416 new Thread(new Runnable()
1421 new SplashScreen(true);
1426 public StringBuffer getAboutMessage(boolean shortv)
1428 StringBuffer message = new StringBuffer();
1429 message.append("<html>");
1432 message.append("<h1><strong>Version: "
1433 + jalview.bin.Cache.getProperty("VERSION")
1434 + "</strong></h1>");
1435 message.append("<strong>Last Updated: <em>"
1436 + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown")
1437 + "</em></strong>");
1443 message.append("<strong>Version "
1444 + jalview.bin.Cache.getProperty("VERSION")
1445 + "; last updated: "
1446 + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown"));
1449 if (jalview.bin.Cache.getDefault("LATEST_VERSION", "Checking")
1450 .equals("Checking"))
1452 message.append("<br>...Checking latest version...</br>");
1454 else if (!jalview.bin.Cache.getDefault("LATEST_VERSION", "Checking")
1455 .equals(jalview.bin.Cache.getProperty("VERSION")))
1457 boolean red = false;
1458 if (jalview.bin.Cache.getProperty("VERSION").toLowerCase()
1459 .indexOf("automated build") == -1)
1462 // Displayed when code version and jnlp version do not match and code
1463 // version is not a development build
1464 message.append("<div style=\"color: #FF0000;font-style: bold;\">");
1467 message.append("<br>!! Version "
1468 + jalview.bin.Cache.getDefault("LATEST_VERSION",
1470 + " is available for download from "
1471 + jalview.bin.Cache.getDefault("www.jalview.org",
1472 "http://www.jalview.org")
1476 message.append("</div>");
1479 message.append("<br>Authors: " + jalview.bin.Cache.getDefault(
1481 "The Jalview Authors (See AUTHORS file for current list)")
1482 + "<br><br>Development managed by The Barton Group, University of Dundee, Scotland, UK.<br>"
1483 + "<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"
1484 + "<br><br>If you use Jalview, please cite:"
1485 + "<br>Waterhouse, A.M., Procter, J.B., Martin, D.M.A, Clamp, M. and Barton, G. J. (2009)"
1486 + "<br>Jalview Version 2 - a multiple sequence alignment editor and analysis workbench"
1487 + "<br>Bioinformatics doi: 10.1093/bioinformatics/btp033"
1493 * Action on requesting Help documentation
1496 public void documentationMenuItem_actionPerformed()
1500 if (Platform.isJS())
1502 BrowserLauncher.openURL("http://www.jalview.org/help.html");
1511 Help.showHelpWindow();
1513 } catch (Exception ex)
1515 System.err.println("Error opening help: " + ex.getMessage());
1520 public void closeAll_actionPerformed(ActionEvent e)
1522 if (desktopPane == null)
1526 // TODO show a progress bar while closing?
1527 JInternalFrame[] frames = desktopPane.getAllFrames();
1528 for (int i = 0; i < frames.length; i++)
1532 frames[i].setClosed(true);
1533 } catch (java.beans.PropertyVetoException ex)
1537 Jalview.setCurrentAlignFrame(null);
1538 System.out.println("ALL CLOSED");
1539 if (v_client != null)
1541 // TODO clear binding to vamsas document objects on close_all
1545 * reset state of singleton objects as appropriate (clear down session state
1546 * when all windows are closed)
1548 getStructureSelectionManager().resetAll();
1552 public void raiseRelated_actionPerformed(ActionEvent e)
1554 reorderAssociatedWindows(false, false);
1558 public void minimizeAssociated_actionPerformed(ActionEvent e)
1560 reorderAssociatedWindows(true, false);
1563 void closeAssociatedWindows()
1565 reorderAssociatedWindows(false, true);
1571 * @seejalview.jbgui.GDesktop#garbageCollect_actionPerformed(java.awt.event.
1575 protected void garbageCollect_actionPerformed(ActionEvent e)
1577 // We simply collect the garbage
1578 jalview.bin.Cache.log.debug("Collecting garbage...");
1580 jalview.bin.Cache.log.debug("Finished garbage collection.");
1587 * jalview.jbgui.GDesktop#showMemusage_actionPerformed(java.awt.event.ActionEvent
1591 protected void showMemusage_actionPerformed(ActionEvent e)
1593 getDesktopPane().showMemoryUsage(showMemusage.isSelected());
1600 * jalview.jbgui.GDesktop#showConsole_actionPerformed(java.awt.event.ActionEvent
1604 protected void showConsole_actionPerformed(ActionEvent e)
1606 showConsole(showConsole.isSelected());
1609 Console jconsole = null;
1612 * control whether the java console is visible or not
1616 void showConsole(boolean selected)
1618 // TODO: decide if we should update properties file
1619 if (jconsole != null) // BH 2018
1621 showConsole.setSelected(selected);
1622 Cache.setProperty("SHOW_JAVA_CONSOLE",
1623 Boolean.valueOf(selected).toString());
1624 jconsole.setVisible(selected);
1628 void reorderAssociatedWindows(boolean minimize, boolean close)
1630 JInternalFrame[] frames = getDesktopPane().getAllFrames();
1631 if (frames == null || frames.length < 1)
1636 AlignmentViewport source = null, target = null;
1637 if (frames[0] instanceof AlignFrame)
1639 source = ((AlignFrame) frames[0]).getCurrentView();
1641 else if (frames[0] instanceof TreePanel)
1643 source = ((TreePanel) frames[0]).getViewPort();
1645 else if (frames[0] instanceof PCAPanel)
1647 source = ((PCAPanel) frames[0]).av;
1649 else if (frames[0].getContentPane() instanceof PairwiseAlignPanel)
1651 source = ((PairwiseAlignPanel) frames[0].getContentPane()).av;
1656 for (int i = 0; i < frames.length; i++)
1659 if (frames[i] == null)
1663 if (frames[i] instanceof AlignFrame)
1665 target = ((AlignFrame) frames[i]).getCurrentView();
1667 else if (frames[i] instanceof TreePanel)
1669 target = ((TreePanel) frames[i]).getViewPort();
1671 else if (frames[i] instanceof PCAPanel)
1673 target = ((PCAPanel) frames[i]).av;
1675 else if (frames[i].getContentPane() instanceof PairwiseAlignPanel)
1677 target = ((PairwiseAlignPanel) frames[i].getContentPane()).av;
1680 if (source == target)
1686 frames[i].setClosed(true);
1690 frames[i].setIcon(minimize);
1693 frames[i].toFront();
1697 } catch (java.beans.PropertyVetoException ex)
1712 protected void preferences_actionPerformed(ActionEvent e)
1718 * Prompts the user to choose a file and then saves the Jalview state as a
1719 * Jalview project file
1722 public void saveState_actionPerformed()
1724 saveState_actionPerformed(false);
1727 public void saveState_actionPerformed(boolean saveAs)
1729 java.io.File projectFile = getProjectFile();
1730 // autoSave indicates we already have a file and don't need to ask
1731 boolean autoSave = projectFile != null && !saveAs
1732 && BackupFiles.getEnabled();
1734 // System.out.println("autoSave="+autoSave+", projectFile='"+projectFile+"',
1735 // saveAs="+saveAs+", Backups
1736 // "+(BackupFiles.getEnabled()?"enabled":"disabled"));
1738 boolean approveSave = false;
1741 JalviewFileChooser chooser = new JalviewFileChooser("jvp",
1744 chooser.setFileView(new JalviewFileView());
1745 chooser.setDialogTitle(MessageManager.getString("label.save_state"));
1747 int value = chooser.showSaveDialog(this);
1749 if (value == JalviewFileChooser.APPROVE_OPTION)
1751 projectFile = chooser.getSelectedFile();
1752 setProjectFile(projectFile);
1757 if (approveSave || autoSave)
1759 final Desktop me = this;
1760 final java.io.File chosenFile = projectFile;
1761 new Thread(new Runnable()
1766 // TODO: refactor to Jalview desktop session controller action.
1767 setProgressBar(MessageManager.formatMessage(
1768 "label.saving_jalview_project", new Object[]
1769 { chosenFile.getName() }), chosenFile.hashCode());
1770 jalview.bin.Cache.setProperty("LAST_DIRECTORY",
1771 chosenFile.getParent());
1772 // TODO catch and handle errors for savestate
1773 // TODO prevent user from messing with the Desktop whilst we're saving
1776 boolean doBackup = BackupFiles.getEnabled();
1777 BackupFiles backupfiles = doBackup ? new BackupFiles(chosenFile) : null;
1779 new Jalview2XML().saveState(doBackup ? backupfiles.getTempFile() : chosenFile);
1783 backupfiles.setWriteSuccess(true);
1784 backupfiles.rollBackupsAndRenameTempFile();
1786 } catch (OutOfMemoryError oom)
1788 new OOMWarning("Whilst saving current state to "
1789 + chosenFile.getName(), oom);
1790 } catch (Exception ex)
1792 Cache.log.error("Problems whilst trying to save to "
1793 + chosenFile.getName(), ex);
1794 JvOptionPane.showMessageDialog(me,
1795 MessageManager.formatMessage(
1796 "label.error_whilst_saving_current_state_to",
1798 { chosenFile.getName() }),
1799 MessageManager.getString("label.couldnt_save_project"),
1800 JvOptionPane.WARNING_MESSAGE);
1802 setProgressBar(null, chosenFile.hashCode());
1809 public void saveAsState_actionPerformed(ActionEvent e)
1811 saveState_actionPerformed(true);
1814 protected void setProjectFile(File choice)
1816 this.projectFile = choice;
1819 public File getProjectFile()
1821 return this.projectFile;
1825 * Shows a file chooser dialog and tries to read in the selected file as a
1829 public void loadState_actionPerformed()
1831 final String[] suffix = new String[] { "jvp", "jar" };
1832 final String[] desc = new String[] { "Jalview Project",
1833 "Jalview Project (old)" };
1834 JalviewFileChooser chooser = new JalviewFileChooser(
1835 Cache.getProperty("LAST_DIRECTORY"), suffix, desc,
1836 "Jalview Project", true, true); // last two booleans: allFiles,
1838 chooser.setFileView(new JalviewFileView());
1839 chooser.setDialogTitle(MessageManager.getString("label.restore_state"));
1840 chooser.setResponseHandler(0, new Runnable()
1845 File selectedFile = chooser.getSelectedFile();
1846 setProjectFile(selectedFile);
1847 String choice = selectedFile.getAbsolutePath();
1848 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1849 new Thread(new Runnable()
1856 new Jalview2XML().loadJalviewAlign(selectedFile);
1857 } catch (OutOfMemoryError oom)
1859 new OOMWarning("Whilst loading project from " + choice, oom);
1860 } catch (Exception ex)
1863 "Problems whilst loading project from " + choice, ex);
1864 JvOptionPane.showMessageDialog(Desktop.getDesktopPane(),
1865 MessageManager.formatMessage(
1866 "label.error_whilst_loading_project_from",
1869 MessageManager.getString("label.couldnt_load_project"),
1870 JvOptionPane.WARNING_MESSAGE);
1877 chooser.showOpenDialog(this);
1881 public void inputSequence_actionPerformed(ActionEvent e)
1883 new SequenceFetcher(this);
1886 JPanel progressPanel;
1888 ArrayList<JPanel> fileLoadingPanels = new ArrayList<>();
1890 public void startLoading(final Object fileName)
1892 if (fileLoadingCount == 0)
1894 fileLoadingPanels.add(addProgressPanel(MessageManager
1895 .formatMessage("label.loading_file", new Object[]
1901 private JPanel addProgressPanel(String string)
1903 if (progressPanel == null)
1905 progressPanel = new JPanel(new GridLayout(1, 1));
1906 totalProgressCount = 0;
1907 getContentPane().add(progressPanel, BorderLayout.SOUTH);
1909 JPanel thisprogress = new JPanel(new BorderLayout(10, 5));
1910 JProgressBar progressBar = new JProgressBar();
1911 progressBar.setIndeterminate(true);
1913 thisprogress.add(new JLabel(string), BorderLayout.WEST);
1915 thisprogress.add(progressBar, BorderLayout.CENTER);
1916 progressPanel.add(thisprogress);
1917 ((GridLayout) progressPanel.getLayout()).setRows(
1918 ((GridLayout) progressPanel.getLayout()).getRows() + 1);
1919 ++totalProgressCount;
1921 return thisprogress;
1924 int totalProgressCount = 0;
1926 private void removeProgressPanel(JPanel progbar)
1928 if (progressPanel != null)
1930 synchronized (progressPanel)
1932 progressPanel.remove(progbar);
1933 GridLayout gl = (GridLayout) progressPanel.getLayout();
1934 gl.setRows(gl.getRows() - 1);
1935 if (--totalProgressCount < 1)
1937 this.getContentPane().remove(progressPanel);
1938 progressPanel = null;
1945 public void stopLoading()
1948 if (fileLoadingCount < 1)
1950 while (fileLoadingPanels.size() > 0)
1952 removeProgressPanel(fileLoadingPanels.remove(0));
1954 fileLoadingPanels.clear();
1955 fileLoadingCount = 0;
1960 public static int getViewCount(String alignmentId)
1962 AlignmentViewport[] aps = getViewports(alignmentId);
1963 return (aps == null) ? 0 : aps.length;
1968 * @param alignmentId
1969 * - if null, all sets are returned
1970 * @return all AlignmentPanels concerning the alignmentId sequence set
1972 public static AlignmentPanel[] getAlignmentPanels(String alignmentId)
1974 if (Desktop.getDesktopPane() == null)
1976 // no frames created and in headless mode
1977 // TODO: verify that frames are recoverable when in headless mode
1980 List<AlignmentPanel> aps = new ArrayList<>();
1981 AlignFrame[] frames = getAlignFrames();
1986 for (AlignFrame af : frames)
1988 for (AlignmentPanel ap : af.alignPanels)
1990 if (alignmentId == null
1991 || alignmentId.equals(ap.av.getSequenceSetId()))
1997 if (aps.size() == 0)
2001 AlignmentPanel[] vap = aps.toArray(new AlignmentPanel[aps.size()]);
2006 * get all the viewports on an alignment.
2008 * @param sequenceSetId
2009 * unique alignment id (may be null - all viewports returned in that
2011 * @return all viewports on the alignment bound to sequenceSetId
2013 public static AlignmentViewport[] getViewports(String sequenceSetId)
2015 List<AlignmentViewport> viewp = new ArrayList<>();
2016 if (getDesktopPane() != null)
2018 AlignFrame[] frames = Desktop.getAlignFrames();
2020 for (AlignFrame afr : frames)
2022 if (sequenceSetId == null || afr.getViewport().getSequenceSetId()
2023 .equals(sequenceSetId))
2025 if (afr.alignPanels != null)
2027 for (AlignmentPanel ap : afr.alignPanels)
2029 if (sequenceSetId == null
2030 || sequenceSetId.equals(ap.av.getSequenceSetId()))
2038 viewp.add(afr.getViewport());
2042 if (viewp.size() > 0)
2044 return viewp.toArray(new AlignmentViewport[viewp.size()]);
2051 * Explode the views in the given frame into separate AlignFrame
2055 public static void explodeViews(AlignFrame af)
2057 int size = af.alignPanels.size();
2063 for (int i = 0; i < size; i++)
2065 AlignmentPanel ap = af.alignPanels.get(i);
2066 AlignFrame newaf = new AlignFrame(ap);
2069 * Restore the view's last exploded frame geometry if known. Multiple
2070 * views from one exploded frame share and restore the same (frame)
2071 * position and size.
2073 Rectangle geometry = ap.av.getExplodedGeometry();
2074 if (geometry != null)
2076 newaf.setBounds(geometry);
2079 ap.av.setGatherViewsHere(false);
2081 addInternalFrame(newaf, af.getTitle(), AlignFrame.DEFAULT_WIDTH,
2082 AlignFrame.DEFAULT_HEIGHT);
2085 af.alignPanels.clear();
2086 af.closeMenuItem_actionPerformed(true);
2091 * Gather expanded views (separate AlignFrame's) with the same sequence set
2092 * identifier back in to this frame as additional views, and close the expanded
2093 * views. Note the expanded frames may themselves have multiple views. We take
2098 public void gatherViews(AlignFrame source)
2100 source.viewport.setGatherViewsHere(true);
2101 source.viewport.setExplodedGeometry(source.getBounds());
2102 JInternalFrame[] frames = getAllFrames();
2103 String viewId = source.viewport.getSequenceSetId();
2105 for (int t = 0; t < frames.length; t++)
2107 if (frames[t] instanceof AlignFrame && frames[t] != source)
2109 AlignFrame af = (AlignFrame) frames[t];
2110 boolean gatherThis = false;
2111 for (int a = 0; a < af.alignPanels.size(); a++)
2113 AlignmentPanel ap = af.alignPanels.get(a);
2114 if (viewId.equals(ap.av.getSequenceSetId()))
2117 ap.av.setGatherViewsHere(false);
2118 ap.av.setExplodedGeometry(af.getBounds());
2119 source.addAlignmentPanel(ap, false);
2125 af.alignPanels.clear();
2126 af.closeMenuItem_actionPerformed(true);
2133 jalview.gui.VamsasApplication v_client = null;
2136 public void vamsasImport_actionPerformed(ActionEvent e)
2138 // TODO: JAL-3048 not needed for Jalview-JS
2140 if (v_client == null)
2142 // Load and try to start a session.
2143 JalviewFileChooser chooser = new JalviewFileChooser(
2144 jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
2146 chooser.setFileView(new JalviewFileView());
2147 chooser.setDialogTitle(
2148 MessageManager.getString("label.open_saved_vamsas_session"));
2149 chooser.setToolTipText(MessageManager.getString(
2150 "label.select_vamsas_session_opened_as_new_vamsas_session"));
2152 int value = chooser.showOpenDialog(this);
2154 if (value == JalviewFileChooser.APPROVE_OPTION)
2156 String fle = chooser.getSelectedFile().toString();
2157 if (!vamsasImport(chooser.getSelectedFile()))
2159 JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
2160 MessageManager.formatMessage(
2161 "label.couldnt_import_as_vamsas_session",
2165 .getString("label.vamsas_document_import_failed"),
2166 JvOptionPane.ERROR_MESSAGE);
2172 jalview.bin.Cache.log.error(
2173 "Implementation error - load session from a running session is not supported.");
2178 * import file into a new vamsas session (uses jalview.gui.VamsasApplication)
2181 * @return true if import was a success and a session was started.
2183 public boolean vamsasImport(URL url)
2185 // TODO: create progress bar
2186 if (v_client != null)
2189 jalview.bin.Cache.log.error(
2190 "Implementation error - load session from a running session is not supported.");
2196 // copy the URL content to a temporary local file
2197 // TODO: be a bit cleverer here with nio (?!)
2198 File file = File.createTempFile("vdocfromurl", ".vdj");
2199 FileOutputStream fos = new FileOutputStream(file);
2200 BufferedInputStream bis = new BufferedInputStream(url.openStream());
2201 byte[] buffer = new byte[2048];
2203 while ((ln = bis.read(buffer)) > -1)
2205 fos.write(buffer, 0, ln);
2209 v_client = new jalview.gui.VamsasApplication(this, file,
2210 url.toExternalForm());
2211 } catch (Exception ex)
2213 jalview.bin.Cache.log.error(
2214 "Failed to create new vamsas session from contents of URL "
2219 setupVamsasConnectedGui();
2220 v_client.initial_update(); // TODO: thread ?
2221 return v_client.inSession();
2225 * import file into a new vamsas session (uses jalview.gui.VamsasApplication)
2228 * @return true if import was a success and a session was started.
2230 public boolean vamsasImport(File file)
2232 if (v_client != null)
2235 jalview.bin.Cache.log.error(
2236 "Implementation error - load session from a running session is not supported.");
2240 setProgressBar(MessageManager.formatMessage(
2241 "status.importing_vamsas_session_from", new Object[]
2242 { file.getName() }), file.hashCode());
2245 v_client = new jalview.gui.VamsasApplication(this, file, null);
2246 } catch (Exception ex)
2248 setProgressBar(MessageManager.formatMessage(
2249 "status.importing_vamsas_session_from", new Object[]
2250 { file.getName() }), file.hashCode());
2251 jalview.bin.Cache.log.error(
2252 "New vamsas session from existing session file failed:", ex);
2255 setupVamsasConnectedGui();
2256 v_client.initial_update(); // TODO: thread ?
2257 setProgressBar(MessageManager.formatMessage(
2258 "status.importing_vamsas_session_from", new Object[]
2259 { file.getName() }), file.hashCode());
2260 return v_client.inSession();
2263 public boolean joinVamsasSession(String mysesid)
2265 if (v_client != null)
2267 throw new Error(MessageManager
2268 .getString("error.try_join_vamsas_session_another"));
2270 if (mysesid == null)
2273 MessageManager.getString("error.invalid_vamsas_session_id"));
2275 v_client = new VamsasApplication(this, mysesid);
2276 setupVamsasConnectedGui();
2277 v_client.initial_update();
2278 return (v_client.inSession());
2282 public void vamsasStart_actionPerformed(ActionEvent e)
2284 if (v_client == null)
2287 // we just start a default session for moment.
2289 * JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache.
2290 * getProperty("LAST_DIRECTORY"));
2292 * chooser.setFileView(new JalviewFileView());
2293 * chooser.setDialogTitle("Load Vamsas file");
2294 * chooser.setToolTipText("Import");
2296 * int value = chooser.showOpenDialog(this);
2298 * if (value == JalviewFileChooser.APPROVE_OPTION) { v_client = new
2299 * jalview.gui.VamsasApplication(this, chooser.getSelectedFile());
2301 v_client = new VamsasApplication(this);
2302 setupVamsasConnectedGui();
2303 v_client.initial_update(); // TODO: thread ?
2307 // store current data in session.
2308 v_client.push_update(); // TODO: thread
2312 protected void setupVamsasConnectedGui()
2314 vamsasStart.setText(MessageManager.getString("label.session_update"));
2315 vamsasSave.setVisible(true);
2316 vamsasStop.setVisible(true);
2317 vamsasImport.setVisible(false); // Document import to existing session is
2318 // not possible for vamsas-client-1.0.
2321 protected void setupVamsasDisconnectedGui()
2323 vamsasSave.setVisible(false);
2324 vamsasStop.setVisible(false);
2325 vamsasImport.setVisible(true);
2327 .setText(MessageManager.getString("label.new_vamsas_session"));
2331 public void vamsasStop_actionPerformed(ActionEvent e)
2333 if (v_client != null)
2335 v_client.end_session();
2337 setupVamsasDisconnectedGui();
2341 protected void buildVamsasStMenu()
2343 if (v_client == null)
2345 String[] sess = null;
2348 sess = VamsasApplication.getSessionList();
2349 } catch (Exception e)
2351 jalview.bin.Cache.log.warn("Problem getting current sessions list.",
2357 jalview.bin.Cache.log.debug(
2358 "Got current sessions list: " + sess.length + " entries.");
2359 VamsasStMenu.removeAll();
2360 for (int i = 0; i < sess.length; i++)
2362 JMenuItem sessit = new JMenuItem();
2363 sessit.setText(sess[i]);
2364 sessit.setToolTipText(MessageManager
2365 .formatMessage("label.connect_to_session", new Object[]
2367 final Desktop dsktp = this;
2368 final String mysesid = sess[i];
2369 sessit.addActionListener(new ActionListener()
2373 public void actionPerformed(ActionEvent e)
2375 if (dsktp.v_client == null)
2377 Thread rthr = new Thread(new Runnable()
2383 dsktp.v_client = new VamsasApplication(dsktp, mysesid);
2384 dsktp.setupVamsasConnectedGui();
2385 dsktp.v_client.initial_update();
2393 VamsasStMenu.add(sessit);
2395 // don't show an empty menu.
2396 VamsasStMenu.setVisible(sess.length > 0);
2401 jalview.bin.Cache.log.debug("No current vamsas sessions.");
2402 VamsasStMenu.removeAll();
2403 VamsasStMenu.setVisible(false);
2408 // Not interested in the content. Just hide ourselves.
2409 VamsasStMenu.setVisible(false);
2414 public void vamsasSave_actionPerformed(ActionEvent e)
2416 // TODO: JAL-3048 not needed for Jalview-JS
2418 if (v_client != null)
2420 // TODO: VAMSAS DOCUMENT EXTENSION is VDJ
2421 JalviewFileChooser chooser = new JalviewFileChooser("vdj",
2424 chooser.setFileView(new JalviewFileView());
2425 chooser.setDialogTitle(MessageManager
2426 .getString("label.save_vamsas_document_archive"));
2428 int value = chooser.showSaveDialog(this);
2430 if (value == JalviewFileChooser.APPROVE_OPTION)
2432 java.io.File choice = chooser.getSelectedFile();
2433 JPanel progpanel = addProgressPanel(MessageManager
2434 .formatMessage("label.saving_vamsas_doc", new Object[]
2435 { choice.getName() }));
2436 Cache.setProperty("LAST_DIRECTORY", choice.getParent());
2437 String warnmsg = null;
2438 String warnttl = null;
2441 v_client.vclient.storeDocument(choice);
2444 warnttl = "Serious Problem saving Vamsas Document";
2445 warnmsg = ex.toString();
2446 jalview.bin.Cache.log
2447 .error("Error Whilst saving document to " + choice, ex);
2449 } catch (Exception ex)
2451 warnttl = "Problem saving Vamsas Document.";
2452 warnmsg = ex.toString();
2453 jalview.bin.Cache.log.warn(
2454 "Exception Whilst saving document to " + choice, ex);
2457 removeProgressPanel(progpanel);
2458 if (warnmsg != null)
2460 JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
2462 warnmsg, warnttl, JvOptionPane.ERROR_MESSAGE);
2468 JPanel vamUpdate = null;
2471 * hide vamsas user gui bits when a vamsas document event is being handled.
2474 * true to hide gui, false to reveal gui
2476 public void setVamsasUpdate(boolean b)
2478 Cache.log.debug("Setting gui for Vamsas update "
2479 + (b ? "in progress" : "finished"));
2481 if (vamUpdate != null)
2483 this.removeProgressPanel(vamUpdate);
2487 vamUpdate = this.addProgressPanel(
2488 MessageManager.getString("label.updating_vamsas_session"));
2490 vamsasStart.setVisible(!b);
2491 vamsasStop.setVisible(!b);
2492 vamsasSave.setVisible(!b);
2495 public JInternalFrame[] getAllFrames()
2497 return desktopPane.getAllFrames();
2501 * Checks the given url to see if it gives a response indicating that the user
2502 * should be informed of a new questionnaire.
2506 public void checkForQuestionnaire(String url)
2508 UserQuestionnaireCheck jvq = new UserQuestionnaireCheck(url);
2509 // javax.swing.SwingUtilities.invokeLater(jvq);
2510 new Thread(jvq).start();
2513 public void checkURLLinks()
2515 // Thread off the URL link checker
2516 addDialogThread(new Runnable()
2521 if (Cache.getDefault("CHECKURLLINKS", true))
2523 // check what the actual links are - if it's just the default don't
2524 // bother with the warning
2525 List<String> links = Preferences.sequenceUrlLinks
2528 // only need to check links if there is one with a
2529 // SEQUENCE_ID which is not the default EMBL_EBI link
2530 ListIterator<String> li = links.listIterator();
2531 boolean check = false;
2532 List<JLabel> urls = new ArrayList<>();
2533 while (li.hasNext())
2535 String link = li.next();
2536 if (link.contains(UrlConstants.SEQUENCE_ID)
2537 && !UrlConstants.isDefaultString(link))
2540 int barPos = link.indexOf("|");
2541 String urlMsg = barPos == -1 ? link
2542 : link.substring(0, barPos) + ": "
2543 + link.substring(barPos + 1);
2544 urls.add(new JLabel(urlMsg));
2552 // ask user to check in case URL links use old style tokens
2553 // ($SEQUENCE_ID$ for sequence id _or_ accession id)
2554 JPanel msgPanel = new JPanel();
2555 msgPanel.setLayout(new BoxLayout(msgPanel, BoxLayout.PAGE_AXIS));
2556 msgPanel.add(Box.createVerticalGlue());
2557 JLabel msg = new JLabel(MessageManager
2558 .getString("label.SEQUENCE_ID_for_DB_ACCESSION1"));
2559 JLabel msg2 = new JLabel(MessageManager
2560 .getString("label.SEQUENCE_ID_for_DB_ACCESSION2"));
2562 for (JLabel url : urls)
2568 final JCheckBox jcb = new JCheckBox(
2569 MessageManager.getString("label.do_not_display_again"));
2570 jcb.addActionListener(new ActionListener()
2573 public void actionPerformed(ActionEvent e)
2575 // update Cache settings for "don't show this again"
2576 boolean showWarningAgain = !jcb.isSelected();
2577 Cache.setProperty("CHECKURLLINKS",
2578 Boolean.valueOf(showWarningAgain).toString());
2583 JvOptionPane.showMessageDialog(desktopPane, msgPanel,
2585 .getString("label.SEQUENCE_ID_no_longer_used"),
2586 JvOptionPane.WARNING_MESSAGE);
2593 * Proxy class for JDesktopPane which optionally displays the current memory
2594 * usage and highlights the desktop area with a red bar if free memory runs low.
2598 public class MyDesktopPane extends JDesktopPane
2601 private static final float ONE_MB = 1048576f;
2603 boolean showMemoryUsage = false;
2607 java.text.NumberFormat df;
2609 float maxMemory, allocatedMemory, freeMemory, totalFreeMemory,
2612 public MyDesktopPane(boolean showMemoryUsage)
2614 showMemoryUsage(showMemoryUsage);
2617 public void showMemoryUsage(boolean showMemory)
2619 this.showMemoryUsage = showMemory;
2622 Thread worker = new Thread(this);
2628 public boolean isShowMemoryUsage()
2630 return showMemoryUsage;
2636 df = java.text.NumberFormat.getNumberInstance();
2637 df.setMaximumFractionDigits(2);
2638 runtime = Runtime.getRuntime();
2640 while (showMemoryUsage)
2644 maxMemory = runtime.maxMemory() / ONE_MB;
2645 allocatedMemory = runtime.totalMemory() / ONE_MB;
2646 freeMemory = runtime.freeMemory() / ONE_MB;
2647 totalFreeMemory = freeMemory + (maxMemory - allocatedMemory);
2649 percentUsage = (totalFreeMemory / maxMemory) * 100;
2651 // if (percentUsage < 20)
2653 // border1 = BorderFactory.createMatteBorder(12, 12, 12, 12,
2655 // instance.set.setBorder(border1);
2658 // sleep after showing usage
2660 } catch (Exception ex)
2662 ex.printStackTrace();
2668 public void paintComponent(Graphics g)
2670 if (showMemoryUsage && g != null && df != null)
2672 if (percentUsage < 20)
2674 g.setColor(Color.red);
2676 FontMetrics fm = g.getFontMetrics();
2679 g.drawString(MessageManager.formatMessage("label.memory_stats",
2681 { df.format(totalFreeMemory), df.format(maxMemory),
2682 df.format(percentUsage) }),
2683 10, getHeight() - fm.getHeight());
2690 * Accessor method to quickly get all the AlignmentFrames loaded.
2692 * @return an array of AlignFrame, or null if none found
2694 public static AlignFrame[] getAlignFrames()
2696 if (Jalview.isHeadlessMode())
2698 // Desktop.getDesktop() is null in headless mode
2699 return new AlignFrame[] { Jalview.getCurrentAlignFrame() };
2702 JInternalFrame[] frames = Desktop.getDesktopPane().getAllFrames();
2708 List<AlignFrame> avp = new ArrayList<>();
2710 for (int i = frames.length - 1; i > -1; i--)
2712 if (frames[i] instanceof AlignFrame)
2714 avp.add((AlignFrame) frames[i]);
2716 else if (frames[i] instanceof SplitFrame)
2719 * Also check for a split frame containing an AlignFrame
2721 GSplitFrame sf = (GSplitFrame) frames[i];
2722 if (sf.getTopFrame() instanceof AlignFrame)
2724 avp.add((AlignFrame) sf.getTopFrame());
2726 if (sf.getBottomFrame() instanceof AlignFrame)
2728 avp.add((AlignFrame) sf.getBottomFrame());
2732 if (avp.size() == 0)
2736 AlignFrame afs[] = avp.toArray(new AlignFrame[avp.size()]);
2741 * Returns an array of any AppJmol frames in the Desktop (or null if none).
2745 public GStructureViewer[] getJmols()
2747 JInternalFrame[] frames = Desktop.getDesktopPane().getAllFrames();
2753 List<GStructureViewer> avp = new ArrayList<>();
2755 for (int i = frames.length - 1; i > -1; i--)
2757 if (frames[i] instanceof AppJmol)
2759 GStructureViewer af = (GStructureViewer) frames[i];
2763 if (avp.size() == 0)
2767 GStructureViewer afs[] = avp.toArray(new GStructureViewer[avp.size()]);
2772 * Add Groovy Support to Jalview
2775 public void groovyShell_actionPerformed()
2779 openGroovyConsole();
2780 } catch (Exception ex)
2782 jalview.bin.Cache.log.error("Groovy Shell Creation failed.", ex);
2783 JvOptionPane.showInternalMessageDialog(desktopPane,
2785 MessageManager.getString("label.couldnt_create_groovy_shell"),
2786 MessageManager.getString("label.groovy_support_failed"),
2787 JvOptionPane.ERROR_MESSAGE);
2792 * Open the Groovy console
2794 private void openGroovyConsole()
2796 if (groovyConsole == null)
2798 groovyConsole = new groovy.ui.Console();
2799 groovyConsole.setVariable("Jalview", this);
2800 groovyConsole.run();
2803 * We allow only one console at a time, so that AlignFrame menu option
2804 * 'Calculate | Run Groovy script' is unambiguous.
2805 * Disable 'Groovy Console', and enable 'Run script', when the console is
2806 * opened, and the reverse when it is closed
2808 Window window = (Window) groovyConsole.getFrame();
2809 window.addWindowListener(new WindowAdapter()
2812 public void windowClosed(WindowEvent e)
2815 * rebind CMD-Q from Groovy Console to Jalview Quit
2818 enableExecuteGroovy(false);
2824 * show Groovy console window (after close and reopen)
2826 ((Window) groovyConsole.getFrame()).setVisible(true);
2829 * if we got this far, enable 'Run Groovy' in AlignFrame menus
2830 * and disable opening a second console
2832 enableExecuteGroovy(true);
2836 * Bind Ctrl/Cmd-Q to Quit - for reset as Groovy Console takes over this binding
2839 protected void addQuitHandler()
2841 getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
2842 .put(KeyStroke.getKeyStroke(KeyEvent.VK_Q,
2843 Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()),
2845 getRootPane().getActionMap().put("Quit", new AbstractAction()
2848 public void actionPerformed(ActionEvent e)
2856 * Enable or disable 'Run Groovy script' in AlignFrame calculate menus
2859 * true if Groovy console is open
2861 public void enableExecuteGroovy(boolean enabled)
2864 * disable opening a second Groovy console
2865 * (or re-enable when the console is closed)
2867 groovyShell.setEnabled(!enabled);
2869 AlignFrame[] alignFrames = getAlignFrames();
2870 if (alignFrames != null)
2872 for (AlignFrame af : alignFrames)
2874 af.setGroovyEnabled(enabled);
2880 * Progress bars managed by the IProgressIndicator method.
2882 private Hashtable<Long, JPanel> progressBars;
2884 private Hashtable<Long, IProgressIndicatorHandler> progressBarHandlers;
2889 * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
2892 public void setProgressBar(String message, long id)
2894 Platform.timeCheck("Desktop " + message, Platform.TIME_MARK);
2896 if (progressBars == null)
2898 progressBars = new Hashtable<>();
2899 progressBarHandlers = new Hashtable<>();
2902 if (progressBars.get(new Long(id)) != null)
2904 JPanel panel = progressBars.remove(new Long(id));
2905 if (progressBarHandlers.contains(new Long(id)))
2907 progressBarHandlers.remove(new Long(id));
2909 removeProgressPanel(panel);
2913 progressBars.put(new Long(id), addProgressPanel(message));
2920 * @see jalview.gui.IProgressIndicator#registerHandler(long,
2921 * jalview.gui.IProgressIndicatorHandler)
2924 public void registerHandler(final long id,
2925 final IProgressIndicatorHandler handler)
2927 if (progressBarHandlers == null
2928 || !progressBars.containsKey(new Long(id)))
2930 throw new Error(MessageManager.getString(
2931 "error.call_setprogressbar_before_registering_handler"));
2933 progressBarHandlers.put(new Long(id), handler);
2934 final JPanel progressPanel = progressBars.get(new Long(id));
2935 if (handler.canCancel())
2937 JButton cancel = new JButton(
2938 MessageManager.getString("action.cancel"));
2939 final IProgressIndicator us = this;
2940 cancel.addActionListener(new ActionListener()
2944 public void actionPerformed(ActionEvent e)
2946 handler.cancelActivity(id);
2947 us.setProgressBar(MessageManager
2948 .formatMessage("label.cancelled_params", new Object[]
2949 { ((JLabel) progressPanel.getComponent(0)).getText() }),
2953 progressPanel.add(cancel, BorderLayout.EAST);
2959 * @return true if any progress bars are still active
2962 public boolean operationInProgress()
2964 if (progressBars != null && progressBars.size() > 0)
2972 * This will return the first AlignFrame holding the given viewport instance. It
2973 * will break if there are more than one AlignFrames viewing a particular av.
2976 * @return alignFrame for viewport
2978 public static AlignFrame getAlignFrameFor(AlignViewportI viewport)
2980 if (getDesktopPane() != null)
2982 AlignmentPanel[] aps = getAlignmentPanels(
2983 viewport.getSequenceSetId());
2984 for (int panel = 0; aps != null && panel < aps.length; panel++)
2986 if (aps[panel] != null && aps[panel].av == viewport)
2988 return aps[panel].alignFrame;
2995 public VamsasApplication getVamsasApplication()
3002 * flag set if jalview GUI is being operated programmatically
3004 private boolean inBatchMode = false;
3007 * check if jalview GUI is being operated programmatically
3009 * @return inBatchMode
3011 public boolean isInBatchMode()
3017 * set flag if jalview GUI is being operated programmatically
3019 * @param inBatchMode
3021 public void setInBatchMode(boolean inBatchMode)
3023 this.inBatchMode = inBatchMode;
3026 public void startServiceDiscovery()
3028 startServiceDiscovery(false);
3031 public void startServiceDiscovery(boolean blocking)
3033 boolean alive = true;
3034 Thread t0 = null, t1 = null, t2 = null;
3035 // JAL-940 - JALVIEW 1 services are now being EOLed as of JABA 2.1 release
3038 // todo: changesupport handlers need to be transferred
3039 if (discoverer == null)
3041 discoverer = Discoverer.getInstance();
3042 // register PCS handler for getDesktop().
3043 discoverer.addPropertyChangeListener(changeSupport);
3045 // JAL-940 - disabled JWS1 service configuration - always start discoverer
3046 // until we phase out completely
3047 (t0 = new Thread(discoverer)).start();
3050 if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
3052 t2 = jalview.ws.jws2.Jws2Discoverer.getInstance()
3053 .startDiscoverer(changeSupport);
3057 // TODO: do rest service discovery
3066 } catch (Exception e)
3069 alive = (t1 != null && t1.isAlive()) || (t2 != null && t2.isAlive())
3070 || (t3 != null && t3.isAlive())
3071 || (t0 != null && t0.isAlive());
3077 * called to check if the service discovery process completed successfully.
3081 protected void JalviewServicesChanged(PropertyChangeEvent evt)
3083 if (evt.getNewValue() == null || evt.getNewValue() instanceof Vector)
3085 final String ermsg = jalview.ws.jws2.Jws2Discoverer.getInstance()
3086 .getErrorMessages();
3089 if (Cache.getDefault("SHOW_WSDISCOVERY_ERRORS", true))
3091 if (serviceChangedDialog == null)
3093 // only run if we aren't already displaying one of these.
3094 addDialogThread(serviceChangedDialog = new Runnable()
3101 * JalviewDialog jd =new JalviewDialog() {
3103 * @Override protected void cancelPressed() { // TODO
3104 * Auto-generated method stub
3106 * }@Override protected void okPressed() { // TODO
3107 * Auto-generated method stub
3109 * }@Override protected void raiseClosed() { // TODO
3110 * Auto-generated method stub
3112 * } }; jd.initDialogFrame(new
3113 * JLabel("<html><table width=\"450\"><tr><td>" + ermsg +
3114 * "<br/>It may be that you have invalid JABA URLs in your web service preferences,"
3115 * + " or mis-configured HTTP proxy settings.<br/>" +
3116 * "Check the <em>Connections</em> and <em>Web services</em> tab of the"
3118 * " Tools->Preferences dialog box to change them.</td></tr></table></html>"
3119 * ), true, true, "Web Service Configuration Problem", 450,
3122 * jd.waitForInput();
3124 JvOptionPane.showConfirmDialog(Desktop.getDesktopPane(),
3125 new JLabel("<html><table width=\"450\"><tr><td>"
3126 + ermsg + "</td></tr></table>"
3127 + "<p>It may be that you have invalid JABA URLs<br/>in your web service preferences,"
3128 + "<br>or as a command-line argument, or mis-configured HTTP proxy settings.</p>"
3129 + "<p>Check the <em>Connections</em> and <em>Web services</em> tab<br/>of the"
3130 + " Tools->Preferences dialog box to change them.</p></html>"),
3131 "Web Service Configuration Problem",
3132 JvOptionPane.DEFAULT_OPTION,
3133 JvOptionPane.ERROR_MESSAGE);
3134 serviceChangedDialog = null;
3143 "Errors reported by JABA discovery service. Check web services preferences.\n"
3150 Runnable serviceChangedDialog = null;
3153 * start a thread to open a URL in the configured browser. Pops up a warning
3154 * dialog to the user if there is an exception when calling out to the browser
3159 public static void showUrl(final String url)
3161 showUrl(url, Desktop.getInstance());
3165 * Like showUrl but allows progress handler to be specified
3169 * (null) or object implementing IProgressIndicator
3171 public static void showUrl(final String url,
3172 final IProgressIndicator progress)
3174 new Thread(new Runnable()
3181 if (progress != null)
3183 progress.setProgressBar(MessageManager
3184 .formatMessage("status.opening_params", new Object[]
3185 { url }), this.hashCode());
3187 BrowserLauncher.openURL(url);
3188 } catch (Exception ex)
3190 JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
3192 .getString("label.web_browser_not_found_unix"),
3193 MessageManager.getString("label.web_browser_not_found"),
3194 JvOptionPane.WARNING_MESSAGE);
3196 ex.printStackTrace();
3198 if (progress != null)
3200 progress.setProgressBar(null, this.hashCode());
3206 private WsParamSetManager wsparamManager = null;
3208 public static ParamManager getUserParameterStore()
3210 Desktop d = Desktop.getInstance();
3211 if (d.wsparamManager == null)
3213 d.wsparamManager = new WsParamSetManager();
3215 return d.wsparamManager;
3219 * static hyperlink handler proxy method for use by Jalview's internal windows
3223 public static void hyperlinkUpdate(HyperlinkEvent e)
3225 if (e.getEventType() == EventType.ACTIVATED)
3230 url = e.getURL().toString();
3231 Desktop.showUrl(url);
3232 } catch (Exception x)
3236 if (Cache.log != null)
3238 Cache.log.error("Couldn't handle string " + url + " as a URL.");
3243 "Couldn't handle string " + url + " as a URL.");
3246 // ignore any exceptions due to dud links.
3253 * single thread that handles display of dialogs to user.
3255 ExecutorService dialogExecutor = Executors.newSingleThreadExecutor();
3258 * flag indicating if dialogExecutor should try to acquire a permit
3260 volatile boolean dialogPause = true;
3265 java.util.concurrent.Semaphore block = new Semaphore(0);
3267 private groovy.ui.Console groovyConsole;
3269 public StructureViewer lastTargetedView;
3272 * add another dialog thread to the queue
3276 public void addDialogThread(final Runnable prompter)
3278 dialogExecutor.submit(new Runnable()
3288 } catch (InterruptedException x)
3292 if (Jalview.isHeadlessMode())
3298 SwingUtilities.invokeAndWait(prompter);
3299 } catch (Exception q)
3301 Cache.log.warn("Unexpected Exception in dialog thread.", q);
3307 public void startDialogQueue()
3309 // set the flag so we don't pause waiting for another permit and semaphore
3310 // the current task to begin
3311 dialogPause = false;
3316 * Outputs an image of the desktop to file in EPS format, after prompting the
3317 * user for choice of Text or Lineart character rendering (unless a preference
3318 * has been set). The file name is generated as
3321 * Jalview_snapshot_nnnnn.eps where nnnnn is the current timestamp in milliseconds
3325 protected void snapShotWindow_actionPerformed(ActionEvent e)
3327 // currently the menu option to do this is not shown
3330 int width = getWidth();
3331 int height = getHeight();
3333 "Jalview_snapshot_" + System.currentTimeMillis() + ".eps");
3334 ImageWriterI writer = new ImageWriterI()
3337 public void exportImage(Graphics g) throws Exception
3340 Cache.log.info("Successfully written snapshot to file "
3341 + of.getAbsolutePath());
3344 String title = "View of desktop";
3345 ImageExporter exporter = new ImageExporter(writer, null, TYPE.EPS,
3347 exporter.doExport(of, this, width, height, title);
3351 * Explode the views in the given SplitFrame into separate SplitFrame windows.
3352 * This respects (remembers) any previous 'exploded geometry' i.e. the size and
3353 * location last time the view was expanded (if any). However it does not
3354 * remember the split pane divider location - this is set to match the
3355 * 'exploding' frame.
3359 public void explodeViews(SplitFrame sf)
3361 AlignFrame oldTopFrame = (AlignFrame) sf.getTopFrame();
3362 AlignFrame oldBottomFrame = (AlignFrame) sf.getBottomFrame();
3363 List<? extends AlignmentViewPanel> topPanels = oldTopFrame
3365 List<? extends AlignmentViewPanel> bottomPanels = oldBottomFrame
3367 int viewCount = topPanels.size();
3374 * Processing in reverse order works, forwards order leaves the first panels
3375 * not visible. I don't know why!
3377 for (int i = viewCount - 1; i >= 0; i--)
3380 * Make new top and bottom frames. These take over the respective
3381 * AlignmentPanel objects, including their AlignmentViewports, so the
3382 * cdna/protein relationships between the viewports is carried over to the
3385 * explodedGeometry holds the (x, y) position of the previously exploded
3386 * SplitFrame, and the (width, height) of the AlignFrame component
3388 AlignmentPanel topPanel = (AlignmentPanel) topPanels.get(i);
3389 AlignFrame newTopFrame = new AlignFrame(topPanel);
3390 newTopFrame.setSize(oldTopFrame.getSize());
3391 newTopFrame.setVisible(true);
3392 Rectangle geometry = ((AlignViewport) topPanel.getAlignViewport())
3393 .getExplodedGeometry();
3394 if (geometry != null)
3396 newTopFrame.setSize(geometry.getSize());
3399 AlignmentPanel bottomPanel = (AlignmentPanel) bottomPanels.get(i);
3400 AlignFrame newBottomFrame = new AlignFrame(bottomPanel);
3401 newBottomFrame.setSize(oldBottomFrame.getSize());
3402 newBottomFrame.setVisible(true);
3403 geometry = ((AlignViewport) bottomPanel.getAlignViewport())
3404 .getExplodedGeometry();
3405 if (geometry != null)
3407 newBottomFrame.setSize(geometry.getSize());
3410 topPanel.av.setGatherViewsHere(false);
3411 bottomPanel.av.setGatherViewsHere(false);
3412 JInternalFrame splitFrame = new SplitFrame(newTopFrame,
3414 if (geometry != null)
3416 splitFrame.setLocation(geometry.getLocation());
3418 Desktop.addInternalFrame(splitFrame, sf.getTitle(), -1, -1);
3422 * Clear references to the panels (now relocated in the new SplitFrames)
3423 * before closing the old SplitFrame.
3426 bottomPanels.clear();
3431 * Gather expanded split frames, sharing the same pairs of sequence set ids,
3432 * back into the given SplitFrame as additional views. Note that the gathered
3433 * frames may themselves have multiple views.
3437 public void gatherViews(GSplitFrame source)
3440 * special handling of explodedGeometry for a view within a SplitFrame: - it
3441 * holds the (x, y) position of the enclosing SplitFrame, and the (width,
3442 * height) of the AlignFrame component
3444 AlignFrame myTopFrame = (AlignFrame) source.getTopFrame();
3445 AlignFrame myBottomFrame = (AlignFrame) source.getBottomFrame();
3446 myTopFrame.viewport.setExplodedGeometry(new Rectangle(source.getX(),
3447 source.getY(), myTopFrame.getWidth(), myTopFrame.getHeight()));
3448 myBottomFrame.viewport
3449 .setExplodedGeometry(new Rectangle(source.getX(), source.getY(),
3450 myBottomFrame.getWidth(), myBottomFrame.getHeight()));
3451 myTopFrame.viewport.setGatherViewsHere(true);
3452 myBottomFrame.viewport.setGatherViewsHere(true);
3453 String topViewId = myTopFrame.viewport.getSequenceSetId();
3454 String bottomViewId = myBottomFrame.viewport.getSequenceSetId();
3456 JInternalFrame[] frames = desktopPane.getAllFrames();
3457 for (JInternalFrame frame : frames)
3459 if (frame instanceof SplitFrame && frame != source)
3461 SplitFrame sf = (SplitFrame) frame;
3462 AlignFrame topFrame = (AlignFrame) sf.getTopFrame();
3463 AlignFrame bottomFrame = (AlignFrame) sf.getBottomFrame();
3464 boolean gatherThis = false;
3465 for (int a = 0; a < topFrame.alignPanels.size(); a++)
3467 AlignmentPanel topPanel = topFrame.alignPanels.get(a);
3468 AlignmentPanel bottomPanel = bottomFrame.alignPanels.get(a);
3469 if (topViewId.equals(topPanel.av.getSequenceSetId())
3470 && bottomViewId.equals(bottomPanel.av.getSequenceSetId()))
3473 topPanel.av.setGatherViewsHere(false);
3474 bottomPanel.av.setGatherViewsHere(false);
3475 topPanel.av.setExplodedGeometry(
3476 new Rectangle(sf.getLocation(), topFrame.getSize()));
3477 bottomPanel.av.setExplodedGeometry(
3478 new Rectangle(sf.getLocation(), bottomFrame.getSize()));
3479 myTopFrame.addAlignmentPanel(topPanel, false);
3480 myBottomFrame.addAlignmentPanel(bottomPanel, false);
3486 topFrame.getAlignPanels().clear();
3487 bottomFrame.getAlignPanels().clear();
3494 * The dust settles...give focus to the tab we did this from.
3496 myTopFrame.setDisplayedView(myTopFrame.alignPanel);
3499 public static groovy.ui.Console getGroovyConsole()
3501 Desktop desktop = Desktop.getInstance();
3502 return desktop == null ? null : desktop.groovyConsole;
3506 * handles the payload of a drag and drop event.
3508 * TODO refactor to desktop utilities class
3511 * - Data source strings extracted from the drop event
3513 * - protocol for each data source extracted from the drop event
3517 * - the payload from the drop event
3520 @SuppressWarnings("unchecked")
3521 public static void transferFromDropTarget(List<Object> files,
3522 List<DataSourceType> protocols, DropTargetDropEvent evt,
3523 Transferable t) throws Exception
3526 // BH 2018 changed List<String> to List<Object> to allow for File from SwingJS
3528 // DataFlavor[] flavors = t.getTransferDataFlavors();
3529 // for (int i = 0; i < flavors.length; i++) {
3530 // if (flavors[i].isFlavorJavaFileListType()) {
3531 // evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
3532 // List<File> list = (List<File>) t.getTransferData(flavors[i]);
3533 // for (int j = 0; j < list.size(); j++) {
3534 // File file = (File) list.get(j);
3535 // byte[] data = getDroppedFileBytes(file);
3536 // fileName.setText(file.getName() + " - " + data.length + " " +
3537 // evt.getLocation());
3538 // JTextArea target = (JTextArea) ((DropTarget) evt.getSource()).getComponent();
3539 // target.setText(new String(data));
3541 // dtde.dropComplete(true);
3546 DataFlavor uriListFlavor = new DataFlavor(
3547 "text/uri-list;class=java.lang.String"), urlFlavour = null;
3550 urlFlavour = new DataFlavor(
3551 "application/x-java-url; class=java.net.URL");
3552 } catch (ClassNotFoundException cfe)
3554 Cache.log.debug("Couldn't instantiate the URL dataflavor.", cfe);
3557 if (urlFlavour != null && t.isDataFlavorSupported(urlFlavour))
3562 java.net.URL url = (URL) t.getTransferData(urlFlavour);
3563 // nb: java 8 osx bug https://bugs.openjdk.java.net/browse/JDK-8156099
3564 // means url may be null.
3567 protocols.add(DataSourceType.URL);
3568 files.add(url.toString());
3569 Cache.log.debug("Drop handled as URL dataflavor "
3570 + files.get(files.size() - 1));
3575 if (Platform.isAMacAndNotJS())
3578 "Please ignore plist error - occurs due to problem with java 8 on OSX");
3581 } catch (Throwable ex)
3583 Cache.log.debug("URL drop handler failed.", ex);
3586 if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
3588 // Works on Windows and MacOSX
3589 Cache.log.debug("Drop handled as javaFileListFlavor");
3590 for (Object file : (List<Object>) t
3591 .getTransferData(DataFlavor.javaFileListFlavor))
3594 protocols.add(DataSourceType.FILE);
3599 // Unix like behaviour
3600 boolean added = false;
3602 if (t.isDataFlavorSupported(uriListFlavor))
3604 Cache.log.debug("Drop handled as uriListFlavor");
3605 // This is used by Unix drag system
3606 data = (String) t.getTransferData(uriListFlavor);
3610 // fallback to text: workaround - on OSX where there's a JVM bug
3611 Cache.log.debug("standard URIListFlavor failed. Trying text");
3612 // try text fallback
3613 DataFlavor textDf = new DataFlavor(
3614 "text/plain;class=java.lang.String");
3615 if (t.isDataFlavorSupported(textDf))
3617 data = (String) t.getTransferData(textDf);
3620 Cache.log.debug("Plain text drop content returned "
3621 + (data == null ? "Null - failed" : data));
3626 while (protocols.size() < files.size())
3628 Cache.log.debug("Adding missing FILE protocol for "
3629 + files.get(protocols.size()));
3630 protocols.add(DataSourceType.FILE);
3632 for (java.util.StringTokenizer st = new java.util.StringTokenizer(
3633 data, "\r\n"); st.hasMoreTokens();)
3636 String s = st.nextToken();
3637 if (s.startsWith("#"))
3639 // the line is a comment (as per the RFC 2483)
3642 java.net.URI uri = new java.net.URI(s);
3643 if (uri.getScheme().toLowerCase().startsWith("http"))
3645 protocols.add(DataSourceType.URL);
3646 files.add(uri.toString());
3650 // otherwise preserve old behaviour: catch all for file objects
3651 java.io.File file = new java.io.File(uri);
3652 protocols.add(DataSourceType.FILE);
3653 files.add(file.toString());
3658 if (Cache.log.isDebugEnabled())
3660 if (data == null || !added)
3663 if (t.getTransferDataFlavors() != null
3664 && t.getTransferDataFlavors().length > 0)
3667 "Couldn't resolve drop data. Here are the supported flavors:");
3668 for (DataFlavor fl : t.getTransferDataFlavors())
3671 "Supported transfer dataflavor: " + fl.toString());
3672 Object df = t.getTransferData(fl);
3675 Cache.log.debug("Retrieves: " + df);
3679 Cache.log.debug("Retrieved nothing");
3685 Cache.log.debug("Couldn't resolve dataflavor for drop: "
3691 if (Platform.isWindowsAndNotJS())
3693 Cache.log.debug("Scanning dropped content for Windows Link Files");
3695 // resolve any .lnk files in the file drop
3696 for (int f = 0; f < files.size(); f++)
3698 String source = files.get(f).toString().toLowerCase();
3699 if (protocols.get(f).equals(DataSourceType.FILE)
3700 && (source.endsWith(".lnk") || source.endsWith(".url")
3701 || source.endsWith(".site")))
3705 Object obj = files.get(f);
3706 File lf = (obj instanceof File ? (File) obj
3707 : new File((String) obj));
3708 // process link file to get a URL
3709 Cache.log.debug("Found potential link file: " + lf);
3710 WindowsShortcut wscfile = new WindowsShortcut(lf);
3711 String fullname = wscfile.getRealFilename();
3712 protocols.set(f, FormatAdapter.checkProtocol(fullname));
3713 files.set(f, fullname);
3714 Cache.log.debug("Parsed real filename " + fullname
3715 + " to extract protocol: " + protocols.get(f));
3716 } catch (Exception ex)
3719 "Couldn't parse " + files.get(f) + " as a link file.",
3728 * Sets the Preferences property for experimental features to True or False
3729 * depending on the state of the controlling menu item
3732 protected void showExperimental_actionPerformed(boolean selected)
3734 Cache.setProperty(EXPERIMENTAL_FEATURES, Boolean.toString(selected));
3738 * Answers a (possibly empty) list of any structure viewer frames (currently for
3739 * either Jmol or Chimera) which are currently open. This may optionally be
3740 * restricted to viewers of a specified class, or viewers linked to a specified
3744 * if not null, only return viewers linked to this panel
3745 * @param structureViewerClass
3746 * if not null, only return viewers of this class
3749 public List<StructureViewerBase> getStructureViewers(
3750 AlignmentPanel apanel,
3751 Class<? extends StructureViewerBase> structureViewerClass)
3753 List<StructureViewerBase> result = new ArrayList<>();
3754 JInternalFrame[] frames = getAllFrames();
3756 for (JInternalFrame frame : frames)
3758 if (frame instanceof StructureViewerBase)
3760 if (structureViewerClass == null
3761 || structureViewerClass.isInstance(frame))
3764 || ((StructureViewerBase) frame).isLinkedWith(apanel))
3766 result.add((StructureViewerBase) frame);