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.IOException;
87 import java.util.ArrayList;
88 import java.util.Hashtable;
89 import java.util.List;
90 import java.util.ListIterator;
91 import java.util.Vector;
92 import java.util.concurrent.ExecutorService;
93 import java.util.concurrent.Executors;
94 import java.util.concurrent.Semaphore;
96 import javax.swing.AbstractAction;
97 import javax.swing.Action;
98 import javax.swing.ActionMap;
99 import javax.swing.Box;
100 import javax.swing.BoxLayout;
101 import javax.swing.DefaultDesktopManager;
102 import javax.swing.DesktopManager;
103 import javax.swing.InputMap;
104 import javax.swing.JButton;
105 import javax.swing.JCheckBox;
106 import javax.swing.JComboBox;
107 import javax.swing.JComponent;
108 import javax.swing.JDesktopPane;
109 import javax.swing.JFrame;
110 import javax.swing.JInternalFrame;
111 import javax.swing.JLabel;
112 import javax.swing.JMenuItem;
113 import javax.swing.JPanel;
114 import javax.swing.JPopupMenu;
115 import javax.swing.JProgressBar;
116 import javax.swing.JTextField;
117 import javax.swing.KeyStroke;
118 import javax.swing.SwingUtilities;
119 import javax.swing.event.HyperlinkEvent;
120 import javax.swing.event.HyperlinkEvent.EventType;
121 import javax.swing.event.InternalFrameAdapter;
122 import javax.swing.event.InternalFrameEvent;
123 import javax.swing.event.MenuEvent;
124 import javax.swing.event.MenuListener;
126 import org.stackoverflowusers.file.WindowsShortcut;
133 * @version $Revision: 1.155 $
135 @SuppressWarnings("serial")
136 public class Desktop extends jalview.jbgui.GDesktop
137 implements DropTargetListener, ClipboardOwner, IProgressIndicator,
138 jalview.api.StructureSelectionManagerProvider
140 private final static int DEFAULT_MIN_WIDTH = 300;
142 private final static int DEFAULT_MIN_HEIGHT = 250;
144 private final static int ALIGN_FRAME_DEFAULT_MIN_WIDTH = 600;
146 private final static int ALIGN_FRAME_DEFAULT_MIN_HEIGHT = 70;
148 private final static String EXPERIMENTAL_FEATURES = "EXPERIMENTAL_FEATURES";
150 private JalviewChangeSupport changeSupport = new JalviewChangeSupport();
153 * news reader - null if it was never started.
155 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,
195 * Singleton Desktop instance only in Java;
197 private static Desktop instance;
199 public static Desktop getInstance()
201 if (instance == null)
206 @SuppressWarnings("unused")
207 ThreadGroup g = Thread.currentThread().getThreadGroup();
209 * @j2sNative d = g._jalviewDesktopInstance;
217 private static void setInstance(Desktop d)
219 @SuppressWarnings("unused")
220 ThreadGroup g = Thread.currentThread().getThreadGroup();
222 * @j2sNative g._jalviewDesktopInstance = d;
229 private MyDesktopPane desktopPane;
231 public static MyDesktopPane getDesktopPane()
233 return getInstance().desktopPane;
236 private void setDesktopPane(MyDesktopPane pane)
238 getInstance().desktopPane = pane;
241 static int openFrameCount = 0;
243 static final int xOffset = 30;
245 static final int yOffset = 30;
247 public jalview.ws.jws1.Discoverer discoverer;
249 public Object[] jalviewClipboard;
251 public boolean internalCopy = false;
253 private static int fileLoadingCount = 0;
255 public JInternalFrame conservationSlider, PIDSlider;
257 private boolean instanceOnly;
259 class MyDesktopManager implements DesktopManager
262 private DesktopManager delegate;
264 public MyDesktopManager(DesktopManager delegate)
266 this.delegate = delegate;
270 public void activateFrame(JInternalFrame f)
274 delegate.activateFrame(f);
275 } catch (NullPointerException npe)
277 Point p = getMousePosition();
278 getInstance().showPasteMenu(p.x, p.y);
283 public void beginDraggingFrame(JComponent f)
285 delegate.beginDraggingFrame(f);
289 public void beginResizingFrame(JComponent f, int direction)
291 delegate.beginResizingFrame(f, direction);
295 public void closeFrame(JInternalFrame f)
297 delegate.closeFrame(f);
301 public void deactivateFrame(JInternalFrame f)
303 delegate.deactivateFrame(f);
307 public void deiconifyFrame(JInternalFrame f)
309 delegate.deiconifyFrame(f);
313 public void dragFrame(JComponent f, int newX, int newY)
319 delegate.dragFrame(f, newX, newY);
323 public void endDraggingFrame(JComponent f)
325 delegate.endDraggingFrame(f);
326 getDesktopPane().repaint();
330 public void endResizingFrame(JComponent f)
332 delegate.endResizingFrame(f);
333 getDesktopPane().repaint();
337 public void iconifyFrame(JInternalFrame f)
339 delegate.iconifyFrame(f);
343 public void maximizeFrame(JInternalFrame f)
345 delegate.maximizeFrame(f);
349 public void minimizeFrame(JInternalFrame f)
351 delegate.minimizeFrame(f);
355 public void openFrame(JInternalFrame f)
357 delegate.openFrame(f);
361 public void resizeFrame(JComponent f, int newX, int newY, int newWidth,
368 delegate.resizeFrame(f, newX, newY, newWidth, newHeight);
372 public void setBoundsForFrame(JComponent f, int newX, int newY,
373 int newWidth, int newHeight)
375 delegate.setBoundsForFrame(f, newX, newY, newWidth, newHeight);
378 // All other methods, simply delegate
382 public Desktop(boolean forInstance)
388 * Creates a new Desktop object.
393 * A note to implementors. It is ESSENTIAL that any activities that might
394 * block are spawned off as threads rather than waited for during this
398 if (!Platform.isJS())
400 doVamsasClientCheck();
403 doConfigureStructurePrefs();
404 setTitle("Jalview " + jalview.bin.Cache.getProperty("VERSION"));
405 setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
406 boolean selmemusage = jalview.bin.Cache.getDefault("SHOW_MEMUSAGE",
408 boolean showjconsole = jalview.bin.Cache.getDefault("SHOW_JAVA_CONSOLE",
410 setDesktopPane(new MyDesktopPane(selmemusage));
412 showMemusage.setSelected(selmemusage);
413 getDesktopPane().setBackground(Color.white);
414 getContentPane().setLayout(new BorderLayout());
415 // alternate config - have scrollbars - see notes in JAL-153
416 // JScrollPane sp = new JScrollPane();
417 // sp.getViewport().setView(desktop);
418 // getContentPane().add(sp, BorderLayout.CENTER);
420 // BH 2018 - just an experiment to try unclipped JInternalFrames.
423 getRootPane().putClientProperty("swingjs.overflow.hidden", "false");
426 getContentPane().add(getDesktopPane(), BorderLayout.CENTER);
427 getDesktopPane().setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
429 // This line prevents Windows Look&Feel resizing all new windows to maximum
430 // if previous window was maximised
431 getDesktopPane().setDesktopManager(new MyDesktopManager(
432 (Platform.isWindowsAndNotJS() ? new DefaultDesktopManager()
433 : Platform.isAMacAndNotJS()
434 ? new AquaInternalFrameManager(
435 getDesktopPane().getDesktopManager())
436 : getDesktopPane().getDesktopManager())));
438 Rectangle dims = getLastKnownDimensions("");
445 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
446 int xPos = Math.max(5, (screenSize.width - 900) / 2);
447 int yPos = Math.max(5, (screenSize.height - 650) / 2);
448 setBounds(xPos, yPos, 900, 650);
451 if (!Platform.isJS())
459 jconsole = new Console(this, showjconsole);
460 // add essential build information
461 jconsole.setHeader("Jalview Version: "
462 + jalview.bin.Cache.getProperty("VERSION") + "\n"
463 + "Jalview Installation: "
464 + jalview.bin.Cache.getDefault("INSTALLATION", "unknown")
465 + "\n" + "Build Date: "
466 + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown") + "\n"
467 + "Java version: " + System.getProperty("java.version") + "\n"
468 + System.getProperty("os.arch") + " "
469 + System.getProperty("os.name") + " "
470 + System.getProperty("os.version"));
472 showConsole(showjconsole);
474 showNews.setVisible(false);
476 experimentalFeatures.setSelected(showExperimental());
478 getIdentifiersOrgData();
482 // Spawn a thread that shows the splashscreen
484 SwingUtilities.invokeLater(new Runnable()
493 // Thread off a new instance of the file chooser - this reduces the time
495 // takes to open it later on.
496 new Thread(new Runnable()
501 Cache.log.debug("Filechooser init thread started.");
502 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
503 JalviewFileChooser.forRead(Cache.getProperty("LAST_DIRECTORY"),
505 Cache.log.debug("Filechooser init thread finished.");
508 // Add the service change listener
509 changeSupport.addJalviewPropertyChangeListener("services",
510 new PropertyChangeListener()
514 public void propertyChange(PropertyChangeEvent evt)
516 Cache.log.debug("Firing service changed event for "
517 + evt.getNewValue());
518 JalviewServicesChanged(evt);
525 this.setDropTarget(new java.awt.dnd.DropTarget(getDesktopPane(), this));
527 this.addWindowListener(new WindowAdapter()
530 public void windowClosing(WindowEvent evt)
537 this.addMouseListener(ma = new MouseAdapter()
540 public void mousePressed(MouseEvent evt)
542 if (evt.isPopupTrigger()) // Mac
544 showPasteMenu(evt.getX(), evt.getY());
549 public void mouseReleased(MouseEvent evt)
551 if (evt.isPopupTrigger()) // Windows
553 showPasteMenu(evt.getX(), evt.getY());
557 getDesktopPane().addMouseListener(ma);
562 * Answers true if user preferences to enable experimental features is True
567 public boolean showExperimental()
569 String experimental = Cache.getDefault(EXPERIMENTAL_FEATURES,
570 Boolean.FALSE.toString());
571 return Boolean.valueOf(experimental).booleanValue();
574 public void doConfigureStructurePrefs()
576 // configure services
577 StructureSelectionManager ssm = StructureSelectionManager
578 .getStructureSelectionManager(this);
579 if (jalview.bin.Cache.getDefault(Preferences.ADD_SS_ANN, true))
581 ssm.setAddTempFacAnnot(jalview.bin.Cache
582 .getDefault(Preferences.ADD_TEMPFACT_ANN, true));
583 ssm.setProcessSecondaryStructure(jalview.bin.Cache
584 .getDefault(Preferences.STRUCT_FROM_PDB, true));
585 ssm.setSecStructServices(
586 jalview.bin.Cache.getDefault(Preferences.USE_RNAVIEW, true));
590 ssm.setAddTempFacAnnot(false);
591 ssm.setProcessSecondaryStructure(false);
592 ssm.setSecStructServices(false);
596 public void checkForNews()
598 final Desktop me = this;
599 // Thread off the news reader, in case there are connection problems.
600 new Thread(new Runnable()
605 Cache.log.debug("Starting news thread.");
606 jvnews = new BlogReader(me);
607 showNews.setVisible(true);
608 Cache.log.debug("Completed news thread.");
613 public void getIdentifiersOrgData()
615 // Thread off the identifiers fetcher
616 new Thread(new Runnable()
621 Cache.log.debug("Downloading data from identifiers.org");
622 // UrlDownloadClient client = new UrlDownloadClient();
625 UrlDownloadClient.download(IdOrgSettings.getUrl(),
626 IdOrgSettings.getDownloadLocation());
627 } catch (IOException e)
629 Cache.log.debug("Exception downloading identifiers.org data"
638 protected void showNews_actionPerformed(ActionEvent e)
640 showNews(showNews.isSelected());
643 protected void showNews(boolean visible)
645 Cache.log.debug((visible ? "Showing" : "Hiding") + " news.");
646 showNews.setSelected(visible);
647 if (visible && !jvnews.isVisible())
649 new Thread(new Runnable()
654 long now = System.currentTimeMillis();
655 Desktop.getInstance().setProgressBar(
656 MessageManager.getString("status.refreshing_news"), now);
657 jvnews.refreshNews();
658 Desktop.getInstance().setProgressBar(null, now);
666 * recover the last known dimensions for a jalview window
669 * - empty string is desktop, all other windows have unique prefix
670 * @return null or last known dimensions scaled to current geometry (if last
671 * window geom was known)
673 Rectangle getLastKnownDimensions(String windowName)
675 // TODO: lock aspect ratio for scaling desktop Bug #0058199
676 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
677 String x = jalview.bin.Cache.getProperty(windowName + "SCREEN_X");
678 String y = jalview.bin.Cache.getProperty(windowName + "SCREEN_Y");
679 String width = jalview.bin.Cache
680 .getProperty(windowName + "SCREEN_WIDTH");
681 String height = jalview.bin.Cache
682 .getProperty(windowName + "SCREEN_HEIGHT");
683 if ((x != null) && (y != null) && (width != null) && (height != null))
685 int ix = Integer.parseInt(x), iy = Integer.parseInt(y),
686 iw = Integer.parseInt(width), ih = Integer.parseInt(height);
687 if (jalview.bin.Cache.getProperty("SCREENGEOMETRY_WIDTH") != null)
689 // attempt #1 - try to cope with change in screen geometry - this
690 // version doesn't preserve original jv aspect ratio.
691 // take ratio of current screen size vs original screen size.
692 double sw = ((1f * screenSize.width) / (1f * Integer.parseInt(
693 jalview.bin.Cache.getProperty("SCREENGEOMETRY_WIDTH"))));
694 double sh = ((1f * screenSize.height) / (1f * Integer.parseInt(
695 jalview.bin.Cache.getProperty("SCREENGEOMETRY_HEIGHT"))));
696 // rescale the bounds depending upon the current screen geometry.
697 ix = (int) (ix * sw);
698 iw = (int) (iw * sw);
699 iy = (int) (iy * sh);
700 ih = (int) (ih * sh);
701 while (ix >= screenSize.width)
703 jalview.bin.Cache.log.debug(
704 "Window geometry location recall error: shifting horizontal to within screenbounds.");
705 ix -= screenSize.width;
707 while (iy >= screenSize.height)
709 jalview.bin.Cache.log.debug(
710 "Window geometry location recall error: shifting vertical to within screenbounds.");
711 iy -= screenSize.height;
713 jalview.bin.Cache.log.debug(
714 "Got last known dimensions for " + windowName + ": x:" + ix
715 + " y:" + iy + " width:" + iw + " height:" + ih);
717 // return dimensions for new instance
718 return new Rectangle(ix, iy, iw, ih);
723 private void doVamsasClientCheck()
725 if (Cache.vamsasJarsPresent())
727 setupVamsasDisconnectedGui();
728 VamsasMenu.setVisible(true);
729 final Desktop us = this;
730 VamsasMenu.addMenuListener(new MenuListener()
732 // this listener remembers when the menu was first selected, and
733 // doesn't rebuild the session list until it has been cleared and
735 boolean refresh = true;
738 public void menuCanceled(MenuEvent e)
744 public void menuDeselected(MenuEvent e)
750 public void menuSelected(MenuEvent e)
754 us.buildVamsasStMenu();
759 vamsasStart.setVisible(true);
763 protected void showPasteMenu(int x, int y)
765 JPopupMenu popup = new JPopupMenu();
766 JMenuItem item = new JMenuItem(
767 MessageManager.getString("label.paste_new_window"));
768 item.addActionListener(new ActionListener()
771 public void actionPerformed(ActionEvent evt)
778 popup.show(this, x, y);
785 Clipboard c = Toolkit.getDefaultToolkit().getSystemClipboard();
786 Transferable contents = c.getContents(this);
788 if (contents != null)
790 String file = (String) contents
791 .getTransferData(DataFlavor.stringFlavor);
793 FileFormatI format = new IdentifyFile().identify(file,
794 DataSourceType.PASTE);
796 new FileLoader().LoadFile(file, DataSourceType.PASTE, format);
799 } catch (Exception ex)
802 "Unable to paste alignment from system clipboard:\n" + ex);
807 * Adds and opens the given frame to the desktop
818 public static synchronized void addInternalFrame(
819 final JInternalFrame frame, String title, int w, int h)
821 addInternalFrame(frame, title, true, w, h, true, false);
825 * Add an internal frame to the Jalview desktop
832 * When true, display frame immediately, otherwise, caller must call
833 * setVisible themselves.
839 public static synchronized void addInternalFrame(
840 final JInternalFrame frame, String title, boolean makeVisible,
843 addInternalFrame(frame, title, makeVisible, w, h, true, false);
847 * Add an internal frame to the Jalview desktop and make it visible
860 public static synchronized void addInternalFrame(
861 final JInternalFrame frame, String title, int w, int h,
864 addInternalFrame(frame, title, true, w, h, resizable, false);
868 * Add an internal frame to the Jalview desktop
875 * When true, display frame immediately, otherwise, caller must call
876 * setVisible themselves.
883 * @param ignoreMinSize
884 * Do not set the default minimum size for frame
886 public static synchronized void addInternalFrame(
887 final JInternalFrame frame, String title, boolean makeVisible,
888 int w, int h, boolean resizable, boolean ignoreMinSize)
891 // TODO: allow callers to determine X and Y position of frame (eg. via
893 // TODO: consider fixing method to update entries in the window submenu with
894 // the current window title
896 frame.setTitle(title);
897 if (frame.getWidth() < 1 || frame.getHeight() < 1)
901 // THIS IS A PUBLIC STATIC METHOD, SO IT MAY BE CALLED EVEN IN
902 // A HEADLESS STATE WHEN NO DESKTOP EXISTS. MUST RETURN
903 // IF JALVIEW IS RUNNING HEADLESS
904 // ///////////////////////////////////////////////
905 if (getInstance().instanceOnly || Jalview.isHeadlessMode())
914 frame.setMinimumSize(
915 new Dimension(DEFAULT_MIN_WIDTH, DEFAULT_MIN_HEIGHT));
917 // Set default dimension for Alignment Frame window.
918 // The Alignment Frame window could be added from a number of places,
920 // I did this here in order not to miss out on any Alignment frame.
921 if (frame instanceof AlignFrame)
923 frame.setMinimumSize(new Dimension(ALIGN_FRAME_DEFAULT_MIN_WIDTH,
924 ALIGN_FRAME_DEFAULT_MIN_HEIGHT));
928 frame.setVisible(makeVisible);
929 frame.setClosable(true);
930 frame.setResizable(resizable);
931 frame.setMaximizable(resizable);
932 frame.setIconifiable(resizable);
933 frame.setOpaque(Platform.isJS());
935 if (frame.getX() < 1 && frame.getY() < 1)
937 frame.setLocation(xOffset * openFrameCount,
938 yOffset * ((openFrameCount - 1) % 10) + yOffset);
942 * add an entry for the new frame in the Window menu
943 * (and remove it when the frame is closed)
945 JMenuItem menuItem = new JMenuItem(title);
946 frame.addInternalFrameListener(new InternalFrameAdapter()
949 public void internalFrameActivated(InternalFrameEvent evt)
951 JInternalFrame itf = getDesktopPane().getSelectedFrame();
954 if (itf instanceof AlignFrame)
956 Jalview.setCurrentAlignFrame((AlignFrame) itf);
963 public void internalFrameClosed(InternalFrameEvent evt)
965 PaintRefresher.RemoveComponent(frame);
968 * defensive check to prevent frames being
969 * added half off the window
971 if (openFrameCount > 0)
977 * ensure no reference to alignFrame retained by menu item listener
979 if (menuItem.getActionListeners().length > 0)
981 menuItem.removeActionListener(menuItem.getActionListeners()[0]);
983 getInstance().windowMenu.remove(menuItem);
987 menuItem.addActionListener(new ActionListener()
990 public void actionPerformed(ActionEvent e)
994 frame.setSelected(true);
995 frame.setIcon(false);
996 } catch (java.beans.PropertyVetoException ex)
998 // System.err.println(ex.toString());
1003 setKeyBindings(frame);
1005 getDesktopPane().add(frame);
1007 getInstance().windowMenu.add(menuItem);
1012 frame.setSelected(true);
1013 frame.requestFocus();
1014 } catch (java.beans.PropertyVetoException ve)
1016 } catch (java.lang.ClassCastException cex)
1019 "Squashed a possible GUI implementation error. If you can recreate this, please look at http://issues.jalview.org/browse/JAL-869",
1025 * Add key bindings to a JInternalFrame so that Ctrl-W and Cmd-W will close the
1030 private static void setKeyBindings(JInternalFrame frame)
1032 final Action closeAction = new AbstractAction()
1035 public void actionPerformed(ActionEvent e)
1042 * set up key bindings for Ctrl-W and Cmd-W, with the same (Close) action
1044 KeyStroke ctrlWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
1045 InputEvent.CTRL_DOWN_MASK);
1046 KeyStroke cmdWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
1047 Toolkit.getDefaultToolkit().getMenuShortcutKeyMask());
1049 InputMap inputMap = frame
1050 .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
1051 String ctrlW = ctrlWKey.toString();
1052 inputMap.put(ctrlWKey, ctrlW);
1053 inputMap.put(cmdWKey, ctrlW);
1055 ActionMap actionMap = frame.getActionMap();
1056 actionMap.put(ctrlW, closeAction);
1060 public void lostOwnership(Clipboard clipboard, Transferable contents)
1064 Desktop.getInstance().jalviewClipboard = null;
1067 internalCopy = false;
1071 public void dragEnter(DropTargetDragEvent evt)
1076 public void dragExit(DropTargetEvent evt)
1081 public void dragOver(DropTargetDragEvent evt)
1086 public void dropActionChanged(DropTargetDragEvent evt)
1097 public void drop(DropTargetDropEvent evt)
1099 boolean success = true;
1100 // JAL-1552 - acceptDrop required before getTransferable call for
1101 // Java's Transferable for native dnd
1102 evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
1103 Transferable t = evt.getTransferable();
1104 List<Object> files = new ArrayList<>();
1105 List<DataSourceType> protocols = new ArrayList<>();
1109 Desktop.transferFromDropTarget(files, protocols, evt, t);
1110 } catch (Exception e)
1112 e.printStackTrace();
1120 for (int i = 0; i < files.size(); i++)
1122 // BH 2018 File or String
1123 Object file = files.get(i);
1124 String fileName = file.toString();
1125 DataSourceType protocol = (protocols == null)
1126 ? DataSourceType.FILE
1128 FileFormatI format = null;
1130 if (fileName.endsWith(".jar"))
1132 format = FileFormat.Jalview;
1137 format = new IdentifyFile().identify(file, protocol);
1139 if (file instanceof File)
1141 Platform.cacheFileData((File) file);
1143 new FileLoader().LoadFile(null, file, protocol, format);
1146 } catch (Exception ex)
1151 evt.dropComplete(success); // need this to ensure input focus is properly
1152 // transfered to any new windows created
1162 public void inputLocalFileMenuItem_actionPerformed(AlignViewport viewport)
1164 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
1165 JalviewFileChooser chooser = JalviewFileChooser
1166 .forRead(Cache.getProperty("LAST_DIRECTORY"), fileFormat, true);
1168 chooser.setFileView(new JalviewFileView());
1169 chooser.setDialogTitle(
1170 MessageManager.getString("label.open_local_file"));
1171 chooser.setToolTipText(MessageManager.getString("action.open"));
1173 chooser.setResponseHandler(0, new Runnable()
1178 File selectedFile = chooser.getSelectedFile();
1179 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1181 FileFormatI format = chooser.getSelectedFormat();
1184 * Call IdentifyFile to verify the file contains what its extension implies.
1185 * Skip this step for dynamically added file formats, because
1186 * IdentifyFile does not know how to recognise them.
1188 if (FileFormats.getInstance().isIdentifiable(format))
1192 format = new IdentifyFile().identify(selectedFile,
1193 DataSourceType.FILE);
1194 } catch (FileFormatException e)
1196 // format = null; //??
1200 new FileLoader().LoadFile(viewport, selectedFile,
1201 DataSourceType.FILE, format);
1204 chooser.showOpenDialog(this);
1208 * Shows a dialog for input of a URL at which to retrieve alignment data
1213 public void inputURLMenuItem_actionPerformed(AlignViewport viewport)
1215 // This construct allows us to have a wider textfield
1217 JLabel label = new JLabel(
1218 MessageManager.getString("label.input_file_url"));
1220 JPanel panel = new JPanel(new GridLayout(2, 1));
1224 * the URL to fetch is
1225 * Java: an editable combobox with history
1226 * JS: (pending JAL-3038) a plain text field
1229 String urlBase = "http://www.";
1230 if (Platform.isJS())
1232 history = new JTextField(urlBase, 35);
1241 JComboBox<String> asCombo = new JComboBox<>();
1242 asCombo.setPreferredSize(new Dimension(400, 20));
1243 asCombo.setEditable(true);
1244 asCombo.addItem(urlBase);
1245 String historyItems = Cache.getProperty("RECENT_URL");
1246 if (historyItems != null)
1248 for (String token : historyItems.split("\\t"))
1250 asCombo.addItem(token);
1257 Object[] options = new Object[] { MessageManager.getString("action.ok"),
1258 MessageManager.getString("action.cancel") };
1259 Runnable action = new Runnable()
1264 @SuppressWarnings("unchecked")
1265 String url = (history instanceof JTextField
1266 ? ((JTextField) history).getText()
1267 : ((JComboBox<String>) history).getSelectedItem()
1270 if (url.toLowerCase().endsWith(".jar"))
1272 if (viewport != null)
1274 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1275 FileFormat.Jalview);
1279 new FileLoader().LoadFile(url, DataSourceType.URL,
1280 FileFormat.Jalview);
1285 FileFormatI format = null;
1288 format = new IdentifyFile().identify(url, DataSourceType.URL);
1289 } catch (FileFormatException e)
1291 // TODO revise error handling, distinguish between
1292 // URL not found and response not valid
1297 String msg = MessageManager
1298 .formatMessage("label.couldnt_locate", url);
1299 JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
1301 MessageManager.getString("label.url_not_found"),
1302 JvOptionPane.WARNING_MESSAGE);
1307 if (viewport != null)
1309 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1314 new FileLoader().LoadFile(url, DataSourceType.URL, format);
1319 String dialogOption = MessageManager
1320 .getString("label.input_alignment_from_url");
1321 JvOptionPane.newOptionDialog(getDesktopPane())
1322 .setResponseHandler(0, action)
1323 .showInternalDialog(panel, dialogOption,
1324 JvOptionPane.YES_NO_CANCEL_OPTION,
1325 JvOptionPane.PLAIN_MESSAGE, null, options,
1326 MessageManager.getString("action.ok"));
1330 * Opens the CutAndPaste window for the user to paste an alignment in to
1333 * - if not null, the pasted alignment is added to the current
1334 * alignment; if null, to a new alignment window
1337 public void inputTextboxMenuItem_actionPerformed(
1338 AlignmentViewPanel viewPanel)
1340 CutAndPasteTransfer cap = new CutAndPasteTransfer();
1341 cap.setForInput(viewPanel);
1342 Desktop.addInternalFrame(cap,
1343 MessageManager.getString("label.cut_paste_alignmen_file"), true,
1353 Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
1354 jalview.bin.Cache.setProperty("SCREENGEOMETRY_WIDTH",
1356 jalview.bin.Cache.setProperty("SCREENGEOMETRY_HEIGHT",
1357 screen.height + "");
1358 storeLastKnownDimensions("", new Rectangle(getBounds().x, getBounds().y,
1359 getWidth(), getHeight()));
1361 if (jconsole != null)
1363 storeLastKnownDimensions("JAVA_CONSOLE_", jconsole.getBounds());
1364 jconsole.stopConsole();
1368 storeLastKnownDimensions("JALVIEW_RSS_WINDOW_", jvnews.getBounds());
1371 if (dialogExecutor != null)
1373 dialogExecutor.shutdownNow();
1375 closeAll_actionPerformed(null);
1377 if (groovyConsole != null)
1379 // suppress a possible repeat prompt to save script
1380 groovyConsole.setDirty(false);
1381 groovyConsole.exit();
1386 private void storeLastKnownDimensions(String string, Rectangle jc)
1388 jalview.bin.Cache.log.debug("Storing last known dimensions for "
1389 + string + ": x:" + jc.x + " y:" + jc.y + " width:" + jc.width
1390 + " height:" + jc.height);
1392 jalview.bin.Cache.setProperty(string + "SCREEN_X", jc.x + "");
1393 jalview.bin.Cache.setProperty(string + "SCREEN_Y", jc.y + "");
1394 jalview.bin.Cache.setProperty(string + "SCREEN_WIDTH", jc.width + "");
1395 jalview.bin.Cache.setProperty(string + "SCREEN_HEIGHT", jc.height + "");
1405 public void aboutMenuItem_actionPerformed(ActionEvent e)
1407 // StringBuffer message = getAboutMessage(false);
1408 // JvOptionPane.showInternalMessageDialog(Desktop.getDesktop(),
1410 // message.toString(), "About Jalview", JvOptionPane.INFORMATION_MESSAGE);
1411 new Thread(new Runnable()
1416 new SplashScreen(true);
1421 public StringBuffer getAboutMessage(boolean shortv)
1423 StringBuffer message = new StringBuffer();
1424 message.append("<html>");
1427 message.append("<h1><strong>Version: "
1428 + jalview.bin.Cache.getProperty("VERSION")
1429 + "</strong></h1>");
1430 message.append("<strong>Last Updated: <em>"
1431 + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown")
1432 + "</em></strong>");
1438 message.append("<strong>Version "
1439 + jalview.bin.Cache.getProperty("VERSION")
1440 + "; last updated: "
1441 + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown"));
1444 if (jalview.bin.Cache.getDefault("LATEST_VERSION", "Checking")
1445 .equals("Checking"))
1447 message.append("<br>...Checking latest version...</br>");
1449 else if (!jalview.bin.Cache.getDefault("LATEST_VERSION", "Checking")
1450 .equals(jalview.bin.Cache.getProperty("VERSION")))
1452 boolean red = false;
1453 if (jalview.bin.Cache.getProperty("VERSION").toLowerCase()
1454 .indexOf("automated build") == -1)
1457 // Displayed when code version and jnlp version do not match and code
1458 // version is not a development build
1459 message.append("<div style=\"color: #FF0000;font-style: bold;\">");
1462 message.append("<br>!! Version "
1463 + jalview.bin.Cache.getDefault("LATEST_VERSION",
1465 + " is available for download from "
1466 + jalview.bin.Cache.getDefault("www.jalview.org",
1467 "http://www.jalview.org")
1471 message.append("</div>");
1474 message.append("<br>Authors: " + jalview.bin.Cache.getDefault(
1476 "The Jalview Authors (See AUTHORS file for current list)")
1477 + "<br><br>Development managed by The Barton Group, University of Dundee, Scotland, UK.<br>"
1478 + "<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"
1479 + "<br><br>If you use Jalview, please cite:"
1480 + "<br>Waterhouse, A.M., Procter, J.B., Martin, D.M.A, Clamp, M. and Barton, G. J. (2009)"
1481 + "<br>Jalview Version 2 - a multiple sequence alignment editor and analysis workbench"
1482 + "<br>Bioinformatics doi: 10.1093/bioinformatics/btp033"
1488 * Action on requesting Help documentation
1491 public void documentationMenuItem_actionPerformed()
1495 if (Platform.isJS())
1497 BrowserLauncher.openURL("http://www.jalview.org/help.html");
1506 Help.showHelpWindow();
1508 } catch (Exception ex)
1510 System.err.println("Error opening help: " + ex.getMessage());
1515 public void closeAll_actionPerformed(ActionEvent e)
1517 // TODO show a progress bar while closing?
1518 JInternalFrame[] frames = getDesktopPane().getAllFrames();
1519 for (int i = 0; i < frames.length; i++)
1523 frames[i].setClosed(true);
1524 } catch (java.beans.PropertyVetoException ex)
1528 Jalview.setCurrentAlignFrame(null);
1529 System.out.println("ALL CLOSED");
1530 if (v_client != null)
1532 // TODO clear binding to vamsas document objects on close_all
1536 * reset state of singleton objects as appropriate (clear down session state
1537 * when all windows are closed)
1539 StructureSelectionManager ssm = StructureSelectionManager
1540 .getStructureSelectionManager(this);
1548 public void raiseRelated_actionPerformed(ActionEvent e)
1550 reorderAssociatedWindows(false, false);
1554 public void minimizeAssociated_actionPerformed(ActionEvent e)
1556 reorderAssociatedWindows(true, false);
1559 void closeAssociatedWindows()
1561 reorderAssociatedWindows(false, true);
1567 * @seejalview.jbgui.GDesktop#garbageCollect_actionPerformed(java.awt.event.
1571 protected void garbageCollect_actionPerformed(ActionEvent e)
1573 // We simply collect the garbage
1574 jalview.bin.Cache.log.debug("Collecting garbage...");
1576 jalview.bin.Cache.log.debug("Finished garbage collection.");
1583 * jalview.jbgui.GDesktop#showMemusage_actionPerformed(java.awt.event.ActionEvent
1587 protected void showMemusage_actionPerformed(ActionEvent e)
1589 getDesktopPane().showMemoryUsage(showMemusage.isSelected());
1596 * jalview.jbgui.GDesktop#showConsole_actionPerformed(java.awt.event.ActionEvent
1600 protected void showConsole_actionPerformed(ActionEvent e)
1602 showConsole(showConsole.isSelected());
1605 Console jconsole = null;
1608 * control whether the java console is visible or not
1612 void showConsole(boolean selected)
1614 // TODO: decide if we should update properties file
1615 if (jconsole != null) // BH 2018
1617 showConsole.setSelected(selected);
1618 Cache.setProperty("SHOW_JAVA_CONSOLE",
1619 Boolean.valueOf(selected).toString());
1620 jconsole.setVisible(selected);
1624 void reorderAssociatedWindows(boolean minimize, boolean close)
1626 JInternalFrame[] frames = getDesktopPane().getAllFrames();
1627 if (frames == null || frames.length < 1)
1632 AlignmentViewport source = null, target = null;
1633 if (frames[0] instanceof AlignFrame)
1635 source = ((AlignFrame) frames[0]).getCurrentView();
1637 else if (frames[0] instanceof TreePanel)
1639 source = ((TreePanel) frames[0]).getViewPort();
1641 else if (frames[0] instanceof PCAPanel)
1643 source = ((PCAPanel) frames[0]).av;
1645 else if (frames[0].getContentPane() instanceof PairwiseAlignPanel)
1647 source = ((PairwiseAlignPanel) frames[0].getContentPane()).av;
1652 for (int i = 0; i < frames.length; i++)
1655 if (frames[i] == null)
1659 if (frames[i] instanceof AlignFrame)
1661 target = ((AlignFrame) frames[i]).getCurrentView();
1663 else if (frames[i] instanceof TreePanel)
1665 target = ((TreePanel) frames[i]).getViewPort();
1667 else if (frames[i] instanceof PCAPanel)
1669 target = ((PCAPanel) frames[i]).av;
1671 else if (frames[i].getContentPane() instanceof PairwiseAlignPanel)
1673 target = ((PairwiseAlignPanel) frames[i].getContentPane()).av;
1676 if (source == target)
1682 frames[i].setClosed(true);
1686 frames[i].setIcon(minimize);
1689 frames[i].toFront();
1693 } catch (java.beans.PropertyVetoException ex)
1708 protected void preferences_actionPerformed(ActionEvent e)
1714 * Prompts the user to choose a file and then saves the Jalview state as a
1715 * Jalview project file
1718 public void saveState_actionPerformed()
1720 saveState_actionPerformed(false);
1723 public void saveState_actionPerformed(boolean saveAs)
1725 java.io.File projectFile = getProjectFile();
1726 // autoSave indicates we already have a file and don't need to ask
1727 boolean autoSave = projectFile != null && !saveAs
1728 && BackupFiles.getEnabled();
1730 // System.out.println("autoSave="+autoSave+", projectFile='"+projectFile+"',
1731 // saveAs="+saveAs+", Backups
1732 // "+(BackupFiles.getEnabled()?"enabled":"disabled"));
1734 boolean approveSave = false;
1737 JalviewFileChooser chooser = new JalviewFileChooser("jvp",
1740 chooser.setFileView(new JalviewFileView());
1741 chooser.setDialogTitle(MessageManager.getString("label.save_state"));
1743 int value = chooser.showSaveDialog(this);
1745 if (value == JalviewFileChooser.APPROVE_OPTION)
1747 projectFile = chooser.getSelectedFile();
1748 setProjectFile(projectFile);
1753 if (approveSave || autoSave)
1755 final Desktop me = this;
1756 final java.io.File chosenFile = projectFile;
1757 new Thread(new Runnable()
1762 // TODO: refactor to Jalview desktop session controller action.
1763 setProgressBar(MessageManager.formatMessage(
1764 "label.saving_jalview_project", new Object[]
1765 { chosenFile.getName() }), chosenFile.hashCode());
1766 jalview.bin.Cache.setProperty("LAST_DIRECTORY",
1767 chosenFile.getParent());
1768 // TODO catch and handle errors for savestate
1769 // TODO prevent user from messing with the Desktop whilst we're saving
1772 boolean doBackup = BackupFiles.getEnabled();
1773 BackupFiles backupfiles = doBackup ? new BackupFiles(chosenFile) : null;
1775 new Jalview2XML().saveState(doBackup ? backupfiles.getTempFile() : chosenFile);
1779 backupfiles.setWriteSuccess(true);
1780 backupfiles.rollBackupsAndRenameTempFile();
1782 } catch (OutOfMemoryError oom)
1784 new OOMWarning("Whilst saving current state to "
1785 + chosenFile.getName(), oom);
1786 } catch (Exception ex)
1788 Cache.log.error("Problems whilst trying to save to "
1789 + chosenFile.getName(), ex);
1790 JvOptionPane.showMessageDialog(me,
1791 MessageManager.formatMessage(
1792 "label.error_whilst_saving_current_state_to",
1794 { chosenFile.getName() }),
1795 MessageManager.getString("label.couldnt_save_project"),
1796 JvOptionPane.WARNING_MESSAGE);
1798 setProgressBar(null, chosenFile.hashCode());
1805 public void saveAsState_actionPerformed(ActionEvent e)
1807 saveState_actionPerformed(true);
1810 protected void setProjectFile(File choice)
1812 this.projectFile = choice;
1815 public File getProjectFile()
1817 return this.projectFile;
1821 * Shows a file chooser dialog and tries to read in the selected file as a
1825 public void loadState_actionPerformed()
1827 final String[] suffix = new String[] { "jvp", "jar" };
1828 final String[] desc = new String[] { "Jalview Project",
1829 "Jalview Project (old)" };
1830 JalviewFileChooser chooser = new JalviewFileChooser(
1831 Cache.getProperty("LAST_DIRECTORY"), suffix, desc,
1832 "Jalview Project", true, true); // last two booleans: allFiles,
1834 chooser.setFileView(new JalviewFileView());
1835 chooser.setDialogTitle(MessageManager.getString("label.restore_state"));
1836 chooser.setResponseHandler(0, new Runnable()
1841 File selectedFile = chooser.getSelectedFile();
1842 setProjectFile(selectedFile);
1843 String choice = selectedFile.getAbsolutePath();
1844 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1845 new Thread(new Runnable()
1852 new Jalview2XML().loadJalviewAlign(choice);
1853 } catch (OutOfMemoryError oom)
1855 new OOMWarning("Whilst loading project from " + choice, oom);
1856 } catch (Exception ex)
1859 "Problems whilst loading project from " + choice, ex);
1860 JvOptionPane.showMessageDialog(Desktop.getDesktopPane(),
1861 MessageManager.formatMessage(
1862 "label.error_whilst_loading_project_from",
1865 MessageManager.getString("label.couldnt_load_project"),
1866 JvOptionPane.WARNING_MESSAGE);
1873 chooser.showOpenDialog(this);
1877 public void inputSequence_actionPerformed(ActionEvent e)
1879 new SequenceFetcher(this);
1882 JPanel progressPanel;
1884 ArrayList<JPanel> fileLoadingPanels = new ArrayList<>();
1886 public void startLoading(final Object fileName)
1888 if (fileLoadingCount == 0)
1890 fileLoadingPanels.add(addProgressPanel(MessageManager
1891 .formatMessage("label.loading_file", new Object[]
1897 private JPanel addProgressPanel(String string)
1899 if (progressPanel == null)
1901 progressPanel = new JPanel(new GridLayout(1, 1));
1902 totalProgressCount = 0;
1903 getInstance().getContentPane().add(progressPanel, BorderLayout.SOUTH);
1905 JPanel thisprogress = new JPanel(new BorderLayout(10, 5));
1906 JProgressBar progressBar = new JProgressBar();
1907 progressBar.setIndeterminate(true);
1909 thisprogress.add(new JLabel(string), BorderLayout.WEST);
1911 thisprogress.add(progressBar, BorderLayout.CENTER);
1912 progressPanel.add(thisprogress);
1913 ((GridLayout) progressPanel.getLayout()).setRows(
1914 ((GridLayout) progressPanel.getLayout()).getRows() + 1);
1915 ++totalProgressCount;
1916 getInstance().validate();
1917 return thisprogress;
1920 int totalProgressCount = 0;
1922 private void removeProgressPanel(JPanel progbar)
1924 if (progressPanel != null)
1926 synchronized (progressPanel)
1928 progressPanel.remove(progbar);
1929 GridLayout gl = (GridLayout) progressPanel.getLayout();
1930 gl.setRows(gl.getRows() - 1);
1931 if (--totalProgressCount < 1)
1933 this.getContentPane().remove(progressPanel);
1934 progressPanel = null;
1941 public void stopLoading()
1944 if (fileLoadingCount < 1)
1946 while (fileLoadingPanels.size() > 0)
1948 removeProgressPanel(fileLoadingPanels.remove(0));
1950 fileLoadingPanels.clear();
1951 fileLoadingCount = 0;
1956 public static int getViewCount(String alignmentId)
1958 AlignmentViewport[] aps = getViewports(alignmentId);
1959 return (aps == null) ? 0 : aps.length;
1964 * @param alignmentId
1965 * - if null, all sets are returned
1966 * @return all AlignmentPanels concerning the alignmentId sequence set
1968 public static AlignmentPanel[] getAlignmentPanels(String alignmentId)
1970 if (Desktop.getDesktopPane() == null)
1972 // no frames created and in headless mode
1973 // TODO: verify that frames are recoverable when in headless mode
1976 List<AlignmentPanel> aps = new ArrayList<>();
1977 AlignFrame[] frames = getAlignFrames();
1982 for (AlignFrame af : frames)
1984 for (AlignmentPanel ap : af.alignPanels)
1986 if (alignmentId == null
1987 || alignmentId.equals(ap.av.getSequenceSetId()))
1993 if (aps.size() == 0)
1997 AlignmentPanel[] vap = aps.toArray(new AlignmentPanel[aps.size()]);
2002 * get all the viewports on an alignment.
2004 * @param sequenceSetId
2005 * unique alignment id (may be null - all viewports returned in that
2007 * @return all viewports on the alignment bound to sequenceSetId
2009 public static AlignmentViewport[] getViewports(String sequenceSetId)
2011 List<AlignmentViewport> viewp = new ArrayList<>();
2012 if (getDesktopPane() != null)
2014 AlignFrame[] frames = Desktop.getAlignFrames();
2016 for (AlignFrame afr : frames)
2018 if (sequenceSetId == null || afr.getViewport().getSequenceSetId()
2019 .equals(sequenceSetId))
2021 if (afr.alignPanels != null)
2023 for (AlignmentPanel ap : afr.alignPanels)
2025 if (sequenceSetId == null
2026 || sequenceSetId.equals(ap.av.getSequenceSetId()))
2034 viewp.add(afr.getViewport());
2038 if (viewp.size() > 0)
2040 return viewp.toArray(new AlignmentViewport[viewp.size()]);
2047 * Explode the views in the given frame into separate AlignFrame
2051 public static void explodeViews(AlignFrame af)
2053 int size = af.alignPanels.size();
2059 for (int i = 0; i < size; i++)
2061 AlignmentPanel ap = af.alignPanels.get(i);
2062 AlignFrame newaf = new AlignFrame(ap);
2065 * Restore the view's last exploded frame geometry if known. Multiple
2066 * views from one exploded frame share and restore the same (frame)
2067 * position and size.
2069 Rectangle geometry = ap.av.getExplodedGeometry();
2070 if (geometry != null)
2072 newaf.setBounds(geometry);
2075 ap.av.setGatherViewsHere(false);
2077 addInternalFrame(newaf, af.getTitle(), AlignFrame.DEFAULT_WIDTH,
2078 AlignFrame.DEFAULT_HEIGHT);
2081 af.alignPanels.clear();
2082 af.closeMenuItem_actionPerformed(true);
2087 * Gather expanded views (separate AlignFrame's) with the same sequence set
2088 * identifier back in to this frame as additional views, and close the expanded
2089 * views. Note the expanded frames may themselves have multiple views. We take
2094 public void gatherViews(AlignFrame source)
2096 source.viewport.setGatherViewsHere(true);
2097 source.viewport.setExplodedGeometry(source.getBounds());
2098 JInternalFrame[] frames = getDesktopPane().getAllFrames();
2099 String viewId = source.viewport.getSequenceSetId();
2101 for (int t = 0; t < frames.length; t++)
2103 if (frames[t] instanceof AlignFrame && frames[t] != source)
2105 AlignFrame af = (AlignFrame) frames[t];
2106 boolean gatherThis = false;
2107 for (int a = 0; a < af.alignPanels.size(); a++)
2109 AlignmentPanel ap = af.alignPanels.get(a);
2110 if (viewId.equals(ap.av.getSequenceSetId()))
2113 ap.av.setGatherViewsHere(false);
2114 ap.av.setExplodedGeometry(af.getBounds());
2115 source.addAlignmentPanel(ap, false);
2121 af.alignPanels.clear();
2122 af.closeMenuItem_actionPerformed(true);
2129 jalview.gui.VamsasApplication v_client = null;
2132 public void vamsasImport_actionPerformed(ActionEvent e)
2134 // TODO: JAL-3048 not needed for Jalview-JS
2136 if (v_client == null)
2138 // Load and try to start a session.
2139 JalviewFileChooser chooser = new JalviewFileChooser(
2140 jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
2142 chooser.setFileView(new JalviewFileView());
2143 chooser.setDialogTitle(
2144 MessageManager.getString("label.open_saved_vamsas_session"));
2145 chooser.setToolTipText(MessageManager.getString(
2146 "label.select_vamsas_session_opened_as_new_vamsas_session"));
2148 int value = chooser.showOpenDialog(this);
2150 if (value == JalviewFileChooser.APPROVE_OPTION)
2152 String fle = chooser.getSelectedFile().toString();
2153 if (!vamsasImport(chooser.getSelectedFile()))
2155 JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
2156 MessageManager.formatMessage(
2157 "label.couldnt_import_as_vamsas_session",
2161 .getString("label.vamsas_document_import_failed"),
2162 JvOptionPane.ERROR_MESSAGE);
2168 jalview.bin.Cache.log.error(
2169 "Implementation error - load session from a running session is not supported.");
2174 * import file into a new vamsas session (uses jalview.gui.VamsasApplication)
2177 * @return true if import was a success and a session was started.
2179 public boolean vamsasImport(URL url)
2181 // TODO: create progress bar
2182 if (v_client != null)
2185 jalview.bin.Cache.log.error(
2186 "Implementation error - load session from a running session is not supported.");
2192 // copy the URL content to a temporary local file
2193 // TODO: be a bit cleverer here with nio (?!)
2194 File file = File.createTempFile("vdocfromurl", ".vdj");
2195 FileOutputStream fos = new FileOutputStream(file);
2196 BufferedInputStream bis = new BufferedInputStream(url.openStream());
2197 byte[] buffer = new byte[2048];
2199 while ((ln = bis.read(buffer)) > -1)
2201 fos.write(buffer, 0, ln);
2205 v_client = new jalview.gui.VamsasApplication(this, file,
2206 url.toExternalForm());
2207 } catch (Exception ex)
2209 jalview.bin.Cache.log.error(
2210 "Failed to create new vamsas session from contents of URL "
2215 setupVamsasConnectedGui();
2216 v_client.initial_update(); // TODO: thread ?
2217 return v_client.inSession();
2221 * import file into a new vamsas session (uses jalview.gui.VamsasApplication)
2224 * @return true if import was a success and a session was started.
2226 public boolean vamsasImport(File file)
2228 if (v_client != null)
2231 jalview.bin.Cache.log.error(
2232 "Implementation error - load session from a running session is not supported.");
2236 setProgressBar(MessageManager.formatMessage(
2237 "status.importing_vamsas_session_from", new Object[]
2238 { file.getName() }), file.hashCode());
2241 v_client = new jalview.gui.VamsasApplication(this, file, null);
2242 } catch (Exception ex)
2244 setProgressBar(MessageManager.formatMessage(
2245 "status.importing_vamsas_session_from", new Object[]
2246 { file.getName() }), file.hashCode());
2247 jalview.bin.Cache.log.error(
2248 "New vamsas session from existing session file failed:", ex);
2251 setupVamsasConnectedGui();
2252 v_client.initial_update(); // TODO: thread ?
2253 setProgressBar(MessageManager.formatMessage(
2254 "status.importing_vamsas_session_from", new Object[]
2255 { file.getName() }), file.hashCode());
2256 return v_client.inSession();
2259 public boolean joinVamsasSession(String mysesid)
2261 if (v_client != null)
2263 throw new Error(MessageManager
2264 .getString("error.try_join_vamsas_session_another"));
2266 if (mysesid == null)
2269 MessageManager.getString("error.invalid_vamsas_session_id"));
2271 v_client = new VamsasApplication(this, mysesid);
2272 setupVamsasConnectedGui();
2273 v_client.initial_update();
2274 return (v_client.inSession());
2278 public void vamsasStart_actionPerformed(ActionEvent e)
2280 if (v_client == null)
2283 // we just start a default session for moment.
2285 * JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache.
2286 * getProperty("LAST_DIRECTORY"));
2288 * chooser.setFileView(new JalviewFileView());
2289 * chooser.setDialogTitle("Load Vamsas file");
2290 * chooser.setToolTipText("Import");
2292 * int value = chooser.showOpenDialog(this);
2294 * if (value == JalviewFileChooser.APPROVE_OPTION) { v_client = new
2295 * jalview.gui.VamsasApplication(this, chooser.getSelectedFile());
2297 v_client = new VamsasApplication(this);
2298 setupVamsasConnectedGui();
2299 v_client.initial_update(); // TODO: thread ?
2303 // store current data in session.
2304 v_client.push_update(); // TODO: thread
2308 protected void setupVamsasConnectedGui()
2310 vamsasStart.setText(MessageManager.getString("label.session_update"));
2311 vamsasSave.setVisible(true);
2312 vamsasStop.setVisible(true);
2313 vamsasImport.setVisible(false); // Document import to existing session is
2314 // not possible for vamsas-client-1.0.
2317 protected void setupVamsasDisconnectedGui()
2319 vamsasSave.setVisible(false);
2320 vamsasStop.setVisible(false);
2321 vamsasImport.setVisible(true);
2323 .setText(MessageManager.getString("label.new_vamsas_session"));
2327 public void vamsasStop_actionPerformed(ActionEvent e)
2329 if (v_client != null)
2331 v_client.end_session();
2333 setupVamsasDisconnectedGui();
2337 protected void buildVamsasStMenu()
2339 if (v_client == null)
2341 String[] sess = null;
2344 sess = VamsasApplication.getSessionList();
2345 } catch (Exception e)
2347 jalview.bin.Cache.log.warn("Problem getting current sessions list.",
2353 jalview.bin.Cache.log.debug(
2354 "Got current sessions list: " + sess.length + " entries.");
2355 VamsasStMenu.removeAll();
2356 for (int i = 0; i < sess.length; i++)
2358 JMenuItem sessit = new JMenuItem();
2359 sessit.setText(sess[i]);
2360 sessit.setToolTipText(MessageManager
2361 .formatMessage("label.connect_to_session", new Object[]
2363 final Desktop dsktp = this;
2364 final String mysesid = sess[i];
2365 sessit.addActionListener(new ActionListener()
2369 public void actionPerformed(ActionEvent e)
2371 if (dsktp.v_client == null)
2373 Thread rthr = new Thread(new Runnable()
2379 dsktp.v_client = new VamsasApplication(dsktp, mysesid);
2380 dsktp.setupVamsasConnectedGui();
2381 dsktp.v_client.initial_update();
2389 VamsasStMenu.add(sessit);
2391 // don't show an empty menu.
2392 VamsasStMenu.setVisible(sess.length > 0);
2397 jalview.bin.Cache.log.debug("No current vamsas sessions.");
2398 VamsasStMenu.removeAll();
2399 VamsasStMenu.setVisible(false);
2404 // Not interested in the content. Just hide ourselves.
2405 VamsasStMenu.setVisible(false);
2410 public void vamsasSave_actionPerformed(ActionEvent e)
2412 // TODO: JAL-3048 not needed for Jalview-JS
2414 if (v_client != null)
2416 // TODO: VAMSAS DOCUMENT EXTENSION is VDJ
2417 JalviewFileChooser chooser = new JalviewFileChooser("vdj",
2420 chooser.setFileView(new JalviewFileView());
2421 chooser.setDialogTitle(MessageManager
2422 .getString("label.save_vamsas_document_archive"));
2424 int value = chooser.showSaveDialog(this);
2426 if (value == JalviewFileChooser.APPROVE_OPTION)
2428 java.io.File choice = chooser.getSelectedFile();
2429 JPanel progpanel = addProgressPanel(MessageManager
2430 .formatMessage("label.saving_vamsas_doc", new Object[]
2431 { choice.getName() }));
2432 Cache.setProperty("LAST_DIRECTORY", choice.getParent());
2433 String warnmsg = null;
2434 String warnttl = null;
2437 v_client.vclient.storeDocument(choice);
2440 warnttl = "Serious Problem saving Vamsas Document";
2441 warnmsg = ex.toString();
2442 jalview.bin.Cache.log
2443 .error("Error Whilst saving document to " + choice, ex);
2445 } catch (Exception ex)
2447 warnttl = "Problem saving Vamsas Document.";
2448 warnmsg = ex.toString();
2449 jalview.bin.Cache.log.warn(
2450 "Exception Whilst saving document to " + choice, ex);
2453 removeProgressPanel(progpanel);
2454 if (warnmsg != null)
2456 JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
2458 warnmsg, warnttl, JvOptionPane.ERROR_MESSAGE);
2464 JPanel vamUpdate = null;
2467 * hide vamsas user gui bits when a vamsas document event is being handled.
2470 * true to hide gui, false to reveal gui
2472 public void setVamsasUpdate(boolean b)
2474 Cache.log.debug("Setting gui for Vamsas update "
2475 + (b ? "in progress" : "finished"));
2477 if (vamUpdate != null)
2479 this.removeProgressPanel(vamUpdate);
2483 vamUpdate = this.addProgressPanel(
2484 MessageManager.getString("label.updating_vamsas_session"));
2486 vamsasStart.setVisible(!b);
2487 vamsasStop.setVisible(!b);
2488 vamsasSave.setVisible(!b);
2491 public JInternalFrame[] getAllFrames()
2493 return getDesktopPane().getAllFrames();
2497 * Checks the given url to see if it gives a response indicating that the user
2498 * should be informed of a new questionnaire.
2502 public void checkForQuestionnaire(String url)
2504 UserQuestionnaireCheck jvq = new UserQuestionnaireCheck(url);
2505 // javax.swing.SwingUtilities.invokeLater(jvq);
2506 new Thread(jvq).start();
2509 public void checkURLLinks()
2511 // Thread off the URL link checker
2512 addDialogThread(new Runnable()
2517 if (Cache.getDefault("CHECKURLLINKS", true))
2519 // check what the actual links are - if it's just the default don't
2520 // bother with the warning
2521 List<String> links = Preferences.sequenceUrlLinks
2524 // only need to check links if there is one with a
2525 // SEQUENCE_ID which is not the default EMBL_EBI link
2526 ListIterator<String> li = links.listIterator();
2527 boolean check = false;
2528 List<JLabel> urls = new ArrayList<>();
2529 while (li.hasNext())
2531 String link = li.next();
2532 if (link.contains(UrlConstants.SEQUENCE_ID)
2533 && !UrlConstants.isDefaultString(link))
2536 int barPos = link.indexOf("|");
2537 String urlMsg = barPos == -1 ? link
2538 : link.substring(0, barPos) + ": "
2539 + link.substring(barPos + 1);
2540 urls.add(new JLabel(urlMsg));
2548 // ask user to check in case URL links use old style tokens
2549 // ($SEQUENCE_ID$ for sequence id _or_ accession id)
2550 JPanel msgPanel = new JPanel();
2551 msgPanel.setLayout(new BoxLayout(msgPanel, BoxLayout.PAGE_AXIS));
2552 msgPanel.add(Box.createVerticalGlue());
2553 JLabel msg = new JLabel(MessageManager
2554 .getString("label.SEQUENCE_ID_for_DB_ACCESSION1"));
2555 JLabel msg2 = new JLabel(MessageManager
2556 .getString("label.SEQUENCE_ID_for_DB_ACCESSION2"));
2558 for (JLabel url : urls)
2564 final JCheckBox jcb = new JCheckBox(
2565 MessageManager.getString("label.do_not_display_again"));
2566 jcb.addActionListener(new ActionListener()
2569 public void actionPerformed(ActionEvent e)
2571 // update Cache settings for "don't show this again"
2572 boolean showWarningAgain = !jcb.isSelected();
2573 Cache.setProperty("CHECKURLLINKS",
2574 Boolean.valueOf(showWarningAgain).toString());
2579 JvOptionPane.showMessageDialog(Desktop.getDesktopPane(), msgPanel,
2581 .getString("label.SEQUENCE_ID_no_longer_used"),
2582 JvOptionPane.WARNING_MESSAGE);
2589 * Proxy class for JDesktopPane which optionally displays the current memory
2590 * usage and highlights the desktop area with a red bar if free memory runs low.
2594 public class MyDesktopPane extends JDesktopPane
2597 private static final float ONE_MB = 1048576f;
2599 boolean showMemoryUsage = false;
2603 java.text.NumberFormat df;
2605 float maxMemory, allocatedMemory, freeMemory, totalFreeMemory,
2608 public MyDesktopPane(boolean showMemoryUsage)
2610 showMemoryUsage(showMemoryUsage);
2613 public void showMemoryUsage(boolean showMemory)
2615 this.showMemoryUsage = showMemory;
2618 Thread worker = new Thread(this);
2624 public boolean isShowMemoryUsage()
2626 return showMemoryUsage;
2632 df = java.text.NumberFormat.getNumberInstance();
2633 df.setMaximumFractionDigits(2);
2634 runtime = Runtime.getRuntime();
2636 while (showMemoryUsage)
2640 maxMemory = runtime.maxMemory() / ONE_MB;
2641 allocatedMemory = runtime.totalMemory() / ONE_MB;
2642 freeMemory = runtime.freeMemory() / ONE_MB;
2643 totalFreeMemory = freeMemory + (maxMemory - allocatedMemory);
2645 percentUsage = (totalFreeMemory / maxMemory) * 100;
2647 // if (percentUsage < 20)
2649 // border1 = BorderFactory.createMatteBorder(12, 12, 12, 12,
2651 // instance.set.setBorder(border1);
2654 // sleep after showing usage
2656 } catch (Exception ex)
2658 ex.printStackTrace();
2664 public void paintComponent(Graphics g)
2666 if (showMemoryUsage && g != null && df != null)
2668 if (percentUsage < 20)
2670 g.setColor(Color.red);
2672 FontMetrics fm = g.getFontMetrics();
2675 g.drawString(MessageManager.formatMessage("label.memory_stats",
2677 { df.format(totalFreeMemory), df.format(maxMemory),
2678 df.format(percentUsage) }),
2679 10, getHeight() - fm.getHeight());
2686 * Accessor method to quickly get all the AlignmentFrames loaded.
2688 * @return an array of AlignFrame, or null if none found
2690 public static AlignFrame[] getAlignFrames()
2692 if (Jalview.isHeadlessMode())
2694 // Desktop.getDesktop() is null in headless mode
2695 return new AlignFrame[] { Jalview.getCurrentAlignFrame() };
2698 JInternalFrame[] frames = Desktop.getDesktopPane().getAllFrames();
2704 List<AlignFrame> avp = new ArrayList<>();
2706 for (int i = frames.length - 1; i > -1; i--)
2708 if (frames[i] instanceof AlignFrame)
2710 avp.add((AlignFrame) frames[i]);
2712 else if (frames[i] instanceof SplitFrame)
2715 * Also check for a split frame containing an AlignFrame
2717 GSplitFrame sf = (GSplitFrame) frames[i];
2718 if (sf.getTopFrame() instanceof AlignFrame)
2720 avp.add((AlignFrame) sf.getTopFrame());
2722 if (sf.getBottomFrame() instanceof AlignFrame)
2724 avp.add((AlignFrame) sf.getBottomFrame());
2728 if (avp.size() == 0)
2732 AlignFrame afs[] = avp.toArray(new AlignFrame[avp.size()]);
2737 * Returns an array of any AppJmol frames in the Desktop (or null if none).
2741 public GStructureViewer[] getJmols()
2743 JInternalFrame[] frames = Desktop.getDesktopPane().getAllFrames();
2749 List<GStructureViewer> avp = new ArrayList<>();
2751 for (int i = frames.length - 1; i > -1; i--)
2753 if (frames[i] instanceof AppJmol)
2755 GStructureViewer af = (GStructureViewer) frames[i];
2759 if (avp.size() == 0)
2763 GStructureViewer afs[] = avp.toArray(new GStructureViewer[avp.size()]);
2768 * Add Groovy Support to Jalview
2771 public void groovyShell_actionPerformed()
2775 openGroovyConsole();
2776 } catch (Exception ex)
2778 jalview.bin.Cache.log.error("Groovy Shell Creation failed.", ex);
2779 JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
2781 MessageManager.getString("label.couldnt_create_groovy_shell"),
2782 MessageManager.getString("label.groovy_support_failed"),
2783 JvOptionPane.ERROR_MESSAGE);
2788 * Open the Groovy console
2790 private void openGroovyConsole()
2792 if (groovyConsole == null)
2794 groovyConsole = new groovy.ui.Console();
2795 groovyConsole.setVariable("Jalview", this);
2796 groovyConsole.run();
2799 * We allow only one console at a time, so that AlignFrame menu option
2800 * 'Calculate | Run Groovy script' is unambiguous.
2801 * Disable 'Groovy Console', and enable 'Run script', when the console is
2802 * opened, and the reverse when it is closed
2804 Window window = (Window) groovyConsole.getFrame();
2805 window.addWindowListener(new WindowAdapter()
2808 public void windowClosed(WindowEvent e)
2811 * rebind CMD-Q from Groovy Console to Jalview Quit
2814 enableExecuteGroovy(false);
2820 * show Groovy console window (after close and reopen)
2822 ((Window) groovyConsole.getFrame()).setVisible(true);
2825 * if we got this far, enable 'Run Groovy' in AlignFrame menus
2826 * and disable opening a second console
2828 enableExecuteGroovy(true);
2832 * Bind Ctrl/Cmd-Q to Quit - for reset as Groovy Console takes over this binding
2835 protected void addQuitHandler()
2837 getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
2838 .put(KeyStroke.getKeyStroke(KeyEvent.VK_Q,
2839 Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()),
2841 getRootPane().getActionMap().put("Quit", new AbstractAction()
2844 public void actionPerformed(ActionEvent e)
2852 * Enable or disable 'Run Groovy script' in AlignFrame calculate menus
2855 * true if Groovy console is open
2857 public void enableExecuteGroovy(boolean enabled)
2860 * disable opening a second Groovy console
2861 * (or re-enable when the console is closed)
2863 groovyShell.setEnabled(!enabled);
2865 AlignFrame[] alignFrames = getAlignFrames();
2866 if (alignFrames != null)
2868 for (AlignFrame af : alignFrames)
2870 af.setGroovyEnabled(enabled);
2876 * Progress bars managed by the IProgressIndicator method.
2878 private Hashtable<Long, JPanel> progressBars;
2880 private Hashtable<Long, IProgressIndicatorHandler> progressBarHandlers;
2885 * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
2888 public void setProgressBar(String message, long id)
2890 Platform.timeCheck("Desktop " + message, Platform.TIME_MARK);
2892 if (progressBars == null)
2894 progressBars = new Hashtable<>();
2895 progressBarHandlers = new Hashtable<>();
2898 if (progressBars.get(new Long(id)) != null)
2900 JPanel panel = progressBars.remove(new Long(id));
2901 if (progressBarHandlers.contains(new Long(id)))
2903 progressBarHandlers.remove(new Long(id));
2905 removeProgressPanel(panel);
2909 progressBars.put(new Long(id), addProgressPanel(message));
2916 * @see jalview.gui.IProgressIndicator#registerHandler(long,
2917 * jalview.gui.IProgressIndicatorHandler)
2920 public void registerHandler(final long id,
2921 final IProgressIndicatorHandler handler)
2923 if (progressBarHandlers == null
2924 || !progressBars.containsKey(new Long(id)))
2926 throw new Error(MessageManager.getString(
2927 "error.call_setprogressbar_before_registering_handler"));
2929 progressBarHandlers.put(new Long(id), handler);
2930 final JPanel progressPanel = progressBars.get(new Long(id));
2931 if (handler.canCancel())
2933 JButton cancel = new JButton(
2934 MessageManager.getString("action.cancel"));
2935 final IProgressIndicator us = this;
2936 cancel.addActionListener(new ActionListener()
2940 public void actionPerformed(ActionEvent e)
2942 handler.cancelActivity(id);
2943 us.setProgressBar(MessageManager
2944 .formatMessage("label.cancelled_params", new Object[]
2945 { ((JLabel) progressPanel.getComponent(0)).getText() }),
2949 progressPanel.add(cancel, BorderLayout.EAST);
2955 * @return true if any progress bars are still active
2958 public boolean operationInProgress()
2960 if (progressBars != null && progressBars.size() > 0)
2968 * This will return the first AlignFrame holding the given viewport instance. It
2969 * will break if there are more than one AlignFrames viewing a particular av.
2972 * @return alignFrame for viewport
2974 public static AlignFrame getAlignFrameFor(AlignViewportI viewport)
2976 if (getDesktopPane() != null)
2978 AlignmentPanel[] aps = getAlignmentPanels(
2979 viewport.getSequenceSetId());
2980 for (int panel = 0; aps != null && panel < aps.length; panel++)
2982 if (aps[panel] != null && aps[panel].av == viewport)
2984 return aps[panel].alignFrame;
2991 public VamsasApplication getVamsasApplication()
2998 * flag set if jalview GUI is being operated programmatically
3000 private boolean inBatchMode = false;
3003 * check if jalview GUI is being operated programmatically
3005 * @return inBatchMode
3007 public boolean isInBatchMode()
3013 * set flag if jalview GUI is being operated programmatically
3015 * @param inBatchMode
3017 public void setInBatchMode(boolean inBatchMode)
3019 this.inBatchMode = inBatchMode;
3022 public void startServiceDiscovery()
3024 startServiceDiscovery(false);
3027 public void startServiceDiscovery(boolean blocking)
3029 boolean alive = true;
3030 Thread t0 = null, t1 = null, t2 = null;
3031 // JAL-940 - JALVIEW 1 services are now being EOLed as of JABA 2.1 release
3034 // todo: changesupport handlers need to be transferred
3035 if (discoverer == null)
3037 discoverer = new jalview.ws.jws1.Discoverer();
3038 // register PCS handler for getDesktop().
3039 discoverer.addPropertyChangeListener(changeSupport);
3041 // JAL-940 - disabled JWS1 service configuration - always start discoverer
3042 // until we phase out completely
3043 (t0 = new Thread(discoverer)).start();
3046 if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
3048 t2 = jalview.ws.jws2.Jws2Discoverer.getDiscoverer()
3049 .startDiscoverer(changeSupport);
3053 // TODO: do rest service discovery
3062 } catch (Exception e)
3065 alive = (t1 != null && t1.isAlive()) || (t2 != null && t2.isAlive())
3066 || (t3 != null && t3.isAlive())
3067 || (t0 != null && t0.isAlive());
3073 * called to check if the service discovery process completed successfully.
3077 protected void JalviewServicesChanged(PropertyChangeEvent evt)
3079 if (evt.getNewValue() == null || evt.getNewValue() instanceof Vector)
3081 final String ermsg = jalview.ws.jws2.Jws2Discoverer.getDiscoverer()
3082 .getErrorMessages();
3085 if (Cache.getDefault("SHOW_WSDISCOVERY_ERRORS", true))
3087 if (serviceChangedDialog == null)
3089 // only run if we aren't already displaying one of these.
3090 addDialogThread(serviceChangedDialog = new Runnable()
3097 * JalviewDialog jd =new JalviewDialog() {
3099 * @Override protected void cancelPressed() { // TODO
3100 * Auto-generated method stub
3102 * }@Override protected void okPressed() { // TODO
3103 * Auto-generated method stub
3105 * }@Override protected void raiseClosed() { // TODO
3106 * Auto-generated method stub
3108 * } }; jd.initDialogFrame(new
3109 * JLabel("<html><table width=\"450\"><tr><td>" + ermsg +
3110 * "<br/>It may be that you have invalid JABA URLs in your web service preferences,"
3111 * + " or mis-configured HTTP proxy settings.<br/>" +
3112 * "Check the <em>Connections</em> and <em>Web services</em> tab of the"
3114 * " Tools->Preferences dialog box to change them.</td></tr></table></html>"
3115 * ), true, true, "Web Service Configuration Problem", 450,
3118 * jd.waitForInput();
3120 JvOptionPane.showConfirmDialog(Desktop.getDesktopPane(),
3121 new JLabel("<html><table width=\"450\"><tr><td>"
3122 + ermsg + "</td></tr></table>"
3123 + "<p>It may be that you have invalid JABA URLs<br/>in your web service preferences,"
3124 + "<br>or as a command-line argument, or mis-configured HTTP proxy settings.</p>"
3125 + "<p>Check the <em>Connections</em> and <em>Web services</em> tab<br/>of the"
3126 + " Tools->Preferences dialog box to change them.</p></html>"),
3127 "Web Service Configuration Problem",
3128 JvOptionPane.DEFAULT_OPTION,
3129 JvOptionPane.ERROR_MESSAGE);
3130 serviceChangedDialog = null;
3139 "Errors reported by JABA discovery service. Check web services preferences.\n"
3146 Runnable serviceChangedDialog = null;
3149 * start a thread to open a URL in the configured browser. Pops up a warning
3150 * dialog to the user if there is an exception when calling out to the browser
3155 public static void showUrl(final String url)
3157 showUrl(url, Desktop.getInstance());
3161 * Like showUrl but allows progress handler to be specified
3165 * (null) or object implementing IProgressIndicator
3167 public static void showUrl(final String url,
3168 final IProgressIndicator progress)
3170 new Thread(new Runnable()
3177 if (progress != null)
3179 progress.setProgressBar(MessageManager
3180 .formatMessage("status.opening_params", new Object[]
3181 { url }), this.hashCode());
3183 BrowserLauncher.openURL(url);
3184 } catch (Exception ex)
3186 JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
3188 .getString("label.web_browser_not_found_unix"),
3189 MessageManager.getString("label.web_browser_not_found"),
3190 JvOptionPane.WARNING_MESSAGE);
3192 ex.printStackTrace();
3194 if (progress != null)
3196 progress.setProgressBar(null, this.hashCode());
3202 private WsParamSetManager wsparamManager = null;
3204 public static ParamManager getUserParameterStore()
3206 Desktop d = getInstance();
3207 if (d.wsparamManager == null)
3209 d.wsparamManager = new WsParamSetManager();
3211 return d.wsparamManager;
3215 * static hyperlink handler proxy method for use by Jalview's internal windows
3219 public static void hyperlinkUpdate(HyperlinkEvent e)
3221 if (e.getEventType() == EventType.ACTIVATED)
3226 url = e.getURL().toString();
3227 Desktop.showUrl(url);
3228 } catch (Exception x)
3232 if (Cache.log != null)
3234 Cache.log.error("Couldn't handle string " + url + " as a URL.");
3239 "Couldn't handle string " + url + " as a URL.");
3242 // ignore any exceptions due to dud links.
3249 * single thread that handles display of dialogs to user.
3251 ExecutorService dialogExecutor = Executors.newSingleThreadExecutor();
3254 * flag indicating if dialogExecutor should try to acquire a permit
3256 volatile boolean dialogPause = true;
3261 java.util.concurrent.Semaphore block = new Semaphore(0);
3263 private groovy.ui.Console groovyConsole;
3265 public StructureViewer lastTargetedView;
3268 * add another dialog thread to the queue
3272 public void addDialogThread(final Runnable prompter)
3274 dialogExecutor.submit(new Runnable()
3284 } catch (InterruptedException x)
3295 SwingUtilities.invokeAndWait(prompter);
3296 } catch (Exception q)
3298 Cache.log.warn("Unexpected Exception in dialog thread.", q);
3304 public void startDialogQueue()
3306 // set the flag so we don't pause waiting for another permit and semaphore
3307 // the current task to begin
3308 dialogPause = false;
3313 * Outputs an image of the desktop to file in EPS format, after prompting the
3314 * user for choice of Text or Lineart character rendering (unless a preference
3315 * has been set). The file name is generated as
3318 * Jalview_snapshot_nnnnn.eps where nnnnn is the current timestamp in milliseconds
3322 protected void snapShotWindow_actionPerformed(ActionEvent e)
3324 // currently the menu option to do this is not shown
3327 int width = getWidth();
3328 int height = getHeight();
3330 "Jalview_snapshot_" + System.currentTimeMillis() + ".eps");
3331 ImageWriterI writer = new ImageWriterI()
3334 public void exportImage(Graphics g) throws Exception
3337 Cache.log.info("Successfully written snapshot to file "
3338 + of.getAbsolutePath());
3341 String title = "View of desktop";
3342 ImageExporter exporter = new ImageExporter(writer, null, TYPE.EPS,
3344 exporter.doExport(of, this, width, height, title);
3348 * Explode the views in the given SplitFrame into separate SplitFrame windows.
3349 * This respects (remembers) any previous 'exploded geometry' i.e. the size and
3350 * location last time the view was expanded (if any). However it does not
3351 * remember the split pane divider location - this is set to match the
3352 * 'exploding' frame.
3356 public void explodeViews(SplitFrame sf)
3358 AlignFrame oldTopFrame = (AlignFrame) sf.getTopFrame();
3359 AlignFrame oldBottomFrame = (AlignFrame) sf.getBottomFrame();
3360 List<? extends AlignmentViewPanel> topPanels = oldTopFrame
3362 List<? extends AlignmentViewPanel> bottomPanels = oldBottomFrame
3364 int viewCount = topPanels.size();
3371 * Processing in reverse order works, forwards order leaves the first panels
3372 * not visible. I don't know why!
3374 for (int i = viewCount - 1; i >= 0; i--)
3377 * Make new top and bottom frames. These take over the respective
3378 * AlignmentPanel objects, including their AlignmentViewports, so the
3379 * cdna/protein relationships between the viewports is carried over to the
3382 * explodedGeometry holds the (x, y) position of the previously exploded
3383 * SplitFrame, and the (width, height) of the AlignFrame component
3385 AlignmentPanel topPanel = (AlignmentPanel) topPanels.get(i);
3386 AlignFrame newTopFrame = new AlignFrame(topPanel);
3387 newTopFrame.setSize(oldTopFrame.getSize());
3388 newTopFrame.setVisible(true);
3389 Rectangle geometry = ((AlignViewport) topPanel.getAlignViewport())
3390 .getExplodedGeometry();
3391 if (geometry != null)
3393 newTopFrame.setSize(geometry.getSize());
3396 AlignmentPanel bottomPanel = (AlignmentPanel) bottomPanels.get(i);
3397 AlignFrame newBottomFrame = new AlignFrame(bottomPanel);
3398 newBottomFrame.setSize(oldBottomFrame.getSize());
3399 newBottomFrame.setVisible(true);
3400 geometry = ((AlignViewport) bottomPanel.getAlignViewport())
3401 .getExplodedGeometry();
3402 if (geometry != null)
3404 newBottomFrame.setSize(geometry.getSize());
3407 topPanel.av.setGatherViewsHere(false);
3408 bottomPanel.av.setGatherViewsHere(false);
3409 JInternalFrame splitFrame = new SplitFrame(newTopFrame,
3411 if (geometry != null)
3413 splitFrame.setLocation(geometry.getLocation());
3415 Desktop.addInternalFrame(splitFrame, sf.getTitle(), -1, -1);
3419 * Clear references to the panels (now relocated in the new SplitFrames)
3420 * before closing the old SplitFrame.
3423 bottomPanels.clear();
3428 * Gather expanded split frames, sharing the same pairs of sequence set ids,
3429 * back into the given SplitFrame as additional views. Note that the gathered
3430 * frames may themselves have multiple views.
3434 public void gatherViews(GSplitFrame source)
3437 * special handling of explodedGeometry for a view within a SplitFrame: - it
3438 * holds the (x, y) position of the enclosing SplitFrame, and the (width,
3439 * height) of the AlignFrame component
3441 AlignFrame myTopFrame = (AlignFrame) source.getTopFrame();
3442 AlignFrame myBottomFrame = (AlignFrame) source.getBottomFrame();
3443 myTopFrame.viewport.setExplodedGeometry(new Rectangle(source.getX(),
3444 source.getY(), myTopFrame.getWidth(), myTopFrame.getHeight()));
3445 myBottomFrame.viewport
3446 .setExplodedGeometry(new Rectangle(source.getX(), source.getY(),
3447 myBottomFrame.getWidth(), myBottomFrame.getHeight()));
3448 myTopFrame.viewport.setGatherViewsHere(true);
3449 myBottomFrame.viewport.setGatherViewsHere(true);
3450 String topViewId = myTopFrame.viewport.getSequenceSetId();
3451 String bottomViewId = myBottomFrame.viewport.getSequenceSetId();
3453 JInternalFrame[] frames = getDesktopPane().getAllFrames();
3454 for (JInternalFrame frame : frames)
3456 if (frame instanceof SplitFrame && frame != source)
3458 SplitFrame sf = (SplitFrame) frame;
3459 AlignFrame topFrame = (AlignFrame) sf.getTopFrame();
3460 AlignFrame bottomFrame = (AlignFrame) sf.getBottomFrame();
3461 boolean gatherThis = false;
3462 for (int a = 0; a < topFrame.alignPanels.size(); a++)
3464 AlignmentPanel topPanel = topFrame.alignPanels.get(a);
3465 AlignmentPanel bottomPanel = bottomFrame.alignPanels.get(a);
3466 if (topViewId.equals(topPanel.av.getSequenceSetId())
3467 && bottomViewId.equals(bottomPanel.av.getSequenceSetId()))
3470 topPanel.av.setGatherViewsHere(false);
3471 bottomPanel.av.setGatherViewsHere(false);
3472 topPanel.av.setExplodedGeometry(
3473 new Rectangle(sf.getLocation(), topFrame.getSize()));
3474 bottomPanel.av.setExplodedGeometry(
3475 new Rectangle(sf.getLocation(), bottomFrame.getSize()));
3476 myTopFrame.addAlignmentPanel(topPanel, false);
3477 myBottomFrame.addAlignmentPanel(bottomPanel, false);
3483 topFrame.getAlignPanels().clear();
3484 bottomFrame.getAlignPanels().clear();
3491 * The dust settles...give focus to the tab we did this from.
3493 myTopFrame.setDisplayedView(myTopFrame.alignPanel);
3496 public static groovy.ui.Console getGroovyConsole()
3498 return getInstance().groovyConsole;
3502 * handles the payload of a drag and drop event.
3504 * TODO refactor to desktop utilities class
3507 * - Data source strings extracted from the drop event
3509 * - protocol for each data source extracted from the drop event
3513 * - the payload from the drop event
3516 @SuppressWarnings("unchecked")
3517 public static void transferFromDropTarget(List<Object> files,
3518 List<DataSourceType> protocols, DropTargetDropEvent evt,
3519 Transferable t) throws Exception
3522 // BH 2018 changed List<String> to List<Object> to allow for File from SwingJS
3524 // DataFlavor[] flavors = t.getTransferDataFlavors();
3525 // for (int i = 0; i < flavors.length; i++) {
3526 // if (flavors[i].isFlavorJavaFileListType()) {
3527 // evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
3528 // List<File> list = (List<File>) t.getTransferData(flavors[i]);
3529 // for (int j = 0; j < list.size(); j++) {
3530 // File file = (File) list.get(j);
3531 // byte[] data = getDroppedFileBytes(file);
3532 // fileName.setText(file.getName() + " - " + data.length + " " +
3533 // evt.getLocation());
3534 // JTextArea target = (JTextArea) ((DropTarget) evt.getSource()).getComponent();
3535 // target.setText(new String(data));
3537 // dtde.dropComplete(true);
3542 DataFlavor uriListFlavor = new DataFlavor(
3543 "text/uri-list;class=java.lang.String"), urlFlavour = null;
3546 urlFlavour = new DataFlavor(
3547 "application/x-java-url; class=java.net.URL");
3548 } catch (ClassNotFoundException cfe)
3550 Cache.log.debug("Couldn't instantiate the URL dataflavor.", cfe);
3553 if (urlFlavour != null && t.isDataFlavorSupported(urlFlavour))
3558 java.net.URL url = (URL) t.getTransferData(urlFlavour);
3559 // nb: java 8 osx bug https://bugs.openjdk.java.net/browse/JDK-8156099
3560 // means url may be null.
3563 protocols.add(DataSourceType.URL);
3564 files.add(url.toString());
3565 Cache.log.debug("Drop handled as URL dataflavor "
3566 + files.get(files.size() - 1));
3571 if (Platform.isAMacAndNotJS())
3574 "Please ignore plist error - occurs due to problem with java 8 on OSX");
3578 } catch (Throwable ex)
3580 Cache.log.debug("URL drop handler failed.", ex);
3583 if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
3585 // Works on Windows and MacOSX
3586 Cache.log.debug("Drop handled as javaFileListFlavor");
3587 for (Object file : (List<Object>) t
3588 .getTransferData(DataFlavor.javaFileListFlavor))
3591 protocols.add(DataSourceType.FILE);
3596 // Unix like behaviour
3597 boolean added = false;
3599 if (t.isDataFlavorSupported(uriListFlavor))
3601 Cache.log.debug("Drop handled as uriListFlavor");
3602 // This is used by Unix drag system
3603 data = (String) t.getTransferData(uriListFlavor);
3607 // fallback to text: workaround - on OSX where there's a JVM bug
3608 Cache.log.debug("standard URIListFlavor failed. Trying text");
3609 // try text fallback
3610 DataFlavor textDf = new DataFlavor(
3611 "text/plain;class=java.lang.String");
3612 if (t.isDataFlavorSupported(textDf))
3614 data = (String) t.getTransferData(textDf);
3617 Cache.log.debug("Plain text drop content returned "
3618 + (data == null ? "Null - failed" : data));
3623 while (protocols.size() < files.size())
3625 Cache.log.debug("Adding missing FILE protocol for "
3626 + files.get(protocols.size()));
3627 protocols.add(DataSourceType.FILE);
3629 for (java.util.StringTokenizer st = new java.util.StringTokenizer(
3630 data, "\r\n"); st.hasMoreTokens();)
3633 String s = st.nextToken();
3634 if (s.startsWith("#"))
3636 // the line is a comment (as per the RFC 2483)
3639 java.net.URI uri = new java.net.URI(s);
3640 if (uri.getScheme().toLowerCase().startsWith("http"))
3642 protocols.add(DataSourceType.URL);
3643 files.add(uri.toString());
3647 // otherwise preserve old behaviour: catch all for file objects
3648 java.io.File file = new java.io.File(uri);
3649 protocols.add(DataSourceType.FILE);
3650 files.add(file.toString());
3655 if (Cache.log.isDebugEnabled())
3657 if (data == null || !added)
3660 if (t.getTransferDataFlavors() != null
3661 && t.getTransferDataFlavors().length > 0)
3664 "Couldn't resolve drop data. Here are the supported flavors:");
3665 for (DataFlavor fl : t.getTransferDataFlavors())
3668 "Supported transfer dataflavor: " + fl.toString());
3669 Object df = t.getTransferData(fl);
3672 Cache.log.debug("Retrieves: " + df);
3676 Cache.log.debug("Retrieved nothing");
3682 Cache.log.debug("Couldn't resolve dataflavor for drop: "
3688 if (Platform.isWindowsAndNotJS())
3690 Cache.log.debug("Scanning dropped content for Windows Link Files");
3692 // resolve any .lnk files in the file drop
3693 for (int f = 0; f < files.size(); f++)
3695 String source = files.get(f).toString().toLowerCase();
3696 if (protocols.get(f).equals(DataSourceType.FILE)
3697 && (source.endsWith(".lnk") || source.endsWith(".url")
3698 || source.endsWith(".site")))
3702 Object obj = files.get(f);
3703 File lf = (obj instanceof File ? (File) obj
3704 : new File((String) obj));
3705 // process link file to get a URL
3706 Cache.log.debug("Found potential link file: " + lf);
3707 WindowsShortcut wscfile = new WindowsShortcut(lf);
3708 String fullname = wscfile.getRealFilename();
3709 protocols.set(f, FormatAdapter.checkProtocol(fullname));
3710 files.set(f, fullname);
3711 Cache.log.debug("Parsed real filename " + fullname
3712 + " to extract protocol: " + protocols.get(f));
3713 } catch (Exception ex)
3716 "Couldn't parse " + files.get(f) + " as a link file.",
3725 * Sets the Preferences property for experimental features to True or False
3726 * depending on the state of the controlling menu item
3729 protected void showExperimental_actionPerformed(boolean selected)
3731 Cache.setProperty(EXPERIMENTAL_FEATURES, Boolean.toString(selected));
3735 * Answers a (possibly empty) list of any structure viewer frames (currently for
3736 * either Jmol or Chimera) which are currently open. This may optionally be
3737 * restricted to viewers of a specified class, or viewers linked to a specified
3741 * if not null, only return viewers linked to this panel
3742 * @param structureViewerClass
3743 * if not null, only return viewers of this class
3746 public List<StructureViewerBase> getStructureViewers(
3747 AlignmentPanel apanel,
3748 Class<? extends StructureViewerBase> structureViewerClass)
3750 List<StructureViewerBase> result = new ArrayList<>();
3751 JInternalFrame[] frames = Desktop.getInstance().getAllFrames();
3753 for (JInternalFrame frame : frames)
3755 if (frame instanceof StructureViewerBase)
3757 if (structureViewerClass == null
3758 || structureViewerClass.isInstance(frame))
3761 || ((StructureViewerBase) frame).isLinkedWith(apanel))
3763 result.add((StructureViewerBase) frame);