2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
5 * This file is part of Jalview.
7 * Jalview is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation, either version 3
10 * of the License, or (at your option) any later version.
12 * Jalview is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19 * The Jalview Authors are detailed in the 'AUTHORS' file.
23 import jalview.api.AlignViewportI;
24 import jalview.api.AlignmentViewPanel;
25 import jalview.api.StructureSelectionManagerProvider;
26 import jalview.bin.ApplicationSingletonProvider;
27 import jalview.bin.ApplicationSingletonProvider.ApplicationSingletonI;
28 import jalview.bin.Cache;
29 import jalview.bin.Jalview;
30 import jalview.gui.ImageExporter.ImageWriterI;
31 import jalview.io.BackupFiles;
32 import jalview.io.DataSourceType;
33 import jalview.io.FileFormat;
34 import jalview.io.FileFormatException;
35 import jalview.io.FileFormatI;
36 import jalview.io.FileFormats;
37 import jalview.io.FileLoader;
38 import jalview.io.FormatAdapter;
39 import jalview.io.IdentifyFile;
40 import jalview.io.JalviewFileChooser;
41 import jalview.io.JalviewFileView;
42 import jalview.jbgui.GDesktop;
43 import jalview.jbgui.GSplitFrame;
44 import jalview.jbgui.GStructureViewer;
45 import jalview.project.Jalview2XML;
46 import jalview.structure.StructureSelectionManager;
47 import jalview.urls.IdOrgSettings;
48 import jalview.util.BrowserLauncher;
49 import jalview.util.ImageMaker.TYPE;
50 import jalview.util.MessageManager;
51 import jalview.util.Platform;
52 import jalview.util.UrlConstants;
53 import jalview.viewmodel.AlignmentViewport;
54 import jalview.ws.jws1.Discoverer;
55 import jalview.ws.params.ParamManager;
56 import jalview.ws.utils.UrlDownloadClient;
58 import java.awt.BorderLayout;
59 import java.awt.Color;
60 import java.awt.Dimension;
61 import java.awt.FontMetrics;
62 import java.awt.Graphics;
63 import java.awt.GridLayout;
64 import java.awt.Point;
65 import java.awt.Rectangle;
66 import java.awt.Toolkit;
67 import java.awt.Window;
68 import java.awt.datatransfer.Clipboard;
69 import java.awt.datatransfer.ClipboardOwner;
70 import java.awt.datatransfer.DataFlavor;
71 import java.awt.datatransfer.Transferable;
72 import java.awt.dnd.DnDConstants;
73 import java.awt.dnd.DropTargetDragEvent;
74 import java.awt.dnd.DropTargetDropEvent;
75 import java.awt.dnd.DropTargetEvent;
76 import java.awt.dnd.DropTargetListener;
77 import java.awt.event.ActionEvent;
78 import java.awt.event.ActionListener;
79 import java.awt.event.InputEvent;
80 import java.awt.event.KeyEvent;
81 import java.awt.event.MouseAdapter;
82 import java.awt.event.MouseEvent;
83 import java.awt.event.WindowAdapter;
84 import java.awt.event.WindowEvent;
85 import java.beans.PropertyChangeEvent;
86 import java.beans.PropertyChangeListener;
87 import java.io.BufferedInputStream;
89 import java.io.FileOutputStream;
90 import java.io.IOException;
92 import java.util.ArrayList;
93 import java.util.Hashtable;
94 import java.util.List;
95 import java.util.ListIterator;
96 import java.util.Vector;
97 import java.util.concurrent.ExecutorService;
98 import java.util.concurrent.Executors;
99 import java.util.concurrent.Semaphore;
101 import javax.swing.AbstractAction;
102 import javax.swing.Action;
103 import javax.swing.ActionMap;
104 import javax.swing.Box;
105 import javax.swing.BoxLayout;
106 import javax.swing.DefaultDesktopManager;
107 import javax.swing.DesktopManager;
108 import javax.swing.InputMap;
109 import javax.swing.JButton;
110 import javax.swing.JCheckBox;
111 import javax.swing.JComboBox;
112 import javax.swing.JComponent;
113 import javax.swing.JDesktopPane;
114 import javax.swing.JFrame;
115 import javax.swing.JInternalFrame;
116 import javax.swing.JLabel;
117 import javax.swing.JMenuItem;
118 import javax.swing.JPanel;
119 import javax.swing.JPopupMenu;
120 import javax.swing.JProgressBar;
121 import javax.swing.JTextField;
122 import javax.swing.KeyStroke;
123 import javax.swing.SwingUtilities;
124 import javax.swing.event.HyperlinkEvent;
125 import javax.swing.event.HyperlinkEvent.EventType;
126 import javax.swing.event.InternalFrameAdapter;
127 import javax.swing.event.InternalFrameEvent;
128 import javax.swing.event.MenuEvent;
129 import javax.swing.event.MenuListener;
131 import org.stackoverflowusers.file.WindowsShortcut;
138 * @version $Revision: 1.155 $
140 @SuppressWarnings("serial")
141 public class Desktop extends GDesktop
142 implements DropTargetListener, ClipboardOwner, IProgressIndicator,
143 StructureSelectionManagerProvider, ApplicationSingletonI
146 private final static int DEFAULT_MIN_WIDTH = 300;
148 private final static int DEFAULT_MIN_HEIGHT = 250;
150 private final static int ALIGN_FRAME_DEFAULT_MIN_WIDTH = 600;
152 private final static int ALIGN_FRAME_DEFAULT_MIN_HEIGHT = 70;
154 private final static String EXPERIMENTAL_FEATURES = "EXPERIMENTAL_FEATURES";
156 private JalviewChangeSupport changeSupport = new JalviewChangeSupport();
159 * news reader - null if it was never started.
161 BlogReader jvnews = null;
163 private File projectFile;
167 * @see jalview.gui.JalviewChangeSupport#addJalviewPropertyChangeListener(java.beans.PropertyChangeListener)
169 public void addJalviewPropertyChangeListener(
170 PropertyChangeListener listener)
172 changeSupport.addJalviewPropertyChangeListener(listener);
176 * @param propertyName
178 * @see jalview.gui.JalviewChangeSupport#addJalviewPropertyChangeListener(java.lang.String,
179 * java.beans.PropertyChangeListener)
181 public void addJalviewPropertyChangeListener(String propertyName,
182 PropertyChangeListener listener)
184 changeSupport.addJalviewPropertyChangeListener(propertyName, listener);
188 * @param propertyName
190 * @see jalview.gui.JalviewChangeSupport#removeJalviewPropertyChangeListener(java.lang.String,
191 * java.beans.PropertyChangeListener)
193 public void removeJalviewPropertyChangeListener(String propertyName,
194 PropertyChangeListener listener)
196 changeSupport.removeJalviewPropertyChangeListener(propertyName,
200 public static MyDesktopPane getDesktopPane()
202 Desktop desktop = Desktop.getInstance();
203 return desktop == null ? null : desktop.desktopPane;
206 public static StructureSelectionManager getStructureSelectionManager()
208 return StructureSelectionManager
209 .getStructureSelectionManager(getInstance());
212 static int openFrameCount = 0;
214 static final int xOffset = 30;
216 static final int yOffset = 30;
218 public Discoverer discoverer;
220 public Object[] jalviewClipboard;
222 public boolean internalCopy = false;
224 private static int fileLoadingCount = 0;
226 public JInternalFrame conservationSlider;
228 public JInternalFrame PIDSlider;
231 * just an instance (for testng, probably); no actual frames
233 private boolean instanceOnly;
235 class MyDesktopManager implements DesktopManager
238 private DesktopManager delegate;
240 public MyDesktopManager(DesktopManager delegate)
242 this.delegate = delegate;
246 public void activateFrame(JInternalFrame f)
250 delegate.activateFrame(f);
251 } catch (NullPointerException npe)
253 Point p = getMousePosition();
254 showPasteMenu(p.x, p.y);
259 public void beginDraggingFrame(JComponent f)
261 delegate.beginDraggingFrame(f);
265 public void beginResizingFrame(JComponent f, int direction)
267 delegate.beginResizingFrame(f, direction);
271 public void closeFrame(JInternalFrame f)
273 delegate.closeFrame(f);
277 public void deactivateFrame(JInternalFrame f)
279 delegate.deactivateFrame(f);
283 public void deiconifyFrame(JInternalFrame f)
285 delegate.deiconifyFrame(f);
289 public void dragFrame(JComponent f, int newX, int newY)
295 delegate.dragFrame(f, newX, newY);
299 public void endDraggingFrame(JComponent f)
301 delegate.endDraggingFrame(f);
302 desktopPane.repaint();
306 public void endResizingFrame(JComponent f)
308 delegate.endResizingFrame(f);
309 desktopPane.repaint();
313 public void iconifyFrame(JInternalFrame f)
315 delegate.iconifyFrame(f);
319 public void maximizeFrame(JInternalFrame f)
321 delegate.maximizeFrame(f);
325 public void minimizeFrame(JInternalFrame f)
327 delegate.minimizeFrame(f);
331 public void openFrame(JInternalFrame f)
333 delegate.openFrame(f);
337 public void resizeFrame(JComponent f, int newX, int newY, int newWidth,
344 delegate.resizeFrame(f, newX, newY, newWidth, newHeight);
348 public void setBoundsForFrame(JComponent f, int newX, int newY,
349 int newWidth, int newHeight)
351 delegate.setBoundsForFrame(f, newX, newY, newWidth, newHeight);
354 // All other methods, simply delegate
358 public MyDesktopPane desktopPane;
361 * Answers an 'application scope' singleton instance of this class. Separate
362 * SwingJS 'applets' running in the same browser page will each have a
363 * distinct instance of Desktop.
367 public static Desktop getInstance()
369 return Jalview.isHeadlessMode() ? null
370 : (Desktop) ApplicationSingletonProvider
371 .getInstance(Desktop.class);
379 public Desktop(boolean forInstance)
385 * Private constructor enforces singleton pattern. It is called by reflection
386 * from ApplicationSingletonProvider.getInstance().
388 @SuppressWarnings("unused")
394 * A note to implementors. It is ESSENTIAL that any activities that might
395 * block are spawned off as threads rather than waited for during this
398 if (!Platform.isJS())
400 doVamsasClientCheck();
403 doConfigureStructurePrefs();
404 setTitle("Jalview " + jalview.bin.Cache.getProperty("VERSION"));
405 setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
406 boolean selmemusage = jalview.bin.Cache.getDefault("SHOW_MEMUSAGE",
408 boolean showjconsole = jalview.bin.Cache.getDefault("SHOW_JAVA_CONSOLE",
410 desktopPane = new MyDesktopPane(selmemusage);
412 showMemusage.setSelected(selmemusage);
413 desktopPane.setBackground(Color.white);
414 getContentPane().setLayout(new BorderLayout());
415 // alternate config - have scrollbars - see notes in JAL-153
416 // JScrollPane sp = new JScrollPane();
417 // sp.getViewport().setView(desktop);
418 // getContentPane().add(sp, BorderLayout.CENTER);
420 // BH 2018 - just an experiment to try unclipped JInternalFrames.
423 getRootPane().putClientProperty("swingjs.overflow.hidden", "false");
426 getContentPane().add(desktopPane, BorderLayout.CENTER);
427 desktopPane.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
429 // This line prevents Windows Look&Feel resizing all new windows to maximum
430 // if previous window was maximised
431 desktopPane.setDesktopManager(new MyDesktopManager(
432 (Platform.isWindowsAndNotJS() ? new DefaultDesktopManager()
433 : Platform.isAMacAndNotJS()
434 ? new AquaInternalFrameManager(
435 desktopPane.getDesktopManager())
436 : desktopPane.getDesktopManager())));
438 Rectangle dims = getLastKnownDimensions("");
445 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
446 int xPos = Math.max(5, (screenSize.width - 900) / 2);
447 int yPos = Math.max(5, (screenSize.height - 650) / 2);
448 setBounds(xPos, yPos, 900, 650);
451 if (!Platform.isJS())
459 jconsole = new Console(this, showjconsole);
460 // add essential build information
461 jconsole.setHeader("Jalview Version: "
462 + jalview.bin.Cache.getProperty("VERSION") + "\n"
463 + "Jalview Installation: "
464 + jalview.bin.Cache.getDefault("INSTALLATION", "unknown")
465 + "\n" + "Build Date: "
466 + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown") + "\n"
467 + "Java version: " + System.getProperty("java.version") + "\n"
468 + System.getProperty("os.arch") + " "
469 + System.getProperty("os.name") + " "
470 + System.getProperty("os.version"));
472 showConsole(showjconsole);
474 showNews.setVisible(false);
476 experimentalFeatures.setSelected(showExperimental());
478 getIdentifiersOrgData();
482 // Spawn a thread that shows the splashscreen
484 SwingUtilities.invokeLater(new Runnable()
493 // Thread off a new instance of the file chooser - this reduces the time
495 // takes to open it later on.
496 new Thread(new Runnable()
501 Cache.log.debug("Filechooser init thread started.");
502 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
503 JalviewFileChooser.forRead(Cache.getProperty("LAST_DIRECTORY"),
505 Cache.log.debug("Filechooser init thread finished.");
508 // Add the service change listener
509 changeSupport.addJalviewPropertyChangeListener("services",
510 new PropertyChangeListener()
514 public void propertyChange(PropertyChangeEvent evt)
516 Cache.log.debug("Firing service changed event for "
517 + evt.getNewValue());
518 JalviewServicesChanged(evt);
525 this.setDropTarget(new java.awt.dnd.DropTarget(desktopPane, this));
527 this.addWindowListener(new WindowAdapter()
530 public void windowClosing(WindowEvent evt)
537 this.addMouseListener(ma = new MouseAdapter()
540 public void mousePressed(MouseEvent evt)
542 if (evt.isPopupTrigger()) // Mac
544 showPasteMenu(evt.getX(), evt.getY());
549 public void mouseReleased(MouseEvent evt)
551 if (evt.isPopupTrigger()) // Windows
553 showPasteMenu(evt.getX(), evt.getY());
557 desktopPane.addMouseListener(ma);
558 } catch (Throwable t)
565 * Answers true if user preferences to enable experimental features is True
570 public boolean showExperimental()
572 String experimental = Cache.getDefault(EXPERIMENTAL_FEATURES,
573 Boolean.FALSE.toString());
574 return Boolean.valueOf(experimental).booleanValue();
577 public void doConfigureStructurePrefs()
579 // configure services
580 StructureSelectionManager ssm = StructureSelectionManager
581 .getStructureSelectionManager(this);
582 if (jalview.bin.Cache.getDefault(Preferences.ADD_SS_ANN, true))
584 ssm.setAddTempFacAnnot(jalview.bin.Cache
585 .getDefault(Preferences.ADD_TEMPFACT_ANN, true));
586 ssm.setProcessSecondaryStructure(jalview.bin.Cache
587 .getDefault(Preferences.STRUCT_FROM_PDB, true));
588 ssm.setSecStructServices(
589 jalview.bin.Cache.getDefault(Preferences.USE_RNAVIEW, true));
593 ssm.setAddTempFacAnnot(false);
594 ssm.setProcessSecondaryStructure(false);
595 ssm.setSecStructServices(false);
599 public void checkForNews()
601 final Desktop me = this;
602 // Thread off the news reader, in case there are connection problems.
603 new Thread(new Runnable()
608 Cache.log.debug("Starting news thread.");
609 jvnews = new BlogReader(me);
610 showNews.setVisible(true);
611 Cache.log.debug("Completed news thread.");
616 public void getIdentifiersOrgData()
618 // Thread off the identifiers fetcher
619 new Thread(new Runnable()
624 Cache.log.debug("Downloading data from identifiers.org");
625 // UrlDownloadClient client = new UrlDownloadClient();
628 UrlDownloadClient.download(IdOrgSettings.getUrl(),
629 IdOrgSettings.getDownloadLocation());
630 } catch (IOException e)
632 Cache.log.debug("Exception downloading identifiers.org data"
641 protected void showNews_actionPerformed(ActionEvent e)
643 showNews(showNews.isSelected());
646 protected void showNews(boolean visible)
648 Cache.log.debug((visible ? "Showing" : "Hiding") + " news.");
649 showNews.setSelected(visible);
650 if (visible && !jvnews.isVisible())
652 new Thread(new Runnable()
657 long now = System.currentTimeMillis();
659 MessageManager.getString("status.refreshing_news"), now);
660 jvnews.refreshNews();
661 setProgressBar(null, now);
669 * recover the last known dimensions for a jalview window
672 * - empty string is desktop, all other windows have unique prefix
673 * @return null or last known dimensions scaled to current geometry (if last
674 * window geom was known)
676 Rectangle getLastKnownDimensions(String windowName)
678 // TODO: lock aspect ratio for scaling desktop Bug #0058199
679 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
680 String x = jalview.bin.Cache.getProperty(windowName + "SCREEN_X");
681 String y = jalview.bin.Cache.getProperty(windowName + "SCREEN_Y");
682 String width = jalview.bin.Cache
683 .getProperty(windowName + "SCREEN_WIDTH");
684 String height = jalview.bin.Cache
685 .getProperty(windowName + "SCREEN_HEIGHT");
686 if ((x != null) && (y != null) && (width != null) && (height != null))
688 int ix = Integer.parseInt(x), iy = Integer.parseInt(y),
689 iw = Integer.parseInt(width), ih = Integer.parseInt(height);
690 if (jalview.bin.Cache.getProperty("SCREENGEOMETRY_WIDTH") != null)
692 // attempt #1 - try to cope with change in screen geometry - this
693 // version doesn't preserve original jv aspect ratio.
694 // take ratio of current screen size vs original screen size.
695 double sw = ((1f * screenSize.width) / (1f * Integer.parseInt(
696 jalview.bin.Cache.getProperty("SCREENGEOMETRY_WIDTH"))));
697 double sh = ((1f * screenSize.height) / (1f * Integer.parseInt(
698 jalview.bin.Cache.getProperty("SCREENGEOMETRY_HEIGHT"))));
699 // rescale the bounds depending upon the current screen geometry.
700 ix = (int) (ix * sw);
701 iw = (int) (iw * sw);
702 iy = (int) (iy * sh);
703 ih = (int) (ih * sh);
704 while (ix >= screenSize.width)
706 jalview.bin.Cache.log.debug(
707 "Window geometry location recall error: shifting horizontal to within screenbounds.");
708 ix -= screenSize.width;
710 while (iy >= screenSize.height)
712 jalview.bin.Cache.log.debug(
713 "Window geometry location recall error: shifting vertical to within screenbounds.");
714 iy -= screenSize.height;
716 jalview.bin.Cache.log.debug(
717 "Got last known dimensions for " + windowName + ": x:" + ix
718 + " y:" + iy + " width:" + iw + " height:" + ih);
720 // return dimensions for new instance
721 return new Rectangle(ix, iy, iw, ih);
726 private void doVamsasClientCheck()
728 if (Cache.vamsasJarsPresent())
730 setupVamsasDisconnectedGui();
731 VamsasMenu.setVisible(true);
732 final Desktop us = this;
733 VamsasMenu.addMenuListener(new MenuListener()
735 // this listener remembers when the menu was first selected, and
736 // doesn't rebuild the session list until it has been cleared and
738 boolean refresh = true;
741 public void menuCanceled(MenuEvent e)
747 public void menuDeselected(MenuEvent e)
753 public void menuSelected(MenuEvent e)
757 us.buildVamsasStMenu();
762 vamsasStart.setVisible(true);
766 protected void showPasteMenu(int x, int y)
768 JPopupMenu popup = new JPopupMenu();
769 JMenuItem item = new JMenuItem(
770 MessageManager.getString("label.paste_new_window"));
771 item.addActionListener(new ActionListener()
774 public void actionPerformed(ActionEvent evt)
781 popup.show(this, x, y);
788 Clipboard c = Toolkit.getDefaultToolkit().getSystemClipboard();
789 Transferable contents = c.getContents(this);
791 if (contents != null)
793 String file = (String) contents
794 .getTransferData(DataFlavor.stringFlavor);
796 FileFormatI format = new IdentifyFile().identify(file,
797 DataSourceType.PASTE);
799 new FileLoader().loadFile(file, DataSourceType.PASTE, format);
802 } catch (Exception ex)
805 "Unable to paste alignment from system clipboard:\n" + ex);
810 * Adds and opens the given frame to the desktop
821 public static synchronized void addInternalFrame(
822 final JInternalFrame frame, String title, int w, int h)
824 addInternalFrame(frame, title, true, w, h, true, false);
828 * Add an internal frame to the Jalview desktop
835 * When true, display frame immediately, otherwise, caller must call
836 * setVisible themselves.
842 public static synchronized void addInternalFrame(
843 final JInternalFrame frame, String title, boolean makeVisible,
846 addInternalFrame(frame, title, makeVisible, w, h, true, false);
850 * Add an internal frame to the Jalview desktop and make it visible
863 public static synchronized void addInternalFrame(
864 final JInternalFrame frame, String title, int w, int h,
867 addInternalFrame(frame, title, true, w, h, resizable, false);
871 * Add an internal frame to the Jalview desktop
878 * When true, display frame immediately, otherwise, caller must call
879 * setVisible themselves.
886 * @param ignoreMinSize
887 * Do not set the default minimum size for frame
889 public static synchronized void addInternalFrame(
890 final JInternalFrame frame, String title, boolean makeVisible,
891 int w, int h, boolean resizable, boolean ignoreMinSize)
895 // TODO: allow callers to determine X and Y position of frame (eg. via
897 // TODO: consider fixing method to update entries in the window submenu with
898 // the current window title
900 frame.setTitle(title);
901 if (w > 0 && (frame.getWidth() < 1 || frame.getHeight() < 1))
905 // THIS IS A PUBLIC STATIC METHOD, SO IT MAY BE CALLED EVEN IN
906 // A HEADLESS STATE WHEN NO DESKTOP EXISTS. MUST RETURN
907 // IF JALVIEW IS RUNNING HEADLESS
908 // ///////////////////////////////////////////////
909 if (Desktop.getInstance().instanceOnly || Jalview.isHeadlessMode())
918 frame.setMinimumSize(
919 new Dimension(DEFAULT_MIN_WIDTH, DEFAULT_MIN_HEIGHT));
921 // Set default dimension for Alignment Frame window.
922 // The Alignment Frame window could be added from a number of places,
924 // I did this here in order not to miss out on any Alignment frame.
925 if (frame instanceof AlignFrame)
927 frame.setMinimumSize(new Dimension(ALIGN_FRAME_DEFAULT_MIN_WIDTH,
928 ALIGN_FRAME_DEFAULT_MIN_HEIGHT));
932 frame.setVisible(makeVisible);
933 frame.setClosable(true);
934 frame.setResizable(resizable);
935 frame.setMaximizable(resizable);
936 frame.setIconifiable(resizable);
937 frame.setOpaque(Platform.isJS());
939 if (frame.getX() < 1 && frame.getY() < 1)
941 frame.setLocation(xOffset * openFrameCount,
942 yOffset * ((openFrameCount - 1) % 10) + yOffset);
946 * add an entry for the new frame in the Window menu
947 * (and remove it when the frame is closed)
949 JMenuItem menuItem = new JMenuItem(title);
950 frame.addInternalFrameListener(new InternalFrameAdapter()
953 public void internalFrameActivated(InternalFrameEvent evt)
955 JInternalFrame itf = getDesktopPane().getSelectedFrame();
958 if (itf instanceof AlignFrame)
960 Jalview.setCurrentAlignFrame((AlignFrame) itf);
967 public void internalFrameClosed(InternalFrameEvent evt)
969 PaintRefresher.RemoveComponent(frame);
972 * defensive check to prevent frames being
973 * added half off the window
975 if (openFrameCount > 0)
981 * ensure no reference to alignFrame retained by menu item listener
983 if (menuItem.getActionListeners().length > 0)
985 menuItem.removeActionListener(menuItem.getActionListeners()[0]);
987 Desktop.getInstance().windowMenu.remove(menuItem);
991 menuItem.addActionListener(new ActionListener()
994 public void actionPerformed(ActionEvent e)
998 frame.setSelected(true);
999 frame.setIcon(false);
1000 } catch (java.beans.PropertyVetoException ex)
1002 // System.err.println(ex.toString());
1007 setKeyBindings(frame);
1009 getDesktopPane().add(frame);
1011 Desktop.getInstance().windowMenu.add(menuItem);
1016 frame.setSelected(true);
1017 frame.requestFocus();
1018 } catch (java.beans.PropertyVetoException ve)
1020 } catch (java.lang.ClassCastException cex)
1023 "Squashed a possible GUI implementation error. If you can recreate this, please look at http://issues.jalview.org/browse/JAL-869",
1029 * Add key bindings to a JInternalFrame so that Ctrl-W and Cmd-W will close the
1034 private static void setKeyBindings(JInternalFrame frame)
1036 final Action closeAction = new AbstractAction()
1039 public void actionPerformed(ActionEvent e)
1046 * set up key bindings for Ctrl-W and Cmd-W, with the same (Close) action
1048 KeyStroke ctrlWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
1049 InputEvent.CTRL_DOWN_MASK);
1050 KeyStroke cmdWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
1051 Toolkit.getDefaultToolkit().getMenuShortcutKeyMask());
1053 InputMap inputMap = frame
1054 .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
1055 String ctrlW = ctrlWKey.toString();
1056 inputMap.put(ctrlWKey, ctrlW);
1057 inputMap.put(cmdWKey, ctrlW);
1059 ActionMap actionMap = frame.getActionMap();
1060 actionMap.put(ctrlW, closeAction);
1064 public void lostOwnership(Clipboard clipboard, Transferable contents)
1068 Desktop.getInstance().jalviewClipboard = null;
1071 internalCopy = false;
1075 public void dragEnter(DropTargetDragEvent evt)
1080 public void dragExit(DropTargetEvent evt)
1085 public void dragOver(DropTargetDragEvent evt)
1090 public void dropActionChanged(DropTargetDragEvent evt)
1101 public void drop(DropTargetDropEvent evt)
1103 boolean success = true;
1104 // JAL-1552 - acceptDrop required before getTransferable call for
1105 // Java's Transferable for native dnd
1106 evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
1107 Transferable t = evt.getTransferable();
1108 List<Object> files = new ArrayList<>();
1109 List<DataSourceType> protocols = new ArrayList<>();
1113 Desktop.transferFromDropTarget(files, protocols, evt, t);
1114 } catch (Exception e)
1116 e.printStackTrace();
1124 for (int i = 0; i < files.size(); i++)
1126 // BH 2018 File or String
1127 Object file = files.get(i);
1128 String fileName = file.toString();
1129 DataSourceType protocol = (protocols == null)
1130 ? DataSourceType.FILE
1132 FileFormatI format = null;
1134 if (fileName.endsWith(".jar"))
1136 format = FileFormat.Jalview;
1141 format = new IdentifyFile().identify(file, protocol);
1143 if (file instanceof File)
1145 Platform.cacheFileData((File) file);
1147 new FileLoader().loadFile(null, file, protocol, format);
1150 } catch (Exception ex)
1155 evt.dropComplete(success); // need this to ensure input focus is properly
1156 // transfered to any new windows created
1166 public void inputLocalFileMenuItem_actionPerformed(AlignViewport viewport)
1168 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
1169 JalviewFileChooser chooser = JalviewFileChooser
1170 .forRead(Cache.getProperty("LAST_DIRECTORY"), fileFormat, true);
1172 chooser.setFileView(new JalviewFileView());
1173 chooser.setDialogTitle(
1174 MessageManager.getString("label.open_local_file"));
1175 chooser.setToolTipText(MessageManager.getString("action.open"));
1177 chooser.setResponseHandler(0, new Runnable()
1182 File selectedFile = chooser.getSelectedFile();
1183 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1185 FileFormatI format = chooser.getSelectedFormat();
1188 * Call IdentifyFile to verify the file contains what its extension implies.
1189 * Skip this step for dynamically added file formats, because
1190 * IdentifyFile does not know how to recognise them.
1192 if (FileFormats.getInstance().isIdentifiable(format))
1196 format = new IdentifyFile().identify(selectedFile,
1197 DataSourceType.FILE);
1198 } catch (FileFormatException e)
1200 // format = null; //??
1204 new FileLoader().loadFile(viewport, selectedFile,
1205 DataSourceType.FILE, format);
1208 chooser.showOpenDialog(this);
1212 * Shows a dialog for input of a URL at which to retrieve alignment data
1217 public void inputURLMenuItem_actionPerformed(AlignViewport viewport)
1219 // This construct allows us to have a wider textfield
1221 JLabel label = new JLabel(
1222 MessageManager.getString("label.input_file_url"));
1224 JPanel panel = new JPanel(new GridLayout(2, 1));
1228 * the URL to fetch is
1229 * Java: an editable combobox with history
1230 * JS: (pending JAL-3038) a plain text field
1233 String urlBase = "http://www.";
1234 if (Platform.isJS())
1236 history = new JTextField(urlBase, 35);
1245 JComboBox<String> asCombo = new JComboBox<>();
1246 asCombo.setPreferredSize(new Dimension(400, 20));
1247 asCombo.setEditable(true);
1248 asCombo.addItem(urlBase);
1249 String historyItems = Cache.getProperty("RECENT_URL");
1250 if (historyItems != null)
1252 for (String token : historyItems.split("\\t"))
1254 asCombo.addItem(token);
1261 Object[] options = new Object[] { MessageManager.getString("action.ok"),
1262 MessageManager.getString("action.cancel") };
1263 Runnable action = new Runnable()
1268 @SuppressWarnings("unchecked")
1269 String url = (history instanceof JTextField
1270 ? ((JTextField) history).getText()
1271 : ((JComboBox<String>) history).getSelectedItem()
1274 if (url.toLowerCase().endsWith(".jar"))
1276 if (viewport != null)
1278 new FileLoader().loadFile(viewport, url, DataSourceType.URL,
1279 FileFormat.Jalview);
1283 new FileLoader().loadFile(url, DataSourceType.URL,
1284 FileFormat.Jalview);
1289 FileFormatI format = null;
1292 format = new IdentifyFile().identify(url, DataSourceType.URL);
1293 } catch (FileFormatException e)
1295 // TODO revise error handling, distinguish between
1296 // URL not found and response not valid
1301 String msg = MessageManager
1302 .formatMessage("label.couldnt_locate", url);
1303 JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
1305 MessageManager.getString("label.url_not_found"),
1306 JvOptionPane.WARNING_MESSAGE);
1311 if (viewport != null)
1313 new FileLoader().loadFile(viewport, url, DataSourceType.URL,
1318 new FileLoader().loadFile(url, DataSourceType.URL, format);
1323 String dialogOption = MessageManager
1324 .getString("label.input_alignment_from_url");
1325 JvOptionPane.newOptionDialog(getDesktopPane())
1326 .setResponseHandler(0, action)
1327 .showInternalDialog(panel, dialogOption,
1328 JvOptionPane.YES_NO_CANCEL_OPTION,
1329 JvOptionPane.PLAIN_MESSAGE, null, options,
1330 MessageManager.getString("action.ok"));
1334 * Opens the CutAndPaste window for the user to paste an alignment in to
1337 * - if not null, the pasted alignment is added to the current
1338 * alignment; if null, to a new alignment window
1341 public void inputTextboxMenuItem_actionPerformed(
1342 AlignmentViewPanel viewPanel)
1344 CutAndPasteTransfer cap = new CutAndPasteTransfer();
1345 cap.setForInput(viewPanel);
1346 Desktop.addInternalFrame(cap,
1347 MessageManager.getString("label.cut_paste_alignmen_file"), true,
1357 Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
1358 jalview.bin.Cache.setProperty("SCREENGEOMETRY_WIDTH",
1360 jalview.bin.Cache.setProperty("SCREENGEOMETRY_HEIGHT",
1361 screen.height + "");
1362 storeLastKnownDimensions("", new Rectangle(getBounds().x, getBounds().y,
1363 getWidth(), getHeight()));
1365 if (jconsole != null)
1367 storeLastKnownDimensions("JAVA_CONSOLE_", jconsole.getBounds());
1368 jconsole.stopConsole();
1372 storeLastKnownDimensions("JALVIEW_RSS_WINDOW_", jvnews.getBounds());
1375 if (dialogExecutor != null)
1377 dialogExecutor.shutdownNow();
1379 closeAll_actionPerformed(null);
1381 if (groovyConsole != null)
1383 // suppress a possible repeat prompt to save script
1384 groovyConsole.setDirty(false);
1385 groovyConsole.exit();
1390 private void storeLastKnownDimensions(String string, Rectangle jc)
1392 jalview.bin.Cache.log.debug("Storing last known dimensions for "
1393 + string + ": x:" + jc.x + " y:" + jc.y + " width:" + jc.width
1394 + " height:" + jc.height);
1396 jalview.bin.Cache.setProperty(string + "SCREEN_X", jc.x + "");
1397 jalview.bin.Cache.setProperty(string + "SCREEN_Y", jc.y + "");
1398 jalview.bin.Cache.setProperty(string + "SCREEN_WIDTH", jc.width + "");
1399 jalview.bin.Cache.setProperty(string + "SCREEN_HEIGHT", jc.height + "");
1409 public void aboutMenuItem_actionPerformed(ActionEvent e)
1411 // StringBuffer message = getAboutMessage(false);
1412 // JvOptionPane.showInternalMessageDialog(Desktop.getDesktop(),
1414 // message.toString(), "About Jalview", JvOptionPane.INFORMATION_MESSAGE);
1415 new Thread(new Runnable()
1420 new SplashScreen(true);
1425 public StringBuffer getAboutMessage(boolean shortv)
1427 StringBuffer message = new StringBuffer();
1428 message.append("<html>");
1431 message.append("<h1><strong>Version: "
1432 + jalview.bin.Cache.getProperty("VERSION")
1433 + "</strong></h1>");
1434 message.append("<strong>Last Updated: <em>"
1435 + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown")
1436 + "</em></strong>");
1442 message.append("<strong>Version "
1443 + jalview.bin.Cache.getProperty("VERSION")
1444 + "; last updated: "
1445 + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown"));
1448 if (jalview.bin.Cache.getDefault("LATEST_VERSION", "Checking")
1449 .equals("Checking"))
1451 message.append("<br>...Checking latest version...</br>");
1453 else if (!jalview.bin.Cache.getDefault("LATEST_VERSION", "Checking")
1454 .equals(jalview.bin.Cache.getProperty("VERSION")))
1456 boolean red = false;
1457 if (jalview.bin.Cache.getProperty("VERSION").toLowerCase()
1458 .indexOf("automated build") == -1)
1461 // Displayed when code version and jnlp version do not match and code
1462 // version is not a development build
1463 message.append("<div style=\"color: #FF0000;font-style: bold;\">");
1466 message.append("<br>!! Version "
1467 + jalview.bin.Cache.getDefault("LATEST_VERSION",
1469 + " is available for download from "
1470 + jalview.bin.Cache.getDefault("www.jalview.org",
1471 "http://www.jalview.org")
1475 message.append("</div>");
1478 message.append("<br>Authors: " + jalview.bin.Cache.getDefault(
1480 "The Jalview Authors (See AUTHORS file for current list)")
1481 + "<br><br>Development managed by The Barton Group, University of Dundee, Scotland, UK.<br>"
1482 + "<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"
1483 + "<br><br>If you use Jalview, please cite:"
1484 + "<br>Waterhouse, A.M., Procter, J.B., Martin, D.M.A, Clamp, M. and Barton, G. J. (2009)"
1485 + "<br>Jalview Version 2 - a multiple sequence alignment editor and analysis workbench"
1486 + "<br>Bioinformatics doi: 10.1093/bioinformatics/btp033"
1492 * Action on requesting Help documentation
1495 public void documentationMenuItem_actionPerformed()
1499 if (Platform.isJS())
1501 BrowserLauncher.openURL("http://www.jalview.org/help.html");
1510 Help.showHelpWindow();
1512 } catch (Exception ex)
1514 System.err.println("Error opening help: " + ex.getMessage());
1519 public void closeAll_actionPerformed(ActionEvent e)
1521 if (desktopPane == null)
1525 // TODO show a progress bar while closing?
1526 JInternalFrame[] frames = desktopPane.getAllFrames();
1527 for (int i = 0; i < frames.length; i++)
1531 frames[i].setClosed(true);
1532 } catch (java.beans.PropertyVetoException ex)
1536 Jalview.setCurrentAlignFrame(null);
1537 System.out.println("ALL CLOSED");
1538 if (v_client != null)
1540 // TODO clear binding to vamsas document objects on close_all
1544 * reset state of singleton objects as appropriate (clear down session state
1545 * when all windows are closed)
1547 getStructureSelectionManager().resetAll();
1551 public void raiseRelated_actionPerformed(ActionEvent e)
1553 reorderAssociatedWindows(false, false);
1557 public void minimizeAssociated_actionPerformed(ActionEvent e)
1559 reorderAssociatedWindows(true, false);
1562 void closeAssociatedWindows()
1564 reorderAssociatedWindows(false, true);
1570 * @seejalview.jbgui.GDesktop#garbageCollect_actionPerformed(java.awt.event.
1574 protected void garbageCollect_actionPerformed(ActionEvent e)
1576 // We simply collect the garbage
1577 jalview.bin.Cache.log.debug("Collecting garbage...");
1579 jalview.bin.Cache.log.debug("Finished garbage collection.");
1586 * jalview.jbgui.GDesktop#showMemusage_actionPerformed(java.awt.event.ActionEvent
1590 protected void showMemusage_actionPerformed(ActionEvent e)
1592 getDesktopPane().showMemoryUsage(showMemusage.isSelected());
1599 * jalview.jbgui.GDesktop#showConsole_actionPerformed(java.awt.event.ActionEvent
1603 protected void showConsole_actionPerformed(ActionEvent e)
1605 showConsole(showConsole.isSelected());
1608 Console jconsole = null;
1611 * control whether the java console is visible or not
1615 void showConsole(boolean selected)
1617 // TODO: decide if we should update properties file
1618 if (jconsole != null) // BH 2018
1620 showConsole.setSelected(selected);
1621 Cache.setProperty("SHOW_JAVA_CONSOLE",
1622 Boolean.valueOf(selected).toString());
1623 jconsole.setVisible(selected);
1627 void reorderAssociatedWindows(boolean minimize, boolean close)
1629 JInternalFrame[] frames = getDesktopPane().getAllFrames();
1630 if (frames == null || frames.length < 1)
1635 AlignmentViewport source = null, target = null;
1636 if (frames[0] instanceof AlignFrame)
1638 source = ((AlignFrame) frames[0]).getCurrentView();
1640 else if (frames[0] instanceof TreePanel)
1642 source = ((TreePanel) frames[0]).getViewPort();
1644 else if (frames[0] instanceof PCAPanel)
1646 source = ((PCAPanel) frames[0]).av;
1648 else if (frames[0].getContentPane() instanceof PairwiseAlignPanel)
1650 source = ((PairwiseAlignPanel) frames[0].getContentPane()).av;
1655 for (int i = 0; i < frames.length; i++)
1658 if (frames[i] == null)
1662 if (frames[i] instanceof AlignFrame)
1664 target = ((AlignFrame) frames[i]).getCurrentView();
1666 else if (frames[i] instanceof TreePanel)
1668 target = ((TreePanel) frames[i]).getViewPort();
1670 else if (frames[i] instanceof PCAPanel)
1672 target = ((PCAPanel) frames[i]).av;
1674 else if (frames[i].getContentPane() instanceof PairwiseAlignPanel)
1676 target = ((PairwiseAlignPanel) frames[i].getContentPane()).av;
1679 if (source == target)
1685 frames[i].setClosed(true);
1689 frames[i].setIcon(minimize);
1692 frames[i].toFront();
1696 } catch (java.beans.PropertyVetoException ex)
1711 protected void preferences_actionPerformed(ActionEvent e)
1717 * Prompts the user to choose a file and then saves the Jalview state as a
1718 * Jalview project file
1721 public void saveState_actionPerformed()
1723 saveState_actionPerformed(false);
1726 public void saveState_actionPerformed(boolean saveAs)
1728 java.io.File projectFile = getProjectFile();
1729 // autoSave indicates we already have a file and don't need to ask
1730 boolean autoSave = projectFile != null && !saveAs
1731 && BackupFiles.getEnabled();
1733 // System.out.println("autoSave="+autoSave+", projectFile='"+projectFile+"',
1734 // saveAs="+saveAs+", Backups
1735 // "+(BackupFiles.getEnabled()?"enabled":"disabled"));
1737 boolean approveSave = false;
1740 JalviewFileChooser chooser = new JalviewFileChooser("jvp",
1743 chooser.setFileView(new JalviewFileView());
1744 chooser.setDialogTitle(MessageManager.getString("label.save_state"));
1746 int value = chooser.showSaveDialog(this);
1748 if (value == JalviewFileChooser.APPROVE_OPTION)
1750 projectFile = chooser.getSelectedFile();
1751 setProjectFile(projectFile);
1756 if (approveSave || autoSave)
1758 final Desktop me = this;
1759 final java.io.File chosenFile = projectFile;
1760 new Thread(new Runnable()
1765 // TODO: refactor to Jalview desktop session controller action.
1766 setProgressBar(MessageManager.formatMessage(
1767 "label.saving_jalview_project", new Object[]
1768 { chosenFile.getName() }), chosenFile.hashCode());
1769 jalview.bin.Cache.setProperty("LAST_DIRECTORY",
1770 chosenFile.getParent());
1771 // TODO catch and handle errors for savestate
1772 // TODO prevent user from messing with the Desktop whilst we're saving
1775 boolean doBackup = BackupFiles.getEnabled();
1776 BackupFiles backupfiles = doBackup ? new BackupFiles(chosenFile) : null;
1778 new Jalview2XML().saveState(doBackup ? backupfiles.getTempFile() : chosenFile);
1782 backupfiles.setWriteSuccess(true);
1783 backupfiles.rollBackupsAndRenameTempFile();
1785 } catch (OutOfMemoryError oom)
1787 new OOMWarning("Whilst saving current state to "
1788 + chosenFile.getName(), oom);
1789 } catch (Exception ex)
1791 Cache.log.error("Problems whilst trying to save to "
1792 + chosenFile.getName(), ex);
1793 JvOptionPane.showMessageDialog(me,
1794 MessageManager.formatMessage(
1795 "label.error_whilst_saving_current_state_to",
1797 { chosenFile.getName() }),
1798 MessageManager.getString("label.couldnt_save_project"),
1799 JvOptionPane.WARNING_MESSAGE);
1801 setProgressBar(null, chosenFile.hashCode());
1808 public void saveAsState_actionPerformed(ActionEvent e)
1810 saveState_actionPerformed(true);
1813 protected void setProjectFile(File choice)
1815 this.projectFile = choice;
1818 public File getProjectFile()
1820 return this.projectFile;
1824 * Shows a file chooser dialog and tries to read in the selected file as a
1828 public void loadState_actionPerformed()
1830 final String[] suffix = new String[] { "jvp", "jar" };
1831 final String[] desc = new String[] { "Jalview Project",
1832 "Jalview Project (old)" };
1833 JalviewFileChooser chooser = new JalviewFileChooser(
1834 Cache.getProperty("LAST_DIRECTORY"), suffix, desc,
1835 "Jalview Project", true, true); // last two booleans: allFiles,
1837 chooser.setFileView(new JalviewFileView());
1838 chooser.setDialogTitle(MessageManager.getString("label.restore_state"));
1839 chooser.setResponseHandler(0, new Runnable()
1844 File selectedFile = chooser.getSelectedFile();
1845 setProjectFile(selectedFile);
1846 String choice = selectedFile.getAbsolutePath();
1847 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1848 new Thread(new Runnable()
1855 new Jalview2XML().loadJalviewAlign(choice);
1856 } catch (OutOfMemoryError oom)
1858 new OOMWarning("Whilst loading project from " + choice, oom);
1859 } catch (Exception ex)
1862 "Problems whilst loading project from " + choice, ex);
1863 JvOptionPane.showMessageDialog(Desktop.getDesktopPane(),
1864 MessageManager.formatMessage(
1865 "label.error_whilst_loading_project_from",
1868 MessageManager.getString("label.couldnt_load_project"),
1869 JvOptionPane.WARNING_MESSAGE);
1876 chooser.showOpenDialog(this);
1880 public void inputSequence_actionPerformed(ActionEvent e)
1882 new SequenceFetcher(this);
1885 JPanel progressPanel;
1887 ArrayList<JPanel> fileLoadingPanels = new ArrayList<>();
1889 public void startLoading(final Object fileName)
1891 if (fileLoadingCount == 0)
1893 fileLoadingPanels.add(addProgressPanel(MessageManager
1894 .formatMessage("label.loading_file", new Object[]
1900 private JPanel addProgressPanel(String string)
1902 if (progressPanel == null)
1904 progressPanel = new JPanel(new GridLayout(1, 1));
1905 totalProgressCount = 0;
1906 getContentPane().add(progressPanel, BorderLayout.SOUTH);
1908 JPanel thisprogress = new JPanel(new BorderLayout(10, 5));
1909 JProgressBar progressBar = new JProgressBar();
1910 progressBar.setIndeterminate(true);
1912 thisprogress.add(new JLabel(string), BorderLayout.WEST);
1914 thisprogress.add(progressBar, BorderLayout.CENTER);
1915 progressPanel.add(thisprogress);
1916 ((GridLayout) progressPanel.getLayout()).setRows(
1917 ((GridLayout) progressPanel.getLayout()).getRows() + 1);
1918 ++totalProgressCount;
1920 return thisprogress;
1923 int totalProgressCount = 0;
1925 private void removeProgressPanel(JPanel progbar)
1927 if (progressPanel != null)
1929 synchronized (progressPanel)
1931 progressPanel.remove(progbar);
1932 GridLayout gl = (GridLayout) progressPanel.getLayout();
1933 gl.setRows(gl.getRows() - 1);
1934 if (--totalProgressCount < 1)
1936 this.getContentPane().remove(progressPanel);
1937 progressPanel = null;
1944 public void stopLoading()
1947 if (fileLoadingCount < 1)
1949 while (fileLoadingPanels.size() > 0)
1951 removeProgressPanel(fileLoadingPanels.remove(0));
1953 fileLoadingPanels.clear();
1954 fileLoadingCount = 0;
1959 public static int getViewCount(String alignmentId)
1961 AlignmentViewport[] aps = getViewports(alignmentId);
1962 return (aps == null) ? 0 : aps.length;
1967 * @param alignmentId
1968 * - if null, all sets are returned
1969 * @return all AlignmentPanels concerning the alignmentId sequence set
1971 public static AlignmentPanel[] getAlignmentPanels(String alignmentId)
1973 if (Desktop.getDesktopPane() == null)
1975 // no frames created and in headless mode
1976 // TODO: verify that frames are recoverable when in headless mode
1979 List<AlignmentPanel> aps = new ArrayList<>();
1980 AlignFrame[] frames = getAlignFrames();
1985 for (AlignFrame af : frames)
1987 for (AlignmentPanel ap : af.alignPanels)
1989 if (alignmentId == null
1990 || alignmentId.equals(ap.av.getSequenceSetId()))
1996 if (aps.size() == 0)
2000 AlignmentPanel[] vap = aps.toArray(new AlignmentPanel[aps.size()]);
2005 * get all the viewports on an alignment.
2007 * @param sequenceSetId
2008 * unique alignment id (may be null - all viewports returned in that
2010 * @return all viewports on the alignment bound to sequenceSetId
2012 public static AlignmentViewport[] getViewports(String sequenceSetId)
2014 List<AlignmentViewport> viewp = new ArrayList<>();
2015 if (getDesktopPane() != null)
2017 AlignFrame[] frames = Desktop.getAlignFrames();
2019 for (AlignFrame afr : frames)
2021 if (sequenceSetId == null || afr.getViewport().getSequenceSetId()
2022 .equals(sequenceSetId))
2024 if (afr.alignPanels != null)
2026 for (AlignmentPanel ap : afr.alignPanels)
2028 if (sequenceSetId == null
2029 || sequenceSetId.equals(ap.av.getSequenceSetId()))
2037 viewp.add(afr.getViewport());
2041 if (viewp.size() > 0)
2043 return viewp.toArray(new AlignmentViewport[viewp.size()]);
2050 * Explode the views in the given frame into separate AlignFrame
2054 public static void explodeViews(AlignFrame af)
2056 int size = af.alignPanels.size();
2062 for (int i = 0; i < size; i++)
2064 AlignmentPanel ap = af.alignPanels.get(i);
2065 AlignFrame newaf = new AlignFrame(ap);
2068 * Restore the view's last exploded frame geometry if known. Multiple
2069 * views from one exploded frame share and restore the same (frame)
2070 * position and size.
2072 Rectangle geometry = ap.av.getExplodedGeometry();
2073 if (geometry != null)
2075 newaf.setBounds(geometry);
2078 ap.av.setGatherViewsHere(false);
2080 addInternalFrame(newaf, af.getTitle(), AlignFrame.DEFAULT_WIDTH,
2081 AlignFrame.DEFAULT_HEIGHT);
2084 af.alignPanels.clear();
2085 af.closeMenuItem_actionPerformed(true);
2090 * Gather expanded views (separate AlignFrame's) with the same sequence set
2091 * identifier back in to this frame as additional views, and close the expanded
2092 * views. Note the expanded frames may themselves have multiple views. We take
2097 public void gatherViews(AlignFrame source)
2099 source.viewport.setGatherViewsHere(true);
2100 source.viewport.setExplodedGeometry(source.getBounds());
2101 JInternalFrame[] frames = getAllFrames();
2102 String viewId = source.viewport.getSequenceSetId();
2104 for (int t = 0; t < frames.length; t++)
2106 if (frames[t] instanceof AlignFrame && frames[t] != source)
2108 AlignFrame af = (AlignFrame) frames[t];
2109 boolean gatherThis = false;
2110 for (int a = 0; a < af.alignPanels.size(); a++)
2112 AlignmentPanel ap = af.alignPanels.get(a);
2113 if (viewId.equals(ap.av.getSequenceSetId()))
2116 ap.av.setGatherViewsHere(false);
2117 ap.av.setExplodedGeometry(af.getBounds());
2118 source.addAlignmentPanel(ap, false);
2124 af.alignPanels.clear();
2125 af.closeMenuItem_actionPerformed(true);
2132 jalview.gui.VamsasApplication v_client = null;
2135 public void vamsasImport_actionPerformed(ActionEvent e)
2137 // TODO: JAL-3048 not needed for Jalview-JS
2139 if (v_client == null)
2141 // Load and try to start a session.
2142 JalviewFileChooser chooser = new JalviewFileChooser(
2143 jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
2145 chooser.setFileView(new JalviewFileView());
2146 chooser.setDialogTitle(
2147 MessageManager.getString("label.open_saved_vamsas_session"));
2148 chooser.setToolTipText(MessageManager.getString(
2149 "label.select_vamsas_session_opened_as_new_vamsas_session"));
2151 int value = chooser.showOpenDialog(this);
2153 if (value == JalviewFileChooser.APPROVE_OPTION)
2155 String fle = chooser.getSelectedFile().toString();
2156 if (!vamsasImport(chooser.getSelectedFile()))
2158 JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
2159 MessageManager.formatMessage(
2160 "label.couldnt_import_as_vamsas_session",
2164 .getString("label.vamsas_document_import_failed"),
2165 JvOptionPane.ERROR_MESSAGE);
2171 jalview.bin.Cache.log.error(
2172 "Implementation error - load session from a running session is not supported.");
2177 * import file into a new vamsas session (uses jalview.gui.VamsasApplication)
2180 * @return true if import was a success and a session was started.
2182 public boolean vamsasImport(URL url)
2184 // TODO: create progress bar
2185 if (v_client != null)
2188 jalview.bin.Cache.log.error(
2189 "Implementation error - load session from a running session is not supported.");
2195 // copy the URL content to a temporary local file
2196 // TODO: be a bit cleverer here with nio (?!)
2197 File file = File.createTempFile("vdocfromurl", ".vdj");
2198 FileOutputStream fos = new FileOutputStream(file);
2199 BufferedInputStream bis = new BufferedInputStream(url.openStream());
2200 byte[] buffer = new byte[2048];
2202 while ((ln = bis.read(buffer)) > -1)
2204 fos.write(buffer, 0, ln);
2208 v_client = new jalview.gui.VamsasApplication(this, file,
2209 url.toExternalForm());
2210 } catch (Exception ex)
2212 jalview.bin.Cache.log.error(
2213 "Failed to create new vamsas session from contents of URL "
2218 setupVamsasConnectedGui();
2219 v_client.initial_update(); // TODO: thread ?
2220 return v_client.inSession();
2224 * import file into a new vamsas session (uses jalview.gui.VamsasApplication)
2227 * @return true if import was a success and a session was started.
2229 public boolean vamsasImport(File file)
2231 if (v_client != null)
2234 jalview.bin.Cache.log.error(
2235 "Implementation error - load session from a running session is not supported.");
2239 setProgressBar(MessageManager.formatMessage(
2240 "status.importing_vamsas_session_from", new Object[]
2241 { file.getName() }), file.hashCode());
2244 v_client = new jalview.gui.VamsasApplication(this, file, null);
2245 } catch (Exception ex)
2247 setProgressBar(MessageManager.formatMessage(
2248 "status.importing_vamsas_session_from", new Object[]
2249 { file.getName() }), file.hashCode());
2250 jalview.bin.Cache.log.error(
2251 "New vamsas session from existing session file failed:", ex);
2254 setupVamsasConnectedGui();
2255 v_client.initial_update(); // TODO: thread ?
2256 setProgressBar(MessageManager.formatMessage(
2257 "status.importing_vamsas_session_from", new Object[]
2258 { file.getName() }), file.hashCode());
2259 return v_client.inSession();
2262 public boolean joinVamsasSession(String mysesid)
2264 if (v_client != null)
2266 throw new Error(MessageManager
2267 .getString("error.try_join_vamsas_session_another"));
2269 if (mysesid == null)
2272 MessageManager.getString("error.invalid_vamsas_session_id"));
2274 v_client = new VamsasApplication(this, mysesid);
2275 setupVamsasConnectedGui();
2276 v_client.initial_update();
2277 return (v_client.inSession());
2281 public void vamsasStart_actionPerformed(ActionEvent e)
2283 if (v_client == null)
2286 // we just start a default session for moment.
2288 * JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache.
2289 * getProperty("LAST_DIRECTORY"));
2291 * chooser.setFileView(new JalviewFileView());
2292 * chooser.setDialogTitle("Load Vamsas file");
2293 * chooser.setToolTipText("Import");
2295 * int value = chooser.showOpenDialog(this);
2297 * if (value == JalviewFileChooser.APPROVE_OPTION) { v_client = new
2298 * jalview.gui.VamsasApplication(this, chooser.getSelectedFile());
2300 v_client = new VamsasApplication(this);
2301 setupVamsasConnectedGui();
2302 v_client.initial_update(); // TODO: thread ?
2306 // store current data in session.
2307 v_client.push_update(); // TODO: thread
2311 protected void setupVamsasConnectedGui()
2313 vamsasStart.setText(MessageManager.getString("label.session_update"));
2314 vamsasSave.setVisible(true);
2315 vamsasStop.setVisible(true);
2316 vamsasImport.setVisible(false); // Document import to existing session is
2317 // not possible for vamsas-client-1.0.
2320 protected void setupVamsasDisconnectedGui()
2322 vamsasSave.setVisible(false);
2323 vamsasStop.setVisible(false);
2324 vamsasImport.setVisible(true);
2326 .setText(MessageManager.getString("label.new_vamsas_session"));
2330 public void vamsasStop_actionPerformed(ActionEvent e)
2332 if (v_client != null)
2334 v_client.end_session();
2336 setupVamsasDisconnectedGui();
2340 protected void buildVamsasStMenu()
2342 if (v_client == null)
2344 String[] sess = null;
2347 sess = VamsasApplication.getSessionList();
2348 } catch (Exception e)
2350 jalview.bin.Cache.log.warn("Problem getting current sessions list.",
2356 jalview.bin.Cache.log.debug(
2357 "Got current sessions list: " + sess.length + " entries.");
2358 VamsasStMenu.removeAll();
2359 for (int i = 0; i < sess.length; i++)
2361 JMenuItem sessit = new JMenuItem();
2362 sessit.setText(sess[i]);
2363 sessit.setToolTipText(MessageManager
2364 .formatMessage("label.connect_to_session", new Object[]
2366 final Desktop dsktp = this;
2367 final String mysesid = sess[i];
2368 sessit.addActionListener(new ActionListener()
2372 public void actionPerformed(ActionEvent e)
2374 if (dsktp.v_client == null)
2376 Thread rthr = new Thread(new Runnable()
2382 dsktp.v_client = new VamsasApplication(dsktp, mysesid);
2383 dsktp.setupVamsasConnectedGui();
2384 dsktp.v_client.initial_update();
2392 VamsasStMenu.add(sessit);
2394 // don't show an empty menu.
2395 VamsasStMenu.setVisible(sess.length > 0);
2400 jalview.bin.Cache.log.debug("No current vamsas sessions.");
2401 VamsasStMenu.removeAll();
2402 VamsasStMenu.setVisible(false);
2407 // Not interested in the content. Just hide ourselves.
2408 VamsasStMenu.setVisible(false);
2413 public void vamsasSave_actionPerformed(ActionEvent e)
2415 // TODO: JAL-3048 not needed for Jalview-JS
2417 if (v_client != null)
2419 // TODO: VAMSAS DOCUMENT EXTENSION is VDJ
2420 JalviewFileChooser chooser = new JalviewFileChooser("vdj",
2423 chooser.setFileView(new JalviewFileView());
2424 chooser.setDialogTitle(MessageManager
2425 .getString("label.save_vamsas_document_archive"));
2427 int value = chooser.showSaveDialog(this);
2429 if (value == JalviewFileChooser.APPROVE_OPTION)
2431 java.io.File choice = chooser.getSelectedFile();
2432 JPanel progpanel = addProgressPanel(MessageManager
2433 .formatMessage("label.saving_vamsas_doc", new Object[]
2434 { choice.getName() }));
2435 Cache.setProperty("LAST_DIRECTORY", choice.getParent());
2436 String warnmsg = null;
2437 String warnttl = null;
2440 v_client.vclient.storeDocument(choice);
2443 warnttl = "Serious Problem saving Vamsas Document";
2444 warnmsg = ex.toString();
2445 jalview.bin.Cache.log
2446 .error("Error Whilst saving document to " + choice, ex);
2448 } catch (Exception ex)
2450 warnttl = "Problem saving Vamsas Document.";
2451 warnmsg = ex.toString();
2452 jalview.bin.Cache.log.warn(
2453 "Exception Whilst saving document to " + choice, ex);
2456 removeProgressPanel(progpanel);
2457 if (warnmsg != null)
2459 JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
2461 warnmsg, warnttl, JvOptionPane.ERROR_MESSAGE);
2467 JPanel vamUpdate = null;
2470 * hide vamsas user gui bits when a vamsas document event is being handled.
2473 * true to hide gui, false to reveal gui
2475 public void setVamsasUpdate(boolean b)
2477 Cache.log.debug("Setting gui for Vamsas update "
2478 + (b ? "in progress" : "finished"));
2480 if (vamUpdate != null)
2482 this.removeProgressPanel(vamUpdate);
2486 vamUpdate = this.addProgressPanel(
2487 MessageManager.getString("label.updating_vamsas_session"));
2489 vamsasStart.setVisible(!b);
2490 vamsasStop.setVisible(!b);
2491 vamsasSave.setVisible(!b);
2494 public JInternalFrame[] getAllFrames()
2496 return desktopPane.getAllFrames();
2500 * Checks the given url to see if it gives a response indicating that the user
2501 * should be informed of a new questionnaire.
2505 public void checkForQuestionnaire(String url)
2507 UserQuestionnaireCheck jvq = new UserQuestionnaireCheck(url);
2508 // javax.swing.SwingUtilities.invokeLater(jvq);
2509 new Thread(jvq).start();
2512 public void checkURLLinks()
2514 // Thread off the URL link checker
2515 addDialogThread(new Runnable()
2520 if (Cache.getDefault("CHECKURLLINKS", true))
2522 // check what the actual links are - if it's just the default don't
2523 // bother with the warning
2524 List<String> links = Preferences.sequenceUrlLinks
2527 // only need to check links if there is one with a
2528 // SEQUENCE_ID which is not the default EMBL_EBI link
2529 ListIterator<String> li = links.listIterator();
2530 boolean check = false;
2531 List<JLabel> urls = new ArrayList<>();
2532 while (li.hasNext())
2534 String link = li.next();
2535 if (link.contains(UrlConstants.SEQUENCE_ID)
2536 && !UrlConstants.isDefaultString(link))
2539 int barPos = link.indexOf("|");
2540 String urlMsg = barPos == -1 ? link
2541 : link.substring(0, barPos) + ": "
2542 + link.substring(barPos + 1);
2543 urls.add(new JLabel(urlMsg));
2551 // ask user to check in case URL links use old style tokens
2552 // ($SEQUENCE_ID$ for sequence id _or_ accession id)
2553 JPanel msgPanel = new JPanel();
2554 msgPanel.setLayout(new BoxLayout(msgPanel, BoxLayout.PAGE_AXIS));
2555 msgPanel.add(Box.createVerticalGlue());
2556 JLabel msg = new JLabel(MessageManager
2557 .getString("label.SEQUENCE_ID_for_DB_ACCESSION1"));
2558 JLabel msg2 = new JLabel(MessageManager
2559 .getString("label.SEQUENCE_ID_for_DB_ACCESSION2"));
2561 for (JLabel url : urls)
2567 final JCheckBox jcb = new JCheckBox(
2568 MessageManager.getString("label.do_not_display_again"));
2569 jcb.addActionListener(new ActionListener()
2572 public void actionPerformed(ActionEvent e)
2574 // update Cache settings for "don't show this again"
2575 boolean showWarningAgain = !jcb.isSelected();
2576 Cache.setProperty("CHECKURLLINKS",
2577 Boolean.valueOf(showWarningAgain).toString());
2582 JvOptionPane.showMessageDialog(desktopPane, msgPanel,
2584 .getString("label.SEQUENCE_ID_no_longer_used"),
2585 JvOptionPane.WARNING_MESSAGE);
2592 * Proxy class for JDesktopPane which optionally displays the current memory
2593 * usage and highlights the desktop area with a red bar if free memory runs low.
2597 public class MyDesktopPane extends JDesktopPane
2600 private static final float ONE_MB = 1048576f;
2602 boolean showMemoryUsage = false;
2606 java.text.NumberFormat df;
2608 float maxMemory, allocatedMemory, freeMemory, totalFreeMemory,
2611 public MyDesktopPane(boolean showMemoryUsage)
2613 showMemoryUsage(showMemoryUsage);
2616 public void showMemoryUsage(boolean showMemory)
2618 this.showMemoryUsage = showMemory;
2621 Thread worker = new Thread(this);
2627 public boolean isShowMemoryUsage()
2629 return showMemoryUsage;
2635 df = java.text.NumberFormat.getNumberInstance();
2636 df.setMaximumFractionDigits(2);
2637 runtime = Runtime.getRuntime();
2639 while (showMemoryUsage)
2643 maxMemory = runtime.maxMemory() / ONE_MB;
2644 allocatedMemory = runtime.totalMemory() / ONE_MB;
2645 freeMemory = runtime.freeMemory() / ONE_MB;
2646 totalFreeMemory = freeMemory + (maxMemory - allocatedMemory);
2648 percentUsage = (totalFreeMemory / maxMemory) * 100;
2650 // if (percentUsage < 20)
2652 // border1 = BorderFactory.createMatteBorder(12, 12, 12, 12,
2654 // instance.set.setBorder(border1);
2657 // sleep after showing usage
2659 } catch (Exception ex)
2661 ex.printStackTrace();
2667 public void paintComponent(Graphics g)
2669 if (showMemoryUsage && g != null && df != null)
2671 if (percentUsage < 20)
2673 g.setColor(Color.red);
2675 FontMetrics fm = g.getFontMetrics();
2678 g.drawString(MessageManager.formatMessage("label.memory_stats",
2680 { df.format(totalFreeMemory), df.format(maxMemory),
2681 df.format(percentUsage) }),
2682 10, getHeight() - fm.getHeight());
2689 * Accessor method to quickly get all the AlignmentFrames loaded.
2691 * @return an array of AlignFrame, or null if none found
2693 public static AlignFrame[] getAlignFrames()
2695 if (Jalview.isHeadlessMode())
2697 // Desktop.getDesktop() is null in headless mode
2698 return new AlignFrame[] { Jalview.getCurrentAlignFrame() };
2701 JInternalFrame[] frames = Desktop.getDesktopPane().getAllFrames();
2707 List<AlignFrame> avp = new ArrayList<>();
2709 for (int i = frames.length - 1; i > -1; i--)
2711 if (frames[i] instanceof AlignFrame)
2713 avp.add((AlignFrame) frames[i]);
2715 else if (frames[i] instanceof SplitFrame)
2718 * Also check for a split frame containing an AlignFrame
2720 GSplitFrame sf = (GSplitFrame) frames[i];
2721 if (sf.getTopFrame() instanceof AlignFrame)
2723 avp.add((AlignFrame) sf.getTopFrame());
2725 if (sf.getBottomFrame() instanceof AlignFrame)
2727 avp.add((AlignFrame) sf.getBottomFrame());
2731 if (avp.size() == 0)
2735 AlignFrame afs[] = avp.toArray(new AlignFrame[avp.size()]);
2740 * Returns an array of any AppJmol frames in the Desktop (or null if none).
2744 public GStructureViewer[] getJmols()
2746 JInternalFrame[] frames = Desktop.getDesktopPane().getAllFrames();
2752 List<GStructureViewer> avp = new ArrayList<>();
2754 for (int i = frames.length - 1; i > -1; i--)
2756 if (frames[i] instanceof AppJmol)
2758 GStructureViewer af = (GStructureViewer) frames[i];
2762 if (avp.size() == 0)
2766 GStructureViewer afs[] = avp.toArray(new GStructureViewer[avp.size()]);
2771 * Add Groovy Support to Jalview
2774 public void groovyShell_actionPerformed()
2778 openGroovyConsole();
2779 } catch (Exception ex)
2781 jalview.bin.Cache.log.error("Groovy Shell Creation failed.", ex);
2782 JvOptionPane.showInternalMessageDialog(desktopPane,
2784 MessageManager.getString("label.couldnt_create_groovy_shell"),
2785 MessageManager.getString("label.groovy_support_failed"),
2786 JvOptionPane.ERROR_MESSAGE);
2791 * Open the Groovy console
2793 private void openGroovyConsole()
2795 if (groovyConsole == null)
2797 groovyConsole = new groovy.ui.Console();
2798 groovyConsole.setVariable("Jalview", this);
2799 groovyConsole.run();
2802 * We allow only one console at a time, so that AlignFrame menu option
2803 * 'Calculate | Run Groovy script' is unambiguous.
2804 * Disable 'Groovy Console', and enable 'Run script', when the console is
2805 * opened, and the reverse when it is closed
2807 Window window = (Window) groovyConsole.getFrame();
2808 window.addWindowListener(new WindowAdapter()
2811 public void windowClosed(WindowEvent e)
2814 * rebind CMD-Q from Groovy Console to Jalview Quit
2817 enableExecuteGroovy(false);
2823 * show Groovy console window (after close and reopen)
2825 ((Window) groovyConsole.getFrame()).setVisible(true);
2828 * if we got this far, enable 'Run Groovy' in AlignFrame menus
2829 * and disable opening a second console
2831 enableExecuteGroovy(true);
2835 * Bind Ctrl/Cmd-Q to Quit - for reset as Groovy Console takes over this binding
2838 protected void addQuitHandler()
2840 getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
2841 .put(KeyStroke.getKeyStroke(KeyEvent.VK_Q,
2842 Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()),
2844 getRootPane().getActionMap().put("Quit", new AbstractAction()
2847 public void actionPerformed(ActionEvent e)
2855 * Enable or disable 'Run Groovy script' in AlignFrame calculate menus
2858 * true if Groovy console is open
2860 public void enableExecuteGroovy(boolean enabled)
2863 * disable opening a second Groovy console
2864 * (or re-enable when the console is closed)
2866 groovyShell.setEnabled(!enabled);
2868 AlignFrame[] alignFrames = getAlignFrames();
2869 if (alignFrames != null)
2871 for (AlignFrame af : alignFrames)
2873 af.setGroovyEnabled(enabled);
2879 * Progress bars managed by the IProgressIndicator method.
2881 private Hashtable<Long, JPanel> progressBars;
2883 private Hashtable<Long, IProgressIndicatorHandler> progressBarHandlers;
2888 * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
2891 public void setProgressBar(String message, long id)
2893 Platform.timeCheck("Desktop " + message, Platform.TIME_MARK);
2895 if (progressBars == null)
2897 progressBars = new Hashtable<>();
2898 progressBarHandlers = new Hashtable<>();
2901 if (progressBars.get(new Long(id)) != null)
2903 JPanel panel = progressBars.remove(new Long(id));
2904 if (progressBarHandlers.contains(new Long(id)))
2906 progressBarHandlers.remove(new Long(id));
2908 removeProgressPanel(panel);
2912 progressBars.put(new Long(id), addProgressPanel(message));
2919 * @see jalview.gui.IProgressIndicator#registerHandler(long,
2920 * jalview.gui.IProgressIndicatorHandler)
2923 public void registerHandler(final long id,
2924 final IProgressIndicatorHandler handler)
2926 if (progressBarHandlers == null
2927 || !progressBars.containsKey(new Long(id)))
2929 throw new Error(MessageManager.getString(
2930 "error.call_setprogressbar_before_registering_handler"));
2932 progressBarHandlers.put(new Long(id), handler);
2933 final JPanel progressPanel = progressBars.get(new Long(id));
2934 if (handler.canCancel())
2936 JButton cancel = new JButton(
2937 MessageManager.getString("action.cancel"));
2938 final IProgressIndicator us = this;
2939 cancel.addActionListener(new ActionListener()
2943 public void actionPerformed(ActionEvent e)
2945 handler.cancelActivity(id);
2946 us.setProgressBar(MessageManager
2947 .formatMessage("label.cancelled_params", new Object[]
2948 { ((JLabel) progressPanel.getComponent(0)).getText() }),
2952 progressPanel.add(cancel, BorderLayout.EAST);
2958 * @return true if any progress bars are still active
2961 public boolean operationInProgress()
2963 if (progressBars != null && progressBars.size() > 0)
2971 * This will return the first AlignFrame holding the given viewport instance. It
2972 * will break if there are more than one AlignFrames viewing a particular av.
2975 * @return alignFrame for viewport
2977 public static AlignFrame getAlignFrameFor(AlignViewportI viewport)
2979 if (getDesktopPane() != null)
2981 AlignmentPanel[] aps = getAlignmentPanels(
2982 viewport.getSequenceSetId());
2983 for (int panel = 0; aps != null && panel < aps.length; panel++)
2985 if (aps[panel] != null && aps[panel].av == viewport)
2987 return aps[panel].alignFrame;
2994 public VamsasApplication getVamsasApplication()
3001 * flag set if jalview GUI is being operated programmatically
3003 private boolean inBatchMode = false;
3006 * check if jalview GUI is being operated programmatically
3008 * @return inBatchMode
3010 public boolean isInBatchMode()
3016 * set flag if jalview GUI is being operated programmatically
3018 * @param inBatchMode
3020 public void setInBatchMode(boolean inBatchMode)
3022 this.inBatchMode = inBatchMode;
3025 public void startServiceDiscovery()
3027 startServiceDiscovery(false);
3030 public void startServiceDiscovery(boolean blocking)
3032 boolean alive = true;
3033 Thread t0 = null, t1 = null, t2 = null;
3034 // JAL-940 - JALVIEW 1 services are now being EOLed as of JABA 2.1 release
3037 // todo: changesupport handlers need to be transferred
3038 if (discoverer == null)
3040 discoverer = Discoverer.getInstance();
3041 // register PCS handler for getDesktop().
3042 discoverer.addPropertyChangeListener(changeSupport);
3044 // JAL-940 - disabled JWS1 service configuration - always start discoverer
3045 // until we phase out completely
3046 (t0 = new Thread(discoverer)).start();
3049 if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
3051 t2 = jalview.ws.jws2.Jws2Discoverer.getInstance()
3052 .startDiscoverer(changeSupport);
3056 // TODO: do rest service discovery
3065 } catch (Exception e)
3068 alive = (t1 != null && t1.isAlive()) || (t2 != null && t2.isAlive())
3069 || (t3 != null && t3.isAlive())
3070 || (t0 != null && t0.isAlive());
3076 * called to check if the service discovery process completed successfully.
3080 protected void JalviewServicesChanged(PropertyChangeEvent evt)
3082 if (evt.getNewValue() == null || evt.getNewValue() instanceof Vector)
3084 final String ermsg = jalview.ws.jws2.Jws2Discoverer.getInstance()
3085 .getErrorMessages();
3088 if (Cache.getDefault("SHOW_WSDISCOVERY_ERRORS", true))
3090 if (serviceChangedDialog == null)
3092 // only run if we aren't already displaying one of these.
3093 addDialogThread(serviceChangedDialog = new Runnable()
3100 * JalviewDialog jd =new JalviewDialog() {
3102 * @Override protected void cancelPressed() { // TODO
3103 * Auto-generated method stub
3105 * }@Override protected void okPressed() { // TODO
3106 * Auto-generated method stub
3108 * }@Override protected void raiseClosed() { // TODO
3109 * Auto-generated method stub
3111 * } }; jd.initDialogFrame(new
3112 * JLabel("<html><table width=\"450\"><tr><td>" + ermsg +
3113 * "<br/>It may be that you have invalid JABA URLs in your web service preferences,"
3114 * + " or mis-configured HTTP proxy settings.<br/>" +
3115 * "Check the <em>Connections</em> and <em>Web services</em> tab of the"
3117 * " Tools->Preferences dialog box to change them.</td></tr></table></html>"
3118 * ), true, true, "Web Service Configuration Problem", 450,
3121 * jd.waitForInput();
3123 JvOptionPane.showConfirmDialog(Desktop.getDesktopPane(),
3124 new JLabel("<html><table width=\"450\"><tr><td>"
3125 + ermsg + "</td></tr></table>"
3126 + "<p>It may be that you have invalid JABA URLs<br/>in your web service preferences,"
3127 + "<br>or as a command-line argument, or mis-configured HTTP proxy settings.</p>"
3128 + "<p>Check the <em>Connections</em> and <em>Web services</em> tab<br/>of the"
3129 + " Tools->Preferences dialog box to change them.</p></html>"),
3130 "Web Service Configuration Problem",
3131 JvOptionPane.DEFAULT_OPTION,
3132 JvOptionPane.ERROR_MESSAGE);
3133 serviceChangedDialog = null;
3142 "Errors reported by JABA discovery service. Check web services preferences.\n"
3149 Runnable serviceChangedDialog = null;
3152 * start a thread to open a URL in the configured browser. Pops up a warning
3153 * dialog to the user if there is an exception when calling out to the browser
3158 public static void showUrl(final String url)
3160 showUrl(url, Desktop.getInstance());
3164 * Like showUrl but allows progress handler to be specified
3168 * (null) or object implementing IProgressIndicator
3170 public static void showUrl(final String url,
3171 final IProgressIndicator progress)
3173 new Thread(new Runnable()
3180 if (progress != null)
3182 progress.setProgressBar(MessageManager
3183 .formatMessage("status.opening_params", new Object[]
3184 { url }), this.hashCode());
3186 BrowserLauncher.openURL(url);
3187 } catch (Exception ex)
3189 JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
3191 .getString("label.web_browser_not_found_unix"),
3192 MessageManager.getString("label.web_browser_not_found"),
3193 JvOptionPane.WARNING_MESSAGE);
3195 ex.printStackTrace();
3197 if (progress != null)
3199 progress.setProgressBar(null, this.hashCode());
3205 private WsParamSetManager wsparamManager = null;
3207 public static ParamManager getUserParameterStore()
3209 Desktop d = Desktop.getInstance();
3210 if (d.wsparamManager == null)
3212 d.wsparamManager = new WsParamSetManager();
3214 return d.wsparamManager;
3218 * static hyperlink handler proxy method for use by Jalview's internal windows
3222 public static void hyperlinkUpdate(HyperlinkEvent e)
3224 if (e.getEventType() == EventType.ACTIVATED)
3229 url = e.getURL().toString();
3230 Desktop.showUrl(url);
3231 } catch (Exception x)
3235 if (Cache.log != null)
3237 Cache.log.error("Couldn't handle string " + url + " as a URL.");
3242 "Couldn't handle string " + url + " as a URL.");
3245 // ignore any exceptions due to dud links.
3252 * single thread that handles display of dialogs to user.
3254 ExecutorService dialogExecutor = Executors.newSingleThreadExecutor();
3257 * flag indicating if dialogExecutor should try to acquire a permit
3259 volatile boolean dialogPause = true;
3264 java.util.concurrent.Semaphore block = new Semaphore(0);
3266 private groovy.ui.Console groovyConsole;
3268 public StructureViewer lastTargetedView;
3271 * add another dialog thread to the queue
3275 public void addDialogThread(final Runnable prompter)
3277 dialogExecutor.submit(new Runnable()
3287 } catch (InterruptedException x)
3297 SwingUtilities.invokeAndWait(prompter);
3298 } catch (Exception q)
3300 Cache.log.warn("Unexpected Exception in dialog thread.", q);
3306 public void startDialogQueue()
3308 // set the flag so we don't pause waiting for another permit and semaphore
3309 // the current task to begin
3310 dialogPause = false;
3315 * Outputs an image of the desktop to file in EPS format, after prompting the
3316 * user for choice of Text or Lineart character rendering (unless a preference
3317 * has been set). The file name is generated as
3320 * Jalview_snapshot_nnnnn.eps where nnnnn is the current timestamp in milliseconds
3324 protected void snapShotWindow_actionPerformed(ActionEvent e)
3326 // currently the menu option to do this is not shown
3329 int width = getWidth();
3330 int height = getHeight();
3332 "Jalview_snapshot_" + System.currentTimeMillis() + ".eps");
3333 ImageWriterI writer = new ImageWriterI()
3336 public void exportImage(Graphics g) throws Exception
3339 Cache.log.info("Successfully written snapshot to file "
3340 + of.getAbsolutePath());
3343 String title = "View of desktop";
3344 ImageExporter exporter = new ImageExporter(writer, null, TYPE.EPS,
3346 exporter.doExport(of, this, width, height, title);
3350 * Explode the views in the given SplitFrame into separate SplitFrame windows.
3351 * This respects (remembers) any previous 'exploded geometry' i.e. the size and
3352 * location last time the view was expanded (if any). However it does not
3353 * remember the split pane divider location - this is set to match the
3354 * 'exploding' frame.
3358 public void explodeViews(SplitFrame sf)
3360 AlignFrame oldTopFrame = (AlignFrame) sf.getTopFrame();
3361 AlignFrame oldBottomFrame = (AlignFrame) sf.getBottomFrame();
3362 List<? extends AlignmentViewPanel> topPanels = oldTopFrame
3364 List<? extends AlignmentViewPanel> bottomPanels = oldBottomFrame
3366 int viewCount = topPanels.size();
3373 * Processing in reverse order works, forwards order leaves the first panels
3374 * not visible. I don't know why!
3376 for (int i = viewCount - 1; i >= 0; i--)
3379 * Make new top and bottom frames. These take over the respective
3380 * AlignmentPanel objects, including their AlignmentViewports, so the
3381 * cdna/protein relationships between the viewports is carried over to the
3384 * explodedGeometry holds the (x, y) position of the previously exploded
3385 * SplitFrame, and the (width, height) of the AlignFrame component
3387 AlignmentPanel topPanel = (AlignmentPanel) topPanels.get(i);
3388 AlignFrame newTopFrame = new AlignFrame(topPanel);
3389 newTopFrame.setSize(oldTopFrame.getSize());
3390 newTopFrame.setVisible(true);
3391 Rectangle geometry = ((AlignViewport) topPanel.getAlignViewport())
3392 .getExplodedGeometry();
3393 if (geometry != null)
3395 newTopFrame.setSize(geometry.getSize());
3398 AlignmentPanel bottomPanel = (AlignmentPanel) bottomPanels.get(i);
3399 AlignFrame newBottomFrame = new AlignFrame(bottomPanel);
3400 newBottomFrame.setSize(oldBottomFrame.getSize());
3401 newBottomFrame.setVisible(true);
3402 geometry = ((AlignViewport) bottomPanel.getAlignViewport())
3403 .getExplodedGeometry();
3404 if (geometry != null)
3406 newBottomFrame.setSize(geometry.getSize());
3409 topPanel.av.setGatherViewsHere(false);
3410 bottomPanel.av.setGatherViewsHere(false);
3411 JInternalFrame splitFrame = new SplitFrame(newTopFrame,
3413 if (geometry != null)
3415 splitFrame.setLocation(geometry.getLocation());
3417 Desktop.addInternalFrame(splitFrame, sf.getTitle(), -1, -1);
3421 * Clear references to the panels (now relocated in the new SplitFrames)
3422 * before closing the old SplitFrame.
3425 bottomPanels.clear();
3430 * Gather expanded split frames, sharing the same pairs of sequence set ids,
3431 * back into the given SplitFrame as additional views. Note that the gathered
3432 * frames may themselves have multiple views.
3436 public void gatherViews(GSplitFrame source)
3439 * special handling of explodedGeometry for a view within a SplitFrame: - it
3440 * holds the (x, y) position of the enclosing SplitFrame, and the (width,
3441 * height) of the AlignFrame component
3443 AlignFrame myTopFrame = (AlignFrame) source.getTopFrame();
3444 AlignFrame myBottomFrame = (AlignFrame) source.getBottomFrame();
3445 myTopFrame.viewport.setExplodedGeometry(new Rectangle(source.getX(),
3446 source.getY(), myTopFrame.getWidth(), myTopFrame.getHeight()));
3447 myBottomFrame.viewport
3448 .setExplodedGeometry(new Rectangle(source.getX(), source.getY(),
3449 myBottomFrame.getWidth(), myBottomFrame.getHeight()));
3450 myTopFrame.viewport.setGatherViewsHere(true);
3451 myBottomFrame.viewport.setGatherViewsHere(true);
3452 String topViewId = myTopFrame.viewport.getSequenceSetId();
3453 String bottomViewId = myBottomFrame.viewport.getSequenceSetId();
3455 JInternalFrame[] frames = desktopPane.getAllFrames();
3456 for (JInternalFrame frame : frames)
3458 if (frame instanceof SplitFrame && frame != source)
3460 SplitFrame sf = (SplitFrame) frame;
3461 AlignFrame topFrame = (AlignFrame) sf.getTopFrame();
3462 AlignFrame bottomFrame = (AlignFrame) sf.getBottomFrame();
3463 boolean gatherThis = false;
3464 for (int a = 0; a < topFrame.alignPanels.size(); a++)
3466 AlignmentPanel topPanel = topFrame.alignPanels.get(a);
3467 AlignmentPanel bottomPanel = bottomFrame.alignPanels.get(a);
3468 if (topViewId.equals(topPanel.av.getSequenceSetId())
3469 && bottomViewId.equals(bottomPanel.av.getSequenceSetId()))
3472 topPanel.av.setGatherViewsHere(false);
3473 bottomPanel.av.setGatherViewsHere(false);
3474 topPanel.av.setExplodedGeometry(
3475 new Rectangle(sf.getLocation(), topFrame.getSize()));
3476 bottomPanel.av.setExplodedGeometry(
3477 new Rectangle(sf.getLocation(), bottomFrame.getSize()));
3478 myTopFrame.addAlignmentPanel(topPanel, false);
3479 myBottomFrame.addAlignmentPanel(bottomPanel, false);
3485 topFrame.getAlignPanels().clear();
3486 bottomFrame.getAlignPanels().clear();
3493 * The dust settles...give focus to the tab we did this from.
3495 myTopFrame.setDisplayedView(myTopFrame.alignPanel);
3498 public static groovy.ui.Console getGroovyConsole()
3500 Desktop desktop = Desktop.getInstance();
3501 return desktop == null ? null : desktop.groovyConsole;
3505 * handles the payload of a drag and drop event.
3507 * TODO refactor to desktop utilities class
3510 * - Data source strings extracted from the drop event
3512 * - protocol for each data source extracted from the drop event
3516 * - the payload from the drop event
3519 @SuppressWarnings("unchecked")
3520 public static void transferFromDropTarget(List<Object> files,
3521 List<DataSourceType> protocols, DropTargetDropEvent evt,
3522 Transferable t) throws Exception
3525 // BH 2018 changed List<String> to List<Object> to allow for File from SwingJS
3527 // DataFlavor[] flavors = t.getTransferDataFlavors();
3528 // for (int i = 0; i < flavors.length; i++) {
3529 // if (flavors[i].isFlavorJavaFileListType()) {
3530 // evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
3531 // List<File> list = (List<File>) t.getTransferData(flavors[i]);
3532 // for (int j = 0; j < list.size(); j++) {
3533 // File file = (File) list.get(j);
3534 // byte[] data = getDroppedFileBytes(file);
3535 // fileName.setText(file.getName() + " - " + data.length + " " +
3536 // evt.getLocation());
3537 // JTextArea target = (JTextArea) ((DropTarget) evt.getSource()).getComponent();
3538 // target.setText(new String(data));
3540 // dtde.dropComplete(true);
3545 DataFlavor uriListFlavor = new DataFlavor(
3546 "text/uri-list;class=java.lang.String"), urlFlavour = null;
3549 urlFlavour = new DataFlavor(
3550 "application/x-java-url; class=java.net.URL");
3551 } catch (ClassNotFoundException cfe)
3553 Cache.log.debug("Couldn't instantiate the URL dataflavor.", cfe);
3556 if (urlFlavour != null && t.isDataFlavorSupported(urlFlavour))
3561 java.net.URL url = (URL) t.getTransferData(urlFlavour);
3562 // nb: java 8 osx bug https://bugs.openjdk.java.net/browse/JDK-8156099
3563 // means url may be null.
3566 protocols.add(DataSourceType.URL);
3567 files.add(url.toString());
3568 Cache.log.debug("Drop handled as URL dataflavor "
3569 + files.get(files.size() - 1));
3574 if (Platform.isAMacAndNotJS())
3577 "Please ignore plist error - occurs due to problem with java 8 on OSX");
3580 } catch (Throwable ex)
3582 Cache.log.debug("URL drop handler failed.", ex);
3585 if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
3587 // Works on Windows and MacOSX
3588 Cache.log.debug("Drop handled as javaFileListFlavor");
3589 for (Object file : (List<Object>) t
3590 .getTransferData(DataFlavor.javaFileListFlavor))
3593 protocols.add(DataSourceType.FILE);
3598 // Unix like behaviour
3599 boolean added = false;
3601 if (t.isDataFlavorSupported(uriListFlavor))
3603 Cache.log.debug("Drop handled as uriListFlavor");
3604 // This is used by Unix drag system
3605 data = (String) t.getTransferData(uriListFlavor);
3609 // fallback to text: workaround - on OSX where there's a JVM bug
3610 Cache.log.debug("standard URIListFlavor failed. Trying text");
3611 // try text fallback
3612 DataFlavor textDf = new DataFlavor(
3613 "text/plain;class=java.lang.String");
3614 if (t.isDataFlavorSupported(textDf))
3616 data = (String) t.getTransferData(textDf);
3619 Cache.log.debug("Plain text drop content returned "
3620 + (data == null ? "Null - failed" : data));
3625 while (protocols.size() < files.size())
3627 Cache.log.debug("Adding missing FILE protocol for "
3628 + files.get(protocols.size()));
3629 protocols.add(DataSourceType.FILE);
3631 for (java.util.StringTokenizer st = new java.util.StringTokenizer(
3632 data, "\r\n"); st.hasMoreTokens();)
3635 String s = st.nextToken();
3636 if (s.startsWith("#"))
3638 // the line is a comment (as per the RFC 2483)
3641 java.net.URI uri = new java.net.URI(s);
3642 if (uri.getScheme().toLowerCase().startsWith("http"))
3644 protocols.add(DataSourceType.URL);
3645 files.add(uri.toString());
3649 // otherwise preserve old behaviour: catch all for file objects
3650 java.io.File file = new java.io.File(uri);
3651 protocols.add(DataSourceType.FILE);
3652 files.add(file.toString());
3657 if (Cache.log.isDebugEnabled())
3659 if (data == null || !added)
3662 if (t.getTransferDataFlavors() != null
3663 && t.getTransferDataFlavors().length > 0)
3666 "Couldn't resolve drop data. Here are the supported flavors:");
3667 for (DataFlavor fl : t.getTransferDataFlavors())
3670 "Supported transfer dataflavor: " + fl.toString());
3671 Object df = t.getTransferData(fl);
3674 Cache.log.debug("Retrieves: " + df);
3678 Cache.log.debug("Retrieved nothing");
3684 Cache.log.debug("Couldn't resolve dataflavor for drop: "
3690 if (Platform.isWindowsAndNotJS())
3692 Cache.log.debug("Scanning dropped content for Windows Link Files");
3694 // resolve any .lnk files in the file drop
3695 for (int f = 0; f < files.size(); f++)
3697 String source = files.get(f).toString().toLowerCase();
3698 if (protocols.get(f).equals(DataSourceType.FILE)
3699 && (source.endsWith(".lnk") || source.endsWith(".url")
3700 || source.endsWith(".site")))
3704 Object obj = files.get(f);
3705 File lf = (obj instanceof File ? (File) obj
3706 : new File((String) obj));
3707 // process link file to get a URL
3708 Cache.log.debug("Found potential link file: " + lf);
3709 WindowsShortcut wscfile = new WindowsShortcut(lf);
3710 String fullname = wscfile.getRealFilename();
3711 protocols.set(f, FormatAdapter.checkProtocol(fullname));
3712 files.set(f, fullname);
3713 Cache.log.debug("Parsed real filename " + fullname
3714 + " to extract protocol: " + protocols.get(f));
3715 } catch (Exception ex)
3718 "Couldn't parse " + files.get(f) + " as a link file.",
3727 * Sets the Preferences property for experimental features to True or False
3728 * depending on the state of the controlling menu item
3731 protected void showExperimental_actionPerformed(boolean selected)
3733 Cache.setProperty(EXPERIMENTAL_FEATURES, Boolean.toString(selected));
3737 * Answers a (possibly empty) list of any structure viewer frames (currently for
3738 * either Jmol or Chimera) which are currently open. This may optionally be
3739 * restricted to viewers of a specified class, or viewers linked to a specified
3743 * if not null, only return viewers linked to this panel
3744 * @param structureViewerClass
3745 * if not null, only return viewers of this class
3748 public List<StructureViewerBase> getStructureViewers(
3749 AlignmentPanel apanel,
3750 Class<? extends StructureViewerBase> structureViewerClass)
3752 List<StructureViewerBase> result = new ArrayList<>();
3753 JInternalFrame[] frames = getAllFrames();
3755 for (JInternalFrame frame : frames)
3757 if (frame instanceof StructureViewerBase)
3759 if (structureViewerClass == null
3760 || structureViewerClass.isInstance(frame))
3763 || ((StructureViewerBase) frame).isLinkedWith(apanel))
3765 result.add((StructureViewerBase) frame);