2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
5 * This file is part of Jalview.
7 * Jalview is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation, either version 3
10 * of the License, or (at your option) any later version.
12 * Jalview is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19 * The Jalview Authors are detailed in the 'AUTHORS' file.
23 import java.awt.BorderLayout;
24 import java.awt.Color;
25 import java.awt.Dimension;
26 import java.awt.FontMetrics;
27 import java.awt.Graphics;
28 import java.awt.GridLayout;
29 import java.awt.Point;
30 import java.awt.Rectangle;
31 import java.awt.Toolkit;
32 import java.awt.Window;
33 import java.awt.datatransfer.Clipboard;
34 import java.awt.datatransfer.ClipboardOwner;
35 import java.awt.datatransfer.DataFlavor;
36 import java.awt.datatransfer.Transferable;
37 import java.awt.dnd.DnDConstants;
38 import java.awt.dnd.DropTargetDragEvent;
39 import java.awt.dnd.DropTargetDropEvent;
40 import java.awt.dnd.DropTargetEvent;
41 import java.awt.dnd.DropTargetListener;
42 import java.awt.event.ActionEvent;
43 import java.awt.event.ActionListener;
44 import java.awt.event.InputEvent;
45 import java.awt.event.KeyEvent;
46 import java.awt.event.MouseAdapter;
47 import java.awt.event.MouseEvent;
48 import java.awt.event.WindowAdapter;
49 import java.awt.event.WindowEvent;
50 import java.beans.PropertyChangeEvent;
51 import java.beans.PropertyChangeListener;
53 import java.io.FileWriter;
54 import java.io.IOException;
56 import java.util.ArrayList;
57 import java.util.HashMap;
58 import java.util.Hashtable;
59 import java.util.List;
60 import java.util.ListIterator;
61 import java.util.Vector;
62 import java.util.concurrent.ExecutorService;
63 import java.util.concurrent.Executors;
64 import java.util.concurrent.Semaphore;
66 import javax.swing.AbstractAction;
67 import javax.swing.Action;
68 import javax.swing.ActionMap;
69 import javax.swing.Box;
70 import javax.swing.BoxLayout;
71 import javax.swing.DefaultDesktopManager;
72 import javax.swing.DesktopManager;
73 import javax.swing.InputMap;
74 import javax.swing.JButton;
75 import javax.swing.JCheckBox;
76 import javax.swing.JComboBox;
77 import javax.swing.JComponent;
78 import javax.swing.JDesktopPane;
79 import javax.swing.JInternalFrame;
80 import javax.swing.JLabel;
81 import javax.swing.JMenuItem;
82 import javax.swing.JPanel;
83 import javax.swing.JPopupMenu;
84 import javax.swing.JProgressBar;
85 import javax.swing.JTextField;
86 import javax.swing.KeyStroke;
87 import javax.swing.SwingUtilities;
88 import javax.swing.event.HyperlinkEvent;
89 import javax.swing.event.HyperlinkEvent.EventType;
90 import javax.swing.event.InternalFrameAdapter;
91 import javax.swing.event.InternalFrameEvent;
93 import org.stackoverflowusers.file.WindowsShortcut;
95 import jalview.api.AlignViewportI;
96 import jalview.api.AlignmentViewPanel;
97 import jalview.bin.Cache;
98 import jalview.bin.Jalview;
99 import jalview.gui.ImageExporter.ImageWriterI;
100 import jalview.io.BackupFiles;
101 import jalview.io.DataSourceType;
102 import jalview.io.FileFormat;
103 import jalview.io.FileFormatException;
104 import jalview.io.FileFormatI;
105 import jalview.io.FileFormats;
106 import jalview.io.FileLoader;
107 import jalview.io.FormatAdapter;
108 import jalview.io.IdentifyFile;
109 import jalview.io.JalviewFileChooser;
110 import jalview.io.JalviewFileView;
111 import jalview.jbgui.GSplitFrame;
112 import jalview.jbgui.GStructureViewer;
113 import jalview.project.Jalview2XML;
114 import jalview.structure.StructureSelectionManager;
115 import jalview.urls.IdOrgSettings;
116 import jalview.util.BrowserLauncher;
117 import jalview.util.ImageMaker.TYPE;
118 import jalview.util.MessageManager;
119 import jalview.util.Platform;
120 import jalview.util.ShortcutKeyMaskExWrapper;
121 import jalview.util.UrlConstants;
122 import jalview.viewmodel.AlignmentViewport;
123 import jalview.ws.params.ParamManager;
124 import jalview.ws.utils.UrlDownloadClient;
131 * @version $Revision: 1.155 $
133 public class Desktop extends jalview.jbgui.GDesktop
134 implements DropTargetListener, ClipboardOwner, IProgressIndicator,
135 jalview.api.StructureSelectionManagerProvider
137 private static final String CITATION = "<br><br>Development managed by The Barton Group, University of Dundee, Scotland, UK.<br>"
138 + "<br><br>For help, see the FAQ at <a href=\"http://www.jalview.org/faq\">www.jalview.org/faq</a> and/or join the jalview-discuss@jalview.org mailing list"
139 + "<br><br>If you use Jalview, please cite:"
140 + "<br>Waterhouse, A.M., Procter, J.B., Martin, D.M.A, Clamp, M. and Barton, G. J. (2009)"
141 + "<br>Jalview Version 2 - a multiple sequence alignment editor and analysis workbench"
142 + "<br>Bioinformatics doi: 10.1093/bioinformatics/btp033";
144 private static final String DEFAULT_AUTHORS = "The Jalview Authors (See AUTHORS file for current list)";
146 private static int DEFAULT_MIN_WIDTH = 300;
148 private static int DEFAULT_MIN_HEIGHT = 250;
150 private static int ALIGN_FRAME_DEFAULT_MIN_WIDTH = 600;
152 private static int ALIGN_FRAME_DEFAULT_MIN_HEIGHT = 70;
154 private static final String EXPERIMENTAL_FEATURES = "EXPERIMENTAL_FEATURES";
156 protected static final String CONFIRM_KEYBOARD_QUIT = "CONFIRM_KEYBOARD_QUIT";
158 public static HashMap<String, FileWriter> savingFiles = new HashMap<String, FileWriter>();
160 private JalviewChangeSupport changeSupport = new JalviewChangeSupport();
163 * news reader - null if it was never started.
165 private BlogReader jvnews = null;
167 private File projectFile;
171 * @see jalview.gui.JalviewChangeSupport#addJalviewPropertyChangeListener(java.beans.PropertyChangeListener)
173 public void addJalviewPropertyChangeListener(
174 PropertyChangeListener listener)
176 changeSupport.addJalviewPropertyChangeListener(listener);
180 * @param propertyName
182 * @see jalview.gui.JalviewChangeSupport#addJalviewPropertyChangeListener(java.lang.String,
183 * java.beans.PropertyChangeListener)
185 public void addJalviewPropertyChangeListener(String propertyName,
186 PropertyChangeListener listener)
188 changeSupport.addJalviewPropertyChangeListener(propertyName, listener);
192 * @param propertyName
194 * @see jalview.gui.JalviewChangeSupport#removeJalviewPropertyChangeListener(java.lang.String,
195 * java.beans.PropertyChangeListener)
197 public void removeJalviewPropertyChangeListener(String propertyName,
198 PropertyChangeListener listener)
200 changeSupport.removeJalviewPropertyChangeListener(propertyName,
204 /** Singleton Desktop instance */
205 public static Desktop instance;
208 * BH TEMPORARY ONLY -- should use ApplicationSingleton
212 public static Object getInstance()
217 public static MyDesktopPane desktop;
219 public static MyDesktopPane getDesktop()
221 // BH 2018 could use currentThread() here as a reference to a
222 // Hashtable<Thread, MyDesktopPane> in JavaScript
226 static int openFrameCount = 0;
228 static final int xOffset = 30;
230 static final int yOffset = 30;
232 public static jalview.ws.jws1.Discoverer discoverer;
234 public static Object[] jalviewClipboard;
236 public static boolean internalCopy = false;
238 static int fileLoadingCount = 0;
240 class MyDesktopManager implements DesktopManager
243 private DesktopManager delegate;
245 public MyDesktopManager(DesktopManager delegate)
247 this.delegate = delegate;
251 public void activateFrame(JInternalFrame f)
255 delegate.activateFrame(f);
256 } catch (NullPointerException npe)
258 Point p = getMousePosition();
259 instance.showPasteMenu(p.x, p.y);
264 public void beginDraggingFrame(JComponent f)
266 delegate.beginDraggingFrame(f);
270 public void beginResizingFrame(JComponent f, int direction)
272 delegate.beginResizingFrame(f, direction);
276 public void closeFrame(JInternalFrame f)
278 delegate.closeFrame(f);
282 public void deactivateFrame(JInternalFrame f)
284 delegate.deactivateFrame(f);
288 public void deiconifyFrame(JInternalFrame f)
290 delegate.deiconifyFrame(f);
294 public void dragFrame(JComponent f, int newX, int newY)
300 delegate.dragFrame(f, newX, newY);
304 public void endDraggingFrame(JComponent f)
306 delegate.endDraggingFrame(f);
311 public void endResizingFrame(JComponent f)
313 delegate.endResizingFrame(f);
318 public void iconifyFrame(JInternalFrame f)
320 delegate.iconifyFrame(f);
324 public void maximizeFrame(JInternalFrame f)
326 delegate.maximizeFrame(f);
330 public void minimizeFrame(JInternalFrame f)
332 delegate.minimizeFrame(f);
336 public void openFrame(JInternalFrame f)
338 delegate.openFrame(f);
342 public void resizeFrame(JComponent f, int newX, int newY, int newWidth,
349 delegate.resizeFrame(f, newX, newY, newWidth, newHeight);
353 public void setBoundsForFrame(JComponent f, int newX, int newY,
354 int newWidth, int newHeight)
356 delegate.setBoundsForFrame(f, newX, newY, newWidth, newHeight);
359 // All other methods, simply delegate
364 * Creates a new Desktop object.
370 * A note to implementors. It is ESSENTIAL that any activities that might
371 * block are spawned off as threads rather than waited for during this
376 doConfigureStructurePrefs();
377 setTitle("Jalview " + Cache.getProperty("VERSION"));
379 if (!Platform.isAMac())
381 // this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
385 this.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
391 APQHandlers.setAPQHandlers(this);
392 } catch (Throwable t)
394 System.out.println("Error setting APQHandlers: " + t.toString());
395 // t.printStackTrace();
398 addWindowListener(new WindowAdapter()
402 public void windowClosing(WindowEvent ev)
408 boolean selmemusage = Cache.getDefault("SHOW_MEMUSAGE",
411 boolean showjconsole = Cache.getDefault("SHOW_JAVA_CONSOLE",
413 desktop = new MyDesktopPane(selmemusage);
415 showMemusage.setSelected(selmemusage);
416 desktop.setBackground(Color.white);
418 getContentPane().setLayout(new BorderLayout());
419 // alternate config - have scrollbars - see notes in JAL-153
420 // JScrollPane sp = new JScrollPane();
421 // sp.getViewport().setView(desktop);
422 // getContentPane().add(sp, BorderLayout.CENTER);
424 // BH 2018 - just an experiment to try unclipped JInternalFrames.
427 getRootPane().putClientProperty("swingjs.overflow.hidden", "false");
430 getContentPane().add(desktop, BorderLayout.CENTER);
431 desktop.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
433 // This line prevents Windows Look&Feel resizing all new windows to maximum
434 // if previous window was maximised
435 desktop.setDesktopManager(new MyDesktopManager(
436 (Platform.isWindowsAndNotJS() ? new DefaultDesktopManager()
437 : Platform.isAMacAndNotJS()
438 ? new AquaInternalFrameManager(
439 desktop.getDesktopManager())
440 : desktop.getDesktopManager())));
442 Rectangle dims = getLastKnownDimensions("");
449 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
450 int xPos = Math.max(5, (screenSize.width - 900) / 2);
451 int yPos = Math.max(5, (screenSize.height - 650) / 2);
452 setBounds(xPos, yPos, 900, 650);
455 if (!Platform.isJS())
462 jconsole = new Console(this, showjconsole);
463 jconsole.setHeader(Cache.getVersionDetailsForConsole());
464 showConsole(showjconsole);
466 showNews.setVisible(false);
468 experimentalFeatures.setSelected(showExperimental());
470 getIdentifiersOrgData();
474 // Spawn a thread that shows the splashscreen
476 SwingUtilities.invokeLater(new Runnable()
481 new SplashScreen(true);
485 // Thread off a new instance of the file chooser - this reduces the time
487 // takes to open it later on.
488 new Thread(new Runnable()
493 Cache.log.debug("Filechooser init thread started.");
494 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
495 JalviewFileChooser.forRead(Cache.getProperty("LAST_DIRECTORY"),
497 Cache.log.debug("Filechooser init thread finished.");
500 // Add the service change listener
501 changeSupport.addJalviewPropertyChangeListener("services",
502 new PropertyChangeListener()
506 public void propertyChange(PropertyChangeEvent evt)
508 Cache.log.debug("Firing service changed event for "
509 + evt.getNewValue());
510 JalviewServicesChanged(evt);
515 this.setDropTarget(new java.awt.dnd.DropTarget(desktop, this));
517 this.addWindowListener(new WindowAdapter()
520 public void windowClosing(WindowEvent evt)
527 this.addMouseListener(ma = new MouseAdapter()
530 public void mousePressed(MouseEvent evt)
532 if (evt.isPopupTrigger()) // Mac
534 showPasteMenu(evt.getX(), evt.getY());
539 public void mouseReleased(MouseEvent evt)
541 if (evt.isPopupTrigger()) // Windows
543 showPasteMenu(evt.getX(), evt.getY());
547 desktop.addMouseListener(ma);
552 * Answers true if user preferences to enable experimental features is True
557 public boolean showExperimental()
559 String experimental = Cache.getDefault(EXPERIMENTAL_FEATURES,
560 Boolean.FALSE.toString());
561 return Boolean.valueOf(experimental).booleanValue();
564 public void doConfigureStructurePrefs()
566 // configure services
567 StructureSelectionManager ssm = StructureSelectionManager
568 .getStructureSelectionManager(this);
569 if (Cache.getDefault(Preferences.ADD_SS_ANN, true))
571 ssm.setAddTempFacAnnot(Cache
572 .getDefault(Preferences.ADD_TEMPFACT_ANN, true));
573 ssm.setProcessSecondaryStructure(Cache
574 .getDefault(Preferences.STRUCT_FROM_PDB, true));
575 ssm.setSecStructServices(
576 Cache.getDefault(Preferences.USE_RNAVIEW, true));
580 ssm.setAddTempFacAnnot(false);
581 ssm.setProcessSecondaryStructure(false);
582 ssm.setSecStructServices(false);
586 public void checkForNews()
588 final Desktop me = this;
589 // Thread off the news reader, in case there are connection problems.
590 new Thread(new Runnable()
595 Cache.log.debug("Starting news thread.");
596 jvnews = new BlogReader(me);
597 showNews.setVisible(true);
598 Cache.log.debug("Completed news thread.");
603 public void getIdentifiersOrgData()
605 // Thread off the identifiers fetcher
606 new Thread(new Runnable()
611 Cache.log.debug("Downloading data from identifiers.org");
614 UrlDownloadClient.download(IdOrgSettings.getUrl(),
615 IdOrgSettings.getDownloadLocation());
616 } catch (IOException e)
618 Cache.log.debug("Exception downloading identifiers.org data"
627 protected void showNews_actionPerformed(ActionEvent e)
629 showNews(showNews.isSelected());
632 void showNews(boolean visible)
634 Cache.log.debug((visible ? "Showing" : "Hiding") + " news.");
635 showNews.setSelected(visible);
636 if (visible && !jvnews.isVisible())
638 new Thread(new Runnable()
643 long now = System.currentTimeMillis();
644 Desktop.instance.setProgressBar(
645 MessageManager.getString("status.refreshing_news"), now);
646 jvnews.refreshNews();
647 Desktop.instance.setProgressBar(null, now);
655 * recover the last known dimensions for a jalview window
658 * - empty string is desktop, all other windows have unique prefix
659 * @return null or last known dimensions scaled to current geometry (if last
660 * window geom was known)
662 Rectangle getLastKnownDimensions(String windowName)
664 // TODO: lock aspect ratio for scaling desktop Bug #0058199
665 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
666 String x = Cache.getProperty(windowName + "SCREEN_X");
667 String y = Cache.getProperty(windowName + "SCREEN_Y");
669 .getProperty(windowName + "SCREEN_WIDTH");
670 String height = Cache
671 .getProperty(windowName + "SCREEN_HEIGHT");
672 if ((x != null) && (y != null) && (width != null) && (height != null))
674 int ix = Integer.parseInt(x), iy = Integer.parseInt(y),
675 iw = Integer.parseInt(width), ih = Integer.parseInt(height);
676 if (Cache.getProperty("SCREENGEOMETRY_WIDTH") != null)
678 // attempt #1 - try to cope with change in screen geometry - this
679 // version doesn't preserve original jv aspect ratio.
680 // take ratio of current screen size vs original screen size.
681 double sw = ((1f * screenSize.width) / (1f * Integer.parseInt(
682 Cache.getProperty("SCREENGEOMETRY_WIDTH"))));
683 double sh = ((1f * screenSize.height) / (1f * Integer.parseInt(
684 Cache.getProperty("SCREENGEOMETRY_HEIGHT"))));
685 // rescale the bounds depending upon the current screen geometry.
686 ix = (int) (ix * sw);
687 iw = (int) (iw * sw);
688 iy = (int) (iy * sh);
689 ih = (int) (ih * sh);
690 while (ix >= screenSize.width)
693 "Window geometry location recall error: shifting horizontal to within screenbounds.");
694 ix -= screenSize.width;
696 while (iy >= screenSize.height)
699 "Window geometry location recall error: shifting vertical to within screenbounds.");
700 iy -= screenSize.height;
703 "Got last known dimensions for " + windowName + ": x:" + ix
704 + " y:" + iy + " width:" + iw + " height:" + ih);
706 // return dimensions for new instance
707 return new Rectangle(ix, iy, iw, ih);
712 void showPasteMenu(int x, int y)
714 JPopupMenu popup = new JPopupMenu();
715 JMenuItem item = new JMenuItem(
716 MessageManager.getString("label.paste_new_window"));
717 item.addActionListener(new ActionListener()
720 public void actionPerformed(ActionEvent evt)
727 popup.show(this, x, y);
734 Clipboard c = Toolkit.getDefaultToolkit().getSystemClipboard();
735 Transferable contents = c.getContents(this);
737 if (contents != null)
739 String file = (String) contents
740 .getTransferData(DataFlavor.stringFlavor);
742 FileFormatI format = new IdentifyFile().identify(file,
743 DataSourceType.PASTE);
745 new FileLoader().LoadFile(file, DataSourceType.PASTE, format);
748 } catch (Exception ex)
751 "Unable to paste alignment from system clipboard:\n" + ex);
756 * Adds and opens the given frame to the desktop
767 public static synchronized void addInternalFrame(
768 final JInternalFrame frame, String title, int w, int h)
770 addInternalFrame(frame, title, true, w, h, true, false);
774 * Add an internal frame to the Jalview desktop
781 * When true, display frame immediately, otherwise, caller must call
782 * setVisible themselves.
788 public static synchronized void addInternalFrame(
789 final JInternalFrame frame, String title, boolean makeVisible,
792 addInternalFrame(frame, title, makeVisible, w, h, true, false);
796 * Add an internal frame to the Jalview desktop and make it visible
809 public static synchronized void addInternalFrame(
810 final JInternalFrame frame, String title, int w, int h,
813 addInternalFrame(frame, title, true, w, h, resizable, false);
817 * Add an internal frame to the Jalview desktop
824 * When true, display frame immediately, otherwise, caller must call
825 * setVisible themselves.
832 * @param ignoreMinSize
833 * Do not set the default minimum size for frame
835 public static synchronized void addInternalFrame(
836 final JInternalFrame frame, String title, boolean makeVisible,
837 int w, int h, boolean resizable, boolean ignoreMinSize)
840 // TODO: allow callers to determine X and Y position of frame (eg. via
842 // TODO: consider fixing method to update entries in the window submenu with
843 // the current window title
845 frame.setTitle(title);
846 if (frame.getWidth() < 1 || frame.getHeight() < 1)
850 // THIS IS A PUBLIC STATIC METHOD, SO IT MAY BE CALLED EVEN IN
851 // A HEADLESS STATE WHEN NO DESKTOP EXISTS. MUST RETURN
852 // IF JALVIEW IS RUNNING HEADLESS
853 // ///////////////////////////////////////////////
854 if (instance == null || (System.getProperty("java.awt.headless") != null
855 && System.getProperty("java.awt.headless").equals("true")))
864 frame.setMinimumSize(
865 new Dimension(DEFAULT_MIN_WIDTH, DEFAULT_MIN_HEIGHT));
867 // Set default dimension for Alignment Frame window.
868 // The Alignment Frame window could be added from a number of places,
870 // I did this here in order not to miss out on any Alignment frame.
871 if (frame instanceof AlignFrame)
873 frame.setMinimumSize(new Dimension(ALIGN_FRAME_DEFAULT_MIN_WIDTH,
874 ALIGN_FRAME_DEFAULT_MIN_HEIGHT));
878 frame.setVisible(makeVisible);
879 frame.setClosable(true);
880 frame.setResizable(resizable);
881 frame.setMaximizable(resizable);
882 frame.setIconifiable(resizable);
883 frame.setOpaque(Platform.isJS());
885 if (frame.getX() < 1 && frame.getY() < 1)
887 frame.setLocation(xOffset * openFrameCount,
888 yOffset * ((openFrameCount - 1) % 10) + yOffset);
892 * add an entry for the new frame in the Window menu
893 * (and remove it when the frame is closed)
895 final JMenuItem menuItem = new JMenuItem(title);
896 frame.addInternalFrameListener(new InternalFrameAdapter()
899 public void internalFrameActivated(InternalFrameEvent evt)
901 JInternalFrame itf = desktop.getSelectedFrame();
904 if (itf instanceof AlignFrame)
906 Jalview.setCurrentAlignFrame((AlignFrame) itf);
913 public void internalFrameClosed(InternalFrameEvent evt)
915 PaintRefresher.RemoveComponent(frame);
918 * defensive check to prevent frames being
919 * added half off the window
921 if (openFrameCount > 0)
927 * ensure no reference to alignFrame retained by menu item listener
929 if (menuItem.getActionListeners().length > 0)
931 menuItem.removeActionListener(menuItem.getActionListeners()[0]);
933 windowMenu.remove(menuItem);
937 menuItem.addActionListener(new ActionListener()
940 public void actionPerformed(ActionEvent e)
944 frame.setSelected(true);
945 frame.setIcon(false);
946 } catch (java.beans.PropertyVetoException ex)
948 // System.err.println(ex.toString());
953 setKeyBindings(frame);
957 windowMenu.add(menuItem);
962 frame.setSelected(true);
963 frame.requestFocus();
964 } catch (java.beans.PropertyVetoException ve)
966 } catch (java.lang.ClassCastException cex)
969 "Squashed a possible GUI implementation error. If you can recreate this, please look at http://issues.jalview.org/browse/JAL-869",
975 * Add key bindings to a JInternalFrame so that Ctrl-W and Cmd-W will close the
980 private static void setKeyBindings(JInternalFrame frame)
982 @SuppressWarnings("serial")
983 final Action closeAction = new AbstractAction()
986 public void actionPerformed(ActionEvent e)
993 * set up key bindings for Ctrl-W and Cmd-W, with the same (Close) action
995 KeyStroke ctrlWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
996 InputEvent.CTRL_DOWN_MASK);
997 KeyStroke cmdWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
998 ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx());
1000 InputMap inputMap = frame
1001 .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
1002 String ctrlW = ctrlWKey.toString();
1003 inputMap.put(ctrlWKey, ctrlW);
1004 inputMap.put(cmdWKey, ctrlW);
1006 ActionMap actionMap = frame.getActionMap();
1007 actionMap.put(ctrlW, closeAction);
1011 public void lostOwnership(Clipboard clipboard, Transferable contents)
1015 Desktop.jalviewClipboard = null;
1018 internalCopy = false;
1022 public void dragEnter(DropTargetDragEvent evt)
1027 public void dragExit(DropTargetEvent evt)
1032 public void dragOver(DropTargetDragEvent evt)
1037 public void dropActionChanged(DropTargetDragEvent evt)
1048 public void drop(DropTargetDropEvent evt)
1050 boolean success = true;
1051 // JAL-1552 - acceptDrop required before getTransferable call for
1052 // Java's Transferable for native dnd
1053 evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
1054 Transferable t = evt.getTransferable();
1055 List<Object> files = new ArrayList<>();
1056 List<DataSourceType> protocols = new ArrayList<>();
1060 Desktop.transferFromDropTarget(files, protocols, evt, t);
1061 } catch (Exception e)
1063 e.printStackTrace();
1071 for (int i = 0; i < files.size(); i++)
1073 // BH 2018 File or String
1074 Object file = files.get(i);
1075 String fileName = file.toString();
1076 DataSourceType protocol = (protocols == null)
1077 ? DataSourceType.FILE
1079 FileFormatI format = null;
1081 if (fileName.endsWith(".jar"))
1083 format = FileFormat.Jalview;
1088 format = new IdentifyFile().identify(file, protocol);
1090 if (file instanceof File)
1092 Platform.cacheFileData((File) file);
1094 new FileLoader().LoadFile(null, file, protocol, format);
1097 } catch (Exception ex)
1102 evt.dropComplete(success); // need this to ensure input focus is properly
1103 // transfered to any new windows created
1113 public void inputLocalFileMenuItem_actionPerformed(AlignViewport viewport)
1115 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
1116 JalviewFileChooser chooser = JalviewFileChooser
1117 .forRead(Cache.getProperty("LAST_DIRECTORY"), fileFormat, BackupFiles.getEnabled());
1119 chooser.setFileView(new JalviewFileView());
1120 chooser.setDialogTitle(
1121 MessageManager.getString("label.open_local_file"));
1122 chooser.setToolTipText(MessageManager.getString("action.open"));
1124 chooser.setResponseHandler(0, new Runnable()
1129 File selectedFile = chooser.getSelectedFile();
1130 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1132 FileFormatI format = chooser.getSelectedFormat();
1135 * Call IdentifyFile to verify the file contains what its extension implies.
1136 * Skip this step for dynamically added file formats, because
1137 * IdentifyFile does not know how to recognise them.
1139 if (FileFormats.getInstance().isIdentifiable(format))
1143 format = new IdentifyFile().identify(selectedFile,
1144 DataSourceType.FILE);
1145 } catch (FileFormatException e)
1147 // format = null; //??
1151 new FileLoader().LoadFile(viewport, selectedFile,
1152 DataSourceType.FILE, format);
1155 chooser.showOpenDialog(this);
1159 * Shows a dialog for input of a URL at which to retrieve alignment data
1164 public void inputURLMenuItem_actionPerformed(AlignViewport viewport)
1166 // This construct allows us to have a wider textfield
1168 JLabel label = new JLabel(
1169 MessageManager.getString("label.input_file_url"));
1171 JPanel panel = new JPanel(new GridLayout(2, 1));
1175 * the URL to fetch is
1176 * Java: an editable combobox with history
1177 * JS: (pending JAL-3038) a plain text field
1180 String urlBase = "http://www.";
1181 if (Platform.isJS())
1183 history = new JTextField(urlBase, 35);
1192 JComboBox<String> asCombo = new JComboBox<>();
1193 asCombo.setPreferredSize(new Dimension(400, 20));
1194 asCombo.setEditable(true);
1195 asCombo.addItem(urlBase);
1196 String historyItems = Cache.getProperty("RECENT_URL");
1197 if (historyItems != null)
1199 for (String token : historyItems.split("\\t"))
1201 asCombo.addItem(token);
1208 Object[] options = new Object[] { MessageManager.getString("action.ok"),
1209 MessageManager.getString("action.cancel") };
1210 Runnable action = new Runnable()
1215 @SuppressWarnings("unchecked")
1216 String url = (history instanceof JTextField
1217 ? ((JTextField) history).getText()
1218 : ((JComboBox<String>) history).getSelectedItem()
1221 if (url.toLowerCase().endsWith(".jar"))
1223 if (viewport != null)
1225 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1226 FileFormat.Jalview);
1230 new FileLoader().LoadFile(url, DataSourceType.URL,
1231 FileFormat.Jalview);
1236 FileFormatI format = null;
1239 format = new IdentifyFile().identify(url, DataSourceType.URL);
1240 } catch (FileFormatException e)
1242 // TODO revise error handling, distinguish between
1243 // URL not found and response not valid
1248 String msg = MessageManager
1249 .formatMessage("label.couldnt_locate", url);
1250 JvOptionPane.showInternalMessageDialog(Desktop.desktop, msg,
1251 MessageManager.getString("label.url_not_found"),
1252 JvOptionPane.WARNING_MESSAGE);
1257 if (viewport != null)
1259 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1264 new FileLoader().LoadFile(url, DataSourceType.URL, format);
1269 String dialogOption = MessageManager
1270 .getString("label.input_alignment_from_url");
1271 JvOptionPane.newOptionDialog(desktop).setResponseHandler(0, action)
1272 .showInternalDialog(panel, dialogOption,
1273 JvOptionPane.YES_NO_CANCEL_OPTION,
1274 JvOptionPane.PLAIN_MESSAGE, null, options,
1275 MessageManager.getString("action.ok"));
1279 * Opens the CutAndPaste window for the user to paste an alignment in to
1282 * - if not null, the pasted alignment is added to the current
1283 * alignment; if null, to a new alignment window
1286 public void inputTextboxMenuItem_actionPerformed(
1287 AlignmentViewPanel viewPanel)
1289 CutAndPasteTransfer cap = new CutAndPasteTransfer();
1290 cap.setForInput(viewPanel);
1291 Desktop.addInternalFrame(cap,
1292 MessageManager.getString("label.cut_paste_alignmen_file"), true,
1302 Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
1303 Cache.setProperty("SCREENGEOMETRY_WIDTH",
1305 Cache.setProperty("SCREENGEOMETRY_HEIGHT",
1306 screen.height + "");
1307 storeLastKnownDimensions("", new Rectangle(getBounds().x, getBounds().y,
1308 getWidth(), getHeight()));
1310 if (jconsole != null)
1312 storeLastKnownDimensions("JAVA_CONSOLE_", jconsole.getBounds());
1313 jconsole.stopConsole();
1317 storeLastKnownDimensions("JALVIEW_RSS_WINDOW_", jvnews.getBounds());
1320 if (dialogExecutor != null)
1322 dialogExecutor.shutdownNow();
1324 closeAll_actionPerformed(null);
1326 if (groovyConsole != null)
1328 // suppress a possible repeat prompt to save script
1329 groovyConsole.setDirty(false);
1330 groovyConsole.exit();
1335 private void storeLastKnownDimensions(String string, Rectangle jc)
1337 Cache.log.debug("Storing last known dimensions for "
1338 + string + ": x:" + jc.x + " y:" + jc.y + " width:" + jc.width
1339 + " height:" + jc.height);
1341 Cache.setProperty(string + "SCREEN_X", jc.x + "");
1342 Cache.setProperty(string + "SCREEN_Y", jc.y + "");
1343 Cache.setProperty(string + "SCREEN_WIDTH", jc.width + "");
1344 Cache.setProperty(string + "SCREEN_HEIGHT", jc.height + "");
1354 public void aboutMenuItem_actionPerformed(ActionEvent e)
1356 new Thread(new Runnable()
1361 new SplashScreen(false);
1367 * Returns the html text for the About screen, including any available version
1368 * number, build details, author details and citation reference, but without
1369 * the enclosing {@code html} tags
1373 public String getAboutMessage()
1375 StringBuilder message = new StringBuilder(1024);
1376 message.append("<h1><strong>Version: ")
1377 .append(Cache.getProperty("VERSION")).append("</strong></h1>")
1378 .append("<strong>Built: <em>")
1379 .append(Cache.getDefault("BUILD_DATE", "unknown"))
1380 .append("</em> from ").append(Cache.getBuildDetailsForSplash())
1381 .append("</strong>");
1383 String latestVersion = Cache.getDefault("LATEST_VERSION", "Checking");
1384 if (latestVersion.equals("Checking"))
1386 // JBP removed this message for 2.11: May be reinstated in future version
1387 // message.append("<br>...Checking latest version...</br>");
1389 else if (!latestVersion.equals(Cache.getProperty("VERSION")))
1391 boolean red = false;
1392 if (Cache.getProperty("VERSION").toLowerCase()
1393 .indexOf("automated build") == -1)
1396 // Displayed when code version and jnlp version do not match and code
1397 // version is not a development build
1398 message.append("<div style=\"color: #FF0000;font-style: bold;\">");
1401 message.append("<br>!! Version ")
1402 .append(Cache.getDefault("LATEST_VERSION", "..Checking.."))
1403 .append(" is available for download from ")
1404 .append(Cache.getDefault("www.jalview.org",
1405 "http://www.jalview.org"))
1409 message.append("</div>");
1412 message.append("<br>Authors: ");
1413 message.append(Cache.getDefault("AUTHORFNAMES", DEFAULT_AUTHORS));
1414 message.append(CITATION);
1416 return message.toString();
1420 * Action on requesting Help documentation
1423 public void documentationMenuItem_actionPerformed()
1427 if (Platform.isJS())
1429 BrowserLauncher.openURL("http://www.jalview.org/help.html");
1438 Help.showHelpWindow();
1440 } catch (Exception ex)
1442 System.err.println("Error opening help: " + ex.getMessage());
1447 public void closeAll_actionPerformed(ActionEvent e)
1449 // TODO show a progress bar while closing?
1450 JInternalFrame[] frames = desktop.getAllFrames();
1451 for (int i = 0; i < frames.length; i++)
1455 frames[i].setClosed(true);
1456 } catch (java.beans.PropertyVetoException ex)
1460 Jalview.setCurrentAlignFrame(null);
1461 System.out.println("ALL CLOSED");
1464 * reset state of singleton objects as appropriate (clear down session state
1465 * when all windows are closed)
1467 StructureSelectionManager ssm = StructureSelectionManager
1468 .getStructureSelectionManager(this);
1476 public void raiseRelated_actionPerformed(ActionEvent e)
1478 reorderAssociatedWindows(false, false);
1482 public void minimizeAssociated_actionPerformed(ActionEvent e)
1484 reorderAssociatedWindows(true, false);
1487 void closeAssociatedWindows()
1489 reorderAssociatedWindows(false, true);
1495 * @seejalview.jbgui.GDesktop#garbageCollect_actionPerformed(java.awt.event.
1499 protected void garbageCollect_actionPerformed(ActionEvent e)
1501 // We simply collect the garbage
1502 Cache.log.debug("Collecting garbage...");
1504 Cache.log.debug("Finished garbage collection.");
1511 * jalview.jbgui.GDesktop#showMemusage_actionPerformed(java.awt.event.ActionEvent
1515 protected void showMemusage_actionPerformed(ActionEvent e)
1517 desktop.showMemoryUsage(showMemusage.isSelected());
1524 * jalview.jbgui.GDesktop#showConsole_actionPerformed(java.awt.event.ActionEvent
1528 protected void showConsole_actionPerformed(ActionEvent e)
1530 showConsole(showConsole.isSelected());
1533 Console jconsole = null;
1536 * control whether the java console is visible or not
1540 void showConsole(boolean selected)
1542 // TODO: decide if we should update properties file
1543 if (jconsole != null) // BH 2018
1545 showConsole.setSelected(selected);
1546 Cache.setProperty("SHOW_JAVA_CONSOLE",
1547 Boolean.valueOf(selected).toString());
1548 jconsole.setVisible(selected);
1552 void reorderAssociatedWindows(boolean minimize, boolean close)
1554 JInternalFrame[] frames = desktop.getAllFrames();
1555 if (frames == null || frames.length < 1)
1560 AlignmentViewport source = null, target = null;
1561 if (frames[0] instanceof AlignFrame)
1563 source = ((AlignFrame) frames[0]).getCurrentView();
1565 else if (frames[0] instanceof TreePanel)
1567 source = ((TreePanel) frames[0]).getViewPort();
1569 else if (frames[0] instanceof PCAPanel)
1571 source = ((PCAPanel) frames[0]).av;
1573 else if (frames[0].getContentPane() instanceof PairwiseAlignPanel)
1575 source = ((PairwiseAlignPanel) frames[0].getContentPane()).av;
1580 for (int i = 0; i < frames.length; i++)
1583 if (frames[i] == null)
1587 if (frames[i] instanceof AlignFrame)
1589 target = ((AlignFrame) frames[i]).getCurrentView();
1591 else if (frames[i] instanceof TreePanel)
1593 target = ((TreePanel) frames[i]).getViewPort();
1595 else if (frames[i] instanceof PCAPanel)
1597 target = ((PCAPanel) frames[i]).av;
1599 else if (frames[i].getContentPane() instanceof PairwiseAlignPanel)
1601 target = ((PairwiseAlignPanel) frames[i].getContentPane()).av;
1604 if (source == target)
1610 frames[i].setClosed(true);
1614 frames[i].setIcon(minimize);
1617 frames[i].toFront();
1621 } catch (java.beans.PropertyVetoException ex)
1636 protected void preferences_actionPerformed(ActionEvent e)
1642 * Prompts the user to choose a file and then saves the Jalview state as a
1643 * Jalview project file
1646 public void saveState_actionPerformed()
1648 saveState_actionPerformed(false);
1651 public void saveState_actionPerformed(boolean saveAs)
1653 java.io.File projectFile = getProjectFile();
1654 // autoSave indicates we already have a file and don't need to ask
1655 boolean autoSave = projectFile != null && !saveAs
1656 && BackupFiles.getEnabled();
1658 // System.out.println("autoSave="+autoSave+", projectFile='"+projectFile+"',
1659 // saveAs="+saveAs+", Backups
1660 // "+(BackupFiles.getEnabled()?"enabled":"disabled"));
1662 boolean approveSave = false;
1665 JalviewFileChooser chooser = new JalviewFileChooser("jvp",
1668 chooser.setFileView(new JalviewFileView());
1669 chooser.setDialogTitle(MessageManager.getString("label.save_state"));
1671 int value = chooser.showSaveDialog(this);
1673 if (value == JalviewFileChooser.APPROVE_OPTION)
1675 projectFile = chooser.getSelectedFile();
1676 setProjectFile(projectFile);
1681 if (approveSave || autoSave)
1683 final Desktop me = this;
1684 final java.io.File chosenFile = projectFile;
1685 new Thread(new Runnable()
1690 // TODO: refactor to Jalview desktop session controller action.
1691 setProgressBar(MessageManager.formatMessage(
1692 "label.saving_jalview_project", new Object[]
1693 { chosenFile.getName() }), chosenFile.hashCode());
1694 Cache.setProperty("LAST_DIRECTORY",
1695 chosenFile.getParent());
1696 // TODO catch and handle errors for savestate
1697 // TODO prevent user from messing with the Desktop whilst we're saving
1700 boolean doBackup = BackupFiles.getEnabled();
1701 BackupFiles backupfiles = doBackup ? new BackupFiles(chosenFile) : null;
1703 new Jalview2XML().saveState(doBackup ? backupfiles.getTempFile() : chosenFile);
1707 backupfiles.setWriteSuccess(true);
1708 backupfiles.rollBackupsAndRenameTempFile();
1710 } catch (OutOfMemoryError oom)
1712 new OOMWarning("Whilst saving current state to "
1713 + chosenFile.getName(), oom);
1714 } catch (Exception ex)
1716 Cache.log.error("Problems whilst trying to save to "
1717 + chosenFile.getName(), ex);
1718 JvOptionPane.showMessageDialog(me,
1719 MessageManager.formatMessage(
1720 "label.error_whilst_saving_current_state_to",
1722 { chosenFile.getName() }),
1723 MessageManager.getString("label.couldnt_save_project"),
1724 JvOptionPane.WARNING_MESSAGE);
1726 setProgressBar(null, chosenFile.hashCode());
1733 public void saveAsState_actionPerformed(ActionEvent e)
1735 saveState_actionPerformed(true);
1738 private void setProjectFile(File choice)
1740 this.projectFile = choice;
1743 public File getProjectFile()
1745 return this.projectFile;
1749 * Shows a file chooser dialog and tries to read in the selected file as a
1753 public void loadState_actionPerformed()
1755 final String[] suffix = new String[] { "jvp", "jar" };
1756 final String[] desc = new String[] { "Jalview Project",
1757 "Jalview Project (old)" };
1758 JalviewFileChooser chooser = new JalviewFileChooser(
1759 Cache.getProperty("LAST_DIRECTORY"), suffix, desc,
1760 "Jalview Project", true, BackupFiles.getEnabled()); // last two booleans: allFiles,
1762 chooser.setFileView(new JalviewFileView());
1763 chooser.setDialogTitle(MessageManager.getString("label.restore_state"));
1764 chooser.setResponseHandler(0, new Runnable()
1769 File selectedFile = chooser.getSelectedFile();
1770 setProjectFile(selectedFile);
1771 String choice = selectedFile.getAbsolutePath();
1772 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1773 new Thread(new Runnable()
1780 new Jalview2XML().loadJalviewAlign(selectedFile);
1781 } catch (OutOfMemoryError oom)
1783 new OOMWarning("Whilst loading project from " + choice, oom);
1784 } catch (Exception ex)
1787 "Problems whilst loading project from " + choice, ex);
1788 JvOptionPane.showMessageDialog(Desktop.desktop,
1789 MessageManager.formatMessage(
1790 "label.error_whilst_loading_project_from",
1793 MessageManager.getString("label.couldnt_load_project"),
1794 JvOptionPane.WARNING_MESSAGE);
1801 chooser.showOpenDialog(this);
1805 public void inputSequence_actionPerformed(ActionEvent e)
1807 new SequenceFetcher(this);
1810 JPanel progressPanel;
1812 ArrayList<JPanel> fileLoadingPanels = new ArrayList<>();
1814 public void startLoading(final Object fileName)
1816 if (fileLoadingCount == 0)
1818 fileLoadingPanels.add(addProgressPanel(MessageManager
1819 .formatMessage("label.loading_file", new Object[]
1825 private JPanel addProgressPanel(String string)
1827 if (progressPanel == null)
1829 progressPanel = new JPanel(new GridLayout(1, 1));
1830 totalProgressCount = 0;
1831 instance.getContentPane().add(progressPanel, BorderLayout.SOUTH);
1833 JPanel thisprogress = new JPanel(new BorderLayout(10, 5));
1834 JProgressBar progressBar = new JProgressBar();
1835 progressBar.setIndeterminate(true);
1837 thisprogress.add(new JLabel(string), BorderLayout.WEST);
1839 thisprogress.add(progressBar, BorderLayout.CENTER);
1840 progressPanel.add(thisprogress);
1841 ((GridLayout) progressPanel.getLayout()).setRows(
1842 ((GridLayout) progressPanel.getLayout()).getRows() + 1);
1843 ++totalProgressCount;
1844 instance.validate();
1845 return thisprogress;
1848 int totalProgressCount = 0;
1850 private void removeProgressPanel(JPanel progbar)
1852 if (progressPanel != null)
1854 synchronized (progressPanel)
1856 progressPanel.remove(progbar);
1857 GridLayout gl = (GridLayout) progressPanel.getLayout();
1858 gl.setRows(gl.getRows() - 1);
1859 if (--totalProgressCount < 1)
1861 this.getContentPane().remove(progressPanel);
1862 progressPanel = null;
1869 public void stopLoading()
1872 if (fileLoadingCount < 1)
1874 while (fileLoadingPanels.size() > 0)
1876 removeProgressPanel(fileLoadingPanels.remove(0));
1878 fileLoadingPanels.clear();
1879 fileLoadingCount = 0;
1884 public static int getViewCount(String alignmentId)
1886 AlignmentViewport[] aps = getViewports(alignmentId);
1887 return (aps == null) ? 0 : aps.length;
1892 * @param alignmentId
1893 * - if null, all sets are returned
1894 * @return all AlignmentPanels concerning the alignmentId sequence set
1896 public static AlignmentPanel[] getAlignmentPanels(String alignmentId)
1898 if (Desktop.desktop == null)
1900 // no frames created and in headless mode
1901 // TODO: verify that frames are recoverable when in headless mode
1904 List<AlignmentPanel> aps = new ArrayList<>();
1905 AlignFrame[] frames = getAlignFrames();
1910 for (AlignFrame af : frames)
1912 for (AlignmentPanel ap : af.alignPanels)
1914 if (alignmentId == null
1915 || alignmentId.equals(ap.av.getSequenceSetId()))
1921 if (aps.size() == 0)
1925 AlignmentPanel[] vap = aps.toArray(new AlignmentPanel[aps.size()]);
1930 * get all the viewports on an alignment.
1932 * @param sequenceSetId
1933 * unique alignment id (may be null - all viewports returned in that
1935 * @return all viewports on the alignment bound to sequenceSetId
1937 public static AlignmentViewport[] getViewports(String sequenceSetId)
1939 List<AlignmentViewport> viewp = new ArrayList<>();
1940 if (desktop != null)
1942 AlignFrame[] frames = Desktop.getAlignFrames();
1944 for (AlignFrame afr : frames)
1946 if (sequenceSetId == null || afr.getViewport().getSequenceSetId()
1947 .equals(sequenceSetId))
1949 if (afr.alignPanels != null)
1951 for (AlignmentPanel ap : afr.alignPanels)
1953 if (sequenceSetId == null
1954 || sequenceSetId.equals(ap.av.getSequenceSetId()))
1962 viewp.add(afr.getViewport());
1966 if (viewp.size() > 0)
1968 return viewp.toArray(new AlignmentViewport[viewp.size()]);
1975 * Explode the views in the given frame into separate AlignFrame
1979 public static void explodeViews(AlignFrame af)
1981 int size = af.alignPanels.size();
1987 // FIXME: ideally should use UI interface API
1988 FeatureSettings viewFeatureSettings = (af.featureSettings != null
1989 && af.featureSettings.isOpen())
1990 ? af.featureSettings
1992 Rectangle fsBounds = af.getFeatureSettingsGeometry();
1993 for (int i = 0; i < size; i++)
1995 AlignmentPanel ap = af.alignPanels.get(i);
1997 AlignFrame newaf = new AlignFrame(ap);
1999 // transfer reference for existing feature settings to new alignFrame
2000 if (ap == af.alignPanel)
2002 if (viewFeatureSettings != null && viewFeatureSettings.fr.ap == ap)
2004 newaf.featureSettings = viewFeatureSettings;
2006 newaf.setFeatureSettingsGeometry(fsBounds);
2010 * Restore the view's last exploded frame geometry if known. Multiple
2011 * views from one exploded frame share and restore the same (frame)
2012 * position and size.
2014 Rectangle geometry = ap.av.getExplodedGeometry();
2015 if (geometry != null)
2017 newaf.setBounds(geometry);
2020 ap.av.setGatherViewsHere(false);
2022 addInternalFrame(newaf, af.getTitle(), AlignFrame.DEFAULT_WIDTH,
2023 AlignFrame.DEFAULT_HEIGHT);
2024 // and materialise a new feature settings dialog instance for the new alignframe
2025 // (closes the old as if 'OK' was pressed)
2026 if (ap == af.alignPanel && newaf.featureSettings != null
2027 && newaf.featureSettings.isOpen()
2028 && af.alignPanel.getAlignViewport().isShowSequenceFeatures())
2030 newaf.showFeatureSettingsUI();
2034 af.featureSettings = null;
2035 af.alignPanels.clear();
2036 af.closeMenuItem_actionPerformed(true);
2041 * Gather expanded views (separate AlignFrame's) with the same sequence set
2042 * identifier back in to this frame as additional views, and close the expanded
2043 * views. Note the expanded frames may themselves have multiple views. We take
2048 public void gatherViews(AlignFrame source)
2050 source.viewport.setGatherViewsHere(true);
2051 source.viewport.setExplodedGeometry(source.getBounds());
2052 JInternalFrame[] frames = desktop.getAllFrames();
2053 String viewId = source.viewport.getSequenceSetId();
2054 for (int t = 0; t < frames.length; t++)
2056 if (frames[t] instanceof AlignFrame && frames[t] != source)
2058 AlignFrame af = (AlignFrame) frames[t];
2059 boolean gatherThis = false;
2060 for (int a = 0; a < af.alignPanels.size(); a++)
2062 AlignmentPanel ap = af.alignPanels.get(a);
2063 if (viewId.equals(ap.av.getSequenceSetId()))
2066 ap.av.setGatherViewsHere(false);
2067 ap.av.setExplodedGeometry(af.getBounds());
2068 source.addAlignmentPanel(ap, false);
2074 if (af.featureSettings != null && af.featureSettings.isOpen())
2076 if (source.featureSettings == null)
2078 // preserve the feature settings geometry for this frame
2079 source.featureSettings = af.featureSettings;
2080 source.setFeatureSettingsGeometry(
2081 af.getFeatureSettingsGeometry());
2085 // close it and forget
2086 af.featureSettings.close();
2089 af.alignPanels.clear();
2090 af.closeMenuItem_actionPerformed(true);
2095 // refresh the feature setting UI for the source frame if it exists
2096 if (source.featureSettings != null
2097 && source.featureSettings.isOpen())
2099 source.showFeatureSettingsUI();
2103 public JInternalFrame[] getAllFrames()
2105 return desktop.getAllFrames();
2109 * Checks the given url to see if it gives a response indicating that the user
2110 * should be informed of a new questionnaire.
2114 public void checkForQuestionnaire(String url)
2116 UserQuestionnaireCheck jvq = new UserQuestionnaireCheck(url);
2117 // javax.swing.SwingUtilities.invokeLater(jvq);
2118 new Thread(jvq).start();
2121 public void checkURLLinks()
2123 // Thread off the URL link checker
2124 addDialogThread(new Runnable()
2129 if (Cache.getDefault("CHECKURLLINKS", true))
2131 // check what the actual links are - if it's just the default don't
2132 // bother with the warning
2133 List<String> links = Preferences.sequenceUrlLinks
2136 // only need to check links if there is one with a
2137 // SEQUENCE_ID which is not the default EMBL_EBI link
2138 ListIterator<String> li = links.listIterator();
2139 boolean check = false;
2140 List<JLabel> urls = new ArrayList<>();
2141 while (li.hasNext())
2143 String link = li.next();
2144 if (link.contains(jalview.util.UrlConstants.SEQUENCE_ID)
2145 && !UrlConstants.isDefaultString(link))
2148 int barPos = link.indexOf("|");
2149 String urlMsg = barPos == -1 ? link
2150 : link.substring(0, barPos) + ": "
2151 + link.substring(barPos + 1);
2152 urls.add(new JLabel(urlMsg));
2160 // ask user to check in case URL links use old style tokens
2161 // ($SEQUENCE_ID$ for sequence id _or_ accession id)
2162 JPanel msgPanel = new JPanel();
2163 msgPanel.setLayout(new BoxLayout(msgPanel, BoxLayout.PAGE_AXIS));
2164 msgPanel.add(Box.createVerticalGlue());
2165 JLabel msg = new JLabel(MessageManager
2166 .getString("label.SEQUENCE_ID_for_DB_ACCESSION1"));
2167 JLabel msg2 = new JLabel(MessageManager
2168 .getString("label.SEQUENCE_ID_for_DB_ACCESSION2"));
2170 for (JLabel url : urls)
2176 final JCheckBox jcb = new JCheckBox(
2177 MessageManager.getString("label.do_not_display_again"));
2178 jcb.addActionListener(new ActionListener()
2181 public void actionPerformed(ActionEvent e)
2183 // update Cache settings for "don't show this again"
2184 boolean showWarningAgain = !jcb.isSelected();
2185 Cache.setProperty("CHECKURLLINKS",
2186 Boolean.valueOf(showWarningAgain).toString());
2191 JvOptionPane.showMessageDialog(Desktop.desktop, msgPanel,
2193 .getString("label.SEQUENCE_ID_no_longer_used"),
2194 JvOptionPane.WARNING_MESSAGE);
2201 * Proxy class for JDesktopPane which optionally displays the current memory
2202 * usage and highlights the desktop area with a red bar if free memory runs low.
2206 public class MyDesktopPane extends JDesktopPane
2209 private static final float ONE_MB = 1048576f;
2211 boolean showMemoryUsage = false;
2215 java.text.NumberFormat df;
2217 float maxMemory, allocatedMemory, freeMemory, totalFreeMemory,
2220 public MyDesktopPane(boolean showMemoryUsage)
2222 showMemoryUsage(showMemoryUsage);
2225 public void showMemoryUsage(boolean showMemory)
2227 this.showMemoryUsage = showMemory;
2230 Thread worker = new Thread(this);
2236 public boolean isShowMemoryUsage()
2238 return showMemoryUsage;
2244 df = java.text.NumberFormat.getNumberInstance();
2245 df.setMaximumFractionDigits(2);
2246 runtime = Runtime.getRuntime();
2248 while (showMemoryUsage)
2252 maxMemory = runtime.maxMemory() / ONE_MB;
2253 allocatedMemory = runtime.totalMemory() / ONE_MB;
2254 freeMemory = runtime.freeMemory() / ONE_MB;
2255 totalFreeMemory = freeMemory + (maxMemory - allocatedMemory);
2257 percentUsage = (totalFreeMemory / maxMemory) * 100;
2259 // if (percentUsage < 20)
2261 // border1 = BorderFactory.createMatteBorder(12, 12, 12, 12,
2263 // instance.set.setBorder(border1);
2266 // sleep after showing usage
2268 } catch (Exception ex)
2270 ex.printStackTrace();
2276 public void paintComponent(Graphics g)
2278 if (showMemoryUsage && g != null && df != null)
2280 if (percentUsage < 20)
2282 g.setColor(Color.red);
2284 FontMetrics fm = g.getFontMetrics();
2287 g.drawString(MessageManager.formatMessage("label.memory_stats",
2289 { df.format(totalFreeMemory), df.format(maxMemory),
2290 df.format(percentUsage) }),
2291 10, getHeight() - fm.getHeight());
2298 * Accessor method to quickly get all the AlignmentFrames loaded.
2300 * @return an array of AlignFrame, or null if none found
2302 public static AlignFrame[] getAlignFrames()
2304 if (Jalview.isHeadlessMode())
2306 // Desktop.desktop is null in headless mode
2307 return new AlignFrame[] { Jalview.currentAlignFrame };
2310 JInternalFrame[] frames = Desktop.desktop.getAllFrames();
2316 List<AlignFrame> avp = new ArrayList<>();
2318 for (int i = frames.length - 1; i > -1; i--)
2320 if (frames[i] instanceof AlignFrame)
2322 avp.add((AlignFrame) frames[i]);
2324 else if (frames[i] instanceof SplitFrame)
2327 * Also check for a split frame containing an AlignFrame
2329 GSplitFrame sf = (GSplitFrame) frames[i];
2330 if (sf.getTopFrame() instanceof AlignFrame)
2332 avp.add((AlignFrame) sf.getTopFrame());
2334 if (sf.getBottomFrame() instanceof AlignFrame)
2336 avp.add((AlignFrame) sf.getBottomFrame());
2340 if (avp.size() == 0)
2344 AlignFrame afs[] = avp.toArray(new AlignFrame[avp.size()]);
2349 * Returns an array of any AppJmol frames in the Desktop (or null if none).
2353 public GStructureViewer[] getJmols()
2355 JInternalFrame[] frames = Desktop.desktop.getAllFrames();
2361 List<GStructureViewer> avp = new ArrayList<>();
2363 for (int i = frames.length - 1; i > -1; i--)
2365 if (frames[i] instanceof AppJmol)
2367 GStructureViewer af = (GStructureViewer) frames[i];
2371 if (avp.size() == 0)
2375 GStructureViewer afs[] = avp.toArray(new GStructureViewer[avp.size()]);
2380 * Add Groovy Support to Jalview
2383 public void groovyShell_actionPerformed()
2387 openGroovyConsole();
2388 } catch (Exception ex)
2390 Cache.log.error("Groovy Shell Creation failed.", ex);
2391 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
2393 MessageManager.getString("label.couldnt_create_groovy_shell"),
2394 MessageManager.getString("label.groovy_support_failed"),
2395 JvOptionPane.ERROR_MESSAGE);
2400 * Open the Groovy console
2402 void openGroovyConsole()
2404 if (groovyConsole == null)
2406 groovyConsole = new groovy.ui.Console();
2407 groovyConsole.setVariable("Jalview", this);
2408 groovyConsole.run();
2411 * We allow only one console at a time, so that AlignFrame menu option
2412 * 'Calculate | Run Groovy script' is unambiguous.
2413 * Disable 'Groovy Console', and enable 'Run script', when the console is
2414 * opened, and the reverse when it is closed
2416 Window window = (Window) groovyConsole.getFrame();
2417 window.addWindowListener(new WindowAdapter()
2420 public void windowClosed(WindowEvent e)
2423 * rebind CMD-Q from Groovy Console to Jalview Quit
2426 enableExecuteGroovy(false);
2432 * show Groovy console window (after close and reopen)
2434 ((Window) groovyConsole.getFrame()).setVisible(true);
2437 * if we got this far, enable 'Run Groovy' in AlignFrame menus
2438 * and disable opening a second console
2440 enableExecuteGroovy(true);
2444 * Bind Ctrl/Cmd-Q to Quit - for reset as Groovy Console takes over this binding
2447 protected void addQuitHandler()
2449 getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
2450 .put(KeyStroke.getKeyStroke(KeyEvent.VK_Q,
2451 jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx()),
2453 getRootPane().getActionMap().put("Quit", new AbstractAction()
2456 public void actionPerformed(ActionEvent e)
2464 * Enable or disable 'Run Groovy script' in AlignFrame calculate menus
2467 * true if Groovy console is open
2469 public void enableExecuteGroovy(boolean enabled)
2472 * disable opening a second Groovy console
2473 * (or re-enable when the console is closed)
2475 groovyShell.setEnabled(!enabled);
2477 AlignFrame[] alignFrames = getAlignFrames();
2478 if (alignFrames != null)
2480 for (AlignFrame af : alignFrames)
2482 af.setGroovyEnabled(enabled);
2488 * Progress bars managed by the IProgressIndicator method.
2490 private Hashtable<Long, JPanel> progressBars;
2492 private Hashtable<Long, IProgressIndicatorHandler> progressBarHandlers;
2497 * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
2500 public void setProgressBar(String message, long id)
2502 // Platform.timeCheck("Desktop " + message, Platform.TIME_MARK);
2504 if (progressBars == null)
2506 progressBars = new Hashtable<>();
2507 progressBarHandlers = new Hashtable<>();
2510 if (progressBars.get(Long.valueOf(id)) != null)
2512 JPanel panel = progressBars.remove(Long.valueOf(id));
2513 if (progressBarHandlers.contains(Long.valueOf(id)))
2515 progressBarHandlers.remove(Long.valueOf(id));
2517 removeProgressPanel(panel);
2521 progressBars.put(Long.valueOf(id), addProgressPanel(message));
2528 * @see jalview.gui.IProgressIndicator#registerHandler(long,
2529 * jalview.gui.IProgressIndicatorHandler)
2532 public void registerHandler(final long id,
2533 final IProgressIndicatorHandler handler)
2535 if (progressBarHandlers == null
2536 || !progressBars.containsKey(Long.valueOf(id)))
2538 throw new Error(MessageManager.getString(
2539 "error.call_setprogressbar_before_registering_handler"));
2541 progressBarHandlers.put(Long.valueOf(id), handler);
2542 final JPanel progressPanel = progressBars.get(Long.valueOf(id));
2543 if (handler.canCancel())
2545 JButton cancel = new JButton(
2546 MessageManager.getString("action.cancel"));
2547 final IProgressIndicator us = this;
2548 cancel.addActionListener(new ActionListener()
2552 public void actionPerformed(ActionEvent e)
2554 handler.cancelActivity(id);
2555 us.setProgressBar(MessageManager
2556 .formatMessage("label.cancelled_params", new Object[]
2557 { ((JLabel) progressPanel.getComponent(0)).getText() }),
2561 progressPanel.add(cancel, BorderLayout.EAST);
2567 * @return true if any progress bars are still active
2570 public boolean operationInProgress()
2572 if (progressBars != null && progressBars.size() > 0)
2580 * This will return the first AlignFrame holding the given viewport instance. It
2581 * will break if there are more than one AlignFrames viewing a particular av.
2584 * @return alignFrame for viewport
2586 public static AlignFrame getAlignFrameFor(AlignViewportI viewport)
2588 if (desktop != null)
2590 AlignmentPanel[] aps = getAlignmentPanels(
2591 viewport.getSequenceSetId());
2592 for (int panel = 0; aps != null && panel < aps.length; panel++)
2594 if (aps[panel] != null && aps[panel].av == viewport)
2596 return aps[panel].alignFrame;
2603 public VamsasApplication getVamsasApplication()
2605 // TODO: JAL-3311 remove remaining code from Jalview relating to VAMSAS
2611 * flag set if jalview GUI is being operated programmatically
2613 private boolean inBatchMode = false;
2616 * check if jalview GUI is being operated programmatically
2618 * @return inBatchMode
2620 public boolean isInBatchMode()
2626 * set flag if jalview GUI is being operated programmatically
2628 * @param inBatchMode
2630 public void setInBatchMode(boolean inBatchMode)
2632 this.inBatchMode = inBatchMode;
2635 public void startServiceDiscovery()
2637 startServiceDiscovery(false);
2640 public void startServiceDiscovery(boolean blocking)
2642 boolean alive = true;
2643 Thread t0 = null, t1 = null, t2 = null;
2644 // JAL-940 - JALVIEW 1 services are now being EOLed as of JABA 2.1 release
2647 // todo: changesupport handlers need to be transferred
2648 if (discoverer == null)
2650 discoverer = new jalview.ws.jws1.Discoverer();
2651 // register PCS handler for desktop.
2652 discoverer.addPropertyChangeListener(changeSupport);
2654 // JAL-940 - disabled JWS1 service configuration - always start discoverer
2655 // until we phase out completely
2656 (t0 = new Thread(discoverer)).start();
2659 if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
2661 t2 = jalview.ws.jws2.Jws2Discoverer.getDiscoverer()
2662 .startDiscoverer(changeSupport);
2666 // TODO: do rest service discovery
2675 } catch (Exception e)
2678 alive = (t1 != null && t1.isAlive()) || (t2 != null && t2.isAlive())
2679 || (t3 != null && t3.isAlive())
2680 || (t0 != null && t0.isAlive());
2686 * called to check if the service discovery process completed successfully.
2690 protected void JalviewServicesChanged(PropertyChangeEvent evt)
2692 if (evt.getNewValue() == null || evt.getNewValue() instanceof Vector)
2694 final String ermsg = jalview.ws.jws2.Jws2Discoverer.getDiscoverer()
2695 .getErrorMessages();
2698 if (Cache.getDefault("SHOW_WSDISCOVERY_ERRORS", true))
2700 if (serviceChangedDialog == null)
2702 // only run if we aren't already displaying one of these.
2703 addDialogThread(serviceChangedDialog = new Runnable()
2710 * JalviewDialog jd =new JalviewDialog() {
2712 * @Override protected void cancelPressed() { // TODO
2713 * Auto-generated method stub
2715 * }@Override protected void okPressed() { // TODO
2716 * Auto-generated method stub
2718 * }@Override protected void raiseClosed() { // TODO
2719 * Auto-generated method stub
2721 * } }; jd.initDialogFrame(new
2722 * JLabel("<html><table width=\"450\"><tr><td>" + ermsg +
2723 * "<br/>It may be that you have invalid JABA URLs in your web service preferences,"
2724 * + " or mis-configured HTTP proxy settings.<br/>" +
2725 * "Check the <em>Connections</em> and <em>Web services</em> tab of the"
2727 * " Tools->Preferences dialog box to change them.</td></tr></table></html>"
2728 * ), true, true, "Web Service Configuration Problem", 450,
2731 * jd.waitForInput();
2733 JvOptionPane.showConfirmDialog(Desktop.desktop,
2734 new JLabel("<html><table width=\"450\"><tr><td>"
2735 + ermsg + "</td></tr></table>"
2736 + "<p>It may be that you have invalid JABA URLs<br/>in your web service preferences,"
2737 + "<br>or as a command-line argument, or mis-configured HTTP proxy settings.</p>"
2738 + "<p>Check the <em>Connections</em> and <em>Web services</em> tab<br/>of the"
2739 + " Tools->Preferences dialog box to change them.</p></html>"),
2740 "Web Service Configuration Problem",
2741 JvOptionPane.DEFAULT_OPTION,
2742 JvOptionPane.ERROR_MESSAGE);
2743 serviceChangedDialog = null;
2752 "Errors reported by JABA discovery service. Check web services preferences.\n"
2759 private Runnable serviceChangedDialog = null;
2762 * start a thread to open a URL in the configured browser. Pops up a warning
2763 * dialog to the user if there is an exception when calling out to the browser
2768 public static void showUrl(final String url)
2770 showUrl(url, Desktop.instance);
2774 * Like showUrl but allows progress handler to be specified
2778 * (null) or object implementing IProgressIndicator
2780 public static void showUrl(final String url,
2781 final IProgressIndicator progress)
2783 new Thread(new Runnable()
2790 if (progress != null)
2792 progress.setProgressBar(MessageManager
2793 .formatMessage("status.opening_params", new Object[]
2794 { url }), this.hashCode());
2796 jalview.util.BrowserLauncher.openURL(url);
2797 } catch (Exception ex)
2799 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
2801 .getString("label.web_browser_not_found_unix"),
2802 MessageManager.getString("label.web_browser_not_found"),
2803 JvOptionPane.WARNING_MESSAGE);
2805 ex.printStackTrace();
2807 if (progress != null)
2809 progress.setProgressBar(null, this.hashCode());
2815 public static WsParamSetManager wsparamManager = null;
2817 public static ParamManager getUserParameterStore()
2819 if (wsparamManager == null)
2821 wsparamManager = new WsParamSetManager();
2823 return wsparamManager;
2827 * static hyperlink handler proxy method for use by Jalview's internal windows
2831 public static void hyperlinkUpdate(HyperlinkEvent e)
2833 if (e.getEventType() == EventType.ACTIVATED)
2838 url = e.getURL().toString();
2839 Desktop.showUrl(url);
2840 } catch (Exception x)
2844 if (Cache.log != null)
2846 Cache.log.error("Couldn't handle string " + url + " as a URL.");
2851 "Couldn't handle string " + url + " as a URL.");
2854 // ignore any exceptions due to dud links.
2861 * single thread that handles display of dialogs to user.
2863 ExecutorService dialogExecutor = Executors.newSingleThreadExecutor();
2866 * flag indicating if dialogExecutor should try to acquire a permit
2868 private volatile boolean dialogPause = true;
2873 private java.util.concurrent.Semaphore block = new Semaphore(0);
2875 private static groovy.ui.Console groovyConsole;
2878 * add another dialog thread to the queue
2882 public void addDialogThread(final Runnable prompter)
2884 dialogExecutor.submit(new Runnable()
2894 } catch (InterruptedException x)
2898 if (instance == null)
2904 SwingUtilities.invokeAndWait(prompter);
2905 } catch (Exception q)
2907 Cache.log.warn("Unexpected Exception in dialog thread.", q);
2913 public void startDialogQueue()
2915 // set the flag so we don't pause waiting for another permit and semaphore
2916 // the current task to begin
2917 dialogPause = false;
2922 * Outputs an image of the desktop to file in EPS format, after prompting the
2923 * user for choice of Text or Lineart character rendering (unless a preference
2924 * has been set). The file name is generated as
2927 * Jalview_snapshot_nnnnn.eps where nnnnn is the current timestamp in milliseconds
2931 protected void snapShotWindow_actionPerformed(ActionEvent e)
2933 // currently the menu option to do this is not shown
2936 int width = getWidth();
2937 int height = getHeight();
2939 "Jalview_snapshot_" + System.currentTimeMillis() + ".eps");
2940 ImageWriterI writer = new ImageWriterI()
2943 public void exportImage(Graphics g) throws Exception
2946 Cache.log.info("Successfully written snapshot to file "
2947 + of.getAbsolutePath());
2950 String title = "View of desktop";
2951 ImageExporter exporter = new ImageExporter(writer, null, TYPE.EPS,
2953 exporter.doExport(of, this, width, height, title);
2957 * Explode the views in the given SplitFrame into separate SplitFrame windows.
2958 * This respects (remembers) any previous 'exploded geometry' i.e. the size and
2959 * location last time the view was expanded (if any). However it does not
2960 * remember the split pane divider location - this is set to match the
2961 * 'exploding' frame.
2965 public void explodeViews(SplitFrame sf)
2967 AlignFrame oldTopFrame = (AlignFrame) sf.getTopFrame();
2968 AlignFrame oldBottomFrame = (AlignFrame) sf.getBottomFrame();
2969 List<? extends AlignmentViewPanel> topPanels = oldTopFrame
2971 List<? extends AlignmentViewPanel> bottomPanels = oldBottomFrame
2973 int viewCount = topPanels.size();
2980 * Processing in reverse order works, forwards order leaves the first panels
2981 * not visible. I don't know why!
2983 for (int i = viewCount - 1; i >= 0; i--)
2986 * Make new top and bottom frames. These take over the respective
2987 * AlignmentPanel objects, including their AlignmentViewports, so the
2988 * cdna/protein relationships between the viewports is carried over to the
2991 * explodedGeometry holds the (x, y) position of the previously exploded
2992 * SplitFrame, and the (width, height) of the AlignFrame component
2994 AlignmentPanel topPanel = (AlignmentPanel) topPanels.get(i);
2995 AlignFrame newTopFrame = new AlignFrame(topPanel);
2996 newTopFrame.setSize(oldTopFrame.getSize());
2997 newTopFrame.setVisible(true);
2998 Rectangle geometry = ((AlignViewport) topPanel.getAlignViewport())
2999 .getExplodedGeometry();
3000 if (geometry != null)
3002 newTopFrame.setSize(geometry.getSize());
3005 AlignmentPanel bottomPanel = (AlignmentPanel) bottomPanels.get(i);
3006 AlignFrame newBottomFrame = new AlignFrame(bottomPanel);
3007 newBottomFrame.setSize(oldBottomFrame.getSize());
3008 newBottomFrame.setVisible(true);
3009 geometry = ((AlignViewport) bottomPanel.getAlignViewport())
3010 .getExplodedGeometry();
3011 if (geometry != null)
3013 newBottomFrame.setSize(geometry.getSize());
3016 topPanel.av.setGatherViewsHere(false);
3017 bottomPanel.av.setGatherViewsHere(false);
3018 JInternalFrame splitFrame = new SplitFrame(newTopFrame,
3020 if (geometry != null)
3022 splitFrame.setLocation(geometry.getLocation());
3024 Desktop.addInternalFrame(splitFrame, sf.getTitle(), -1, -1);
3028 * Clear references to the panels (now relocated in the new SplitFrames)
3029 * before closing the old SplitFrame.
3032 bottomPanels.clear();
3037 * Gather expanded split frames, sharing the same pairs of sequence set ids,
3038 * back into the given SplitFrame as additional views. Note that the gathered
3039 * frames may themselves have multiple views.
3043 public void gatherViews(GSplitFrame source)
3046 * special handling of explodedGeometry for a view within a SplitFrame: - it
3047 * holds the (x, y) position of the enclosing SplitFrame, and the (width,
3048 * height) of the AlignFrame component
3050 AlignFrame myTopFrame = (AlignFrame) source.getTopFrame();
3051 AlignFrame myBottomFrame = (AlignFrame) source.getBottomFrame();
3052 myTopFrame.viewport.setExplodedGeometry(new Rectangle(source.getX(),
3053 source.getY(), myTopFrame.getWidth(), myTopFrame.getHeight()));
3054 myBottomFrame.viewport
3055 .setExplodedGeometry(new Rectangle(source.getX(), source.getY(),
3056 myBottomFrame.getWidth(), myBottomFrame.getHeight()));
3057 myTopFrame.viewport.setGatherViewsHere(true);
3058 myBottomFrame.viewport.setGatherViewsHere(true);
3059 String topViewId = myTopFrame.viewport.getSequenceSetId();
3060 String bottomViewId = myBottomFrame.viewport.getSequenceSetId();
3062 JInternalFrame[] frames = desktop.getAllFrames();
3063 for (JInternalFrame frame : frames)
3065 if (frame instanceof SplitFrame && frame != source)
3067 SplitFrame sf = (SplitFrame) frame;
3068 AlignFrame topFrame = (AlignFrame) sf.getTopFrame();
3069 AlignFrame bottomFrame = (AlignFrame) sf.getBottomFrame();
3070 boolean gatherThis = false;
3071 for (int a = 0; a < topFrame.alignPanels.size(); a++)
3073 AlignmentPanel topPanel = topFrame.alignPanels.get(a);
3074 AlignmentPanel bottomPanel = bottomFrame.alignPanels.get(a);
3075 if (topViewId.equals(topPanel.av.getSequenceSetId())
3076 && bottomViewId.equals(bottomPanel.av.getSequenceSetId()))
3079 topPanel.av.setGatherViewsHere(false);
3080 bottomPanel.av.setGatherViewsHere(false);
3081 topPanel.av.setExplodedGeometry(
3082 new Rectangle(sf.getLocation(), topFrame.getSize()));
3083 bottomPanel.av.setExplodedGeometry(
3084 new Rectangle(sf.getLocation(), bottomFrame.getSize()));
3085 myTopFrame.addAlignmentPanel(topPanel, false);
3086 myBottomFrame.addAlignmentPanel(bottomPanel, false);
3092 topFrame.getAlignPanels().clear();
3093 bottomFrame.getAlignPanels().clear();
3100 * The dust settles...give focus to the tab we did this from.
3102 myTopFrame.setDisplayedView(myTopFrame.alignPanel);
3105 public static groovy.ui.Console getGroovyConsole()
3107 return groovyConsole;
3111 * handles the payload of a drag and drop event.
3113 * TODO refactor to desktop utilities class
3116 * - Data source strings extracted from the drop event
3118 * - protocol for each data source extracted from the drop event
3122 * - the payload from the drop event
3125 public static void transferFromDropTarget(List<Object> files,
3126 List<DataSourceType> protocols, DropTargetDropEvent evt,
3127 Transferable t) throws Exception
3130 // BH 2018 changed List<String> to List<Object> to allow for File from SwingJS
3132 // DataFlavor[] flavors = t.getTransferDataFlavors();
3133 // for (int i = 0; i < flavors.length; i++) {
3134 // if (flavors[i].isFlavorJavaFileListType()) {
3135 // evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
3136 // List<File> list = (List<File>) t.getTransferData(flavors[i]);
3137 // for (int j = 0; j < list.size(); j++) {
3138 // File file = (File) list.get(j);
3139 // byte[] data = getDroppedFileBytes(file);
3140 // fileName.setText(file.getName() + " - " + data.length + " " +
3141 // evt.getLocation());
3142 // JTextArea target = (JTextArea) ((DropTarget) evt.getSource()).getComponent();
3143 // target.setText(new String(data));
3145 // dtde.dropComplete(true);
3150 DataFlavor uriListFlavor = new DataFlavor(
3151 "text/uri-list;class=java.lang.String"), urlFlavour = null;
3154 urlFlavour = new DataFlavor(
3155 "application/x-java-url; class=java.net.URL");
3156 } catch (ClassNotFoundException cfe)
3158 Cache.log.debug("Couldn't instantiate the URL dataflavor.", cfe);
3161 if (urlFlavour != null && t.isDataFlavorSupported(urlFlavour))
3166 java.net.URL url = (URL) t.getTransferData(urlFlavour);
3167 // nb: java 8 osx bug https://bugs.openjdk.java.net/browse/JDK-8156099
3168 // means url may be null.
3171 protocols.add(DataSourceType.URL);
3172 files.add(url.toString());
3173 Cache.log.debug("Drop handled as URL dataflavor "
3174 + files.get(files.size() - 1));
3179 if (Platform.isAMacAndNotJS())
3182 "Please ignore plist error - occurs due to problem with java 8 on OSX");
3185 } catch (Throwable ex)
3187 Cache.log.debug("URL drop handler failed.", ex);
3190 if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
3192 // Works on Windows and MacOSX
3193 Cache.log.debug("Drop handled as javaFileListFlavor");
3194 for (Object file : (List) t
3195 .getTransferData(DataFlavor.javaFileListFlavor))
3198 protocols.add(DataSourceType.FILE);
3203 // Unix like behaviour
3204 boolean added = false;
3206 if (t.isDataFlavorSupported(uriListFlavor))
3208 Cache.log.debug("Drop handled as uriListFlavor");
3209 // This is used by Unix drag system
3210 data = (String) t.getTransferData(uriListFlavor);
3214 // fallback to text: workaround - on OSX where there's a JVM bug
3215 Cache.log.debug("standard URIListFlavor failed. Trying text");
3216 // try text fallback
3217 DataFlavor textDf = new DataFlavor(
3218 "text/plain;class=java.lang.String");
3219 if (t.isDataFlavorSupported(textDf))
3221 data = (String) t.getTransferData(textDf);
3224 Cache.log.debug("Plain text drop content returned "
3225 + (data == null ? "Null - failed" : data));
3230 while (protocols.size() < files.size())
3232 Cache.log.debug("Adding missing FILE protocol for "
3233 + files.get(protocols.size()));
3234 protocols.add(DataSourceType.FILE);
3236 for (java.util.StringTokenizer st = new java.util.StringTokenizer(
3237 data, "\r\n"); st.hasMoreTokens();)
3240 String s = st.nextToken();
3241 if (s.startsWith("#"))
3243 // the line is a comment (as per the RFC 2483)
3246 java.net.URI uri = new java.net.URI(s);
3247 if (uri.getScheme().toLowerCase().startsWith("http"))
3249 protocols.add(DataSourceType.URL);
3250 files.add(uri.toString());
3254 // otherwise preserve old behaviour: catch all for file objects
3255 java.io.File file = new java.io.File(uri);
3256 protocols.add(DataSourceType.FILE);
3257 files.add(file.toString());
3262 if (Cache.log.isDebugEnabled())
3264 if (data == null || !added)
3267 if (t.getTransferDataFlavors() != null
3268 && t.getTransferDataFlavors().length > 0)
3271 "Couldn't resolve drop data. Here are the supported flavors:");
3272 for (DataFlavor fl : t.getTransferDataFlavors())
3275 "Supported transfer dataflavor: " + fl.toString());
3276 Object df = t.getTransferData(fl);
3279 Cache.log.debug("Retrieves: " + df);
3283 Cache.log.debug("Retrieved nothing");
3289 Cache.log.debug("Couldn't resolve dataflavor for drop: "
3295 if (Platform.isWindowsAndNotJS())
3297 Cache.log.debug("Scanning dropped content for Windows Link Files");
3299 // resolve any .lnk files in the file drop
3300 for (int f = 0; f < files.size(); f++)
3302 String source = files.get(f).toString().toLowerCase();
3303 if (protocols.get(f).equals(DataSourceType.FILE)
3304 && (source.endsWith(".lnk") || source.endsWith(".url")
3305 || source.endsWith(".site")))
3309 Object obj = files.get(f);
3310 File lf = (obj instanceof File ? (File) obj
3311 : new File((String) obj));
3312 // process link file to get a URL
3313 Cache.log.debug("Found potential link file: " + lf);
3314 WindowsShortcut wscfile = new WindowsShortcut(lf);
3315 String fullname = wscfile.getRealFilename();
3316 protocols.set(f, FormatAdapter.checkProtocol(fullname));
3317 files.set(f, fullname);
3318 Cache.log.debug("Parsed real filename " + fullname
3319 + " to extract protocol: " + protocols.get(f));
3320 } catch (Exception ex)
3323 "Couldn't parse " + files.get(f) + " as a link file.",
3332 * Sets the Preferences property for experimental features to True or False
3333 * depending on the state of the controlling menu item
3336 protected void showExperimental_actionPerformed(boolean selected)
3338 Cache.setProperty(EXPERIMENTAL_FEATURES, Boolean.toString(selected));
3342 * Answers a (possibly empty) list of any structure viewer frames (currently for
3343 * either Jmol or Chimera) which are currently open. This may optionally be
3344 * restricted to viewers of a specified class, or viewers linked to a specified
3348 * if not null, only return viewers linked to this panel
3349 * @param structureViewerClass
3350 * if not null, only return viewers of this class
3353 public List<StructureViewerBase> getStructureViewers(
3354 AlignmentPanel apanel,
3355 Class<? extends StructureViewerBase> structureViewerClass)
3357 List<StructureViewerBase> result = new ArrayList<>();
3358 JInternalFrame[] frames = Desktop.instance.getAllFrames();
3360 for (JInternalFrame frame : frames)
3362 if (frame instanceof StructureViewerBase)
3364 if (structureViewerClass == null
3365 || structureViewerClass.isInstance(frame))
3368 || ((StructureViewerBase) frame).isLinkedWith(apanel))
3370 result.add((StructureViewerBase) frame);