2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
5 * This file is part of Jalview.
7 * Jalview is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation, either version 3
10 * of the License, or (at your option) any later version.
12 * Jalview is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19 * The Jalview Authors are detailed in the 'AUTHORS' file.
23 import jalview.api.AlignViewportI;
24 import jalview.api.AlignmentViewPanel;
25 import jalview.api.StructureSelectionManagerProvider;
26 import jalview.bin.ApplicationSingletonProvider;
27 import jalview.bin.ApplicationSingletonProvider.ApplicationSingletonI;
28 import jalview.bin.Cache;
29 import jalview.bin.Jalview;
30 import jalview.gui.ImageExporter.ImageWriterI;
31 import jalview.io.BackupFiles;
32 import jalview.io.DataSourceType;
33 import jalview.io.FileFormat;
34 import jalview.io.FileFormatException;
35 import jalview.io.FileFormatI;
36 import jalview.io.FileFormats;
37 import jalview.io.FileLoader;
38 import jalview.io.FormatAdapter;
39 import jalview.io.IdentifyFile;
40 import jalview.io.JalviewFileChooser;
41 import jalview.io.JalviewFileView;
42 import jalview.jbgui.GDesktop;
43 import jalview.jbgui.GSplitFrame;
44 import jalview.jbgui.GStructureViewer;
45 import jalview.project.Jalview2XML;
46 import jalview.structure.StructureSelectionManager;
47 import jalview.urls.IdOrgSettings;
48 import jalview.util.BrowserLauncher;
49 import jalview.util.ImageMaker.TYPE;
50 import jalview.util.MessageManager;
51 import jalview.util.Platform;
52 import jalview.util.UrlConstants;
53 import jalview.viewmodel.AlignmentViewport;
54 import jalview.ws.jws1.Discoverer;
55 import jalview.ws.params.ParamManager;
56 import jalview.ws.utils.UrlDownloadClient;
58 import java.awt.BorderLayout;
59 import java.awt.Color;
60 import java.awt.Dimension;
61 import java.awt.FontMetrics;
62 import java.awt.Graphics;
63 import java.awt.GridLayout;
64 import java.awt.Point;
65 import java.awt.Rectangle;
66 import java.awt.Toolkit;
67 import java.awt.Window;
68 import java.awt.datatransfer.Clipboard;
69 import java.awt.datatransfer.ClipboardOwner;
70 import java.awt.datatransfer.DataFlavor;
71 import java.awt.datatransfer.Transferable;
72 import java.awt.dnd.DnDConstants;
73 import java.awt.dnd.DropTargetDragEvent;
74 import java.awt.dnd.DropTargetDropEvent;
75 import java.awt.dnd.DropTargetEvent;
76 import java.awt.dnd.DropTargetListener;
77 import java.awt.event.ActionEvent;
78 import java.awt.event.ActionListener;
79 import java.awt.event.InputEvent;
80 import java.awt.event.KeyEvent;
81 import java.awt.event.MouseAdapter;
82 import java.awt.event.MouseEvent;
83 import java.awt.event.WindowAdapter;
84 import java.awt.event.WindowEvent;
85 import java.beans.PropertyChangeEvent;
86 import java.beans.PropertyChangeListener;
87 import java.io.BufferedInputStream;
89 import java.io.FileOutputStream;
90 import java.io.IOException;
92 import java.util.ArrayList;
93 import java.util.Hashtable;
94 import java.util.List;
95 import java.util.ListIterator;
96 import java.util.Vector;
97 import java.util.concurrent.ExecutorService;
98 import java.util.concurrent.Executors;
99 import java.util.concurrent.Semaphore;
101 import javax.swing.AbstractAction;
102 import javax.swing.Action;
103 import javax.swing.ActionMap;
104 import javax.swing.Box;
105 import javax.swing.BoxLayout;
106 import javax.swing.DefaultDesktopManager;
107 import javax.swing.DesktopManager;
108 import javax.swing.InputMap;
109 import javax.swing.JButton;
110 import javax.swing.JCheckBox;
111 import javax.swing.JComboBox;
112 import javax.swing.JComponent;
113 import javax.swing.JDesktopPane;
114 import javax.swing.JFrame;
115 import javax.swing.JInternalFrame;
116 import javax.swing.JLabel;
117 import javax.swing.JMenuItem;
118 import javax.swing.JPanel;
119 import javax.swing.JPopupMenu;
120 import javax.swing.JProgressBar;
121 import javax.swing.JTextField;
122 import javax.swing.KeyStroke;
123 import javax.swing.SwingUtilities;
124 import javax.swing.event.HyperlinkEvent;
125 import javax.swing.event.HyperlinkEvent.EventType;
126 import javax.swing.event.InternalFrameAdapter;
127 import javax.swing.event.InternalFrameEvent;
128 import javax.swing.event.MenuEvent;
129 import javax.swing.event.MenuListener;
131 import org.stackoverflowusers.file.WindowsShortcut;
138 * @version $Revision: 1.155 $
140 @SuppressWarnings("serial")
141 public class Desktop extends GDesktop
142 implements DropTargetListener, ClipboardOwner, IProgressIndicator,
143 StructureSelectionManagerProvider, ApplicationSingletonI
146 private final static int DEFAULT_MIN_WIDTH = 300;
148 private final static int DEFAULT_MIN_HEIGHT = 250;
150 private final static int ALIGN_FRAME_DEFAULT_MIN_WIDTH = 600;
152 private final static int ALIGN_FRAME_DEFAULT_MIN_HEIGHT = 70;
154 private final static String EXPERIMENTAL_FEATURES = "EXPERIMENTAL_FEATURES";
156 private JalviewChangeSupport changeSupport = new JalviewChangeSupport();
159 * news reader - null if it was never started.
161 BlogReader jvnews = null;
163 private File projectFile;
167 * @see jalview.gui.JalviewChangeSupport#addJalviewPropertyChangeListener(java.beans.PropertyChangeListener)
169 public void addJalviewPropertyChangeListener(
170 PropertyChangeListener listener)
172 changeSupport.addJalviewPropertyChangeListener(listener);
176 * @param propertyName
178 * @see jalview.gui.JalviewChangeSupport#addJalviewPropertyChangeListener(java.lang.String,
179 * java.beans.PropertyChangeListener)
181 public void addJalviewPropertyChangeListener(String propertyName,
182 PropertyChangeListener listener)
184 changeSupport.addJalviewPropertyChangeListener(propertyName, listener);
188 * @param propertyName
190 * @see jalview.gui.JalviewChangeSupport#removeJalviewPropertyChangeListener(java.lang.String,
191 * java.beans.PropertyChangeListener)
193 public void removeJalviewPropertyChangeListener(String propertyName,
194 PropertyChangeListener listener)
196 changeSupport.removeJalviewPropertyChangeListener(propertyName,
200 public static MyDesktopPane getDesktopPane()
202 Desktop desktop = Desktop.getInstance();
203 return desktop == null ? null : desktop.desktopPane;
206 public static StructureSelectionManager getStructureSelectionManager()
208 return StructureSelectionManager
209 .getStructureSelectionManager(getInstance());
212 static int openFrameCount = 0;
214 static final int xOffset = 30;
216 static final int yOffset = 30;
218 public Discoverer discoverer;
220 public Object[] jalviewClipboard;
222 public boolean internalCopy = false;
224 private static int fileLoadingCount = 0;
226 public JInternalFrame conservationSlider;
228 public JInternalFrame PIDSlider;
231 * just an instance (for testng, probably); no actual frames
233 private boolean instanceOnly;
235 class MyDesktopManager implements DesktopManager
238 private DesktopManager delegate;
240 public MyDesktopManager(DesktopManager delegate)
242 this.delegate = delegate;
246 public void activateFrame(JInternalFrame f)
250 delegate.activateFrame(f);
251 } catch (NullPointerException npe)
253 Point p = getMousePosition();
254 showPasteMenu(p.x, p.y);
259 public void beginDraggingFrame(JComponent f)
261 delegate.beginDraggingFrame(f);
265 public void beginResizingFrame(JComponent f, int direction)
267 delegate.beginResizingFrame(f, direction);
271 public void closeFrame(JInternalFrame f)
273 delegate.closeFrame(f);
277 public void deactivateFrame(JInternalFrame f)
279 delegate.deactivateFrame(f);
283 public void deiconifyFrame(JInternalFrame f)
285 delegate.deiconifyFrame(f);
289 public void dragFrame(JComponent f, int newX, int newY)
295 delegate.dragFrame(f, newX, newY);
299 public void endDraggingFrame(JComponent f)
301 delegate.endDraggingFrame(f);
302 desktopPane.repaint();
306 public void endResizingFrame(JComponent f)
308 delegate.endResizingFrame(f);
309 desktopPane.repaint();
313 public void iconifyFrame(JInternalFrame f)
315 delegate.iconifyFrame(f);
319 public void maximizeFrame(JInternalFrame f)
321 delegate.maximizeFrame(f);
325 public void minimizeFrame(JInternalFrame f)
327 delegate.minimizeFrame(f);
331 public void openFrame(JInternalFrame f)
333 delegate.openFrame(f);
337 public void resizeFrame(JComponent f, int newX, int newY, int newWidth,
344 delegate.resizeFrame(f, newX, newY, newWidth, newHeight);
348 public void setBoundsForFrame(JComponent f, int newX, int newY,
349 int newWidth, int newHeight)
351 delegate.setBoundsForFrame(f, newX, newY, newWidth, newHeight);
354 // All other methods, simply delegate
358 public MyDesktopPane desktopPane;
361 * Answers an 'application scope' singleton instance of this class. Separate
362 * SwingJS 'applets' running in the same browser page will each have a
363 * distinct instance of Desktop.
367 public static Desktop getInstance()
369 return Jalview.isHeadlessMode() ? null
370 : (Desktop) ApplicationSingletonProvider
371 .getInstance(Desktop.class);
379 public Desktop(boolean forInstance)
385 * Private constructor enforces singleton pattern. It is called by reflection
386 * from ApplicationSingletonProvider.getInstance().
388 @SuppressWarnings("unused")
394 * A note to implementors. It is ESSENTIAL that any activities that might
395 * block are spawned off as threads rather than waited for during this
398 if (!Platform.isJS())
400 doVamsasClientCheck();
403 doConfigureStructurePrefs();
404 setTitle("Jalview " + jalview.bin.Cache.getProperty("VERSION"));
405 setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
406 boolean selmemusage = jalview.bin.Cache.getDefault("SHOW_MEMUSAGE",
408 boolean showjconsole = jalview.bin.Cache.getDefault("SHOW_JAVA_CONSOLE",
410 desktopPane = new MyDesktopPane(selmemusage);
412 showMemusage.setSelected(selmemusage);
413 desktopPane.setBackground(Color.white);
414 getContentPane().setLayout(new BorderLayout());
415 // alternate config - have scrollbars - see notes in JAL-153
416 // JScrollPane sp = new JScrollPane();
417 // sp.getViewport().setView(desktop);
418 // getContentPane().add(sp, BorderLayout.CENTER);
420 // BH 2018 - just an experiment to try unclipped JInternalFrames.
423 getRootPane().putClientProperty("swingjs.overflow.hidden", "false");
426 getContentPane().add(desktopPane, BorderLayout.CENTER);
427 desktopPane.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
429 // This line prevents Windows Look&Feel resizing all new windows to maximum
430 // if previous window was maximised
431 desktopPane.setDesktopManager(new MyDesktopManager(
432 (Platform.isWindowsAndNotJS() ? new DefaultDesktopManager()
433 : Platform.isAMacAndNotJS()
434 ? new AquaInternalFrameManager(
435 desktopPane.getDesktopManager())
436 : desktopPane.getDesktopManager())));
438 Rectangle dims = getLastKnownDimensions("");
445 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
446 int xPos = Math.max(5, (screenSize.width - 900) / 2);
447 int yPos = Math.max(5, (screenSize.height - 650) / 2);
448 setBounds(xPos, yPos, 900, 650);
451 if (!Platform.isJS())
459 jconsole = new Console(this, showjconsole);
460 // add essential build information
461 jconsole.setHeader("Jalview Version: "
462 + jalview.bin.Cache.getProperty("VERSION") + "\n"
463 + "Jalview Installation: "
464 + jalview.bin.Cache.getDefault("INSTALLATION", "unknown")
465 + "\n" + "Build Date: "
466 + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown") + "\n"
467 + "Java version: " + System.getProperty("java.version") + "\n"
468 + System.getProperty("os.arch") + " "
469 + System.getProperty("os.name") + " "
470 + System.getProperty("os.version"));
472 showConsole(showjconsole);
474 showNews.setVisible(false);
476 experimentalFeatures.setSelected(showExperimental());
478 getIdentifiersOrgData();
482 // Spawn a thread that shows the splashscreen
484 SwingUtilities.invokeLater(new Runnable()
493 // Thread off a new instance of the file chooser - this reduces the time
495 // takes to open it later on.
496 new Thread(new Runnable()
501 Cache.log.debug("Filechooser init thread started.");
502 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
503 JalviewFileChooser.forRead(Cache.getProperty("LAST_DIRECTORY"),
505 Cache.log.debug("Filechooser init thread finished.");
508 // Add the service change listener
509 changeSupport.addJalviewPropertyChangeListener("services",
510 new PropertyChangeListener()
514 public void propertyChange(PropertyChangeEvent evt)
516 Cache.log.debug("Firing service changed event for "
517 + evt.getNewValue());
518 JalviewServicesChanged(evt);
525 this.setDropTarget(new java.awt.dnd.DropTarget(desktopPane, this));
527 this.addWindowListener(new WindowAdapter()
530 public void windowClosing(WindowEvent evt)
537 this.addMouseListener(ma = new MouseAdapter()
540 public void mousePressed(MouseEvent evt)
542 if (evt.isPopupTrigger()) // Mac
544 showPasteMenu(evt.getX(), evt.getY());
549 public void mouseReleased(MouseEvent evt)
551 if (evt.isPopupTrigger()) // Windows
553 showPasteMenu(evt.getX(), evt.getY());
557 desktopPane.addMouseListener(ma);
558 } catch (Throwable t)
565 * Answers true if user preferences to enable experimental features is True
570 public boolean showExperimental()
572 String experimental = Cache.getDefault(EXPERIMENTAL_FEATURES,
573 Boolean.FALSE.toString());
574 return Boolean.valueOf(experimental).booleanValue();
577 public void doConfigureStructurePrefs()
579 // configure services
580 StructureSelectionManager ssm = StructureSelectionManager
581 .getStructureSelectionManager(this);
582 if (jalview.bin.Cache.getDefault(Preferences.ADD_SS_ANN, true))
584 ssm.setAddTempFacAnnot(jalview.bin.Cache
585 .getDefault(Preferences.ADD_TEMPFACT_ANN, true));
586 ssm.setProcessSecondaryStructure(jalview.bin.Cache
587 .getDefault(Preferences.STRUCT_FROM_PDB, true));
588 ssm.setSecStructServices(
589 jalview.bin.Cache.getDefault(Preferences.USE_RNAVIEW, true));
593 ssm.setAddTempFacAnnot(false);
594 ssm.setProcessSecondaryStructure(false);
595 ssm.setSecStructServices(false);
599 public void checkForNews()
601 final Desktop me = this;
602 // Thread off the news reader, in case there are connection problems.
603 new Thread(new Runnable()
608 Cache.log.debug("Starting news thread.");
609 jvnews = new BlogReader(me);
610 showNews.setVisible(true);
611 Cache.log.debug("Completed news thread.");
616 public void getIdentifiersOrgData()
618 // Thread off the identifiers fetcher
619 new Thread(new Runnable()
624 Cache.log.debug("Downloading data from identifiers.org");
625 // UrlDownloadClient client = new UrlDownloadClient();
628 UrlDownloadClient.download(IdOrgSettings.getUrl(),
629 IdOrgSettings.getDownloadLocation());
630 } catch (IOException e)
632 Cache.log.debug("Exception downloading identifiers.org data"
641 protected void showNews_actionPerformed(ActionEvent e)
643 showNews(showNews.isSelected());
646 protected void showNews(boolean visible)
648 Cache.log.debug((visible ? "Showing" : "Hiding") + " news.");
649 showNews.setSelected(visible);
650 if (visible && !jvnews.isVisible())
652 new Thread(new Runnable()
657 long now = System.currentTimeMillis();
659 MessageManager.getString("status.refreshing_news"), now);
660 jvnews.refreshNews();
661 setProgressBar(null, now);
669 * recover the last known dimensions for a jalview window
672 * - empty string is desktop, all other windows have unique prefix
673 * @return null or last known dimensions scaled to current geometry (if last
674 * window geom was known)
676 Rectangle getLastKnownDimensions(String windowName)
678 // TODO: lock aspect ratio for scaling desktop Bug #0058199
679 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
680 String x = jalview.bin.Cache.getProperty(windowName + "SCREEN_X");
681 String y = jalview.bin.Cache.getProperty(windowName + "SCREEN_Y");
682 String width = jalview.bin.Cache
683 .getProperty(windowName + "SCREEN_WIDTH");
684 String height = jalview.bin.Cache
685 .getProperty(windowName + "SCREEN_HEIGHT");
686 if ((x != null) && (y != null) && (width != null) && (height != null))
688 int ix = Integer.parseInt(x), iy = Integer.parseInt(y),
689 iw = Integer.parseInt(width), ih = Integer.parseInt(height);
690 if (jalview.bin.Cache.getProperty("SCREENGEOMETRY_WIDTH") != null)
692 // attempt #1 - try to cope with change in screen geometry - this
693 // version doesn't preserve original jv aspect ratio.
694 // take ratio of current screen size vs original screen size.
695 double sw = ((1f * screenSize.width) / (1f * Integer.parseInt(
696 jalview.bin.Cache.getProperty("SCREENGEOMETRY_WIDTH"))));
697 double sh = ((1f * screenSize.height) / (1f * Integer.parseInt(
698 jalview.bin.Cache.getProperty("SCREENGEOMETRY_HEIGHT"))));
699 // rescale the bounds depending upon the current screen geometry.
700 ix = (int) (ix * sw);
701 iw = (int) (iw * sw);
702 iy = (int) (iy * sh);
703 ih = (int) (ih * sh);
704 while (ix >= screenSize.width)
706 jalview.bin.Cache.log.debug(
707 "Window geometry location recall error: shifting horizontal to within screenbounds.");
708 ix -= screenSize.width;
710 while (iy >= screenSize.height)
712 jalview.bin.Cache.log.debug(
713 "Window geometry location recall error: shifting vertical to within screenbounds.");
714 iy -= screenSize.height;
716 jalview.bin.Cache.log.debug(
717 "Got last known dimensions for " + windowName + ": x:" + ix
718 + " y:" + iy + " width:" + iw + " height:" + ih);
720 // return dimensions for new instance
721 return new Rectangle(ix, iy, iw, ih);
726 private void doVamsasClientCheck()
728 if (Cache.vamsasJarsPresent())
730 setupVamsasDisconnectedGui();
731 VamsasMenu.setVisible(true);
732 final Desktop us = this;
733 VamsasMenu.addMenuListener(new MenuListener()
735 // this listener remembers when the menu was first selected, and
736 // doesn't rebuild the session list until it has been cleared and
738 boolean refresh = true;
741 public void menuCanceled(MenuEvent e)
747 public void menuDeselected(MenuEvent e)
753 public void menuSelected(MenuEvent e)
757 us.buildVamsasStMenu();
762 vamsasStart.setVisible(true);
766 protected void showPasteMenu(int x, int y)
768 JPopupMenu popup = new JPopupMenu();
769 JMenuItem item = new JMenuItem(
770 MessageManager.getString("label.paste_new_window"));
771 item.addActionListener(new ActionListener()
774 public void actionPerformed(ActionEvent evt)
781 popup.show(this, x, y);
788 Clipboard c = Toolkit.getDefaultToolkit().getSystemClipboard();
789 Transferable contents = c.getContents(this);
791 if (contents != null)
793 String file = (String) contents
794 .getTransferData(DataFlavor.stringFlavor);
796 FileFormatI format = new IdentifyFile().identify(file,
797 DataSourceType.PASTE);
799 new FileLoader().loadFile(file, DataSourceType.PASTE, format);
802 } catch (Exception ex)
805 "Unable to paste alignment from system clipboard:\n" + ex);
810 * Adds and opens the given frame to the desktop
821 public static synchronized void addInternalFrame(
822 final JInternalFrame frame, String title, int w, int h)
824 addInternalFrame(frame, title, true, w, h, true, false);
828 * Add an internal frame to the Jalview desktop
835 * When true, display frame immediately, otherwise, caller must call
836 * setVisible themselves.
842 public static synchronized void addInternalFrame(
843 final JInternalFrame frame, String title, boolean makeVisible,
846 addInternalFrame(frame, title, makeVisible, w, h, true, false);
850 * Add an internal frame to the Jalview desktop and make it visible
863 public static synchronized void addInternalFrame(
864 final JInternalFrame frame, String title, int w, int h,
867 addInternalFrame(frame, title, true, w, h, resizable, false);
871 * Add an internal frame to the Jalview desktop
878 * When true, display frame immediately, otherwise, caller must call
879 * setVisible themselves.
886 * @param ignoreMinSize
887 * Do not set the default minimum size for frame
889 public static synchronized void addInternalFrame(
890 final JInternalFrame frame, String title, boolean makeVisible,
891 int w, int h, boolean resizable, boolean ignoreMinSize)
894 // TODO: allow callers to determine X and Y position of frame (eg. via
896 // TODO: consider fixing method to update entries in the window submenu with
897 // the current window title
899 frame.setTitle(title);
900 if (frame.getWidth() < 1 || frame.getHeight() < 1)
904 // THIS IS A PUBLIC STATIC METHOD, SO IT MAY BE CALLED EVEN IN
905 // A HEADLESS STATE WHEN NO DESKTOP EXISTS. MUST RETURN
906 // IF JALVIEW IS RUNNING HEADLESS
907 // ///////////////////////////////////////////////
908 if (Desktop.getInstance().instanceOnly || Jalview.isHeadlessMode())
917 frame.setMinimumSize(
918 new Dimension(DEFAULT_MIN_WIDTH, DEFAULT_MIN_HEIGHT));
920 // Set default dimension for Alignment Frame window.
921 // The Alignment Frame window could be added from a number of places,
923 // I did this here in order not to miss out on any Alignment frame.
924 if (frame instanceof AlignFrame)
926 frame.setMinimumSize(new Dimension(ALIGN_FRAME_DEFAULT_MIN_WIDTH,
927 ALIGN_FRAME_DEFAULT_MIN_HEIGHT));
931 frame.setVisible(makeVisible);
932 frame.setClosable(true);
933 frame.setResizable(resizable);
934 frame.setMaximizable(resizable);
935 frame.setIconifiable(resizable);
936 frame.setOpaque(Platform.isJS());
938 if (frame.getX() < 1 && frame.getY() < 1)
940 frame.setLocation(xOffset * openFrameCount,
941 yOffset * ((openFrameCount - 1) % 10) + yOffset);
945 * add an entry for the new frame in the Window menu
946 * (and remove it when the frame is closed)
948 JMenuItem menuItem = new JMenuItem(title);
949 frame.addInternalFrameListener(new InternalFrameAdapter()
952 public void internalFrameActivated(InternalFrameEvent evt)
954 JInternalFrame itf = getDesktopPane().getSelectedFrame();
957 if (itf instanceof AlignFrame)
959 Jalview.setCurrentAlignFrame((AlignFrame) itf);
966 public void internalFrameClosed(InternalFrameEvent evt)
968 PaintRefresher.RemoveComponent(frame);
971 * defensive check to prevent frames being
972 * added half off the window
974 if (openFrameCount > 0)
980 * ensure no reference to alignFrame retained by menu item listener
982 if (menuItem.getActionListeners().length > 0)
984 menuItem.removeActionListener(menuItem.getActionListeners()[0]);
986 Desktop.getInstance().windowMenu.remove(menuItem);
990 menuItem.addActionListener(new ActionListener()
993 public void actionPerformed(ActionEvent e)
997 frame.setSelected(true);
998 frame.setIcon(false);
999 } catch (java.beans.PropertyVetoException ex)
1001 // System.err.println(ex.toString());
1006 setKeyBindings(frame);
1008 getDesktopPane().add(frame);
1010 Desktop.getInstance().windowMenu.add(menuItem);
1015 frame.setSelected(true);
1016 frame.requestFocus();
1017 } catch (java.beans.PropertyVetoException ve)
1019 } catch (java.lang.ClassCastException cex)
1022 "Squashed a possible GUI implementation error. If you can recreate this, please look at http://issues.jalview.org/browse/JAL-869",
1028 * Add key bindings to a JInternalFrame so that Ctrl-W and Cmd-W will close the
1033 private static void setKeyBindings(JInternalFrame frame)
1035 final Action closeAction = new AbstractAction()
1038 public void actionPerformed(ActionEvent e)
1045 * set up key bindings for Ctrl-W and Cmd-W, with the same (Close) action
1047 KeyStroke ctrlWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
1048 InputEvent.CTRL_DOWN_MASK);
1049 KeyStroke cmdWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
1050 Toolkit.getDefaultToolkit().getMenuShortcutKeyMask());
1052 InputMap inputMap = frame
1053 .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
1054 String ctrlW = ctrlWKey.toString();
1055 inputMap.put(ctrlWKey, ctrlW);
1056 inputMap.put(cmdWKey, ctrlW);
1058 ActionMap actionMap = frame.getActionMap();
1059 actionMap.put(ctrlW, closeAction);
1063 public void lostOwnership(Clipboard clipboard, Transferable contents)
1067 Desktop.getInstance().jalviewClipboard = null;
1070 internalCopy = false;
1074 public void dragEnter(DropTargetDragEvent evt)
1079 public void dragExit(DropTargetEvent evt)
1084 public void dragOver(DropTargetDragEvent evt)
1089 public void dropActionChanged(DropTargetDragEvent evt)
1100 public void drop(DropTargetDropEvent evt)
1102 boolean success = true;
1103 // JAL-1552 - acceptDrop required before getTransferable call for
1104 // Java's Transferable for native dnd
1105 evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
1106 Transferable t = evt.getTransferable();
1107 List<Object> files = new ArrayList<>();
1108 List<DataSourceType> protocols = new ArrayList<>();
1112 Desktop.transferFromDropTarget(files, protocols, evt, t);
1113 } catch (Exception e)
1115 e.printStackTrace();
1123 for (int i = 0; i < files.size(); i++)
1125 // BH 2018 File or String
1126 Object file = files.get(i);
1127 String fileName = file.toString();
1128 DataSourceType protocol = (protocols == null)
1129 ? DataSourceType.FILE
1131 FileFormatI format = null;
1133 if (fileName.endsWith(".jar"))
1135 format = FileFormat.Jalview;
1140 format = new IdentifyFile().identify(file, protocol);
1142 if (file instanceof File)
1144 Platform.cacheFileData((File) file);
1146 new FileLoader().loadFile(null, file, protocol, format);
1149 } catch (Exception ex)
1154 evt.dropComplete(success); // need this to ensure input focus is properly
1155 // transfered to any new windows created
1165 public void inputLocalFileMenuItem_actionPerformed(AlignViewport viewport)
1167 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
1168 JalviewFileChooser chooser = JalviewFileChooser
1169 .forRead(Cache.getProperty("LAST_DIRECTORY"), fileFormat, true);
1171 chooser.setFileView(new JalviewFileView());
1172 chooser.setDialogTitle(
1173 MessageManager.getString("label.open_local_file"));
1174 chooser.setToolTipText(MessageManager.getString("action.open"));
1176 chooser.setResponseHandler(0, new Runnable()
1181 File selectedFile = chooser.getSelectedFile();
1182 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1184 FileFormatI format = chooser.getSelectedFormat();
1187 * Call IdentifyFile to verify the file contains what its extension implies.
1188 * Skip this step for dynamically added file formats, because
1189 * IdentifyFile does not know how to recognise them.
1191 if (FileFormats.getInstance().isIdentifiable(format))
1195 format = new IdentifyFile().identify(selectedFile,
1196 DataSourceType.FILE);
1197 } catch (FileFormatException e)
1199 // format = null; //??
1203 new FileLoader().loadFile(viewport, selectedFile,
1204 DataSourceType.FILE, format);
1207 chooser.showOpenDialog(this);
1211 * Shows a dialog for input of a URL at which to retrieve alignment data
1216 public void inputURLMenuItem_actionPerformed(AlignViewport viewport)
1218 // This construct allows us to have a wider textfield
1220 JLabel label = new JLabel(
1221 MessageManager.getString("label.input_file_url"));
1223 JPanel panel = new JPanel(new GridLayout(2, 1));
1227 * the URL to fetch is
1228 * Java: an editable combobox with history
1229 * JS: (pending JAL-3038) a plain text field
1232 String urlBase = "http://www.";
1233 if (Platform.isJS())
1235 history = new JTextField(urlBase, 35);
1244 JComboBox<String> asCombo = new JComboBox<>();
1245 asCombo.setPreferredSize(new Dimension(400, 20));
1246 asCombo.setEditable(true);
1247 asCombo.addItem(urlBase);
1248 String historyItems = Cache.getProperty("RECENT_URL");
1249 if (historyItems != null)
1251 for (String token : historyItems.split("\\t"))
1253 asCombo.addItem(token);
1260 Object[] options = new Object[] { MessageManager.getString("action.ok"),
1261 MessageManager.getString("action.cancel") };
1262 Runnable action = new Runnable()
1267 @SuppressWarnings("unchecked")
1268 String url = (history instanceof JTextField
1269 ? ((JTextField) history).getText()
1270 : ((JComboBox<String>) history).getSelectedItem()
1273 if (url.toLowerCase().endsWith(".jar"))
1275 if (viewport != null)
1277 new FileLoader().loadFile(viewport, url, DataSourceType.URL,
1278 FileFormat.Jalview);
1282 new FileLoader().loadFile(url, DataSourceType.URL,
1283 FileFormat.Jalview);
1288 FileFormatI format = null;
1291 format = new IdentifyFile().identify(url, DataSourceType.URL);
1292 } catch (FileFormatException e)
1294 // TODO revise error handling, distinguish between
1295 // URL not found and response not valid
1300 String msg = MessageManager
1301 .formatMessage("label.couldnt_locate", url);
1302 JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
1304 MessageManager.getString("label.url_not_found"),
1305 JvOptionPane.WARNING_MESSAGE);
1310 if (viewport != null)
1312 new FileLoader().loadFile(viewport, url, DataSourceType.URL,
1317 new FileLoader().loadFile(url, DataSourceType.URL, format);
1322 String dialogOption = MessageManager
1323 .getString("label.input_alignment_from_url");
1324 JvOptionPane.newOptionDialog(getDesktopPane())
1325 .setResponseHandler(0, action)
1326 .showInternalDialog(panel, dialogOption,
1327 JvOptionPane.YES_NO_CANCEL_OPTION,
1328 JvOptionPane.PLAIN_MESSAGE, null, options,
1329 MessageManager.getString("action.ok"));
1333 * Opens the CutAndPaste window for the user to paste an alignment in to
1336 * - if not null, the pasted alignment is added to the current
1337 * alignment; if null, to a new alignment window
1340 public void inputTextboxMenuItem_actionPerformed(
1341 AlignmentViewPanel viewPanel)
1343 CutAndPasteTransfer cap = new CutAndPasteTransfer();
1344 cap.setForInput(viewPanel);
1345 Desktop.addInternalFrame(cap,
1346 MessageManager.getString("label.cut_paste_alignmen_file"), true,
1356 Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
1357 jalview.bin.Cache.setProperty("SCREENGEOMETRY_WIDTH",
1359 jalview.bin.Cache.setProperty("SCREENGEOMETRY_HEIGHT",
1360 screen.height + "");
1361 storeLastKnownDimensions("", new Rectangle(getBounds().x, getBounds().y,
1362 getWidth(), getHeight()));
1364 if (jconsole != null)
1366 storeLastKnownDimensions("JAVA_CONSOLE_", jconsole.getBounds());
1367 jconsole.stopConsole();
1371 storeLastKnownDimensions("JALVIEW_RSS_WINDOW_", jvnews.getBounds());
1374 if (dialogExecutor != null)
1376 dialogExecutor.shutdownNow();
1378 closeAll_actionPerformed(null);
1380 if (groovyConsole != null)
1382 // suppress a possible repeat prompt to save script
1383 groovyConsole.setDirty(false);
1384 groovyConsole.exit();
1389 private void storeLastKnownDimensions(String string, Rectangle jc)
1391 jalview.bin.Cache.log.debug("Storing last known dimensions for "
1392 + string + ": x:" + jc.x + " y:" + jc.y + " width:" + jc.width
1393 + " height:" + jc.height);
1395 jalview.bin.Cache.setProperty(string + "SCREEN_X", jc.x + "");
1396 jalview.bin.Cache.setProperty(string + "SCREEN_Y", jc.y + "");
1397 jalview.bin.Cache.setProperty(string + "SCREEN_WIDTH", jc.width + "");
1398 jalview.bin.Cache.setProperty(string + "SCREEN_HEIGHT", jc.height + "");
1408 public void aboutMenuItem_actionPerformed(ActionEvent e)
1410 // StringBuffer message = getAboutMessage(false);
1411 // JvOptionPane.showInternalMessageDialog(Desktop.getDesktop(),
1413 // message.toString(), "About Jalview", JvOptionPane.INFORMATION_MESSAGE);
1414 new Thread(new Runnable()
1419 new SplashScreen(true);
1424 public StringBuffer getAboutMessage(boolean shortv)
1426 StringBuffer message = new StringBuffer();
1427 message.append("<html>");
1430 message.append("<h1><strong>Version: "
1431 + jalview.bin.Cache.getProperty("VERSION")
1432 + "</strong></h1>");
1433 message.append("<strong>Last Updated: <em>"
1434 + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown")
1435 + "</em></strong>");
1441 message.append("<strong>Version "
1442 + jalview.bin.Cache.getProperty("VERSION")
1443 + "; last updated: "
1444 + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown"));
1447 if (jalview.bin.Cache.getDefault("LATEST_VERSION", "Checking")
1448 .equals("Checking"))
1450 message.append("<br>...Checking latest version...</br>");
1452 else if (!jalview.bin.Cache.getDefault("LATEST_VERSION", "Checking")
1453 .equals(jalview.bin.Cache.getProperty("VERSION")))
1455 boolean red = false;
1456 if (jalview.bin.Cache.getProperty("VERSION").toLowerCase()
1457 .indexOf("automated build") == -1)
1460 // Displayed when code version and jnlp version do not match and code
1461 // version is not a development build
1462 message.append("<div style=\"color: #FF0000;font-style: bold;\">");
1465 message.append("<br>!! Version "
1466 + jalview.bin.Cache.getDefault("LATEST_VERSION",
1468 + " is available for download from "
1469 + jalview.bin.Cache.getDefault("www.jalview.org",
1470 "http://www.jalview.org")
1474 message.append("</div>");
1477 message.append("<br>Authors: " + jalview.bin.Cache.getDefault(
1479 "The Jalview Authors (See AUTHORS file for current list)")
1480 + "<br><br>Development managed by The Barton Group, University of Dundee, Scotland, UK.<br>"
1481 + "<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"
1482 + "<br><br>If you use Jalview, please cite:"
1483 + "<br>Waterhouse, A.M., Procter, J.B., Martin, D.M.A, Clamp, M. and Barton, G. J. (2009)"
1484 + "<br>Jalview Version 2 - a multiple sequence alignment editor and analysis workbench"
1485 + "<br>Bioinformatics doi: 10.1093/bioinformatics/btp033"
1491 * Action on requesting Help documentation
1494 public void documentationMenuItem_actionPerformed()
1498 if (Platform.isJS())
1500 BrowserLauncher.openURL("http://www.jalview.org/help.html");
1509 Help.showHelpWindow();
1511 } catch (Exception ex)
1513 System.err.println("Error opening help: " + ex.getMessage());
1518 public void closeAll_actionPerformed(ActionEvent e)
1520 if (desktopPane == null)
1524 // TODO show a progress bar while closing?
1525 JInternalFrame[] frames = desktopPane.getAllFrames();
1526 for (int i = 0; i < frames.length; i++)
1530 frames[i].setClosed(true);
1531 } catch (java.beans.PropertyVetoException ex)
1535 Jalview.setCurrentAlignFrame(null);
1536 System.out.println("ALL CLOSED");
1537 if (v_client != null)
1539 // TODO clear binding to vamsas document objects on close_all
1543 * reset state of singleton objects as appropriate (clear down session state
1544 * when all windows are closed)
1546 getStructureSelectionManager().resetAll();
1550 public void raiseRelated_actionPerformed(ActionEvent e)
1552 reorderAssociatedWindows(false, false);
1556 public void minimizeAssociated_actionPerformed(ActionEvent e)
1558 reorderAssociatedWindows(true, false);
1561 void closeAssociatedWindows()
1563 reorderAssociatedWindows(false, true);
1569 * @seejalview.jbgui.GDesktop#garbageCollect_actionPerformed(java.awt.event.
1573 protected void garbageCollect_actionPerformed(ActionEvent e)
1575 // We simply collect the garbage
1576 jalview.bin.Cache.log.debug("Collecting garbage...");
1578 jalview.bin.Cache.log.debug("Finished garbage collection.");
1585 * jalview.jbgui.GDesktop#showMemusage_actionPerformed(java.awt.event.ActionEvent
1589 protected void showMemusage_actionPerformed(ActionEvent e)
1591 getDesktopPane().showMemoryUsage(showMemusage.isSelected());
1598 * jalview.jbgui.GDesktop#showConsole_actionPerformed(java.awt.event.ActionEvent
1602 protected void showConsole_actionPerformed(ActionEvent e)
1604 showConsole(showConsole.isSelected());
1607 Console jconsole = null;
1610 * control whether the java console is visible or not
1614 void showConsole(boolean selected)
1616 // TODO: decide if we should update properties file
1617 if (jconsole != null) // BH 2018
1619 showConsole.setSelected(selected);
1620 Cache.setProperty("SHOW_JAVA_CONSOLE",
1621 Boolean.valueOf(selected).toString());
1622 jconsole.setVisible(selected);
1626 void reorderAssociatedWindows(boolean minimize, boolean close)
1628 JInternalFrame[] frames = getDesktopPane().getAllFrames();
1629 if (frames == null || frames.length < 1)
1634 AlignmentViewport source = null, target = null;
1635 if (frames[0] instanceof AlignFrame)
1637 source = ((AlignFrame) frames[0]).getCurrentView();
1639 else if (frames[0] instanceof TreePanel)
1641 source = ((TreePanel) frames[0]).getViewPort();
1643 else if (frames[0] instanceof PCAPanel)
1645 source = ((PCAPanel) frames[0]).av;
1647 else if (frames[0].getContentPane() instanceof PairwiseAlignPanel)
1649 source = ((PairwiseAlignPanel) frames[0].getContentPane()).av;
1654 for (int i = 0; i < frames.length; i++)
1657 if (frames[i] == null)
1661 if (frames[i] instanceof AlignFrame)
1663 target = ((AlignFrame) frames[i]).getCurrentView();
1665 else if (frames[i] instanceof TreePanel)
1667 target = ((TreePanel) frames[i]).getViewPort();
1669 else if (frames[i] instanceof PCAPanel)
1671 target = ((PCAPanel) frames[i]).av;
1673 else if (frames[i].getContentPane() instanceof PairwiseAlignPanel)
1675 target = ((PairwiseAlignPanel) frames[i].getContentPane()).av;
1678 if (source == target)
1684 frames[i].setClosed(true);
1688 frames[i].setIcon(minimize);
1691 frames[i].toFront();
1695 } catch (java.beans.PropertyVetoException ex)
1710 protected void preferences_actionPerformed(ActionEvent e)
1716 * Prompts the user to choose a file and then saves the Jalview state as a
1717 * Jalview project file
1720 public void saveState_actionPerformed()
1722 saveState_actionPerformed(false);
1725 public void saveState_actionPerformed(boolean saveAs)
1727 java.io.File projectFile = getProjectFile();
1728 // autoSave indicates we already have a file and don't need to ask
1729 boolean autoSave = projectFile != null && !saveAs
1730 && BackupFiles.getEnabled();
1732 // System.out.println("autoSave="+autoSave+", projectFile='"+projectFile+"',
1733 // saveAs="+saveAs+", Backups
1734 // "+(BackupFiles.getEnabled()?"enabled":"disabled"));
1736 boolean approveSave = false;
1739 JalviewFileChooser chooser = new JalviewFileChooser("jvp",
1742 chooser.setFileView(new JalviewFileView());
1743 chooser.setDialogTitle(MessageManager.getString("label.save_state"));
1745 int value = chooser.showSaveDialog(this);
1747 if (value == JalviewFileChooser.APPROVE_OPTION)
1749 projectFile = chooser.getSelectedFile();
1750 setProjectFile(projectFile);
1755 if (approveSave || autoSave)
1757 final Desktop me = this;
1758 final java.io.File chosenFile = projectFile;
1759 new Thread(new Runnable()
1764 // TODO: refactor to Jalview desktop session controller action.
1765 setProgressBar(MessageManager.formatMessage(
1766 "label.saving_jalview_project", new Object[]
1767 { chosenFile.getName() }), chosenFile.hashCode());
1768 jalview.bin.Cache.setProperty("LAST_DIRECTORY",
1769 chosenFile.getParent());
1770 // TODO catch and handle errors for savestate
1771 // TODO prevent user from messing with the Desktop whilst we're saving
1774 boolean doBackup = BackupFiles.getEnabled();
1775 BackupFiles backupfiles = doBackup ? new BackupFiles(chosenFile) : null;
1777 new Jalview2XML().saveState(doBackup ? backupfiles.getTempFile() : chosenFile);
1781 backupfiles.setWriteSuccess(true);
1782 backupfiles.rollBackupsAndRenameTempFile();
1784 } catch (OutOfMemoryError oom)
1786 new OOMWarning("Whilst saving current state to "
1787 + chosenFile.getName(), oom);
1788 } catch (Exception ex)
1790 Cache.log.error("Problems whilst trying to save to "
1791 + chosenFile.getName(), ex);
1792 JvOptionPane.showMessageDialog(me,
1793 MessageManager.formatMessage(
1794 "label.error_whilst_saving_current_state_to",
1796 { chosenFile.getName() }),
1797 MessageManager.getString("label.couldnt_save_project"),
1798 JvOptionPane.WARNING_MESSAGE);
1800 setProgressBar(null, chosenFile.hashCode());
1807 public void saveAsState_actionPerformed(ActionEvent e)
1809 saveState_actionPerformed(true);
1812 protected void setProjectFile(File choice)
1814 this.projectFile = choice;
1817 public File getProjectFile()
1819 return this.projectFile;
1823 * Shows a file chooser dialog and tries to read in the selected file as a
1827 public void loadState_actionPerformed()
1829 final String[] suffix = new String[] { "jvp", "jar" };
1830 final String[] desc = new String[] { "Jalview Project",
1831 "Jalview Project (old)" };
1832 JalviewFileChooser chooser = new JalviewFileChooser(
1833 Cache.getProperty("LAST_DIRECTORY"), suffix, desc,
1834 "Jalview Project", true, true); // last two booleans: allFiles,
1836 chooser.setFileView(new JalviewFileView());
1837 chooser.setDialogTitle(MessageManager.getString("label.restore_state"));
1838 chooser.setResponseHandler(0, new Runnable()
1843 File selectedFile = chooser.getSelectedFile();
1844 setProjectFile(selectedFile);
1845 String choice = selectedFile.getAbsolutePath();
1846 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1847 new Thread(new Runnable()
1854 new Jalview2XML().loadJalviewAlign(choice);
1855 } catch (OutOfMemoryError oom)
1857 new OOMWarning("Whilst loading project from " + choice, oom);
1858 } catch (Exception ex)
1861 "Problems whilst loading project from " + choice, ex);
1862 JvOptionPane.showMessageDialog(Desktop.getDesktopPane(),
1863 MessageManager.formatMessage(
1864 "label.error_whilst_loading_project_from",
1867 MessageManager.getString("label.couldnt_load_project"),
1868 JvOptionPane.WARNING_MESSAGE);
1875 chooser.showOpenDialog(this);
1879 public void inputSequence_actionPerformed(ActionEvent e)
1881 new SequenceFetcher(this);
1884 JPanel progressPanel;
1886 ArrayList<JPanel> fileLoadingPanels = new ArrayList<>();
1888 public void startLoading(final Object fileName)
1890 if (fileLoadingCount == 0)
1892 fileLoadingPanels.add(addProgressPanel(MessageManager
1893 .formatMessage("label.loading_file", new Object[]
1899 private JPanel addProgressPanel(String string)
1901 if (progressPanel == null)
1903 progressPanel = new JPanel(new GridLayout(1, 1));
1904 totalProgressCount = 0;
1905 getContentPane().add(progressPanel, BorderLayout.SOUTH);
1907 JPanel thisprogress = new JPanel(new BorderLayout(10, 5));
1908 JProgressBar progressBar = new JProgressBar();
1909 progressBar.setIndeterminate(true);
1911 thisprogress.add(new JLabel(string), BorderLayout.WEST);
1913 thisprogress.add(progressBar, BorderLayout.CENTER);
1914 progressPanel.add(thisprogress);
1915 ((GridLayout) progressPanel.getLayout()).setRows(
1916 ((GridLayout) progressPanel.getLayout()).getRows() + 1);
1917 ++totalProgressCount;
1919 return thisprogress;
1922 int totalProgressCount = 0;
1924 private void removeProgressPanel(JPanel progbar)
1926 if (progressPanel != null)
1928 synchronized (progressPanel)
1930 progressPanel.remove(progbar);
1931 GridLayout gl = (GridLayout) progressPanel.getLayout();
1932 gl.setRows(gl.getRows() - 1);
1933 if (--totalProgressCount < 1)
1935 this.getContentPane().remove(progressPanel);
1936 progressPanel = null;
1943 public void stopLoading()
1946 if (fileLoadingCount < 1)
1948 while (fileLoadingPanels.size() > 0)
1950 removeProgressPanel(fileLoadingPanels.remove(0));
1952 fileLoadingPanels.clear();
1953 fileLoadingCount = 0;
1958 public static int getViewCount(String alignmentId)
1960 AlignmentViewport[] aps = getViewports(alignmentId);
1961 return (aps == null) ? 0 : aps.length;
1966 * @param alignmentId
1967 * - if null, all sets are returned
1968 * @return all AlignmentPanels concerning the alignmentId sequence set
1970 public static AlignmentPanel[] getAlignmentPanels(String alignmentId)
1972 if (Desktop.getDesktopPane() == null)
1974 // no frames created and in headless mode
1975 // TODO: verify that frames are recoverable when in headless mode
1978 List<AlignmentPanel> aps = new ArrayList<>();
1979 AlignFrame[] frames = getAlignFrames();
1984 for (AlignFrame af : frames)
1986 for (AlignmentPanel ap : af.alignPanels)
1988 if (alignmentId == null
1989 || alignmentId.equals(ap.av.getSequenceSetId()))
1995 if (aps.size() == 0)
1999 AlignmentPanel[] vap = aps.toArray(new AlignmentPanel[aps.size()]);
2004 * get all the viewports on an alignment.
2006 * @param sequenceSetId
2007 * unique alignment id (may be null - all viewports returned in that
2009 * @return all viewports on the alignment bound to sequenceSetId
2011 public static AlignmentViewport[] getViewports(String sequenceSetId)
2013 List<AlignmentViewport> viewp = new ArrayList<>();
2014 if (getDesktopPane() != null)
2016 AlignFrame[] frames = Desktop.getAlignFrames();
2018 for (AlignFrame afr : frames)
2020 if (sequenceSetId == null || afr.getViewport().getSequenceSetId()
2021 .equals(sequenceSetId))
2023 if (afr.alignPanels != null)
2025 for (AlignmentPanel ap : afr.alignPanels)
2027 if (sequenceSetId == null
2028 || sequenceSetId.equals(ap.av.getSequenceSetId()))
2036 viewp.add(afr.getViewport());
2040 if (viewp.size() > 0)
2042 return viewp.toArray(new AlignmentViewport[viewp.size()]);
2049 * Explode the views in the given frame into separate AlignFrame
2053 public static void explodeViews(AlignFrame af)
2055 int size = af.alignPanels.size();
2061 for (int i = 0; i < size; i++)
2063 AlignmentPanel ap = af.alignPanels.get(i);
2064 AlignFrame newaf = new AlignFrame(ap);
2067 * Restore the view's last exploded frame geometry if known. Multiple
2068 * views from one exploded frame share and restore the same (frame)
2069 * position and size.
2071 Rectangle geometry = ap.av.getExplodedGeometry();
2072 if (geometry != null)
2074 newaf.setBounds(geometry);
2077 ap.av.setGatherViewsHere(false);
2079 addInternalFrame(newaf, af.getTitle(), AlignFrame.DEFAULT_WIDTH,
2080 AlignFrame.DEFAULT_HEIGHT);
2083 af.alignPanels.clear();
2084 af.closeMenuItem_actionPerformed(true);
2089 * Gather expanded views (separate AlignFrame's) with the same sequence set
2090 * identifier back in to this frame as additional views, and close the expanded
2091 * views. Note the expanded frames may themselves have multiple views. We take
2096 public void gatherViews(AlignFrame source)
2098 source.viewport.setGatherViewsHere(true);
2099 source.viewport.setExplodedGeometry(source.getBounds());
2100 JInternalFrame[] frames = getAllFrames();
2101 String viewId = source.viewport.getSequenceSetId();
2103 for (int t = 0; t < frames.length; t++)
2105 if (frames[t] instanceof AlignFrame && frames[t] != source)
2107 AlignFrame af = (AlignFrame) frames[t];
2108 boolean gatherThis = false;
2109 for (int a = 0; a < af.alignPanels.size(); a++)
2111 AlignmentPanel ap = af.alignPanels.get(a);
2112 if (viewId.equals(ap.av.getSequenceSetId()))
2115 ap.av.setGatherViewsHere(false);
2116 ap.av.setExplodedGeometry(af.getBounds());
2117 source.addAlignmentPanel(ap, false);
2123 af.alignPanels.clear();
2124 af.closeMenuItem_actionPerformed(true);
2131 jalview.gui.VamsasApplication v_client = null;
2134 public void vamsasImport_actionPerformed(ActionEvent e)
2136 // TODO: JAL-3048 not needed for Jalview-JS
2138 if (v_client == null)
2140 // Load and try to start a session.
2141 JalviewFileChooser chooser = new JalviewFileChooser(
2142 jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
2144 chooser.setFileView(new JalviewFileView());
2145 chooser.setDialogTitle(
2146 MessageManager.getString("label.open_saved_vamsas_session"));
2147 chooser.setToolTipText(MessageManager.getString(
2148 "label.select_vamsas_session_opened_as_new_vamsas_session"));
2150 int value = chooser.showOpenDialog(this);
2152 if (value == JalviewFileChooser.APPROVE_OPTION)
2154 String fle = chooser.getSelectedFile().toString();
2155 if (!vamsasImport(chooser.getSelectedFile()))
2157 JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
2158 MessageManager.formatMessage(
2159 "label.couldnt_import_as_vamsas_session",
2163 .getString("label.vamsas_document_import_failed"),
2164 JvOptionPane.ERROR_MESSAGE);
2170 jalview.bin.Cache.log.error(
2171 "Implementation error - load session from a running session is not supported.");
2176 * import file into a new vamsas session (uses jalview.gui.VamsasApplication)
2179 * @return true if import was a success and a session was started.
2181 public boolean vamsasImport(URL url)
2183 // TODO: create progress bar
2184 if (v_client != null)
2187 jalview.bin.Cache.log.error(
2188 "Implementation error - load session from a running session is not supported.");
2194 // copy the URL content to a temporary local file
2195 // TODO: be a bit cleverer here with nio (?!)
2196 File file = File.createTempFile("vdocfromurl", ".vdj");
2197 FileOutputStream fos = new FileOutputStream(file);
2198 BufferedInputStream bis = new BufferedInputStream(url.openStream());
2199 byte[] buffer = new byte[2048];
2201 while ((ln = bis.read(buffer)) > -1)
2203 fos.write(buffer, 0, ln);
2207 v_client = new jalview.gui.VamsasApplication(this, file,
2208 url.toExternalForm());
2209 } catch (Exception ex)
2211 jalview.bin.Cache.log.error(
2212 "Failed to create new vamsas session from contents of URL "
2217 setupVamsasConnectedGui();
2218 v_client.initial_update(); // TODO: thread ?
2219 return v_client.inSession();
2223 * import file into a new vamsas session (uses jalview.gui.VamsasApplication)
2226 * @return true if import was a success and a session was started.
2228 public boolean vamsasImport(File file)
2230 if (v_client != null)
2233 jalview.bin.Cache.log.error(
2234 "Implementation error - load session from a running session is not supported.");
2238 setProgressBar(MessageManager.formatMessage(
2239 "status.importing_vamsas_session_from", new Object[]
2240 { file.getName() }), file.hashCode());
2243 v_client = new jalview.gui.VamsasApplication(this, file, null);
2244 } catch (Exception ex)
2246 setProgressBar(MessageManager.formatMessage(
2247 "status.importing_vamsas_session_from", new Object[]
2248 { file.getName() }), file.hashCode());
2249 jalview.bin.Cache.log.error(
2250 "New vamsas session from existing session file failed:", ex);
2253 setupVamsasConnectedGui();
2254 v_client.initial_update(); // TODO: thread ?
2255 setProgressBar(MessageManager.formatMessage(
2256 "status.importing_vamsas_session_from", new Object[]
2257 { file.getName() }), file.hashCode());
2258 return v_client.inSession();
2261 public boolean joinVamsasSession(String mysesid)
2263 if (v_client != null)
2265 throw new Error(MessageManager
2266 .getString("error.try_join_vamsas_session_another"));
2268 if (mysesid == null)
2271 MessageManager.getString("error.invalid_vamsas_session_id"));
2273 v_client = new VamsasApplication(this, mysesid);
2274 setupVamsasConnectedGui();
2275 v_client.initial_update();
2276 return (v_client.inSession());
2280 public void vamsasStart_actionPerformed(ActionEvent e)
2282 if (v_client == null)
2285 // we just start a default session for moment.
2287 * JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache.
2288 * getProperty("LAST_DIRECTORY"));
2290 * chooser.setFileView(new JalviewFileView());
2291 * chooser.setDialogTitle("Load Vamsas file");
2292 * chooser.setToolTipText("Import");
2294 * int value = chooser.showOpenDialog(this);
2296 * if (value == JalviewFileChooser.APPROVE_OPTION) { v_client = new
2297 * jalview.gui.VamsasApplication(this, chooser.getSelectedFile());
2299 v_client = new VamsasApplication(this);
2300 setupVamsasConnectedGui();
2301 v_client.initial_update(); // TODO: thread ?
2305 // store current data in session.
2306 v_client.push_update(); // TODO: thread
2310 protected void setupVamsasConnectedGui()
2312 vamsasStart.setText(MessageManager.getString("label.session_update"));
2313 vamsasSave.setVisible(true);
2314 vamsasStop.setVisible(true);
2315 vamsasImport.setVisible(false); // Document import to existing session is
2316 // not possible for vamsas-client-1.0.
2319 protected void setupVamsasDisconnectedGui()
2321 vamsasSave.setVisible(false);
2322 vamsasStop.setVisible(false);
2323 vamsasImport.setVisible(true);
2325 .setText(MessageManager.getString("label.new_vamsas_session"));
2329 public void vamsasStop_actionPerformed(ActionEvent e)
2331 if (v_client != null)
2333 v_client.end_session();
2335 setupVamsasDisconnectedGui();
2339 protected void buildVamsasStMenu()
2341 if (v_client == null)
2343 String[] sess = null;
2346 sess = VamsasApplication.getSessionList();
2347 } catch (Exception e)
2349 jalview.bin.Cache.log.warn("Problem getting current sessions list.",
2355 jalview.bin.Cache.log.debug(
2356 "Got current sessions list: " + sess.length + " entries.");
2357 VamsasStMenu.removeAll();
2358 for (int i = 0; i < sess.length; i++)
2360 JMenuItem sessit = new JMenuItem();
2361 sessit.setText(sess[i]);
2362 sessit.setToolTipText(MessageManager
2363 .formatMessage("label.connect_to_session", new Object[]
2365 final Desktop dsktp = this;
2366 final String mysesid = sess[i];
2367 sessit.addActionListener(new ActionListener()
2371 public void actionPerformed(ActionEvent e)
2373 if (dsktp.v_client == null)
2375 Thread rthr = new Thread(new Runnable()
2381 dsktp.v_client = new VamsasApplication(dsktp, mysesid);
2382 dsktp.setupVamsasConnectedGui();
2383 dsktp.v_client.initial_update();
2391 VamsasStMenu.add(sessit);
2393 // don't show an empty menu.
2394 VamsasStMenu.setVisible(sess.length > 0);
2399 jalview.bin.Cache.log.debug("No current vamsas sessions.");
2400 VamsasStMenu.removeAll();
2401 VamsasStMenu.setVisible(false);
2406 // Not interested in the content. Just hide ourselves.
2407 VamsasStMenu.setVisible(false);
2412 public void vamsasSave_actionPerformed(ActionEvent e)
2414 // TODO: JAL-3048 not needed for Jalview-JS
2416 if (v_client != null)
2418 // TODO: VAMSAS DOCUMENT EXTENSION is VDJ
2419 JalviewFileChooser chooser = new JalviewFileChooser("vdj",
2422 chooser.setFileView(new JalviewFileView());
2423 chooser.setDialogTitle(MessageManager
2424 .getString("label.save_vamsas_document_archive"));
2426 int value = chooser.showSaveDialog(this);
2428 if (value == JalviewFileChooser.APPROVE_OPTION)
2430 java.io.File choice = chooser.getSelectedFile();
2431 JPanel progpanel = addProgressPanel(MessageManager
2432 .formatMessage("label.saving_vamsas_doc", new Object[]
2433 { choice.getName() }));
2434 Cache.setProperty("LAST_DIRECTORY", choice.getParent());
2435 String warnmsg = null;
2436 String warnttl = null;
2439 v_client.vclient.storeDocument(choice);
2442 warnttl = "Serious Problem saving Vamsas Document";
2443 warnmsg = ex.toString();
2444 jalview.bin.Cache.log
2445 .error("Error Whilst saving document to " + choice, ex);
2447 } catch (Exception ex)
2449 warnttl = "Problem saving Vamsas Document.";
2450 warnmsg = ex.toString();
2451 jalview.bin.Cache.log.warn(
2452 "Exception Whilst saving document to " + choice, ex);
2455 removeProgressPanel(progpanel);
2456 if (warnmsg != null)
2458 JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
2460 warnmsg, warnttl, JvOptionPane.ERROR_MESSAGE);
2466 JPanel vamUpdate = null;
2469 * hide vamsas user gui bits when a vamsas document event is being handled.
2472 * true to hide gui, false to reveal gui
2474 public void setVamsasUpdate(boolean b)
2476 Cache.log.debug("Setting gui for Vamsas update "
2477 + (b ? "in progress" : "finished"));
2479 if (vamUpdate != null)
2481 this.removeProgressPanel(vamUpdate);
2485 vamUpdate = this.addProgressPanel(
2486 MessageManager.getString("label.updating_vamsas_session"));
2488 vamsasStart.setVisible(!b);
2489 vamsasStop.setVisible(!b);
2490 vamsasSave.setVisible(!b);
2493 public JInternalFrame[] getAllFrames()
2495 return desktopPane.getAllFrames();
2499 * Checks the given url to see if it gives a response indicating that the user
2500 * should be informed of a new questionnaire.
2504 public void checkForQuestionnaire(String url)
2506 UserQuestionnaireCheck jvq = new UserQuestionnaireCheck(url);
2507 // javax.swing.SwingUtilities.invokeLater(jvq);
2508 new Thread(jvq).start();
2511 public void checkURLLinks()
2513 // Thread off the URL link checker
2514 addDialogThread(new Runnable()
2519 if (Cache.getDefault("CHECKURLLINKS", true))
2521 // check what the actual links are - if it's just the default don't
2522 // bother with the warning
2523 List<String> links = Preferences.sequenceUrlLinks
2526 // only need to check links if there is one with a
2527 // SEQUENCE_ID which is not the default EMBL_EBI link
2528 ListIterator<String> li = links.listIterator();
2529 boolean check = false;
2530 List<JLabel> urls = new ArrayList<>();
2531 while (li.hasNext())
2533 String link = li.next();
2534 if (link.contains(UrlConstants.SEQUENCE_ID)
2535 && !UrlConstants.isDefaultString(link))
2538 int barPos = link.indexOf("|");
2539 String urlMsg = barPos == -1 ? link
2540 : link.substring(0, barPos) + ": "
2541 + link.substring(barPos + 1);
2542 urls.add(new JLabel(urlMsg));
2550 // ask user to check in case URL links use old style tokens
2551 // ($SEQUENCE_ID$ for sequence id _or_ accession id)
2552 JPanel msgPanel = new JPanel();
2553 msgPanel.setLayout(new BoxLayout(msgPanel, BoxLayout.PAGE_AXIS));
2554 msgPanel.add(Box.createVerticalGlue());
2555 JLabel msg = new JLabel(MessageManager
2556 .getString("label.SEQUENCE_ID_for_DB_ACCESSION1"));
2557 JLabel msg2 = new JLabel(MessageManager
2558 .getString("label.SEQUENCE_ID_for_DB_ACCESSION2"));
2560 for (JLabel url : urls)
2566 final JCheckBox jcb = new JCheckBox(
2567 MessageManager.getString("label.do_not_display_again"));
2568 jcb.addActionListener(new ActionListener()
2571 public void actionPerformed(ActionEvent e)
2573 // update Cache settings for "don't show this again"
2574 boolean showWarningAgain = !jcb.isSelected();
2575 Cache.setProperty("CHECKURLLINKS",
2576 Boolean.valueOf(showWarningAgain).toString());
2581 JvOptionPane.showMessageDialog(desktopPane, msgPanel,
2583 .getString("label.SEQUENCE_ID_no_longer_used"),
2584 JvOptionPane.WARNING_MESSAGE);
2591 * Proxy class for JDesktopPane which optionally displays the current memory
2592 * usage and highlights the desktop area with a red bar if free memory runs low.
2596 public class MyDesktopPane extends JDesktopPane
2599 private static final float ONE_MB = 1048576f;
2601 boolean showMemoryUsage = false;
2605 java.text.NumberFormat df;
2607 float maxMemory, allocatedMemory, freeMemory, totalFreeMemory,
2610 public MyDesktopPane(boolean showMemoryUsage)
2612 showMemoryUsage(showMemoryUsage);
2615 public void showMemoryUsage(boolean showMemory)
2617 this.showMemoryUsage = showMemory;
2620 Thread worker = new Thread(this);
2626 public boolean isShowMemoryUsage()
2628 return showMemoryUsage;
2634 df = java.text.NumberFormat.getNumberInstance();
2635 df.setMaximumFractionDigits(2);
2636 runtime = Runtime.getRuntime();
2638 while (showMemoryUsage)
2642 maxMemory = runtime.maxMemory() / ONE_MB;
2643 allocatedMemory = runtime.totalMemory() / ONE_MB;
2644 freeMemory = runtime.freeMemory() / ONE_MB;
2645 totalFreeMemory = freeMemory + (maxMemory - allocatedMemory);
2647 percentUsage = (totalFreeMemory / maxMemory) * 100;
2649 // if (percentUsage < 20)
2651 // border1 = BorderFactory.createMatteBorder(12, 12, 12, 12,
2653 // instance.set.setBorder(border1);
2656 // sleep after showing usage
2658 } catch (Exception ex)
2660 ex.printStackTrace();
2666 public void paintComponent(Graphics g)
2668 if (showMemoryUsage && g != null && df != null)
2670 if (percentUsage < 20)
2672 g.setColor(Color.red);
2674 FontMetrics fm = g.getFontMetrics();
2677 g.drawString(MessageManager.formatMessage("label.memory_stats",
2679 { df.format(totalFreeMemory), df.format(maxMemory),
2680 df.format(percentUsage) }),
2681 10, getHeight() - fm.getHeight());
2688 * Accessor method to quickly get all the AlignmentFrames loaded.
2690 * @return an array of AlignFrame, or null if none found
2692 public static AlignFrame[] getAlignFrames()
2694 if (Jalview.isHeadlessMode())
2696 // Desktop.getDesktop() is null in headless mode
2697 return new AlignFrame[] { Jalview.getCurrentAlignFrame() };
2700 JInternalFrame[] frames = Desktop.getDesktopPane().getAllFrames();
2706 List<AlignFrame> avp = new ArrayList<>();
2708 for (int i = frames.length - 1; i > -1; i--)
2710 if (frames[i] instanceof AlignFrame)
2712 avp.add((AlignFrame) frames[i]);
2714 else if (frames[i] instanceof SplitFrame)
2717 * Also check for a split frame containing an AlignFrame
2719 GSplitFrame sf = (GSplitFrame) frames[i];
2720 if (sf.getTopFrame() instanceof AlignFrame)
2722 avp.add((AlignFrame) sf.getTopFrame());
2724 if (sf.getBottomFrame() instanceof AlignFrame)
2726 avp.add((AlignFrame) sf.getBottomFrame());
2730 if (avp.size() == 0)
2734 AlignFrame afs[] = avp.toArray(new AlignFrame[avp.size()]);
2739 * Returns an array of any AppJmol frames in the Desktop (or null if none).
2743 public GStructureViewer[] getJmols()
2745 JInternalFrame[] frames = Desktop.getDesktopPane().getAllFrames();
2751 List<GStructureViewer> avp = new ArrayList<>();
2753 for (int i = frames.length - 1; i > -1; i--)
2755 if (frames[i] instanceof AppJmol)
2757 GStructureViewer af = (GStructureViewer) frames[i];
2761 if (avp.size() == 0)
2765 GStructureViewer afs[] = avp.toArray(new GStructureViewer[avp.size()]);
2770 * Add Groovy Support to Jalview
2773 public void groovyShell_actionPerformed()
2777 openGroovyConsole();
2778 } catch (Exception ex)
2780 jalview.bin.Cache.log.error("Groovy Shell Creation failed.", ex);
2781 JvOptionPane.showInternalMessageDialog(desktopPane,
2783 MessageManager.getString("label.couldnt_create_groovy_shell"),
2784 MessageManager.getString("label.groovy_support_failed"),
2785 JvOptionPane.ERROR_MESSAGE);
2790 * Open the Groovy console
2792 private void openGroovyConsole()
2794 if (groovyConsole == null)
2796 groovyConsole = new groovy.ui.Console();
2797 groovyConsole.setVariable("Jalview", this);
2798 groovyConsole.run();
2801 * We allow only one console at a time, so that AlignFrame menu option
2802 * 'Calculate | Run Groovy script' is unambiguous.
2803 * Disable 'Groovy Console', and enable 'Run script', when the console is
2804 * opened, and the reverse when it is closed
2806 Window window = (Window) groovyConsole.getFrame();
2807 window.addWindowListener(new WindowAdapter()
2810 public void windowClosed(WindowEvent e)
2813 * rebind CMD-Q from Groovy Console to Jalview Quit
2816 enableExecuteGroovy(false);
2822 * show Groovy console window (after close and reopen)
2824 ((Window) groovyConsole.getFrame()).setVisible(true);
2827 * if we got this far, enable 'Run Groovy' in AlignFrame menus
2828 * and disable opening a second console
2830 enableExecuteGroovy(true);
2834 * Bind Ctrl/Cmd-Q to Quit - for reset as Groovy Console takes over this binding
2837 protected void addQuitHandler()
2839 getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
2840 .put(KeyStroke.getKeyStroke(KeyEvent.VK_Q,
2841 Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()),
2843 getRootPane().getActionMap().put("Quit", new AbstractAction()
2846 public void actionPerformed(ActionEvent e)
2854 * Enable or disable 'Run Groovy script' in AlignFrame calculate menus
2857 * true if Groovy console is open
2859 public void enableExecuteGroovy(boolean enabled)
2862 * disable opening a second Groovy console
2863 * (or re-enable when the console is closed)
2865 groovyShell.setEnabled(!enabled);
2867 AlignFrame[] alignFrames = getAlignFrames();
2868 if (alignFrames != null)
2870 for (AlignFrame af : alignFrames)
2872 af.setGroovyEnabled(enabled);
2878 * Progress bars managed by the IProgressIndicator method.
2880 private Hashtable<Long, JPanel> progressBars;
2882 private Hashtable<Long, IProgressIndicatorHandler> progressBarHandlers;
2887 * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
2890 public void setProgressBar(String message, long id)
2892 Platform.timeCheck("Desktop " + message, Platform.TIME_MARK);
2894 if (progressBars == null)
2896 progressBars = new Hashtable<>();
2897 progressBarHandlers = new Hashtable<>();
2900 if (progressBars.get(new Long(id)) != null)
2902 JPanel panel = progressBars.remove(new Long(id));
2903 if (progressBarHandlers.contains(new Long(id)))
2905 progressBarHandlers.remove(new Long(id));
2907 removeProgressPanel(panel);
2911 progressBars.put(new Long(id), addProgressPanel(message));
2918 * @see jalview.gui.IProgressIndicator#registerHandler(long,
2919 * jalview.gui.IProgressIndicatorHandler)
2922 public void registerHandler(final long id,
2923 final IProgressIndicatorHandler handler)
2925 if (progressBarHandlers == null
2926 || !progressBars.containsKey(new Long(id)))
2928 throw new Error(MessageManager.getString(
2929 "error.call_setprogressbar_before_registering_handler"));
2931 progressBarHandlers.put(new Long(id), handler);
2932 final JPanel progressPanel = progressBars.get(new Long(id));
2933 if (handler.canCancel())
2935 JButton cancel = new JButton(
2936 MessageManager.getString("action.cancel"));
2937 final IProgressIndicator us = this;
2938 cancel.addActionListener(new ActionListener()
2942 public void actionPerformed(ActionEvent e)
2944 handler.cancelActivity(id);
2945 us.setProgressBar(MessageManager
2946 .formatMessage("label.cancelled_params", new Object[]
2947 { ((JLabel) progressPanel.getComponent(0)).getText() }),
2951 progressPanel.add(cancel, BorderLayout.EAST);
2957 * @return true if any progress bars are still active
2960 public boolean operationInProgress()
2962 if (progressBars != null && progressBars.size() > 0)
2970 * This will return the first AlignFrame holding the given viewport instance. It
2971 * will break if there are more than one AlignFrames viewing a particular av.
2974 * @return alignFrame for viewport
2976 public static AlignFrame getAlignFrameFor(AlignViewportI viewport)
2978 if (getDesktopPane() != null)
2980 AlignmentPanel[] aps = getAlignmentPanels(
2981 viewport.getSequenceSetId());
2982 for (int panel = 0; aps != null && panel < aps.length; panel++)
2984 if (aps[panel] != null && aps[panel].av == viewport)
2986 return aps[panel].alignFrame;
2993 public VamsasApplication getVamsasApplication()
3000 * flag set if jalview GUI is being operated programmatically
3002 private boolean inBatchMode = false;
3005 * check if jalview GUI is being operated programmatically
3007 * @return inBatchMode
3009 public boolean isInBatchMode()
3015 * set flag if jalview GUI is being operated programmatically
3017 * @param inBatchMode
3019 public void setInBatchMode(boolean inBatchMode)
3021 this.inBatchMode = inBatchMode;
3024 public void startServiceDiscovery()
3026 startServiceDiscovery(false);
3029 public void startServiceDiscovery(boolean blocking)
3031 boolean alive = true;
3032 Thread t0 = null, t1 = null, t2 = null;
3033 // JAL-940 - JALVIEW 1 services are now being EOLed as of JABA 2.1 release
3036 // todo: changesupport handlers need to be transferred
3037 if (discoverer == null)
3039 discoverer = Discoverer.getInstance();
3040 // register PCS handler for getDesktop().
3041 discoverer.addPropertyChangeListener(changeSupport);
3043 // JAL-940 - disabled JWS1 service configuration - always start discoverer
3044 // until we phase out completely
3045 (t0 = new Thread(discoverer)).start();
3048 if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
3050 t2 = jalview.ws.jws2.Jws2Discoverer.getInstance()
3051 .startDiscoverer(changeSupport);
3055 // TODO: do rest service discovery
3064 } catch (Exception e)
3067 alive = (t1 != null && t1.isAlive()) || (t2 != null && t2.isAlive())
3068 || (t3 != null && t3.isAlive())
3069 || (t0 != null && t0.isAlive());
3075 * called to check if the service discovery process completed successfully.
3079 protected void JalviewServicesChanged(PropertyChangeEvent evt)
3081 if (evt.getNewValue() == null || evt.getNewValue() instanceof Vector)
3083 final String ermsg = jalview.ws.jws2.Jws2Discoverer.getInstance()
3084 .getErrorMessages();
3087 if (Cache.getDefault("SHOW_WSDISCOVERY_ERRORS", true))
3089 if (serviceChangedDialog == null)
3091 // only run if we aren't already displaying one of these.
3092 addDialogThread(serviceChangedDialog = new Runnable()
3099 * JalviewDialog jd =new JalviewDialog() {
3101 * @Override protected void cancelPressed() { // TODO
3102 * Auto-generated method stub
3104 * }@Override protected void okPressed() { // TODO
3105 * Auto-generated method stub
3107 * }@Override protected void raiseClosed() { // TODO
3108 * Auto-generated method stub
3110 * } }; jd.initDialogFrame(new
3111 * JLabel("<html><table width=\"450\"><tr><td>" + ermsg +
3112 * "<br/>It may be that you have invalid JABA URLs in your web service preferences,"
3113 * + " or mis-configured HTTP proxy settings.<br/>" +
3114 * "Check the <em>Connections</em> and <em>Web services</em> tab of the"
3116 * " Tools->Preferences dialog box to change them.</td></tr></table></html>"
3117 * ), true, true, "Web Service Configuration Problem", 450,
3120 * jd.waitForInput();
3122 JvOptionPane.showConfirmDialog(Desktop.getDesktopPane(),
3123 new JLabel("<html><table width=\"450\"><tr><td>"
3124 + ermsg + "</td></tr></table>"
3125 + "<p>It may be that you have invalid JABA URLs<br/>in your web service preferences,"
3126 + "<br>or as a command-line argument, or mis-configured HTTP proxy settings.</p>"
3127 + "<p>Check the <em>Connections</em> and <em>Web services</em> tab<br/>of the"
3128 + " Tools->Preferences dialog box to change them.</p></html>"),
3129 "Web Service Configuration Problem",
3130 JvOptionPane.DEFAULT_OPTION,
3131 JvOptionPane.ERROR_MESSAGE);
3132 serviceChangedDialog = null;
3141 "Errors reported by JABA discovery service. Check web services preferences.\n"
3148 Runnable serviceChangedDialog = null;
3151 * start a thread to open a URL in the configured browser. Pops up a warning
3152 * dialog to the user if there is an exception when calling out to the browser
3157 public static void showUrl(final String url)
3159 showUrl(url, Desktop.getInstance());
3163 * Like showUrl but allows progress handler to be specified
3167 * (null) or object implementing IProgressIndicator
3169 public static void showUrl(final String url,
3170 final IProgressIndicator progress)
3172 new Thread(new Runnable()
3179 if (progress != null)
3181 progress.setProgressBar(MessageManager
3182 .formatMessage("status.opening_params", new Object[]
3183 { url }), this.hashCode());
3185 BrowserLauncher.openURL(url);
3186 } catch (Exception ex)
3188 JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
3190 .getString("label.web_browser_not_found_unix"),
3191 MessageManager.getString("label.web_browser_not_found"),
3192 JvOptionPane.WARNING_MESSAGE);
3194 ex.printStackTrace();
3196 if (progress != null)
3198 progress.setProgressBar(null, this.hashCode());
3204 private WsParamSetManager wsparamManager = null;
3206 public static ParamManager getUserParameterStore()
3208 Desktop d = Desktop.getInstance();
3209 if (d.wsparamManager == null)
3211 d.wsparamManager = new WsParamSetManager();
3213 return d.wsparamManager;
3217 * static hyperlink handler proxy method for use by Jalview's internal windows
3221 public static void hyperlinkUpdate(HyperlinkEvent e)
3223 if (e.getEventType() == EventType.ACTIVATED)
3228 url = e.getURL().toString();
3229 Desktop.showUrl(url);
3230 } catch (Exception x)
3234 if (Cache.log != null)
3236 Cache.log.error("Couldn't handle string " + url + " as a URL.");
3241 "Couldn't handle string " + url + " as a URL.");
3244 // ignore any exceptions due to dud links.
3251 * single thread that handles display of dialogs to user.
3253 ExecutorService dialogExecutor = Executors.newSingleThreadExecutor();
3256 * flag indicating if dialogExecutor should try to acquire a permit
3258 volatile boolean dialogPause = true;
3263 java.util.concurrent.Semaphore block = new Semaphore(0);
3265 private groovy.ui.Console groovyConsole;
3267 public StructureViewer lastTargetedView;
3270 * add another dialog thread to the queue
3274 public void addDialogThread(final Runnable prompter)
3276 dialogExecutor.submit(new Runnable()
3286 } catch (InterruptedException x)
3296 SwingUtilities.invokeAndWait(prompter);
3297 } catch (Exception q)
3299 Cache.log.warn("Unexpected Exception in dialog thread.", q);
3305 public void startDialogQueue()
3307 // set the flag so we don't pause waiting for another permit and semaphore
3308 // the current task to begin
3309 dialogPause = false;
3314 * Outputs an image of the desktop to file in EPS format, after prompting the
3315 * user for choice of Text or Lineart character rendering (unless a preference
3316 * has been set). The file name is generated as
3319 * Jalview_snapshot_nnnnn.eps where nnnnn is the current timestamp in milliseconds
3323 protected void snapShotWindow_actionPerformed(ActionEvent e)
3325 // currently the menu option to do this is not shown
3328 int width = getWidth();
3329 int height = getHeight();
3331 "Jalview_snapshot_" + System.currentTimeMillis() + ".eps");
3332 ImageWriterI writer = new ImageWriterI()
3335 public void exportImage(Graphics g) throws Exception
3338 Cache.log.info("Successfully written snapshot to file "
3339 + of.getAbsolutePath());
3342 String title = "View of desktop";
3343 ImageExporter exporter = new ImageExporter(writer, null, TYPE.EPS,
3345 exporter.doExport(of, this, width, height, title);
3349 * Explode the views in the given SplitFrame into separate SplitFrame windows.
3350 * This respects (remembers) any previous 'exploded geometry' i.e. the size and
3351 * location last time the view was expanded (if any). However it does not
3352 * remember the split pane divider location - this is set to match the
3353 * 'exploding' frame.
3357 public void explodeViews(SplitFrame sf)
3359 AlignFrame oldTopFrame = (AlignFrame) sf.getTopFrame();
3360 AlignFrame oldBottomFrame = (AlignFrame) sf.getBottomFrame();
3361 List<? extends AlignmentViewPanel> topPanels = oldTopFrame
3363 List<? extends AlignmentViewPanel> bottomPanels = oldBottomFrame
3365 int viewCount = topPanels.size();
3372 * Processing in reverse order works, forwards order leaves the first panels
3373 * not visible. I don't know why!
3375 for (int i = viewCount - 1; i >= 0; i--)
3378 * Make new top and bottom frames. These take over the respective
3379 * AlignmentPanel objects, including their AlignmentViewports, so the
3380 * cdna/protein relationships between the viewports is carried over to the
3383 * explodedGeometry holds the (x, y) position of the previously exploded
3384 * SplitFrame, and the (width, height) of the AlignFrame component
3386 AlignmentPanel topPanel = (AlignmentPanel) topPanels.get(i);
3387 AlignFrame newTopFrame = new AlignFrame(topPanel);
3388 newTopFrame.setSize(oldTopFrame.getSize());
3389 newTopFrame.setVisible(true);
3390 Rectangle geometry = ((AlignViewport) topPanel.getAlignViewport())
3391 .getExplodedGeometry();
3392 if (geometry != null)
3394 newTopFrame.setSize(geometry.getSize());
3397 AlignmentPanel bottomPanel = (AlignmentPanel) bottomPanels.get(i);
3398 AlignFrame newBottomFrame = new AlignFrame(bottomPanel);
3399 newBottomFrame.setSize(oldBottomFrame.getSize());
3400 newBottomFrame.setVisible(true);
3401 geometry = ((AlignViewport) bottomPanel.getAlignViewport())
3402 .getExplodedGeometry();
3403 if (geometry != null)
3405 newBottomFrame.setSize(geometry.getSize());
3408 topPanel.av.setGatherViewsHere(false);
3409 bottomPanel.av.setGatherViewsHere(false);
3410 JInternalFrame splitFrame = new SplitFrame(newTopFrame,
3412 if (geometry != null)
3414 splitFrame.setLocation(geometry.getLocation());
3416 Desktop.addInternalFrame(splitFrame, sf.getTitle(), -1, -1);
3420 * Clear references to the panels (now relocated in the new SplitFrames)
3421 * before closing the old SplitFrame.
3424 bottomPanels.clear();
3429 * Gather expanded split frames, sharing the same pairs of sequence set ids,
3430 * back into the given SplitFrame as additional views. Note that the gathered
3431 * frames may themselves have multiple views.
3435 public void gatherViews(GSplitFrame source)
3438 * special handling of explodedGeometry for a view within a SplitFrame: - it
3439 * holds the (x, y) position of the enclosing SplitFrame, and the (width,
3440 * height) of the AlignFrame component
3442 AlignFrame myTopFrame = (AlignFrame) source.getTopFrame();
3443 AlignFrame myBottomFrame = (AlignFrame) source.getBottomFrame();
3444 myTopFrame.viewport.setExplodedGeometry(new Rectangle(source.getX(),
3445 source.getY(), myTopFrame.getWidth(), myTopFrame.getHeight()));
3446 myBottomFrame.viewport
3447 .setExplodedGeometry(new Rectangle(source.getX(), source.getY(),
3448 myBottomFrame.getWidth(), myBottomFrame.getHeight()));
3449 myTopFrame.viewport.setGatherViewsHere(true);
3450 myBottomFrame.viewport.setGatherViewsHere(true);
3451 String topViewId = myTopFrame.viewport.getSequenceSetId();
3452 String bottomViewId = myBottomFrame.viewport.getSequenceSetId();
3454 JInternalFrame[] frames = desktopPane.getAllFrames();
3455 for (JInternalFrame frame : frames)
3457 if (frame instanceof SplitFrame && frame != source)
3459 SplitFrame sf = (SplitFrame) frame;
3460 AlignFrame topFrame = (AlignFrame) sf.getTopFrame();
3461 AlignFrame bottomFrame = (AlignFrame) sf.getBottomFrame();
3462 boolean gatherThis = false;
3463 for (int a = 0; a < topFrame.alignPanels.size(); a++)
3465 AlignmentPanel topPanel = topFrame.alignPanels.get(a);
3466 AlignmentPanel bottomPanel = bottomFrame.alignPanels.get(a);
3467 if (topViewId.equals(topPanel.av.getSequenceSetId())
3468 && bottomViewId.equals(bottomPanel.av.getSequenceSetId()))
3471 topPanel.av.setGatherViewsHere(false);
3472 bottomPanel.av.setGatherViewsHere(false);
3473 topPanel.av.setExplodedGeometry(
3474 new Rectangle(sf.getLocation(), topFrame.getSize()));
3475 bottomPanel.av.setExplodedGeometry(
3476 new Rectangle(sf.getLocation(), bottomFrame.getSize()));
3477 myTopFrame.addAlignmentPanel(topPanel, false);
3478 myBottomFrame.addAlignmentPanel(bottomPanel, false);
3484 topFrame.getAlignPanels().clear();
3485 bottomFrame.getAlignPanels().clear();
3492 * The dust settles...give focus to the tab we did this from.
3494 myTopFrame.setDisplayedView(myTopFrame.alignPanel);
3497 public static groovy.ui.Console getGroovyConsole()
3499 Desktop desktop = Desktop.getInstance();
3500 return desktop == null ? null : desktop.groovyConsole;
3504 * handles the payload of a drag and drop event.
3506 * TODO refactor to desktop utilities class
3509 * - Data source strings extracted from the drop event
3511 * - protocol for each data source extracted from the drop event
3515 * - the payload from the drop event
3518 @SuppressWarnings("unchecked")
3519 public static void transferFromDropTarget(List<Object> files,
3520 List<DataSourceType> protocols, DropTargetDropEvent evt,
3521 Transferable t) throws Exception
3524 // BH 2018 changed List<String> to List<Object> to allow for File from SwingJS
3526 // DataFlavor[] flavors = t.getTransferDataFlavors();
3527 // for (int i = 0; i < flavors.length; i++) {
3528 // if (flavors[i].isFlavorJavaFileListType()) {
3529 // evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
3530 // List<File> list = (List<File>) t.getTransferData(flavors[i]);
3531 // for (int j = 0; j < list.size(); j++) {
3532 // File file = (File) list.get(j);
3533 // byte[] data = getDroppedFileBytes(file);
3534 // fileName.setText(file.getName() + " - " + data.length + " " +
3535 // evt.getLocation());
3536 // JTextArea target = (JTextArea) ((DropTarget) evt.getSource()).getComponent();
3537 // target.setText(new String(data));
3539 // dtde.dropComplete(true);
3544 DataFlavor uriListFlavor = new DataFlavor(
3545 "text/uri-list;class=java.lang.String"), urlFlavour = null;
3548 urlFlavour = new DataFlavor(
3549 "application/x-java-url; class=java.net.URL");
3550 } catch (ClassNotFoundException cfe)
3552 Cache.log.debug("Couldn't instantiate the URL dataflavor.", cfe);
3555 if (urlFlavour != null && t.isDataFlavorSupported(urlFlavour))
3560 java.net.URL url = (URL) t.getTransferData(urlFlavour);
3561 // nb: java 8 osx bug https://bugs.openjdk.java.net/browse/JDK-8156099
3562 // means url may be null.
3565 protocols.add(DataSourceType.URL);
3566 files.add(url.toString());
3567 Cache.log.debug("Drop handled as URL dataflavor "
3568 + files.get(files.size() - 1));
3573 if (Platform.isAMacAndNotJS())
3576 "Please ignore plist error - occurs due to problem with java 8 on OSX");
3579 } catch (Throwable ex)
3581 Cache.log.debug("URL drop handler failed.", ex);
3584 if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
3586 // Works on Windows and MacOSX
3587 Cache.log.debug("Drop handled as javaFileListFlavor");
3588 for (Object file : (List<Object>) t
3589 .getTransferData(DataFlavor.javaFileListFlavor))
3592 protocols.add(DataSourceType.FILE);
3597 // Unix like behaviour
3598 boolean added = false;
3600 if (t.isDataFlavorSupported(uriListFlavor))
3602 Cache.log.debug("Drop handled as uriListFlavor");
3603 // This is used by Unix drag system
3604 data = (String) t.getTransferData(uriListFlavor);
3608 // fallback to text: workaround - on OSX where there's a JVM bug
3609 Cache.log.debug("standard URIListFlavor failed. Trying text");
3610 // try text fallback
3611 DataFlavor textDf = new DataFlavor(
3612 "text/plain;class=java.lang.String");
3613 if (t.isDataFlavorSupported(textDf))
3615 data = (String) t.getTransferData(textDf);
3618 Cache.log.debug("Plain text drop content returned "
3619 + (data == null ? "Null - failed" : data));
3624 while (protocols.size() < files.size())
3626 Cache.log.debug("Adding missing FILE protocol for "
3627 + files.get(protocols.size()));
3628 protocols.add(DataSourceType.FILE);
3630 for (java.util.StringTokenizer st = new java.util.StringTokenizer(
3631 data, "\r\n"); st.hasMoreTokens();)
3634 String s = st.nextToken();
3635 if (s.startsWith("#"))
3637 // the line is a comment (as per the RFC 2483)
3640 java.net.URI uri = new java.net.URI(s);
3641 if (uri.getScheme().toLowerCase().startsWith("http"))
3643 protocols.add(DataSourceType.URL);
3644 files.add(uri.toString());
3648 // otherwise preserve old behaviour: catch all for file objects
3649 java.io.File file = new java.io.File(uri);
3650 protocols.add(DataSourceType.FILE);
3651 files.add(file.toString());
3656 if (Cache.log.isDebugEnabled())
3658 if (data == null || !added)
3661 if (t.getTransferDataFlavors() != null
3662 && t.getTransferDataFlavors().length > 0)
3665 "Couldn't resolve drop data. Here are the supported flavors:");
3666 for (DataFlavor fl : t.getTransferDataFlavors())
3669 "Supported transfer dataflavor: " + fl.toString());
3670 Object df = t.getTransferData(fl);
3673 Cache.log.debug("Retrieves: " + df);
3677 Cache.log.debug("Retrieved nothing");
3683 Cache.log.debug("Couldn't resolve dataflavor for drop: "
3689 if (Platform.isWindowsAndNotJS())
3691 Cache.log.debug("Scanning dropped content for Windows Link Files");
3693 // resolve any .lnk files in the file drop
3694 for (int f = 0; f < files.size(); f++)
3696 String source = files.get(f).toString().toLowerCase();
3697 if (protocols.get(f).equals(DataSourceType.FILE)
3698 && (source.endsWith(".lnk") || source.endsWith(".url")
3699 || source.endsWith(".site")))
3703 Object obj = files.get(f);
3704 File lf = (obj instanceof File ? (File) obj
3705 : new File((String) obj));
3706 // process link file to get a URL
3707 Cache.log.debug("Found potential link file: " + lf);
3708 WindowsShortcut wscfile = new WindowsShortcut(lf);
3709 String fullname = wscfile.getRealFilename();
3710 protocols.set(f, FormatAdapter.checkProtocol(fullname));
3711 files.set(f, fullname);
3712 Cache.log.debug("Parsed real filename " + fullname
3713 + " to extract protocol: " + protocols.get(f));
3714 } catch (Exception ex)
3717 "Couldn't parse " + files.get(f) + " as a link file.",
3726 * Sets the Preferences property for experimental features to True or False
3727 * depending on the state of the controlling menu item
3730 protected void showExperimental_actionPerformed(boolean selected)
3732 Cache.setProperty(EXPERIMENTAL_FEATURES, Boolean.toString(selected));
3736 * Answers a (possibly empty) list of any structure viewer frames (currently for
3737 * either Jmol or Chimera) which are currently open. This may optionally be
3738 * restricted to viewers of a specified class, or viewers linked to a specified
3742 * if not null, only return viewers linked to this panel
3743 * @param structureViewerClass
3744 * if not null, only return viewers of this class
3747 public List<StructureViewerBase> getStructureViewers(
3748 AlignmentPanel apanel,
3749 Class<? extends StructureViewerBase> structureViewerClass)
3751 List<StructureViewerBase> result = new ArrayList<>();
3752 JInternalFrame[] frames = getAllFrames();
3754 for (JInternalFrame frame : frames)
3756 if (frame instanceof StructureViewerBase)
3758 if (structureViewerClass == null
3759 || structureViewerClass.isInstance(frame))
3762 || ((StructureViewerBase) frame).isLinkedWith(apanel))
3764 result.add((StructureViewerBase) frame);