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;
208 * BH TEMPORARY ONLY -- should use ApplicationSingleton
212 public static Desktop getInstance()
217 public static MyDesktopPane desktop;
219 public static MyDesktopPane getDesktopPane()
221 // BH 2018 could use currentThread() here as a reference to a
222 // Hashtable<Thread, MyDesktopPane> in JavaScript
226 public static StructureSelectionManager getStructureSelectionManager()
228 return StructureSelectionManager
229 .getStructureSelectionManager(getInstance());
232 static int openFrameCount = 0;
234 static final int xOffset = 30;
236 static final int yOffset = 30;
238 public static jalview.ws.jws1.Discoverer discoverer;
240 public static Object[] jalviewClipboard;
242 public static boolean internalCopy = false;
244 static int fileLoadingCount = 0;
246 class MyDesktopManager implements DesktopManager
249 private DesktopManager delegate;
251 public MyDesktopManager(DesktopManager delegate)
253 this.delegate = delegate;
257 public void activateFrame(JInternalFrame f)
261 delegate.activateFrame(f);
262 } catch (NullPointerException npe)
264 Point p = getMousePosition();
265 instance.showPasteMenu(p.x, p.y);
270 public void beginDraggingFrame(JComponent f)
272 delegate.beginDraggingFrame(f);
276 public void beginResizingFrame(JComponent f, int direction)
278 delegate.beginResizingFrame(f, direction);
282 public void closeFrame(JInternalFrame f)
284 delegate.closeFrame(f);
288 public void deactivateFrame(JInternalFrame f)
290 delegate.deactivateFrame(f);
294 public void deiconifyFrame(JInternalFrame f)
296 delegate.deiconifyFrame(f);
300 public void dragFrame(JComponent f, int newX, int newY)
306 delegate.dragFrame(f, newX, newY);
310 public void endDraggingFrame(JComponent f)
312 delegate.endDraggingFrame(f);
317 public void endResizingFrame(JComponent f)
319 delegate.endResizingFrame(f);
324 public void iconifyFrame(JInternalFrame f)
326 delegate.iconifyFrame(f);
330 public void maximizeFrame(JInternalFrame f)
332 delegate.maximizeFrame(f);
336 public void minimizeFrame(JInternalFrame f)
338 delegate.minimizeFrame(f);
342 public void openFrame(JInternalFrame f)
344 delegate.openFrame(f);
348 public void resizeFrame(JComponent f, int newX, int newY, int newWidth,
355 delegate.resizeFrame(f, newX, newY, newWidth, newHeight);
359 public void setBoundsForFrame(JComponent f, int newX, int newY,
360 int newWidth, int newHeight)
362 delegate.setBoundsForFrame(f, newX, newY, newWidth, newHeight);
365 // All other methods, simply delegate
370 * Creates a new Desktop object.
376 * A note to implementors. It is ESSENTIAL that any activities that might
377 * block are spawned off as threads rather than waited for during this
382 doConfigureStructurePrefs();
383 setTitle("Jalview " + Cache.getProperty("VERSION"));
385 if (!Platform.isAMac())
387 // this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
391 this.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
397 APQHandlers.setAPQHandlers(this);
398 } catch (Throwable t)
400 System.out.println("Error setting APQHandlers: " + t.toString());
401 // t.printStackTrace();
404 addWindowListener(new WindowAdapter()
408 public void windowClosing(WindowEvent ev)
414 boolean selmemusage = Cache.getDefault("SHOW_MEMUSAGE",
417 boolean showjconsole = Cache.getDefault("SHOW_JAVA_CONSOLE",
419 desktop = new MyDesktopPane(selmemusage);
421 showMemusage.setSelected(selmemusage);
422 desktop.setBackground(Color.white);
424 getContentPane().setLayout(new BorderLayout());
425 // alternate config - have scrollbars - see notes in JAL-153
426 // JScrollPane sp = new JScrollPane();
427 // sp.getViewport().setView(desktop);
428 // getContentPane().add(sp, BorderLayout.CENTER);
430 // BH 2018 - just an experiment to try unclipped JInternalFrames.
433 getRootPane().putClientProperty("swingjs.overflow.hidden", "false");
436 getContentPane().add(desktop, BorderLayout.CENTER);
437 desktop.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
439 // This line prevents Windows Look&Feel resizing all new windows to maximum
440 // if previous window was maximised
441 desktop.setDesktopManager(new MyDesktopManager(
442 (Platform.isWindowsAndNotJS() ? new DefaultDesktopManager()
443 : Platform.isAMacAndNotJS()
444 ? new AquaInternalFrameManager(
445 desktop.getDesktopManager())
446 : desktop.getDesktopManager())));
448 Rectangle dims = getLastKnownDimensions("");
455 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
456 int xPos = Math.max(5, (screenSize.width - 900) / 2);
457 int yPos = Math.max(5, (screenSize.height - 650) / 2);
458 setBounds(xPos, yPos, 900, 650);
461 if (!Platform.isJS())
468 jconsole = new Console(this, showjconsole);
469 jconsole.setHeader(Cache.getVersionDetailsForConsole());
470 showConsole(showjconsole);
472 showNews.setVisible(false);
474 experimentalFeatures.setSelected(showExperimental());
476 getIdentifiersOrgData();
480 // Spawn a thread that shows the splashscreen
482 SwingUtilities.invokeLater(new Runnable()
487 new SplashScreen(true);
491 // Thread off a new instance of the file chooser - this reduces the time
493 // takes to open it later on.
494 new Thread(new Runnable()
499 Cache.log.debug("Filechooser init thread started.");
500 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
501 JalviewFileChooser.forRead(Cache.getProperty("LAST_DIRECTORY"),
503 Cache.log.debug("Filechooser init thread finished.");
506 // Add the service change listener
507 changeSupport.addJalviewPropertyChangeListener("services",
508 new PropertyChangeListener()
512 public void propertyChange(PropertyChangeEvent evt)
514 Cache.log.debug("Firing service changed event for "
515 + evt.getNewValue());
516 JalviewServicesChanged(evt);
521 this.setDropTarget(new java.awt.dnd.DropTarget(desktop, this));
523 this.addWindowListener(new WindowAdapter()
526 public void windowClosing(WindowEvent evt)
533 this.addMouseListener(ma = new MouseAdapter()
536 public void mousePressed(MouseEvent evt)
538 if (evt.isPopupTrigger()) // Mac
540 showPasteMenu(evt.getX(), evt.getY());
545 public void mouseReleased(MouseEvent evt)
547 if (evt.isPopupTrigger()) // Windows
549 showPasteMenu(evt.getX(), evt.getY());
553 desktop.addMouseListener(ma);
558 * Answers true if user preferences to enable experimental features is True
563 public boolean showExperimental()
565 String experimental = Cache.getDefault(EXPERIMENTAL_FEATURES,
566 Boolean.FALSE.toString());
567 return Boolean.valueOf(experimental).booleanValue();
570 public void doConfigureStructurePrefs()
572 // configure services
573 StructureSelectionManager ssm = StructureSelectionManager
574 .getStructureSelectionManager(this);
575 if (Cache.getDefault(Preferences.ADD_SS_ANN, true))
577 ssm.setAddTempFacAnnot(Cache
578 .getDefault(Preferences.ADD_TEMPFACT_ANN, true));
579 ssm.setProcessSecondaryStructure(Cache
580 .getDefault(Preferences.STRUCT_FROM_PDB, true));
581 ssm.setSecStructServices(
582 Cache.getDefault(Preferences.USE_RNAVIEW, true));
586 ssm.setAddTempFacAnnot(false);
587 ssm.setProcessSecondaryStructure(false);
588 ssm.setSecStructServices(false);
592 public void checkForNews()
594 final Desktop me = this;
595 // Thread off the news reader, in case there are connection problems.
596 new Thread(new Runnable()
601 Cache.log.debug("Starting news thread.");
602 jvnews = new BlogReader(me);
603 showNews.setVisible(true);
604 Cache.log.debug("Completed news thread.");
609 public void getIdentifiersOrgData()
611 // Thread off the identifiers fetcher
612 new Thread(new Runnable()
617 Cache.log.debug("Downloading data from identifiers.org");
620 UrlDownloadClient.download(IdOrgSettings.getUrl(),
621 IdOrgSettings.getDownloadLocation());
622 } catch (IOException e)
624 Cache.log.debug("Exception downloading identifiers.org data"
633 protected void showNews_actionPerformed(ActionEvent e)
635 showNews(showNews.isSelected());
638 void showNews(boolean visible)
640 Cache.log.debug((visible ? "Showing" : "Hiding") + " news.");
641 showNews.setSelected(visible);
642 if (visible && !jvnews.isVisible())
644 new Thread(new Runnable()
649 long now = System.currentTimeMillis();
650 Desktop.instance.setProgressBar(
651 MessageManager.getString("status.refreshing_news"), now);
652 jvnews.refreshNews();
653 Desktop.instance.setProgressBar(null, now);
661 * recover the last known dimensions for a jalview window
664 * - empty string is desktop, all other windows have unique prefix
665 * @return null or last known dimensions scaled to current geometry (if last
666 * window geom was known)
668 Rectangle getLastKnownDimensions(String windowName)
670 // TODO: lock aspect ratio for scaling desktop Bug #0058199
671 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
672 String x = Cache.getProperty(windowName + "SCREEN_X");
673 String y = Cache.getProperty(windowName + "SCREEN_Y");
675 .getProperty(windowName + "SCREEN_WIDTH");
676 String height = Cache
677 .getProperty(windowName + "SCREEN_HEIGHT");
678 if ((x != null) && (y != null) && (width != null) && (height != null))
680 int ix = Integer.parseInt(x), iy = Integer.parseInt(y),
681 iw = Integer.parseInt(width), ih = Integer.parseInt(height);
682 if (Cache.getProperty("SCREENGEOMETRY_WIDTH") != null)
684 // attempt #1 - try to cope with change in screen geometry - this
685 // version doesn't preserve original jv aspect ratio.
686 // take ratio of current screen size vs original screen size.
687 double sw = ((1f * screenSize.width) / (1f * Integer.parseInt(
688 Cache.getProperty("SCREENGEOMETRY_WIDTH"))));
689 double sh = ((1f * screenSize.height) / (1f * Integer.parseInt(
690 Cache.getProperty("SCREENGEOMETRY_HEIGHT"))));
691 // rescale the bounds depending upon the current screen geometry.
692 ix = (int) (ix * sw);
693 iw = (int) (iw * sw);
694 iy = (int) (iy * sh);
695 ih = (int) (ih * sh);
696 while (ix >= screenSize.width)
699 "Window geometry location recall error: shifting horizontal to within screenbounds.");
700 ix -= screenSize.width;
702 while (iy >= screenSize.height)
705 "Window geometry location recall error: shifting vertical to within screenbounds.");
706 iy -= screenSize.height;
709 "Got last known dimensions for " + windowName + ": x:" + ix
710 + " y:" + iy + " width:" + iw + " height:" + ih);
712 // return dimensions for new instance
713 return new Rectangle(ix, iy, iw, ih);
718 void showPasteMenu(int x, int y)
720 JPopupMenu popup = new JPopupMenu();
721 JMenuItem item = new JMenuItem(
722 MessageManager.getString("label.paste_new_window"));
723 item.addActionListener(new ActionListener()
726 public void actionPerformed(ActionEvent evt)
733 popup.show(this, x, y);
740 Clipboard c = Toolkit.getDefaultToolkit().getSystemClipboard();
741 Transferable contents = c.getContents(this);
743 if (contents != null)
745 String file = (String) contents
746 .getTransferData(DataFlavor.stringFlavor);
748 FileFormatI format = new IdentifyFile().identify(file,
749 DataSourceType.PASTE);
751 new FileLoader().LoadFile(file, DataSourceType.PASTE, format);
754 } catch (Exception ex)
757 "Unable to paste alignment from system clipboard:\n" + ex);
762 * Adds and opens the given frame to the desktop
773 public static synchronized void addInternalFrame(
774 final JInternalFrame frame, String title, int w, int h)
776 addInternalFrame(frame, title, true, w, h, true, false);
780 * Add an internal frame to the Jalview desktop
787 * When true, display frame immediately, otherwise, caller must call
788 * setVisible themselves.
794 public static synchronized void addInternalFrame(
795 final JInternalFrame frame, String title, boolean makeVisible,
798 addInternalFrame(frame, title, makeVisible, w, h, true, false);
802 * Add an internal frame to the Jalview desktop and make it visible
815 public static synchronized void addInternalFrame(
816 final JInternalFrame frame, String title, int w, int h,
819 addInternalFrame(frame, title, true, w, h, resizable, false);
823 * Add an internal frame to the Jalview desktop
830 * When true, display frame immediately, otherwise, caller must call
831 * setVisible themselves.
838 * @param ignoreMinSize
839 * Do not set the default minimum size for frame
841 public static synchronized void addInternalFrame(
842 final JInternalFrame frame, String title, boolean makeVisible,
843 int w, int h, boolean resizable, boolean ignoreMinSize)
846 // TODO: allow callers to determine X and Y position of frame (eg. via
848 // TODO: consider fixing method to update entries in the window submenu with
849 // the current window title
851 frame.setTitle(title);
852 if (frame.getWidth() < 1 || frame.getHeight() < 1)
856 // THIS IS A PUBLIC STATIC METHOD, SO IT MAY BE CALLED EVEN IN
857 // A HEADLESS STATE WHEN NO DESKTOP EXISTS. MUST RETURN
858 // IF JALVIEW IS RUNNING HEADLESS
859 // ///////////////////////////////////////////////
860 if (instance == null || (System.getProperty("java.awt.headless") != null
861 && System.getProperty("java.awt.headless").equals("true")))
870 frame.setMinimumSize(
871 new Dimension(DEFAULT_MIN_WIDTH, DEFAULT_MIN_HEIGHT));
873 // Set default dimension for Alignment Frame window.
874 // The Alignment Frame window could be added from a number of places,
876 // I did this here in order not to miss out on any Alignment frame.
877 if (frame instanceof AlignFrame)
879 frame.setMinimumSize(new Dimension(ALIGN_FRAME_DEFAULT_MIN_WIDTH,
880 ALIGN_FRAME_DEFAULT_MIN_HEIGHT));
884 frame.setVisible(makeVisible);
885 frame.setClosable(true);
886 frame.setResizable(resizable);
887 frame.setMaximizable(resizable);
888 frame.setIconifiable(resizable);
889 frame.setOpaque(Platform.isJS());
890 boolean isEmbedded = (Platform.getDimIfEmbedded(frame, -1, -1) != null);
891 if (!isEmbedded && frame.getX() < 1 && frame.getY() < 1)
893 frame.setLocation(xOffset * openFrameCount,
894 yOffset * ((openFrameCount - 1) % 10) + yOffset);
898 * add an entry for the new frame in the Window menu
899 * (and remove it when the frame is closed)
901 final JMenuItem menuItem = new JMenuItem(title);
902 frame.addInternalFrameListener(new InternalFrameAdapter()
905 public void internalFrameActivated(InternalFrameEvent evt)
907 JInternalFrame itf = desktop.getSelectedFrame();
910 if (itf instanceof AlignFrame)
912 Jalview.setCurrentAlignFrame((AlignFrame) itf);
919 public void internalFrameClosed(InternalFrameEvent evt)
921 PaintRefresher.RemoveComponent(frame);
924 * defensive check to prevent frames being
925 * added half off the window
927 if (openFrameCount > 0)
933 * ensure no reference to alignFrame retained by menu item listener
935 if (menuItem.getActionListeners().length > 0)
937 menuItem.removeActionListener(menuItem.getActionListeners()[0]);
939 windowMenu.remove(menuItem);
943 menuItem.addActionListener(new ActionListener()
946 public void actionPerformed(ActionEvent e)
950 frame.setSelected(true);
951 frame.setIcon(false);
952 } catch (java.beans.PropertyVetoException ex)
954 // System.err.println(ex.toString());
959 setKeyBindings(frame);
963 windowMenu.add(menuItem);
968 frame.setSelected(true);
969 frame.requestFocus();
970 } catch (java.beans.PropertyVetoException ve)
972 } catch (java.lang.ClassCastException cex)
975 "Squashed a possible GUI implementation error. If you can recreate this, please look at http://issues.jalview.org/browse/JAL-869",
981 * Add key bindings to a JInternalFrame so that Ctrl-W and Cmd-W will close the
986 private static void setKeyBindings(JInternalFrame frame)
988 @SuppressWarnings("serial")
989 final Action closeAction = new AbstractAction()
992 public void actionPerformed(ActionEvent e)
999 * set up key bindings for Ctrl-W and Cmd-W, with the same (Close) action
1001 KeyStroke ctrlWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
1002 InputEvent.CTRL_DOWN_MASK);
1003 KeyStroke cmdWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
1004 ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx());
1006 InputMap inputMap = frame
1007 .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
1008 String ctrlW = ctrlWKey.toString();
1009 inputMap.put(ctrlWKey, ctrlW);
1010 inputMap.put(cmdWKey, ctrlW);
1012 ActionMap actionMap = frame.getActionMap();
1013 actionMap.put(ctrlW, closeAction);
1017 public void lostOwnership(Clipboard clipboard, Transferable contents)
1021 Desktop.jalviewClipboard = null;
1024 internalCopy = false;
1028 public void dragEnter(DropTargetDragEvent evt)
1033 public void dragExit(DropTargetEvent evt)
1038 public void dragOver(DropTargetDragEvent evt)
1043 public void dropActionChanged(DropTargetDragEvent evt)
1054 public void drop(DropTargetDropEvent evt)
1056 boolean success = true;
1057 // JAL-1552 - acceptDrop required before getTransferable call for
1058 // Java's Transferable for native dnd
1059 evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
1060 Transferable t = evt.getTransferable();
1061 List<Object> files = new ArrayList<>();
1062 List<DataSourceType> protocols = new ArrayList<>();
1066 Desktop.transferFromDropTarget(files, protocols, evt, t);
1067 } catch (Exception e)
1069 e.printStackTrace();
1077 for (int i = 0; i < files.size(); i++)
1079 // BH 2018 File or String
1080 Object file = files.get(i);
1081 String fileName = file.toString();
1082 DataSourceType protocol = (protocols == null)
1083 ? DataSourceType.FILE
1085 FileFormatI format = null;
1087 if (fileName.endsWith(".jar"))
1089 format = FileFormat.Jalview;
1094 format = new IdentifyFile().identify(file, protocol);
1096 if (file instanceof File)
1098 Platform.cacheFileData((File) file);
1100 new FileLoader().LoadFile(null, file, protocol, format);
1103 } catch (Exception ex)
1108 evt.dropComplete(success); // need this to ensure input focus is properly
1109 // transfered to any new windows created
1119 public void inputLocalFileMenuItem_actionPerformed(AlignViewport viewport)
1121 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
1122 JalviewFileChooser chooser = JalviewFileChooser
1123 .forRead(Cache.getProperty("LAST_DIRECTORY"), fileFormat, BackupFiles.getEnabled());
1125 chooser.setFileView(new JalviewFileView());
1126 chooser.setDialogTitle(
1127 MessageManager.getString("label.open_local_file"));
1128 chooser.setToolTipText(MessageManager.getString("action.open"));
1130 chooser.setResponseHandler(0, new Runnable()
1135 File selectedFile = chooser.getSelectedFile();
1136 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1138 FileFormatI format = chooser.getSelectedFormat();
1141 * Call IdentifyFile to verify the file contains what its extension implies.
1142 * Skip this step for dynamically added file formats, because
1143 * IdentifyFile does not know how to recognise them.
1145 if (FileFormats.getInstance().isIdentifiable(format))
1149 format = new IdentifyFile().identify(selectedFile,
1150 DataSourceType.FILE);
1151 } catch (FileFormatException e)
1153 // format = null; //??
1157 new FileLoader().LoadFile(viewport, selectedFile,
1158 DataSourceType.FILE, format);
1161 chooser.showOpenDialog(this);
1165 * Shows a dialog for input of a URL at which to retrieve alignment data
1170 public void inputURLMenuItem_actionPerformed(AlignViewport viewport)
1172 // This construct allows us to have a wider textfield
1174 JLabel label = new JLabel(
1175 MessageManager.getString("label.input_file_url"));
1177 JPanel panel = new JPanel(new GridLayout(2, 1));
1181 * the URL to fetch is
1182 * Java: an editable combobox with history
1183 * JS: (pending JAL-3038) a plain text field
1186 String urlBase = "http://www.";
1187 if (Platform.isJS())
1189 history = new JTextField(urlBase, 35);
1198 JComboBox<String> asCombo = new JComboBox<>();
1199 asCombo.setPreferredSize(new Dimension(400, 20));
1200 asCombo.setEditable(true);
1201 asCombo.addItem(urlBase);
1202 String historyItems = Cache.getProperty("RECENT_URL");
1203 if (historyItems != null)
1205 for (String token : historyItems.split("\\t"))
1207 asCombo.addItem(token);
1214 Object[] options = new Object[] { MessageManager.getString("action.ok"),
1215 MessageManager.getString("action.cancel") };
1216 Runnable action = new Runnable()
1221 @SuppressWarnings("unchecked")
1222 String url = (history instanceof JTextField
1223 ? ((JTextField) history).getText()
1224 : ((JComboBox<String>) history).getSelectedItem()
1227 if (url.toLowerCase().endsWith(".jar"))
1229 if (viewport != null)
1231 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1232 FileFormat.Jalview);
1236 new FileLoader().LoadFile(url, DataSourceType.URL,
1237 FileFormat.Jalview);
1242 FileFormatI format = null;
1245 format = new IdentifyFile().identify(url, DataSourceType.URL);
1246 } catch (FileFormatException e)
1248 // TODO revise error handling, distinguish between
1249 // URL not found and response not valid
1254 String msg = MessageManager
1255 .formatMessage("label.couldnt_locate", url);
1256 JvOptionPane.showInternalMessageDialog(Desktop.desktop, msg,
1257 MessageManager.getString("label.url_not_found"),
1258 JvOptionPane.WARNING_MESSAGE);
1263 if (viewport != null)
1265 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1270 new FileLoader().LoadFile(url, DataSourceType.URL, format);
1275 String dialogOption = MessageManager
1276 .getString("label.input_alignment_from_url");
1277 JvOptionPane.newOptionDialog(desktop).setResponseHandler(0, action)
1278 .showInternalDialog(panel, dialogOption,
1279 JvOptionPane.YES_NO_CANCEL_OPTION,
1280 JvOptionPane.PLAIN_MESSAGE, null, options,
1281 MessageManager.getString("action.ok"));
1285 * Opens the CutAndPaste window for the user to paste an alignment in to
1288 * - if not null, the pasted alignment is added to the current
1289 * alignment; if null, to a new alignment window
1292 public void inputTextboxMenuItem_actionPerformed(
1293 AlignmentViewPanel viewPanel)
1295 CutAndPasteTransfer cap = new CutAndPasteTransfer();
1296 cap.setForInput(viewPanel);
1297 Desktop.addInternalFrame(cap,
1298 MessageManager.getString("label.cut_paste_alignmen_file"), true,
1308 Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
1309 Cache.setProperty("SCREENGEOMETRY_WIDTH",
1311 Cache.setProperty("SCREENGEOMETRY_HEIGHT",
1312 screen.height + "");
1313 storeLastKnownDimensions("", new Rectangle(getBounds().x, getBounds().y,
1314 getWidth(), getHeight()));
1316 if (jconsole != null)
1318 storeLastKnownDimensions("JAVA_CONSOLE_", jconsole.getBounds());
1319 jconsole.stopConsole();
1323 storeLastKnownDimensions("JALVIEW_RSS_WINDOW_", jvnews.getBounds());
1326 if (dialogExecutor != null)
1328 dialogExecutor.shutdownNow();
1330 closeAll_actionPerformed(null);
1332 if (groovyConsole != null)
1334 // suppress a possible repeat prompt to save script
1335 groovyConsole.setDirty(false);
1336 groovyConsole.exit();
1341 private void storeLastKnownDimensions(String string, Rectangle jc)
1343 Cache.log.debug("Storing last known dimensions for "
1344 + string + ": x:" + jc.x + " y:" + jc.y + " width:" + jc.width
1345 + " height:" + jc.height);
1347 Cache.setProperty(string + "SCREEN_X", jc.x + "");
1348 Cache.setProperty(string + "SCREEN_Y", jc.y + "");
1349 Cache.setProperty(string + "SCREEN_WIDTH", jc.width + "");
1350 Cache.setProperty(string + "SCREEN_HEIGHT", jc.height + "");
1360 public void aboutMenuItem_actionPerformed(ActionEvent e)
1362 new Thread(new Runnable()
1367 new SplashScreen(false);
1373 * Returns the html text for the About screen, including any available version
1374 * number, build details, author details and citation reference, but without
1375 * the enclosing {@code html} tags
1379 public String getAboutMessage()
1381 StringBuilder message = new StringBuilder(1024);
1382 message.append("<h1><strong>Version: ")
1383 .append(Cache.getProperty("VERSION")).append("</strong></h1>")
1384 .append("<strong>Built: <em>")
1385 .append(Cache.getDefault("BUILD_DATE", "unknown"))
1386 .append("</em> from ").append(Cache.getBuildDetailsForSplash())
1387 .append("</strong>");
1389 String latestVersion = Cache.getDefault("LATEST_VERSION", "Checking");
1390 if (latestVersion.equals("Checking"))
1392 // JBP removed this message for 2.11: May be reinstated in future version
1393 // message.append("<br>...Checking latest version...</br>");
1395 else if (!latestVersion.equals(Cache.getProperty("VERSION")))
1397 boolean red = false;
1398 if (Cache.getProperty("VERSION").toLowerCase()
1399 .indexOf("automated build") == -1)
1402 // Displayed when code version and jnlp version do not match and code
1403 // version is not a development build
1404 message.append("<div style=\"color: #FF0000;font-style: bold;\">");
1407 message.append("<br>!! Version ")
1408 .append(Cache.getDefault("LATEST_VERSION", "..Checking.."))
1409 .append(" is available for download from ")
1410 .append(Cache.getDefault("www.jalview.org",
1411 "http://www.jalview.org"))
1415 message.append("</div>");
1418 message.append("<br>Authors: ");
1419 message.append(Cache.getDefault("AUTHORFNAMES", DEFAULT_AUTHORS));
1420 message.append(CITATION);
1422 return message.toString();
1426 * Action on requesting Help documentation
1429 public void documentationMenuItem_actionPerformed()
1433 if (Platform.isJS())
1435 BrowserLauncher.openURL("http://www.jalview.org/help.html");
1444 Help.showHelpWindow();
1446 } catch (Exception ex)
1448 System.err.println("Error opening help: " + ex.getMessage());
1453 public void closeAll_actionPerformed(ActionEvent e)
1455 // TODO show a progress bar while closing?
1456 JInternalFrame[] frames = desktop.getAllFrames();
1457 for (int i = 0; i < frames.length; i++)
1461 frames[i].setClosed(true);
1462 } catch (java.beans.PropertyVetoException ex)
1466 Jalview.setCurrentAlignFrame(null);
1467 System.out.println("ALL CLOSED");
1470 * reset state of singleton objects as appropriate (clear down session state
1471 * when all windows are closed)
1473 StructureSelectionManager ssm = StructureSelectionManager
1474 .getStructureSelectionManager(this);
1482 public void raiseRelated_actionPerformed(ActionEvent e)
1484 reorderAssociatedWindows(false, false);
1488 public void minimizeAssociated_actionPerformed(ActionEvent e)
1490 reorderAssociatedWindows(true, false);
1493 void closeAssociatedWindows()
1495 reorderAssociatedWindows(false, true);
1501 * @seejalview.jbgui.GDesktop#garbageCollect_actionPerformed(java.awt.event.
1505 protected void garbageCollect_actionPerformed(ActionEvent e)
1507 // We simply collect the garbage
1508 Cache.log.debug("Collecting garbage...");
1510 Cache.log.debug("Finished garbage collection.");
1517 * jalview.jbgui.GDesktop#showMemusage_actionPerformed(java.awt.event.ActionEvent
1521 protected void showMemusage_actionPerformed(ActionEvent e)
1523 desktop.showMemoryUsage(showMemusage.isSelected());
1530 * jalview.jbgui.GDesktop#showConsole_actionPerformed(java.awt.event.ActionEvent
1534 protected void showConsole_actionPerformed(ActionEvent e)
1536 showConsole(showConsole.isSelected());
1539 Console jconsole = null;
1542 * control whether the java console is visible or not
1546 void showConsole(boolean selected)
1548 // TODO: decide if we should update properties file
1549 if (jconsole != null) // BH 2018
1551 showConsole.setSelected(selected);
1552 Cache.setProperty("SHOW_JAVA_CONSOLE",
1553 Boolean.valueOf(selected).toString());
1554 jconsole.setVisible(selected);
1558 void reorderAssociatedWindows(boolean minimize, boolean close)
1560 JInternalFrame[] frames = desktop.getAllFrames();
1561 if (frames == null || frames.length < 1)
1566 AlignmentViewport source = null, target = null;
1567 if (frames[0] instanceof AlignFrame)
1569 source = ((AlignFrame) frames[0]).getCurrentView();
1571 else if (frames[0] instanceof TreePanel)
1573 source = ((TreePanel) frames[0]).getViewPort();
1575 else if (frames[0] instanceof PCAPanel)
1577 source = ((PCAPanel) frames[0]).av;
1579 else if (frames[0].getContentPane() instanceof PairwiseAlignPanel)
1581 source = ((PairwiseAlignPanel) frames[0].getContentPane()).av;
1586 for (int i = 0; i < frames.length; i++)
1589 if (frames[i] == null)
1593 if (frames[i] instanceof AlignFrame)
1595 target = ((AlignFrame) frames[i]).getCurrentView();
1597 else if (frames[i] instanceof TreePanel)
1599 target = ((TreePanel) frames[i]).getViewPort();
1601 else if (frames[i] instanceof PCAPanel)
1603 target = ((PCAPanel) frames[i]).av;
1605 else if (frames[i].getContentPane() instanceof PairwiseAlignPanel)
1607 target = ((PairwiseAlignPanel) frames[i].getContentPane()).av;
1610 if (source == target)
1616 frames[i].setClosed(true);
1620 frames[i].setIcon(minimize);
1623 frames[i].toFront();
1627 } catch (java.beans.PropertyVetoException ex)
1642 protected void preferences_actionPerformed(ActionEvent e)
1648 * Prompts the user to choose a file and then saves the Jalview state as a
1649 * Jalview project file
1652 public void saveState_actionPerformed()
1654 saveState_actionPerformed(false);
1657 public void saveState_actionPerformed(boolean saveAs)
1659 java.io.File projectFile = getProjectFile();
1660 // autoSave indicates we already have a file and don't need to ask
1661 boolean autoSave = projectFile != null && !saveAs
1662 && BackupFiles.getEnabled();
1664 // System.out.println("autoSave="+autoSave+", projectFile='"+projectFile+"',
1665 // saveAs="+saveAs+", Backups
1666 // "+(BackupFiles.getEnabled()?"enabled":"disabled"));
1668 boolean approveSave = false;
1671 JalviewFileChooser chooser = new JalviewFileChooser("jvp",
1674 chooser.setFileView(new JalviewFileView());
1675 chooser.setDialogTitle(MessageManager.getString("label.save_state"));
1677 int value = chooser.showSaveDialog(this);
1679 if (value == JalviewFileChooser.APPROVE_OPTION)
1681 projectFile = chooser.getSelectedFile();
1682 setProjectFile(projectFile);
1687 if (approveSave || autoSave)
1689 final Desktop me = this;
1690 final java.io.File chosenFile = projectFile;
1691 new Thread(new Runnable()
1696 // TODO: refactor to Jalview desktop session controller action.
1697 setProgressBar(MessageManager.formatMessage(
1698 "label.saving_jalview_project", new Object[]
1699 { chosenFile.getName() }), chosenFile.hashCode());
1700 Cache.setProperty("LAST_DIRECTORY",
1701 chosenFile.getParent());
1702 // TODO catch and handle errors for savestate
1703 // TODO prevent user from messing with the Desktop whilst we're saving
1706 boolean doBackup = BackupFiles.getEnabled();
1707 BackupFiles backupfiles = doBackup ? new BackupFiles(chosenFile) : null;
1709 new Jalview2XML().saveState(doBackup ? backupfiles.getTempFile() : chosenFile);
1713 backupfiles.setWriteSuccess(true);
1714 backupfiles.rollBackupsAndRenameTempFile();
1716 } catch (OutOfMemoryError oom)
1718 new OOMWarning("Whilst saving current state to "
1719 + chosenFile.getName(), oom);
1720 } catch (Exception ex)
1722 Cache.log.error("Problems whilst trying to save to "
1723 + chosenFile.getName(), ex);
1724 JvOptionPane.showMessageDialog(me,
1725 MessageManager.formatMessage(
1726 "label.error_whilst_saving_current_state_to",
1728 { chosenFile.getName() }),
1729 MessageManager.getString("label.couldnt_save_project"),
1730 JvOptionPane.WARNING_MESSAGE);
1732 setProgressBar(null, chosenFile.hashCode());
1739 public void saveAsState_actionPerformed(ActionEvent e)
1741 saveState_actionPerformed(true);
1744 private void setProjectFile(File choice)
1746 this.projectFile = choice;
1749 public File getProjectFile()
1751 return this.projectFile;
1755 * Shows a file chooser dialog and tries to read in the selected file as a
1759 public void loadState_actionPerformed()
1761 final String[] suffix = new String[] { "jvp", "jar" };
1762 final String[] desc = new String[] { "Jalview Project",
1763 "Jalview Project (old)" };
1764 JalviewFileChooser chooser = new JalviewFileChooser(
1765 Cache.getProperty("LAST_DIRECTORY"), suffix, desc,
1766 "Jalview Project", true, BackupFiles.getEnabled()); // last two booleans: allFiles,
1768 chooser.setFileView(new JalviewFileView());
1769 chooser.setDialogTitle(MessageManager.getString("label.restore_state"));
1770 chooser.setResponseHandler(0, new Runnable()
1775 File selectedFile = chooser.getSelectedFile();
1776 setProjectFile(selectedFile);
1777 String choice = selectedFile.getAbsolutePath();
1778 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1779 new Thread(new Runnable()
1786 new Jalview2XML().loadJalviewAlign(selectedFile);
1787 } catch (OutOfMemoryError oom)
1789 new OOMWarning("Whilst loading project from " + choice, oom);
1790 } catch (Exception ex)
1793 "Problems whilst loading project from " + choice, ex);
1794 JvOptionPane.showMessageDialog(Desktop.desktop,
1795 MessageManager.formatMessage(
1796 "label.error_whilst_loading_project_from",
1799 MessageManager.getString("label.couldnt_load_project"),
1800 JvOptionPane.WARNING_MESSAGE);
1807 chooser.showOpenDialog(this);
1811 public void inputSequence_actionPerformed(ActionEvent e)
1813 new SequenceFetcher(this);
1816 JPanel progressPanel;
1818 ArrayList<JPanel> fileLoadingPanels = new ArrayList<>();
1820 public void startLoading(final Object fileName)
1822 if (fileLoadingCount == 0)
1824 fileLoadingPanels.add(addProgressPanel(MessageManager
1825 .formatMessage("label.loading_file", new Object[]
1831 private JPanel addProgressPanel(String string)
1833 if (progressPanel == null)
1835 progressPanel = new JPanel(new GridLayout(1, 1));
1836 totalProgressCount = 0;
1837 instance.getContentPane().add(progressPanel, BorderLayout.SOUTH);
1839 JPanel thisprogress = new JPanel(new BorderLayout(10, 5));
1840 JProgressBar progressBar = new JProgressBar();
1841 progressBar.setIndeterminate(true);
1843 thisprogress.add(new JLabel(string), BorderLayout.WEST);
1845 thisprogress.add(progressBar, BorderLayout.CENTER);
1846 progressPanel.add(thisprogress);
1847 ((GridLayout) progressPanel.getLayout()).setRows(
1848 ((GridLayout) progressPanel.getLayout()).getRows() + 1);
1849 ++totalProgressCount;
1850 instance.validate();
1851 return thisprogress;
1854 int totalProgressCount = 0;
1856 private void removeProgressPanel(JPanel progbar)
1858 if (progressPanel != null)
1860 synchronized (progressPanel)
1862 progressPanel.remove(progbar);
1863 GridLayout gl = (GridLayout) progressPanel.getLayout();
1864 gl.setRows(gl.getRows() - 1);
1865 if (--totalProgressCount < 1)
1867 this.getContentPane().remove(progressPanel);
1868 progressPanel = null;
1875 public void stopLoading()
1878 if (fileLoadingCount < 1)
1880 while (fileLoadingPanels.size() > 0)
1882 removeProgressPanel(fileLoadingPanels.remove(0));
1884 fileLoadingPanels.clear();
1885 fileLoadingCount = 0;
1890 public static int getViewCount(String alignmentId)
1892 AlignmentViewport[] aps = getViewports(alignmentId);
1893 return (aps == null) ? 0 : aps.length;
1898 * @param alignmentId
1899 * - if null, all sets are returned
1900 * @return all AlignmentPanels concerning the alignmentId sequence set
1902 public static AlignmentPanel[] getAlignmentPanels(String alignmentId)
1904 if (Desktop.desktop == null)
1906 // no frames created and in headless mode
1907 // TODO: verify that frames are recoverable when in headless mode
1910 List<AlignmentPanel> aps = new ArrayList<>();
1911 AlignFrame[] frames = getAlignFrames();
1916 for (AlignFrame af : frames)
1918 for (AlignmentPanel ap : af.alignPanels)
1920 if (alignmentId == null
1921 || alignmentId.equals(ap.av.getSequenceSetId()))
1927 if (aps.size() == 0)
1931 AlignmentPanel[] vap = aps.toArray(new AlignmentPanel[aps.size()]);
1936 * get all the viewports on an alignment.
1938 * @param sequenceSetId
1939 * unique alignment id (may be null - all viewports returned in that
1941 * @return all viewports on the alignment bound to sequenceSetId
1943 public static AlignmentViewport[] getViewports(String sequenceSetId)
1945 List<AlignmentViewport> viewp = new ArrayList<>();
1946 if (desktop != null)
1948 AlignFrame[] frames = Desktop.getAlignFrames();
1950 for (AlignFrame afr : frames)
1952 if (sequenceSetId == null || afr.getViewport().getSequenceSetId()
1953 .equals(sequenceSetId))
1955 if (afr.alignPanels != null)
1957 for (AlignmentPanel ap : afr.alignPanels)
1959 if (sequenceSetId == null
1960 || sequenceSetId.equals(ap.av.getSequenceSetId()))
1968 viewp.add(afr.getViewport());
1972 if (viewp.size() > 0)
1974 return viewp.toArray(new AlignmentViewport[viewp.size()]);
1981 * Explode the views in the given frame into separate AlignFrame
1985 public static void explodeViews(AlignFrame af)
1987 int size = af.alignPanels.size();
1993 // FIXME: ideally should use UI interface API
1994 FeatureSettings viewFeatureSettings = (af.featureSettings != null
1995 && af.featureSettings.isOpen())
1996 ? af.featureSettings
1998 Rectangle fsBounds = af.getFeatureSettingsGeometry();
1999 for (int i = 0; i < size; i++)
2001 AlignmentPanel ap = af.alignPanels.get(i);
2003 AlignFrame newaf = new AlignFrame(ap);
2005 // transfer reference for existing feature settings to new alignFrame
2006 if (ap == af.alignPanel)
2008 if (viewFeatureSettings != null && viewFeatureSettings.fr.ap == ap)
2010 newaf.featureSettings = viewFeatureSettings;
2012 newaf.setFeatureSettingsGeometry(fsBounds);
2016 * Restore the view's last exploded frame geometry if known. Multiple
2017 * views from one exploded frame share and restore the same (frame)
2018 * position and size.
2020 Rectangle geometry = ap.av.getExplodedGeometry();
2021 if (geometry != null)
2023 newaf.setBounds(geometry);
2026 ap.av.setGatherViewsHere(false);
2028 addInternalFrame(newaf, af.getTitle(), AlignFrame.DEFAULT_WIDTH,
2029 AlignFrame.DEFAULT_HEIGHT);
2030 // and materialise a new feature settings dialog instance for the new alignframe
2031 // (closes the old as if 'OK' was pressed)
2032 if (ap == af.alignPanel && newaf.featureSettings != null
2033 && newaf.featureSettings.isOpen()
2034 && af.alignPanel.getAlignViewport().isShowSequenceFeatures())
2036 newaf.showFeatureSettingsUI();
2040 af.featureSettings = null;
2041 af.alignPanels.clear();
2042 af.closeMenuItem_actionPerformed(true);
2047 * Gather expanded views (separate AlignFrame's) with the same sequence set
2048 * identifier back in to this frame as additional views, and close the expanded
2049 * views. Note the expanded frames may themselves have multiple views. We take
2054 public void gatherViews(AlignFrame source)
2056 source.viewport.setGatherViewsHere(true);
2057 source.viewport.setExplodedGeometry(source.getBounds());
2058 JInternalFrame[] frames = desktop.getAllFrames();
2059 String viewId = source.viewport.getSequenceSetId();
2060 for (int t = 0; t < frames.length; t++)
2062 if (frames[t] instanceof AlignFrame && frames[t] != source)
2064 AlignFrame af = (AlignFrame) frames[t];
2065 boolean gatherThis = false;
2066 for (int a = 0; a < af.alignPanels.size(); a++)
2068 AlignmentPanel ap = af.alignPanels.get(a);
2069 if (viewId.equals(ap.av.getSequenceSetId()))
2072 ap.av.setGatherViewsHere(false);
2073 ap.av.setExplodedGeometry(af.getBounds());
2074 source.addAlignmentPanel(ap, false);
2080 if (af.featureSettings != null && af.featureSettings.isOpen())
2082 if (source.featureSettings == null)
2084 // preserve the feature settings geometry for this frame
2085 source.featureSettings = af.featureSettings;
2086 source.setFeatureSettingsGeometry(
2087 af.getFeatureSettingsGeometry());
2091 // close it and forget
2092 af.featureSettings.close();
2095 af.alignPanels.clear();
2096 af.closeMenuItem_actionPerformed(true);
2101 // refresh the feature setting UI for the source frame if it exists
2102 if (source.featureSettings != null
2103 && source.featureSettings.isOpen())
2105 source.showFeatureSettingsUI();
2109 public JInternalFrame[] getAllFrames()
2111 return desktop.getAllFrames();
2115 * Checks the given url to see if it gives a response indicating that the user
2116 * should be informed of a new questionnaire.
2120 public void checkForQuestionnaire(String url)
2122 UserQuestionnaireCheck jvq = new UserQuestionnaireCheck(url);
2123 // javax.swing.SwingUtilities.invokeLater(jvq);
2124 new Thread(jvq).start();
2127 public void checkURLLinks()
2129 // Thread off the URL link checker
2130 addDialogThread(new Runnable()
2135 if (Cache.getDefault("CHECKURLLINKS", true))
2137 // check what the actual links are - if it's just the default don't
2138 // bother with the warning
2139 List<String> links = Preferences.sequenceUrlLinks
2142 // only need to check links if there is one with a
2143 // SEQUENCE_ID which is not the default EMBL_EBI link
2144 ListIterator<String> li = links.listIterator();
2145 boolean check = false;
2146 List<JLabel> urls = new ArrayList<>();
2147 while (li.hasNext())
2149 String link = li.next();
2150 if (link.contains(jalview.util.UrlConstants.SEQUENCE_ID)
2151 && !UrlConstants.isDefaultString(link))
2154 int barPos = link.indexOf("|");
2155 String urlMsg = barPos == -1 ? link
2156 : link.substring(0, barPos) + ": "
2157 + link.substring(barPos + 1);
2158 urls.add(new JLabel(urlMsg));
2166 // ask user to check in case URL links use old style tokens
2167 // ($SEQUENCE_ID$ for sequence id _or_ accession id)
2168 JPanel msgPanel = new JPanel();
2169 msgPanel.setLayout(new BoxLayout(msgPanel, BoxLayout.PAGE_AXIS));
2170 msgPanel.add(Box.createVerticalGlue());
2171 JLabel msg = new JLabel(MessageManager
2172 .getString("label.SEQUENCE_ID_for_DB_ACCESSION1"));
2173 JLabel msg2 = new JLabel(MessageManager
2174 .getString("label.SEQUENCE_ID_for_DB_ACCESSION2"));
2176 for (JLabel url : urls)
2182 final JCheckBox jcb = new JCheckBox(
2183 MessageManager.getString("label.do_not_display_again"));
2184 jcb.addActionListener(new ActionListener()
2187 public void actionPerformed(ActionEvent e)
2189 // update Cache settings for "don't show this again"
2190 boolean showWarningAgain = !jcb.isSelected();
2191 Cache.setProperty("CHECKURLLINKS",
2192 Boolean.valueOf(showWarningAgain).toString());
2197 JvOptionPane.showMessageDialog(Desktop.desktop, msgPanel,
2199 .getString("label.SEQUENCE_ID_no_longer_used"),
2200 JvOptionPane.WARNING_MESSAGE);
2207 * Proxy class for JDesktopPane which optionally displays the current memory
2208 * usage and highlights the desktop area with a red bar if free memory runs low.
2212 public class MyDesktopPane extends JDesktopPane
2215 private static final float ONE_MB = 1048576f;
2217 boolean showMemoryUsage = false;
2221 java.text.NumberFormat df;
2223 float maxMemory, allocatedMemory, freeMemory, totalFreeMemory,
2226 public MyDesktopPane(boolean showMemoryUsage)
2228 showMemoryUsage(showMemoryUsage);
2231 public void showMemoryUsage(boolean showMemory)
2233 this.showMemoryUsage = showMemory;
2236 Thread worker = new Thread(this);
2242 public boolean isShowMemoryUsage()
2244 return showMemoryUsage;
2250 df = java.text.NumberFormat.getNumberInstance();
2251 df.setMaximumFractionDigits(2);
2252 runtime = Runtime.getRuntime();
2254 while (showMemoryUsage)
2258 maxMemory = runtime.maxMemory() / ONE_MB;
2259 allocatedMemory = runtime.totalMemory() / ONE_MB;
2260 freeMemory = runtime.freeMemory() / ONE_MB;
2261 totalFreeMemory = freeMemory + (maxMemory - allocatedMemory);
2263 percentUsage = (totalFreeMemory / maxMemory) * 100;
2265 // if (percentUsage < 20)
2267 // border1 = BorderFactory.createMatteBorder(12, 12, 12, 12,
2269 // instance.set.setBorder(border1);
2272 // sleep after showing usage
2274 } catch (Exception ex)
2276 ex.printStackTrace();
2282 public void paintComponent(Graphics g)
2284 if (showMemoryUsage && g != null && df != null)
2286 if (percentUsage < 20)
2288 g.setColor(Color.red);
2290 FontMetrics fm = g.getFontMetrics();
2293 g.drawString(MessageManager.formatMessage("label.memory_stats",
2295 { df.format(totalFreeMemory), df.format(maxMemory),
2296 df.format(percentUsage) }),
2297 10, getHeight() - fm.getHeight());
2304 * Accessor method to quickly get all the AlignmentFrames loaded.
2306 * @return an array of AlignFrame, or null if none found
2308 public static AlignFrame[] getAlignFrames()
2310 if (Jalview.isHeadlessMode())
2312 // Desktop.desktop is null in headless mode
2313 return new AlignFrame[] { Jalview.currentAlignFrame };
2316 JInternalFrame[] frames = Desktop.desktop.getAllFrames();
2322 List<AlignFrame> avp = new ArrayList<>();
2324 for (int i = frames.length - 1; i > -1; i--)
2326 if (frames[i] instanceof AlignFrame)
2328 avp.add((AlignFrame) frames[i]);
2330 else if (frames[i] instanceof SplitFrame)
2333 * Also check for a split frame containing an AlignFrame
2335 GSplitFrame sf = (GSplitFrame) frames[i];
2336 if (sf.getTopFrame() instanceof AlignFrame)
2338 avp.add((AlignFrame) sf.getTopFrame());
2340 if (sf.getBottomFrame() instanceof AlignFrame)
2342 avp.add((AlignFrame) sf.getBottomFrame());
2346 if (avp.size() == 0)
2350 AlignFrame afs[] = avp.toArray(new AlignFrame[avp.size()]);
2355 * Returns an array of any AppJmol frames in the Desktop (or null if none).
2359 public GStructureViewer[] getJmols()
2361 JInternalFrame[] frames = Desktop.desktop.getAllFrames();
2367 List<GStructureViewer> avp = new ArrayList<>();
2369 for (int i = frames.length - 1; i > -1; i--)
2371 if (frames[i] instanceof AppJmol)
2373 GStructureViewer af = (GStructureViewer) frames[i];
2377 if (avp.size() == 0)
2381 GStructureViewer afs[] = avp.toArray(new GStructureViewer[avp.size()]);
2386 * Add Groovy Support to Jalview
2389 public void groovyShell_actionPerformed()
2393 openGroovyConsole();
2394 } catch (Exception ex)
2396 Cache.log.error("Groovy Shell Creation failed.", ex);
2397 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
2399 MessageManager.getString("label.couldnt_create_groovy_shell"),
2400 MessageManager.getString("label.groovy_support_failed"),
2401 JvOptionPane.ERROR_MESSAGE);
2406 * Open the Groovy console
2408 void openGroovyConsole()
2410 if (groovyConsole == null)
2412 groovyConsole = new groovy.ui.Console();
2413 groovyConsole.setVariable("Jalview", this);
2414 groovyConsole.run();
2417 * We allow only one console at a time, so that AlignFrame menu option
2418 * 'Calculate | Run Groovy script' is unambiguous.
2419 * Disable 'Groovy Console', and enable 'Run script', when the console is
2420 * opened, and the reverse when it is closed
2422 Window window = (Window) groovyConsole.getFrame();
2423 window.addWindowListener(new WindowAdapter()
2426 public void windowClosed(WindowEvent e)
2429 * rebind CMD-Q from Groovy Console to Jalview Quit
2432 enableExecuteGroovy(false);
2438 * show Groovy console window (after close and reopen)
2440 ((Window) groovyConsole.getFrame()).setVisible(true);
2443 * if we got this far, enable 'Run Groovy' in AlignFrame menus
2444 * and disable opening a second console
2446 enableExecuteGroovy(true);
2450 * Bind Ctrl/Cmd-Q to Quit - for reset as Groovy Console takes over this binding
2453 protected void addQuitHandler()
2455 getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
2456 .put(KeyStroke.getKeyStroke(KeyEvent.VK_Q,
2457 jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx()),
2459 getRootPane().getActionMap().put("Quit", new AbstractAction()
2462 public void actionPerformed(ActionEvent e)
2470 * Enable or disable 'Run Groovy script' in AlignFrame calculate menus
2473 * true if Groovy console is open
2475 public void enableExecuteGroovy(boolean enabled)
2478 * disable opening a second Groovy console
2479 * (or re-enable when the console is closed)
2481 groovyShell.setEnabled(!enabled);
2483 AlignFrame[] alignFrames = getAlignFrames();
2484 if (alignFrames != null)
2486 for (AlignFrame af : alignFrames)
2488 af.setGroovyEnabled(enabled);
2494 * Progress bars managed by the IProgressIndicator method.
2496 private Hashtable<Long, JPanel> progressBars;
2498 private Hashtable<Long, IProgressIndicatorHandler> progressBarHandlers;
2503 * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
2506 public void setProgressBar(String message, long id)
2508 // Platform.timeCheck("Desktop " + message, Platform.TIME_MARK);
2510 if (progressBars == null)
2512 progressBars = new Hashtable<>();
2513 progressBarHandlers = new Hashtable<>();
2516 if (progressBars.get(Long.valueOf(id)) != null)
2518 JPanel panel = progressBars.remove(Long.valueOf(id));
2519 if (progressBarHandlers.contains(Long.valueOf(id)))
2521 progressBarHandlers.remove(Long.valueOf(id));
2523 removeProgressPanel(panel);
2527 progressBars.put(Long.valueOf(id), addProgressPanel(message));
2534 * @see jalview.gui.IProgressIndicator#registerHandler(long,
2535 * jalview.gui.IProgressIndicatorHandler)
2538 public void registerHandler(final long id,
2539 final IProgressIndicatorHandler handler)
2541 if (progressBarHandlers == null
2542 || !progressBars.containsKey(Long.valueOf(id)))
2544 throw new Error(MessageManager.getString(
2545 "error.call_setprogressbar_before_registering_handler"));
2547 progressBarHandlers.put(Long.valueOf(id), handler);
2548 final JPanel progressPanel = progressBars.get(Long.valueOf(id));
2549 if (handler.canCancel())
2551 JButton cancel = new JButton(
2552 MessageManager.getString("action.cancel"));
2553 final IProgressIndicator us = this;
2554 cancel.addActionListener(new ActionListener()
2558 public void actionPerformed(ActionEvent e)
2560 handler.cancelActivity(id);
2561 us.setProgressBar(MessageManager
2562 .formatMessage("label.cancelled_params", new Object[]
2563 { ((JLabel) progressPanel.getComponent(0)).getText() }),
2567 progressPanel.add(cancel, BorderLayout.EAST);
2573 * @return true if any progress bars are still active
2576 public boolean operationInProgress()
2578 if (progressBars != null && progressBars.size() > 0)
2586 * This will return the first AlignFrame holding the given viewport instance. It
2587 * will break if there are more than one AlignFrames viewing a particular av.
2590 * @return alignFrame for viewport
2592 public static AlignFrame getAlignFrameFor(AlignViewportI viewport)
2594 if (desktop != null)
2596 AlignmentPanel[] aps = getAlignmentPanels(
2597 viewport.getSequenceSetId());
2598 for (int panel = 0; aps != null && panel < aps.length; panel++)
2600 if (aps[panel] != null && aps[panel].av == viewport)
2602 return aps[panel].alignFrame;
2609 public VamsasApplication getVamsasApplication()
2611 // TODO: JAL-3311 remove remaining code from Jalview relating to VAMSAS
2617 * flag set if jalview GUI is being operated programmatically
2619 private boolean inBatchMode = false;
2622 * check if jalview GUI is being operated programmatically
2624 * @return inBatchMode
2626 public boolean isInBatchMode()
2632 * set flag if jalview GUI is being operated programmatically
2634 * @param inBatchMode
2636 public void setInBatchMode(boolean inBatchMode)
2638 this.inBatchMode = inBatchMode;
2641 public void startServiceDiscovery()
2643 startServiceDiscovery(false);
2646 public void startServiceDiscovery(boolean blocking)
2648 boolean alive = true;
2649 Thread t0 = null, t1 = null, t2 = null;
2650 // JAL-940 - JALVIEW 1 services are now being EOLed as of JABA 2.1 release
2653 // todo: changesupport handlers need to be transferred
2654 if (discoverer == null)
2656 discoverer = new jalview.ws.jws1.Discoverer();
2657 // register PCS handler for desktop.
2658 discoverer.addPropertyChangeListener(changeSupport);
2660 // JAL-940 - disabled JWS1 service configuration - always start discoverer
2661 // until we phase out completely
2662 (t0 = new Thread(discoverer)).start();
2665 if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
2667 t2 = jalview.ws.jws2.Jws2Discoverer.getInstance()
2668 .startDiscoverer(changeSupport);
2672 // TODO: do rest service discovery
2681 } catch (Exception e)
2684 alive = (t1 != null && t1.isAlive()) || (t2 != null && t2.isAlive())
2685 || (t3 != null && t3.isAlive())
2686 || (t0 != null && t0.isAlive());
2692 * called to check if the service discovery process completed successfully.
2696 protected void JalviewServicesChanged(PropertyChangeEvent evt)
2698 if (evt.getNewValue() == null || evt.getNewValue() instanceof Vector)
2700 final String ermsg = jalview.ws.jws2.Jws2Discoverer.getInstance()
2701 .getErrorMessages();
2704 if (Cache.getDefault("SHOW_WSDISCOVERY_ERRORS", true))
2706 if (serviceChangedDialog == null)
2708 // only run if we aren't already displaying one of these.
2709 addDialogThread(serviceChangedDialog = new Runnable()
2716 * JalviewDialog jd =new JalviewDialog() {
2718 * @Override protected void cancelPressed() { // TODO
2719 * Auto-generated method stub
2721 * }@Override protected void okPressed() { // TODO
2722 * Auto-generated method stub
2724 * }@Override protected void raiseClosed() { // TODO
2725 * Auto-generated method stub
2727 * } }; jd.initDialogFrame(new
2728 * JLabel("<html><table width=\"450\"><tr><td>" + ermsg +
2729 * "<br/>It may be that you have invalid JABA URLs in your web service preferences,"
2730 * + " or mis-configured HTTP proxy settings.<br/>" +
2731 * "Check the <em>Connections</em> and <em>Web services</em> tab of the"
2733 * " Tools->Preferences dialog box to change them.</td></tr></table></html>"
2734 * ), true, true, "Web Service Configuration Problem", 450,
2737 * jd.waitForInput();
2739 JvOptionPane.showConfirmDialog(Desktop.desktop,
2740 new JLabel("<html><table width=\"450\"><tr><td>"
2741 + ermsg + "</td></tr></table>"
2742 + "<p>It may be that you have invalid JABA URLs<br/>in your web service preferences,"
2743 + "<br>or as a command-line argument, or mis-configured HTTP proxy settings.</p>"
2744 + "<p>Check the <em>Connections</em> and <em>Web services</em> tab<br/>of the"
2745 + " Tools->Preferences dialog box to change them.</p></html>"),
2746 "Web Service Configuration Problem",
2747 JvOptionPane.DEFAULT_OPTION,
2748 JvOptionPane.ERROR_MESSAGE);
2749 serviceChangedDialog = null;
2758 "Errors reported by JABA discovery service. Check web services preferences.\n"
2765 private Runnable serviceChangedDialog = null;
2768 * start a thread to open a URL in the configured browser. Pops up a warning
2769 * dialog to the user if there is an exception when calling out to the browser
2774 public static void showUrl(final String url)
2776 showUrl(url, Desktop.instance);
2780 * Like showUrl but allows progress handler to be specified
2784 * (null) or object implementing IProgressIndicator
2786 public static void showUrl(final String url,
2787 final IProgressIndicator progress)
2789 new Thread(new Runnable()
2796 if (progress != null)
2798 progress.setProgressBar(MessageManager
2799 .formatMessage("status.opening_params", new Object[]
2800 { url }), this.hashCode());
2802 jalview.util.BrowserLauncher.openURL(url);
2803 } catch (Exception ex)
2805 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
2807 .getString("label.web_browser_not_found_unix"),
2808 MessageManager.getString("label.web_browser_not_found"),
2809 JvOptionPane.WARNING_MESSAGE);
2811 ex.printStackTrace();
2813 if (progress != null)
2815 progress.setProgressBar(null, this.hashCode());
2821 public static WsParamSetManager wsparamManager = null;
2823 public static ParamManager getUserParameterStore()
2825 if (wsparamManager == null)
2827 wsparamManager = new WsParamSetManager();
2829 return wsparamManager;
2833 * static hyperlink handler proxy method for use by Jalview's internal windows
2837 public static void hyperlinkUpdate(HyperlinkEvent e)
2839 if (e.getEventType() == EventType.ACTIVATED)
2844 url = e.getURL().toString();
2845 Desktop.showUrl(url);
2846 } catch (Exception x)
2850 if (Cache.log != null)
2852 Cache.log.error("Couldn't handle string " + url + " as a URL.");
2857 "Couldn't handle string " + url + " as a URL.");
2860 // ignore any exceptions due to dud links.
2867 * single thread that handles display of dialogs to user.
2869 ExecutorService dialogExecutor = Executors.newSingleThreadExecutor();
2872 * flag indicating if dialogExecutor should try to acquire a permit
2874 private volatile boolean dialogPause = true;
2879 private java.util.concurrent.Semaphore block = new Semaphore(0);
2881 private static groovy.ui.Console groovyConsole;
2884 * add another dialog thread to the queue
2888 public void addDialogThread(final Runnable prompter)
2890 dialogExecutor.submit(new Runnable()
2900 } catch (InterruptedException x)
2904 if (instance == null)
2910 SwingUtilities.invokeAndWait(prompter);
2911 } catch (Exception q)
2913 Cache.log.warn("Unexpected Exception in dialog thread.", q);
2919 public void startDialogQueue()
2921 // set the flag so we don't pause waiting for another permit and semaphore
2922 // the current task to begin
2923 dialogPause = false;
2928 * Outputs an image of the desktop to file in EPS format, after prompting the
2929 * user for choice of Text or Lineart character rendering (unless a preference
2930 * has been set). The file name is generated as
2933 * Jalview_snapshot_nnnnn.eps where nnnnn is the current timestamp in milliseconds
2937 protected void snapShotWindow_actionPerformed(ActionEvent e)
2939 // currently the menu option to do this is not shown
2942 int width = getWidth();
2943 int height = getHeight();
2945 "Jalview_snapshot_" + System.currentTimeMillis() + ".eps");
2946 ImageWriterI writer = new ImageWriterI()
2949 public void exportImage(Graphics g) throws Exception
2952 Cache.log.info("Successfully written snapshot to file "
2953 + of.getAbsolutePath());
2956 String title = "View of desktop";
2957 ImageExporter exporter = new ImageExporter(writer, null, TYPE.EPS,
2959 exporter.doExport(of, this, width, height, title);
2963 * Explode the views in the given SplitFrame into separate SplitFrame windows.
2964 * This respects (remembers) any previous 'exploded geometry' i.e. the size and
2965 * location last time the view was expanded (if any). However it does not
2966 * remember the split pane divider location - this is set to match the
2967 * 'exploding' frame.
2971 public void explodeViews(SplitFrame sf)
2973 AlignFrame oldTopFrame = (AlignFrame) sf.getTopFrame();
2974 AlignFrame oldBottomFrame = (AlignFrame) sf.getBottomFrame();
2975 List<? extends AlignmentViewPanel> topPanels = oldTopFrame
2977 List<? extends AlignmentViewPanel> bottomPanels = oldBottomFrame
2979 int viewCount = topPanels.size();
2986 * Processing in reverse order works, forwards order leaves the first panels
2987 * not visible. I don't know why!
2989 for (int i = viewCount - 1; i >= 0; i--)
2992 * Make new top and bottom frames. These take over the respective
2993 * AlignmentPanel objects, including their AlignmentViewports, so the
2994 * cdna/protein relationships between the viewports is carried over to the
2997 * explodedGeometry holds the (x, y) position of the previously exploded
2998 * SplitFrame, and the (width, height) of the AlignFrame component
3000 AlignmentPanel topPanel = (AlignmentPanel) topPanels.get(i);
3001 AlignFrame newTopFrame = new AlignFrame(topPanel);
3002 newTopFrame.setSize(oldTopFrame.getSize());
3003 newTopFrame.setVisible(true);
3004 Rectangle geometry = ((AlignViewport) topPanel.getAlignViewport())
3005 .getExplodedGeometry();
3006 if (geometry != null)
3008 newTopFrame.setSize(geometry.getSize());
3011 AlignmentPanel bottomPanel = (AlignmentPanel) bottomPanels.get(i);
3012 AlignFrame newBottomFrame = new AlignFrame(bottomPanel);
3013 newBottomFrame.setSize(oldBottomFrame.getSize());
3014 newBottomFrame.setVisible(true);
3015 geometry = ((AlignViewport) bottomPanel.getAlignViewport())
3016 .getExplodedGeometry();
3017 if (geometry != null)
3019 newBottomFrame.setSize(geometry.getSize());
3022 topPanel.av.setGatherViewsHere(false);
3023 bottomPanel.av.setGatherViewsHere(false);
3024 JInternalFrame splitFrame = new SplitFrame(newTopFrame,
3026 if (geometry != null)
3028 splitFrame.setLocation(geometry.getLocation());
3030 Desktop.addInternalFrame(splitFrame, sf.getTitle(), -1, -1);
3034 * Clear references to the panels (now relocated in the new SplitFrames)
3035 * before closing the old SplitFrame.
3038 bottomPanels.clear();
3043 * Gather expanded split frames, sharing the same pairs of sequence set ids,
3044 * back into the given SplitFrame as additional views. Note that the gathered
3045 * frames may themselves have multiple views.
3049 public void gatherViews(GSplitFrame source)
3052 * special handling of explodedGeometry for a view within a SplitFrame: - it
3053 * holds the (x, y) position of the enclosing SplitFrame, and the (width,
3054 * height) of the AlignFrame component
3056 AlignFrame myTopFrame = (AlignFrame) source.getTopFrame();
3057 AlignFrame myBottomFrame = (AlignFrame) source.getBottomFrame();
3058 myTopFrame.viewport.setExplodedGeometry(new Rectangle(source.getX(),
3059 source.getY(), myTopFrame.getWidth(), myTopFrame.getHeight()));
3060 myBottomFrame.viewport
3061 .setExplodedGeometry(new Rectangle(source.getX(), source.getY(),
3062 myBottomFrame.getWidth(), myBottomFrame.getHeight()));
3063 myTopFrame.viewport.setGatherViewsHere(true);
3064 myBottomFrame.viewport.setGatherViewsHere(true);
3065 String topViewId = myTopFrame.viewport.getSequenceSetId();
3066 String bottomViewId = myBottomFrame.viewport.getSequenceSetId();
3068 JInternalFrame[] frames = desktop.getAllFrames();
3069 for (JInternalFrame frame : frames)
3071 if (frame instanceof SplitFrame && frame != source)
3073 SplitFrame sf = (SplitFrame) frame;
3074 AlignFrame topFrame = (AlignFrame) sf.getTopFrame();
3075 AlignFrame bottomFrame = (AlignFrame) sf.getBottomFrame();
3076 boolean gatherThis = false;
3077 for (int a = 0; a < topFrame.alignPanels.size(); a++)
3079 AlignmentPanel topPanel = topFrame.alignPanels.get(a);
3080 AlignmentPanel bottomPanel = bottomFrame.alignPanels.get(a);
3081 if (topViewId.equals(topPanel.av.getSequenceSetId())
3082 && bottomViewId.equals(bottomPanel.av.getSequenceSetId()))
3085 topPanel.av.setGatherViewsHere(false);
3086 bottomPanel.av.setGatherViewsHere(false);
3087 topPanel.av.setExplodedGeometry(
3088 new Rectangle(sf.getLocation(), topFrame.getSize()));
3089 bottomPanel.av.setExplodedGeometry(
3090 new Rectangle(sf.getLocation(), bottomFrame.getSize()));
3091 myTopFrame.addAlignmentPanel(topPanel, false);
3092 myBottomFrame.addAlignmentPanel(bottomPanel, false);
3098 topFrame.getAlignPanels().clear();
3099 bottomFrame.getAlignPanels().clear();
3106 * The dust settles...give focus to the tab we did this from.
3108 myTopFrame.setDisplayedView(myTopFrame.alignPanel);
3111 public static groovy.ui.Console getGroovyConsole()
3113 return groovyConsole;
3117 * handles the payload of a drag and drop event.
3119 * TODO refactor to desktop utilities class
3122 * - Data source strings extracted from the drop event
3124 * - protocol for each data source extracted from the drop event
3128 * - the payload from the drop event
3131 public static void transferFromDropTarget(List<Object> files,
3132 List<DataSourceType> protocols, DropTargetDropEvent evt,
3133 Transferable t) throws Exception
3136 // BH 2018 changed List<String> to List<Object> to allow for File from SwingJS
3138 // DataFlavor[] flavors = t.getTransferDataFlavors();
3139 // for (int i = 0; i < flavors.length; i++) {
3140 // if (flavors[i].isFlavorJavaFileListType()) {
3141 // evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
3142 // List<File> list = (List<File>) t.getTransferData(flavors[i]);
3143 // for (int j = 0; j < list.size(); j++) {
3144 // File file = (File) list.get(j);
3145 // byte[] data = getDroppedFileBytes(file);
3146 // fileName.setText(file.getName() + " - " + data.length + " " +
3147 // evt.getLocation());
3148 // JTextArea target = (JTextArea) ((DropTarget) evt.getSource()).getComponent();
3149 // target.setText(new String(data));
3151 // dtde.dropComplete(true);
3156 DataFlavor uriListFlavor = new DataFlavor(
3157 "text/uri-list;class=java.lang.String"), urlFlavour = null;
3160 urlFlavour = new DataFlavor(
3161 "application/x-java-url; class=java.net.URL");
3162 } catch (ClassNotFoundException cfe)
3164 Cache.log.debug("Couldn't instantiate the URL dataflavor.", cfe);
3167 if (urlFlavour != null && t.isDataFlavorSupported(urlFlavour))
3172 java.net.URL url = (URL) t.getTransferData(urlFlavour);
3173 // nb: java 8 osx bug https://bugs.openjdk.java.net/browse/JDK-8156099
3174 // means url may be null.
3177 protocols.add(DataSourceType.URL);
3178 files.add(url.toString());
3179 Cache.log.debug("Drop handled as URL dataflavor "
3180 + files.get(files.size() - 1));
3185 if (Platform.isAMacAndNotJS())
3188 "Please ignore plist error - occurs due to problem with java 8 on OSX");
3191 } catch (Throwable ex)
3193 Cache.log.debug("URL drop handler failed.", ex);
3196 if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
3198 // Works on Windows and MacOSX
3199 Cache.log.debug("Drop handled as javaFileListFlavor");
3200 for (Object file : (List) t
3201 .getTransferData(DataFlavor.javaFileListFlavor))
3204 protocols.add(DataSourceType.FILE);
3209 // Unix like behaviour
3210 boolean added = false;
3212 if (t.isDataFlavorSupported(uriListFlavor))
3214 Cache.log.debug("Drop handled as uriListFlavor");
3215 // This is used by Unix drag system
3216 data = (String) t.getTransferData(uriListFlavor);
3220 // fallback to text: workaround - on OSX where there's a JVM bug
3221 Cache.log.debug("standard URIListFlavor failed. Trying text");
3222 // try text fallback
3223 DataFlavor textDf = new DataFlavor(
3224 "text/plain;class=java.lang.String");
3225 if (t.isDataFlavorSupported(textDf))
3227 data = (String) t.getTransferData(textDf);
3230 Cache.log.debug("Plain text drop content returned "
3231 + (data == null ? "Null - failed" : data));
3236 while (protocols.size() < files.size())
3238 Cache.log.debug("Adding missing FILE protocol for "
3239 + files.get(protocols.size()));
3240 protocols.add(DataSourceType.FILE);
3242 for (java.util.StringTokenizer st = new java.util.StringTokenizer(
3243 data, "\r\n"); st.hasMoreTokens();)
3246 String s = st.nextToken();
3247 if (s.startsWith("#"))
3249 // the line is a comment (as per the RFC 2483)
3252 java.net.URI uri = new java.net.URI(s);
3253 if (uri.getScheme().toLowerCase().startsWith("http"))
3255 protocols.add(DataSourceType.URL);
3256 files.add(uri.toString());
3260 // otherwise preserve old behaviour: catch all for file objects
3261 java.io.File file = new java.io.File(uri);
3262 protocols.add(DataSourceType.FILE);
3263 files.add(file.toString());
3268 if (Cache.log.isDebugEnabled())
3270 if (data == null || !added)
3273 if (t.getTransferDataFlavors() != null
3274 && t.getTransferDataFlavors().length > 0)
3277 "Couldn't resolve drop data. Here are the supported flavors:");
3278 for (DataFlavor fl : t.getTransferDataFlavors())
3281 "Supported transfer dataflavor: " + fl.toString());
3282 Object df = t.getTransferData(fl);
3285 Cache.log.debug("Retrieves: " + df);
3289 Cache.log.debug("Retrieved nothing");
3295 Cache.log.debug("Couldn't resolve dataflavor for drop: "
3301 if (Platform.isWindowsAndNotJS())
3303 Cache.log.debug("Scanning dropped content for Windows Link Files");
3305 // resolve any .lnk files in the file drop
3306 for (int f = 0; f < files.size(); f++)
3308 String source = files.get(f).toString().toLowerCase();
3309 if (protocols.get(f).equals(DataSourceType.FILE)
3310 && (source.endsWith(".lnk") || source.endsWith(".url")
3311 || source.endsWith(".site")))
3315 Object obj = files.get(f);
3316 File lf = (obj instanceof File ? (File) obj
3317 : new File((String) obj));
3318 // process link file to get a URL
3319 Cache.log.debug("Found potential link file: " + lf);
3320 WindowsShortcut wscfile = new WindowsShortcut(lf);
3321 String fullname = wscfile.getRealFilename();
3322 protocols.set(f, FormatAdapter.checkProtocol(fullname));
3323 files.set(f, fullname);
3324 Cache.log.debug("Parsed real filename " + fullname
3325 + " to extract protocol: " + protocols.get(f));
3326 } catch (Exception ex)
3329 "Couldn't parse " + files.get(f) + " as a link file.",
3338 * Sets the Preferences property for experimental features to True or False
3339 * depending on the state of the controlling menu item
3342 protected void showExperimental_actionPerformed(boolean selected)
3344 Cache.setProperty(EXPERIMENTAL_FEATURES, Boolean.toString(selected));
3348 * Answers a (possibly empty) list of any structure viewer frames (currently for
3349 * either Jmol or Chimera) which are currently open. This may optionally be
3350 * restricted to viewers of a specified class, or viewers linked to a specified
3354 * if not null, only return viewers linked to this panel
3355 * @param structureViewerClass
3356 * if not null, only return viewers of this class
3359 public List<StructureViewerBase> getStructureViewers(
3360 AlignmentPanel apanel,
3361 Class<? extends StructureViewerBase> structureViewerClass)
3363 List<StructureViewerBase> result = new ArrayList<>();
3364 JInternalFrame[] frames = Desktop.instance.getAllFrames();
3366 for (JInternalFrame frame : frames)
3368 if (frame instanceof StructureViewerBase)
3370 if (structureViewerClass == null
3371 || structureViewerClass.isInstance(frame))
3374 || ((StructureViewerBase) frame).isLinkedWith(apanel))
3376 result.add((StructureViewerBase) frame);