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<>();
160 private JalviewChangeSupport changeSupport = new JalviewChangeSupport();
163 * news reader - null if it was never started.
165 private BlogReader jvnews = null;
167 private File projectFile;
171 * @see jalview.gui.JalviewChangeSupport#addJalviewPropertyChangeListener(java.beans.PropertyChangeListener)
173 public void addJalviewPropertyChangeListener(
174 PropertyChangeListener listener)
176 changeSupport.addJalviewPropertyChangeListener(listener);
180 * @param propertyName
182 * @see jalview.gui.JalviewChangeSupport#addJalviewPropertyChangeListener(java.lang.String,
183 * java.beans.PropertyChangeListener)
185 public void addJalviewPropertyChangeListener(String propertyName,
186 PropertyChangeListener listener)
188 changeSupport.addJalviewPropertyChangeListener(propertyName, listener);
192 * @param propertyName
194 * @see jalview.gui.JalviewChangeSupport#removeJalviewPropertyChangeListener(java.lang.String,
195 * java.beans.PropertyChangeListener)
197 public void removeJalviewPropertyChangeListener(String propertyName,
198 PropertyChangeListener listener)
200 changeSupport.removeJalviewPropertyChangeListener(propertyName,
204 /** Singleton Desktop instance */
205 public static Desktop instance;
207 public static MyDesktopPane desktop;
209 public static MyDesktopPane getDesktop()
211 // BH 2018 could use currentThread() here as a reference to a
212 // Hashtable<Thread, MyDesktopPane> in JavaScript
216 static int openFrameCount = 0;
218 static final int xOffset = 30;
220 static final int yOffset = 30;
222 public static jalview.ws.jws1.Discoverer discoverer;
224 public static Object[] jalviewClipboard;
226 public static boolean internalCopy = false;
228 static int fileLoadingCount = 0;
230 class MyDesktopManager implements DesktopManager
233 private DesktopManager delegate;
235 public MyDesktopManager(DesktopManager delegate)
237 this.delegate = delegate;
241 public void activateFrame(JInternalFrame f)
245 delegate.activateFrame(f);
246 } catch (NullPointerException npe)
248 Point p = getMousePosition();
249 instance.showPasteMenu(p.x, p.y);
254 public void beginDraggingFrame(JComponent f)
256 delegate.beginDraggingFrame(f);
260 public void beginResizingFrame(JComponent f, int direction)
262 delegate.beginResizingFrame(f, direction);
266 public void closeFrame(JInternalFrame f)
268 delegate.closeFrame(f);
272 public void deactivateFrame(JInternalFrame f)
274 delegate.deactivateFrame(f);
278 public void deiconifyFrame(JInternalFrame f)
280 delegate.deiconifyFrame(f);
284 public void dragFrame(JComponent f, int newX, int newY)
290 delegate.dragFrame(f, newX, newY);
294 public void endDraggingFrame(JComponent f)
296 delegate.endDraggingFrame(f);
301 public void endResizingFrame(JComponent f)
303 delegate.endResizingFrame(f);
308 public void iconifyFrame(JInternalFrame f)
310 delegate.iconifyFrame(f);
314 public void maximizeFrame(JInternalFrame f)
316 delegate.maximizeFrame(f);
320 public void minimizeFrame(JInternalFrame f)
322 delegate.minimizeFrame(f);
326 public void openFrame(JInternalFrame f)
328 delegate.openFrame(f);
332 public void resizeFrame(JComponent f, int newX, int newY, int newWidth,
339 delegate.resizeFrame(f, newX, newY, newWidth, newHeight);
343 public void setBoundsForFrame(JComponent f, int newX, int newY,
344 int newWidth, int newHeight)
346 delegate.setBoundsForFrame(f, newX, newY, newWidth, newHeight);
349 // All other methods, simply delegate
354 * Creates a new Desktop object.
360 * A note to implementors. It is ESSENTIAL that any activities that might
361 * block are spawned off as threads rather than waited for during this
366 doConfigureStructurePrefs();
367 setTitle("Jalview " + Cache.getProperty("VERSION"));
369 if (!Platform.isAMac())
371 // this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
375 this.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
381 APQHandlers.setAPQHandlers(this);
382 } catch (Throwable t)
384 System.out.println("Error setting APQHandlers: " + t.toString());
385 // t.printStackTrace();
388 addWindowListener(new WindowAdapter()
392 public void windowClosing(WindowEvent ev)
398 boolean selmemusage = Cache.getDefault("SHOW_MEMUSAGE",
401 boolean showjconsole = Cache.getDefault("SHOW_JAVA_CONSOLE",
403 desktop = new MyDesktopPane(selmemusage);
405 showMemusage.setSelected(selmemusage);
406 desktop.setBackground(Color.white);
408 getContentPane().setLayout(new BorderLayout());
409 // alternate config - have scrollbars - see notes in JAL-153
410 // JScrollPane sp = new JScrollPane();
411 // sp.getViewport().setView(desktop);
412 // getContentPane().add(sp, BorderLayout.CENTER);
414 // BH 2018 - just an experiment to try unclipped JInternalFrames.
417 getRootPane().putClientProperty("swingjs.overflow.hidden", "false");
420 getContentPane().add(desktop, BorderLayout.CENTER);
421 desktop.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
423 // This line prevents Windows Look&Feel resizing all new windows to maximum
424 // if previous window was maximised
425 desktop.setDesktopManager(new MyDesktopManager(
426 (Platform.isWindowsAndNotJS() ? new DefaultDesktopManager()
427 : Platform.isAMacAndNotJS()
428 ? new AquaInternalFrameManager(
429 desktop.getDesktopManager())
430 : desktop.getDesktopManager())));
432 Rectangle dims = getLastKnownDimensions("");
439 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
440 int xPos = Math.max(5, (screenSize.width - 900) / 2);
441 int yPos = Math.max(5, (screenSize.height - 650) / 2);
442 setBounds(xPos, yPos, 900, 650);
445 if (!Platform.isJS())
452 jconsole = new Console(this, showjconsole);
453 jconsole.setHeader(Cache.getVersionDetailsForConsole());
454 showConsole(showjconsole);
456 showNews.setVisible(false);
458 experimentalFeatures.setSelected(showExperimental());
460 getIdentifiersOrgData();
464 // Spawn a thread that shows the splashscreen
466 SwingUtilities.invokeLater(new Runnable()
471 new SplashScreen(true);
475 // Thread off a new instance of the file chooser - this reduces the time
477 // takes to open it later on.
478 new Thread(new Runnable()
483 Cache.log.debug("Filechooser init thread started.");
484 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
485 JalviewFileChooser.forRead(Cache.getProperty("LAST_DIRECTORY"),
487 Cache.log.debug("Filechooser init thread finished.");
490 // Add the service change listener
491 changeSupport.addJalviewPropertyChangeListener("services",
492 new PropertyChangeListener()
496 public void propertyChange(PropertyChangeEvent evt)
498 Cache.log.debug("Firing service changed event for "
499 + evt.getNewValue());
500 JalviewServicesChanged(evt);
505 this.setDropTarget(new java.awt.dnd.DropTarget(desktop, this));
507 this.addWindowListener(new WindowAdapter()
510 public void windowClosing(WindowEvent evt)
517 this.addMouseListener(ma = new MouseAdapter()
520 public void mousePressed(MouseEvent evt)
522 if (evt.isPopupTrigger()) // Mac
524 showPasteMenu(evt.getX(), evt.getY());
529 public void mouseReleased(MouseEvent evt)
531 if (evt.isPopupTrigger()) // Windows
533 showPasteMenu(evt.getX(), evt.getY());
537 desktop.addMouseListener(ma);
542 * Answers true if user preferences to enable experimental features is True
547 public boolean showExperimental()
549 String experimental = Cache.getDefault(EXPERIMENTAL_FEATURES,
550 Boolean.FALSE.toString());
551 return Boolean.valueOf(experimental).booleanValue();
554 public void doConfigureStructurePrefs()
556 // configure services
557 StructureSelectionManager ssm = StructureSelectionManager
558 .getStructureSelectionManager(this);
559 if (Cache.getDefault(Preferences.ADD_SS_ANN, true))
561 ssm.setAddTempFacAnnot(Cache
562 .getDefault(Preferences.ADD_TEMPFACT_ANN, true));
563 ssm.setProcessSecondaryStructure(Cache
564 .getDefault(Preferences.STRUCT_FROM_PDB, true));
565 ssm.setSecStructServices(
566 Cache.getDefault(Preferences.USE_RNAVIEW, true));
570 ssm.setAddTempFacAnnot(false);
571 ssm.setProcessSecondaryStructure(false);
572 ssm.setSecStructServices(false);
576 public void checkForNews()
578 final Desktop me = this;
579 // Thread off the news reader, in case there are connection problems.
580 new Thread(new Runnable()
585 Cache.log.debug("Starting news thread.");
586 jvnews = new BlogReader(me);
587 showNews.setVisible(true);
588 Cache.log.debug("Completed news thread.");
593 public void getIdentifiersOrgData()
595 // Thread off the identifiers fetcher
596 new Thread(new Runnable()
601 Cache.log.debug("Downloading data from identifiers.org");
604 UrlDownloadClient.download(IdOrgSettings.getUrl(),
605 IdOrgSettings.getDownloadLocation());
606 } catch (IOException e)
608 Cache.log.debug("Exception downloading identifiers.org data"
617 protected void showNews_actionPerformed(ActionEvent e)
619 showNews(showNews.isSelected());
622 void showNews(boolean visible)
624 Cache.log.debug((visible ? "Showing" : "Hiding") + " news.");
625 showNews.setSelected(visible);
626 if (visible && !jvnews.isVisible())
628 new Thread(new Runnable()
633 long now = System.currentTimeMillis();
634 Desktop.instance.setProgressBar(
635 MessageManager.getString("status.refreshing_news"), now);
636 jvnews.refreshNews();
637 Desktop.instance.setProgressBar(null, now);
645 * recover the last known dimensions for a jalview window
648 * - empty string is desktop, all other windows have unique prefix
649 * @return null or last known dimensions scaled to current geometry (if last
650 * window geom was known)
652 Rectangle getLastKnownDimensions(String windowName)
654 // TODO: lock aspect ratio for scaling desktop Bug #0058199
655 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
656 String x = Cache.getProperty(windowName + "SCREEN_X");
657 String y = Cache.getProperty(windowName + "SCREEN_Y");
659 .getProperty(windowName + "SCREEN_WIDTH");
660 String height = Cache
661 .getProperty(windowName + "SCREEN_HEIGHT");
662 if ((x != null) && (y != null) && (width != null) && (height != null))
664 int ix = Integer.parseInt(x), iy = Integer.parseInt(y),
665 iw = Integer.parseInt(width), ih = Integer.parseInt(height);
666 if (Cache.getProperty("SCREENGEOMETRY_WIDTH") != null)
668 // attempt #1 - try to cope with change in screen geometry - this
669 // version doesn't preserve original jv aspect ratio.
670 // take ratio of current screen size vs original screen size.
671 double sw = ((1f * screenSize.width) / (1f * Integer.parseInt(
672 Cache.getProperty("SCREENGEOMETRY_WIDTH"))));
673 double sh = ((1f * screenSize.height) / (1f * Integer.parseInt(
674 Cache.getProperty("SCREENGEOMETRY_HEIGHT"))));
675 // rescale the bounds depending upon the current screen geometry.
676 ix = (int) (ix * sw);
677 iw = (int) (iw * sw);
678 iy = (int) (iy * sh);
679 ih = (int) (ih * sh);
680 while (ix >= screenSize.width)
683 "Window geometry location recall error: shifting horizontal to within screenbounds.");
684 ix -= screenSize.width;
686 while (iy >= screenSize.height)
689 "Window geometry location recall error: shifting vertical to within screenbounds.");
690 iy -= screenSize.height;
693 "Got last known dimensions for " + windowName + ": x:" + ix
694 + " y:" + iy + " width:" + iw + " height:" + ih);
696 // return dimensions for new instance
697 return new Rectangle(ix, iy, iw, ih);
702 void showPasteMenu(int x, int y)
704 JPopupMenu popup = new JPopupMenu();
705 JMenuItem item = new JMenuItem(
706 MessageManager.getString("label.paste_new_window"));
707 item.addActionListener(new ActionListener()
710 public void actionPerformed(ActionEvent evt)
717 popup.show(this, x, y);
724 Clipboard c = Toolkit.getDefaultToolkit().getSystemClipboard();
725 Transferable contents = c.getContents(this);
727 if (contents != null)
729 String file = (String) contents
730 .getTransferData(DataFlavor.stringFlavor);
732 FileFormatI format = new IdentifyFile().identify(file,
733 DataSourceType.PASTE);
735 new FileLoader().LoadFile(file, DataSourceType.PASTE, format);
738 } catch (Exception ex)
741 "Unable to paste alignment from system clipboard:\n" + ex);
746 * Adds and opens the given frame to the desktop
757 public static synchronized void addInternalFrame(
758 final JInternalFrame frame, String title, int w, int h)
760 addInternalFrame(frame, title, true, w, h, true, false);
764 * Add an internal frame to the Jalview desktop
771 * When true, display frame immediately, otherwise, caller must call
772 * setVisible themselves.
778 public static synchronized void addInternalFrame(
779 final JInternalFrame frame, String title, boolean makeVisible,
782 addInternalFrame(frame, title, makeVisible, w, h, true, false);
786 * Add an internal frame to the Jalview desktop and make it visible
799 public static synchronized void addInternalFrame(
800 final JInternalFrame frame, String title, int w, int h,
803 addInternalFrame(frame, title, true, w, h, resizable, false);
807 * Add an internal frame to the Jalview desktop
814 * When true, display frame immediately, otherwise, caller must call
815 * setVisible themselves.
822 * @param ignoreMinSize
823 * Do not set the default minimum size for frame
825 public static synchronized void addInternalFrame(
826 final JInternalFrame frame, String title, boolean makeVisible,
827 int w, int h, boolean resizable, boolean ignoreMinSize)
830 // TODO: allow callers to determine X and Y position of frame (eg. via
832 // TODO: consider fixing method to update entries in the window submenu with
833 // the current window title
835 frame.setTitle(title);
836 if (frame.getWidth() < 1 || frame.getHeight() < 1)
840 // THIS IS A PUBLIC STATIC METHOD, SO IT MAY BE CALLED EVEN IN
841 // A HEADLESS STATE WHEN NO DESKTOP EXISTS. MUST RETURN
842 // IF JALVIEW IS RUNNING HEADLESS
843 // ///////////////////////////////////////////////
844 if (instance == null || (System.getProperty("java.awt.headless") != null
845 && System.getProperty("java.awt.headless").equals("true")))
854 frame.setMinimumSize(
855 new Dimension(DEFAULT_MIN_WIDTH, DEFAULT_MIN_HEIGHT));
857 // Set default dimension for Alignment Frame window.
858 // The Alignment Frame window could be added from a number of places,
860 // I did this here in order not to miss out on any Alignment frame.
861 if (frame instanceof AlignFrame)
863 frame.setMinimumSize(new Dimension(ALIGN_FRAME_DEFAULT_MIN_WIDTH,
864 ALIGN_FRAME_DEFAULT_MIN_HEIGHT));
868 frame.setVisible(makeVisible);
869 frame.setClosable(true);
870 frame.setResizable(resizable);
871 frame.setMaximizable(resizable);
872 frame.setIconifiable(resizable);
873 frame.setOpaque(Platform.isJS());
875 if (frame.getX() < 1 && frame.getY() < 1)
877 frame.setLocation(xOffset * openFrameCount,
878 yOffset * ((openFrameCount - 1) % 10) + yOffset);
882 * add an entry for the new frame in the Window menu
883 * (and remove it when the frame is closed)
885 final JMenuItem menuItem = new JMenuItem(title);
886 frame.addInternalFrameListener(new InternalFrameAdapter()
889 public void internalFrameActivated(InternalFrameEvent evt)
891 JInternalFrame itf = desktop.getSelectedFrame();
894 if (itf instanceof AlignFrame)
896 Jalview.setCurrentAlignFrame((AlignFrame) itf);
903 public void internalFrameClosed(InternalFrameEvent evt)
905 PaintRefresher.RemoveComponent(frame);
908 * defensive check to prevent frames being
909 * added half off the window
911 if (openFrameCount > 0)
917 * ensure no reference to alignFrame retained by menu item listener
919 if (menuItem.getActionListeners().length > 0)
921 menuItem.removeActionListener(menuItem.getActionListeners()[0]);
923 windowMenu.remove(menuItem);
927 menuItem.addActionListener(new ActionListener()
930 public void actionPerformed(ActionEvent e)
934 frame.setSelected(true);
935 frame.setIcon(false);
936 } catch (java.beans.PropertyVetoException ex)
938 // System.err.println(ex.toString());
943 setKeyBindings(frame);
947 windowMenu.add(menuItem);
952 frame.setSelected(true);
953 frame.requestFocus();
954 } catch (java.beans.PropertyVetoException ve)
956 } catch (java.lang.ClassCastException cex)
959 "Squashed a possible GUI implementation error. If you can recreate this, please look at http://issues.jalview.org/browse/JAL-869",
965 * Add key bindings to a JInternalFrame so that Ctrl-W and Cmd-W will close the
970 private static void setKeyBindings(JInternalFrame frame)
972 @SuppressWarnings("serial")
973 final Action closeAction = new AbstractAction()
976 public void actionPerformed(ActionEvent e)
983 * set up key bindings for Ctrl-W and Cmd-W, with the same (Close) action
985 KeyStroke ctrlWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
986 InputEvent.CTRL_DOWN_MASK);
987 KeyStroke cmdWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
988 ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx());
990 InputMap inputMap = frame
991 .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
992 String ctrlW = ctrlWKey.toString();
993 inputMap.put(ctrlWKey, ctrlW);
994 inputMap.put(cmdWKey, ctrlW);
996 ActionMap actionMap = frame.getActionMap();
997 actionMap.put(ctrlW, closeAction);
1001 public void lostOwnership(Clipboard clipboard, Transferable contents)
1005 Desktop.jalviewClipboard = null;
1008 internalCopy = false;
1012 public void dragEnter(DropTargetDragEvent evt)
1017 public void dragExit(DropTargetEvent evt)
1022 public void dragOver(DropTargetDragEvent evt)
1027 public void dropActionChanged(DropTargetDragEvent evt)
1038 public void drop(DropTargetDropEvent evt)
1040 boolean success = true;
1041 // JAL-1552 - acceptDrop required before getTransferable call for
1042 // Java's Transferable for native dnd
1043 evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
1044 Transferable t = evt.getTransferable();
1045 List<Object> files = new ArrayList<>();
1046 List<DataSourceType> protocols = new ArrayList<>();
1050 Desktop.transferFromDropTarget(files, protocols, evt, t);
1051 } catch (Exception e)
1053 e.printStackTrace();
1061 for (int i = 0; i < files.size(); i++)
1063 // BH 2018 File or String
1064 Object file = files.get(i);
1065 String fileName = file.toString();
1066 DataSourceType protocol = (protocols == null)
1067 ? DataSourceType.FILE
1069 FileFormatI format = null;
1071 if (fileName.endsWith(".jar"))
1073 format = FileFormat.Jalview;
1078 format = new IdentifyFile().identify(file, protocol);
1080 if (file instanceof File)
1082 Platform.cacheFileData((File) file);
1084 new FileLoader().LoadFile(null, file, protocol, format);
1087 } catch (Exception ex)
1092 evt.dropComplete(success); // need this to ensure input focus is properly
1093 // transfered to any new windows created
1103 public void inputLocalFileMenuItem_actionPerformed(AlignViewport viewport)
1105 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
1106 JalviewFileChooser chooser = JalviewFileChooser
1107 .forRead(Cache.getProperty("LAST_DIRECTORY"), fileFormat, BackupFiles.getEnabled());
1109 chooser.setFileView(new JalviewFileView());
1110 chooser.setDialogTitle(
1111 MessageManager.getString("label.open_local_file"));
1112 chooser.setToolTipText(MessageManager.getString("action.open"));
1114 chooser.setResponseHandler(0, new Runnable()
1119 File selectedFile = chooser.getSelectedFile();
1120 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1122 FileFormatI format = chooser.getSelectedFormat();
1125 * Call IdentifyFile to verify the file contains what its extension implies.
1126 * Skip this step for dynamically added file formats, because
1127 * IdentifyFile does not know how to recognise them.
1129 if (FileFormats.getInstance().isIdentifiable(format))
1133 format = new IdentifyFile().identify(selectedFile,
1134 DataSourceType.FILE);
1135 } catch (FileFormatException e)
1137 // format = null; //??
1141 new FileLoader().LoadFile(viewport, selectedFile,
1142 DataSourceType.FILE, format);
1145 chooser.showOpenDialog(this);
1149 * Shows a dialog for input of a URL at which to retrieve alignment data
1154 public void inputURLMenuItem_actionPerformed(AlignViewport viewport)
1156 // This construct allows us to have a wider textfield
1158 JLabel label = new JLabel(
1159 MessageManager.getString("label.input_file_url"));
1161 JPanel panel = new JPanel(new GridLayout(2, 1));
1165 * the URL to fetch is
1166 * Java: an editable combobox with history
1167 * JS: (pending JAL-3038) a plain text field
1170 String urlBase = "http://www.";
1171 if (Platform.isJS())
1173 history = new JTextField(urlBase, 35);
1182 JComboBox<String> asCombo = new JComboBox<>();
1183 asCombo.setPreferredSize(new Dimension(400, 20));
1184 asCombo.setEditable(true);
1185 asCombo.addItem(urlBase);
1186 String historyItems = Cache.getProperty("RECENT_URL");
1187 if (historyItems != null)
1189 for (String token : historyItems.split("\\t"))
1191 asCombo.addItem(token);
1198 Object[] options = new Object[] { MessageManager.getString("action.ok"),
1199 MessageManager.getString("action.cancel") };
1200 Runnable action = new Runnable()
1205 @SuppressWarnings("unchecked")
1206 String url = (history instanceof JTextField
1207 ? ((JTextField) history).getText()
1208 : ((JComboBox<String>) history).getSelectedItem()
1211 if (url.toLowerCase().endsWith(".jar"))
1213 if (viewport != null)
1215 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1216 FileFormat.Jalview);
1220 new FileLoader().LoadFile(url, DataSourceType.URL,
1221 FileFormat.Jalview);
1226 FileFormatI format = null;
1229 format = new IdentifyFile().identify(url, DataSourceType.URL);
1230 } catch (FileFormatException e)
1232 // TODO revise error handling, distinguish between
1233 // URL not found and response not valid
1238 String msg = MessageManager
1239 .formatMessage("label.couldnt_locate", url);
1240 JvOptionPane.showInternalMessageDialog(Desktop.desktop, msg,
1241 MessageManager.getString("label.url_not_found"),
1242 JvOptionPane.WARNING_MESSAGE);
1247 if (viewport != null)
1249 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1254 new FileLoader().LoadFile(url, DataSourceType.URL, format);
1259 String dialogOption = MessageManager
1260 .getString("label.input_alignment_from_url");
1261 JvOptionPane.newOptionDialog(desktop).setResponseHandler(0, action)
1262 .showInternalDialog(panel, dialogOption,
1263 JvOptionPane.YES_NO_CANCEL_OPTION,
1264 JvOptionPane.PLAIN_MESSAGE, null, options,
1265 MessageManager.getString("action.ok"));
1269 * Opens the CutAndPaste window for the user to paste an alignment in to
1272 * - if not null, the pasted alignment is added to the current
1273 * alignment; if null, to a new alignment window
1276 public void inputTextboxMenuItem_actionPerformed(
1277 AlignmentViewPanel viewPanel)
1279 CutAndPasteTransfer cap = new CutAndPasteTransfer();
1280 cap.setForInput(viewPanel);
1281 Desktop.addInternalFrame(cap,
1282 MessageManager.getString("label.cut_paste_alignmen_file"), true,
1292 Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
1293 Cache.setProperty("SCREENGEOMETRY_WIDTH",
1295 Cache.setProperty("SCREENGEOMETRY_HEIGHT",
1296 screen.height + "");
1297 storeLastKnownDimensions("", new Rectangle(getBounds().x, getBounds().y,
1298 getWidth(), getHeight()));
1300 if (jconsole != null)
1302 storeLastKnownDimensions("JAVA_CONSOLE_", jconsole.getBounds());
1303 jconsole.stopConsole();
1307 storeLastKnownDimensions("JALVIEW_RSS_WINDOW_", jvnews.getBounds());
1310 if (dialogExecutor != null)
1312 dialogExecutor.shutdownNow();
1314 closeAll_actionPerformed(null);
1316 if (groovyConsole != null)
1318 // suppress a possible repeat prompt to save script
1319 groovyConsole.setDirty(false);
1320 groovyConsole.exit();
1325 private void storeLastKnownDimensions(String string, Rectangle jc)
1327 Cache.log.debug("Storing last known dimensions for "
1328 + string + ": x:" + jc.x + " y:" + jc.y + " width:" + jc.width
1329 + " height:" + jc.height);
1331 Cache.setProperty(string + "SCREEN_X", jc.x + "");
1332 Cache.setProperty(string + "SCREEN_Y", jc.y + "");
1333 Cache.setProperty(string + "SCREEN_WIDTH", jc.width + "");
1334 Cache.setProperty(string + "SCREEN_HEIGHT", jc.height + "");
1344 public void aboutMenuItem_actionPerformed(ActionEvent e)
1346 new Thread(new Runnable()
1351 new SplashScreen(false);
1357 * Returns the html text for the About screen, including any available version
1358 * number, build details, author details and citation reference, but without
1359 * the enclosing {@code html} tags
1363 public String getAboutMessage()
1365 StringBuilder message = new StringBuilder(1024);
1366 message.append("<h1><strong>Version: ")
1367 .append(Cache.getProperty("VERSION")).append("</strong></h1>")
1368 .append("<strong>Built: <em>")
1369 .append(Cache.getDefault("BUILD_DATE", "unknown"))
1370 .append("</em> from ").append(Cache.getBuildDetailsForSplash())
1371 .append("</strong>");
1373 String latestVersion = Cache.getDefault("LATEST_VERSION", "Checking");
1374 if (latestVersion.equals("Checking"))
1376 // JBP removed this message for 2.11: May be reinstated in future version
1377 // message.append("<br>...Checking latest version...</br>");
1379 else if (!latestVersion.equals(Cache.getProperty("VERSION")))
1381 boolean red = false;
1382 if (Cache.getProperty("VERSION").toLowerCase()
1383 .indexOf("automated build") == -1)
1386 // Displayed when code version and jnlp version do not match and code
1387 // version is not a development build
1388 message.append("<div style=\"color: #FF0000;font-style: bold;\">");
1391 message.append("<br>!! Version ")
1392 .append(Cache.getDefault("LATEST_VERSION", "..Checking.."))
1393 .append(" is available for download from ")
1394 .append(Cache.getDefault("www.jalview.org",
1395 "http://www.jalview.org"))
1399 message.append("</div>");
1402 message.append("<br>Authors: ");
1403 message.append(Cache.getDefault("AUTHORFNAMES", DEFAULT_AUTHORS));
1404 message.append(CITATION);
1406 return message.toString();
1410 * Action on requesting Help documentation
1413 public void documentationMenuItem_actionPerformed()
1417 if (Platform.isJS())
1419 BrowserLauncher.openURL("http://www.jalview.org/help.html");
1428 Help.showHelpWindow();
1430 } catch (Exception ex)
1432 System.err.println("Error opening help: " + ex.getMessage());
1437 public void closeAll_actionPerformed(ActionEvent e)
1439 // TODO show a progress bar while closing?
1440 JInternalFrame[] frames = desktop.getAllFrames();
1441 for (int i = 0; i < frames.length; i++)
1445 frames[i].setClosed(true);
1446 } catch (java.beans.PropertyVetoException ex)
1450 Jalview.setCurrentAlignFrame(null);
1451 System.out.println("ALL CLOSED");
1454 * reset state of singleton objects as appropriate (clear down session state
1455 * when all windows are closed)
1457 StructureSelectionManager ssm = StructureSelectionManager
1458 .getStructureSelectionManager(this);
1466 public void raiseRelated_actionPerformed(ActionEvent e)
1468 reorderAssociatedWindows(false, false);
1472 public void minimizeAssociated_actionPerformed(ActionEvent e)
1474 reorderAssociatedWindows(true, false);
1477 void closeAssociatedWindows()
1479 reorderAssociatedWindows(false, true);
1485 * @seejalview.jbgui.GDesktop#garbageCollect_actionPerformed(java.awt.event.
1489 protected void garbageCollect_actionPerformed(ActionEvent e)
1491 // We simply collect the garbage
1492 Cache.log.debug("Collecting garbage...");
1494 Cache.log.debug("Finished garbage collection.");
1501 * jalview.jbgui.GDesktop#showMemusage_actionPerformed(java.awt.event.ActionEvent
1505 protected void showMemusage_actionPerformed(ActionEvent e)
1507 desktop.showMemoryUsage(showMemusage.isSelected());
1514 * jalview.jbgui.GDesktop#showConsole_actionPerformed(java.awt.event.ActionEvent
1518 protected void showConsole_actionPerformed(ActionEvent e)
1520 showConsole(showConsole.isSelected());
1523 Console jconsole = null;
1526 * control whether the java console is visible or not
1530 void showConsole(boolean selected)
1532 // TODO: decide if we should update properties file
1533 if (jconsole != null) // BH 2018
1535 showConsole.setSelected(selected);
1536 Cache.setProperty("SHOW_JAVA_CONSOLE",
1537 Boolean.valueOf(selected).toString());
1538 jconsole.setVisible(selected);
1542 void reorderAssociatedWindows(boolean minimize, boolean close)
1544 JInternalFrame[] frames = desktop.getAllFrames();
1545 if (frames == null || frames.length < 1)
1550 AlignmentViewport source = null, target = null;
1551 if (frames[0] instanceof AlignFrame)
1553 source = ((AlignFrame) frames[0]).getCurrentView();
1555 else if (frames[0] instanceof TreePanel)
1557 source = ((TreePanel) frames[0]).getViewPort();
1559 else if (frames[0] instanceof PCAPanel)
1561 source = ((PCAPanel) frames[0]).av;
1563 else if (frames[0].getContentPane() instanceof PairwiseAlignPanel)
1565 source = ((PairwiseAlignPanel) frames[0].getContentPane()).av;
1570 for (int i = 0; i < frames.length; i++)
1573 if (frames[i] == null)
1577 if (frames[i] instanceof AlignFrame)
1579 target = ((AlignFrame) frames[i]).getCurrentView();
1581 else if (frames[i] instanceof TreePanel)
1583 target = ((TreePanel) frames[i]).getViewPort();
1585 else if (frames[i] instanceof PCAPanel)
1587 target = ((PCAPanel) frames[i]).av;
1589 else if (frames[i].getContentPane() instanceof PairwiseAlignPanel)
1591 target = ((PairwiseAlignPanel) frames[i].getContentPane()).av;
1594 if (source == target)
1600 frames[i].setClosed(true);
1604 frames[i].setIcon(minimize);
1607 frames[i].toFront();
1611 } catch (java.beans.PropertyVetoException ex)
1626 protected void preferences_actionPerformed(ActionEvent e)
1632 * Prompts the user to choose a file and then saves the Jalview state as a
1633 * Jalview project file
1636 public void saveState_actionPerformed()
1638 saveState_actionPerformed(false);
1641 public void saveState_actionPerformed(boolean saveAs)
1643 java.io.File projectFile = getProjectFile();
1644 // autoSave indicates we already have a file and don't need to ask
1645 boolean autoSave = projectFile != null && !saveAs
1646 && BackupFiles.getEnabled();
1648 // System.out.println("autoSave="+autoSave+", projectFile='"+projectFile+"',
1649 // saveAs="+saveAs+", Backups
1650 // "+(BackupFiles.getEnabled()?"enabled":"disabled"));
1652 boolean approveSave = false;
1655 JalviewFileChooser chooser = new JalviewFileChooser("jvp",
1658 chooser.setFileView(new JalviewFileView());
1659 chooser.setDialogTitle(MessageManager.getString("label.save_state"));
1661 int value = chooser.showSaveDialog(this);
1663 if (value == JalviewFileChooser.APPROVE_OPTION)
1665 projectFile = chooser.getSelectedFile();
1666 setProjectFile(projectFile);
1671 if (approveSave || autoSave)
1673 final Desktop me = this;
1674 final java.io.File chosenFile = projectFile;
1675 new Thread(new Runnable()
1680 // TODO: refactor to Jalview desktop session controller action.
1681 setProgressBar(MessageManager.formatMessage(
1682 "label.saving_jalview_project", new Object[]
1683 { chosenFile.getName() }), chosenFile.hashCode());
1684 Cache.setProperty("LAST_DIRECTORY",
1685 chosenFile.getParent());
1686 // TODO catch and handle errors for savestate
1687 // TODO prevent user from messing with the Desktop whilst we're saving
1690 boolean doBackup = BackupFiles.getEnabled();
1691 BackupFiles backupfiles = doBackup ? new BackupFiles(chosenFile) : null;
1693 new Jalview2XML().saveState(doBackup ? backupfiles.getTempFile() : chosenFile);
1697 backupfiles.setWriteSuccess(true);
1698 backupfiles.rollBackupsAndRenameTempFile();
1700 } catch (OutOfMemoryError oom)
1702 new OOMWarning("Whilst saving current state to "
1703 + chosenFile.getName(), oom);
1704 } catch (Exception ex)
1706 Cache.log.error("Problems whilst trying to save to "
1707 + chosenFile.getName(), ex);
1708 JvOptionPane.showMessageDialog(me,
1709 MessageManager.formatMessage(
1710 "label.error_whilst_saving_current_state_to",
1712 { chosenFile.getName() }),
1713 MessageManager.getString("label.couldnt_save_project"),
1714 JvOptionPane.WARNING_MESSAGE);
1716 setProgressBar(null, chosenFile.hashCode());
1723 public void saveAsState_actionPerformed(ActionEvent e)
1725 saveState_actionPerformed(true);
1728 private void setProjectFile(File choice)
1730 this.projectFile = choice;
1733 public File getProjectFile()
1735 return this.projectFile;
1739 * Shows a file chooser dialog and tries to read in the selected file as a
1743 public void loadState_actionPerformed()
1745 final String[] suffix = new String[] { "jvp", "jar" };
1746 final String[] desc = new String[] { "Jalview Project",
1747 "Jalview Project (old)" };
1748 JalviewFileChooser chooser = new JalviewFileChooser(
1749 Cache.getProperty("LAST_DIRECTORY"), suffix, desc,
1750 "Jalview Project", true, BackupFiles.getEnabled()); // last two booleans: allFiles,
1752 chooser.setFileView(new JalviewFileView());
1753 chooser.setDialogTitle(MessageManager.getString("label.restore_state"));
1754 chooser.setResponseHandler(0, new Runnable()
1759 File selectedFile = chooser.getSelectedFile();
1760 setProjectFile(selectedFile);
1761 String choice = selectedFile.getAbsolutePath();
1762 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1763 new Thread(new Runnable()
1770 new Jalview2XML().loadJalviewAlign(choice);
1771 } catch (OutOfMemoryError oom)
1773 new OOMWarning("Whilst loading project from " + choice, oom);
1774 } catch (Exception ex)
1777 "Problems whilst loading project from " + choice, ex);
1778 JvOptionPane.showMessageDialog(Desktop.desktop,
1779 MessageManager.formatMessage(
1780 "label.error_whilst_loading_project_from",
1783 MessageManager.getString("label.couldnt_load_project"),
1784 JvOptionPane.WARNING_MESSAGE);
1791 chooser.showOpenDialog(this);
1795 public void inputSequence_actionPerformed(ActionEvent e)
1797 new SequenceFetcher(this);
1800 JPanel progressPanel;
1802 ArrayList<JPanel> fileLoadingPanels = new ArrayList<>();
1804 public void startLoading(final Object fileName)
1806 if (fileLoadingCount == 0)
1808 fileLoadingPanels.add(addProgressPanel(MessageManager
1809 .formatMessage("label.loading_file", new Object[]
1815 private JPanel addProgressPanel(String string)
1817 if (progressPanel == null)
1819 progressPanel = new JPanel(new GridLayout(1, 1));
1820 totalProgressCount = 0;
1821 instance.getContentPane().add(progressPanel, BorderLayout.SOUTH);
1823 JPanel thisprogress = new JPanel(new BorderLayout(10, 5));
1824 JProgressBar progressBar = new JProgressBar();
1825 progressBar.setIndeterminate(true);
1827 thisprogress.add(new JLabel(string), BorderLayout.WEST);
1829 thisprogress.add(progressBar, BorderLayout.CENTER);
1830 progressPanel.add(thisprogress);
1831 ((GridLayout) progressPanel.getLayout()).setRows(
1832 ((GridLayout) progressPanel.getLayout()).getRows() + 1);
1833 ++totalProgressCount;
1834 instance.validate();
1835 return thisprogress;
1838 int totalProgressCount = 0;
1840 private void removeProgressPanel(JPanel progbar)
1842 if (progressPanel != null)
1844 synchronized (progressPanel)
1846 progressPanel.remove(progbar);
1847 GridLayout gl = (GridLayout) progressPanel.getLayout();
1848 gl.setRows(gl.getRows() - 1);
1849 if (--totalProgressCount < 1)
1851 this.getContentPane().remove(progressPanel);
1852 progressPanel = null;
1859 public void stopLoading()
1862 if (fileLoadingCount < 1)
1864 while (fileLoadingPanels.size() > 0)
1866 removeProgressPanel(fileLoadingPanels.remove(0));
1868 fileLoadingPanels.clear();
1869 fileLoadingCount = 0;
1874 public static int getViewCount(String alignmentId)
1876 AlignmentViewport[] aps = getViewports(alignmentId);
1877 return (aps == null) ? 0 : aps.length;
1882 * @param alignmentId
1883 * - if null, all sets are returned
1884 * @return all AlignmentPanels concerning the alignmentId sequence set
1886 public static AlignmentPanel[] getAlignmentPanels(String alignmentId)
1888 if (Desktop.desktop == null)
1890 // no frames created and in headless mode
1891 // TODO: verify that frames are recoverable when in headless mode
1894 List<AlignmentPanel> aps = new ArrayList<>();
1895 AlignFrame[] frames = getAlignFrames();
1900 for (AlignFrame af : frames)
1902 for (AlignmentPanel ap : af.alignPanels)
1904 if (alignmentId == null
1905 || alignmentId.equals(ap.av.getSequenceSetId()))
1911 if (aps.size() == 0)
1915 AlignmentPanel[] vap = aps.toArray(new AlignmentPanel[aps.size()]);
1920 * get all the viewports on an alignment.
1922 * @param sequenceSetId
1923 * unique alignment id (may be null - all viewports returned in that
1925 * @return all viewports on the alignment bound to sequenceSetId
1927 public static AlignmentViewport[] getViewports(String sequenceSetId)
1929 List<AlignmentViewport> viewp = new ArrayList<>();
1930 if (desktop != null)
1932 AlignFrame[] frames = Desktop.getAlignFrames();
1934 for (AlignFrame afr : frames)
1936 if (sequenceSetId == null || afr.getViewport().getSequenceSetId()
1937 .equals(sequenceSetId))
1939 if (afr.alignPanels != null)
1941 for (AlignmentPanel ap : afr.alignPanels)
1943 if (sequenceSetId == null
1944 || sequenceSetId.equals(ap.av.getSequenceSetId()))
1952 viewp.add(afr.getViewport());
1956 if (viewp.size() > 0)
1958 return viewp.toArray(new AlignmentViewport[viewp.size()]);
1965 * Explode the views in the given frame into separate AlignFrame
1969 public static void explodeViews(AlignFrame af)
1971 int size = af.alignPanels.size();
1977 // FIXME: ideally should use UI interface API
1978 FeatureSettings viewFeatureSettings = (af.featureSettings != null
1979 && af.featureSettings.isOpen())
1980 ? af.featureSettings
1982 Rectangle fsBounds = af.getFeatureSettingsGeometry();
1983 for (int i = 0; i < size; i++)
1985 AlignmentPanel ap = af.alignPanels.get(i);
1987 AlignFrame newaf = new AlignFrame(ap);
1989 // transfer reference for existing feature settings to new alignFrame
1990 if (ap == af.alignPanel)
1992 if (viewFeatureSettings != null && viewFeatureSettings.fr.ap == ap)
1994 newaf.featureSettings = viewFeatureSettings;
1996 newaf.setFeatureSettingsGeometry(fsBounds);
2000 * Restore the view's last exploded frame geometry if known. Multiple
2001 * views from one exploded frame share and restore the same (frame)
2002 * position and size.
2004 Rectangle geometry = ap.av.getExplodedGeometry();
2005 if (geometry != null)
2007 newaf.setBounds(geometry);
2010 ap.av.setGatherViewsHere(false);
2012 addInternalFrame(newaf, af.getTitle(), AlignFrame.DEFAULT_WIDTH,
2013 AlignFrame.DEFAULT_HEIGHT);
2014 // and materialise a new feature settings dialog instance for the new alignframe
2015 // (closes the old as if 'OK' was pressed)
2016 if (ap == af.alignPanel && newaf.featureSettings != null
2017 && newaf.featureSettings.isOpen()
2018 && af.alignPanel.getAlignViewport().isShowSequenceFeatures())
2020 newaf.showFeatureSettingsUI();
2024 af.featureSettings = null;
2025 af.alignPanels.clear();
2026 af.closeMenuItem_actionPerformed(true);
2031 * Gather expanded views (separate AlignFrame's) with the same sequence set
2032 * identifier back in to this frame as additional views, and close the expanded
2033 * views. Note the expanded frames may themselves have multiple views. We take
2038 public void gatherViews(AlignFrame source)
2040 source.viewport.setGatherViewsHere(true);
2041 source.viewport.setExplodedGeometry(source.getBounds());
2042 JInternalFrame[] frames = desktop.getAllFrames();
2043 String viewId = source.viewport.getSequenceSetId();
2044 for (int t = 0; t < frames.length; t++)
2046 if (frames[t] instanceof AlignFrame && frames[t] != source)
2048 AlignFrame af = (AlignFrame) frames[t];
2049 boolean gatherThis = false;
2050 for (int a = 0; a < af.alignPanels.size(); a++)
2052 AlignmentPanel ap = af.alignPanels.get(a);
2053 if (viewId.equals(ap.av.getSequenceSetId()))
2056 ap.av.setGatherViewsHere(false);
2057 ap.av.setExplodedGeometry(af.getBounds());
2058 source.addAlignmentPanel(ap, false);
2064 if (af.featureSettings != null && af.featureSettings.isOpen())
2066 if (source.featureSettings == null)
2068 // preserve the feature settings geometry for this frame
2069 source.featureSettings = af.featureSettings;
2070 source.setFeatureSettingsGeometry(
2071 af.getFeatureSettingsGeometry());
2075 // close it and forget
2076 af.featureSettings.close();
2079 af.alignPanels.clear();
2080 af.closeMenuItem_actionPerformed(true);
2085 // refresh the feature setting UI for the source frame if it exists
2086 if (source.featureSettings != null
2087 && source.featureSettings.isOpen())
2089 source.showFeatureSettingsUI();
2093 public JInternalFrame[] getAllFrames()
2095 return desktop.getAllFrames();
2099 * Checks the given url to see if it gives a response indicating that the user
2100 * should be informed of a new questionnaire.
2104 public void checkForQuestionnaire(String url)
2106 UserQuestionnaireCheck jvq = new UserQuestionnaireCheck(url);
2107 // javax.swing.SwingUtilities.invokeLater(jvq);
2108 new Thread(jvq).start();
2111 public void checkURLLinks()
2113 // Thread off the URL link checker
2114 addDialogThread(new Runnable()
2119 if (Cache.getDefault("CHECKURLLINKS", true))
2121 // check what the actual links are - if it's just the default don't
2122 // bother with the warning
2123 List<String> links = Preferences.sequenceUrlLinks
2126 // only need to check links if there is one with a
2127 // SEQUENCE_ID which is not the default EMBL_EBI link
2128 ListIterator<String> li = links.listIterator();
2129 boolean check = false;
2130 List<JLabel> urls = new ArrayList<>();
2131 while (li.hasNext())
2133 String link = li.next();
2134 if (link.contains(jalview.util.UrlConstants.SEQUENCE_ID)
2135 && !UrlConstants.isDefaultString(link))
2138 int barPos = link.indexOf("|");
2139 String urlMsg = barPos == -1 ? link
2140 : link.substring(0, barPos) + ": "
2141 + link.substring(barPos + 1);
2142 urls.add(new JLabel(urlMsg));
2150 // ask user to check in case URL links use old style tokens
2151 // ($SEQUENCE_ID$ for sequence id _or_ accession id)
2152 JPanel msgPanel = new JPanel();
2153 msgPanel.setLayout(new BoxLayout(msgPanel, BoxLayout.PAGE_AXIS));
2154 msgPanel.add(Box.createVerticalGlue());
2155 JLabel msg = new JLabel(MessageManager
2156 .getString("label.SEQUENCE_ID_for_DB_ACCESSION1"));
2157 JLabel msg2 = new JLabel(MessageManager
2158 .getString("label.SEQUENCE_ID_for_DB_ACCESSION2"));
2160 for (JLabel url : urls)
2166 final JCheckBox jcb = new JCheckBox(
2167 MessageManager.getString("label.do_not_display_again"));
2168 jcb.addActionListener(new ActionListener()
2171 public void actionPerformed(ActionEvent e)
2173 // update Cache settings for "don't show this again"
2174 boolean showWarningAgain = !jcb.isSelected();
2175 Cache.setProperty("CHECKURLLINKS",
2176 Boolean.valueOf(showWarningAgain).toString());
2181 JvOptionPane.showMessageDialog(Desktop.desktop, msgPanel,
2183 .getString("label.SEQUENCE_ID_no_longer_used"),
2184 JvOptionPane.WARNING_MESSAGE);
2191 * Proxy class for JDesktopPane which optionally displays the current memory
2192 * usage and highlights the desktop area with a red bar if free memory runs low.
2196 public class MyDesktopPane extends JDesktopPane
2199 private static final float ONE_MB = 1048576f;
2201 boolean showMemoryUsage = false;
2205 java.text.NumberFormat df;
2207 float maxMemory, allocatedMemory, freeMemory, totalFreeMemory,
2210 public MyDesktopPane(boolean showMemoryUsage)
2212 showMemoryUsage(showMemoryUsage);
2215 public void showMemoryUsage(boolean showMemory)
2217 this.showMemoryUsage = showMemory;
2220 Thread worker = new Thread(this);
2226 public boolean isShowMemoryUsage()
2228 return showMemoryUsage;
2234 df = java.text.NumberFormat.getNumberInstance();
2235 df.setMaximumFractionDigits(2);
2236 runtime = Runtime.getRuntime();
2238 while (showMemoryUsage)
2242 maxMemory = runtime.maxMemory() / ONE_MB;
2243 allocatedMemory = runtime.totalMemory() / ONE_MB;
2244 freeMemory = runtime.freeMemory() / ONE_MB;
2245 totalFreeMemory = freeMemory + (maxMemory - allocatedMemory);
2247 percentUsage = (totalFreeMemory / maxMemory) * 100;
2249 // if (percentUsage < 20)
2251 // border1 = BorderFactory.createMatteBorder(12, 12, 12, 12,
2253 // instance.set.setBorder(border1);
2256 // sleep after showing usage
2258 } catch (Exception ex)
2260 ex.printStackTrace();
2266 public void paintComponent(Graphics g)
2268 if (showMemoryUsage && g != null && df != null)
2270 if (percentUsage < 20)
2272 g.setColor(Color.red);
2274 FontMetrics fm = g.getFontMetrics();
2277 g.drawString(MessageManager.formatMessage("label.memory_stats",
2279 { df.format(totalFreeMemory), df.format(maxMemory),
2280 df.format(percentUsage) }),
2281 10, getHeight() - fm.getHeight());
2288 * Accessor method to quickly get all the AlignmentFrames loaded.
2290 * @return an array of AlignFrame, or null if none found
2292 public static AlignFrame[] getAlignFrames()
2294 if (Jalview.isHeadlessMode())
2296 // Desktop.desktop is null in headless mode
2297 return new AlignFrame[] { Jalview.currentAlignFrame };
2300 JInternalFrame[] frames = Desktop.desktop.getAllFrames();
2306 List<AlignFrame> avp = new ArrayList<>();
2308 for (int i = frames.length - 1; i > -1; i--)
2310 if (frames[i] instanceof AlignFrame)
2312 avp.add((AlignFrame) frames[i]);
2314 else if (frames[i] instanceof SplitFrame)
2317 * Also check for a split frame containing an AlignFrame
2319 GSplitFrame sf = (GSplitFrame) frames[i];
2320 if (sf.getTopFrame() instanceof AlignFrame)
2322 avp.add((AlignFrame) sf.getTopFrame());
2324 if (sf.getBottomFrame() instanceof AlignFrame)
2326 avp.add((AlignFrame) sf.getBottomFrame());
2330 if (avp.size() == 0)
2334 AlignFrame afs[] = avp.toArray(new AlignFrame[avp.size()]);
2339 * Returns an array of any AppJmol frames in the Desktop (or null if none).
2343 public GStructureViewer[] getJmols()
2345 JInternalFrame[] frames = Desktop.desktop.getAllFrames();
2351 List<GStructureViewer> avp = new ArrayList<>();
2353 for (int i = frames.length - 1; i > -1; i--)
2355 if (frames[i] instanceof AppJmol)
2357 GStructureViewer af = (GStructureViewer) frames[i];
2361 if (avp.size() == 0)
2365 GStructureViewer afs[] = avp.toArray(new GStructureViewer[avp.size()]);
2370 * Add Groovy Support to Jalview
2373 public void groovyShell_actionPerformed()
2377 openGroovyConsole();
2378 } catch (Exception ex)
2380 Cache.log.error("Groovy Shell Creation failed.", ex);
2381 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
2383 MessageManager.getString("label.couldnt_create_groovy_shell"),
2384 MessageManager.getString("label.groovy_support_failed"),
2385 JvOptionPane.ERROR_MESSAGE);
2390 * Open the Groovy console
2392 void openGroovyConsole()
2394 if (groovyConsole == null)
2396 groovyConsole = new groovy.ui.Console();
2397 groovyConsole.setVariable("Jalview", this);
2398 groovyConsole.run();
2401 * We allow only one console at a time, so that AlignFrame menu option
2402 * 'Calculate | Run Groovy script' is unambiguous.
2403 * Disable 'Groovy Console', and enable 'Run script', when the console is
2404 * opened, and the reverse when it is closed
2406 Window window = (Window) groovyConsole.getFrame();
2407 window.addWindowListener(new WindowAdapter()
2410 public void windowClosed(WindowEvent e)
2413 * rebind CMD-Q from Groovy Console to Jalview Quit
2416 enableExecuteGroovy(false);
2422 * show Groovy console window (after close and reopen)
2424 ((Window) groovyConsole.getFrame()).setVisible(true);
2427 * if we got this far, enable 'Run Groovy' in AlignFrame menus
2428 * and disable opening a second console
2430 enableExecuteGroovy(true);
2434 * Bind Ctrl/Cmd-Q to Quit - for reset as Groovy Console takes over this binding
2437 protected void addQuitHandler()
2439 getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
2440 .put(KeyStroke.getKeyStroke(KeyEvent.VK_Q,
2441 jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx()),
2443 getRootPane().getActionMap().put("Quit", new AbstractAction()
2446 public void actionPerformed(ActionEvent e)
2454 * Enable or disable 'Run Groovy script' in AlignFrame calculate menus
2457 * true if Groovy console is open
2459 public void enableExecuteGroovy(boolean enabled)
2462 * disable opening a second Groovy console
2463 * (or re-enable when the console is closed)
2465 groovyShell.setEnabled(!enabled);
2467 AlignFrame[] alignFrames = getAlignFrames();
2468 if (alignFrames != null)
2470 for (AlignFrame af : alignFrames)
2472 af.setGroovyEnabled(enabled);
2478 * Progress bars managed by the IProgressIndicator method.
2480 private Hashtable<Long, JPanel> progressBars;
2482 private Hashtable<Long, IProgressIndicatorHandler> progressBarHandlers;
2487 * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
2490 public void setProgressBar(String message, long id)
2492 Platform.timeCheck("Desktop " + message, Platform.TIME_MARK);
2494 if (progressBars == null)
2496 progressBars = new Hashtable<>();
2497 progressBarHandlers = new Hashtable<>();
2500 if (progressBars.get(Long.valueOf(id)) != null)
2502 JPanel panel = progressBars.remove(Long.valueOf(id));
2503 if (progressBarHandlers.contains(Long.valueOf(id)))
2505 progressBarHandlers.remove(Long.valueOf(id));
2507 removeProgressPanel(panel);
2511 progressBars.put(Long.valueOf(id), addProgressPanel(message));
2518 * @see jalview.gui.IProgressIndicator#registerHandler(long,
2519 * jalview.gui.IProgressIndicatorHandler)
2522 public void registerHandler(final long id,
2523 final IProgressIndicatorHandler handler)
2525 if (progressBarHandlers == null
2526 || !progressBars.containsKey(Long.valueOf(id)))
2528 throw new Error(MessageManager.getString(
2529 "error.call_setprogressbar_before_registering_handler"));
2531 progressBarHandlers.put(Long.valueOf(id), handler);
2532 final JPanel progressPanel = progressBars.get(Long.valueOf(id));
2533 if (handler.canCancel())
2535 JButton cancel = new JButton(
2536 MessageManager.getString("action.cancel"));
2537 final IProgressIndicator us = this;
2538 cancel.addActionListener(new ActionListener()
2542 public void actionPerformed(ActionEvent e)
2544 handler.cancelActivity(id);
2545 us.setProgressBar(MessageManager
2546 .formatMessage("label.cancelled_params", new Object[]
2547 { ((JLabel) progressPanel.getComponent(0)).getText() }),
2551 progressPanel.add(cancel, BorderLayout.EAST);
2557 * @return true if any progress bars are still active
2560 public boolean operationInProgress()
2562 if (progressBars != null && progressBars.size() > 0)
2570 * This will return the first AlignFrame holding the given viewport instance. It
2571 * will break if there are more than one AlignFrames viewing a particular av.
2574 * @return alignFrame for viewport
2576 public static AlignFrame getAlignFrameFor(AlignViewportI viewport)
2578 if (desktop != null)
2580 AlignmentPanel[] aps = getAlignmentPanels(
2581 viewport.getSequenceSetId());
2582 for (int panel = 0; aps != null && panel < aps.length; panel++)
2584 if (aps[panel] != null && aps[panel].av == viewport)
2586 return aps[panel].alignFrame;
2593 public VamsasApplication getVamsasApplication()
2595 // TODO: JAL-3311 remove remaining code from Jalview relating to VAMSAS
2601 * flag set if jalview GUI is being operated programmatically
2603 private boolean inBatchMode = false;
2606 * check if jalview GUI is being operated programmatically
2608 * @return inBatchMode
2610 public boolean isInBatchMode()
2616 * set flag if jalview GUI is being operated programmatically
2618 * @param inBatchMode
2620 public void setInBatchMode(boolean inBatchMode)
2622 this.inBatchMode = inBatchMode;
2625 public void startServiceDiscovery()
2627 startServiceDiscovery(false);
2630 public void startServiceDiscovery(boolean blocking)
2632 boolean alive = true;
2633 Thread t0 = null, t1 = null, t2 = null;
2634 // JAL-940 - JALVIEW 1 services are now being EOLed as of JABA 2.1 release
2637 // todo: changesupport handlers need to be transferred
2638 if (discoverer == null)
2640 discoverer = new jalview.ws.jws1.Discoverer();
2641 // register PCS handler for desktop.
2642 discoverer.addPropertyChangeListener(changeSupport);
2644 // JAL-940 - disabled JWS1 service configuration - always start discoverer
2645 // until we phase out completely
2646 (t0 = new Thread(discoverer)).start();
2649 if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
2651 t2 = jalview.ws.jws2.Jws2Discoverer.getDiscoverer()
2652 .startDiscoverer(changeSupport);
2656 // TODO: do rest service discovery
2665 } catch (Exception e)
2668 alive = (t1 != null && t1.isAlive()) || (t2 != null && t2.isAlive())
2669 || (t3 != null && t3.isAlive())
2670 || (t0 != null && t0.isAlive());
2676 * called to check if the service discovery process completed successfully.
2680 protected void JalviewServicesChanged(PropertyChangeEvent evt)
2682 if (evt.getNewValue() == null || evt.getNewValue() instanceof Vector)
2684 final String ermsg = jalview.ws.jws2.Jws2Discoverer.getDiscoverer()
2685 .getErrorMessages();
2688 if (Cache.getDefault("SHOW_WSDISCOVERY_ERRORS", true))
2690 if (serviceChangedDialog == null)
2692 // only run if we aren't already displaying one of these.
2693 addDialogThread(serviceChangedDialog = new Runnable()
2700 * JalviewDialog jd =new JalviewDialog() {
2702 * @Override protected void cancelPressed() { // TODO
2703 * Auto-generated method stub
2705 * }@Override protected void okPressed() { // TODO
2706 * Auto-generated method stub
2708 * }@Override protected void raiseClosed() { // TODO
2709 * Auto-generated method stub
2711 * } }; jd.initDialogFrame(new
2712 * JLabel("<html><table width=\"450\"><tr><td>" + ermsg +
2713 * "<br/>It may be that you have invalid JABA URLs in your web service preferences,"
2714 * + " or mis-configured HTTP proxy settings.<br/>" +
2715 * "Check the <em>Connections</em> and <em>Web services</em> tab of the"
2717 * " Tools->Preferences dialog box to change them.</td></tr></table></html>"
2718 * ), true, true, "Web Service Configuration Problem", 450,
2721 * jd.waitForInput();
2723 JvOptionPane.showConfirmDialog(Desktop.desktop,
2724 new JLabel("<html><table width=\"450\"><tr><td>"
2725 + ermsg + "</td></tr></table>"
2726 + "<p>It may be that you have invalid JABA URLs<br/>in your web service preferences,"
2727 + "<br>or as a command-line argument, or mis-configured HTTP proxy settings.</p>"
2728 + "<p>Check the <em>Connections</em> and <em>Web services</em> tab<br/>of the"
2729 + " Tools->Preferences dialog box to change them.</p></html>"),
2730 "Web Service Configuration Problem",
2731 JvOptionPane.DEFAULT_OPTION,
2732 JvOptionPane.ERROR_MESSAGE);
2733 serviceChangedDialog = null;
2742 "Errors reported by JABA discovery service. Check web services preferences.\n"
2749 private Runnable serviceChangedDialog = null;
2752 * start a thread to open a URL in the configured browser. Pops up a warning
2753 * dialog to the user if there is an exception when calling out to the browser
2758 public static void showUrl(final String url)
2760 showUrl(url, Desktop.instance);
2764 * Like showUrl but allows progress handler to be specified
2768 * (null) or object implementing IProgressIndicator
2770 public static void showUrl(final String url,
2771 final IProgressIndicator progress)
2773 new Thread(new Runnable()
2780 if (progress != null)
2782 progress.setProgressBar(MessageManager
2783 .formatMessage("status.opening_params", new Object[]
2784 { url }), this.hashCode());
2786 jalview.util.BrowserLauncher.openURL(url);
2787 } catch (Exception ex)
2789 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
2791 .getString("label.web_browser_not_found_unix"),
2792 MessageManager.getString("label.web_browser_not_found"),
2793 JvOptionPane.WARNING_MESSAGE);
2795 ex.printStackTrace();
2797 if (progress != null)
2799 progress.setProgressBar(null, this.hashCode());
2805 public static WsParamSetManager wsparamManager = null;
2807 public static ParamManager getUserParameterStore()
2809 if (wsparamManager == null)
2811 wsparamManager = new WsParamSetManager();
2813 return wsparamManager;
2817 * static hyperlink handler proxy method for use by Jalview's internal windows
2821 public static void hyperlinkUpdate(HyperlinkEvent e)
2823 if (e.getEventType() == EventType.ACTIVATED)
2828 url = e.getURL().toString();
2829 Desktop.showUrl(url);
2830 } catch (Exception x)
2834 if (Cache.log != null)
2836 Cache.log.error("Couldn't handle string " + url + " as a URL.");
2841 "Couldn't handle string " + url + " as a URL.");
2844 // ignore any exceptions due to dud links.
2851 * single thread that handles display of dialogs to user.
2853 ExecutorService dialogExecutor = Executors.newSingleThreadExecutor();
2856 * flag indicating if dialogExecutor should try to acquire a permit
2858 private volatile boolean dialogPause = true;
2863 private java.util.concurrent.Semaphore block = new Semaphore(0);
2865 private static groovy.ui.Console groovyConsole;
2868 * add another dialog thread to the queue
2872 public void addDialogThread(final Runnable prompter)
2874 dialogExecutor.submit(new Runnable()
2884 } catch (InterruptedException x)
2888 if (instance == null)
2894 SwingUtilities.invokeAndWait(prompter);
2895 } catch (Exception q)
2897 Cache.log.warn("Unexpected Exception in dialog thread.", q);
2903 public void startDialogQueue()
2905 // set the flag so we don't pause waiting for another permit and semaphore
2906 // the current task to begin
2907 dialogPause = false;
2912 * Outputs an image of the desktop to file in EPS format, after prompting the
2913 * user for choice of Text or Lineart character rendering (unless a preference
2914 * has been set). The file name is generated as
2917 * Jalview_snapshot_nnnnn.eps where nnnnn is the current timestamp in milliseconds
2921 protected void snapShotWindow_actionPerformed(ActionEvent e)
2923 // currently the menu option to do this is not shown
2926 int width = getWidth();
2927 int height = getHeight();
2929 "Jalview_snapshot_" + System.currentTimeMillis() + ".eps");
2930 ImageWriterI writer = new ImageWriterI()
2933 public void exportImage(Graphics g) throws Exception
2936 Cache.log.info("Successfully written snapshot to file "
2937 + of.getAbsolutePath());
2940 String title = "View of desktop";
2941 ImageExporter exporter = new ImageExporter(writer, null, TYPE.EPS,
2943 exporter.doExport(of, this, width, height, title);
2947 * Explode the views in the given SplitFrame into separate SplitFrame windows.
2948 * This respects (remembers) any previous 'exploded geometry' i.e. the size and
2949 * location last time the view was expanded (if any). However it does not
2950 * remember the split pane divider location - this is set to match the
2951 * 'exploding' frame.
2955 public void explodeViews(SplitFrame sf)
2957 AlignFrame oldTopFrame = (AlignFrame) sf.getTopFrame();
2958 AlignFrame oldBottomFrame = (AlignFrame) sf.getBottomFrame();
2959 List<? extends AlignmentViewPanel> topPanels = oldTopFrame
2961 List<? extends AlignmentViewPanel> bottomPanels = oldBottomFrame
2963 int viewCount = topPanels.size();
2970 * Processing in reverse order works, forwards order leaves the first panels
2971 * not visible. I don't know why!
2973 for (int i = viewCount - 1; i >= 0; i--)
2976 * Make new top and bottom frames. These take over the respective
2977 * AlignmentPanel objects, including their AlignmentViewports, so the
2978 * cdna/protein relationships between the viewports is carried over to the
2981 * explodedGeometry holds the (x, y) position of the previously exploded
2982 * SplitFrame, and the (width, height) of the AlignFrame component
2984 AlignmentPanel topPanel = (AlignmentPanel) topPanels.get(i);
2985 AlignFrame newTopFrame = new AlignFrame(topPanel);
2986 newTopFrame.setSize(oldTopFrame.getSize());
2987 newTopFrame.setVisible(true);
2988 Rectangle geometry = ((AlignViewport) topPanel.getAlignViewport())
2989 .getExplodedGeometry();
2990 if (geometry != null)
2992 newTopFrame.setSize(geometry.getSize());
2995 AlignmentPanel bottomPanel = (AlignmentPanel) bottomPanels.get(i);
2996 AlignFrame newBottomFrame = new AlignFrame(bottomPanel);
2997 newBottomFrame.setSize(oldBottomFrame.getSize());
2998 newBottomFrame.setVisible(true);
2999 geometry = ((AlignViewport) bottomPanel.getAlignViewport())
3000 .getExplodedGeometry();
3001 if (geometry != null)
3003 newBottomFrame.setSize(geometry.getSize());
3006 topPanel.av.setGatherViewsHere(false);
3007 bottomPanel.av.setGatherViewsHere(false);
3008 JInternalFrame splitFrame = new SplitFrame(newTopFrame,
3010 if (geometry != null)
3012 splitFrame.setLocation(geometry.getLocation());
3014 Desktop.addInternalFrame(splitFrame, sf.getTitle(), -1, -1);
3018 * Clear references to the panels (now relocated in the new SplitFrames)
3019 * before closing the old SplitFrame.
3022 bottomPanels.clear();
3027 * Gather expanded split frames, sharing the same pairs of sequence set ids,
3028 * back into the given SplitFrame as additional views. Note that the gathered
3029 * frames may themselves have multiple views.
3033 public void gatherViews(GSplitFrame source)
3036 * special handling of explodedGeometry for a view within a SplitFrame: - it
3037 * holds the (x, y) position of the enclosing SplitFrame, and the (width,
3038 * height) of the AlignFrame component
3040 AlignFrame myTopFrame = (AlignFrame) source.getTopFrame();
3041 AlignFrame myBottomFrame = (AlignFrame) source.getBottomFrame();
3042 myTopFrame.viewport.setExplodedGeometry(new Rectangle(source.getX(),
3043 source.getY(), myTopFrame.getWidth(), myTopFrame.getHeight()));
3044 myBottomFrame.viewport
3045 .setExplodedGeometry(new Rectangle(source.getX(), source.getY(),
3046 myBottomFrame.getWidth(), myBottomFrame.getHeight()));
3047 myTopFrame.viewport.setGatherViewsHere(true);
3048 myBottomFrame.viewport.setGatherViewsHere(true);
3049 String topViewId = myTopFrame.viewport.getSequenceSetId();
3050 String bottomViewId = myBottomFrame.viewport.getSequenceSetId();
3052 JInternalFrame[] frames = desktop.getAllFrames();
3053 for (JInternalFrame frame : frames)
3055 if (frame instanceof SplitFrame && frame != source)
3057 SplitFrame sf = (SplitFrame) frame;
3058 AlignFrame topFrame = (AlignFrame) sf.getTopFrame();
3059 AlignFrame bottomFrame = (AlignFrame) sf.getBottomFrame();
3060 boolean gatherThis = false;
3061 for (int a = 0; a < topFrame.alignPanels.size(); a++)
3063 AlignmentPanel topPanel = topFrame.alignPanels.get(a);
3064 AlignmentPanel bottomPanel = bottomFrame.alignPanels.get(a);
3065 if (topViewId.equals(topPanel.av.getSequenceSetId())
3066 && bottomViewId.equals(bottomPanel.av.getSequenceSetId()))
3069 topPanel.av.setGatherViewsHere(false);
3070 bottomPanel.av.setGatherViewsHere(false);
3071 topPanel.av.setExplodedGeometry(
3072 new Rectangle(sf.getLocation(), topFrame.getSize()));
3073 bottomPanel.av.setExplodedGeometry(
3074 new Rectangle(sf.getLocation(), bottomFrame.getSize()));
3075 myTopFrame.addAlignmentPanel(topPanel, false);
3076 myBottomFrame.addAlignmentPanel(bottomPanel, false);
3082 topFrame.getAlignPanels().clear();
3083 bottomFrame.getAlignPanels().clear();
3090 * The dust settles...give focus to the tab we did this from.
3092 myTopFrame.setDisplayedView(myTopFrame.alignPanel);
3095 public static groovy.ui.Console getGroovyConsole()
3097 return groovyConsole;
3101 * handles the payload of a drag and drop event.
3103 * TODO refactor to desktop utilities class
3106 * - Data source strings extracted from the drop event
3108 * - protocol for each data source extracted from the drop event
3112 * - the payload from the drop event
3115 public static void transferFromDropTarget(List<Object> files,
3116 List<DataSourceType> protocols, DropTargetDropEvent evt,
3117 Transferable t) throws Exception
3120 // BH 2018 changed List<String> to List<Object> to allow for File from SwingJS
3122 // DataFlavor[] flavors = t.getTransferDataFlavors();
3123 // for (int i = 0; i < flavors.length; i++) {
3124 // if (flavors[i].isFlavorJavaFileListType()) {
3125 // evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
3126 // List<File> list = (List<File>) t.getTransferData(flavors[i]);
3127 // for (int j = 0; j < list.size(); j++) {
3128 // File file = (File) list.get(j);
3129 // byte[] data = getDroppedFileBytes(file);
3130 // fileName.setText(file.getName() + " - " + data.length + " " +
3131 // evt.getLocation());
3132 // JTextArea target = (JTextArea) ((DropTarget) evt.getSource()).getComponent();
3133 // target.setText(new String(data));
3135 // dtde.dropComplete(true);
3140 DataFlavor uriListFlavor = new DataFlavor(
3141 "text/uri-list;class=java.lang.String"), urlFlavour = null;
3144 urlFlavour = new DataFlavor(
3145 "application/x-java-url; class=java.net.URL");
3146 } catch (ClassNotFoundException cfe)
3148 Cache.log.debug("Couldn't instantiate the URL dataflavor.", cfe);
3151 if (urlFlavour != null && t.isDataFlavorSupported(urlFlavour))
3156 java.net.URL url = (URL) t.getTransferData(urlFlavour);
3157 // nb: java 8 osx bug https://bugs.openjdk.java.net/browse/JDK-8156099
3158 // means url may be null.
3161 protocols.add(DataSourceType.URL);
3162 files.add(url.toString());
3163 Cache.log.debug("Drop handled as URL dataflavor "
3164 + files.get(files.size() - 1));
3169 if (Platform.isAMacAndNotJS())
3172 "Please ignore plist error - occurs due to problem with java 8 on OSX");
3175 } catch (Throwable ex)
3177 Cache.log.debug("URL drop handler failed.", ex);
3180 if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
3182 // Works on Windows and MacOSX
3183 Cache.log.debug("Drop handled as javaFileListFlavor");
3184 for (Object file : (List) t
3185 .getTransferData(DataFlavor.javaFileListFlavor))
3188 protocols.add(DataSourceType.FILE);
3193 // Unix like behaviour
3194 boolean added = false;
3196 if (t.isDataFlavorSupported(uriListFlavor))
3198 Cache.log.debug("Drop handled as uriListFlavor");
3199 // This is used by Unix drag system
3200 data = (String) t.getTransferData(uriListFlavor);
3204 // fallback to text: workaround - on OSX where there's a JVM bug
3205 Cache.log.debug("standard URIListFlavor failed. Trying text");
3206 // try text fallback
3207 DataFlavor textDf = new DataFlavor(
3208 "text/plain;class=java.lang.String");
3209 if (t.isDataFlavorSupported(textDf))
3211 data = (String) t.getTransferData(textDf);
3214 Cache.log.debug("Plain text drop content returned "
3215 + (data == null ? "Null - failed" : data));
3220 while (protocols.size() < files.size())
3222 Cache.log.debug("Adding missing FILE protocol for "
3223 + files.get(protocols.size()));
3224 protocols.add(DataSourceType.FILE);
3226 for (java.util.StringTokenizer st = new java.util.StringTokenizer(
3227 data, "\r\n"); st.hasMoreTokens();)
3230 String s = st.nextToken();
3231 if (s.startsWith("#"))
3233 // the line is a comment (as per the RFC 2483)
3236 java.net.URI uri = new java.net.URI(s);
3237 if (uri.getScheme().toLowerCase().startsWith("http"))
3239 protocols.add(DataSourceType.URL);
3240 files.add(uri.toString());
3244 // otherwise preserve old behaviour: catch all for file objects
3245 java.io.File file = new java.io.File(uri);
3246 protocols.add(DataSourceType.FILE);
3247 files.add(file.toString());
3252 if (Cache.log.isDebugEnabled())
3254 if (data == null || !added)
3257 if (t.getTransferDataFlavors() != null
3258 && t.getTransferDataFlavors().length > 0)
3261 "Couldn't resolve drop data. Here are the supported flavors:");
3262 for (DataFlavor fl : t.getTransferDataFlavors())
3265 "Supported transfer dataflavor: " + fl.toString());
3266 Object df = t.getTransferData(fl);
3269 Cache.log.debug("Retrieves: " + df);
3273 Cache.log.debug("Retrieved nothing");
3279 Cache.log.debug("Couldn't resolve dataflavor for drop: "
3285 if (Platform.isWindowsAndNotJS())
3287 Cache.log.debug("Scanning dropped content for Windows Link Files");
3289 // resolve any .lnk files in the file drop
3290 for (int f = 0; f < files.size(); f++)
3292 String source = files.get(f).toString().toLowerCase();
3293 if (protocols.get(f).equals(DataSourceType.FILE)
3294 && (source.endsWith(".lnk") || source.endsWith(".url")
3295 || source.endsWith(".site")))
3299 Object obj = files.get(f);
3300 File lf = (obj instanceof File ? (File) obj
3301 : new File((String) obj));
3302 // process link file to get a URL
3303 Cache.log.debug("Found potential link file: " + lf);
3304 WindowsShortcut wscfile = new WindowsShortcut(lf);
3305 String fullname = wscfile.getRealFilename();
3306 protocols.set(f, FormatAdapter.checkProtocol(fullname));
3307 files.set(f, fullname);
3308 Cache.log.debug("Parsed real filename " + fullname
3309 + " to extract protocol: " + protocols.get(f));
3310 } catch (Exception ex)
3313 "Couldn't parse " + files.get(f) + " as a link file.",
3322 * Sets the Preferences property for experimental features to True or False
3323 * depending on the state of the controlling menu item
3326 protected void showExperimental_actionPerformed(boolean selected)
3328 Cache.setProperty(EXPERIMENTAL_FEATURES, Boolean.toString(selected));
3332 * Answers a (possibly empty) list of any structure viewer frames (currently for
3333 * either Jmol or Chimera) which are currently open. This may optionally be
3334 * restricted to viewers of a specified class, or viewers linked to a specified
3338 * if not null, only return viewers linked to this panel
3339 * @param structureViewerClass
3340 * if not null, only return viewers of this class
3343 public List<StructureViewerBase> getStructureViewers(
3344 AlignmentPanel apanel,
3345 Class<? extends StructureViewerBase> structureViewerClass)
3347 List<StructureViewerBase> result = new ArrayList<>();
3348 JInternalFrame[] frames = Desktop.instance.getAllFrames();
3350 for (JInternalFrame frame : frames)
3352 if (frame instanceof StructureViewerBase)
3354 if (structureViewerClass == null
3355 || structureViewerClass.isInstance(frame))
3358 || ((StructureViewerBase) frame).isLinkedWith(apanel))
3360 result.add((StructureViewerBase) frame);