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 static jalview.util.UrlConstants.SEQUENCE_ID;
25 import jalview.api.AlignViewportI;
26 import jalview.api.AlignmentViewPanel;
27 import jalview.bin.Cache;
28 import jalview.bin.Jalview;
29 import jalview.gui.ImageExporter.ImageWriterI;
30 import jalview.io.BackupFiles;
31 import jalview.io.DataSourceType;
32 import jalview.io.FileFormat;
33 import jalview.io.FileFormatException;
34 import jalview.io.FileFormatI;
35 import jalview.io.FileFormats;
36 import jalview.io.FileLoader;
37 import jalview.io.FormatAdapter;
38 import jalview.io.IdentifyFile;
39 import jalview.io.JalviewFileChooser;
40 import jalview.io.JalviewFileView;
41 import jalview.jbgui.GSplitFrame;
42 import jalview.jbgui.GStructureViewer;
43 import jalview.project.Jalview2XML;
44 import jalview.structure.StructureSelectionManager;
45 import jalview.urls.IdOrgSettings;
46 import jalview.util.BrowserLauncher;
47 import jalview.util.ImageMaker.TYPE;
48 import jalview.util.MessageManager;
49 import jalview.util.Platform;
50 import jalview.util.UrlConstants;
51 import jalview.viewmodel.AlignmentViewport;
52 import jalview.ws.params.ParamManager;
53 import jalview.ws.utils.UrlDownloadClient;
55 import java.awt.BorderLayout;
56 import java.awt.Color;
57 import java.awt.Dimension;
58 import java.awt.FontMetrics;
59 import java.awt.Graphics;
60 import java.awt.GridLayout;
61 import java.awt.Point;
62 import java.awt.Rectangle;
63 import java.awt.Toolkit;
64 import java.awt.Window;
65 import java.awt.datatransfer.Clipboard;
66 import java.awt.datatransfer.ClipboardOwner;
67 import java.awt.datatransfer.DataFlavor;
68 import java.awt.datatransfer.Transferable;
69 import java.awt.dnd.DnDConstants;
70 import java.awt.dnd.DropTargetDragEvent;
71 import java.awt.dnd.DropTargetDropEvent;
72 import java.awt.dnd.DropTargetEvent;
73 import java.awt.dnd.DropTargetListener;
74 import java.awt.event.ActionEvent;
75 import java.awt.event.ActionListener;
76 import java.awt.event.InputEvent;
77 import java.awt.event.KeyEvent;
78 import java.awt.event.MouseAdapter;
79 import java.awt.event.MouseEvent;
80 import java.awt.event.WindowAdapter;
81 import java.awt.event.WindowEvent;
82 import java.beans.PropertyChangeEvent;
83 import java.beans.PropertyChangeListener;
84 import java.io.BufferedInputStream;
86 import java.io.FileOutputStream;
87 import java.io.IOException;
89 import java.util.ArrayList;
90 import java.util.Hashtable;
91 import java.util.List;
92 import java.util.ListIterator;
93 import java.util.Vector;
94 import java.util.concurrent.ExecutorService;
95 import java.util.concurrent.Executors;
96 import java.util.concurrent.Semaphore;
98 import javax.swing.AbstractAction;
99 import javax.swing.Action;
100 import javax.swing.ActionMap;
101 import javax.swing.Box;
102 import javax.swing.BoxLayout;
103 import javax.swing.DefaultDesktopManager;
104 import javax.swing.DesktopManager;
105 import javax.swing.InputMap;
106 import javax.swing.JButton;
107 import javax.swing.JCheckBox;
108 import javax.swing.JComboBox;
109 import javax.swing.JComponent;
110 import javax.swing.JDesktopPane;
111 import javax.swing.JFrame;
112 import javax.swing.JInternalFrame;
113 import javax.swing.JLabel;
114 import javax.swing.JMenuItem;
115 import javax.swing.JPanel;
116 import javax.swing.JPopupMenu;
117 import javax.swing.JProgressBar;
118 import javax.swing.JTextField;
119 import javax.swing.KeyStroke;
120 import javax.swing.SwingUtilities;
121 import javax.swing.event.HyperlinkEvent;
122 import javax.swing.event.HyperlinkEvent.EventType;
123 import javax.swing.event.InternalFrameAdapter;
124 import javax.swing.event.InternalFrameEvent;
125 import javax.swing.event.MenuEvent;
126 import javax.swing.event.MenuListener;
128 import org.stackoverflowusers.file.WindowsShortcut;
135 * @version $Revision: 1.155 $
137 public class Desktop extends jalview.jbgui.GDesktop
138 implements DropTargetListener, ClipboardOwner, IProgressIndicator,
139 jalview.api.StructureSelectionManagerProvider
141 private static int DEFAULT_MIN_WIDTH = 300;
143 private static int DEFAULT_MIN_HEIGHT = 250;
145 private static int ALIGN_FRAME_DEFAULT_MIN_WIDTH = 600;
147 private static int ALIGN_FRAME_DEFAULT_MIN_HEIGHT = 70;
149 private static final String EXPERIMENTAL_FEATURES = "EXPERIMENTAL_FEATURES";
151 private JalviewChangeSupport changeSupport = new JalviewChangeSupport();
154 * news reader - null if it was never started.
156 private BlogReader jvnews = null;
158 private File projectFile;
162 * @see jalview.gui.JalviewChangeSupport#addJalviewPropertyChangeListener(java.beans.PropertyChangeListener)
164 public void addJalviewPropertyChangeListener(
165 PropertyChangeListener listener)
167 changeSupport.addJalviewPropertyChangeListener(listener);
171 * @param propertyName
173 * @see jalview.gui.JalviewChangeSupport#addJalviewPropertyChangeListener(java.lang.String,
174 * java.beans.PropertyChangeListener)
176 public void addJalviewPropertyChangeListener(String propertyName,
177 PropertyChangeListener listener)
179 changeSupport.addJalviewPropertyChangeListener(propertyName, listener);
183 * @param propertyName
185 * @see jalview.gui.JalviewChangeSupport#removeJalviewPropertyChangeListener(java.lang.String,
186 * java.beans.PropertyChangeListener)
188 public void removeJalviewPropertyChangeListener(String propertyName,
189 PropertyChangeListener listener)
191 changeSupport.removeJalviewPropertyChangeListener(propertyName,
195 /** Singleton Desktop instance */
196 public static Desktop instance;
198 public static MyDesktopPane desktop;
200 public static MyDesktopPane getDesktop()
202 // BH 2018 could use currentThread() here as a reference to a
203 // Hashtable<Thread, MyDesktopPane> in JavaScript
207 static int openFrameCount = 0;
209 static final int xOffset = 30;
211 static final int yOffset = 30;
213 public static jalview.ws.jws1.Discoverer discoverer;
215 public static Object[] jalviewClipboard;
217 public static boolean internalCopy = false;
219 static int fileLoadingCount = 0;
221 class MyDesktopManager implements DesktopManager
224 private DesktopManager delegate;
226 public MyDesktopManager(DesktopManager delegate)
228 this.delegate = delegate;
232 public void activateFrame(JInternalFrame f)
236 delegate.activateFrame(f);
237 } catch (NullPointerException npe)
239 Point p = getMousePosition();
240 instance.showPasteMenu(p.x, p.y);
245 public void beginDraggingFrame(JComponent f)
247 delegate.beginDraggingFrame(f);
251 public void beginResizingFrame(JComponent f, int direction)
253 delegate.beginResizingFrame(f, direction);
257 public void closeFrame(JInternalFrame f)
259 delegate.closeFrame(f);
263 public void deactivateFrame(JInternalFrame f)
265 delegate.deactivateFrame(f);
269 public void deiconifyFrame(JInternalFrame f)
271 delegate.deiconifyFrame(f);
275 public void dragFrame(JComponent f, int newX, int newY)
281 delegate.dragFrame(f, newX, newY);
285 public void endDraggingFrame(JComponent f)
287 delegate.endDraggingFrame(f);
292 public void endResizingFrame(JComponent f)
294 delegate.endResizingFrame(f);
299 public void iconifyFrame(JInternalFrame f)
301 delegate.iconifyFrame(f);
305 public void maximizeFrame(JInternalFrame f)
307 delegate.maximizeFrame(f);
311 public void minimizeFrame(JInternalFrame f)
313 delegate.minimizeFrame(f);
317 public void openFrame(JInternalFrame f)
319 delegate.openFrame(f);
323 public void resizeFrame(JComponent f, int newX, int newY, int newWidth,
330 delegate.resizeFrame(f, newX, newY, newWidth, newHeight);
334 public void setBoundsForFrame(JComponent f, int newX, int newY,
335 int newWidth, int newHeight)
337 delegate.setBoundsForFrame(f, newX, newY, newWidth, newHeight);
340 // All other methods, simply delegate
345 * Creates a new Desktop object.
351 * A note to implementors. It is ESSENTIAL that any activities that might
352 * block are spawned off as threads rather than waited for during this
356 if (!Platform.isJS())
358 doVamsasClientCheck();
361 doConfigureStructurePrefs();
362 setTitle("Jalview " + jalview.bin.Cache.getProperty("VERSION"));
363 setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
364 boolean selmemusage = jalview.bin.Cache.getDefault("SHOW_MEMUSAGE",
366 boolean showjconsole = jalview.bin.Cache.getDefault("SHOW_JAVA_CONSOLE",
368 desktop = new MyDesktopPane(selmemusage);
370 showMemusage.setSelected(selmemusage);
371 desktop.setBackground(Color.white);
372 getContentPane().setLayout(new BorderLayout());
373 // alternate config - have scrollbars - see notes in JAL-153
374 // JScrollPane sp = new JScrollPane();
375 // sp.getViewport().setView(desktop);
376 // getContentPane().add(sp, BorderLayout.CENTER);
378 // BH 2018 - just an experiment to try unclipped JInternalFrames.
381 getRootPane().putClientProperty("swingjs.overflow.hidden", "false");
384 getContentPane().add(desktop, BorderLayout.CENTER);
385 desktop.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
387 // This line prevents Windows Look&Feel resizing all new windows to maximum
388 // if previous window was maximised
389 desktop.setDesktopManager(new MyDesktopManager(
390 (Platform.isWindowsAndNotJS() ? new DefaultDesktopManager()
391 : Platform.isAMacAndNotJS()
392 ? new AquaInternalFrameManager(
393 desktop.getDesktopManager())
394 : desktop.getDesktopManager())));
396 Rectangle dims = getLastKnownDimensions("");
403 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
404 int xPos = Math.max(5, (screenSize.width - 900) / 2);
405 int yPos = Math.max(5, (screenSize.height - 650) / 2);
406 setBounds(xPos, yPos, 900, 650);
409 if (!Platform.isJS())
417 jconsole = new Console(this, showjconsole);
418 // add essential build information
419 jconsole.setHeader("Jalview Version: "
420 + jalview.bin.Cache.getProperty("VERSION") + "\n"
421 + "Jalview Installation: "
422 + jalview.bin.Cache.getDefault("INSTALLATION", "unknown")
423 + "\n" + "Build Date: "
424 + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown") + "\n"
425 + "Java version: " + System.getProperty("java.version") + "\n"
426 + System.getProperty("os.arch") + " "
427 + System.getProperty("os.name") + " "
428 + System.getProperty("os.version"));
430 showConsole(showjconsole);
432 showNews.setVisible(false);
434 experimentalFeatures.setSelected(showExperimental());
436 getIdentifiersOrgData();
440 // Spawn a thread that shows the splashscreen
442 SwingUtilities.invokeLater(new Runnable()
451 // Thread off a new instance of the file chooser - this reduces the time
453 // takes to open it later on.
454 new Thread(new Runnable()
459 Cache.log.debug("Filechooser init thread started.");
460 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
461 JalviewFileChooser.forRead(Cache.getProperty("LAST_DIRECTORY"),
463 Cache.log.debug("Filechooser init thread finished.");
466 // Add the service change listener
467 changeSupport.addJalviewPropertyChangeListener("services",
468 new PropertyChangeListener()
472 public void propertyChange(PropertyChangeEvent evt)
474 Cache.log.debug("Firing service changed event for "
475 + evt.getNewValue());
476 JalviewServicesChanged(evt);
483 this.setDropTarget(new java.awt.dnd.DropTarget(desktop, this));
485 this.addWindowListener(new WindowAdapter()
488 public void windowClosing(WindowEvent evt)
495 this.addMouseListener(ma = new MouseAdapter()
498 public void mousePressed(MouseEvent evt)
500 if (evt.isPopupTrigger()) // Mac
502 showPasteMenu(evt.getX(), evt.getY());
507 public void mouseReleased(MouseEvent evt)
509 if (evt.isPopupTrigger()) // Windows
511 showPasteMenu(evt.getX(), evt.getY());
515 desktop.addMouseListener(ma);
520 * Answers true if user preferences to enable experimental features is True
525 public boolean showExperimental()
527 String experimental = Cache.getDefault(EXPERIMENTAL_FEATURES,
528 Boolean.FALSE.toString());
529 return Boolean.valueOf(experimental).booleanValue();
532 public void doConfigureStructurePrefs()
534 // configure services
535 StructureSelectionManager ssm = StructureSelectionManager
536 .getStructureSelectionManager(this);
537 if (jalview.bin.Cache.getDefault(Preferences.ADD_SS_ANN, true))
539 ssm.setAddTempFacAnnot(jalview.bin.Cache
540 .getDefault(Preferences.ADD_TEMPFACT_ANN, true));
541 ssm.setProcessSecondaryStructure(jalview.bin.Cache
542 .getDefault(Preferences.STRUCT_FROM_PDB, true));
543 ssm.setSecStructServices(
544 jalview.bin.Cache.getDefault(Preferences.USE_RNAVIEW, true));
548 ssm.setAddTempFacAnnot(false);
549 ssm.setProcessSecondaryStructure(false);
550 ssm.setSecStructServices(false);
554 public void checkForNews()
556 final Desktop me = this;
557 // Thread off the news reader, in case there are connection problems.
558 new Thread(new Runnable()
563 Cache.log.debug("Starting news thread.");
564 jvnews = new BlogReader(me);
565 showNews.setVisible(true);
566 Cache.log.debug("Completed news thread.");
571 public void getIdentifiersOrgData()
573 // Thread off the identifiers fetcher
574 new Thread(new Runnable()
579 Cache.log.debug("Downloading data from identifiers.org");
580 UrlDownloadClient client = new UrlDownloadClient();
583 client.download(IdOrgSettings.getUrl(),
584 IdOrgSettings.getDownloadLocation());
585 } catch (IOException e)
587 Cache.log.debug("Exception downloading identifiers.org data"
596 protected void showNews_actionPerformed(ActionEvent e)
598 showNews(showNews.isSelected());
601 void showNews(boolean visible)
603 Cache.log.debug((visible ? "Showing" : "Hiding") + " news.");
604 showNews.setSelected(visible);
605 if (visible && !jvnews.isVisible())
607 new Thread(new Runnable()
612 long now = System.currentTimeMillis();
613 Desktop.instance.setProgressBar(
614 MessageManager.getString("status.refreshing_news"), now);
615 jvnews.refreshNews();
616 Desktop.instance.setProgressBar(null, now);
624 * recover the last known dimensions for a jalview window
627 * - empty string is desktop, all other windows have unique prefix
628 * @return null or last known dimensions scaled to current geometry (if last
629 * window geom was known)
631 Rectangle getLastKnownDimensions(String windowName)
633 // TODO: lock aspect ratio for scaling desktop Bug #0058199
634 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
635 String x = jalview.bin.Cache.getProperty(windowName + "SCREEN_X");
636 String y = jalview.bin.Cache.getProperty(windowName + "SCREEN_Y");
637 String width = jalview.bin.Cache
638 .getProperty(windowName + "SCREEN_WIDTH");
639 String height = jalview.bin.Cache
640 .getProperty(windowName + "SCREEN_HEIGHT");
641 if ((x != null) && (y != null) && (width != null) && (height != null))
643 int ix = Integer.parseInt(x), iy = Integer.parseInt(y),
644 iw = Integer.parseInt(width), ih = Integer.parseInt(height);
645 if (jalview.bin.Cache.getProperty("SCREENGEOMETRY_WIDTH") != null)
647 // attempt #1 - try to cope with change in screen geometry - this
648 // version doesn't preserve original jv aspect ratio.
649 // take ratio of current screen size vs original screen size.
650 double sw = ((1f * screenSize.width) / (1f * Integer.parseInt(
651 jalview.bin.Cache.getProperty("SCREENGEOMETRY_WIDTH"))));
652 double sh = ((1f * screenSize.height) / (1f * Integer.parseInt(
653 jalview.bin.Cache.getProperty("SCREENGEOMETRY_HEIGHT"))));
654 // rescale the bounds depending upon the current screen geometry.
655 ix = (int) (ix * sw);
656 iw = (int) (iw * sw);
657 iy = (int) (iy * sh);
658 ih = (int) (ih * sh);
659 while (ix >= screenSize.width)
661 jalview.bin.Cache.log.debug(
662 "Window geometry location recall error: shifting horizontal to within screenbounds.");
663 ix -= screenSize.width;
665 while (iy >= screenSize.height)
667 jalview.bin.Cache.log.debug(
668 "Window geometry location recall error: shifting vertical to within screenbounds.");
669 iy -= screenSize.height;
671 jalview.bin.Cache.log.debug(
672 "Got last known dimensions for " + windowName + ": x:" + ix
673 + " y:" + iy + " width:" + iw + " height:" + ih);
675 // return dimensions for new instance
676 return new Rectangle(ix, iy, iw, ih);
681 private void doVamsasClientCheck()
683 if (Cache.vamsasJarsPresent())
685 setupVamsasDisconnectedGui();
686 VamsasMenu.setVisible(true);
687 final Desktop us = this;
688 VamsasMenu.addMenuListener(new MenuListener()
690 // this listener remembers when the menu was first selected, and
691 // doesn't rebuild the session list until it has been cleared and
693 boolean refresh = true;
696 public void menuCanceled(MenuEvent e)
702 public void menuDeselected(MenuEvent e)
708 public void menuSelected(MenuEvent e)
712 us.buildVamsasStMenu();
717 vamsasStart.setVisible(true);
721 void showPasteMenu(int x, int y)
723 JPopupMenu popup = new JPopupMenu();
724 JMenuItem item = new JMenuItem(
725 MessageManager.getString("label.paste_new_window"));
726 item.addActionListener(new ActionListener()
729 public void actionPerformed(ActionEvent evt)
736 popup.show(this, x, y);
743 Clipboard c = Toolkit.getDefaultToolkit().getSystemClipboard();
744 Transferable contents = c.getContents(this);
746 if (contents != null)
748 String file = (String) contents
749 .getTransferData(DataFlavor.stringFlavor);
751 FileFormatI format = new IdentifyFile().identify(file,
752 DataSourceType.PASTE);
754 new FileLoader().LoadFile(file, DataSourceType.PASTE, format);
757 } catch (Exception ex)
760 "Unable to paste alignment from system clipboard:\n" + ex);
765 * Adds and opens the given frame to the desktop
776 public static synchronized void addInternalFrame(
777 final JInternalFrame frame, String title, int w, int h)
779 addInternalFrame(frame, title, true, w, h, true, false);
783 * Add an internal frame to the Jalview desktop
790 * When true, display frame immediately, otherwise, caller must call
791 * setVisible themselves.
797 public static synchronized void addInternalFrame(
798 final JInternalFrame frame, String title, boolean makeVisible,
801 addInternalFrame(frame, title, makeVisible, w, h, true, false);
805 * Add an internal frame to the Jalview desktop and make it visible
818 public static synchronized void addInternalFrame(
819 final JInternalFrame frame, String title, int w, int h,
822 addInternalFrame(frame, title, true, w, h, resizable, false);
826 * Add an internal frame to the Jalview desktop
833 * When true, display frame immediately, otherwise, caller must call
834 * setVisible themselves.
841 * @param ignoreMinSize
842 * Do not set the default minimum size for frame
844 public static synchronized void addInternalFrame(
845 final JInternalFrame frame, String title, boolean makeVisible,
846 int w, int h, boolean resizable, boolean ignoreMinSize)
849 // TODO: allow callers to determine X and Y position of frame (eg. via
851 // TODO: consider fixing method to update entries in the window submenu with
852 // the current window title
854 frame.setTitle(title);
855 if (frame.getWidth() < 1 || frame.getHeight() < 1)
859 // THIS IS A PUBLIC STATIC METHOD, SO IT MAY BE CALLED EVEN IN
860 // A HEADLESS STATE WHEN NO DESKTOP EXISTS. MUST RETURN
861 // IF JALVIEW IS RUNNING HEADLESS
862 // ///////////////////////////////////////////////
863 if (instance == null || (System.getProperty("java.awt.headless") != null
864 && System.getProperty("java.awt.headless").equals("true")))
873 frame.setMinimumSize(
874 new Dimension(DEFAULT_MIN_WIDTH, DEFAULT_MIN_HEIGHT));
876 // Set default dimension for Alignment Frame window.
877 // The Alignment Frame window could be added from a number of places,
879 // I did this here in order not to miss out on any Alignment frame.
880 if (frame instanceof AlignFrame)
882 frame.setMinimumSize(new Dimension(ALIGN_FRAME_DEFAULT_MIN_WIDTH,
883 ALIGN_FRAME_DEFAULT_MIN_HEIGHT));
887 frame.setVisible(makeVisible);
888 frame.setClosable(true);
889 frame.setResizable(resizable);
890 frame.setMaximizable(resizable);
891 frame.setIconifiable(resizable);
892 frame.setOpaque(Platform.isJS());
894 if (frame.getX() < 1 && frame.getY() < 1)
896 frame.setLocation(xOffset * openFrameCount,
897 yOffset * ((openFrameCount - 1) % 10) + yOffset);
901 * add an entry for the new frame in the Window menu
902 * (and remove it when the frame is closed)
904 final JMenuItem menuItem = new JMenuItem(title);
905 frame.addInternalFrameListener(new InternalFrameAdapter()
908 public void internalFrameActivated(InternalFrameEvent evt)
910 JInternalFrame itf = desktop.getSelectedFrame();
913 if (itf instanceof AlignFrame)
915 Jalview.setCurrentAlignFrame((AlignFrame) itf);
922 public void internalFrameClosed(InternalFrameEvent evt)
924 PaintRefresher.RemoveComponent(frame);
927 * defensive check to prevent frames being
928 * added half off the window
930 if (openFrameCount > 0)
936 * ensure no reference to alignFrame retained by menu item listener
938 if (menuItem.getActionListeners().length > 0)
940 menuItem.removeActionListener(menuItem.getActionListeners()[0]);
942 windowMenu.remove(menuItem);
946 menuItem.addActionListener(new ActionListener()
949 public void actionPerformed(ActionEvent e)
953 frame.setSelected(true);
954 frame.setIcon(false);
955 } catch (java.beans.PropertyVetoException ex)
957 // System.err.println(ex.toString());
962 setKeyBindings(frame);
966 windowMenu.add(menuItem);
971 frame.setSelected(true);
972 frame.requestFocus();
973 } catch (java.beans.PropertyVetoException ve)
975 } catch (java.lang.ClassCastException cex)
978 "Squashed a possible GUI implementation error. If you can recreate this, please look at http://issues.jalview.org/browse/JAL-869",
984 * Add key bindings to a JInternalFrame so that Ctrl-W and Cmd-W will close the
989 private static void setKeyBindings(JInternalFrame frame)
991 @SuppressWarnings("serial")
992 final Action closeAction = new AbstractAction()
995 public void actionPerformed(ActionEvent e)
1002 * set up key bindings for Ctrl-W and Cmd-W, with the same (Close) action
1004 KeyStroke ctrlWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
1005 InputEvent.CTRL_DOWN_MASK);
1006 KeyStroke cmdWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
1007 Toolkit.getDefaultToolkit().getMenuShortcutKeyMask());
1009 InputMap inputMap = frame
1010 .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
1011 String ctrlW = ctrlWKey.toString();
1012 inputMap.put(ctrlWKey, ctrlW);
1013 inputMap.put(cmdWKey, ctrlW);
1015 ActionMap actionMap = frame.getActionMap();
1016 actionMap.put(ctrlW, closeAction);
1020 public void lostOwnership(Clipboard clipboard, Transferable contents)
1024 Desktop.jalviewClipboard = null;
1027 internalCopy = false;
1031 public void dragEnter(DropTargetDragEvent evt)
1036 public void dragExit(DropTargetEvent evt)
1041 public void dragOver(DropTargetDragEvent evt)
1046 public void dropActionChanged(DropTargetDragEvent evt)
1057 public void drop(DropTargetDropEvent evt)
1059 boolean success = true;
1060 // JAL-1552 - acceptDrop required before getTransferable call for
1061 // Java's Transferable for native dnd
1062 evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
1063 Transferable t = evt.getTransferable();
1064 List<Object> files = new ArrayList<>();
1065 List<DataSourceType> protocols = new ArrayList<>();
1069 Desktop.transferFromDropTarget(files, protocols, evt, t);
1070 } catch (Exception e)
1072 e.printStackTrace();
1080 for (int i = 0; i < files.size(); i++)
1082 // BH 2018 File or String
1083 Object file = files.get(i);
1084 String fileName = file.toString();
1085 DataSourceType protocol = (protocols == null)
1086 ? DataSourceType.FILE
1088 FileFormatI format = null;
1090 if (fileName.endsWith(".jar"))
1092 format = FileFormat.Jalview;
1097 format = new IdentifyFile().identify(file, protocol);
1100 new FileLoader().LoadFile(null, file, protocol, format);
1103 } catch (Exception ex)
1108 evt.dropComplete(success); // need this to ensure input focus is properly
1109 // transfered to any new windows created
1119 public void inputLocalFileMenuItem_actionPerformed(AlignViewport viewport)
1121 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
1122 JalviewFileChooser chooser = JalviewFileChooser
1123 .forRead(Cache.getProperty("LAST_DIRECTORY"), fileFormat, true);
1125 chooser.setFileView(new JalviewFileView());
1126 chooser.setDialogTitle(
1127 MessageManager.getString("label.open_local_file"));
1128 chooser.setToolTipText(MessageManager.getString("action.open"));
1130 chooser.setResponseHandler(0, new Runnable()
1135 File selectedFile = chooser.getSelectedFile();
1136 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1138 FileFormatI format = chooser.getSelectedFormat();
1141 * Call IdentifyFile to verify the file contains what its extension implies.
1142 * Skip this step for dynamically added file formats, because
1143 * IdentifyFile does not know how to recognise them.
1145 if (FileFormats.getInstance().isIdentifiable(format))
1149 format = new IdentifyFile().identify(selectedFile,
1150 DataSourceType.FILE);
1151 } catch (FileFormatException e)
1153 // format = null; //??
1157 new FileLoader().LoadFile(viewport, selectedFile,
1158 DataSourceType.FILE, format);
1161 chooser.showOpenDialog(this);
1165 * Shows a dialog for input of a URL at which to retrieve alignment data
1170 public void inputURLMenuItem_actionPerformed(AlignViewport viewport)
1172 // This construct allows us to have a wider textfield
1174 JLabel label = new JLabel(
1175 MessageManager.getString("label.input_file_url"));
1177 JPanel panel = new JPanel(new GridLayout(2, 1));
1181 * the URL to fetch is
1182 * Java: an editable combobox with history
1183 * JS: (pending JAL-3038) a plain text field
1186 String urlBase = "http://www.";
1187 if (Platform.isJS())
1189 history = new JTextField(urlBase, 35);
1198 JComboBox<String> asCombo = new JComboBox<>();
1199 asCombo.setPreferredSize(new Dimension(400, 20));
1200 asCombo.setEditable(true);
1201 asCombo.addItem(urlBase);
1202 String historyItems = Cache.getProperty("RECENT_URL");
1203 if (historyItems != null)
1205 for (String token : historyItems.split("\\t"))
1207 asCombo.addItem(token);
1214 Object[] options = new Object[] { MessageManager.getString("action.ok"),
1215 MessageManager.getString("action.cancel") };
1216 Runnable action = new Runnable()
1221 @SuppressWarnings("unchecked")
1222 String url = (history instanceof JTextField
1223 ? ((JTextField) history).getText()
1224 : ((JComboBox<String>) history).getSelectedItem()
1227 if (url.toLowerCase().endsWith(".jar"))
1229 if (viewport != null)
1231 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1232 FileFormat.Jalview);
1236 new FileLoader().LoadFile(url, DataSourceType.URL,
1237 FileFormat.Jalview);
1242 FileFormatI format = null;
1245 format = new IdentifyFile().identify(url, DataSourceType.URL);
1246 } catch (FileFormatException e)
1248 // TODO revise error handling, distinguish between
1249 // URL not found and response not valid
1254 String msg = MessageManager
1255 .formatMessage("label.couldnt_locate", url);
1256 JvOptionPane.showInternalMessageDialog(Desktop.desktop, msg,
1257 MessageManager.getString("label.url_not_found"),
1258 JvOptionPane.WARNING_MESSAGE);
1263 if (viewport != null)
1265 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1270 new FileLoader().LoadFile(url, DataSourceType.URL, format);
1275 String dialogOption = MessageManager
1276 .getString("label.input_alignment_from_url");
1277 JvOptionPane.newOptionDialog(desktop).setResponseHandler(0, action)
1278 .showInternalDialog(panel, dialogOption,
1279 JvOptionPane.YES_NO_CANCEL_OPTION,
1280 JvOptionPane.PLAIN_MESSAGE, null, options,
1281 MessageManager.getString("action.ok"));
1285 * Opens the CutAndPaste window for the user to paste an alignment in to
1288 * - if not null, the pasted alignment is added to the current
1289 * alignment; if null, to a new alignment window
1292 public void inputTextboxMenuItem_actionPerformed(
1293 AlignmentViewPanel viewPanel)
1295 CutAndPasteTransfer cap = new CutAndPasteTransfer();
1296 cap.setForInput(viewPanel);
1297 Desktop.addInternalFrame(cap,
1298 MessageManager.getString("label.cut_paste_alignmen_file"), true,
1308 Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
1309 jalview.bin.Cache.setProperty("SCREENGEOMETRY_WIDTH",
1311 jalview.bin.Cache.setProperty("SCREENGEOMETRY_HEIGHT",
1312 screen.height + "");
1313 storeLastKnownDimensions("", new Rectangle(getBounds().x, getBounds().y,
1314 getWidth(), getHeight()));
1316 if (jconsole != null)
1318 storeLastKnownDimensions("JAVA_CONSOLE_", jconsole.getBounds());
1319 jconsole.stopConsole();
1323 storeLastKnownDimensions("JALVIEW_RSS_WINDOW_", jvnews.getBounds());
1326 if (dialogExecutor != null)
1328 dialogExecutor.shutdownNow();
1330 closeAll_actionPerformed(null);
1332 if (groovyConsole != null)
1334 // suppress a possible repeat prompt to save script
1335 groovyConsole.setDirty(false);
1336 groovyConsole.exit();
1341 private void storeLastKnownDimensions(String string, Rectangle jc)
1343 jalview.bin.Cache.log.debug("Storing last known dimensions for "
1344 + string + ": x:" + jc.x + " y:" + jc.y + " width:" + jc.width
1345 + " height:" + jc.height);
1347 jalview.bin.Cache.setProperty(string + "SCREEN_X", jc.x + "");
1348 jalview.bin.Cache.setProperty(string + "SCREEN_Y", jc.y + "");
1349 jalview.bin.Cache.setProperty(string + "SCREEN_WIDTH", jc.width + "");
1350 jalview.bin.Cache.setProperty(string + "SCREEN_HEIGHT", jc.height + "");
1360 public void aboutMenuItem_actionPerformed(ActionEvent e)
1362 // StringBuffer message = getAboutMessage(false);
1363 // JvOptionPane.showInternalMessageDialog(Desktop.desktop,
1365 // message.toString(), "About Jalview", JvOptionPane.INFORMATION_MESSAGE);
1366 new Thread(new Runnable()
1371 new SplashScreen(true);
1376 public StringBuffer getAboutMessage(boolean shortv)
1378 StringBuffer message = new StringBuffer();
1379 message.append("<html>");
1382 message.append("<h1><strong>Version: "
1383 + jalview.bin.Cache.getProperty("VERSION")
1384 + "</strong></h1>");
1385 message.append("<strong>Last Updated: <em>"
1386 + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown")
1387 + "</em></strong>");
1393 message.append("<strong>Version "
1394 + jalview.bin.Cache.getProperty("VERSION")
1395 + "; last updated: "
1396 + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown"));
1399 if (jalview.bin.Cache.getDefault("LATEST_VERSION", "Checking")
1400 .equals("Checking"))
1402 message.append("<br>...Checking latest version...</br>");
1404 else if (!jalview.bin.Cache.getDefault("LATEST_VERSION", "Checking")
1405 .equals(jalview.bin.Cache.getProperty("VERSION")))
1407 boolean red = false;
1408 if (jalview.bin.Cache.getProperty("VERSION").toLowerCase()
1409 .indexOf("automated build") == -1)
1412 // Displayed when code version and jnlp version do not match and code
1413 // version is not a development build
1414 message.append("<div style=\"color: #FF0000;font-style: bold;\">");
1417 message.append("<br>!! Version "
1418 + jalview.bin.Cache.getDefault("LATEST_VERSION",
1420 + " is available for download from "
1421 + jalview.bin.Cache.getDefault("www.jalview.org",
1422 "http://www.jalview.org")
1426 message.append("</div>");
1429 message.append("<br>Authors: " + jalview.bin.Cache.getDefault(
1431 "The Jalview Authors (See AUTHORS file for current list)")
1432 + "<br><br>Development managed by The Barton Group, University of Dundee, Scotland, UK.<br>"
1433 + "<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"
1434 + "<br><br>If you use Jalview, please cite:"
1435 + "<br>Waterhouse, A.M., Procter, J.B., Martin, D.M.A, Clamp, M. and Barton, G. J. (2009)"
1436 + "<br>Jalview Version 2 - a multiple sequence alignment editor and analysis workbench"
1437 + "<br>Bioinformatics doi: 10.1093/bioinformatics/btp033"
1443 * Action on requesting Help documentation
1446 public void documentationMenuItem_actionPerformed()
1450 if (Platform.isJS())
1452 BrowserLauncher.openURL("http://www.jalview.org/help.html");
1461 Help.showHelpWindow();
1463 } catch (Exception ex)
1465 System.err.println("Error opening help: " + ex.getMessage());
1470 public void closeAll_actionPerformed(ActionEvent e)
1472 // TODO show a progress bar while closing?
1473 JInternalFrame[] frames = desktop.getAllFrames();
1474 for (int i = 0; i < frames.length; i++)
1478 frames[i].setClosed(true);
1479 } catch (java.beans.PropertyVetoException ex)
1483 Jalview.setCurrentAlignFrame(null);
1484 System.out.println("ALL CLOSED");
1485 if (v_client != null)
1487 // TODO clear binding to vamsas document objects on close_all
1491 * reset state of singleton objects as appropriate (clear down session state
1492 * when all windows are closed)
1494 StructureSelectionManager ssm = StructureSelectionManager
1495 .getStructureSelectionManager(this);
1503 public void raiseRelated_actionPerformed(ActionEvent e)
1505 reorderAssociatedWindows(false, false);
1509 public void minimizeAssociated_actionPerformed(ActionEvent e)
1511 reorderAssociatedWindows(true, false);
1514 void closeAssociatedWindows()
1516 reorderAssociatedWindows(false, true);
1522 * @seejalview.jbgui.GDesktop#garbageCollect_actionPerformed(java.awt.event.
1526 protected void garbageCollect_actionPerformed(ActionEvent e)
1528 // We simply collect the garbage
1529 jalview.bin.Cache.log.debug("Collecting garbage...");
1531 jalview.bin.Cache.log.debug("Finished garbage collection.");
1538 * jalview.jbgui.GDesktop#showMemusage_actionPerformed(java.awt.event.ActionEvent
1542 protected void showMemusage_actionPerformed(ActionEvent e)
1544 desktop.showMemoryUsage(showMemusage.isSelected());
1551 * jalview.jbgui.GDesktop#showConsole_actionPerformed(java.awt.event.ActionEvent
1555 protected void showConsole_actionPerformed(ActionEvent e)
1557 showConsole(showConsole.isSelected());
1560 Console jconsole = null;
1563 * control whether the java console is visible or not
1567 void showConsole(boolean selected)
1569 // TODO: decide if we should update properties file
1570 if (jconsole != null) // BH 2018
1572 showConsole.setSelected(selected);
1573 Cache.setProperty("SHOW_JAVA_CONSOLE",
1574 Boolean.valueOf(selected).toString());
1575 jconsole.setVisible(selected);
1579 void reorderAssociatedWindows(boolean minimize, boolean close)
1581 JInternalFrame[] frames = desktop.getAllFrames();
1582 if (frames == null || frames.length < 1)
1587 AlignmentViewport source = null, target = null;
1588 if (frames[0] instanceof AlignFrame)
1590 source = ((AlignFrame) frames[0]).getCurrentView();
1592 else if (frames[0] instanceof TreePanel)
1594 source = ((TreePanel) frames[0]).getViewPort();
1596 else if (frames[0] instanceof PCAPanel)
1598 source = ((PCAPanel) frames[0]).av;
1600 else if (frames[0].getContentPane() instanceof PairwiseAlignPanel)
1602 source = ((PairwiseAlignPanel) frames[0].getContentPane()).av;
1607 for (int i = 0; i < frames.length; i++)
1610 if (frames[i] == null)
1614 if (frames[i] instanceof AlignFrame)
1616 target = ((AlignFrame) frames[i]).getCurrentView();
1618 else if (frames[i] instanceof TreePanel)
1620 target = ((TreePanel) frames[i]).getViewPort();
1622 else if (frames[i] instanceof PCAPanel)
1624 target = ((PCAPanel) frames[i]).av;
1626 else if (frames[i].getContentPane() instanceof PairwiseAlignPanel)
1628 target = ((PairwiseAlignPanel) frames[i].getContentPane()).av;
1631 if (source == target)
1637 frames[i].setClosed(true);
1641 frames[i].setIcon(minimize);
1644 frames[i].toFront();
1648 } catch (java.beans.PropertyVetoException ex)
1663 protected void preferences_actionPerformed(ActionEvent e)
1669 * Prompts the user to choose a file and then saves the Jalview state as a
1670 * Jalview project file
1673 public void saveState_actionPerformed()
1675 saveState_actionPerformed(false);
1678 public void saveState_actionPerformed(boolean saveAs)
1680 java.io.File projectFile = getProjectFile();
1681 // autoSave indicates we already have a file and don't need to ask
1682 boolean autoSave = projectFile != null && !saveAs
1683 && BackupFiles.getEnabled();
1685 // System.out.println("autoSave="+autoSave+", projectFile='"+projectFile+"',
1686 // saveAs="+saveAs+", Backups
1687 // "+(BackupFiles.getEnabled()?"enabled":"disabled"));
1689 boolean approveSave = false;
1692 JalviewFileChooser chooser = new JalviewFileChooser("jvp",
1695 chooser.setFileView(new JalviewFileView());
1696 chooser.setDialogTitle(MessageManager.getString("label.save_state"));
1698 int value = chooser.showSaveDialog(this);
1700 if (value == JalviewFileChooser.APPROVE_OPTION)
1702 projectFile = chooser.getSelectedFile();
1703 setProjectFile(projectFile);
1708 if (approveSave || autoSave)
1710 final Desktop me = this;
1711 final java.io.File chosenFile = projectFile;
1712 new Thread(new Runnable()
1717 // TODO: refactor to Jalview desktop session controller action.
1718 setProgressBar(MessageManager.formatMessage(
1719 "label.saving_jalview_project", new Object[]
1720 { chosenFile.getName() }), chosenFile.hashCode());
1721 jalview.bin.Cache.setProperty("LAST_DIRECTORY",
1722 chosenFile.getParent());
1723 // TODO catch and handle errors for savestate
1724 // TODO prevent user from messing with the Desktop whilst we're saving
1727 boolean doBackup = BackupFiles.getEnabled();
1728 BackupFiles backupfiles = doBackup ? new BackupFiles(chosenFile) : null;
1730 new Jalview2XML().saveState(doBackup ? backupfiles.getTempFile() : chosenFile);
1734 backupfiles.setWriteSuccess(true);
1735 backupfiles.rollBackupsAndRenameTempFile();
1737 } catch (OutOfMemoryError oom)
1739 new OOMWarning("Whilst saving current state to "
1740 + chosenFile.getName(), oom);
1741 } catch (Exception ex)
1743 Cache.log.error("Problems whilst trying to save to "
1744 + chosenFile.getName(), ex);
1745 JvOptionPane.showMessageDialog(me,
1746 MessageManager.formatMessage(
1747 "label.error_whilst_saving_current_state_to",
1749 { chosenFile.getName() }),
1750 MessageManager.getString("label.couldnt_save_project"),
1751 JvOptionPane.WARNING_MESSAGE);
1753 setProgressBar(null, chosenFile.hashCode());
1760 public void saveAsState_actionPerformed(ActionEvent e)
1762 saveState_actionPerformed(true);
1765 private void setProjectFile(File choice)
1767 this.projectFile = choice;
1770 public File getProjectFile()
1772 return this.projectFile;
1776 * Shows a file chooser dialog and tries to read in the selected file as a
1780 public void loadState_actionPerformed()
1782 final String[] suffix = new String[] { "jvp", "jar" };
1783 final String[] desc = new String[] { "Jalview Project",
1784 "Jalview Project (old)" };
1785 JalviewFileChooser chooser = new JalviewFileChooser(
1786 Cache.getProperty("LAST_DIRECTORY"), suffix, desc,
1787 "Jalview Project", true, true); // last two booleans: allFiles,
1789 chooser.setFileView(new JalviewFileView());
1790 chooser.setDialogTitle(MessageManager.getString("label.restore_state"));
1791 chooser.setResponseHandler(0, new Runnable()
1796 File selectedFile = chooser.getSelectedFile();
1797 setProjectFile(selectedFile);
1798 String choice = selectedFile.getAbsolutePath();
1799 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1800 new Thread(new Runnable()
1807 new Jalview2XML().loadJalviewAlign(choice);
1808 } catch (OutOfMemoryError oom)
1810 new OOMWarning("Whilst loading project from " + choice, oom);
1811 } catch (Exception ex)
1814 "Problems whilst loading project from " + choice, ex);
1815 JvOptionPane.showMessageDialog(Desktop.desktop,
1816 MessageManager.formatMessage(
1817 "label.error_whilst_loading_project_from",
1820 MessageManager.getString("label.couldnt_load_project"),
1821 JvOptionPane.WARNING_MESSAGE);
1828 chooser.showOpenDialog(this);
1832 public void inputSequence_actionPerformed(ActionEvent e)
1834 new SequenceFetcher(this);
1837 JPanel progressPanel;
1839 ArrayList<JPanel> fileLoadingPanels = new ArrayList<>();
1841 public void startLoading(final Object fileName)
1843 if (fileLoadingCount == 0)
1845 fileLoadingPanels.add(addProgressPanel(MessageManager
1846 .formatMessage("label.loading_file", new Object[]
1852 private JPanel addProgressPanel(String string)
1854 if (progressPanel == null)
1856 progressPanel = new JPanel(new GridLayout(1, 1));
1857 totalProgressCount = 0;
1858 instance.getContentPane().add(progressPanel, BorderLayout.SOUTH);
1860 JPanel thisprogress = new JPanel(new BorderLayout(10, 5));
1861 JProgressBar progressBar = new JProgressBar();
1862 progressBar.setIndeterminate(true);
1864 thisprogress.add(new JLabel(string), BorderLayout.WEST);
1866 thisprogress.add(progressBar, BorderLayout.CENTER);
1867 progressPanel.add(thisprogress);
1868 ((GridLayout) progressPanel.getLayout()).setRows(
1869 ((GridLayout) progressPanel.getLayout()).getRows() + 1);
1870 ++totalProgressCount;
1871 instance.validate();
1872 return thisprogress;
1875 int totalProgressCount = 0;
1877 private void removeProgressPanel(JPanel progbar)
1879 if (progressPanel != null)
1881 synchronized (progressPanel)
1883 progressPanel.remove(progbar);
1884 GridLayout gl = (GridLayout) progressPanel.getLayout();
1885 gl.setRows(gl.getRows() - 1);
1886 if (--totalProgressCount < 1)
1888 this.getContentPane().remove(progressPanel);
1889 progressPanel = null;
1896 public void stopLoading()
1899 if (fileLoadingCount < 1)
1901 while (fileLoadingPanels.size() > 0)
1903 removeProgressPanel(fileLoadingPanels.remove(0));
1905 fileLoadingPanels.clear();
1906 fileLoadingCount = 0;
1911 public static int getViewCount(String alignmentId)
1913 AlignmentViewport[] aps = getViewports(alignmentId);
1914 return (aps == null) ? 0 : aps.length;
1919 * @param alignmentId
1920 * - if null, all sets are returned
1921 * @return all AlignmentPanels concerning the alignmentId sequence set
1923 public static AlignmentPanel[] getAlignmentPanels(String alignmentId)
1925 if (Desktop.desktop == null)
1927 // no frames created and in headless mode
1928 // TODO: verify that frames are recoverable when in headless mode
1931 List<AlignmentPanel> aps = new ArrayList<>();
1932 AlignFrame[] frames = getAlignFrames();
1937 for (AlignFrame af : frames)
1939 for (AlignmentPanel ap : af.alignPanels)
1941 if (alignmentId == null
1942 || alignmentId.equals(ap.av.getSequenceSetId()))
1948 if (aps.size() == 0)
1952 AlignmentPanel[] vap = aps.toArray(new AlignmentPanel[aps.size()]);
1957 * get all the viewports on an alignment.
1959 * @param sequenceSetId
1960 * unique alignment id (may be null - all viewports returned in that
1962 * @return all viewports on the alignment bound to sequenceSetId
1964 public static AlignmentViewport[] getViewports(String sequenceSetId)
1966 List<AlignmentViewport> viewp = new ArrayList<>();
1967 if (desktop != null)
1969 AlignFrame[] frames = Desktop.getAlignFrames();
1971 for (AlignFrame afr : frames)
1973 if (sequenceSetId == null || afr.getViewport().getSequenceSetId()
1974 .equals(sequenceSetId))
1976 if (afr.alignPanels != null)
1978 for (AlignmentPanel ap : afr.alignPanels)
1980 if (sequenceSetId == null
1981 || sequenceSetId.equals(ap.av.getSequenceSetId()))
1989 viewp.add(afr.getViewport());
1993 if (viewp.size() > 0)
1995 return viewp.toArray(new AlignmentViewport[viewp.size()]);
2002 * Explode the views in the given frame into separate AlignFrame
2006 public static void explodeViews(AlignFrame af)
2008 int size = af.alignPanels.size();
2014 for (int i = 0; i < size; i++)
2016 AlignmentPanel ap = af.alignPanels.get(i);
2017 AlignFrame newaf = new AlignFrame(ap);
2020 * Restore the view's last exploded frame geometry if known. Multiple
2021 * views from one exploded frame share and restore the same (frame)
2022 * position and size.
2024 Rectangle geometry = ap.av.getExplodedGeometry();
2025 if (geometry != null)
2027 newaf.setBounds(geometry);
2030 ap.av.setGatherViewsHere(false);
2032 addInternalFrame(newaf, af.getTitle(), AlignFrame.DEFAULT_WIDTH,
2033 AlignFrame.DEFAULT_HEIGHT);
2036 af.alignPanels.clear();
2037 af.closeMenuItem_actionPerformed(true);
2042 * Gather expanded views (separate AlignFrame's) with the same sequence set
2043 * identifier back in to this frame as additional views, and close the expanded
2044 * views. Note the expanded frames may themselves have multiple views. We take
2049 public void gatherViews(AlignFrame source)
2051 source.viewport.setGatherViewsHere(true);
2052 source.viewport.setExplodedGeometry(source.getBounds());
2053 JInternalFrame[] frames = desktop.getAllFrames();
2054 String viewId = source.viewport.getSequenceSetId();
2056 for (int t = 0; t < frames.length; t++)
2058 if (frames[t] instanceof AlignFrame && frames[t] != source)
2060 AlignFrame af = (AlignFrame) frames[t];
2061 boolean gatherThis = false;
2062 for (int a = 0; a < af.alignPanels.size(); a++)
2064 AlignmentPanel ap = af.alignPanels.get(a);
2065 if (viewId.equals(ap.av.getSequenceSetId()))
2068 ap.av.setGatherViewsHere(false);
2069 ap.av.setExplodedGeometry(af.getBounds());
2070 source.addAlignmentPanel(ap, false);
2076 af.alignPanels.clear();
2077 af.closeMenuItem_actionPerformed(true);
2084 jalview.gui.VamsasApplication v_client = null;
2087 public void vamsasImport_actionPerformed(ActionEvent e)
2089 // TODO: JAL-3048 not needed for Jalview-JS
2091 if (v_client == null)
2093 // Load and try to start a session.
2094 JalviewFileChooser chooser = new JalviewFileChooser(
2095 jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
2097 chooser.setFileView(new JalviewFileView());
2098 chooser.setDialogTitle(
2099 MessageManager.getString("label.open_saved_vamsas_session"));
2100 chooser.setToolTipText(MessageManager.getString(
2101 "label.select_vamsas_session_opened_as_new_vamsas_session"));
2103 int value = chooser.showOpenDialog(this);
2105 if (value == JalviewFileChooser.APPROVE_OPTION)
2107 String fle = chooser.getSelectedFile().toString();
2108 if (!vamsasImport(chooser.getSelectedFile()))
2110 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
2111 MessageManager.formatMessage(
2112 "label.couldnt_import_as_vamsas_session",
2116 .getString("label.vamsas_document_import_failed"),
2117 JvOptionPane.ERROR_MESSAGE);
2123 jalview.bin.Cache.log.error(
2124 "Implementation error - load session from a running session is not supported.");
2129 * import file into a new vamsas session (uses jalview.gui.VamsasApplication)
2132 * @return true if import was a success and a session was started.
2134 public boolean vamsasImport(URL url)
2136 // TODO: create progress bar
2137 if (v_client != null)
2140 jalview.bin.Cache.log.error(
2141 "Implementation error - load session from a running session is not supported.");
2147 // copy the URL content to a temporary local file
2148 // TODO: be a bit cleverer here with nio (?!)
2149 File file = File.createTempFile("vdocfromurl", ".vdj");
2150 FileOutputStream fos = new FileOutputStream(file);
2151 BufferedInputStream bis = new BufferedInputStream(url.openStream());
2152 byte[] buffer = new byte[2048];
2154 while ((ln = bis.read(buffer)) > -1)
2156 fos.write(buffer, 0, ln);
2160 v_client = new jalview.gui.VamsasApplication(this, file,
2161 url.toExternalForm());
2162 } catch (Exception ex)
2164 jalview.bin.Cache.log.error(
2165 "Failed to create new vamsas session from contents of URL "
2170 setupVamsasConnectedGui();
2171 v_client.initial_update(); // TODO: thread ?
2172 return v_client.inSession();
2176 * import file into a new vamsas session (uses jalview.gui.VamsasApplication)
2179 * @return true if import was a success and a session was started.
2181 public boolean vamsasImport(File file)
2183 if (v_client != null)
2186 jalview.bin.Cache.log.error(
2187 "Implementation error - load session from a running session is not supported.");
2191 setProgressBar(MessageManager.formatMessage(
2192 "status.importing_vamsas_session_from", new Object[]
2193 { file.getName() }), file.hashCode());
2196 v_client = new jalview.gui.VamsasApplication(this, file, null);
2197 } catch (Exception ex)
2199 setProgressBar(MessageManager.formatMessage(
2200 "status.importing_vamsas_session_from", new Object[]
2201 { file.getName() }), file.hashCode());
2202 jalview.bin.Cache.log.error(
2203 "New vamsas session from existing session file failed:", ex);
2206 setupVamsasConnectedGui();
2207 v_client.initial_update(); // TODO: thread ?
2208 setProgressBar(MessageManager.formatMessage(
2209 "status.importing_vamsas_session_from", new Object[]
2210 { file.getName() }), file.hashCode());
2211 return v_client.inSession();
2214 public boolean joinVamsasSession(String mysesid)
2216 if (v_client != null)
2218 throw new Error(MessageManager
2219 .getString("error.try_join_vamsas_session_another"));
2221 if (mysesid == null)
2224 MessageManager.getString("error.invalid_vamsas_session_id"));
2226 v_client = new VamsasApplication(this, mysesid);
2227 setupVamsasConnectedGui();
2228 v_client.initial_update();
2229 return (v_client.inSession());
2233 public void vamsasStart_actionPerformed(ActionEvent e)
2235 if (v_client == null)
2238 // we just start a default session for moment.
2240 * JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache.
2241 * getProperty("LAST_DIRECTORY"));
2243 * chooser.setFileView(new JalviewFileView());
2244 * chooser.setDialogTitle("Load Vamsas file");
2245 * chooser.setToolTipText("Import");
2247 * int value = chooser.showOpenDialog(this);
2249 * if (value == JalviewFileChooser.APPROVE_OPTION) { v_client = new
2250 * jalview.gui.VamsasApplication(this, chooser.getSelectedFile());
2252 v_client = new VamsasApplication(this);
2253 setupVamsasConnectedGui();
2254 v_client.initial_update(); // TODO: thread ?
2258 // store current data in session.
2259 v_client.push_update(); // TODO: thread
2263 protected void setupVamsasConnectedGui()
2265 vamsasStart.setText(MessageManager.getString("label.session_update"));
2266 vamsasSave.setVisible(true);
2267 vamsasStop.setVisible(true);
2268 vamsasImport.setVisible(false); // Document import to existing session is
2269 // not possible for vamsas-client-1.0.
2272 protected void setupVamsasDisconnectedGui()
2274 vamsasSave.setVisible(false);
2275 vamsasStop.setVisible(false);
2276 vamsasImport.setVisible(true);
2278 .setText(MessageManager.getString("label.new_vamsas_session"));
2282 public void vamsasStop_actionPerformed(ActionEvent e)
2284 if (v_client != null)
2286 v_client.end_session();
2288 setupVamsasDisconnectedGui();
2292 protected void buildVamsasStMenu()
2294 if (v_client == null)
2296 String[] sess = null;
2299 sess = VamsasApplication.getSessionList();
2300 } catch (Exception e)
2302 jalview.bin.Cache.log.warn("Problem getting current sessions list.",
2308 jalview.bin.Cache.log.debug(
2309 "Got current sessions list: " + sess.length + " entries.");
2310 VamsasStMenu.removeAll();
2311 for (int i = 0; i < sess.length; i++)
2313 JMenuItem sessit = new JMenuItem();
2314 sessit.setText(sess[i]);
2315 sessit.setToolTipText(MessageManager
2316 .formatMessage("label.connect_to_session", new Object[]
2318 final Desktop dsktp = this;
2319 final String mysesid = sess[i];
2320 sessit.addActionListener(new ActionListener()
2324 public void actionPerformed(ActionEvent e)
2326 if (dsktp.v_client == null)
2328 Thread rthr = new Thread(new Runnable()
2334 dsktp.v_client = new VamsasApplication(dsktp, mysesid);
2335 dsktp.setupVamsasConnectedGui();
2336 dsktp.v_client.initial_update();
2344 VamsasStMenu.add(sessit);
2346 // don't show an empty menu.
2347 VamsasStMenu.setVisible(sess.length > 0);
2352 jalview.bin.Cache.log.debug("No current vamsas sessions.");
2353 VamsasStMenu.removeAll();
2354 VamsasStMenu.setVisible(false);
2359 // Not interested in the content. Just hide ourselves.
2360 VamsasStMenu.setVisible(false);
2365 public void vamsasSave_actionPerformed(ActionEvent e)
2367 // TODO: JAL-3048 not needed for Jalview-JS
2369 if (v_client != null)
2371 // TODO: VAMSAS DOCUMENT EXTENSION is VDJ
2372 JalviewFileChooser chooser = new JalviewFileChooser("vdj",
2375 chooser.setFileView(new JalviewFileView());
2376 chooser.setDialogTitle(MessageManager
2377 .getString("label.save_vamsas_document_archive"));
2379 int value = chooser.showSaveDialog(this);
2381 if (value == JalviewFileChooser.APPROVE_OPTION)
2383 java.io.File choice = chooser.getSelectedFile();
2384 JPanel progpanel = addProgressPanel(MessageManager
2385 .formatMessage("label.saving_vamsas_doc", new Object[]
2386 { choice.getName() }));
2387 Cache.setProperty("LAST_DIRECTORY", choice.getParent());
2388 String warnmsg = null;
2389 String warnttl = null;
2392 v_client.vclient.storeDocument(choice);
2395 warnttl = "Serious Problem saving Vamsas Document";
2396 warnmsg = ex.toString();
2397 jalview.bin.Cache.log
2398 .error("Error Whilst saving document to " + choice, ex);
2400 } catch (Exception ex)
2402 warnttl = "Problem saving Vamsas Document.";
2403 warnmsg = ex.toString();
2404 jalview.bin.Cache.log.warn(
2405 "Exception Whilst saving document to " + choice, ex);
2408 removeProgressPanel(progpanel);
2409 if (warnmsg != null)
2411 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
2413 warnmsg, warnttl, JvOptionPane.ERROR_MESSAGE);
2419 JPanel vamUpdate = null;
2422 * hide vamsas user gui bits when a vamsas document event is being handled.
2425 * true to hide gui, false to reveal gui
2427 public void setVamsasUpdate(boolean b)
2429 Cache.log.debug("Setting gui for Vamsas update "
2430 + (b ? "in progress" : "finished"));
2432 if (vamUpdate != null)
2434 this.removeProgressPanel(vamUpdate);
2438 vamUpdate = this.addProgressPanel(
2439 MessageManager.getString("label.updating_vamsas_session"));
2441 vamsasStart.setVisible(!b);
2442 vamsasStop.setVisible(!b);
2443 vamsasSave.setVisible(!b);
2446 public JInternalFrame[] getAllFrames()
2448 return desktop.getAllFrames();
2452 * Checks the given url to see if it gives a response indicating that the user
2453 * should be informed of a new questionnaire.
2457 public void checkForQuestionnaire(String url)
2459 UserQuestionnaireCheck jvq = new UserQuestionnaireCheck(url);
2460 // javax.swing.SwingUtilities.invokeLater(jvq);
2461 new Thread(jvq).start();
2464 public void checkURLLinks()
2466 // Thread off the URL link checker
2467 addDialogThread(new Runnable()
2472 if (Cache.getDefault("CHECKURLLINKS", true))
2474 // check what the actual links are - if it's just the default don't
2475 // bother with the warning
2476 List<String> links = Preferences.sequenceUrlLinks
2479 // only need to check links if there is one with a
2480 // SEQUENCE_ID which is not the default EMBL_EBI link
2481 ListIterator<String> li = links.listIterator();
2482 boolean check = false;
2483 List<JLabel> urls = new ArrayList<>();
2484 while (li.hasNext())
2486 String link = li.next();
2487 if (link.contains(SEQUENCE_ID)
2488 && !UrlConstants.isDefaultString(link))
2491 int barPos = link.indexOf("|");
2492 String urlMsg = barPos == -1 ? link
2493 : link.substring(0, barPos) + ": "
2494 + link.substring(barPos + 1);
2495 urls.add(new JLabel(urlMsg));
2503 // ask user to check in case URL links use old style tokens
2504 // ($SEQUENCE_ID$ for sequence id _or_ accession id)
2505 JPanel msgPanel = new JPanel();
2506 msgPanel.setLayout(new BoxLayout(msgPanel, BoxLayout.PAGE_AXIS));
2507 msgPanel.add(Box.createVerticalGlue());
2508 JLabel msg = new JLabel(MessageManager
2509 .getString("label.SEQUENCE_ID_for_DB_ACCESSION1"));
2510 JLabel msg2 = new JLabel(MessageManager
2511 .getString("label.SEQUENCE_ID_for_DB_ACCESSION2"));
2513 for (JLabel url : urls)
2519 final JCheckBox jcb = new JCheckBox(
2520 MessageManager.getString("label.do_not_display_again"));
2521 jcb.addActionListener(new ActionListener()
2524 public void actionPerformed(ActionEvent e)
2526 // update Cache settings for "don't show this again"
2527 boolean showWarningAgain = !jcb.isSelected();
2528 Cache.setProperty("CHECKURLLINKS",
2529 Boolean.valueOf(showWarningAgain).toString());
2534 JvOptionPane.showMessageDialog(Desktop.desktop, msgPanel,
2536 .getString("label.SEQUENCE_ID_no_longer_used"),
2537 JvOptionPane.WARNING_MESSAGE);
2544 * Proxy class for JDesktopPane which optionally displays the current memory
2545 * usage and highlights the desktop area with a red bar if free memory runs low.
2549 public class MyDesktopPane extends JDesktopPane
2552 private static final float ONE_MB = 1048576f;
2554 boolean showMemoryUsage = false;
2558 java.text.NumberFormat df;
2560 float maxMemory, allocatedMemory, freeMemory, totalFreeMemory,
2563 public MyDesktopPane(boolean showMemoryUsage)
2565 showMemoryUsage(showMemoryUsage);
2568 public void showMemoryUsage(boolean showMemory)
2570 this.showMemoryUsage = showMemory;
2573 Thread worker = new Thread(this);
2579 public boolean isShowMemoryUsage()
2581 return showMemoryUsage;
2587 df = java.text.NumberFormat.getNumberInstance();
2588 df.setMaximumFractionDigits(2);
2589 runtime = Runtime.getRuntime();
2591 while (showMemoryUsage)
2595 maxMemory = runtime.maxMemory() / ONE_MB;
2596 allocatedMemory = runtime.totalMemory() / ONE_MB;
2597 freeMemory = runtime.freeMemory() / ONE_MB;
2598 totalFreeMemory = freeMemory + (maxMemory - allocatedMemory);
2600 percentUsage = (totalFreeMemory / maxMemory) * 100;
2602 // if (percentUsage < 20)
2604 // border1 = BorderFactory.createMatteBorder(12, 12, 12, 12,
2606 // instance.set.setBorder(border1);
2609 // sleep after showing usage
2611 } catch (Exception ex)
2613 ex.printStackTrace();
2619 public void paintComponent(Graphics g)
2621 if (showMemoryUsage && g != null && df != null)
2623 if (percentUsage < 20)
2625 g.setColor(Color.red);
2627 FontMetrics fm = g.getFontMetrics();
2630 g.drawString(MessageManager.formatMessage("label.memory_stats",
2632 { df.format(totalFreeMemory), df.format(maxMemory),
2633 df.format(percentUsage) }),
2634 10, getHeight() - fm.getHeight());
2641 * Accessor method to quickly get all the AlignmentFrames loaded.
2643 * @return an array of AlignFrame, or null if none found
2645 public static AlignFrame[] getAlignFrames()
2647 if (Jalview.isHeadlessMode())
2649 // Desktop.desktop is null in headless mode
2650 return new AlignFrame[] { Jalview.currentAlignFrame };
2653 JInternalFrame[] frames = Desktop.desktop.getAllFrames();
2659 List<AlignFrame> avp = new ArrayList<>();
2661 for (int i = frames.length - 1; i > -1; i--)
2663 if (frames[i] instanceof AlignFrame)
2665 avp.add((AlignFrame) frames[i]);
2667 else if (frames[i] instanceof SplitFrame)
2670 * Also check for a split frame containing an AlignFrame
2672 GSplitFrame sf = (GSplitFrame) frames[i];
2673 if (sf.getTopFrame() instanceof AlignFrame)
2675 avp.add((AlignFrame) sf.getTopFrame());
2677 if (sf.getBottomFrame() instanceof AlignFrame)
2679 avp.add((AlignFrame) sf.getBottomFrame());
2683 if (avp.size() == 0)
2687 AlignFrame afs[] = avp.toArray(new AlignFrame[avp.size()]);
2692 * Returns an array of any AppJmol frames in the Desktop (or null if none).
2696 public GStructureViewer[] getJmols()
2698 JInternalFrame[] frames = Desktop.desktop.getAllFrames();
2704 List<GStructureViewer> avp = new ArrayList<>();
2706 for (int i = frames.length - 1; i > -1; i--)
2708 if (frames[i] instanceof AppJmol)
2710 GStructureViewer af = (GStructureViewer) frames[i];
2714 if (avp.size() == 0)
2718 GStructureViewer afs[] = avp.toArray(new GStructureViewer[avp.size()]);
2723 * Add Groovy Support to Jalview
2726 public void groovyShell_actionPerformed()
2730 openGroovyConsole();
2731 } catch (Exception ex)
2733 jalview.bin.Cache.log.error("Groovy Shell Creation failed.", ex);
2734 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
2736 MessageManager.getString("label.couldnt_create_groovy_shell"),
2737 MessageManager.getString("label.groovy_support_failed"),
2738 JvOptionPane.ERROR_MESSAGE);
2743 * Open the Groovy console
2745 void openGroovyConsole()
2747 if (groovyConsole == null)
2749 groovyConsole = new groovy.ui.Console();
2750 groovyConsole.setVariable("Jalview", this);
2751 groovyConsole.run();
2754 * We allow only one console at a time, so that AlignFrame menu option
2755 * 'Calculate | Run Groovy script' is unambiguous.
2756 * Disable 'Groovy Console', and enable 'Run script', when the console is
2757 * opened, and the reverse when it is closed
2759 Window window = (Window) groovyConsole.getFrame();
2760 window.addWindowListener(new WindowAdapter()
2763 public void windowClosed(WindowEvent e)
2766 * rebind CMD-Q from Groovy Console to Jalview Quit
2769 enableExecuteGroovy(false);
2775 * show Groovy console window (after close and reopen)
2777 ((Window) groovyConsole.getFrame()).setVisible(true);
2780 * if we got this far, enable 'Run Groovy' in AlignFrame menus
2781 * and disable opening a second console
2783 enableExecuteGroovy(true);
2787 * Bind Ctrl/Cmd-Q to Quit - for reset as Groovy Console takes over this binding
2790 protected void addQuitHandler()
2792 getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
2793 .put(KeyStroke.getKeyStroke(KeyEvent.VK_Q,
2794 Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()),
2796 getRootPane().getActionMap().put("Quit", new AbstractAction()
2799 public void actionPerformed(ActionEvent e)
2807 * Enable or disable 'Run Groovy script' in AlignFrame calculate menus
2810 * true if Groovy console is open
2812 public void enableExecuteGroovy(boolean enabled)
2815 * disable opening a second Groovy console
2816 * (or re-enable when the console is closed)
2818 groovyShell.setEnabled(!enabled);
2820 AlignFrame[] alignFrames = getAlignFrames();
2821 if (alignFrames != null)
2823 for (AlignFrame af : alignFrames)
2825 af.setGroovyEnabled(enabled);
2831 * Progress bars managed by the IProgressIndicator method.
2833 private Hashtable<Long, JPanel> progressBars;
2835 private Hashtable<Long, IProgressIndicatorHandler> progressBarHandlers;
2840 * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
2843 public void setProgressBar(String message, long id)
2845 Platform.timeCheck("Desktop " + message, Platform.TIME_MARK);
2847 if (progressBars == null)
2849 progressBars = new Hashtable<>();
2850 progressBarHandlers = new Hashtable<>();
2853 if (progressBars.get(new Long(id)) != null)
2855 JPanel panel = progressBars.remove(new Long(id));
2856 if (progressBarHandlers.contains(new Long(id)))
2858 progressBarHandlers.remove(new Long(id));
2860 removeProgressPanel(panel);
2864 progressBars.put(new Long(id), addProgressPanel(message));
2871 * @see jalview.gui.IProgressIndicator#registerHandler(long,
2872 * jalview.gui.IProgressIndicatorHandler)
2875 public void registerHandler(final long id,
2876 final IProgressIndicatorHandler handler)
2878 if (progressBarHandlers == null
2879 || !progressBars.containsKey(new Long(id)))
2881 throw new Error(MessageManager.getString(
2882 "error.call_setprogressbar_before_registering_handler"));
2884 progressBarHandlers.put(new Long(id), handler);
2885 final JPanel progressPanel = progressBars.get(new Long(id));
2886 if (handler.canCancel())
2888 JButton cancel = new JButton(
2889 MessageManager.getString("action.cancel"));
2890 final IProgressIndicator us = this;
2891 cancel.addActionListener(new ActionListener()
2895 public void actionPerformed(ActionEvent e)
2897 handler.cancelActivity(id);
2898 us.setProgressBar(MessageManager
2899 .formatMessage("label.cancelled_params", new Object[]
2900 { ((JLabel) progressPanel.getComponent(0)).getText() }),
2904 progressPanel.add(cancel, BorderLayout.EAST);
2910 * @return true if any progress bars are still active
2913 public boolean operationInProgress()
2915 if (progressBars != null && progressBars.size() > 0)
2923 * This will return the first AlignFrame holding the given viewport instance. It
2924 * will break if there are more than one AlignFrames viewing a particular av.
2927 * @return alignFrame for viewport
2929 public static AlignFrame getAlignFrameFor(AlignViewportI viewport)
2931 if (desktop != null)
2933 AlignmentPanel[] aps = getAlignmentPanels(
2934 viewport.getSequenceSetId());
2935 for (int panel = 0; aps != null && panel < aps.length; panel++)
2937 if (aps[panel] != null && aps[panel].av == viewport)
2939 return aps[panel].alignFrame;
2946 public VamsasApplication getVamsasApplication()
2953 * flag set if jalview GUI is being operated programmatically
2955 private boolean inBatchMode = false;
2958 * check if jalview GUI is being operated programmatically
2960 * @return inBatchMode
2962 public boolean isInBatchMode()
2968 * set flag if jalview GUI is being operated programmatically
2970 * @param inBatchMode
2972 public void setInBatchMode(boolean inBatchMode)
2974 this.inBatchMode = inBatchMode;
2977 public void startServiceDiscovery()
2979 startServiceDiscovery(false);
2982 public void startServiceDiscovery(boolean blocking)
2984 boolean alive = true;
2985 Thread t0 = null, t1 = null, t2 = null;
2986 // JAL-940 - JALVIEW 1 services are now being EOLed as of JABA 2.1 release
2989 // todo: changesupport handlers need to be transferred
2990 if (discoverer == null)
2992 discoverer = new jalview.ws.jws1.Discoverer();
2993 // register PCS handler for desktop.
2994 discoverer.addPropertyChangeListener(changeSupport);
2996 // JAL-940 - disabled JWS1 service configuration - always start discoverer
2997 // until we phase out completely
2998 (t0 = new Thread(discoverer)).start();
3001 if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
3003 t2 = jalview.ws.jws2.Jws2Discoverer.getDiscoverer()
3004 .startDiscoverer(changeSupport);
3008 // TODO: do rest service discovery
3017 } catch (Exception e)
3020 alive = (t1 != null && t1.isAlive()) || (t2 != null && t2.isAlive())
3021 || (t3 != null && t3.isAlive())
3022 || (t0 != null && t0.isAlive());
3028 * called to check if the service discovery process completed successfully.
3032 protected void JalviewServicesChanged(PropertyChangeEvent evt)
3034 if (evt.getNewValue() == null || evt.getNewValue() instanceof Vector)
3036 final String ermsg = jalview.ws.jws2.Jws2Discoverer.getDiscoverer()
3037 .getErrorMessages();
3040 if (Cache.getDefault("SHOW_WSDISCOVERY_ERRORS", true))
3042 if (serviceChangedDialog == null)
3044 // only run if we aren't already displaying one of these.
3045 addDialogThread(serviceChangedDialog = new Runnable()
3052 * JalviewDialog jd =new JalviewDialog() {
3054 * @Override protected void cancelPressed() { // TODO
3055 * Auto-generated method stub
3057 * }@Override protected void okPressed() { // TODO
3058 * Auto-generated method stub
3060 * }@Override protected void raiseClosed() { // TODO
3061 * Auto-generated method stub
3063 * } }; jd.initDialogFrame(new
3064 * JLabel("<html><table width=\"450\"><tr><td>" + ermsg +
3065 * "<br/>It may be that you have invalid JABA URLs in your web service preferences,"
3066 * + " or mis-configured HTTP proxy settings.<br/>" +
3067 * "Check the <em>Connections</em> and <em>Web services</em> tab of the"
3069 * " Tools->Preferences dialog box to change them.</td></tr></table></html>"
3070 * ), true, true, "Web Service Configuration Problem", 450,
3073 * jd.waitForInput();
3075 JvOptionPane.showConfirmDialog(Desktop.desktop,
3076 new JLabel("<html><table width=\"450\"><tr><td>"
3077 + ermsg + "</td></tr></table>"
3078 + "<p>It may be that you have invalid JABA URLs<br/>in your web service preferences,"
3079 + "<br>or as a command-line argument, or mis-configured HTTP proxy settings.</p>"
3080 + "<p>Check the <em>Connections</em> and <em>Web services</em> tab<br/>of the"
3081 + " Tools->Preferences dialog box to change them.</p></html>"),
3082 "Web Service Configuration Problem",
3083 JvOptionPane.DEFAULT_OPTION,
3084 JvOptionPane.ERROR_MESSAGE);
3085 serviceChangedDialog = null;
3094 "Errors reported by JABA discovery service. Check web services preferences.\n"
3101 private Runnable serviceChangedDialog = null;
3104 * start a thread to open a URL in the configured browser. Pops up a warning
3105 * dialog to the user if there is an exception when calling out to the browser
3110 public static void showUrl(final String url)
3112 showUrl(url, Desktop.instance);
3116 * Like showUrl but allows progress handler to be specified
3120 * (null) or object implementing IProgressIndicator
3122 public static void showUrl(final String url,
3123 final IProgressIndicator progress)
3125 new Thread(new Runnable()
3132 if (progress != null)
3134 progress.setProgressBar(MessageManager
3135 .formatMessage("status.opening_params", new Object[]
3136 { url }), this.hashCode());
3138 jalview.util.BrowserLauncher.openURL(url);
3139 } catch (Exception ex)
3141 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
3143 .getString("label.web_browser_not_found_unix"),
3144 MessageManager.getString("label.web_browser_not_found"),
3145 JvOptionPane.WARNING_MESSAGE);
3147 ex.printStackTrace();
3149 if (progress != null)
3151 progress.setProgressBar(null, this.hashCode());
3157 public static WsParamSetManager wsparamManager = null;
3159 public static ParamManager getUserParameterStore()
3161 if (wsparamManager == null)
3163 wsparamManager = new WsParamSetManager();
3165 return wsparamManager;
3169 * static hyperlink handler proxy method for use by Jalview's internal windows
3173 public static void hyperlinkUpdate(HyperlinkEvent e)
3175 if (e.getEventType() == EventType.ACTIVATED)
3180 url = e.getURL().toString();
3181 Desktop.showUrl(url);
3182 } catch (Exception x)
3186 if (Cache.log != null)
3188 Cache.log.error("Couldn't handle string " + url + " as a URL.");
3193 "Couldn't handle string " + url + " as a URL.");
3196 // ignore any exceptions due to dud links.
3203 * single thread that handles display of dialogs to user.
3205 ExecutorService dialogExecutor = Executors.newSingleThreadExecutor();
3208 * flag indicating if dialogExecutor should try to acquire a permit
3210 private volatile boolean dialogPause = true;
3215 private java.util.concurrent.Semaphore block = new Semaphore(0);
3217 private static groovy.ui.Console groovyConsole;
3220 * add another dialog thread to the queue
3224 public void addDialogThread(final Runnable prompter)
3226 dialogExecutor.submit(new Runnable()
3236 } catch (InterruptedException x)
3241 if (instance == null)
3247 SwingUtilities.invokeAndWait(prompter);
3248 } catch (Exception q)
3250 Cache.log.warn("Unexpected Exception in dialog thread.", q);
3256 public void startDialogQueue()
3258 // set the flag so we don't pause waiting for another permit and semaphore
3259 // the current task to begin
3260 dialogPause = false;
3265 * Outputs an image of the desktop to file in EPS format, after prompting the
3266 * user for choice of Text or Lineart character rendering (unless a preference
3267 * has been set). The file name is generated as
3270 * Jalview_snapshot_nnnnn.eps where nnnnn is the current timestamp in milliseconds
3274 protected void snapShotWindow_actionPerformed(ActionEvent e)
3276 // currently the menu option to do this is not shown
3279 int width = getWidth();
3280 int height = getHeight();
3282 "Jalview_snapshot_" + System.currentTimeMillis() + ".eps");
3283 ImageWriterI writer = new ImageWriterI()
3286 public void exportImage(Graphics g) throws Exception
3289 Cache.log.info("Successfully written snapshot to file "
3290 + of.getAbsolutePath());
3293 String title = "View of desktop";
3294 ImageExporter exporter = new ImageExporter(writer, null, TYPE.EPS,
3296 exporter.doExport(of, this, width, height, title);
3300 * Explode the views in the given SplitFrame into separate SplitFrame windows.
3301 * This respects (remembers) any previous 'exploded geometry' i.e. the size and
3302 * location last time the view was expanded (if any). However it does not
3303 * remember the split pane divider location - this is set to match the
3304 * 'exploding' frame.
3308 public void explodeViews(SplitFrame sf)
3310 AlignFrame oldTopFrame = (AlignFrame) sf.getTopFrame();
3311 AlignFrame oldBottomFrame = (AlignFrame) sf.getBottomFrame();
3312 List<? extends AlignmentViewPanel> topPanels = oldTopFrame
3314 List<? extends AlignmentViewPanel> bottomPanels = oldBottomFrame
3316 int viewCount = topPanels.size();
3323 * Processing in reverse order works, forwards order leaves the first panels
3324 * not visible. I don't know why!
3326 for (int i = viewCount - 1; i >= 0; i--)
3329 * Make new top and bottom frames. These take over the respective
3330 * AlignmentPanel objects, including their AlignmentViewports, so the
3331 * cdna/protein relationships between the viewports is carried over to the
3334 * explodedGeometry holds the (x, y) position of the previously exploded
3335 * SplitFrame, and the (width, height) of the AlignFrame component
3337 AlignmentPanel topPanel = (AlignmentPanel) topPanels.get(i);
3338 AlignFrame newTopFrame = new AlignFrame(topPanel);
3339 newTopFrame.setSize(oldTopFrame.getSize());
3340 newTopFrame.setVisible(true);
3341 Rectangle geometry = ((AlignViewport) topPanel.getAlignViewport())
3342 .getExplodedGeometry();
3343 if (geometry != null)
3345 newTopFrame.setSize(geometry.getSize());
3348 AlignmentPanel bottomPanel = (AlignmentPanel) bottomPanels.get(i);
3349 AlignFrame newBottomFrame = new AlignFrame(bottomPanel);
3350 newBottomFrame.setSize(oldBottomFrame.getSize());
3351 newBottomFrame.setVisible(true);
3352 geometry = ((AlignViewport) bottomPanel.getAlignViewport())
3353 .getExplodedGeometry();
3354 if (geometry != null)
3356 newBottomFrame.setSize(geometry.getSize());
3359 topPanel.av.setGatherViewsHere(false);
3360 bottomPanel.av.setGatherViewsHere(false);
3361 JInternalFrame splitFrame = new SplitFrame(newTopFrame,
3363 if (geometry != null)
3365 splitFrame.setLocation(geometry.getLocation());
3367 Desktop.addInternalFrame(splitFrame, sf.getTitle(), -1, -1);
3371 * Clear references to the panels (now relocated in the new SplitFrames)
3372 * before closing the old SplitFrame.
3375 bottomPanels.clear();
3380 * Gather expanded split frames, sharing the same pairs of sequence set ids,
3381 * back into the given SplitFrame as additional views. Note that the gathered
3382 * frames may themselves have multiple views.
3386 public void gatherViews(GSplitFrame source)
3389 * special handling of explodedGeometry for a view within a SplitFrame: - it
3390 * holds the (x, y) position of the enclosing SplitFrame, and the (width,
3391 * height) of the AlignFrame component
3393 AlignFrame myTopFrame = (AlignFrame) source.getTopFrame();
3394 AlignFrame myBottomFrame = (AlignFrame) source.getBottomFrame();
3395 myTopFrame.viewport.setExplodedGeometry(new Rectangle(source.getX(),
3396 source.getY(), myTopFrame.getWidth(), myTopFrame.getHeight()));
3397 myBottomFrame.viewport
3398 .setExplodedGeometry(new Rectangle(source.getX(), source.getY(),
3399 myBottomFrame.getWidth(), myBottomFrame.getHeight()));
3400 myTopFrame.viewport.setGatherViewsHere(true);
3401 myBottomFrame.viewport.setGatherViewsHere(true);
3402 String topViewId = myTopFrame.viewport.getSequenceSetId();
3403 String bottomViewId = myBottomFrame.viewport.getSequenceSetId();
3405 JInternalFrame[] frames = desktop.getAllFrames();
3406 for (JInternalFrame frame : frames)
3408 if (frame instanceof SplitFrame && frame != source)
3410 SplitFrame sf = (SplitFrame) frame;
3411 AlignFrame topFrame = (AlignFrame) sf.getTopFrame();
3412 AlignFrame bottomFrame = (AlignFrame) sf.getBottomFrame();
3413 boolean gatherThis = false;
3414 for (int a = 0; a < topFrame.alignPanels.size(); a++)
3416 AlignmentPanel topPanel = topFrame.alignPanels.get(a);
3417 AlignmentPanel bottomPanel = bottomFrame.alignPanels.get(a);
3418 if (topViewId.equals(topPanel.av.getSequenceSetId())
3419 && bottomViewId.equals(bottomPanel.av.getSequenceSetId()))
3422 topPanel.av.setGatherViewsHere(false);
3423 bottomPanel.av.setGatherViewsHere(false);
3424 topPanel.av.setExplodedGeometry(
3425 new Rectangle(sf.getLocation(), topFrame.getSize()));
3426 bottomPanel.av.setExplodedGeometry(
3427 new Rectangle(sf.getLocation(), bottomFrame.getSize()));
3428 myTopFrame.addAlignmentPanel(topPanel, false);
3429 myBottomFrame.addAlignmentPanel(bottomPanel, false);
3435 topFrame.getAlignPanels().clear();
3436 bottomFrame.getAlignPanels().clear();
3443 * The dust settles...give focus to the tab we did this from.
3445 myTopFrame.setDisplayedView(myTopFrame.alignPanel);
3448 public static groovy.ui.Console getGroovyConsole()
3450 return groovyConsole;
3454 * handles the payload of a drag and drop event.
3456 * TODO refactor to desktop utilities class
3459 * - Data source strings extracted from the drop event
3461 * - protocol for each data source extracted from the drop event
3465 * - the payload from the drop event
3468 public static void transferFromDropTarget(List<Object> files,
3469 List<DataSourceType> protocols, DropTargetDropEvent evt,
3470 Transferable t) throws Exception
3473 // BH 2018 changed List<String> to List<Object> to allow for File from SwingJS
3475 // DataFlavor[] flavors = t.getTransferDataFlavors();
3476 // for (int i = 0; i < flavors.length; i++) {
3477 // if (flavors[i].isFlavorJavaFileListType()) {
3478 // evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
3479 // List<File> list = (List<File>) t.getTransferData(flavors[i]);
3480 // for (int j = 0; j < list.size(); j++) {
3481 // File file = (File) list.get(j);
3482 // byte[] data = getDroppedFileBytes(file);
3483 // fileName.setText(file.getName() + " - " + data.length + " " +
3484 // evt.getLocation());
3485 // JTextArea target = (JTextArea) ((DropTarget) evt.getSource()).getComponent();
3486 // target.setText(new String(data));
3488 // dtde.dropComplete(true);
3493 DataFlavor uriListFlavor = new DataFlavor(
3494 "text/uri-list;class=java.lang.String"), urlFlavour = null;
3497 urlFlavour = new DataFlavor(
3498 "application/x-java-url; class=java.net.URL");
3499 } catch (ClassNotFoundException cfe)
3501 Cache.log.debug("Couldn't instantiate the URL dataflavor.", cfe);
3504 if (urlFlavour != null && t.isDataFlavorSupported(urlFlavour))
3509 java.net.URL url = (URL) t.getTransferData(urlFlavour);
3510 // nb: java 8 osx bug https://bugs.openjdk.java.net/browse/JDK-8156099
3511 // means url may be null.
3514 protocols.add(DataSourceType.URL);
3515 files.add(url.toString());
3516 Cache.log.debug("Drop handled as URL dataflavor "
3517 + files.get(files.size() - 1));
3522 if (Platform.isAMacAndNotJS())
3525 "Please ignore plist error - occurs due to problem with java 8 on OSX");
3529 } catch (Throwable ex)
3531 Cache.log.debug("URL drop handler failed.", ex);
3534 if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
3536 // Works on Windows and MacOSX
3537 Cache.log.debug("Drop handled as javaFileListFlavor");
3538 for (Object file : (List) t
3539 .getTransferData(DataFlavor.javaFileListFlavor))
3542 protocols.add(DataSourceType.FILE);
3547 // Unix like behaviour
3548 boolean added = false;
3550 if (t.isDataFlavorSupported(uriListFlavor))
3552 Cache.log.debug("Drop handled as uriListFlavor");
3553 // This is used by Unix drag system
3554 data = (String) t.getTransferData(uriListFlavor);
3558 // fallback to text: workaround - on OSX where there's a JVM bug
3559 Cache.log.debug("standard URIListFlavor failed. Trying text");
3560 // try text fallback
3561 DataFlavor textDf = new DataFlavor(
3562 "text/plain;class=java.lang.String");
3563 if (t.isDataFlavorSupported(textDf))
3565 data = (String) t.getTransferData(textDf);
3568 Cache.log.debug("Plain text drop content returned "
3569 + (data == null ? "Null - failed" : data));
3574 while (protocols.size() < files.size())
3576 Cache.log.debug("Adding missing FILE protocol for "
3577 + files.get(protocols.size()));
3578 protocols.add(DataSourceType.FILE);
3580 for (java.util.StringTokenizer st = new java.util.StringTokenizer(
3581 data, "\r\n"); st.hasMoreTokens();)
3584 String s = st.nextToken();
3585 if (s.startsWith("#"))
3587 // the line is a comment (as per the RFC 2483)
3590 java.net.URI uri = new java.net.URI(s);
3591 if (uri.getScheme().toLowerCase().startsWith("http"))
3593 protocols.add(DataSourceType.URL);
3594 files.add(uri.toString());
3598 // otherwise preserve old behaviour: catch all for file objects
3599 java.io.File file = new java.io.File(uri);
3600 protocols.add(DataSourceType.FILE);
3601 files.add(file.toString());
3606 if (Cache.log.isDebugEnabled())
3608 if (data == null || !added)
3611 if (t.getTransferDataFlavors() != null
3612 && t.getTransferDataFlavors().length > 0)
3615 "Couldn't resolve drop data. Here are the supported flavors:");
3616 for (DataFlavor fl : t.getTransferDataFlavors())
3619 "Supported transfer dataflavor: " + fl.toString());
3620 Object df = t.getTransferData(fl);
3623 Cache.log.debug("Retrieves: " + df);
3627 Cache.log.debug("Retrieved nothing");
3633 Cache.log.debug("Couldn't resolve dataflavor for drop: "
3639 if (Platform.isWindowsAndNotJS())
3641 Cache.log.debug("Scanning dropped content for Windows Link Files");
3643 // resolve any .lnk files in the file drop
3644 for (int f = 0; f < files.size(); f++)
3646 String source = files.get(f).toString().toLowerCase();
3647 if (protocols.get(f).equals(DataSourceType.FILE)
3648 && (source.endsWith(".lnk") || source.endsWith(".url")
3649 || source.endsWith(".site")))
3653 Object obj = files.get(f);
3654 File lf = (obj instanceof File ? (File) obj
3655 : new File((String) obj));
3656 // process link file to get a URL
3657 Cache.log.debug("Found potential link file: " + lf);
3658 WindowsShortcut wscfile = new WindowsShortcut(lf);
3659 String fullname = wscfile.getRealFilename();
3660 protocols.set(f, FormatAdapter.checkProtocol(fullname));
3661 files.set(f, fullname);
3662 Cache.log.debug("Parsed real filename " + fullname
3663 + " to extract protocol: " + protocols.get(f));
3664 } catch (Exception ex)
3667 "Couldn't parse " + files.get(f) + " as a link file.",
3676 * Sets the Preferences property for experimental features to True or False
3677 * depending on the state of the controlling menu item
3680 protected void showExperimental_actionPerformed(boolean selected)
3682 Cache.setProperty(EXPERIMENTAL_FEATURES, Boolean.toString(selected));
3686 * Answers a (possibly empty) list of any structure viewer frames (currently for
3687 * either Jmol or Chimera) which are currently open. This may optionally be
3688 * restricted to viewers of a specified class, or viewers linked to a specified
3692 * if not null, only return viewers linked to this panel
3693 * @param structureViewerClass
3694 * if not null, only return viewers of this class
3697 public List<StructureViewerBase> getStructureViewers(
3698 AlignmentPanel apanel,
3699 Class<? extends StructureViewerBase> structureViewerClass)
3701 List<StructureViewerBase> result = new ArrayList<>();
3702 JInternalFrame[] frames = Desktop.instance.getAllFrames();
3704 for (JInternalFrame frame : frames)
3706 if (frame instanceof StructureViewerBase)
3708 if (structureViewerClass == null
3709 || structureViewerClass.isInstance(frame))
3712 || ((StructureViewerBase) frame).isLinkedWith(apanel))
3714 result.add((StructureViewerBase) frame);