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.DataSourceType;
31 import jalview.io.FileFormat;
32 import jalview.io.FileFormatException;
33 import jalview.io.FileFormatI;
34 import jalview.io.FileFormats;
35 import jalview.io.FileLoader;
36 import jalview.io.FormatAdapter;
37 import jalview.io.IdentifyFile;
38 import jalview.io.JalviewFileChooser;
39 import jalview.io.JalviewFileView;
40 import jalview.jbgui.GSplitFrame;
41 import jalview.jbgui.GStructureViewer;
42 import jalview.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.util.dialogrunner.RunResponse;
50 import jalview.viewmodel.AlignmentViewport;
51 import jalview.ws.params.ParamManager;
52 import jalview.ws.utils.UrlDownloadClient;
54 import java.awt.BorderLayout;
55 import java.awt.Color;
56 import java.awt.Dimension;
57 import java.awt.FontMetrics;
58 import java.awt.Graphics;
59 import java.awt.GridLayout;
60 import java.awt.Point;
61 import java.awt.Rectangle;
62 import java.awt.Toolkit;
63 import java.awt.Window;
64 import java.awt.datatransfer.Clipboard;
65 import java.awt.datatransfer.ClipboardOwner;
66 import java.awt.datatransfer.DataFlavor;
67 import java.awt.datatransfer.Transferable;
68 import java.awt.dnd.DnDConstants;
69 import java.awt.dnd.DropTargetDragEvent;
70 import java.awt.dnd.DropTargetDropEvent;
71 import java.awt.dnd.DropTargetEvent;
72 import java.awt.dnd.DropTargetListener;
73 import java.awt.event.ActionEvent;
74 import java.awt.event.ActionListener;
75 import java.awt.event.InputEvent;
76 import java.awt.event.KeyEvent;
77 import java.awt.event.MouseAdapter;
78 import java.awt.event.MouseEvent;
79 import java.awt.event.WindowAdapter;
80 import java.awt.event.WindowEvent;
81 import java.beans.PropertyChangeEvent;
82 import java.beans.PropertyChangeListener;
83 import java.io.BufferedInputStream;
85 import java.io.FileOutputStream;
86 import java.io.IOException;
88 import java.util.ArrayList;
89 import java.util.Hashtable;
90 import java.util.List;
91 import java.util.ListIterator;
92 import java.util.StringTokenizer;
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.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 private JalviewChangeSupport changeSupport = new JalviewChangeSupport();
153 * news reader - null if it was never started.
155 private BlogReader jvnews = null;
157 private File projectFile;
161 * @see jalview.gui.JalviewChangeSupport#addJalviewPropertyChangeListener(java.beans.PropertyChangeListener)
163 public void addJalviewPropertyChangeListener(
164 PropertyChangeListener listener)
166 changeSupport.addJalviewPropertyChangeListener(listener);
170 * @param propertyName
172 * @see jalview.gui.JalviewChangeSupport#addJalviewPropertyChangeListener(java.lang.String,
173 * java.beans.PropertyChangeListener)
175 public void addJalviewPropertyChangeListener(String propertyName,
176 PropertyChangeListener listener)
178 changeSupport.addJalviewPropertyChangeListener(propertyName, listener);
182 * @param propertyName
184 * @see jalview.gui.JalviewChangeSupport#removeJalviewPropertyChangeListener(java.lang.String,
185 * java.beans.PropertyChangeListener)
187 public void removeJalviewPropertyChangeListener(String propertyName,
188 PropertyChangeListener listener)
190 changeSupport.removeJalviewPropertyChangeListener(propertyName,
194 /** Singleton Desktop instance */
195 public static Desktop instance;
197 public static MyDesktopPane desktop;
199 public static MyDesktopPane getDesktop()
201 // BH 2018 could use currentThread() here as a reference to a
202 // Hashtable<Thread, MyDesktopPane> in JavaScript
206 static int openFrameCount = 0;
208 static final int xOffset = 30;
210 static final int yOffset = 30;
212 public static jalview.ws.jws1.Discoverer discoverer;
214 public static Object[] jalviewClipboard;
216 public static boolean internalCopy = false;
218 static int fileLoadingCount = 0;
220 class MyDesktopManager implements DesktopManager
223 private DesktopManager delegate;
225 public MyDesktopManager(DesktopManager delegate)
227 this.delegate = delegate;
231 public void activateFrame(JInternalFrame f)
235 delegate.activateFrame(f);
236 } catch (NullPointerException npe)
238 Point p = getMousePosition();
239 instance.showPasteMenu(p.x, p.y);
244 public void beginDraggingFrame(JComponent f)
246 delegate.beginDraggingFrame(f);
250 public void beginResizingFrame(JComponent f, int direction)
252 delegate.beginResizingFrame(f, direction);
256 public void closeFrame(JInternalFrame f)
258 delegate.closeFrame(f);
262 public void deactivateFrame(JInternalFrame f)
264 delegate.deactivateFrame(f);
268 public void deiconifyFrame(JInternalFrame f)
270 delegate.deiconifyFrame(f);
274 public void dragFrame(JComponent f, int newX, int newY)
280 delegate.dragFrame(f, newX, newY);
284 public void endDraggingFrame(JComponent f)
286 delegate.endDraggingFrame(f);
291 public void endResizingFrame(JComponent f)
293 delegate.endResizingFrame(f);
298 public void iconifyFrame(JInternalFrame f)
300 delegate.iconifyFrame(f);
304 public void maximizeFrame(JInternalFrame f)
306 delegate.maximizeFrame(f);
310 public void minimizeFrame(JInternalFrame f)
312 delegate.minimizeFrame(f);
316 public void openFrame(JInternalFrame f)
318 delegate.openFrame(f);
322 public void resizeFrame(JComponent f, int newX, int newY, int newWidth,
329 delegate.resizeFrame(f, newX, newY, newWidth, newHeight);
333 public void setBoundsForFrame(JComponent f, int newX, int newY,
334 int newWidth, int newHeight)
336 delegate.setBoundsForFrame(f, newX, newY, newWidth, newHeight);
339 // All other methods, simply delegate
344 * Creates a new Desktop object.
349 * A note to implementors. It is ESSENTIAL that any activities that might block
350 * are spawned off as threads rather than waited for during this constructor.
353 doVamsasClientCheck();
355 doConfigureStructurePrefs();
356 setTitle("Jalview " + jalview.bin.Cache.getProperty("VERSION"));
357 setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
358 boolean selmemusage = jalview.bin.Cache.getDefault("SHOW_MEMUSAGE",
360 boolean showjconsole = jalview.bin.Cache.getDefault("SHOW_JAVA_CONSOLE",
362 desktop = new MyDesktopPane(selmemusage);
363 showMemusage.setSelected(selmemusage);
364 desktop.setBackground(Color.white);
365 getContentPane().setLayout(new BorderLayout());
366 // alternate config - have scrollbars - see notes in JAL-153
367 // JScrollPane sp = new JScrollPane();
368 // sp.getViewport().setView(desktop);
369 // getContentPane().add(sp, BorderLayout.CENTER);
370 getContentPane().add(desktop, BorderLayout.CENTER);
371 desktop.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
373 // This line prevents Windows Look&Feel resizing all new windows to maximum
374 // if previous window was maximised
375 desktop.setDesktopManager(new MyDesktopManager(
376 (Platform.isWindows() ? new DefaultDesktopManager()
378 ? new AquaInternalFrameManager(
379 desktop.getDesktopManager())
380 : desktop.getDesktopManager())));
382 Rectangle dims = getLastKnownDimensions("");
389 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
390 int xPos = Math.max(5, (screenSize.width - 900) / 2);
391 int yPos = Math.max(5, (screenSize.height - 650) / 2);
392 setBounds(xPos, yPos, 900, 650);
401 jconsole = new Console(this, showjconsole);
402 // add essential build information
403 jconsole.setHeader("Jalview Version: "
404 + jalview.bin.Cache.getProperty("VERSION") + "\n"
405 + "Jalview Installation: "
406 + jalview.bin.Cache.getDefault("INSTALLATION", "unknown")
407 + "\n" + "Build Date: "
408 + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown") + "\n"
409 + "Java version: " + System.getProperty("java.version") + "\n"
410 + System.getProperty("os.arch") + " "
411 + System.getProperty("os.name") + " "
412 + System.getProperty("os.version"));
414 showConsole(showjconsole);
416 showNews.setVisible(false);
418 experimentalFeatures.setSelected(showExperimental());
420 getIdentifiersOrgData();
424 // Spawn a thread that shows the splashscreen
426 SwingUtilities.invokeLater(new Runnable()
435 // Thread off a new instance of the file chooser - this reduces the time it
436 // takes to open it later on.
437 new Thread(new Runnable()
442 Cache.log.debug("Filechooser init thread started.");
443 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
444 JalviewFileChooser.forRead(Cache.getProperty("LAST_DIRECTORY"),
446 Cache.log.debug("Filechooser init thread finished.");
449 // Add the service change listener
450 changeSupport.addJalviewPropertyChangeListener("services",
451 new PropertyChangeListener()
455 public void propertyChange(PropertyChangeEvent evt)
457 Cache.log.debug("Firing service changed event for "
458 + evt.getNewValue());
459 JalviewServicesChanged(evt);
464 } // end BH 2018 ignore
466 this.setDropTarget(new java.awt.dnd.DropTarget(desktop, this));
468 this.addWindowListener(new WindowAdapter()
471 public void windowClosing(WindowEvent evt)
478 this.addMouseListener(ma = new MouseAdapter()
481 public void mousePressed(MouseEvent evt)
483 if (evt.isPopupTrigger()) // Mac
485 showPasteMenu(evt.getX(), evt.getY());
490 public void mouseReleased(MouseEvent evt)
492 if (evt.isPopupTrigger()) // Windows
494 showPasteMenu(evt.getX(), evt.getY());
498 desktop.addMouseListener(ma);
503 * Answers true if user preferences to enable experimental features is True
508 public boolean showExperimental()
510 String experimental = Cache.getDefault(EXPERIMENTAL_FEATURES,
511 Boolean.FALSE.toString());
512 return Boolean.valueOf(experimental).booleanValue();
515 public void doConfigureStructurePrefs()
517 // configure services
518 StructureSelectionManager ssm = StructureSelectionManager
519 .getStructureSelectionManager(this);
520 if (jalview.bin.Cache.getDefault(Preferences.ADD_SS_ANN, true))
522 ssm.setAddTempFacAnnot(jalview.bin.Cache
523 .getDefault(Preferences.ADD_TEMPFACT_ANN, true));
524 ssm.setProcessSecondaryStructure(jalview.bin.Cache
525 .getDefault(Preferences.STRUCT_FROM_PDB, true));
526 ssm.setSecStructServices(
527 jalview.bin.Cache.getDefault(Preferences.USE_RNAVIEW, true));
531 ssm.setAddTempFacAnnot(false);
532 ssm.setProcessSecondaryStructure(false);
533 ssm.setSecStructServices(false);
537 public void checkForNews()
546 final Desktop me = this;
547 // Thread off the news reader, in case there are connection problems.
548 addDialogThread(new Runnable()
553 Cache.log.debug("Starting news thread.");
555 jvnews = new BlogReader(me);
556 showNews.setVisible(true);
557 Cache.log.debug("Completed news thread.");
563 public void getIdentifiersOrgData()
565 // Thread off the identifiers fetcher
566 addDialogThread(new Runnable()
571 Cache.log.debug("Downloading data from identifiers.org");
572 UrlDownloadClient client = new UrlDownloadClient();
575 client.download(IdOrgSettings.getUrl(),
576 IdOrgSettings.getDownloadLocation());
577 } catch (IOException e)
579 Cache.log.debug("Exception downloading identifiers.org data"
587 protected void showNews_actionPerformed(ActionEvent e)
589 showNews(showNews.isSelected());
592 void showNews(boolean visible)
601 Cache.log.debug((visible ? "Showing" : "Hiding") + " news.");
602 showNews.setSelected(visible);
603 if (visible && !jvnews.isVisible())
605 new Thread(new Runnable()
610 long now = System.currentTimeMillis();
611 Desktop.instance.setProgressBar(
612 MessageManager.getString("status.refreshing_news"),
614 jvnews.refreshNews();
615 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 (/** @j2sNative false && */ // BH 2018
684 jalview.bin.Cache.vamsasJarsPresent())
686 setupVamsasDisconnectedGui();
687 VamsasMenu.setVisible(true);
688 final Desktop us = this;
689 VamsasMenu.addMenuListener(new MenuListener()
691 // this listener remembers when the menu was first selected, and
692 // doesn't rebuild the session list until it has been cleared and
694 boolean refresh = true;
697 public void menuCanceled(MenuEvent e)
703 public void menuDeselected(MenuEvent e)
709 public void menuSelected(MenuEvent e)
713 us.buildVamsasStMenu();
718 vamsasStart.setVisible(true);
722 void showPasteMenu(int x, int y)
724 JPopupMenu popup = new JPopupMenu();
725 JMenuItem item = new JMenuItem(
726 MessageManager.getString("label.paste_new_window"));
727 item.addActionListener(new ActionListener()
730 public void actionPerformed(ActionEvent evt)
737 popup.show(this, x, y);
744 Clipboard c = Toolkit.getDefaultToolkit().getSystemClipboard();
745 Transferable contents = c.getContents(this);
747 if (contents != null)
749 String file = (String) contents
750 .getTransferData(DataFlavor.stringFlavor);
752 FileFormatI format = new IdentifyFile().identify(file,
753 DataSourceType.PASTE);
755 new FileLoader().LoadFile(file, DataSourceType.PASTE, format);
758 } catch (Exception ex)
761 "Unable to paste alignment from system clipboard:\n" + ex);
766 * Adds and opens the given frame to the desktop
777 public static synchronized void addInternalFrame(
778 final JInternalFrame frame, String title, int w, int h)
780 addInternalFrame(frame, title, true, w, h, true, false);
784 * Add an internal frame to the Jalview desktop
791 * When true, display frame immediately, otherwise, caller must call
792 * setVisible themselves.
798 public static synchronized void addInternalFrame(
799 final JInternalFrame frame, String title, boolean makeVisible,
802 addInternalFrame(frame, title, makeVisible, w, h, true, false);
806 * Add an internal frame to the Jalview desktop and make it visible
819 public static synchronized void addInternalFrame(
820 final JInternalFrame frame, String title, int w, int h,
823 addInternalFrame(frame, title, true, w, h, resizable, false);
827 * Add an internal frame to the Jalview desktop
834 * When true, display frame immediately, otherwise, caller must call
835 * setVisible themselves.
842 * @param ignoreMinSize
843 * Do not set the default minimum size for frame
845 public static synchronized void addInternalFrame(
846 final JInternalFrame frame, String title, boolean makeVisible,
847 int w, int h, boolean resizable, boolean ignoreMinSize)
850 // TODO: allow callers to determine X and Y position of frame (eg. via
852 // TODO: consider fixing method to update entries in the window submenu with
853 // the current window title
855 frame.setTitle(title);
856 if (frame.getWidth() < 1 || frame.getHeight() < 1)
860 // THIS IS A PUBLIC STATIC METHOD, SO IT MAY BE CALLED EVEN IN
861 // A HEADLESS STATE WHEN NO DESKTOP EXISTS. MUST RETURN
862 // IF JALVIEW IS RUNNING HEADLESS
863 // ///////////////////////////////////////////////
864 if (instance == null || (System.getProperty("java.awt.headless") != null
865 && System.getProperty("java.awt.headless").equals("true")))
874 frame.setMinimumSize(
875 new Dimension(DEFAULT_MIN_WIDTH, DEFAULT_MIN_HEIGHT));
877 // Set default dimension for Alignment Frame window.
878 // The Alignment Frame window could be added from a number of places,
880 // I did this here in order not to miss out on any Alignment frame.
881 if (frame instanceof AlignFrame)
883 frame.setMinimumSize(new Dimension(ALIGN_FRAME_DEFAULT_MIN_WIDTH,
884 ALIGN_FRAME_DEFAULT_MIN_HEIGHT));
888 frame.setVisible(makeVisible);
889 frame.setClosable(true);
890 frame.setResizable(resizable);
891 frame.setMaximizable(resizable);
892 frame.setIconifiable(resizable);
893 frame.setOpaque(/** @j2sNative true || */
896 if (frame.getX() < 1 && frame.getY() < 1)
898 frame.setLocation(xOffset * openFrameCount,
899 yOffset * ((openFrameCount - 1) % 10) + yOffset);
903 * add an entry for the new frame in the Window menu
904 * (and remove it when the frame is closed)
906 final JMenuItem menuItem = new JMenuItem(title);
907 frame.addInternalFrameListener(new InternalFrameAdapter()
910 public void internalFrameActivated(InternalFrameEvent evt)
912 JInternalFrame itf = desktop.getSelectedFrame();
915 if (itf instanceof AlignFrame)
917 Jalview.setCurrentAlignFrame((AlignFrame) itf);
924 public void internalFrameClosed(InternalFrameEvent evt)
926 PaintRefresher.RemoveComponent(frame);
929 * defensive check to prevent frames being
930 * added half off the window
932 if (openFrameCount > 0)
938 * ensure no reference to alignFrame retained by menu item listener
940 if (menuItem.getActionListeners().length > 0)
942 menuItem.removeActionListener(menuItem.getActionListeners()[0]);
944 windowMenu.remove(menuItem);
948 menuItem.addActionListener(new ActionListener()
951 public void actionPerformed(ActionEvent e)
955 frame.setSelected(true);
956 frame.setIcon(false);
957 } catch (java.beans.PropertyVetoException ex)
964 setKeyBindings(frame);
968 windowMenu.add(menuItem);
973 frame.setSelected(true);
974 frame.requestFocus();
975 } catch (java.beans.PropertyVetoException ve)
977 } catch (java.lang.ClassCastException cex)
980 "Squashed a possible GUI implementation error. If you can recreate this, please look at http://issues.jalview.org/browse/JAL-869",
986 * Add key bindings to a JInternalFrame so that Ctrl-W and Cmd-W will close the
991 private static void setKeyBindings(JInternalFrame frame)
993 @SuppressWarnings("serial")
994 final Action closeAction = new AbstractAction()
997 public void actionPerformed(ActionEvent e)
1004 * set up key bindings for Ctrl-W and Cmd-W, with the same (Close) action
1006 KeyStroke ctrlWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
1007 InputEvent.CTRL_DOWN_MASK);
1008 KeyStroke cmdWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
1009 Toolkit.getDefaultToolkit().getMenuShortcutKeyMask());
1011 InputMap inputMap = frame
1012 .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
1013 String ctrlW = ctrlWKey.toString();
1014 inputMap.put(ctrlWKey, ctrlW);
1015 inputMap.put(cmdWKey, ctrlW);
1017 ActionMap actionMap = frame.getActionMap();
1018 actionMap.put(ctrlW, closeAction);
1022 public void lostOwnership(Clipboard clipboard, Transferable contents)
1026 Desktop.jalviewClipboard = null;
1029 internalCopy = false;
1033 public void dragEnter(DropTargetDragEvent evt)
1038 public void dragExit(DropTargetEvent evt)
1043 public void dragOver(DropTargetDragEvent evt)
1048 public void dropActionChanged(DropTargetDragEvent evt)
1059 public void drop(DropTargetDropEvent evt)
1061 boolean success = true;
1062 // JAL-1552 - acceptDrop required before getTransferable call for
1063 // Java's Transferable for native dnd
1064 evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
1065 Transferable t = evt.getTransferable();
1066 List<Object> files = new ArrayList<>();
1067 List<DataSourceType> protocols = new ArrayList<>();
1071 Desktop.transferFromDropTarget(files, protocols, evt, t);
1072 } catch (Exception e)
1074 e.printStackTrace();
1082 for (int i = 0; i < files.size(); i++)
1084 // BH 2018 File or String
1085 Object file = files.get(i);
1086 String fileName = file.toString();
1087 DataSourceType protocol = (protocols == null)
1088 ? DataSourceType.FILE
1090 FileFormatI format = null;
1092 if (fileName.endsWith(".jar"))
1094 format = FileFormat.Jalview;
1099 format = new IdentifyFile().identify(file, protocol);
1102 new FileLoader().LoadFile(null, file, protocol, format);
1105 } catch (Exception ex)
1110 evt.dropComplete(success); // need this to ensure input focus is properly
1111 // transfered to any new windows created
1121 public void inputLocalFileMenuItem_actionPerformed(AlignViewport viewport)
1123 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
1124 JalviewFileChooser chooser = JalviewFileChooser
1125 .forRead(Cache.getProperty("LAST_DIRECTORY"), fileFormat);
1127 chooser.setFileView(new JalviewFileView());
1128 chooser.setDialogTitle(
1129 MessageManager.getString("label.open_local_file"));
1130 chooser.setToolTipText(MessageManager.getString("action.open"));
1132 chooser.response(new RunResponse(JalviewFileChooser.APPROVE_OPTION)
1138 File selectedFile = chooser.getSelectedFile();
1139 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1141 FileFormatI format = chooser.getSelectedFormat();
1144 * Call IdentifyFile to verify the file contains what its extension implies.
1145 * Skip this step for dynamically added file formats, because
1146 * IdentifyFile does not know how to recognise them.
1148 if (FileFormats.getInstance().isIdentifiable(format))
1152 format = new IdentifyFile().identify(selectedFile,
1153 DataSourceType.FILE);
1154 } catch (FileFormatException e)
1156 // format = null; //??
1160 new FileLoader().LoadFile(viewport, selectedFile,
1161 DataSourceType.FILE, format);
1163 }).openDialog(this);
1173 public void inputURLMenuItem_actionPerformed(AlignViewport viewport)
1175 // This construct allows us to have a wider textfield
1177 JLabel label = new JLabel(
1178 MessageManager.getString("label.input_file_url"));
1180 JComboBox history = new JComboBox();
1181 JPanel panel = new JPanel(new GridLayout(2, 1));
1184 history.setPreferredSize(new Dimension(400, 20));
1185 history.setEditable(true);
1186 history.addItem("http://www.");
1188 String historyItems = jalview.bin.Cache.getProperty("RECENT_URL");
1192 if (historyItems != null)
1194 st = new StringTokenizer(historyItems, "\t");
1196 while (st.hasMoreTokens())
1198 history.addItem(st.nextElement());
1202 // BH 2018 -- providing a callback for SwingJS
1203 // dialogOption is just a simple way to provide
1204 // context for the modal-like response.
1205 // The only requirement is that desktop implement
1206 // PropertyChangeListener, which is used already in Java
1207 // for changes in input value and such within the dialogs.
1209 String dialogOption = "label.input_alignment_from_url";
1210 desktop.dialogData = new Object[] { dialogOption, viewport, history };
1211 desktop.onDialogReturn(JvOptionPane.showInternalConfirmDialog(desktop,
1212 panel, MessageManager.getString(dialogOption),
1213 JvOptionPane.OK_CANCEL_OPTION));
1215 // no code may follow this, as SwingJS will not block
1216 // callback in JavaScript comes via a property change event,
1217 // thus going into desktop.onDialogReturn(int) just the same as
1223 * Opens the CutAndPaste window for the user to paste an alignment in to
1226 * - if not null, the pasted alignment is added to the current
1227 * alignment; if null, to a new alignment window
1230 public void inputTextboxMenuItem_actionPerformed(
1231 AlignmentViewPanel viewPanel)
1233 CutAndPasteTransfer cap = new CutAndPasteTransfer();
1234 cap.setForInput(viewPanel);
1235 Desktop.addInternalFrame(cap,
1236 MessageManager.getString("label.cut_paste_alignmen_file"), true,
1246 Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
1247 jalview.bin.Cache.setProperty("SCREENGEOMETRY_WIDTH",
1249 jalview.bin.Cache.setProperty("SCREENGEOMETRY_HEIGHT",
1250 screen.height + "");
1251 storeLastKnownDimensions("", new Rectangle(getBounds().x, getBounds().y,
1252 getWidth(), getHeight()));
1254 if (jconsole != null)
1256 storeLastKnownDimensions("JAVA_CONSOLE_", jconsole.getBounds());
1257 jconsole.stopConsole();
1261 storeLastKnownDimensions("JALVIEW_RSS_WINDOW_", jvnews.getBounds());
1264 if (dialogExecutor != null)
1266 dialogExecutor.shutdownNow();
1268 closeAll_actionPerformed(null);
1270 if (groovyConsole != null)
1272 // suppress a possible repeat prompt to save script
1273 groovyConsole.setDirty(false);
1274 groovyConsole.exit();
1279 private void storeLastKnownDimensions(String string, Rectangle jc)
1281 jalview.bin.Cache.log.debug("Storing last known dimensions for "
1282 + string + ": x:" + jc.x + " y:" + jc.y + " width:" + jc.width
1283 + " height:" + jc.height);
1285 jalview.bin.Cache.setProperty(string + "SCREEN_X", jc.x + "");
1286 jalview.bin.Cache.setProperty(string + "SCREEN_Y", jc.y + "");
1287 jalview.bin.Cache.setProperty(string + "SCREEN_WIDTH", jc.width + "");
1288 jalview.bin.Cache.setProperty(string + "SCREEN_HEIGHT", jc.height + "");
1298 public void aboutMenuItem_actionPerformed(ActionEvent e)
1300 // StringBuffer message = getAboutMessage(false);
1301 // JvOptionPane.showInternalMessageDialog(Desktop.desktop,
1303 // message.toString(), "About Jalview", JvOptionPane.INFORMATION_MESSAGE);
1304 new Thread(new Runnable()
1309 new SplashScreen(true);
1314 public StringBuffer getAboutMessage(boolean shortv)
1316 StringBuffer message = new StringBuffer();
1317 message.append("<html>");
1320 message.append("<h1><strong>Version: "
1321 + jalview.bin.Cache.getProperty("VERSION")
1322 + "</strong></h1>");
1323 message.append("<strong>Last Updated: <em>"
1324 + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown")
1325 + "</em></strong>");
1331 message.append("<strong>Version "
1332 + jalview.bin.Cache.getProperty("VERSION")
1333 + "; last updated: "
1334 + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown"));
1337 if (jalview.bin.Cache.getDefault("LATEST_VERSION", "Checking")
1338 .equals("Checking"))
1340 message.append("<br>...Checking latest version...</br>");
1342 else if (!jalview.bin.Cache.getDefault("LATEST_VERSION", "Checking")
1343 .equals(jalview.bin.Cache.getProperty("VERSION")))
1345 boolean red = false;
1346 if (jalview.bin.Cache.getProperty("VERSION").toLowerCase()
1347 .indexOf("automated build") == -1)
1350 // Displayed when code version and jnlp version do not match and code
1351 // version is not a development build
1352 message.append("<div style=\"color: #FF0000;font-style: bold;\">");
1355 message.append("<br>!! Version "
1356 + jalview.bin.Cache.getDefault("LATEST_VERSION",
1358 + " is available for download from "
1359 + jalview.bin.Cache.getDefault("www.jalview.org",
1360 "http://www.jalview.org")
1364 message.append("</div>");
1367 message.append("<br>Authors: " + jalview.bin.Cache.getDefault(
1369 "The Jalview Authors (See AUTHORS file for current list)")
1370 + "<br><br>Development managed by The Barton Group, University of Dundee, Scotland, UK.<br>"
1371 + "<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"
1372 + "<br><br>If you use Jalview, please cite:"
1373 + "<br>Waterhouse, A.M., Procter, J.B., Martin, D.M.A, Clamp, M. and Barton, G. J. (2009)"
1374 + "<br>Jalview Version 2 - a multiple sequence alignment editor and analysis workbench"
1375 + "<br>Bioinformatics doi: 10.1093/bioinformatics/btp033"
1381 * Action on requesting Help documentation
1384 public void documentationMenuItem_actionPerformed()
1390 BrowserLauncher.openURL("http://www.jalview.org/help.html");
1394 Help.showHelpWindow();
1396 } catch (Exception ex)
1398 System.err.println("Error opening help: " + ex.getMessage());
1403 public void closeAll_actionPerformed(ActionEvent e)
1405 // TODO show a progress bar while closing?
1406 JInternalFrame[] frames = desktop.getAllFrames();
1407 for (int i = 0; i < frames.length; i++)
1411 frames[i].setClosed(true);
1412 } catch (java.beans.PropertyVetoException ex)
1416 Jalview.setCurrentAlignFrame(null);
1417 System.out.println("ALL CLOSED");
1418 if (v_client != null)
1420 // TODO clear binding to vamsas document objects on close_all
1424 * reset state of singleton objects as appropriate (clear down session state
1425 * when all windows are closed)
1427 StructureSelectionManager ssm = StructureSelectionManager
1428 .getStructureSelectionManager(this);
1436 public void raiseRelated_actionPerformed(ActionEvent e)
1438 reorderAssociatedWindows(false, false);
1442 public void minimizeAssociated_actionPerformed(ActionEvent e)
1444 reorderAssociatedWindows(true, false);
1447 void closeAssociatedWindows()
1449 reorderAssociatedWindows(false, true);
1455 * @seejalview.jbgui.GDesktop#garbageCollect_actionPerformed(java.awt.event.
1459 protected void garbageCollect_actionPerformed(ActionEvent e)
1461 // We simply collect the garbage
1462 jalview.bin.Cache.log.debug("Collecting garbage...");
1464 jalview.bin.Cache.log.debug("Finished garbage collection.");
1471 * jalview.jbgui.GDesktop#showMemusage_actionPerformed(java.awt.event.ActionEvent
1475 protected void showMemusage_actionPerformed(ActionEvent e)
1477 desktop.showMemoryUsage(showMemusage.isSelected());
1484 * jalview.jbgui.GDesktop#showConsole_actionPerformed(java.awt.event.ActionEvent
1488 protected void showConsole_actionPerformed(ActionEvent e)
1490 showConsole(showConsole.isSelected());
1493 Console jconsole = null;
1496 * control whether the java console is visible or not
1500 void showConsole(boolean selected)
1502 // TODO: decide if we should update properties file
1503 if (jconsole != null) // BH 2018
1505 showConsole.setSelected(selected);
1506 Cache.setProperty("SHOW_JAVA_CONSOLE",
1507 Boolean.valueOf(selected).toString());
1508 jconsole.setVisible(selected);
1512 void reorderAssociatedWindows(boolean minimize, boolean close)
1514 JInternalFrame[] frames = desktop.getAllFrames();
1515 if (frames == null || frames.length < 1)
1520 AlignmentViewport source = null, target = null;
1521 if (frames[0] instanceof AlignFrame)
1523 source = ((AlignFrame) frames[0]).getCurrentView();
1525 else if (frames[0] instanceof TreePanel)
1527 source = ((TreePanel) frames[0]).getViewPort();
1529 else if (frames[0] instanceof PCAPanel)
1531 source = ((PCAPanel) frames[0]).av;
1533 else if (frames[0].getContentPane() instanceof PairwiseAlignPanel)
1535 source = ((PairwiseAlignPanel) frames[0].getContentPane()).av;
1540 for (int i = 0; i < frames.length; i++)
1543 if (frames[i] == null)
1547 if (frames[i] instanceof AlignFrame)
1549 target = ((AlignFrame) frames[i]).getCurrentView();
1551 else if (frames[i] instanceof TreePanel)
1553 target = ((TreePanel) frames[i]).getViewPort();
1555 else if (frames[i] instanceof PCAPanel)
1557 target = ((PCAPanel) frames[i]).av;
1559 else if (frames[i].getContentPane() instanceof PairwiseAlignPanel)
1561 target = ((PairwiseAlignPanel) frames[i].getContentPane()).av;
1564 if (source == target)
1570 frames[i].setClosed(true);
1574 frames[i].setIcon(minimize);
1577 frames[i].toFront();
1581 } catch (java.beans.PropertyVetoException ex)
1596 protected void preferences_actionPerformed(ActionEvent e)
1602 * Prompts the user to choose a file and then saves the Jalview state as a
1603 * Jalview project file
1606 public void saveState_actionPerformed()
1608 JalviewFileChooser chooser = new JalviewFileChooser("jvp",
1611 chooser.setFileView(new JalviewFileView());
1612 chooser.setDialogTitle(MessageManager.getString("label.save_state"));
1614 int value = chooser.showSaveDialog(this);
1616 if (value == JalviewFileChooser.APPROVE_OPTION)
1618 final Desktop me = this;
1619 final java.io.File choice = chooser.getSelectedFile();
1620 setProjectFile(choice);
1622 new Thread(new Runnable()
1627 // TODO: refactor to Jalview desktop session controller action.
1628 setProgressBar(MessageManager.formatMessage(
1629 "label.saving_jalview_project", new Object[]
1630 { choice.getName() }), choice.hashCode());
1631 jalview.bin.Cache.setProperty("LAST_DIRECTORY",
1632 choice.getParent());
1633 // TODO catch and handle errors for savestate
1634 // TODO prevent user from messing with the Desktop whilst we're saving
1637 new Jalview2XML().saveState(choice);
1638 } catch (OutOfMemoryError oom)
1641 "Whilst saving current state to " + choice.getName(),
1643 } catch (Exception ex)
1646 "Problems whilst trying to save to " + choice.getName(),
1648 JvOptionPane.showMessageDialog(me,
1649 MessageManager.formatMessage(
1650 "label.error_whilst_saving_current_state_to",
1652 { choice.getName() }),
1653 MessageManager.getString("label.couldnt_save_project"),
1654 JvOptionPane.WARNING_MESSAGE);
1656 setProgressBar(null, choice.hashCode());
1662 private void setProjectFile(File choice)
1664 this.projectFile = choice;
1667 public File getProjectFile()
1669 return this.projectFile;
1673 * Prompts the user to choose a file and loads in as a Jalview project file
1676 public void loadState_actionPerformed()
1678 JalviewFileChooser chooser = new JalviewFileChooser(
1679 Cache.getProperty("LAST_DIRECTORY"), new String[]
1682 { "Jalview Project", "Jalview Project (old)" },
1684 chooser.setFileView(new JalviewFileView());
1685 chooser.setDialogTitle(MessageManager.getString("label.restore_state"));
1687 int value = chooser.showOpenDialog(this);
1689 if (value == JalviewFileChooser.APPROVE_OPTION)
1691 final File selectedFile = chooser.getSelectedFile();
1692 setProjectFile(selectedFile);
1693 final String choice = selectedFile.getAbsolutePath();
1694 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1695 new Thread(new Runnable()
1700 setProgressBar(MessageManager.formatMessage(
1701 "label.loading_jalview_project", new Object[]
1702 { choice }), choice.hashCode());
1705 new Jalview2XML().loadJalviewAlign(choice);
1706 } catch (OutOfMemoryError oom)
1708 new OOMWarning("Whilst loading project from " + choice, oom);
1709 } catch (Exception ex)
1712 "Problems whilst loading project from " + choice, ex);
1713 JvOptionPane.showMessageDialog(Desktop.desktop,
1714 MessageManager.formatMessage(
1715 "label.error_whilst_loading_project_from",
1718 MessageManager.getString("label.couldnt_load_project"),
1719 JvOptionPane.WARNING_MESSAGE);
1721 setProgressBar(null, choice.hashCode());
1728 public void inputSequence_actionPerformed(ActionEvent e)
1730 new SequenceFetcher(this);
1733 JPanel progressPanel;
1735 ArrayList<JPanel> fileLoadingPanels = new ArrayList<>();
1737 public void startLoading(final Object fileName)
1739 if (fileLoadingCount == 0)
1741 fileLoadingPanels.add(addProgressPanel(MessageManager
1742 .formatMessage("label.loading_file", new Object[]
1748 private JPanel addProgressPanel(String string)
1750 if (progressPanel == null)
1752 progressPanel = new JPanel(new GridLayout(1, 1));
1753 totalProgressCount = 0;
1754 instance.getContentPane().add(progressPanel, BorderLayout.SOUTH);
1756 JPanel thisprogress = new JPanel(new BorderLayout(10, 5));
1757 JProgressBar progressBar = new JProgressBar();
1758 progressBar.setIndeterminate(true);
1760 thisprogress.add(new JLabel(string), BorderLayout.WEST);
1762 thisprogress.add(progressBar, BorderLayout.CENTER);
1763 progressPanel.add(thisprogress);
1764 ((GridLayout) progressPanel.getLayout()).setRows(
1765 ((GridLayout) progressPanel.getLayout()).getRows() + 1);
1766 ++totalProgressCount;
1767 instance.validate();
1768 return thisprogress;
1771 int totalProgressCount = 0;
1773 private void removeProgressPanel(JPanel progbar)
1775 if (progressPanel != null)
1777 synchronized (progressPanel)
1779 progressPanel.remove(progbar);
1780 GridLayout gl = (GridLayout) progressPanel.getLayout();
1781 gl.setRows(gl.getRows() - 1);
1782 if (--totalProgressCount < 1)
1784 this.getContentPane().remove(progressPanel);
1785 progressPanel = null;
1792 public void stopLoading()
1795 if (fileLoadingCount < 1)
1797 while (fileLoadingPanels.size() > 0)
1799 removeProgressPanel(fileLoadingPanels.remove(0));
1801 fileLoadingPanels.clear();
1802 fileLoadingCount = 0;
1807 public static int getViewCount(String alignmentId)
1809 AlignmentViewport[] aps = getViewports(alignmentId);
1810 return (aps == null) ? 0 : aps.length;
1815 * @param alignmentId
1816 * - if null, all sets are returned
1817 * @return all AlignmentPanels concerning the alignmentId sequence set
1819 public static AlignmentPanel[] getAlignmentPanels(String alignmentId)
1821 if (Desktop.desktop == null)
1823 // no frames created and in headless mode
1824 // TODO: verify that frames are recoverable when in headless mode
1827 List<AlignmentPanel> aps = new ArrayList<>();
1828 AlignFrame[] frames = getAlignFrames();
1833 for (AlignFrame af : frames)
1835 for (AlignmentPanel ap : af.alignPanels)
1837 if (alignmentId == null
1838 || alignmentId.equals(ap.av.getSequenceSetId()))
1844 if (aps.size() == 0)
1848 AlignmentPanel[] vap = aps.toArray(new AlignmentPanel[aps.size()]);
1853 * get all the viewports on an alignment.
1855 * @param sequenceSetId
1856 * unique alignment id (may be null - all viewports returned in that
1858 * @return all viewports on the alignment bound to sequenceSetId
1860 public static AlignmentViewport[] getViewports(String sequenceSetId)
1862 List<AlignmentViewport> viewp = new ArrayList<>();
1863 if (desktop != null)
1865 AlignFrame[] frames = Desktop.getAlignFrames();
1867 for (AlignFrame afr : frames)
1869 if (sequenceSetId == null || afr.getViewport().getSequenceSetId()
1870 .equals(sequenceSetId))
1872 if (afr.alignPanels != null)
1874 for (AlignmentPanel ap : afr.alignPanels)
1876 if (sequenceSetId == null
1877 || sequenceSetId.equals(ap.av.getSequenceSetId()))
1885 viewp.add(afr.getViewport());
1889 if (viewp.size() > 0)
1891 return viewp.toArray(new AlignmentViewport[viewp.size()]);
1898 * Explode the views in the given frame into separate AlignFrame
1902 public static void explodeViews(AlignFrame af)
1904 int size = af.alignPanels.size();
1910 for (int i = 0; i < size; i++)
1912 AlignmentPanel ap = af.alignPanels.get(i);
1913 AlignFrame newaf = new AlignFrame(ap);
1916 * Restore the view's last exploded frame geometry if known. Multiple
1917 * views from one exploded frame share and restore the same (frame)
1918 * position and size.
1920 Rectangle geometry = ap.av.getExplodedGeometry();
1921 if (geometry != null)
1923 newaf.setBounds(geometry);
1926 ap.av.setGatherViewsHere(false);
1928 addInternalFrame(newaf, af.getTitle(), AlignFrame.DEFAULT_WIDTH,
1929 AlignFrame.DEFAULT_HEIGHT);
1932 af.alignPanels.clear();
1933 af.closeMenuItem_actionPerformed(true);
1938 * Gather expanded views (separate AlignFrame's) with the same sequence set
1939 * identifier back in to this frame as additional views, and close the expanded
1940 * views. Note the expanded frames may themselves have multiple views. We take
1945 public void gatherViews(AlignFrame source)
1947 source.viewport.setGatherViewsHere(true);
1948 source.viewport.setExplodedGeometry(source.getBounds());
1949 JInternalFrame[] frames = desktop.getAllFrames();
1950 String viewId = source.viewport.getSequenceSetId();
1952 for (int t = 0; t < frames.length; t++)
1954 if (frames[t] instanceof AlignFrame && frames[t] != source)
1956 AlignFrame af = (AlignFrame) frames[t];
1957 boolean gatherThis = false;
1958 for (int a = 0; a < af.alignPanels.size(); a++)
1960 AlignmentPanel ap = af.alignPanels.get(a);
1961 if (viewId.equals(ap.av.getSequenceSetId()))
1964 ap.av.setGatherViewsHere(false);
1965 ap.av.setExplodedGeometry(af.getBounds());
1966 source.addAlignmentPanel(ap, false);
1972 af.alignPanels.clear();
1973 af.closeMenuItem_actionPerformed(true);
1980 jalview.gui.VamsasApplication v_client = null;
1983 public void vamsasImport_actionPerformed(ActionEvent e)
1985 // TODO: JAL-3048 not needed for Jalview-JS
1987 if (v_client == null)
1989 // Load and try to start a session.
1990 JalviewFileChooser chooser = new JalviewFileChooser(
1991 jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
1993 chooser.setFileView(new JalviewFileView());
1994 chooser.setDialogTitle(
1995 MessageManager.getString("label.open_saved_vamsas_session"));
1996 chooser.setToolTipText(MessageManager.getString(
1997 "label.select_vamsas_session_opened_as_new_vamsas_session"));
1999 int value = chooser.showOpenDialog(this);
2001 if (value == JalviewFileChooser.APPROVE_OPTION)
2003 String fle = chooser.getSelectedFile().toString();
2004 if (!vamsasImport(chooser.getSelectedFile()))
2006 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
2007 MessageManager.formatMessage(
2008 "label.couldnt_import_as_vamsas_session",
2012 .getString("label.vamsas_document_import_failed"),
2013 JvOptionPane.ERROR_MESSAGE);
2019 jalview.bin.Cache.log.error(
2020 "Implementation error - load session from a running session is not supported.");
2025 * import file into a new vamsas session (uses jalview.gui.VamsasApplication)
2028 * @return true if import was a success and a session was started.
2030 public boolean vamsasImport(URL url)
2032 // TODO: create progress bar
2033 if (v_client != null)
2036 jalview.bin.Cache.log.error(
2037 "Implementation error - load session from a running session is not supported.");
2043 // copy the URL content to a temporary local file
2044 // TODO: be a bit cleverer here with nio (?!)
2045 File file = File.createTempFile("vdocfromurl", ".vdj");
2046 FileOutputStream fos = new FileOutputStream(file);
2047 BufferedInputStream bis = new BufferedInputStream(url.openStream());
2048 byte[] buffer = new byte[2048];
2050 while ((ln = bis.read(buffer)) > -1)
2052 fos.write(buffer, 0, ln);
2056 v_client = new jalview.gui.VamsasApplication(this, file,
2057 url.toExternalForm());
2058 } catch (Exception ex)
2060 jalview.bin.Cache.log.error(
2061 "Failed to create new vamsas session from contents of URL "
2066 setupVamsasConnectedGui();
2067 v_client.initial_update(); // TODO: thread ?
2068 return v_client.inSession();
2072 * import file into a new vamsas session (uses jalview.gui.VamsasApplication)
2075 * @return true if import was a success and a session was started.
2077 public boolean vamsasImport(File file)
2079 if (v_client != null)
2082 jalview.bin.Cache.log.error(
2083 "Implementation error - load session from a running session is not supported.");
2087 setProgressBar(MessageManager.formatMessage(
2088 "status.importing_vamsas_session_from", new Object[]
2089 { file.getName() }), file.hashCode());
2092 v_client = new jalview.gui.VamsasApplication(this, file, null);
2093 } catch (Exception ex)
2095 setProgressBar(MessageManager.formatMessage(
2096 "status.importing_vamsas_session_from", new Object[]
2097 { file.getName() }), file.hashCode());
2098 jalview.bin.Cache.log.error(
2099 "New vamsas session from existing session file failed:", ex);
2102 setupVamsasConnectedGui();
2103 v_client.initial_update(); // TODO: thread ?
2104 setProgressBar(MessageManager.formatMessage(
2105 "status.importing_vamsas_session_from", new Object[]
2106 { file.getName() }), file.hashCode());
2107 return v_client.inSession();
2110 public boolean joinVamsasSession(String mysesid)
2112 if (v_client != null)
2114 throw new Error(MessageManager
2115 .getString("error.try_join_vamsas_session_another"));
2117 if (mysesid == null)
2120 MessageManager.getString("error.invalid_vamsas_session_id"));
2122 v_client = new VamsasApplication(this, mysesid);
2123 setupVamsasConnectedGui();
2124 v_client.initial_update();
2125 return (v_client.inSession());
2129 public void vamsasStart_actionPerformed(ActionEvent e)
2131 if (v_client == null)
2134 // we just start a default session for moment.
2136 * JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache.
2137 * getProperty("LAST_DIRECTORY"));
2139 * chooser.setFileView(new JalviewFileView());
2140 * chooser.setDialogTitle("Load Vamsas file");
2141 * chooser.setToolTipText("Import");
2143 * int value = chooser.showOpenDialog(this);
2145 * if (value == JalviewFileChooser.APPROVE_OPTION) { v_client = new
2146 * jalview.gui.VamsasApplication(this, chooser.getSelectedFile());
2148 v_client = new VamsasApplication(this);
2149 setupVamsasConnectedGui();
2150 v_client.initial_update(); // TODO: thread ?
2154 // store current data in session.
2155 v_client.push_update(); // TODO: thread
2159 protected void setupVamsasConnectedGui()
2161 vamsasStart.setText(MessageManager.getString("label.session_update"));
2162 vamsasSave.setVisible(true);
2163 vamsasStop.setVisible(true);
2164 vamsasImport.setVisible(false); // Document import to existing session is
2165 // not possible for vamsas-client-1.0.
2168 protected void setupVamsasDisconnectedGui()
2170 vamsasSave.setVisible(false);
2171 vamsasStop.setVisible(false);
2172 vamsasImport.setVisible(true);
2174 .setText(MessageManager.getString("label.new_vamsas_session"));
2178 public void vamsasStop_actionPerformed(ActionEvent e)
2180 if (v_client != null)
2182 v_client.end_session();
2184 setupVamsasDisconnectedGui();
2188 protected void buildVamsasStMenu()
2190 if (v_client == null)
2192 String[] sess = null;
2195 sess = VamsasApplication.getSessionList();
2196 } catch (Exception e)
2198 jalview.bin.Cache.log.warn("Problem getting current sessions list.",
2204 jalview.bin.Cache.log.debug(
2205 "Got current sessions list: " + sess.length + " entries.");
2206 VamsasStMenu.removeAll();
2207 for (int i = 0; i < sess.length; i++)
2209 JMenuItem sessit = new JMenuItem();
2210 sessit.setText(sess[i]);
2211 sessit.setToolTipText(MessageManager
2212 .formatMessage("label.connect_to_session", new Object[]
2214 final Desktop dsktp = this;
2215 final String mysesid = sess[i];
2216 sessit.addActionListener(new ActionListener()
2220 public void actionPerformed(ActionEvent e)
2222 if (dsktp.v_client == null)
2224 Thread rthr = new Thread(new Runnable()
2230 dsktp.v_client = new VamsasApplication(dsktp, mysesid);
2231 dsktp.setupVamsasConnectedGui();
2232 dsktp.v_client.initial_update();
2240 VamsasStMenu.add(sessit);
2242 // don't show an empty menu.
2243 VamsasStMenu.setVisible(sess.length > 0);
2248 jalview.bin.Cache.log.debug("No current vamsas sessions.");
2249 VamsasStMenu.removeAll();
2250 VamsasStMenu.setVisible(false);
2255 // Not interested in the content. Just hide ourselves.
2256 VamsasStMenu.setVisible(false);
2261 public void vamsasSave_actionPerformed(ActionEvent e)
2263 // TODO: JAL-3048 not needed for Jalview-JS
2265 if (v_client != null)
2267 // TODO: VAMSAS DOCUMENT EXTENSION is VDJ
2268 JalviewFileChooser chooser = new JalviewFileChooser("vdj",
2271 chooser.setFileView(new JalviewFileView());
2272 chooser.setDialogTitle(MessageManager
2273 .getString("label.save_vamsas_document_archive"));
2275 int value = chooser.showSaveDialog(this);
2277 if (value == JalviewFileChooser.APPROVE_OPTION)
2279 java.io.File choice = chooser.getSelectedFile();
2280 JPanel progpanel = addProgressPanel(MessageManager
2281 .formatMessage("label.saving_vamsas_doc", new Object[]
2282 { choice.getName() }));
2283 Cache.setProperty("LAST_DIRECTORY", choice.getParent());
2284 String warnmsg = null;
2285 String warnttl = null;
2288 v_client.vclient.storeDocument(choice);
2291 warnttl = "Serious Problem saving Vamsas Document";
2292 warnmsg = ex.toString();
2293 jalview.bin.Cache.log
2294 .error("Error Whilst saving document to " + choice, ex);
2296 } catch (Exception ex)
2298 warnttl = "Problem saving Vamsas Document.";
2299 warnmsg = ex.toString();
2300 jalview.bin.Cache.log.warn(
2301 "Exception Whilst saving document to " + choice, ex);
2304 removeProgressPanel(progpanel);
2305 if (warnmsg != null)
2307 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
2309 warnmsg, warnttl, JvOptionPane.ERROR_MESSAGE);
2315 JPanel vamUpdate = null;
2318 * hide vamsas user gui bits when a vamsas document event is being handled.
2321 * true to hide gui, false to reveal gui
2323 public void setVamsasUpdate(boolean b)
2325 Cache.log.debug("Setting gui for Vamsas update "
2326 + (b ? "in progress" : "finished"));
2328 if (vamUpdate != null)
2330 this.removeProgressPanel(vamUpdate);
2334 vamUpdate = this.addProgressPanel(
2335 MessageManager.getString("label.updating_vamsas_session"));
2337 vamsasStart.setVisible(!b);
2338 vamsasStop.setVisible(!b);
2339 vamsasSave.setVisible(!b);
2342 public JInternalFrame[] getAllFrames()
2344 return desktop.getAllFrames();
2348 * Checks the given url to see if it gives a response indicating that the user
2349 * should be informed of a new questionnaire.
2353 public void checkForQuestionnaire(String url)
2355 UserQuestionnaireCheck jvq = new UserQuestionnaireCheck(url);
2356 // javax.swing.SwingUtilities.invokeLater(jvq);
2357 new Thread(jvq).start();
2360 public void checkURLLinks()
2362 // Thread off the URL link checker
2363 addDialogThread(new Runnable()
2368 if (/** @j2sNative false && */ // BH 2018
2369 Cache.getDefault("CHECKURLLINKS", true))
2371 // check what the actual links are - if it's just the default don't
2372 // bother with the warning
2373 List<String> links = Preferences.sequenceUrlLinks
2376 // only need to check links if there is one with a
2377 // SEQUENCE_ID which is not the default EMBL_EBI link
2378 ListIterator<String> li = links.listIterator();
2379 boolean check = false;
2380 List<JLabel> urls = new ArrayList<>();
2381 while (li.hasNext())
2383 String link = li.next();
2384 if (link.contains(SEQUENCE_ID)
2385 && !UrlConstants.isDefaultString(link))
2388 int barPos = link.indexOf("|");
2389 String urlMsg = barPos == -1 ? link
2390 : link.substring(0, barPos) + ": "
2391 + link.substring(barPos + 1);
2392 urls.add(new JLabel(urlMsg));
2400 // ask user to check in case URL links use old style tokens
2401 // ($SEQUENCE_ID$ for sequence id _or_ accession id)
2402 JPanel msgPanel = new JPanel();
2403 msgPanel.setLayout(new BoxLayout(msgPanel, BoxLayout.PAGE_AXIS));
2404 msgPanel.add(Box.createVerticalGlue());
2405 JLabel msg = new JLabel(MessageManager
2406 .getString("label.SEQUENCE_ID_for_DB_ACCESSION1"));
2407 JLabel msg2 = new JLabel(MessageManager
2408 .getString("label.SEQUENCE_ID_for_DB_ACCESSION2"));
2410 for (JLabel url : urls)
2416 final JCheckBox jcb = new JCheckBox(
2417 MessageManager.getString("label.do_not_display_again"));
2418 jcb.addActionListener(new ActionListener()
2421 public void actionPerformed(ActionEvent e)
2423 // update Cache settings for "don't show this again"
2424 boolean showWarningAgain = !jcb.isSelected();
2425 Cache.setProperty("CHECKURLLINKS",
2426 Boolean.valueOf(showWarningAgain).toString());
2431 JvOptionPane.showMessageDialog(Desktop.desktop, msgPanel,
2433 .getString("label.SEQUENCE_ID_no_longer_used"),
2434 JvOptionPane.WARNING_MESSAGE);
2441 * Proxy class for JDesktopPane which optionally displays the current memory
2442 * usage and highlights the desktop area with a red bar if free memory runs low.
2446 public class MyDesktopPane extends JDesktopPane
2447 implements Runnable, PropertyChangeListener
2450 public Object[] dialogData;
2454 public void propertyChange(PropertyChangeEvent event)
2456 // TODO this is obsolete with JAL-3048 - delete?
2457 Object val = event.getNewValue();
2458 String name = event.getPropertyName();
2459 System.out.println(name);
2460 switch (event.getSource().getClass().getName())
2462 case "javax.swing.JOptionPane":
2466 onDialogReturn(val);
2469 if (val instanceof Integer)
2471 onDialogReturn(((Integer) val).intValue());
2475 onDialogReturn(val);
2480 case "javax.swing.JFileChooser":
2483 case "SelectedFile":
2484 // in JavaScript, this File object will have a _bytes property,
2485 // because the file data has already been loaded
2486 onDialogReturn(new Object[] { (File) val });
2491 System.out.println(event.getSource().getClass().getName() + " "
2492 + event.getPropertyName() + ": " + event.getNewValue());
2495 // JSCOmponent.DialogCaller interface
2496 void onDialogReturn(Object value)
2498 switch ((String) dialogData[0])
2500 case "SelectedFile":
2502 dialogData[0] = value;
2503 ((Runnable) dialogData[1]).run();
2509 // JSCOmponent.DialogCaller interface
2510 void onDialogReturn(int value)
2512 if (value != Math.floor(value))
2514 // in JavaScript, this will be NaN, oddly enough
2518 switch ((String) dialogData[0])
2521 dialogData[0] = Integer.valueOf(value);
2522 ((Runnable) dialogData[1]).run();
2524 case "label.input_alignment_from_url":
2525 // reconstruct the parameter data
2527 AlignViewport viewport = (AlignViewport) dialogData[1];
2528 JComboBox history = (JComboBox) dialogData[2];
2529 // the rest of this is unchangaed
2530 if (reply != JvOptionPane.OK_OPTION)
2535 String url = history.getSelectedItem().toString();
2537 if (url.toLowerCase().endsWith(".jar"))
2539 if (viewport != null)
2541 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
2542 FileFormat.Jalview);
2546 new FileLoader().LoadFile(url, DataSourceType.URL,
2547 FileFormat.Jalview);
2552 FileFormatI format = null;
2555 format = new IdentifyFile().identify(url, DataSourceType.URL);
2556 } catch (FileFormatException e)
2558 // TODO revise error handling, distinguish between
2559 // URL not found and response not valid
2564 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
2565 MessageManager.formatMessage("label.couldnt_locate",
2568 MessageManager.getString("label.url_not_found"),
2569 JvOptionPane.WARNING_MESSAGE);
2574 if (viewport != null)
2576 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
2581 new FileLoader().LoadFile(url, DataSourceType.URL, format);
2590 private static final float ONE_MB = 1048576f;
2592 boolean showMemoryUsage = false;
2596 java.text.NumberFormat df;
2598 float maxMemory, allocatedMemory, freeMemory, totalFreeMemory,
2601 public MyDesktopPane(boolean showMemoryUsage)
2603 showMemoryUsage(showMemoryUsage);
2606 public void showMemoryUsage(boolean showMemory)
2608 this.showMemoryUsage = showMemory;
2611 Thread worker = new Thread(this);
2617 public boolean isShowMemoryUsage()
2619 return showMemoryUsage;
2625 df = java.text.NumberFormat.getNumberInstance();
2626 df.setMaximumFractionDigits(2);
2627 runtime = Runtime.getRuntime();
2629 while (showMemoryUsage)
2633 maxMemory = runtime.maxMemory() / ONE_MB;
2634 allocatedMemory = runtime.totalMemory() / ONE_MB;
2635 freeMemory = runtime.freeMemory() / ONE_MB;
2636 totalFreeMemory = freeMemory + (maxMemory - allocatedMemory);
2638 percentUsage = (totalFreeMemory / maxMemory) * 100;
2640 // if (percentUsage < 20)
2642 // border1 = BorderFactory.createMatteBorder(12, 12, 12, 12,
2644 // instance.set.setBorder(border1);
2647 // sleep after showing usage
2649 } catch (Exception ex)
2651 ex.printStackTrace();
2657 public void paintComponent(Graphics g)
2659 if (showMemoryUsage && g != null && df != null)
2661 if (percentUsage < 20)
2663 g.setColor(Color.red);
2665 FontMetrics fm = g.getFontMetrics();
2668 g.drawString(MessageManager.formatMessage("label.memory_stats",
2670 { df.format(totalFreeMemory), df.format(maxMemory),
2671 df.format(percentUsage) }),
2672 10, getHeight() - fm.getHeight());
2679 * Accessor method to quickly get all the AlignmentFrames loaded.
2681 * @return an array of AlignFrame, or null if none found
2683 public static AlignFrame[] getAlignFrames()
2685 if (Jalview.isHeadlessMode())
2687 // Desktop.desktop is null in headless mode
2688 return new AlignFrame[] { Jalview.currentAlignFrame };
2691 JInternalFrame[] frames = Desktop.desktop.getAllFrames();
2697 List<AlignFrame> avp = new ArrayList<>();
2699 for (int i = frames.length - 1; i > -1; i--)
2701 if (frames[i] instanceof AlignFrame)
2703 avp.add((AlignFrame) frames[i]);
2705 else if (frames[i] instanceof SplitFrame)
2708 * Also check for a split frame containing an AlignFrame
2710 GSplitFrame sf = (GSplitFrame) frames[i];
2711 if (sf.getTopFrame() instanceof AlignFrame)
2713 avp.add((AlignFrame) sf.getTopFrame());
2715 if (sf.getBottomFrame() instanceof AlignFrame)
2717 avp.add((AlignFrame) sf.getBottomFrame());
2721 if (avp.size() == 0)
2725 AlignFrame afs[] = avp.toArray(new AlignFrame[avp.size()]);
2730 * Returns an array of any AppJmol frames in the Desktop (or null if none).
2734 public GStructureViewer[] getJmols()
2736 JInternalFrame[] frames = Desktop.desktop.getAllFrames();
2742 List<GStructureViewer> avp = new ArrayList<>();
2744 for (int i = frames.length - 1; i > -1; i--)
2746 if (frames[i] instanceof AppJmol)
2748 GStructureViewer af = (GStructureViewer) frames[i];
2752 if (avp.size() == 0)
2756 GStructureViewer afs[] = avp.toArray(new GStructureViewer[avp.size()]);
2761 * Add Groovy Support to Jalview
2764 public void groovyShell_actionPerformed()
2768 openGroovyConsole();
2769 } catch (Exception ex)
2771 jalview.bin.Cache.log.error("Groovy Shell Creation failed.", ex);
2772 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
2774 MessageManager.getString("label.couldnt_create_groovy_shell"),
2775 MessageManager.getString("label.groovy_support_failed"),
2776 JvOptionPane.ERROR_MESSAGE);
2781 * Open the Groovy console
2783 void openGroovyConsole()
2785 if (groovyConsole == null)
2787 groovyConsole = new groovy.ui.Console();
2788 groovyConsole.setVariable("Jalview", this);
2789 groovyConsole.run();
2792 * We allow only one console at a time, so that AlignFrame menu option
2793 * 'Calculate | Run Groovy script' is unambiguous.
2794 * Disable 'Groovy Console', and enable 'Run script', when the console is
2795 * opened, and the reverse when it is closed
2797 Window window = (Window) groovyConsole.getFrame();
2798 window.addWindowListener(new WindowAdapter()
2801 public void windowClosed(WindowEvent e)
2804 * rebind CMD-Q from Groovy Console to Jalview Quit
2807 enableExecuteGroovy(false);
2813 * show Groovy console window (after close and reopen)
2815 ((Window) groovyConsole.getFrame()).setVisible(true);
2818 * if we got this far, enable 'Run Groovy' in AlignFrame menus
2819 * and disable opening a second console
2821 enableExecuteGroovy(true);
2825 * Bind Ctrl/Cmd-Q to Quit - for reset as Groovy Console takes over this binding
2828 protected void addQuitHandler()
2830 getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
2831 .put(KeyStroke.getKeyStroke(KeyEvent.VK_Q,
2832 Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()),
2834 getRootPane().getActionMap().put("Quit", new AbstractAction()
2837 public void actionPerformed(ActionEvent e)
2845 * Enable or disable 'Run Groovy script' in AlignFrame calculate menus
2848 * true if Groovy console is open
2850 public void enableExecuteGroovy(boolean enabled)
2853 * disable opening a second Groovy console
2854 * (or re-enable when the console is closed)
2856 groovyShell.setEnabled(!enabled);
2858 AlignFrame[] alignFrames = getAlignFrames();
2859 if (alignFrames != null)
2861 for (AlignFrame af : alignFrames)
2863 af.setGroovyEnabled(enabled);
2869 * Progress bars managed by the IProgressIndicator method.
2871 private Hashtable<Long, JPanel> progressBars;
2873 private Hashtable<Long, IProgressIndicatorHandler> progressBarHandlers;
2878 * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
2881 public void setProgressBar(String message, long id)
2883 if (progressBars == null)
2885 progressBars = new Hashtable<>();
2886 progressBarHandlers = new Hashtable<>();
2889 if (progressBars.get(new Long(id)) != null)
2891 JPanel panel = progressBars.remove(new Long(id));
2892 if (progressBarHandlers.contains(new Long(id)))
2894 progressBarHandlers.remove(new Long(id));
2896 removeProgressPanel(panel);
2900 progressBars.put(new Long(id), addProgressPanel(message));
2907 * @see jalview.gui.IProgressIndicator#registerHandler(long,
2908 * jalview.gui.IProgressIndicatorHandler)
2911 public void registerHandler(final long id,
2912 final IProgressIndicatorHandler handler)
2914 if (progressBarHandlers == null
2915 || !progressBars.containsKey(new Long(id)))
2917 throw new Error(MessageManager.getString(
2918 "error.call_setprogressbar_before_registering_handler"));
2920 progressBarHandlers.put(new Long(id), handler);
2921 final JPanel progressPanel = progressBars.get(new Long(id));
2922 if (handler.canCancel())
2924 JButton cancel = new JButton(
2925 MessageManager.getString("action.cancel"));
2926 final IProgressIndicator us = this;
2927 cancel.addActionListener(new ActionListener()
2931 public void actionPerformed(ActionEvent e)
2933 handler.cancelActivity(id);
2934 us.setProgressBar(MessageManager
2935 .formatMessage("label.cancelled_params", new Object[]
2936 { ((JLabel) progressPanel.getComponent(0)).getText() }),
2940 progressPanel.add(cancel, BorderLayout.EAST);
2946 * @return true if any progress bars are still active
2949 public boolean operationInProgress()
2951 if (progressBars != null && progressBars.size() > 0)
2959 * This will return the first AlignFrame holding the given viewport instance. It
2960 * will break if there are more than one AlignFrames viewing a particular av.
2963 * @return alignFrame for viewport
2965 public static AlignFrame getAlignFrameFor(AlignViewportI viewport)
2967 if (desktop != null)
2969 AlignmentPanel[] aps = getAlignmentPanels(
2970 viewport.getSequenceSetId());
2971 for (int panel = 0; aps != null && panel < aps.length; panel++)
2973 if (aps[panel] != null && aps[panel].av == viewport)
2975 return aps[panel].alignFrame;
2982 public VamsasApplication getVamsasApplication()
2989 * flag set if jalview GUI is being operated programmatically
2991 private boolean inBatchMode = false;
2994 * check if jalview GUI is being operated programmatically
2996 * @return inBatchMode
2998 public boolean isInBatchMode()
3004 * set flag if jalview GUI is being operated programmatically
3006 * @param inBatchMode
3008 public void setInBatchMode(boolean inBatchMode)
3010 this.inBatchMode = inBatchMode;
3013 public void startServiceDiscovery()
3015 startServiceDiscovery(false);
3018 public void startServiceDiscovery(boolean blocking)
3020 boolean alive = true;
3021 Thread t0 = null, t1 = null, t2 = null;
3022 // JAL-940 - JALVIEW 1 services are now being EOLed as of JABA 2.1 release
3025 // todo: changesupport handlers need to be transferred
3026 if (discoverer == null)
3028 discoverer = new jalview.ws.jws1.Discoverer();
3029 // register PCS handler for desktop.
3030 discoverer.addPropertyChangeListener(changeSupport);
3032 // JAL-940 - disabled JWS1 service configuration - always start discoverer
3033 // until we phase out completely
3034 (t0 = new Thread(discoverer)).start();
3037 if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
3039 t2 = jalview.ws.jws2.Jws2Discoverer.getDiscoverer()
3040 .startDiscoverer(changeSupport);
3044 // TODO: do rest service discovery
3053 } catch (Exception e)
3056 alive = (t1 != null && t1.isAlive()) || (t2 != null && t2.isAlive())
3057 || (t3 != null && t3.isAlive())
3058 || (t0 != null && t0.isAlive());
3064 * called to check if the service discovery process completed successfully.
3068 protected void JalviewServicesChanged(PropertyChangeEvent evt)
3070 if (evt.getNewValue() == null || evt.getNewValue() instanceof Vector)
3072 final String ermsg = jalview.ws.jws2.Jws2Discoverer.getDiscoverer()
3073 .getErrorMessages();
3076 if (Cache.getDefault("SHOW_WSDISCOVERY_ERRORS", true))
3078 if (serviceChangedDialog == null)
3080 // only run if we aren't already displaying one of these.
3081 addDialogThread(serviceChangedDialog = new Runnable()
3088 * JalviewDialog jd =new JalviewDialog() {
3090 * @Override protected void cancelPressed() { // TODO
3091 * Auto-generated method stub
3093 * }@Override protected void okPressed() { // TODO
3094 * Auto-generated method stub
3096 * }@Override protected void raiseClosed() { // TODO
3097 * Auto-generated method stub
3099 * } }; jd.initDialogFrame(new
3100 * JLabel("<html><table width=\"450\"><tr><td>" + ermsg +
3101 * "<br/>It may be that you have invalid JABA URLs in your web service preferences,"
3102 * + " or mis-configured HTTP proxy settings.<br/>" +
3103 * "Check the <em>Connections</em> and <em>Web services</em> tab of the"
3105 * " Tools->Preferences dialog box to change them.</td></tr></table></html>"
3106 * ), true, true, "Web Service Configuration Problem", 450,
3109 * jd.waitForInput();
3111 JvOptionPane.showConfirmDialog(Desktop.desktop,
3112 new JLabel("<html><table width=\"450\"><tr><td>"
3113 + ermsg + "</td></tr></table>"
3114 + "<p>It may be that you have invalid JABA URLs<br/>in your web service preferences,"
3115 + "<br>or as a command-line argument, or mis-configured HTTP proxy settings.</p>"
3116 + "<p>Check the <em>Connections</em> and <em>Web services</em> tab<br/>of the"
3117 + " Tools->Preferences dialog box to change them.</p></html>"),
3118 "Web Service Configuration Problem",
3119 JvOptionPane.DEFAULT_OPTION,
3120 JvOptionPane.ERROR_MESSAGE);
3121 serviceChangedDialog = null;
3130 "Errors reported by JABA discovery service. Check web services preferences.\n"
3137 private Runnable serviceChangedDialog = null;
3140 * start a thread to open a URL in the configured browser. Pops up a warning
3141 * dialog to the user if there is an exception when calling out to the browser
3146 public static void showUrl(final String url)
3148 showUrl(url, Desktop.instance);
3152 * Like showUrl but allows progress handler to be specified
3156 * (null) or object implementing IProgressIndicator
3158 public static void showUrl(final String url,
3159 final IProgressIndicator progress)
3161 new Thread(new Runnable()
3168 if (progress != null)
3170 progress.setProgressBar(MessageManager
3171 .formatMessage("status.opening_params", new Object[]
3172 { url }), this.hashCode());
3174 jalview.util.BrowserLauncher.openURL(url);
3175 } catch (Exception ex)
3177 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
3179 .getString("label.web_browser_not_found_unix"),
3180 MessageManager.getString("label.web_browser_not_found"),
3181 JvOptionPane.WARNING_MESSAGE);
3183 ex.printStackTrace();
3185 if (progress != null)
3187 progress.setProgressBar(null, this.hashCode());
3193 public static WsParamSetManager wsparamManager = null;
3195 public static ParamManager getUserParameterStore()
3197 if (wsparamManager == null)
3199 wsparamManager = new WsParamSetManager();
3201 return wsparamManager;
3205 * static hyperlink handler proxy method for use by Jalview's internal windows
3209 public static void hyperlinkUpdate(HyperlinkEvent e)
3211 if (e.getEventType() == EventType.ACTIVATED)
3216 url = e.getURL().toString();
3217 Desktop.showUrl(url);
3218 } catch (Exception x)
3222 if (Cache.log != null)
3224 Cache.log.error("Couldn't handle string " + url + " as a URL.");
3229 "Couldn't handle string " + url + " as a URL.");
3232 // ignore any exceptions due to dud links.
3239 * single thread that handles display of dialogs to user.
3241 ExecutorService dialogExecutor = Executors.newSingleThreadExecutor();
3244 * flag indicating if dialogExecutor should try to acquire a permit
3246 private volatile boolean dialogPause = true;
3251 private java.util.concurrent.Semaphore block = new Semaphore(0);
3253 private static groovy.ui.Console groovyConsole;
3256 * add another dialog thread to the queue
3260 public void addDialogThread(final Runnable prompter)
3262 dialogExecutor.submit(new Runnable()
3272 } catch (InterruptedException x)
3277 if (instance == null)
3283 SwingUtilities.invokeAndWait(prompter);
3284 } catch (Exception q)
3286 Cache.log.warn("Unexpected Exception in dialog thread.", q);
3292 public void startDialogQueue()
3294 // set the flag so we don't pause waiting for another permit and semaphore
3295 // the current task to begin
3296 dialogPause = false;
3301 * Outputs an image of the desktop to file in EPS format, after prompting the
3302 * user for choice of Text or Lineart character rendering (unless a preference
3303 * has been set). The file name is generated as
3306 * Jalview_snapshot_nnnnn.eps where nnnnn is the current timestamp in milliseconds
3310 protected void snapShotWindow_actionPerformed(ActionEvent e)
3312 // currently the menu option to do this is not shown
3315 int width = getWidth();
3316 int height = getHeight();
3318 "Jalview_snapshot_" + System.currentTimeMillis() + ".eps");
3319 ImageWriterI writer = new ImageWriterI()
3322 public void exportImage(Graphics g) throws Exception
3325 Cache.log.info("Successfully written snapshot to file "
3326 + of.getAbsolutePath());
3329 String title = "View of desktop";
3330 ImageExporter exporter = new ImageExporter(writer, null, TYPE.EPS,
3332 exporter.doExport(of, this, width, height, title);
3336 * Explode the views in the given SplitFrame into separate SplitFrame windows.
3337 * This respects (remembers) any previous 'exploded geometry' i.e. the size and
3338 * location last time the view was expanded (if any). However it does not
3339 * remember the split pane divider location - this is set to match the
3340 * 'exploding' frame.
3344 public void explodeViews(SplitFrame sf)
3346 AlignFrame oldTopFrame = (AlignFrame) sf.getTopFrame();
3347 AlignFrame oldBottomFrame = (AlignFrame) sf.getBottomFrame();
3348 List<? extends AlignmentViewPanel> topPanels = oldTopFrame
3350 List<? extends AlignmentViewPanel> bottomPanels = oldBottomFrame
3352 int viewCount = topPanels.size();
3359 * Processing in reverse order works, forwards order leaves the first panels
3360 * not visible. I don't know why!
3362 for (int i = viewCount - 1; i >= 0; i--)
3365 * Make new top and bottom frames. These take over the respective
3366 * AlignmentPanel objects, including their AlignmentViewports, so the
3367 * cdna/protein relationships between the viewports is carried over to the
3370 * explodedGeometry holds the (x, y) position of the previously exploded
3371 * SplitFrame, and the (width, height) of the AlignFrame component
3373 AlignmentPanel topPanel = (AlignmentPanel) topPanels.get(i);
3374 AlignFrame newTopFrame = new AlignFrame(topPanel);
3375 newTopFrame.setSize(oldTopFrame.getSize());
3376 newTopFrame.setVisible(true);
3377 Rectangle geometry = ((AlignViewport) topPanel.getAlignViewport())
3378 .getExplodedGeometry();
3379 if (geometry != null)
3381 newTopFrame.setSize(geometry.getSize());
3384 AlignmentPanel bottomPanel = (AlignmentPanel) bottomPanels.get(i);
3385 AlignFrame newBottomFrame = new AlignFrame(bottomPanel);
3386 newBottomFrame.setSize(oldBottomFrame.getSize());
3387 newBottomFrame.setVisible(true);
3388 geometry = ((AlignViewport) bottomPanel.getAlignViewport())
3389 .getExplodedGeometry();
3390 if (geometry != null)
3392 newBottomFrame.setSize(geometry.getSize());
3395 topPanel.av.setGatherViewsHere(false);
3396 bottomPanel.av.setGatherViewsHere(false);
3397 JInternalFrame splitFrame = new SplitFrame(newTopFrame,
3399 if (geometry != null)
3401 splitFrame.setLocation(geometry.getLocation());
3403 Desktop.addInternalFrame(splitFrame, sf.getTitle(), -1, -1);
3407 * Clear references to the panels (now relocated in the new SplitFrames)
3408 * before closing the old SplitFrame.
3411 bottomPanels.clear();
3416 * Gather expanded split frames, sharing the same pairs of sequence set ids,
3417 * back into the given SplitFrame as additional views. Note that the gathered
3418 * frames may themselves have multiple views.
3422 public void gatherViews(GSplitFrame source)
3425 * special handling of explodedGeometry for a view within a SplitFrame: - it
3426 * holds the (x, y) position of the enclosing SplitFrame, and the (width,
3427 * height) of the AlignFrame component
3429 AlignFrame myTopFrame = (AlignFrame) source.getTopFrame();
3430 AlignFrame myBottomFrame = (AlignFrame) source.getBottomFrame();
3431 myTopFrame.viewport.setExplodedGeometry(new Rectangle(source.getX(),
3432 source.getY(), myTopFrame.getWidth(), myTopFrame.getHeight()));
3433 myBottomFrame.viewport
3434 .setExplodedGeometry(new Rectangle(source.getX(), source.getY(),
3435 myBottomFrame.getWidth(), myBottomFrame.getHeight()));
3436 myTopFrame.viewport.setGatherViewsHere(true);
3437 myBottomFrame.viewport.setGatherViewsHere(true);
3438 String topViewId = myTopFrame.viewport.getSequenceSetId();
3439 String bottomViewId = myBottomFrame.viewport.getSequenceSetId();
3441 JInternalFrame[] frames = desktop.getAllFrames();
3442 for (JInternalFrame frame : frames)
3444 if (frame instanceof SplitFrame && frame != source)
3446 SplitFrame sf = (SplitFrame) frame;
3447 AlignFrame topFrame = (AlignFrame) sf.getTopFrame();
3448 AlignFrame bottomFrame = (AlignFrame) sf.getBottomFrame();
3449 boolean gatherThis = false;
3450 for (int a = 0; a < topFrame.alignPanels.size(); a++)
3452 AlignmentPanel topPanel = topFrame.alignPanels.get(a);
3453 AlignmentPanel bottomPanel = bottomFrame.alignPanels.get(a);
3454 if (topViewId.equals(topPanel.av.getSequenceSetId())
3455 && bottomViewId.equals(bottomPanel.av.getSequenceSetId()))
3458 topPanel.av.setGatherViewsHere(false);
3459 bottomPanel.av.setGatherViewsHere(false);
3460 topPanel.av.setExplodedGeometry(
3461 new Rectangle(sf.getLocation(), topFrame.getSize()));
3462 bottomPanel.av.setExplodedGeometry(
3463 new Rectangle(sf.getLocation(), bottomFrame.getSize()));
3464 myTopFrame.addAlignmentPanel(topPanel, false);
3465 myBottomFrame.addAlignmentPanel(bottomPanel, false);
3471 topFrame.getAlignPanels().clear();
3472 bottomFrame.getAlignPanels().clear();
3479 * The dust settles...give focus to the tab we did this from.
3481 myTopFrame.setDisplayedView(myTopFrame.alignPanel);
3484 public static groovy.ui.Console getGroovyConsole()
3486 return groovyConsole;
3490 * handles the payload of a drag and drop event.
3492 * TODO refactor to desktop utilities class
3495 * - Data source strings extracted from the drop event
3497 * - protocol for each data source extracted from the drop event
3501 * - the payload from the drop event
3504 public static void transferFromDropTarget(List<Object> files,
3505 List<DataSourceType> protocols, DropTargetDropEvent evt,
3506 Transferable t) throws Exception
3509 // BH 2018 changed List<String> to List<Object> to allow for File from SwingJS
3511 // DataFlavor[] flavors = t.getTransferDataFlavors();
3512 // for (int i = 0; i < flavors.length; i++) {
3513 // if (flavors[i].isFlavorJavaFileListType()) {
3514 // evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
3515 // List<File> list = (List<File>) t.getTransferData(flavors[i]);
3516 // for (int j = 0; j < list.size(); j++) {
3517 // File file = (File) list.get(j);
3518 // byte[] data = getDroppedFileBytes(file);
3519 // fileName.setText(file.getName() + " - " + data.length + " " +
3520 // evt.getLocation());
3521 // JTextArea target = (JTextArea) ((DropTarget) evt.getSource()).getComponent();
3522 // target.setText(new String(data));
3524 // dtde.dropComplete(true);
3529 DataFlavor uriListFlavor = new DataFlavor(
3530 "text/uri-list;class=java.lang.String"), urlFlavour = null;
3533 urlFlavour = new DataFlavor(
3534 "application/x-java-url; class=java.net.URL");
3535 } catch (ClassNotFoundException cfe)
3537 Cache.log.debug("Couldn't instantiate the URL dataflavor.", cfe);
3540 if (urlFlavour != null && t.isDataFlavorSupported(urlFlavour))
3545 java.net.URL url = (URL) t.getTransferData(urlFlavour);
3546 // nb: java 8 osx bug https://bugs.openjdk.java.net/browse/JDK-8156099
3547 // means url may be null.
3550 protocols.add(DataSourceType.URL);
3551 files.add(url.toString());
3552 Cache.log.debug("Drop handled as URL dataflavor "
3553 + files.get(files.size() - 1));
3558 if (Platform.isAMac())
3561 "Please ignore plist error - occurs due to problem with java 8 on OSX");
3565 } catch (Throwable ex)
3567 Cache.log.debug("URL drop handler failed.", ex);
3570 if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
3572 // Works on Windows and MacOSX
3573 Cache.log.debug("Drop handled as javaFileListFlavor");
3574 for (Object file : (List) t
3575 .getTransferData(DataFlavor.javaFileListFlavor))
3578 protocols.add(DataSourceType.FILE);
3583 // Unix like behaviour
3584 boolean added = false;
3586 if (t.isDataFlavorSupported(uriListFlavor))
3588 Cache.log.debug("Drop handled as uriListFlavor");
3589 // This is used by Unix drag system
3590 data = (String) t.getTransferData(uriListFlavor);
3594 // fallback to text: workaround - on OSX where there's a JVM bug
3595 Cache.log.debug("standard URIListFlavor failed. Trying text");
3596 // try text fallback
3597 DataFlavor textDf = new DataFlavor(
3598 "text/plain;class=java.lang.String");
3599 if (t.isDataFlavorSupported(textDf))
3601 data = (String) t.getTransferData(textDf);
3604 Cache.log.debug("Plain text drop content returned "
3605 + (data == null ? "Null - failed" : data));
3610 while (protocols.size() < files.size())
3612 Cache.log.debug("Adding missing FILE protocol for "
3613 + files.get(protocols.size()));
3614 protocols.add(DataSourceType.FILE);
3616 for (java.util.StringTokenizer st = new java.util.StringTokenizer(
3617 data, "\r\n"); st.hasMoreTokens();)
3620 String s = st.nextToken();
3621 if (s.startsWith("#"))
3623 // the line is a comment (as per the RFC 2483)
3626 java.net.URI uri = new java.net.URI(s);
3627 if (uri.getScheme().toLowerCase().startsWith("http"))
3629 protocols.add(DataSourceType.URL);
3630 files.add(uri.toString());
3634 // otherwise preserve old behaviour: catch all for file objects
3635 java.io.File file = new java.io.File(uri);
3636 protocols.add(DataSourceType.FILE);
3637 files.add(file.toString());
3642 if (Cache.log.isDebugEnabled())
3644 if (data == null || !added)
3647 if (t.getTransferDataFlavors() != null
3648 && t.getTransferDataFlavors().length > 0)
3651 "Couldn't resolve drop data. Here are the supported flavors:");
3652 for (DataFlavor fl : t.getTransferDataFlavors())
3655 "Supported transfer dataflavor: " + fl.toString());
3656 Object df = t.getTransferData(fl);
3659 Cache.log.debug("Retrieves: " + df);
3663 Cache.log.debug("Retrieved nothing");
3669 Cache.log.debug("Couldn't resolve dataflavor for drop: "
3675 if (Platform.isWindows())
3678 Cache.log.debug("Scanning dropped content for Windows Link Files");
3680 // resolve any .lnk files in the file drop
3681 for (int f = 0; f < files.size(); f++)
3683 String source = files.get(f).toString().toLowerCase();
3684 if (protocols.get(f).equals(DataSourceType.FILE)
3685 && (source.endsWith(".lnk") || source.endsWith(".url")
3686 || source.endsWith(".site")))
3690 Object obj = files.get(f);
3691 File lf = (obj instanceof File ? (File) obj
3692 : new File((String) obj));
3693 // process link file to get a URL
3694 Cache.log.debug("Found potential link file: " + lf);
3695 WindowsShortcut wscfile = new WindowsShortcut(lf);
3696 String fullname = wscfile.getRealFilename();
3697 protocols.set(f, FormatAdapter.checkProtocol(fullname));
3698 files.set(f, fullname);
3699 Cache.log.debug("Parsed real filename " + fullname
3700 + " to extract protocol: " + protocols.get(f));
3701 } catch (Exception ex)
3704 "Couldn't parse " + files.get(f) + " as a link file.",
3713 * Sets the Preferences property for experimental features to True or False
3714 * depending on the state of the controlling menu item
3717 protected void showExperimental_actionPerformed(boolean selected)
3719 Cache.setProperty(EXPERIMENTAL_FEATURES, Boolean.toString(selected));
3723 * Answers a (possibly empty) list of any structure viewer frames (currently for
3724 * either Jmol or Chimera) which are currently open. This may optionally be
3725 * restricted to viewers of a specified class, or viewers linked to a specified
3729 * if not null, only return viewers linked to this panel
3730 * @param structureViewerClass
3731 * if not null, only return viewers of this class
3734 public List<StructureViewerBase> getStructureViewers(
3735 AlignmentPanel apanel,
3736 Class<? extends StructureViewerBase> structureViewerClass)
3738 List<StructureViewerBase> result = new ArrayList<>();
3739 JInternalFrame[] frames = Desktop.instance.getAllFrames();
3741 for (JInternalFrame frame : frames)
3743 if (frame instanceof StructureViewerBase)
3745 if (structureViewerClass == null
3746 || structureViewerClass.isInstance(frame))
3749 || ((StructureViewerBase) frame).isLinkedWith(apanel))
3751 result.add((StructureViewerBase) frame);