2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
5 * This file is part of Jalview.
7 * Jalview is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation, either version 3
10 * of the License, or (at your option) any later version.
12 * Jalview is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19 * The Jalview Authors are detailed in the 'AUTHORS' file.
23 import jalview.api.AlignViewportI;
24 import jalview.api.AlignmentViewPanel;
25 import jalview.bin.Cache;
26 import jalview.bin.Jalview;
27 import jalview.gui.ImageExporter.ImageWriterI;
28 import jalview.io.BackupFiles;
29 import jalview.io.DataSourceType;
30 import jalview.io.FileFormat;
31 import jalview.io.FileFormatException;
32 import jalview.io.FileFormatI;
33 import jalview.io.FileFormats;
34 import jalview.io.FileLoader;
35 import jalview.io.FormatAdapter;
36 import jalview.io.IdentifyFile;
37 import jalview.io.JalviewFileChooser;
38 import jalview.io.JalviewFileView;
39 import jalview.jbgui.GSplitFrame;
40 import jalview.jbgui.GStructureViewer;
41 import jalview.project.Jalview2XML;
42 import jalview.structure.StructureSelectionManager;
43 import jalview.urls.IdOrgSettings;
44 import jalview.util.BrowserLauncher;
45 import jalview.util.ImageMaker.TYPE;
46 import jalview.util.MessageManager;
47 import jalview.util.Platform;
48 import jalview.util.UrlConstants;
49 import jalview.viewmodel.AlignmentViewport;
50 import jalview.ws.params.ParamManager;
51 import jalview.ws.utils.UrlDownloadClient;
53 import java.awt.BorderLayout;
54 import java.awt.Color;
55 import java.awt.Dimension;
56 import java.awt.FontMetrics;
57 import java.awt.Graphics;
58 import java.awt.GridLayout;
59 import java.awt.Point;
60 import java.awt.Rectangle;
61 import java.awt.Toolkit;
62 import java.awt.Window;
63 import java.awt.datatransfer.Clipboard;
64 import java.awt.datatransfer.ClipboardOwner;
65 import java.awt.datatransfer.DataFlavor;
66 import java.awt.datatransfer.Transferable;
67 import java.awt.dnd.DnDConstants;
68 import java.awt.dnd.DropTargetDragEvent;
69 import java.awt.dnd.DropTargetDropEvent;
70 import java.awt.dnd.DropTargetEvent;
71 import java.awt.dnd.DropTargetListener;
72 import java.awt.event.ActionEvent;
73 import java.awt.event.ActionListener;
74 import java.awt.event.InputEvent;
75 import java.awt.event.KeyEvent;
76 import java.awt.event.MouseAdapter;
77 import java.awt.event.MouseEvent;
78 import java.awt.event.WindowAdapter;
79 import java.awt.event.WindowEvent;
80 import java.beans.PropertyChangeEvent;
81 import java.beans.PropertyChangeListener;
82 import java.io.BufferedInputStream;
84 import java.io.FileOutputStream;
85 import java.io.FileWriter;
86 import java.io.IOException;
88 import java.util.ArrayList;
89 import java.util.HashMap;
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.JInternalFrame;
112 import javax.swing.JLabel;
113 import javax.swing.JMenuItem;
114 import javax.swing.JPanel;
115 import javax.swing.JPopupMenu;
116 import javax.swing.JProgressBar;
117 import javax.swing.JTextField;
118 import javax.swing.KeyStroke;
119 import javax.swing.SwingUtilities;
120 import javax.swing.event.HyperlinkEvent;
121 import javax.swing.event.HyperlinkEvent.EventType;
122 import javax.swing.event.InternalFrameAdapter;
123 import javax.swing.event.InternalFrameEvent;
124 import javax.swing.event.MenuEvent;
125 import javax.swing.event.MenuListener;
127 import org.stackoverflowusers.file.WindowsShortcut;
134 * @version $Revision: 1.155 $
136 public class Desktop extends jalview.jbgui.GDesktop
137 implements DropTargetListener, ClipboardOwner, IProgressIndicator,
138 jalview.api.StructureSelectionManagerProvider
140 private static int DEFAULT_MIN_WIDTH = 300;
142 private static int DEFAULT_MIN_HEIGHT = 250;
144 private static int ALIGN_FRAME_DEFAULT_MIN_WIDTH = 600;
146 private static int ALIGN_FRAME_DEFAULT_MIN_HEIGHT = 70;
148 private static final String EXPERIMENTAL_FEATURES = "EXPERIMENTAL_FEATURES";
150 protected static final String CONFIRM_KEYBOARD_QUIT = "CONFIRM_KEYBOARD_QUIT";
152 public static HashMap<String, FileWriter> savingFiles = new HashMap<>();
154 private JalviewChangeSupport changeSupport = new JalviewChangeSupport();
157 * news reader - null if it was never started.
159 private BlogReader jvnews = null;
161 private File projectFile;
165 * @see jalview.gui.JalviewChangeSupport#addJalviewPropertyChangeListener(java.beans.PropertyChangeListener)
167 public void addJalviewPropertyChangeListener(
168 PropertyChangeListener listener)
170 changeSupport.addJalviewPropertyChangeListener(listener);
174 * @param propertyName
176 * @see jalview.gui.JalviewChangeSupport#addJalviewPropertyChangeListener(java.lang.String,
177 * java.beans.PropertyChangeListener)
179 public void addJalviewPropertyChangeListener(String propertyName,
180 PropertyChangeListener listener)
182 changeSupport.addJalviewPropertyChangeListener(propertyName, listener);
186 * @param propertyName
188 * @see jalview.gui.JalviewChangeSupport#removeJalviewPropertyChangeListener(java.lang.String,
189 * java.beans.PropertyChangeListener)
191 public void removeJalviewPropertyChangeListener(String propertyName,
192 PropertyChangeListener listener)
194 changeSupport.removeJalviewPropertyChangeListener(propertyName,
198 /** Singleton Desktop instance */
199 public static Desktop instance;
201 public static MyDesktopPane desktop;
203 public static MyDesktopPane getDesktop()
205 // BH 2018 could use currentThread() here as a reference to a
206 // Hashtable<Thread, MyDesktopPane> in JavaScript
210 static int openFrameCount = 0;
212 static final int xOffset = 30;
214 static final int yOffset = 30;
216 public static jalview.ws.jws1.Discoverer discoverer;
218 public static Object[] jalviewClipboard;
220 public static boolean internalCopy = false;
222 static int fileLoadingCount = 0;
224 class MyDesktopManager implements DesktopManager
227 private DesktopManager delegate;
229 public MyDesktopManager(DesktopManager delegate)
231 this.delegate = delegate;
235 public void activateFrame(JInternalFrame f)
239 delegate.activateFrame(f);
240 } catch (NullPointerException npe)
242 Point p = getMousePosition();
243 instance.showPasteMenu(p.x, p.y);
248 public void beginDraggingFrame(JComponent f)
250 delegate.beginDraggingFrame(f);
254 public void beginResizingFrame(JComponent f, int direction)
256 delegate.beginResizingFrame(f, direction);
260 public void closeFrame(JInternalFrame f)
262 delegate.closeFrame(f);
266 public void deactivateFrame(JInternalFrame f)
268 delegate.deactivateFrame(f);
272 public void deiconifyFrame(JInternalFrame f)
274 delegate.deiconifyFrame(f);
278 public void dragFrame(JComponent f, int newX, int newY)
284 delegate.dragFrame(f, newX, newY);
288 public void endDraggingFrame(JComponent f)
290 delegate.endDraggingFrame(f);
295 public void endResizingFrame(JComponent f)
297 delegate.endResizingFrame(f);
302 public void iconifyFrame(JInternalFrame f)
304 delegate.iconifyFrame(f);
308 public void maximizeFrame(JInternalFrame f)
310 delegate.maximizeFrame(f);
314 public void minimizeFrame(JInternalFrame f)
316 delegate.minimizeFrame(f);
320 public void openFrame(JInternalFrame f)
322 delegate.openFrame(f);
326 public void resizeFrame(JComponent f, int newX, int newY, int newWidth,
333 delegate.resizeFrame(f, newX, newY, newWidth, newHeight);
337 public void setBoundsForFrame(JComponent f, int newX, int newY,
338 int newWidth, int newHeight)
340 delegate.setBoundsForFrame(f, newX, newY, newWidth, newHeight);
343 // All other methods, simply delegate
348 * Creates a new Desktop object.
354 * A note to implementors. It is ESSENTIAL that any activities that might
355 * block are spawned off as threads rather than waited for during this
359 if (!Platform.isJS())
361 doVamsasClientCheck();
364 doConfigureStructurePrefs();
365 setTitle("Jalview " + jalview.bin.Cache.getProperty("VERSION"));
367 if (!Platform.isAMac())
369 // this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
373 this.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
379 if (!Platform.isJS())
384 APQHandlers.setAPQHandlers(this);
386 } catch (Exception e)
388 System.out.println("Cannot set APQHandlers");
389 // e.printStackTrace();
390 } catch (Throwable t)
392 System.out.println("Cannot set APQHandlers");
393 // t.printStackTrace();
397 addWindowListener(new WindowAdapter()
401 public void windowClosing(WindowEvent ev)
407 boolean selmemusage = jalview.bin.Cache.getDefault("SHOW_MEMUSAGE",
410 boolean showjconsole = jalview.bin.Cache.getDefault("SHOW_JAVA_CONSOLE",
412 desktop = new MyDesktopPane(selmemusage);
414 showMemusage.setSelected(selmemusage);
415 desktop.setBackground(Color.white);
417 getContentPane().setLayout(new BorderLayout());
418 // alternate config - have scrollbars - see notes in JAL-153
419 // JScrollPane sp = new JScrollPane();
420 // sp.getViewport().setView(desktop);
421 // getContentPane().add(sp, BorderLayout.CENTER);
423 // BH 2018 - just an experiment to try unclipped JInternalFrames.
426 getRootPane().putClientProperty("swingjs.overflow.hidden", "false");
429 getContentPane().add(desktop, BorderLayout.CENTER);
430 desktop.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
432 // This line prevents Windows Look&Feel resizing all new windows to maximum
433 // if previous window was maximised
434 desktop.setDesktopManager(new MyDesktopManager(
435 (Platform.isWindowsAndNotJS() ? new DefaultDesktopManager()
436 : Platform.isAMacAndNotJS()
437 ? new AquaInternalFrameManager(
438 desktop.getDesktopManager())
439 : desktop.getDesktopManager())));
441 Rectangle dims = getLastKnownDimensions("");
448 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
449 int xPos = Math.max(5, (screenSize.width - 900) / 2);
450 int yPos = Math.max(5, (screenSize.height - 650) / 2);
451 setBounds(xPos, yPos, 900, 650);
454 if (!Platform.isJS())
462 jconsole = new Console(this, showjconsole);
463 // add essential build information
464 jconsole.setHeader("Jalview Version: "
465 + jalview.bin.Cache.getProperty("VERSION") + "\n"
466 + "Jalview Installation: "
467 + jalview.bin.Cache.getDefault("INSTALLATION", "unknown")
468 + "\n" + "Build Date: "
469 + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown") + "\n"
470 + "Java version: " + System.getProperty("java.version") + "\n"
471 + System.getProperty("os.arch") + " "
472 + System.getProperty("os.name") + " "
473 + System.getProperty("os.version")
474 + (jalview.bin.Cache.getProperty("VERSION").equals("DEVELOPMENT")
476 + System.getProperty(
478 + File.separator + "bin"
479 + File.separator + "java"
482 showConsole(showjconsole);
484 showNews.setVisible(false);
486 experimentalFeatures.setSelected(showExperimental());
488 getIdentifiersOrgData();
492 // Spawn a thread that shows the splashscreen
494 SwingUtilities.invokeLater(new Runnable()
503 // Thread off a new instance of the file chooser - this reduces the time
504 // it takes to open it later on.
505 new Thread(new Runnable()
510 Cache.log.debug("Filechooser init thread started.");
511 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
512 JalviewFileChooser.forRead(Cache.getProperty("LAST_DIRECTORY"),
514 Cache.log.debug("Filechooser init thread finished.");
517 // Add the service change listener
518 changeSupport.addJalviewPropertyChangeListener("services",
519 new PropertyChangeListener()
523 public void propertyChange(PropertyChangeEvent evt)
525 Cache.log.debug("Firing service changed event for "
526 + evt.getNewValue());
527 JalviewServicesChanged(evt);
534 this.setDropTarget(new java.awt.dnd.DropTarget(desktop, this));
536 this.addWindowListener(new WindowAdapter()
539 public void windowClosing(WindowEvent evt)
546 this.addMouseListener(ma = new MouseAdapter()
549 public void mousePressed(MouseEvent evt)
551 if (evt.isPopupTrigger()) // Mac
553 showPasteMenu(evt.getX(), evt.getY());
558 public void mouseReleased(MouseEvent evt)
560 if (evt.isPopupTrigger()) // Windows
562 showPasteMenu(evt.getX(), evt.getY());
566 desktop.addMouseListener(ma);
571 * Answers true if user preferences to enable experimental features is True
576 public boolean showExperimental()
578 String experimental = Cache.getDefault(EXPERIMENTAL_FEATURES,
579 Boolean.FALSE.toString());
580 return Boolean.valueOf(experimental).booleanValue();
583 public void doConfigureStructurePrefs()
585 // configure services
586 StructureSelectionManager ssm = StructureSelectionManager
587 .getStructureSelectionManager(this);
588 if (jalview.bin.Cache.getDefault(Preferences.ADD_SS_ANN, true))
590 ssm.setAddTempFacAnnot(jalview.bin.Cache
591 .getDefault(Preferences.ADD_TEMPFACT_ANN, true));
592 ssm.setProcessSecondaryStructure(jalview.bin.Cache
593 .getDefault(Preferences.STRUCT_FROM_PDB, true));
594 ssm.setSecStructServices(
595 jalview.bin.Cache.getDefault(Preferences.USE_RNAVIEW, true));
599 ssm.setAddTempFacAnnot(false);
600 ssm.setProcessSecondaryStructure(false);
601 ssm.setSecStructServices(false);
605 public void checkForNews()
607 final Desktop me = this;
608 // Thread off the news reader, in case there are connection problems.
609 new Thread(new Runnable()
614 Cache.log.debug("Starting news thread.");
615 jvnews = new BlogReader(me);
616 showNews.setVisible(true);
617 Cache.log.debug("Completed news thread.");
622 public void getIdentifiersOrgData()
624 // Thread off the identifiers fetcher
625 new Thread(new Runnable()
630 Cache.log.debug("Downloading data from identifiers.org");
631 UrlDownloadClient client = new UrlDownloadClient();
634 client.download(IdOrgSettings.getUrl(),
635 IdOrgSettings.getDownloadLocation());
636 } catch (IOException e)
638 Cache.log.debug("Exception downloading identifiers.org data"
647 protected void showNews_actionPerformed(ActionEvent e)
649 showNews(showNews.isSelected());
652 void showNews(boolean visible)
654 Cache.log.debug((visible ? "Showing" : "Hiding") + " news.");
655 showNews.setSelected(visible);
656 if (visible && !jvnews.isVisible())
658 new Thread(new Runnable()
663 long now = System.currentTimeMillis();
664 Desktop.instance.setProgressBar(
665 MessageManager.getString("status.refreshing_news"), now);
666 jvnews.refreshNews();
667 Desktop.instance.setProgressBar(null, now);
675 * recover the last known dimensions for a jalview window
678 * - empty string is desktop, all other windows have unique prefix
679 * @return null or last known dimensions scaled to current geometry (if last
680 * window geom was known)
682 Rectangle getLastKnownDimensions(String windowName)
684 // TODO: lock aspect ratio for scaling desktop Bug #0058199
685 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
686 String x = jalview.bin.Cache.getProperty(windowName + "SCREEN_X");
687 String y = jalview.bin.Cache.getProperty(windowName + "SCREEN_Y");
688 String width = jalview.bin.Cache
689 .getProperty(windowName + "SCREEN_WIDTH");
690 String height = jalview.bin.Cache
691 .getProperty(windowName + "SCREEN_HEIGHT");
692 if ((x != null) && (y != null) && (width != null) && (height != null))
694 int ix = Integer.parseInt(x), iy = Integer.parseInt(y),
695 iw = Integer.parseInt(width), ih = Integer.parseInt(height);
696 if (jalview.bin.Cache.getProperty("SCREENGEOMETRY_WIDTH") != null)
698 // attempt #1 - try to cope with change in screen geometry - this
699 // version doesn't preserve original jv aspect ratio.
700 // take ratio of current screen size vs original screen size.
701 double sw = ((1f * screenSize.width) / (1f * Integer.parseInt(
702 jalview.bin.Cache.getProperty("SCREENGEOMETRY_WIDTH"))));
703 double sh = ((1f * screenSize.height) / (1f * Integer.parseInt(
704 jalview.bin.Cache.getProperty("SCREENGEOMETRY_HEIGHT"))));
705 // rescale the bounds depending upon the current screen geometry.
706 ix = (int) (ix * sw);
707 iw = (int) (iw * sw);
708 iy = (int) (iy * sh);
709 ih = (int) (ih * sh);
710 while (ix >= screenSize.width)
712 jalview.bin.Cache.log.debug(
713 "Window geometry location recall error: shifting horizontal to within screenbounds.");
714 ix -= screenSize.width;
716 while (iy >= screenSize.height)
718 jalview.bin.Cache.log.debug(
719 "Window geometry location recall error: shifting vertical to within screenbounds.");
720 iy -= screenSize.height;
722 jalview.bin.Cache.log.debug(
723 "Got last known dimensions for " + windowName + ": x:" + ix
724 + " y:" + iy + " width:" + iw + " height:" + ih);
726 // return dimensions for new instance
727 return new Rectangle(ix, iy, iw, ih);
732 private void doVamsasClientCheck()
734 if (Cache.vamsasJarsPresent())
736 setupVamsasDisconnectedGui();
737 VamsasMenu.setVisible(true);
738 final Desktop us = this;
739 VamsasMenu.addMenuListener(new MenuListener()
741 // this listener remembers when the menu was first selected, and
742 // doesn't rebuild the session list until it has been cleared and
744 boolean refresh = true;
747 public void menuCanceled(MenuEvent e)
753 public void menuDeselected(MenuEvent e)
759 public void menuSelected(MenuEvent e)
763 us.buildVamsasStMenu();
768 vamsasStart.setVisible(true);
772 void showPasteMenu(int x, int y)
774 JPopupMenu popup = new JPopupMenu();
775 JMenuItem item = new JMenuItem(
776 MessageManager.getString("label.paste_new_window"));
777 item.addActionListener(new ActionListener()
780 public void actionPerformed(ActionEvent evt)
787 popup.show(this, x, y);
794 Clipboard c = Toolkit.getDefaultToolkit().getSystemClipboard();
795 Transferable contents = c.getContents(this);
797 if (contents != null)
799 String file = (String) contents
800 .getTransferData(DataFlavor.stringFlavor);
802 FileFormatI format = new IdentifyFile().identify(file,
803 DataSourceType.PASTE);
805 new FileLoader().LoadFile(file, DataSourceType.PASTE, format);
808 } catch (Exception ex)
811 "Unable to paste alignment from system clipboard:\n" + ex);
816 * Adds and opens the given frame to the desktop
827 public static synchronized void addInternalFrame(
828 final JInternalFrame frame, String title, int w, int h)
830 addInternalFrame(frame, title, true, w, h, true, false);
834 * Add an internal frame to the Jalview desktop
841 * When true, display frame immediately, otherwise, caller must call
842 * setVisible themselves.
848 public static synchronized void addInternalFrame(
849 final JInternalFrame frame, String title, boolean makeVisible,
852 addInternalFrame(frame, title, makeVisible, w, h, true, false);
856 * Add an internal frame to the Jalview desktop and make it visible
869 public static synchronized void addInternalFrame(
870 final JInternalFrame frame, String title, int w, int h,
873 addInternalFrame(frame, title, true, w, h, resizable, false);
877 * Add an internal frame to the Jalview desktop
884 * When true, display frame immediately, otherwise, caller must call
885 * setVisible themselves.
892 * @param ignoreMinSize
893 * Do not set the default minimum size for frame
895 public static synchronized void addInternalFrame(
896 final JInternalFrame frame, String title, boolean makeVisible,
897 int w, int h, boolean resizable, boolean ignoreMinSize)
900 // TODO: allow callers to determine X and Y position of frame (eg. via
902 // TODO: consider fixing method to update entries in the window submenu with
903 // the current window title
905 frame.setTitle(title);
906 if (frame.getWidth() < 1 || frame.getHeight() < 1)
910 // THIS IS A PUBLIC STATIC METHOD, SO IT MAY BE CALLED EVEN IN
911 // A HEADLESS STATE WHEN NO DESKTOP EXISTS. MUST RETURN
912 // IF JALVIEW IS RUNNING HEADLESS
913 // ///////////////////////////////////////////////
914 if (instance == null || (System.getProperty("java.awt.headless") != null
915 && System.getProperty("java.awt.headless").equals("true")))
924 frame.setMinimumSize(
925 new Dimension(DEFAULT_MIN_WIDTH, DEFAULT_MIN_HEIGHT));
927 // Set default dimension for Alignment Frame window.
928 // The Alignment Frame window could be added from a number of places,
930 // I did this here in order not to miss out on any Alignment frame.
931 if (frame instanceof AlignFrame)
933 frame.setMinimumSize(new Dimension(ALIGN_FRAME_DEFAULT_MIN_WIDTH,
934 ALIGN_FRAME_DEFAULT_MIN_HEIGHT));
938 frame.setVisible(makeVisible);
939 frame.setClosable(true);
940 frame.setResizable(resizable);
941 frame.setMaximizable(resizable);
942 frame.setIconifiable(resizable);
943 frame.setOpaque(Platform.isJS());
945 if (frame.getX() < 1 && frame.getY() < 1)
947 frame.setLocation(xOffset * openFrameCount,
948 yOffset * ((openFrameCount - 1) % 10) + yOffset);
952 * add an entry for the new frame in the Window menu
953 * (and remove it when the frame is closed)
955 final JMenuItem menuItem = new JMenuItem(title);
956 frame.addInternalFrameListener(new InternalFrameAdapter()
959 public void internalFrameActivated(InternalFrameEvent evt)
961 JInternalFrame itf = desktop.getSelectedFrame();
964 if (itf instanceof AlignFrame)
966 Jalview.setCurrentAlignFrame((AlignFrame) itf);
973 public void internalFrameClosed(InternalFrameEvent evt)
975 PaintRefresher.RemoveComponent(frame);
978 * defensive check to prevent frames being
979 * added half off the window
981 if (openFrameCount > 0)
987 * ensure no reference to alignFrame retained by menu item listener
989 if (menuItem.getActionListeners().length > 0)
991 menuItem.removeActionListener(menuItem.getActionListeners()[0]);
993 windowMenu.remove(menuItem);
997 menuItem.addActionListener(new ActionListener()
1000 public void actionPerformed(ActionEvent e)
1004 frame.setSelected(true);
1005 frame.setIcon(false);
1006 } catch (java.beans.PropertyVetoException ex)
1008 // System.err.println(ex.toString());
1013 setKeyBindings(frame);
1017 windowMenu.add(menuItem);
1022 frame.setSelected(true);
1023 frame.requestFocus();
1024 } catch (java.beans.PropertyVetoException ve)
1026 } catch (java.lang.ClassCastException cex)
1029 "Squashed a possible GUI implementation error. If you can recreate this, please look at http://issues.jalview.org/browse/JAL-869",
1035 * Add key bindings to a JInternalFrame so that Ctrl-W and Cmd-W will close the
1040 private static void setKeyBindings(JInternalFrame frame)
1042 @SuppressWarnings("serial")
1043 final Action closeAction = new AbstractAction()
1046 public void actionPerformed(ActionEvent e)
1053 * set up key bindings for Ctrl-W and Cmd-W, with the same (Close) action
1055 KeyStroke ctrlWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
1056 InputEvent.CTRL_DOWN_MASK);
1057 KeyStroke cmdWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
1058 jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx());
1060 InputMap inputMap = frame
1061 .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
1062 String ctrlW = ctrlWKey.toString();
1063 inputMap.put(ctrlWKey, ctrlW);
1064 inputMap.put(cmdWKey, ctrlW);
1066 ActionMap actionMap = frame.getActionMap();
1067 actionMap.put(ctrlW, closeAction);
1071 public void lostOwnership(Clipboard clipboard, Transferable contents)
1075 Desktop.jalviewClipboard = null;
1078 internalCopy = false;
1082 public void dragEnter(DropTargetDragEvent evt)
1087 public void dragExit(DropTargetEvent evt)
1092 public void dragOver(DropTargetDragEvent evt)
1097 public void dropActionChanged(DropTargetDragEvent evt)
1108 public void drop(DropTargetDropEvent evt)
1110 boolean success = true;
1111 // JAL-1552 - acceptDrop required before getTransferable call for
1112 // Java's Transferable for native dnd
1113 evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
1114 Transferable t = evt.getTransferable();
1115 List<Object> files = new ArrayList<>();
1116 List<DataSourceType> protocols = new ArrayList<>();
1120 Desktop.transferFromDropTarget(files, protocols, evt, t);
1121 } catch (Exception e)
1123 e.printStackTrace();
1131 for (int i = 0; i < files.size(); i++)
1133 // BH 2018 File or String
1134 Object file = files.get(i);
1135 String fileName = file.toString();
1136 DataSourceType protocol = (protocols == null)
1137 ? DataSourceType.FILE
1139 FileFormatI format = null;
1141 if (fileName.endsWith(".jar"))
1143 format = FileFormat.Jalview;
1148 format = new IdentifyFile().identify(file, protocol);
1150 if (file instanceof File)
1152 Platform.cacheFileData((File) file);
1154 new FileLoader().LoadFile(null, file, protocol, format);
1157 } catch (Exception ex)
1162 evt.dropComplete(success); // need this to ensure input focus is properly
1163 // transfered to any new windows created
1173 public void inputLocalFileMenuItem_actionPerformed(AlignViewport viewport)
1175 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
1176 JalviewFileChooser chooser = JalviewFileChooser
1177 .forRead(Cache.getProperty("LAST_DIRECTORY"), fileFormat, BackupFiles.getEnabled());
1179 chooser.setFileView(new JalviewFileView());
1180 chooser.setDialogTitle(
1181 MessageManager.getString("label.open_local_file"));
1182 chooser.setToolTipText(MessageManager.getString("action.open"));
1184 chooser.setResponseHandler(0, new Runnable()
1189 File selectedFile = chooser.getSelectedFile();
1190 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1192 FileFormatI format = chooser.getSelectedFormat();
1195 * Call IdentifyFile to verify the file contains what its extension implies.
1196 * Skip this step for dynamically added file formats, because
1197 * IdentifyFile does not know how to recognise them.
1199 if (FileFormats.getInstance().isIdentifiable(format))
1203 format = new IdentifyFile().identify(selectedFile,
1204 DataSourceType.FILE);
1205 } catch (FileFormatException e)
1207 // format = null; //??
1211 new FileLoader().LoadFile(viewport, selectedFile,
1212 DataSourceType.FILE, format);
1215 chooser.showOpenDialog(this);
1219 * Shows a dialog for input of a URL at which to retrieve alignment data
1224 public void inputURLMenuItem_actionPerformed(AlignViewport viewport)
1226 // This construct allows us to have a wider textfield
1228 JLabel label = new JLabel(
1229 MessageManager.getString("label.input_file_url"));
1231 JPanel panel = new JPanel(new GridLayout(2, 1));
1235 * the URL to fetch is
1236 * Java: an editable combobox with history
1237 * JS: (pending JAL-3038) a plain text field
1240 String urlBase = "http://www.";
1241 if (Platform.isJS())
1243 history = new JTextField(urlBase, 35);
1252 JComboBox<String> asCombo = new JComboBox<>();
1253 asCombo.setPreferredSize(new Dimension(400, 20));
1254 asCombo.setEditable(true);
1255 asCombo.addItem(urlBase);
1256 String historyItems = Cache.getProperty("RECENT_URL");
1257 if (historyItems != null)
1259 for (String token : historyItems.split("\\t"))
1261 asCombo.addItem(token);
1268 Object[] options = new Object[] { MessageManager.getString("action.ok"),
1269 MessageManager.getString("action.cancel") };
1270 Runnable action = new Runnable()
1275 @SuppressWarnings("unchecked")
1276 String url = (history instanceof JTextField
1277 ? ((JTextField) history).getText()
1278 : ((JComboBox<String>) history).getSelectedItem()
1281 if (url.toLowerCase().endsWith(".jar"))
1283 if (viewport != null)
1285 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1286 FileFormat.Jalview);
1290 new FileLoader().LoadFile(url, DataSourceType.URL,
1291 FileFormat.Jalview);
1296 FileFormatI format = null;
1299 format = new IdentifyFile().identify(url, DataSourceType.URL);
1300 } catch (FileFormatException e)
1302 // TODO revise error handling, distinguish between
1303 // URL not found and response not valid
1308 String msg = MessageManager
1309 .formatMessage("label.couldnt_locate", url);
1310 JvOptionPane.showInternalMessageDialog(Desktop.desktop, msg,
1311 MessageManager.getString("label.url_not_found"),
1312 JvOptionPane.WARNING_MESSAGE);
1317 if (viewport != null)
1319 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1324 new FileLoader().LoadFile(url, DataSourceType.URL, format);
1329 String dialogOption = MessageManager
1330 .getString("label.input_alignment_from_url");
1331 JvOptionPane.newOptionDialog(desktop).setResponseHandler(0, action)
1332 .showInternalDialog(panel, dialogOption,
1333 JvOptionPane.YES_NO_CANCEL_OPTION,
1334 JvOptionPane.PLAIN_MESSAGE, null, options,
1335 MessageManager.getString("action.ok"));
1339 * Opens the CutAndPaste window for the user to paste an alignment in to
1342 * - if not null, the pasted alignment is added to the current
1343 * alignment; if null, to a new alignment window
1346 public void inputTextboxMenuItem_actionPerformed(
1347 AlignmentViewPanel viewPanel)
1349 CutAndPasteTransfer cap = new CutAndPasteTransfer();
1350 cap.setForInput(viewPanel);
1351 Desktop.addInternalFrame(cap,
1352 MessageManager.getString("label.cut_paste_alignmen_file"), true,
1362 Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
1363 jalview.bin.Cache.setProperty("SCREENGEOMETRY_WIDTH",
1365 jalview.bin.Cache.setProperty("SCREENGEOMETRY_HEIGHT",
1366 screen.height + "");
1367 storeLastKnownDimensions("", new Rectangle(getBounds().x, getBounds().y,
1368 getWidth(), getHeight()));
1370 if (jconsole != null)
1372 storeLastKnownDimensions("JAVA_CONSOLE_", jconsole.getBounds());
1373 jconsole.stopConsole();
1377 storeLastKnownDimensions("JALVIEW_RSS_WINDOW_", jvnews.getBounds());
1380 if (dialogExecutor != null)
1382 dialogExecutor.shutdownNow();
1384 closeAll_actionPerformed(null);
1386 if (groovyConsole != null)
1388 // suppress a possible repeat prompt to save script
1389 groovyConsole.setDirty(false);
1390 groovyConsole.exit();
1395 private void storeLastKnownDimensions(String string, Rectangle jc)
1397 jalview.bin.Cache.log.debug("Storing last known dimensions for "
1398 + string + ": x:" + jc.x + " y:" + jc.y + " width:" + jc.width
1399 + " height:" + jc.height);
1401 jalview.bin.Cache.setProperty(string + "SCREEN_X", jc.x + "");
1402 jalview.bin.Cache.setProperty(string + "SCREEN_Y", jc.y + "");
1403 jalview.bin.Cache.setProperty(string + "SCREEN_WIDTH", jc.width + "");
1404 jalview.bin.Cache.setProperty(string + "SCREEN_HEIGHT", jc.height + "");
1414 public void aboutMenuItem_actionPerformed(ActionEvent e)
1416 // StringBuffer message = getAboutMessage(false);
1417 // JvOptionPane.showInternalMessageDialog(Desktop.desktop,
1419 // message.toString(), "About Jalview", JvOptionPane.INFORMATION_MESSAGE);
1420 new Thread(new Runnable()
1425 new SplashScreen(true);
1430 public StringBuffer getAboutMessage(boolean shortv)
1432 StringBuffer message = new StringBuffer();
1433 message.append("<html>");
1436 message.append("<h1><strong>Version: "
1437 + jalview.bin.Cache.getProperty("VERSION")
1438 + "</strong></h1>");
1439 message.append("<strong>Last Updated: <em>"
1440 + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown")
1441 + "</em></strong>");
1447 message.append("<strong>Version "
1448 + jalview.bin.Cache.getProperty("VERSION")
1449 + "; last updated: "
1450 + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown"));
1453 if (jalview.bin.Cache.getDefault("LATEST_VERSION", "Checking")
1454 .equals("Checking"))
1456 message.append("<br>...Checking latest version...</br>");
1458 else if (!jalview.bin.Cache.getDefault("LATEST_VERSION", "Checking")
1459 .equals(jalview.bin.Cache.getProperty("VERSION")))
1461 boolean red = false;
1462 if (jalview.bin.Cache.getProperty("VERSION").toLowerCase()
1463 .indexOf("automated build") == -1)
1466 // Displayed when code version and jnlp version do not match and code
1467 // version is not a development build
1468 message.append("<div style=\"color: #FF0000;font-style: bold;\">");
1471 message.append("<br>!! Version "
1472 + jalview.bin.Cache.getDefault("LATEST_VERSION",
1474 + " is available for download from "
1475 + jalview.bin.Cache.getDefault("www.jalview.org",
1476 "http://www.jalview.org")
1480 message.append("</div>");
1483 message.append("<br>Authors: " + jalview.bin.Cache.getDefault(
1485 "The Jalview Authors (See AUTHORS file for current list)")
1486 + "<br><br>Development managed by The Barton Group, University of Dundee, Scotland, UK.<br>"
1487 + "<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"
1488 + "<br><br>If you use Jalview, please cite:"
1489 + "<br>Waterhouse, A.M., Procter, J.B., Martin, D.M.A, Clamp, M. and Barton, G. J. (2009)"
1490 + "<br>Jalview Version 2 - a multiple sequence alignment editor and analysis workbench"
1491 + "<br>Bioinformatics doi: 10.1093/bioinformatics/btp033"
1497 * Action on requesting Help documentation
1500 public void documentationMenuItem_actionPerformed()
1504 if (Platform.isJS())
1506 BrowserLauncher.openURL("http://www.jalview.org/help.html");
1515 Help.showHelpWindow();
1517 } catch (Exception ex)
1519 System.err.println("Error opening help: " + ex.getMessage());
1524 public void closeAll_actionPerformed(ActionEvent e)
1526 // TODO show a progress bar while closing?
1527 JInternalFrame[] frames = desktop.getAllFrames();
1528 for (int i = 0; i < frames.length; i++)
1532 frames[i].setClosed(true);
1533 } catch (java.beans.PropertyVetoException ex)
1537 Jalview.setCurrentAlignFrame(null);
1538 System.out.println("ALL CLOSED");
1539 if (v_client != null)
1541 // TODO clear binding to vamsas document objects on close_all
1545 * reset state of singleton objects as appropriate (clear down session state
1546 * when all windows are closed)
1548 StructureSelectionManager ssm = StructureSelectionManager
1549 .getStructureSelectionManager(this);
1557 public void raiseRelated_actionPerformed(ActionEvent e)
1559 reorderAssociatedWindows(false, false);
1563 public void minimizeAssociated_actionPerformed(ActionEvent e)
1565 reorderAssociatedWindows(true, false);
1568 void closeAssociatedWindows()
1570 reorderAssociatedWindows(false, true);
1576 * @seejalview.jbgui.GDesktop#garbageCollect_actionPerformed(java.awt.event.
1580 protected void garbageCollect_actionPerformed(ActionEvent e)
1582 // We simply collect the garbage
1583 jalview.bin.Cache.log.debug("Collecting garbage...");
1585 jalview.bin.Cache.log.debug("Finished garbage collection.");
1592 * jalview.jbgui.GDesktop#showMemusage_actionPerformed(java.awt.event.ActionEvent
1596 protected void showMemusage_actionPerformed(ActionEvent e)
1598 desktop.showMemoryUsage(showMemusage.isSelected());
1605 * jalview.jbgui.GDesktop#showConsole_actionPerformed(java.awt.event.ActionEvent
1609 protected void showConsole_actionPerformed(ActionEvent e)
1611 showConsole(showConsole.isSelected());
1614 Console jconsole = null;
1617 * control whether the java console is visible or not
1621 void showConsole(boolean selected)
1623 // TODO: decide if we should update properties file
1624 if (jconsole != null) // BH 2018
1626 showConsole.setSelected(selected);
1627 Cache.setProperty("SHOW_JAVA_CONSOLE",
1628 Boolean.valueOf(selected).toString());
1629 jconsole.setVisible(selected);
1633 void reorderAssociatedWindows(boolean minimize, boolean close)
1635 JInternalFrame[] frames = desktop.getAllFrames();
1636 if (frames == null || frames.length < 1)
1641 AlignmentViewport source = null, target = null;
1642 if (frames[0] instanceof AlignFrame)
1644 source = ((AlignFrame) frames[0]).getCurrentView();
1646 else if (frames[0] instanceof TreePanel)
1648 source = ((TreePanel) frames[0]).getViewPort();
1650 else if (frames[0] instanceof PCAPanel)
1652 source = ((PCAPanel) frames[0]).av;
1654 else if (frames[0].getContentPane() instanceof PairwiseAlignPanel)
1656 source = ((PairwiseAlignPanel) frames[0].getContentPane()).av;
1661 for (int i = 0; i < frames.length; i++)
1664 if (frames[i] == null)
1668 if (frames[i] instanceof AlignFrame)
1670 target = ((AlignFrame) frames[i]).getCurrentView();
1672 else if (frames[i] instanceof TreePanel)
1674 target = ((TreePanel) frames[i]).getViewPort();
1676 else if (frames[i] instanceof PCAPanel)
1678 target = ((PCAPanel) frames[i]).av;
1680 else if (frames[i].getContentPane() instanceof PairwiseAlignPanel)
1682 target = ((PairwiseAlignPanel) frames[i].getContentPane()).av;
1685 if (source == target)
1691 frames[i].setClosed(true);
1695 frames[i].setIcon(minimize);
1698 frames[i].toFront();
1702 } catch (java.beans.PropertyVetoException ex)
1717 protected void preferences_actionPerformed(ActionEvent e)
1723 * Prompts the user to choose a file and then saves the Jalview state as a
1724 * Jalview project file
1727 public void saveState_actionPerformed()
1729 saveState_actionPerformed(false);
1732 public void saveState_actionPerformed(boolean saveAs)
1734 java.io.File projectFile = getProjectFile();
1735 // autoSave indicates we already have a file and don't need to ask
1736 boolean autoSave = projectFile != null && !saveAs
1737 && BackupFiles.getEnabled();
1739 // System.out.println("autoSave="+autoSave+", projectFile='"+projectFile+"',
1740 // saveAs="+saveAs+", Backups
1741 // "+(BackupFiles.getEnabled()?"enabled":"disabled"));
1743 boolean approveSave = false;
1746 JalviewFileChooser chooser = new JalviewFileChooser("jvp",
1749 chooser.setFileView(new JalviewFileView());
1750 chooser.setDialogTitle(MessageManager.getString("label.save_state"));
1752 int value = chooser.showSaveDialog(this);
1754 if (value == JalviewFileChooser.APPROVE_OPTION)
1756 projectFile = chooser.getSelectedFile();
1757 setProjectFile(projectFile);
1762 if (approveSave || autoSave)
1764 final Desktop me = this;
1765 final java.io.File chosenFile = projectFile;
1766 new Thread(new Runnable()
1771 // TODO: refactor to Jalview desktop session controller action.
1772 setProgressBar(MessageManager.formatMessage(
1773 "label.saving_jalview_project", new Object[]
1774 { chosenFile.getName() }), chosenFile.hashCode());
1775 jalview.bin.Cache.setProperty("LAST_DIRECTORY",
1776 chosenFile.getParent());
1777 // TODO catch and handle errors for savestate
1778 // TODO prevent user from messing with the Desktop whilst we're saving
1781 boolean doBackup = BackupFiles.getEnabled();
1782 BackupFiles backupfiles = doBackup ? new BackupFiles(chosenFile) : null;
1784 new Jalview2XML().saveState(doBackup ? backupfiles.getTempFile() : chosenFile);
1788 backupfiles.setWriteSuccess(true);
1789 backupfiles.rollBackupsAndRenameTempFile();
1791 } catch (OutOfMemoryError oom)
1793 new OOMWarning("Whilst saving current state to "
1794 + chosenFile.getName(), oom);
1795 } catch (Exception ex)
1797 Cache.log.error("Problems whilst trying to save to "
1798 + chosenFile.getName(), ex);
1799 JvOptionPane.showMessageDialog(me,
1800 MessageManager.formatMessage(
1801 "label.error_whilst_saving_current_state_to",
1803 { chosenFile.getName() }),
1804 MessageManager.getString("label.couldnt_save_project"),
1805 JvOptionPane.WARNING_MESSAGE);
1807 setProgressBar(null, chosenFile.hashCode());
1814 public void saveAsState_actionPerformed(ActionEvent e)
1816 saveState_actionPerformed(true);
1819 private void setProjectFile(File choice)
1821 this.projectFile = choice;
1824 public File getProjectFile()
1826 return this.projectFile;
1830 * Shows a file chooser dialog and tries to read in the selected file as a
1834 public void loadState_actionPerformed()
1836 final String[] suffix = new String[] { "jvp", "jar" };
1837 final String[] desc = new String[] { "Jalview Project",
1838 "Jalview Project (old)" };
1839 JalviewFileChooser chooser = new JalviewFileChooser(
1840 Cache.getProperty("LAST_DIRECTORY"), suffix, desc,
1841 "Jalview Project", true, BackupFiles.getEnabled()); // last two booleans: allFiles,
1843 chooser.setFileView(new JalviewFileView());
1844 chooser.setDialogTitle(MessageManager.getString("label.restore_state"));
1845 chooser.setResponseHandler(0, new Runnable()
1850 File selectedFile = chooser.getSelectedFile();
1851 setProjectFile(selectedFile);
1852 String choice = selectedFile.getAbsolutePath();
1853 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1854 new Thread(new Runnable()
1861 new Jalview2XML().loadJalviewAlign(choice);
1862 } catch (OutOfMemoryError oom)
1864 new OOMWarning("Whilst loading project from " + choice, oom);
1865 } catch (Exception ex)
1868 "Problems whilst loading project from " + choice, ex);
1869 JvOptionPane.showMessageDialog(Desktop.desktop,
1870 MessageManager.formatMessage(
1871 "label.error_whilst_loading_project_from",
1874 MessageManager.getString("label.couldnt_load_project"),
1875 JvOptionPane.WARNING_MESSAGE);
1882 chooser.showOpenDialog(this);
1886 public void inputSequence_actionPerformed(ActionEvent e)
1888 new SequenceFetcher(this);
1891 JPanel progressPanel;
1893 ArrayList<JPanel> fileLoadingPanels = new ArrayList<>();
1895 public void startLoading(final Object fileName)
1897 if (fileLoadingCount == 0)
1899 fileLoadingPanels.add(addProgressPanel(MessageManager
1900 .formatMessage("label.loading_file", new Object[]
1906 private JPanel addProgressPanel(String string)
1908 if (progressPanel == null)
1910 progressPanel = new JPanel(new GridLayout(1, 1));
1911 totalProgressCount = 0;
1912 instance.getContentPane().add(progressPanel, BorderLayout.SOUTH);
1914 JPanel thisprogress = new JPanel(new BorderLayout(10, 5));
1915 JProgressBar progressBar = new JProgressBar();
1916 progressBar.setIndeterminate(true);
1918 thisprogress.add(new JLabel(string), BorderLayout.WEST);
1920 thisprogress.add(progressBar, BorderLayout.CENTER);
1921 progressPanel.add(thisprogress);
1922 ((GridLayout) progressPanel.getLayout()).setRows(
1923 ((GridLayout) progressPanel.getLayout()).getRows() + 1);
1924 ++totalProgressCount;
1925 instance.validate();
1926 return thisprogress;
1929 int totalProgressCount = 0;
1931 private void removeProgressPanel(JPanel progbar)
1933 if (progressPanel != null)
1935 synchronized (progressPanel)
1937 progressPanel.remove(progbar);
1938 GridLayout gl = (GridLayout) progressPanel.getLayout();
1939 gl.setRows(gl.getRows() - 1);
1940 if (--totalProgressCount < 1)
1942 this.getContentPane().remove(progressPanel);
1943 progressPanel = null;
1950 public void stopLoading()
1953 if (fileLoadingCount < 1)
1955 while (fileLoadingPanels.size() > 0)
1957 removeProgressPanel(fileLoadingPanels.remove(0));
1959 fileLoadingPanels.clear();
1960 fileLoadingCount = 0;
1965 public static int getViewCount(String alignmentId)
1967 AlignmentViewport[] aps = getViewports(alignmentId);
1968 return (aps == null) ? 0 : aps.length;
1973 * @param alignmentId
1974 * - if null, all sets are returned
1975 * @return all AlignmentPanels concerning the alignmentId sequence set
1977 public static AlignmentPanel[] getAlignmentPanels(String alignmentId)
1979 if (Desktop.desktop == null)
1981 // no frames created and in headless mode
1982 // TODO: verify that frames are recoverable when in headless mode
1985 List<AlignmentPanel> aps = new ArrayList<>();
1986 AlignFrame[] frames = getAlignFrames();
1991 for (AlignFrame af : frames)
1993 for (AlignmentPanel ap : af.alignPanels)
1995 if (alignmentId == null
1996 || alignmentId.equals(ap.av.getSequenceSetId()))
2002 if (aps.size() == 0)
2006 AlignmentPanel[] vap = aps.toArray(new AlignmentPanel[aps.size()]);
2011 * get all the viewports on an alignment.
2013 * @param sequenceSetId
2014 * unique alignment id (may be null - all viewports returned in that
2016 * @return all viewports on the alignment bound to sequenceSetId
2018 public static AlignmentViewport[] getViewports(String sequenceSetId)
2020 List<AlignmentViewport> viewp = new ArrayList<>();
2021 if (desktop != null)
2023 AlignFrame[] frames = Desktop.getAlignFrames();
2025 for (AlignFrame afr : frames)
2027 if (sequenceSetId == null || afr.getViewport().getSequenceSetId()
2028 .equals(sequenceSetId))
2030 if (afr.alignPanels != null)
2032 for (AlignmentPanel ap : afr.alignPanels)
2034 if (sequenceSetId == null
2035 || sequenceSetId.equals(ap.av.getSequenceSetId()))
2043 viewp.add(afr.getViewport());
2047 if (viewp.size() > 0)
2049 return viewp.toArray(new AlignmentViewport[viewp.size()]);
2056 * Explode the views in the given frame into separate AlignFrame
2060 public static void explodeViews(AlignFrame af)
2062 int size = af.alignPanels.size();
2068 for (int i = 0; i < size; i++)
2070 AlignmentPanel ap = af.alignPanels.get(i);
2071 AlignFrame newaf = new AlignFrame(ap);
2074 * Restore the view's last exploded frame geometry if known. Multiple
2075 * views from one exploded frame share and restore the same (frame)
2076 * position and size.
2078 Rectangle geometry = ap.av.getExplodedGeometry();
2079 if (geometry != null)
2081 newaf.setBounds(geometry);
2084 ap.av.setGatherViewsHere(false);
2086 addInternalFrame(newaf, af.getTitle(), AlignFrame.DEFAULT_WIDTH,
2087 AlignFrame.DEFAULT_HEIGHT);
2090 af.alignPanels.clear();
2091 af.closeMenuItem_actionPerformed(true);
2096 * Gather expanded views (separate AlignFrame's) with the same sequence set
2097 * identifier back in to this frame as additional views, and close the expanded
2098 * views. Note the expanded frames may themselves have multiple views. We take
2103 public void gatherViews(AlignFrame source)
2105 source.viewport.setGatherViewsHere(true);
2106 source.viewport.setExplodedGeometry(source.getBounds());
2107 JInternalFrame[] frames = desktop.getAllFrames();
2108 String viewId = source.viewport.getSequenceSetId();
2110 for (int t = 0; t < frames.length; t++)
2112 if (frames[t] instanceof AlignFrame && frames[t] != source)
2114 AlignFrame af = (AlignFrame) frames[t];
2115 boolean gatherThis = false;
2116 for (int a = 0; a < af.alignPanels.size(); a++)
2118 AlignmentPanel ap = af.alignPanels.get(a);
2119 if (viewId.equals(ap.av.getSequenceSetId()))
2122 ap.av.setGatherViewsHere(false);
2123 ap.av.setExplodedGeometry(af.getBounds());
2124 source.addAlignmentPanel(ap, false);
2130 af.alignPanels.clear();
2131 af.closeMenuItem_actionPerformed(true);
2138 jalview.gui.VamsasApplication v_client = null;
2141 public void vamsasImport_actionPerformed(ActionEvent e)
2143 // TODO: JAL-3048 not needed for Jalview-JS
2145 if (v_client == null)
2147 // Load and try to start a session.
2148 JalviewFileChooser chooser = new JalviewFileChooser(
2149 jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
2151 chooser.setFileView(new JalviewFileView());
2152 chooser.setDialogTitle(
2153 MessageManager.getString("label.open_saved_vamsas_session"));
2154 chooser.setToolTipText(MessageManager.getString(
2155 "label.select_vamsas_session_opened_as_new_vamsas_session"));
2157 int value = chooser.showOpenDialog(this);
2159 if (value == JalviewFileChooser.APPROVE_OPTION)
2161 String fle = chooser.getSelectedFile().toString();
2162 if (!vamsasImport(chooser.getSelectedFile()))
2164 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
2165 MessageManager.formatMessage(
2166 "label.couldnt_import_as_vamsas_session",
2170 .getString("label.vamsas_document_import_failed"),
2171 JvOptionPane.ERROR_MESSAGE);
2177 jalview.bin.Cache.log.error(
2178 "Implementation error - load session from a running session is not supported.");
2183 * import file into a new vamsas session (uses jalview.gui.VamsasApplication)
2186 * @return true if import was a success and a session was started.
2188 public boolean vamsasImport(URL url)
2190 // TODO: create progress bar
2191 if (v_client != null)
2194 jalview.bin.Cache.log.error(
2195 "Implementation error - load session from a running session is not supported.");
2201 // copy the URL content to a temporary local file
2202 // TODO: be a bit cleverer here with nio (?!)
2203 File file = File.createTempFile("vdocfromurl", ".vdj");
2204 FileOutputStream fos = new FileOutputStream(file);
2205 BufferedInputStream bis = new BufferedInputStream(url.openStream());
2206 byte[] buffer = new byte[2048];
2208 while ((ln = bis.read(buffer)) > -1)
2210 fos.write(buffer, 0, ln);
2214 v_client = new jalview.gui.VamsasApplication(this, file,
2215 url.toExternalForm());
2216 } catch (Exception ex)
2218 jalview.bin.Cache.log.error(
2219 "Failed to create new vamsas session from contents of URL "
2224 setupVamsasConnectedGui();
2225 v_client.initial_update(); // TODO: thread ?
2226 return v_client.inSession();
2230 * import file into a new vamsas session (uses jalview.gui.VamsasApplication)
2233 * @return true if import was a success and a session was started.
2235 public boolean vamsasImport(File file)
2237 if (v_client != null)
2240 jalview.bin.Cache.log.error(
2241 "Implementation error - load session from a running session is not supported.");
2245 setProgressBar(MessageManager.formatMessage(
2246 "status.importing_vamsas_session_from", new Object[]
2247 { file.getName() }), file.hashCode());
2250 v_client = new jalview.gui.VamsasApplication(this, file, null);
2251 } catch (Exception ex)
2253 setProgressBar(MessageManager.formatMessage(
2254 "status.importing_vamsas_session_from", new Object[]
2255 { file.getName() }), file.hashCode());
2256 jalview.bin.Cache.log.error(
2257 "New vamsas session from existing session file failed:", ex);
2260 setupVamsasConnectedGui();
2261 v_client.initial_update(); // TODO: thread ?
2262 setProgressBar(MessageManager.formatMessage(
2263 "status.importing_vamsas_session_from", new Object[]
2264 { file.getName() }), file.hashCode());
2265 return v_client.inSession();
2268 public boolean joinVamsasSession(String mysesid)
2270 if (v_client != null)
2272 throw new Error(MessageManager
2273 .getString("error.try_join_vamsas_session_another"));
2275 if (mysesid == null)
2278 MessageManager.getString("error.invalid_vamsas_session_id"));
2280 v_client = new VamsasApplication(this, mysesid);
2281 setupVamsasConnectedGui();
2282 v_client.initial_update();
2283 return (v_client.inSession());
2287 public void vamsasStart_actionPerformed(ActionEvent e)
2289 if (v_client == null)
2292 // we just start a default session for moment.
2294 * JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache.
2295 * getProperty("LAST_DIRECTORY"));
2297 * chooser.setFileView(new JalviewFileView());
2298 * chooser.setDialogTitle("Load Vamsas file");
2299 * chooser.setToolTipText("Import");
2301 * int value = chooser.showOpenDialog(this);
2303 * if (value == JalviewFileChooser.APPROVE_OPTION) { v_client = new
2304 * jalview.gui.VamsasApplication(this, chooser.getSelectedFile());
2306 v_client = new VamsasApplication(this);
2307 setupVamsasConnectedGui();
2308 v_client.initial_update(); // TODO: thread ?
2312 // store current data in session.
2313 v_client.push_update(); // TODO: thread
2317 protected void setupVamsasConnectedGui()
2319 vamsasStart.setText(MessageManager.getString("label.session_update"));
2320 vamsasSave.setVisible(true);
2321 vamsasStop.setVisible(true);
2322 vamsasImport.setVisible(false); // Document import to existing session is
2323 // not possible for vamsas-client-1.0.
2326 protected void setupVamsasDisconnectedGui()
2328 vamsasSave.setVisible(false);
2329 vamsasStop.setVisible(false);
2330 vamsasImport.setVisible(true);
2332 .setText(MessageManager.getString("label.new_vamsas_session"));
2336 public void vamsasStop_actionPerformed(ActionEvent e)
2338 if (v_client != null)
2340 v_client.end_session();
2342 setupVamsasDisconnectedGui();
2346 protected void buildVamsasStMenu()
2348 if (v_client == null)
2350 String[] sess = null;
2353 sess = VamsasApplication.getSessionList();
2354 } catch (Exception e)
2356 jalview.bin.Cache.log.warn("Problem getting current sessions list.",
2362 jalview.bin.Cache.log.debug(
2363 "Got current sessions list: " + sess.length + " entries.");
2364 VamsasStMenu.removeAll();
2365 for (int i = 0; i < sess.length; i++)
2367 JMenuItem sessit = new JMenuItem();
2368 sessit.setText(sess[i]);
2369 sessit.setToolTipText(MessageManager
2370 .formatMessage("label.connect_to_session", new Object[]
2372 final Desktop dsktp = this;
2373 final String mysesid = sess[i];
2374 sessit.addActionListener(new ActionListener()
2378 public void actionPerformed(ActionEvent e)
2380 if (dsktp.v_client == null)
2382 Thread rthr = new Thread(new Runnable()
2388 dsktp.v_client = new VamsasApplication(dsktp, mysesid);
2389 dsktp.setupVamsasConnectedGui();
2390 dsktp.v_client.initial_update();
2398 VamsasStMenu.add(sessit);
2400 // don't show an empty menu.
2401 VamsasStMenu.setVisible(sess.length > 0);
2406 jalview.bin.Cache.log.debug("No current vamsas sessions.");
2407 VamsasStMenu.removeAll();
2408 VamsasStMenu.setVisible(false);
2413 // Not interested in the content. Just hide ourselves.
2414 VamsasStMenu.setVisible(false);
2419 public void vamsasSave_actionPerformed(ActionEvent e)
2421 // TODO: JAL-3048 not needed for Jalview-JS
2423 if (v_client != null)
2425 // TODO: VAMSAS DOCUMENT EXTENSION is VDJ
2426 JalviewFileChooser chooser = new JalviewFileChooser("vdj",
2429 chooser.setFileView(new JalviewFileView());
2430 chooser.setDialogTitle(MessageManager
2431 .getString("label.save_vamsas_document_archive"));
2433 int value = chooser.showSaveDialog(this);
2435 if (value == JalviewFileChooser.APPROVE_OPTION)
2437 java.io.File choice = chooser.getSelectedFile();
2438 JPanel progpanel = addProgressPanel(MessageManager
2439 .formatMessage("label.saving_vamsas_doc", new Object[]
2440 { choice.getName() }));
2441 Cache.setProperty("LAST_DIRECTORY", choice.getParent());
2442 String warnmsg = null;
2443 String warnttl = null;
2446 v_client.vclient.storeDocument(choice);
2449 warnttl = "Serious Problem saving Vamsas Document";
2450 warnmsg = ex.toString();
2451 jalview.bin.Cache.log
2452 .error("Error Whilst saving document to " + choice, ex);
2454 } catch (Exception ex)
2456 warnttl = "Problem saving Vamsas Document.";
2457 warnmsg = ex.toString();
2458 jalview.bin.Cache.log.warn(
2459 "Exception Whilst saving document to " + choice, ex);
2462 removeProgressPanel(progpanel);
2463 if (warnmsg != null)
2465 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
2467 warnmsg, warnttl, JvOptionPane.ERROR_MESSAGE);
2473 JPanel vamUpdate = null;
2476 * hide vamsas user gui bits when a vamsas document event is being handled.
2479 * true to hide gui, false to reveal gui
2481 public void setVamsasUpdate(boolean b)
2483 Cache.log.debug("Setting gui for Vamsas update "
2484 + (b ? "in progress" : "finished"));
2486 if (vamUpdate != null)
2488 this.removeProgressPanel(vamUpdate);
2492 vamUpdate = this.addProgressPanel(
2493 MessageManager.getString("label.updating_vamsas_session"));
2495 vamsasStart.setVisible(!b);
2496 vamsasStop.setVisible(!b);
2497 vamsasSave.setVisible(!b);
2500 public JInternalFrame[] getAllFrames()
2502 return desktop.getAllFrames();
2506 * Checks the given url to see if it gives a response indicating that the user
2507 * should be informed of a new questionnaire.
2511 public void checkForQuestionnaire(String url)
2513 UserQuestionnaireCheck jvq = new UserQuestionnaireCheck(url);
2514 // javax.swing.SwingUtilities.invokeLater(jvq);
2515 new Thread(jvq).start();
2518 public void checkURLLinks()
2520 // Thread off the URL link checker
2521 addDialogThread(new Runnable()
2526 if (Cache.getDefault("CHECKURLLINKS", true))
2528 // check what the actual links are - if it's just the default don't
2529 // bother with the warning
2530 List<String> links = Preferences.sequenceUrlLinks
2533 // only need to check links if there is one with a
2534 // SEQUENCE_ID which is not the default EMBL_EBI link
2535 ListIterator<String> li = links.listIterator();
2536 boolean check = false;
2537 List<JLabel> urls = new ArrayList<>();
2538 while (li.hasNext())
2540 String link = li.next();
2541 if (link.contains(jalview.util.UrlConstants.SEQUENCE_ID)
2542 && !UrlConstants.isDefaultString(link))
2545 int barPos = link.indexOf("|");
2546 String urlMsg = barPos == -1 ? link
2547 : link.substring(0, barPos) + ": "
2548 + link.substring(barPos + 1);
2549 urls.add(new JLabel(urlMsg));
2557 // ask user to check in case URL links use old style tokens
2558 // ($SEQUENCE_ID$ for sequence id _or_ accession id)
2559 JPanel msgPanel = new JPanel();
2560 msgPanel.setLayout(new BoxLayout(msgPanel, BoxLayout.PAGE_AXIS));
2561 msgPanel.add(Box.createVerticalGlue());
2562 JLabel msg = new JLabel(MessageManager
2563 .getString("label.SEQUENCE_ID_for_DB_ACCESSION1"));
2564 JLabel msg2 = new JLabel(MessageManager
2565 .getString("label.SEQUENCE_ID_for_DB_ACCESSION2"));
2567 for (JLabel url : urls)
2573 final JCheckBox jcb = new JCheckBox(
2574 MessageManager.getString("label.do_not_display_again"));
2575 jcb.addActionListener(new ActionListener()
2578 public void actionPerformed(ActionEvent e)
2580 // update Cache settings for "don't show this again"
2581 boolean showWarningAgain = !jcb.isSelected();
2582 Cache.setProperty("CHECKURLLINKS",
2583 Boolean.valueOf(showWarningAgain).toString());
2588 JvOptionPane.showMessageDialog(Desktop.desktop, msgPanel,
2590 .getString("label.SEQUENCE_ID_no_longer_used"),
2591 JvOptionPane.WARNING_MESSAGE);
2598 * Proxy class for JDesktopPane which optionally displays the current memory
2599 * usage and highlights the desktop area with a red bar if free memory runs low.
2603 public class MyDesktopPane extends JDesktopPane
2606 private static final float ONE_MB = 1048576f;
2608 boolean showMemoryUsage = false;
2612 java.text.NumberFormat df;
2614 float maxMemory, allocatedMemory, freeMemory, totalFreeMemory,
2617 public MyDesktopPane(boolean showMemoryUsage)
2619 showMemoryUsage(showMemoryUsage);
2622 public void showMemoryUsage(boolean showMemory)
2624 this.showMemoryUsage = showMemory;
2627 Thread worker = new Thread(this);
2633 public boolean isShowMemoryUsage()
2635 return showMemoryUsage;
2641 df = java.text.NumberFormat.getNumberInstance();
2642 df.setMaximumFractionDigits(2);
2643 runtime = Runtime.getRuntime();
2645 while (showMemoryUsage)
2649 maxMemory = runtime.maxMemory() / ONE_MB;
2650 allocatedMemory = runtime.totalMemory() / ONE_MB;
2651 freeMemory = runtime.freeMemory() / ONE_MB;
2652 totalFreeMemory = freeMemory + (maxMemory - allocatedMemory);
2654 percentUsage = (totalFreeMemory / maxMemory) * 100;
2656 // if (percentUsage < 20)
2658 // border1 = BorderFactory.createMatteBorder(12, 12, 12, 12,
2660 // instance.set.setBorder(border1);
2663 // sleep after showing usage
2665 } catch (Exception ex)
2667 ex.printStackTrace();
2673 public void paintComponent(Graphics g)
2675 if (showMemoryUsage && g != null && df != null)
2677 if (percentUsage < 20)
2679 g.setColor(Color.red);
2681 FontMetrics fm = g.getFontMetrics();
2684 g.drawString(MessageManager.formatMessage("label.memory_stats",
2686 { df.format(totalFreeMemory), df.format(maxMemory),
2687 df.format(percentUsage) }),
2688 10, getHeight() - fm.getHeight());
2695 * Accessor method to quickly get all the AlignmentFrames loaded.
2697 * @return an array of AlignFrame, or null if none found
2699 public static AlignFrame[] getAlignFrames()
2701 if (Jalview.isHeadlessMode())
2703 // Desktop.desktop is null in headless mode
2704 return new AlignFrame[] { Jalview.currentAlignFrame };
2707 JInternalFrame[] frames = Desktop.desktop.getAllFrames();
2713 List<AlignFrame> avp = new ArrayList<>();
2715 for (int i = frames.length - 1; i > -1; i--)
2717 if (frames[i] instanceof AlignFrame)
2719 avp.add((AlignFrame) frames[i]);
2721 else if (frames[i] instanceof SplitFrame)
2724 * Also check for a split frame containing an AlignFrame
2726 GSplitFrame sf = (GSplitFrame) frames[i];
2727 if (sf.getTopFrame() instanceof AlignFrame)
2729 avp.add((AlignFrame) sf.getTopFrame());
2731 if (sf.getBottomFrame() instanceof AlignFrame)
2733 avp.add((AlignFrame) sf.getBottomFrame());
2737 if (avp.size() == 0)
2741 AlignFrame afs[] = avp.toArray(new AlignFrame[avp.size()]);
2746 * Returns an array of any AppJmol frames in the Desktop (or null if none).
2750 public GStructureViewer[] getJmols()
2752 JInternalFrame[] frames = Desktop.desktop.getAllFrames();
2758 List<GStructureViewer> avp = new ArrayList<>();
2760 for (int i = frames.length - 1; i > -1; i--)
2762 if (frames[i] instanceof AppJmol)
2764 GStructureViewer af = (GStructureViewer) frames[i];
2768 if (avp.size() == 0)
2772 GStructureViewer afs[] = avp.toArray(new GStructureViewer[avp.size()]);
2777 * Add Groovy Support to Jalview
2780 public void groovyShell_actionPerformed()
2784 openGroovyConsole();
2785 } catch (Exception ex)
2787 jalview.bin.Cache.log.error("Groovy Shell Creation failed.", ex);
2788 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
2790 MessageManager.getString("label.couldnt_create_groovy_shell"),
2791 MessageManager.getString("label.groovy_support_failed"),
2792 JvOptionPane.ERROR_MESSAGE);
2797 * Open the Groovy console
2799 void openGroovyConsole()
2801 if (groovyConsole == null)
2803 groovyConsole = new groovy.ui.Console();
2804 groovyConsole.setVariable("Jalview", this);
2805 groovyConsole.run();
2808 * We allow only one console at a time, so that AlignFrame menu option
2809 * 'Calculate | Run Groovy script' is unambiguous.
2810 * Disable 'Groovy Console', and enable 'Run script', when the console is
2811 * opened, and the reverse when it is closed
2813 Window window = (Window) groovyConsole.getFrame();
2814 window.addWindowListener(new WindowAdapter()
2817 public void windowClosed(WindowEvent e)
2820 * rebind CMD-Q from Groovy Console to Jalview Quit
2823 enableExecuteGroovy(false);
2829 * show Groovy console window (after close and reopen)
2831 ((Window) groovyConsole.getFrame()).setVisible(true);
2834 * if we got this far, enable 'Run Groovy' in AlignFrame menus
2835 * and disable opening a second console
2837 enableExecuteGroovy(true);
2841 * Bind Ctrl/Cmd-Q to Quit - for reset as Groovy Console takes over this binding
2844 protected void addQuitHandler()
2846 getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
2847 .put(KeyStroke.getKeyStroke(KeyEvent.VK_Q,
2848 jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx()),
2850 getRootPane().getActionMap().put("Quit", new AbstractAction()
2853 public void actionPerformed(ActionEvent e)
2861 * Enable or disable 'Run Groovy script' in AlignFrame calculate menus
2864 * true if Groovy console is open
2866 public void enableExecuteGroovy(boolean enabled)
2869 * disable opening a second Groovy console
2870 * (or re-enable when the console is closed)
2872 groovyShell.setEnabled(!enabled);
2874 AlignFrame[] alignFrames = getAlignFrames();
2875 if (alignFrames != null)
2877 for (AlignFrame af : alignFrames)
2879 af.setGroovyEnabled(enabled);
2885 * Progress bars managed by the IProgressIndicator method.
2887 private Hashtable<Long, JPanel> progressBars;
2889 private Hashtable<Long, IProgressIndicatorHandler> progressBarHandlers;
2894 * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
2897 public void setProgressBar(String message, long id)
2899 Platform.timeCheck("Desktop " + message, Platform.TIME_MARK);
2901 if (progressBars == null)
2903 progressBars = new Hashtable<>();
2904 progressBarHandlers = new Hashtable<>();
2907 if (progressBars.get(Long.valueOf(id)) != null)
2909 JPanel panel = progressBars.remove(Long.valueOf(id));
2910 if (progressBarHandlers.contains(Long.valueOf(id)))
2912 progressBarHandlers.remove(Long.valueOf(id));
2914 removeProgressPanel(panel);
2918 progressBars.put(Long.valueOf(id), addProgressPanel(message));
2925 * @see jalview.gui.IProgressIndicator#registerHandler(long,
2926 * jalview.gui.IProgressIndicatorHandler)
2929 public void registerHandler(final long id,
2930 final IProgressIndicatorHandler handler)
2932 if (progressBarHandlers == null
2933 || !progressBars.containsKey(Long.valueOf(id)))
2935 throw new Error(MessageManager.getString(
2936 "error.call_setprogressbar_before_registering_handler"));
2938 progressBarHandlers.put(Long.valueOf(id), handler);
2939 final JPanel progressPanel = progressBars.get(Long.valueOf(id));
2940 if (handler.canCancel())
2942 JButton cancel = new JButton(
2943 MessageManager.getString("action.cancel"));
2944 final IProgressIndicator us = this;
2945 cancel.addActionListener(new ActionListener()
2949 public void actionPerformed(ActionEvent e)
2951 handler.cancelActivity(id);
2952 us.setProgressBar(MessageManager
2953 .formatMessage("label.cancelled_params", new Object[]
2954 { ((JLabel) progressPanel.getComponent(0)).getText() }),
2958 progressPanel.add(cancel, BorderLayout.EAST);
2964 * @return true if any progress bars are still active
2967 public boolean operationInProgress()
2969 if (progressBars != null && progressBars.size() > 0)
2977 * This will return the first AlignFrame holding the given viewport instance. It
2978 * will break if there are more than one AlignFrames viewing a particular av.
2981 * @return alignFrame for viewport
2983 public static AlignFrame getAlignFrameFor(AlignViewportI viewport)
2985 if (desktop != null)
2987 AlignmentPanel[] aps = getAlignmentPanels(
2988 viewport.getSequenceSetId());
2989 for (int panel = 0; aps != null && panel < aps.length; panel++)
2991 if (aps[panel] != null && aps[panel].av == viewport)
2993 return aps[panel].alignFrame;
3000 public VamsasApplication getVamsasApplication()
3007 * flag set if jalview GUI is being operated programmatically
3009 private boolean inBatchMode = false;
3012 * check if jalview GUI is being operated programmatically
3014 * @return inBatchMode
3016 public boolean isInBatchMode()
3022 * set flag if jalview GUI is being operated programmatically
3024 * @param inBatchMode
3026 public void setInBatchMode(boolean inBatchMode)
3028 this.inBatchMode = inBatchMode;
3031 public void startServiceDiscovery()
3033 startServiceDiscovery(false);
3036 public void startServiceDiscovery(boolean blocking)
3038 boolean alive = true;
3039 Thread t0 = null, t1 = null, t2 = null;
3040 // JAL-940 - JALVIEW 1 services are now being EOLed as of JABA 2.1 release
3043 // todo: changesupport handlers need to be transferred
3044 if (discoverer == null)
3046 discoverer = new jalview.ws.jws1.Discoverer();
3047 // register PCS handler for desktop.
3048 discoverer.addPropertyChangeListener(changeSupport);
3050 // JAL-940 - disabled JWS1 service configuration - always start discoverer
3051 // until we phase out completely
3052 (t0 = new Thread(discoverer)).start();
3055 if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
3057 t2 = jalview.ws.jws2.Jws2Discoverer.getDiscoverer()
3058 .startDiscoverer(changeSupport);
3062 // TODO: do rest service discovery
3071 } catch (Exception e)
3074 alive = (t1 != null && t1.isAlive()) || (t2 != null && t2.isAlive())
3075 || (t3 != null && t3.isAlive())
3076 || (t0 != null && t0.isAlive());
3082 * called to check if the service discovery process completed successfully.
3086 protected void JalviewServicesChanged(PropertyChangeEvent evt)
3088 if (evt.getNewValue() == null || evt.getNewValue() instanceof Vector)
3090 final String ermsg = jalview.ws.jws2.Jws2Discoverer.getDiscoverer()
3091 .getErrorMessages();
3094 if (Cache.getDefault("SHOW_WSDISCOVERY_ERRORS", true))
3096 if (serviceChangedDialog == null)
3098 // only run if we aren't already displaying one of these.
3099 addDialogThread(serviceChangedDialog = new Runnable()
3106 * JalviewDialog jd =new JalviewDialog() {
3108 * @Override protected void cancelPressed() { // TODO
3109 * Auto-generated method stub
3111 * }@Override protected void okPressed() { // TODO
3112 * Auto-generated method stub
3114 * }@Override protected void raiseClosed() { // TODO
3115 * Auto-generated method stub
3117 * } }; jd.initDialogFrame(new
3118 * JLabel("<html><table width=\"450\"><tr><td>" + ermsg +
3119 * "<br/>It may be that you have invalid JABA URLs in your web service preferences,"
3120 * + " or mis-configured HTTP proxy settings.<br/>" +
3121 * "Check the <em>Connections</em> and <em>Web services</em> tab of the"
3123 * " Tools->Preferences dialog box to change them.</td></tr></table></html>"
3124 * ), true, true, "Web Service Configuration Problem", 450,
3127 * jd.waitForInput();
3129 JvOptionPane.showConfirmDialog(Desktop.desktop,
3130 new JLabel("<html><table width=\"450\"><tr><td>"
3131 + ermsg + "</td></tr></table>"
3132 + "<p>It may be that you have invalid JABA URLs<br/>in your web service preferences,"
3133 + "<br>or as a command-line argument, or mis-configured HTTP proxy settings.</p>"
3134 + "<p>Check the <em>Connections</em> and <em>Web services</em> tab<br/>of the"
3135 + " Tools->Preferences dialog box to change them.</p></html>"),
3136 "Web Service Configuration Problem",
3137 JvOptionPane.DEFAULT_OPTION,
3138 JvOptionPane.ERROR_MESSAGE);
3139 serviceChangedDialog = null;
3148 "Errors reported by JABA discovery service. Check web services preferences.\n"
3155 private Runnable serviceChangedDialog = null;
3158 * start a thread to open a URL in the configured browser. Pops up a warning
3159 * dialog to the user if there is an exception when calling out to the browser
3164 public static void showUrl(final String url)
3166 showUrl(url, Desktop.instance);
3170 * Like showUrl but allows progress handler to be specified
3174 * (null) or object implementing IProgressIndicator
3176 public static void showUrl(final String url,
3177 final IProgressIndicator progress)
3179 new Thread(new Runnable()
3186 if (progress != null)
3188 progress.setProgressBar(MessageManager
3189 .formatMessage("status.opening_params", new Object[]
3190 { url }), this.hashCode());
3192 jalview.util.BrowserLauncher.openURL(url);
3193 } catch (Exception ex)
3195 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
3197 .getString("label.web_browser_not_found_unix"),
3198 MessageManager.getString("label.web_browser_not_found"),
3199 JvOptionPane.WARNING_MESSAGE);
3201 ex.printStackTrace();
3203 if (progress != null)
3205 progress.setProgressBar(null, this.hashCode());
3211 public static WsParamSetManager wsparamManager = null;
3213 public static ParamManager getUserParameterStore()
3215 if (wsparamManager == null)
3217 wsparamManager = new WsParamSetManager();
3219 return wsparamManager;
3223 * static hyperlink handler proxy method for use by Jalview's internal windows
3227 public static void hyperlinkUpdate(HyperlinkEvent e)
3229 if (e.getEventType() == EventType.ACTIVATED)
3234 url = e.getURL().toString();
3235 Desktop.showUrl(url);
3236 } catch (Exception x)
3240 if (Cache.log != null)
3242 Cache.log.error("Couldn't handle string " + url + " as a URL.");
3247 "Couldn't handle string " + url + " as a URL.");
3250 // ignore any exceptions due to dud links.
3257 * single thread that handles display of dialogs to user.
3259 ExecutorService dialogExecutor = Executors.newSingleThreadExecutor();
3262 * flag indicating if dialogExecutor should try to acquire a permit
3264 private volatile boolean dialogPause = true;
3269 private java.util.concurrent.Semaphore block = new Semaphore(0);
3271 private static groovy.ui.Console groovyConsole;
3274 * add another dialog thread to the queue
3278 public void addDialogThread(final Runnable prompter)
3280 dialogExecutor.submit(new Runnable()
3290 } catch (InterruptedException x)
3294 if (instance == null)
3300 SwingUtilities.invokeAndWait(prompter);
3301 } catch (Exception q)
3303 Cache.log.warn("Unexpected Exception in dialog thread.", q);
3309 public void startDialogQueue()
3311 // set the flag so we don't pause waiting for another permit and semaphore
3312 // the current task to begin
3313 dialogPause = false;
3318 * Outputs an image of the desktop to file in EPS format, after prompting the
3319 * user for choice of Text or Lineart character rendering (unless a preference
3320 * has been set). The file name is generated as
3323 * Jalview_snapshot_nnnnn.eps where nnnnn is the current timestamp in milliseconds
3327 protected void snapShotWindow_actionPerformed(ActionEvent e)
3329 // currently the menu option to do this is not shown
3332 int width = getWidth();
3333 int height = getHeight();
3335 "Jalview_snapshot_" + System.currentTimeMillis() + ".eps");
3336 ImageWriterI writer = new ImageWriterI()
3339 public void exportImage(Graphics g) throws Exception
3342 Cache.log.info("Successfully written snapshot to file "
3343 + of.getAbsolutePath());
3346 String title = "View of desktop";
3347 ImageExporter exporter = new ImageExporter(writer, null, TYPE.EPS,
3349 exporter.doExport(of, this, width, height, title);
3353 * Explode the views in the given SplitFrame into separate SplitFrame windows.
3354 * This respects (remembers) any previous 'exploded geometry' i.e. the size and
3355 * location last time the view was expanded (if any). However it does not
3356 * remember the split pane divider location - this is set to match the
3357 * 'exploding' frame.
3361 public void explodeViews(SplitFrame sf)
3363 AlignFrame oldTopFrame = (AlignFrame) sf.getTopFrame();
3364 AlignFrame oldBottomFrame = (AlignFrame) sf.getBottomFrame();
3365 List<? extends AlignmentViewPanel> topPanels = oldTopFrame
3367 List<? extends AlignmentViewPanel> bottomPanels = oldBottomFrame
3369 int viewCount = topPanels.size();
3376 * Processing in reverse order works, forwards order leaves the first panels
3377 * not visible. I don't know why!
3379 for (int i = viewCount - 1; i >= 0; i--)
3382 * Make new top and bottom frames. These take over the respective
3383 * AlignmentPanel objects, including their AlignmentViewports, so the
3384 * cdna/protein relationships between the viewports is carried over to the
3387 * explodedGeometry holds the (x, y) position of the previously exploded
3388 * SplitFrame, and the (width, height) of the AlignFrame component
3390 AlignmentPanel topPanel = (AlignmentPanel) topPanels.get(i);
3391 AlignFrame newTopFrame = new AlignFrame(topPanel);
3392 newTopFrame.setSize(oldTopFrame.getSize());
3393 newTopFrame.setVisible(true);
3394 Rectangle geometry = ((AlignViewport) topPanel.getAlignViewport())
3395 .getExplodedGeometry();
3396 if (geometry != null)
3398 newTopFrame.setSize(geometry.getSize());
3401 AlignmentPanel bottomPanel = (AlignmentPanel) bottomPanels.get(i);
3402 AlignFrame newBottomFrame = new AlignFrame(bottomPanel);
3403 newBottomFrame.setSize(oldBottomFrame.getSize());
3404 newBottomFrame.setVisible(true);
3405 geometry = ((AlignViewport) bottomPanel.getAlignViewport())
3406 .getExplodedGeometry();
3407 if (geometry != null)
3409 newBottomFrame.setSize(geometry.getSize());
3412 topPanel.av.setGatherViewsHere(false);
3413 bottomPanel.av.setGatherViewsHere(false);
3414 JInternalFrame splitFrame = new SplitFrame(newTopFrame,
3416 if (geometry != null)
3418 splitFrame.setLocation(geometry.getLocation());
3420 Desktop.addInternalFrame(splitFrame, sf.getTitle(), -1, -1);
3424 * Clear references to the panels (now relocated in the new SplitFrames)
3425 * before closing the old SplitFrame.
3428 bottomPanels.clear();
3433 * Gather expanded split frames, sharing the same pairs of sequence set ids,
3434 * back into the given SplitFrame as additional views. Note that the gathered
3435 * frames may themselves have multiple views.
3439 public void gatherViews(GSplitFrame source)
3442 * special handling of explodedGeometry for a view within a SplitFrame: - it
3443 * holds the (x, y) position of the enclosing SplitFrame, and the (width,
3444 * height) of the AlignFrame component
3446 AlignFrame myTopFrame = (AlignFrame) source.getTopFrame();
3447 AlignFrame myBottomFrame = (AlignFrame) source.getBottomFrame();
3448 myTopFrame.viewport.setExplodedGeometry(new Rectangle(source.getX(),
3449 source.getY(), myTopFrame.getWidth(), myTopFrame.getHeight()));
3450 myBottomFrame.viewport
3451 .setExplodedGeometry(new Rectangle(source.getX(), source.getY(),
3452 myBottomFrame.getWidth(), myBottomFrame.getHeight()));
3453 myTopFrame.viewport.setGatherViewsHere(true);
3454 myBottomFrame.viewport.setGatherViewsHere(true);
3455 String topViewId = myTopFrame.viewport.getSequenceSetId();
3456 String bottomViewId = myBottomFrame.viewport.getSequenceSetId();
3458 JInternalFrame[] frames = desktop.getAllFrames();
3459 for (JInternalFrame frame : frames)
3461 if (frame instanceof SplitFrame && frame != source)
3463 SplitFrame sf = (SplitFrame) frame;
3464 AlignFrame topFrame = (AlignFrame) sf.getTopFrame();
3465 AlignFrame bottomFrame = (AlignFrame) sf.getBottomFrame();
3466 boolean gatherThis = false;
3467 for (int a = 0; a < topFrame.alignPanels.size(); a++)
3469 AlignmentPanel topPanel = topFrame.alignPanels.get(a);
3470 AlignmentPanel bottomPanel = bottomFrame.alignPanels.get(a);
3471 if (topViewId.equals(topPanel.av.getSequenceSetId())
3472 && bottomViewId.equals(bottomPanel.av.getSequenceSetId()))
3475 topPanel.av.setGatherViewsHere(false);
3476 bottomPanel.av.setGatherViewsHere(false);
3477 topPanel.av.setExplodedGeometry(
3478 new Rectangle(sf.getLocation(), topFrame.getSize()));
3479 bottomPanel.av.setExplodedGeometry(
3480 new Rectangle(sf.getLocation(), bottomFrame.getSize()));
3481 myTopFrame.addAlignmentPanel(topPanel, false);
3482 myBottomFrame.addAlignmentPanel(bottomPanel, false);
3488 topFrame.getAlignPanels().clear();
3489 bottomFrame.getAlignPanels().clear();
3496 * The dust settles...give focus to the tab we did this from.
3498 myTopFrame.setDisplayedView(myTopFrame.alignPanel);
3501 public static groovy.ui.Console getGroovyConsole()
3503 return groovyConsole;
3507 * handles the payload of a drag and drop event.
3509 * TODO refactor to desktop utilities class
3512 * - Data source strings extracted from the drop event
3514 * - protocol for each data source extracted from the drop event
3518 * - the payload from the drop event
3521 public static void transferFromDropTarget(List<Object> files,
3522 List<DataSourceType> protocols, DropTargetDropEvent evt,
3523 Transferable t) throws Exception
3526 // BH 2018 changed List<String> to List<Object> to allow for File from SwingJS
3528 // DataFlavor[] flavors = t.getTransferDataFlavors();
3529 // for (int i = 0; i < flavors.length; i++) {
3530 // if (flavors[i].isFlavorJavaFileListType()) {
3531 // evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
3532 // List<File> list = (List<File>) t.getTransferData(flavors[i]);
3533 // for (int j = 0; j < list.size(); j++) {
3534 // File file = (File) list.get(j);
3535 // byte[] data = getDroppedFileBytes(file);
3536 // fileName.setText(file.getName() + " - " + data.length + " " +
3537 // evt.getLocation());
3538 // JTextArea target = (JTextArea) ((DropTarget) evt.getSource()).getComponent();
3539 // target.setText(new String(data));
3541 // dtde.dropComplete(true);
3546 DataFlavor uriListFlavor = new DataFlavor(
3547 "text/uri-list;class=java.lang.String"), urlFlavour = null;
3550 urlFlavour = new DataFlavor(
3551 "application/x-java-url; class=java.net.URL");
3552 } catch (ClassNotFoundException cfe)
3554 Cache.log.debug("Couldn't instantiate the URL dataflavor.", cfe);
3557 if (urlFlavour != null && t.isDataFlavorSupported(urlFlavour))
3562 java.net.URL url = (URL) t.getTransferData(urlFlavour);
3563 // nb: java 8 osx bug https://bugs.openjdk.java.net/browse/JDK-8156099
3564 // means url may be null.
3567 protocols.add(DataSourceType.URL);
3568 files.add(url.toString());
3569 Cache.log.debug("Drop handled as URL dataflavor "
3570 + files.get(files.size() - 1));
3575 if (Platform.isAMacAndNotJS())
3578 "Please ignore plist error - occurs due to problem with java 8 on OSX");
3581 } catch (Throwable ex)
3583 Cache.log.debug("URL drop handler failed.", ex);
3586 if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
3588 // Works on Windows and MacOSX
3589 Cache.log.debug("Drop handled as javaFileListFlavor");
3590 for (Object file : (List) t
3591 .getTransferData(DataFlavor.javaFileListFlavor))
3594 protocols.add(DataSourceType.FILE);
3599 // Unix like behaviour
3600 boolean added = false;
3602 if (t.isDataFlavorSupported(uriListFlavor))
3604 Cache.log.debug("Drop handled as uriListFlavor");
3605 // This is used by Unix drag system
3606 data = (String) t.getTransferData(uriListFlavor);
3610 // fallback to text: workaround - on OSX where there's a JVM bug
3611 Cache.log.debug("standard URIListFlavor failed. Trying text");
3612 // try text fallback
3613 DataFlavor textDf = new DataFlavor(
3614 "text/plain;class=java.lang.String");
3615 if (t.isDataFlavorSupported(textDf))
3617 data = (String) t.getTransferData(textDf);
3620 Cache.log.debug("Plain text drop content returned "
3621 + (data == null ? "Null - failed" : data));
3626 while (protocols.size() < files.size())
3628 Cache.log.debug("Adding missing FILE protocol for "
3629 + files.get(protocols.size()));
3630 protocols.add(DataSourceType.FILE);
3632 for (java.util.StringTokenizer st = new java.util.StringTokenizer(
3633 data, "\r\n"); st.hasMoreTokens();)
3636 String s = st.nextToken();
3637 if (s.startsWith("#"))
3639 // the line is a comment (as per the RFC 2483)
3642 java.net.URI uri = new java.net.URI(s);
3643 if (uri.getScheme().toLowerCase().startsWith("http"))
3645 protocols.add(DataSourceType.URL);
3646 files.add(uri.toString());
3650 // otherwise preserve old behaviour: catch all for file objects
3651 java.io.File file = new java.io.File(uri);
3652 protocols.add(DataSourceType.FILE);
3653 files.add(file.toString());
3658 if (Cache.log.isDebugEnabled())
3660 if (data == null || !added)
3663 if (t.getTransferDataFlavors() != null
3664 && t.getTransferDataFlavors().length > 0)
3667 "Couldn't resolve drop data. Here are the supported flavors:");
3668 for (DataFlavor fl : t.getTransferDataFlavors())
3671 "Supported transfer dataflavor: " + fl.toString());
3672 Object df = t.getTransferData(fl);
3675 Cache.log.debug("Retrieves: " + df);
3679 Cache.log.debug("Retrieved nothing");
3685 Cache.log.debug("Couldn't resolve dataflavor for drop: "
3691 if (Platform.isWindowsAndNotJS())
3693 Cache.log.debug("Scanning dropped content for Windows Link Files");
3695 // resolve any .lnk files in the file drop
3696 for (int f = 0; f < files.size(); f++)
3698 String source = files.get(f).toString().toLowerCase();
3699 if (protocols.get(f).equals(DataSourceType.FILE)
3700 && (source.endsWith(".lnk") || source.endsWith(".url")
3701 || source.endsWith(".site")))
3705 Object obj = files.get(f);
3706 File lf = (obj instanceof File ? (File) obj
3707 : new File((String) obj));
3708 // process link file to get a URL
3709 Cache.log.debug("Found potential link file: " + lf);
3710 WindowsShortcut wscfile = new WindowsShortcut(lf);
3711 String fullname = wscfile.getRealFilename();
3712 protocols.set(f, FormatAdapter.checkProtocol(fullname));
3713 files.set(f, fullname);
3714 Cache.log.debug("Parsed real filename " + fullname
3715 + " to extract protocol: " + protocols.get(f));
3716 } catch (Exception ex)
3719 "Couldn't parse " + files.get(f) + " as a link file.",
3728 * Sets the Preferences property for experimental features to True or False
3729 * depending on the state of the controlling menu item
3732 protected void showExperimental_actionPerformed(boolean selected)
3734 Cache.setProperty(EXPERIMENTAL_FEATURES, Boolean.toString(selected));
3738 * Answers a (possibly empty) list of any structure viewer frames (currently for
3739 * either Jmol or Chimera) which are currently open. This may optionally be
3740 * restricted to viewers of a specified class, or viewers linked to a specified
3744 * if not null, only return viewers linked to this panel
3745 * @param structureViewerClass
3746 * if not null, only return viewers of this class
3749 public List<StructureViewerBase> getStructureViewers(
3750 AlignmentPanel apanel,
3751 Class<? extends StructureViewerBase> structureViewerClass)
3753 List<StructureViewerBase> result = new ArrayList<>();
3754 JInternalFrame[] frames = Desktop.instance.getAllFrames();
3756 for (JInternalFrame frame : frames)
3758 if (frame instanceof StructureViewerBase)
3760 if (structureViewerClass == null
3761 || structureViewerClass.isInstance(frame))
3764 || ((StructureViewerBase) frame).isLinkedWith(apanel))
3766 result.add((StructureViewerBase) frame);