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.bin.Cache;
26 import jalview.bin.Jalview;
27 import jalview.gui.ImageExporter.ImageWriterI;
28 import jalview.io.BackupFiles;
29 import jalview.io.DataSourceType;
30 import jalview.io.FileFormat;
31 import jalview.io.FileFormatException;
32 import jalview.io.FileFormatI;
33 import jalview.io.FileFormats;
34 import jalview.io.FileLoader;
35 import jalview.io.FormatAdapter;
36 import jalview.io.IdentifyFile;
37 import jalview.io.JalviewFileChooser;
38 import jalview.io.JalviewFileView;
39 import jalview.jbgui.GSplitFrame;
40 import jalview.jbgui.GStructureViewer;
41 import jalview.project.Jalview2XML;
42 import jalview.structure.StructureSelectionManager;
43 import jalview.urls.IdOrgSettings;
44 import jalview.util.BrowserLauncher;
45 import jalview.util.ImageMaker.TYPE;
46 import jalview.util.MessageManager;
47 import jalview.util.Platform;
48 import jalview.util.UrlConstants;
49 import jalview.viewmodel.AlignmentViewport;
50 import jalview.ws.params.ParamManager;
51 import jalview.ws.utils.UrlDownloadClient;
53 import java.awt.BorderLayout;
54 import java.awt.Color;
55 import java.awt.Dimension;
56 import java.awt.FontMetrics;
57 import java.awt.Graphics;
58 import java.awt.GridLayout;
59 import java.awt.Point;
60 import java.awt.Rectangle;
61 import java.awt.Toolkit;
62 import java.awt.Window;
63 import java.awt.datatransfer.Clipboard;
64 import java.awt.datatransfer.ClipboardOwner;
65 import java.awt.datatransfer.DataFlavor;
66 import java.awt.datatransfer.Transferable;
67 import java.awt.dnd.DnDConstants;
68 import java.awt.dnd.DropTargetDragEvent;
69 import java.awt.dnd.DropTargetDropEvent;
70 import java.awt.dnd.DropTargetEvent;
71 import java.awt.dnd.DropTargetListener;
72 import java.awt.event.ActionEvent;
73 import java.awt.event.ActionListener;
74 import java.awt.event.InputEvent;
75 import java.awt.event.KeyEvent;
76 import java.awt.event.MouseAdapter;
77 import java.awt.event.MouseEvent;
78 import java.awt.event.WindowAdapter;
79 import java.awt.event.WindowEvent;
80 import java.beans.PropertyChangeEvent;
81 import java.beans.PropertyChangeListener;
82 import java.io.BufferedInputStream;
84 import java.io.FileOutputStream;
85 import java.io.IOException;
87 import java.util.ArrayList;
88 import java.util.Hashtable;
89 import java.util.List;
90 import java.util.ListIterator;
91 import java.util.Vector;
92 import java.util.concurrent.ExecutorService;
93 import java.util.concurrent.Executors;
94 import java.util.concurrent.Semaphore;
96 import javax.swing.AbstractAction;
97 import javax.swing.Action;
98 import javax.swing.ActionMap;
99 import javax.swing.Box;
100 import javax.swing.BoxLayout;
101 import javax.swing.DefaultDesktopManager;
102 import javax.swing.DesktopManager;
103 import javax.swing.InputMap;
104 import javax.swing.JButton;
105 import javax.swing.JCheckBox;
106 import javax.swing.JComboBox;
107 import javax.swing.JComponent;
108 import javax.swing.JDesktopPane;
109 import javax.swing.JFrame;
110 import javax.swing.JInternalFrame;
111 import javax.swing.JLabel;
112 import javax.swing.JMenuItem;
113 import javax.swing.JPanel;
114 import javax.swing.JPopupMenu;
115 import javax.swing.JProgressBar;
116 import javax.swing.JTextField;
117 import javax.swing.KeyStroke;
118 import javax.swing.SwingUtilities;
119 import javax.swing.event.HyperlinkEvent;
120 import javax.swing.event.HyperlinkEvent.EventType;
121 import javax.swing.event.InternalFrameAdapter;
122 import javax.swing.event.InternalFrameEvent;
123 import javax.swing.event.MenuEvent;
124 import javax.swing.event.MenuListener;
126 import org.stackoverflowusers.file.WindowsShortcut;
133 * @version $Revision: 1.155 $
135 @SuppressWarnings("serial")
136 public class Desktop extends jalview.jbgui.GDesktop
137 implements DropTargetListener, ClipboardOwner, IProgressIndicator,
138 jalview.api.StructureSelectionManagerProvider
140 private final static int DEFAULT_MIN_WIDTH = 300;
142 private final static int DEFAULT_MIN_HEIGHT = 250;
144 private final static int ALIGN_FRAME_DEFAULT_MIN_WIDTH = 600;
146 private final static int ALIGN_FRAME_DEFAULT_MIN_HEIGHT = 70;
148 private final static String EXPERIMENTAL_FEATURES = "EXPERIMENTAL_FEATURES";
150 private JalviewChangeSupport changeSupport = new JalviewChangeSupport();
153 * news reader - null if it was never started.
155 BlogReader jvnews = null;
157 private File projectFile;
161 * @see jalview.gui.JalviewChangeSupport#addJalviewPropertyChangeListener(java.beans.PropertyChangeListener)
163 public void addJalviewPropertyChangeListener(
164 PropertyChangeListener listener)
166 changeSupport.addJalviewPropertyChangeListener(listener);
170 * @param propertyName
172 * @see jalview.gui.JalviewChangeSupport#addJalviewPropertyChangeListener(java.lang.String,
173 * java.beans.PropertyChangeListener)
175 public void addJalviewPropertyChangeListener(String propertyName,
176 PropertyChangeListener listener)
178 changeSupport.addJalviewPropertyChangeListener(propertyName, listener);
182 * @param propertyName
184 * @see jalview.gui.JalviewChangeSupport#removeJalviewPropertyChangeListener(java.lang.String,
185 * java.beans.PropertyChangeListener)
187 public void removeJalviewPropertyChangeListener(String propertyName,
188 PropertyChangeListener listener)
190 changeSupport.removeJalviewPropertyChangeListener(propertyName,
195 * Singleton Desktop instance only in Java;
197 private static Desktop instance;
199 public static Desktop getInstance()
202 @SuppressWarnings("unused")
203 ThreadGroup g = Thread.currentThread().getThreadGroup();
205 * @j2sNative d = g._jalviewDesktopInstance;
213 private static void setInstance(Desktop d)
215 @SuppressWarnings("unused")
216 ThreadGroup g = Thread.currentThread().getThreadGroup();
218 * @j2sNative g._jalviewDesktopInstance = d;
225 private MyDesktopPane desktopPane;
227 public static MyDesktopPane getDesktopPane()
229 return getInstance().desktopPane;
232 private void setDesktopPane(MyDesktopPane pane)
234 getInstance().desktopPane = pane;
237 static int openFrameCount = 0;
239 static final int xOffset = 30;
241 static final int yOffset = 30;
243 public jalview.ws.jws1.Discoverer discoverer;
245 public Object[] jalviewClipboard;
247 public boolean internalCopy = false;
249 private static int fileLoadingCount = 0;
251 public JInternalFrame conservationSlider, PIDSlider;
253 class MyDesktopManager implements DesktopManager
256 private DesktopManager delegate;
258 public MyDesktopManager(DesktopManager delegate)
260 this.delegate = delegate;
264 public void activateFrame(JInternalFrame f)
268 delegate.activateFrame(f);
269 } catch (NullPointerException npe)
271 Point p = getMousePosition();
272 getInstance().showPasteMenu(p.x, p.y);
277 public void beginDraggingFrame(JComponent f)
279 delegate.beginDraggingFrame(f);
283 public void beginResizingFrame(JComponent f, int direction)
285 delegate.beginResizingFrame(f, direction);
289 public void closeFrame(JInternalFrame f)
291 delegate.closeFrame(f);
295 public void deactivateFrame(JInternalFrame f)
297 delegate.deactivateFrame(f);
301 public void deiconifyFrame(JInternalFrame f)
303 delegate.deiconifyFrame(f);
307 public void dragFrame(JComponent f, int newX, int newY)
313 delegate.dragFrame(f, newX, newY);
317 public void endDraggingFrame(JComponent f)
319 delegate.endDraggingFrame(f);
320 getDesktopPane().repaint();
324 public void endResizingFrame(JComponent f)
326 delegate.endResizingFrame(f);
327 getDesktopPane().repaint();
331 public void iconifyFrame(JInternalFrame f)
333 delegate.iconifyFrame(f);
337 public void maximizeFrame(JInternalFrame f)
339 delegate.maximizeFrame(f);
343 public void minimizeFrame(JInternalFrame f)
345 delegate.minimizeFrame(f);
349 public void openFrame(JInternalFrame f)
351 delegate.openFrame(f);
355 public void resizeFrame(JComponent f, int newX, int newY, int newWidth,
362 delegate.resizeFrame(f, newX, newY, newWidth, newHeight);
366 public void setBoundsForFrame(JComponent f, int newX, int newY,
367 int newWidth, int newHeight)
369 delegate.setBoundsForFrame(f, newX, newY, newWidth, newHeight);
372 // All other methods, simply delegate
377 * Creates a new Desktop object.
383 * A note to implementors. It is ESSENTIAL that any activities that might
384 * block are spawned off as threads rather than waited for during this
388 if (!Platform.isJS())
390 doVamsasClientCheck();
393 doConfigureStructurePrefs();
394 setTitle("Jalview " + jalview.bin.Cache.getProperty("VERSION"));
395 setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
396 boolean selmemusage = jalview.bin.Cache.getDefault("SHOW_MEMUSAGE",
398 boolean showjconsole = jalview.bin.Cache.getDefault("SHOW_JAVA_CONSOLE",
400 setDesktopPane(new MyDesktopPane(selmemusage));
402 showMemusage.setSelected(selmemusage);
403 getDesktopPane().setBackground(Color.white);
404 getContentPane().setLayout(new BorderLayout());
405 // alternate config - have scrollbars - see notes in JAL-153
406 // JScrollPane sp = new JScrollPane();
407 // sp.getViewport().setView(desktop);
408 // getContentPane().add(sp, BorderLayout.CENTER);
410 // BH 2018 - just an experiment to try unclipped JInternalFrames.
413 getRootPane().putClientProperty("swingjs.overflow.hidden", "false");
416 getContentPane().add(getDesktopPane(), BorderLayout.CENTER);
417 getDesktopPane().setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
419 // This line prevents Windows Look&Feel resizing all new windows to maximum
420 // if previous window was maximised
421 getDesktopPane().setDesktopManager(new MyDesktopManager(
422 (Platform.isWindowsAndNotJS() ? new DefaultDesktopManager()
423 : Platform.isAMacAndNotJS()
424 ? new AquaInternalFrameManager(
425 getDesktopPane().getDesktopManager())
426 : getDesktopPane().getDesktopManager())));
428 Rectangle dims = getLastKnownDimensions("");
435 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
436 int xPos = Math.max(5, (screenSize.width - 900) / 2);
437 int yPos = Math.max(5, (screenSize.height - 650) / 2);
438 setBounds(xPos, yPos, 900, 650);
441 if (!Platform.isJS())
449 jconsole = new Console(this, showjconsole);
450 // add essential build information
451 jconsole.setHeader("Jalview Version: "
452 + jalview.bin.Cache.getProperty("VERSION") + "\n"
453 + "Jalview Installation: "
454 + jalview.bin.Cache.getDefault("INSTALLATION", "unknown")
455 + "\n" + "Build Date: "
456 + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown") + "\n"
457 + "Java version: " + System.getProperty("java.version") + "\n"
458 + System.getProperty("os.arch") + " "
459 + System.getProperty("os.name") + " "
460 + System.getProperty("os.version"));
462 showConsole(showjconsole);
464 showNews.setVisible(false);
466 experimentalFeatures.setSelected(showExperimental());
468 getIdentifiersOrgData();
472 // Spawn a thread that shows the splashscreen
474 SwingUtilities.invokeLater(new Runnable()
483 // Thread off a new instance of the file chooser - this reduces the time
485 // takes to open it later on.
486 new Thread(new Runnable()
491 Cache.log.debug("Filechooser init thread started.");
492 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
493 JalviewFileChooser.forRead(Cache.getProperty("LAST_DIRECTORY"),
495 Cache.log.debug("Filechooser init thread finished.");
498 // Add the service change listener
499 changeSupport.addJalviewPropertyChangeListener("services",
500 new PropertyChangeListener()
504 public void propertyChange(PropertyChangeEvent evt)
506 Cache.log.debug("Firing service changed event for "
507 + evt.getNewValue());
508 JalviewServicesChanged(evt);
515 this.setDropTarget(new java.awt.dnd.DropTarget(getDesktopPane(), this));
517 this.addWindowListener(new WindowAdapter()
520 public void windowClosing(WindowEvent evt)
527 this.addMouseListener(ma = new MouseAdapter()
530 public void mousePressed(MouseEvent evt)
532 if (evt.isPopupTrigger()) // Mac
534 showPasteMenu(evt.getX(), evt.getY());
539 public void mouseReleased(MouseEvent evt)
541 if (evt.isPopupTrigger()) // Windows
543 showPasteMenu(evt.getX(), evt.getY());
547 getDesktopPane().addMouseListener(ma);
552 * Answers true if user preferences to enable experimental features is True
557 public boolean showExperimental()
559 String experimental = Cache.getDefault(EXPERIMENTAL_FEATURES,
560 Boolean.FALSE.toString());
561 return Boolean.valueOf(experimental).booleanValue();
564 public void doConfigureStructurePrefs()
566 // configure services
567 StructureSelectionManager ssm = StructureSelectionManager
568 .getStructureSelectionManager(this);
569 if (jalview.bin.Cache.getDefault(Preferences.ADD_SS_ANN, true))
571 ssm.setAddTempFacAnnot(jalview.bin.Cache
572 .getDefault(Preferences.ADD_TEMPFACT_ANN, true));
573 ssm.setProcessSecondaryStructure(jalview.bin.Cache
574 .getDefault(Preferences.STRUCT_FROM_PDB, true));
575 ssm.setSecStructServices(
576 jalview.bin.Cache.getDefault(Preferences.USE_RNAVIEW, true));
580 ssm.setAddTempFacAnnot(false);
581 ssm.setProcessSecondaryStructure(false);
582 ssm.setSecStructServices(false);
586 public void checkForNews()
588 final Desktop me = this;
589 // Thread off the news reader, in case there are connection problems.
590 new Thread(new Runnable()
595 Cache.log.debug("Starting news thread.");
596 jvnews = new BlogReader(me);
597 showNews.setVisible(true);
598 Cache.log.debug("Completed news thread.");
603 public void getIdentifiersOrgData()
605 // Thread off the identifiers fetcher
606 new Thread(new Runnable()
611 Cache.log.debug("Downloading data from identifiers.org");
612 // UrlDownloadClient client = new UrlDownloadClient();
615 UrlDownloadClient.download(IdOrgSettings.getUrl(),
616 IdOrgSettings.getDownloadLocation());
617 } catch (IOException e)
619 Cache.log.debug("Exception downloading identifiers.org data"
628 protected void showNews_actionPerformed(ActionEvent e)
630 showNews(showNews.isSelected());
633 protected void showNews(boolean visible)
635 Cache.log.debug((visible ? "Showing" : "Hiding") + " news.");
636 showNews.setSelected(visible);
637 if (visible && !jvnews.isVisible())
639 new Thread(new Runnable()
644 long now = System.currentTimeMillis();
645 Desktop.getInstance().setProgressBar(
646 MessageManager.getString("status.refreshing_news"), now);
647 jvnews.refreshNews();
648 Desktop.getInstance().setProgressBar(null, now);
656 * recover the last known dimensions for a jalview window
659 * - empty string is desktop, all other windows have unique prefix
660 * @return null or last known dimensions scaled to current geometry (if last
661 * window geom was known)
663 Rectangle getLastKnownDimensions(String windowName)
665 // TODO: lock aspect ratio for scaling desktop Bug #0058199
666 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
667 String x = jalview.bin.Cache.getProperty(windowName + "SCREEN_X");
668 String y = jalview.bin.Cache.getProperty(windowName + "SCREEN_Y");
669 String width = jalview.bin.Cache
670 .getProperty(windowName + "SCREEN_WIDTH");
671 String height = jalview.bin.Cache
672 .getProperty(windowName + "SCREEN_HEIGHT");
673 if ((x != null) && (y != null) && (width != null) && (height != null))
675 int ix = Integer.parseInt(x), iy = Integer.parseInt(y),
676 iw = Integer.parseInt(width), ih = Integer.parseInt(height);
677 if (jalview.bin.Cache.getProperty("SCREENGEOMETRY_WIDTH") != null)
679 // attempt #1 - try to cope with change in screen geometry - this
680 // version doesn't preserve original jv aspect ratio.
681 // take ratio of current screen size vs original screen size.
682 double sw = ((1f * screenSize.width) / (1f * Integer.parseInt(
683 jalview.bin.Cache.getProperty("SCREENGEOMETRY_WIDTH"))));
684 double sh = ((1f * screenSize.height) / (1f * Integer.parseInt(
685 jalview.bin.Cache.getProperty("SCREENGEOMETRY_HEIGHT"))));
686 // rescale the bounds depending upon the current screen geometry.
687 ix = (int) (ix * sw);
688 iw = (int) (iw * sw);
689 iy = (int) (iy * sh);
690 ih = (int) (ih * sh);
691 while (ix >= screenSize.width)
693 jalview.bin.Cache.log.debug(
694 "Window geometry location recall error: shifting horizontal to within screenbounds.");
695 ix -= screenSize.width;
697 while (iy >= screenSize.height)
699 jalview.bin.Cache.log.debug(
700 "Window geometry location recall error: shifting vertical to within screenbounds.");
701 iy -= screenSize.height;
703 jalview.bin.Cache.log.debug(
704 "Got last known dimensions for " + windowName + ": x:" + ix
705 + " y:" + iy + " width:" + iw + " height:" + ih);
707 // return dimensions for new instance
708 return new Rectangle(ix, iy, iw, ih);
713 private void doVamsasClientCheck()
715 if (Cache.vamsasJarsPresent())
717 setupVamsasDisconnectedGui();
718 VamsasMenu.setVisible(true);
719 final Desktop us = this;
720 VamsasMenu.addMenuListener(new MenuListener()
722 // this listener remembers when the menu was first selected, and
723 // doesn't rebuild the session list until it has been cleared and
725 boolean refresh = true;
728 public void menuCanceled(MenuEvent e)
734 public void menuDeselected(MenuEvent e)
740 public void menuSelected(MenuEvent e)
744 us.buildVamsasStMenu();
749 vamsasStart.setVisible(true);
753 protected void showPasteMenu(int x, int y)
755 JPopupMenu popup = new JPopupMenu();
756 JMenuItem item = new JMenuItem(
757 MessageManager.getString("label.paste_new_window"));
758 item.addActionListener(new ActionListener()
761 public void actionPerformed(ActionEvent evt)
768 popup.show(this, x, y);
775 Clipboard c = Toolkit.getDefaultToolkit().getSystemClipboard();
776 Transferable contents = c.getContents(this);
778 if (contents != null)
780 String file = (String) contents
781 .getTransferData(DataFlavor.stringFlavor);
783 FileFormatI format = new IdentifyFile().identify(file,
784 DataSourceType.PASTE);
786 new FileLoader().LoadFile(file, DataSourceType.PASTE, format);
789 } catch (Exception ex)
792 "Unable to paste alignment from system clipboard:\n" + ex);
797 * Adds and opens the given frame to the desktop
808 public static synchronized void addInternalFrame(
809 final JInternalFrame frame, String title, int w, int h)
811 addInternalFrame(frame, title, true, w, h, true, false);
815 * Add an internal frame to the Jalview desktop
822 * When true, display frame immediately, otherwise, caller must call
823 * setVisible themselves.
829 public static synchronized void addInternalFrame(
830 final JInternalFrame frame, String title, boolean makeVisible,
833 addInternalFrame(frame, title, makeVisible, w, h, true, false);
837 * Add an internal frame to the Jalview desktop and make it visible
850 public static synchronized void addInternalFrame(
851 final JInternalFrame frame, String title, int w, int h,
854 addInternalFrame(frame, title, true, w, h, resizable, false);
858 * Add an internal frame to the Jalview desktop
865 * When true, display frame immediately, otherwise, caller must call
866 * setVisible themselves.
873 * @param ignoreMinSize
874 * Do not set the default minimum size for frame
876 public static synchronized void addInternalFrame(
877 final JInternalFrame frame, String title, boolean makeVisible,
878 int w, int h, boolean resizable, boolean ignoreMinSize)
881 // TODO: allow callers to determine X and Y position of frame (eg. via
883 // TODO: consider fixing method to update entries in the window submenu with
884 // the current window title
886 frame.setTitle(title);
887 if (frame.getWidth() < 1 || frame.getHeight() < 1)
891 // THIS IS A PUBLIC STATIC METHOD, SO IT MAY BE CALLED EVEN IN
892 // A HEADLESS STATE WHEN NO DESKTOP EXISTS. MUST RETURN
893 // IF JALVIEW IS RUNNING HEADLESS
894 // ///////////////////////////////////////////////
895 if (getInstance() == null || Jalview.isHeadlessMode())
904 frame.setMinimumSize(
905 new Dimension(DEFAULT_MIN_WIDTH, DEFAULT_MIN_HEIGHT));
907 // Set default dimension for Alignment Frame window.
908 // The Alignment Frame window could be added from a number of places,
910 // I did this here in order not to miss out on any Alignment frame.
911 if (frame instanceof AlignFrame)
913 frame.setMinimumSize(new Dimension(ALIGN_FRAME_DEFAULT_MIN_WIDTH,
914 ALIGN_FRAME_DEFAULT_MIN_HEIGHT));
918 frame.setVisible(makeVisible);
919 frame.setClosable(true);
920 frame.setResizable(resizable);
921 frame.setMaximizable(resizable);
922 frame.setIconifiable(resizable);
923 frame.setOpaque(Platform.isJS());
925 if (frame.getX() < 1 && frame.getY() < 1)
927 frame.setLocation(xOffset * openFrameCount,
928 yOffset * ((openFrameCount - 1) % 10) + yOffset);
932 * add an entry for the new frame in the Window menu
933 * (and remove it when the frame is closed)
935 JMenuItem menuItem = new JMenuItem(title);
936 frame.addInternalFrameListener(new InternalFrameAdapter()
939 public void internalFrameActivated(InternalFrameEvent evt)
941 JInternalFrame itf = getDesktopPane().getSelectedFrame();
944 if (itf instanceof AlignFrame)
946 Jalview.setCurrentAlignFrame((AlignFrame) itf);
953 public void internalFrameClosed(InternalFrameEvent evt)
955 PaintRefresher.RemoveComponent(frame);
958 * defensive check to prevent frames being
959 * added half off the window
961 if (openFrameCount > 0)
967 * ensure no reference to alignFrame retained by menu item listener
969 if (menuItem.getActionListeners().length > 0)
971 menuItem.removeActionListener(menuItem.getActionListeners()[0]);
973 getInstance().windowMenu.remove(menuItem);
977 menuItem.addActionListener(new ActionListener()
980 public void actionPerformed(ActionEvent e)
984 frame.setSelected(true);
985 frame.setIcon(false);
986 } catch (java.beans.PropertyVetoException ex)
988 // System.err.println(ex.toString());
993 setKeyBindings(frame);
995 getDesktopPane().add(frame);
997 getInstance().windowMenu.add(menuItem);
1002 frame.setSelected(true);
1003 frame.requestFocus();
1004 } catch (java.beans.PropertyVetoException ve)
1006 } catch (java.lang.ClassCastException cex)
1009 "Squashed a possible GUI implementation error. If you can recreate this, please look at http://issues.jalview.org/browse/JAL-869",
1015 * Add key bindings to a JInternalFrame so that Ctrl-W and Cmd-W will close the
1020 private static void setKeyBindings(JInternalFrame frame)
1022 final Action closeAction = new AbstractAction()
1025 public void actionPerformed(ActionEvent e)
1032 * set up key bindings for Ctrl-W and Cmd-W, with the same (Close) action
1034 KeyStroke ctrlWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
1035 InputEvent.CTRL_DOWN_MASK);
1036 KeyStroke cmdWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
1037 Toolkit.getDefaultToolkit().getMenuShortcutKeyMask());
1039 InputMap inputMap = frame
1040 .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
1041 String ctrlW = ctrlWKey.toString();
1042 inputMap.put(ctrlWKey, ctrlW);
1043 inputMap.put(cmdWKey, ctrlW);
1045 ActionMap actionMap = frame.getActionMap();
1046 actionMap.put(ctrlW, closeAction);
1050 public void lostOwnership(Clipboard clipboard, Transferable contents)
1054 Desktop.getInstance().jalviewClipboard = null;
1057 internalCopy = false;
1061 public void dragEnter(DropTargetDragEvent evt)
1066 public void dragExit(DropTargetEvent evt)
1071 public void dragOver(DropTargetDragEvent evt)
1076 public void dropActionChanged(DropTargetDragEvent evt)
1087 public void drop(DropTargetDropEvent evt)
1089 boolean success = true;
1090 // JAL-1552 - acceptDrop required before getTransferable call for
1091 // Java's Transferable for native dnd
1092 evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
1093 Transferable t = evt.getTransferable();
1094 List<Object> files = new ArrayList<>();
1095 List<DataSourceType> protocols = new ArrayList<>();
1099 Desktop.transferFromDropTarget(files, protocols, evt, t);
1100 } catch (Exception e)
1102 e.printStackTrace();
1110 for (int i = 0; i < files.size(); i++)
1112 // BH 2018 File or String
1113 Object file = files.get(i);
1114 String fileName = file.toString();
1115 DataSourceType protocol = (protocols == null)
1116 ? DataSourceType.FILE
1118 FileFormatI format = null;
1120 if (fileName.endsWith(".jar"))
1122 format = FileFormat.Jalview;
1127 format = new IdentifyFile().identify(file, protocol);
1129 if (file instanceof File)
1131 Platform.cacheFileData((File) file);
1133 new FileLoader().LoadFile(null, file, protocol, format);
1136 } catch (Exception ex)
1141 evt.dropComplete(success); // need this to ensure input focus is properly
1142 // transfered to any new windows created
1152 public void inputLocalFileMenuItem_actionPerformed(AlignViewport viewport)
1154 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
1155 JalviewFileChooser chooser = JalviewFileChooser
1156 .forRead(Cache.getProperty("LAST_DIRECTORY"), fileFormat, true);
1158 chooser.setFileView(new JalviewFileView());
1159 chooser.setDialogTitle(
1160 MessageManager.getString("label.open_local_file"));
1161 chooser.setToolTipText(MessageManager.getString("action.open"));
1163 chooser.setResponseHandler(0, new Runnable()
1168 File selectedFile = chooser.getSelectedFile();
1169 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1171 FileFormatI format = chooser.getSelectedFormat();
1174 * Call IdentifyFile to verify the file contains what its extension implies.
1175 * Skip this step for dynamically added file formats, because
1176 * IdentifyFile does not know how to recognise them.
1178 if (FileFormats.getInstance().isIdentifiable(format))
1182 format = new IdentifyFile().identify(selectedFile,
1183 DataSourceType.FILE);
1184 } catch (FileFormatException e)
1186 // format = null; //??
1190 new FileLoader().LoadFile(viewport, selectedFile,
1191 DataSourceType.FILE, format);
1194 chooser.showOpenDialog(this);
1198 * Shows a dialog for input of a URL at which to retrieve alignment data
1203 public void inputURLMenuItem_actionPerformed(AlignViewport viewport)
1205 // This construct allows us to have a wider textfield
1207 JLabel label = new JLabel(
1208 MessageManager.getString("label.input_file_url"));
1210 JPanel panel = new JPanel(new GridLayout(2, 1));
1214 * the URL to fetch is
1215 * Java: an editable combobox with history
1216 * JS: (pending JAL-3038) a plain text field
1219 String urlBase = "http://www.";
1220 if (Platform.isJS())
1222 history = new JTextField(urlBase, 35);
1231 JComboBox<String> asCombo = new JComboBox<>();
1232 asCombo.setPreferredSize(new Dimension(400, 20));
1233 asCombo.setEditable(true);
1234 asCombo.addItem(urlBase);
1235 String historyItems = Cache.getProperty("RECENT_URL");
1236 if (historyItems != null)
1238 for (String token : historyItems.split("\\t"))
1240 asCombo.addItem(token);
1247 Object[] options = new Object[] { MessageManager.getString("action.ok"),
1248 MessageManager.getString("action.cancel") };
1249 Runnable action = new Runnable()
1254 @SuppressWarnings("unchecked")
1255 String url = (history instanceof JTextField
1256 ? ((JTextField) history).getText()
1257 : ((JComboBox<String>) history).getSelectedItem()
1260 if (url.toLowerCase().endsWith(".jar"))
1262 if (viewport != null)
1264 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1265 FileFormat.Jalview);
1269 new FileLoader().LoadFile(url, DataSourceType.URL,
1270 FileFormat.Jalview);
1275 FileFormatI format = null;
1278 format = new IdentifyFile().identify(url, DataSourceType.URL);
1279 } catch (FileFormatException e)
1281 // TODO revise error handling, distinguish between
1282 // URL not found and response not valid
1287 String msg = MessageManager
1288 .formatMessage("label.couldnt_locate", url);
1289 JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
1291 MessageManager.getString("label.url_not_found"),
1292 JvOptionPane.WARNING_MESSAGE);
1297 if (viewport != null)
1299 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1304 new FileLoader().LoadFile(url, DataSourceType.URL, format);
1309 String dialogOption = MessageManager
1310 .getString("label.input_alignment_from_url");
1311 JvOptionPane.newOptionDialog(getDesktopPane())
1312 .setResponseHandler(0, action)
1313 .showInternalDialog(panel, dialogOption,
1314 JvOptionPane.YES_NO_CANCEL_OPTION,
1315 JvOptionPane.PLAIN_MESSAGE, null, options,
1316 MessageManager.getString("action.ok"));
1320 * Opens the CutAndPaste window for the user to paste an alignment in to
1323 * - if not null, the pasted alignment is added to the current
1324 * alignment; if null, to a new alignment window
1327 public void inputTextboxMenuItem_actionPerformed(
1328 AlignmentViewPanel viewPanel)
1330 CutAndPasteTransfer cap = new CutAndPasteTransfer();
1331 cap.setForInput(viewPanel);
1332 Desktop.addInternalFrame(cap,
1333 MessageManager.getString("label.cut_paste_alignmen_file"), true,
1343 Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
1344 jalview.bin.Cache.setProperty("SCREENGEOMETRY_WIDTH",
1346 jalview.bin.Cache.setProperty("SCREENGEOMETRY_HEIGHT",
1347 screen.height + "");
1348 storeLastKnownDimensions("", new Rectangle(getBounds().x, getBounds().y,
1349 getWidth(), getHeight()));
1351 if (jconsole != null)
1353 storeLastKnownDimensions("JAVA_CONSOLE_", jconsole.getBounds());
1354 jconsole.stopConsole();
1358 storeLastKnownDimensions("JALVIEW_RSS_WINDOW_", jvnews.getBounds());
1361 if (dialogExecutor != null)
1363 dialogExecutor.shutdownNow();
1365 closeAll_actionPerformed(null);
1367 if (groovyConsole != null)
1369 // suppress a possible repeat prompt to save script
1370 groovyConsole.setDirty(false);
1371 groovyConsole.exit();
1376 private void storeLastKnownDimensions(String string, Rectangle jc)
1378 jalview.bin.Cache.log.debug("Storing last known dimensions for "
1379 + string + ": x:" + jc.x + " y:" + jc.y + " width:" + jc.width
1380 + " height:" + jc.height);
1382 jalview.bin.Cache.setProperty(string + "SCREEN_X", jc.x + "");
1383 jalview.bin.Cache.setProperty(string + "SCREEN_Y", jc.y + "");
1384 jalview.bin.Cache.setProperty(string + "SCREEN_WIDTH", jc.width + "");
1385 jalview.bin.Cache.setProperty(string + "SCREEN_HEIGHT", jc.height + "");
1395 public void aboutMenuItem_actionPerformed(ActionEvent e)
1397 // StringBuffer message = getAboutMessage(false);
1398 // JvOptionPane.showInternalMessageDialog(Desktop.getDesktop(),
1400 // message.toString(), "About Jalview", JvOptionPane.INFORMATION_MESSAGE);
1401 new Thread(new Runnable()
1406 new SplashScreen(true);
1411 public StringBuffer getAboutMessage(boolean shortv)
1413 StringBuffer message = new StringBuffer();
1414 message.append("<html>");
1417 message.append("<h1><strong>Version: "
1418 + jalview.bin.Cache.getProperty("VERSION")
1419 + "</strong></h1>");
1420 message.append("<strong>Last Updated: <em>"
1421 + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown")
1422 + "</em></strong>");
1428 message.append("<strong>Version "
1429 + jalview.bin.Cache.getProperty("VERSION")
1430 + "; last updated: "
1431 + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown"));
1434 if (jalview.bin.Cache.getDefault("LATEST_VERSION", "Checking")
1435 .equals("Checking"))
1437 message.append("<br>...Checking latest version...</br>");
1439 else if (!jalview.bin.Cache.getDefault("LATEST_VERSION", "Checking")
1440 .equals(jalview.bin.Cache.getProperty("VERSION")))
1442 boolean red = false;
1443 if (jalview.bin.Cache.getProperty("VERSION").toLowerCase()
1444 .indexOf("automated build") == -1)
1447 // Displayed when code version and jnlp version do not match and code
1448 // version is not a development build
1449 message.append("<div style=\"color: #FF0000;font-style: bold;\">");
1452 message.append("<br>!! Version "
1453 + jalview.bin.Cache.getDefault("LATEST_VERSION",
1455 + " is available for download from "
1456 + jalview.bin.Cache.getDefault("www.jalview.org",
1457 "http://www.jalview.org")
1461 message.append("</div>");
1464 message.append("<br>Authors: " + jalview.bin.Cache.getDefault(
1466 "The Jalview Authors (See AUTHORS file for current list)")
1467 + "<br><br>Development managed by The Barton Group, University of Dundee, Scotland, UK.<br>"
1468 + "<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"
1469 + "<br><br>If you use Jalview, please cite:"
1470 + "<br>Waterhouse, A.M., Procter, J.B., Martin, D.M.A, Clamp, M. and Barton, G. J. (2009)"
1471 + "<br>Jalview Version 2 - a multiple sequence alignment editor and analysis workbench"
1472 + "<br>Bioinformatics doi: 10.1093/bioinformatics/btp033"
1478 * Action on requesting Help documentation
1481 public void documentationMenuItem_actionPerformed()
1485 if (Platform.isJS())
1487 BrowserLauncher.openURL("http://www.jalview.org/help.html");
1496 Help.showHelpWindow();
1498 } catch (Exception ex)
1500 System.err.println("Error opening help: " + ex.getMessage());
1505 public void closeAll_actionPerformed(ActionEvent e)
1507 // TODO show a progress bar while closing?
1508 JInternalFrame[] frames = getDesktopPane().getAllFrames();
1509 for (int i = 0; i < frames.length; i++)
1513 frames[i].setClosed(true);
1514 } catch (java.beans.PropertyVetoException ex)
1518 Jalview.setCurrentAlignFrame(null);
1519 System.out.println("ALL CLOSED");
1520 if (v_client != null)
1522 // TODO clear binding to vamsas document objects on close_all
1526 * reset state of singleton objects as appropriate (clear down session state
1527 * when all windows are closed)
1529 StructureSelectionManager ssm = StructureSelectionManager
1530 .getStructureSelectionManager(this);
1538 public void raiseRelated_actionPerformed(ActionEvent e)
1540 reorderAssociatedWindows(false, false);
1544 public void minimizeAssociated_actionPerformed(ActionEvent e)
1546 reorderAssociatedWindows(true, false);
1549 void closeAssociatedWindows()
1551 reorderAssociatedWindows(false, true);
1557 * @seejalview.jbgui.GDesktop#garbageCollect_actionPerformed(java.awt.event.
1561 protected void garbageCollect_actionPerformed(ActionEvent e)
1563 // We simply collect the garbage
1564 jalview.bin.Cache.log.debug("Collecting garbage...");
1566 jalview.bin.Cache.log.debug("Finished garbage collection.");
1573 * jalview.jbgui.GDesktop#showMemusage_actionPerformed(java.awt.event.ActionEvent
1577 protected void showMemusage_actionPerformed(ActionEvent e)
1579 getDesktopPane().showMemoryUsage(showMemusage.isSelected());
1586 * jalview.jbgui.GDesktop#showConsole_actionPerformed(java.awt.event.ActionEvent
1590 protected void showConsole_actionPerformed(ActionEvent e)
1592 showConsole(showConsole.isSelected());
1595 Console jconsole = null;
1598 * control whether the java console is visible or not
1602 void showConsole(boolean selected)
1604 // TODO: decide if we should update properties file
1605 if (jconsole != null) // BH 2018
1607 showConsole.setSelected(selected);
1608 Cache.setProperty("SHOW_JAVA_CONSOLE",
1609 Boolean.valueOf(selected).toString());
1610 jconsole.setVisible(selected);
1614 void reorderAssociatedWindows(boolean minimize, boolean close)
1616 JInternalFrame[] frames = getDesktopPane().getAllFrames();
1617 if (frames == null || frames.length < 1)
1622 AlignmentViewport source = null, target = null;
1623 if (frames[0] instanceof AlignFrame)
1625 source = ((AlignFrame) frames[0]).getCurrentView();
1627 else if (frames[0] instanceof TreePanel)
1629 source = ((TreePanel) frames[0]).getViewPort();
1631 else if (frames[0] instanceof PCAPanel)
1633 source = ((PCAPanel) frames[0]).av;
1635 else if (frames[0].getContentPane() instanceof PairwiseAlignPanel)
1637 source = ((PairwiseAlignPanel) frames[0].getContentPane()).av;
1642 for (int i = 0; i < frames.length; i++)
1645 if (frames[i] == null)
1649 if (frames[i] instanceof AlignFrame)
1651 target = ((AlignFrame) frames[i]).getCurrentView();
1653 else if (frames[i] instanceof TreePanel)
1655 target = ((TreePanel) frames[i]).getViewPort();
1657 else if (frames[i] instanceof PCAPanel)
1659 target = ((PCAPanel) frames[i]).av;
1661 else if (frames[i].getContentPane() instanceof PairwiseAlignPanel)
1663 target = ((PairwiseAlignPanel) frames[i].getContentPane()).av;
1666 if (source == target)
1672 frames[i].setClosed(true);
1676 frames[i].setIcon(minimize);
1679 frames[i].toFront();
1683 } catch (java.beans.PropertyVetoException ex)
1698 protected void preferences_actionPerformed(ActionEvent e)
1704 * Prompts the user to choose a file and then saves the Jalview state as a
1705 * Jalview project file
1708 public void saveState_actionPerformed()
1710 saveState_actionPerformed(false);
1713 public void saveState_actionPerformed(boolean saveAs)
1715 java.io.File projectFile = getProjectFile();
1716 // autoSave indicates we already have a file and don't need to ask
1717 boolean autoSave = projectFile != null && !saveAs
1718 && BackupFiles.getEnabled();
1720 // System.out.println("autoSave="+autoSave+", projectFile='"+projectFile+"',
1721 // saveAs="+saveAs+", Backups
1722 // "+(BackupFiles.getEnabled()?"enabled":"disabled"));
1724 boolean approveSave = false;
1727 JalviewFileChooser chooser = new JalviewFileChooser("jvp",
1730 chooser.setFileView(new JalviewFileView());
1731 chooser.setDialogTitle(MessageManager.getString("label.save_state"));
1733 int value = chooser.showSaveDialog(this);
1735 if (value == JalviewFileChooser.APPROVE_OPTION)
1737 projectFile = chooser.getSelectedFile();
1738 setProjectFile(projectFile);
1743 if (approveSave || autoSave)
1745 final Desktop me = this;
1746 final java.io.File chosenFile = projectFile;
1747 new Thread(new Runnable()
1752 // TODO: refactor to Jalview desktop session controller action.
1753 setProgressBar(MessageManager.formatMessage(
1754 "label.saving_jalview_project", new Object[]
1755 { chosenFile.getName() }), chosenFile.hashCode());
1756 jalview.bin.Cache.setProperty("LAST_DIRECTORY",
1757 chosenFile.getParent());
1758 // TODO catch and handle errors for savestate
1759 // TODO prevent user from messing with the Desktop whilst we're saving
1762 boolean doBackup = BackupFiles.getEnabled();
1763 BackupFiles backupfiles = doBackup ? new BackupFiles(chosenFile) : null;
1765 new Jalview2XML().saveState(doBackup ? backupfiles.getTempFile() : chosenFile);
1769 backupfiles.setWriteSuccess(true);
1770 backupfiles.rollBackupsAndRenameTempFile();
1772 } catch (OutOfMemoryError oom)
1774 new OOMWarning("Whilst saving current state to "
1775 + chosenFile.getName(), oom);
1776 } catch (Exception ex)
1778 Cache.log.error("Problems whilst trying to save to "
1779 + chosenFile.getName(), ex);
1780 JvOptionPane.showMessageDialog(me,
1781 MessageManager.formatMessage(
1782 "label.error_whilst_saving_current_state_to",
1784 { chosenFile.getName() }),
1785 MessageManager.getString("label.couldnt_save_project"),
1786 JvOptionPane.WARNING_MESSAGE);
1788 setProgressBar(null, chosenFile.hashCode());
1795 public void saveAsState_actionPerformed(ActionEvent e)
1797 saveState_actionPerformed(true);
1800 protected void setProjectFile(File choice)
1802 this.projectFile = choice;
1805 public File getProjectFile()
1807 return this.projectFile;
1811 * Shows a file chooser dialog and tries to read in the selected file as a
1815 public void loadState_actionPerformed()
1817 final String[] suffix = new String[] { "jvp", "jar" };
1818 final String[] desc = new String[] { "Jalview Project",
1819 "Jalview Project (old)" };
1820 JalviewFileChooser chooser = new JalviewFileChooser(
1821 Cache.getProperty("LAST_DIRECTORY"), suffix, desc,
1822 "Jalview Project", true, true); // last two booleans: allFiles,
1824 chooser.setFileView(new JalviewFileView());
1825 chooser.setDialogTitle(MessageManager.getString("label.restore_state"));
1826 chooser.setResponseHandler(0, new Runnable()
1831 File selectedFile = chooser.getSelectedFile();
1832 setProjectFile(selectedFile);
1833 String choice = selectedFile.getAbsolutePath();
1834 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1835 new Thread(new Runnable()
1842 new Jalview2XML().loadJalviewAlign(choice);
1843 } catch (OutOfMemoryError oom)
1845 new OOMWarning("Whilst loading project from " + choice, oom);
1846 } catch (Exception ex)
1849 "Problems whilst loading project from " + choice, ex);
1850 JvOptionPane.showMessageDialog(Desktop.getDesktopPane(),
1851 MessageManager.formatMessage(
1852 "label.error_whilst_loading_project_from",
1855 MessageManager.getString("label.couldnt_load_project"),
1856 JvOptionPane.WARNING_MESSAGE);
1863 chooser.showOpenDialog(this);
1867 public void inputSequence_actionPerformed(ActionEvent e)
1869 new SequenceFetcher(this);
1872 JPanel progressPanel;
1874 ArrayList<JPanel> fileLoadingPanels = new ArrayList<>();
1876 public void startLoading(final Object fileName)
1878 if (fileLoadingCount == 0)
1880 fileLoadingPanels.add(addProgressPanel(MessageManager
1881 .formatMessage("label.loading_file", new Object[]
1887 private JPanel addProgressPanel(String string)
1889 if (progressPanel == null)
1891 progressPanel = new JPanel(new GridLayout(1, 1));
1892 totalProgressCount = 0;
1893 getInstance().getContentPane().add(progressPanel, BorderLayout.SOUTH);
1895 JPanel thisprogress = new JPanel(new BorderLayout(10, 5));
1896 JProgressBar progressBar = new JProgressBar();
1897 progressBar.setIndeterminate(true);
1899 thisprogress.add(new JLabel(string), BorderLayout.WEST);
1901 thisprogress.add(progressBar, BorderLayout.CENTER);
1902 progressPanel.add(thisprogress);
1903 ((GridLayout) progressPanel.getLayout()).setRows(
1904 ((GridLayout) progressPanel.getLayout()).getRows() + 1);
1905 ++totalProgressCount;
1906 getInstance().validate();
1907 return thisprogress;
1910 int totalProgressCount = 0;
1912 private void removeProgressPanel(JPanel progbar)
1914 if (progressPanel != null)
1916 synchronized (progressPanel)
1918 progressPanel.remove(progbar);
1919 GridLayout gl = (GridLayout) progressPanel.getLayout();
1920 gl.setRows(gl.getRows() - 1);
1921 if (--totalProgressCount < 1)
1923 this.getContentPane().remove(progressPanel);
1924 progressPanel = null;
1931 public void stopLoading()
1934 if (fileLoadingCount < 1)
1936 while (fileLoadingPanels.size() > 0)
1938 removeProgressPanel(fileLoadingPanels.remove(0));
1940 fileLoadingPanels.clear();
1941 fileLoadingCount = 0;
1946 public static int getViewCount(String alignmentId)
1948 AlignmentViewport[] aps = getViewports(alignmentId);
1949 return (aps == null) ? 0 : aps.length;
1954 * @param alignmentId
1955 * - if null, all sets are returned
1956 * @return all AlignmentPanels concerning the alignmentId sequence set
1958 public static AlignmentPanel[] getAlignmentPanels(String alignmentId)
1960 if (Desktop.getDesktopPane() == null)
1962 // no frames created and in headless mode
1963 // TODO: verify that frames are recoverable when in headless mode
1966 List<AlignmentPanel> aps = new ArrayList<>();
1967 AlignFrame[] frames = getAlignFrames();
1972 for (AlignFrame af : frames)
1974 for (AlignmentPanel ap : af.alignPanels)
1976 if (alignmentId == null
1977 || alignmentId.equals(ap.av.getSequenceSetId()))
1983 if (aps.size() == 0)
1987 AlignmentPanel[] vap = aps.toArray(new AlignmentPanel[aps.size()]);
1992 * get all the viewports on an alignment.
1994 * @param sequenceSetId
1995 * unique alignment id (may be null - all viewports returned in that
1997 * @return all viewports on the alignment bound to sequenceSetId
1999 public static AlignmentViewport[] getViewports(String sequenceSetId)
2001 List<AlignmentViewport> viewp = new ArrayList<>();
2002 if (getDesktopPane() != null)
2004 AlignFrame[] frames = Desktop.getAlignFrames();
2006 for (AlignFrame afr : frames)
2008 if (sequenceSetId == null || afr.getViewport().getSequenceSetId()
2009 .equals(sequenceSetId))
2011 if (afr.alignPanels != null)
2013 for (AlignmentPanel ap : afr.alignPanels)
2015 if (sequenceSetId == null
2016 || sequenceSetId.equals(ap.av.getSequenceSetId()))
2024 viewp.add(afr.getViewport());
2028 if (viewp.size() > 0)
2030 return viewp.toArray(new AlignmentViewport[viewp.size()]);
2037 * Explode the views in the given frame into separate AlignFrame
2041 public static void explodeViews(AlignFrame af)
2043 int size = af.alignPanels.size();
2049 for (int i = 0; i < size; i++)
2051 AlignmentPanel ap = af.alignPanels.get(i);
2052 AlignFrame newaf = new AlignFrame(ap);
2055 * Restore the view's last exploded frame geometry if known. Multiple
2056 * views from one exploded frame share and restore the same (frame)
2057 * position and size.
2059 Rectangle geometry = ap.av.getExplodedGeometry();
2060 if (geometry != null)
2062 newaf.setBounds(geometry);
2065 ap.av.setGatherViewsHere(false);
2067 addInternalFrame(newaf, af.getTitle(), AlignFrame.DEFAULT_WIDTH,
2068 AlignFrame.DEFAULT_HEIGHT);
2071 af.alignPanels.clear();
2072 af.closeMenuItem_actionPerformed(true);
2077 * Gather expanded views (separate AlignFrame's) with the same sequence set
2078 * identifier back in to this frame as additional views, and close the expanded
2079 * views. Note the expanded frames may themselves have multiple views. We take
2084 public void gatherViews(AlignFrame source)
2086 source.viewport.setGatherViewsHere(true);
2087 source.viewport.setExplodedGeometry(source.getBounds());
2088 JInternalFrame[] frames = getDesktopPane().getAllFrames();
2089 String viewId = source.viewport.getSequenceSetId();
2091 for (int t = 0; t < frames.length; t++)
2093 if (frames[t] instanceof AlignFrame && frames[t] != source)
2095 AlignFrame af = (AlignFrame) frames[t];
2096 boolean gatherThis = false;
2097 for (int a = 0; a < af.alignPanels.size(); a++)
2099 AlignmentPanel ap = af.alignPanels.get(a);
2100 if (viewId.equals(ap.av.getSequenceSetId()))
2103 ap.av.setGatherViewsHere(false);
2104 ap.av.setExplodedGeometry(af.getBounds());
2105 source.addAlignmentPanel(ap, false);
2111 af.alignPanels.clear();
2112 af.closeMenuItem_actionPerformed(true);
2119 jalview.gui.VamsasApplication v_client = null;
2122 public void vamsasImport_actionPerformed(ActionEvent e)
2124 // TODO: JAL-3048 not needed for Jalview-JS
2126 if (v_client == null)
2128 // Load and try to start a session.
2129 JalviewFileChooser chooser = new JalviewFileChooser(
2130 jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
2132 chooser.setFileView(new JalviewFileView());
2133 chooser.setDialogTitle(
2134 MessageManager.getString("label.open_saved_vamsas_session"));
2135 chooser.setToolTipText(MessageManager.getString(
2136 "label.select_vamsas_session_opened_as_new_vamsas_session"));
2138 int value = chooser.showOpenDialog(this);
2140 if (value == JalviewFileChooser.APPROVE_OPTION)
2142 String fle = chooser.getSelectedFile().toString();
2143 if (!vamsasImport(chooser.getSelectedFile()))
2145 JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
2146 MessageManager.formatMessage(
2147 "label.couldnt_import_as_vamsas_session",
2151 .getString("label.vamsas_document_import_failed"),
2152 JvOptionPane.ERROR_MESSAGE);
2158 jalview.bin.Cache.log.error(
2159 "Implementation error - load session from a running session is not supported.");
2164 * import file into a new vamsas session (uses jalview.gui.VamsasApplication)
2167 * @return true if import was a success and a session was started.
2169 public boolean vamsasImport(URL url)
2171 // TODO: create progress bar
2172 if (v_client != null)
2175 jalview.bin.Cache.log.error(
2176 "Implementation error - load session from a running session is not supported.");
2182 // copy the URL content to a temporary local file
2183 // TODO: be a bit cleverer here with nio (?!)
2184 File file = File.createTempFile("vdocfromurl", ".vdj");
2185 FileOutputStream fos = new FileOutputStream(file);
2186 BufferedInputStream bis = new BufferedInputStream(url.openStream());
2187 byte[] buffer = new byte[2048];
2189 while ((ln = bis.read(buffer)) > -1)
2191 fos.write(buffer, 0, ln);
2195 v_client = new jalview.gui.VamsasApplication(this, file,
2196 url.toExternalForm());
2197 } catch (Exception ex)
2199 jalview.bin.Cache.log.error(
2200 "Failed to create new vamsas session from contents of URL "
2205 setupVamsasConnectedGui();
2206 v_client.initial_update(); // TODO: thread ?
2207 return v_client.inSession();
2211 * import file into a new vamsas session (uses jalview.gui.VamsasApplication)
2214 * @return true if import was a success and a session was started.
2216 public boolean vamsasImport(File file)
2218 if (v_client != null)
2221 jalview.bin.Cache.log.error(
2222 "Implementation error - load session from a running session is not supported.");
2226 setProgressBar(MessageManager.formatMessage(
2227 "status.importing_vamsas_session_from", new Object[]
2228 { file.getName() }), file.hashCode());
2231 v_client = new jalview.gui.VamsasApplication(this, file, null);
2232 } catch (Exception ex)
2234 setProgressBar(MessageManager.formatMessage(
2235 "status.importing_vamsas_session_from", new Object[]
2236 { file.getName() }), file.hashCode());
2237 jalview.bin.Cache.log.error(
2238 "New vamsas session from existing session file failed:", ex);
2241 setupVamsasConnectedGui();
2242 v_client.initial_update(); // TODO: thread ?
2243 setProgressBar(MessageManager.formatMessage(
2244 "status.importing_vamsas_session_from", new Object[]
2245 { file.getName() }), file.hashCode());
2246 return v_client.inSession();
2249 public boolean joinVamsasSession(String mysesid)
2251 if (v_client != null)
2253 throw new Error(MessageManager
2254 .getString("error.try_join_vamsas_session_another"));
2256 if (mysesid == null)
2259 MessageManager.getString("error.invalid_vamsas_session_id"));
2261 v_client = new VamsasApplication(this, mysesid);
2262 setupVamsasConnectedGui();
2263 v_client.initial_update();
2264 return (v_client.inSession());
2268 public void vamsasStart_actionPerformed(ActionEvent e)
2270 if (v_client == null)
2273 // we just start a default session for moment.
2275 * JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache.
2276 * getProperty("LAST_DIRECTORY"));
2278 * chooser.setFileView(new JalviewFileView());
2279 * chooser.setDialogTitle("Load Vamsas file");
2280 * chooser.setToolTipText("Import");
2282 * int value = chooser.showOpenDialog(this);
2284 * if (value == JalviewFileChooser.APPROVE_OPTION) { v_client = new
2285 * jalview.gui.VamsasApplication(this, chooser.getSelectedFile());
2287 v_client = new VamsasApplication(this);
2288 setupVamsasConnectedGui();
2289 v_client.initial_update(); // TODO: thread ?
2293 // store current data in session.
2294 v_client.push_update(); // TODO: thread
2298 protected void setupVamsasConnectedGui()
2300 vamsasStart.setText(MessageManager.getString("label.session_update"));
2301 vamsasSave.setVisible(true);
2302 vamsasStop.setVisible(true);
2303 vamsasImport.setVisible(false); // Document import to existing session is
2304 // not possible for vamsas-client-1.0.
2307 protected void setupVamsasDisconnectedGui()
2309 vamsasSave.setVisible(false);
2310 vamsasStop.setVisible(false);
2311 vamsasImport.setVisible(true);
2313 .setText(MessageManager.getString("label.new_vamsas_session"));
2317 public void vamsasStop_actionPerformed(ActionEvent e)
2319 if (v_client != null)
2321 v_client.end_session();
2323 setupVamsasDisconnectedGui();
2327 protected void buildVamsasStMenu()
2329 if (v_client == null)
2331 String[] sess = null;
2334 sess = VamsasApplication.getSessionList();
2335 } catch (Exception e)
2337 jalview.bin.Cache.log.warn("Problem getting current sessions list.",
2343 jalview.bin.Cache.log.debug(
2344 "Got current sessions list: " + sess.length + " entries.");
2345 VamsasStMenu.removeAll();
2346 for (int i = 0; i < sess.length; i++)
2348 JMenuItem sessit = new JMenuItem();
2349 sessit.setText(sess[i]);
2350 sessit.setToolTipText(MessageManager
2351 .formatMessage("label.connect_to_session", new Object[]
2353 final Desktop dsktp = this;
2354 final String mysesid = sess[i];
2355 sessit.addActionListener(new ActionListener()
2359 public void actionPerformed(ActionEvent e)
2361 if (dsktp.v_client == null)
2363 Thread rthr = new Thread(new Runnable()
2369 dsktp.v_client = new VamsasApplication(dsktp, mysesid);
2370 dsktp.setupVamsasConnectedGui();
2371 dsktp.v_client.initial_update();
2379 VamsasStMenu.add(sessit);
2381 // don't show an empty menu.
2382 VamsasStMenu.setVisible(sess.length > 0);
2387 jalview.bin.Cache.log.debug("No current vamsas sessions.");
2388 VamsasStMenu.removeAll();
2389 VamsasStMenu.setVisible(false);
2394 // Not interested in the content. Just hide ourselves.
2395 VamsasStMenu.setVisible(false);
2400 public void vamsasSave_actionPerformed(ActionEvent e)
2402 // TODO: JAL-3048 not needed for Jalview-JS
2404 if (v_client != null)
2406 // TODO: VAMSAS DOCUMENT EXTENSION is VDJ
2407 JalviewFileChooser chooser = new JalviewFileChooser("vdj",
2410 chooser.setFileView(new JalviewFileView());
2411 chooser.setDialogTitle(MessageManager
2412 .getString("label.save_vamsas_document_archive"));
2414 int value = chooser.showSaveDialog(this);
2416 if (value == JalviewFileChooser.APPROVE_OPTION)
2418 java.io.File choice = chooser.getSelectedFile();
2419 JPanel progpanel = addProgressPanel(MessageManager
2420 .formatMessage("label.saving_vamsas_doc", new Object[]
2421 { choice.getName() }));
2422 Cache.setProperty("LAST_DIRECTORY", choice.getParent());
2423 String warnmsg = null;
2424 String warnttl = null;
2427 v_client.vclient.storeDocument(choice);
2430 warnttl = "Serious Problem saving Vamsas Document";
2431 warnmsg = ex.toString();
2432 jalview.bin.Cache.log
2433 .error("Error Whilst saving document to " + choice, ex);
2435 } catch (Exception ex)
2437 warnttl = "Problem saving Vamsas Document.";
2438 warnmsg = ex.toString();
2439 jalview.bin.Cache.log.warn(
2440 "Exception Whilst saving document to " + choice, ex);
2443 removeProgressPanel(progpanel);
2444 if (warnmsg != null)
2446 JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
2448 warnmsg, warnttl, JvOptionPane.ERROR_MESSAGE);
2454 JPanel vamUpdate = null;
2457 * hide vamsas user gui bits when a vamsas document event is being handled.
2460 * true to hide gui, false to reveal gui
2462 public void setVamsasUpdate(boolean b)
2464 Cache.log.debug("Setting gui for Vamsas update "
2465 + (b ? "in progress" : "finished"));
2467 if (vamUpdate != null)
2469 this.removeProgressPanel(vamUpdate);
2473 vamUpdate = this.addProgressPanel(
2474 MessageManager.getString("label.updating_vamsas_session"));
2476 vamsasStart.setVisible(!b);
2477 vamsasStop.setVisible(!b);
2478 vamsasSave.setVisible(!b);
2481 public JInternalFrame[] getAllFrames()
2483 return getDesktopPane().getAllFrames();
2487 * Checks the given url to see if it gives a response indicating that the user
2488 * should be informed of a new questionnaire.
2492 public void checkForQuestionnaire(String url)
2494 UserQuestionnaireCheck jvq = new UserQuestionnaireCheck(url);
2495 // javax.swing.SwingUtilities.invokeLater(jvq);
2496 new Thread(jvq).start();
2499 public void checkURLLinks()
2501 // Thread off the URL link checker
2502 addDialogThread(new Runnable()
2507 if (Cache.getDefault("CHECKURLLINKS", true))
2509 // check what the actual links are - if it's just the default don't
2510 // bother with the warning
2511 List<String> links = Preferences.sequenceUrlLinks
2514 // only need to check links if there is one with a
2515 // SEQUENCE_ID which is not the default EMBL_EBI link
2516 ListIterator<String> li = links.listIterator();
2517 boolean check = false;
2518 List<JLabel> urls = new ArrayList<>();
2519 while (li.hasNext())
2521 String link = li.next();
2522 if (link.contains(UrlConstants.SEQUENCE_ID)
2523 && !UrlConstants.isDefaultString(link))
2526 int barPos = link.indexOf("|");
2527 String urlMsg = barPos == -1 ? link
2528 : link.substring(0, barPos) + ": "
2529 + link.substring(barPos + 1);
2530 urls.add(new JLabel(urlMsg));
2538 // ask user to check in case URL links use old style tokens
2539 // ($SEQUENCE_ID$ for sequence id _or_ accession id)
2540 JPanel msgPanel = new JPanel();
2541 msgPanel.setLayout(new BoxLayout(msgPanel, BoxLayout.PAGE_AXIS));
2542 msgPanel.add(Box.createVerticalGlue());
2543 JLabel msg = new JLabel(MessageManager
2544 .getString("label.SEQUENCE_ID_for_DB_ACCESSION1"));
2545 JLabel msg2 = new JLabel(MessageManager
2546 .getString("label.SEQUENCE_ID_for_DB_ACCESSION2"));
2548 for (JLabel url : urls)
2554 final JCheckBox jcb = new JCheckBox(
2555 MessageManager.getString("label.do_not_display_again"));
2556 jcb.addActionListener(new ActionListener()
2559 public void actionPerformed(ActionEvent e)
2561 // update Cache settings for "don't show this again"
2562 boolean showWarningAgain = !jcb.isSelected();
2563 Cache.setProperty("CHECKURLLINKS",
2564 Boolean.valueOf(showWarningAgain).toString());
2569 JvOptionPane.showMessageDialog(Desktop.getDesktopPane(), msgPanel,
2571 .getString("label.SEQUENCE_ID_no_longer_used"),
2572 JvOptionPane.WARNING_MESSAGE);
2579 * Proxy class for JDesktopPane which optionally displays the current memory
2580 * usage and highlights the desktop area with a red bar if free memory runs low.
2584 public class MyDesktopPane extends JDesktopPane
2587 private static final float ONE_MB = 1048576f;
2589 boolean showMemoryUsage = false;
2593 java.text.NumberFormat df;
2595 float maxMemory, allocatedMemory, freeMemory, totalFreeMemory,
2598 public MyDesktopPane(boolean showMemoryUsage)
2600 showMemoryUsage(showMemoryUsage);
2603 public void showMemoryUsage(boolean showMemory)
2605 this.showMemoryUsage = showMemory;
2608 Thread worker = new Thread(this);
2614 public boolean isShowMemoryUsage()
2616 return showMemoryUsage;
2622 df = java.text.NumberFormat.getNumberInstance();
2623 df.setMaximumFractionDigits(2);
2624 runtime = Runtime.getRuntime();
2626 while (showMemoryUsage)
2630 maxMemory = runtime.maxMemory() / ONE_MB;
2631 allocatedMemory = runtime.totalMemory() / ONE_MB;
2632 freeMemory = runtime.freeMemory() / ONE_MB;
2633 totalFreeMemory = freeMemory + (maxMemory - allocatedMemory);
2635 percentUsage = (totalFreeMemory / maxMemory) * 100;
2637 // if (percentUsage < 20)
2639 // border1 = BorderFactory.createMatteBorder(12, 12, 12, 12,
2641 // instance.set.setBorder(border1);
2644 // sleep after showing usage
2646 } catch (Exception ex)
2648 ex.printStackTrace();
2654 public void paintComponent(Graphics g)
2656 if (showMemoryUsage && g != null && df != null)
2658 if (percentUsage < 20)
2660 g.setColor(Color.red);
2662 FontMetrics fm = g.getFontMetrics();
2665 g.drawString(MessageManager.formatMessage("label.memory_stats",
2667 { df.format(totalFreeMemory), df.format(maxMemory),
2668 df.format(percentUsage) }),
2669 10, getHeight() - fm.getHeight());
2676 * Accessor method to quickly get all the AlignmentFrames loaded.
2678 * @return an array of AlignFrame, or null if none found
2680 public static AlignFrame[] getAlignFrames()
2682 if (Jalview.isHeadlessMode())
2684 // Desktop.getDesktop() is null in headless mode
2685 return new AlignFrame[] { Jalview.getCurrentAlignFrame() };
2688 JInternalFrame[] frames = Desktop.getDesktopPane().getAllFrames();
2694 List<AlignFrame> avp = new ArrayList<>();
2696 for (int i = frames.length - 1; i > -1; i--)
2698 if (frames[i] instanceof AlignFrame)
2700 avp.add((AlignFrame) frames[i]);
2702 else if (frames[i] instanceof SplitFrame)
2705 * Also check for a split frame containing an AlignFrame
2707 GSplitFrame sf = (GSplitFrame) frames[i];
2708 if (sf.getTopFrame() instanceof AlignFrame)
2710 avp.add((AlignFrame) sf.getTopFrame());
2712 if (sf.getBottomFrame() instanceof AlignFrame)
2714 avp.add((AlignFrame) sf.getBottomFrame());
2718 if (avp.size() == 0)
2722 AlignFrame afs[] = avp.toArray(new AlignFrame[avp.size()]);
2727 * Returns an array of any AppJmol frames in the Desktop (or null if none).
2731 public GStructureViewer[] getJmols()
2733 JInternalFrame[] frames = Desktop.getDesktopPane().getAllFrames();
2739 List<GStructureViewer> avp = new ArrayList<>();
2741 for (int i = frames.length - 1; i > -1; i--)
2743 if (frames[i] instanceof AppJmol)
2745 GStructureViewer af = (GStructureViewer) frames[i];
2749 if (avp.size() == 0)
2753 GStructureViewer afs[] = avp.toArray(new GStructureViewer[avp.size()]);
2758 * Add Groovy Support to Jalview
2761 public void groovyShell_actionPerformed()
2765 openGroovyConsole();
2766 } catch (Exception ex)
2768 jalview.bin.Cache.log.error("Groovy Shell Creation failed.", ex);
2769 JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
2771 MessageManager.getString("label.couldnt_create_groovy_shell"),
2772 MessageManager.getString("label.groovy_support_failed"),
2773 JvOptionPane.ERROR_MESSAGE);
2778 * Open the Groovy console
2780 private void openGroovyConsole()
2782 if (groovyConsole == null)
2784 groovyConsole = new groovy.ui.Console();
2785 groovyConsole.setVariable("Jalview", this);
2786 groovyConsole.run();
2789 * We allow only one console at a time, so that AlignFrame menu option
2790 * 'Calculate | Run Groovy script' is unambiguous.
2791 * Disable 'Groovy Console', and enable 'Run script', when the console is
2792 * opened, and the reverse when it is closed
2794 Window window = (Window) groovyConsole.getFrame();
2795 window.addWindowListener(new WindowAdapter()
2798 public void windowClosed(WindowEvent e)
2801 * rebind CMD-Q from Groovy Console to Jalview Quit
2804 enableExecuteGroovy(false);
2810 * show Groovy console window (after close and reopen)
2812 ((Window) groovyConsole.getFrame()).setVisible(true);
2815 * if we got this far, enable 'Run Groovy' in AlignFrame menus
2816 * and disable opening a second console
2818 enableExecuteGroovy(true);
2822 * Bind Ctrl/Cmd-Q to Quit - for reset as Groovy Console takes over this binding
2825 protected void addQuitHandler()
2827 getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
2828 .put(KeyStroke.getKeyStroke(KeyEvent.VK_Q,
2829 Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()),
2831 getRootPane().getActionMap().put("Quit", new AbstractAction()
2834 public void actionPerformed(ActionEvent e)
2842 * Enable or disable 'Run Groovy script' in AlignFrame calculate menus
2845 * true if Groovy console is open
2847 public void enableExecuteGroovy(boolean enabled)
2850 * disable opening a second Groovy console
2851 * (or re-enable when the console is closed)
2853 groovyShell.setEnabled(!enabled);
2855 AlignFrame[] alignFrames = getAlignFrames();
2856 if (alignFrames != null)
2858 for (AlignFrame af : alignFrames)
2860 af.setGroovyEnabled(enabled);
2866 * Progress bars managed by the IProgressIndicator method.
2868 private Hashtable<Long, JPanel> progressBars;
2870 private Hashtable<Long, IProgressIndicatorHandler> progressBarHandlers;
2875 * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
2878 public void setProgressBar(String message, long id)
2880 Platform.timeCheck("Desktop " + message, Platform.TIME_MARK);
2882 if (progressBars == null)
2884 progressBars = new Hashtable<>();
2885 progressBarHandlers = new Hashtable<>();
2888 if (progressBars.get(new Long(id)) != null)
2890 JPanel panel = progressBars.remove(new Long(id));
2891 if (progressBarHandlers.contains(new Long(id)))
2893 progressBarHandlers.remove(new Long(id));
2895 removeProgressPanel(panel);
2899 progressBars.put(new Long(id), addProgressPanel(message));
2906 * @see jalview.gui.IProgressIndicator#registerHandler(long,
2907 * jalview.gui.IProgressIndicatorHandler)
2910 public void registerHandler(final long id,
2911 final IProgressIndicatorHandler handler)
2913 if (progressBarHandlers == null
2914 || !progressBars.containsKey(new Long(id)))
2916 throw new Error(MessageManager.getString(
2917 "error.call_setprogressbar_before_registering_handler"));
2919 progressBarHandlers.put(new Long(id), handler);
2920 final JPanel progressPanel = progressBars.get(new Long(id));
2921 if (handler.canCancel())
2923 JButton cancel = new JButton(
2924 MessageManager.getString("action.cancel"));
2925 final IProgressIndicator us = this;
2926 cancel.addActionListener(new ActionListener()
2930 public void actionPerformed(ActionEvent e)
2932 handler.cancelActivity(id);
2933 us.setProgressBar(MessageManager
2934 .formatMessage("label.cancelled_params", new Object[]
2935 { ((JLabel) progressPanel.getComponent(0)).getText() }),
2939 progressPanel.add(cancel, BorderLayout.EAST);
2945 * @return true if any progress bars are still active
2948 public boolean operationInProgress()
2950 if (progressBars != null && progressBars.size() > 0)
2958 * This will return the first AlignFrame holding the given viewport instance. It
2959 * will break if there are more than one AlignFrames viewing a particular av.
2962 * @return alignFrame for viewport
2964 public static AlignFrame getAlignFrameFor(AlignViewportI viewport)
2966 if (getDesktopPane() != null)
2968 AlignmentPanel[] aps = getAlignmentPanels(
2969 viewport.getSequenceSetId());
2970 for (int panel = 0; aps != null && panel < aps.length; panel++)
2972 if (aps[panel] != null && aps[panel].av == viewport)
2974 return aps[panel].alignFrame;
2981 public VamsasApplication getVamsasApplication()
2988 * flag set if jalview GUI is being operated programmatically
2990 private boolean inBatchMode = false;
2993 * check if jalview GUI is being operated programmatically
2995 * @return inBatchMode
2997 public boolean isInBatchMode()
3003 * set flag if jalview GUI is being operated programmatically
3005 * @param inBatchMode
3007 public void setInBatchMode(boolean inBatchMode)
3009 this.inBatchMode = inBatchMode;
3012 public void startServiceDiscovery()
3014 startServiceDiscovery(false);
3017 public void startServiceDiscovery(boolean blocking)
3019 boolean alive = true;
3020 Thread t0 = null, t1 = null, t2 = null;
3021 // JAL-940 - JALVIEW 1 services are now being EOLed as of JABA 2.1 release
3024 // todo: changesupport handlers need to be transferred
3025 if (discoverer == null)
3027 discoverer = new jalview.ws.jws1.Discoverer();
3028 // register PCS handler for getDesktop().
3029 discoverer.addPropertyChangeListener(changeSupport);
3031 // JAL-940 - disabled JWS1 service configuration - always start discoverer
3032 // until we phase out completely
3033 (t0 = new Thread(discoverer)).start();
3036 if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
3038 t2 = jalview.ws.jws2.Jws2Discoverer.getDiscoverer()
3039 .startDiscoverer(changeSupport);
3043 // TODO: do rest service discovery
3052 } catch (Exception e)
3055 alive = (t1 != null && t1.isAlive()) || (t2 != null && t2.isAlive())
3056 || (t3 != null && t3.isAlive())
3057 || (t0 != null && t0.isAlive());
3063 * called to check if the service discovery process completed successfully.
3067 protected void JalviewServicesChanged(PropertyChangeEvent evt)
3069 if (evt.getNewValue() == null || evt.getNewValue() instanceof Vector)
3071 final String ermsg = jalview.ws.jws2.Jws2Discoverer.getDiscoverer()
3072 .getErrorMessages();
3075 if (Cache.getDefault("SHOW_WSDISCOVERY_ERRORS", true))
3077 if (serviceChangedDialog == null)
3079 // only run if we aren't already displaying one of these.
3080 addDialogThread(serviceChangedDialog = new Runnable()
3087 * JalviewDialog jd =new JalviewDialog() {
3089 * @Override protected void cancelPressed() { // TODO
3090 * Auto-generated method stub
3092 * }@Override protected void okPressed() { // TODO
3093 * Auto-generated method stub
3095 * }@Override protected void raiseClosed() { // TODO
3096 * Auto-generated method stub
3098 * } }; jd.initDialogFrame(new
3099 * JLabel("<html><table width=\"450\"><tr><td>" + ermsg +
3100 * "<br/>It may be that you have invalid JABA URLs in your web service preferences,"
3101 * + " or mis-configured HTTP proxy settings.<br/>" +
3102 * "Check the <em>Connections</em> and <em>Web services</em> tab of the"
3104 * " Tools->Preferences dialog box to change them.</td></tr></table></html>"
3105 * ), true, true, "Web Service Configuration Problem", 450,
3108 * jd.waitForInput();
3110 JvOptionPane.showConfirmDialog(Desktop.getDesktopPane(),
3111 new JLabel("<html><table width=\"450\"><tr><td>"
3112 + ermsg + "</td></tr></table>"
3113 + "<p>It may be that you have invalid JABA URLs<br/>in your web service preferences,"
3114 + "<br>or as a command-line argument, or mis-configured HTTP proxy settings.</p>"
3115 + "<p>Check the <em>Connections</em> and <em>Web services</em> tab<br/>of the"
3116 + " Tools->Preferences dialog box to change them.</p></html>"),
3117 "Web Service Configuration Problem",
3118 JvOptionPane.DEFAULT_OPTION,
3119 JvOptionPane.ERROR_MESSAGE);
3120 serviceChangedDialog = null;
3129 "Errors reported by JABA discovery service. Check web services preferences.\n"
3136 Runnable serviceChangedDialog = null;
3139 * start a thread to open a URL in the configured browser. Pops up a warning
3140 * dialog to the user if there is an exception when calling out to the browser
3145 public static void showUrl(final String url)
3147 showUrl(url, Desktop.getInstance());
3151 * Like showUrl but allows progress handler to be specified
3155 * (null) or object implementing IProgressIndicator
3157 public static void showUrl(final String url,
3158 final IProgressIndicator progress)
3160 new Thread(new Runnable()
3167 if (progress != null)
3169 progress.setProgressBar(MessageManager
3170 .formatMessage("status.opening_params", new Object[]
3171 { url }), this.hashCode());
3173 BrowserLauncher.openURL(url);
3174 } catch (Exception ex)
3176 JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
3178 .getString("label.web_browser_not_found_unix"),
3179 MessageManager.getString("label.web_browser_not_found"),
3180 JvOptionPane.WARNING_MESSAGE);
3182 ex.printStackTrace();
3184 if (progress != null)
3186 progress.setProgressBar(null, this.hashCode());
3192 private WsParamSetManager wsparamManager = null;
3194 public static ParamManager getUserParameterStore()
3196 Desktop d = getInstance();
3197 if (d.wsparamManager == null)
3199 d.wsparamManager = new WsParamSetManager();
3201 return d.wsparamManager;
3205 * static hyperlink handler proxy method for use by Jalview's internal windows
3209 public static void hyperlinkUpdate(HyperlinkEvent e)
3211 if (e.getEventType() == EventType.ACTIVATED)
3216 url = e.getURL().toString();
3217 Desktop.showUrl(url);
3218 } catch (Exception x)
3222 if (Cache.log != null)
3224 Cache.log.error("Couldn't handle string " + url + " as a URL.");
3229 "Couldn't handle string " + url + " as a URL.");
3232 // ignore any exceptions due to dud links.
3239 * single thread that handles display of dialogs to user.
3241 ExecutorService dialogExecutor = Executors.newSingleThreadExecutor();
3244 * flag indicating if dialogExecutor should try to acquire a permit
3246 volatile boolean dialogPause = true;
3251 java.util.concurrent.Semaphore block = new Semaphore(0);
3253 private groovy.ui.Console groovyConsole;
3255 public StructureViewer lastTargetedView;
3258 * add another dialog thread to the queue
3262 public void addDialogThread(final Runnable prompter)
3264 dialogExecutor.submit(new Runnable()
3274 } catch (InterruptedException x)
3279 if (getInstance() == null)
3285 SwingUtilities.invokeAndWait(prompter);
3286 } catch (Exception q)
3288 Cache.log.warn("Unexpected Exception in dialog thread.", q);
3294 public void startDialogQueue()
3296 // set the flag so we don't pause waiting for another permit and semaphore
3297 // the current task to begin
3298 dialogPause = false;
3303 * Outputs an image of the desktop to file in EPS format, after prompting the
3304 * user for choice of Text or Lineart character rendering (unless a preference
3305 * has been set). The file name is generated as
3308 * Jalview_snapshot_nnnnn.eps where nnnnn is the current timestamp in milliseconds
3312 protected void snapShotWindow_actionPerformed(ActionEvent e)
3314 // currently the menu option to do this is not shown
3317 int width = getWidth();
3318 int height = getHeight();
3320 "Jalview_snapshot_" + System.currentTimeMillis() + ".eps");
3321 ImageWriterI writer = new ImageWriterI()
3324 public void exportImage(Graphics g) throws Exception
3327 Cache.log.info("Successfully written snapshot to file "
3328 + of.getAbsolutePath());
3331 String title = "View of desktop";
3332 ImageExporter exporter = new ImageExporter(writer, null, TYPE.EPS,
3334 exporter.doExport(of, this, width, height, title);
3338 * Explode the views in the given SplitFrame into separate SplitFrame windows.
3339 * This respects (remembers) any previous 'exploded geometry' i.e. the size and
3340 * location last time the view was expanded (if any). However it does not
3341 * remember the split pane divider location - this is set to match the
3342 * 'exploding' frame.
3346 public void explodeViews(SplitFrame sf)
3348 AlignFrame oldTopFrame = (AlignFrame) sf.getTopFrame();
3349 AlignFrame oldBottomFrame = (AlignFrame) sf.getBottomFrame();
3350 List<? extends AlignmentViewPanel> topPanels = oldTopFrame
3352 List<? extends AlignmentViewPanel> bottomPanels = oldBottomFrame
3354 int viewCount = topPanels.size();
3361 * Processing in reverse order works, forwards order leaves the first panels
3362 * not visible. I don't know why!
3364 for (int i = viewCount - 1; i >= 0; i--)
3367 * Make new top and bottom frames. These take over the respective
3368 * AlignmentPanel objects, including their AlignmentViewports, so the
3369 * cdna/protein relationships between the viewports is carried over to the
3372 * explodedGeometry holds the (x, y) position of the previously exploded
3373 * SplitFrame, and the (width, height) of the AlignFrame component
3375 AlignmentPanel topPanel = (AlignmentPanel) topPanels.get(i);
3376 AlignFrame newTopFrame = new AlignFrame(topPanel);
3377 newTopFrame.setSize(oldTopFrame.getSize());
3378 newTopFrame.setVisible(true);
3379 Rectangle geometry = ((AlignViewport) topPanel.getAlignViewport())
3380 .getExplodedGeometry();
3381 if (geometry != null)
3383 newTopFrame.setSize(geometry.getSize());
3386 AlignmentPanel bottomPanel = (AlignmentPanel) bottomPanels.get(i);
3387 AlignFrame newBottomFrame = new AlignFrame(bottomPanel);
3388 newBottomFrame.setSize(oldBottomFrame.getSize());
3389 newBottomFrame.setVisible(true);
3390 geometry = ((AlignViewport) bottomPanel.getAlignViewport())
3391 .getExplodedGeometry();
3392 if (geometry != null)
3394 newBottomFrame.setSize(geometry.getSize());
3397 topPanel.av.setGatherViewsHere(false);
3398 bottomPanel.av.setGatherViewsHere(false);
3399 JInternalFrame splitFrame = new SplitFrame(newTopFrame,
3401 if (geometry != null)
3403 splitFrame.setLocation(geometry.getLocation());
3405 Desktop.addInternalFrame(splitFrame, sf.getTitle(), -1, -1);
3409 * Clear references to the panels (now relocated in the new SplitFrames)
3410 * before closing the old SplitFrame.
3413 bottomPanels.clear();
3418 * Gather expanded split frames, sharing the same pairs of sequence set ids,
3419 * back into the given SplitFrame as additional views. Note that the gathered
3420 * frames may themselves have multiple views.
3424 public void gatherViews(GSplitFrame source)
3427 * special handling of explodedGeometry for a view within a SplitFrame: - it
3428 * holds the (x, y) position of the enclosing SplitFrame, and the (width,
3429 * height) of the AlignFrame component
3431 AlignFrame myTopFrame = (AlignFrame) source.getTopFrame();
3432 AlignFrame myBottomFrame = (AlignFrame) source.getBottomFrame();
3433 myTopFrame.viewport.setExplodedGeometry(new Rectangle(source.getX(),
3434 source.getY(), myTopFrame.getWidth(), myTopFrame.getHeight()));
3435 myBottomFrame.viewport
3436 .setExplodedGeometry(new Rectangle(source.getX(), source.getY(),
3437 myBottomFrame.getWidth(), myBottomFrame.getHeight()));
3438 myTopFrame.viewport.setGatherViewsHere(true);
3439 myBottomFrame.viewport.setGatherViewsHere(true);
3440 String topViewId = myTopFrame.viewport.getSequenceSetId();
3441 String bottomViewId = myBottomFrame.viewport.getSequenceSetId();
3443 JInternalFrame[] frames = getDesktopPane().getAllFrames();
3444 for (JInternalFrame frame : frames)
3446 if (frame instanceof SplitFrame && frame != source)
3448 SplitFrame sf = (SplitFrame) frame;
3449 AlignFrame topFrame = (AlignFrame) sf.getTopFrame();
3450 AlignFrame bottomFrame = (AlignFrame) sf.getBottomFrame();
3451 boolean gatherThis = false;
3452 for (int a = 0; a < topFrame.alignPanels.size(); a++)
3454 AlignmentPanel topPanel = topFrame.alignPanels.get(a);
3455 AlignmentPanel bottomPanel = bottomFrame.alignPanels.get(a);
3456 if (topViewId.equals(topPanel.av.getSequenceSetId())
3457 && bottomViewId.equals(bottomPanel.av.getSequenceSetId()))
3460 topPanel.av.setGatherViewsHere(false);
3461 bottomPanel.av.setGatherViewsHere(false);
3462 topPanel.av.setExplodedGeometry(
3463 new Rectangle(sf.getLocation(), topFrame.getSize()));
3464 bottomPanel.av.setExplodedGeometry(
3465 new Rectangle(sf.getLocation(), bottomFrame.getSize()));
3466 myTopFrame.addAlignmentPanel(topPanel, false);
3467 myBottomFrame.addAlignmentPanel(bottomPanel, false);
3473 topFrame.getAlignPanels().clear();
3474 bottomFrame.getAlignPanels().clear();
3481 * The dust settles...give focus to the tab we did this from.
3483 myTopFrame.setDisplayedView(myTopFrame.alignPanel);
3486 public static groovy.ui.Console getGroovyConsole()
3488 return getInstance().groovyConsole;
3492 * handles the payload of a drag and drop event.
3494 * TODO refactor to desktop utilities class
3497 * - Data source strings extracted from the drop event
3499 * - protocol for each data source extracted from the drop event
3503 * - the payload from the drop event
3506 @SuppressWarnings("unchecked")
3507 public static void transferFromDropTarget(List<Object> files,
3508 List<DataSourceType> protocols, DropTargetDropEvent evt,
3509 Transferable t) throws Exception
3512 // BH 2018 changed List<String> to List<Object> to allow for File from SwingJS
3514 // DataFlavor[] flavors = t.getTransferDataFlavors();
3515 // for (int i = 0; i < flavors.length; i++) {
3516 // if (flavors[i].isFlavorJavaFileListType()) {
3517 // evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
3518 // List<File> list = (List<File>) t.getTransferData(flavors[i]);
3519 // for (int j = 0; j < list.size(); j++) {
3520 // File file = (File) list.get(j);
3521 // byte[] data = getDroppedFileBytes(file);
3522 // fileName.setText(file.getName() + " - " + data.length + " " +
3523 // evt.getLocation());
3524 // JTextArea target = (JTextArea) ((DropTarget) evt.getSource()).getComponent();
3525 // target.setText(new String(data));
3527 // dtde.dropComplete(true);
3532 DataFlavor uriListFlavor = new DataFlavor(
3533 "text/uri-list;class=java.lang.String"), urlFlavour = null;
3536 urlFlavour = new DataFlavor(
3537 "application/x-java-url; class=java.net.URL");
3538 } catch (ClassNotFoundException cfe)
3540 Cache.log.debug("Couldn't instantiate the URL dataflavor.", cfe);
3543 if (urlFlavour != null && t.isDataFlavorSupported(urlFlavour))
3548 java.net.URL url = (URL) t.getTransferData(urlFlavour);
3549 // nb: java 8 osx bug https://bugs.openjdk.java.net/browse/JDK-8156099
3550 // means url may be null.
3553 protocols.add(DataSourceType.URL);
3554 files.add(url.toString());
3555 Cache.log.debug("Drop handled as URL dataflavor "
3556 + files.get(files.size() - 1));
3561 if (Platform.isAMacAndNotJS())
3564 "Please ignore plist error - occurs due to problem with java 8 on OSX");
3568 } catch (Throwable ex)
3570 Cache.log.debug("URL drop handler failed.", ex);
3573 if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
3575 // Works on Windows and MacOSX
3576 Cache.log.debug("Drop handled as javaFileListFlavor");
3577 for (Object file : (List<Object>) t
3578 .getTransferData(DataFlavor.javaFileListFlavor))
3581 protocols.add(DataSourceType.FILE);
3586 // Unix like behaviour
3587 boolean added = false;
3589 if (t.isDataFlavorSupported(uriListFlavor))
3591 Cache.log.debug("Drop handled as uriListFlavor");
3592 // This is used by Unix drag system
3593 data = (String) t.getTransferData(uriListFlavor);
3597 // fallback to text: workaround - on OSX where there's a JVM bug
3598 Cache.log.debug("standard URIListFlavor failed. Trying text");
3599 // try text fallback
3600 DataFlavor textDf = new DataFlavor(
3601 "text/plain;class=java.lang.String");
3602 if (t.isDataFlavorSupported(textDf))
3604 data = (String) t.getTransferData(textDf);
3607 Cache.log.debug("Plain text drop content returned "
3608 + (data == null ? "Null - failed" : data));
3613 while (protocols.size() < files.size())
3615 Cache.log.debug("Adding missing FILE protocol for "
3616 + files.get(protocols.size()));
3617 protocols.add(DataSourceType.FILE);
3619 for (java.util.StringTokenizer st = new java.util.StringTokenizer(
3620 data, "\r\n"); st.hasMoreTokens();)
3623 String s = st.nextToken();
3624 if (s.startsWith("#"))
3626 // the line is a comment (as per the RFC 2483)
3629 java.net.URI uri = new java.net.URI(s);
3630 if (uri.getScheme().toLowerCase().startsWith("http"))
3632 protocols.add(DataSourceType.URL);
3633 files.add(uri.toString());
3637 // otherwise preserve old behaviour: catch all for file objects
3638 java.io.File file = new java.io.File(uri);
3639 protocols.add(DataSourceType.FILE);
3640 files.add(file.toString());
3645 if (Cache.log.isDebugEnabled())
3647 if (data == null || !added)
3650 if (t.getTransferDataFlavors() != null
3651 && t.getTransferDataFlavors().length > 0)
3654 "Couldn't resolve drop data. Here are the supported flavors:");
3655 for (DataFlavor fl : t.getTransferDataFlavors())
3658 "Supported transfer dataflavor: " + fl.toString());
3659 Object df = t.getTransferData(fl);
3662 Cache.log.debug("Retrieves: " + df);
3666 Cache.log.debug("Retrieved nothing");
3672 Cache.log.debug("Couldn't resolve dataflavor for drop: "
3678 if (Platform.isWindowsAndNotJS())
3680 Cache.log.debug("Scanning dropped content for Windows Link Files");
3682 // resolve any .lnk files in the file drop
3683 for (int f = 0; f < files.size(); f++)
3685 String source = files.get(f).toString().toLowerCase();
3686 if (protocols.get(f).equals(DataSourceType.FILE)
3687 && (source.endsWith(".lnk") || source.endsWith(".url")
3688 || source.endsWith(".site")))
3692 Object obj = files.get(f);
3693 File lf = (obj instanceof File ? (File) obj
3694 : new File((String) obj));
3695 // process link file to get a URL
3696 Cache.log.debug("Found potential link file: " + lf);
3697 WindowsShortcut wscfile = new WindowsShortcut(lf);
3698 String fullname = wscfile.getRealFilename();
3699 protocols.set(f, FormatAdapter.checkProtocol(fullname));
3700 files.set(f, fullname);
3701 Cache.log.debug("Parsed real filename " + fullname
3702 + " to extract protocol: " + protocols.get(f));
3703 } catch (Exception ex)
3706 "Couldn't parse " + files.get(f) + " as a link file.",
3715 * Sets the Preferences property for experimental features to True or False
3716 * depending on the state of the controlling menu item
3719 protected void showExperimental_actionPerformed(boolean selected)
3721 Cache.setProperty(EXPERIMENTAL_FEATURES, Boolean.toString(selected));
3725 * Answers a (possibly empty) list of any structure viewer frames (currently for
3726 * either Jmol or Chimera) which are currently open. This may optionally be
3727 * restricted to viewers of a specified class, or viewers linked to a specified
3731 * if not null, only return viewers linked to this panel
3732 * @param structureViewerClass
3733 * if not null, only return viewers of this class
3736 public List<StructureViewerBase> getStructureViewers(
3737 AlignmentPanel apanel,
3738 Class<? extends StructureViewerBase> structureViewerClass)
3740 List<StructureViewerBase> result = new ArrayList<>();
3741 JInternalFrame[] frames = Desktop.getInstance().getAllFrames();
3743 for (JInternalFrame frame : frames)
3745 if (frame instanceof StructureViewerBase)
3747 if (structureViewerClass == null
3748 || structureViewerClass.isInstance(frame))
3751 || ((StructureViewerBase) frame).isLinkedWith(apanel))
3753 result.add((StructureViewerBase) frame);