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;
231 * just an instance (for testng, probably); no actual frames
233 private boolean instanceOnly;
235 class MyDesktopManager implements DesktopManager
238 private DesktopManager delegate;
240 public MyDesktopManager(DesktopManager delegate)
242 this.delegate = delegate;
246 public void activateFrame(JInternalFrame f)
250 delegate.activateFrame(f);
251 } catch (NullPointerException npe)
253 Point p = getMousePosition();
254 showPasteMenu(p.x, p.y);
259 public void beginDraggingFrame(JComponent f)
261 delegate.beginDraggingFrame(f);
265 public void beginResizingFrame(JComponent f, int direction)
267 delegate.beginResizingFrame(f, direction);
271 public void closeFrame(JInternalFrame f)
273 delegate.closeFrame(f);
277 public void deactivateFrame(JInternalFrame f)
279 delegate.deactivateFrame(f);
283 public void deiconifyFrame(JInternalFrame f)
285 delegate.deiconifyFrame(f);
289 public void dragFrame(JComponent f, int newX, int newY)
295 delegate.dragFrame(f, newX, newY);
299 public void endDraggingFrame(JComponent f)
301 delegate.endDraggingFrame(f);
302 desktopPane.repaint();
306 public void endResizingFrame(JComponent f)
308 delegate.endResizingFrame(f);
309 desktopPane.repaint();
313 public void iconifyFrame(JInternalFrame f)
315 delegate.iconifyFrame(f);
319 public void maximizeFrame(JInternalFrame f)
321 delegate.maximizeFrame(f);
325 public void minimizeFrame(JInternalFrame f)
327 delegate.minimizeFrame(f);
331 public void openFrame(JInternalFrame f)
333 delegate.openFrame(f);
337 public void resizeFrame(JComponent f, int newX, int newY, int newWidth,
344 delegate.resizeFrame(f, newX, newY, newWidth, newHeight);
348 public void setBoundsForFrame(JComponent f, int newX, int newY,
349 int newWidth, int newHeight)
351 delegate.setBoundsForFrame(f, newX, newY, newWidth, newHeight);
354 // All other methods, simply delegate
358 public MyDesktopPane desktopPane;
361 * Answers an 'application scope' singleton instance of this class. Separate
362 * SwingJS 'applets' running in the same browser page will each have a
363 * distinct instance of Desktop.
367 public static Desktop getInstance()
369 return Jalview.isHeadlessMode() ? null
370 : (Desktop) ApplicationSingletonProvider
371 .getInstance(Desktop.class);
379 public Desktop(boolean forInstance)
385 * Private constructor enforces singleton pattern. It is called by reflection
386 * from ApplicationSingletonProvider.getInstance().
388 @SuppressWarnings("unused")
394 * A note to implementors. It is ESSENTIAL that any activities that might
395 * block are spawned off as threads rather than waited for during this
398 if (!Platform.isJS())
400 doVamsasClientCheck();
403 doConfigureStructurePrefs();
404 setTitle("Jalview " + jalview.bin.Cache.getProperty("VERSION"));
405 setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
406 boolean selmemusage = jalview.bin.Cache.getDefault("SHOW_MEMUSAGE",
408 boolean showjconsole = jalview.bin.Cache.getDefault("SHOW_JAVA_CONSOLE",
410 desktopPane = new MyDesktopPane(selmemusage);
412 showMemusage.setSelected(selmemusage);
413 desktopPane.setBackground(Color.white);
414 getContentPane().setLayout(new BorderLayout());
415 // alternate config - have scrollbars - see notes in JAL-153
416 // JScrollPane sp = new JScrollPane();
417 // sp.getViewport().setView(desktop);
418 // getContentPane().add(sp, BorderLayout.CENTER);
420 // BH 2018 - just an experiment to try unclipped JInternalFrames.
423 getRootPane().putClientProperty("swingjs.overflow.hidden", "false");
426 getContentPane().add(desktopPane, BorderLayout.CENTER);
427 desktopPane.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
429 // This line prevents Windows Look&Feel resizing all new windows to maximum
430 // if previous window was maximised
431 desktopPane.setDesktopManager(new MyDesktopManager(
432 (Platform.isWindowsAndNotJS() ? new DefaultDesktopManager()
433 : Platform.isAMacAndNotJS()
434 ? new AquaInternalFrameManager(
435 desktopPane.getDesktopManager())
436 : desktopPane.getDesktopManager())));
438 Rectangle dims = getLastKnownDimensions("");
445 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
446 int xPos = Math.max(5, (screenSize.width - 900) / 2);
447 int yPos = Math.max(5, (screenSize.height - 650) / 2);
448 setBounds(xPos, yPos, 900, 650);
451 if (!Platform.isJS())
459 jconsole = new Console(this, showjconsole);
460 // add essential build information
461 jconsole.setHeader("Jalview Version: "
462 + jalview.bin.Cache.getProperty("VERSION") + "\n"
463 + "Jalview Installation: "
464 + jalview.bin.Cache.getDefault("INSTALLATION", "unknown")
465 + "\n" + "Build Date: "
466 + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown") + "\n"
467 + "Java version: " + System.getProperty("java.version") + "\n"
468 + System.getProperty("os.arch") + " "
469 + System.getProperty("os.name") + " "
470 + System.getProperty("os.version"));
472 showConsole(showjconsole);
474 showNews.setVisible(false);
476 experimentalFeatures.setSelected(showExperimental());
478 getIdentifiersOrgData();
482 // Spawn a thread that shows the splashscreen
484 SwingUtilities.invokeLater(new Runnable()
493 // Thread off a new instance of the file chooser - this reduces the time
495 // takes to open it later on.
496 new Thread(new Runnable()
501 Cache.log.debug("Filechooser init thread started.");
502 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
503 JalviewFileChooser.forRead(Cache.getProperty("LAST_DIRECTORY"),
505 Cache.log.debug("Filechooser init thread finished.");
508 // Add the service change listener
509 changeSupport.addJalviewPropertyChangeListener("services",
510 new PropertyChangeListener()
514 public void propertyChange(PropertyChangeEvent evt)
516 Cache.log.debug("Firing service changed event for "
517 + evt.getNewValue());
518 JalviewServicesChanged(evt);
525 this.setDropTarget(new java.awt.dnd.DropTarget(desktopPane, this));
527 this.addWindowListener(new WindowAdapter()
530 public void windowClosing(WindowEvent evt)
537 this.addMouseListener(ma = new MouseAdapter()
540 public void mousePressed(MouseEvent evt)
542 if (evt.isPopupTrigger()) // Mac
544 showPasteMenu(evt.getX(), evt.getY());
549 public void mouseReleased(MouseEvent evt)
551 if (evt.isPopupTrigger()) // Windows
553 showPasteMenu(evt.getX(), evt.getY());
557 desktopPane.addMouseListener(ma);
558 } 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, null);
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 JInternalFrame frame, String title, boolean makeVisible, int w,
891 int h, boolean resizable, boolean ignoreMinSize)
893 addInternalFrame(frame, title, makeVisible, w, h, resizable,
894 ignoreMinSize, null);
898 * Add an internal frame to the Jalview desktop
905 * When true, display frame immediately, otherwise, caller must call
906 * setVisible themselves.
913 * @param ignoreMinSize
914 * Do not set the default minimum size for frame
916 * for HTML div embedding
918 public static synchronized void addInternalFrame(
919 final JInternalFrame frame, String title, boolean makeVisible,
920 int w, int h, boolean resizable, boolean ignoreMinSize,
925 // TODO: allow callers to determine X and Y position of frame (eg. via
927 // TODO: consider fixing method to update entries in the window submenu with
928 // the current window title
932 frame.setName(Jalview.getAppID(name));
935 frame.setTitle(title);
936 if (frame.getWidth() < 1 || frame.getHeight() < 1)
940 // THIS IS A PUBLIC STATIC METHOD, SO IT MAY BE CALLED EVEN IN
941 // A HEADLESS STATE WHEN NO DESKTOP EXISTS. MUST RETURN
942 // IF JALVIEW IS RUNNING HEADLESS
943 // ///////////////////////////////////////////////
944 if (Desktop.getInstance().instanceOnly || Jalview.isHeadlessMode())
953 frame.setMinimumSize(
954 new Dimension(DEFAULT_MIN_WIDTH, DEFAULT_MIN_HEIGHT));
956 // Set default dimension for Alignment Frame window.
957 // The Alignment Frame window could be added from a number of places,
959 // I did this here in order not to miss out on any Alignment frame.
960 if (frame instanceof AlignFrame)
962 frame.setMinimumSize(new Dimension(ALIGN_FRAME_DEFAULT_MIN_WIDTH,
963 ALIGN_FRAME_DEFAULT_MIN_HEIGHT));
967 frame.setVisible(makeVisible);
968 frame.setClosable(true);
969 frame.setResizable(resizable);
970 frame.setMaximizable(resizable);
971 frame.setIconifiable(resizable);
972 frame.setOpaque(Platform.isJS());
974 if (frame.getX() < 1 && frame.getY() < 1)
976 frame.setLocation(xOffset * openFrameCount,
977 yOffset * ((openFrameCount - 1) % 10) + yOffset);
981 * add an entry for the new frame in the Window menu
982 * (and remove it when the frame is closed)
984 JMenuItem menuItem = new JMenuItem(title);
985 frame.addInternalFrameListener(new InternalFrameAdapter()
988 public void internalFrameActivated(InternalFrameEvent evt)
990 JInternalFrame itf = getDesktopPane().getSelectedFrame();
993 if (itf instanceof AlignFrame)
995 Jalview.setCurrentAlignFrame((AlignFrame) itf);
1002 public void internalFrameClosed(InternalFrameEvent evt)
1004 PaintRefresher.RemoveComponent(frame);
1007 * defensive check to prevent frames being
1008 * added half off the window
1010 if (openFrameCount > 0)
1016 * ensure no reference to alignFrame retained by menu item listener
1018 if (menuItem.getActionListeners().length > 0)
1020 menuItem.removeActionListener(menuItem.getActionListeners()[0]);
1022 Desktop.getInstance().windowMenu.remove(menuItem);
1026 menuItem.addActionListener(new ActionListener()
1029 public void actionPerformed(ActionEvent e)
1033 frame.setSelected(true);
1034 frame.setIcon(false);
1035 } catch (java.beans.PropertyVetoException ex)
1037 // System.err.println(ex.toString());
1042 setKeyBindings(frame);
1044 getDesktopPane().add(frame);
1046 Desktop.getInstance().windowMenu.add(menuItem);
1051 frame.setSelected(true);
1052 frame.requestFocus();
1053 } catch (java.beans.PropertyVetoException ve)
1055 } catch (java.lang.ClassCastException cex)
1058 "Squashed a possible GUI implementation error. If you can recreate this, please look at http://issues.jalview.org/browse/JAL-869",
1064 * Add key bindings to a JInternalFrame so that Ctrl-W and Cmd-W will close the
1069 private static void setKeyBindings(JInternalFrame frame)
1071 final Action closeAction = new AbstractAction()
1074 public void actionPerformed(ActionEvent e)
1081 * set up key bindings for Ctrl-W and Cmd-W, with the same (Close) action
1083 KeyStroke ctrlWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
1084 InputEvent.CTRL_DOWN_MASK);
1085 KeyStroke cmdWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
1086 Toolkit.getDefaultToolkit().getMenuShortcutKeyMask());
1088 InputMap inputMap = frame
1089 .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
1090 String ctrlW = ctrlWKey.toString();
1091 inputMap.put(ctrlWKey, ctrlW);
1092 inputMap.put(cmdWKey, ctrlW);
1094 ActionMap actionMap = frame.getActionMap();
1095 actionMap.put(ctrlW, closeAction);
1099 public void lostOwnership(Clipboard clipboard, Transferable contents)
1103 Desktop.getInstance().jalviewClipboard = null;
1106 internalCopy = false;
1110 public void dragEnter(DropTargetDragEvent evt)
1115 public void dragExit(DropTargetEvent evt)
1120 public void dragOver(DropTargetDragEvent evt)
1125 public void dropActionChanged(DropTargetDragEvent evt)
1136 public void drop(DropTargetDropEvent evt)
1138 boolean success = true;
1139 // JAL-1552 - acceptDrop required before getTransferable call for
1140 // Java's Transferable for native dnd
1141 evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
1142 Transferable t = evt.getTransferable();
1143 List<Object> files = new ArrayList<>();
1144 List<DataSourceType> protocols = new ArrayList<>();
1148 Desktop.transferFromDropTarget(files, protocols, evt, t);
1149 } catch (Exception e)
1151 e.printStackTrace();
1159 for (int i = 0; i < files.size(); i++)
1161 // BH 2018 File or String
1162 Object file = files.get(i);
1163 String fileName = file.toString();
1164 DataSourceType protocol = (protocols == null)
1165 ? DataSourceType.FILE
1167 FileFormatI format = null;
1169 if (fileName.endsWith(".jar"))
1171 format = FileFormat.Jalview;
1176 format = new IdentifyFile().identify(file, protocol);
1178 if (file instanceof File)
1180 Platform.cacheFileData((File) file);
1182 new FileLoader().loadFile(null, file, protocol, format);
1185 } catch (Exception ex)
1190 evt.dropComplete(success); // need this to ensure input focus is properly
1191 // transfered to any new windows created
1201 public void inputLocalFileMenuItem_actionPerformed(AlignViewport viewport)
1203 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
1204 JalviewFileChooser chooser = JalviewFileChooser
1205 .forRead(Cache.getProperty("LAST_DIRECTORY"), fileFormat, true);
1207 chooser.setFileView(new JalviewFileView());
1208 chooser.setDialogTitle(
1209 MessageManager.getString("label.open_local_file"));
1210 chooser.setToolTipText(MessageManager.getString("action.open"));
1212 chooser.setResponseHandler(0, new Runnable()
1217 File selectedFile = chooser.getSelectedFile();
1218 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1220 FileFormatI format = chooser.getSelectedFormat();
1223 * Call IdentifyFile to verify the file contains what its extension implies.
1224 * Skip this step for dynamically added file formats, because
1225 * IdentifyFile does not know how to recognise them.
1227 if (FileFormats.getInstance().isIdentifiable(format))
1231 format = new IdentifyFile().identify(selectedFile,
1232 DataSourceType.FILE);
1233 } catch (FileFormatException e)
1235 // format = null; //??
1239 new FileLoader().loadFile(viewport, selectedFile,
1240 DataSourceType.FILE, format);
1243 chooser.showOpenDialog(this);
1247 * Shows a dialog for input of a URL at which to retrieve alignment data
1252 public void inputURLMenuItem_actionPerformed(AlignViewport viewport)
1254 // This construct allows us to have a wider textfield
1256 JLabel label = new JLabel(
1257 MessageManager.getString("label.input_file_url"));
1259 JPanel panel = new JPanel(new GridLayout(2, 1));
1263 * the URL to fetch is
1264 * Java: an editable combobox with history
1265 * JS: (pending JAL-3038) a plain text field
1268 String urlBase = "http://www.";
1269 if (Platform.isJS())
1271 history = new JTextField(urlBase, 35);
1280 JComboBox<String> asCombo = new JComboBox<>();
1281 asCombo.setPreferredSize(new Dimension(400, 20));
1282 asCombo.setEditable(true);
1283 asCombo.addItem(urlBase);
1284 String historyItems = Cache.getProperty("RECENT_URL");
1285 if (historyItems != null)
1287 for (String token : historyItems.split("\\t"))
1289 asCombo.addItem(token);
1296 Object[] options = new Object[] { MessageManager.getString("action.ok"),
1297 MessageManager.getString("action.cancel") };
1298 Runnable action = new Runnable()
1303 @SuppressWarnings("unchecked")
1304 String url = (history instanceof JTextField
1305 ? ((JTextField) history).getText()
1306 : ((JComboBox<String>) history).getSelectedItem()
1309 if (url.toLowerCase().endsWith(".jar"))
1311 if (viewport != null)
1313 new FileLoader().loadFile(viewport, url, DataSourceType.URL,
1314 FileFormat.Jalview);
1318 new FileLoader().loadFile(url, DataSourceType.URL,
1319 FileFormat.Jalview);
1324 FileFormatI format = null;
1327 format = new IdentifyFile().identify(url, DataSourceType.URL);
1328 } catch (FileFormatException e)
1330 // TODO revise error handling, distinguish between
1331 // URL not found and response not valid
1336 String msg = MessageManager
1337 .formatMessage("label.couldnt_locate", url);
1338 JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
1340 MessageManager.getString("label.url_not_found"),
1341 JvOptionPane.WARNING_MESSAGE);
1346 if (viewport != null)
1348 new FileLoader().loadFile(viewport, url, DataSourceType.URL,
1353 new FileLoader().loadFile(url, DataSourceType.URL, format);
1358 String dialogOption = MessageManager
1359 .getString("label.input_alignment_from_url");
1360 JvOptionPane.newOptionDialog(getDesktopPane())
1361 .setResponseHandler(0, action)
1362 .showInternalDialog(panel, dialogOption,
1363 JvOptionPane.YES_NO_CANCEL_OPTION,
1364 JvOptionPane.PLAIN_MESSAGE, null, options,
1365 MessageManager.getString("action.ok"));
1369 * Opens the CutAndPaste window for the user to paste an alignment in to
1372 * - if not null, the pasted alignment is added to the current
1373 * alignment; if null, to a new alignment window
1376 public void inputTextboxMenuItem_actionPerformed(
1377 AlignmentViewPanel viewPanel)
1379 CutAndPasteTransfer cap = new CutAndPasteTransfer();
1380 cap.setForInput(viewPanel);
1381 Desktop.addInternalFrame(cap,
1382 MessageManager.getString("label.cut_paste_alignmen_file"), true,
1392 Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
1393 jalview.bin.Cache.setProperty("SCREENGEOMETRY_WIDTH",
1395 jalview.bin.Cache.setProperty("SCREENGEOMETRY_HEIGHT",
1396 screen.height + "");
1397 storeLastKnownDimensions("", new Rectangle(getBounds().x, getBounds().y,
1398 getWidth(), getHeight()));
1400 if (jconsole != null)
1402 storeLastKnownDimensions("JAVA_CONSOLE_", jconsole.getBounds());
1403 jconsole.stopConsole();
1407 storeLastKnownDimensions("JALVIEW_RSS_WINDOW_", jvnews.getBounds());
1410 if (dialogExecutor != null)
1412 dialogExecutor.shutdownNow();
1414 closeAll_actionPerformed(null);
1416 if (groovyConsole != null)
1418 // suppress a possible repeat prompt to save script
1419 groovyConsole.setDirty(false);
1420 groovyConsole.exit();
1425 private void storeLastKnownDimensions(String string, Rectangle jc)
1427 jalview.bin.Cache.log.debug("Storing last known dimensions for "
1428 + string + ": x:" + jc.x + " y:" + jc.y + " width:" + jc.width
1429 + " height:" + jc.height);
1431 jalview.bin.Cache.setProperty(string + "SCREEN_X", jc.x + "");
1432 jalview.bin.Cache.setProperty(string + "SCREEN_Y", jc.y + "");
1433 jalview.bin.Cache.setProperty(string + "SCREEN_WIDTH", jc.width + "");
1434 jalview.bin.Cache.setProperty(string + "SCREEN_HEIGHT", jc.height + "");
1444 public void aboutMenuItem_actionPerformed(ActionEvent e)
1446 // StringBuffer message = getAboutMessage(false);
1447 // JvOptionPane.showInternalMessageDialog(Desktop.getDesktop(),
1449 // message.toString(), "About Jalview", JvOptionPane.INFORMATION_MESSAGE);
1450 new Thread(new Runnable()
1455 new SplashScreen(true);
1460 public StringBuffer getAboutMessage(boolean shortv)
1462 StringBuffer message = new StringBuffer();
1463 message.append("<html>");
1466 message.append("<h1><strong>Version: "
1467 + jalview.bin.Cache.getProperty("VERSION")
1468 + "</strong></h1>");
1469 message.append("<strong>Last Updated: <em>"
1470 + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown")
1471 + "</em></strong>");
1477 message.append("<strong>Version "
1478 + jalview.bin.Cache.getProperty("VERSION")
1479 + "; last updated: "
1480 + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown"));
1483 if (jalview.bin.Cache.getDefault("LATEST_VERSION", "Checking")
1484 .equals("Checking"))
1486 message.append("<br>...Checking latest version...</br>");
1488 else if (!jalview.bin.Cache.getDefault("LATEST_VERSION", "Checking")
1489 .equals(jalview.bin.Cache.getProperty("VERSION")))
1491 boolean red = false;
1492 if (jalview.bin.Cache.getProperty("VERSION").toLowerCase()
1493 .indexOf("automated build") == -1)
1496 // Displayed when code version and jnlp version do not match and code
1497 // version is not a development build
1498 message.append("<div style=\"color: #FF0000;font-style: bold;\">");
1501 message.append("<br>!! Version "
1502 + jalview.bin.Cache.getDefault("LATEST_VERSION",
1504 + " is available for download from "
1505 + jalview.bin.Cache.getDefault("www.jalview.org",
1506 "http://www.jalview.org")
1510 message.append("</div>");
1513 message.append("<br>Authors: " + jalview.bin.Cache.getDefault(
1515 "The Jalview Authors (See AUTHORS file for current list)")
1516 + "<br><br>Development managed by The Barton Group, University of Dundee, Scotland, UK.<br>"
1517 + "<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"
1518 + "<br><br>If you use Jalview, please cite:"
1519 + "<br>Waterhouse, A.M., Procter, J.B., Martin, D.M.A, Clamp, M. and Barton, G. J. (2009)"
1520 + "<br>Jalview Version 2 - a multiple sequence alignment editor and analysis workbench"
1521 + "<br>Bioinformatics doi: 10.1093/bioinformatics/btp033"
1527 * Action on requesting Help documentation
1530 public void documentationMenuItem_actionPerformed()
1534 if (Platform.isJS())
1536 BrowserLauncher.openURL("http://www.jalview.org/help.html");
1545 Help.showHelpWindow();
1547 } catch (Exception ex)
1549 System.err.println("Error opening help: " + ex.getMessage());
1554 public void closeAll_actionPerformed(ActionEvent e)
1556 if (desktopPane == null)
1560 // TODO show a progress bar while closing?
1561 JInternalFrame[] frames = desktopPane.getAllFrames();
1562 for (int i = 0; i < frames.length; i++)
1566 frames[i].setClosed(true);
1567 } catch (java.beans.PropertyVetoException ex)
1571 Jalview.setCurrentAlignFrame(null);
1572 System.out.println("ALL CLOSED");
1573 if (v_client != null)
1575 // TODO clear binding to vamsas document objects on close_all
1579 * reset state of singleton objects as appropriate (clear down session state
1580 * when all windows are closed)
1582 getStructureSelectionManager().resetAll();
1586 public void raiseRelated_actionPerformed(ActionEvent e)
1588 reorderAssociatedWindows(false, false);
1592 public void minimizeAssociated_actionPerformed(ActionEvent e)
1594 reorderAssociatedWindows(true, false);
1597 void closeAssociatedWindows()
1599 reorderAssociatedWindows(false, true);
1605 * @seejalview.jbgui.GDesktop#garbageCollect_actionPerformed(java.awt.event.
1609 protected void garbageCollect_actionPerformed(ActionEvent e)
1611 // We simply collect the garbage
1612 jalview.bin.Cache.log.debug("Collecting garbage...");
1614 jalview.bin.Cache.log.debug("Finished garbage collection.");
1621 * jalview.jbgui.GDesktop#showMemusage_actionPerformed(java.awt.event.ActionEvent
1625 protected void showMemusage_actionPerformed(ActionEvent e)
1627 getDesktopPane().showMemoryUsage(showMemusage.isSelected());
1634 * jalview.jbgui.GDesktop#showConsole_actionPerformed(java.awt.event.ActionEvent
1638 protected void showConsole_actionPerformed(ActionEvent e)
1640 showConsole(showConsole.isSelected());
1643 Console jconsole = null;
1646 * control whether the java console is visible or not
1650 void showConsole(boolean selected)
1652 // TODO: decide if we should update properties file
1653 if (jconsole != null) // BH 2018
1655 showConsole.setSelected(selected);
1656 Cache.setProperty("SHOW_JAVA_CONSOLE",
1657 Boolean.valueOf(selected).toString());
1658 jconsole.setVisible(selected);
1662 void reorderAssociatedWindows(boolean minimize, boolean close)
1664 JInternalFrame[] frames = getDesktopPane().getAllFrames();
1665 if (frames == null || frames.length < 1)
1670 AlignmentViewport source = null, target = null;
1671 if (frames[0] instanceof AlignFrame)
1673 source = ((AlignFrame) frames[0]).getCurrentView();
1675 else if (frames[0] instanceof TreePanel)
1677 source = ((TreePanel) frames[0]).getViewPort();
1679 else if (frames[0] instanceof PCAPanel)
1681 source = ((PCAPanel) frames[0]).av;
1683 else if (frames[0].getContentPane() instanceof PairwiseAlignPanel)
1685 source = ((PairwiseAlignPanel) frames[0].getContentPane()).av;
1690 for (int i = 0; i < frames.length; i++)
1693 if (frames[i] == null)
1697 if (frames[i] instanceof AlignFrame)
1699 target = ((AlignFrame) frames[i]).getCurrentView();
1701 else if (frames[i] instanceof TreePanel)
1703 target = ((TreePanel) frames[i]).getViewPort();
1705 else if (frames[i] instanceof PCAPanel)
1707 target = ((PCAPanel) frames[i]).av;
1709 else if (frames[i].getContentPane() instanceof PairwiseAlignPanel)
1711 target = ((PairwiseAlignPanel) frames[i].getContentPane()).av;
1714 if (source == target)
1720 frames[i].setClosed(true);
1724 frames[i].setIcon(minimize);
1727 frames[i].toFront();
1731 } catch (java.beans.PropertyVetoException ex)
1746 protected void preferences_actionPerformed(ActionEvent e)
1752 * Prompts the user to choose a file and then saves the Jalview state as a
1753 * Jalview project file
1756 public void saveState_actionPerformed()
1758 saveState_actionPerformed(false);
1761 public void saveState_actionPerformed(boolean saveAs)
1763 java.io.File projectFile = getProjectFile();
1764 // autoSave indicates we already have a file and don't need to ask
1765 boolean autoSave = projectFile != null && !saveAs
1766 && BackupFiles.getEnabled();
1768 // System.out.println("autoSave="+autoSave+", projectFile='"+projectFile+"',
1769 // saveAs="+saveAs+", Backups
1770 // "+(BackupFiles.getEnabled()?"enabled":"disabled"));
1772 boolean approveSave = false;
1775 JalviewFileChooser chooser = new JalviewFileChooser("jvp",
1778 chooser.setFileView(new JalviewFileView());
1779 chooser.setDialogTitle(MessageManager.getString("label.save_state"));
1781 int value = chooser.showSaveDialog(this);
1783 if (value == JalviewFileChooser.APPROVE_OPTION)
1785 projectFile = chooser.getSelectedFile();
1786 setProjectFile(projectFile);
1791 if (approveSave || autoSave)
1793 final Desktop me = this;
1794 final java.io.File chosenFile = projectFile;
1795 new Thread(new Runnable()
1800 // TODO: refactor to Jalview desktop session controller action.
1801 setProgressBar(MessageManager.formatMessage(
1802 "label.saving_jalview_project", new Object[]
1803 { chosenFile.getName() }), chosenFile.hashCode());
1804 jalview.bin.Cache.setProperty("LAST_DIRECTORY",
1805 chosenFile.getParent());
1806 // TODO catch and handle errors for savestate
1807 // TODO prevent user from messing with the Desktop whilst we're saving
1810 boolean doBackup = BackupFiles.getEnabled();
1811 BackupFiles backupfiles = doBackup ? new BackupFiles(chosenFile) : null;
1813 new Jalview2XML().saveState(doBackup ? backupfiles.getTempFile() : chosenFile);
1817 backupfiles.setWriteSuccess(true);
1818 backupfiles.rollBackupsAndRenameTempFile();
1820 } catch (OutOfMemoryError oom)
1822 new OOMWarning("Whilst saving current state to "
1823 + chosenFile.getName(), oom);
1824 } catch (Exception ex)
1826 Cache.log.error("Problems whilst trying to save to "
1827 + chosenFile.getName(), ex);
1828 JvOptionPane.showMessageDialog(me,
1829 MessageManager.formatMessage(
1830 "label.error_whilst_saving_current_state_to",
1832 { chosenFile.getName() }),
1833 MessageManager.getString("label.couldnt_save_project"),
1834 JvOptionPane.WARNING_MESSAGE);
1836 setProgressBar(null, chosenFile.hashCode());
1843 public void saveAsState_actionPerformed(ActionEvent e)
1845 saveState_actionPerformed(true);
1848 protected void setProjectFile(File choice)
1850 this.projectFile = choice;
1853 public File getProjectFile()
1855 return this.projectFile;
1859 * Shows a file chooser dialog and tries to read in the selected file as a
1863 public void loadState_actionPerformed()
1865 final String[] suffix = new String[] { "jvp", "jar" };
1866 final String[] desc = new String[] { "Jalview Project",
1867 "Jalview Project (old)" };
1868 JalviewFileChooser chooser = new JalviewFileChooser(
1869 Cache.getProperty("LAST_DIRECTORY"), suffix, desc,
1870 "Jalview Project", true, true); // last two booleans: allFiles,
1872 chooser.setFileView(new JalviewFileView());
1873 chooser.setDialogTitle(MessageManager.getString("label.restore_state"));
1874 chooser.setResponseHandler(0, new Runnable()
1879 File selectedFile = chooser.getSelectedFile();
1880 setProjectFile(selectedFile);
1881 String choice = selectedFile.getAbsolutePath();
1882 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1883 new Thread(new Runnable()
1890 new Jalview2XML().loadJalviewAlign(choice);
1891 } catch (OutOfMemoryError oom)
1893 new OOMWarning("Whilst loading project from " + choice, oom);
1894 } catch (Exception ex)
1897 "Problems whilst loading project from " + choice, ex);
1898 JvOptionPane.showMessageDialog(Desktop.getDesktopPane(),
1899 MessageManager.formatMessage(
1900 "label.error_whilst_loading_project_from",
1903 MessageManager.getString("label.couldnt_load_project"),
1904 JvOptionPane.WARNING_MESSAGE);
1911 chooser.showOpenDialog(this);
1915 public void inputSequence_actionPerformed(ActionEvent e)
1917 new SequenceFetcher(this);
1920 JPanel progressPanel;
1922 ArrayList<JPanel> fileLoadingPanels = new ArrayList<>();
1924 public void startLoading(final Object fileName)
1926 if (fileLoadingCount == 0)
1928 fileLoadingPanels.add(addProgressPanel(MessageManager
1929 .formatMessage("label.loading_file", new Object[]
1935 private JPanel addProgressPanel(String string)
1937 if (progressPanel == null)
1939 progressPanel = new JPanel(new GridLayout(1, 1));
1940 totalProgressCount = 0;
1941 getContentPane().add(progressPanel, BorderLayout.SOUTH);
1943 JPanel thisprogress = new JPanel(new BorderLayout(10, 5));
1944 JProgressBar progressBar = new JProgressBar();
1945 progressBar.setIndeterminate(true);
1947 thisprogress.add(new JLabel(string), BorderLayout.WEST);
1949 thisprogress.add(progressBar, BorderLayout.CENTER);
1950 progressPanel.add(thisprogress);
1951 ((GridLayout) progressPanel.getLayout()).setRows(
1952 ((GridLayout) progressPanel.getLayout()).getRows() + 1);
1953 ++totalProgressCount;
1955 return thisprogress;
1958 int totalProgressCount = 0;
1960 private void removeProgressPanel(JPanel progbar)
1962 if (progressPanel != null)
1964 synchronized (progressPanel)
1966 progressPanel.remove(progbar);
1967 GridLayout gl = (GridLayout) progressPanel.getLayout();
1968 gl.setRows(gl.getRows() - 1);
1969 if (--totalProgressCount < 1)
1971 this.getContentPane().remove(progressPanel);
1972 progressPanel = null;
1979 public void stopLoading()
1982 if (fileLoadingCount < 1)
1984 while (fileLoadingPanels.size() > 0)
1986 removeProgressPanel(fileLoadingPanels.remove(0));
1988 fileLoadingPanels.clear();
1989 fileLoadingCount = 0;
1994 public static int getViewCount(String alignmentId)
1996 AlignmentViewport[] aps = getViewports(alignmentId);
1997 return (aps == null) ? 0 : aps.length;
2002 * @param alignmentId
2003 * - if null, all sets are returned
2004 * @return all AlignmentPanels concerning the alignmentId sequence set
2006 public static AlignmentPanel[] getAlignmentPanels(String alignmentId)
2008 if (Desktop.getDesktopPane() == null)
2010 // no frames created and in headless mode
2011 // TODO: verify that frames are recoverable when in headless mode
2014 List<AlignmentPanel> aps = new ArrayList<>();
2015 AlignFrame[] frames = getAlignFrames();
2020 for (AlignFrame af : frames)
2022 for (AlignmentPanel ap : af.alignPanels)
2024 if (alignmentId == null
2025 || alignmentId.equals(ap.av.getSequenceSetId()))
2031 if (aps.size() == 0)
2035 AlignmentPanel[] vap = aps.toArray(new AlignmentPanel[aps.size()]);
2040 * get all the viewports on an alignment.
2042 * @param sequenceSetId
2043 * unique alignment id (may be null - all viewports returned in that
2045 * @return all viewports on the alignment bound to sequenceSetId
2047 public static AlignmentViewport[] getViewports(String sequenceSetId)
2049 List<AlignmentViewport> viewp = new ArrayList<>();
2050 if (getDesktopPane() != null)
2052 AlignFrame[] frames = Desktop.getAlignFrames();
2054 for (AlignFrame afr : frames)
2056 if (sequenceSetId == null || afr.getViewport().getSequenceSetId()
2057 .equals(sequenceSetId))
2059 if (afr.alignPanels != null)
2061 for (AlignmentPanel ap : afr.alignPanels)
2063 if (sequenceSetId == null
2064 || sequenceSetId.equals(ap.av.getSequenceSetId()))
2072 viewp.add(afr.getViewport());
2076 if (viewp.size() > 0)
2078 return viewp.toArray(new AlignmentViewport[viewp.size()]);
2085 * Explode the views in the given frame into separate AlignFrame
2089 public static void explodeViews(AlignFrame af)
2091 int size = af.alignPanels.size();
2097 for (int i = 0; i < size; i++)
2099 AlignmentPanel ap = af.alignPanels.get(i);
2100 AlignFrame newaf = new AlignFrame(ap);
2103 * Restore the view's last exploded frame geometry if known. Multiple
2104 * views from one exploded frame share and restore the same (frame)
2105 * position and size.
2107 Rectangle geometry = ap.av.getExplodedGeometry();
2108 if (geometry != null)
2110 newaf.setBounds(geometry);
2113 ap.av.setGatherViewsHere(false);
2115 addInternalFrame(newaf, af.getTitle(), AlignFrame.DEFAULT_WIDTH,
2116 AlignFrame.DEFAULT_HEIGHT);
2119 af.alignPanels.clear();
2120 af.closeMenuItem_actionPerformed(true);
2125 * Gather expanded views (separate AlignFrame's) with the same sequence set
2126 * identifier back in to this frame as additional views, and close the expanded
2127 * views. Note the expanded frames may themselves have multiple views. We take
2132 public void gatherViews(AlignFrame source)
2134 source.viewport.setGatherViewsHere(true);
2135 source.viewport.setExplodedGeometry(source.getBounds());
2136 JInternalFrame[] frames = getAllFrames();
2137 String viewId = source.viewport.getSequenceSetId();
2139 for (int t = 0; t < frames.length; t++)
2141 if (frames[t] instanceof AlignFrame && frames[t] != source)
2143 AlignFrame af = (AlignFrame) frames[t];
2144 boolean gatherThis = false;
2145 for (int a = 0; a < af.alignPanels.size(); a++)
2147 AlignmentPanel ap = af.alignPanels.get(a);
2148 if (viewId.equals(ap.av.getSequenceSetId()))
2151 ap.av.setGatherViewsHere(false);
2152 ap.av.setExplodedGeometry(af.getBounds());
2153 source.addAlignmentPanel(ap, false);
2159 af.alignPanels.clear();
2160 af.closeMenuItem_actionPerformed(true);
2167 jalview.gui.VamsasApplication v_client = null;
2170 public void vamsasImport_actionPerformed(ActionEvent e)
2172 // TODO: JAL-3048 not needed for Jalview-JS
2174 if (v_client == null)
2176 // Load and try to start a session.
2177 JalviewFileChooser chooser = new JalviewFileChooser(
2178 jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
2180 chooser.setFileView(new JalviewFileView());
2181 chooser.setDialogTitle(
2182 MessageManager.getString("label.open_saved_vamsas_session"));
2183 chooser.setToolTipText(MessageManager.getString(
2184 "label.select_vamsas_session_opened_as_new_vamsas_session"));
2186 int value = chooser.showOpenDialog(this);
2188 if (value == JalviewFileChooser.APPROVE_OPTION)
2190 String fle = chooser.getSelectedFile().toString();
2191 if (!vamsasImport(chooser.getSelectedFile()))
2193 JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
2194 MessageManager.formatMessage(
2195 "label.couldnt_import_as_vamsas_session",
2199 .getString("label.vamsas_document_import_failed"),
2200 JvOptionPane.ERROR_MESSAGE);
2206 jalview.bin.Cache.log.error(
2207 "Implementation error - load session from a running session is not supported.");
2212 * import file into a new vamsas session (uses jalview.gui.VamsasApplication)
2215 * @return true if import was a success and a session was started.
2217 public boolean vamsasImport(URL url)
2219 // TODO: create progress bar
2220 if (v_client != null)
2223 jalview.bin.Cache.log.error(
2224 "Implementation error - load session from a running session is not supported.");
2230 // copy the URL content to a temporary local file
2231 // TODO: be a bit cleverer here with nio (?!)
2232 File file = File.createTempFile("vdocfromurl", ".vdj");
2233 FileOutputStream fos = new FileOutputStream(file);
2234 BufferedInputStream bis = new BufferedInputStream(url.openStream());
2235 byte[] buffer = new byte[2048];
2237 while ((ln = bis.read(buffer)) > -1)
2239 fos.write(buffer, 0, ln);
2243 v_client = new jalview.gui.VamsasApplication(this, file,
2244 url.toExternalForm());
2245 } catch (Exception ex)
2247 jalview.bin.Cache.log.error(
2248 "Failed to create new vamsas session from contents of URL "
2253 setupVamsasConnectedGui();
2254 v_client.initial_update(); // TODO: thread ?
2255 return v_client.inSession();
2259 * import file into a new vamsas session (uses jalview.gui.VamsasApplication)
2262 * @return true if import was a success and a session was started.
2264 public boolean vamsasImport(File file)
2266 if (v_client != null)
2269 jalview.bin.Cache.log.error(
2270 "Implementation error - load session from a running session is not supported.");
2274 setProgressBar(MessageManager.formatMessage(
2275 "status.importing_vamsas_session_from", new Object[]
2276 { file.getName() }), file.hashCode());
2279 v_client = new jalview.gui.VamsasApplication(this, file, null);
2280 } catch (Exception ex)
2282 setProgressBar(MessageManager.formatMessage(
2283 "status.importing_vamsas_session_from", new Object[]
2284 { file.getName() }), file.hashCode());
2285 jalview.bin.Cache.log.error(
2286 "New vamsas session from existing session file failed:", ex);
2289 setupVamsasConnectedGui();
2290 v_client.initial_update(); // TODO: thread ?
2291 setProgressBar(MessageManager.formatMessage(
2292 "status.importing_vamsas_session_from", new Object[]
2293 { file.getName() }), file.hashCode());
2294 return v_client.inSession();
2297 public boolean joinVamsasSession(String mysesid)
2299 if (v_client != null)
2301 throw new Error(MessageManager
2302 .getString("error.try_join_vamsas_session_another"));
2304 if (mysesid == null)
2307 MessageManager.getString("error.invalid_vamsas_session_id"));
2309 v_client = new VamsasApplication(this, mysesid);
2310 setupVamsasConnectedGui();
2311 v_client.initial_update();
2312 return (v_client.inSession());
2316 public void vamsasStart_actionPerformed(ActionEvent e)
2318 if (v_client == null)
2321 // we just start a default session for moment.
2323 * JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache.
2324 * getProperty("LAST_DIRECTORY"));
2326 * chooser.setFileView(new JalviewFileView());
2327 * chooser.setDialogTitle("Load Vamsas file");
2328 * chooser.setToolTipText("Import");
2330 * int value = chooser.showOpenDialog(this);
2332 * if (value == JalviewFileChooser.APPROVE_OPTION) { v_client = new
2333 * jalview.gui.VamsasApplication(this, chooser.getSelectedFile());
2335 v_client = new VamsasApplication(this);
2336 setupVamsasConnectedGui();
2337 v_client.initial_update(); // TODO: thread ?
2341 // store current data in session.
2342 v_client.push_update(); // TODO: thread
2346 protected void setupVamsasConnectedGui()
2348 vamsasStart.setText(MessageManager.getString("label.session_update"));
2349 vamsasSave.setVisible(true);
2350 vamsasStop.setVisible(true);
2351 vamsasImport.setVisible(false); // Document import to existing session is
2352 // not possible for vamsas-client-1.0.
2355 protected void setupVamsasDisconnectedGui()
2357 vamsasSave.setVisible(false);
2358 vamsasStop.setVisible(false);
2359 vamsasImport.setVisible(true);
2361 .setText(MessageManager.getString("label.new_vamsas_session"));
2365 public void vamsasStop_actionPerformed(ActionEvent e)
2367 if (v_client != null)
2369 v_client.end_session();
2371 setupVamsasDisconnectedGui();
2375 protected void buildVamsasStMenu()
2377 if (v_client == null)
2379 String[] sess = null;
2382 sess = VamsasApplication.getSessionList();
2383 } catch (Exception e)
2385 jalview.bin.Cache.log.warn("Problem getting current sessions list.",
2391 jalview.bin.Cache.log.debug(
2392 "Got current sessions list: " + sess.length + " entries.");
2393 VamsasStMenu.removeAll();
2394 for (int i = 0; i < sess.length; i++)
2396 JMenuItem sessit = new JMenuItem();
2397 sessit.setText(sess[i]);
2398 sessit.setToolTipText(MessageManager
2399 .formatMessage("label.connect_to_session", new Object[]
2401 final Desktop dsktp = this;
2402 final String mysesid = sess[i];
2403 sessit.addActionListener(new ActionListener()
2407 public void actionPerformed(ActionEvent e)
2409 if (dsktp.v_client == null)
2411 Thread rthr = new Thread(new Runnable()
2417 dsktp.v_client = new VamsasApplication(dsktp, mysesid);
2418 dsktp.setupVamsasConnectedGui();
2419 dsktp.v_client.initial_update();
2427 VamsasStMenu.add(sessit);
2429 // don't show an empty menu.
2430 VamsasStMenu.setVisible(sess.length > 0);
2435 jalview.bin.Cache.log.debug("No current vamsas sessions.");
2436 VamsasStMenu.removeAll();
2437 VamsasStMenu.setVisible(false);
2442 // Not interested in the content. Just hide ourselves.
2443 VamsasStMenu.setVisible(false);
2448 public void vamsasSave_actionPerformed(ActionEvent e)
2450 // TODO: JAL-3048 not needed for Jalview-JS
2452 if (v_client != null)
2454 // TODO: VAMSAS DOCUMENT EXTENSION is VDJ
2455 JalviewFileChooser chooser = new JalviewFileChooser("vdj",
2458 chooser.setFileView(new JalviewFileView());
2459 chooser.setDialogTitle(MessageManager
2460 .getString("label.save_vamsas_document_archive"));
2462 int value = chooser.showSaveDialog(this);
2464 if (value == JalviewFileChooser.APPROVE_OPTION)
2466 java.io.File choice = chooser.getSelectedFile();
2467 JPanel progpanel = addProgressPanel(MessageManager
2468 .formatMessage("label.saving_vamsas_doc", new Object[]
2469 { choice.getName() }));
2470 Cache.setProperty("LAST_DIRECTORY", choice.getParent());
2471 String warnmsg = null;
2472 String warnttl = null;
2475 v_client.vclient.storeDocument(choice);
2478 warnttl = "Serious Problem saving Vamsas Document";
2479 warnmsg = ex.toString();
2480 jalview.bin.Cache.log
2481 .error("Error Whilst saving document to " + choice, ex);
2483 } catch (Exception ex)
2485 warnttl = "Problem saving Vamsas Document.";
2486 warnmsg = ex.toString();
2487 jalview.bin.Cache.log.warn(
2488 "Exception Whilst saving document to " + choice, ex);
2491 removeProgressPanel(progpanel);
2492 if (warnmsg != null)
2494 JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
2496 warnmsg, warnttl, JvOptionPane.ERROR_MESSAGE);
2502 JPanel vamUpdate = null;
2505 * hide vamsas user gui bits when a vamsas document event is being handled.
2508 * true to hide gui, false to reveal gui
2510 public void setVamsasUpdate(boolean b)
2512 Cache.log.debug("Setting gui for Vamsas update "
2513 + (b ? "in progress" : "finished"));
2515 if (vamUpdate != null)
2517 this.removeProgressPanel(vamUpdate);
2521 vamUpdate = this.addProgressPanel(
2522 MessageManager.getString("label.updating_vamsas_session"));
2524 vamsasStart.setVisible(!b);
2525 vamsasStop.setVisible(!b);
2526 vamsasSave.setVisible(!b);
2529 public JInternalFrame[] getAllFrames()
2531 return desktopPane.getAllFrames();
2535 * Checks the given url to see if it gives a response indicating that the user
2536 * should be informed of a new questionnaire.
2540 public void checkForQuestionnaire(String url)
2542 UserQuestionnaireCheck jvq = new UserQuestionnaireCheck(url);
2543 // javax.swing.SwingUtilities.invokeLater(jvq);
2544 new Thread(jvq).start();
2547 public void checkURLLinks()
2549 // Thread off the URL link checker
2550 addDialogThread(new Runnable()
2555 if (Cache.getDefault("CHECKURLLINKS", true))
2557 // check what the actual links are - if it's just the default don't
2558 // bother with the warning
2559 List<String> links = Preferences.sequenceUrlLinks
2562 // only need to check links if there is one with a
2563 // SEQUENCE_ID which is not the default EMBL_EBI link
2564 ListIterator<String> li = links.listIterator();
2565 boolean check = false;
2566 List<JLabel> urls = new ArrayList<>();
2567 while (li.hasNext())
2569 String link = li.next();
2570 if (link.contains(UrlConstants.SEQUENCE_ID)
2571 && !UrlConstants.isDefaultString(link))
2574 int barPos = link.indexOf("|");
2575 String urlMsg = barPos == -1 ? link
2576 : link.substring(0, barPos) + ": "
2577 + link.substring(barPos + 1);
2578 urls.add(new JLabel(urlMsg));
2586 // ask user to check in case URL links use old style tokens
2587 // ($SEQUENCE_ID$ for sequence id _or_ accession id)
2588 JPanel msgPanel = new JPanel();
2589 msgPanel.setLayout(new BoxLayout(msgPanel, BoxLayout.PAGE_AXIS));
2590 msgPanel.add(Box.createVerticalGlue());
2591 JLabel msg = new JLabel(MessageManager
2592 .getString("label.SEQUENCE_ID_for_DB_ACCESSION1"));
2593 JLabel msg2 = new JLabel(MessageManager
2594 .getString("label.SEQUENCE_ID_for_DB_ACCESSION2"));
2596 for (JLabel url : urls)
2602 final JCheckBox jcb = new JCheckBox(
2603 MessageManager.getString("label.do_not_display_again"));
2604 jcb.addActionListener(new ActionListener()
2607 public void actionPerformed(ActionEvent e)
2609 // update Cache settings for "don't show this again"
2610 boolean showWarningAgain = !jcb.isSelected();
2611 Cache.setProperty("CHECKURLLINKS",
2612 Boolean.valueOf(showWarningAgain).toString());
2617 JvOptionPane.showMessageDialog(desktopPane, msgPanel,
2619 .getString("label.SEQUENCE_ID_no_longer_used"),
2620 JvOptionPane.WARNING_MESSAGE);
2627 * Proxy class for JDesktopPane which optionally displays the current memory
2628 * usage and highlights the desktop area with a red bar if free memory runs low.
2632 public class MyDesktopPane extends JDesktopPane
2635 private static final float ONE_MB = 1048576f;
2637 boolean showMemoryUsage = false;
2641 java.text.NumberFormat df;
2643 float maxMemory, allocatedMemory, freeMemory, totalFreeMemory,
2646 public MyDesktopPane(boolean showMemoryUsage)
2648 showMemoryUsage(showMemoryUsage);
2651 public void showMemoryUsage(boolean showMemory)
2653 this.showMemoryUsage = showMemory;
2656 Thread worker = new Thread(this);
2662 public boolean isShowMemoryUsage()
2664 return showMemoryUsage;
2670 df = java.text.NumberFormat.getNumberInstance();
2671 df.setMaximumFractionDigits(2);
2672 runtime = Runtime.getRuntime();
2674 while (showMemoryUsage)
2678 maxMemory = runtime.maxMemory() / ONE_MB;
2679 allocatedMemory = runtime.totalMemory() / ONE_MB;
2680 freeMemory = runtime.freeMemory() / ONE_MB;
2681 totalFreeMemory = freeMemory + (maxMemory - allocatedMemory);
2683 percentUsage = (totalFreeMemory / maxMemory) * 100;
2685 // if (percentUsage < 20)
2687 // border1 = BorderFactory.createMatteBorder(12, 12, 12, 12,
2689 // instance.set.setBorder(border1);
2692 // sleep after showing usage
2694 } catch (Exception ex)
2696 ex.printStackTrace();
2702 public void paintComponent(Graphics g)
2704 if (showMemoryUsage && g != null && df != null)
2706 if (percentUsage < 20)
2708 g.setColor(Color.red);
2710 FontMetrics fm = g.getFontMetrics();
2713 g.drawString(MessageManager.formatMessage("label.memory_stats",
2715 { df.format(totalFreeMemory), df.format(maxMemory),
2716 df.format(percentUsage) }),
2717 10, getHeight() - fm.getHeight());
2724 * Accessor method to quickly get all the AlignmentFrames loaded.
2726 * @return an array of AlignFrame, or null if none found
2728 public static AlignFrame[] getAlignFrames()
2730 if (Jalview.isHeadlessMode())
2732 // Desktop.getDesktop() is null in headless mode
2733 return new AlignFrame[] { Jalview.getCurrentAlignFrame() };
2736 JInternalFrame[] frames = Desktop.getDesktopPane().getAllFrames();
2742 List<AlignFrame> avp = new ArrayList<>();
2744 for (int i = frames.length - 1; i > -1; i--)
2746 if (frames[i] instanceof AlignFrame)
2748 avp.add((AlignFrame) frames[i]);
2750 else if (frames[i] instanceof SplitFrame)
2753 * Also check for a split frame containing an AlignFrame
2755 GSplitFrame sf = (GSplitFrame) frames[i];
2756 if (sf.getTopFrame() instanceof AlignFrame)
2758 avp.add((AlignFrame) sf.getTopFrame());
2760 if (sf.getBottomFrame() instanceof AlignFrame)
2762 avp.add((AlignFrame) sf.getBottomFrame());
2766 if (avp.size() == 0)
2770 AlignFrame afs[] = avp.toArray(new AlignFrame[avp.size()]);
2775 * Returns an array of any AppJmol frames in the Desktop (or null if none).
2779 public GStructureViewer[] getJmols()
2781 JInternalFrame[] frames = Desktop.getDesktopPane().getAllFrames();
2787 List<GStructureViewer> avp = new ArrayList<>();
2789 for (int i = frames.length - 1; i > -1; i--)
2791 if (frames[i] instanceof AppJmol)
2793 GStructureViewer af = (GStructureViewer) frames[i];
2797 if (avp.size() == 0)
2801 GStructureViewer afs[] = avp.toArray(new GStructureViewer[avp.size()]);
2806 * Add Groovy Support to Jalview
2809 public void groovyShell_actionPerformed()
2813 openGroovyConsole();
2814 } catch (Exception ex)
2816 jalview.bin.Cache.log.error("Groovy Shell Creation failed.", ex);
2817 JvOptionPane.showInternalMessageDialog(desktopPane,
2819 MessageManager.getString("label.couldnt_create_groovy_shell"),
2820 MessageManager.getString("label.groovy_support_failed"),
2821 JvOptionPane.ERROR_MESSAGE);
2826 * Open the Groovy console
2828 private void openGroovyConsole()
2830 if (groovyConsole == null)
2832 groovyConsole = new groovy.ui.Console();
2833 groovyConsole.setVariable("Jalview", this);
2834 groovyConsole.run();
2837 * We allow only one console at a time, so that AlignFrame menu option
2838 * 'Calculate | Run Groovy script' is unambiguous.
2839 * Disable 'Groovy Console', and enable 'Run script', when the console is
2840 * opened, and the reverse when it is closed
2842 Window window = (Window) groovyConsole.getFrame();
2843 window.addWindowListener(new WindowAdapter()
2846 public void windowClosed(WindowEvent e)
2849 * rebind CMD-Q from Groovy Console to Jalview Quit
2852 enableExecuteGroovy(false);
2858 * show Groovy console window (after close and reopen)
2860 ((Window) groovyConsole.getFrame()).setVisible(true);
2863 * if we got this far, enable 'Run Groovy' in AlignFrame menus
2864 * and disable opening a second console
2866 enableExecuteGroovy(true);
2870 * Bind Ctrl/Cmd-Q to Quit - for reset as Groovy Console takes over this binding
2873 protected void addQuitHandler()
2875 getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
2876 .put(KeyStroke.getKeyStroke(KeyEvent.VK_Q,
2877 Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()),
2879 getRootPane().getActionMap().put("Quit", new AbstractAction()
2882 public void actionPerformed(ActionEvent e)
2890 * Enable or disable 'Run Groovy script' in AlignFrame calculate menus
2893 * true if Groovy console is open
2895 public void enableExecuteGroovy(boolean enabled)
2898 * disable opening a second Groovy console
2899 * (or re-enable when the console is closed)
2901 groovyShell.setEnabled(!enabled);
2903 AlignFrame[] alignFrames = getAlignFrames();
2904 if (alignFrames != null)
2906 for (AlignFrame af : alignFrames)
2908 af.setGroovyEnabled(enabled);
2914 * Progress bars managed by the IProgressIndicator method.
2916 private Hashtable<Long, JPanel> progressBars;
2918 private Hashtable<Long, IProgressIndicatorHandler> progressBarHandlers;
2923 * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
2926 public void setProgressBar(String message, long id)
2928 Platform.timeCheck("Desktop " + message, Platform.TIME_MARK);
2930 if (progressBars == null)
2932 progressBars = new Hashtable<>();
2933 progressBarHandlers = new Hashtable<>();
2936 if (progressBars.get(new Long(id)) != null)
2938 JPanel panel = progressBars.remove(new Long(id));
2939 if (progressBarHandlers.contains(new Long(id)))
2941 progressBarHandlers.remove(new Long(id));
2943 removeProgressPanel(panel);
2947 progressBars.put(new Long(id), addProgressPanel(message));
2954 * @see jalview.gui.IProgressIndicator#registerHandler(long,
2955 * jalview.gui.IProgressIndicatorHandler)
2958 public void registerHandler(final long id,
2959 final IProgressIndicatorHandler handler)
2961 if (progressBarHandlers == null
2962 || !progressBars.containsKey(new Long(id)))
2964 throw new Error(MessageManager.getString(
2965 "error.call_setprogressbar_before_registering_handler"));
2967 progressBarHandlers.put(new Long(id), handler);
2968 final JPanel progressPanel = progressBars.get(new Long(id));
2969 if (handler.canCancel())
2971 JButton cancel = new JButton(
2972 MessageManager.getString("action.cancel"));
2973 final IProgressIndicator us = this;
2974 cancel.addActionListener(new ActionListener()
2978 public void actionPerformed(ActionEvent e)
2980 handler.cancelActivity(id);
2981 us.setProgressBar(MessageManager
2982 .formatMessage("label.cancelled_params", new Object[]
2983 { ((JLabel) progressPanel.getComponent(0)).getText() }),
2987 progressPanel.add(cancel, BorderLayout.EAST);
2993 * @return true if any progress bars are still active
2996 public boolean operationInProgress()
2998 if (progressBars != null && progressBars.size() > 0)
3006 * This will return the first AlignFrame holding the given viewport instance. It
3007 * will break if there are more than one AlignFrames viewing a particular av.
3010 * @return alignFrame for viewport
3012 public static AlignFrame getAlignFrameFor(AlignViewportI viewport)
3014 if (getDesktopPane() != null)
3016 AlignmentPanel[] aps = getAlignmentPanels(
3017 viewport.getSequenceSetId());
3018 for (int panel = 0; aps != null && panel < aps.length; panel++)
3020 if (aps[panel] != null && aps[panel].av == viewport)
3022 return aps[panel].alignFrame;
3029 public VamsasApplication getVamsasApplication()
3036 * flag set if jalview GUI is being operated programmatically
3038 private boolean inBatchMode = false;
3041 * check if jalview GUI is being operated programmatically
3043 * @return inBatchMode
3045 public boolean isInBatchMode()
3051 * set flag if jalview GUI is being operated programmatically
3053 * @param inBatchMode
3055 public void setInBatchMode(boolean inBatchMode)
3057 this.inBatchMode = inBatchMode;
3060 public void startServiceDiscovery()
3062 startServiceDiscovery(false);
3065 public void startServiceDiscovery(boolean blocking)
3067 boolean alive = true;
3068 Thread t0 = null, t1 = null, t2 = null;
3069 // JAL-940 - JALVIEW 1 services are now being EOLed as of JABA 2.1 release
3072 // todo: changesupport handlers need to be transferred
3073 if (discoverer == null)
3075 discoverer = Discoverer.getInstance();
3076 // register PCS handler for getDesktop().
3077 discoverer.addPropertyChangeListener(changeSupport);
3079 // JAL-940 - disabled JWS1 service configuration - always start discoverer
3080 // until we phase out completely
3081 (t0 = new Thread(discoverer)).start();
3084 if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
3086 t2 = jalview.ws.jws2.Jws2Discoverer.getInstance()
3087 .startDiscoverer(changeSupport);
3091 // TODO: do rest service discovery
3100 } catch (Exception e)
3103 alive = (t1 != null && t1.isAlive()) || (t2 != null && t2.isAlive())
3104 || (t3 != null && t3.isAlive())
3105 || (t0 != null && t0.isAlive());
3111 * called to check if the service discovery process completed successfully.
3115 protected void JalviewServicesChanged(PropertyChangeEvent evt)
3117 if (evt.getNewValue() == null || evt.getNewValue() instanceof Vector)
3119 final String ermsg = jalview.ws.jws2.Jws2Discoverer.getInstance()
3120 .getErrorMessages();
3123 if (Cache.getDefault("SHOW_WSDISCOVERY_ERRORS", true))
3125 if (serviceChangedDialog == null)
3127 // only run if we aren't already displaying one of these.
3128 addDialogThread(serviceChangedDialog = new Runnable()
3135 * JalviewDialog jd =new JalviewDialog() {
3137 * @Override protected void cancelPressed() { // TODO
3138 * Auto-generated method stub
3140 * }@Override protected void okPressed() { // TODO
3141 * Auto-generated method stub
3143 * }@Override protected void raiseClosed() { // TODO
3144 * Auto-generated method stub
3146 * } }; jd.initDialogFrame(new
3147 * JLabel("<html><table width=\"450\"><tr><td>" + ermsg +
3148 * "<br/>It may be that you have invalid JABA URLs in your web service preferences,"
3149 * + " or mis-configured HTTP proxy settings.<br/>" +
3150 * "Check the <em>Connections</em> and <em>Web services</em> tab of the"
3152 * " Tools->Preferences dialog box to change them.</td></tr></table></html>"
3153 * ), true, true, "Web Service Configuration Problem", 450,
3156 * jd.waitForInput();
3158 JvOptionPane.showConfirmDialog(Desktop.getDesktopPane(),
3159 new JLabel("<html><table width=\"450\"><tr><td>"
3160 + ermsg + "</td></tr></table>"
3161 + "<p>It may be that you have invalid JABA URLs<br/>in your web service preferences,"
3162 + "<br>or as a command-line argument, or mis-configured HTTP proxy settings.</p>"
3163 + "<p>Check the <em>Connections</em> and <em>Web services</em> tab<br/>of the"
3164 + " Tools->Preferences dialog box to change them.</p></html>"),
3165 "Web Service Configuration Problem",
3166 JvOptionPane.DEFAULT_OPTION,
3167 JvOptionPane.ERROR_MESSAGE);
3168 serviceChangedDialog = null;
3177 "Errors reported by JABA discovery service. Check web services preferences.\n"
3184 Runnable serviceChangedDialog = null;
3187 * start a thread to open a URL in the configured browser. Pops up a warning
3188 * dialog to the user if there is an exception when calling out to the browser
3193 public static void showUrl(final String url)
3195 showUrl(url, Desktop.getInstance());
3199 * Like showUrl but allows progress handler to be specified
3203 * (null) or object implementing IProgressIndicator
3205 public static void showUrl(final String url,
3206 final IProgressIndicator progress)
3208 new Thread(new Runnable()
3215 if (progress != null)
3217 progress.setProgressBar(MessageManager
3218 .formatMessage("status.opening_params", new Object[]
3219 { url }), this.hashCode());
3221 BrowserLauncher.openURL(url);
3222 } catch (Exception ex)
3224 JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
3226 .getString("label.web_browser_not_found_unix"),
3227 MessageManager.getString("label.web_browser_not_found"),
3228 JvOptionPane.WARNING_MESSAGE);
3230 ex.printStackTrace();
3232 if (progress != null)
3234 progress.setProgressBar(null, this.hashCode());
3240 private WsParamSetManager wsparamManager = null;
3242 public static ParamManager getUserParameterStore()
3244 Desktop d = Desktop.getInstance();
3245 if (d.wsparamManager == null)
3247 d.wsparamManager = new WsParamSetManager();
3249 return d.wsparamManager;
3253 * static hyperlink handler proxy method for use by Jalview's internal windows
3257 public static void hyperlinkUpdate(HyperlinkEvent e)
3259 if (e.getEventType() == EventType.ACTIVATED)
3264 url = e.getURL().toString();
3265 Desktop.showUrl(url);
3266 } catch (Exception x)
3270 if (Cache.log != null)
3272 Cache.log.error("Couldn't handle string " + url + " as a URL.");
3277 "Couldn't handle string " + url + " as a URL.");
3280 // ignore any exceptions due to dud links.
3287 * single thread that handles display of dialogs to user.
3289 ExecutorService dialogExecutor = Executors.newSingleThreadExecutor();
3292 * flag indicating if dialogExecutor should try to acquire a permit
3294 volatile boolean dialogPause = true;
3299 java.util.concurrent.Semaphore block = new Semaphore(0);
3301 private groovy.ui.Console groovyConsole;
3303 public StructureViewer lastTargetedView;
3306 * add another dialog thread to the queue
3310 public void addDialogThread(final Runnable prompter)
3312 dialogExecutor.submit(new Runnable()
3322 } catch (InterruptedException x)
3332 SwingUtilities.invokeAndWait(prompter);
3333 } catch (Exception q)
3335 Cache.log.warn("Unexpected Exception in dialog thread.", q);
3341 public void startDialogQueue()
3343 // set the flag so we don't pause waiting for another permit and semaphore
3344 // the current task to begin
3345 dialogPause = false;
3350 * Outputs an image of the desktop to file in EPS format, after prompting the
3351 * user for choice of Text or Lineart character rendering (unless a preference
3352 * has been set). The file name is generated as
3355 * Jalview_snapshot_nnnnn.eps where nnnnn is the current timestamp in milliseconds
3359 protected void snapShotWindow_actionPerformed(ActionEvent e)
3361 // currently the menu option to do this is not shown
3364 int width = getWidth();
3365 int height = getHeight();
3367 "Jalview_snapshot_" + System.currentTimeMillis() + ".eps");
3368 ImageWriterI writer = new ImageWriterI()
3371 public void exportImage(Graphics g) throws Exception
3374 Cache.log.info("Successfully written snapshot to file "
3375 + of.getAbsolutePath());
3378 String title = "View of desktop";
3379 ImageExporter exporter = new ImageExporter(writer, null, TYPE.EPS,
3381 exporter.doExport(of, this, width, height, title);
3385 * Explode the views in the given SplitFrame into separate SplitFrame windows.
3386 * This respects (remembers) any previous 'exploded geometry' i.e. the size and
3387 * location last time the view was expanded (if any). However it does not
3388 * remember the split pane divider location - this is set to match the
3389 * 'exploding' frame.
3393 public void explodeViews(SplitFrame sf)
3395 AlignFrame oldTopFrame = (AlignFrame) sf.getTopFrame();
3396 AlignFrame oldBottomFrame = (AlignFrame) sf.getBottomFrame();
3397 List<? extends AlignmentViewPanel> topPanels = oldTopFrame
3399 List<? extends AlignmentViewPanel> bottomPanels = oldBottomFrame
3401 int viewCount = topPanels.size();
3408 * Processing in reverse order works, forwards order leaves the first panels
3409 * not visible. I don't know why!
3411 for (int i = viewCount - 1; i >= 0; i--)
3414 * Make new top and bottom frames. These take over the respective
3415 * AlignmentPanel objects, including their AlignmentViewports, so the
3416 * cdna/protein relationships between the viewports is carried over to the
3419 * explodedGeometry holds the (x, y) position of the previously exploded
3420 * SplitFrame, and the (width, height) of the AlignFrame component
3422 AlignmentPanel topPanel = (AlignmentPanel) topPanels.get(i);
3423 AlignFrame newTopFrame = new AlignFrame(topPanel);
3424 newTopFrame.setSize(oldTopFrame.getSize());
3425 newTopFrame.setVisible(true);
3426 Rectangle geometry = ((AlignViewport) topPanel.getAlignViewport())
3427 .getExplodedGeometry();
3428 if (geometry != null)
3430 newTopFrame.setSize(geometry.getSize());
3433 AlignmentPanel bottomPanel = (AlignmentPanel) bottomPanels.get(i);
3434 AlignFrame newBottomFrame = new AlignFrame(bottomPanel);
3435 newBottomFrame.setSize(oldBottomFrame.getSize());
3436 newBottomFrame.setVisible(true);
3437 geometry = ((AlignViewport) bottomPanel.getAlignViewport())
3438 .getExplodedGeometry();
3439 if (geometry != null)
3441 newBottomFrame.setSize(geometry.getSize());
3444 topPanel.av.setGatherViewsHere(false);
3445 bottomPanel.av.setGatherViewsHere(false);
3446 JInternalFrame splitFrame = new SplitFrame(newTopFrame,
3448 if (geometry != null)
3450 splitFrame.setLocation(geometry.getLocation());
3452 Desktop.addInternalFrame(splitFrame, sf.getTitle(), -1, -1);
3456 * Clear references to the panels (now relocated in the new SplitFrames)
3457 * before closing the old SplitFrame.
3460 bottomPanels.clear();
3465 * Gather expanded split frames, sharing the same pairs of sequence set ids,
3466 * back into the given SplitFrame as additional views. Note that the gathered
3467 * frames may themselves have multiple views.
3471 public void gatherViews(GSplitFrame source)
3474 * special handling of explodedGeometry for a view within a SplitFrame: - it
3475 * holds the (x, y) position of the enclosing SplitFrame, and the (width,
3476 * height) of the AlignFrame component
3478 AlignFrame myTopFrame = (AlignFrame) source.getTopFrame();
3479 AlignFrame myBottomFrame = (AlignFrame) source.getBottomFrame();
3480 myTopFrame.viewport.setExplodedGeometry(new Rectangle(source.getX(),
3481 source.getY(), myTopFrame.getWidth(), myTopFrame.getHeight()));
3482 myBottomFrame.viewport
3483 .setExplodedGeometry(new Rectangle(source.getX(), source.getY(),
3484 myBottomFrame.getWidth(), myBottomFrame.getHeight()));
3485 myTopFrame.viewport.setGatherViewsHere(true);
3486 myBottomFrame.viewport.setGatherViewsHere(true);
3487 String topViewId = myTopFrame.viewport.getSequenceSetId();
3488 String bottomViewId = myBottomFrame.viewport.getSequenceSetId();
3490 JInternalFrame[] frames = desktopPane.getAllFrames();
3491 for (JInternalFrame frame : frames)
3493 if (frame instanceof SplitFrame && frame != source)
3495 SplitFrame sf = (SplitFrame) frame;
3496 AlignFrame topFrame = (AlignFrame) sf.getTopFrame();
3497 AlignFrame bottomFrame = (AlignFrame) sf.getBottomFrame();
3498 boolean gatherThis = false;
3499 for (int a = 0; a < topFrame.alignPanels.size(); a++)
3501 AlignmentPanel topPanel = topFrame.alignPanels.get(a);
3502 AlignmentPanel bottomPanel = bottomFrame.alignPanels.get(a);
3503 if (topViewId.equals(topPanel.av.getSequenceSetId())
3504 && bottomViewId.equals(bottomPanel.av.getSequenceSetId()))
3507 topPanel.av.setGatherViewsHere(false);
3508 bottomPanel.av.setGatherViewsHere(false);
3509 topPanel.av.setExplodedGeometry(
3510 new Rectangle(sf.getLocation(), topFrame.getSize()));
3511 bottomPanel.av.setExplodedGeometry(
3512 new Rectangle(sf.getLocation(), bottomFrame.getSize()));
3513 myTopFrame.addAlignmentPanel(topPanel, false);
3514 myBottomFrame.addAlignmentPanel(bottomPanel, false);
3520 topFrame.getAlignPanels().clear();
3521 bottomFrame.getAlignPanels().clear();
3528 * The dust settles...give focus to the tab we did this from.
3530 myTopFrame.setDisplayedView(myTopFrame.alignPanel);
3533 public static groovy.ui.Console getGroovyConsole()
3535 Desktop desktop = Desktop.getInstance();
3536 return desktop == null ? null : desktop.groovyConsole;
3540 * handles the payload of a drag and drop event.
3542 * TODO refactor to desktop utilities class
3545 * - Data source strings extracted from the drop event
3547 * - protocol for each data source extracted from the drop event
3551 * - the payload from the drop event
3554 @SuppressWarnings("unchecked")
3555 public static void transferFromDropTarget(List<Object> files,
3556 List<DataSourceType> protocols, DropTargetDropEvent evt,
3557 Transferable t) throws Exception
3560 // BH 2018 changed List<String> to List<Object> to allow for File from SwingJS
3562 // DataFlavor[] flavors = t.getTransferDataFlavors();
3563 // for (int i = 0; i < flavors.length; i++) {
3564 // if (flavors[i].isFlavorJavaFileListType()) {
3565 // evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
3566 // List<File> list = (List<File>) t.getTransferData(flavors[i]);
3567 // for (int j = 0; j < list.size(); j++) {
3568 // File file = (File) list.get(j);
3569 // byte[] data = getDroppedFileBytes(file);
3570 // fileName.setText(file.getName() + " - " + data.length + " " +
3571 // evt.getLocation());
3572 // JTextArea target = (JTextArea) ((DropTarget) evt.getSource()).getComponent();
3573 // target.setText(new String(data));
3575 // dtde.dropComplete(true);
3580 DataFlavor uriListFlavor = new DataFlavor(
3581 "text/uri-list;class=java.lang.String"), urlFlavour = null;
3584 urlFlavour = new DataFlavor(
3585 "application/x-java-url; class=java.net.URL");
3586 } catch (ClassNotFoundException cfe)
3588 Cache.log.debug("Couldn't instantiate the URL dataflavor.", cfe);
3591 if (urlFlavour != null && t.isDataFlavorSupported(urlFlavour))
3596 java.net.URL url = (URL) t.getTransferData(urlFlavour);
3597 // nb: java 8 osx bug https://bugs.openjdk.java.net/browse/JDK-8156099
3598 // means url may be null.
3601 protocols.add(DataSourceType.URL);
3602 files.add(url.toString());
3603 Cache.log.debug("Drop handled as URL dataflavor "
3604 + files.get(files.size() - 1));
3609 if (Platform.isAMacAndNotJS())
3612 "Please ignore plist error - occurs due to problem with java 8 on OSX");
3615 } catch (Throwable ex)
3617 Cache.log.debug("URL drop handler failed.", ex);
3620 if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
3622 // Works on Windows and MacOSX
3623 Cache.log.debug("Drop handled as javaFileListFlavor");
3624 for (Object file : (List<Object>) t
3625 .getTransferData(DataFlavor.javaFileListFlavor))
3628 protocols.add(DataSourceType.FILE);
3633 // Unix like behaviour
3634 boolean added = false;
3636 if (t.isDataFlavorSupported(uriListFlavor))
3638 Cache.log.debug("Drop handled as uriListFlavor");
3639 // This is used by Unix drag system
3640 data = (String) t.getTransferData(uriListFlavor);
3644 // fallback to text: workaround - on OSX where there's a JVM bug
3645 Cache.log.debug("standard URIListFlavor failed. Trying text");
3646 // try text fallback
3647 DataFlavor textDf = new DataFlavor(
3648 "text/plain;class=java.lang.String");
3649 if (t.isDataFlavorSupported(textDf))
3651 data = (String) t.getTransferData(textDf);
3654 Cache.log.debug("Plain text drop content returned "
3655 + (data == null ? "Null - failed" : data));
3660 while (protocols.size() < files.size())
3662 Cache.log.debug("Adding missing FILE protocol for "
3663 + files.get(protocols.size()));
3664 protocols.add(DataSourceType.FILE);
3666 for (java.util.StringTokenizer st = new java.util.StringTokenizer(
3667 data, "\r\n"); st.hasMoreTokens();)
3670 String s = st.nextToken();
3671 if (s.startsWith("#"))
3673 // the line is a comment (as per the RFC 2483)
3676 java.net.URI uri = new java.net.URI(s);
3677 if (uri.getScheme().toLowerCase().startsWith("http"))
3679 protocols.add(DataSourceType.URL);
3680 files.add(uri.toString());
3684 // otherwise preserve old behaviour: catch all for file objects
3685 java.io.File file = new java.io.File(uri);
3686 protocols.add(DataSourceType.FILE);
3687 files.add(file.toString());
3692 if (Cache.log.isDebugEnabled())
3694 if (data == null || !added)
3697 if (t.getTransferDataFlavors() != null
3698 && t.getTransferDataFlavors().length > 0)
3701 "Couldn't resolve drop data. Here are the supported flavors:");
3702 for (DataFlavor fl : t.getTransferDataFlavors())
3705 "Supported transfer dataflavor: " + fl.toString());
3706 Object df = t.getTransferData(fl);
3709 Cache.log.debug("Retrieves: " + df);
3713 Cache.log.debug("Retrieved nothing");
3719 Cache.log.debug("Couldn't resolve dataflavor for drop: "
3725 if (Platform.isWindowsAndNotJS())
3727 Cache.log.debug("Scanning dropped content for Windows Link Files");
3729 // resolve any .lnk files in the file drop
3730 for (int f = 0; f < files.size(); f++)
3732 String source = files.get(f).toString().toLowerCase();
3733 if (protocols.get(f).equals(DataSourceType.FILE)
3734 && (source.endsWith(".lnk") || source.endsWith(".url")
3735 || source.endsWith(".site")))
3739 Object obj = files.get(f);
3740 File lf = (obj instanceof File ? (File) obj
3741 : new File((String) obj));
3742 // process link file to get a URL
3743 Cache.log.debug("Found potential link file: " + lf);
3744 WindowsShortcut wscfile = new WindowsShortcut(lf);
3745 String fullname = wscfile.getRealFilename();
3746 protocols.set(f, FormatAdapter.checkProtocol(fullname));
3747 files.set(f, fullname);
3748 Cache.log.debug("Parsed real filename " + fullname
3749 + " to extract protocol: " + protocols.get(f));
3750 } catch (Exception ex)
3753 "Couldn't parse " + files.get(f) + " as a link file.",
3762 * Sets the Preferences property for experimental features to True or False
3763 * depending on the state of the controlling menu item
3766 protected void showExperimental_actionPerformed(boolean selected)
3768 Cache.setProperty(EXPERIMENTAL_FEATURES, Boolean.toString(selected));
3772 * Answers a (possibly empty) list of any structure viewer frames (currently for
3773 * either Jmol or Chimera) which are currently open. This may optionally be
3774 * restricted to viewers of a specified class, or viewers linked to a specified
3778 * if not null, only return viewers linked to this panel
3779 * @param structureViewerClass
3780 * if not null, only return viewers of this class
3783 public List<StructureViewerBase> getStructureViewers(
3784 AlignmentPanel apanel,
3785 Class<? extends StructureViewerBase> structureViewerClass)
3787 List<StructureViewerBase> result = new ArrayList<>();
3788 JInternalFrame[] frames = getAllFrames();
3790 for (JInternalFrame frame : frames)
3792 if (frame instanceof StructureViewerBase)
3794 if (structureViewerClass == null
3795 || structureViewerClass.isInstance(frame))
3798 || ((StructureViewerBase) frame).isLinkedWith(apanel))
3800 result.add((StructureViewerBase) frame);