2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
5 * This file is part of Jalview.
7 * Jalview is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation, either version 3
10 * of the License, or (at your option) any later version.
12 * Jalview is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19 * The Jalview Authors are detailed in the 'AUTHORS' file.
23 import java.awt.BorderLayout;
24 import java.awt.Color;
25 import java.awt.Dimension;
26 import java.awt.FontMetrics;
27 import java.awt.Graphics;
28 import java.awt.GridLayout;
29 import java.awt.Point;
30 import java.awt.Rectangle;
31 import java.awt.Toolkit;
32 import java.awt.Window;
33 import java.awt.datatransfer.Clipboard;
34 import java.awt.datatransfer.ClipboardOwner;
35 import java.awt.datatransfer.DataFlavor;
36 import java.awt.datatransfer.Transferable;
37 import java.awt.dnd.DnDConstants;
38 import java.awt.dnd.DropTargetDragEvent;
39 import java.awt.dnd.DropTargetDropEvent;
40 import java.awt.dnd.DropTargetEvent;
41 import java.awt.dnd.DropTargetListener;
42 import java.awt.event.ActionEvent;
43 import java.awt.event.ActionListener;
44 import java.awt.event.InputEvent;
45 import java.awt.event.KeyEvent;
46 import java.awt.event.MouseAdapter;
47 import java.awt.event.MouseEvent;
48 import java.awt.event.WindowAdapter;
49 import java.awt.event.WindowEvent;
50 import java.beans.PropertyChangeEvent;
51 import java.beans.PropertyChangeListener;
53 import java.io.FileWriter;
54 import java.io.IOException;
56 import java.util.ArrayList;
57 import java.util.HashMap;
58 import java.util.Hashtable;
59 import java.util.List;
60 import java.util.ListIterator;
61 import java.util.Vector;
62 import java.util.concurrent.ExecutorService;
63 import java.util.concurrent.Executors;
64 import java.util.concurrent.Semaphore;
66 import javax.swing.AbstractAction;
67 import javax.swing.Action;
68 import javax.swing.ActionMap;
69 import javax.swing.Box;
70 import javax.swing.BoxLayout;
71 import javax.swing.DefaultDesktopManager;
72 import javax.swing.DesktopManager;
73 import javax.swing.InputMap;
74 import javax.swing.JButton;
75 import javax.swing.JCheckBox;
76 import javax.swing.JComboBox;
77 import javax.swing.JComponent;
78 import javax.swing.JDesktopPane;
79 import javax.swing.JInternalFrame;
80 import javax.swing.JLabel;
81 import javax.swing.JMenuItem;
82 import javax.swing.JPanel;
83 import javax.swing.JPopupMenu;
84 import javax.swing.JProgressBar;
85 import javax.swing.JTextField;
86 import javax.swing.KeyStroke;
87 import javax.swing.SwingUtilities;
88 import javax.swing.event.HyperlinkEvent;
89 import javax.swing.event.HyperlinkEvent.EventType;
90 import javax.swing.event.InternalFrameAdapter;
91 import javax.swing.event.InternalFrameEvent;
93 import org.stackoverflowusers.file.WindowsShortcut;
95 import jalview.api.AlignViewportI;
96 import jalview.api.AlignmentViewPanel;
97 import jalview.bin.Cache;
98 import jalview.bin.Jalview;
99 import jalview.gui.ImageExporter.ImageWriterI;
100 import jalview.io.BackupFiles;
101 import jalview.io.DataSourceType;
102 import jalview.io.FileFormat;
103 import jalview.io.FileFormatException;
104 import jalview.io.FileFormatI;
105 import jalview.io.FileFormats;
106 import jalview.io.FileLoader;
107 import jalview.io.FormatAdapter;
108 import jalview.io.IdentifyFile;
109 import jalview.io.JalviewFileChooser;
110 import jalview.io.JalviewFileView;
111 import jalview.jbgui.GSplitFrame;
112 import jalview.jbgui.GStructureViewer;
113 import jalview.project.Jalview2XML;
114 import jalview.structure.StructureSelectionManager;
115 import jalview.urls.IdOrgSettings;
116 import jalview.util.BrowserLauncher;
117 import jalview.util.ImageMaker.TYPE;
118 import jalview.util.MessageManager;
119 import jalview.util.Platform;
120 import jalview.util.ShortcutKeyMaskExWrapper;
121 import jalview.util.UrlConstants;
122 import jalview.viewmodel.AlignmentViewport;
123 import jalview.ws.params.ParamManager;
124 import jalview.ws.utils.UrlDownloadClient;
131 * @version $Revision: 1.155 $
133 public class Desktop extends jalview.jbgui.GDesktop
134 implements DropTargetListener, ClipboardOwner, IProgressIndicator,
135 jalview.api.StructureSelectionManagerProvider
137 private static final String CITATION = "<br><br>Development managed by The Barton Group, University of Dundee, Scotland, UK.<br>"
138 + "<br><br>For help, see the FAQ at <a href=\"http://www.jalview.org/faq\">www.jalview.org/faq</a> and/or join the jalview-discuss@jalview.org mailing list"
139 + "<br><br>If you use Jalview, please cite:"
140 + "<br>Waterhouse, A.M., Procter, J.B., Martin, D.M.A, Clamp, M. and Barton, G. J. (2009)"
141 + "<br>Jalview Version 2 - a multiple sequence alignment editor and analysis workbench"
142 + "<br>Bioinformatics doi: 10.1093/bioinformatics/btp033";
144 private static final String DEFAULT_AUTHORS = "The Jalview Authors (See AUTHORS file for current list)";
146 private static int DEFAULT_MIN_WIDTH = 300;
148 private static int DEFAULT_MIN_HEIGHT = 250;
150 private static int ALIGN_FRAME_DEFAULT_MIN_WIDTH = 600;
152 private static int ALIGN_FRAME_DEFAULT_MIN_HEIGHT = 70;
154 private static final String EXPERIMENTAL_FEATURES = "EXPERIMENTAL_FEATURES";
156 protected static final String CONFIRM_KEYBOARD_QUIT = "CONFIRM_KEYBOARD_QUIT";
158 public static HashMap<String, FileWriter> savingFiles = new HashMap<String, FileWriter>();
160 private JalviewChangeSupport changeSupport = new JalviewChangeSupport();
163 * news reader - null if it was never started.
165 private BlogReader jvnews = null;
167 private File projectFile;
171 * @see jalview.gui.JalviewChangeSupport#addJalviewPropertyChangeListener(java.beans.PropertyChangeListener)
173 public void addJalviewPropertyChangeListener(
174 PropertyChangeListener listener)
176 changeSupport.addJalviewPropertyChangeListener(listener);
180 * @param propertyName
182 * @see jalview.gui.JalviewChangeSupport#addJalviewPropertyChangeListener(java.lang.String,
183 * java.beans.PropertyChangeListener)
185 public void addJalviewPropertyChangeListener(String propertyName,
186 PropertyChangeListener listener)
188 changeSupport.addJalviewPropertyChangeListener(propertyName, listener);
192 * @param propertyName
194 * @see jalview.gui.JalviewChangeSupport#removeJalviewPropertyChangeListener(java.lang.String,
195 * java.beans.PropertyChangeListener)
197 public void removeJalviewPropertyChangeListener(String propertyName,
198 PropertyChangeListener listener)
200 changeSupport.removeJalviewPropertyChangeListener(propertyName,
204 /** Singleton Desktop instance */
205 public static Desktop instance;
207 public static MyDesktopPane desktop;
209 public static MyDesktopPane getDesktop()
211 // BH 2018 could use currentThread() here as a reference to a
212 // Hashtable<Thread, MyDesktopPane> in JavaScript
216 static int openFrameCount = 0;
218 static final int xOffset = 30;
220 static final int yOffset = 30;
222 public static jalview.ws.jws1.Discoverer discoverer;
224 public static Object[] jalviewClipboard;
226 public static boolean internalCopy = false;
228 static int fileLoadingCount = 0;
230 class MyDesktopManager implements DesktopManager
233 private DesktopManager delegate;
235 public MyDesktopManager(DesktopManager delegate)
237 this.delegate = delegate;
241 public void activateFrame(JInternalFrame f)
245 delegate.activateFrame(f);
246 } catch (NullPointerException npe)
248 Point p = getMousePosition();
249 instance.showPasteMenu(p.x, p.y);
254 public void beginDraggingFrame(JComponent f)
256 delegate.beginDraggingFrame(f);
260 public void beginResizingFrame(JComponent f, int direction)
262 delegate.beginResizingFrame(f, direction);
266 public void closeFrame(JInternalFrame f)
268 delegate.closeFrame(f);
272 public void deactivateFrame(JInternalFrame f)
274 delegate.deactivateFrame(f);
278 public void deiconifyFrame(JInternalFrame f)
280 delegate.deiconifyFrame(f);
284 public void dragFrame(JComponent f, int newX, int newY)
290 delegate.dragFrame(f, newX, newY);
294 public void endDraggingFrame(JComponent f)
296 delegate.endDraggingFrame(f);
301 public void endResizingFrame(JComponent f)
303 delegate.endResizingFrame(f);
308 public void iconifyFrame(JInternalFrame f)
310 delegate.iconifyFrame(f);
314 public void maximizeFrame(JInternalFrame f)
316 delegate.maximizeFrame(f);
320 public void minimizeFrame(JInternalFrame f)
322 delegate.minimizeFrame(f);
326 public void openFrame(JInternalFrame f)
328 delegate.openFrame(f);
332 public void resizeFrame(JComponent f, int newX, int newY, int newWidth,
339 delegate.resizeFrame(f, newX, newY, newWidth, newHeight);
343 public void setBoundsForFrame(JComponent f, int newX, int newY,
344 int newWidth, int newHeight)
346 delegate.setBoundsForFrame(f, newX, newY, newWidth, newHeight);
349 // All other methods, simply delegate
354 * Creates a new Desktop object.
360 * A note to implementors. It is ESSENTIAL that any activities that might
361 * block are spawned off as threads rather than waited for during this
366 doConfigureStructurePrefs();
367 setTitle("Jalview " + Cache.getProperty("VERSION"));
369 if (!Platform.isAMac())
371 // this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
375 this.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
381 APQHandlers.setAPQHandlers(this);
382 } catch (Throwable t)
384 System.out.println("Error setting APQHandlers: " + t.toString());
385 // t.printStackTrace();
388 addWindowListener(new WindowAdapter()
392 public void windowClosing(WindowEvent ev)
398 boolean selmemusage = Cache.getDefault("SHOW_MEMUSAGE",
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 AlignViewportI source = null;
1551 AlignViewportI target = null;
1552 if (frames[0] instanceof AlignFrame)
1554 source = ((AlignFrame) frames[0]).getCurrentView();
1556 else if (frames[0] instanceof TreePanel)
1558 source = ((TreePanel) frames[0]).getViewPort();
1560 else if (frames[0] instanceof PCAPanel)
1562 source = ((PCAPanel) frames[0]).av;
1564 else if (frames[0].getContentPane() instanceof PairwiseAlignPanel)
1566 source = ((PairwiseAlignPanel) frames[0].getContentPane()).av;
1571 for (int i = 0; i < frames.length; i++)
1574 if (frames[i] == null)
1578 if (frames[i] instanceof AlignFrame)
1580 target = ((AlignFrame) frames[i]).getCurrentView();
1582 else if (frames[i] instanceof TreePanel)
1584 target = ((TreePanel) frames[i]).getViewPort();
1586 else if (frames[i] instanceof PCAPanel)
1588 target = ((PCAPanel) frames[i]).av;
1590 else if (frames[i].getContentPane() instanceof PairwiseAlignPanel)
1592 target = ((PairwiseAlignPanel) frames[i].getContentPane()).av;
1595 if (source == target)
1601 frames[i].setClosed(true);
1605 frames[i].setIcon(minimize);
1608 frames[i].toFront();
1612 } catch (java.beans.PropertyVetoException ex)
1627 protected void preferences_actionPerformed(ActionEvent e)
1633 * Prompts the user to choose a file and then saves the Jalview state as a
1634 * Jalview project file
1637 public void saveState_actionPerformed()
1639 saveState_actionPerformed(false);
1642 public void saveState_actionPerformed(boolean saveAs)
1644 java.io.File projectFile = getProjectFile();
1645 // autoSave indicates we already have a file and don't need to ask
1646 boolean autoSave = projectFile != null && !saveAs
1647 && BackupFiles.getEnabled();
1649 // System.out.println("autoSave="+autoSave+", projectFile='"+projectFile+"',
1650 // saveAs="+saveAs+", Backups
1651 // "+(BackupFiles.getEnabled()?"enabled":"disabled"));
1653 boolean approveSave = false;
1656 JalviewFileChooser chooser = new JalviewFileChooser("jvp",
1659 chooser.setFileView(new JalviewFileView());
1660 chooser.setDialogTitle(MessageManager.getString("label.save_state"));
1662 int value = chooser.showSaveDialog(this);
1664 if (value == JalviewFileChooser.APPROVE_OPTION)
1666 projectFile = chooser.getSelectedFile();
1667 setProjectFile(projectFile);
1672 if (approveSave || autoSave)
1674 final Desktop me = this;
1675 final java.io.File chosenFile = projectFile;
1676 new Thread(new Runnable()
1681 // TODO: refactor to Jalview desktop session controller action.
1682 setProgressBar(MessageManager.formatMessage(
1683 "label.saving_jalview_project", new Object[]
1684 { chosenFile.getName() }), chosenFile.hashCode());
1685 Cache.setProperty("LAST_DIRECTORY",
1686 chosenFile.getParent());
1687 // TODO catch and handle errors for savestate
1688 // TODO prevent user from messing with the Desktop whilst we're saving
1691 boolean doBackup = BackupFiles.getEnabled();
1692 BackupFiles backupfiles = doBackup ? new BackupFiles(chosenFile) : null;
1694 new Jalview2XML().saveState(doBackup ? backupfiles.getTempFile() : chosenFile);
1698 backupfiles.setWriteSuccess(true);
1699 backupfiles.rollBackupsAndRenameTempFile();
1701 } catch (OutOfMemoryError oom)
1703 new OOMWarning("Whilst saving current state to "
1704 + chosenFile.getName(), oom);
1705 } catch (Exception ex)
1707 Cache.log.error("Problems whilst trying to save to "
1708 + chosenFile.getName(), ex);
1709 JvOptionPane.showMessageDialog(me,
1710 MessageManager.formatMessage(
1711 "label.error_whilst_saving_current_state_to",
1713 { chosenFile.getName() }),
1714 MessageManager.getString("label.couldnt_save_project"),
1715 JvOptionPane.WARNING_MESSAGE);
1717 setProgressBar(null, chosenFile.hashCode());
1724 public void saveAsState_actionPerformed(ActionEvent e)
1726 saveState_actionPerformed(true);
1729 private void setProjectFile(File choice)
1731 this.projectFile = choice;
1734 public File getProjectFile()
1736 return this.projectFile;
1740 * Shows a file chooser dialog and tries to read in the selected file as a
1744 public void loadState_actionPerformed()
1746 final String[] suffix = new String[] { "jvp", "jar" };
1747 final String[] desc = new String[] { "Jalview Project",
1748 "Jalview Project (old)" };
1749 JalviewFileChooser chooser = new JalviewFileChooser(
1750 Cache.getProperty("LAST_DIRECTORY"), suffix, desc,
1751 "Jalview Project", true, BackupFiles.getEnabled()); // last two booleans: allFiles,
1753 chooser.setFileView(new JalviewFileView());
1754 chooser.setDialogTitle(MessageManager.getString("label.restore_state"));
1755 chooser.setResponseHandler(0, new Runnable()
1760 File selectedFile = chooser.getSelectedFile();
1761 setProjectFile(selectedFile);
1762 String choice = selectedFile.getAbsolutePath();
1763 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1764 new Thread(new Runnable()
1771 new Jalview2XML().loadJalviewAlign(selectedFile);
1772 } catch (OutOfMemoryError oom)
1774 new OOMWarning("Whilst loading project from " + choice, oom);
1775 } catch (Exception ex)
1778 "Problems whilst loading project from " + choice, ex);
1779 JvOptionPane.showMessageDialog(Desktop.desktop,
1780 MessageManager.formatMessage(
1781 "label.error_whilst_loading_project_from",
1784 MessageManager.getString("label.couldnt_load_project"),
1785 JvOptionPane.WARNING_MESSAGE);
1792 chooser.showOpenDialog(this);
1796 public void inputSequence_actionPerformed(ActionEvent e)
1798 new SequenceFetcher(this);
1801 JPanel progressPanel;
1803 ArrayList<JPanel> fileLoadingPanels = new ArrayList<>();
1805 public void startLoading(final Object fileName)
1807 if (fileLoadingCount == 0)
1809 fileLoadingPanels.add(addProgressPanel(MessageManager
1810 .formatMessage("label.loading_file", new Object[]
1816 private JPanel addProgressPanel(String string)
1818 if (progressPanel == null)
1820 progressPanel = new JPanel(new GridLayout(1, 1));
1821 totalProgressCount = 0;
1822 instance.getContentPane().add(progressPanel, BorderLayout.SOUTH);
1824 JPanel thisprogress = new JPanel(new BorderLayout(10, 5));
1825 JProgressBar progressBar = new JProgressBar();
1826 progressBar.setIndeterminate(true);
1828 thisprogress.add(new JLabel(string), BorderLayout.WEST);
1830 thisprogress.add(progressBar, BorderLayout.CENTER);
1831 progressPanel.add(thisprogress);
1832 ((GridLayout) progressPanel.getLayout()).setRows(
1833 ((GridLayout) progressPanel.getLayout()).getRows() + 1);
1834 ++totalProgressCount;
1835 instance.validate();
1836 return thisprogress;
1839 int totalProgressCount = 0;
1841 private void removeProgressPanel(JPanel progbar)
1843 if (progressPanel != null)
1845 synchronized (progressPanel)
1847 progressPanel.remove(progbar);
1848 GridLayout gl = (GridLayout) progressPanel.getLayout();
1849 gl.setRows(gl.getRows() - 1);
1850 if (--totalProgressCount < 1)
1852 this.getContentPane().remove(progressPanel);
1853 progressPanel = null;
1860 public void stopLoading()
1863 if (fileLoadingCount < 1)
1865 while (fileLoadingPanels.size() > 0)
1867 removeProgressPanel(fileLoadingPanels.remove(0));
1869 fileLoadingPanels.clear();
1870 fileLoadingCount = 0;
1875 public static int getViewCount(String alignmentId)
1877 AlignmentViewport[] aps = getViewports(alignmentId);
1878 return (aps == null) ? 0 : aps.length;
1883 * @param alignmentId
1884 * - if null, all sets are returned
1885 * @return all AlignmentPanels concerning the alignmentId sequence set
1887 public static AlignmentPanel[] getAlignmentPanels(String alignmentId)
1889 if (Desktop.desktop == null)
1891 // no frames created and in headless mode
1892 // TODO: verify that frames are recoverable when in headless mode
1895 List<AlignmentPanel> aps = new ArrayList<>();
1896 AlignFrame[] frames = getAlignFrames();
1901 for (AlignFrame af : frames)
1903 for (AlignmentPanel ap : af.alignPanels)
1905 if (alignmentId == null
1906 || alignmentId.equals(ap.av.getSequenceSetId()))
1912 if (aps.size() == 0)
1916 AlignmentPanel[] vap = aps.toArray(new AlignmentPanel[aps.size()]);
1921 * get all the viewports on an alignment.
1923 * @param sequenceSetId
1924 * unique alignment id (may be null - all viewports returned in that
1926 * @return all viewports on the alignment bound to sequenceSetId
1928 public static AlignmentViewport[] getViewports(String sequenceSetId)
1930 List<AlignmentViewport> viewp = new ArrayList<>();
1931 if (desktop != null)
1933 AlignFrame[] frames = Desktop.getAlignFrames();
1935 for (AlignFrame afr : frames)
1937 if (sequenceSetId == null || afr.getViewport().getSequenceSetId()
1938 .equals(sequenceSetId))
1940 if (afr.alignPanels != null)
1942 for (AlignmentPanel ap : afr.alignPanels)
1944 if (sequenceSetId == null
1945 || sequenceSetId.equals(ap.av.getSequenceSetId()))
1953 viewp.add(afr.getViewport());
1957 if (viewp.size() > 0)
1959 return viewp.toArray(new AlignmentViewport[viewp.size()]);
1966 * Explode the views in the given frame into separate AlignFrame
1970 public static void explodeViews(AlignFrame af)
1972 int size = af.alignPanels.size();
1978 // FIXME: ideally should use UI interface API
1979 FeatureSettings viewFeatureSettings = (af.featureSettings != null
1980 && af.featureSettings.isOpen())
1981 ? af.featureSettings
1983 Rectangle fsBounds = af.getFeatureSettingsGeometry();
1984 for (int i = 0; i < size; i++)
1986 AlignmentPanel ap = af.alignPanels.get(i);
1988 AlignFrame newaf = new AlignFrame(ap);
1990 // transfer reference for existing feature settings to new alignFrame
1991 if (ap == af.alignPanel)
1993 if (viewFeatureSettings != null && viewFeatureSettings.fr.ap == ap)
1995 newaf.featureSettings = viewFeatureSettings;
1997 newaf.setFeatureSettingsGeometry(fsBounds);
2001 * Restore the view's last exploded frame geometry if known. Multiple
2002 * views from one exploded frame share and restore the same (frame)
2003 * position and size.
2005 Rectangle geometry = ap.av.getExplodedGeometry();
2006 if (geometry != null)
2008 newaf.setBounds(geometry);
2011 ap.av.setGatherViewsHere(false);
2013 addInternalFrame(newaf, af.getTitle(), AlignFrame.DEFAULT_WIDTH,
2014 AlignFrame.DEFAULT_HEIGHT);
2015 // and materialise a new feature settings dialog instance for the new alignframe
2016 // (closes the old as if 'OK' was pressed)
2017 if (ap == af.alignPanel && newaf.featureSettings != null
2018 && newaf.featureSettings.isOpen()
2019 && af.alignPanel.getAlignViewport().isShowSequenceFeatures())
2021 newaf.showFeatureSettingsUI();
2025 af.featureSettings = null;
2026 af.alignPanels.clear();
2027 af.closeMenuItem_actionPerformed(true);
2032 * Gather expanded views (separate AlignFrame's) with the same sequence set
2033 * identifier back in to this frame as additional views, and close the expanded
2034 * views. Note the expanded frames may themselves have multiple views. We take
2039 public void gatherViews(AlignFrame source)
2041 source.viewport.setGatherViewsHere(true);
2042 source.viewport.setExplodedGeometry(source.getBounds());
2043 JInternalFrame[] frames = desktop.getAllFrames();
2044 String viewId = source.viewport.getSequenceSetId();
2045 for (int t = 0; t < frames.length; t++)
2047 if (frames[t] instanceof AlignFrame && frames[t] != source)
2049 AlignFrame af = (AlignFrame) frames[t];
2050 boolean gatherThis = false;
2051 for (int a = 0; a < af.alignPanels.size(); a++)
2053 AlignmentPanel ap = af.alignPanels.get(a);
2054 if (viewId.equals(ap.av.getSequenceSetId()))
2057 ap.av.setGatherViewsHere(false);
2058 ap.av.setExplodedGeometry(af.getBounds());
2059 source.addAlignmentPanel(ap, false);
2065 if (af.featureSettings != null && af.featureSettings.isOpen())
2067 if (source.featureSettings == null)
2069 // preserve the feature settings geometry for this frame
2070 source.featureSettings = af.featureSettings;
2071 source.setFeatureSettingsGeometry(
2072 af.getFeatureSettingsGeometry());
2076 // close it and forget
2077 af.featureSettings.close();
2080 af.alignPanels.clear();
2081 af.closeMenuItem_actionPerformed(true);
2086 // refresh the feature setting UI for the source frame if it exists
2087 if (source.featureSettings != null
2088 && source.featureSettings.isOpen())
2090 source.showFeatureSettingsUI();
2094 public JInternalFrame[] getAllFrames()
2096 return desktop.getAllFrames();
2100 * Checks the given url to see if it gives a response indicating that the user
2101 * should be informed of a new questionnaire.
2105 public void checkForQuestionnaire(String url)
2107 UserQuestionnaireCheck jvq = new UserQuestionnaireCheck(url);
2108 // javax.swing.SwingUtilities.invokeLater(jvq);
2109 new Thread(jvq).start();
2112 public void checkURLLinks()
2114 // Thread off the URL link checker
2115 addDialogThread(new Runnable()
2120 if (Cache.getDefault("CHECKURLLINKS", true))
2122 // check what the actual links are - if it's just the default don't
2123 // bother with the warning
2124 List<String> links = Preferences.sequenceUrlLinks
2127 // only need to check links if there is one with a
2128 // SEQUENCE_ID which is not the default EMBL_EBI link
2129 ListIterator<String> li = links.listIterator();
2130 boolean check = false;
2131 List<JLabel> urls = new ArrayList<>();
2132 while (li.hasNext())
2134 String link = li.next();
2135 if (link.contains(jalview.util.UrlConstants.SEQUENCE_ID)
2136 && !UrlConstants.isDefaultString(link))
2139 int barPos = link.indexOf("|");
2140 String urlMsg = barPos == -1 ? link
2141 : link.substring(0, barPos) + ": "
2142 + link.substring(barPos + 1);
2143 urls.add(new JLabel(urlMsg));
2151 // ask user to check in case URL links use old style tokens
2152 // ($SEQUENCE_ID$ for sequence id _or_ accession id)
2153 JPanel msgPanel = new JPanel();
2154 msgPanel.setLayout(new BoxLayout(msgPanel, BoxLayout.PAGE_AXIS));
2155 msgPanel.add(Box.createVerticalGlue());
2156 JLabel msg = new JLabel(MessageManager
2157 .getString("label.SEQUENCE_ID_for_DB_ACCESSION1"));
2158 JLabel msg2 = new JLabel(MessageManager
2159 .getString("label.SEQUENCE_ID_for_DB_ACCESSION2"));
2161 for (JLabel url : urls)
2167 final JCheckBox jcb = new JCheckBox(
2168 MessageManager.getString("label.do_not_display_again"));
2169 jcb.addActionListener(new ActionListener()
2172 public void actionPerformed(ActionEvent e)
2174 // update Cache settings for "don't show this again"
2175 boolean showWarningAgain = !jcb.isSelected();
2176 Cache.setProperty("CHECKURLLINKS",
2177 Boolean.valueOf(showWarningAgain).toString());
2182 JvOptionPane.showMessageDialog(Desktop.desktop, msgPanel,
2184 .getString("label.SEQUENCE_ID_no_longer_used"),
2185 JvOptionPane.WARNING_MESSAGE);
2192 * Proxy class for JDesktopPane which optionally displays the current memory
2193 * usage and highlights the desktop area with a red bar if free memory runs low.
2197 public class MyDesktopPane extends JDesktopPane
2200 private static final float ONE_MB = 1048576f;
2202 boolean showMemoryUsage = false;
2206 java.text.NumberFormat df;
2208 float maxMemory, allocatedMemory, freeMemory, totalFreeMemory,
2211 public MyDesktopPane(boolean showMemoryUsage)
2213 showMemoryUsage(showMemoryUsage);
2216 public void showMemoryUsage(boolean showMemory)
2218 this.showMemoryUsage = showMemory;
2221 Thread worker = new Thread(this);
2227 public boolean isShowMemoryUsage()
2229 return showMemoryUsage;
2235 df = java.text.NumberFormat.getNumberInstance();
2236 df.setMaximumFractionDigits(2);
2237 runtime = Runtime.getRuntime();
2239 while (showMemoryUsage)
2243 maxMemory = runtime.maxMemory() / ONE_MB;
2244 allocatedMemory = runtime.totalMemory() / ONE_MB;
2245 freeMemory = runtime.freeMemory() / ONE_MB;
2246 totalFreeMemory = freeMemory + (maxMemory - allocatedMemory);
2248 percentUsage = (totalFreeMemory / maxMemory) * 100;
2250 // if (percentUsage < 20)
2252 // border1 = BorderFactory.createMatteBorder(12, 12, 12, 12,
2254 // instance.set.setBorder(border1);
2257 // sleep after showing usage
2259 } catch (Exception ex)
2261 ex.printStackTrace();
2267 public void paintComponent(Graphics g)
2269 if (showMemoryUsage && g != null && df != null)
2271 if (percentUsage < 20)
2273 g.setColor(Color.red);
2275 FontMetrics fm = g.getFontMetrics();
2278 g.drawString(MessageManager.formatMessage("label.memory_stats",
2280 { df.format(totalFreeMemory), df.format(maxMemory),
2281 df.format(percentUsage) }),
2282 10, getHeight() - fm.getHeight());
2289 * Accessor method to quickly get all the AlignmentFrames loaded.
2291 * @return an array of AlignFrame, or null if none found
2293 public static AlignFrame[] getAlignFrames()
2295 if (Jalview.isHeadlessMode())
2297 // Desktop.desktop is null in headless mode
2298 return new AlignFrame[] { Jalview.currentAlignFrame };
2301 JInternalFrame[] frames = Desktop.desktop.getAllFrames();
2307 List<AlignFrame> avp = new ArrayList<>();
2309 for (int i = frames.length - 1; i > -1; i--)
2311 if (frames[i] instanceof AlignFrame)
2313 avp.add((AlignFrame) frames[i]);
2315 else if (frames[i] instanceof SplitFrame)
2318 * Also check for a split frame containing an AlignFrame
2320 GSplitFrame sf = (GSplitFrame) frames[i];
2321 if (sf.getTopFrame() instanceof AlignFrame)
2323 avp.add((AlignFrame) sf.getTopFrame());
2325 if (sf.getBottomFrame() instanceof AlignFrame)
2327 avp.add((AlignFrame) sf.getBottomFrame());
2331 if (avp.size() == 0)
2335 AlignFrame afs[] = avp.toArray(new AlignFrame[avp.size()]);
2340 * Returns an array of any AppJmol frames in the Desktop (or null if none).
2344 public GStructureViewer[] getJmols()
2346 JInternalFrame[] frames = Desktop.desktop.getAllFrames();
2352 List<GStructureViewer> avp = new ArrayList<>();
2354 for (int i = frames.length - 1; i > -1; i--)
2356 if (frames[i] instanceof AppJmol)
2358 GStructureViewer af = (GStructureViewer) frames[i];
2362 if (avp.size() == 0)
2366 GStructureViewer afs[] = avp.toArray(new GStructureViewer[avp.size()]);
2371 * Add Groovy Support to Jalview
2374 public void groovyShell_actionPerformed()
2378 openGroovyConsole();
2379 } catch (Exception ex)
2381 Cache.log.error("Groovy Shell Creation failed.", ex);
2382 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
2384 MessageManager.getString("label.couldnt_create_groovy_shell"),
2385 MessageManager.getString("label.groovy_support_failed"),
2386 JvOptionPane.ERROR_MESSAGE);
2391 * Open the Groovy console
2393 void openGroovyConsole()
2395 if (groovyConsole == null)
2397 groovyConsole = new groovy.ui.Console();
2398 groovyConsole.setVariable("Jalview", this);
2399 groovyConsole.run();
2402 * We allow only one console at a time, so that AlignFrame menu option
2403 * 'Calculate | Run Groovy script' is unambiguous.
2404 * Disable 'Groovy Console', and enable 'Run script', when the console is
2405 * opened, and the reverse when it is closed
2407 Window window = (Window) groovyConsole.getFrame();
2408 window.addWindowListener(new WindowAdapter()
2411 public void windowClosed(WindowEvent e)
2414 * rebind CMD-Q from Groovy Console to Jalview Quit
2417 enableExecuteGroovy(false);
2423 * show Groovy console window (after close and reopen)
2425 ((Window) groovyConsole.getFrame()).setVisible(true);
2428 * if we got this far, enable 'Run Groovy' in AlignFrame menus
2429 * and disable opening a second console
2431 enableExecuteGroovy(true);
2435 * Bind Ctrl/Cmd-Q to Quit - for reset as Groovy Console takes over this binding
2438 protected void addQuitHandler()
2440 getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
2441 .put(KeyStroke.getKeyStroke(KeyEvent.VK_Q,
2442 jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx()),
2444 getRootPane().getActionMap().put("Quit", new AbstractAction()
2447 public void actionPerformed(ActionEvent e)
2455 * Enable or disable 'Run Groovy script' in AlignFrame calculate menus
2458 * true if Groovy console is open
2460 public void enableExecuteGroovy(boolean enabled)
2463 * disable opening a second Groovy console
2464 * (or re-enable when the console is closed)
2466 groovyShell.setEnabled(!enabled);
2468 AlignFrame[] alignFrames = getAlignFrames();
2469 if (alignFrames != null)
2471 for (AlignFrame af : alignFrames)
2473 af.setGroovyEnabled(enabled);
2479 * Progress bars managed by the IProgressIndicator method.
2481 private Hashtable<Long, JPanel> progressBars;
2483 private Hashtable<Long, IProgressIndicatorHandler> progressBarHandlers;
2488 * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
2491 public void setProgressBar(String message, long id)
2493 // Platform.timeCheck("Desktop " + message, Platform.TIME_MARK);
2495 if (progressBars == null)
2497 progressBars = new Hashtable<>();
2498 progressBarHandlers = new Hashtable<>();
2501 if (progressBars.get(Long.valueOf(id)) != null)
2503 JPanel panel = progressBars.remove(Long.valueOf(id));
2504 if (progressBarHandlers.contains(Long.valueOf(id)))
2506 progressBarHandlers.remove(Long.valueOf(id));
2508 removeProgressPanel(panel);
2512 progressBars.put(Long.valueOf(id), addProgressPanel(message));
2519 * @see jalview.gui.IProgressIndicator#registerHandler(long,
2520 * jalview.gui.IProgressIndicatorHandler)
2523 public void registerHandler(final long id,
2524 final IProgressIndicatorHandler handler)
2526 if (progressBarHandlers == null
2527 || !progressBars.containsKey(Long.valueOf(id)))
2529 throw new Error(MessageManager.getString(
2530 "error.call_setprogressbar_before_registering_handler"));
2532 progressBarHandlers.put(Long.valueOf(id), handler);
2533 final JPanel progressPanel = progressBars.get(Long.valueOf(id));
2534 if (handler.canCancel())
2536 JButton cancel = new JButton(
2537 MessageManager.getString("action.cancel"));
2538 final IProgressIndicator us = this;
2539 cancel.addActionListener(new ActionListener()
2543 public void actionPerformed(ActionEvent e)
2545 handler.cancelActivity(id);
2546 us.setProgressBar(MessageManager
2547 .formatMessage("label.cancelled_params", new Object[]
2548 { ((JLabel) progressPanel.getComponent(0)).getText() }),
2552 progressPanel.add(cancel, BorderLayout.EAST);
2558 * @return true if any progress bars are still active
2561 public boolean operationInProgress()
2563 if (progressBars != null && progressBars.size() > 0)
2571 * This will return the first AlignFrame holding the given viewport instance. It
2572 * will break if there are more than one AlignFrames viewing a particular av.
2575 * @return alignFrame for viewport
2577 public static AlignFrame getAlignFrameFor(AlignViewportI viewport)
2579 if (desktop != null)
2581 AlignmentPanel[] aps = getAlignmentPanels(
2582 viewport.getSequenceSetId());
2583 for (int panel = 0; aps != null && panel < aps.length; panel++)
2585 if (aps[panel] != null && aps[panel].av == viewport)
2587 return aps[panel].alignFrame;
2594 public VamsasApplication getVamsasApplication()
2596 // TODO: JAL-3311 remove remaining code from Jalview relating to VAMSAS
2602 * flag set if jalview GUI is being operated programmatically
2604 private boolean inBatchMode = false;
2607 * check if jalview GUI is being operated programmatically
2609 * @return inBatchMode
2611 public boolean isInBatchMode()
2617 * set flag if jalview GUI is being operated programmatically
2619 * @param inBatchMode
2621 public void setInBatchMode(boolean inBatchMode)
2623 this.inBatchMode = inBatchMode;
2626 public void startServiceDiscovery()
2628 startServiceDiscovery(false);
2631 public void startServiceDiscovery(boolean blocking)
2633 boolean alive = true;
2634 Thread t0 = null, t1 = null, t2 = null;
2635 // JAL-940 - JALVIEW 1 services are now being EOLed as of JABA 2.1 release
2638 // todo: changesupport handlers need to be transferred
2639 if (discoverer == null)
2641 discoverer = new jalview.ws.jws1.Discoverer();
2642 // register PCS handler for desktop.
2643 discoverer.addPropertyChangeListener(changeSupport);
2645 // JAL-940 - disabled JWS1 service configuration - always start discoverer
2646 // until we phase out completely
2647 (t0 = new Thread(discoverer)).start();
2650 if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
2652 t2 = jalview.ws.jws2.Jws2Discoverer.getDiscoverer()
2653 .startDiscoverer(changeSupport);
2657 // start slivka discovery
2658 t3 = new Thread(jalview.ws.slivkaws.SlivkaWSDiscoverer.getInstance());
2668 } catch (Exception e)
2671 alive = (t1 != null && t1.isAlive()) || (t2 != null && t2.isAlive())
2672 || (t3 != null && t3.isAlive())
2673 || (t0 != null && t0.isAlive());
2679 * called to check if the service discovery process completed successfully.
2683 protected void JalviewServicesChanged(PropertyChangeEvent evt)
2685 if (evt.getNewValue() == null || evt.getNewValue() instanceof Vector)
2687 final String ermsg = jalview.ws.jws2.Jws2Discoverer.getDiscoverer()
2688 .getErrorMessages();
2691 if (Cache.getDefault("SHOW_WSDISCOVERY_ERRORS", true))
2693 if (serviceChangedDialog == null)
2695 // only run if we aren't already displaying one of these.
2696 addDialogThread(serviceChangedDialog = new Runnable()
2703 * JalviewDialog jd =new JalviewDialog() {
2705 * @Override protected void cancelPressed() { // TODO
2706 * Auto-generated method stub
2708 * }@Override protected void okPressed() { // TODO
2709 * Auto-generated method stub
2711 * }@Override protected void raiseClosed() { // TODO
2712 * Auto-generated method stub
2714 * } }; jd.initDialogFrame(new
2715 * JLabel("<html><table width=\"450\"><tr><td>" + ermsg +
2716 * "<br/>It may be that you have invalid JABA URLs in your web service preferences,"
2717 * + " or mis-configured HTTP proxy settings.<br/>" +
2718 * "Check the <em>Connections</em> and <em>Web services</em> tab of the"
2720 * " Tools->Preferences dialog box to change them.</td></tr></table></html>"
2721 * ), true, true, "Web Service Configuration Problem", 450,
2724 * jd.waitForInput();
2726 JvOptionPane.showConfirmDialog(Desktop.desktop,
2727 new JLabel("<html><table width=\"450\"><tr><td>"
2728 + ermsg + "</td></tr></table>"
2729 + "<p>It may be that you have invalid JABA URLs<br/>in your web service preferences,"
2730 + "<br>or as a command-line argument, or mis-configured HTTP proxy settings.</p>"
2731 + "<p>Check the <em>Connections</em> and <em>Web services</em> tab<br/>of the"
2732 + " Tools->Preferences dialog box to change them.</p></html>"),
2733 "Web Service Configuration Problem",
2734 JvOptionPane.DEFAULT_OPTION,
2735 JvOptionPane.ERROR_MESSAGE);
2736 serviceChangedDialog = null;
2745 "Errors reported by JABA discovery service. Check web services preferences.\n"
2752 private Runnable serviceChangedDialog = null;
2755 * start a thread to open a URL in the configured browser. Pops up a warning
2756 * dialog to the user if there is an exception when calling out to the browser
2761 public static void showUrl(final String url)
2763 showUrl(url, Desktop.instance);
2767 * Like showUrl but allows progress handler to be specified
2771 * (null) or object implementing IProgressIndicator
2773 public static void showUrl(final String url,
2774 final IProgressIndicator progress)
2776 new Thread(new Runnable()
2783 if (progress != null)
2785 progress.setProgressBar(MessageManager
2786 .formatMessage("status.opening_params", new Object[]
2787 { url }), this.hashCode());
2789 jalview.util.BrowserLauncher.openURL(url);
2790 } catch (Exception ex)
2792 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
2794 .getString("label.web_browser_not_found_unix"),
2795 MessageManager.getString("label.web_browser_not_found"),
2796 JvOptionPane.WARNING_MESSAGE);
2798 ex.printStackTrace();
2800 if (progress != null)
2802 progress.setProgressBar(null, this.hashCode());
2808 public static WsParamSetManager wsparamManager = null;
2810 public static ParamManager getUserParameterStore()
2812 if (wsparamManager == null)
2814 wsparamManager = new WsParamSetManager();
2816 return wsparamManager;
2820 * static hyperlink handler proxy method for use by Jalview's internal windows
2824 public static void hyperlinkUpdate(HyperlinkEvent e)
2826 if (e.getEventType() == EventType.ACTIVATED)
2831 url = e.getURL().toString();
2832 Desktop.showUrl(url);
2833 } catch (Exception x)
2837 if (Cache.log != null)
2839 Cache.log.error("Couldn't handle string " + url + " as a URL.");
2844 "Couldn't handle string " + url + " as a URL.");
2847 // ignore any exceptions due to dud links.
2854 * single thread that handles display of dialogs to user.
2856 ExecutorService dialogExecutor = Executors.newSingleThreadExecutor();
2859 * flag indicating if dialogExecutor should try to acquire a permit
2861 private volatile boolean dialogPause = true;
2866 private java.util.concurrent.Semaphore block = new Semaphore(0);
2868 private static groovy.ui.Console groovyConsole;
2871 * add another dialog thread to the queue
2875 public void addDialogThread(final Runnable prompter)
2877 dialogExecutor.submit(new Runnable()
2887 } catch (InterruptedException x)
2891 if (instance == null)
2897 SwingUtilities.invokeAndWait(prompter);
2898 } catch (Exception q)
2900 Cache.log.warn("Unexpected Exception in dialog thread.", q);
2906 public void startDialogQueue()
2908 // set the flag so we don't pause waiting for another permit and semaphore
2909 // the current task to begin
2910 dialogPause = false;
2915 * Outputs an image of the desktop to file in EPS format, after prompting the
2916 * user for choice of Text or Lineart character rendering (unless a preference
2917 * has been set). The file name is generated as
2920 * Jalview_snapshot_nnnnn.eps where nnnnn is the current timestamp in milliseconds
2924 protected void snapShotWindow_actionPerformed(ActionEvent e)
2926 // currently the menu option to do this is not shown
2929 int width = getWidth();
2930 int height = getHeight();
2932 "Jalview_snapshot_" + System.currentTimeMillis() + ".eps");
2933 ImageWriterI writer = new ImageWriterI()
2936 public void exportImage(Graphics g) throws Exception
2939 Cache.log.info("Successfully written snapshot to file "
2940 + of.getAbsolutePath());
2943 String title = "View of desktop";
2944 ImageExporter exporter = new ImageExporter(writer, null, TYPE.EPS,
2946 exporter.doExport(of, this, width, height, title);
2950 * Explode the views in the given SplitFrame into separate SplitFrame windows.
2951 * This respects (remembers) any previous 'exploded geometry' i.e. the size and
2952 * location last time the view was expanded (if any). However it does not
2953 * remember the split pane divider location - this is set to match the
2954 * 'exploding' frame.
2958 public void explodeViews(SplitFrame sf)
2960 AlignFrame oldTopFrame = (AlignFrame) sf.getTopFrame();
2961 AlignFrame oldBottomFrame = (AlignFrame) sf.getBottomFrame();
2962 List<? extends AlignmentViewPanel> topPanels = oldTopFrame
2964 List<? extends AlignmentViewPanel> bottomPanels = oldBottomFrame
2966 int viewCount = topPanels.size();
2973 * Processing in reverse order works, forwards order leaves the first panels
2974 * not visible. I don't know why!
2976 for (int i = viewCount - 1; i >= 0; i--)
2979 * Make new top and bottom frames. These take over the respective
2980 * AlignmentPanel objects, including their AlignmentViewports, so the
2981 * cdna/protein relationships between the viewports is carried over to the
2984 * explodedGeometry holds the (x, y) position of the previously exploded
2985 * SplitFrame, and the (width, height) of the AlignFrame component
2987 AlignmentPanel topPanel = (AlignmentPanel) topPanels.get(i);
2988 AlignFrame newTopFrame = new AlignFrame(topPanel);
2989 newTopFrame.setSize(oldTopFrame.getSize());
2990 newTopFrame.setVisible(true);
2991 Rectangle geometry = ((AlignViewport) topPanel.getAlignViewport())
2992 .getExplodedGeometry();
2993 if (geometry != null)
2995 newTopFrame.setSize(geometry.getSize());
2998 AlignmentPanel bottomPanel = (AlignmentPanel) bottomPanels.get(i);
2999 AlignFrame newBottomFrame = new AlignFrame(bottomPanel);
3000 newBottomFrame.setSize(oldBottomFrame.getSize());
3001 newBottomFrame.setVisible(true);
3002 geometry = ((AlignViewport) bottomPanel.getAlignViewport())
3003 .getExplodedGeometry();
3004 if (geometry != null)
3006 newBottomFrame.setSize(geometry.getSize());
3009 topPanel.av.setGatherViewsHere(false);
3010 bottomPanel.av.setGatherViewsHere(false);
3011 JInternalFrame splitFrame = new SplitFrame(newTopFrame,
3013 if (geometry != null)
3015 splitFrame.setLocation(geometry.getLocation());
3017 Desktop.addInternalFrame(splitFrame, sf.getTitle(), -1, -1);
3021 * Clear references to the panels (now relocated in the new SplitFrames)
3022 * before closing the old SplitFrame.
3025 bottomPanels.clear();
3030 * Gather expanded split frames, sharing the same pairs of sequence set ids,
3031 * back into the given SplitFrame as additional views. Note that the gathered
3032 * frames may themselves have multiple views.
3036 public void gatherViews(GSplitFrame source)
3039 * special handling of explodedGeometry for a view within a SplitFrame: - it
3040 * holds the (x, y) position of the enclosing SplitFrame, and the (width,
3041 * height) of the AlignFrame component
3043 AlignFrame myTopFrame = (AlignFrame) source.getTopFrame();
3044 AlignFrame myBottomFrame = (AlignFrame) source.getBottomFrame();
3045 myTopFrame.viewport.setExplodedGeometry(new Rectangle(source.getX(),
3046 source.getY(), myTopFrame.getWidth(), myTopFrame.getHeight()));
3047 myBottomFrame.viewport
3048 .setExplodedGeometry(new Rectangle(source.getX(), source.getY(),
3049 myBottomFrame.getWidth(), myBottomFrame.getHeight()));
3050 myTopFrame.viewport.setGatherViewsHere(true);
3051 myBottomFrame.viewport.setGatherViewsHere(true);
3052 String topViewId = myTopFrame.viewport.getSequenceSetId();
3053 String bottomViewId = myBottomFrame.viewport.getSequenceSetId();
3055 JInternalFrame[] frames = desktop.getAllFrames();
3056 for (JInternalFrame frame : frames)
3058 if (frame instanceof SplitFrame && frame != source)
3060 SplitFrame sf = (SplitFrame) frame;
3061 AlignFrame topFrame = (AlignFrame) sf.getTopFrame();
3062 AlignFrame bottomFrame = (AlignFrame) sf.getBottomFrame();
3063 boolean gatherThis = false;
3064 for (int a = 0; a < topFrame.alignPanels.size(); a++)
3066 AlignmentPanel topPanel = topFrame.alignPanels.get(a);
3067 AlignmentPanel bottomPanel = bottomFrame.alignPanels.get(a);
3068 if (topViewId.equals(topPanel.av.getSequenceSetId())
3069 && bottomViewId.equals(bottomPanel.av.getSequenceSetId()))
3072 topPanel.av.setGatherViewsHere(false);
3073 bottomPanel.av.setGatherViewsHere(false);
3074 topPanel.av.setExplodedGeometry(
3075 new Rectangle(sf.getLocation(), topFrame.getSize()));
3076 bottomPanel.av.setExplodedGeometry(
3077 new Rectangle(sf.getLocation(), bottomFrame.getSize()));
3078 myTopFrame.addAlignmentPanel(topPanel, false);
3079 myBottomFrame.addAlignmentPanel(bottomPanel, false);
3085 topFrame.getAlignPanels().clear();
3086 bottomFrame.getAlignPanels().clear();
3093 * The dust settles...give focus to the tab we did this from.
3095 myTopFrame.setDisplayedView(myTopFrame.alignPanel);
3098 public static groovy.ui.Console getGroovyConsole()
3100 return groovyConsole;
3104 * handles the payload of a drag and drop event.
3106 * TODO refactor to desktop utilities class
3109 * - Data source strings extracted from the drop event
3111 * - protocol for each data source extracted from the drop event
3115 * - the payload from the drop event
3118 public static void transferFromDropTarget(List<Object> files,
3119 List<DataSourceType> protocols, DropTargetDropEvent evt,
3120 Transferable t) throws Exception
3123 // BH 2018 changed List<String> to List<Object> to allow for File from SwingJS
3125 // DataFlavor[] flavors = t.getTransferDataFlavors();
3126 // for (int i = 0; i < flavors.length; i++) {
3127 // if (flavors[i].isFlavorJavaFileListType()) {
3128 // evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
3129 // List<File> list = (List<File>) t.getTransferData(flavors[i]);
3130 // for (int j = 0; j < list.size(); j++) {
3131 // File file = (File) list.get(j);
3132 // byte[] data = getDroppedFileBytes(file);
3133 // fileName.setText(file.getName() + " - " + data.length + " " +
3134 // evt.getLocation());
3135 // JTextArea target = (JTextArea) ((DropTarget) evt.getSource()).getComponent();
3136 // target.setText(new String(data));
3138 // dtde.dropComplete(true);
3143 DataFlavor uriListFlavor = new DataFlavor(
3144 "text/uri-list;class=java.lang.String"), urlFlavour = null;
3147 urlFlavour = new DataFlavor(
3148 "application/x-java-url; class=java.net.URL");
3149 } catch (ClassNotFoundException cfe)
3151 Cache.log.debug("Couldn't instantiate the URL dataflavor.", cfe);
3154 if (urlFlavour != null && t.isDataFlavorSupported(urlFlavour))
3159 java.net.URL url = (URL) t.getTransferData(urlFlavour);
3160 // nb: java 8 osx bug https://bugs.openjdk.java.net/browse/JDK-8156099
3161 // means url may be null.
3164 protocols.add(DataSourceType.URL);
3165 files.add(url.toString());
3166 Cache.log.debug("Drop handled as URL dataflavor "
3167 + files.get(files.size() - 1));
3172 if (Platform.isAMacAndNotJS())
3175 "Please ignore plist error - occurs due to problem with java 8 on OSX");
3178 } catch (Throwable ex)
3180 Cache.log.debug("URL drop handler failed.", ex);
3183 if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
3185 // Works on Windows and MacOSX
3186 Cache.log.debug("Drop handled as javaFileListFlavor");
3187 for (Object file : (List) t
3188 .getTransferData(DataFlavor.javaFileListFlavor))
3191 protocols.add(DataSourceType.FILE);
3196 // Unix like behaviour
3197 boolean added = false;
3199 if (t.isDataFlavorSupported(uriListFlavor))
3201 Cache.log.debug("Drop handled as uriListFlavor");
3202 // This is used by Unix drag system
3203 data = (String) t.getTransferData(uriListFlavor);
3207 // fallback to text: workaround - on OSX where there's a JVM bug
3208 Cache.log.debug("standard URIListFlavor failed. Trying text");
3209 // try text fallback
3210 DataFlavor textDf = new DataFlavor(
3211 "text/plain;class=java.lang.String");
3212 if (t.isDataFlavorSupported(textDf))
3214 data = (String) t.getTransferData(textDf);
3217 Cache.log.debug("Plain text drop content returned "
3218 + (data == null ? "Null - failed" : data));
3223 while (protocols.size() < files.size())
3225 Cache.log.debug("Adding missing FILE protocol for "
3226 + files.get(protocols.size()));
3227 protocols.add(DataSourceType.FILE);
3229 for (java.util.StringTokenizer st = new java.util.StringTokenizer(
3230 data, "\r\n"); st.hasMoreTokens();)
3233 String s = st.nextToken();
3234 if (s.startsWith("#"))
3236 // the line is a comment (as per the RFC 2483)
3239 java.net.URI uri = new java.net.URI(s);
3240 if (uri.getScheme().toLowerCase().startsWith("http"))
3242 protocols.add(DataSourceType.URL);
3243 files.add(uri.toString());
3247 // otherwise preserve old behaviour: catch all for file objects
3248 java.io.File file = new java.io.File(uri);
3249 protocols.add(DataSourceType.FILE);
3250 files.add(file.toString());
3255 if (Cache.log.isDebugEnabled())
3257 if (data == null || !added)
3260 if (t.getTransferDataFlavors() != null
3261 && t.getTransferDataFlavors().length > 0)
3264 "Couldn't resolve drop data. Here are the supported flavors:");
3265 for (DataFlavor fl : t.getTransferDataFlavors())
3268 "Supported transfer dataflavor: " + fl.toString());
3269 Object df = t.getTransferData(fl);
3272 Cache.log.debug("Retrieves: " + df);
3276 Cache.log.debug("Retrieved nothing");
3282 Cache.log.debug("Couldn't resolve dataflavor for drop: "
3288 if (Platform.isWindowsAndNotJS())
3290 Cache.log.debug("Scanning dropped content for Windows Link Files");
3292 // resolve any .lnk files in the file drop
3293 for (int f = 0; f < files.size(); f++)
3295 String source = files.get(f).toString().toLowerCase();
3296 if (protocols.get(f).equals(DataSourceType.FILE)
3297 && (source.endsWith(".lnk") || source.endsWith(".url")
3298 || source.endsWith(".site")))
3302 Object obj = files.get(f);
3303 File lf = (obj instanceof File ? (File) obj
3304 : new File((String) obj));
3305 // process link file to get a URL
3306 Cache.log.debug("Found potential link file: " + lf);
3307 WindowsShortcut wscfile = new WindowsShortcut(lf);
3308 String fullname = wscfile.getRealFilename();
3309 protocols.set(f, FormatAdapter.checkProtocol(fullname));
3310 files.set(f, fullname);
3311 Cache.log.debug("Parsed real filename " + fullname
3312 + " to extract protocol: " + protocols.get(f));
3313 } catch (Exception ex)
3316 "Couldn't parse " + files.get(f) + " as a link file.",
3325 * Sets the Preferences property for experimental features to True or False
3326 * depending on the state of the controlling menu item
3329 protected void showExperimental_actionPerformed(boolean selected)
3331 Cache.setProperty(EXPERIMENTAL_FEATURES, Boolean.toString(selected));
3335 * Answers a (possibly empty) list of any structure viewer frames (currently for
3336 * either Jmol or Chimera) which are currently open. This may optionally be
3337 * restricted to viewers of a specified class, or viewers linked to a specified
3341 * if not null, only return viewers linked to this panel
3342 * @param structureViewerClass
3343 * if not null, only return viewers of this class
3346 public List<StructureViewerBase> getStructureViewers(
3347 AlignmentPanel apanel,
3348 Class<? extends StructureViewerBase> structureViewerClass)
3350 List<StructureViewerBase> result = new ArrayList<>();
3351 JInternalFrame[] frames = Desktop.instance.getAllFrames();
3353 for (JInternalFrame frame : frames)
3355 if (frame instanceof StructureViewerBase)
3357 if (structureViewerClass == null
3358 || structureViewerClass.isInstance(frame))
3361 || ((StructureViewerBase) frame).isLinkedWith(apanel))
3363 result.add((StructureViewerBase) frame);