2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
5 * This file is part of Jalview.
7 * Jalview is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation, either version 3
10 * of the License, or (at your option) any later version.
12 * Jalview is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19 * The Jalview Authors are detailed in the 'AUTHORS' file.
23 import jalview.api.AlignViewportI;
24 import jalview.api.AlignmentViewPanel;
25 import jalview.api.StructureSelectionManagerProvider;
26 import jalview.bin.ApplicationSingletonProvider;
27 import jalview.bin.ApplicationSingletonProvider.ApplicationSingletonI;
28 import jalview.bin.Cache;
29 import jalview.bin.Jalview;
30 import jalview.gui.ImageExporter.ImageWriterI;
31 import jalview.io.BackupFiles;
32 import jalview.io.DataSourceType;
33 import jalview.io.FileFormat;
34 import jalview.io.FileFormatException;
35 import jalview.io.FileFormatI;
36 import jalview.io.FileFormats;
37 import jalview.io.FileLoader;
38 import jalview.io.FormatAdapter;
39 import jalview.io.IdentifyFile;
40 import jalview.io.JalviewFileChooser;
41 import jalview.io.JalviewFileView;
42 import jalview.jbgui.GDesktop;
43 import jalview.jbgui.GSplitFrame;
44 import jalview.jbgui.GStructureViewer;
45 import jalview.project.Jalview2XML;
46 import jalview.structure.StructureSelectionManager;
47 import jalview.urls.IdOrgSettings;
48 import jalview.util.BrowserLauncher;
49 import jalview.util.ImageMaker.TYPE;
50 import jalview.util.MessageManager;
51 import jalview.util.Platform;
52 import jalview.util.UrlConstants;
53 import jalview.viewmodel.AlignmentViewport;
54 import jalview.ws.jws1.Discoverer;
55 import jalview.ws.params.ParamManager;
56 import jalview.ws.utils.UrlDownloadClient;
58 import java.awt.BorderLayout;
59 import java.awt.Color;
60 import java.awt.Dimension;
61 import java.awt.FontMetrics;
62 import java.awt.Graphics;
63 import java.awt.GridLayout;
64 import java.awt.Point;
65 import java.awt.Rectangle;
66 import java.awt.Toolkit;
67 import java.awt.Window;
68 import java.awt.datatransfer.Clipboard;
69 import java.awt.datatransfer.ClipboardOwner;
70 import java.awt.datatransfer.DataFlavor;
71 import java.awt.datatransfer.Transferable;
72 import java.awt.dnd.DnDConstants;
73 import java.awt.dnd.DropTargetDragEvent;
74 import java.awt.dnd.DropTargetDropEvent;
75 import java.awt.dnd.DropTargetEvent;
76 import java.awt.dnd.DropTargetListener;
77 import java.awt.event.ActionEvent;
78 import java.awt.event.ActionListener;
79 import java.awt.event.InputEvent;
80 import java.awt.event.KeyEvent;
81 import java.awt.event.MouseAdapter;
82 import java.awt.event.MouseEvent;
83 import java.awt.event.WindowAdapter;
84 import java.awt.event.WindowEvent;
85 import java.beans.PropertyChangeEvent;
86 import java.beans.PropertyChangeListener;
87 import java.io.BufferedInputStream;
89 import java.io.FileOutputStream;
90 import java.io.IOException;
92 import java.util.ArrayList;
93 import java.util.Hashtable;
94 import java.util.List;
95 import java.util.ListIterator;
96 import java.util.Vector;
97 import java.util.concurrent.ExecutorService;
98 import java.util.concurrent.Executors;
99 import java.util.concurrent.Semaphore;
101 import javax.swing.AbstractAction;
102 import javax.swing.Action;
103 import javax.swing.ActionMap;
104 import javax.swing.Box;
105 import javax.swing.BoxLayout;
106 import javax.swing.DefaultDesktopManager;
107 import javax.swing.DesktopManager;
108 import javax.swing.InputMap;
109 import javax.swing.JButton;
110 import javax.swing.JCheckBox;
111 import javax.swing.JComboBox;
112 import javax.swing.JComponent;
113 import javax.swing.JDesktopPane;
114 import javax.swing.JFrame;
115 import javax.swing.JInternalFrame;
116 import javax.swing.JLabel;
117 import javax.swing.JMenuItem;
118 import javax.swing.JPanel;
119 import javax.swing.JPopupMenu;
120 import javax.swing.JProgressBar;
121 import javax.swing.JTextField;
122 import javax.swing.KeyStroke;
123 import javax.swing.SwingUtilities;
124 import javax.swing.event.HyperlinkEvent;
125 import javax.swing.event.HyperlinkEvent.EventType;
126 import javax.swing.event.InternalFrameAdapter;
127 import javax.swing.event.InternalFrameEvent;
128 import javax.swing.event.MenuEvent;
129 import javax.swing.event.MenuListener;
131 import org.stackoverflowusers.file.WindowsShortcut;
138 * @version $Revision: 1.155 $
140 @SuppressWarnings("serial")
141 public class Desktop extends GDesktop
142 implements DropTargetListener, ClipboardOwner, IProgressIndicator,
143 StructureSelectionManagerProvider, ApplicationSingletonI
146 private final static int DEFAULT_MIN_WIDTH = 300;
148 private final static int DEFAULT_MIN_HEIGHT = 250;
150 private final static int ALIGN_FRAME_DEFAULT_MIN_WIDTH = 600;
152 private final static int ALIGN_FRAME_DEFAULT_MIN_HEIGHT = 70;
154 private final static String EXPERIMENTAL_FEATURES = "EXPERIMENTAL_FEATURES";
156 private JalviewChangeSupport changeSupport = new JalviewChangeSupport();
159 * news reader - null if it was never started.
161 BlogReader jvnews = null;
163 private File projectFile;
167 * @see jalview.gui.JalviewChangeSupport#addJalviewPropertyChangeListener(java.beans.PropertyChangeListener)
169 public void addJalviewPropertyChangeListener(
170 PropertyChangeListener listener)
172 changeSupport.addJalviewPropertyChangeListener(listener);
176 * @param propertyName
178 * @see jalview.gui.JalviewChangeSupport#addJalviewPropertyChangeListener(java.lang.String,
179 * java.beans.PropertyChangeListener)
181 public void addJalviewPropertyChangeListener(String propertyName,
182 PropertyChangeListener listener)
184 changeSupport.addJalviewPropertyChangeListener(propertyName, listener);
188 * @param propertyName
190 * @see jalview.gui.JalviewChangeSupport#removeJalviewPropertyChangeListener(java.lang.String,
191 * java.beans.PropertyChangeListener)
193 public void removeJalviewPropertyChangeListener(String propertyName,
194 PropertyChangeListener listener)
196 changeSupport.removeJalviewPropertyChangeListener(propertyName,
200 public static MyDesktopPane getDesktopPane()
202 return Desktop.getInstance().desktopPane;
205 public StructureSelectionManager getStructureSelectionManager()
207 return StructureSelectionManager
208 .getStructureSelectionManager(this);
211 static int openFrameCount = 0;
213 static final int xOffset = 30;
215 static final int yOffset = 30;
217 public Discoverer discoverer;
219 public Object[] jalviewClipboard;
221 public boolean internalCopy = false;
223 private static int fileLoadingCount = 0;
225 public JInternalFrame conservationSlider, PIDSlider;
228 * just an instance (for testng, probably); no actual frames
230 private boolean instanceOnly;
232 class MyDesktopManager implements DesktopManager
235 private DesktopManager delegate;
237 public MyDesktopManager(DesktopManager delegate)
239 this.delegate = delegate;
243 public void activateFrame(JInternalFrame f)
247 delegate.activateFrame(f);
248 } catch (NullPointerException npe)
250 Point p = getMousePosition();
251 showPasteMenu(p.x, p.y);
256 public void beginDraggingFrame(JComponent f)
258 delegate.beginDraggingFrame(f);
262 public void beginResizingFrame(JComponent f, int direction)
264 delegate.beginResizingFrame(f, direction);
268 public void closeFrame(JInternalFrame f)
270 delegate.closeFrame(f);
274 public void deactivateFrame(JInternalFrame f)
276 delegate.deactivateFrame(f);
280 public void deiconifyFrame(JInternalFrame f)
282 delegate.deiconifyFrame(f);
286 public void dragFrame(JComponent f, int newX, int newY)
292 delegate.dragFrame(f, newX, newY);
296 public void endDraggingFrame(JComponent f)
298 delegate.endDraggingFrame(f);
299 desktopPane.repaint();
303 public void endResizingFrame(JComponent f)
305 delegate.endResizingFrame(f);
306 desktopPane.repaint();
310 public void iconifyFrame(JInternalFrame f)
312 delegate.iconifyFrame(f);
316 public void maximizeFrame(JInternalFrame f)
318 delegate.maximizeFrame(f);
322 public void minimizeFrame(JInternalFrame f)
324 delegate.minimizeFrame(f);
328 public void openFrame(JInternalFrame f)
330 delegate.openFrame(f);
334 public void resizeFrame(JComponent f, int newX, int newY, int newWidth,
341 delegate.resizeFrame(f, newX, newY, newWidth, newHeight);
345 public void setBoundsForFrame(JComponent f, int newX, int newY,
346 int newWidth, int newHeight)
348 delegate.setBoundsForFrame(f, newX, newY, newWidth, newHeight);
351 // All other methods, simply delegate
355 public MyDesktopPane desktopPane;
358 * Answers an 'application scope' singleton instance of this class. Separate
359 * SwingJS 'applets' running in the same browser page will each have a
360 * distinct instance of Desktop.
364 public static Desktop getInstance()
366 return (Desktop) ApplicationSingletonProvider
367 .getInstance(Desktop.class);
375 public Desktop(boolean forInstance)
381 * Private constructor enforces singleton pattern. It is called by reflection
382 * from ApplicationSingletonProvider.getInstance().
384 @SuppressWarnings("unused")
388 * A note to implementors. It is ESSENTIAL that any activities that might
389 * block are spawned off as threads rather than waited for during this
392 if (!Platform.isJS())
394 doVamsasClientCheck();
397 doConfigureStructurePrefs();
398 setTitle("Jalview " + jalview.bin.Cache.getProperty("VERSION"));
399 setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
400 boolean selmemusage = jalview.bin.Cache.getDefault("SHOW_MEMUSAGE",
402 boolean showjconsole = jalview.bin.Cache.getDefault("SHOW_JAVA_CONSOLE",
404 desktopPane = new MyDesktopPane(selmemusage);
406 showMemusage.setSelected(selmemusage);
407 desktopPane.setBackground(Color.white);
408 getContentPane().setLayout(new BorderLayout());
409 // alternate config - have scrollbars - see notes in JAL-153
410 // JScrollPane sp = new JScrollPane();
411 // sp.getViewport().setView(desktop);
412 // getContentPane().add(sp, BorderLayout.CENTER);
414 // BH 2018 - just an experiment to try unclipped JInternalFrames.
417 getRootPane().putClientProperty("swingjs.overflow.hidden", "false");
420 getContentPane().add(desktopPane, BorderLayout.CENTER);
421 desktopPane.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
423 // This line prevents Windows Look&Feel resizing all new windows to maximum
424 // if previous window was maximised
425 desktopPane.setDesktopManager(new MyDesktopManager(
426 (Platform.isWindowsAndNotJS() ? new DefaultDesktopManager()
427 : Platform.isAMacAndNotJS()
428 ? new AquaInternalFrameManager(
429 desktopPane.getDesktopManager())
430 : desktopPane.getDesktopManager())));
432 Rectangle dims = getLastKnownDimensions("");
439 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
440 int xPos = Math.max(5, (screenSize.width - 900) / 2);
441 int yPos = Math.max(5, (screenSize.height - 650) / 2);
442 setBounds(xPos, yPos, 900, 650);
445 if (!Platform.isJS())
453 jconsole = new Console(this, showjconsole);
454 // add essential build information
455 jconsole.setHeader("Jalview Version: "
456 + jalview.bin.Cache.getProperty("VERSION") + "\n"
457 + "Jalview Installation: "
458 + jalview.bin.Cache.getDefault("INSTALLATION", "unknown")
459 + "\n" + "Build Date: "
460 + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown") + "\n"
461 + "Java version: " + System.getProperty("java.version") + "\n"
462 + System.getProperty("os.arch") + " "
463 + System.getProperty("os.name") + " "
464 + System.getProperty("os.version"));
466 showConsole(showjconsole);
468 showNews.setVisible(false);
470 experimentalFeatures.setSelected(showExperimental());
472 getIdentifiersOrgData();
476 // Spawn a thread that shows the splashscreen
478 SwingUtilities.invokeLater(new Runnable()
487 // Thread off a new instance of the file chooser - this reduces the time
489 // takes to open it later on.
490 new Thread(new Runnable()
495 Cache.log.debug("Filechooser init thread started.");
496 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
497 JalviewFileChooser.forRead(Cache.getProperty("LAST_DIRECTORY"),
499 Cache.log.debug("Filechooser init thread finished.");
502 // Add the service change listener
503 changeSupport.addJalviewPropertyChangeListener("services",
504 new PropertyChangeListener()
508 public void propertyChange(PropertyChangeEvent evt)
510 Cache.log.debug("Firing service changed event for "
511 + evt.getNewValue());
512 JalviewServicesChanged(evt);
519 this.setDropTarget(new java.awt.dnd.DropTarget(desktopPane, this));
521 this.addWindowListener(new WindowAdapter()
524 public void windowClosing(WindowEvent evt)
531 this.addMouseListener(ma = new MouseAdapter()
534 public void mousePressed(MouseEvent evt)
536 if (evt.isPopupTrigger()) // Mac
538 showPasteMenu(evt.getX(), evt.getY());
543 public void mouseReleased(MouseEvent evt)
545 if (evt.isPopupTrigger()) // Windows
547 showPasteMenu(evt.getX(), evt.getY());
551 desktopPane.addMouseListener(ma);
555 * Answers true if user preferences to enable experimental features is True
560 public boolean showExperimental()
562 String experimental = Cache.getDefault(EXPERIMENTAL_FEATURES,
563 Boolean.FALSE.toString());
564 return Boolean.valueOf(experimental).booleanValue();
567 public void doConfigureStructurePrefs()
569 // configure services
570 StructureSelectionManager ssm = getStructureSelectionManager();
571 if (jalview.bin.Cache.getDefault(Preferences.ADD_SS_ANN, true))
573 ssm.setAddTempFacAnnot(jalview.bin.Cache
574 .getDefault(Preferences.ADD_TEMPFACT_ANN, true));
575 ssm.setProcessSecondaryStructure(jalview.bin.Cache
576 .getDefault(Preferences.STRUCT_FROM_PDB, true));
577 ssm.setSecStructServices(
578 jalview.bin.Cache.getDefault(Preferences.USE_RNAVIEW, true));
582 ssm.setAddTempFacAnnot(false);
583 ssm.setProcessSecondaryStructure(false);
584 ssm.setSecStructServices(false);
588 public void checkForNews()
590 final Desktop me = this;
591 // Thread off the news reader, in case there are connection problems.
592 new Thread(new Runnable()
597 Cache.log.debug("Starting news thread.");
598 jvnews = new BlogReader(me);
599 showNews.setVisible(true);
600 Cache.log.debug("Completed news thread.");
605 public void getIdentifiersOrgData()
607 // Thread off the identifiers fetcher
608 new Thread(new Runnable()
613 Cache.log.debug("Downloading data from identifiers.org");
614 // UrlDownloadClient client = new UrlDownloadClient();
617 UrlDownloadClient.download(IdOrgSettings.getUrl(),
618 IdOrgSettings.getDownloadLocation());
619 } catch (IOException e)
621 Cache.log.debug("Exception downloading identifiers.org data"
630 protected void showNews_actionPerformed(ActionEvent e)
632 showNews(showNews.isSelected());
635 protected void showNews(boolean visible)
637 Cache.log.debug((visible ? "Showing" : "Hiding") + " news.");
638 showNews.setSelected(visible);
639 if (visible && !jvnews.isVisible())
641 new Thread(new Runnable()
646 long now = System.currentTimeMillis();
648 MessageManager.getString("status.refreshing_news"), now);
649 jvnews.refreshNews();
650 setProgressBar(null, now);
658 * recover the last known dimensions for a jalview window
661 * - empty string is desktop, all other windows have unique prefix
662 * @return null or last known dimensions scaled to current geometry (if last
663 * window geom was known)
665 Rectangle getLastKnownDimensions(String windowName)
667 // TODO: lock aspect ratio for scaling desktop Bug #0058199
668 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
669 String x = jalview.bin.Cache.getProperty(windowName + "SCREEN_X");
670 String y = jalview.bin.Cache.getProperty(windowName + "SCREEN_Y");
671 String width = jalview.bin.Cache
672 .getProperty(windowName + "SCREEN_WIDTH");
673 String height = jalview.bin.Cache
674 .getProperty(windowName + "SCREEN_HEIGHT");
675 if ((x != null) && (y != null) && (width != null) && (height != null))
677 int ix = Integer.parseInt(x), iy = Integer.parseInt(y),
678 iw = Integer.parseInt(width), ih = Integer.parseInt(height);
679 if (jalview.bin.Cache.getProperty("SCREENGEOMETRY_WIDTH") != null)
681 // attempt #1 - try to cope with change in screen geometry - this
682 // version doesn't preserve original jv aspect ratio.
683 // take ratio of current screen size vs original screen size.
684 double sw = ((1f * screenSize.width) / (1f * Integer.parseInt(
685 jalview.bin.Cache.getProperty("SCREENGEOMETRY_WIDTH"))));
686 double sh = ((1f * screenSize.height) / (1f * Integer.parseInt(
687 jalview.bin.Cache.getProperty("SCREENGEOMETRY_HEIGHT"))));
688 // rescale the bounds depending upon the current screen geometry.
689 ix = (int) (ix * sw);
690 iw = (int) (iw * sw);
691 iy = (int) (iy * sh);
692 ih = (int) (ih * sh);
693 while (ix >= screenSize.width)
695 jalview.bin.Cache.log.debug(
696 "Window geometry location recall error: shifting horizontal to within screenbounds.");
697 ix -= screenSize.width;
699 while (iy >= screenSize.height)
701 jalview.bin.Cache.log.debug(
702 "Window geometry location recall error: shifting vertical to within screenbounds.");
703 iy -= screenSize.height;
705 jalview.bin.Cache.log.debug(
706 "Got last known dimensions for " + windowName + ": x:" + ix
707 + " y:" + iy + " width:" + iw + " height:" + ih);
709 // return dimensions for new instance
710 return new Rectangle(ix, iy, iw, ih);
715 private void doVamsasClientCheck()
717 if (Cache.vamsasJarsPresent())
719 setupVamsasDisconnectedGui();
720 VamsasMenu.setVisible(true);
721 final Desktop us = this;
722 VamsasMenu.addMenuListener(new MenuListener()
724 // this listener remembers when the menu was first selected, and
725 // doesn't rebuild the session list until it has been cleared and
727 boolean refresh = true;
730 public void menuCanceled(MenuEvent e)
736 public void menuDeselected(MenuEvent e)
742 public void menuSelected(MenuEvent e)
746 us.buildVamsasStMenu();
751 vamsasStart.setVisible(true);
755 protected void showPasteMenu(int x, int y)
757 JPopupMenu popup = new JPopupMenu();
758 JMenuItem item = new JMenuItem(
759 MessageManager.getString("label.paste_new_window"));
760 item.addActionListener(new ActionListener()
763 public void actionPerformed(ActionEvent evt)
770 popup.show(this, x, y);
777 Clipboard c = Toolkit.getDefaultToolkit().getSystemClipboard();
778 Transferable contents = c.getContents(this);
780 if (contents != null)
782 String file = (String) contents
783 .getTransferData(DataFlavor.stringFlavor);
785 FileFormatI format = new IdentifyFile().identify(file,
786 DataSourceType.PASTE);
788 new FileLoader().LoadFile(file, DataSourceType.PASTE, format);
791 } catch (Exception ex)
794 "Unable to paste alignment from system clipboard:\n" + ex);
799 * Adds and opens the given frame to the desktop
810 public static synchronized void addInternalFrame(
811 final JInternalFrame frame, String title, int w, int h)
813 addInternalFrame(frame, title, true, w, h, true, false);
817 * Add an internal frame to the Jalview desktop
824 * When true, display frame immediately, otherwise, caller must call
825 * setVisible themselves.
831 public static synchronized void addInternalFrame(
832 final JInternalFrame frame, String title, boolean makeVisible,
835 addInternalFrame(frame, title, makeVisible, w, h, true, false);
839 * Add an internal frame to the Jalview desktop and make it visible
852 public static synchronized void addInternalFrame(
853 final JInternalFrame frame, String title, int w, int h,
856 addInternalFrame(frame, title, true, w, h, resizable, false);
860 * Add an internal frame to the Jalview desktop
867 * When true, display frame immediately, otherwise, caller must call
868 * setVisible themselves.
875 * @param ignoreMinSize
876 * Do not set the default minimum size for frame
878 public static synchronized void addInternalFrame(
879 final JInternalFrame frame, String title, boolean makeVisible,
880 int w, int h, boolean resizable, boolean ignoreMinSize)
883 // TODO: allow callers to determine X and Y position of frame (eg. via
885 // TODO: consider fixing method to update entries in the window submenu with
886 // the current window title
888 frame.setTitle(title);
889 if (frame.getWidth() < 1 || frame.getHeight() < 1)
893 // THIS IS A PUBLIC STATIC METHOD, SO IT MAY BE CALLED EVEN IN
894 // A HEADLESS STATE WHEN NO DESKTOP EXISTS. MUST RETURN
895 // IF JALVIEW IS RUNNING HEADLESS
896 // ///////////////////////////////////////////////
897 if (Desktop.getInstance().instanceOnly || Jalview.isHeadlessMode())
906 frame.setMinimumSize(
907 new Dimension(DEFAULT_MIN_WIDTH, DEFAULT_MIN_HEIGHT));
909 // Set default dimension for Alignment Frame window.
910 // The Alignment Frame window could be added from a number of places,
912 // I did this here in order not to miss out on any Alignment frame.
913 if (frame instanceof AlignFrame)
915 frame.setMinimumSize(new Dimension(ALIGN_FRAME_DEFAULT_MIN_WIDTH,
916 ALIGN_FRAME_DEFAULT_MIN_HEIGHT));
920 frame.setVisible(makeVisible);
921 frame.setClosable(true);
922 frame.setResizable(resizable);
923 frame.setMaximizable(resizable);
924 frame.setIconifiable(resizable);
925 frame.setOpaque(Platform.isJS());
927 if (frame.getX() < 1 && frame.getY() < 1)
929 frame.setLocation(xOffset * openFrameCount,
930 yOffset * ((openFrameCount - 1) % 10) + yOffset);
934 * add an entry for the new frame in the Window menu
935 * (and remove it when the frame is closed)
937 JMenuItem menuItem = new JMenuItem(title);
938 frame.addInternalFrameListener(new InternalFrameAdapter()
941 public void internalFrameActivated(InternalFrameEvent evt)
943 JInternalFrame itf = getDesktopPane().getSelectedFrame();
946 if (itf instanceof AlignFrame)
948 Jalview.setCurrentAlignFrame((AlignFrame) itf);
955 public void internalFrameClosed(InternalFrameEvent evt)
957 PaintRefresher.RemoveComponent(frame);
960 * defensive check to prevent frames being
961 * added half off the window
963 if (openFrameCount > 0)
969 * ensure no reference to alignFrame retained by menu item listener
971 if (menuItem.getActionListeners().length > 0)
973 menuItem.removeActionListener(menuItem.getActionListeners()[0]);
975 Desktop.getInstance().windowMenu.remove(menuItem);
979 menuItem.addActionListener(new ActionListener()
982 public void actionPerformed(ActionEvent e)
986 frame.setSelected(true);
987 frame.setIcon(false);
988 } catch (java.beans.PropertyVetoException ex)
990 // System.err.println(ex.toString());
995 setKeyBindings(frame);
997 getDesktopPane().add(frame);
999 Desktop.getInstance().windowMenu.add(menuItem);
1004 frame.setSelected(true);
1005 frame.requestFocus();
1006 } catch (java.beans.PropertyVetoException ve)
1008 } catch (java.lang.ClassCastException cex)
1011 "Squashed a possible GUI implementation error. If you can recreate this, please look at http://issues.jalview.org/browse/JAL-869",
1017 * Add key bindings to a JInternalFrame so that Ctrl-W and Cmd-W will close the
1022 private static void setKeyBindings(JInternalFrame frame)
1024 final Action closeAction = new AbstractAction()
1027 public void actionPerformed(ActionEvent e)
1034 * set up key bindings for Ctrl-W and Cmd-W, with the same (Close) action
1036 KeyStroke ctrlWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
1037 InputEvent.CTRL_DOWN_MASK);
1038 KeyStroke cmdWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
1039 Toolkit.getDefaultToolkit().getMenuShortcutKeyMask());
1041 InputMap inputMap = frame
1042 .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
1043 String ctrlW = ctrlWKey.toString();
1044 inputMap.put(ctrlWKey, ctrlW);
1045 inputMap.put(cmdWKey, ctrlW);
1047 ActionMap actionMap = frame.getActionMap();
1048 actionMap.put(ctrlW, closeAction);
1052 public void lostOwnership(Clipboard clipboard, Transferable contents)
1056 Desktop.getInstance().jalviewClipboard = null;
1059 internalCopy = false;
1063 public void dragEnter(DropTargetDragEvent evt)
1068 public void dragExit(DropTargetEvent evt)
1073 public void dragOver(DropTargetDragEvent evt)
1078 public void dropActionChanged(DropTargetDragEvent evt)
1089 public void drop(DropTargetDropEvent evt)
1091 boolean success = true;
1092 // JAL-1552 - acceptDrop required before getTransferable call for
1093 // Java's Transferable for native dnd
1094 evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
1095 Transferable t = evt.getTransferable();
1096 List<Object> files = new ArrayList<>();
1097 List<DataSourceType> protocols = new ArrayList<>();
1101 Desktop.transferFromDropTarget(files, protocols, evt, t);
1102 } catch (Exception e)
1104 e.printStackTrace();
1112 for (int i = 0; i < files.size(); i++)
1114 // BH 2018 File or String
1115 Object file = files.get(i);
1116 String fileName = file.toString();
1117 DataSourceType protocol = (protocols == null)
1118 ? DataSourceType.FILE
1120 FileFormatI format = null;
1122 if (fileName.endsWith(".jar"))
1124 format = FileFormat.Jalview;
1129 format = new IdentifyFile().identify(file, protocol);
1131 if (file instanceof File)
1133 Platform.cacheFileData((File) file);
1135 new FileLoader().LoadFile(null, file, protocol, format);
1138 } catch (Exception ex)
1143 evt.dropComplete(success); // need this to ensure input focus is properly
1144 // transfered to any new windows created
1154 public void inputLocalFileMenuItem_actionPerformed(AlignViewport viewport)
1156 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
1157 JalviewFileChooser chooser = JalviewFileChooser
1158 .forRead(Cache.getProperty("LAST_DIRECTORY"), fileFormat, true);
1160 chooser.setFileView(new JalviewFileView());
1161 chooser.setDialogTitle(
1162 MessageManager.getString("label.open_local_file"));
1163 chooser.setToolTipText(MessageManager.getString("action.open"));
1165 chooser.setResponseHandler(0, new Runnable()
1170 File selectedFile = chooser.getSelectedFile();
1171 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1173 FileFormatI format = chooser.getSelectedFormat();
1176 * Call IdentifyFile to verify the file contains what its extension implies.
1177 * Skip this step for dynamically added file formats, because
1178 * IdentifyFile does not know how to recognise them.
1180 if (FileFormats.getInstance().isIdentifiable(format))
1184 format = new IdentifyFile().identify(selectedFile,
1185 DataSourceType.FILE);
1186 } catch (FileFormatException e)
1188 // format = null; //??
1192 new FileLoader().LoadFile(viewport, selectedFile,
1193 DataSourceType.FILE, format);
1196 chooser.showOpenDialog(this);
1200 * Shows a dialog for input of a URL at which to retrieve alignment data
1205 public void inputURLMenuItem_actionPerformed(AlignViewport viewport)
1207 // This construct allows us to have a wider textfield
1209 JLabel label = new JLabel(
1210 MessageManager.getString("label.input_file_url"));
1212 JPanel panel = new JPanel(new GridLayout(2, 1));
1216 * the URL to fetch is
1217 * Java: an editable combobox with history
1218 * JS: (pending JAL-3038) a plain text field
1221 String urlBase = "http://www.";
1222 if (Platform.isJS())
1224 history = new JTextField(urlBase, 35);
1233 JComboBox<String> asCombo = new JComboBox<>();
1234 asCombo.setPreferredSize(new Dimension(400, 20));
1235 asCombo.setEditable(true);
1236 asCombo.addItem(urlBase);
1237 String historyItems = Cache.getProperty("RECENT_URL");
1238 if (historyItems != null)
1240 for (String token : historyItems.split("\\t"))
1242 asCombo.addItem(token);
1249 Object[] options = new Object[] { MessageManager.getString("action.ok"),
1250 MessageManager.getString("action.cancel") };
1251 Runnable action = new Runnable()
1256 @SuppressWarnings("unchecked")
1257 String url = (history instanceof JTextField
1258 ? ((JTextField) history).getText()
1259 : ((JComboBox<String>) history).getSelectedItem()
1262 if (url.toLowerCase().endsWith(".jar"))
1264 if (viewport != null)
1266 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1267 FileFormat.Jalview);
1271 new FileLoader().LoadFile(url, DataSourceType.URL,
1272 FileFormat.Jalview);
1277 FileFormatI format = null;
1280 format = new IdentifyFile().identify(url, DataSourceType.URL);
1281 } catch (FileFormatException e)
1283 // TODO revise error handling, distinguish between
1284 // URL not found and response not valid
1289 String msg = MessageManager
1290 .formatMessage("label.couldnt_locate", url);
1291 JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
1293 MessageManager.getString("label.url_not_found"),
1294 JvOptionPane.WARNING_MESSAGE);
1299 if (viewport != null)
1301 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1306 new FileLoader().LoadFile(url, DataSourceType.URL, format);
1311 String dialogOption = MessageManager
1312 .getString("label.input_alignment_from_url");
1313 JvOptionPane.newOptionDialog(getDesktopPane())
1314 .setResponseHandler(0, action)
1315 .showInternalDialog(panel, dialogOption,
1316 JvOptionPane.YES_NO_CANCEL_OPTION,
1317 JvOptionPane.PLAIN_MESSAGE, null, options,
1318 MessageManager.getString("action.ok"));
1322 * Opens the CutAndPaste window for the user to paste an alignment in to
1325 * - if not null, the pasted alignment is added to the current
1326 * alignment; if null, to a new alignment window
1329 public void inputTextboxMenuItem_actionPerformed(
1330 AlignmentViewPanel viewPanel)
1332 CutAndPasteTransfer cap = new CutAndPasteTransfer();
1333 cap.setForInput(viewPanel);
1334 Desktop.addInternalFrame(cap,
1335 MessageManager.getString("label.cut_paste_alignmen_file"), true,
1345 Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
1346 jalview.bin.Cache.setProperty("SCREENGEOMETRY_WIDTH",
1348 jalview.bin.Cache.setProperty("SCREENGEOMETRY_HEIGHT",
1349 screen.height + "");
1350 storeLastKnownDimensions("", new Rectangle(getBounds().x, getBounds().y,
1351 getWidth(), getHeight()));
1353 if (jconsole != null)
1355 storeLastKnownDimensions("JAVA_CONSOLE_", jconsole.getBounds());
1356 jconsole.stopConsole();
1360 storeLastKnownDimensions("JALVIEW_RSS_WINDOW_", jvnews.getBounds());
1363 if (dialogExecutor != null)
1365 dialogExecutor.shutdownNow();
1367 closeAll_actionPerformed(null);
1369 if (groovyConsole != null)
1371 // suppress a possible repeat prompt to save script
1372 groovyConsole.setDirty(false);
1373 groovyConsole.exit();
1378 private void storeLastKnownDimensions(String string, Rectangle jc)
1380 jalview.bin.Cache.log.debug("Storing last known dimensions for "
1381 + string + ": x:" + jc.x + " y:" + jc.y + " width:" + jc.width
1382 + " height:" + jc.height);
1384 jalview.bin.Cache.setProperty(string + "SCREEN_X", jc.x + "");
1385 jalview.bin.Cache.setProperty(string + "SCREEN_Y", jc.y + "");
1386 jalview.bin.Cache.setProperty(string + "SCREEN_WIDTH", jc.width + "");
1387 jalview.bin.Cache.setProperty(string + "SCREEN_HEIGHT", jc.height + "");
1397 public void aboutMenuItem_actionPerformed(ActionEvent e)
1399 // StringBuffer message = getAboutMessage(false);
1400 // JvOptionPane.showInternalMessageDialog(Desktop.getDesktop(),
1402 // message.toString(), "About Jalview", JvOptionPane.INFORMATION_MESSAGE);
1403 new Thread(new Runnable()
1408 new SplashScreen(true);
1413 public StringBuffer getAboutMessage(boolean shortv)
1415 StringBuffer message = new StringBuffer();
1416 message.append("<html>");
1419 message.append("<h1><strong>Version: "
1420 + jalview.bin.Cache.getProperty("VERSION")
1421 + "</strong></h1>");
1422 message.append("<strong>Last Updated: <em>"
1423 + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown")
1424 + "</em></strong>");
1430 message.append("<strong>Version "
1431 + jalview.bin.Cache.getProperty("VERSION")
1432 + "; last updated: "
1433 + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown"));
1436 if (jalview.bin.Cache.getDefault("LATEST_VERSION", "Checking")
1437 .equals("Checking"))
1439 message.append("<br>...Checking latest version...</br>");
1441 else if (!jalview.bin.Cache.getDefault("LATEST_VERSION", "Checking")
1442 .equals(jalview.bin.Cache.getProperty("VERSION")))
1444 boolean red = false;
1445 if (jalview.bin.Cache.getProperty("VERSION").toLowerCase()
1446 .indexOf("automated build") == -1)
1449 // Displayed when code version and jnlp version do not match and code
1450 // version is not a development build
1451 message.append("<div style=\"color: #FF0000;font-style: bold;\">");
1454 message.append("<br>!! Version "
1455 + jalview.bin.Cache.getDefault("LATEST_VERSION",
1457 + " is available for download from "
1458 + jalview.bin.Cache.getDefault("www.jalview.org",
1459 "http://www.jalview.org")
1463 message.append("</div>");
1466 message.append("<br>Authors: " + jalview.bin.Cache.getDefault(
1468 "The Jalview Authors (See AUTHORS file for current list)")
1469 + "<br><br>Development managed by The Barton Group, University of Dundee, Scotland, UK.<br>"
1470 + "<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"
1471 + "<br><br>If you use Jalview, please cite:"
1472 + "<br>Waterhouse, A.M., Procter, J.B., Martin, D.M.A, Clamp, M. and Barton, G. J. (2009)"
1473 + "<br>Jalview Version 2 - a multiple sequence alignment editor and analysis workbench"
1474 + "<br>Bioinformatics doi: 10.1093/bioinformatics/btp033"
1480 * Action on requesting Help documentation
1483 public void documentationMenuItem_actionPerformed()
1487 if (Platform.isJS())
1489 BrowserLauncher.openURL("http://www.jalview.org/help.html");
1498 Help.showHelpWindow();
1500 } catch (Exception ex)
1502 System.err.println("Error opening help: " + ex.getMessage());
1507 public void closeAll_actionPerformed(ActionEvent e)
1509 if (desktopPane == null)
1513 // TODO show a progress bar while closing?
1514 JInternalFrame[] frames = desktopPane.getAllFrames();
1515 for (int i = 0; i < frames.length; i++)
1519 frames[i].setClosed(true);
1520 } catch (java.beans.PropertyVetoException ex)
1524 Jalview.setCurrentAlignFrame(null);
1525 System.out.println("ALL CLOSED");
1526 if (v_client != null)
1528 // TODO clear binding to vamsas document objects on close_all
1532 * reset state of singleton objects as appropriate (clear down session state
1533 * when all windows are closed)
1535 getStructureSelectionManager().resetAll();
1539 public void raiseRelated_actionPerformed(ActionEvent e)
1541 reorderAssociatedWindows(false, false);
1545 public void minimizeAssociated_actionPerformed(ActionEvent e)
1547 reorderAssociatedWindows(true, false);
1550 void closeAssociatedWindows()
1552 reorderAssociatedWindows(false, true);
1558 * @seejalview.jbgui.GDesktop#garbageCollect_actionPerformed(java.awt.event.
1562 protected void garbageCollect_actionPerformed(ActionEvent e)
1564 // We simply collect the garbage
1565 jalview.bin.Cache.log.debug("Collecting garbage...");
1567 jalview.bin.Cache.log.debug("Finished garbage collection.");
1574 * jalview.jbgui.GDesktop#showMemusage_actionPerformed(java.awt.event.ActionEvent
1578 protected void showMemusage_actionPerformed(ActionEvent e)
1580 getDesktopPane().showMemoryUsage(showMemusage.isSelected());
1587 * jalview.jbgui.GDesktop#showConsole_actionPerformed(java.awt.event.ActionEvent
1591 protected void showConsole_actionPerformed(ActionEvent e)
1593 showConsole(showConsole.isSelected());
1596 Console jconsole = null;
1599 * control whether the java console is visible or not
1603 void showConsole(boolean selected)
1605 // TODO: decide if we should update properties file
1606 if (jconsole != null) // BH 2018
1608 showConsole.setSelected(selected);
1609 Cache.setProperty("SHOW_JAVA_CONSOLE",
1610 Boolean.valueOf(selected).toString());
1611 jconsole.setVisible(selected);
1615 void reorderAssociatedWindows(boolean minimize, boolean close)
1617 JInternalFrame[] frames = getDesktopPane().getAllFrames();
1618 if (frames == null || frames.length < 1)
1623 AlignmentViewport source = null, target = null;
1624 if (frames[0] instanceof AlignFrame)
1626 source = ((AlignFrame) frames[0]).getCurrentView();
1628 else if (frames[0] instanceof TreePanel)
1630 source = ((TreePanel) frames[0]).getViewPort();
1632 else if (frames[0] instanceof PCAPanel)
1634 source = ((PCAPanel) frames[0]).av;
1636 else if (frames[0].getContentPane() instanceof PairwiseAlignPanel)
1638 source = ((PairwiseAlignPanel) frames[0].getContentPane()).av;
1643 for (int i = 0; i < frames.length; i++)
1646 if (frames[i] == null)
1650 if (frames[i] instanceof AlignFrame)
1652 target = ((AlignFrame) frames[i]).getCurrentView();
1654 else if (frames[i] instanceof TreePanel)
1656 target = ((TreePanel) frames[i]).getViewPort();
1658 else if (frames[i] instanceof PCAPanel)
1660 target = ((PCAPanel) frames[i]).av;
1662 else if (frames[i].getContentPane() instanceof PairwiseAlignPanel)
1664 target = ((PairwiseAlignPanel) frames[i].getContentPane()).av;
1667 if (source == target)
1673 frames[i].setClosed(true);
1677 frames[i].setIcon(minimize);
1680 frames[i].toFront();
1684 } catch (java.beans.PropertyVetoException ex)
1699 protected void preferences_actionPerformed(ActionEvent e)
1705 * Prompts the user to choose a file and then saves the Jalview state as a
1706 * Jalview project file
1709 public void saveState_actionPerformed()
1711 saveState_actionPerformed(false);
1714 public void saveState_actionPerformed(boolean saveAs)
1716 java.io.File projectFile = getProjectFile();
1717 // autoSave indicates we already have a file and don't need to ask
1718 boolean autoSave = projectFile != null && !saveAs
1719 && BackupFiles.getEnabled();
1721 // System.out.println("autoSave="+autoSave+", projectFile='"+projectFile+"',
1722 // saveAs="+saveAs+", Backups
1723 // "+(BackupFiles.getEnabled()?"enabled":"disabled"));
1725 boolean approveSave = false;
1728 JalviewFileChooser chooser = new JalviewFileChooser("jvp",
1731 chooser.setFileView(new JalviewFileView());
1732 chooser.setDialogTitle(MessageManager.getString("label.save_state"));
1734 int value = chooser.showSaveDialog(this);
1736 if (value == JalviewFileChooser.APPROVE_OPTION)
1738 projectFile = chooser.getSelectedFile();
1739 setProjectFile(projectFile);
1744 if (approveSave || autoSave)
1746 final Desktop me = this;
1747 final java.io.File chosenFile = projectFile;
1748 new Thread(new Runnable()
1753 // TODO: refactor to Jalview desktop session controller action.
1754 setProgressBar(MessageManager.formatMessage(
1755 "label.saving_jalview_project", new Object[]
1756 { chosenFile.getName() }), chosenFile.hashCode());
1757 jalview.bin.Cache.setProperty("LAST_DIRECTORY",
1758 chosenFile.getParent());
1759 // TODO catch and handle errors for savestate
1760 // TODO prevent user from messing with the Desktop whilst we're saving
1763 boolean doBackup = BackupFiles.getEnabled();
1764 BackupFiles backupfiles = doBackup ? new BackupFiles(chosenFile) : null;
1766 new Jalview2XML().saveState(doBackup ? backupfiles.getTempFile() : chosenFile);
1770 backupfiles.setWriteSuccess(true);
1771 backupfiles.rollBackupsAndRenameTempFile();
1773 } catch (OutOfMemoryError oom)
1775 new OOMWarning("Whilst saving current state to "
1776 + chosenFile.getName(), oom);
1777 } catch (Exception ex)
1779 Cache.log.error("Problems whilst trying to save to "
1780 + chosenFile.getName(), ex);
1781 JvOptionPane.showMessageDialog(me,
1782 MessageManager.formatMessage(
1783 "label.error_whilst_saving_current_state_to",
1785 { chosenFile.getName() }),
1786 MessageManager.getString("label.couldnt_save_project"),
1787 JvOptionPane.WARNING_MESSAGE);
1789 setProgressBar(null, chosenFile.hashCode());
1796 public void saveAsState_actionPerformed(ActionEvent e)
1798 saveState_actionPerformed(true);
1801 protected void setProjectFile(File choice)
1803 this.projectFile = choice;
1806 public File getProjectFile()
1808 return this.projectFile;
1812 * Shows a file chooser dialog and tries to read in the selected file as a
1816 public void loadState_actionPerformed()
1818 final String[] suffix = new String[] { "jvp", "jar" };
1819 final String[] desc = new String[] { "Jalview Project",
1820 "Jalview Project (old)" };
1821 JalviewFileChooser chooser = new JalviewFileChooser(
1822 Cache.getProperty("LAST_DIRECTORY"), suffix, desc,
1823 "Jalview Project", true, true); // last two booleans: allFiles,
1825 chooser.setFileView(new JalviewFileView());
1826 chooser.setDialogTitle(MessageManager.getString("label.restore_state"));
1827 chooser.setResponseHandler(0, new Runnable()
1832 File selectedFile = chooser.getSelectedFile();
1833 setProjectFile(selectedFile);
1834 String choice = selectedFile.getAbsolutePath();
1835 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1836 new Thread(new Runnable()
1843 new Jalview2XML().loadJalviewAlign(choice);
1844 } catch (OutOfMemoryError oom)
1846 new OOMWarning("Whilst loading project from " + choice, oom);
1847 } catch (Exception ex)
1850 "Problems whilst loading project from " + choice, ex);
1851 JvOptionPane.showMessageDialog(Desktop.getDesktopPane(),
1852 MessageManager.formatMessage(
1853 "label.error_whilst_loading_project_from",
1856 MessageManager.getString("label.couldnt_load_project"),
1857 JvOptionPane.WARNING_MESSAGE);
1864 chooser.showOpenDialog(this);
1868 public void inputSequence_actionPerformed(ActionEvent e)
1870 new SequenceFetcher(this);
1873 JPanel progressPanel;
1875 ArrayList<JPanel> fileLoadingPanels = new ArrayList<>();
1877 public void startLoading(final Object fileName)
1879 if (fileLoadingCount == 0)
1881 fileLoadingPanels.add(addProgressPanel(MessageManager
1882 .formatMessage("label.loading_file", new Object[]
1888 private JPanel addProgressPanel(String string)
1890 if (progressPanel == null)
1892 progressPanel = new JPanel(new GridLayout(1, 1));
1893 totalProgressCount = 0;
1894 getContentPane().add(progressPanel, BorderLayout.SOUTH);
1896 JPanel thisprogress = new JPanel(new BorderLayout(10, 5));
1897 JProgressBar progressBar = new JProgressBar();
1898 progressBar.setIndeterminate(true);
1900 thisprogress.add(new JLabel(string), BorderLayout.WEST);
1902 thisprogress.add(progressBar, BorderLayout.CENTER);
1903 progressPanel.add(thisprogress);
1904 ((GridLayout) progressPanel.getLayout()).setRows(
1905 ((GridLayout) progressPanel.getLayout()).getRows() + 1);
1906 ++totalProgressCount;
1908 return thisprogress;
1911 int totalProgressCount = 0;
1913 private void removeProgressPanel(JPanel progbar)
1915 if (progressPanel != null)
1917 synchronized (progressPanel)
1919 progressPanel.remove(progbar);
1920 GridLayout gl = (GridLayout) progressPanel.getLayout();
1921 gl.setRows(gl.getRows() - 1);
1922 if (--totalProgressCount < 1)
1924 this.getContentPane().remove(progressPanel);
1925 progressPanel = null;
1932 public void stopLoading()
1935 if (fileLoadingCount < 1)
1937 while (fileLoadingPanels.size() > 0)
1939 removeProgressPanel(fileLoadingPanels.remove(0));
1941 fileLoadingPanels.clear();
1942 fileLoadingCount = 0;
1947 public static int getViewCount(String alignmentId)
1949 AlignmentViewport[] aps = getViewports(alignmentId);
1950 return (aps == null) ? 0 : aps.length;
1955 * @param alignmentId
1956 * - if null, all sets are returned
1957 * @return all AlignmentPanels concerning the alignmentId sequence set
1959 public static AlignmentPanel[] getAlignmentPanels(String alignmentId)
1961 if (Desktop.getDesktopPane() == null)
1963 // no frames created and in headless mode
1964 // TODO: verify that frames are recoverable when in headless mode
1967 List<AlignmentPanel> aps = new ArrayList<>();
1968 AlignFrame[] frames = getAlignFrames();
1973 for (AlignFrame af : frames)
1975 for (AlignmentPanel ap : af.alignPanels)
1977 if (alignmentId == null
1978 || alignmentId.equals(ap.av.getSequenceSetId()))
1984 if (aps.size() == 0)
1988 AlignmentPanel[] vap = aps.toArray(new AlignmentPanel[aps.size()]);
1993 * get all the viewports on an alignment.
1995 * @param sequenceSetId
1996 * unique alignment id (may be null - all viewports returned in that
1998 * @return all viewports on the alignment bound to sequenceSetId
2000 public static AlignmentViewport[] getViewports(String sequenceSetId)
2002 List<AlignmentViewport> viewp = new ArrayList<>();
2003 if (getDesktopPane() != null)
2005 AlignFrame[] frames = Desktop.getAlignFrames();
2007 for (AlignFrame afr : frames)
2009 if (sequenceSetId == null || afr.getViewport().getSequenceSetId()
2010 .equals(sequenceSetId))
2012 if (afr.alignPanels != null)
2014 for (AlignmentPanel ap : afr.alignPanels)
2016 if (sequenceSetId == null
2017 || sequenceSetId.equals(ap.av.getSequenceSetId()))
2025 viewp.add(afr.getViewport());
2029 if (viewp.size() > 0)
2031 return viewp.toArray(new AlignmentViewport[viewp.size()]);
2038 * Explode the views in the given frame into separate AlignFrame
2042 public static void explodeViews(AlignFrame af)
2044 int size = af.alignPanels.size();
2050 for (int i = 0; i < size; i++)
2052 AlignmentPanel ap = af.alignPanels.get(i);
2053 AlignFrame newaf = new AlignFrame(ap);
2056 * Restore the view's last exploded frame geometry if known. Multiple
2057 * views from one exploded frame share and restore the same (frame)
2058 * position and size.
2060 Rectangle geometry = ap.av.getExplodedGeometry();
2061 if (geometry != null)
2063 newaf.setBounds(geometry);
2066 ap.av.setGatherViewsHere(false);
2068 addInternalFrame(newaf, af.getTitle(), AlignFrame.DEFAULT_WIDTH,
2069 AlignFrame.DEFAULT_HEIGHT);
2072 af.alignPanels.clear();
2073 af.closeMenuItem_actionPerformed(true);
2078 * Gather expanded views (separate AlignFrame's) with the same sequence set
2079 * identifier back in to this frame as additional views, and close the expanded
2080 * views. Note the expanded frames may themselves have multiple views. We take
2085 public void gatherViews(AlignFrame source)
2087 source.viewport.setGatherViewsHere(true);
2088 source.viewport.setExplodedGeometry(source.getBounds());
2089 JInternalFrame[] frames = getAllFrames();
2090 String viewId = source.viewport.getSequenceSetId();
2092 for (int t = 0; t < frames.length; t++)
2094 if (frames[t] instanceof AlignFrame && frames[t] != source)
2096 AlignFrame af = (AlignFrame) frames[t];
2097 boolean gatherThis = false;
2098 for (int a = 0; a < af.alignPanels.size(); a++)
2100 AlignmentPanel ap = af.alignPanels.get(a);
2101 if (viewId.equals(ap.av.getSequenceSetId()))
2104 ap.av.setGatherViewsHere(false);
2105 ap.av.setExplodedGeometry(af.getBounds());
2106 source.addAlignmentPanel(ap, false);
2112 af.alignPanels.clear();
2113 af.closeMenuItem_actionPerformed(true);
2120 jalview.gui.VamsasApplication v_client = null;
2123 public void vamsasImport_actionPerformed(ActionEvent e)
2125 // TODO: JAL-3048 not needed for Jalview-JS
2127 if (v_client == null)
2129 // Load and try to start a session.
2130 JalviewFileChooser chooser = new JalviewFileChooser(
2131 jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
2133 chooser.setFileView(new JalviewFileView());
2134 chooser.setDialogTitle(
2135 MessageManager.getString("label.open_saved_vamsas_session"));
2136 chooser.setToolTipText(MessageManager.getString(
2137 "label.select_vamsas_session_opened_as_new_vamsas_session"));
2139 int value = chooser.showOpenDialog(this);
2141 if (value == JalviewFileChooser.APPROVE_OPTION)
2143 String fle = chooser.getSelectedFile().toString();
2144 if (!vamsasImport(chooser.getSelectedFile()))
2146 JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
2147 MessageManager.formatMessage(
2148 "label.couldnt_import_as_vamsas_session",
2152 .getString("label.vamsas_document_import_failed"),
2153 JvOptionPane.ERROR_MESSAGE);
2159 jalview.bin.Cache.log.error(
2160 "Implementation error - load session from a running session is not supported.");
2165 * import file into a new vamsas session (uses jalview.gui.VamsasApplication)
2168 * @return true if import was a success and a session was started.
2170 public boolean vamsasImport(URL url)
2172 // TODO: create progress bar
2173 if (v_client != null)
2176 jalview.bin.Cache.log.error(
2177 "Implementation error - load session from a running session is not supported.");
2183 // copy the URL content to a temporary local file
2184 // TODO: be a bit cleverer here with nio (?!)
2185 File file = File.createTempFile("vdocfromurl", ".vdj");
2186 FileOutputStream fos = new FileOutputStream(file);
2187 BufferedInputStream bis = new BufferedInputStream(url.openStream());
2188 byte[] buffer = new byte[2048];
2190 while ((ln = bis.read(buffer)) > -1)
2192 fos.write(buffer, 0, ln);
2196 v_client = new jalview.gui.VamsasApplication(this, file,
2197 url.toExternalForm());
2198 } catch (Exception ex)
2200 jalview.bin.Cache.log.error(
2201 "Failed to create new vamsas session from contents of URL "
2206 setupVamsasConnectedGui();
2207 v_client.initial_update(); // TODO: thread ?
2208 return v_client.inSession();
2212 * import file into a new vamsas session (uses jalview.gui.VamsasApplication)
2215 * @return true if import was a success and a session was started.
2217 public boolean vamsasImport(File file)
2219 if (v_client != null)
2222 jalview.bin.Cache.log.error(
2223 "Implementation error - load session from a running session is not supported.");
2227 setProgressBar(MessageManager.formatMessage(
2228 "status.importing_vamsas_session_from", new Object[]
2229 { file.getName() }), file.hashCode());
2232 v_client = new jalview.gui.VamsasApplication(this, file, null);
2233 } catch (Exception ex)
2235 setProgressBar(MessageManager.formatMessage(
2236 "status.importing_vamsas_session_from", new Object[]
2237 { file.getName() }), file.hashCode());
2238 jalview.bin.Cache.log.error(
2239 "New vamsas session from existing session file failed:", ex);
2242 setupVamsasConnectedGui();
2243 v_client.initial_update(); // TODO: thread ?
2244 setProgressBar(MessageManager.formatMessage(
2245 "status.importing_vamsas_session_from", new Object[]
2246 { file.getName() }), file.hashCode());
2247 return v_client.inSession();
2250 public boolean joinVamsasSession(String mysesid)
2252 if (v_client != null)
2254 throw new Error(MessageManager
2255 .getString("error.try_join_vamsas_session_another"));
2257 if (mysesid == null)
2260 MessageManager.getString("error.invalid_vamsas_session_id"));
2262 v_client = new VamsasApplication(this, mysesid);
2263 setupVamsasConnectedGui();
2264 v_client.initial_update();
2265 return (v_client.inSession());
2269 public void vamsasStart_actionPerformed(ActionEvent e)
2271 if (v_client == null)
2274 // we just start a default session for moment.
2276 * JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache.
2277 * getProperty("LAST_DIRECTORY"));
2279 * chooser.setFileView(new JalviewFileView());
2280 * chooser.setDialogTitle("Load Vamsas file");
2281 * chooser.setToolTipText("Import");
2283 * int value = chooser.showOpenDialog(this);
2285 * if (value == JalviewFileChooser.APPROVE_OPTION) { v_client = new
2286 * jalview.gui.VamsasApplication(this, chooser.getSelectedFile());
2288 v_client = new VamsasApplication(this);
2289 setupVamsasConnectedGui();
2290 v_client.initial_update(); // TODO: thread ?
2294 // store current data in session.
2295 v_client.push_update(); // TODO: thread
2299 protected void setupVamsasConnectedGui()
2301 vamsasStart.setText(MessageManager.getString("label.session_update"));
2302 vamsasSave.setVisible(true);
2303 vamsasStop.setVisible(true);
2304 vamsasImport.setVisible(false); // Document import to existing session is
2305 // not possible for vamsas-client-1.0.
2308 protected void setupVamsasDisconnectedGui()
2310 vamsasSave.setVisible(false);
2311 vamsasStop.setVisible(false);
2312 vamsasImport.setVisible(true);
2314 .setText(MessageManager.getString("label.new_vamsas_session"));
2318 public void vamsasStop_actionPerformed(ActionEvent e)
2320 if (v_client != null)
2322 v_client.end_session();
2324 setupVamsasDisconnectedGui();
2328 protected void buildVamsasStMenu()
2330 if (v_client == null)
2332 String[] sess = null;
2335 sess = VamsasApplication.getSessionList();
2336 } catch (Exception e)
2338 jalview.bin.Cache.log.warn("Problem getting current sessions list.",
2344 jalview.bin.Cache.log.debug(
2345 "Got current sessions list: " + sess.length + " entries.");
2346 VamsasStMenu.removeAll();
2347 for (int i = 0; i < sess.length; i++)
2349 JMenuItem sessit = new JMenuItem();
2350 sessit.setText(sess[i]);
2351 sessit.setToolTipText(MessageManager
2352 .formatMessage("label.connect_to_session", new Object[]
2354 final Desktop dsktp = this;
2355 final String mysesid = sess[i];
2356 sessit.addActionListener(new ActionListener()
2360 public void actionPerformed(ActionEvent e)
2362 if (dsktp.v_client == null)
2364 Thread rthr = new Thread(new Runnable()
2370 dsktp.v_client = new VamsasApplication(dsktp, mysesid);
2371 dsktp.setupVamsasConnectedGui();
2372 dsktp.v_client.initial_update();
2380 VamsasStMenu.add(sessit);
2382 // don't show an empty menu.
2383 VamsasStMenu.setVisible(sess.length > 0);
2388 jalview.bin.Cache.log.debug("No current vamsas sessions.");
2389 VamsasStMenu.removeAll();
2390 VamsasStMenu.setVisible(false);
2395 // Not interested in the content. Just hide ourselves.
2396 VamsasStMenu.setVisible(false);
2401 public void vamsasSave_actionPerformed(ActionEvent e)
2403 // TODO: JAL-3048 not needed for Jalview-JS
2405 if (v_client != null)
2407 // TODO: VAMSAS DOCUMENT EXTENSION is VDJ
2408 JalviewFileChooser chooser = new JalviewFileChooser("vdj",
2411 chooser.setFileView(new JalviewFileView());
2412 chooser.setDialogTitle(MessageManager
2413 .getString("label.save_vamsas_document_archive"));
2415 int value = chooser.showSaveDialog(this);
2417 if (value == JalviewFileChooser.APPROVE_OPTION)
2419 java.io.File choice = chooser.getSelectedFile();
2420 JPanel progpanel = addProgressPanel(MessageManager
2421 .formatMessage("label.saving_vamsas_doc", new Object[]
2422 { choice.getName() }));
2423 Cache.setProperty("LAST_DIRECTORY", choice.getParent());
2424 String warnmsg = null;
2425 String warnttl = null;
2428 v_client.vclient.storeDocument(choice);
2431 warnttl = "Serious Problem saving Vamsas Document";
2432 warnmsg = ex.toString();
2433 jalview.bin.Cache.log
2434 .error("Error Whilst saving document to " + choice, ex);
2436 } catch (Exception ex)
2438 warnttl = "Problem saving Vamsas Document.";
2439 warnmsg = ex.toString();
2440 jalview.bin.Cache.log.warn(
2441 "Exception Whilst saving document to " + choice, ex);
2444 removeProgressPanel(progpanel);
2445 if (warnmsg != null)
2447 JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
2449 warnmsg, warnttl, JvOptionPane.ERROR_MESSAGE);
2455 JPanel vamUpdate = null;
2458 * hide vamsas user gui bits when a vamsas document event is being handled.
2461 * true to hide gui, false to reveal gui
2463 public void setVamsasUpdate(boolean b)
2465 Cache.log.debug("Setting gui for Vamsas update "
2466 + (b ? "in progress" : "finished"));
2468 if (vamUpdate != null)
2470 this.removeProgressPanel(vamUpdate);
2474 vamUpdate = this.addProgressPanel(
2475 MessageManager.getString("label.updating_vamsas_session"));
2477 vamsasStart.setVisible(!b);
2478 vamsasStop.setVisible(!b);
2479 vamsasSave.setVisible(!b);
2482 public JInternalFrame[] getAllFrames()
2484 return desktopPane.getAllFrames();
2488 * Checks the given url to see if it gives a response indicating that the user
2489 * should be informed of a new questionnaire.
2493 public void checkForQuestionnaire(String url)
2495 UserQuestionnaireCheck jvq = new UserQuestionnaireCheck(url);
2496 // javax.swing.SwingUtilities.invokeLater(jvq);
2497 new Thread(jvq).start();
2500 public void checkURLLinks()
2502 // Thread off the URL link checker
2503 addDialogThread(new Runnable()
2508 if (Cache.getDefault("CHECKURLLINKS", true))
2510 // check what the actual links are - if it's just the default don't
2511 // bother with the warning
2512 List<String> links = Preferences.sequenceUrlLinks
2515 // only need to check links if there is one with a
2516 // SEQUENCE_ID which is not the default EMBL_EBI link
2517 ListIterator<String> li = links.listIterator();
2518 boolean check = false;
2519 List<JLabel> urls = new ArrayList<>();
2520 while (li.hasNext())
2522 String link = li.next();
2523 if (link.contains(UrlConstants.SEQUENCE_ID)
2524 && !UrlConstants.isDefaultString(link))
2527 int barPos = link.indexOf("|");
2528 String urlMsg = barPos == -1 ? link
2529 : link.substring(0, barPos) + ": "
2530 + link.substring(barPos + 1);
2531 urls.add(new JLabel(urlMsg));
2539 // ask user to check in case URL links use old style tokens
2540 // ($SEQUENCE_ID$ for sequence id _or_ accession id)
2541 JPanel msgPanel = new JPanel();
2542 msgPanel.setLayout(new BoxLayout(msgPanel, BoxLayout.PAGE_AXIS));
2543 msgPanel.add(Box.createVerticalGlue());
2544 JLabel msg = new JLabel(MessageManager
2545 .getString("label.SEQUENCE_ID_for_DB_ACCESSION1"));
2546 JLabel msg2 = new JLabel(MessageManager
2547 .getString("label.SEQUENCE_ID_for_DB_ACCESSION2"));
2549 for (JLabel url : urls)
2555 final JCheckBox jcb = new JCheckBox(
2556 MessageManager.getString("label.do_not_display_again"));
2557 jcb.addActionListener(new ActionListener()
2560 public void actionPerformed(ActionEvent e)
2562 // update Cache settings for "don't show this again"
2563 boolean showWarningAgain = !jcb.isSelected();
2564 Cache.setProperty("CHECKURLLINKS",
2565 Boolean.valueOf(showWarningAgain).toString());
2570 JvOptionPane.showMessageDialog(desktopPane, msgPanel,
2572 .getString("label.SEQUENCE_ID_no_longer_used"),
2573 JvOptionPane.WARNING_MESSAGE);
2580 * Proxy class for JDesktopPane which optionally displays the current memory
2581 * usage and highlights the desktop area with a red bar if free memory runs low.
2585 public class MyDesktopPane extends JDesktopPane
2588 private static final float ONE_MB = 1048576f;
2590 boolean showMemoryUsage = false;
2594 java.text.NumberFormat df;
2596 float maxMemory, allocatedMemory, freeMemory, totalFreeMemory,
2599 public MyDesktopPane(boolean showMemoryUsage)
2601 showMemoryUsage(showMemoryUsage);
2604 public void showMemoryUsage(boolean showMemory)
2606 this.showMemoryUsage = showMemory;
2609 Thread worker = new Thread(this);
2615 public boolean isShowMemoryUsage()
2617 return showMemoryUsage;
2623 df = java.text.NumberFormat.getNumberInstance();
2624 df.setMaximumFractionDigits(2);
2625 runtime = Runtime.getRuntime();
2627 while (showMemoryUsage)
2631 maxMemory = runtime.maxMemory() / ONE_MB;
2632 allocatedMemory = runtime.totalMemory() / ONE_MB;
2633 freeMemory = runtime.freeMemory() / ONE_MB;
2634 totalFreeMemory = freeMemory + (maxMemory - allocatedMemory);
2636 percentUsage = (totalFreeMemory / maxMemory) * 100;
2638 // if (percentUsage < 20)
2640 // border1 = BorderFactory.createMatteBorder(12, 12, 12, 12,
2642 // instance.set.setBorder(border1);
2645 // sleep after showing usage
2647 } catch (Exception ex)
2649 ex.printStackTrace();
2655 public void paintComponent(Graphics g)
2657 if (showMemoryUsage && g != null && df != null)
2659 if (percentUsage < 20)
2661 g.setColor(Color.red);
2663 FontMetrics fm = g.getFontMetrics();
2666 g.drawString(MessageManager.formatMessage("label.memory_stats",
2668 { df.format(totalFreeMemory), df.format(maxMemory),
2669 df.format(percentUsage) }),
2670 10, getHeight() - fm.getHeight());
2677 * Accessor method to quickly get all the AlignmentFrames loaded.
2679 * @return an array of AlignFrame, or null if none found
2681 public static AlignFrame[] getAlignFrames()
2683 if (Jalview.isHeadlessMode())
2685 // Desktop.getDesktop() is null in headless mode
2686 return new AlignFrame[] { Jalview.getCurrentAlignFrame() };
2689 JInternalFrame[] frames = Desktop.getDesktopPane().getAllFrames();
2695 List<AlignFrame> avp = new ArrayList<>();
2697 for (int i = frames.length - 1; i > -1; i--)
2699 if (frames[i] instanceof AlignFrame)
2701 avp.add((AlignFrame) frames[i]);
2703 else if (frames[i] instanceof SplitFrame)
2706 * Also check for a split frame containing an AlignFrame
2708 GSplitFrame sf = (GSplitFrame) frames[i];
2709 if (sf.getTopFrame() instanceof AlignFrame)
2711 avp.add((AlignFrame) sf.getTopFrame());
2713 if (sf.getBottomFrame() instanceof AlignFrame)
2715 avp.add((AlignFrame) sf.getBottomFrame());
2719 if (avp.size() == 0)
2723 AlignFrame afs[] = avp.toArray(new AlignFrame[avp.size()]);
2728 * Returns an array of any AppJmol frames in the Desktop (or null if none).
2732 public GStructureViewer[] getJmols()
2734 JInternalFrame[] frames = Desktop.getDesktopPane().getAllFrames();
2740 List<GStructureViewer> avp = new ArrayList<>();
2742 for (int i = frames.length - 1; i > -1; i--)
2744 if (frames[i] instanceof AppJmol)
2746 GStructureViewer af = (GStructureViewer) frames[i];
2750 if (avp.size() == 0)
2754 GStructureViewer afs[] = avp.toArray(new GStructureViewer[avp.size()]);
2759 * Add Groovy Support to Jalview
2762 public void groovyShell_actionPerformed()
2766 openGroovyConsole();
2767 } catch (Exception ex)
2769 jalview.bin.Cache.log.error("Groovy Shell Creation failed.", ex);
2770 JvOptionPane.showInternalMessageDialog(desktopPane,
2772 MessageManager.getString("label.couldnt_create_groovy_shell"),
2773 MessageManager.getString("label.groovy_support_failed"),
2774 JvOptionPane.ERROR_MESSAGE);
2779 * Open the Groovy console
2781 private void openGroovyConsole()
2783 if (groovyConsole == null)
2785 groovyConsole = new groovy.ui.Console();
2786 groovyConsole.setVariable("Jalview", this);
2787 groovyConsole.run();
2790 * We allow only one console at a time, so that AlignFrame menu option
2791 * 'Calculate | Run Groovy script' is unambiguous.
2792 * Disable 'Groovy Console', and enable 'Run script', when the console is
2793 * opened, and the reverse when it is closed
2795 Window window = (Window) groovyConsole.getFrame();
2796 window.addWindowListener(new WindowAdapter()
2799 public void windowClosed(WindowEvent e)
2802 * rebind CMD-Q from Groovy Console to Jalview Quit
2805 enableExecuteGroovy(false);
2811 * show Groovy console window (after close and reopen)
2813 ((Window) groovyConsole.getFrame()).setVisible(true);
2816 * if we got this far, enable 'Run Groovy' in AlignFrame menus
2817 * and disable opening a second console
2819 enableExecuteGroovy(true);
2823 * Bind Ctrl/Cmd-Q to Quit - for reset as Groovy Console takes over this binding
2826 protected void addQuitHandler()
2828 getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
2829 .put(KeyStroke.getKeyStroke(KeyEvent.VK_Q,
2830 Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()),
2832 getRootPane().getActionMap().put("Quit", new AbstractAction()
2835 public void actionPerformed(ActionEvent e)
2843 * Enable or disable 'Run Groovy script' in AlignFrame calculate menus
2846 * true if Groovy console is open
2848 public void enableExecuteGroovy(boolean enabled)
2851 * disable opening a second Groovy console
2852 * (or re-enable when the console is closed)
2854 groovyShell.setEnabled(!enabled);
2856 AlignFrame[] alignFrames = getAlignFrames();
2857 if (alignFrames != null)
2859 for (AlignFrame af : alignFrames)
2861 af.setGroovyEnabled(enabled);
2867 * Progress bars managed by the IProgressIndicator method.
2869 private Hashtable<Long, JPanel> progressBars;
2871 private Hashtable<Long, IProgressIndicatorHandler> progressBarHandlers;
2876 * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
2879 public void setProgressBar(String message, long id)
2881 Platform.timeCheck("Desktop " + message, Platform.TIME_MARK);
2883 if (progressBars == null)
2885 progressBars = new Hashtable<>();
2886 progressBarHandlers = new Hashtable<>();
2889 if (progressBars.get(new Long(id)) != null)
2891 JPanel panel = progressBars.remove(new Long(id));
2892 if (progressBarHandlers.contains(new Long(id)))
2894 progressBarHandlers.remove(new Long(id));
2896 removeProgressPanel(panel);
2900 progressBars.put(new Long(id), addProgressPanel(message));
2907 * @see jalview.gui.IProgressIndicator#registerHandler(long,
2908 * jalview.gui.IProgressIndicatorHandler)
2911 public void registerHandler(final long id,
2912 final IProgressIndicatorHandler handler)
2914 if (progressBarHandlers == null
2915 || !progressBars.containsKey(new Long(id)))
2917 throw new Error(MessageManager.getString(
2918 "error.call_setprogressbar_before_registering_handler"));
2920 progressBarHandlers.put(new Long(id), handler);
2921 final JPanel progressPanel = progressBars.get(new Long(id));
2922 if (handler.canCancel())
2924 JButton cancel = new JButton(
2925 MessageManager.getString("action.cancel"));
2926 final IProgressIndicator us = this;
2927 cancel.addActionListener(new ActionListener()
2931 public void actionPerformed(ActionEvent e)
2933 handler.cancelActivity(id);
2934 us.setProgressBar(MessageManager
2935 .formatMessage("label.cancelled_params", new Object[]
2936 { ((JLabel) progressPanel.getComponent(0)).getText() }),
2940 progressPanel.add(cancel, BorderLayout.EAST);
2946 * @return true if any progress bars are still active
2949 public boolean operationInProgress()
2951 if (progressBars != null && progressBars.size() > 0)
2959 * This will return the first AlignFrame holding the given viewport instance. It
2960 * will break if there are more than one AlignFrames viewing a particular av.
2963 * @return alignFrame for viewport
2965 public static AlignFrame getAlignFrameFor(AlignViewportI viewport)
2967 if (getDesktopPane() != null)
2969 AlignmentPanel[] aps = getAlignmentPanels(
2970 viewport.getSequenceSetId());
2971 for (int panel = 0; aps != null && panel < aps.length; panel++)
2973 if (aps[panel] != null && aps[panel].av == viewport)
2975 return aps[panel].alignFrame;
2982 public VamsasApplication getVamsasApplication()
2989 * flag set if jalview GUI is being operated programmatically
2991 private boolean inBatchMode = false;
2994 * check if jalview GUI is being operated programmatically
2996 * @return inBatchMode
2998 public boolean isInBatchMode()
3004 * set flag if jalview GUI is being operated programmatically
3006 * @param inBatchMode
3008 public void setInBatchMode(boolean inBatchMode)
3010 this.inBatchMode = inBatchMode;
3013 public void startServiceDiscovery()
3015 startServiceDiscovery(false);
3018 public void startServiceDiscovery(boolean blocking)
3020 boolean alive = true;
3021 Thread t0 = null, t1 = null, t2 = null;
3022 // JAL-940 - JALVIEW 1 services are now being EOLed as of JABA 2.1 release
3025 // todo: changesupport handlers need to be transferred
3026 if (discoverer == null)
3028 discoverer = Discoverer.getInstance();
3029 // register PCS handler for getDesktop().
3030 discoverer.addPropertyChangeListener(changeSupport);
3032 // JAL-940 - disabled JWS1 service configuration - always start discoverer
3033 // until we phase out completely
3034 (t0 = new Thread(discoverer)).start();
3037 if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
3039 t2 = jalview.ws.jws2.Jws2Discoverer.getInstance()
3040 .startDiscoverer(changeSupport);
3044 // TODO: do rest service discovery
3053 } catch (Exception e)
3056 alive = (t1 != null && t1.isAlive()) || (t2 != null && t2.isAlive())
3057 || (t3 != null && t3.isAlive())
3058 || (t0 != null && t0.isAlive());
3064 * called to check if the service discovery process completed successfully.
3068 protected void JalviewServicesChanged(PropertyChangeEvent evt)
3070 if (evt.getNewValue() == null || evt.getNewValue() instanceof Vector)
3072 final String ermsg = jalview.ws.jws2.Jws2Discoverer.getInstance()
3073 .getErrorMessages();
3076 if (Cache.getDefault("SHOW_WSDISCOVERY_ERRORS", true))
3078 if (serviceChangedDialog == null)
3080 // only run if we aren't already displaying one of these.
3081 addDialogThread(serviceChangedDialog = new Runnable()
3088 * JalviewDialog jd =new JalviewDialog() {
3090 * @Override protected void cancelPressed() { // TODO
3091 * Auto-generated method stub
3093 * }@Override protected void okPressed() { // TODO
3094 * Auto-generated method stub
3096 * }@Override protected void raiseClosed() { // TODO
3097 * Auto-generated method stub
3099 * } }; jd.initDialogFrame(new
3100 * JLabel("<html><table width=\"450\"><tr><td>" + ermsg +
3101 * "<br/>It may be that you have invalid JABA URLs in your web service preferences,"
3102 * + " or mis-configured HTTP proxy settings.<br/>" +
3103 * "Check the <em>Connections</em> and <em>Web services</em> tab of the"
3105 * " Tools->Preferences dialog box to change them.</td></tr></table></html>"
3106 * ), true, true, "Web Service Configuration Problem", 450,
3109 * jd.waitForInput();
3111 JvOptionPane.showConfirmDialog(Desktop.getDesktopPane(),
3112 new JLabel("<html><table width=\"450\"><tr><td>"
3113 + ermsg + "</td></tr></table>"
3114 + "<p>It may be that you have invalid JABA URLs<br/>in your web service preferences,"
3115 + "<br>or as a command-line argument, or mis-configured HTTP proxy settings.</p>"
3116 + "<p>Check the <em>Connections</em> and <em>Web services</em> tab<br/>of the"
3117 + " Tools->Preferences dialog box to change them.</p></html>"),
3118 "Web Service Configuration Problem",
3119 JvOptionPane.DEFAULT_OPTION,
3120 JvOptionPane.ERROR_MESSAGE);
3121 serviceChangedDialog = null;
3130 "Errors reported by JABA discovery service. Check web services preferences.\n"
3137 Runnable serviceChangedDialog = null;
3140 * start a thread to open a URL in the configured browser. Pops up a warning
3141 * dialog to the user if there is an exception when calling out to the browser
3146 public static void showUrl(final String url)
3148 showUrl(url, Desktop.getInstance());
3152 * Like showUrl but allows progress handler to be specified
3156 * (null) or object implementing IProgressIndicator
3158 public static void showUrl(final String url,
3159 final IProgressIndicator progress)
3161 new Thread(new Runnable()
3168 if (progress != null)
3170 progress.setProgressBar(MessageManager
3171 .formatMessage("status.opening_params", new Object[]
3172 { url }), this.hashCode());
3174 BrowserLauncher.openURL(url);
3175 } catch (Exception ex)
3177 JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
3179 .getString("label.web_browser_not_found_unix"),
3180 MessageManager.getString("label.web_browser_not_found"),
3181 JvOptionPane.WARNING_MESSAGE);
3183 ex.printStackTrace();
3185 if (progress != null)
3187 progress.setProgressBar(null, this.hashCode());
3193 private WsParamSetManager wsparamManager = null;
3195 public static ParamManager getUserParameterStore()
3197 Desktop d = Desktop.getInstance();
3198 if (d.wsparamManager == null)
3200 d.wsparamManager = new WsParamSetManager();
3202 return d.wsparamManager;
3206 * static hyperlink handler proxy method for use by Jalview's internal windows
3210 public static void hyperlinkUpdate(HyperlinkEvent e)
3212 if (e.getEventType() == EventType.ACTIVATED)
3217 url = e.getURL().toString();
3218 Desktop.showUrl(url);
3219 } catch (Exception x)
3223 if (Cache.log != null)
3225 Cache.log.error("Couldn't handle string " + url + " as a URL.");
3230 "Couldn't handle string " + url + " as a URL.");
3233 // ignore any exceptions due to dud links.
3240 * single thread that handles display of dialogs to user.
3242 ExecutorService dialogExecutor = Executors.newSingleThreadExecutor();
3245 * flag indicating if dialogExecutor should try to acquire a permit
3247 volatile boolean dialogPause = true;
3252 java.util.concurrent.Semaphore block = new Semaphore(0);
3254 private groovy.ui.Console groovyConsole;
3256 public StructureViewer lastTargetedView;
3259 * add another dialog thread to the queue
3263 public void addDialogThread(final Runnable prompter)
3265 dialogExecutor.submit(new Runnable()
3275 } catch (InterruptedException x)
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 = desktopPane.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 Desktop.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");
3567 } catch (Throwable ex)
3569 Cache.log.debug("URL drop handler failed.", ex);
3572 if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
3574 // Works on Windows and MacOSX
3575 Cache.log.debug("Drop handled as javaFileListFlavor");
3576 for (Object file : (List<Object>) t
3577 .getTransferData(DataFlavor.javaFileListFlavor))
3580 protocols.add(DataSourceType.FILE);
3585 // Unix like behaviour
3586 boolean added = false;
3588 if (t.isDataFlavorSupported(uriListFlavor))
3590 Cache.log.debug("Drop handled as uriListFlavor");
3591 // This is used by Unix drag system
3592 data = (String) t.getTransferData(uriListFlavor);
3596 // fallback to text: workaround - on OSX where there's a JVM bug
3597 Cache.log.debug("standard URIListFlavor failed. Trying text");
3598 // try text fallback
3599 DataFlavor textDf = new DataFlavor(
3600 "text/plain;class=java.lang.String");
3601 if (t.isDataFlavorSupported(textDf))
3603 data = (String) t.getTransferData(textDf);
3606 Cache.log.debug("Plain text drop content returned "
3607 + (data == null ? "Null - failed" : data));
3612 while (protocols.size() < files.size())
3614 Cache.log.debug("Adding missing FILE protocol for "
3615 + files.get(protocols.size()));
3616 protocols.add(DataSourceType.FILE);
3618 for (java.util.StringTokenizer st = new java.util.StringTokenizer(
3619 data, "\r\n"); st.hasMoreTokens();)
3622 String s = st.nextToken();
3623 if (s.startsWith("#"))
3625 // the line is a comment (as per the RFC 2483)
3628 java.net.URI uri = new java.net.URI(s);
3629 if (uri.getScheme().toLowerCase().startsWith("http"))
3631 protocols.add(DataSourceType.URL);
3632 files.add(uri.toString());
3636 // otherwise preserve old behaviour: catch all for file objects
3637 java.io.File file = new java.io.File(uri);
3638 protocols.add(DataSourceType.FILE);
3639 files.add(file.toString());
3644 if (Cache.log.isDebugEnabled())
3646 if (data == null || !added)
3649 if (t.getTransferDataFlavors() != null
3650 && t.getTransferDataFlavors().length > 0)
3653 "Couldn't resolve drop data. Here are the supported flavors:");
3654 for (DataFlavor fl : t.getTransferDataFlavors())
3657 "Supported transfer dataflavor: " + fl.toString());
3658 Object df = t.getTransferData(fl);
3661 Cache.log.debug("Retrieves: " + df);
3665 Cache.log.debug("Retrieved nothing");
3671 Cache.log.debug("Couldn't resolve dataflavor for drop: "
3677 if (Platform.isWindowsAndNotJS())
3679 Cache.log.debug("Scanning dropped content for Windows Link Files");
3681 // resolve any .lnk files in the file drop
3682 for (int f = 0; f < files.size(); f++)
3684 String source = files.get(f).toString().toLowerCase();
3685 if (protocols.get(f).equals(DataSourceType.FILE)
3686 && (source.endsWith(".lnk") || source.endsWith(".url")
3687 || source.endsWith(".site")))
3691 Object obj = files.get(f);
3692 File lf = (obj instanceof File ? (File) obj
3693 : new File((String) obj));
3694 // process link file to get a URL
3695 Cache.log.debug("Found potential link file: " + lf);
3696 WindowsShortcut wscfile = new WindowsShortcut(lf);
3697 String fullname = wscfile.getRealFilename();
3698 protocols.set(f, FormatAdapter.checkProtocol(fullname));
3699 files.set(f, fullname);
3700 Cache.log.debug("Parsed real filename " + fullname
3701 + " to extract protocol: " + protocols.get(f));
3702 } catch (Exception ex)
3705 "Couldn't parse " + files.get(f) + " as a link file.",
3714 * Sets the Preferences property for experimental features to True or False
3715 * depending on the state of the controlling menu item
3718 protected void showExperimental_actionPerformed(boolean selected)
3720 Cache.setProperty(EXPERIMENTAL_FEATURES, Boolean.toString(selected));
3724 * Answers a (possibly empty) list of any structure viewer frames (currently for
3725 * either Jmol or Chimera) which are currently open. This may optionally be
3726 * restricted to viewers of a specified class, or viewers linked to a specified
3730 * if not null, only return viewers linked to this panel
3731 * @param structureViewerClass
3732 * if not null, only return viewers of this class
3735 public List<StructureViewerBase> getStructureViewers(
3736 AlignmentPanel apanel,
3737 Class<? extends StructureViewerBase> structureViewerClass)
3739 List<StructureViewerBase> result = new ArrayList<>();
3740 JInternalFrame[] frames = getAllFrames();
3742 for (JInternalFrame frame : frames)
3744 if (frame instanceof StructureViewerBase)
3746 if (structureViewerClass == null
3747 || structureViewerClass.isInstance(frame))
3750 || ((StructureViewerBase) frame).isLinkedWith(apanel))
3752 result.add((StructureViewerBase) frame);