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.ExecutionException;
63 import java.util.concurrent.ExecutorService;
64 import java.util.concurrent.Executors;
65 import java.util.concurrent.Future;
66 import java.util.concurrent.FutureTask;
67 import java.util.concurrent.Semaphore;
69 import javax.swing.AbstractAction;
70 import javax.swing.Action;
71 import javax.swing.ActionMap;
72 import javax.swing.Box;
73 import javax.swing.BoxLayout;
74 import javax.swing.DefaultDesktopManager;
75 import javax.swing.DesktopManager;
76 import javax.swing.InputMap;
77 import javax.swing.JButton;
78 import javax.swing.JCheckBox;
79 import javax.swing.JComboBox;
80 import javax.swing.JComponent;
81 import javax.swing.JDesktopPane;
82 import javax.swing.JInternalFrame;
83 import javax.swing.JLabel;
84 import javax.swing.JMenuItem;
85 import javax.swing.JPanel;
86 import javax.swing.JPopupMenu;
87 import javax.swing.JProgressBar;
88 import javax.swing.JTextField;
89 import javax.swing.KeyStroke;
90 import javax.swing.SwingUtilities;
91 import javax.swing.event.HyperlinkEvent;
92 import javax.swing.event.HyperlinkEvent.EventType;
93 import javax.swing.event.InternalFrameAdapter;
94 import javax.swing.event.InternalFrameEvent;
96 import org.stackoverflowusers.file.WindowsShortcut;
98 import jalview.api.AlignViewportI;
99 import jalview.api.AlignmentViewPanel;
100 import jalview.bin.Cache;
101 import jalview.bin.Jalview;
102 import jalview.gui.ImageExporter.ImageWriterI;
103 import jalview.io.BackupFiles;
104 import jalview.io.DataSourceType;
105 import jalview.io.FileFormat;
106 import jalview.io.FileFormatException;
107 import jalview.io.FileFormatI;
108 import jalview.io.FileFormats;
109 import jalview.io.FileLoader;
110 import jalview.io.FormatAdapter;
111 import jalview.io.IdentifyFile;
112 import jalview.io.JalviewFileChooser;
113 import jalview.io.JalviewFileView;
114 import jalview.jbgui.GSplitFrame;
115 import jalview.jbgui.GStructureViewer;
116 import jalview.project.Jalview2XML;
117 import jalview.structure.StructureSelectionManager;
118 import jalview.urls.IdOrgSettings;
119 import jalview.util.BrowserLauncher;
120 import jalview.util.ImageMaker.TYPE;
121 import jalview.util.MessageManager;
122 import jalview.util.Platform;
123 import jalview.util.ShortcutKeyMaskExWrapper;
124 import jalview.util.UrlConstants;
125 import jalview.viewmodel.AlignmentViewport;
126 import jalview.ws.WSDiscovererI;
127 import jalview.ws.params.ParamManager;
128 import jalview.ws.utils.UrlDownloadClient;
135 * @version $Revision: 1.155 $
137 public class Desktop extends jalview.jbgui.GDesktop
138 implements DropTargetListener, ClipboardOwner, IProgressIndicator,
139 jalview.api.StructureSelectionManagerProvider
141 private static final String CITATION = "<br><br>Development managed by The Barton Group, University of Dundee, Scotland, UK.<br>"
142 + "<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"
143 + "<br><br>If you use Jalview, please cite:"
144 + "<br>Waterhouse, A.M., Procter, J.B., Martin, D.M.A, Clamp, M. and Barton, G. J. (2009)"
145 + "<br>Jalview Version 2 - a multiple sequence alignment editor and analysis workbench"
146 + "<br>Bioinformatics doi: 10.1093/bioinformatics/btp033";
148 private static final String DEFAULT_AUTHORS = "The Jalview Authors (See AUTHORS file for current list)";
150 private static int DEFAULT_MIN_WIDTH = 300;
152 private static int DEFAULT_MIN_HEIGHT = 250;
154 private static int ALIGN_FRAME_DEFAULT_MIN_WIDTH = 600;
156 private static int ALIGN_FRAME_DEFAULT_MIN_HEIGHT = 70;
158 private static final String EXPERIMENTAL_FEATURES = "EXPERIMENTAL_FEATURES";
160 protected static final String CONFIRM_KEYBOARD_QUIT = "CONFIRM_KEYBOARD_QUIT";
162 public static HashMap<String, FileWriter> savingFiles = new HashMap<String, FileWriter>();
164 private JalviewChangeSupport changeSupport = new JalviewChangeSupport();
167 * news reader - null if it was never started.
169 private BlogReader jvnews = null;
171 private File projectFile;
175 * @see jalview.gui.JalviewChangeSupport#addJalviewPropertyChangeListener(java.beans.PropertyChangeListener)
177 public void addJalviewPropertyChangeListener(
178 PropertyChangeListener listener)
180 changeSupport.addJalviewPropertyChangeListener(listener);
184 * @param propertyName
186 * @see jalview.gui.JalviewChangeSupport#addJalviewPropertyChangeListener(java.lang.String,
187 * java.beans.PropertyChangeListener)
189 public void addJalviewPropertyChangeListener(String propertyName,
190 PropertyChangeListener listener)
192 changeSupport.addJalviewPropertyChangeListener(propertyName, listener);
196 * @param propertyName
198 * @see jalview.gui.JalviewChangeSupport#removeJalviewPropertyChangeListener(java.lang.String,
199 * java.beans.PropertyChangeListener)
201 public void removeJalviewPropertyChangeListener(String propertyName,
202 PropertyChangeListener listener)
204 changeSupport.removeJalviewPropertyChangeListener(propertyName,
208 /** Singleton Desktop instance */
209 public static Desktop instance;
211 public static MyDesktopPane desktop;
213 public static MyDesktopPane getDesktop()
215 // BH 2018 could use currentThread() here as a reference to a
216 // Hashtable<Thread, MyDesktopPane> in JavaScript
220 static int openFrameCount = 0;
222 static final int xOffset = 30;
224 static final int yOffset = 30;
226 public static jalview.ws.jws1.Discoverer discoverer;
228 public static Object[] jalviewClipboard;
230 public static boolean internalCopy = false;
232 static int fileLoadingCount = 0;
234 class MyDesktopManager implements DesktopManager
237 private DesktopManager delegate;
239 public MyDesktopManager(DesktopManager delegate)
241 this.delegate = delegate;
245 public void activateFrame(JInternalFrame f)
249 delegate.activateFrame(f);
250 } catch (NullPointerException npe)
252 Point p = getMousePosition();
253 instance.showPasteMenu(p.x, p.y);
258 public void beginDraggingFrame(JComponent f)
260 delegate.beginDraggingFrame(f);
264 public void beginResizingFrame(JComponent f, int direction)
266 delegate.beginResizingFrame(f, direction);
270 public void closeFrame(JInternalFrame f)
272 delegate.closeFrame(f);
276 public void deactivateFrame(JInternalFrame f)
278 delegate.deactivateFrame(f);
282 public void deiconifyFrame(JInternalFrame f)
284 delegate.deiconifyFrame(f);
288 public void dragFrame(JComponent f, int newX, int newY)
294 delegate.dragFrame(f, newX, newY);
298 public void endDraggingFrame(JComponent f)
300 delegate.endDraggingFrame(f);
305 public void endResizingFrame(JComponent f)
307 delegate.endResizingFrame(f);
312 public void iconifyFrame(JInternalFrame f)
314 delegate.iconifyFrame(f);
318 public void maximizeFrame(JInternalFrame f)
320 delegate.maximizeFrame(f);
324 public void minimizeFrame(JInternalFrame f)
326 delegate.minimizeFrame(f);
330 public void openFrame(JInternalFrame f)
332 delegate.openFrame(f);
336 public void resizeFrame(JComponent f, int newX, int newY, int newWidth,
343 delegate.resizeFrame(f, newX, newY, newWidth, newHeight);
347 public void setBoundsForFrame(JComponent f, int newX, int newY,
348 int newWidth, int newHeight)
350 delegate.setBoundsForFrame(f, newX, newY, newWidth, newHeight);
353 // All other methods, simply delegate
358 * Creates a new Desktop object.
364 * A note to implementors. It is ESSENTIAL that any activities that might
365 * block are spawned off as threads rather than waited for during this
370 doConfigureStructurePrefs();
371 setTitle("Jalview " + Cache.getProperty("VERSION"));
373 if (!Platform.isAMac())
375 // this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
379 this.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
385 APQHandlers.setAPQHandlers(this);
386 } catch (Throwable t)
388 System.out.println("Error setting APQHandlers: " + t.toString());
389 // t.printStackTrace();
392 addWindowListener(new WindowAdapter()
396 public void windowClosing(WindowEvent ev)
402 boolean selmemusage = Cache.getDefault("SHOW_MEMUSAGE",
405 boolean showjconsole = Cache.getDefault("SHOW_JAVA_CONSOLE",
407 desktop = new MyDesktopPane(selmemusage);
409 showMemusage.setSelected(selmemusage);
410 desktop.setBackground(Color.white);
412 getContentPane().setLayout(new BorderLayout());
413 // alternate config - have scrollbars - see notes in JAL-153
414 // JScrollPane sp = new JScrollPane();
415 // sp.getViewport().setView(desktop);
416 // getContentPane().add(sp, BorderLayout.CENTER);
418 // BH 2018 - just an experiment to try unclipped JInternalFrames.
421 getRootPane().putClientProperty("swingjs.overflow.hidden", "false");
424 getContentPane().add(desktop, BorderLayout.CENTER);
425 desktop.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
427 // This line prevents Windows Look&Feel resizing all new windows to maximum
428 // if previous window was maximised
429 desktop.setDesktopManager(new MyDesktopManager(
430 (Platform.isWindowsAndNotJS() ? new DefaultDesktopManager()
431 : Platform.isAMacAndNotJS()
432 ? new AquaInternalFrameManager(
433 desktop.getDesktopManager())
434 : desktop.getDesktopManager())));
436 Rectangle dims = getLastKnownDimensions("");
443 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
444 int xPos = Math.max(5, (screenSize.width - 900) / 2);
445 int yPos = Math.max(5, (screenSize.height - 650) / 2);
446 setBounds(xPos, yPos, 900, 650);
449 if (!Platform.isJS())
456 jconsole = new Console(this, showjconsole);
457 jconsole.setHeader(Cache.getVersionDetailsForConsole());
458 showConsole(showjconsole);
460 showNews.setVisible(false);
462 experimentalFeatures.setSelected(showExperimental());
464 getIdentifiersOrgData();
468 // Spawn a thread that shows the splashscreen
470 SwingUtilities.invokeLater(new Runnable()
475 new SplashScreen(true);
479 // Thread off a new instance of the file chooser - this reduces the time
481 // takes to open it later on.
482 new Thread(new Runnable()
487 Cache.log.debug("Filechooser init thread started.");
488 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
489 JalviewFileChooser.forRead(Cache.getProperty("LAST_DIRECTORY"),
491 Cache.log.debug("Filechooser init thread finished.");
494 // Add the service change listener
495 changeSupport.addJalviewPropertyChangeListener("services",
496 new PropertyChangeListener()
500 public void propertyChange(PropertyChangeEvent evt)
502 Cache.log.debug("Firing service changed event for "
503 + evt.getNewValue());
504 JalviewServicesChanged(evt);
509 this.setDropTarget(new java.awt.dnd.DropTarget(desktop, this));
511 this.addWindowListener(new WindowAdapter()
514 public void windowClosing(WindowEvent evt)
521 this.addMouseListener(ma = new MouseAdapter()
524 public void mousePressed(MouseEvent evt)
526 if (evt.isPopupTrigger()) // Mac
528 showPasteMenu(evt.getX(), evt.getY());
533 public void mouseReleased(MouseEvent evt)
535 if (evt.isPopupTrigger()) // Windows
537 showPasteMenu(evt.getX(), evt.getY());
541 desktop.addMouseListener(ma);
546 * Answers true if user preferences to enable experimental features is True
551 public boolean showExperimental()
553 String experimental = Cache.getDefault(EXPERIMENTAL_FEATURES,
554 Boolean.FALSE.toString());
555 return Boolean.valueOf(experimental).booleanValue();
558 public void doConfigureStructurePrefs()
560 // configure services
561 StructureSelectionManager ssm = StructureSelectionManager
562 .getStructureSelectionManager(this);
563 if (Cache.getDefault(Preferences.ADD_SS_ANN, true))
565 ssm.setAddTempFacAnnot(Cache
566 .getDefault(Preferences.ADD_TEMPFACT_ANN, true));
567 ssm.setProcessSecondaryStructure(Cache
568 .getDefault(Preferences.STRUCT_FROM_PDB, true));
569 ssm.setSecStructServices(
570 Cache.getDefault(Preferences.USE_RNAVIEW, true));
574 ssm.setAddTempFacAnnot(false);
575 ssm.setProcessSecondaryStructure(false);
576 ssm.setSecStructServices(false);
580 public void checkForNews()
582 final Desktop me = this;
583 // Thread off the news reader, in case there are connection problems.
584 new Thread(new Runnable()
589 Cache.log.debug("Starting news thread.");
590 jvnews = new BlogReader(me);
591 showNews.setVisible(true);
592 Cache.log.debug("Completed news thread.");
597 public void getIdentifiersOrgData()
599 // Thread off the identifiers fetcher
600 new Thread(new Runnable()
605 Cache.log.debug("Downloading data from identifiers.org");
608 UrlDownloadClient.download(IdOrgSettings.getUrl(),
609 IdOrgSettings.getDownloadLocation());
610 } catch (IOException e)
612 Cache.log.debug("Exception downloading identifiers.org data"
621 protected void showNews_actionPerformed(ActionEvent e)
623 showNews(showNews.isSelected());
626 void showNews(boolean visible)
628 Cache.log.debug((visible ? "Showing" : "Hiding") + " news.");
629 showNews.setSelected(visible);
630 if (visible && !jvnews.isVisible())
632 new Thread(new Runnable()
637 long now = System.currentTimeMillis();
638 Desktop.instance.setProgressBar(
639 MessageManager.getString("status.refreshing_news"), now);
640 jvnews.refreshNews();
641 Desktop.instance.setProgressBar(null, now);
649 * recover the last known dimensions for a jalview window
652 * - empty string is desktop, all other windows have unique prefix
653 * @return null or last known dimensions scaled to current geometry (if last
654 * window geom was known)
656 Rectangle getLastKnownDimensions(String windowName)
658 // TODO: lock aspect ratio for scaling desktop Bug #0058199
659 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
660 String x = Cache.getProperty(windowName + "SCREEN_X");
661 String y = Cache.getProperty(windowName + "SCREEN_Y");
663 .getProperty(windowName + "SCREEN_WIDTH");
664 String height = Cache
665 .getProperty(windowName + "SCREEN_HEIGHT");
666 if ((x != null) && (y != null) && (width != null) && (height != null))
668 int ix = Integer.parseInt(x), iy = Integer.parseInt(y),
669 iw = Integer.parseInt(width), ih = Integer.parseInt(height);
670 if (Cache.getProperty("SCREENGEOMETRY_WIDTH") != null)
672 // attempt #1 - try to cope with change in screen geometry - this
673 // version doesn't preserve original jv aspect ratio.
674 // take ratio of current screen size vs original screen size.
675 double sw = ((1f * screenSize.width) / (1f * Integer.parseInt(
676 Cache.getProperty("SCREENGEOMETRY_WIDTH"))));
677 double sh = ((1f * screenSize.height) / (1f * Integer.parseInt(
678 Cache.getProperty("SCREENGEOMETRY_HEIGHT"))));
679 // rescale the bounds depending upon the current screen geometry.
680 ix = (int) (ix * sw);
681 iw = (int) (iw * sw);
682 iy = (int) (iy * sh);
683 ih = (int) (ih * sh);
684 while (ix >= screenSize.width)
687 "Window geometry location recall error: shifting horizontal to within screenbounds.");
688 ix -= screenSize.width;
690 while (iy >= screenSize.height)
693 "Window geometry location recall error: shifting vertical to within screenbounds.");
694 iy -= screenSize.height;
697 "Got last known dimensions for " + windowName + ": x:" + ix
698 + " y:" + iy + " width:" + iw + " height:" + ih);
700 // return dimensions for new instance
701 return new Rectangle(ix, iy, iw, ih);
706 void showPasteMenu(int x, int y)
708 JPopupMenu popup = new JPopupMenu();
709 JMenuItem item = new JMenuItem(
710 MessageManager.getString("label.paste_new_window"));
711 item.addActionListener(new ActionListener()
714 public void actionPerformed(ActionEvent evt)
721 popup.show(this, x, y);
728 Clipboard c = Toolkit.getDefaultToolkit().getSystemClipboard();
729 Transferable contents = c.getContents(this);
731 if (contents != null)
733 String file = (String) contents
734 .getTransferData(DataFlavor.stringFlavor);
736 FileFormatI format = new IdentifyFile().identify(file,
737 DataSourceType.PASTE);
739 new FileLoader().LoadFile(file, DataSourceType.PASTE, format);
742 } catch (Exception ex)
745 "Unable to paste alignment from system clipboard:\n" + ex);
750 * Adds and opens the given frame to the desktop
761 public static synchronized void addInternalFrame(
762 final JInternalFrame frame, String title, int w, int h)
764 addInternalFrame(frame, title, true, w, h, true, false);
768 * Add an internal frame to the Jalview desktop
775 * When true, display frame immediately, otherwise, caller must call
776 * setVisible themselves.
782 public static synchronized void addInternalFrame(
783 final JInternalFrame frame, String title, boolean makeVisible,
786 addInternalFrame(frame, title, makeVisible, w, h, true, false);
790 * Add an internal frame to the Jalview desktop and make it visible
803 public static synchronized void addInternalFrame(
804 final JInternalFrame frame, String title, int w, int h,
807 addInternalFrame(frame, title, true, w, h, resizable, false);
811 * Add an internal frame to the Jalview desktop
818 * When true, display frame immediately, otherwise, caller must call
819 * setVisible themselves.
826 * @param ignoreMinSize
827 * Do not set the default minimum size for frame
829 public static synchronized void addInternalFrame(
830 final JInternalFrame frame, String title, boolean makeVisible,
831 int w, int h, boolean resizable, boolean ignoreMinSize)
834 // TODO: allow callers to determine X and Y position of frame (eg. via
836 // TODO: consider fixing method to update entries in the window submenu with
837 // the current window title
839 frame.setTitle(title);
840 if (frame.getWidth() < 1 || frame.getHeight() < 1)
844 // THIS IS A PUBLIC STATIC METHOD, SO IT MAY BE CALLED EVEN IN
845 // A HEADLESS STATE WHEN NO DESKTOP EXISTS. MUST RETURN
846 // IF JALVIEW IS RUNNING HEADLESS
847 // ///////////////////////////////////////////////
848 if (instance == null || (System.getProperty("java.awt.headless") != null
849 && System.getProperty("java.awt.headless").equals("true")))
858 frame.setMinimumSize(
859 new Dimension(DEFAULT_MIN_WIDTH, DEFAULT_MIN_HEIGHT));
861 // Set default dimension for Alignment Frame window.
862 // The Alignment Frame window could be added from a number of places,
864 // I did this here in order not to miss out on any Alignment frame.
865 if (frame instanceof AlignFrame)
867 frame.setMinimumSize(new Dimension(ALIGN_FRAME_DEFAULT_MIN_WIDTH,
868 ALIGN_FRAME_DEFAULT_MIN_HEIGHT));
872 frame.setVisible(makeVisible);
873 frame.setClosable(true);
874 frame.setResizable(resizable);
875 frame.setMaximizable(resizable);
876 frame.setIconifiable(resizable);
877 frame.setOpaque(Platform.isJS());
879 if (frame.getX() < 1 && frame.getY() < 1)
881 frame.setLocation(xOffset * openFrameCount,
882 yOffset * ((openFrameCount - 1) % 10) + yOffset);
886 * add an entry for the new frame in the Window menu
887 * (and remove it when the frame is closed)
889 final JMenuItem menuItem = new JMenuItem(title);
890 frame.addInternalFrameListener(new InternalFrameAdapter()
893 public void internalFrameActivated(InternalFrameEvent evt)
895 JInternalFrame itf = desktop.getSelectedFrame();
898 if (itf instanceof AlignFrame)
900 Jalview.setCurrentAlignFrame((AlignFrame) itf);
907 public void internalFrameClosed(InternalFrameEvent evt)
909 PaintRefresher.RemoveComponent(frame);
912 * defensive check to prevent frames being
913 * added half off the window
915 if (openFrameCount > 0)
921 * ensure no reference to alignFrame retained by menu item listener
923 if (menuItem.getActionListeners().length > 0)
925 menuItem.removeActionListener(menuItem.getActionListeners()[0]);
927 windowMenu.remove(menuItem);
931 menuItem.addActionListener(new ActionListener()
934 public void actionPerformed(ActionEvent e)
938 frame.setSelected(true);
939 frame.setIcon(false);
940 } catch (java.beans.PropertyVetoException ex)
942 // System.err.println(ex.toString());
947 setKeyBindings(frame);
951 windowMenu.add(menuItem);
956 frame.setSelected(true);
957 frame.requestFocus();
958 } catch (java.beans.PropertyVetoException ve)
960 } catch (java.lang.ClassCastException cex)
963 "Squashed a possible GUI implementation error. If you can recreate this, please look at http://issues.jalview.org/browse/JAL-869",
969 * Add key bindings to a JInternalFrame so that Ctrl-W and Cmd-W will close the
974 private static void setKeyBindings(JInternalFrame frame)
976 @SuppressWarnings("serial")
977 final Action closeAction = new AbstractAction()
980 public void actionPerformed(ActionEvent e)
987 * set up key bindings for Ctrl-W and Cmd-W, with the same (Close) action
989 KeyStroke ctrlWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
990 InputEvent.CTRL_DOWN_MASK);
991 KeyStroke cmdWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
992 ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx());
994 InputMap inputMap = frame
995 .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
996 String ctrlW = ctrlWKey.toString();
997 inputMap.put(ctrlWKey, ctrlW);
998 inputMap.put(cmdWKey, ctrlW);
1000 ActionMap actionMap = frame.getActionMap();
1001 actionMap.put(ctrlW, closeAction);
1005 public void lostOwnership(Clipboard clipboard, Transferable contents)
1009 Desktop.jalviewClipboard = null;
1012 internalCopy = false;
1016 public void dragEnter(DropTargetDragEvent evt)
1021 public void dragExit(DropTargetEvent evt)
1026 public void dragOver(DropTargetDragEvent evt)
1031 public void dropActionChanged(DropTargetDragEvent evt)
1042 public void drop(DropTargetDropEvent evt)
1044 boolean success = true;
1045 // JAL-1552 - acceptDrop required before getTransferable call for
1046 // Java's Transferable for native dnd
1047 evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
1048 Transferable t = evt.getTransferable();
1049 List<Object> files = new ArrayList<>();
1050 List<DataSourceType> protocols = new ArrayList<>();
1054 Desktop.transferFromDropTarget(files, protocols, evt, t);
1055 } catch (Exception e)
1057 e.printStackTrace();
1065 for (int i = 0; i < files.size(); i++)
1067 // BH 2018 File or String
1068 Object file = files.get(i);
1069 String fileName = file.toString();
1070 DataSourceType protocol = (protocols == null)
1071 ? DataSourceType.FILE
1073 FileFormatI format = null;
1075 if (fileName.endsWith(".jar"))
1077 format = FileFormat.Jalview;
1082 format = new IdentifyFile().identify(file, protocol);
1084 if (file instanceof File)
1086 Platform.cacheFileData((File) file);
1088 new FileLoader().LoadFile(null, file, protocol, format);
1091 } catch (Exception ex)
1096 evt.dropComplete(success); // need this to ensure input focus is properly
1097 // transfered to any new windows created
1107 public void inputLocalFileMenuItem_actionPerformed(AlignViewport viewport)
1109 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
1110 JalviewFileChooser chooser = JalviewFileChooser
1111 .forRead(Cache.getProperty("LAST_DIRECTORY"), fileFormat, BackupFiles.getEnabled());
1113 chooser.setFileView(new JalviewFileView());
1114 chooser.setDialogTitle(
1115 MessageManager.getString("label.open_local_file"));
1116 chooser.setToolTipText(MessageManager.getString("action.open"));
1118 chooser.setResponseHandler(0, new Runnable()
1123 File selectedFile = chooser.getSelectedFile();
1124 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1126 FileFormatI format = chooser.getSelectedFormat();
1129 * Call IdentifyFile to verify the file contains what its extension implies.
1130 * Skip this step for dynamically added file formats, because
1131 * IdentifyFile does not know how to recognise them.
1133 if (FileFormats.getInstance().isIdentifiable(format))
1137 format = new IdentifyFile().identify(selectedFile,
1138 DataSourceType.FILE);
1139 } catch (FileFormatException e)
1141 // format = null; //??
1145 new FileLoader().LoadFile(viewport, selectedFile,
1146 DataSourceType.FILE, format);
1149 chooser.showOpenDialog(this);
1153 * Shows a dialog for input of a URL at which to retrieve alignment data
1158 public void inputURLMenuItem_actionPerformed(AlignViewport viewport)
1160 // This construct allows us to have a wider textfield
1162 JLabel label = new JLabel(
1163 MessageManager.getString("label.input_file_url"));
1165 JPanel panel = new JPanel(new GridLayout(2, 1));
1169 * the URL to fetch is
1170 * Java: an editable combobox with history
1171 * JS: (pending JAL-3038) a plain text field
1174 String urlBase = "http://www.";
1175 if (Platform.isJS())
1177 history = new JTextField(urlBase, 35);
1186 JComboBox<String> asCombo = new JComboBox<>();
1187 asCombo.setPreferredSize(new Dimension(400, 20));
1188 asCombo.setEditable(true);
1189 asCombo.addItem(urlBase);
1190 String historyItems = Cache.getProperty("RECENT_URL");
1191 if (historyItems != null)
1193 for (String token : historyItems.split("\\t"))
1195 asCombo.addItem(token);
1202 Object[] options = new Object[] { MessageManager.getString("action.ok"),
1203 MessageManager.getString("action.cancel") };
1204 Runnable action = new Runnable()
1209 @SuppressWarnings("unchecked")
1210 String url = (history instanceof JTextField
1211 ? ((JTextField) history).getText()
1212 : ((JComboBox<String>) history).getSelectedItem()
1215 if (url.toLowerCase().endsWith(".jar"))
1217 if (viewport != null)
1219 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1220 FileFormat.Jalview);
1224 new FileLoader().LoadFile(url, DataSourceType.URL,
1225 FileFormat.Jalview);
1230 FileFormatI format = null;
1233 format = new IdentifyFile().identify(url, DataSourceType.URL);
1234 } catch (FileFormatException e)
1236 // TODO revise error handling, distinguish between
1237 // URL not found and response not valid
1242 String msg = MessageManager
1243 .formatMessage("label.couldnt_locate", url);
1244 JvOptionPane.showInternalMessageDialog(Desktop.desktop, msg,
1245 MessageManager.getString("label.url_not_found"),
1246 JvOptionPane.WARNING_MESSAGE);
1251 if (viewport != null)
1253 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1258 new FileLoader().LoadFile(url, DataSourceType.URL, format);
1263 String dialogOption = MessageManager
1264 .getString("label.input_alignment_from_url");
1265 JvOptionPane.newOptionDialog(desktop).setResponseHandler(0, action)
1266 .showInternalDialog(panel, dialogOption,
1267 JvOptionPane.YES_NO_CANCEL_OPTION,
1268 JvOptionPane.PLAIN_MESSAGE, null, options,
1269 MessageManager.getString("action.ok"));
1273 * Opens the CutAndPaste window for the user to paste an alignment in to
1276 * - if not null, the pasted alignment is added to the current
1277 * alignment; if null, to a new alignment window
1280 public void inputTextboxMenuItem_actionPerformed(
1281 AlignmentViewPanel viewPanel)
1283 CutAndPasteTransfer cap = new CutAndPasteTransfer();
1284 cap.setForInput(viewPanel);
1285 Desktop.addInternalFrame(cap,
1286 MessageManager.getString("label.cut_paste_alignmen_file"), true,
1296 Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
1297 Cache.setProperty("SCREENGEOMETRY_WIDTH",
1299 Cache.setProperty("SCREENGEOMETRY_HEIGHT",
1300 screen.height + "");
1301 storeLastKnownDimensions("", new Rectangle(getBounds().x, getBounds().y,
1302 getWidth(), getHeight()));
1304 if (jconsole != null)
1306 storeLastKnownDimensions("JAVA_CONSOLE_", jconsole.getBounds());
1307 jconsole.stopConsole();
1311 storeLastKnownDimensions("JALVIEW_RSS_WINDOW_", jvnews.getBounds());
1314 if (dialogExecutor != null)
1316 dialogExecutor.shutdownNow();
1318 closeAll_actionPerformed(null);
1320 if (groovyConsole != null)
1322 // suppress a possible repeat prompt to save script
1323 groovyConsole.setDirty(false);
1324 groovyConsole.exit();
1329 private void storeLastKnownDimensions(String string, Rectangle jc)
1331 Cache.log.debug("Storing last known dimensions for "
1332 + string + ": x:" + jc.x + " y:" + jc.y + " width:" + jc.width
1333 + " height:" + jc.height);
1335 Cache.setProperty(string + "SCREEN_X", jc.x + "");
1336 Cache.setProperty(string + "SCREEN_Y", jc.y + "");
1337 Cache.setProperty(string + "SCREEN_WIDTH", jc.width + "");
1338 Cache.setProperty(string + "SCREEN_HEIGHT", jc.height + "");
1348 public void aboutMenuItem_actionPerformed(ActionEvent e)
1350 new Thread(new Runnable()
1355 new SplashScreen(false);
1361 * Returns the html text for the About screen, including any available version
1362 * number, build details, author details and citation reference, but without
1363 * the enclosing {@code html} tags
1367 public String getAboutMessage()
1369 StringBuilder message = new StringBuilder(1024);
1370 message.append("<h1><strong>Version: ")
1371 .append(Cache.getProperty("VERSION")).append("</strong></h1>")
1372 .append("<strong>Built: <em>")
1373 .append(Cache.getDefault("BUILD_DATE", "unknown"))
1374 .append("</em> from ").append(Cache.getBuildDetailsForSplash())
1375 .append("</strong>");
1377 String latestVersion = Cache.getDefault("LATEST_VERSION", "Checking");
1378 if (latestVersion.equals("Checking"))
1380 // JBP removed this message for 2.11: May be reinstated in future version
1381 // message.append("<br>...Checking latest version...</br>");
1383 else if (!latestVersion.equals(Cache.getProperty("VERSION")))
1385 boolean red = false;
1386 if (Cache.getProperty("VERSION").toLowerCase()
1387 .indexOf("automated build") == -1)
1390 // Displayed when code version and jnlp version do not match and code
1391 // version is not a development build
1392 message.append("<div style=\"color: #FF0000;font-style: bold;\">");
1395 message.append("<br>!! Version ")
1396 .append(Cache.getDefault("LATEST_VERSION", "..Checking.."))
1397 .append(" is available for download from ")
1398 .append(Cache.getDefault("www.jalview.org",
1399 "http://www.jalview.org"))
1403 message.append("</div>");
1406 message.append("<br>Authors: ");
1407 message.append(Cache.getDefault("AUTHORFNAMES", DEFAULT_AUTHORS));
1408 message.append(CITATION);
1410 return message.toString();
1414 * Action on requesting Help documentation
1417 public void documentationMenuItem_actionPerformed()
1421 if (Platform.isJS())
1423 BrowserLauncher.openURL("http://www.jalview.org/help.html");
1432 Help.showHelpWindow();
1434 } catch (Exception ex)
1436 System.err.println("Error opening help: " + ex.getMessage());
1441 public void closeAll_actionPerformed(ActionEvent e)
1443 // TODO show a progress bar while closing?
1444 JInternalFrame[] frames = desktop.getAllFrames();
1445 for (int i = 0; i < frames.length; i++)
1449 frames[i].setClosed(true);
1450 } catch (java.beans.PropertyVetoException ex)
1454 Jalview.setCurrentAlignFrame(null);
1455 System.out.println("ALL CLOSED");
1458 * reset state of singleton objects as appropriate (clear down session state
1459 * when all windows are closed)
1461 StructureSelectionManager ssm = StructureSelectionManager
1462 .getStructureSelectionManager(this);
1470 public void raiseRelated_actionPerformed(ActionEvent e)
1472 reorderAssociatedWindows(false, false);
1476 public void minimizeAssociated_actionPerformed(ActionEvent e)
1478 reorderAssociatedWindows(true, false);
1481 void closeAssociatedWindows()
1483 reorderAssociatedWindows(false, true);
1489 * @seejalview.jbgui.GDesktop#garbageCollect_actionPerformed(java.awt.event.
1493 protected void garbageCollect_actionPerformed(ActionEvent e)
1495 // We simply collect the garbage
1496 Cache.log.debug("Collecting garbage...");
1498 Cache.log.debug("Finished garbage collection.");
1505 * jalview.jbgui.GDesktop#showMemusage_actionPerformed(java.awt.event.ActionEvent
1509 protected void showMemusage_actionPerformed(ActionEvent e)
1511 desktop.showMemoryUsage(showMemusage.isSelected());
1518 * jalview.jbgui.GDesktop#showConsole_actionPerformed(java.awt.event.ActionEvent
1522 protected void showConsole_actionPerformed(ActionEvent e)
1524 showConsole(showConsole.isSelected());
1527 Console jconsole = null;
1530 * control whether the java console is visible or not
1534 void showConsole(boolean selected)
1536 // TODO: decide if we should update properties file
1537 if (jconsole != null) // BH 2018
1539 showConsole.setSelected(selected);
1540 Cache.setProperty("SHOW_JAVA_CONSOLE",
1541 Boolean.valueOf(selected).toString());
1542 jconsole.setVisible(selected);
1546 void reorderAssociatedWindows(boolean minimize, boolean close)
1548 JInternalFrame[] frames = desktop.getAllFrames();
1549 if (frames == null || frames.length < 1)
1554 AlignViewportI source = null;
1555 AlignViewportI target = null;
1556 if (frames[0] instanceof AlignFrame)
1558 source = ((AlignFrame) frames[0]).getCurrentView();
1560 else if (frames[0] instanceof TreePanel)
1562 source = ((TreePanel) frames[0]).getViewPort();
1564 else if (frames[0] instanceof PCAPanel)
1566 source = ((PCAPanel) frames[0]).av;
1568 else if (frames[0].getContentPane() instanceof PairwiseAlignPanel)
1570 source = ((PairwiseAlignPanel) frames[0].getContentPane()).av;
1575 for (int i = 0; i < frames.length; i++)
1578 if (frames[i] == null)
1582 if (frames[i] instanceof AlignFrame)
1584 target = ((AlignFrame) frames[i]).getCurrentView();
1586 else if (frames[i] instanceof TreePanel)
1588 target = ((TreePanel) frames[i]).getViewPort();
1590 else if (frames[i] instanceof PCAPanel)
1592 target = ((PCAPanel) frames[i]).av;
1594 else if (frames[i].getContentPane() instanceof PairwiseAlignPanel)
1596 target = ((PairwiseAlignPanel) frames[i].getContentPane()).av;
1599 if (source == target)
1605 frames[i].setClosed(true);
1609 frames[i].setIcon(minimize);
1612 frames[i].toFront();
1616 } catch (java.beans.PropertyVetoException ex)
1631 protected void preferences_actionPerformed(ActionEvent e)
1637 * Prompts the user to choose a file and then saves the Jalview state as a
1638 * Jalview project file
1641 public void saveState_actionPerformed()
1643 saveState_actionPerformed(false);
1646 public void saveState_actionPerformed(boolean saveAs)
1648 java.io.File projectFile = getProjectFile();
1649 // autoSave indicates we already have a file and don't need to ask
1650 boolean autoSave = projectFile != null && !saveAs
1651 && BackupFiles.getEnabled();
1653 // System.out.println("autoSave="+autoSave+", projectFile='"+projectFile+"',
1654 // saveAs="+saveAs+", Backups
1655 // "+(BackupFiles.getEnabled()?"enabled":"disabled"));
1657 boolean approveSave = false;
1660 JalviewFileChooser chooser = new JalviewFileChooser("jvp",
1663 chooser.setFileView(new JalviewFileView());
1664 chooser.setDialogTitle(MessageManager.getString("label.save_state"));
1666 int value = chooser.showSaveDialog(this);
1668 if (value == JalviewFileChooser.APPROVE_OPTION)
1670 projectFile = chooser.getSelectedFile();
1671 setProjectFile(projectFile);
1676 if (approveSave || autoSave)
1678 final Desktop me = this;
1679 final java.io.File chosenFile = projectFile;
1680 new Thread(new Runnable()
1685 // TODO: refactor to Jalview desktop session controller action.
1686 setProgressBar(MessageManager.formatMessage(
1687 "label.saving_jalview_project", new Object[]
1688 { chosenFile.getName() }), chosenFile.hashCode());
1689 Cache.setProperty("LAST_DIRECTORY",
1690 chosenFile.getParent());
1691 // TODO catch and handle errors for savestate
1692 // TODO prevent user from messing with the Desktop whilst we're saving
1695 boolean doBackup = BackupFiles.getEnabled();
1696 BackupFiles backupfiles = doBackup ? new BackupFiles(chosenFile) : null;
1698 new Jalview2XML().saveState(doBackup ? backupfiles.getTempFile() : chosenFile);
1702 backupfiles.setWriteSuccess(true);
1703 backupfiles.rollBackupsAndRenameTempFile();
1705 } catch (OutOfMemoryError oom)
1707 new OOMWarning("Whilst saving current state to "
1708 + chosenFile.getName(), oom);
1709 } catch (Exception ex)
1711 Cache.log.error("Problems whilst trying to save to "
1712 + chosenFile.getName(), ex);
1713 JvOptionPane.showMessageDialog(me,
1714 MessageManager.formatMessage(
1715 "label.error_whilst_saving_current_state_to",
1717 { chosenFile.getName() }),
1718 MessageManager.getString("label.couldnt_save_project"),
1719 JvOptionPane.WARNING_MESSAGE);
1721 setProgressBar(null, chosenFile.hashCode());
1728 public void saveAsState_actionPerformed(ActionEvent e)
1730 saveState_actionPerformed(true);
1733 private void setProjectFile(File choice)
1735 this.projectFile = choice;
1738 public File getProjectFile()
1740 return this.projectFile;
1744 * Shows a file chooser dialog and tries to read in the selected file as a
1748 public void loadState_actionPerformed()
1750 final String[] suffix = new String[] { "jvp", "jar" };
1751 final String[] desc = new String[] { "Jalview Project",
1752 "Jalview Project (old)" };
1753 JalviewFileChooser chooser = new JalviewFileChooser(
1754 Cache.getProperty("LAST_DIRECTORY"), suffix, desc,
1755 "Jalview Project", true, BackupFiles.getEnabled()); // last two booleans: allFiles,
1757 chooser.setFileView(new JalviewFileView());
1758 chooser.setDialogTitle(MessageManager.getString("label.restore_state"));
1759 chooser.setResponseHandler(0, new Runnable()
1764 File selectedFile = chooser.getSelectedFile();
1765 setProjectFile(selectedFile);
1766 String choice = selectedFile.getAbsolutePath();
1767 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1768 new Thread(new Runnable()
1775 new Jalview2XML().loadJalviewAlign(selectedFile);
1776 } catch (OutOfMemoryError oom)
1778 new OOMWarning("Whilst loading project from " + choice, oom);
1779 } catch (Exception ex)
1782 "Problems whilst loading project from " + choice, ex);
1783 JvOptionPane.showMessageDialog(Desktop.desktop,
1784 MessageManager.formatMessage(
1785 "label.error_whilst_loading_project_from",
1788 MessageManager.getString("label.couldnt_load_project"),
1789 JvOptionPane.WARNING_MESSAGE);
1796 chooser.showOpenDialog(this);
1800 public void inputSequence_actionPerformed(ActionEvent e)
1802 new SequenceFetcher(this);
1805 JPanel progressPanel;
1807 ArrayList<JPanel> fileLoadingPanels = new ArrayList<>();
1809 public void startLoading(final Object fileName)
1811 if (fileLoadingCount == 0)
1813 fileLoadingPanels.add(addProgressPanel(MessageManager
1814 .formatMessage("label.loading_file", new Object[]
1820 private JPanel addProgressPanel(String string)
1822 if (progressPanel == null)
1824 progressPanel = new JPanel(new GridLayout(1, 1));
1825 totalProgressCount = 0;
1826 instance.getContentPane().add(progressPanel, BorderLayout.SOUTH);
1828 JPanel thisprogress = new JPanel(new BorderLayout(10, 5));
1829 JProgressBar progressBar = new JProgressBar();
1830 progressBar.setIndeterminate(true);
1832 thisprogress.add(new JLabel(string), BorderLayout.WEST);
1834 thisprogress.add(progressBar, BorderLayout.CENTER);
1835 progressPanel.add(thisprogress);
1836 ((GridLayout) progressPanel.getLayout()).setRows(
1837 ((GridLayout) progressPanel.getLayout()).getRows() + 1);
1838 ++totalProgressCount;
1839 instance.validate();
1840 return thisprogress;
1843 int totalProgressCount = 0;
1845 private void removeProgressPanel(JPanel progbar)
1847 if (progressPanel != null)
1849 synchronized (progressPanel)
1851 progressPanel.remove(progbar);
1852 GridLayout gl = (GridLayout) progressPanel.getLayout();
1853 gl.setRows(gl.getRows() - 1);
1854 if (--totalProgressCount < 1)
1856 this.getContentPane().remove(progressPanel);
1857 progressPanel = null;
1864 public void stopLoading()
1867 if (fileLoadingCount < 1)
1869 while (fileLoadingPanels.size() > 0)
1871 removeProgressPanel(fileLoadingPanels.remove(0));
1873 fileLoadingPanels.clear();
1874 fileLoadingCount = 0;
1879 public static int getViewCount(String alignmentId)
1881 AlignmentViewport[] aps = getViewports(alignmentId);
1882 return (aps == null) ? 0 : aps.length;
1887 * @param alignmentId
1888 * - if null, all sets are returned
1889 * @return all AlignmentPanels concerning the alignmentId sequence set
1891 public static AlignmentPanel[] getAlignmentPanels(String alignmentId)
1893 if (Desktop.desktop == null)
1895 // no frames created and in headless mode
1896 // TODO: verify that frames are recoverable when in headless mode
1899 List<AlignmentPanel> aps = new ArrayList<>();
1900 AlignFrame[] frames = getAlignFrames();
1905 for (AlignFrame af : frames)
1907 for (AlignmentPanel ap : af.alignPanels)
1909 if (alignmentId == null
1910 || alignmentId.equals(ap.av.getSequenceSetId()))
1916 if (aps.size() == 0)
1920 AlignmentPanel[] vap = aps.toArray(new AlignmentPanel[aps.size()]);
1925 * get all the viewports on an alignment.
1927 * @param sequenceSetId
1928 * unique alignment id (may be null - all viewports returned in that
1930 * @return all viewports on the alignment bound to sequenceSetId
1932 public static AlignmentViewport[] getViewports(String sequenceSetId)
1934 List<AlignmentViewport> viewp = new ArrayList<>();
1935 if (desktop != null)
1937 AlignFrame[] frames = Desktop.getAlignFrames();
1939 for (AlignFrame afr : frames)
1941 if (sequenceSetId == null || afr.getViewport().getSequenceSetId()
1942 .equals(sequenceSetId))
1944 if (afr.alignPanels != null)
1946 for (AlignmentPanel ap : afr.alignPanels)
1948 if (sequenceSetId == null
1949 || sequenceSetId.equals(ap.av.getSequenceSetId()))
1957 viewp.add(afr.getViewport());
1961 if (viewp.size() > 0)
1963 return viewp.toArray(new AlignmentViewport[viewp.size()]);
1970 * Explode the views in the given frame into separate AlignFrame
1974 public static void explodeViews(AlignFrame af)
1976 int size = af.alignPanels.size();
1982 // FIXME: ideally should use UI interface API
1983 FeatureSettings viewFeatureSettings = (af.featureSettings != null
1984 && af.featureSettings.isOpen())
1985 ? af.featureSettings
1987 Rectangle fsBounds = af.getFeatureSettingsGeometry();
1988 for (int i = 0; i < size; i++)
1990 AlignmentPanel ap = af.alignPanels.get(i);
1992 AlignFrame newaf = new AlignFrame(ap);
1994 // transfer reference for existing feature settings to new alignFrame
1995 if (ap == af.alignPanel)
1997 if (viewFeatureSettings != null && viewFeatureSettings.fr.ap == ap)
1999 newaf.featureSettings = viewFeatureSettings;
2001 newaf.setFeatureSettingsGeometry(fsBounds);
2005 * Restore the view's last exploded frame geometry if known. Multiple
2006 * views from one exploded frame share and restore the same (frame)
2007 * position and size.
2009 Rectangle geometry = ap.av.getExplodedGeometry();
2010 if (geometry != null)
2012 newaf.setBounds(geometry);
2015 ap.av.setGatherViewsHere(false);
2017 addInternalFrame(newaf, af.getTitle(), AlignFrame.DEFAULT_WIDTH,
2018 AlignFrame.DEFAULT_HEIGHT);
2019 // and materialise a new feature settings dialog instance for the new alignframe
2020 // (closes the old as if 'OK' was pressed)
2021 if (ap == af.alignPanel && newaf.featureSettings != null
2022 && newaf.featureSettings.isOpen()
2023 && af.alignPanel.getAlignViewport().isShowSequenceFeatures())
2025 newaf.showFeatureSettingsUI();
2029 af.featureSettings = null;
2030 af.alignPanels.clear();
2031 af.closeMenuItem_actionPerformed(true);
2036 * Gather expanded views (separate AlignFrame's) with the same sequence set
2037 * identifier back in to this frame as additional views, and close the expanded
2038 * views. Note the expanded frames may themselves have multiple views. We take
2043 public void gatherViews(AlignFrame source)
2045 source.viewport.setGatherViewsHere(true);
2046 source.viewport.setExplodedGeometry(source.getBounds());
2047 JInternalFrame[] frames = desktop.getAllFrames();
2048 String viewId = source.viewport.getSequenceSetId();
2049 for (int t = 0; t < frames.length; t++)
2051 if (frames[t] instanceof AlignFrame && frames[t] != source)
2053 AlignFrame af = (AlignFrame) frames[t];
2054 boolean gatherThis = false;
2055 for (int a = 0; a < af.alignPanels.size(); a++)
2057 AlignmentPanel ap = af.alignPanels.get(a);
2058 if (viewId.equals(ap.av.getSequenceSetId()))
2061 ap.av.setGatherViewsHere(false);
2062 ap.av.setExplodedGeometry(af.getBounds());
2063 source.addAlignmentPanel(ap, false);
2069 if (af.featureSettings != null && af.featureSettings.isOpen())
2071 if (source.featureSettings == null)
2073 // preserve the feature settings geometry for this frame
2074 source.featureSettings = af.featureSettings;
2075 source.setFeatureSettingsGeometry(
2076 af.getFeatureSettingsGeometry());
2080 // close it and forget
2081 af.featureSettings.close();
2084 af.alignPanels.clear();
2085 af.closeMenuItem_actionPerformed(true);
2090 // refresh the feature setting UI for the source frame if it exists
2091 if (source.featureSettings != null
2092 && source.featureSettings.isOpen())
2094 source.showFeatureSettingsUI();
2098 public JInternalFrame[] getAllFrames()
2100 return desktop.getAllFrames();
2104 * Checks the given url to see if it gives a response indicating that the user
2105 * should be informed of a new questionnaire.
2109 public void checkForQuestionnaire(String url)
2111 UserQuestionnaireCheck jvq = new UserQuestionnaireCheck(url);
2112 // javax.swing.SwingUtilities.invokeLater(jvq);
2113 new Thread(jvq).start();
2116 public void checkURLLinks()
2118 // Thread off the URL link checker
2119 addDialogThread(new Runnable()
2124 if (Cache.getDefault("CHECKURLLINKS", true))
2126 // check what the actual links are - if it's just the default don't
2127 // bother with the warning
2128 List<String> links = Preferences.sequenceUrlLinks
2131 // only need to check links if there is one with a
2132 // SEQUENCE_ID which is not the default EMBL_EBI link
2133 ListIterator<String> li = links.listIterator();
2134 boolean check = false;
2135 List<JLabel> urls = new ArrayList<>();
2136 while (li.hasNext())
2138 String link = li.next();
2139 if (link.contains(jalview.util.UrlConstants.SEQUENCE_ID)
2140 && !UrlConstants.isDefaultString(link))
2143 int barPos = link.indexOf("|");
2144 String urlMsg = barPos == -1 ? link
2145 : link.substring(0, barPos) + ": "
2146 + link.substring(barPos + 1);
2147 urls.add(new JLabel(urlMsg));
2155 // ask user to check in case URL links use old style tokens
2156 // ($SEQUENCE_ID$ for sequence id _or_ accession id)
2157 JPanel msgPanel = new JPanel();
2158 msgPanel.setLayout(new BoxLayout(msgPanel, BoxLayout.PAGE_AXIS));
2159 msgPanel.add(Box.createVerticalGlue());
2160 JLabel msg = new JLabel(MessageManager
2161 .getString("label.SEQUENCE_ID_for_DB_ACCESSION1"));
2162 JLabel msg2 = new JLabel(MessageManager
2163 .getString("label.SEQUENCE_ID_for_DB_ACCESSION2"));
2165 for (JLabel url : urls)
2171 final JCheckBox jcb = new JCheckBox(
2172 MessageManager.getString("label.do_not_display_again"));
2173 jcb.addActionListener(new ActionListener()
2176 public void actionPerformed(ActionEvent e)
2178 // update Cache settings for "don't show this again"
2179 boolean showWarningAgain = !jcb.isSelected();
2180 Cache.setProperty("CHECKURLLINKS",
2181 Boolean.valueOf(showWarningAgain).toString());
2186 JvOptionPane.showMessageDialog(Desktop.desktop, msgPanel,
2188 .getString("label.SEQUENCE_ID_no_longer_used"),
2189 JvOptionPane.WARNING_MESSAGE);
2196 * Proxy class for JDesktopPane which optionally displays the current memory
2197 * usage and highlights the desktop area with a red bar if free memory runs low.
2201 public class MyDesktopPane extends JDesktopPane
2204 private static final float ONE_MB = 1048576f;
2206 boolean showMemoryUsage = false;
2210 java.text.NumberFormat df;
2212 float maxMemory, allocatedMemory, freeMemory, totalFreeMemory,
2215 public MyDesktopPane(boolean showMemoryUsage)
2217 showMemoryUsage(showMemoryUsage);
2220 public void showMemoryUsage(boolean showMemory)
2222 this.showMemoryUsage = showMemory;
2225 Thread worker = new Thread(this);
2231 public boolean isShowMemoryUsage()
2233 return showMemoryUsage;
2239 df = java.text.NumberFormat.getNumberInstance();
2240 df.setMaximumFractionDigits(2);
2241 runtime = Runtime.getRuntime();
2243 while (showMemoryUsage)
2247 maxMemory = runtime.maxMemory() / ONE_MB;
2248 allocatedMemory = runtime.totalMemory() / ONE_MB;
2249 freeMemory = runtime.freeMemory() / ONE_MB;
2250 totalFreeMemory = freeMemory + (maxMemory - allocatedMemory);
2252 percentUsage = (totalFreeMemory / maxMemory) * 100;
2254 // if (percentUsage < 20)
2256 // border1 = BorderFactory.createMatteBorder(12, 12, 12, 12,
2258 // instance.set.setBorder(border1);
2261 // sleep after showing usage
2263 } catch (Exception ex)
2265 ex.printStackTrace();
2271 public void paintComponent(Graphics g)
2273 if (showMemoryUsage && g != null && df != null)
2275 if (percentUsage < 20)
2277 g.setColor(Color.red);
2279 FontMetrics fm = g.getFontMetrics();
2282 g.drawString(MessageManager.formatMessage("label.memory_stats",
2284 { df.format(totalFreeMemory), df.format(maxMemory),
2285 df.format(percentUsage) }),
2286 10, getHeight() - fm.getHeight());
2293 * Accessor method to quickly get all the AlignmentFrames loaded.
2295 * @return an array of AlignFrame, or null if none found
2297 public static AlignFrame[] getAlignFrames()
2299 if (Jalview.isHeadlessMode())
2301 // Desktop.desktop is null in headless mode
2302 return new AlignFrame[] { Jalview.currentAlignFrame };
2305 JInternalFrame[] frames = Desktop.desktop.getAllFrames();
2311 List<AlignFrame> avp = new ArrayList<>();
2313 for (int i = frames.length - 1; i > -1; i--)
2315 if (frames[i] instanceof AlignFrame)
2317 avp.add((AlignFrame) frames[i]);
2319 else if (frames[i] instanceof SplitFrame)
2322 * Also check for a split frame containing an AlignFrame
2324 GSplitFrame sf = (GSplitFrame) frames[i];
2325 if (sf.getTopFrame() instanceof AlignFrame)
2327 avp.add((AlignFrame) sf.getTopFrame());
2329 if (sf.getBottomFrame() instanceof AlignFrame)
2331 avp.add((AlignFrame) sf.getBottomFrame());
2335 if (avp.size() == 0)
2339 AlignFrame afs[] = avp.toArray(new AlignFrame[avp.size()]);
2344 * Returns an array of any AppJmol frames in the Desktop (or null if none).
2348 public GStructureViewer[] getJmols()
2350 JInternalFrame[] frames = Desktop.desktop.getAllFrames();
2356 List<GStructureViewer> avp = new ArrayList<>();
2358 for (int i = frames.length - 1; i > -1; i--)
2360 if (frames[i] instanceof AppJmol)
2362 GStructureViewer af = (GStructureViewer) frames[i];
2366 if (avp.size() == 0)
2370 GStructureViewer afs[] = avp.toArray(new GStructureViewer[avp.size()]);
2375 * Add Groovy Support to Jalview
2378 public void groovyShell_actionPerformed()
2382 openGroovyConsole();
2383 } catch (Exception ex)
2385 Cache.log.error("Groovy Shell Creation failed.", ex);
2386 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
2388 MessageManager.getString("label.couldnt_create_groovy_shell"),
2389 MessageManager.getString("label.groovy_support_failed"),
2390 JvOptionPane.ERROR_MESSAGE);
2395 * Open the Groovy console
2397 void openGroovyConsole()
2399 if (groovyConsole == null)
2401 groovyConsole = new groovy.ui.Console();
2402 groovyConsole.setVariable("Jalview", this);
2403 groovyConsole.run();
2406 * We allow only one console at a time, so that AlignFrame menu option
2407 * 'Calculate | Run Groovy script' is unambiguous.
2408 * Disable 'Groovy Console', and enable 'Run script', when the console is
2409 * opened, and the reverse when it is closed
2411 Window window = (Window) groovyConsole.getFrame();
2412 window.addWindowListener(new WindowAdapter()
2415 public void windowClosed(WindowEvent e)
2418 * rebind CMD-Q from Groovy Console to Jalview Quit
2421 enableExecuteGroovy(false);
2427 * show Groovy console window (after close and reopen)
2429 ((Window) groovyConsole.getFrame()).setVisible(true);
2432 * if we got this far, enable 'Run Groovy' in AlignFrame menus
2433 * and disable opening a second console
2435 enableExecuteGroovy(true);
2439 * Bind Ctrl/Cmd-Q to Quit - for reset as Groovy Console takes over this binding
2442 protected void addQuitHandler()
2444 getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
2445 .put(KeyStroke.getKeyStroke(KeyEvent.VK_Q,
2446 jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx()),
2448 getRootPane().getActionMap().put("Quit", new AbstractAction()
2451 public void actionPerformed(ActionEvent e)
2459 * Enable or disable 'Run Groovy script' in AlignFrame calculate menus
2462 * true if Groovy console is open
2464 public void enableExecuteGroovy(boolean enabled)
2467 * disable opening a second Groovy console
2468 * (or re-enable when the console is closed)
2470 groovyShell.setEnabled(!enabled);
2472 AlignFrame[] alignFrames = getAlignFrames();
2473 if (alignFrames != null)
2475 for (AlignFrame af : alignFrames)
2477 af.setGroovyEnabled(enabled);
2483 * Progress bars managed by the IProgressIndicator method.
2485 private Hashtable<Long, JPanel> progressBars;
2487 private Hashtable<Long, IProgressIndicatorHandler> progressBarHandlers;
2492 * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
2495 public void setProgressBar(String message, long id)
2497 // Platform.timeCheck("Desktop " + message, Platform.TIME_MARK);
2499 if (progressBars == null)
2501 progressBars = new Hashtable<>();
2502 progressBarHandlers = new Hashtable<>();
2505 if (progressBars.get(Long.valueOf(id)) != null)
2507 JPanel panel = progressBars.remove(Long.valueOf(id));
2508 if (progressBarHandlers.contains(Long.valueOf(id)))
2510 progressBarHandlers.remove(Long.valueOf(id));
2512 removeProgressPanel(panel);
2516 progressBars.put(Long.valueOf(id), addProgressPanel(message));
2523 * @see jalview.gui.IProgressIndicator#registerHandler(long,
2524 * jalview.gui.IProgressIndicatorHandler)
2527 public void registerHandler(final long id,
2528 final IProgressIndicatorHandler handler)
2530 if (progressBarHandlers == null
2531 || !progressBars.containsKey(Long.valueOf(id)))
2533 throw new Error(MessageManager.getString(
2534 "error.call_setprogressbar_before_registering_handler"));
2536 progressBarHandlers.put(Long.valueOf(id), handler);
2537 final JPanel progressPanel = progressBars.get(Long.valueOf(id));
2538 if (handler.canCancel())
2540 JButton cancel = new JButton(
2541 MessageManager.getString("action.cancel"));
2542 final IProgressIndicator us = this;
2543 cancel.addActionListener(new ActionListener()
2547 public void actionPerformed(ActionEvent e)
2549 handler.cancelActivity(id);
2550 us.setProgressBar(MessageManager
2551 .formatMessage("label.cancelled_params", new Object[]
2552 { ((JLabel) progressPanel.getComponent(0)).getText() }),
2556 progressPanel.add(cancel, BorderLayout.EAST);
2562 * @return true if any progress bars are still active
2565 public boolean operationInProgress()
2567 if (progressBars != null && progressBars.size() > 0)
2575 * This will return the first AlignFrame holding the given viewport instance. It
2576 * will break if there are more than one AlignFrames viewing a particular av.
2579 * @return alignFrame for viewport
2581 public static AlignFrame getAlignFrameFor(AlignViewportI viewport)
2583 if (desktop != null)
2585 AlignmentPanel[] aps = getAlignmentPanels(
2586 viewport.getSequenceSetId());
2587 for (int panel = 0; aps != null && panel < aps.length; panel++)
2589 if (aps[panel] != null && aps[panel].av == viewport)
2591 return aps[panel].alignFrame;
2598 public VamsasApplication getVamsasApplication()
2600 // TODO: JAL-3311 remove remaining code from Jalview relating to VAMSAS
2606 * flag set if jalview GUI is being operated programmatically
2608 private boolean inBatchMode = false;
2611 * check if jalview GUI is being operated programmatically
2613 * @return inBatchMode
2615 public boolean isInBatchMode()
2621 * set flag if jalview GUI is being operated programmatically
2623 * @param inBatchMode
2625 public void setInBatchMode(boolean inBatchMode)
2627 this.inBatchMode = inBatchMode;
2630 public void startServiceDiscovery()
2632 startServiceDiscovery(false);
2635 public void startServiceDiscovery(boolean blocking)
2637 var tasks = new ArrayList<Future<?>>();
2638 // JAL-940 - JALVIEW 1 services are now being EOLed as of JABA 2.1 release
2641 // todo: changesupport handlers need to be transferred
2642 if (discoverer == null)
2644 discoverer = new jalview.ws.jws1.Discoverer();
2645 // register PCS handler for desktop.
2646 discoverer.addPropertyChangeListener(changeSupport);
2648 // JAL-940 - disabled JWS1 service configuration - always start discoverer
2649 // until we phase out completely
2650 var f = new FutureTask<Void>(discoverer, null);
2651 new Thread(f).start();
2655 if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
2657 tasks.add(jalview.ws.jws2.Jws2Discoverer.getDiscoverer().startDiscoverer());
2659 if (Cache.getDefault("SHOW_SLIVKA_SERVICES", true))
2661 tasks.add(jalview.ws.slivkaws.SlivkaWSDiscoverer.getInstance().startDiscoverer());
2665 for (Future<?> task : tasks) {
2668 // block until all discovery tasks are done
2670 } catch (Exception e)
2672 e.printStackTrace();
2679 * called to check if the service discovery process completed successfully.
2683 protected void JalviewServicesChanged(PropertyChangeEvent evt)
2685 if (evt.getNewValue() == null || evt.getNewValue() instanceof Vector)
2687 final WSDiscovererI discoverer = jalview.ws.jws2.Jws2Discoverer
2689 final String ermsg = discoverer.getErrorMessages();
2692 if (Cache.getDefault("SHOW_WSDISCOVERY_ERRORS", true))
2694 if (serviceChangedDialog == null)
2696 // only run if we aren't already displaying one of these.
2697 addDialogThread(serviceChangedDialog = new Runnable()
2704 * JalviewDialog jd =new JalviewDialog() {
2706 * @Override protected void cancelPressed() { // TODO
2707 * Auto-generated method stub
2709 * }@Override protected void okPressed() { // TODO
2710 * Auto-generated method stub
2712 * }@Override protected void raiseClosed() { // TODO
2713 * Auto-generated method stub
2715 * } }; jd.initDialogFrame(new
2716 * JLabel("<html><table width=\"450\"><tr><td>" + ermsg +
2717 * "<br/>It may be that you have invalid JABA URLs in your web service preferences,"
2718 * + " or mis-configured HTTP proxy settings.<br/>" +
2719 * "Check the <em>Connections</em> and <em>Web services</em> tab of the"
2721 * " Tools->Preferences dialog box to change them.</td></tr></table></html>"
2722 * ), true, true, "Web Service Configuration Problem", 450,
2725 * jd.waitForInput();
2727 JvOptionPane.showConfirmDialog(Desktop.desktop,
2728 new JLabel("<html><table width=\"450\"><tr><td>"
2729 + ermsg + "</td></tr></table>"
2730 + "<p>It may be that you have invalid JABA URLs<br/>in your web service preferences,"
2731 + "<br>or as a command-line argument, or mis-configured HTTP proxy settings.</p>"
2732 + "<p>Check the <em>Connections</em> and <em>Web services</em> tab<br/>of the"
2733 + " Tools->Preferences dialog box to change them.</p></html>"),
2734 "Web Service Configuration Problem",
2735 JvOptionPane.DEFAULT_OPTION,
2736 JvOptionPane.ERROR_MESSAGE);
2737 serviceChangedDialog = null;
2746 "Errors reported by JABA discovery service. Check web services preferences.\n"
2753 private Runnable serviceChangedDialog = null;
2756 * start a thread to open a URL in the configured browser. Pops up a warning
2757 * dialog to the user if there is an exception when calling out to the browser
2762 public static void showUrl(final String url)
2764 showUrl(url, Desktop.instance);
2768 * Like showUrl but allows progress handler to be specified
2772 * (null) or object implementing IProgressIndicator
2774 public static void showUrl(final String url,
2775 final IProgressIndicator progress)
2777 new Thread(new Runnable()
2784 if (progress != null)
2786 progress.setProgressBar(MessageManager
2787 .formatMessage("status.opening_params", new Object[]
2788 { url }), this.hashCode());
2790 jalview.util.BrowserLauncher.openURL(url);
2791 } catch (Exception ex)
2793 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
2795 .getString("label.web_browser_not_found_unix"),
2796 MessageManager.getString("label.web_browser_not_found"),
2797 JvOptionPane.WARNING_MESSAGE);
2799 ex.printStackTrace();
2801 if (progress != null)
2803 progress.setProgressBar(null, this.hashCode());
2809 public static WsParamSetManager wsparamManager = null;
2811 public static ParamManager getUserParameterStore()
2813 if (wsparamManager == null)
2815 wsparamManager = new WsParamSetManager();
2817 return wsparamManager;
2821 * static hyperlink handler proxy method for use by Jalview's internal windows
2825 public static void hyperlinkUpdate(HyperlinkEvent e)
2827 if (e.getEventType() == EventType.ACTIVATED)
2832 url = e.getURL().toString();
2833 Desktop.showUrl(url);
2834 } catch (Exception x)
2838 if (Cache.log != null)
2840 Cache.log.error("Couldn't handle string " + url + " as a URL.");
2845 "Couldn't handle string " + url + " as a URL.");
2848 // ignore any exceptions due to dud links.
2855 * single thread that handles display of dialogs to user.
2857 ExecutorService dialogExecutor = Executors.newSingleThreadExecutor();
2860 * flag indicating if dialogExecutor should try to acquire a permit
2862 private volatile boolean dialogPause = true;
2867 private java.util.concurrent.Semaphore block = new Semaphore(0);
2869 private static groovy.ui.Console groovyConsole;
2872 * add another dialog thread to the queue
2876 public void addDialogThread(final Runnable prompter)
2878 dialogExecutor.submit(new Runnable()
2888 } catch (InterruptedException x)
2892 if (instance == null)
2898 SwingUtilities.invokeAndWait(prompter);
2899 } catch (Exception q)
2901 Cache.log.warn("Unexpected Exception in dialog thread.", q);
2907 public void startDialogQueue()
2909 // set the flag so we don't pause waiting for another permit and semaphore
2910 // the current task to begin
2911 dialogPause = false;
2916 * Outputs an image of the desktop to file in EPS format, after prompting the
2917 * user for choice of Text or Lineart character rendering (unless a preference
2918 * has been set). The file name is generated as
2921 * Jalview_snapshot_nnnnn.eps where nnnnn is the current timestamp in milliseconds
2925 protected void snapShotWindow_actionPerformed(ActionEvent e)
2927 // currently the menu option to do this is not shown
2930 int width = getWidth();
2931 int height = getHeight();
2933 "Jalview_snapshot_" + System.currentTimeMillis() + ".eps");
2934 ImageWriterI writer = new ImageWriterI()
2937 public void exportImage(Graphics g) throws Exception
2940 Cache.log.info("Successfully written snapshot to file "
2941 + of.getAbsolutePath());
2944 String title = "View of desktop";
2945 ImageExporter exporter = new ImageExporter(writer, null, TYPE.EPS,
2947 exporter.doExport(of, this, width, height, title);
2951 * Explode the views in the given SplitFrame into separate SplitFrame windows.
2952 * This respects (remembers) any previous 'exploded geometry' i.e. the size and
2953 * location last time the view was expanded (if any). However it does not
2954 * remember the split pane divider location - this is set to match the
2955 * 'exploding' frame.
2959 public void explodeViews(SplitFrame sf)
2961 AlignFrame oldTopFrame = (AlignFrame) sf.getTopFrame();
2962 AlignFrame oldBottomFrame = (AlignFrame) sf.getBottomFrame();
2963 List<? extends AlignmentViewPanel> topPanels = oldTopFrame
2965 List<? extends AlignmentViewPanel> bottomPanels = oldBottomFrame
2967 int viewCount = topPanels.size();
2974 * Processing in reverse order works, forwards order leaves the first panels
2975 * not visible. I don't know why!
2977 for (int i = viewCount - 1; i >= 0; i--)
2980 * Make new top and bottom frames. These take over the respective
2981 * AlignmentPanel objects, including their AlignmentViewports, so the
2982 * cdna/protein relationships between the viewports is carried over to the
2985 * explodedGeometry holds the (x, y) position of the previously exploded
2986 * SplitFrame, and the (width, height) of the AlignFrame component
2988 AlignmentPanel topPanel = (AlignmentPanel) topPanels.get(i);
2989 AlignFrame newTopFrame = new AlignFrame(topPanel);
2990 newTopFrame.setSize(oldTopFrame.getSize());
2991 newTopFrame.setVisible(true);
2992 Rectangle geometry = ((AlignViewport) topPanel.getAlignViewport())
2993 .getExplodedGeometry();
2994 if (geometry != null)
2996 newTopFrame.setSize(geometry.getSize());
2999 AlignmentPanel bottomPanel = (AlignmentPanel) bottomPanels.get(i);
3000 AlignFrame newBottomFrame = new AlignFrame(bottomPanel);
3001 newBottomFrame.setSize(oldBottomFrame.getSize());
3002 newBottomFrame.setVisible(true);
3003 geometry = ((AlignViewport) bottomPanel.getAlignViewport())
3004 .getExplodedGeometry();
3005 if (geometry != null)
3007 newBottomFrame.setSize(geometry.getSize());
3010 topPanel.av.setGatherViewsHere(false);
3011 bottomPanel.av.setGatherViewsHere(false);
3012 JInternalFrame splitFrame = new SplitFrame(newTopFrame,
3014 if (geometry != null)
3016 splitFrame.setLocation(geometry.getLocation());
3018 Desktop.addInternalFrame(splitFrame, sf.getTitle(), -1, -1);
3022 * Clear references to the panels (now relocated in the new SplitFrames)
3023 * before closing the old SplitFrame.
3026 bottomPanels.clear();
3031 * Gather expanded split frames, sharing the same pairs of sequence set ids,
3032 * back into the given SplitFrame as additional views. Note that the gathered
3033 * frames may themselves have multiple views.
3037 public void gatherViews(GSplitFrame source)
3040 * special handling of explodedGeometry for a view within a SplitFrame: - it
3041 * holds the (x, y) position of the enclosing SplitFrame, and the (width,
3042 * height) of the AlignFrame component
3044 AlignFrame myTopFrame = (AlignFrame) source.getTopFrame();
3045 AlignFrame myBottomFrame = (AlignFrame) source.getBottomFrame();
3046 myTopFrame.viewport.setExplodedGeometry(new Rectangle(source.getX(),
3047 source.getY(), myTopFrame.getWidth(), myTopFrame.getHeight()));
3048 myBottomFrame.viewport
3049 .setExplodedGeometry(new Rectangle(source.getX(), source.getY(),
3050 myBottomFrame.getWidth(), myBottomFrame.getHeight()));
3051 myTopFrame.viewport.setGatherViewsHere(true);
3052 myBottomFrame.viewport.setGatherViewsHere(true);
3053 String topViewId = myTopFrame.viewport.getSequenceSetId();
3054 String bottomViewId = myBottomFrame.viewport.getSequenceSetId();
3056 JInternalFrame[] frames = desktop.getAllFrames();
3057 for (JInternalFrame frame : frames)
3059 if (frame instanceof SplitFrame && frame != source)
3061 SplitFrame sf = (SplitFrame) frame;
3062 AlignFrame topFrame = (AlignFrame) sf.getTopFrame();
3063 AlignFrame bottomFrame = (AlignFrame) sf.getBottomFrame();
3064 boolean gatherThis = false;
3065 for (int a = 0; a < topFrame.alignPanels.size(); a++)
3067 AlignmentPanel topPanel = topFrame.alignPanels.get(a);
3068 AlignmentPanel bottomPanel = bottomFrame.alignPanels.get(a);
3069 if (topViewId.equals(topPanel.av.getSequenceSetId())
3070 && bottomViewId.equals(bottomPanel.av.getSequenceSetId()))
3073 topPanel.av.setGatherViewsHere(false);
3074 bottomPanel.av.setGatherViewsHere(false);
3075 topPanel.av.setExplodedGeometry(
3076 new Rectangle(sf.getLocation(), topFrame.getSize()));
3077 bottomPanel.av.setExplodedGeometry(
3078 new Rectangle(sf.getLocation(), bottomFrame.getSize()));
3079 myTopFrame.addAlignmentPanel(topPanel, false);
3080 myBottomFrame.addAlignmentPanel(bottomPanel, false);
3086 topFrame.getAlignPanels().clear();
3087 bottomFrame.getAlignPanels().clear();
3094 * The dust settles...give focus to the tab we did this from.
3096 myTopFrame.setDisplayedView(myTopFrame.alignPanel);
3099 public static groovy.ui.Console getGroovyConsole()
3101 return groovyConsole;
3105 * handles the payload of a drag and drop event.
3107 * TODO refactor to desktop utilities class
3110 * - Data source strings extracted from the drop event
3112 * - protocol for each data source extracted from the drop event
3116 * - the payload from the drop event
3119 public static void transferFromDropTarget(List<Object> files,
3120 List<DataSourceType> protocols, DropTargetDropEvent evt,
3121 Transferable t) throws Exception
3124 // BH 2018 changed List<String> to List<Object> to allow for File from SwingJS
3126 // DataFlavor[] flavors = t.getTransferDataFlavors();
3127 // for (int i = 0; i < flavors.length; i++) {
3128 // if (flavors[i].isFlavorJavaFileListType()) {
3129 // evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
3130 // List<File> list = (List<File>) t.getTransferData(flavors[i]);
3131 // for (int j = 0; j < list.size(); j++) {
3132 // File file = (File) list.get(j);
3133 // byte[] data = getDroppedFileBytes(file);
3134 // fileName.setText(file.getName() + " - " + data.length + " " +
3135 // evt.getLocation());
3136 // JTextArea target = (JTextArea) ((DropTarget) evt.getSource()).getComponent();
3137 // target.setText(new String(data));
3139 // dtde.dropComplete(true);
3144 DataFlavor uriListFlavor = new DataFlavor(
3145 "text/uri-list;class=java.lang.String"), urlFlavour = null;
3148 urlFlavour = new DataFlavor(
3149 "application/x-java-url; class=java.net.URL");
3150 } catch (ClassNotFoundException cfe)
3152 Cache.log.debug("Couldn't instantiate the URL dataflavor.", cfe);
3155 if (urlFlavour != null && t.isDataFlavorSupported(urlFlavour))
3160 java.net.URL url = (URL) t.getTransferData(urlFlavour);
3161 // nb: java 8 osx bug https://bugs.openjdk.java.net/browse/JDK-8156099
3162 // means url may be null.
3165 protocols.add(DataSourceType.URL);
3166 files.add(url.toString());
3167 Cache.log.debug("Drop handled as URL dataflavor "
3168 + files.get(files.size() - 1));
3173 if (Platform.isAMacAndNotJS())
3176 "Please ignore plist error - occurs due to problem with java 8 on OSX");
3179 } catch (Throwable ex)
3181 Cache.log.debug("URL drop handler failed.", ex);
3184 if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
3186 // Works on Windows and MacOSX
3187 Cache.log.debug("Drop handled as javaFileListFlavor");
3188 for (Object file : (List) t
3189 .getTransferData(DataFlavor.javaFileListFlavor))
3192 protocols.add(DataSourceType.FILE);
3197 // Unix like behaviour
3198 boolean added = false;
3200 if (t.isDataFlavorSupported(uriListFlavor))
3202 Cache.log.debug("Drop handled as uriListFlavor");
3203 // This is used by Unix drag system
3204 data = (String) t.getTransferData(uriListFlavor);
3208 // fallback to text: workaround - on OSX where there's a JVM bug
3209 Cache.log.debug("standard URIListFlavor failed. Trying text");
3210 // try text fallback
3211 DataFlavor textDf = new DataFlavor(
3212 "text/plain;class=java.lang.String");
3213 if (t.isDataFlavorSupported(textDf))
3215 data = (String) t.getTransferData(textDf);
3218 Cache.log.debug("Plain text drop content returned "
3219 + (data == null ? "Null - failed" : data));
3224 while (protocols.size() < files.size())
3226 Cache.log.debug("Adding missing FILE protocol for "
3227 + files.get(protocols.size()));
3228 protocols.add(DataSourceType.FILE);
3230 for (java.util.StringTokenizer st = new java.util.StringTokenizer(
3231 data, "\r\n"); st.hasMoreTokens();)
3234 String s = st.nextToken();
3235 if (s.startsWith("#"))
3237 // the line is a comment (as per the RFC 2483)
3240 java.net.URI uri = new java.net.URI(s);
3241 if (uri.getScheme().toLowerCase().startsWith("http"))
3243 protocols.add(DataSourceType.URL);
3244 files.add(uri.toString());
3248 // otherwise preserve old behaviour: catch all for file objects
3249 java.io.File file = new java.io.File(uri);
3250 protocols.add(DataSourceType.FILE);
3251 files.add(file.toString());
3256 if (Cache.log.isDebugEnabled())
3258 if (data == null || !added)
3261 if (t.getTransferDataFlavors() != null
3262 && t.getTransferDataFlavors().length > 0)
3265 "Couldn't resolve drop data. Here are the supported flavors:");
3266 for (DataFlavor fl : t.getTransferDataFlavors())
3269 "Supported transfer dataflavor: " + fl.toString());
3270 Object df = t.getTransferData(fl);
3273 Cache.log.debug("Retrieves: " + df);
3277 Cache.log.debug("Retrieved nothing");
3283 Cache.log.debug("Couldn't resolve dataflavor for drop: "
3289 if (Platform.isWindowsAndNotJS())
3291 Cache.log.debug("Scanning dropped content for Windows Link Files");
3293 // resolve any .lnk files in the file drop
3294 for (int f = 0; f < files.size(); f++)
3296 String source = files.get(f).toString().toLowerCase();
3297 if (protocols.get(f).equals(DataSourceType.FILE)
3298 && (source.endsWith(".lnk") || source.endsWith(".url")
3299 || source.endsWith(".site")))
3303 Object obj = files.get(f);
3304 File lf = (obj instanceof File ? (File) obj
3305 : new File((String) obj));
3306 // process link file to get a URL
3307 Cache.log.debug("Found potential link file: " + lf);
3308 WindowsShortcut wscfile = new WindowsShortcut(lf);
3309 String fullname = wscfile.getRealFilename();
3310 protocols.set(f, FormatAdapter.checkProtocol(fullname));
3311 files.set(f, fullname);
3312 Cache.log.debug("Parsed real filename " + fullname
3313 + " to extract protocol: " + protocols.get(f));
3314 } catch (Exception ex)
3317 "Couldn't parse " + files.get(f) + " as a link file.",
3326 * Sets the Preferences property for experimental features to True or False
3327 * depending on the state of the controlling menu item
3330 protected void showExperimental_actionPerformed(boolean selected)
3332 Cache.setProperty(EXPERIMENTAL_FEATURES, Boolean.toString(selected));
3336 * Answers a (possibly empty) list of any structure viewer frames (currently for
3337 * either Jmol or Chimera) which are currently open. This may optionally be
3338 * restricted to viewers of a specified class, or viewers linked to a specified
3342 * if not null, only return viewers linked to this panel
3343 * @param structureViewerClass
3344 * if not null, only return viewers of this class
3347 public List<StructureViewerBase> getStructureViewers(
3348 AlignmentPanel apanel,
3349 Class<? extends StructureViewerBase> structureViewerClass)
3351 List<StructureViewerBase> result = new ArrayList<>();
3352 JInternalFrame[] frames = Desktop.instance.getAllFrames();
3354 for (JInternalFrame frame : frames)
3356 if (frame instanceof StructureViewerBase)
3358 if (structureViewerClass == null
3359 || structureViewerClass.isInstance(frame))
3362 || ((StructureViewerBase) frame).isLinkedWith(apanel))
3364 result.add((StructureViewerBase) frame);