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.Instance;
27 import jalview.bin.Jalview;
28 import jalview.gui.ImageExporter.ImageWriterI;
29 import jalview.io.BackupFiles;
30 import jalview.io.DataSourceType;
31 import jalview.io.FileFormat;
32 import jalview.io.FileFormatException;
33 import jalview.io.FileFormatI;
34 import jalview.io.FileFormats;
35 import jalview.io.FileLoader;
36 import jalview.io.FormatAdapter;
37 import jalview.io.IdentifyFile;
38 import jalview.io.JalviewFileChooser;
39 import jalview.io.JalviewFileView;
40 import jalview.jbgui.GSplitFrame;
41 import jalview.jbgui.GStructureViewer;
42 import jalview.project.Jalview2XML;
43 import jalview.structure.StructureSelectionManager;
44 import jalview.urls.IdOrgSettings;
45 import jalview.util.BrowserLauncher;
46 import jalview.util.ImageMaker.TYPE;
47 import jalview.util.MessageManager;
48 import jalview.util.Platform;
49 import jalview.util.UrlConstants;
50 import jalview.viewmodel.AlignmentViewport;
51 import jalview.ws.jws1.Discoverer;
52 import jalview.ws.params.ParamManager;
53 import jalview.ws.utils.UrlDownloadClient;
55 import java.awt.BorderLayout;
56 import java.awt.Color;
57 import java.awt.Dimension;
58 import java.awt.FontMetrics;
59 import java.awt.Graphics;
60 import java.awt.GridLayout;
61 import java.awt.Point;
62 import java.awt.Rectangle;
63 import java.awt.Toolkit;
64 import java.awt.Window;
65 import java.awt.datatransfer.Clipboard;
66 import java.awt.datatransfer.ClipboardOwner;
67 import java.awt.datatransfer.DataFlavor;
68 import java.awt.datatransfer.Transferable;
69 import java.awt.dnd.DnDConstants;
70 import java.awt.dnd.DropTargetDragEvent;
71 import java.awt.dnd.DropTargetDropEvent;
72 import java.awt.dnd.DropTargetEvent;
73 import java.awt.dnd.DropTargetListener;
74 import java.awt.event.ActionEvent;
75 import java.awt.event.ActionListener;
76 import java.awt.event.InputEvent;
77 import java.awt.event.KeyEvent;
78 import java.awt.event.MouseAdapter;
79 import java.awt.event.MouseEvent;
80 import java.awt.event.WindowAdapter;
81 import java.awt.event.WindowEvent;
82 import java.beans.PropertyChangeEvent;
83 import java.beans.PropertyChangeListener;
84 import java.io.BufferedInputStream;
86 import java.io.FileOutputStream;
87 import java.io.IOException;
89 import java.util.ArrayList;
90 import java.util.Hashtable;
91 import java.util.List;
92 import java.util.ListIterator;
93 import java.util.Vector;
94 import java.util.concurrent.ExecutorService;
95 import java.util.concurrent.Executors;
96 import java.util.concurrent.Semaphore;
98 import javax.swing.AbstractAction;
99 import javax.swing.Action;
100 import javax.swing.ActionMap;
101 import javax.swing.Box;
102 import javax.swing.BoxLayout;
103 import javax.swing.DefaultDesktopManager;
104 import javax.swing.DesktopManager;
105 import javax.swing.InputMap;
106 import javax.swing.JButton;
107 import javax.swing.JCheckBox;
108 import javax.swing.JComboBox;
109 import javax.swing.JComponent;
110 import javax.swing.JDesktopPane;
111 import javax.swing.JFrame;
112 import javax.swing.JInternalFrame;
113 import javax.swing.JLabel;
114 import javax.swing.JMenuItem;
115 import javax.swing.JPanel;
116 import javax.swing.JPopupMenu;
117 import javax.swing.JProgressBar;
118 import javax.swing.JTextField;
119 import javax.swing.KeyStroke;
120 import javax.swing.SwingUtilities;
121 import javax.swing.event.HyperlinkEvent;
122 import javax.swing.event.HyperlinkEvent.EventType;
123 import javax.swing.event.InternalFrameAdapter;
124 import javax.swing.event.InternalFrameEvent;
125 import javax.swing.event.MenuEvent;
126 import javax.swing.event.MenuListener;
128 import org.stackoverflowusers.file.WindowsShortcut;
135 * @version $Revision: 1.155 $
137 @SuppressWarnings("serial")
138 public class Desktop extends jalview.jbgui.GDesktop
139 implements DropTargetListener, ClipboardOwner, IProgressIndicator,
140 jalview.api.StructureSelectionManagerProvider
142 private final static int DEFAULT_MIN_WIDTH = 300;
144 private final static int DEFAULT_MIN_HEIGHT = 250;
146 private final static int ALIGN_FRAME_DEFAULT_MIN_WIDTH = 600;
148 private final static int ALIGN_FRAME_DEFAULT_MIN_HEIGHT = 70;
150 private final static String EXPERIMENTAL_FEATURES = "EXPERIMENTAL_FEATURES";
152 private JalviewChangeSupport changeSupport = new JalviewChangeSupport();
155 * news reader - null if it was never started.
157 BlogReader jvnews = null;
159 private File projectFile;
163 * @see jalview.gui.JalviewChangeSupport#addJalviewPropertyChangeListener(java.beans.PropertyChangeListener)
165 public void addJalviewPropertyChangeListener(
166 PropertyChangeListener listener)
168 changeSupport.addJalviewPropertyChangeListener(listener);
172 * @param propertyName
174 * @see jalview.gui.JalviewChangeSupport#addJalviewPropertyChangeListener(java.lang.String,
175 * java.beans.PropertyChangeListener)
177 public void addJalviewPropertyChangeListener(String propertyName,
178 PropertyChangeListener listener)
180 changeSupport.addJalviewPropertyChangeListener(propertyName, listener);
184 * @param propertyName
186 * @see jalview.gui.JalviewChangeSupport#removeJalviewPropertyChangeListener(java.lang.String,
187 * java.beans.PropertyChangeListener)
189 public void removeJalviewPropertyChangeListener(String propertyName,
190 PropertyChangeListener listener)
192 changeSupport.removeJalviewPropertyChangeListener(propertyName,
196 public static MyDesktopPane getDesktopPane()
198 return Instance.getDesktop().desktopPane;
201 static int openFrameCount = 0;
203 static final int xOffset = 30;
205 static final int yOffset = 30;
207 public Discoverer discoverer;
209 public Object[] jalviewClipboard;
211 public boolean internalCopy = false;
213 private static int fileLoadingCount = 0;
215 public JInternalFrame conservationSlider, PIDSlider;
218 * just an instance (for testng, probably); no actual frames
220 private boolean instanceOnly;
222 class MyDesktopManager implements DesktopManager
225 private DesktopManager delegate;
227 public MyDesktopManager(DesktopManager delegate)
229 this.delegate = delegate;
233 public void activateFrame(JInternalFrame f)
237 delegate.activateFrame(f);
238 } catch (NullPointerException npe)
240 Point p = getMousePosition();
241 showPasteMenu(p.x, p.y);
246 public void beginDraggingFrame(JComponent f)
248 delegate.beginDraggingFrame(f);
252 public void beginResizingFrame(JComponent f, int direction)
254 delegate.beginResizingFrame(f, direction);
258 public void closeFrame(JInternalFrame f)
260 delegate.closeFrame(f);
264 public void deactivateFrame(JInternalFrame f)
266 delegate.deactivateFrame(f);
270 public void deiconifyFrame(JInternalFrame f)
272 delegate.deiconifyFrame(f);
276 public void dragFrame(JComponent f, int newX, int newY)
282 delegate.dragFrame(f, newX, newY);
286 public void endDraggingFrame(JComponent f)
288 delegate.endDraggingFrame(f);
289 desktopPane.repaint();
293 public void endResizingFrame(JComponent f)
295 delegate.endResizingFrame(f);
296 desktopPane.repaint();
300 public void iconifyFrame(JInternalFrame f)
302 delegate.iconifyFrame(f);
306 public void maximizeFrame(JInternalFrame f)
308 delegate.maximizeFrame(f);
312 public void minimizeFrame(JInternalFrame f)
314 delegate.minimizeFrame(f);
318 public void openFrame(JInternalFrame f)
320 delegate.openFrame(f);
324 public void resizeFrame(JComponent f, int newX, int newY, int newWidth,
331 delegate.resizeFrame(f, newX, newY, newWidth, newHeight);
335 public void setBoundsForFrame(JComponent f, int newX, int newY,
336 int newWidth, int newHeight)
338 delegate.setBoundsForFrame(f, newX, newY, newWidth, newHeight);
341 // All other methods, simply delegate
345 public MyDesktopPane desktopPane;
347 public Desktop(boolean forInstance)
352 * Creates a new Desktop object.
357 * A note to implementors. It is ESSENTIAL that any activities that might
358 * block are spawned off as threads rather than waited for during this
361 Instance.setDesktop(this);
362 if (!Platform.isJS())
364 doVamsasClientCheck();
367 doConfigureStructurePrefs();
368 setTitle("Jalview " + jalview.bin.Cache.getProperty("VERSION"));
369 setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
370 boolean selmemusage = jalview.bin.Cache.getDefault("SHOW_MEMUSAGE",
372 boolean showjconsole = jalview.bin.Cache.getDefault("SHOW_JAVA_CONSOLE",
374 desktopPane = new MyDesktopPane(selmemusage);
376 showMemusage.setSelected(selmemusage);
377 desktopPane.setBackground(Color.white);
378 getContentPane().setLayout(new BorderLayout());
379 // alternate config - have scrollbars - see notes in JAL-153
380 // JScrollPane sp = new JScrollPane();
381 // sp.getViewport().setView(desktop);
382 // getContentPane().add(sp, BorderLayout.CENTER);
384 // BH 2018 - just an experiment to try unclipped JInternalFrames.
387 getRootPane().putClientProperty("swingjs.overflow.hidden", "false");
390 getContentPane().add(desktopPane, BorderLayout.CENTER);
391 desktopPane.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
393 // This line prevents Windows Look&Feel resizing all new windows to maximum
394 // if previous window was maximised
395 desktopPane.setDesktopManager(new MyDesktopManager(
396 (Platform.isWindowsAndNotJS() ? new DefaultDesktopManager()
397 : Platform.isAMacAndNotJS()
398 ? new AquaInternalFrameManager(
399 desktopPane.getDesktopManager())
400 : desktopPane.getDesktopManager())));
402 Rectangle dims = getLastKnownDimensions("");
409 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
410 int xPos = Math.max(5, (screenSize.width - 900) / 2);
411 int yPos = Math.max(5, (screenSize.height - 650) / 2);
412 setBounds(xPos, yPos, 900, 650);
415 if (!Platform.isJS())
423 jconsole = new Console(this, showjconsole);
424 // add essential build information
425 jconsole.setHeader("Jalview Version: "
426 + jalview.bin.Cache.getProperty("VERSION") + "\n"
427 + "Jalview Installation: "
428 + jalview.bin.Cache.getDefault("INSTALLATION", "unknown")
429 + "\n" + "Build Date: "
430 + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown") + "\n"
431 + "Java version: " + System.getProperty("java.version") + "\n"
432 + System.getProperty("os.arch") + " "
433 + System.getProperty("os.name") + " "
434 + System.getProperty("os.version"));
436 showConsole(showjconsole);
438 showNews.setVisible(false);
440 experimentalFeatures.setSelected(showExperimental());
442 getIdentifiersOrgData();
446 // Spawn a thread that shows the splashscreen
448 SwingUtilities.invokeLater(new Runnable()
457 // Thread off a new instance of the file chooser - this reduces the time
459 // takes to open it later on.
460 new Thread(new Runnable()
465 Cache.log.debug("Filechooser init thread started.");
466 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
467 JalviewFileChooser.forRead(Cache.getProperty("LAST_DIRECTORY"),
469 Cache.log.debug("Filechooser init thread finished.");
472 // Add the service change listener
473 changeSupport.addJalviewPropertyChangeListener("services",
474 new PropertyChangeListener()
478 public void propertyChange(PropertyChangeEvent evt)
480 Cache.log.debug("Firing service changed event for "
481 + evt.getNewValue());
482 JalviewServicesChanged(evt);
489 this.setDropTarget(new java.awt.dnd.DropTarget(desktopPane, this));
491 this.addWindowListener(new WindowAdapter()
494 public void windowClosing(WindowEvent evt)
501 this.addMouseListener(ma = new MouseAdapter()
504 public void mousePressed(MouseEvent evt)
506 if (evt.isPopupTrigger()) // Mac
508 showPasteMenu(evt.getX(), evt.getY());
513 public void mouseReleased(MouseEvent evt)
515 if (evt.isPopupTrigger()) // Windows
517 showPasteMenu(evt.getX(), evt.getY());
521 desktopPane.addMouseListener(ma);
526 * Answers true if user preferences to enable experimental features is True
531 public boolean showExperimental()
533 String experimental = Cache.getDefault(EXPERIMENTAL_FEATURES,
534 Boolean.FALSE.toString());
535 return Boolean.valueOf(experimental).booleanValue();
538 public void doConfigureStructurePrefs()
540 // configure services
541 StructureSelectionManager ssm = StructureSelectionManager
542 .getStructureSelectionManager(this);
543 if (jalview.bin.Cache.getDefault(Preferences.ADD_SS_ANN, true))
545 ssm.setAddTempFacAnnot(jalview.bin.Cache
546 .getDefault(Preferences.ADD_TEMPFACT_ANN, true));
547 ssm.setProcessSecondaryStructure(jalview.bin.Cache
548 .getDefault(Preferences.STRUCT_FROM_PDB, true));
549 ssm.setSecStructServices(
550 jalview.bin.Cache.getDefault(Preferences.USE_RNAVIEW, true));
554 ssm.setAddTempFacAnnot(false);
555 ssm.setProcessSecondaryStructure(false);
556 ssm.setSecStructServices(false);
560 public void checkForNews()
562 final Desktop me = this;
563 // Thread off the news reader, in case there are connection problems.
564 new Thread(new Runnable()
569 Cache.log.debug("Starting news thread.");
570 jvnews = new BlogReader(me);
571 showNews.setVisible(true);
572 Cache.log.debug("Completed news thread.");
577 public void getIdentifiersOrgData()
579 // Thread off the identifiers fetcher
580 new Thread(new Runnable()
585 Cache.log.debug("Downloading data from identifiers.org");
586 // UrlDownloadClient client = new UrlDownloadClient();
589 UrlDownloadClient.download(IdOrgSettings.getUrl(),
590 IdOrgSettings.getDownloadLocation());
591 } catch (IOException e)
593 Cache.log.debug("Exception downloading identifiers.org data"
602 protected void showNews_actionPerformed(ActionEvent e)
604 showNews(showNews.isSelected());
607 protected void showNews(boolean visible)
609 Cache.log.debug((visible ? "Showing" : "Hiding") + " news.");
610 showNews.setSelected(visible);
611 if (visible && !jvnews.isVisible())
613 new Thread(new Runnable()
618 long now = System.currentTimeMillis();
620 MessageManager.getString("status.refreshing_news"), now);
621 jvnews.refreshNews();
622 setProgressBar(null, now);
630 * recover the last known dimensions for a jalview window
633 * - empty string is desktop, all other windows have unique prefix
634 * @return null or last known dimensions scaled to current geometry (if last
635 * window geom was known)
637 Rectangle getLastKnownDimensions(String windowName)
639 // TODO: lock aspect ratio for scaling desktop Bug #0058199
640 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
641 String x = jalview.bin.Cache.getProperty(windowName + "SCREEN_X");
642 String y = jalview.bin.Cache.getProperty(windowName + "SCREEN_Y");
643 String width = jalview.bin.Cache
644 .getProperty(windowName + "SCREEN_WIDTH");
645 String height = jalview.bin.Cache
646 .getProperty(windowName + "SCREEN_HEIGHT");
647 if ((x != null) && (y != null) && (width != null) && (height != null))
649 int ix = Integer.parseInt(x), iy = Integer.parseInt(y),
650 iw = Integer.parseInt(width), ih = Integer.parseInt(height);
651 if (jalview.bin.Cache.getProperty("SCREENGEOMETRY_WIDTH") != null)
653 // attempt #1 - try to cope with change in screen geometry - this
654 // version doesn't preserve original jv aspect ratio.
655 // take ratio of current screen size vs original screen size.
656 double sw = ((1f * screenSize.width) / (1f * Integer.parseInt(
657 jalview.bin.Cache.getProperty("SCREENGEOMETRY_WIDTH"))));
658 double sh = ((1f * screenSize.height) / (1f * Integer.parseInt(
659 jalview.bin.Cache.getProperty("SCREENGEOMETRY_HEIGHT"))));
660 // rescale the bounds depending upon the current screen geometry.
661 ix = (int) (ix * sw);
662 iw = (int) (iw * sw);
663 iy = (int) (iy * sh);
664 ih = (int) (ih * sh);
665 while (ix >= screenSize.width)
667 jalview.bin.Cache.log.debug(
668 "Window geometry location recall error: shifting horizontal to within screenbounds.");
669 ix -= screenSize.width;
671 while (iy >= screenSize.height)
673 jalview.bin.Cache.log.debug(
674 "Window geometry location recall error: shifting vertical to within screenbounds.");
675 iy -= screenSize.height;
677 jalview.bin.Cache.log.debug(
678 "Got last known dimensions for " + windowName + ": x:" + ix
679 + " y:" + iy + " width:" + iw + " height:" + ih);
681 // return dimensions for new instance
682 return new Rectangle(ix, iy, iw, ih);
687 private void doVamsasClientCheck()
689 if (Cache.vamsasJarsPresent())
691 setupVamsasDisconnectedGui();
692 VamsasMenu.setVisible(true);
693 final Desktop us = this;
694 VamsasMenu.addMenuListener(new MenuListener()
696 // this listener remembers when the menu was first selected, and
697 // doesn't rebuild the session list until it has been cleared and
699 boolean refresh = true;
702 public void menuCanceled(MenuEvent e)
708 public void menuDeselected(MenuEvent e)
714 public void menuSelected(MenuEvent e)
718 us.buildVamsasStMenu();
723 vamsasStart.setVisible(true);
727 protected void showPasteMenu(int x, int y)
729 JPopupMenu popup = new JPopupMenu();
730 JMenuItem item = new JMenuItem(
731 MessageManager.getString("label.paste_new_window"));
732 item.addActionListener(new ActionListener()
735 public void actionPerformed(ActionEvent evt)
742 popup.show(this, x, y);
749 Clipboard c = Toolkit.getDefaultToolkit().getSystemClipboard();
750 Transferable contents = c.getContents(this);
752 if (contents != null)
754 String file = (String) contents
755 .getTransferData(DataFlavor.stringFlavor);
757 FileFormatI format = new IdentifyFile().identify(file,
758 DataSourceType.PASTE);
760 new FileLoader().LoadFile(file, DataSourceType.PASTE, format);
763 } catch (Exception ex)
766 "Unable to paste alignment from system clipboard:\n" + ex);
771 * Adds and opens the given frame to the desktop
782 public static synchronized void addInternalFrame(
783 final JInternalFrame frame, String title, int w, int h)
785 addInternalFrame(frame, title, true, w, h, true, false);
789 * Add an internal frame to the Jalview desktop
796 * When true, display frame immediately, otherwise, caller must call
797 * setVisible themselves.
803 public static synchronized void addInternalFrame(
804 final JInternalFrame frame, String title, boolean makeVisible,
807 addInternalFrame(frame, title, makeVisible, w, h, true, false);
811 * Add an internal frame to the Jalview desktop and make it visible
824 public static synchronized void addInternalFrame(
825 final JInternalFrame frame, String title, int w, int h,
828 addInternalFrame(frame, title, true, w, h, resizable, false);
832 * Add an internal frame to the Jalview desktop
839 * When true, display frame immediately, otherwise, caller must call
840 * setVisible themselves.
847 * @param ignoreMinSize
848 * Do not set the default minimum size for frame
850 public static synchronized void addInternalFrame(
851 final JInternalFrame frame, String title, boolean makeVisible,
852 int w, int h, boolean resizable, boolean ignoreMinSize)
855 // TODO: allow callers to determine X and Y position of frame (eg. via
857 // TODO: consider fixing method to update entries in the window submenu with
858 // the current window title
860 frame.setTitle(title);
861 if (frame.getWidth() < 1 || frame.getHeight() < 1)
865 // THIS IS A PUBLIC STATIC METHOD, SO IT MAY BE CALLED EVEN IN
866 // A HEADLESS STATE WHEN NO DESKTOP EXISTS. MUST RETURN
867 // IF JALVIEW IS RUNNING HEADLESS
868 // ///////////////////////////////////////////////
869 if (Instance.getDesktop().instanceOnly || Jalview.isHeadlessMode())
878 frame.setMinimumSize(
879 new Dimension(DEFAULT_MIN_WIDTH, DEFAULT_MIN_HEIGHT));
881 // Set default dimension for Alignment Frame window.
882 // The Alignment Frame window could be added from a number of places,
884 // I did this here in order not to miss out on any Alignment frame.
885 if (frame instanceof AlignFrame)
887 frame.setMinimumSize(new Dimension(ALIGN_FRAME_DEFAULT_MIN_WIDTH,
888 ALIGN_FRAME_DEFAULT_MIN_HEIGHT));
892 frame.setVisible(makeVisible);
893 frame.setClosable(true);
894 frame.setResizable(resizable);
895 frame.setMaximizable(resizable);
896 frame.setIconifiable(resizable);
897 frame.setOpaque(Platform.isJS());
899 if (frame.getX() < 1 && frame.getY() < 1)
901 frame.setLocation(xOffset * openFrameCount,
902 yOffset * ((openFrameCount - 1) % 10) + yOffset);
906 * add an entry for the new frame in the Window menu
907 * (and remove it when the frame is closed)
909 JMenuItem menuItem = new JMenuItem(title);
910 frame.addInternalFrameListener(new InternalFrameAdapter()
913 public void internalFrameActivated(InternalFrameEvent evt)
915 JInternalFrame itf = getDesktopPane().getSelectedFrame();
918 if (itf instanceof AlignFrame)
920 Jalview.setCurrentAlignFrame((AlignFrame) itf);
927 public void internalFrameClosed(InternalFrameEvent evt)
929 PaintRefresher.RemoveComponent(frame);
932 * defensive check to prevent frames being
933 * added half off the window
935 if (openFrameCount > 0)
941 * ensure no reference to alignFrame retained by menu item listener
943 if (menuItem.getActionListeners().length > 0)
945 menuItem.removeActionListener(menuItem.getActionListeners()[0]);
947 Instance.getDesktop().windowMenu.remove(menuItem);
951 menuItem.addActionListener(new ActionListener()
954 public void actionPerformed(ActionEvent e)
958 frame.setSelected(true);
959 frame.setIcon(false);
960 } catch (java.beans.PropertyVetoException ex)
962 // System.err.println(ex.toString());
967 setKeyBindings(frame);
969 getDesktopPane().add(frame);
971 Instance.getDesktop().windowMenu.add(menuItem);
976 frame.setSelected(true);
977 frame.requestFocus();
978 } catch (java.beans.PropertyVetoException ve)
980 } catch (java.lang.ClassCastException cex)
983 "Squashed a possible GUI implementation error. If you can recreate this, please look at http://issues.jalview.org/browse/JAL-869",
989 * Add key bindings to a JInternalFrame so that Ctrl-W and Cmd-W will close the
994 private static void setKeyBindings(JInternalFrame frame)
996 final Action closeAction = new AbstractAction()
999 public void actionPerformed(ActionEvent e)
1006 * set up key bindings for Ctrl-W and Cmd-W, with the same (Close) action
1008 KeyStroke ctrlWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
1009 InputEvent.CTRL_DOWN_MASK);
1010 KeyStroke cmdWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
1011 Toolkit.getDefaultToolkit().getMenuShortcutKeyMask());
1013 InputMap inputMap = frame
1014 .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
1015 String ctrlW = ctrlWKey.toString();
1016 inputMap.put(ctrlWKey, ctrlW);
1017 inputMap.put(cmdWKey, ctrlW);
1019 ActionMap actionMap = frame.getActionMap();
1020 actionMap.put(ctrlW, closeAction);
1024 public void lostOwnership(Clipboard clipboard, Transferable contents)
1028 Instance.getDesktop().jalviewClipboard = null;
1031 internalCopy = false;
1035 public void dragEnter(DropTargetDragEvent evt)
1040 public void dragExit(DropTargetEvent evt)
1045 public void dragOver(DropTargetDragEvent evt)
1050 public void dropActionChanged(DropTargetDragEvent evt)
1061 public void drop(DropTargetDropEvent evt)
1063 boolean success = true;
1064 // JAL-1552 - acceptDrop required before getTransferable call for
1065 // Java's Transferable for native dnd
1066 evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
1067 Transferable t = evt.getTransferable();
1068 List<Object> files = new ArrayList<>();
1069 List<DataSourceType> protocols = new ArrayList<>();
1073 Desktop.transferFromDropTarget(files, protocols, evt, t);
1074 } catch (Exception e)
1076 e.printStackTrace();
1084 for (int i = 0; i < files.size(); i++)
1086 // BH 2018 File or String
1087 Object file = files.get(i);
1088 String fileName = file.toString();
1089 DataSourceType protocol = (protocols == null)
1090 ? DataSourceType.FILE
1092 FileFormatI format = null;
1094 if (fileName.endsWith(".jar"))
1096 format = FileFormat.Jalview;
1101 format = new IdentifyFile().identify(file, protocol);
1103 if (file instanceof File)
1105 Platform.cacheFileData((File) file);
1107 new FileLoader().LoadFile(null, file, protocol, format);
1110 } catch (Exception ex)
1115 evt.dropComplete(success); // need this to ensure input focus is properly
1116 // transfered to any new windows created
1126 public void inputLocalFileMenuItem_actionPerformed(AlignViewport viewport)
1128 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
1129 JalviewFileChooser chooser = JalviewFileChooser
1130 .forRead(Cache.getProperty("LAST_DIRECTORY"), fileFormat, true);
1132 chooser.setFileView(new JalviewFileView());
1133 chooser.setDialogTitle(
1134 MessageManager.getString("label.open_local_file"));
1135 chooser.setToolTipText(MessageManager.getString("action.open"));
1137 chooser.setResponseHandler(0, new Runnable()
1142 File selectedFile = chooser.getSelectedFile();
1143 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1145 FileFormatI format = chooser.getSelectedFormat();
1148 * Call IdentifyFile to verify the file contains what its extension implies.
1149 * Skip this step for dynamically added file formats, because
1150 * IdentifyFile does not know how to recognise them.
1152 if (FileFormats.getInstance().isIdentifiable(format))
1156 format = new IdentifyFile().identify(selectedFile,
1157 DataSourceType.FILE);
1158 } catch (FileFormatException e)
1160 // format = null; //??
1164 new FileLoader().LoadFile(viewport, selectedFile,
1165 DataSourceType.FILE, format);
1168 chooser.showOpenDialog(this);
1172 * Shows a dialog for input of a URL at which to retrieve alignment data
1177 public void inputURLMenuItem_actionPerformed(AlignViewport viewport)
1179 // This construct allows us to have a wider textfield
1181 JLabel label = new JLabel(
1182 MessageManager.getString("label.input_file_url"));
1184 JPanel panel = new JPanel(new GridLayout(2, 1));
1188 * the URL to fetch is
1189 * Java: an editable combobox with history
1190 * JS: (pending JAL-3038) a plain text field
1193 String urlBase = "http://www.";
1194 if (Platform.isJS())
1196 history = new JTextField(urlBase, 35);
1205 JComboBox<String> asCombo = new JComboBox<>();
1206 asCombo.setPreferredSize(new Dimension(400, 20));
1207 asCombo.setEditable(true);
1208 asCombo.addItem(urlBase);
1209 String historyItems = Cache.getProperty("RECENT_URL");
1210 if (historyItems != null)
1212 for (String token : historyItems.split("\\t"))
1214 asCombo.addItem(token);
1221 Object[] options = new Object[] { MessageManager.getString("action.ok"),
1222 MessageManager.getString("action.cancel") };
1223 Runnable action = new Runnable()
1228 @SuppressWarnings("unchecked")
1229 String url = (history instanceof JTextField
1230 ? ((JTextField) history).getText()
1231 : ((JComboBox<String>) history).getSelectedItem()
1234 if (url.toLowerCase().endsWith(".jar"))
1236 if (viewport != null)
1238 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1239 FileFormat.Jalview);
1243 new FileLoader().LoadFile(url, DataSourceType.URL,
1244 FileFormat.Jalview);
1249 FileFormatI format = null;
1252 format = new IdentifyFile().identify(url, DataSourceType.URL);
1253 } catch (FileFormatException e)
1255 // TODO revise error handling, distinguish between
1256 // URL not found and response not valid
1261 String msg = MessageManager
1262 .formatMessage("label.couldnt_locate", url);
1263 JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
1265 MessageManager.getString("label.url_not_found"),
1266 JvOptionPane.WARNING_MESSAGE);
1271 if (viewport != null)
1273 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1278 new FileLoader().LoadFile(url, DataSourceType.URL, format);
1283 String dialogOption = MessageManager
1284 .getString("label.input_alignment_from_url");
1285 JvOptionPane.newOptionDialog(getDesktopPane())
1286 .setResponseHandler(0, action)
1287 .showInternalDialog(panel, dialogOption,
1288 JvOptionPane.YES_NO_CANCEL_OPTION,
1289 JvOptionPane.PLAIN_MESSAGE, null, options,
1290 MessageManager.getString("action.ok"));
1294 * Opens the CutAndPaste window for the user to paste an alignment in to
1297 * - if not null, the pasted alignment is added to the current
1298 * alignment; if null, to a new alignment window
1301 public void inputTextboxMenuItem_actionPerformed(
1302 AlignmentViewPanel viewPanel)
1304 CutAndPasteTransfer cap = new CutAndPasteTransfer();
1305 cap.setForInput(viewPanel);
1306 Desktop.addInternalFrame(cap,
1307 MessageManager.getString("label.cut_paste_alignmen_file"), true,
1317 Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
1318 jalview.bin.Cache.setProperty("SCREENGEOMETRY_WIDTH",
1320 jalview.bin.Cache.setProperty("SCREENGEOMETRY_HEIGHT",
1321 screen.height + "");
1322 storeLastKnownDimensions("", new Rectangle(getBounds().x, getBounds().y,
1323 getWidth(), getHeight()));
1325 if (jconsole != null)
1327 storeLastKnownDimensions("JAVA_CONSOLE_", jconsole.getBounds());
1328 jconsole.stopConsole();
1332 storeLastKnownDimensions("JALVIEW_RSS_WINDOW_", jvnews.getBounds());
1335 if (dialogExecutor != null)
1337 dialogExecutor.shutdownNow();
1339 closeAll_actionPerformed(null);
1341 if (groovyConsole != null)
1343 // suppress a possible repeat prompt to save script
1344 groovyConsole.setDirty(false);
1345 groovyConsole.exit();
1350 private void storeLastKnownDimensions(String string, Rectangle jc)
1352 jalview.bin.Cache.log.debug("Storing last known dimensions for "
1353 + string + ": x:" + jc.x + " y:" + jc.y + " width:" + jc.width
1354 + " height:" + jc.height);
1356 jalview.bin.Cache.setProperty(string + "SCREEN_X", jc.x + "");
1357 jalview.bin.Cache.setProperty(string + "SCREEN_Y", jc.y + "");
1358 jalview.bin.Cache.setProperty(string + "SCREEN_WIDTH", jc.width + "");
1359 jalview.bin.Cache.setProperty(string + "SCREEN_HEIGHT", jc.height + "");
1369 public void aboutMenuItem_actionPerformed(ActionEvent e)
1371 // StringBuffer message = getAboutMessage(false);
1372 // JvOptionPane.showInternalMessageDialog(Desktop.getDesktop(),
1374 // message.toString(), "About Jalview", JvOptionPane.INFORMATION_MESSAGE);
1375 new Thread(new Runnable()
1380 new SplashScreen(true);
1385 public StringBuffer getAboutMessage(boolean shortv)
1387 StringBuffer message = new StringBuffer();
1388 message.append("<html>");
1391 message.append("<h1><strong>Version: "
1392 + jalview.bin.Cache.getProperty("VERSION")
1393 + "</strong></h1>");
1394 message.append("<strong>Last Updated: <em>"
1395 + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown")
1396 + "</em></strong>");
1402 message.append("<strong>Version "
1403 + jalview.bin.Cache.getProperty("VERSION")
1404 + "; last updated: "
1405 + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown"));
1408 if (jalview.bin.Cache.getDefault("LATEST_VERSION", "Checking")
1409 .equals("Checking"))
1411 message.append("<br>...Checking latest version...</br>");
1413 else if (!jalview.bin.Cache.getDefault("LATEST_VERSION", "Checking")
1414 .equals(jalview.bin.Cache.getProperty("VERSION")))
1416 boolean red = false;
1417 if (jalview.bin.Cache.getProperty("VERSION").toLowerCase()
1418 .indexOf("automated build") == -1)
1421 // Displayed when code version and jnlp version do not match and code
1422 // version is not a development build
1423 message.append("<div style=\"color: #FF0000;font-style: bold;\">");
1426 message.append("<br>!! Version "
1427 + jalview.bin.Cache.getDefault("LATEST_VERSION",
1429 + " is available for download from "
1430 + jalview.bin.Cache.getDefault("www.jalview.org",
1431 "http://www.jalview.org")
1435 message.append("</div>");
1438 message.append("<br>Authors: " + jalview.bin.Cache.getDefault(
1440 "The Jalview Authors (See AUTHORS file for current list)")
1441 + "<br><br>Development managed by The Barton Group, University of Dundee, Scotland, UK.<br>"
1442 + "<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"
1443 + "<br><br>If you use Jalview, please cite:"
1444 + "<br>Waterhouse, A.M., Procter, J.B., Martin, D.M.A, Clamp, M. and Barton, G. J. (2009)"
1445 + "<br>Jalview Version 2 - a multiple sequence alignment editor and analysis workbench"
1446 + "<br>Bioinformatics doi: 10.1093/bioinformatics/btp033"
1452 * Action on requesting Help documentation
1455 public void documentationMenuItem_actionPerformed()
1459 if (Platform.isJS())
1461 BrowserLauncher.openURL("http://www.jalview.org/help.html");
1470 Help.showHelpWindow();
1472 } catch (Exception ex)
1474 System.err.println("Error opening help: " + ex.getMessage());
1479 public void closeAll_actionPerformed(ActionEvent e)
1481 if (desktopPane == null)
1485 // TODO show a progress bar while closing?
1486 JInternalFrame[] frames = desktopPane.getAllFrames();
1487 for (int i = 0; i < frames.length; i++)
1491 frames[i].setClosed(true);
1492 } catch (java.beans.PropertyVetoException ex)
1496 Jalview.setCurrentAlignFrame(null);
1497 System.out.println("ALL CLOSED");
1498 if (v_client != null)
1500 // TODO clear binding to vamsas document objects on close_all
1504 * reset state of singleton objects as appropriate (clear down session state
1505 * when all windows are closed)
1507 StructureSelectionManager ssm = StructureSelectionManager
1508 .getStructureSelectionManager(this);
1516 public void raiseRelated_actionPerformed(ActionEvent e)
1518 reorderAssociatedWindows(false, false);
1522 public void minimizeAssociated_actionPerformed(ActionEvent e)
1524 reorderAssociatedWindows(true, false);
1527 void closeAssociatedWindows()
1529 reorderAssociatedWindows(false, true);
1535 * @seejalview.jbgui.GDesktop#garbageCollect_actionPerformed(java.awt.event.
1539 protected void garbageCollect_actionPerformed(ActionEvent e)
1541 // We simply collect the garbage
1542 jalview.bin.Cache.log.debug("Collecting garbage...");
1544 jalview.bin.Cache.log.debug("Finished garbage collection.");
1551 * jalview.jbgui.GDesktop#showMemusage_actionPerformed(java.awt.event.ActionEvent
1555 protected void showMemusage_actionPerformed(ActionEvent e)
1557 getDesktopPane().showMemoryUsage(showMemusage.isSelected());
1564 * jalview.jbgui.GDesktop#showConsole_actionPerformed(java.awt.event.ActionEvent
1568 protected void showConsole_actionPerformed(ActionEvent e)
1570 showConsole(showConsole.isSelected());
1573 Console jconsole = null;
1576 * control whether the java console is visible or not
1580 void showConsole(boolean selected)
1582 // TODO: decide if we should update properties file
1583 if (jconsole != null) // BH 2018
1585 showConsole.setSelected(selected);
1586 Cache.setProperty("SHOW_JAVA_CONSOLE",
1587 Boolean.valueOf(selected).toString());
1588 jconsole.setVisible(selected);
1592 void reorderAssociatedWindows(boolean minimize, boolean close)
1594 JInternalFrame[] frames = getDesktopPane().getAllFrames();
1595 if (frames == null || frames.length < 1)
1600 AlignmentViewport source = null, target = null;
1601 if (frames[0] instanceof AlignFrame)
1603 source = ((AlignFrame) frames[0]).getCurrentView();
1605 else if (frames[0] instanceof TreePanel)
1607 source = ((TreePanel) frames[0]).getViewPort();
1609 else if (frames[0] instanceof PCAPanel)
1611 source = ((PCAPanel) frames[0]).av;
1613 else if (frames[0].getContentPane() instanceof PairwiseAlignPanel)
1615 source = ((PairwiseAlignPanel) frames[0].getContentPane()).av;
1620 for (int i = 0; i < frames.length; i++)
1623 if (frames[i] == null)
1627 if (frames[i] instanceof AlignFrame)
1629 target = ((AlignFrame) frames[i]).getCurrentView();
1631 else if (frames[i] instanceof TreePanel)
1633 target = ((TreePanel) frames[i]).getViewPort();
1635 else if (frames[i] instanceof PCAPanel)
1637 target = ((PCAPanel) frames[i]).av;
1639 else if (frames[i].getContentPane() instanceof PairwiseAlignPanel)
1641 target = ((PairwiseAlignPanel) frames[i].getContentPane()).av;
1644 if (source == target)
1650 frames[i].setClosed(true);
1654 frames[i].setIcon(minimize);
1657 frames[i].toFront();
1661 } catch (java.beans.PropertyVetoException ex)
1676 protected void preferences_actionPerformed(ActionEvent e)
1682 * Prompts the user to choose a file and then saves the Jalview state as a
1683 * Jalview project file
1686 public void saveState_actionPerformed()
1688 saveState_actionPerformed(false);
1691 public void saveState_actionPerformed(boolean saveAs)
1693 java.io.File projectFile = getProjectFile();
1694 // autoSave indicates we already have a file and don't need to ask
1695 boolean autoSave = projectFile != null && !saveAs
1696 && BackupFiles.getEnabled();
1698 // System.out.println("autoSave="+autoSave+", projectFile='"+projectFile+"',
1699 // saveAs="+saveAs+", Backups
1700 // "+(BackupFiles.getEnabled()?"enabled":"disabled"));
1702 boolean approveSave = false;
1705 JalviewFileChooser chooser = new JalviewFileChooser("jvp",
1708 chooser.setFileView(new JalviewFileView());
1709 chooser.setDialogTitle(MessageManager.getString("label.save_state"));
1711 int value = chooser.showSaveDialog(this);
1713 if (value == JalviewFileChooser.APPROVE_OPTION)
1715 projectFile = chooser.getSelectedFile();
1716 setProjectFile(projectFile);
1721 if (approveSave || autoSave)
1723 final Desktop me = this;
1724 final java.io.File chosenFile = projectFile;
1725 new Thread(new Runnable()
1730 // TODO: refactor to Jalview desktop session controller action.
1731 setProgressBar(MessageManager.formatMessage(
1732 "label.saving_jalview_project", new Object[]
1733 { chosenFile.getName() }), chosenFile.hashCode());
1734 jalview.bin.Cache.setProperty("LAST_DIRECTORY",
1735 chosenFile.getParent());
1736 // TODO catch and handle errors for savestate
1737 // TODO prevent user from messing with the Desktop whilst we're saving
1740 boolean doBackup = BackupFiles.getEnabled();
1741 BackupFiles backupfiles = doBackup ? new BackupFiles(chosenFile) : null;
1743 new Jalview2XML().saveState(doBackup ? backupfiles.getTempFile() : chosenFile);
1747 backupfiles.setWriteSuccess(true);
1748 backupfiles.rollBackupsAndRenameTempFile();
1750 } catch (OutOfMemoryError oom)
1752 new OOMWarning("Whilst saving current state to "
1753 + chosenFile.getName(), oom);
1754 } catch (Exception ex)
1756 Cache.log.error("Problems whilst trying to save to "
1757 + chosenFile.getName(), ex);
1758 JvOptionPane.showMessageDialog(me,
1759 MessageManager.formatMessage(
1760 "label.error_whilst_saving_current_state_to",
1762 { chosenFile.getName() }),
1763 MessageManager.getString("label.couldnt_save_project"),
1764 JvOptionPane.WARNING_MESSAGE);
1766 setProgressBar(null, chosenFile.hashCode());
1773 public void saveAsState_actionPerformed(ActionEvent e)
1775 saveState_actionPerformed(true);
1778 protected void setProjectFile(File choice)
1780 this.projectFile = choice;
1783 public File getProjectFile()
1785 return this.projectFile;
1789 * Shows a file chooser dialog and tries to read in the selected file as a
1793 public void loadState_actionPerformed()
1795 final String[] suffix = new String[] { "jvp", "jar" };
1796 final String[] desc = new String[] { "Jalview Project",
1797 "Jalview Project (old)" };
1798 JalviewFileChooser chooser = new JalviewFileChooser(
1799 Cache.getProperty("LAST_DIRECTORY"), suffix, desc,
1800 "Jalview Project", true, true); // last two booleans: allFiles,
1802 chooser.setFileView(new JalviewFileView());
1803 chooser.setDialogTitle(MessageManager.getString("label.restore_state"));
1804 chooser.setResponseHandler(0, new Runnable()
1809 File selectedFile = chooser.getSelectedFile();
1810 setProjectFile(selectedFile);
1811 String choice = selectedFile.getAbsolutePath();
1812 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1813 new Thread(new Runnable()
1820 new Jalview2XML().loadJalviewAlign(choice);
1821 } catch (OutOfMemoryError oom)
1823 new OOMWarning("Whilst loading project from " + choice, oom);
1824 } catch (Exception ex)
1827 "Problems whilst loading project from " + choice, ex);
1828 JvOptionPane.showMessageDialog(Desktop.getDesktopPane(),
1829 MessageManager.formatMessage(
1830 "label.error_whilst_loading_project_from",
1833 MessageManager.getString("label.couldnt_load_project"),
1834 JvOptionPane.WARNING_MESSAGE);
1841 chooser.showOpenDialog(this);
1845 public void inputSequence_actionPerformed(ActionEvent e)
1847 new SequenceFetcher(this);
1850 JPanel progressPanel;
1852 ArrayList<JPanel> fileLoadingPanels = new ArrayList<>();
1854 public void startLoading(final Object fileName)
1856 if (fileLoadingCount == 0)
1858 fileLoadingPanels.add(addProgressPanel(MessageManager
1859 .formatMessage("label.loading_file", new Object[]
1865 private JPanel addProgressPanel(String string)
1867 if (progressPanel == null)
1869 progressPanel = new JPanel(new GridLayout(1, 1));
1870 totalProgressCount = 0;
1871 getContentPane().add(progressPanel, BorderLayout.SOUTH);
1873 JPanel thisprogress = new JPanel(new BorderLayout(10, 5));
1874 JProgressBar progressBar = new JProgressBar();
1875 progressBar.setIndeterminate(true);
1877 thisprogress.add(new JLabel(string), BorderLayout.WEST);
1879 thisprogress.add(progressBar, BorderLayout.CENTER);
1880 progressPanel.add(thisprogress);
1881 ((GridLayout) progressPanel.getLayout()).setRows(
1882 ((GridLayout) progressPanel.getLayout()).getRows() + 1);
1883 ++totalProgressCount;
1885 return thisprogress;
1888 int totalProgressCount = 0;
1890 private void removeProgressPanel(JPanel progbar)
1892 if (progressPanel != null)
1894 synchronized (progressPanel)
1896 progressPanel.remove(progbar);
1897 GridLayout gl = (GridLayout) progressPanel.getLayout();
1898 gl.setRows(gl.getRows() - 1);
1899 if (--totalProgressCount < 1)
1901 this.getContentPane().remove(progressPanel);
1902 progressPanel = null;
1909 public void stopLoading()
1912 if (fileLoadingCount < 1)
1914 while (fileLoadingPanels.size() > 0)
1916 removeProgressPanel(fileLoadingPanels.remove(0));
1918 fileLoadingPanels.clear();
1919 fileLoadingCount = 0;
1924 public static int getViewCount(String alignmentId)
1926 AlignmentViewport[] aps = getViewports(alignmentId);
1927 return (aps == null) ? 0 : aps.length;
1932 * @param alignmentId
1933 * - if null, all sets are returned
1934 * @return all AlignmentPanels concerning the alignmentId sequence set
1936 public static AlignmentPanel[] getAlignmentPanels(String alignmentId)
1938 if (Desktop.getDesktopPane() == null)
1940 // no frames created and in headless mode
1941 // TODO: verify that frames are recoverable when in headless mode
1944 List<AlignmentPanel> aps = new ArrayList<>();
1945 AlignFrame[] frames = getAlignFrames();
1950 for (AlignFrame af : frames)
1952 for (AlignmentPanel ap : af.alignPanels)
1954 if (alignmentId == null
1955 || alignmentId.equals(ap.av.getSequenceSetId()))
1961 if (aps.size() == 0)
1965 AlignmentPanel[] vap = aps.toArray(new AlignmentPanel[aps.size()]);
1970 * get all the viewports on an alignment.
1972 * @param sequenceSetId
1973 * unique alignment id (may be null - all viewports returned in that
1975 * @return all viewports on the alignment bound to sequenceSetId
1977 public static AlignmentViewport[] getViewports(String sequenceSetId)
1979 List<AlignmentViewport> viewp = new ArrayList<>();
1980 if (getDesktopPane() != null)
1982 AlignFrame[] frames = Desktop.getAlignFrames();
1984 for (AlignFrame afr : frames)
1986 if (sequenceSetId == null || afr.getViewport().getSequenceSetId()
1987 .equals(sequenceSetId))
1989 if (afr.alignPanels != null)
1991 for (AlignmentPanel ap : afr.alignPanels)
1993 if (sequenceSetId == null
1994 || sequenceSetId.equals(ap.av.getSequenceSetId()))
2002 viewp.add(afr.getViewport());
2006 if (viewp.size() > 0)
2008 return viewp.toArray(new AlignmentViewport[viewp.size()]);
2015 * Explode the views in the given frame into separate AlignFrame
2019 public static void explodeViews(AlignFrame af)
2021 int size = af.alignPanels.size();
2027 for (int i = 0; i < size; i++)
2029 AlignmentPanel ap = af.alignPanels.get(i);
2030 AlignFrame newaf = new AlignFrame(ap);
2033 * Restore the view's last exploded frame geometry if known. Multiple
2034 * views from one exploded frame share and restore the same (frame)
2035 * position and size.
2037 Rectangle geometry = ap.av.getExplodedGeometry();
2038 if (geometry != null)
2040 newaf.setBounds(geometry);
2043 ap.av.setGatherViewsHere(false);
2045 addInternalFrame(newaf, af.getTitle(), AlignFrame.DEFAULT_WIDTH,
2046 AlignFrame.DEFAULT_HEIGHT);
2049 af.alignPanels.clear();
2050 af.closeMenuItem_actionPerformed(true);
2055 * Gather expanded views (separate AlignFrame's) with the same sequence set
2056 * identifier back in to this frame as additional views, and close the expanded
2057 * views. Note the expanded frames may themselves have multiple views. We take
2062 public void gatherViews(AlignFrame source)
2064 source.viewport.setGatherViewsHere(true);
2065 source.viewport.setExplodedGeometry(source.getBounds());
2066 JInternalFrame[] frames = getAllFrames();
2067 String viewId = source.viewport.getSequenceSetId();
2069 for (int t = 0; t < frames.length; t++)
2071 if (frames[t] instanceof AlignFrame && frames[t] != source)
2073 AlignFrame af = (AlignFrame) frames[t];
2074 boolean gatherThis = false;
2075 for (int a = 0; a < af.alignPanels.size(); a++)
2077 AlignmentPanel ap = af.alignPanels.get(a);
2078 if (viewId.equals(ap.av.getSequenceSetId()))
2081 ap.av.setGatherViewsHere(false);
2082 ap.av.setExplodedGeometry(af.getBounds());
2083 source.addAlignmentPanel(ap, false);
2089 af.alignPanels.clear();
2090 af.closeMenuItem_actionPerformed(true);
2097 jalview.gui.VamsasApplication v_client = null;
2100 public void vamsasImport_actionPerformed(ActionEvent e)
2102 // TODO: JAL-3048 not needed for Jalview-JS
2104 if (v_client == null)
2106 // Load and try to start a session.
2107 JalviewFileChooser chooser = new JalviewFileChooser(
2108 jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
2110 chooser.setFileView(new JalviewFileView());
2111 chooser.setDialogTitle(
2112 MessageManager.getString("label.open_saved_vamsas_session"));
2113 chooser.setToolTipText(MessageManager.getString(
2114 "label.select_vamsas_session_opened_as_new_vamsas_session"));
2116 int value = chooser.showOpenDialog(this);
2118 if (value == JalviewFileChooser.APPROVE_OPTION)
2120 String fle = chooser.getSelectedFile().toString();
2121 if (!vamsasImport(chooser.getSelectedFile()))
2123 JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
2124 MessageManager.formatMessage(
2125 "label.couldnt_import_as_vamsas_session",
2129 .getString("label.vamsas_document_import_failed"),
2130 JvOptionPane.ERROR_MESSAGE);
2136 jalview.bin.Cache.log.error(
2137 "Implementation error - load session from a running session is not supported.");
2142 * import file into a new vamsas session (uses jalview.gui.VamsasApplication)
2145 * @return true if import was a success and a session was started.
2147 public boolean vamsasImport(URL url)
2149 // TODO: create progress bar
2150 if (v_client != null)
2153 jalview.bin.Cache.log.error(
2154 "Implementation error - load session from a running session is not supported.");
2160 // copy the URL content to a temporary local file
2161 // TODO: be a bit cleverer here with nio (?!)
2162 File file = File.createTempFile("vdocfromurl", ".vdj");
2163 FileOutputStream fos = new FileOutputStream(file);
2164 BufferedInputStream bis = new BufferedInputStream(url.openStream());
2165 byte[] buffer = new byte[2048];
2167 while ((ln = bis.read(buffer)) > -1)
2169 fos.write(buffer, 0, ln);
2173 v_client = new jalview.gui.VamsasApplication(this, file,
2174 url.toExternalForm());
2175 } catch (Exception ex)
2177 jalview.bin.Cache.log.error(
2178 "Failed to create new vamsas session from contents of URL "
2183 setupVamsasConnectedGui();
2184 v_client.initial_update(); // TODO: thread ?
2185 return v_client.inSession();
2189 * import file into a new vamsas session (uses jalview.gui.VamsasApplication)
2192 * @return true if import was a success and a session was started.
2194 public boolean vamsasImport(File file)
2196 if (v_client != null)
2199 jalview.bin.Cache.log.error(
2200 "Implementation error - load session from a running session is not supported.");
2204 setProgressBar(MessageManager.formatMessage(
2205 "status.importing_vamsas_session_from", new Object[]
2206 { file.getName() }), file.hashCode());
2209 v_client = new jalview.gui.VamsasApplication(this, file, null);
2210 } catch (Exception ex)
2212 setProgressBar(MessageManager.formatMessage(
2213 "status.importing_vamsas_session_from", new Object[]
2214 { file.getName() }), file.hashCode());
2215 jalview.bin.Cache.log.error(
2216 "New vamsas session from existing session file failed:", ex);
2219 setupVamsasConnectedGui();
2220 v_client.initial_update(); // TODO: thread ?
2221 setProgressBar(MessageManager.formatMessage(
2222 "status.importing_vamsas_session_from", new Object[]
2223 { file.getName() }), file.hashCode());
2224 return v_client.inSession();
2227 public boolean joinVamsasSession(String mysesid)
2229 if (v_client != null)
2231 throw new Error(MessageManager
2232 .getString("error.try_join_vamsas_session_another"));
2234 if (mysesid == null)
2237 MessageManager.getString("error.invalid_vamsas_session_id"));
2239 v_client = new VamsasApplication(this, mysesid);
2240 setupVamsasConnectedGui();
2241 v_client.initial_update();
2242 return (v_client.inSession());
2246 public void vamsasStart_actionPerformed(ActionEvent e)
2248 if (v_client == null)
2251 // we just start a default session for moment.
2253 * JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache.
2254 * getProperty("LAST_DIRECTORY"));
2256 * chooser.setFileView(new JalviewFileView());
2257 * chooser.setDialogTitle("Load Vamsas file");
2258 * chooser.setToolTipText("Import");
2260 * int value = chooser.showOpenDialog(this);
2262 * if (value == JalviewFileChooser.APPROVE_OPTION) { v_client = new
2263 * jalview.gui.VamsasApplication(this, chooser.getSelectedFile());
2265 v_client = new VamsasApplication(this);
2266 setupVamsasConnectedGui();
2267 v_client.initial_update(); // TODO: thread ?
2271 // store current data in session.
2272 v_client.push_update(); // TODO: thread
2276 protected void setupVamsasConnectedGui()
2278 vamsasStart.setText(MessageManager.getString("label.session_update"));
2279 vamsasSave.setVisible(true);
2280 vamsasStop.setVisible(true);
2281 vamsasImport.setVisible(false); // Document import to existing session is
2282 // not possible for vamsas-client-1.0.
2285 protected void setupVamsasDisconnectedGui()
2287 vamsasSave.setVisible(false);
2288 vamsasStop.setVisible(false);
2289 vamsasImport.setVisible(true);
2291 .setText(MessageManager.getString("label.new_vamsas_session"));
2295 public void vamsasStop_actionPerformed(ActionEvent e)
2297 if (v_client != null)
2299 v_client.end_session();
2301 setupVamsasDisconnectedGui();
2305 protected void buildVamsasStMenu()
2307 if (v_client == null)
2309 String[] sess = null;
2312 sess = VamsasApplication.getSessionList();
2313 } catch (Exception e)
2315 jalview.bin.Cache.log.warn("Problem getting current sessions list.",
2321 jalview.bin.Cache.log.debug(
2322 "Got current sessions list: " + sess.length + " entries.");
2323 VamsasStMenu.removeAll();
2324 for (int i = 0; i < sess.length; i++)
2326 JMenuItem sessit = new JMenuItem();
2327 sessit.setText(sess[i]);
2328 sessit.setToolTipText(MessageManager
2329 .formatMessage("label.connect_to_session", new Object[]
2331 final Desktop dsktp = this;
2332 final String mysesid = sess[i];
2333 sessit.addActionListener(new ActionListener()
2337 public void actionPerformed(ActionEvent e)
2339 if (dsktp.v_client == null)
2341 Thread rthr = new Thread(new Runnable()
2347 dsktp.v_client = new VamsasApplication(dsktp, mysesid);
2348 dsktp.setupVamsasConnectedGui();
2349 dsktp.v_client.initial_update();
2357 VamsasStMenu.add(sessit);
2359 // don't show an empty menu.
2360 VamsasStMenu.setVisible(sess.length > 0);
2365 jalview.bin.Cache.log.debug("No current vamsas sessions.");
2366 VamsasStMenu.removeAll();
2367 VamsasStMenu.setVisible(false);
2372 // Not interested in the content. Just hide ourselves.
2373 VamsasStMenu.setVisible(false);
2378 public void vamsasSave_actionPerformed(ActionEvent e)
2380 // TODO: JAL-3048 not needed for Jalview-JS
2382 if (v_client != null)
2384 // TODO: VAMSAS DOCUMENT EXTENSION is VDJ
2385 JalviewFileChooser chooser = new JalviewFileChooser("vdj",
2388 chooser.setFileView(new JalviewFileView());
2389 chooser.setDialogTitle(MessageManager
2390 .getString("label.save_vamsas_document_archive"));
2392 int value = chooser.showSaveDialog(this);
2394 if (value == JalviewFileChooser.APPROVE_OPTION)
2396 java.io.File choice = chooser.getSelectedFile();
2397 JPanel progpanel = addProgressPanel(MessageManager
2398 .formatMessage("label.saving_vamsas_doc", new Object[]
2399 { choice.getName() }));
2400 Cache.setProperty("LAST_DIRECTORY", choice.getParent());
2401 String warnmsg = null;
2402 String warnttl = null;
2405 v_client.vclient.storeDocument(choice);
2408 warnttl = "Serious Problem saving Vamsas Document";
2409 warnmsg = ex.toString();
2410 jalview.bin.Cache.log
2411 .error("Error Whilst saving document to " + choice, ex);
2413 } catch (Exception ex)
2415 warnttl = "Problem saving Vamsas Document.";
2416 warnmsg = ex.toString();
2417 jalview.bin.Cache.log.warn(
2418 "Exception Whilst saving document to " + choice, ex);
2421 removeProgressPanel(progpanel);
2422 if (warnmsg != null)
2424 JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
2426 warnmsg, warnttl, JvOptionPane.ERROR_MESSAGE);
2432 JPanel vamUpdate = null;
2435 * hide vamsas user gui bits when a vamsas document event is being handled.
2438 * true to hide gui, false to reveal gui
2440 public void setVamsasUpdate(boolean b)
2442 Cache.log.debug("Setting gui for Vamsas update "
2443 + (b ? "in progress" : "finished"));
2445 if (vamUpdate != null)
2447 this.removeProgressPanel(vamUpdate);
2451 vamUpdate = this.addProgressPanel(
2452 MessageManager.getString("label.updating_vamsas_session"));
2454 vamsasStart.setVisible(!b);
2455 vamsasStop.setVisible(!b);
2456 vamsasSave.setVisible(!b);
2459 public JInternalFrame[] getAllFrames()
2461 return desktopPane.getAllFrames();
2465 * Checks the given url to see if it gives a response indicating that the user
2466 * should be informed of a new questionnaire.
2470 public void checkForQuestionnaire(String url)
2472 UserQuestionnaireCheck jvq = new UserQuestionnaireCheck(url);
2473 // javax.swing.SwingUtilities.invokeLater(jvq);
2474 new Thread(jvq).start();
2477 public void checkURLLinks()
2479 // Thread off the URL link checker
2480 addDialogThread(new Runnable()
2485 if (Cache.getDefault("CHECKURLLINKS", true))
2487 // check what the actual links are - if it's just the default don't
2488 // bother with the warning
2489 List<String> links = Preferences.sequenceUrlLinks
2492 // only need to check links if there is one with a
2493 // SEQUENCE_ID which is not the default EMBL_EBI link
2494 ListIterator<String> li = links.listIterator();
2495 boolean check = false;
2496 List<JLabel> urls = new ArrayList<>();
2497 while (li.hasNext())
2499 String link = li.next();
2500 if (link.contains(UrlConstants.SEQUENCE_ID)
2501 && !UrlConstants.isDefaultString(link))
2504 int barPos = link.indexOf("|");
2505 String urlMsg = barPos == -1 ? link
2506 : link.substring(0, barPos) + ": "
2507 + link.substring(barPos + 1);
2508 urls.add(new JLabel(urlMsg));
2516 // ask user to check in case URL links use old style tokens
2517 // ($SEQUENCE_ID$ for sequence id _or_ accession id)
2518 JPanel msgPanel = new JPanel();
2519 msgPanel.setLayout(new BoxLayout(msgPanel, BoxLayout.PAGE_AXIS));
2520 msgPanel.add(Box.createVerticalGlue());
2521 JLabel msg = new JLabel(MessageManager
2522 .getString("label.SEQUENCE_ID_for_DB_ACCESSION1"));
2523 JLabel msg2 = new JLabel(MessageManager
2524 .getString("label.SEQUENCE_ID_for_DB_ACCESSION2"));
2526 for (JLabel url : urls)
2532 final JCheckBox jcb = new JCheckBox(
2533 MessageManager.getString("label.do_not_display_again"));
2534 jcb.addActionListener(new ActionListener()
2537 public void actionPerformed(ActionEvent e)
2539 // update Cache settings for "don't show this again"
2540 boolean showWarningAgain = !jcb.isSelected();
2541 Cache.setProperty("CHECKURLLINKS",
2542 Boolean.valueOf(showWarningAgain).toString());
2547 JvOptionPane.showMessageDialog(desktopPane, msgPanel,
2549 .getString("label.SEQUENCE_ID_no_longer_used"),
2550 JvOptionPane.WARNING_MESSAGE);
2557 * Proxy class for JDesktopPane which optionally displays the current memory
2558 * usage and highlights the desktop area with a red bar if free memory runs low.
2562 public class MyDesktopPane extends JDesktopPane
2565 private static final float ONE_MB = 1048576f;
2567 boolean showMemoryUsage = false;
2571 java.text.NumberFormat df;
2573 float maxMemory, allocatedMemory, freeMemory, totalFreeMemory,
2576 public MyDesktopPane(boolean showMemoryUsage)
2578 showMemoryUsage(showMemoryUsage);
2581 public void showMemoryUsage(boolean showMemory)
2583 this.showMemoryUsage = showMemory;
2586 Thread worker = new Thread(this);
2592 public boolean isShowMemoryUsage()
2594 return showMemoryUsage;
2600 df = java.text.NumberFormat.getNumberInstance();
2601 df.setMaximumFractionDigits(2);
2602 runtime = Runtime.getRuntime();
2604 while (showMemoryUsage)
2608 maxMemory = runtime.maxMemory() / ONE_MB;
2609 allocatedMemory = runtime.totalMemory() / ONE_MB;
2610 freeMemory = runtime.freeMemory() / ONE_MB;
2611 totalFreeMemory = freeMemory + (maxMemory - allocatedMemory);
2613 percentUsage = (totalFreeMemory / maxMemory) * 100;
2615 // if (percentUsage < 20)
2617 // border1 = BorderFactory.createMatteBorder(12, 12, 12, 12,
2619 // instance.set.setBorder(border1);
2622 // sleep after showing usage
2624 } catch (Exception ex)
2626 ex.printStackTrace();
2632 public void paintComponent(Graphics g)
2634 if (showMemoryUsage && g != null && df != null)
2636 if (percentUsage < 20)
2638 g.setColor(Color.red);
2640 FontMetrics fm = g.getFontMetrics();
2643 g.drawString(MessageManager.formatMessage("label.memory_stats",
2645 { df.format(totalFreeMemory), df.format(maxMemory),
2646 df.format(percentUsage) }),
2647 10, getHeight() - fm.getHeight());
2654 * Accessor method to quickly get all the AlignmentFrames loaded.
2656 * @return an array of AlignFrame, or null if none found
2658 public static AlignFrame[] getAlignFrames()
2660 if (Jalview.isHeadlessMode())
2662 // Desktop.getDesktop() is null in headless mode
2663 return new AlignFrame[] { Jalview.getCurrentAlignFrame() };
2666 JInternalFrame[] frames = Desktop.getDesktopPane().getAllFrames();
2672 List<AlignFrame> avp = new ArrayList<>();
2674 for (int i = frames.length - 1; i > -1; i--)
2676 if (frames[i] instanceof AlignFrame)
2678 avp.add((AlignFrame) frames[i]);
2680 else if (frames[i] instanceof SplitFrame)
2683 * Also check for a split frame containing an AlignFrame
2685 GSplitFrame sf = (GSplitFrame) frames[i];
2686 if (sf.getTopFrame() instanceof AlignFrame)
2688 avp.add((AlignFrame) sf.getTopFrame());
2690 if (sf.getBottomFrame() instanceof AlignFrame)
2692 avp.add((AlignFrame) sf.getBottomFrame());
2696 if (avp.size() == 0)
2700 AlignFrame afs[] = avp.toArray(new AlignFrame[avp.size()]);
2705 * Returns an array of any AppJmol frames in the Desktop (or null if none).
2709 public GStructureViewer[] getJmols()
2711 JInternalFrame[] frames = Desktop.getDesktopPane().getAllFrames();
2717 List<GStructureViewer> avp = new ArrayList<>();
2719 for (int i = frames.length - 1; i > -1; i--)
2721 if (frames[i] instanceof AppJmol)
2723 GStructureViewer af = (GStructureViewer) frames[i];
2727 if (avp.size() == 0)
2731 GStructureViewer afs[] = avp.toArray(new GStructureViewer[avp.size()]);
2736 * Add Groovy Support to Jalview
2739 public void groovyShell_actionPerformed()
2743 openGroovyConsole();
2744 } catch (Exception ex)
2746 jalview.bin.Cache.log.error("Groovy Shell Creation failed.", ex);
2747 JvOptionPane.showInternalMessageDialog(desktopPane,
2749 MessageManager.getString("label.couldnt_create_groovy_shell"),
2750 MessageManager.getString("label.groovy_support_failed"),
2751 JvOptionPane.ERROR_MESSAGE);
2756 * Open the Groovy console
2758 private void openGroovyConsole()
2760 if (groovyConsole == null)
2762 groovyConsole = new groovy.ui.Console();
2763 groovyConsole.setVariable("Jalview", this);
2764 groovyConsole.run();
2767 * We allow only one console at a time, so that AlignFrame menu option
2768 * 'Calculate | Run Groovy script' is unambiguous.
2769 * Disable 'Groovy Console', and enable 'Run script', when the console is
2770 * opened, and the reverse when it is closed
2772 Window window = (Window) groovyConsole.getFrame();
2773 window.addWindowListener(new WindowAdapter()
2776 public void windowClosed(WindowEvent e)
2779 * rebind CMD-Q from Groovy Console to Jalview Quit
2782 enableExecuteGroovy(false);
2788 * show Groovy console window (after close and reopen)
2790 ((Window) groovyConsole.getFrame()).setVisible(true);
2793 * if we got this far, enable 'Run Groovy' in AlignFrame menus
2794 * and disable opening a second console
2796 enableExecuteGroovy(true);
2800 * Bind Ctrl/Cmd-Q to Quit - for reset as Groovy Console takes over this binding
2803 protected void addQuitHandler()
2805 getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
2806 .put(KeyStroke.getKeyStroke(KeyEvent.VK_Q,
2807 Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()),
2809 getRootPane().getActionMap().put("Quit", new AbstractAction()
2812 public void actionPerformed(ActionEvent e)
2820 * Enable or disable 'Run Groovy script' in AlignFrame calculate menus
2823 * true if Groovy console is open
2825 public void enableExecuteGroovy(boolean enabled)
2828 * disable opening a second Groovy console
2829 * (or re-enable when the console is closed)
2831 groovyShell.setEnabled(!enabled);
2833 AlignFrame[] alignFrames = getAlignFrames();
2834 if (alignFrames != null)
2836 for (AlignFrame af : alignFrames)
2838 af.setGroovyEnabled(enabled);
2844 * Progress bars managed by the IProgressIndicator method.
2846 private Hashtable<Long, JPanel> progressBars;
2848 private Hashtable<Long, IProgressIndicatorHandler> progressBarHandlers;
2853 * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
2856 public void setProgressBar(String message, long id)
2858 Platform.timeCheck("Desktop " + message, Platform.TIME_MARK);
2860 if (progressBars == null)
2862 progressBars = new Hashtable<>();
2863 progressBarHandlers = new Hashtable<>();
2866 if (progressBars.get(new Long(id)) != null)
2868 JPanel panel = progressBars.remove(new Long(id));
2869 if (progressBarHandlers.contains(new Long(id)))
2871 progressBarHandlers.remove(new Long(id));
2873 removeProgressPanel(panel);
2877 progressBars.put(new Long(id), addProgressPanel(message));
2884 * @see jalview.gui.IProgressIndicator#registerHandler(long,
2885 * jalview.gui.IProgressIndicatorHandler)
2888 public void registerHandler(final long id,
2889 final IProgressIndicatorHandler handler)
2891 if (progressBarHandlers == null
2892 || !progressBars.containsKey(new Long(id)))
2894 throw new Error(MessageManager.getString(
2895 "error.call_setprogressbar_before_registering_handler"));
2897 progressBarHandlers.put(new Long(id), handler);
2898 final JPanel progressPanel = progressBars.get(new Long(id));
2899 if (handler.canCancel())
2901 JButton cancel = new JButton(
2902 MessageManager.getString("action.cancel"));
2903 final IProgressIndicator us = this;
2904 cancel.addActionListener(new ActionListener()
2908 public void actionPerformed(ActionEvent e)
2910 handler.cancelActivity(id);
2911 us.setProgressBar(MessageManager
2912 .formatMessage("label.cancelled_params", new Object[]
2913 { ((JLabel) progressPanel.getComponent(0)).getText() }),
2917 progressPanel.add(cancel, BorderLayout.EAST);
2923 * @return true if any progress bars are still active
2926 public boolean operationInProgress()
2928 if (progressBars != null && progressBars.size() > 0)
2936 * This will return the first AlignFrame holding the given viewport instance. It
2937 * will break if there are more than one AlignFrames viewing a particular av.
2940 * @return alignFrame for viewport
2942 public static AlignFrame getAlignFrameFor(AlignViewportI viewport)
2944 if (getDesktopPane() != null)
2946 AlignmentPanel[] aps = getAlignmentPanels(
2947 viewport.getSequenceSetId());
2948 for (int panel = 0; aps != null && panel < aps.length; panel++)
2950 if (aps[panel] != null && aps[panel].av == viewport)
2952 return aps[panel].alignFrame;
2959 public VamsasApplication getVamsasApplication()
2966 * flag set if jalview GUI is being operated programmatically
2968 private boolean inBatchMode = false;
2971 * check if jalview GUI is being operated programmatically
2973 * @return inBatchMode
2975 public boolean isInBatchMode()
2981 * set flag if jalview GUI is being operated programmatically
2983 * @param inBatchMode
2985 public void setInBatchMode(boolean inBatchMode)
2987 this.inBatchMode = inBatchMode;
2990 public void startServiceDiscovery()
2992 startServiceDiscovery(false);
2995 public void startServiceDiscovery(boolean blocking)
2997 boolean alive = true;
2998 Thread t0 = null, t1 = null, t2 = null;
2999 // JAL-940 - JALVIEW 1 services are now being EOLed as of JABA 2.1 release
3002 // todo: changesupport handlers need to be transferred
3003 if (discoverer == null)
3005 discoverer = Discoverer.getInstance();
3006 // register PCS handler for getDesktop().
3007 discoverer.addPropertyChangeListener(changeSupport);
3009 // JAL-940 - disabled JWS1 service configuration - always start discoverer
3010 // until we phase out completely
3011 (t0 = new Thread(discoverer)).start();
3014 if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
3016 t2 = jalview.ws.jws2.Jws2Discoverer.getDiscoverer()
3017 .startDiscoverer(changeSupport);
3021 // TODO: do rest service discovery
3030 } catch (Exception e)
3033 alive = (t1 != null && t1.isAlive()) || (t2 != null && t2.isAlive())
3034 || (t3 != null && t3.isAlive())
3035 || (t0 != null && t0.isAlive());
3041 * called to check if the service discovery process completed successfully.
3045 protected void JalviewServicesChanged(PropertyChangeEvent evt)
3047 if (evt.getNewValue() == null || evt.getNewValue() instanceof Vector)
3049 final String ermsg = jalview.ws.jws2.Jws2Discoverer.getDiscoverer()
3050 .getErrorMessages();
3053 if (Cache.getDefault("SHOW_WSDISCOVERY_ERRORS", true))
3055 if (serviceChangedDialog == null)
3057 // only run if we aren't already displaying one of these.
3058 addDialogThread(serviceChangedDialog = new Runnable()
3065 * JalviewDialog jd =new JalviewDialog() {
3067 * @Override protected void cancelPressed() { // TODO
3068 * Auto-generated method stub
3070 * }@Override protected void okPressed() { // TODO
3071 * Auto-generated method stub
3073 * }@Override protected void raiseClosed() { // TODO
3074 * Auto-generated method stub
3076 * } }; jd.initDialogFrame(new
3077 * JLabel("<html><table width=\"450\"><tr><td>" + ermsg +
3078 * "<br/>It may be that you have invalid JABA URLs in your web service preferences,"
3079 * + " or mis-configured HTTP proxy settings.<br/>" +
3080 * "Check the <em>Connections</em> and <em>Web services</em> tab of the"
3082 * " Tools->Preferences dialog box to change them.</td></tr></table></html>"
3083 * ), true, true, "Web Service Configuration Problem", 450,
3086 * jd.waitForInput();
3088 JvOptionPane.showConfirmDialog(Desktop.getDesktopPane(),
3089 new JLabel("<html><table width=\"450\"><tr><td>"
3090 + ermsg + "</td></tr></table>"
3091 + "<p>It may be that you have invalid JABA URLs<br/>in your web service preferences,"
3092 + "<br>or as a command-line argument, or mis-configured HTTP proxy settings.</p>"
3093 + "<p>Check the <em>Connections</em> and <em>Web services</em> tab<br/>of the"
3094 + " Tools->Preferences dialog box to change them.</p></html>"),
3095 "Web Service Configuration Problem",
3096 JvOptionPane.DEFAULT_OPTION,
3097 JvOptionPane.ERROR_MESSAGE);
3098 serviceChangedDialog = null;
3107 "Errors reported by JABA discovery service. Check web services preferences.\n"
3114 Runnable serviceChangedDialog = null;
3117 * start a thread to open a URL in the configured browser. Pops up a warning
3118 * dialog to the user if there is an exception when calling out to the browser
3123 public static void showUrl(final String url)
3125 showUrl(url, Instance.getDesktop());
3129 * Like showUrl but allows progress handler to be specified
3133 * (null) or object implementing IProgressIndicator
3135 public static void showUrl(final String url,
3136 final IProgressIndicator progress)
3138 new Thread(new Runnable()
3145 if (progress != null)
3147 progress.setProgressBar(MessageManager
3148 .formatMessage("status.opening_params", new Object[]
3149 { url }), this.hashCode());
3151 BrowserLauncher.openURL(url);
3152 } catch (Exception ex)
3154 JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
3156 .getString("label.web_browser_not_found_unix"),
3157 MessageManager.getString("label.web_browser_not_found"),
3158 JvOptionPane.WARNING_MESSAGE);
3160 ex.printStackTrace();
3162 if (progress != null)
3164 progress.setProgressBar(null, this.hashCode());
3170 private WsParamSetManager wsparamManager = null;
3172 public static ParamManager getUserParameterStore()
3174 Desktop d = Instance.getDesktop();
3175 if (d.wsparamManager == null)
3177 d.wsparamManager = new WsParamSetManager();
3179 return d.wsparamManager;
3183 * static hyperlink handler proxy method for use by Jalview's internal windows
3187 public static void hyperlinkUpdate(HyperlinkEvent e)
3189 if (e.getEventType() == EventType.ACTIVATED)
3194 url = e.getURL().toString();
3195 Desktop.showUrl(url);
3196 } catch (Exception x)
3200 if (Cache.log != null)
3202 Cache.log.error("Couldn't handle string " + url + " as a URL.");
3207 "Couldn't handle string " + url + " as a URL.");
3210 // ignore any exceptions due to dud links.
3217 * single thread that handles display of dialogs to user.
3219 ExecutorService dialogExecutor = Executors.newSingleThreadExecutor();
3222 * flag indicating if dialogExecutor should try to acquire a permit
3224 volatile boolean dialogPause = true;
3229 java.util.concurrent.Semaphore block = new Semaphore(0);
3231 private groovy.ui.Console groovyConsole;
3233 public StructureViewer lastTargetedView;
3236 * add another dialog thread to the queue
3240 public void addDialogThread(final Runnable prompter)
3242 dialogExecutor.submit(new Runnable()
3252 } catch (InterruptedException x)
3263 SwingUtilities.invokeAndWait(prompter);
3264 } catch (Exception q)
3266 Cache.log.warn("Unexpected Exception in dialog thread.", q);
3272 public void startDialogQueue()
3274 // set the flag so we don't pause waiting for another permit and semaphore
3275 // the current task to begin
3276 dialogPause = false;
3281 * Outputs an image of the desktop to file in EPS format, after prompting the
3282 * user for choice of Text or Lineart character rendering (unless a preference
3283 * has been set). The file name is generated as
3286 * Jalview_snapshot_nnnnn.eps where nnnnn is the current timestamp in milliseconds
3290 protected void snapShotWindow_actionPerformed(ActionEvent e)
3292 // currently the menu option to do this is not shown
3295 int width = getWidth();
3296 int height = getHeight();
3298 "Jalview_snapshot_" + System.currentTimeMillis() + ".eps");
3299 ImageWriterI writer = new ImageWriterI()
3302 public void exportImage(Graphics g) throws Exception
3305 Cache.log.info("Successfully written snapshot to file "
3306 + of.getAbsolutePath());
3309 String title = "View of desktop";
3310 ImageExporter exporter = new ImageExporter(writer, null, TYPE.EPS,
3312 exporter.doExport(of, this, width, height, title);
3316 * Explode the views in the given SplitFrame into separate SplitFrame windows.
3317 * This respects (remembers) any previous 'exploded geometry' i.e. the size and
3318 * location last time the view was expanded (if any). However it does not
3319 * remember the split pane divider location - this is set to match the
3320 * 'exploding' frame.
3324 public void explodeViews(SplitFrame sf)
3326 AlignFrame oldTopFrame = (AlignFrame) sf.getTopFrame();
3327 AlignFrame oldBottomFrame = (AlignFrame) sf.getBottomFrame();
3328 List<? extends AlignmentViewPanel> topPanels = oldTopFrame
3330 List<? extends AlignmentViewPanel> bottomPanels = oldBottomFrame
3332 int viewCount = topPanels.size();
3339 * Processing in reverse order works, forwards order leaves the first panels
3340 * not visible. I don't know why!
3342 for (int i = viewCount - 1; i >= 0; i--)
3345 * Make new top and bottom frames. These take over the respective
3346 * AlignmentPanel objects, including their AlignmentViewports, so the
3347 * cdna/protein relationships between the viewports is carried over to the
3350 * explodedGeometry holds the (x, y) position of the previously exploded
3351 * SplitFrame, and the (width, height) of the AlignFrame component
3353 AlignmentPanel topPanel = (AlignmentPanel) topPanels.get(i);
3354 AlignFrame newTopFrame = new AlignFrame(topPanel);
3355 newTopFrame.setSize(oldTopFrame.getSize());
3356 newTopFrame.setVisible(true);
3357 Rectangle geometry = ((AlignViewport) topPanel.getAlignViewport())
3358 .getExplodedGeometry();
3359 if (geometry != null)
3361 newTopFrame.setSize(geometry.getSize());
3364 AlignmentPanel bottomPanel = (AlignmentPanel) bottomPanels.get(i);
3365 AlignFrame newBottomFrame = new AlignFrame(bottomPanel);
3366 newBottomFrame.setSize(oldBottomFrame.getSize());
3367 newBottomFrame.setVisible(true);
3368 geometry = ((AlignViewport) bottomPanel.getAlignViewport())
3369 .getExplodedGeometry();
3370 if (geometry != null)
3372 newBottomFrame.setSize(geometry.getSize());
3375 topPanel.av.setGatherViewsHere(false);
3376 bottomPanel.av.setGatherViewsHere(false);
3377 JInternalFrame splitFrame = new SplitFrame(newTopFrame,
3379 if (geometry != null)
3381 splitFrame.setLocation(geometry.getLocation());
3383 Desktop.addInternalFrame(splitFrame, sf.getTitle(), -1, -1);
3387 * Clear references to the panels (now relocated in the new SplitFrames)
3388 * before closing the old SplitFrame.
3391 bottomPanels.clear();
3396 * Gather expanded split frames, sharing the same pairs of sequence set ids,
3397 * back into the given SplitFrame as additional views. Note that the gathered
3398 * frames may themselves have multiple views.
3402 public void gatherViews(GSplitFrame source)
3405 * special handling of explodedGeometry for a view within a SplitFrame: - it
3406 * holds the (x, y) position of the enclosing SplitFrame, and the (width,
3407 * height) of the AlignFrame component
3409 AlignFrame myTopFrame = (AlignFrame) source.getTopFrame();
3410 AlignFrame myBottomFrame = (AlignFrame) source.getBottomFrame();
3411 myTopFrame.viewport.setExplodedGeometry(new Rectangle(source.getX(),
3412 source.getY(), myTopFrame.getWidth(), myTopFrame.getHeight()));
3413 myBottomFrame.viewport
3414 .setExplodedGeometry(new Rectangle(source.getX(), source.getY(),
3415 myBottomFrame.getWidth(), myBottomFrame.getHeight()));
3416 myTopFrame.viewport.setGatherViewsHere(true);
3417 myBottomFrame.viewport.setGatherViewsHere(true);
3418 String topViewId = myTopFrame.viewport.getSequenceSetId();
3419 String bottomViewId = myBottomFrame.viewport.getSequenceSetId();
3421 JInternalFrame[] frames = desktopPane.getAllFrames();
3422 for (JInternalFrame frame : frames)
3424 if (frame instanceof SplitFrame && frame != source)
3426 SplitFrame sf = (SplitFrame) frame;
3427 AlignFrame topFrame = (AlignFrame) sf.getTopFrame();
3428 AlignFrame bottomFrame = (AlignFrame) sf.getBottomFrame();
3429 boolean gatherThis = false;
3430 for (int a = 0; a < topFrame.alignPanels.size(); a++)
3432 AlignmentPanel topPanel = topFrame.alignPanels.get(a);
3433 AlignmentPanel bottomPanel = bottomFrame.alignPanels.get(a);
3434 if (topViewId.equals(topPanel.av.getSequenceSetId())
3435 && bottomViewId.equals(bottomPanel.av.getSequenceSetId()))
3438 topPanel.av.setGatherViewsHere(false);
3439 bottomPanel.av.setGatherViewsHere(false);
3440 topPanel.av.setExplodedGeometry(
3441 new Rectangle(sf.getLocation(), topFrame.getSize()));
3442 bottomPanel.av.setExplodedGeometry(
3443 new Rectangle(sf.getLocation(), bottomFrame.getSize()));
3444 myTopFrame.addAlignmentPanel(topPanel, false);
3445 myBottomFrame.addAlignmentPanel(bottomPanel, false);
3451 topFrame.getAlignPanels().clear();
3452 bottomFrame.getAlignPanels().clear();
3459 * The dust settles...give focus to the tab we did this from.
3461 myTopFrame.setDisplayedView(myTopFrame.alignPanel);
3464 public static groovy.ui.Console getGroovyConsole()
3466 return Instance.getDesktop().groovyConsole;
3470 * handles the payload of a drag and drop event.
3472 * TODO refactor to desktop utilities class
3475 * - Data source strings extracted from the drop event
3477 * - protocol for each data source extracted from the drop event
3481 * - the payload from the drop event
3484 @SuppressWarnings("unchecked")
3485 public static void transferFromDropTarget(List<Object> files,
3486 List<DataSourceType> protocols, DropTargetDropEvent evt,
3487 Transferable t) throws Exception
3490 // BH 2018 changed List<String> to List<Object> to allow for File from SwingJS
3492 // DataFlavor[] flavors = t.getTransferDataFlavors();
3493 // for (int i = 0; i < flavors.length; i++) {
3494 // if (flavors[i].isFlavorJavaFileListType()) {
3495 // evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
3496 // List<File> list = (List<File>) t.getTransferData(flavors[i]);
3497 // for (int j = 0; j < list.size(); j++) {
3498 // File file = (File) list.get(j);
3499 // byte[] data = getDroppedFileBytes(file);
3500 // fileName.setText(file.getName() + " - " + data.length + " " +
3501 // evt.getLocation());
3502 // JTextArea target = (JTextArea) ((DropTarget) evt.getSource()).getComponent();
3503 // target.setText(new String(data));
3505 // dtde.dropComplete(true);
3510 DataFlavor uriListFlavor = new DataFlavor(
3511 "text/uri-list;class=java.lang.String"), urlFlavour = null;
3514 urlFlavour = new DataFlavor(
3515 "application/x-java-url; class=java.net.URL");
3516 } catch (ClassNotFoundException cfe)
3518 Cache.log.debug("Couldn't instantiate the URL dataflavor.", cfe);
3521 if (urlFlavour != null && t.isDataFlavorSupported(urlFlavour))
3526 java.net.URL url = (URL) t.getTransferData(urlFlavour);
3527 // nb: java 8 osx bug https://bugs.openjdk.java.net/browse/JDK-8156099
3528 // means url may be null.
3531 protocols.add(DataSourceType.URL);
3532 files.add(url.toString());
3533 Cache.log.debug("Drop handled as URL dataflavor "
3534 + files.get(files.size() - 1));
3539 if (Platform.isAMacAndNotJS())
3542 "Please ignore plist error - occurs due to problem with java 8 on OSX");
3546 } catch (Throwable ex)
3548 Cache.log.debug("URL drop handler failed.", ex);
3551 if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
3553 // Works on Windows and MacOSX
3554 Cache.log.debug("Drop handled as javaFileListFlavor");
3555 for (Object file : (List<Object>) t
3556 .getTransferData(DataFlavor.javaFileListFlavor))
3559 protocols.add(DataSourceType.FILE);
3564 // Unix like behaviour
3565 boolean added = false;
3567 if (t.isDataFlavorSupported(uriListFlavor))
3569 Cache.log.debug("Drop handled as uriListFlavor");
3570 // This is used by Unix drag system
3571 data = (String) t.getTransferData(uriListFlavor);
3575 // fallback to text: workaround - on OSX where there's a JVM bug
3576 Cache.log.debug("standard URIListFlavor failed. Trying text");
3577 // try text fallback
3578 DataFlavor textDf = new DataFlavor(
3579 "text/plain;class=java.lang.String");
3580 if (t.isDataFlavorSupported(textDf))
3582 data = (String) t.getTransferData(textDf);
3585 Cache.log.debug("Plain text drop content returned "
3586 + (data == null ? "Null - failed" : data));
3591 while (protocols.size() < files.size())
3593 Cache.log.debug("Adding missing FILE protocol for "
3594 + files.get(protocols.size()));
3595 protocols.add(DataSourceType.FILE);
3597 for (java.util.StringTokenizer st = new java.util.StringTokenizer(
3598 data, "\r\n"); st.hasMoreTokens();)
3601 String s = st.nextToken();
3602 if (s.startsWith("#"))
3604 // the line is a comment (as per the RFC 2483)
3607 java.net.URI uri = new java.net.URI(s);
3608 if (uri.getScheme().toLowerCase().startsWith("http"))
3610 protocols.add(DataSourceType.URL);
3611 files.add(uri.toString());
3615 // otherwise preserve old behaviour: catch all for file objects
3616 java.io.File file = new java.io.File(uri);
3617 protocols.add(DataSourceType.FILE);
3618 files.add(file.toString());
3623 if (Cache.log.isDebugEnabled())
3625 if (data == null || !added)
3628 if (t.getTransferDataFlavors() != null
3629 && t.getTransferDataFlavors().length > 0)
3632 "Couldn't resolve drop data. Here are the supported flavors:");
3633 for (DataFlavor fl : t.getTransferDataFlavors())
3636 "Supported transfer dataflavor: " + fl.toString());
3637 Object df = t.getTransferData(fl);
3640 Cache.log.debug("Retrieves: " + df);
3644 Cache.log.debug("Retrieved nothing");
3650 Cache.log.debug("Couldn't resolve dataflavor for drop: "
3656 if (Platform.isWindowsAndNotJS())
3658 Cache.log.debug("Scanning dropped content for Windows Link Files");
3660 // resolve any .lnk files in the file drop
3661 for (int f = 0; f < files.size(); f++)
3663 String source = files.get(f).toString().toLowerCase();
3664 if (protocols.get(f).equals(DataSourceType.FILE)
3665 && (source.endsWith(".lnk") || source.endsWith(".url")
3666 || source.endsWith(".site")))
3670 Object obj = files.get(f);
3671 File lf = (obj instanceof File ? (File) obj
3672 : new File((String) obj));
3673 // process link file to get a URL
3674 Cache.log.debug("Found potential link file: " + lf);
3675 WindowsShortcut wscfile = new WindowsShortcut(lf);
3676 String fullname = wscfile.getRealFilename();
3677 protocols.set(f, FormatAdapter.checkProtocol(fullname));
3678 files.set(f, fullname);
3679 Cache.log.debug("Parsed real filename " + fullname
3680 + " to extract protocol: " + protocols.get(f));
3681 } catch (Exception ex)
3684 "Couldn't parse " + files.get(f) + " as a link file.",
3693 * Sets the Preferences property for experimental features to True or False
3694 * depending on the state of the controlling menu item
3697 protected void showExperimental_actionPerformed(boolean selected)
3699 Cache.setProperty(EXPERIMENTAL_FEATURES, Boolean.toString(selected));
3703 * Answers a (possibly empty) list of any structure viewer frames (currently for
3704 * either Jmol or Chimera) which are currently open. This may optionally be
3705 * restricted to viewers of a specified class, or viewers linked to a specified
3709 * if not null, only return viewers linked to this panel
3710 * @param structureViewerClass
3711 * if not null, only return viewers of this class
3714 public List<StructureViewerBase> getStructureViewers(
3715 AlignmentPanel apanel,
3716 Class<? extends StructureViewerBase> structureViewerClass)
3718 List<StructureViewerBase> result = new ArrayList<>();
3719 JInternalFrame[] frames = getAllFrames();
3721 for (JInternalFrame frame : frames)
3723 if (frame instanceof StructureViewerBase)
3725 if (structureViewerClass == null
3726 || structureViewerClass.isInstance(frame))
3729 || ((StructureViewerBase) frame).isLinkedWith(apanel))
3731 result.add((StructureViewerBase) frame);