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 java.awt.BorderLayout;
24 import java.awt.Color;
25 import java.awt.Dimension;
26 import java.awt.FontMetrics;
27 import java.awt.Graphics;
28 import java.awt.GridLayout;
29 import java.awt.Point;
30 import java.awt.Rectangle;
31 import java.awt.Toolkit;
32 import java.awt.Window;
33 import java.awt.datatransfer.Clipboard;
34 import java.awt.datatransfer.ClipboardOwner;
35 import java.awt.datatransfer.DataFlavor;
36 import java.awt.datatransfer.Transferable;
37 import java.awt.dnd.DnDConstants;
38 import java.awt.dnd.DropTargetDragEvent;
39 import java.awt.dnd.DropTargetDropEvent;
40 import java.awt.dnd.DropTargetEvent;
41 import java.awt.dnd.DropTargetListener;
42 import java.awt.event.ActionEvent;
43 import java.awt.event.ActionListener;
44 import java.awt.event.InputEvent;
45 import java.awt.event.KeyEvent;
46 import java.awt.event.MouseAdapter;
47 import java.awt.event.MouseEvent;
48 import java.awt.event.WindowAdapter;
49 import java.awt.event.WindowEvent;
50 import java.beans.PropertyChangeEvent;
51 import java.beans.PropertyChangeListener;
53 import java.io.FileWriter;
54 import java.io.IOException;
56 import java.util.ArrayList;
57 import java.util.HashMap;
58 import java.util.Hashtable;
59 import java.util.List;
60 import java.util.ListIterator;
61 import java.util.Vector;
62 import java.util.concurrent.ExecutorService;
63 import java.util.concurrent.Executors;
64 import java.util.concurrent.Semaphore;
66 import javax.swing.AbstractAction;
67 import javax.swing.Action;
68 import javax.swing.ActionMap;
69 import javax.swing.Box;
70 import javax.swing.BoxLayout;
71 import javax.swing.DefaultDesktopManager;
72 import javax.swing.DesktopManager;
73 import javax.swing.InputMap;
74 import javax.swing.JButton;
75 import javax.swing.JCheckBox;
76 import javax.swing.JComboBox;
77 import javax.swing.JComponent;
78 import javax.swing.JDesktopPane;
79 import javax.swing.JInternalFrame;
80 import javax.swing.JLabel;
81 import javax.swing.JMenuItem;
82 import javax.swing.JPanel;
83 import javax.swing.JPopupMenu;
84 import javax.swing.JProgressBar;
85 import javax.swing.JTextField;
86 import javax.swing.KeyStroke;
87 import javax.swing.SwingUtilities;
88 import javax.swing.event.HyperlinkEvent;
89 import javax.swing.event.HyperlinkEvent.EventType;
90 import javax.swing.event.InternalFrameAdapter;
91 import javax.swing.event.InternalFrameEvent;
93 import org.stackoverflowusers.file.WindowsShortcut;
95 import jalview.api.AlignViewportI;
96 import jalview.api.AlignmentViewPanel;
97 import jalview.bin.Cache;
98 import jalview.bin.Jalview;
99 import jalview.gui.ImageExporter.ImageWriterI;
100 import jalview.io.BackupFiles;
101 import jalview.io.DataSourceType;
102 import jalview.io.FileFormat;
103 import jalview.io.FileFormatException;
104 import jalview.io.FileFormatI;
105 import jalview.io.FileFormats;
106 import jalview.io.FileLoader;
107 import jalview.io.FormatAdapter;
108 import jalview.io.IdentifyFile;
109 import jalview.io.JalviewFileChooser;
110 import jalview.io.JalviewFileView;
111 import jalview.jbgui.GSplitFrame;
112 import jalview.jbgui.GStructureViewer;
113 import jalview.project.Jalview2XML;
114 import jalview.structure.StructureSelectionManager;
115 import jalview.urls.IdOrgSettings;
116 import jalview.util.BrowserLauncher;
117 import jalview.util.ImageMaker.TYPE;
118 import jalview.util.MessageManager;
119 import jalview.util.Platform;
120 import jalview.util.ShortcutKeyMaskExWrapper;
121 import jalview.util.UrlConstants;
122 import jalview.viewmodel.AlignmentViewport;
123 import jalview.ws.params.ParamManager;
124 import jalview.ws.utils.UrlDownloadClient;
131 * @version $Revision: 1.155 $
133 public class Desktop extends jalview.jbgui.GDesktop
134 implements DropTargetListener, ClipboardOwner, IProgressIndicator,
135 jalview.api.StructureSelectionManagerProvider
137 private static final String CITATION = "<br><br>Development managed by The Barton Group, University of Dundee, Scotland, UK.<br>"
138 + "<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"
139 + "<br><br>If you use Jalview, please cite:"
140 + "<br>Waterhouse, A.M., Procter, J.B., Martin, D.M.A, Clamp, M. and Barton, G. J. (2009)"
141 + "<br>Jalview Version 2 - a multiple sequence alignment editor and analysis workbench"
142 + "<br>Bioinformatics doi: 10.1093/bioinformatics/btp033";
144 private static final String DEFAULT_AUTHORS = "The Jalview Authors (See AUTHORS file for current list)";
146 private static int DEFAULT_MIN_WIDTH = 300;
148 private static int DEFAULT_MIN_HEIGHT = 250;
150 private static int ALIGN_FRAME_DEFAULT_MIN_WIDTH = 600;
152 private static int ALIGN_FRAME_DEFAULT_MIN_HEIGHT = 70;
154 private static final String EXPERIMENTAL_FEATURES = "EXPERIMENTAL_FEATURES";
156 protected static final String CONFIRM_KEYBOARD_QUIT = "CONFIRM_KEYBOARD_QUIT";
158 public static HashMap<String, FileWriter> savingFiles = new HashMap<String, FileWriter>();
160 private JalviewChangeSupport changeSupport = new JalviewChangeSupport();
163 * news reader - null if it was never started.
165 private BlogReader jvnews = null;
167 private File projectFile;
171 * @see jalview.gui.JalviewChangeSupport#addJalviewPropertyChangeListener(java.beans.PropertyChangeListener)
173 public void addJalviewPropertyChangeListener(
174 PropertyChangeListener listener)
176 changeSupport.addJalviewPropertyChangeListener(listener);
180 * @param propertyName
182 * @see jalview.gui.JalviewChangeSupport#addJalviewPropertyChangeListener(java.lang.String,
183 * java.beans.PropertyChangeListener)
185 public void addJalviewPropertyChangeListener(String propertyName,
186 PropertyChangeListener listener)
188 changeSupport.addJalviewPropertyChangeListener(propertyName, listener);
192 * @param propertyName
194 * @see jalview.gui.JalviewChangeSupport#removeJalviewPropertyChangeListener(java.lang.String,
195 * java.beans.PropertyChangeListener)
197 public void removeJalviewPropertyChangeListener(String propertyName,
198 PropertyChangeListener listener)
200 changeSupport.removeJalviewPropertyChangeListener(propertyName,
204 /** Singleton Desktop instance */
205 public static Desktop instance;
207 public static MyDesktopPane desktop;
209 public static MyDesktopPane getDesktop()
211 // BH 2018 could use currentThread() here as a reference to a
212 // Hashtable<Thread, MyDesktopPane> in JavaScript
216 static int openFrameCount = 0;
218 static final int xOffset = 30;
220 static final int yOffset = 30;
222 public static jalview.ws.jws1.Discoverer discoverer;
224 public static Object[] jalviewClipboard;
226 public static boolean internalCopy = false;
228 static int fileLoadingCount = 0;
230 class MyDesktopManager implements DesktopManager
233 private DesktopManager delegate;
235 public MyDesktopManager(DesktopManager delegate)
237 this.delegate = delegate;
241 public void activateFrame(JInternalFrame f)
245 delegate.activateFrame(f);
246 } catch (NullPointerException npe)
248 Point p = getMousePosition();
249 instance.showPasteMenu(p.x, p.y);
254 public void beginDraggingFrame(JComponent f)
256 delegate.beginDraggingFrame(f);
260 public void beginResizingFrame(JComponent f, int direction)
262 delegate.beginResizingFrame(f, direction);
266 public void closeFrame(JInternalFrame f)
268 delegate.closeFrame(f);
272 public void deactivateFrame(JInternalFrame f)
274 delegate.deactivateFrame(f);
278 public void deiconifyFrame(JInternalFrame f)
280 delegate.deiconifyFrame(f);
284 public void dragFrame(JComponent f, int newX, int newY)
290 delegate.dragFrame(f, newX, newY);
294 public void endDraggingFrame(JComponent f)
296 delegate.endDraggingFrame(f);
301 public void endResizingFrame(JComponent f)
303 delegate.endResizingFrame(f);
308 public void iconifyFrame(JInternalFrame f)
310 delegate.iconifyFrame(f);
314 public void maximizeFrame(JInternalFrame f)
316 delegate.maximizeFrame(f);
320 public void minimizeFrame(JInternalFrame f)
322 delegate.minimizeFrame(f);
326 public void openFrame(JInternalFrame f)
328 delegate.openFrame(f);
332 public void resizeFrame(JComponent f, int newX, int newY, int newWidth,
339 delegate.resizeFrame(f, newX, newY, newWidth, newHeight);
343 public void setBoundsForFrame(JComponent f, int newX, int newY,
344 int newWidth, int newHeight)
346 delegate.setBoundsForFrame(f, newX, newY, newWidth, newHeight);
349 // All other methods, simply delegate
354 * Creates a new Desktop object.
360 * A note to implementors. It is ESSENTIAL that any activities that might
361 * block are spawned off as threads rather than waited for during this
366 doConfigureStructurePrefs();
367 setTitle("Jalview " + Cache.getProperty("VERSION"));
369 if (!Platform.isAMac())
371 // this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
375 this.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
381 APQHandlers.setAPQHandlers(this);
382 } catch (Throwable t)
384 System.out.println("Error setting APQHandlers: " + t.toString());
385 // t.printStackTrace();
388 addWindowListener(new WindowAdapter()
392 public void windowClosing(WindowEvent ev)
398 boolean selmemusage = Cache.getDefault("SHOW_MEMUSAGE", false);
400 boolean showjconsole = Cache.getDefault("SHOW_JAVA_CONSOLE", false);
401 desktop = new MyDesktopPane(selmemusage);
403 showMemusage.setSelected(selmemusage);
404 desktop.setBackground(Color.white);
406 getContentPane().setLayout(new BorderLayout());
407 // alternate config - have scrollbars - see notes in JAL-153
408 // JScrollPane sp = new JScrollPane();
409 // sp.getViewport().setView(desktop);
410 // getContentPane().add(sp, BorderLayout.CENTER);
412 // BH 2018 - just an experiment to try unclipped JInternalFrames.
415 getRootPane().putClientProperty("swingjs.overflow.hidden", "false");
418 getContentPane().add(desktop, BorderLayout.CENTER);
419 desktop.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
421 // This line prevents Windows Look&Feel resizing all new windows to maximum
422 // if previous window was maximised
423 desktop.setDesktopManager(new MyDesktopManager(
424 (Platform.isWindowsAndNotJS() ? new DefaultDesktopManager()
425 : Platform.isAMacAndNotJS()
426 ? new AquaInternalFrameManager(
427 desktop.getDesktopManager())
428 : desktop.getDesktopManager())));
430 Rectangle dims = getLastKnownDimensions("");
437 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
438 int xPos = Math.max(5, (screenSize.width - 900) / 2);
439 int yPos = Math.max(5, (screenSize.height - 650) / 2);
440 setBounds(xPos, yPos, 900, 650);
443 if (!Platform.isJS())
450 jconsole = new Console(this, showjconsole);
451 jconsole.setHeader(Cache.getVersionDetailsForConsole());
452 showConsole(showjconsole);
454 showNews.setVisible(false);
456 experimentalFeatures.setSelected(showExperimental());
458 getIdentifiersOrgData();
462 // Spawn a thread that shows the splashscreen
464 SwingUtilities.invokeLater(new Runnable()
469 new SplashScreen(true);
473 // Thread off a new instance of the file chooser - this reduces the time
475 // takes to open it later on.
476 new Thread(new Runnable()
481 Cache.log.debug("Filechooser init thread started.");
482 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
483 JalviewFileChooser.forRead(Cache.getProperty("LAST_DIRECTORY"),
485 Cache.log.debug("Filechooser init thread finished.");
488 // Add the service change listener
489 changeSupport.addJalviewPropertyChangeListener("services",
490 new PropertyChangeListener()
494 public void propertyChange(PropertyChangeEvent evt)
496 Cache.log.debug("Firing service changed event for "
497 + evt.getNewValue());
498 JalviewServicesChanged(evt);
503 this.setDropTarget(new java.awt.dnd.DropTarget(desktop, this));
505 this.addWindowListener(new WindowAdapter()
508 public void windowClosing(WindowEvent evt)
515 this.addMouseListener(ma = new MouseAdapter()
518 public void mousePressed(MouseEvent evt)
520 if (evt.isPopupTrigger()) // Mac
522 showPasteMenu(evt.getX(), evt.getY());
527 public void mouseReleased(MouseEvent evt)
529 if (evt.isPopupTrigger()) // Windows
531 showPasteMenu(evt.getX(), evt.getY());
535 desktop.addMouseListener(ma);
540 * Answers true if user preferences to enable experimental features is True
545 public boolean showExperimental()
547 String experimental = Cache.getDefault(EXPERIMENTAL_FEATURES,
548 Boolean.FALSE.toString());
549 return Boolean.valueOf(experimental).booleanValue();
552 public void doConfigureStructurePrefs()
554 // configure services
555 StructureSelectionManager ssm = StructureSelectionManager
556 .getStructureSelectionManager(this);
557 if (Cache.getDefault(Preferences.ADD_SS_ANN, true))
559 ssm.setAddTempFacAnnot(
560 Cache.getDefault(Preferences.ADD_TEMPFACT_ANN, true));
561 ssm.setProcessSecondaryStructure(
562 Cache.getDefault(Preferences.STRUCT_FROM_PDB, true));
563 ssm.setSecStructServices(
564 Cache.getDefault(Preferences.USE_RNAVIEW, true));
568 ssm.setAddTempFacAnnot(false);
569 ssm.setProcessSecondaryStructure(false);
570 ssm.setSecStructServices(false);
574 public void checkForNews()
576 final Desktop me = this;
577 // Thread off the news reader, in case there are connection problems.
578 new Thread(new Runnable()
583 Cache.log.debug("Starting news thread.");
584 jvnews = new BlogReader(me);
585 showNews.setVisible(true);
586 Cache.log.debug("Completed news thread.");
591 public void getIdentifiersOrgData()
593 // Thread off the identifiers fetcher
594 new Thread(new Runnable()
599 Cache.log.debug("Downloading data from identifiers.org");
602 UrlDownloadClient.download(IdOrgSettings.getUrl(),
603 IdOrgSettings.getDownloadLocation());
604 } catch (IOException e)
606 Cache.log.debug("Exception downloading identifiers.org data"
615 protected void showNews_actionPerformed(ActionEvent e)
617 showNews(showNews.isSelected());
620 void showNews(boolean visible)
622 Cache.log.debug((visible ? "Showing" : "Hiding") + " news.");
623 showNews.setSelected(visible);
624 if (visible && !jvnews.isVisible())
626 new Thread(new Runnable()
631 long now = System.currentTimeMillis();
632 Desktop.instance.setProgressBar(
633 MessageManager.getString("status.refreshing_news"), now);
634 jvnews.refreshNews();
635 Desktop.instance.setProgressBar(null, now);
643 * recover the last known dimensions for a jalview window
646 * - empty string is desktop, all other windows have unique prefix
647 * @return null or last known dimensions scaled to current geometry (if last
648 * window geom was known)
650 Rectangle getLastKnownDimensions(String windowName)
652 // TODO: lock aspect ratio for scaling desktop Bug #0058199
653 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
654 String x = Cache.getProperty(windowName + "SCREEN_X");
655 String y = Cache.getProperty(windowName + "SCREEN_Y");
656 String width = Cache.getProperty(windowName + "SCREEN_WIDTH");
657 String height = Cache.getProperty(windowName + "SCREEN_HEIGHT");
658 if ((x != null) && (y != null) && (width != null) && (height != null))
660 int ix = Integer.parseInt(x), iy = Integer.parseInt(y),
661 iw = Integer.parseInt(width), ih = Integer.parseInt(height);
662 if (Cache.getProperty("SCREENGEOMETRY_WIDTH") != null)
664 // attempt #1 - try to cope with change in screen geometry - this
665 // version doesn't preserve original jv aspect ratio.
666 // take ratio of current screen size vs original screen size.
667 double sw = ((1f * screenSize.width) / (1f * Integer
668 .parseInt(Cache.getProperty("SCREENGEOMETRY_WIDTH"))));
669 double sh = ((1f * screenSize.height) / (1f * Integer
670 .parseInt(Cache.getProperty("SCREENGEOMETRY_HEIGHT"))));
671 // rescale the bounds depending upon the current screen geometry.
672 ix = (int) (ix * sw);
673 iw = (int) (iw * sw);
674 iy = (int) (iy * sh);
675 ih = (int) (ih * sh);
676 while (ix >= screenSize.width)
679 "Window geometry location recall error: shifting horizontal to within screenbounds.");
680 ix -= screenSize.width;
682 while (iy >= screenSize.height)
685 "Window geometry location recall error: shifting vertical to within screenbounds.");
686 iy -= screenSize.height;
689 "Got last known dimensions for " + windowName + ": x:" + ix
690 + " y:" + iy + " width:" + iw + " height:" + ih);
692 // return dimensions for new instance
693 return new Rectangle(ix, iy, iw, ih);
698 void showPasteMenu(int x, int y)
700 JPopupMenu popup = new JPopupMenu();
701 JMenuItem item = new JMenuItem(
702 MessageManager.getString("label.paste_new_window"));
703 item.addActionListener(new ActionListener()
706 public void actionPerformed(ActionEvent evt)
713 popup.show(this, x, y);
720 Clipboard c = Toolkit.getDefaultToolkit().getSystemClipboard();
721 Transferable contents = c.getContents(this);
723 if (contents != null)
725 String file = (String) contents
726 .getTransferData(DataFlavor.stringFlavor);
728 FileFormatI format = new IdentifyFile().identify(file,
729 DataSourceType.PASTE);
731 new FileLoader().LoadFile(file, DataSourceType.PASTE, format);
734 } catch (Exception ex)
737 "Unable to paste alignment from system clipboard:\n" + ex);
742 * Adds and opens the given frame to the desktop
753 public static synchronized void addInternalFrame(
754 final JInternalFrame frame, String title, int w, int h)
756 addInternalFrame(frame, title, true, w, h, true, false);
760 * Add an internal frame to the Jalview desktop
767 * When true, display frame immediately, otherwise, caller must call
768 * setVisible themselves.
774 public static synchronized void addInternalFrame(
775 final JInternalFrame frame, String title, boolean makeVisible,
778 addInternalFrame(frame, title, makeVisible, w, h, true, false);
782 * Add an internal frame to the Jalview desktop and make it visible
795 public static synchronized void addInternalFrame(
796 final JInternalFrame frame, String title, int w, int h,
799 addInternalFrame(frame, title, true, w, h, resizable, false);
803 * Add an internal frame to the Jalview desktop
810 * When true, display frame immediately, otherwise, caller must call
811 * setVisible themselves.
818 * @param ignoreMinSize
819 * Do not set the default minimum size for frame
821 public static synchronized void addInternalFrame(
822 final JInternalFrame frame, String title, boolean makeVisible,
823 int w, int h, boolean resizable, boolean ignoreMinSize)
826 // TODO: allow callers to determine X and Y position of frame (eg. via
828 // TODO: consider fixing method to update entries in the window submenu with
829 // the current window title
831 frame.setTitle(title);
832 if (frame.getWidth() < 1 || frame.getHeight() < 1)
836 // THIS IS A PUBLIC STATIC METHOD, SO IT MAY BE CALLED EVEN IN
837 // A HEADLESS STATE WHEN NO DESKTOP EXISTS. MUST RETURN
838 // IF JALVIEW IS RUNNING HEADLESS
839 // ///////////////////////////////////////////////
840 if (instance == null || (System.getProperty("java.awt.headless") != null
841 && System.getProperty("java.awt.headless").equals("true")))
850 frame.setMinimumSize(
851 new Dimension(DEFAULT_MIN_WIDTH, DEFAULT_MIN_HEIGHT));
853 // Set default dimension for Alignment Frame window.
854 // The Alignment Frame window could be added from a number of places,
856 // I did this here in order not to miss out on any Alignment frame.
857 if (frame instanceof AlignFrame)
859 frame.setMinimumSize(new Dimension(ALIGN_FRAME_DEFAULT_MIN_WIDTH,
860 ALIGN_FRAME_DEFAULT_MIN_HEIGHT));
864 frame.setVisible(makeVisible);
865 frame.setClosable(true);
866 frame.setResizable(resizable);
867 frame.setMaximizable(resizable);
868 frame.setIconifiable(resizable);
869 frame.setOpaque(Platform.isJS());
871 if (frame.getX() < 1 && frame.getY() < 1)
873 frame.setLocation(xOffset * openFrameCount,
874 yOffset * ((openFrameCount - 1) % 10) + yOffset);
878 * add an entry for the new frame in the Window menu
879 * (and remove it when the frame is closed)
881 final JMenuItem menuItem = new JMenuItem(title);
882 frame.addInternalFrameListener(new InternalFrameAdapter()
885 public void internalFrameActivated(InternalFrameEvent evt)
887 JInternalFrame itf = desktop.getSelectedFrame();
890 if (itf instanceof AlignFrame)
892 Jalview.setCurrentAlignFrame((AlignFrame) itf);
899 public void internalFrameClosed(InternalFrameEvent evt)
901 PaintRefresher.RemoveComponent(frame);
904 * defensive check to prevent frames being
905 * added half off the window
907 if (openFrameCount > 0)
913 * ensure no reference to alignFrame retained by menu item listener
915 if (menuItem.getActionListeners().length > 0)
917 menuItem.removeActionListener(menuItem.getActionListeners()[0]);
919 windowMenu.remove(menuItem);
923 menuItem.addActionListener(new ActionListener()
926 public void actionPerformed(ActionEvent e)
930 frame.setSelected(true);
931 frame.setIcon(false);
932 } catch (java.beans.PropertyVetoException ex)
934 // System.err.println(ex.toString());
939 setKeyBindings(frame);
943 windowMenu.add(menuItem);
948 frame.setSelected(true);
949 frame.requestFocus();
950 } catch (java.beans.PropertyVetoException ve)
952 } catch (java.lang.ClassCastException cex)
955 "Squashed a possible GUI implementation error. If you can recreate this, please look at http://issues.jalview.org/browse/JAL-869",
961 * Add key bindings to a JInternalFrame so that Ctrl-W and Cmd-W will close
966 private static void setKeyBindings(JInternalFrame frame)
968 @SuppressWarnings("serial")
969 final Action closeAction = new AbstractAction()
972 public void actionPerformed(ActionEvent e)
979 * set up key bindings for Ctrl-W and Cmd-W, with the same (Close) action
981 KeyStroke ctrlWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
982 InputEvent.CTRL_DOWN_MASK);
983 KeyStroke cmdWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
984 ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx());
986 InputMap inputMap = frame
987 .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
988 String ctrlW = ctrlWKey.toString();
989 inputMap.put(ctrlWKey, ctrlW);
990 inputMap.put(cmdWKey, ctrlW);
992 ActionMap actionMap = frame.getActionMap();
993 actionMap.put(ctrlW, closeAction);
997 public void lostOwnership(Clipboard clipboard, Transferable contents)
1001 Desktop.jalviewClipboard = null;
1004 internalCopy = false;
1008 public void dragEnter(DropTargetDragEvent evt)
1013 public void dragExit(DropTargetEvent evt)
1018 public void dragOver(DropTargetDragEvent evt)
1023 public void dropActionChanged(DropTargetDragEvent evt)
1034 public void drop(DropTargetDropEvent evt)
1036 boolean success = true;
1037 // JAL-1552 - acceptDrop required before getTransferable call for
1038 // Java's Transferable for native dnd
1039 evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
1040 Transferable t = evt.getTransferable();
1041 List<Object> files = new ArrayList<>();
1042 List<DataSourceType> protocols = new ArrayList<>();
1046 Desktop.transferFromDropTarget(files, protocols, evt, t);
1047 } catch (Exception e)
1049 e.printStackTrace();
1057 for (int i = 0; i < files.size(); i++)
1059 // BH 2018 File or String
1060 Object file = files.get(i);
1061 String fileName = file.toString();
1062 DataSourceType protocol = (protocols == null)
1063 ? DataSourceType.FILE
1065 FileFormatI format = null;
1067 if (fileName.endsWith(".jar"))
1069 format = FileFormat.Jalview;
1074 format = new IdentifyFile().identify(file, protocol);
1076 if (file instanceof File)
1078 Platform.cacheFileData((File) file);
1080 new FileLoader().LoadFile(null, file, protocol, format);
1083 } catch (Exception ex)
1088 evt.dropComplete(success); // need this to ensure input focus is properly
1089 // transfered to any new windows created
1099 public void inputLocalFileMenuItem_actionPerformed(AlignViewport viewport)
1101 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
1102 JalviewFileChooser chooser = JalviewFileChooser.forRead(
1103 Cache.getProperty("LAST_DIRECTORY"), fileFormat,
1104 BackupFiles.getEnabled());
1106 chooser.setFileView(new JalviewFileView());
1107 chooser.setDialogTitle(
1108 MessageManager.getString("label.open_local_file"));
1109 chooser.setToolTipText(MessageManager.getString("action.open"));
1111 chooser.setResponseHandler(0, new Runnable()
1116 File selectedFile = chooser.getSelectedFile();
1117 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1119 FileFormatI format = chooser.getSelectedFormat();
1122 * Call IdentifyFile to verify the file contains what its extension implies.
1123 * Skip this step for dynamically added file formats, because
1124 * IdentifyFile does not know how to recognise them.
1126 if (FileFormats.getInstance().isIdentifiable(format))
1130 format = new IdentifyFile().identify(selectedFile,
1131 DataSourceType.FILE);
1132 } catch (FileFormatException e)
1134 // format = null; //??
1138 new FileLoader().LoadFile(viewport, selectedFile,
1139 DataSourceType.FILE, format);
1142 chooser.showOpenDialog(this);
1146 * Shows a dialog for input of a URL at which to retrieve alignment data
1151 public void inputURLMenuItem_actionPerformed(AlignViewport viewport)
1153 // This construct allows us to have a wider textfield
1155 JLabel label = new JLabel(
1156 MessageManager.getString("label.input_file_url"));
1158 JPanel panel = new JPanel(new GridLayout(2, 1));
1162 * the URL to fetch is
1163 * Java: an editable combobox with history
1164 * JS: (pending JAL-3038) a plain text field
1167 String urlBase = "http://www.";
1168 if (Platform.isJS())
1170 history = new JTextField(urlBase, 35);
1179 JComboBox<String> asCombo = new JComboBox<>();
1180 asCombo.setPreferredSize(new Dimension(400, 20));
1181 asCombo.setEditable(true);
1182 asCombo.addItem(urlBase);
1183 String historyItems = Cache.getProperty("RECENT_URL");
1184 if (historyItems != null)
1186 for (String token : historyItems.split("\\t"))
1188 asCombo.addItem(token);
1195 Object[] options = new Object[] { MessageManager.getString("action.ok"),
1196 MessageManager.getString("action.cancel") };
1197 Runnable action = new Runnable()
1202 @SuppressWarnings("unchecked")
1203 String url = (history instanceof JTextField
1204 ? ((JTextField) history).getText()
1205 : ((JComboBox<String>) history).getSelectedItem()
1208 if (url.toLowerCase().endsWith(".jar"))
1210 if (viewport != null)
1212 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1213 FileFormat.Jalview);
1217 new FileLoader().LoadFile(url, DataSourceType.URL,
1218 FileFormat.Jalview);
1223 FileFormatI format = null;
1226 format = new IdentifyFile().identify(url, DataSourceType.URL);
1227 } catch (FileFormatException e)
1229 // TODO revise error handling, distinguish between
1230 // URL not found and response not valid
1235 String msg = MessageManager
1236 .formatMessage("label.couldnt_locate", url);
1237 JvOptionPane.showInternalMessageDialog(Desktop.desktop, msg,
1238 MessageManager.getString("label.url_not_found"),
1239 JvOptionPane.WARNING_MESSAGE);
1244 if (viewport != null)
1246 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1251 new FileLoader().LoadFile(url, DataSourceType.URL, format);
1256 String dialogOption = MessageManager
1257 .getString("label.input_alignment_from_url");
1258 JvOptionPane.newOptionDialog(desktop).setResponseHandler(0, action)
1259 .showInternalDialog(panel, dialogOption,
1260 JvOptionPane.YES_NO_CANCEL_OPTION,
1261 JvOptionPane.PLAIN_MESSAGE, null, options,
1262 MessageManager.getString("action.ok"));
1266 * Opens the CutAndPaste window for the user to paste an alignment in to
1269 * - if not null, the pasted alignment is added to the current
1270 * alignment; if null, to a new alignment window
1273 public void inputTextboxMenuItem_actionPerformed(
1274 AlignmentViewPanel viewPanel)
1276 CutAndPasteTransfer cap = new CutAndPasteTransfer();
1277 cap.setForInput(viewPanel);
1278 Desktop.addInternalFrame(cap,
1279 MessageManager.getString("label.cut_paste_alignmen_file"), true,
1289 Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
1290 Cache.setProperty("SCREENGEOMETRY_WIDTH", screen.width + "");
1291 Cache.setProperty("SCREENGEOMETRY_HEIGHT", screen.height + "");
1292 storeLastKnownDimensions("", new Rectangle(getBounds().x, getBounds().y,
1293 getWidth(), getHeight()));
1295 if (jconsole != null)
1297 storeLastKnownDimensions("JAVA_CONSOLE_", jconsole.getBounds());
1298 jconsole.stopConsole();
1302 storeLastKnownDimensions("JALVIEW_RSS_WINDOW_", jvnews.getBounds());
1305 if (dialogExecutor != null)
1307 dialogExecutor.shutdownNow();
1309 closeAll_actionPerformed(null);
1311 if (groovyConsole != null)
1313 // suppress a possible repeat prompt to save script
1314 groovyConsole.setDirty(false);
1315 groovyConsole.exit();
1320 private void storeLastKnownDimensions(String string, Rectangle jc)
1322 Cache.log.debug("Storing last known dimensions for " + string + ": x:"
1323 + jc.x + " y:" + jc.y + " width:" + jc.width + " height:"
1326 Cache.setProperty(string + "SCREEN_X", jc.x + "");
1327 Cache.setProperty(string + "SCREEN_Y", jc.y + "");
1328 Cache.setProperty(string + "SCREEN_WIDTH", jc.width + "");
1329 Cache.setProperty(string + "SCREEN_HEIGHT", jc.height + "");
1339 public void aboutMenuItem_actionPerformed(ActionEvent e)
1341 new Thread(new Runnable()
1346 new SplashScreen(false);
1352 * Returns the html text for the About screen, including any available version
1353 * number, build details, author details and citation reference, but without
1354 * the enclosing {@code html} tags
1358 public String getAboutMessage()
1360 StringBuilder message = new StringBuilder(1024);
1361 message.append("<h1><strong>Version: ")
1362 .append(Cache.getProperty("VERSION")).append("</strong></h1>")
1363 .append("<strong>Built: <em>")
1364 .append(Cache.getDefault("BUILD_DATE", "unknown"))
1365 .append("</em> from ").append(Cache.getBuildDetailsForSplash())
1366 .append("</strong>");
1368 String latestVersion = Cache.getDefault("LATEST_VERSION", "Checking");
1369 if (latestVersion.equals("Checking"))
1371 // JBP removed this message for 2.11: May be reinstated in future version
1372 // message.append("<br>...Checking latest version...</br>");
1374 else if (!latestVersion.equals(Cache.getProperty("VERSION")))
1376 boolean red = false;
1377 if (Cache.getProperty("VERSION").toLowerCase()
1378 .indexOf("automated build") == -1)
1381 // Displayed when code version and jnlp version do not match and code
1382 // version is not a development build
1383 message.append("<div style=\"color: #FF0000;font-style: bold;\">");
1386 message.append("<br>!! Version ")
1387 .append(Cache.getDefault("LATEST_VERSION", "..Checking.."))
1388 .append(" is available for download from ")
1389 .append(Cache.getDefault("www.jalview.org",
1390 "http://www.jalview.org"))
1394 message.append("</div>");
1397 message.append("<br>Authors: ");
1398 message.append(Cache.getDefault("AUTHORFNAMES", DEFAULT_AUTHORS));
1399 message.append(CITATION);
1401 return message.toString();
1405 * Action on requesting Help documentation
1408 public void documentationMenuItem_actionPerformed()
1412 if (Platform.isJS())
1414 BrowserLauncher.openURL("http://www.jalview.org/help.html");
1423 Help.showHelpWindow();
1425 } catch (Exception ex)
1427 System.err.println("Error opening help: " + ex.getMessage());
1432 public void closeAll_actionPerformed(ActionEvent e)
1434 // TODO show a progress bar while closing?
1435 JInternalFrame[] frames = desktop.getAllFrames();
1436 for (int i = 0; i < frames.length; i++)
1440 frames[i].setClosed(true);
1441 } catch (java.beans.PropertyVetoException ex)
1445 Jalview.setCurrentAlignFrame(null);
1446 System.out.println("ALL CLOSED");
1449 * reset state of singleton objects as appropriate (clear down session state
1450 * when all windows are closed)
1452 StructureSelectionManager ssm = StructureSelectionManager
1453 .getStructureSelectionManager(this);
1461 public void raiseRelated_actionPerformed(ActionEvent e)
1463 reorderAssociatedWindows(false, false);
1467 public void minimizeAssociated_actionPerformed(ActionEvent e)
1469 reorderAssociatedWindows(true, false);
1472 void closeAssociatedWindows()
1474 reorderAssociatedWindows(false, true);
1480 * @seejalview.jbgui.GDesktop#garbageCollect_actionPerformed(java.awt.event.
1484 protected void garbageCollect_actionPerformed(ActionEvent e)
1486 // We simply collect the garbage
1487 Cache.log.debug("Collecting garbage...");
1489 Cache.log.debug("Finished garbage collection.");
1496 * jalview.jbgui.GDesktop#showMemusage_actionPerformed(java.awt.event.ActionEvent
1500 protected void showMemusage_actionPerformed(ActionEvent e)
1502 desktop.showMemoryUsage(showMemusage.isSelected());
1509 * jalview.jbgui.GDesktop#showConsole_actionPerformed(java.awt.event.ActionEvent
1513 protected void showConsole_actionPerformed(ActionEvent e)
1515 showConsole(showConsole.isSelected());
1518 Console jconsole = null;
1521 * control whether the java console is visible or not
1525 void showConsole(boolean selected)
1527 // TODO: decide if we should update properties file
1528 if (jconsole != null) // BH 2018
1530 showConsole.setSelected(selected);
1531 Cache.setProperty("SHOW_JAVA_CONSOLE",
1532 Boolean.valueOf(selected).toString());
1533 jconsole.setVisible(selected);
1537 void reorderAssociatedWindows(boolean minimize, boolean close)
1539 JInternalFrame[] frames = desktop.getAllFrames();
1540 if (frames == null || frames.length < 1)
1545 AlignmentViewport source = null, target = null;
1546 if (frames[0] instanceof AlignFrame)
1548 source = ((AlignFrame) frames[0]).getCurrentView();
1550 else if (frames[0] instanceof TreePanel)
1552 source = ((TreePanel) frames[0]).getViewPort();
1554 else if (frames[0] instanceof PCAPanel)
1556 source = ((PCAPanel) frames[0]).av;
1558 else if (frames[0].getContentPane() instanceof PairwiseAlignPanel)
1560 source = ((PairwiseAlignPanel) frames[0].getContentPane()).av;
1565 for (int i = 0; i < frames.length; i++)
1568 if (frames[i] == null)
1572 if (frames[i] instanceof AlignFrame)
1574 target = ((AlignFrame) frames[i]).getCurrentView();
1576 else if (frames[i] instanceof TreePanel)
1578 target = ((TreePanel) frames[i]).getViewPort();
1580 else if (frames[i] instanceof PCAPanel)
1582 target = ((PCAPanel) frames[i]).av;
1584 else if (frames[i].getContentPane() instanceof PairwiseAlignPanel)
1586 target = ((PairwiseAlignPanel) frames[i].getContentPane()).av;
1589 if (source == target)
1595 frames[i].setClosed(true);
1599 frames[i].setIcon(minimize);
1602 frames[i].toFront();
1606 } catch (java.beans.PropertyVetoException ex)
1621 protected void preferences_actionPerformed(ActionEvent e)
1623 Preferences.openPreferences();
1627 * Prompts the user to choose a file and then saves the Jalview state as a
1628 * Jalview project file
1631 public void saveState_actionPerformed()
1633 saveState_actionPerformed(false);
1636 public void saveState_actionPerformed(boolean saveAs)
1638 java.io.File projectFile = getProjectFile();
1639 // autoSave indicates we already have a file and don't need to ask
1640 boolean autoSave = projectFile != null && !saveAs
1641 && BackupFiles.getEnabled();
1643 // System.out.println("autoSave="+autoSave+", projectFile='"+projectFile+"',
1644 // saveAs="+saveAs+", Backups
1645 // "+(BackupFiles.getEnabled()?"enabled":"disabled"));
1647 boolean approveSave = false;
1650 JalviewFileChooser chooser = new JalviewFileChooser("jvp",
1653 chooser.setFileView(new JalviewFileView());
1654 chooser.setDialogTitle(MessageManager.getString("label.save_state"));
1656 int value = chooser.showSaveDialog(this);
1658 if (value == JalviewFileChooser.APPROVE_OPTION)
1660 projectFile = chooser.getSelectedFile();
1661 setProjectFile(projectFile);
1666 if (approveSave || autoSave)
1668 final Desktop me = this;
1669 final java.io.File chosenFile = projectFile;
1670 new Thread(new Runnable()
1675 // TODO: refactor to Jalview desktop session controller action.
1676 setProgressBar(MessageManager.formatMessage(
1677 "label.saving_jalview_project", new Object[]
1678 { chosenFile.getName() }), chosenFile.hashCode());
1679 Cache.setProperty("LAST_DIRECTORY", chosenFile.getParent());
1680 // TODO catch and handle errors for savestate
1681 // TODO prevent user from messing with the Desktop whilst we're saving
1684 boolean doBackup = BackupFiles.getEnabled();
1685 BackupFiles backupfiles = doBackup ? new BackupFiles(chosenFile)
1688 new Jalview2XML().saveState(
1689 doBackup ? backupfiles.getTempFile() : chosenFile);
1693 backupfiles.setWriteSuccess(true);
1694 backupfiles.rollBackupsAndRenameTempFile();
1696 } catch (OutOfMemoryError oom)
1698 new OOMWarning("Whilst saving current state to "
1699 + chosenFile.getName(), oom);
1700 } catch (Exception ex)
1702 Cache.log.error("Problems whilst trying to save to "
1703 + chosenFile.getName(), ex);
1704 JvOptionPane.showMessageDialog(me,
1705 MessageManager.formatMessage(
1706 "label.error_whilst_saving_current_state_to",
1708 { chosenFile.getName() }),
1709 MessageManager.getString("label.couldnt_save_project"),
1710 JvOptionPane.WARNING_MESSAGE);
1712 setProgressBar(null, chosenFile.hashCode());
1719 public void saveAsState_actionPerformed(ActionEvent e)
1721 saveState_actionPerformed(true);
1724 private void setProjectFile(File choice)
1726 this.projectFile = choice;
1729 public File getProjectFile()
1731 return this.projectFile;
1735 * Shows a file chooser dialog and tries to read in the selected file as a
1739 public void loadState_actionPerformed()
1741 final String[] suffix = new String[] { "jvp", "jar" };
1742 final String[] desc = new String[] { "Jalview Project",
1743 "Jalview Project (old)" };
1744 JalviewFileChooser chooser = new JalviewFileChooser(
1745 Cache.getProperty("LAST_DIRECTORY"), suffix, desc,
1746 "Jalview Project", true, BackupFiles.getEnabled()); // last two
1750 chooser.setFileView(new JalviewFileView());
1751 chooser.setDialogTitle(MessageManager.getString("label.restore_state"));
1752 chooser.setResponseHandler(0, new Runnable()
1757 File selectedFile = chooser.getSelectedFile();
1758 setProjectFile(selectedFile);
1759 String choice = selectedFile.getAbsolutePath();
1760 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1761 new Thread(new Runnable()
1768 new Jalview2XML().loadJalviewAlign(selectedFile);
1769 } catch (OutOfMemoryError oom)
1771 new OOMWarning("Whilst loading project from " + choice, oom);
1772 } catch (Exception ex)
1775 "Problems whilst loading project from " + choice, ex);
1776 JvOptionPane.showMessageDialog(Desktop.desktop,
1777 MessageManager.formatMessage(
1778 "label.error_whilst_loading_project_from",
1782 .getString("label.couldnt_load_project"),
1783 JvOptionPane.WARNING_MESSAGE);
1786 }, "Project Loader").start();
1790 chooser.showOpenDialog(this);
1794 public void inputSequence_actionPerformed(ActionEvent e)
1796 new SequenceFetcher(this);
1799 JPanel progressPanel;
1801 ArrayList<JPanel> fileLoadingPanels = new ArrayList<>();
1803 public void startLoading(final Object fileName)
1805 if (fileLoadingCount == 0)
1807 fileLoadingPanels.add(addProgressPanel(MessageManager
1808 .formatMessage("label.loading_file", new Object[]
1814 private JPanel addProgressPanel(String string)
1816 if (progressPanel == null)
1818 progressPanel = new JPanel(new GridLayout(1, 1));
1819 totalProgressCount = 0;
1820 instance.getContentPane().add(progressPanel, BorderLayout.SOUTH);
1822 JPanel thisprogress = new JPanel(new BorderLayout(10, 5));
1823 JProgressBar progressBar = new JProgressBar();
1824 progressBar.setIndeterminate(true);
1826 thisprogress.add(new JLabel(string), BorderLayout.WEST);
1828 thisprogress.add(progressBar, BorderLayout.CENTER);
1829 progressPanel.add(thisprogress);
1830 ((GridLayout) progressPanel.getLayout()).setRows(
1831 ((GridLayout) progressPanel.getLayout()).getRows() + 1);
1832 ++totalProgressCount;
1833 instance.validate();
1834 return thisprogress;
1837 int totalProgressCount = 0;
1839 private void removeProgressPanel(JPanel progbar)
1841 if (progressPanel != null)
1843 synchronized (progressPanel)
1845 progressPanel.remove(progbar);
1846 GridLayout gl = (GridLayout) progressPanel.getLayout();
1847 gl.setRows(gl.getRows() - 1);
1848 if (--totalProgressCount < 1)
1850 this.getContentPane().remove(progressPanel);
1851 progressPanel = null;
1858 public void stopLoading()
1861 if (fileLoadingCount < 1)
1863 while (fileLoadingPanels.size() > 0)
1865 removeProgressPanel(fileLoadingPanels.remove(0));
1867 fileLoadingPanels.clear();
1868 fileLoadingCount = 0;
1873 public static int getViewCount(String alignmentId)
1875 AlignmentViewport[] aps = getViewports(alignmentId);
1876 return (aps == null) ? 0 : aps.length;
1881 * @param alignmentId
1882 * - if null, all sets are returned
1883 * @return all AlignmentPanels concerning the alignmentId sequence set
1885 public static AlignmentPanel[] getAlignmentPanels(String alignmentId)
1887 if (Desktop.desktop == null)
1889 // no frames created and in headless mode
1890 // TODO: verify that frames are recoverable when in headless mode
1893 List<AlignmentPanel> aps = new ArrayList<>();
1894 AlignFrame[] frames = getAlignFrames();
1899 for (AlignFrame af : frames)
1901 for (AlignmentPanel ap : af.alignPanels)
1903 if (alignmentId == null
1904 || alignmentId.equals(ap.av.getSequenceSetId()))
1910 if (aps.size() == 0)
1914 AlignmentPanel[] vap = aps.toArray(new AlignmentPanel[aps.size()]);
1919 * get all the viewports on an alignment.
1921 * @param sequenceSetId
1922 * unique alignment id (may be null - all viewports returned in that
1924 * @return all viewports on the alignment bound to sequenceSetId
1926 public static AlignmentViewport[] getViewports(String sequenceSetId)
1928 List<AlignmentViewport> viewp = new ArrayList<>();
1929 if (desktop != null)
1931 AlignFrame[] frames = Desktop.getAlignFrames();
1933 for (AlignFrame afr : frames)
1935 if (sequenceSetId == null || afr.getViewport().getSequenceSetId()
1936 .equals(sequenceSetId))
1938 if (afr.alignPanels != null)
1940 for (AlignmentPanel ap : afr.alignPanels)
1942 if (sequenceSetId == null
1943 || sequenceSetId.equals(ap.av.getSequenceSetId()))
1951 viewp.add(afr.getViewport());
1955 if (viewp.size() > 0)
1957 return viewp.toArray(new AlignmentViewport[viewp.size()]);
1964 * Explode the views in the given frame into separate AlignFrame
1968 public static void explodeViews(AlignFrame af)
1970 int size = af.alignPanels.size();
1976 // FIXME: ideally should use UI interface API
1977 FeatureSettings viewFeatureSettings = (af.featureSettings != null
1978 && af.featureSettings.isOpen()) ? af.featureSettings : null;
1979 Rectangle fsBounds = af.getFeatureSettingsGeometry();
1980 for (int i = 0; i < size; i++)
1982 AlignmentPanel ap = af.alignPanels.get(i);
1984 AlignFrame newaf = new AlignFrame(ap);
1986 // transfer reference for existing feature settings to new alignFrame
1987 if (ap == af.alignPanel)
1989 if (viewFeatureSettings != null && viewFeatureSettings.fr.ap == ap)
1991 newaf.featureSettings = viewFeatureSettings;
1993 newaf.setFeatureSettingsGeometry(fsBounds);
1997 * Restore the view's last exploded frame geometry if known. Multiple
1998 * views from one exploded frame share and restore the same (frame)
1999 * position and size.
2001 Rectangle geometry = ap.av.getExplodedGeometry();
2002 if (geometry != null)
2004 newaf.setBounds(geometry);
2007 ap.av.setGatherViewsHere(false);
2009 addInternalFrame(newaf, af.getTitle(), AlignFrame.DEFAULT_WIDTH,
2010 AlignFrame.DEFAULT_HEIGHT);
2011 // and materialise a new feature settings dialog instance for the new
2013 // (closes the old as if 'OK' was pressed)
2014 if (ap == af.alignPanel && newaf.featureSettings != null
2015 && newaf.featureSettings.isOpen()
2016 && af.alignPanel.getAlignViewport().isShowSequenceFeatures())
2018 newaf.showFeatureSettingsUI();
2022 af.featureSettings = null;
2023 af.alignPanels.clear();
2024 af.closeMenuItem_actionPerformed(true);
2029 * Gather expanded views (separate AlignFrame's) with the same sequence set
2030 * identifier back in to this frame as additional views, and close the
2031 * expanded views. Note the expanded frames may themselves have multiple
2032 * views. We take the lot.
2036 public void gatherViews(AlignFrame source)
2038 source.viewport.setGatherViewsHere(true);
2039 source.viewport.setExplodedGeometry(source.getBounds());
2040 JInternalFrame[] frames = desktop.getAllFrames();
2041 String viewId = source.viewport.getSequenceSetId();
2042 for (int t = 0; t < frames.length; t++)
2044 if (frames[t] instanceof AlignFrame && frames[t] != source)
2046 AlignFrame af = (AlignFrame) frames[t];
2047 boolean gatherThis = false;
2048 for (int a = 0; a < af.alignPanels.size(); a++)
2050 AlignmentPanel ap = af.alignPanels.get(a);
2051 if (viewId.equals(ap.av.getSequenceSetId()))
2054 ap.av.setGatherViewsHere(false);
2055 ap.av.setExplodedGeometry(af.getBounds());
2056 source.addAlignmentPanel(ap, false);
2062 if (af.featureSettings != null && af.featureSettings.isOpen())
2064 if (source.featureSettings == null)
2066 // preserve the feature settings geometry for this frame
2067 source.featureSettings = af.featureSettings;
2068 source.setFeatureSettingsGeometry(
2069 af.getFeatureSettingsGeometry());
2073 // close it and forget
2074 af.featureSettings.close();
2077 af.alignPanels.clear();
2078 af.closeMenuItem_actionPerformed(true);
2083 // refresh the feature setting UI for the source frame if it exists
2084 if (source.featureSettings != null && source.featureSettings.isOpen())
2086 source.showFeatureSettingsUI();
2090 public JInternalFrame[] getAllFrames()
2092 return desktop.getAllFrames();
2096 * Checks the given url to see if it gives a response indicating that the user
2097 * should be informed of a new questionnaire.
2101 public void checkForQuestionnaire(String url)
2103 UserQuestionnaireCheck jvq = new UserQuestionnaireCheck(url);
2104 // javax.swing.SwingUtilities.invokeLater(jvq);
2105 new Thread(jvq).start();
2108 public void checkURLLinks()
2110 // Thread off the URL link checker
2111 addDialogThread(new Runnable()
2116 if (Cache.getDefault("CHECKURLLINKS", true))
2118 // check what the actual links are - if it's just the default don't
2119 // bother with the warning
2120 List<String> links = Preferences.sequenceUrlLinks
2123 // only need to check links if there is one with a
2124 // SEQUENCE_ID which is not the default EMBL_EBI link
2125 ListIterator<String> li = links.listIterator();
2126 boolean check = false;
2127 List<JLabel> urls = new ArrayList<>();
2128 while (li.hasNext())
2130 String link = li.next();
2131 if (link.contains(jalview.util.UrlConstants.SEQUENCE_ID)
2132 && !UrlConstants.isDefaultString(link))
2135 int barPos = link.indexOf("|");
2136 String urlMsg = barPos == -1 ? link
2137 : link.substring(0, barPos) + ": "
2138 + link.substring(barPos + 1);
2139 urls.add(new JLabel(urlMsg));
2147 // ask user to check in case URL links use old style tokens
2148 // ($SEQUENCE_ID$ for sequence id _or_ accession id)
2149 JPanel msgPanel = new JPanel();
2150 msgPanel.setLayout(new BoxLayout(msgPanel, BoxLayout.PAGE_AXIS));
2151 msgPanel.add(Box.createVerticalGlue());
2152 JLabel msg = new JLabel(MessageManager
2153 .getString("label.SEQUENCE_ID_for_DB_ACCESSION1"));
2154 JLabel msg2 = new JLabel(MessageManager
2155 .getString("label.SEQUENCE_ID_for_DB_ACCESSION2"));
2157 for (JLabel url : urls)
2163 final JCheckBox jcb = new JCheckBox(
2164 MessageManager.getString("label.do_not_display_again"));
2165 jcb.addActionListener(new ActionListener()
2168 public void actionPerformed(ActionEvent e)
2170 // update Cache settings for "don't show this again"
2171 boolean showWarningAgain = !jcb.isSelected();
2172 Cache.setProperty("CHECKURLLINKS",
2173 Boolean.valueOf(showWarningAgain).toString());
2178 JvOptionPane.showMessageDialog(Desktop.desktop, msgPanel,
2180 .getString("label.SEQUENCE_ID_no_longer_used"),
2181 JvOptionPane.WARNING_MESSAGE);
2188 * Proxy class for JDesktopPane which optionally displays the current memory
2189 * usage and highlights the desktop area with a red bar if free memory runs
2194 public class MyDesktopPane extends JDesktopPane implements Runnable
2196 private static final float ONE_MB = 1048576f;
2198 boolean showMemoryUsage = false;
2202 java.text.NumberFormat df;
2204 float maxMemory, allocatedMemory, freeMemory, totalFreeMemory,
2207 public MyDesktopPane(boolean showMemoryUsage)
2209 showMemoryUsage(showMemoryUsage);
2212 public void showMemoryUsage(boolean showMemory)
2214 this.showMemoryUsage = showMemory;
2217 Thread worker = new Thread(this);
2223 public boolean isShowMemoryUsage()
2225 return showMemoryUsage;
2231 df = java.text.NumberFormat.getNumberInstance();
2232 df.setMaximumFractionDigits(2);
2233 runtime = Runtime.getRuntime();
2235 while (showMemoryUsage)
2239 maxMemory = runtime.maxMemory() / ONE_MB;
2240 allocatedMemory = runtime.totalMemory() / ONE_MB;
2241 freeMemory = runtime.freeMemory() / ONE_MB;
2242 totalFreeMemory = freeMemory + (maxMemory - allocatedMemory);
2244 percentUsage = (totalFreeMemory / maxMemory) * 100;
2246 // if (percentUsage < 20)
2248 // border1 = BorderFactory.createMatteBorder(12, 12, 12, 12,
2250 // instance.set.setBorder(border1);
2253 // sleep after showing usage
2255 } catch (Exception ex)
2257 ex.printStackTrace();
2263 public void paintComponent(Graphics g)
2265 if (showMemoryUsage && g != null && df != null)
2267 if (percentUsage < 20)
2269 g.setColor(Color.red);
2271 FontMetrics fm = g.getFontMetrics();
2274 g.drawString(MessageManager.formatMessage("label.memory_stats",
2276 { df.format(totalFreeMemory), df.format(maxMemory),
2277 df.format(percentUsage) }),
2278 10, getHeight() - fm.getHeight());
2285 * Accessor method to quickly get all the AlignmentFrames loaded.
2287 * @return an array of AlignFrame, or null if none found
2289 public static AlignFrame[] getAlignFrames()
2291 if (Jalview.isHeadlessMode())
2293 // Desktop.desktop is null in headless mode
2294 return new AlignFrame[] { Jalview.currentAlignFrame };
2297 JInternalFrame[] frames = Desktop.desktop.getAllFrames();
2303 List<AlignFrame> avp = new ArrayList<>();
2305 for (int i = frames.length - 1; i > -1; i--)
2307 if (frames[i] instanceof AlignFrame)
2309 avp.add((AlignFrame) frames[i]);
2311 else if (frames[i] instanceof SplitFrame)
2314 * Also check for a split frame containing an AlignFrame
2316 GSplitFrame sf = (GSplitFrame) frames[i];
2317 if (sf.getTopFrame() instanceof AlignFrame)
2319 avp.add((AlignFrame) sf.getTopFrame());
2321 if (sf.getBottomFrame() instanceof AlignFrame)
2323 avp.add((AlignFrame) sf.getBottomFrame());
2327 if (avp.size() == 0)
2331 AlignFrame afs[] = avp.toArray(new AlignFrame[avp.size()]);
2336 * Returns an array of any AppJmol frames in the Desktop (or null if none).
2340 public GStructureViewer[] getJmols()
2342 JInternalFrame[] frames = Desktop.desktop.getAllFrames();
2348 List<GStructureViewer> avp = new ArrayList<>();
2350 for (int i = frames.length - 1; i > -1; i--)
2352 if (frames[i] instanceof AppJmol)
2354 GStructureViewer af = (GStructureViewer) frames[i];
2358 if (avp.size() == 0)
2362 GStructureViewer afs[] = avp.toArray(new GStructureViewer[avp.size()]);
2367 * Add Groovy Support to Jalview
2370 public void groovyShell_actionPerformed()
2374 openGroovyConsole();
2375 } catch (Exception ex)
2377 Cache.log.error("Groovy Shell Creation failed.", ex);
2378 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
2380 MessageManager.getString("label.couldnt_create_groovy_shell"),
2381 MessageManager.getString("label.groovy_support_failed"),
2382 JvOptionPane.ERROR_MESSAGE);
2387 * Open the Groovy console
2389 void openGroovyConsole()
2391 if (groovyConsole == null)
2393 groovyConsole = new groovy.ui.Console();
2394 groovyConsole.setVariable("Jalview", this);
2395 groovyConsole.run();
2398 * We allow only one console at a time, so that AlignFrame menu option
2399 * 'Calculate | Run Groovy script' is unambiguous.
2400 * Disable 'Groovy Console', and enable 'Run script', when the console is
2401 * opened, and the reverse when it is closed
2403 Window window = (Window) groovyConsole.getFrame();
2404 window.addWindowListener(new WindowAdapter()
2407 public void windowClosed(WindowEvent e)
2410 * rebind CMD-Q from Groovy Console to Jalview Quit
2413 enableExecuteGroovy(false);
2419 * show Groovy console window (after close and reopen)
2421 ((Window) groovyConsole.getFrame()).setVisible(true);
2424 * if we got this far, enable 'Run Groovy' in AlignFrame menus
2425 * and disable opening a second console
2427 enableExecuteGroovy(true);
2431 * Bind Ctrl/Cmd-Q to Quit - for reset as Groovy Console takes over this
2432 * binding when opened
2434 protected void addQuitHandler()
2437 .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
2439 .getKeyStroke(KeyEvent.VK_Q,
2440 jalview.util.ShortcutKeyMaskExWrapper
2441 .getMenuShortcutKeyMaskEx()),
2443 getRootPane().getActionMap().put("Quit", new AbstractAction()
2446 public void actionPerformed(ActionEvent e)
2454 * Enable or disable 'Run Groovy script' in AlignFrame calculate menus
2457 * true if Groovy console is open
2459 public void enableExecuteGroovy(boolean enabled)
2462 * disable opening a second Groovy console
2463 * (or re-enable when the console is closed)
2465 groovyShell.setEnabled(!enabled);
2467 AlignFrame[] alignFrames = getAlignFrames();
2468 if (alignFrames != null)
2470 for (AlignFrame af : alignFrames)
2472 af.setGroovyEnabled(enabled);
2478 * Progress bars managed by the IProgressIndicator method.
2480 private Hashtable<Long, JPanel> progressBars;
2482 private Hashtable<Long, IProgressIndicatorHandler> progressBarHandlers;
2487 * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
2490 public void setProgressBar(String message, long id)
2492 // Platform.timeCheck("Desktop " + message, Platform.TIME_MARK);
2494 if (progressBars == null)
2496 progressBars = new Hashtable<>();
2497 progressBarHandlers = new Hashtable<>();
2500 if (progressBars.get(Long.valueOf(id)) != null)
2502 JPanel panel = progressBars.remove(Long.valueOf(id));
2503 if (progressBarHandlers.contains(Long.valueOf(id)))
2505 progressBarHandlers.remove(Long.valueOf(id));
2507 removeProgressPanel(panel);
2511 progressBars.put(Long.valueOf(id), addProgressPanel(message));
2518 * @see jalview.gui.IProgressIndicator#registerHandler(long,
2519 * jalview.gui.IProgressIndicatorHandler)
2522 public void registerHandler(final long id,
2523 final IProgressIndicatorHandler handler)
2525 if (progressBarHandlers == null
2526 || !progressBars.containsKey(Long.valueOf(id)))
2528 throw new Error(MessageManager.getString(
2529 "error.call_setprogressbar_before_registering_handler"));
2531 progressBarHandlers.put(Long.valueOf(id), handler);
2532 final JPanel progressPanel = progressBars.get(Long.valueOf(id));
2533 if (handler.canCancel())
2535 JButton cancel = new JButton(
2536 MessageManager.getString("action.cancel"));
2537 final IProgressIndicator us = this;
2538 cancel.addActionListener(new ActionListener()
2542 public void actionPerformed(ActionEvent e)
2544 handler.cancelActivity(id);
2545 us.setProgressBar(MessageManager
2546 .formatMessage("label.cancelled_params", new Object[]
2547 { ((JLabel) progressPanel.getComponent(0)).getText() }),
2551 progressPanel.add(cancel, BorderLayout.EAST);
2557 * @return true if any progress bars are still active
2560 public boolean operationInProgress()
2562 if (progressBars != null && progressBars.size() > 0)
2570 * This will return the first AlignFrame holding the given viewport instance.
2571 * It will break if there are more than one AlignFrames viewing a particular
2575 * @return alignFrame for viewport
2577 public static AlignFrame getAlignFrameFor(AlignViewportI viewport)
2579 if (desktop != null)
2581 AlignmentPanel[] aps = getAlignmentPanels(
2582 viewport.getSequenceSetId());
2583 for (int panel = 0; aps != null && panel < aps.length; panel++)
2585 if (aps[panel] != null && aps[panel].av == viewport)
2587 return aps[panel].alignFrame;
2594 public VamsasApplication getVamsasApplication()
2596 // TODO: JAL-3311 remove remaining code from Jalview relating to VAMSAS
2602 * flag set if jalview GUI is being operated programmatically
2604 private boolean inBatchMode = false;
2607 * check if jalview GUI is being operated programmatically
2609 * @return inBatchMode
2611 public boolean isInBatchMode()
2617 * set flag if jalview GUI is being operated programmatically
2619 * @param inBatchMode
2621 public void setInBatchMode(boolean inBatchMode)
2623 this.inBatchMode = inBatchMode;
2626 public void startServiceDiscovery()
2628 startServiceDiscovery(false);
2631 public void startServiceDiscovery(boolean blocking)
2633 boolean alive = true;
2634 Thread t0 = null, t1 = null, t2 = null;
2635 // JAL-940 - JALVIEW 1 services are now being EOLed as of JABA 2.1 release
2638 // todo: changesupport handlers need to be transferred
2639 if (discoverer == null)
2641 discoverer = new jalview.ws.jws1.Discoverer();
2642 // register PCS handler for desktop.
2643 discoverer.addPropertyChangeListener(changeSupport);
2645 // JAL-940 - disabled JWS1 service configuration - always start discoverer
2646 // until we phase out completely
2647 (t0 = new Thread(discoverer)).start();
2650 if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
2652 t2 = jalview.ws.jws2.Jws2Discoverer.getDiscoverer()
2653 .startDiscoverer(changeSupport);
2657 // TODO: do rest service discovery
2666 } catch (Exception e)
2669 alive = (t1 != null && t1.isAlive()) || (t2 != null && t2.isAlive())
2670 || (t3 != null && t3.isAlive())
2671 || (t0 != null && t0.isAlive());
2677 * called to check if the service discovery process completed successfully.
2681 protected void JalviewServicesChanged(PropertyChangeEvent evt)
2683 if (evt.getNewValue() == null || evt.getNewValue() instanceof Vector)
2685 final String ermsg = jalview.ws.jws2.Jws2Discoverer.getDiscoverer()
2686 .getErrorMessages();
2689 if (Cache.getDefault("SHOW_WSDISCOVERY_ERRORS", true))
2691 if (serviceChangedDialog == null)
2693 // only run if we aren't already displaying one of these.
2694 addDialogThread(serviceChangedDialog = new Runnable()
2701 * JalviewDialog jd =new JalviewDialog() {
2703 * @Override protected void cancelPressed() { // TODO
2704 * Auto-generated method stub
2706 * }@Override protected void okPressed() { // TODO
2707 * Auto-generated method stub
2709 * }@Override protected void raiseClosed() { // TODO
2710 * Auto-generated method stub
2712 * } }; jd.initDialogFrame(new
2713 * JLabel("<html><table width=\"450\"><tr><td>" + ermsg +
2714 * "<br/>It may be that you have invalid JABA URLs in your web service preferences,"
2715 * + " or mis-configured HTTP proxy settings.<br/>" +
2716 * "Check the <em>Connections</em> and <em>Web services</em> tab of the"
2718 * " Tools->Preferences dialog box to change them.</td></tr></table></html>"
2719 * ), true, true, "Web Service Configuration Problem", 450,
2722 * jd.waitForInput();
2724 JvOptionPane.showConfirmDialog(Desktop.desktop,
2725 new JLabel("<html><table width=\"450\"><tr><td>"
2726 + ermsg + "</td></tr></table>"
2727 + "<p>It may be that you have invalid JABA URLs<br/>in your web service preferences,"
2728 + "<br>or as a command-line argument, or mis-configured HTTP proxy settings.</p>"
2729 + "<p>Check the <em>Connections</em> and <em>Web services</em> tab<br/>of the"
2730 + " Tools->Preferences dialog box to change them.</p></html>"),
2731 "Web Service Configuration Problem",
2732 JvOptionPane.DEFAULT_OPTION,
2733 JvOptionPane.ERROR_MESSAGE);
2734 serviceChangedDialog = null;
2743 "Errors reported by JABA discovery service. Check web services preferences.\n"
2750 private Runnable serviceChangedDialog = null;
2753 * start a thread to open a URL in the configured browser. Pops up a warning
2754 * dialog to the user if there is an exception when calling out to the browser
2759 public static void showUrl(final String url)
2761 showUrl(url, Desktop.instance);
2765 * Like showUrl but allows progress handler to be specified
2769 * (null) or object implementing IProgressIndicator
2771 public static void showUrl(final String url,
2772 final IProgressIndicator progress)
2774 new Thread(new Runnable()
2781 if (progress != null)
2783 progress.setProgressBar(MessageManager
2784 .formatMessage("status.opening_params", new Object[]
2785 { url }), this.hashCode());
2787 jalview.util.BrowserLauncher.openURL(url);
2788 } catch (Exception ex)
2790 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
2792 .getString("label.web_browser_not_found_unix"),
2793 MessageManager.getString("label.web_browser_not_found"),
2794 JvOptionPane.WARNING_MESSAGE);
2796 ex.printStackTrace();
2798 if (progress != null)
2800 progress.setProgressBar(null, this.hashCode());
2806 public static WsParamSetManager wsparamManager = null;
2808 public static ParamManager getUserParameterStore()
2810 if (wsparamManager == null)
2812 wsparamManager = new WsParamSetManager();
2814 return wsparamManager;
2818 * static hyperlink handler proxy method for use by Jalview's internal windows
2822 public static void hyperlinkUpdate(HyperlinkEvent e)
2824 if (e.getEventType() == EventType.ACTIVATED)
2829 url = e.getURL().toString();
2830 Desktop.showUrl(url);
2831 } catch (Exception x)
2835 if (Cache.log != null)
2837 Cache.log.error("Couldn't handle string " + url + " as a URL.");
2842 "Couldn't handle string " + url + " as a URL.");
2845 // ignore any exceptions due to dud links.
2852 * single thread that handles display of dialogs to user.
2854 ExecutorService dialogExecutor = Executors.newSingleThreadExecutor();
2857 * flag indicating if dialogExecutor should try to acquire a permit
2859 private volatile boolean dialogPause = true;
2864 private java.util.concurrent.Semaphore block = new Semaphore(0);
2866 private static groovy.ui.Console groovyConsole;
2869 * add another dialog thread to the queue
2873 public void addDialogThread(final Runnable prompter)
2875 dialogExecutor.submit(new Runnable()
2885 } catch (InterruptedException x)
2889 if (instance == null)
2895 SwingUtilities.invokeAndWait(prompter);
2896 } catch (Exception q)
2898 Cache.log.warn("Unexpected Exception in dialog thread.", q);
2904 public void startDialogQueue()
2906 // set the flag so we don't pause waiting for another permit and semaphore
2907 // the current task to begin
2908 dialogPause = false;
2913 * Outputs an image of the desktop to file in EPS format, after prompting the
2914 * user for choice of Text or Lineart character rendering (unless a preference
2915 * has been set). The file name is generated as
2918 * Jalview_snapshot_nnnnn.eps where nnnnn is the current timestamp in milliseconds
2922 protected void snapShotWindow_actionPerformed(ActionEvent e)
2924 // currently the menu option to do this is not shown
2927 int width = getWidth();
2928 int height = getHeight();
2930 "Jalview_snapshot_" + System.currentTimeMillis() + ".eps");
2931 ImageWriterI writer = new ImageWriterI()
2934 public void exportImage(Graphics g) throws Exception
2937 Cache.log.info("Successfully written snapshot to file "
2938 + of.getAbsolutePath());
2941 String title = "View of desktop";
2942 ImageExporter exporter = new ImageExporter(writer, null, TYPE.EPS,
2944 exporter.doExport(of, this, width, height, title);
2948 * Explode the views in the given SplitFrame into separate SplitFrame windows.
2949 * This respects (remembers) any previous 'exploded geometry' i.e. the size
2950 * and location last time the view was expanded (if any). However it does not
2951 * remember the split pane divider location - this is set to match the
2952 * 'exploding' frame.
2956 public void explodeViews(SplitFrame sf)
2958 AlignFrame oldTopFrame = (AlignFrame) sf.getTopFrame();
2959 AlignFrame oldBottomFrame = (AlignFrame) sf.getBottomFrame();
2960 List<? extends AlignmentViewPanel> topPanels = oldTopFrame
2962 List<? extends AlignmentViewPanel> bottomPanels = oldBottomFrame
2964 int viewCount = topPanels.size();
2971 * Processing in reverse order works, forwards order leaves the first panels
2972 * not visible. I don't know why!
2974 for (int i = viewCount - 1; i >= 0; i--)
2977 * Make new top and bottom frames. These take over the respective
2978 * AlignmentPanel objects, including their AlignmentViewports, so the
2979 * cdna/protein relationships between the viewports is carried over to the
2982 * explodedGeometry holds the (x, y) position of the previously exploded
2983 * SplitFrame, and the (width, height) of the AlignFrame component
2985 AlignmentPanel topPanel = (AlignmentPanel) topPanels.get(i);
2986 AlignFrame newTopFrame = new AlignFrame(topPanel);
2987 newTopFrame.setSize(oldTopFrame.getSize());
2988 newTopFrame.setVisible(true);
2989 Rectangle geometry = ((AlignViewport) topPanel.getAlignViewport())
2990 .getExplodedGeometry();
2991 if (geometry != null)
2993 newTopFrame.setSize(geometry.getSize());
2996 AlignmentPanel bottomPanel = (AlignmentPanel) bottomPanels.get(i);
2997 AlignFrame newBottomFrame = new AlignFrame(bottomPanel);
2998 newBottomFrame.setSize(oldBottomFrame.getSize());
2999 newBottomFrame.setVisible(true);
3000 geometry = ((AlignViewport) bottomPanel.getAlignViewport())
3001 .getExplodedGeometry();
3002 if (geometry != null)
3004 newBottomFrame.setSize(geometry.getSize());
3007 topPanel.av.setGatherViewsHere(false);
3008 bottomPanel.av.setGatherViewsHere(false);
3009 JInternalFrame splitFrame = new SplitFrame(newTopFrame,
3011 if (geometry != null)
3013 splitFrame.setLocation(geometry.getLocation());
3015 Desktop.addInternalFrame(splitFrame, sf.getTitle(), -1, -1);
3019 * Clear references to the panels (now relocated in the new SplitFrames)
3020 * before closing the old SplitFrame.
3023 bottomPanels.clear();
3028 * Gather expanded split frames, sharing the same pairs of sequence set ids,
3029 * back into the given SplitFrame as additional views. Note that the gathered
3030 * frames may themselves have multiple views.
3034 public void gatherViews(GSplitFrame source)
3037 * special handling of explodedGeometry for a view within a SplitFrame: - it
3038 * holds the (x, y) position of the enclosing SplitFrame, and the (width,
3039 * height) of the AlignFrame component
3041 AlignFrame myTopFrame = (AlignFrame) source.getTopFrame();
3042 AlignFrame myBottomFrame = (AlignFrame) source.getBottomFrame();
3043 myTopFrame.viewport.setExplodedGeometry(new Rectangle(source.getX(),
3044 source.getY(), myTopFrame.getWidth(), myTopFrame.getHeight()));
3045 myBottomFrame.viewport
3046 .setExplodedGeometry(new Rectangle(source.getX(), source.getY(),
3047 myBottomFrame.getWidth(), myBottomFrame.getHeight()));
3048 myTopFrame.viewport.setGatherViewsHere(true);
3049 myBottomFrame.viewport.setGatherViewsHere(true);
3050 String topViewId = myTopFrame.viewport.getSequenceSetId();
3051 String bottomViewId = myBottomFrame.viewport.getSequenceSetId();
3053 JInternalFrame[] frames = desktop.getAllFrames();
3054 for (JInternalFrame frame : frames)
3056 if (frame instanceof SplitFrame && frame != source)
3058 SplitFrame sf = (SplitFrame) frame;
3059 AlignFrame topFrame = (AlignFrame) sf.getTopFrame();
3060 AlignFrame bottomFrame = (AlignFrame) sf.getBottomFrame();
3061 boolean gatherThis = false;
3062 for (int a = 0; a < topFrame.alignPanels.size(); a++)
3064 AlignmentPanel topPanel = topFrame.alignPanels.get(a);
3065 AlignmentPanel bottomPanel = bottomFrame.alignPanels.get(a);
3066 if (topViewId.equals(topPanel.av.getSequenceSetId())
3067 && bottomViewId.equals(bottomPanel.av.getSequenceSetId()))
3070 topPanel.av.setGatherViewsHere(false);
3071 bottomPanel.av.setGatherViewsHere(false);
3072 topPanel.av.setExplodedGeometry(
3073 new Rectangle(sf.getLocation(), topFrame.getSize()));
3074 bottomPanel.av.setExplodedGeometry(
3075 new Rectangle(sf.getLocation(), bottomFrame.getSize()));
3076 myTopFrame.addAlignmentPanel(topPanel, false);
3077 myBottomFrame.addAlignmentPanel(bottomPanel, false);
3083 topFrame.getAlignPanels().clear();
3084 bottomFrame.getAlignPanels().clear();
3091 * The dust settles...give focus to the tab we did this from.
3093 myTopFrame.setDisplayedView(myTopFrame.alignPanel);
3096 public static groovy.ui.Console getGroovyConsole()
3098 return groovyConsole;
3102 * handles the payload of a drag and drop event.
3104 * TODO refactor to desktop utilities class
3107 * - Data source strings extracted from the drop event
3109 * - protocol for each data source extracted from the drop event
3113 * - the payload from the drop event
3116 public static void transferFromDropTarget(List<Object> files,
3117 List<DataSourceType> protocols, DropTargetDropEvent evt,
3118 Transferable t) throws Exception
3121 // BH 2018 changed List<String> to List<Object> to allow for File from
3124 // DataFlavor[] flavors = t.getTransferDataFlavors();
3125 // for (int i = 0; i < flavors.length; i++) {
3126 // if (flavors[i].isFlavorJavaFileListType()) {
3127 // evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
3128 // List<File> list = (List<File>) t.getTransferData(flavors[i]);
3129 // for (int j = 0; j < list.size(); j++) {
3130 // File file = (File) list.get(j);
3131 // byte[] data = getDroppedFileBytes(file);
3132 // fileName.setText(file.getName() + " - " + data.length + " " +
3133 // evt.getLocation());
3134 // JTextArea target = (JTextArea) ((DropTarget)
3135 // evt.getSource()).getComponent();
3136 // target.setText(new String(data));
3138 // dtde.dropComplete(true);
3143 DataFlavor uriListFlavor = new DataFlavor(
3144 "text/uri-list;class=java.lang.String"), urlFlavour = null;
3147 urlFlavour = new DataFlavor(
3148 "application/x-java-url; class=java.net.URL");
3149 } catch (ClassNotFoundException cfe)
3151 Cache.log.debug("Couldn't instantiate the URL dataflavor.", cfe);
3154 if (urlFlavour != null && t.isDataFlavorSupported(urlFlavour))
3159 java.net.URL url = (URL) t.getTransferData(urlFlavour);
3160 // nb: java 8 osx bug https://bugs.openjdk.java.net/browse/JDK-8156099
3161 // means url may be null.
3164 protocols.add(DataSourceType.URL);
3165 files.add(url.toString());
3166 Cache.log.debug("Drop handled as URL dataflavor "
3167 + files.get(files.size() - 1));
3172 if (Platform.isAMacAndNotJS())
3175 "Please ignore plist error - occurs due to problem with java 8 on OSX");
3178 } catch (Throwable ex)
3180 Cache.log.debug("URL drop handler failed.", ex);
3183 if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
3185 // Works on Windows and MacOSX
3186 Cache.log.debug("Drop handled as javaFileListFlavor");
3187 for (Object file : (List) t
3188 .getTransferData(DataFlavor.javaFileListFlavor))
3191 protocols.add(DataSourceType.FILE);
3196 // Unix like behaviour
3197 boolean added = false;
3199 if (t.isDataFlavorSupported(uriListFlavor))
3201 Cache.log.debug("Drop handled as uriListFlavor");
3202 // This is used by Unix drag system
3203 data = (String) t.getTransferData(uriListFlavor);
3207 // fallback to text: workaround - on OSX where there's a JVM bug
3208 Cache.log.debug("standard URIListFlavor failed. Trying text");
3209 // try text fallback
3210 DataFlavor textDf = new DataFlavor(
3211 "text/plain;class=java.lang.String");
3212 if (t.isDataFlavorSupported(textDf))
3214 data = (String) t.getTransferData(textDf);
3217 Cache.log.debug("Plain text drop content returned "
3218 + (data == null ? "Null - failed" : data));
3223 while (protocols.size() < files.size())
3225 Cache.log.debug("Adding missing FILE protocol for "
3226 + files.get(protocols.size()));
3227 protocols.add(DataSourceType.FILE);
3229 for (java.util.StringTokenizer st = new java.util.StringTokenizer(
3230 data, "\r\n"); st.hasMoreTokens();)
3233 String s = st.nextToken();
3234 if (s.startsWith("#"))
3236 // the line is a comment (as per the RFC 2483)
3239 java.net.URI uri = new java.net.URI(s);
3240 if (uri.getScheme().toLowerCase().startsWith("http"))
3242 protocols.add(DataSourceType.URL);
3243 files.add(uri.toString());
3247 // otherwise preserve old behaviour: catch all for file objects
3248 java.io.File file = new java.io.File(uri);
3249 protocols.add(DataSourceType.FILE);
3250 files.add(file.toString());
3255 if (Cache.log.isDebugEnabled())
3257 if (data == null || !added)
3260 if (t.getTransferDataFlavors() != null
3261 && t.getTransferDataFlavors().length > 0)
3264 "Couldn't resolve drop data. Here are the supported flavors:");
3265 for (DataFlavor fl : t.getTransferDataFlavors())
3268 "Supported transfer dataflavor: " + fl.toString());
3269 Object df = t.getTransferData(fl);
3272 Cache.log.debug("Retrieves: " + df);
3276 Cache.log.debug("Retrieved nothing");
3282 Cache.log.debug("Couldn't resolve dataflavor for drop: "
3288 if (Platform.isWindowsAndNotJS())
3290 Cache.log.debug("Scanning dropped content for Windows Link Files");
3292 // resolve any .lnk files in the file drop
3293 for (int f = 0; f < files.size(); f++)
3295 String source = files.get(f).toString().toLowerCase();
3296 if (protocols.get(f).equals(DataSourceType.FILE)
3297 && (source.endsWith(".lnk") || source.endsWith(".url")
3298 || source.endsWith(".site")))
3302 Object obj = files.get(f);
3303 File lf = (obj instanceof File ? (File) obj
3304 : new File((String) obj));
3305 // process link file to get a URL
3306 Cache.log.debug("Found potential link file: " + lf);
3307 WindowsShortcut wscfile = new WindowsShortcut(lf);
3308 String fullname = wscfile.getRealFilename();
3309 protocols.set(f, FormatAdapter.checkProtocol(fullname));
3310 files.set(f, fullname);
3311 Cache.log.debug("Parsed real filename " + fullname
3312 + " to extract protocol: " + protocols.get(f));
3313 } catch (Exception ex)
3316 "Couldn't parse " + files.get(f) + " as a link file.",
3325 * Sets the Preferences property for experimental features to True or False
3326 * depending on the state of the controlling menu item
3329 protected void showExperimental_actionPerformed(boolean selected)
3331 Cache.setProperty(EXPERIMENTAL_FEATURES, Boolean.toString(selected));
3335 * Answers a (possibly empty) list of any structure viewer frames (currently
3336 * for either Jmol or Chimera) which are currently open. This may optionally
3337 * be restricted to viewers of a specified class, or viewers linked to a
3338 * specified alignment panel.
3341 * if not null, only return viewers linked to this panel
3342 * @param structureViewerClass
3343 * if not null, only return viewers of this class
3346 public List<StructureViewerBase> getStructureViewers(
3347 AlignmentPanel apanel,
3348 Class<? extends StructureViewerBase> structureViewerClass)
3350 List<StructureViewerBase> result = new ArrayList<>();
3351 JInternalFrame[] frames = Desktop.instance.getAllFrames();
3353 for (JInternalFrame frame : frames)
3355 if (frame instanceof StructureViewerBase)
3357 if (structureViewerClass == null
3358 || structureViewerClass.isInstance(frame))
3361 || ((StructureViewerBase) frame).isLinkedWith(apanel))
3363 result.add((StructureViewerBase) frame);