2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
5 * This file is part of Jalview.
7 * Jalview is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation, either version 3
10 * of the License, or (at your option) any later version.
12 * Jalview is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19 * The Jalview Authors are detailed in the 'AUTHORS' file.
23 import jalview.api.AlignViewportI;
24 import jalview.api.AlignmentViewPanel;
25 import jalview.bin.Cache;
26 import jalview.bin.Jalview;
27 import jalview.gui.ImageExporter.ImageWriterI;
28 import jalview.io.BackupFiles;
29 import jalview.io.DataSourceType;
30 import jalview.io.FileFormat;
31 import jalview.io.FileFormatException;
32 import jalview.io.FileFormatI;
33 import jalview.io.FileFormats;
34 import jalview.io.FileLoader;
35 import jalview.io.FormatAdapter;
36 import jalview.io.IdentifyFile;
37 import jalview.io.JalviewFileChooser;
38 import jalview.io.JalviewFileView;
39 import jalview.jbgui.GSplitFrame;
40 import jalview.jbgui.GStructureViewer;
41 import jalview.project.Jalview2XML;
42 import jalview.structure.StructureSelectionManager;
43 import jalview.urls.IdOrgSettings;
44 import jalview.util.BrowserLauncher;
45 import jalview.util.ImageMaker.TYPE;
46 import jalview.util.MessageManager;
47 import jalview.util.Platform;
48 import jalview.util.UrlConstants;
49 import jalview.viewmodel.AlignmentViewport;
50 import jalview.ws.params.ParamManager;
51 import jalview.ws.utils.UrlDownloadClient;
53 import java.awt.BorderLayout;
54 import java.awt.Color;
55 import java.awt.Dimension;
56 import java.awt.FontMetrics;
57 import java.awt.Graphics;
58 import java.awt.GridLayout;
59 import java.awt.Point;
60 import java.awt.Rectangle;
61 import java.awt.Toolkit;
62 import java.awt.Window;
63 import java.awt.datatransfer.Clipboard;
64 import java.awt.datatransfer.ClipboardOwner;
65 import java.awt.datatransfer.DataFlavor;
66 import java.awt.datatransfer.Transferable;
67 import java.awt.dnd.DnDConstants;
68 import java.awt.dnd.DropTargetDragEvent;
69 import java.awt.dnd.DropTargetDropEvent;
70 import java.awt.dnd.DropTargetEvent;
71 import java.awt.dnd.DropTargetListener;
72 import java.awt.event.ActionEvent;
73 import java.awt.event.ActionListener;
74 import java.awt.event.InputEvent;
75 import java.awt.event.KeyEvent;
76 import java.awt.event.MouseAdapter;
77 import java.awt.event.MouseEvent;
78 import java.awt.event.WindowAdapter;
79 import java.awt.event.WindowEvent;
80 import java.beans.PropertyChangeEvent;
81 import java.beans.PropertyChangeListener;
82 import java.io.BufferedInputStream;
84 import java.io.FileOutputStream;
85 import java.io.FileWriter;
86 import java.io.IOException;
88 import java.util.ArrayList;
89 import java.util.HashMap;
90 import java.util.Hashtable;
91 import java.util.List;
92 import java.util.ListIterator;
93 import java.util.Vector;
94 import java.util.concurrent.ExecutorService;
95 import java.util.concurrent.Executors;
96 import java.util.concurrent.Semaphore;
98 import javax.swing.AbstractAction;
99 import javax.swing.Action;
100 import javax.swing.ActionMap;
101 import javax.swing.Box;
102 import javax.swing.BoxLayout;
103 import javax.swing.DefaultDesktopManager;
104 import javax.swing.DesktopManager;
105 import javax.swing.InputMap;
106 import javax.swing.JButton;
107 import javax.swing.JCheckBox;
108 import javax.swing.JComboBox;
109 import javax.swing.JComponent;
110 import javax.swing.JDesktopPane;
111 import javax.swing.JInternalFrame;
112 import javax.swing.JLabel;
113 import javax.swing.JMenuItem;
114 import javax.swing.JPanel;
115 import javax.swing.JPopupMenu;
116 import javax.swing.JProgressBar;
117 import javax.swing.JTextField;
118 import javax.swing.KeyStroke;
119 import javax.swing.SwingUtilities;
120 import javax.swing.event.HyperlinkEvent;
121 import javax.swing.event.HyperlinkEvent.EventType;
122 import javax.swing.event.InternalFrameAdapter;
123 import javax.swing.event.InternalFrameEvent;
124 import javax.swing.event.MenuEvent;
125 import javax.swing.event.MenuListener;
127 import org.stackoverflowusers.file.WindowsShortcut;
134 * @version $Revision: 1.155 $
136 public class Desktop extends jalview.jbgui.GDesktop
137 implements DropTargetListener, ClipboardOwner, IProgressIndicator,
138 jalview.api.StructureSelectionManagerProvider
140 private static int DEFAULT_MIN_WIDTH = 300;
142 private static int DEFAULT_MIN_HEIGHT = 250;
144 private static int ALIGN_FRAME_DEFAULT_MIN_WIDTH = 600;
146 private static int ALIGN_FRAME_DEFAULT_MIN_HEIGHT = 70;
148 private static final String EXPERIMENTAL_FEATURES = "EXPERIMENTAL_FEATURES";
150 protected static final String CONFIRM_KEYBOARD_QUIT = "CONFIRM_KEYBOARD_QUIT";
152 public static HashMap<String, FileWriter> savingFiles = new HashMap<>();
154 private JalviewChangeSupport changeSupport = new JalviewChangeSupport();
157 * news reader - null if it was never started.
159 private BlogReader jvnews = null;
161 private File projectFile;
165 * @see jalview.gui.JalviewChangeSupport#addJalviewPropertyChangeListener(java.beans.PropertyChangeListener)
167 public void addJalviewPropertyChangeListener(
168 PropertyChangeListener listener)
170 changeSupport.addJalviewPropertyChangeListener(listener);
174 * @param propertyName
176 * @see jalview.gui.JalviewChangeSupport#addJalviewPropertyChangeListener(java.lang.String,
177 * java.beans.PropertyChangeListener)
179 public void addJalviewPropertyChangeListener(String propertyName,
180 PropertyChangeListener listener)
182 changeSupport.addJalviewPropertyChangeListener(propertyName, listener);
186 * @param propertyName
188 * @see jalview.gui.JalviewChangeSupport#removeJalviewPropertyChangeListener(java.lang.String,
189 * java.beans.PropertyChangeListener)
191 public void removeJalviewPropertyChangeListener(String propertyName,
192 PropertyChangeListener listener)
194 changeSupport.removeJalviewPropertyChangeListener(propertyName,
198 /** Singleton Desktop instance */
199 public static Desktop instance;
201 public static MyDesktopPane desktop;
203 public static MyDesktopPane getDesktop()
205 // BH 2018 could use currentThread() here as a reference to a
206 // Hashtable<Thread, MyDesktopPane> in JavaScript
210 static int openFrameCount = 0;
212 static final int xOffset = 30;
214 static final int yOffset = 30;
216 public static jalview.ws.jws1.Discoverer discoverer;
218 public static Object[] jalviewClipboard;
220 public static boolean internalCopy = false;
222 static int fileLoadingCount = 0;
224 class MyDesktopManager implements DesktopManager
227 private DesktopManager delegate;
229 public MyDesktopManager(DesktopManager delegate)
231 this.delegate = delegate;
235 public void activateFrame(JInternalFrame f)
239 delegate.activateFrame(f);
240 } catch (NullPointerException npe)
242 Point p = getMousePosition();
243 instance.showPasteMenu(p.x, p.y);
248 public void beginDraggingFrame(JComponent f)
250 delegate.beginDraggingFrame(f);
254 public void beginResizingFrame(JComponent f, int direction)
256 delegate.beginResizingFrame(f, direction);
260 public void closeFrame(JInternalFrame f)
262 delegate.closeFrame(f);
266 public void deactivateFrame(JInternalFrame f)
268 delegate.deactivateFrame(f);
272 public void deiconifyFrame(JInternalFrame f)
274 delegate.deiconifyFrame(f);
278 public void dragFrame(JComponent f, int newX, int newY)
284 delegate.dragFrame(f, newX, newY);
288 public void endDraggingFrame(JComponent f)
290 delegate.endDraggingFrame(f);
295 public void endResizingFrame(JComponent f)
297 delegate.endResizingFrame(f);
302 public void iconifyFrame(JInternalFrame f)
304 delegate.iconifyFrame(f);
308 public void maximizeFrame(JInternalFrame f)
310 delegate.maximizeFrame(f);
314 public void minimizeFrame(JInternalFrame f)
316 delegate.minimizeFrame(f);
320 public void openFrame(JInternalFrame f)
322 delegate.openFrame(f);
326 public void resizeFrame(JComponent f, int newX, int newY, int newWidth,
333 delegate.resizeFrame(f, newX, newY, newWidth, newHeight);
337 public void setBoundsForFrame(JComponent f, int newX, int newY,
338 int newWidth, int newHeight)
340 delegate.setBoundsForFrame(f, newX, newY, newWidth, newHeight);
343 // All other methods, simply delegate
348 * Creates a new Desktop object.
354 * A note to implementors. It is ESSENTIAL that any activities that might
355 * block are spawned off as threads rather than waited for during this
359 if (!Platform.isJS())
361 doVamsasClientCheck();
364 doConfigureStructurePrefs();
365 setTitle("Jalview " + jalview.bin.Cache.getProperty("VERSION"));
367 if (!Platform.isAMac())
369 // this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
373 this.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
379 APQHandlers.setAPQHandlers(this);
380 } catch (Exception e)
382 System.out.println("Exception when trying to set APQHandlers");
384 } catch (Throwable t)
386 System.out.println("Throwable when trying to set APQHandlers");
391 addWindowListener(new WindowAdapter()
395 public void windowClosing(WindowEvent ev)
401 boolean selmemusage = jalview.bin.Cache.getDefault("SHOW_MEMUSAGE",
404 boolean showjconsole = jalview.bin.Cache.getDefault("SHOW_JAVA_CONSOLE",
406 desktop = new MyDesktopPane(selmemusage);
408 showMemusage.setSelected(selmemusage);
409 desktop.setBackground(Color.white);
411 getContentPane().setLayout(new BorderLayout());
412 // alternate config - have scrollbars - see notes in JAL-153
413 // JScrollPane sp = new JScrollPane();
414 // sp.getViewport().setView(desktop);
415 // getContentPane().add(sp, BorderLayout.CENTER);
417 // BH 2018 - just an experiment to try unclipped JInternalFrames.
420 getRootPane().putClientProperty("swingjs.overflow.hidden", "false");
423 getContentPane().add(desktop, BorderLayout.CENTER);
424 desktop.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
426 // This line prevents Windows Look&Feel resizing all new windows to maximum
427 // if previous window was maximised
428 desktop.setDesktopManager(new MyDesktopManager(
429 (Platform.isWindowsAndNotJS() ? new DefaultDesktopManager()
430 : Platform.isAMacAndNotJS()
431 ? new AquaInternalFrameManager(
432 desktop.getDesktopManager())
433 : desktop.getDesktopManager())));
435 Rectangle dims = getLastKnownDimensions("");
442 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
443 int xPos = Math.max(5, (screenSize.width - 900) / 2);
444 int yPos = Math.max(5, (screenSize.height - 650) / 2);
445 setBounds(xPos, yPos, 900, 650);
448 if (!Platform.isJS())
456 jconsole = new Console(this, showjconsole);
457 // add essential build information
458 jconsole.setHeader("Jalview Version: "
459 + jalview.bin.Cache.getProperty("VERSION") + "\n"
460 + "Jalview Installation: "
461 + jalview.bin.Cache.getDefault("INSTALLATION", "unknown")
462 + "\n" + "Build Date: "
463 + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown") + "\n"
464 + "Java version: " + System.getProperty("java.version") + "\n"
465 + System.getProperty("os.arch") + " "
466 + System.getProperty("os.name") + " "
467 + System.getProperty("os.version")
468 + (jalview.bin.Cache.getProperty("VERSION").equals("DEVELOPMENT")
470 + System.getProperty(
472 + File.separator + "bin"
473 + File.separator + "java"
476 showConsole(showjconsole);
478 showNews.setVisible(false);
480 experimentalFeatures.setSelected(showExperimental());
482 getIdentifiersOrgData();
486 // Spawn a thread that shows the splashscreen
488 SwingUtilities.invokeLater(new Runnable()
497 // Thread off a new instance of the file chooser - this reduces the time
498 // it takes to open it later on.
499 new Thread(new Runnable()
504 Cache.log.debug("Filechooser init thread started.");
505 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
506 JalviewFileChooser.forRead(Cache.getProperty("LAST_DIRECTORY"),
508 Cache.log.debug("Filechooser init thread finished.");
511 // Add the service change listener
512 changeSupport.addJalviewPropertyChangeListener("services",
513 new PropertyChangeListener()
517 public void propertyChange(PropertyChangeEvent evt)
519 Cache.log.debug("Firing service changed event for "
520 + evt.getNewValue());
521 JalviewServicesChanged(evt);
528 this.setDropTarget(new java.awt.dnd.DropTarget(desktop, this));
530 this.addWindowListener(new WindowAdapter()
533 public void windowClosing(WindowEvent evt)
540 this.addMouseListener(ma = new MouseAdapter()
543 public void mousePressed(MouseEvent evt)
545 if (evt.isPopupTrigger()) // Mac
547 showPasteMenu(evt.getX(), evt.getY());
552 public void mouseReleased(MouseEvent evt)
554 if (evt.isPopupTrigger()) // Windows
556 showPasteMenu(evt.getX(), evt.getY());
560 desktop.addMouseListener(ma);
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 client.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 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();
658 Desktop.instance.setProgressBar(
659 MessageManager.getString("status.refreshing_news"), now);
660 jvnews.refreshNews();
661 Desktop.instance.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 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 (instance == null || (System.getProperty("java.awt.headless") != null
909 && System.getProperty("java.awt.headless").equals("true")))
918 frame.setMinimumSize(
919 new Dimension(DEFAULT_MIN_WIDTH, DEFAULT_MIN_HEIGHT));
921 // Set default dimension for Alignment Frame window.
922 // The Alignment Frame window could be added from a number of places,
924 // I did this here in order not to miss out on any Alignment frame.
925 if (frame instanceof AlignFrame)
927 frame.setMinimumSize(new Dimension(ALIGN_FRAME_DEFAULT_MIN_WIDTH,
928 ALIGN_FRAME_DEFAULT_MIN_HEIGHT));
932 frame.setVisible(makeVisible);
933 frame.setClosable(true);
934 frame.setResizable(resizable);
935 frame.setMaximizable(resizable);
936 frame.setIconifiable(resizable);
937 frame.setOpaque(Platform.isJS());
939 if (frame.getX() < 1 && frame.getY() < 1)
941 frame.setLocation(xOffset * openFrameCount,
942 yOffset * ((openFrameCount - 1) % 10) + yOffset);
946 * add an entry for the new frame in the Window menu
947 * (and remove it when the frame is closed)
949 final JMenuItem menuItem = new JMenuItem(title);
950 frame.addInternalFrameListener(new InternalFrameAdapter()
953 public void internalFrameActivated(InternalFrameEvent evt)
955 JInternalFrame itf = desktop.getSelectedFrame();
958 if (itf instanceof AlignFrame)
960 Jalview.setCurrentAlignFrame((AlignFrame) itf);
967 public void internalFrameClosed(InternalFrameEvent evt)
969 PaintRefresher.RemoveComponent(frame);
972 * defensive check to prevent frames being
973 * added half off the window
975 if (openFrameCount > 0)
981 * ensure no reference to alignFrame retained by menu item listener
983 if (menuItem.getActionListeners().length > 0)
985 menuItem.removeActionListener(menuItem.getActionListeners()[0]);
987 windowMenu.remove(menuItem);
991 menuItem.addActionListener(new ActionListener()
994 public void actionPerformed(ActionEvent e)
998 frame.setSelected(true);
999 frame.setIcon(false);
1000 } catch (java.beans.PropertyVetoException ex)
1002 // System.err.println(ex.toString());
1007 setKeyBindings(frame);
1011 windowMenu.add(menuItem);
1016 frame.setSelected(true);
1017 frame.requestFocus();
1018 } catch (java.beans.PropertyVetoException ve)
1020 } catch (java.lang.ClassCastException cex)
1023 "Squashed a possible GUI implementation error. If you can recreate this, please look at http://issues.jalview.org/browse/JAL-869",
1029 * Add key bindings to a JInternalFrame so that Ctrl-W and Cmd-W will close the
1034 private static void setKeyBindings(JInternalFrame frame)
1036 @SuppressWarnings("serial")
1037 final Action closeAction = new AbstractAction()
1040 public void actionPerformed(ActionEvent e)
1047 * set up key bindings for Ctrl-W and Cmd-W, with the same (Close) action
1049 KeyStroke ctrlWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
1050 InputEvent.CTRL_DOWN_MASK);
1051 KeyStroke cmdWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
1052 jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx());
1054 InputMap inputMap = frame
1055 .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
1056 String ctrlW = ctrlWKey.toString();
1057 inputMap.put(ctrlWKey, ctrlW);
1058 inputMap.put(cmdWKey, ctrlW);
1060 ActionMap actionMap = frame.getActionMap();
1061 actionMap.put(ctrlW, closeAction);
1065 public void lostOwnership(Clipboard clipboard, Transferable contents)
1069 Desktop.jalviewClipboard = null;
1072 internalCopy = false;
1076 public void dragEnter(DropTargetDragEvent evt)
1081 public void dragExit(DropTargetEvent evt)
1086 public void dragOver(DropTargetDragEvent evt)
1091 public void dropActionChanged(DropTargetDragEvent evt)
1102 public void drop(DropTargetDropEvent evt)
1104 boolean success = true;
1105 // JAL-1552 - acceptDrop required before getTransferable call for
1106 // Java's Transferable for native dnd
1107 evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
1108 Transferable t = evt.getTransferable();
1109 List<Object> files = new ArrayList<>();
1110 List<DataSourceType> protocols = new ArrayList<>();
1114 Desktop.transferFromDropTarget(files, protocols, evt, t);
1115 } catch (Exception e)
1117 e.printStackTrace();
1125 for (int i = 0; i < files.size(); i++)
1127 // BH 2018 File or String
1128 Object file = files.get(i);
1129 String fileName = file.toString();
1130 DataSourceType protocol = (protocols == null)
1131 ? DataSourceType.FILE
1133 FileFormatI format = null;
1135 if (fileName.endsWith(".jar"))
1137 format = FileFormat.Jalview;
1142 format = new IdentifyFile().identify(file, protocol);
1144 if (file instanceof File)
1146 Platform.cacheFileData((File) file);
1148 new FileLoader().LoadFile(null, file, protocol, format);
1151 } catch (Exception ex)
1156 evt.dropComplete(success); // need this to ensure input focus is properly
1157 // transfered to any new windows created
1167 public void inputLocalFileMenuItem_actionPerformed(AlignViewport viewport)
1169 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
1170 JalviewFileChooser chooser = JalviewFileChooser
1171 .forRead(Cache.getProperty("LAST_DIRECTORY"), fileFormat, true);
1173 chooser.setFileView(new JalviewFileView());
1174 chooser.setDialogTitle(
1175 MessageManager.getString("label.open_local_file"));
1176 chooser.setToolTipText(MessageManager.getString("action.open"));
1178 chooser.setResponseHandler(0, new Runnable()
1183 File selectedFile = chooser.getSelectedFile();
1184 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1186 FileFormatI format = chooser.getSelectedFormat();
1189 * Call IdentifyFile to verify the file contains what its extension implies.
1190 * Skip this step for dynamically added file formats, because
1191 * IdentifyFile does not know how to recognise them.
1193 if (FileFormats.getInstance().isIdentifiable(format))
1197 format = new IdentifyFile().identify(selectedFile,
1198 DataSourceType.FILE);
1199 } catch (FileFormatException e)
1201 // format = null; //??
1205 new FileLoader().LoadFile(viewport, selectedFile,
1206 DataSourceType.FILE, format);
1209 chooser.showOpenDialog(this);
1213 * Shows a dialog for input of a URL at which to retrieve alignment data
1218 public void inputURLMenuItem_actionPerformed(AlignViewport viewport)
1220 // This construct allows us to have a wider textfield
1222 JLabel label = new JLabel(
1223 MessageManager.getString("label.input_file_url"));
1225 JPanel panel = new JPanel(new GridLayout(2, 1));
1229 * the URL to fetch is
1230 * Java: an editable combobox with history
1231 * JS: (pending JAL-3038) a plain text field
1234 String urlBase = "http://www.";
1235 if (Platform.isJS())
1237 history = new JTextField(urlBase, 35);
1246 JComboBox<String> asCombo = new JComboBox<>();
1247 asCombo.setPreferredSize(new Dimension(400, 20));
1248 asCombo.setEditable(true);
1249 asCombo.addItem(urlBase);
1250 String historyItems = Cache.getProperty("RECENT_URL");
1251 if (historyItems != null)
1253 for (String token : historyItems.split("\\t"))
1255 asCombo.addItem(token);
1262 Object[] options = new Object[] { MessageManager.getString("action.ok"),
1263 MessageManager.getString("action.cancel") };
1264 Runnable action = new Runnable()
1269 @SuppressWarnings("unchecked")
1270 String url = (history instanceof JTextField
1271 ? ((JTextField) history).getText()
1272 : ((JComboBox<String>) history).getSelectedItem()
1275 if (url.toLowerCase().endsWith(".jar"))
1277 if (viewport != null)
1279 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1280 FileFormat.Jalview);
1284 new FileLoader().LoadFile(url, DataSourceType.URL,
1285 FileFormat.Jalview);
1290 FileFormatI format = null;
1293 format = new IdentifyFile().identify(url, DataSourceType.URL);
1294 } catch (FileFormatException e)
1296 // TODO revise error handling, distinguish between
1297 // URL not found and response not valid
1302 String msg = MessageManager
1303 .formatMessage("label.couldnt_locate", url);
1304 JvOptionPane.showInternalMessageDialog(Desktop.desktop, msg,
1305 MessageManager.getString("label.url_not_found"),
1306 JvOptionPane.WARNING_MESSAGE);
1311 if (viewport != null)
1313 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1318 new FileLoader().LoadFile(url, DataSourceType.URL, format);
1323 String dialogOption = MessageManager
1324 .getString("label.input_alignment_from_url");
1325 JvOptionPane.newOptionDialog(desktop).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 //System.out.println("********** Desktop.quit()");
1357 //System.out.println(savingFiles.toString());
1358 Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
1359 jalview.bin.Cache.setProperty("SCREENGEOMETRY_WIDTH",
1361 jalview.bin.Cache.setProperty("SCREENGEOMETRY_HEIGHT",
1362 screen.height + "");
1363 storeLastKnownDimensions("", new Rectangle(getBounds().x, getBounds().y,
1364 getWidth(), getHeight()));
1366 if (jconsole != null)
1368 storeLastKnownDimensions("JAVA_CONSOLE_", jconsole.getBounds());
1369 jconsole.stopConsole();
1373 storeLastKnownDimensions("JALVIEW_RSS_WINDOW_", jvnews.getBounds());
1376 if (dialogExecutor != null)
1378 dialogExecutor.shutdownNow();
1380 closeAll_actionPerformed(null);
1382 if (groovyConsole != null)
1384 // suppress a possible repeat prompt to save script
1385 groovyConsole.setDirty(false);
1386 groovyConsole.exit();
1391 private void storeLastKnownDimensions(String string, Rectangle jc)
1393 jalview.bin.Cache.log.debug("Storing last known dimensions for "
1394 + string + ": x:" + jc.x + " y:" + jc.y + " width:" + jc.width
1395 + " height:" + jc.height);
1397 jalview.bin.Cache.setProperty(string + "SCREEN_X", jc.x + "");
1398 jalview.bin.Cache.setProperty(string + "SCREEN_Y", jc.y + "");
1399 jalview.bin.Cache.setProperty(string + "SCREEN_WIDTH", jc.width + "");
1400 jalview.bin.Cache.setProperty(string + "SCREEN_HEIGHT", jc.height + "");
1410 public void aboutMenuItem_actionPerformed(ActionEvent e)
1412 // StringBuffer message = getAboutMessage(false);
1413 // JvOptionPane.showInternalMessageDialog(Desktop.desktop,
1415 // message.toString(), "About Jalview", JvOptionPane.INFORMATION_MESSAGE);
1416 new Thread(new Runnable()
1421 new SplashScreen(true);
1426 public StringBuffer getAboutMessage(boolean shortv)
1428 StringBuffer message = new StringBuffer();
1429 message.append("<html>");
1432 message.append("<h1><strong>Version: "
1433 + jalview.bin.Cache.getProperty("VERSION")
1434 + "</strong></h1>");
1435 message.append("<strong>Last Updated: <em>"
1436 + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown")
1437 + "</em></strong>");
1443 message.append("<strong>Version "
1444 + jalview.bin.Cache.getProperty("VERSION")
1445 + "; last updated: "
1446 + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown"));
1449 if (jalview.bin.Cache.getDefault("LATEST_VERSION", "Checking")
1450 .equals("Checking"))
1452 message.append("<br>...Checking latest version...</br>");
1454 else if (!jalview.bin.Cache.getDefault("LATEST_VERSION", "Checking")
1455 .equals(jalview.bin.Cache.getProperty("VERSION")))
1457 boolean red = false;
1458 if (jalview.bin.Cache.getProperty("VERSION").toLowerCase()
1459 .indexOf("automated build") == -1)
1462 // Displayed when code version and jnlp version do not match and code
1463 // version is not a development build
1464 message.append("<div style=\"color: #FF0000;font-style: bold;\">");
1467 message.append("<br>!! Version "
1468 + jalview.bin.Cache.getDefault("LATEST_VERSION",
1470 + " is available for download from "
1471 + jalview.bin.Cache.getDefault("www.jalview.org",
1472 "http://www.jalview.org")
1476 message.append("</div>");
1479 message.append("<br>Authors: " + jalview.bin.Cache.getDefault(
1481 "The Jalview Authors (See AUTHORS file for current list)")
1482 + "<br><br>Development managed by The Barton Group, University of Dundee, Scotland, UK.<br>"
1483 + "<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"
1484 + "<br><br>If you use Jalview, please cite:"
1485 + "<br>Waterhouse, A.M., Procter, J.B., Martin, D.M.A, Clamp, M. and Barton, G. J. (2009)"
1486 + "<br>Jalview Version 2 - a multiple sequence alignment editor and analysis workbench"
1487 + "<br>Bioinformatics doi: 10.1093/bioinformatics/btp033"
1493 * Action on requesting Help documentation
1496 public void documentationMenuItem_actionPerformed()
1500 if (Platform.isJS())
1502 BrowserLauncher.openURL("http://www.jalview.org/help.html");
1511 Help.showHelpWindow();
1513 } catch (Exception ex)
1515 System.err.println("Error opening help: " + ex.getMessage());
1520 public void closeAll_actionPerformed(ActionEvent e)
1522 // TODO show a progress bar while closing?
1523 JInternalFrame[] frames = desktop.getAllFrames();
1524 for (int i = 0; i < frames.length; i++)
1528 frames[i].setClosed(true);
1529 } catch (java.beans.PropertyVetoException ex)
1533 Jalview.setCurrentAlignFrame(null);
1534 System.out.println("ALL CLOSED");
1535 if (v_client != null)
1537 // TODO clear binding to vamsas document objects on close_all
1541 * reset state of singleton objects as appropriate (clear down session state
1542 * when all windows are closed)
1544 StructureSelectionManager ssm = StructureSelectionManager
1545 .getStructureSelectionManager(this);
1553 public void raiseRelated_actionPerformed(ActionEvent e)
1555 reorderAssociatedWindows(false, false);
1559 public void minimizeAssociated_actionPerformed(ActionEvent e)
1561 reorderAssociatedWindows(true, false);
1564 void closeAssociatedWindows()
1566 reorderAssociatedWindows(false, true);
1572 * @seejalview.jbgui.GDesktop#garbageCollect_actionPerformed(java.awt.event.
1576 protected void garbageCollect_actionPerformed(ActionEvent e)
1578 // We simply collect the garbage
1579 jalview.bin.Cache.log.debug("Collecting garbage...");
1581 jalview.bin.Cache.log.debug("Finished garbage collection.");
1588 * jalview.jbgui.GDesktop#showMemusage_actionPerformed(java.awt.event.ActionEvent
1592 protected void showMemusage_actionPerformed(ActionEvent e)
1594 desktop.showMemoryUsage(showMemusage.isSelected());
1601 * jalview.jbgui.GDesktop#showConsole_actionPerformed(java.awt.event.ActionEvent
1605 protected void showConsole_actionPerformed(ActionEvent e)
1607 showConsole(showConsole.isSelected());
1610 Console jconsole = null;
1613 * control whether the java console is visible or not
1617 void showConsole(boolean selected)
1619 // TODO: decide if we should update properties file
1620 if (jconsole != null) // BH 2018
1622 showConsole.setSelected(selected);
1623 Cache.setProperty("SHOW_JAVA_CONSOLE",
1624 Boolean.valueOf(selected).toString());
1625 jconsole.setVisible(selected);
1629 void reorderAssociatedWindows(boolean minimize, boolean close)
1631 JInternalFrame[] frames = desktop.getAllFrames();
1632 if (frames == null || frames.length < 1)
1637 AlignmentViewport source = null, target = null;
1638 if (frames[0] instanceof AlignFrame)
1640 source = ((AlignFrame) frames[0]).getCurrentView();
1642 else if (frames[0] instanceof TreePanel)
1644 source = ((TreePanel) frames[0]).getViewPort();
1646 else if (frames[0] instanceof PCAPanel)
1648 source = ((PCAPanel) frames[0]).av;
1650 else if (frames[0].getContentPane() instanceof PairwiseAlignPanel)
1652 source = ((PairwiseAlignPanel) frames[0].getContentPane()).av;
1657 for (int i = 0; i < frames.length; i++)
1660 if (frames[i] == null)
1664 if (frames[i] instanceof AlignFrame)
1666 target = ((AlignFrame) frames[i]).getCurrentView();
1668 else if (frames[i] instanceof TreePanel)
1670 target = ((TreePanel) frames[i]).getViewPort();
1672 else if (frames[i] instanceof PCAPanel)
1674 target = ((PCAPanel) frames[i]).av;
1676 else if (frames[i].getContentPane() instanceof PairwiseAlignPanel)
1678 target = ((PairwiseAlignPanel) frames[i].getContentPane()).av;
1681 if (source == target)
1687 frames[i].setClosed(true);
1691 frames[i].setIcon(minimize);
1694 frames[i].toFront();
1698 } catch (java.beans.PropertyVetoException ex)
1713 protected void preferences_actionPerformed(ActionEvent e)
1719 * Prompts the user to choose a file and then saves the Jalview state as a
1720 * Jalview project file
1723 public void saveState_actionPerformed()
1725 saveState_actionPerformed(false);
1728 public void saveState_actionPerformed(boolean saveAs)
1730 java.io.File projectFile = getProjectFile();
1731 // autoSave indicates we already have a file and don't need to ask
1732 boolean autoSave = projectFile != null && !saveAs
1733 && BackupFiles.getEnabled();
1735 // System.out.println("autoSave="+autoSave+", projectFile='"+projectFile+"',
1736 // saveAs="+saveAs+", Backups
1737 // "+(BackupFiles.getEnabled()?"enabled":"disabled"));
1739 boolean approveSave = false;
1742 JalviewFileChooser chooser = new JalviewFileChooser("jvp",
1745 chooser.setFileView(new JalviewFileView());
1746 chooser.setDialogTitle(MessageManager.getString("label.save_state"));
1748 int value = chooser.showSaveDialog(this);
1750 if (value == JalviewFileChooser.APPROVE_OPTION)
1752 projectFile = chooser.getSelectedFile();
1753 setProjectFile(projectFile);
1758 if (approveSave || autoSave)
1760 final Desktop me = this;
1761 final java.io.File chosenFile = projectFile;
1762 new Thread(new Runnable()
1767 // TODO: refactor to Jalview desktop session controller action.
1768 setProgressBar(MessageManager.formatMessage(
1769 "label.saving_jalview_project", new Object[]
1770 { chosenFile.getName() }), chosenFile.hashCode());
1771 jalview.bin.Cache.setProperty("LAST_DIRECTORY",
1772 chosenFile.getParent());
1773 // TODO catch and handle errors for savestate
1774 // TODO prevent user from messing with the Desktop whilst we're saving
1777 boolean doBackup = BackupFiles.getEnabled();
1778 BackupFiles backupfiles = doBackup ? new BackupFiles(chosenFile) : null;
1780 new Jalview2XML().saveState(doBackup ? backupfiles.getTempFile() : chosenFile);
1784 backupfiles.setWriteSuccess(true);
1785 backupfiles.rollBackupsAndRenameTempFile();
1787 } catch (OutOfMemoryError oom)
1789 new OOMWarning("Whilst saving current state to "
1790 + chosenFile.getName(), oom);
1791 } catch (Exception ex)
1793 Cache.log.error("Problems whilst trying to save to "
1794 + chosenFile.getName(), ex);
1795 JvOptionPane.showMessageDialog(me,
1796 MessageManager.formatMessage(
1797 "label.error_whilst_saving_current_state_to",
1799 { chosenFile.getName() }),
1800 MessageManager.getString("label.couldnt_save_project"),
1801 JvOptionPane.WARNING_MESSAGE);
1803 setProgressBar(null, chosenFile.hashCode());
1810 public void saveAsState_actionPerformed(ActionEvent e)
1812 saveState_actionPerformed(true);
1815 private void setProjectFile(File choice)
1817 this.projectFile = choice;
1820 public File getProjectFile()
1822 return this.projectFile;
1826 * Shows a file chooser dialog and tries to read in the selected file as a
1830 public void loadState_actionPerformed()
1832 final String[] suffix = new String[] { "jvp", "jar" };
1833 final String[] desc = new String[] { "Jalview Project",
1834 "Jalview Project (old)" };
1835 JalviewFileChooser chooser = new JalviewFileChooser(
1836 Cache.getProperty("LAST_DIRECTORY"), suffix, desc,
1837 "Jalview Project", true, true); // last two booleans: allFiles,
1839 chooser.setFileView(new JalviewFileView());
1840 chooser.setDialogTitle(MessageManager.getString("label.restore_state"));
1841 chooser.setResponseHandler(0, new Runnable()
1846 File selectedFile = chooser.getSelectedFile();
1847 setProjectFile(selectedFile);
1848 String choice = selectedFile.getAbsolutePath();
1849 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1850 new Thread(new Runnable()
1857 new Jalview2XML().loadJalviewAlign(choice);
1858 } catch (OutOfMemoryError oom)
1860 new OOMWarning("Whilst loading project from " + choice, oom);
1861 } catch (Exception ex)
1864 "Problems whilst loading project from " + choice, ex);
1865 JvOptionPane.showMessageDialog(Desktop.desktop,
1866 MessageManager.formatMessage(
1867 "label.error_whilst_loading_project_from",
1870 MessageManager.getString("label.couldnt_load_project"),
1871 JvOptionPane.WARNING_MESSAGE);
1878 chooser.showOpenDialog(this);
1882 public void inputSequence_actionPerformed(ActionEvent e)
1884 new SequenceFetcher(this);
1887 JPanel progressPanel;
1889 ArrayList<JPanel> fileLoadingPanels = new ArrayList<>();
1891 public void startLoading(final Object fileName)
1893 if (fileLoadingCount == 0)
1895 fileLoadingPanels.add(addProgressPanel(MessageManager
1896 .formatMessage("label.loading_file", new Object[]
1902 private JPanel addProgressPanel(String string)
1904 if (progressPanel == null)
1906 progressPanel = new JPanel(new GridLayout(1, 1));
1907 totalProgressCount = 0;
1908 instance.getContentPane().add(progressPanel, BorderLayout.SOUTH);
1910 JPanel thisprogress = new JPanel(new BorderLayout(10, 5));
1911 JProgressBar progressBar = new JProgressBar();
1912 progressBar.setIndeterminate(true);
1914 thisprogress.add(new JLabel(string), BorderLayout.WEST);
1916 thisprogress.add(progressBar, BorderLayout.CENTER);
1917 progressPanel.add(thisprogress);
1918 ((GridLayout) progressPanel.getLayout()).setRows(
1919 ((GridLayout) progressPanel.getLayout()).getRows() + 1);
1920 ++totalProgressCount;
1921 instance.validate();
1922 return thisprogress;
1925 int totalProgressCount = 0;
1927 private void removeProgressPanel(JPanel progbar)
1929 if (progressPanel != null)
1931 synchronized (progressPanel)
1933 progressPanel.remove(progbar);
1934 GridLayout gl = (GridLayout) progressPanel.getLayout();
1935 gl.setRows(gl.getRows() - 1);
1936 if (--totalProgressCount < 1)
1938 this.getContentPane().remove(progressPanel);
1939 progressPanel = null;
1946 public void stopLoading()
1949 if (fileLoadingCount < 1)
1951 while (fileLoadingPanels.size() > 0)
1953 removeProgressPanel(fileLoadingPanels.remove(0));
1955 fileLoadingPanels.clear();
1956 fileLoadingCount = 0;
1961 public static int getViewCount(String alignmentId)
1963 AlignmentViewport[] aps = getViewports(alignmentId);
1964 return (aps == null) ? 0 : aps.length;
1969 * @param alignmentId
1970 * - if null, all sets are returned
1971 * @return all AlignmentPanels concerning the alignmentId sequence set
1973 public static AlignmentPanel[] getAlignmentPanels(String alignmentId)
1975 if (Desktop.desktop == null)
1977 // no frames created and in headless mode
1978 // TODO: verify that frames are recoverable when in headless mode
1981 List<AlignmentPanel> aps = new ArrayList<>();
1982 AlignFrame[] frames = getAlignFrames();
1987 for (AlignFrame af : frames)
1989 for (AlignmentPanel ap : af.alignPanels)
1991 if (alignmentId == null
1992 || alignmentId.equals(ap.av.getSequenceSetId()))
1998 if (aps.size() == 0)
2002 AlignmentPanel[] vap = aps.toArray(new AlignmentPanel[aps.size()]);
2007 * get all the viewports on an alignment.
2009 * @param sequenceSetId
2010 * unique alignment id (may be null - all viewports returned in that
2012 * @return all viewports on the alignment bound to sequenceSetId
2014 public static AlignmentViewport[] getViewports(String sequenceSetId)
2016 List<AlignmentViewport> viewp = new ArrayList<>();
2017 if (desktop != null)
2019 AlignFrame[] frames = Desktop.getAlignFrames();
2021 for (AlignFrame afr : frames)
2023 if (sequenceSetId == null || afr.getViewport().getSequenceSetId()
2024 .equals(sequenceSetId))
2026 if (afr.alignPanels != null)
2028 for (AlignmentPanel ap : afr.alignPanels)
2030 if (sequenceSetId == null
2031 || sequenceSetId.equals(ap.av.getSequenceSetId()))
2039 viewp.add(afr.getViewport());
2043 if (viewp.size() > 0)
2045 return viewp.toArray(new AlignmentViewport[viewp.size()]);
2052 * Explode the views in the given frame into separate AlignFrame
2056 public static void explodeViews(AlignFrame af)
2058 int size = af.alignPanels.size();
2064 for (int i = 0; i < size; i++)
2066 AlignmentPanel ap = af.alignPanels.get(i);
2067 AlignFrame newaf = new AlignFrame(ap);
2070 * Restore the view's last exploded frame geometry if known. Multiple
2071 * views from one exploded frame share and restore the same (frame)
2072 * position and size.
2074 Rectangle geometry = ap.av.getExplodedGeometry();
2075 if (geometry != null)
2077 newaf.setBounds(geometry);
2080 ap.av.setGatherViewsHere(false);
2082 addInternalFrame(newaf, af.getTitle(), AlignFrame.DEFAULT_WIDTH,
2083 AlignFrame.DEFAULT_HEIGHT);
2086 af.alignPanels.clear();
2087 af.closeMenuItem_actionPerformed(true);
2092 * Gather expanded views (separate AlignFrame's) with the same sequence set
2093 * identifier back in to this frame as additional views, and close the expanded
2094 * views. Note the expanded frames may themselves have multiple views. We take
2099 public void gatherViews(AlignFrame source)
2101 source.viewport.setGatherViewsHere(true);
2102 source.viewport.setExplodedGeometry(source.getBounds());
2103 JInternalFrame[] frames = desktop.getAllFrames();
2104 String viewId = source.viewport.getSequenceSetId();
2106 for (int t = 0; t < frames.length; t++)
2108 if (frames[t] instanceof AlignFrame && frames[t] != source)
2110 AlignFrame af = (AlignFrame) frames[t];
2111 boolean gatherThis = false;
2112 for (int a = 0; a < af.alignPanels.size(); a++)
2114 AlignmentPanel ap = af.alignPanels.get(a);
2115 if (viewId.equals(ap.av.getSequenceSetId()))
2118 ap.av.setGatherViewsHere(false);
2119 ap.av.setExplodedGeometry(af.getBounds());
2120 source.addAlignmentPanel(ap, false);
2126 af.alignPanels.clear();
2127 af.closeMenuItem_actionPerformed(true);
2134 jalview.gui.VamsasApplication v_client = null;
2137 public void vamsasImport_actionPerformed(ActionEvent e)
2139 // TODO: JAL-3048 not needed for Jalview-JS
2141 if (v_client == null)
2143 // Load and try to start a session.
2144 JalviewFileChooser chooser = new JalviewFileChooser(
2145 jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
2147 chooser.setFileView(new JalviewFileView());
2148 chooser.setDialogTitle(
2149 MessageManager.getString("label.open_saved_vamsas_session"));
2150 chooser.setToolTipText(MessageManager.getString(
2151 "label.select_vamsas_session_opened_as_new_vamsas_session"));
2153 int value = chooser.showOpenDialog(this);
2155 if (value == JalviewFileChooser.APPROVE_OPTION)
2157 String fle = chooser.getSelectedFile().toString();
2158 if (!vamsasImport(chooser.getSelectedFile()))
2160 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
2161 MessageManager.formatMessage(
2162 "label.couldnt_import_as_vamsas_session",
2166 .getString("label.vamsas_document_import_failed"),
2167 JvOptionPane.ERROR_MESSAGE);
2173 jalview.bin.Cache.log.error(
2174 "Implementation error - load session from a running session is not supported.");
2179 * import file into a new vamsas session (uses jalview.gui.VamsasApplication)
2182 * @return true if import was a success and a session was started.
2184 public boolean vamsasImport(URL url)
2186 // TODO: create progress bar
2187 if (v_client != null)
2190 jalview.bin.Cache.log.error(
2191 "Implementation error - load session from a running session is not supported.");
2197 // copy the URL content to a temporary local file
2198 // TODO: be a bit cleverer here with nio (?!)
2199 File file = File.createTempFile("vdocfromurl", ".vdj");
2200 FileOutputStream fos = new FileOutputStream(file);
2201 BufferedInputStream bis = new BufferedInputStream(url.openStream());
2202 byte[] buffer = new byte[2048];
2204 while ((ln = bis.read(buffer)) > -1)
2206 fos.write(buffer, 0, ln);
2210 v_client = new jalview.gui.VamsasApplication(this, file,
2211 url.toExternalForm());
2212 } catch (Exception ex)
2214 jalview.bin.Cache.log.error(
2215 "Failed to create new vamsas session from contents of URL "
2220 setupVamsasConnectedGui();
2221 v_client.initial_update(); // TODO: thread ?
2222 return v_client.inSession();
2226 * import file into a new vamsas session (uses jalview.gui.VamsasApplication)
2229 * @return true if import was a success and a session was started.
2231 public boolean vamsasImport(File file)
2233 if (v_client != null)
2236 jalview.bin.Cache.log.error(
2237 "Implementation error - load session from a running session is not supported.");
2241 setProgressBar(MessageManager.formatMessage(
2242 "status.importing_vamsas_session_from", new Object[]
2243 { file.getName() }), file.hashCode());
2246 v_client = new jalview.gui.VamsasApplication(this, file, null);
2247 } catch (Exception ex)
2249 setProgressBar(MessageManager.formatMessage(
2250 "status.importing_vamsas_session_from", new Object[]
2251 { file.getName() }), file.hashCode());
2252 jalview.bin.Cache.log.error(
2253 "New vamsas session from existing session file failed:", ex);
2256 setupVamsasConnectedGui();
2257 v_client.initial_update(); // TODO: thread ?
2258 setProgressBar(MessageManager.formatMessage(
2259 "status.importing_vamsas_session_from", new Object[]
2260 { file.getName() }), file.hashCode());
2261 return v_client.inSession();
2264 public boolean joinVamsasSession(String mysesid)
2266 if (v_client != null)
2268 throw new Error(MessageManager
2269 .getString("error.try_join_vamsas_session_another"));
2271 if (mysesid == null)
2274 MessageManager.getString("error.invalid_vamsas_session_id"));
2276 v_client = new VamsasApplication(this, mysesid);
2277 setupVamsasConnectedGui();
2278 v_client.initial_update();
2279 return (v_client.inSession());
2283 public void vamsasStart_actionPerformed(ActionEvent e)
2285 if (v_client == null)
2288 // we just start a default session for moment.
2290 * JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache.
2291 * getProperty("LAST_DIRECTORY"));
2293 * chooser.setFileView(new JalviewFileView());
2294 * chooser.setDialogTitle("Load Vamsas file");
2295 * chooser.setToolTipText("Import");
2297 * int value = chooser.showOpenDialog(this);
2299 * if (value == JalviewFileChooser.APPROVE_OPTION) { v_client = new
2300 * jalview.gui.VamsasApplication(this, chooser.getSelectedFile());
2302 v_client = new VamsasApplication(this);
2303 setupVamsasConnectedGui();
2304 v_client.initial_update(); // TODO: thread ?
2308 // store current data in session.
2309 v_client.push_update(); // TODO: thread
2313 protected void setupVamsasConnectedGui()
2315 vamsasStart.setText(MessageManager.getString("label.session_update"));
2316 vamsasSave.setVisible(true);
2317 vamsasStop.setVisible(true);
2318 vamsasImport.setVisible(false); // Document import to existing session is
2319 // not possible for vamsas-client-1.0.
2322 protected void setupVamsasDisconnectedGui()
2324 vamsasSave.setVisible(false);
2325 vamsasStop.setVisible(false);
2326 vamsasImport.setVisible(true);
2328 .setText(MessageManager.getString("label.new_vamsas_session"));
2332 public void vamsasStop_actionPerformed(ActionEvent e)
2334 if (v_client != null)
2336 v_client.end_session();
2338 setupVamsasDisconnectedGui();
2342 protected void buildVamsasStMenu()
2344 if (v_client == null)
2346 String[] sess = null;
2349 sess = VamsasApplication.getSessionList();
2350 } catch (Exception e)
2352 jalview.bin.Cache.log.warn("Problem getting current sessions list.",
2358 jalview.bin.Cache.log.debug(
2359 "Got current sessions list: " + sess.length + " entries.");
2360 VamsasStMenu.removeAll();
2361 for (int i = 0; i < sess.length; i++)
2363 JMenuItem sessit = new JMenuItem();
2364 sessit.setText(sess[i]);
2365 sessit.setToolTipText(MessageManager
2366 .formatMessage("label.connect_to_session", new Object[]
2368 final Desktop dsktp = this;
2369 final String mysesid = sess[i];
2370 sessit.addActionListener(new ActionListener()
2374 public void actionPerformed(ActionEvent e)
2376 if (dsktp.v_client == null)
2378 Thread rthr = new Thread(new Runnable()
2384 dsktp.v_client = new VamsasApplication(dsktp, mysesid);
2385 dsktp.setupVamsasConnectedGui();
2386 dsktp.v_client.initial_update();
2394 VamsasStMenu.add(sessit);
2396 // don't show an empty menu.
2397 VamsasStMenu.setVisible(sess.length > 0);
2402 jalview.bin.Cache.log.debug("No current vamsas sessions.");
2403 VamsasStMenu.removeAll();
2404 VamsasStMenu.setVisible(false);
2409 // Not interested in the content. Just hide ourselves.
2410 VamsasStMenu.setVisible(false);
2415 public void vamsasSave_actionPerformed(ActionEvent e)
2417 // TODO: JAL-3048 not needed for Jalview-JS
2419 if (v_client != null)
2421 // TODO: VAMSAS DOCUMENT EXTENSION is VDJ
2422 JalviewFileChooser chooser = new JalviewFileChooser("vdj",
2425 chooser.setFileView(new JalviewFileView());
2426 chooser.setDialogTitle(MessageManager
2427 .getString("label.save_vamsas_document_archive"));
2429 int value = chooser.showSaveDialog(this);
2431 if (value == JalviewFileChooser.APPROVE_OPTION)
2433 java.io.File choice = chooser.getSelectedFile();
2434 JPanel progpanel = addProgressPanel(MessageManager
2435 .formatMessage("label.saving_vamsas_doc", new Object[]
2436 { choice.getName() }));
2437 Cache.setProperty("LAST_DIRECTORY", choice.getParent());
2438 String warnmsg = null;
2439 String warnttl = null;
2442 v_client.vclient.storeDocument(choice);
2445 warnttl = "Serious Problem saving Vamsas Document";
2446 warnmsg = ex.toString();
2447 jalview.bin.Cache.log
2448 .error("Error Whilst saving document to " + choice, ex);
2450 } catch (Exception ex)
2452 warnttl = "Problem saving Vamsas Document.";
2453 warnmsg = ex.toString();
2454 jalview.bin.Cache.log.warn(
2455 "Exception Whilst saving document to " + choice, ex);
2458 removeProgressPanel(progpanel);
2459 if (warnmsg != null)
2461 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
2463 warnmsg, warnttl, JvOptionPane.ERROR_MESSAGE);
2469 JPanel vamUpdate = null;
2472 * hide vamsas user gui bits when a vamsas document event is being handled.
2475 * true to hide gui, false to reveal gui
2477 public void setVamsasUpdate(boolean b)
2479 Cache.log.debug("Setting gui for Vamsas update "
2480 + (b ? "in progress" : "finished"));
2482 if (vamUpdate != null)
2484 this.removeProgressPanel(vamUpdate);
2488 vamUpdate = this.addProgressPanel(
2489 MessageManager.getString("label.updating_vamsas_session"));
2491 vamsasStart.setVisible(!b);
2492 vamsasStop.setVisible(!b);
2493 vamsasSave.setVisible(!b);
2496 public JInternalFrame[] getAllFrames()
2498 return desktop.getAllFrames();
2502 * Checks the given url to see if it gives a response indicating that the user
2503 * should be informed of a new questionnaire.
2507 public void checkForQuestionnaire(String url)
2509 UserQuestionnaireCheck jvq = new UserQuestionnaireCheck(url);
2510 // javax.swing.SwingUtilities.invokeLater(jvq);
2511 new Thread(jvq).start();
2514 public void checkURLLinks()
2516 // Thread off the URL link checker
2517 addDialogThread(new Runnable()
2522 if (Cache.getDefault("CHECKURLLINKS", true))
2524 // check what the actual links are - if it's just the default don't
2525 // bother with the warning
2526 List<String> links = Preferences.sequenceUrlLinks
2529 // only need to check links if there is one with a
2530 // SEQUENCE_ID which is not the default EMBL_EBI link
2531 ListIterator<String> li = links.listIterator();
2532 boolean check = false;
2533 List<JLabel> urls = new ArrayList<>();
2534 while (li.hasNext())
2536 String link = li.next();
2537 if (link.contains(jalview.util.UrlConstants.SEQUENCE_ID)
2538 && !UrlConstants.isDefaultString(link))
2541 int barPos = link.indexOf("|");
2542 String urlMsg = barPos == -1 ? link
2543 : link.substring(0, barPos) + ": "
2544 + link.substring(barPos + 1);
2545 urls.add(new JLabel(urlMsg));
2553 // ask user to check in case URL links use old style tokens
2554 // ($SEQUENCE_ID$ for sequence id _or_ accession id)
2555 JPanel msgPanel = new JPanel();
2556 msgPanel.setLayout(new BoxLayout(msgPanel, BoxLayout.PAGE_AXIS));
2557 msgPanel.add(Box.createVerticalGlue());
2558 JLabel msg = new JLabel(MessageManager
2559 .getString("label.SEQUENCE_ID_for_DB_ACCESSION1"));
2560 JLabel msg2 = new JLabel(MessageManager
2561 .getString("label.SEQUENCE_ID_for_DB_ACCESSION2"));
2563 for (JLabel url : urls)
2569 final JCheckBox jcb = new JCheckBox(
2570 MessageManager.getString("label.do_not_display_again"));
2571 jcb.addActionListener(new ActionListener()
2574 public void actionPerformed(ActionEvent e)
2576 // update Cache settings for "don't show this again"
2577 boolean showWarningAgain = !jcb.isSelected();
2578 Cache.setProperty("CHECKURLLINKS",
2579 Boolean.valueOf(showWarningAgain).toString());
2584 JvOptionPane.showMessageDialog(Desktop.desktop, msgPanel,
2586 .getString("label.SEQUENCE_ID_no_longer_used"),
2587 JvOptionPane.WARNING_MESSAGE);
2594 * Proxy class for JDesktopPane which optionally displays the current memory
2595 * usage and highlights the desktop area with a red bar if free memory runs low.
2599 public class MyDesktopPane extends JDesktopPane
2602 private static final float ONE_MB = 1048576f;
2604 boolean showMemoryUsage = false;
2608 java.text.NumberFormat df;
2610 float maxMemory, allocatedMemory, freeMemory, totalFreeMemory,
2613 public MyDesktopPane(boolean showMemoryUsage)
2615 showMemoryUsage(showMemoryUsage);
2618 public void showMemoryUsage(boolean showMemory)
2620 this.showMemoryUsage = showMemory;
2623 Thread worker = new Thread(this);
2629 public boolean isShowMemoryUsage()
2631 return showMemoryUsage;
2637 df = java.text.NumberFormat.getNumberInstance();
2638 df.setMaximumFractionDigits(2);
2639 runtime = Runtime.getRuntime();
2641 while (showMemoryUsage)
2645 maxMemory = runtime.maxMemory() / ONE_MB;
2646 allocatedMemory = runtime.totalMemory() / ONE_MB;
2647 freeMemory = runtime.freeMemory() / ONE_MB;
2648 totalFreeMemory = freeMemory + (maxMemory - allocatedMemory);
2650 percentUsage = (totalFreeMemory / maxMemory) * 100;
2652 // if (percentUsage < 20)
2654 // border1 = BorderFactory.createMatteBorder(12, 12, 12, 12,
2656 // instance.set.setBorder(border1);
2659 // sleep after showing usage
2661 } catch (Exception ex)
2663 ex.printStackTrace();
2669 public void paintComponent(Graphics g)
2671 if (showMemoryUsage && g != null && df != null)
2673 if (percentUsage < 20)
2675 g.setColor(Color.red);
2677 FontMetrics fm = g.getFontMetrics();
2680 g.drawString(MessageManager.formatMessage("label.memory_stats",
2682 { df.format(totalFreeMemory), df.format(maxMemory),
2683 df.format(percentUsage) }),
2684 10, getHeight() - fm.getHeight());
2691 * Accessor method to quickly get all the AlignmentFrames loaded.
2693 * @return an array of AlignFrame, or null if none found
2695 public static AlignFrame[] getAlignFrames()
2697 if (Jalview.isHeadlessMode())
2699 // Desktop.desktop is null in headless mode
2700 return new AlignFrame[] { Jalview.currentAlignFrame };
2703 JInternalFrame[] frames = Desktop.desktop.getAllFrames();
2709 List<AlignFrame> avp = new ArrayList<>();
2711 for (int i = frames.length - 1; i > -1; i--)
2713 if (frames[i] instanceof AlignFrame)
2715 avp.add((AlignFrame) frames[i]);
2717 else if (frames[i] instanceof SplitFrame)
2720 * Also check for a split frame containing an AlignFrame
2722 GSplitFrame sf = (GSplitFrame) frames[i];
2723 if (sf.getTopFrame() instanceof AlignFrame)
2725 avp.add((AlignFrame) sf.getTopFrame());
2727 if (sf.getBottomFrame() instanceof AlignFrame)
2729 avp.add((AlignFrame) sf.getBottomFrame());
2733 if (avp.size() == 0)
2737 AlignFrame afs[] = avp.toArray(new AlignFrame[avp.size()]);
2742 * Returns an array of any AppJmol frames in the Desktop (or null if none).
2746 public GStructureViewer[] getJmols()
2748 JInternalFrame[] frames = Desktop.desktop.getAllFrames();
2754 List<GStructureViewer> avp = new ArrayList<>();
2756 for (int i = frames.length - 1; i > -1; i--)
2758 if (frames[i] instanceof AppJmol)
2760 GStructureViewer af = (GStructureViewer) frames[i];
2764 if (avp.size() == 0)
2768 GStructureViewer afs[] = avp.toArray(new GStructureViewer[avp.size()]);
2773 * Add Groovy Support to Jalview
2776 public void groovyShell_actionPerformed()
2780 openGroovyConsole();
2781 } catch (Exception ex)
2783 jalview.bin.Cache.log.error("Groovy Shell Creation failed.", ex);
2784 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
2786 MessageManager.getString("label.couldnt_create_groovy_shell"),
2787 MessageManager.getString("label.groovy_support_failed"),
2788 JvOptionPane.ERROR_MESSAGE);
2793 * Open the Groovy console
2795 void openGroovyConsole()
2797 if (groovyConsole == null)
2799 groovyConsole = new groovy.ui.Console();
2800 groovyConsole.setVariable("Jalview", this);
2801 groovyConsole.run();
2804 * We allow only one console at a time, so that AlignFrame menu option
2805 * 'Calculate | Run Groovy script' is unambiguous.
2806 * Disable 'Groovy Console', and enable 'Run script', when the console is
2807 * opened, and the reverse when it is closed
2809 Window window = (Window) groovyConsole.getFrame();
2810 window.addWindowListener(new WindowAdapter()
2813 public void windowClosed(WindowEvent e)
2816 * rebind CMD-Q from Groovy Console to Jalview Quit
2819 enableExecuteGroovy(false);
2825 * show Groovy console window (after close and reopen)
2827 ((Window) groovyConsole.getFrame()).setVisible(true);
2830 * if we got this far, enable 'Run Groovy' in AlignFrame menus
2831 * and disable opening a second console
2833 enableExecuteGroovy(true);
2837 * Bind Ctrl/Cmd-Q to Quit - for reset as Groovy Console takes over this binding
2840 protected void addQuitHandler()
2842 getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
2843 .put(KeyStroke.getKeyStroke(KeyEvent.VK_Q,
2844 jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx()),
2846 getRootPane().getActionMap().put("Quit", new AbstractAction()
2849 public void actionPerformed(ActionEvent e)
2857 * Enable or disable 'Run Groovy script' in AlignFrame calculate menus
2860 * true if Groovy console is open
2862 public void enableExecuteGroovy(boolean enabled)
2865 * disable opening a second Groovy console
2866 * (or re-enable when the console is closed)
2868 groovyShell.setEnabled(!enabled);
2870 AlignFrame[] alignFrames = getAlignFrames();
2871 if (alignFrames != null)
2873 for (AlignFrame af : alignFrames)
2875 af.setGroovyEnabled(enabled);
2881 * Progress bars managed by the IProgressIndicator method.
2883 private Hashtable<Long, JPanel> progressBars;
2885 private Hashtable<Long, IProgressIndicatorHandler> progressBarHandlers;
2890 * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
2893 public void setProgressBar(String message, long id)
2895 Platform.timeCheck("Desktop " + message, Platform.TIME_MARK);
2897 if (progressBars == null)
2899 progressBars = new Hashtable<>();
2900 progressBarHandlers = new Hashtable<>();
2903 if (progressBars.get(Long.valueOf(id)) != null)
2905 JPanel panel = progressBars.remove(Long.valueOf(id));
2906 if (progressBarHandlers.contains(Long.valueOf(id)))
2908 progressBarHandlers.remove(Long.valueOf(id));
2910 removeProgressPanel(panel);
2914 progressBars.put(Long.valueOf(id), addProgressPanel(message));
2921 * @see jalview.gui.IProgressIndicator#registerHandler(long,
2922 * jalview.gui.IProgressIndicatorHandler)
2925 public void registerHandler(final long id,
2926 final IProgressIndicatorHandler handler)
2928 if (progressBarHandlers == null
2929 || !progressBars.containsKey(Long.valueOf(id)))
2931 throw new Error(MessageManager.getString(
2932 "error.call_setprogressbar_before_registering_handler"));
2934 progressBarHandlers.put(Long.valueOf(id), handler);
2935 final JPanel progressPanel = progressBars.get(Long.valueOf(id));
2936 if (handler.canCancel())
2938 JButton cancel = new JButton(
2939 MessageManager.getString("action.cancel"));
2940 final IProgressIndicator us = this;
2941 cancel.addActionListener(new ActionListener()
2945 public void actionPerformed(ActionEvent e)
2947 handler.cancelActivity(id);
2948 us.setProgressBar(MessageManager
2949 .formatMessage("label.cancelled_params", new Object[]
2950 { ((JLabel) progressPanel.getComponent(0)).getText() }),
2954 progressPanel.add(cancel, BorderLayout.EAST);
2960 * @return true if any progress bars are still active
2963 public boolean operationInProgress()
2965 if (progressBars != null && progressBars.size() > 0)
2973 * This will return the first AlignFrame holding the given viewport instance. It
2974 * will break if there are more than one AlignFrames viewing a particular av.
2977 * @return alignFrame for viewport
2979 public static AlignFrame getAlignFrameFor(AlignViewportI viewport)
2981 if (desktop != null)
2983 AlignmentPanel[] aps = getAlignmentPanels(
2984 viewport.getSequenceSetId());
2985 for (int panel = 0; aps != null && panel < aps.length; panel++)
2987 if (aps[panel] != null && aps[panel].av == viewport)
2989 return aps[panel].alignFrame;
2996 public VamsasApplication getVamsasApplication()
3003 * flag set if jalview GUI is being operated programmatically
3005 private boolean inBatchMode = false;
3008 * check if jalview GUI is being operated programmatically
3010 * @return inBatchMode
3012 public boolean isInBatchMode()
3018 * set flag if jalview GUI is being operated programmatically
3020 * @param inBatchMode
3022 public void setInBatchMode(boolean inBatchMode)
3024 this.inBatchMode = inBatchMode;
3027 public void startServiceDiscovery()
3029 startServiceDiscovery(false);
3032 public void startServiceDiscovery(boolean blocking)
3034 boolean alive = true;
3035 Thread t0 = null, t1 = null, t2 = null;
3036 // JAL-940 - JALVIEW 1 services are now being EOLed as of JABA 2.1 release
3039 // todo: changesupport handlers need to be transferred
3040 if (discoverer == null)
3042 discoverer = new jalview.ws.jws1.Discoverer();
3043 // register PCS handler for desktop.
3044 discoverer.addPropertyChangeListener(changeSupport);
3046 // JAL-940 - disabled JWS1 service configuration - always start discoverer
3047 // until we phase out completely
3048 (t0 = new Thread(discoverer)).start();
3051 if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
3053 t2 = jalview.ws.jws2.Jws2Discoverer.getDiscoverer()
3054 .startDiscoverer(changeSupport);
3058 // TODO: do rest service discovery
3067 } catch (Exception e)
3070 alive = (t1 != null && t1.isAlive()) || (t2 != null && t2.isAlive())
3071 || (t3 != null && t3.isAlive())
3072 || (t0 != null && t0.isAlive());
3078 * called to check if the service discovery process completed successfully.
3082 protected void JalviewServicesChanged(PropertyChangeEvent evt)
3084 if (evt.getNewValue() == null || evt.getNewValue() instanceof Vector)
3086 final String ermsg = jalview.ws.jws2.Jws2Discoverer.getDiscoverer()
3087 .getErrorMessages();
3090 if (Cache.getDefault("SHOW_WSDISCOVERY_ERRORS", true))
3092 if (serviceChangedDialog == null)
3094 // only run if we aren't already displaying one of these.
3095 addDialogThread(serviceChangedDialog = new Runnable()
3102 * JalviewDialog jd =new JalviewDialog() {
3104 * @Override protected void cancelPressed() { // TODO
3105 * Auto-generated method stub
3107 * }@Override protected void okPressed() { // TODO
3108 * Auto-generated method stub
3110 * }@Override protected void raiseClosed() { // TODO
3111 * Auto-generated method stub
3113 * } }; jd.initDialogFrame(new
3114 * JLabel("<html><table width=\"450\"><tr><td>" + ermsg +
3115 * "<br/>It may be that you have invalid JABA URLs in your web service preferences,"
3116 * + " or mis-configured HTTP proxy settings.<br/>" +
3117 * "Check the <em>Connections</em> and <em>Web services</em> tab of the"
3119 * " Tools->Preferences dialog box to change them.</td></tr></table></html>"
3120 * ), true, true, "Web Service Configuration Problem", 450,
3123 * jd.waitForInput();
3125 JvOptionPane.showConfirmDialog(Desktop.desktop,
3126 new JLabel("<html><table width=\"450\"><tr><td>"
3127 + ermsg + "</td></tr></table>"
3128 + "<p>It may be that you have invalid JABA URLs<br/>in your web service preferences,"
3129 + "<br>or as a command-line argument, or mis-configured HTTP proxy settings.</p>"
3130 + "<p>Check the <em>Connections</em> and <em>Web services</em> tab<br/>of the"
3131 + " Tools->Preferences dialog box to change them.</p></html>"),
3132 "Web Service Configuration Problem",
3133 JvOptionPane.DEFAULT_OPTION,
3134 JvOptionPane.ERROR_MESSAGE);
3135 serviceChangedDialog = null;
3144 "Errors reported by JABA discovery service. Check web services preferences.\n"
3151 private Runnable serviceChangedDialog = null;
3154 * start a thread to open a URL in the configured browser. Pops up a warning
3155 * dialog to the user if there is an exception when calling out to the browser
3160 public static void showUrl(final String url)
3162 showUrl(url, Desktop.instance);
3166 * Like showUrl but allows progress handler to be specified
3170 * (null) or object implementing IProgressIndicator
3172 public static void showUrl(final String url,
3173 final IProgressIndicator progress)
3175 new Thread(new Runnable()
3182 if (progress != null)
3184 progress.setProgressBar(MessageManager
3185 .formatMessage("status.opening_params", new Object[]
3186 { url }), this.hashCode());
3188 jalview.util.BrowserLauncher.openURL(url);
3189 } catch (Exception ex)
3191 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
3193 .getString("label.web_browser_not_found_unix"),
3194 MessageManager.getString("label.web_browser_not_found"),
3195 JvOptionPane.WARNING_MESSAGE);
3197 ex.printStackTrace();
3199 if (progress != null)
3201 progress.setProgressBar(null, this.hashCode());
3207 public static WsParamSetManager wsparamManager = null;
3209 public static ParamManager getUserParameterStore()
3211 if (wsparamManager == null)
3213 wsparamManager = new WsParamSetManager();
3215 return wsparamManager;
3219 * static hyperlink handler proxy method for use by Jalview's internal windows
3223 public static void hyperlinkUpdate(HyperlinkEvent e)
3225 if (e.getEventType() == EventType.ACTIVATED)
3230 url = e.getURL().toString();
3231 Desktop.showUrl(url);
3232 } catch (Exception x)
3236 if (Cache.log != null)
3238 Cache.log.error("Couldn't handle string " + url + " as a URL.");
3243 "Couldn't handle string " + url + " as a URL.");
3246 // ignore any exceptions due to dud links.
3253 * single thread that handles display of dialogs to user.
3255 ExecutorService dialogExecutor = Executors.newSingleThreadExecutor();
3258 * flag indicating if dialogExecutor should try to acquire a permit
3260 private volatile boolean dialogPause = true;
3265 private java.util.concurrent.Semaphore block = new Semaphore(0);
3267 private static groovy.ui.Console groovyConsole;
3270 * add another dialog thread to the queue
3274 public void addDialogThread(final Runnable prompter)
3276 dialogExecutor.submit(new Runnable()
3286 } catch (InterruptedException x)
3290 if (instance == null)
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 = desktop.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 return groovyConsole;
3503 * handles the payload of a drag and drop event.
3505 * TODO refactor to desktop utilities class
3508 * - Data source strings extracted from the drop event
3510 * - protocol for each data source extracted from the drop event
3514 * - the payload from the drop event
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");
3577 } catch (Throwable ex)
3579 Cache.log.debug("URL drop handler failed.", ex);
3582 if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
3584 // Works on Windows and MacOSX
3585 Cache.log.debug("Drop handled as javaFileListFlavor");
3586 for (Object file : (List) t
3587 .getTransferData(DataFlavor.javaFileListFlavor))
3590 protocols.add(DataSourceType.FILE);
3595 // Unix like behaviour
3596 boolean added = false;
3598 if (t.isDataFlavorSupported(uriListFlavor))
3600 Cache.log.debug("Drop handled as uriListFlavor");
3601 // This is used by Unix drag system
3602 data = (String) t.getTransferData(uriListFlavor);
3606 // fallback to text: workaround - on OSX where there's a JVM bug
3607 Cache.log.debug("standard URIListFlavor failed. Trying text");
3608 // try text fallback
3609 DataFlavor textDf = new DataFlavor(
3610 "text/plain;class=java.lang.String");
3611 if (t.isDataFlavorSupported(textDf))
3613 data = (String) t.getTransferData(textDf);
3616 Cache.log.debug("Plain text drop content returned "
3617 + (data == null ? "Null - failed" : data));
3622 while (protocols.size() < files.size())
3624 Cache.log.debug("Adding missing FILE protocol for "
3625 + files.get(protocols.size()));
3626 protocols.add(DataSourceType.FILE);
3628 for (java.util.StringTokenizer st = new java.util.StringTokenizer(
3629 data, "\r\n"); st.hasMoreTokens();)
3632 String s = st.nextToken();
3633 if (s.startsWith("#"))
3635 // the line is a comment (as per the RFC 2483)
3638 java.net.URI uri = new java.net.URI(s);
3639 if (uri.getScheme().toLowerCase().startsWith("http"))
3641 protocols.add(DataSourceType.URL);
3642 files.add(uri.toString());
3646 // otherwise preserve old behaviour: catch all for file objects
3647 java.io.File file = new java.io.File(uri);
3648 protocols.add(DataSourceType.FILE);
3649 files.add(file.toString());
3654 if (Cache.log.isDebugEnabled())
3656 if (data == null || !added)
3659 if (t.getTransferDataFlavors() != null
3660 && t.getTransferDataFlavors().length > 0)
3663 "Couldn't resolve drop data. Here are the supported flavors:");
3664 for (DataFlavor fl : t.getTransferDataFlavors())
3667 "Supported transfer dataflavor: " + fl.toString());
3668 Object df = t.getTransferData(fl);
3671 Cache.log.debug("Retrieves: " + df);
3675 Cache.log.debug("Retrieved nothing");
3681 Cache.log.debug("Couldn't resolve dataflavor for drop: "
3687 if (Platform.isWindowsAndNotJS())
3689 Cache.log.debug("Scanning dropped content for Windows Link Files");
3691 // resolve any .lnk files in the file drop
3692 for (int f = 0; f < files.size(); f++)
3694 String source = files.get(f).toString().toLowerCase();
3695 if (protocols.get(f).equals(DataSourceType.FILE)
3696 && (source.endsWith(".lnk") || source.endsWith(".url")
3697 || source.endsWith(".site")))
3701 Object obj = files.get(f);
3702 File lf = (obj instanceof File ? (File) obj
3703 : new File((String) obj));
3704 // process link file to get a URL
3705 Cache.log.debug("Found potential link file: " + lf);
3706 WindowsShortcut wscfile = new WindowsShortcut(lf);
3707 String fullname = wscfile.getRealFilename();
3708 protocols.set(f, FormatAdapter.checkProtocol(fullname));
3709 files.set(f, fullname);
3710 Cache.log.debug("Parsed real filename " + fullname
3711 + " to extract protocol: " + protocols.get(f));
3712 } catch (Exception ex)
3715 "Couldn't parse " + files.get(f) + " as a link file.",
3724 * Sets the Preferences property for experimental features to True or False
3725 * depending on the state of the controlling menu item
3728 protected void showExperimental_actionPerformed(boolean selected)
3730 Cache.setProperty(EXPERIMENTAL_FEATURES, Boolean.toString(selected));
3734 * Answers a (possibly empty) list of any structure viewer frames (currently for
3735 * either Jmol or Chimera) which are currently open. This may optionally be
3736 * restricted to viewers of a specified class, or viewers linked to a specified
3740 * if not null, only return viewers linked to this panel
3741 * @param structureViewerClass
3742 * if not null, only return viewers of this class
3745 public List<StructureViewerBase> getStructureViewers(
3746 AlignmentPanel apanel,
3747 Class<? extends StructureViewerBase> structureViewerClass)
3749 List<StructureViewerBase> result = new ArrayList<>();
3750 JInternalFrame[] frames = Desktop.instance.getAllFrames();
3752 for (JInternalFrame frame : frames)
3754 if (frame instanceof StructureViewerBase)
3756 if (structureViewerClass == null
3757 || structureViewerClass.isInstance(frame))
3760 || ((StructureViewerBase) frame).isLinkedWith(apanel))
3762 result.add((StructureViewerBase) frame);