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.ChannelProperties;
118 import jalview.util.ImageMaker.TYPE;
119 import jalview.util.MessageManager;
120 import jalview.util.Platform;
121 import jalview.util.ShortcutKeyMaskExWrapper;
122 import jalview.util.UrlConstants;
123 import jalview.viewmodel.AlignmentViewport;
124 import jalview.ws.params.ParamManager;
125 import jalview.ws.utils.UrlDownloadClient;
132 * @version $Revision: 1.155 $
134 public class Desktop extends jalview.jbgui.GDesktop
135 implements DropTargetListener, ClipboardOwner, IProgressIndicator,
136 jalview.api.StructureSelectionManagerProvider
138 private static final String CITATION = "<br><br>Development managed by The Barton Group, University of Dundee, Scotland, UK.<br>"
139 + "<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"
140 + "<br><br>If you use Jalview, please cite:"
141 + "<br>Waterhouse, A.M., Procter, J.B., Martin, D.M.A, Clamp, M. and Barton, G. J. (2009)"
142 + "<br>Jalview Version 2 - a multiple sequence alignment editor and analysis workbench"
143 + "<br>Bioinformatics doi: 10.1093/bioinformatics/btp033";
145 private static final String DEFAULT_AUTHORS = "The Jalview Authors (See AUTHORS file for current list)";
147 private static int DEFAULT_MIN_WIDTH = 300;
149 private static int DEFAULT_MIN_HEIGHT = 250;
151 private static int ALIGN_FRAME_DEFAULT_MIN_WIDTH = 600;
153 private static int ALIGN_FRAME_DEFAULT_MIN_HEIGHT = 70;
155 private static final String EXPERIMENTAL_FEATURES = "EXPERIMENTAL_FEATURES";
157 protected static final String CONFIRM_KEYBOARD_QUIT = "CONFIRM_KEYBOARD_QUIT";
159 public static HashMap<String, FileWriter> savingFiles = new HashMap<String, FileWriter>();
161 private JalviewChangeSupport changeSupport = new JalviewChangeSupport();
164 * news reader - null if it was never started.
166 private BlogReader jvnews = null;
168 private File projectFile;
172 * @see jalview.gui.JalviewChangeSupport#addJalviewPropertyChangeListener(java.beans.PropertyChangeListener)
174 public void addJalviewPropertyChangeListener(
175 PropertyChangeListener listener)
177 changeSupport.addJalviewPropertyChangeListener(listener);
181 * @param propertyName
183 * @see jalview.gui.JalviewChangeSupport#addJalviewPropertyChangeListener(java.lang.String,
184 * java.beans.PropertyChangeListener)
186 public void addJalviewPropertyChangeListener(String propertyName,
187 PropertyChangeListener listener)
189 changeSupport.addJalviewPropertyChangeListener(propertyName, listener);
193 * @param propertyName
195 * @see jalview.gui.JalviewChangeSupport#removeJalviewPropertyChangeListener(java.lang.String,
196 * java.beans.PropertyChangeListener)
198 public void removeJalviewPropertyChangeListener(String propertyName,
199 PropertyChangeListener listener)
201 changeSupport.removeJalviewPropertyChangeListener(propertyName,
205 /** Singleton Desktop instance */
206 public static Desktop instance;
208 public static MyDesktopPane desktop;
210 public static MyDesktopPane getDesktop()
212 // BH 2018 could use currentThread() here as a reference to a
213 // Hashtable<Thread, MyDesktopPane> in JavaScript
217 static int openFrameCount = 0;
219 static final int xOffset = 30;
221 static final int yOffset = 30;
223 public static jalview.ws.jws1.Discoverer discoverer;
225 public static Object[] jalviewClipboard;
227 public static boolean internalCopy = false;
229 static int fileLoadingCount = 0;
231 class MyDesktopManager implements DesktopManager
234 private DesktopManager delegate;
236 public MyDesktopManager(DesktopManager delegate)
238 this.delegate = delegate;
242 public void activateFrame(JInternalFrame f)
246 delegate.activateFrame(f);
247 } catch (NullPointerException npe)
249 Point p = getMousePosition();
250 instance.showPasteMenu(p.x, p.y);
255 public void beginDraggingFrame(JComponent f)
257 delegate.beginDraggingFrame(f);
261 public void beginResizingFrame(JComponent f, int direction)
263 delegate.beginResizingFrame(f, direction);
267 public void closeFrame(JInternalFrame f)
269 delegate.closeFrame(f);
273 public void deactivateFrame(JInternalFrame f)
275 delegate.deactivateFrame(f);
279 public void deiconifyFrame(JInternalFrame f)
281 delegate.deiconifyFrame(f);
285 public void dragFrame(JComponent f, int newX, int newY)
291 delegate.dragFrame(f, newX, newY);
295 public void endDraggingFrame(JComponent f)
297 delegate.endDraggingFrame(f);
302 public void endResizingFrame(JComponent f)
304 delegate.endResizingFrame(f);
309 public void iconifyFrame(JInternalFrame f)
311 delegate.iconifyFrame(f);
315 public void maximizeFrame(JInternalFrame f)
317 delegate.maximizeFrame(f);
321 public void minimizeFrame(JInternalFrame f)
323 delegate.minimizeFrame(f);
327 public void openFrame(JInternalFrame f)
329 delegate.openFrame(f);
333 public void resizeFrame(JComponent f, int newX, int newY, int newWidth,
340 delegate.resizeFrame(f, newX, newY, newWidth, newHeight);
344 public void setBoundsForFrame(JComponent f, int newX, int newY,
345 int newWidth, int newHeight)
347 delegate.setBoundsForFrame(f, newX, newY, newWidth, newHeight);
350 // All other methods, simply delegate
355 * Creates a new Desktop object.
361 * A note to implementors. It is ESSENTIAL that any activities that might
362 * block are spawned off as threads rather than waited for during this
367 doConfigureStructurePrefs();
368 setTitle(ChannelProperties.getProperty("app_name") + " "
369 + Cache.getProperty("VERSION"));
371 if (!Platform.isAMac())
373 // this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
377 this.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
383 APQHandlers.setAPQHandlers(this);
384 } catch (Throwable t)
386 System.out.println("Error setting APQHandlers: " + t.toString());
387 // t.printStackTrace();
390 addWindowListener(new WindowAdapter()
394 public void windowClosing(WindowEvent ev)
400 boolean selmemusage = Cache.getDefault("SHOW_MEMUSAGE", false);
402 boolean showjconsole = Cache.getDefault("SHOW_JAVA_CONSOLE", false);
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(
562 Cache.getDefault(Preferences.ADD_TEMPFACT_ANN, true));
563 ssm.setProcessSecondaryStructure(
564 Cache.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");
658 String width = Cache.getProperty(windowName + "SCREEN_WIDTH");
659 String height = Cache.getProperty(windowName + "SCREEN_HEIGHT");
660 if ((x != null) && (y != null) && (width != null) && (height != null))
662 int ix = Integer.parseInt(x), iy = Integer.parseInt(y),
663 iw = Integer.parseInt(width), ih = Integer.parseInt(height);
664 if (Cache.getProperty("SCREENGEOMETRY_WIDTH") != null)
666 // attempt #1 - try to cope with change in screen geometry - this
667 // version doesn't preserve original jv aspect ratio.
668 // take ratio of current screen size vs original screen size.
669 double sw = ((1f * screenSize.width) / (1f * Integer
670 .parseInt(Cache.getProperty("SCREENGEOMETRY_WIDTH"))));
671 double sh = ((1f * screenSize.height) / (1f * Integer
672 .parseInt(Cache.getProperty("SCREENGEOMETRY_HEIGHT"))));
673 // rescale the bounds depending upon the current screen geometry.
674 ix = (int) (ix * sw);
675 iw = (int) (iw * sw);
676 iy = (int) (iy * sh);
677 ih = (int) (ih * sh);
678 while (ix >= screenSize.width)
681 "Window geometry location recall error: shifting horizontal to within screenbounds.");
682 ix -= screenSize.width;
684 while (iy >= screenSize.height)
687 "Window geometry location recall error: shifting vertical to within screenbounds.");
688 iy -= screenSize.height;
691 "Got last known dimensions for " + windowName + ": x:" + ix
692 + " y:" + iy + " width:" + iw + " height:" + ih);
694 // return dimensions for new instance
695 return new Rectangle(ix, iy, iw, ih);
700 void showPasteMenu(int x, int y)
702 JPopupMenu popup = new JPopupMenu();
703 JMenuItem item = new JMenuItem(
704 MessageManager.getString("label.paste_new_window"));
705 item.addActionListener(new ActionListener()
708 public void actionPerformed(ActionEvent evt)
715 popup.show(this, x, y);
722 Clipboard c = Toolkit.getDefaultToolkit().getSystemClipboard();
723 Transferable contents = c.getContents(this);
725 if (contents != null)
727 String file = (String) contents
728 .getTransferData(DataFlavor.stringFlavor);
730 FileFormatI format = new IdentifyFile().identify(file,
731 DataSourceType.PASTE);
733 new FileLoader().LoadFile(file, DataSourceType.PASTE, format);
736 } catch (Exception ex)
739 "Unable to paste alignment from system clipboard:\n" + ex);
744 * Adds and opens the given frame to the desktop
755 public static synchronized void addInternalFrame(
756 final JInternalFrame frame, String title, int w, int h)
758 addInternalFrame(frame, title, true, w, h, true, false);
762 * Add an internal frame to the Jalview desktop
769 * When true, display frame immediately, otherwise, caller must call
770 * setVisible themselves.
776 public static synchronized void addInternalFrame(
777 final JInternalFrame frame, String title, boolean makeVisible,
780 addInternalFrame(frame, title, makeVisible, w, h, true, false);
784 * Add an internal frame to the Jalview desktop and make it visible
797 public static synchronized void addInternalFrame(
798 final JInternalFrame frame, String title, int w, int h,
801 addInternalFrame(frame, title, true, w, h, resizable, false);
805 * Add an internal frame to the Jalview desktop
812 * When true, display frame immediately, otherwise, caller must call
813 * setVisible themselves.
820 * @param ignoreMinSize
821 * Do not set the default minimum size for frame
823 public static synchronized void addInternalFrame(
824 final JInternalFrame frame, String title, boolean makeVisible,
825 int w, int h, boolean resizable, boolean ignoreMinSize)
828 // TODO: allow callers to determine X and Y position of frame (eg. via
830 // TODO: consider fixing method to update entries in the window submenu with
831 // the current window title
833 frame.setTitle(title);
834 if (frame.getWidth() < 1 || frame.getHeight() < 1)
838 // THIS IS A PUBLIC STATIC METHOD, SO IT MAY BE CALLED EVEN IN
839 // A HEADLESS STATE WHEN NO DESKTOP EXISTS. MUST RETURN
840 // IF JALVIEW IS RUNNING HEADLESS
841 // ///////////////////////////////////////////////
842 if (instance == null || (System.getProperty("java.awt.headless") != null
843 && System.getProperty("java.awt.headless").equals("true")))
852 frame.setMinimumSize(
853 new Dimension(DEFAULT_MIN_WIDTH, DEFAULT_MIN_HEIGHT));
855 // Set default dimension for Alignment Frame window.
856 // The Alignment Frame window could be added from a number of places,
858 // I did this here in order not to miss out on any Alignment frame.
859 if (frame instanceof AlignFrame)
861 frame.setMinimumSize(new Dimension(ALIGN_FRAME_DEFAULT_MIN_WIDTH,
862 ALIGN_FRAME_DEFAULT_MIN_HEIGHT));
866 frame.setVisible(makeVisible);
867 frame.setClosable(true);
868 frame.setResizable(resizable);
869 frame.setMaximizable(resizable);
870 frame.setIconifiable(resizable);
871 frame.setOpaque(Platform.isJS());
873 if (frame.getX() < 1 && frame.getY() < 1)
875 frame.setLocation(xOffset * openFrameCount,
876 yOffset * ((openFrameCount - 1) % 10) + yOffset);
880 * add an entry for the new frame in the Window menu
881 * (and remove it when the frame is closed)
883 final JMenuItem menuItem = new JMenuItem(title);
884 frame.addInternalFrameListener(new InternalFrameAdapter()
887 public void internalFrameActivated(InternalFrameEvent evt)
889 JInternalFrame itf = desktop.getSelectedFrame();
892 if (itf instanceof AlignFrame)
894 Jalview.setCurrentAlignFrame((AlignFrame) itf);
901 public void internalFrameClosed(InternalFrameEvent evt)
903 PaintRefresher.RemoveComponent(frame);
906 * defensive check to prevent frames being
907 * added half off the window
909 if (openFrameCount > 0)
915 * ensure no reference to alignFrame retained by menu item listener
917 if (menuItem.getActionListeners().length > 0)
919 menuItem.removeActionListener(menuItem.getActionListeners()[0]);
921 windowMenu.remove(menuItem);
925 menuItem.addActionListener(new ActionListener()
928 public void actionPerformed(ActionEvent e)
932 frame.setSelected(true);
933 frame.setIcon(false);
934 } catch (java.beans.PropertyVetoException ex)
936 // System.err.println(ex.toString());
941 setKeyBindings(frame);
945 windowMenu.add(menuItem);
950 frame.setSelected(true);
951 frame.requestFocus();
952 } catch (java.beans.PropertyVetoException ve)
954 } catch (java.lang.ClassCastException cex)
957 "Squashed a possible GUI implementation error. If you can recreate this, please look at http://issues.jalview.org/browse/JAL-869",
963 * Add key bindings to a JInternalFrame so that Ctrl-W and Cmd-W will close
968 private static void setKeyBindings(JInternalFrame frame)
970 @SuppressWarnings("serial")
971 final Action closeAction = new AbstractAction()
974 public void actionPerformed(ActionEvent e)
981 * set up key bindings for Ctrl-W and Cmd-W, with the same (Close) action
983 KeyStroke ctrlWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
984 InputEvent.CTRL_DOWN_MASK);
985 KeyStroke cmdWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
986 ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx());
988 InputMap inputMap = frame
989 .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
990 String ctrlW = ctrlWKey.toString();
991 inputMap.put(ctrlWKey, ctrlW);
992 inputMap.put(cmdWKey, ctrlW);
994 ActionMap actionMap = frame.getActionMap();
995 actionMap.put(ctrlW, closeAction);
999 public void lostOwnership(Clipboard clipboard, Transferable contents)
1003 Desktop.jalviewClipboard = null;
1006 internalCopy = false;
1010 public void dragEnter(DropTargetDragEvent evt)
1015 public void dragExit(DropTargetEvent evt)
1020 public void dragOver(DropTargetDragEvent evt)
1025 public void dropActionChanged(DropTargetDragEvent evt)
1036 public void drop(DropTargetDropEvent evt)
1038 boolean success = true;
1039 // JAL-1552 - acceptDrop required before getTransferable call for
1040 // Java's Transferable for native dnd
1041 evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
1042 Transferable t = evt.getTransferable();
1043 List<Object> files = new ArrayList<>();
1044 List<DataSourceType> protocols = new ArrayList<>();
1048 Desktop.transferFromDropTarget(files, protocols, evt, t);
1049 } catch (Exception e)
1051 e.printStackTrace();
1059 for (int i = 0; i < files.size(); i++)
1061 // BH 2018 File or String
1062 Object file = files.get(i);
1063 String fileName = file.toString();
1064 DataSourceType protocol = (protocols == null)
1065 ? DataSourceType.FILE
1067 FileFormatI format = null;
1069 if (fileName.endsWith(".jar"))
1071 format = FileFormat.Jalview;
1076 format = new IdentifyFile().identify(file, protocol);
1078 if (file instanceof File)
1080 Platform.cacheFileData((File) file);
1082 new FileLoader().LoadFile(null, file, protocol, format);
1085 } catch (Exception ex)
1090 evt.dropComplete(success); // need this to ensure input focus is properly
1091 // transfered to any new windows created
1101 public void inputLocalFileMenuItem_actionPerformed(AlignViewport viewport)
1103 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
1104 JalviewFileChooser chooser = JalviewFileChooser.forRead(
1105 Cache.getProperty("LAST_DIRECTORY"), fileFormat,
1106 BackupFiles.getEnabled());
1108 chooser.setFileView(new JalviewFileView());
1109 chooser.setDialogTitle(
1110 MessageManager.getString("label.open_local_file"));
1111 chooser.setToolTipText(MessageManager.getString("action.open"));
1113 chooser.setResponseHandler(0, new Runnable()
1118 File selectedFile = chooser.getSelectedFile();
1119 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1121 FileFormatI format = chooser.getSelectedFormat();
1124 * Call IdentifyFile to verify the file contains what its extension implies.
1125 * Skip this step for dynamically added file formats, because
1126 * IdentifyFile does not know how to recognise them.
1128 if (FileFormats.getInstance().isIdentifiable(format))
1132 format = new IdentifyFile().identify(selectedFile,
1133 DataSourceType.FILE);
1134 } catch (FileFormatException e)
1136 // format = null; //??
1140 new FileLoader().LoadFile(viewport, selectedFile,
1141 DataSourceType.FILE, format);
1144 chooser.showOpenDialog(this);
1148 * Shows a dialog for input of a URL at which to retrieve alignment data
1153 public void inputURLMenuItem_actionPerformed(AlignViewport viewport)
1155 // This construct allows us to have a wider textfield
1157 JLabel label = new JLabel(
1158 MessageManager.getString("label.input_file_url"));
1160 JPanel panel = new JPanel(new GridLayout(2, 1));
1164 * the URL to fetch is
1165 * Java: an editable combobox with history
1166 * JS: (pending JAL-3038) a plain text field
1169 String urlBase = "http://www.";
1170 if (Platform.isJS())
1172 history = new JTextField(urlBase, 35);
1181 JComboBox<String> asCombo = new JComboBox<>();
1182 asCombo.setPreferredSize(new Dimension(400, 20));
1183 asCombo.setEditable(true);
1184 asCombo.addItem(urlBase);
1185 String historyItems = Cache.getProperty("RECENT_URL");
1186 if (historyItems != null)
1188 for (String token : historyItems.split("\\t"))
1190 asCombo.addItem(token);
1197 Object[] options = new Object[] { MessageManager.getString("action.ok"),
1198 MessageManager.getString("action.cancel") };
1199 Runnable action = new Runnable()
1204 @SuppressWarnings("unchecked")
1205 String url = (history instanceof JTextField
1206 ? ((JTextField) history).getText()
1207 : ((JComboBox<String>) history).getSelectedItem()
1210 if (url.toLowerCase().endsWith(".jar"))
1212 if (viewport != null)
1214 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1215 FileFormat.Jalview);
1219 new FileLoader().LoadFile(url, DataSourceType.URL,
1220 FileFormat.Jalview);
1225 FileFormatI format = null;
1228 format = new IdentifyFile().identify(url, DataSourceType.URL);
1229 } catch (FileFormatException e)
1231 // TODO revise error handling, distinguish between
1232 // URL not found and response not valid
1237 String msg = MessageManager
1238 .formatMessage("label.couldnt_locate", url);
1239 JvOptionPane.showInternalMessageDialog(Desktop.desktop, msg,
1240 MessageManager.getString("label.url_not_found"),
1241 JvOptionPane.WARNING_MESSAGE);
1246 if (viewport != null)
1248 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1253 new FileLoader().LoadFile(url, DataSourceType.URL, format);
1258 String dialogOption = MessageManager
1259 .getString("label.input_alignment_from_url");
1260 JvOptionPane.newOptionDialog(desktop).setResponseHandler(0, action)
1261 .showInternalDialog(panel, dialogOption,
1262 JvOptionPane.YES_NO_CANCEL_OPTION,
1263 JvOptionPane.PLAIN_MESSAGE, null, options,
1264 MessageManager.getString("action.ok"));
1268 * Opens the CutAndPaste window for the user to paste an alignment in to
1271 * - if not null, the pasted alignment is added to the current
1272 * alignment; if null, to a new alignment window
1275 public void inputTextboxMenuItem_actionPerformed(
1276 AlignmentViewPanel viewPanel)
1278 CutAndPasteTransfer cap = new CutAndPasteTransfer();
1279 cap.setForInput(viewPanel);
1280 Desktop.addInternalFrame(cap,
1281 MessageManager.getString("label.cut_paste_alignmen_file"), true,
1291 Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
1292 Cache.setProperty("SCREENGEOMETRY_WIDTH", screen.width + "");
1293 Cache.setProperty("SCREENGEOMETRY_HEIGHT", screen.height + "");
1294 storeLastKnownDimensions("", new Rectangle(getBounds().x, getBounds().y,
1295 getWidth(), getHeight()));
1297 if (jconsole != null)
1299 storeLastKnownDimensions("JAVA_CONSOLE_", jconsole.getBounds());
1300 jconsole.stopConsole();
1304 storeLastKnownDimensions("JALVIEW_RSS_WINDOW_", jvnews.getBounds());
1307 if (dialogExecutor != null)
1309 dialogExecutor.shutdownNow();
1311 closeAll_actionPerformed(null);
1313 if (groovyConsole != null)
1315 // suppress a possible repeat prompt to save script
1316 groovyConsole.setDirty(false);
1317 groovyConsole.exit();
1322 private void storeLastKnownDimensions(String string, Rectangle jc)
1324 Cache.log.debug("Storing last known dimensions for " + string + ": x:"
1325 + jc.x + " y:" + jc.y + " width:" + jc.width + " height:"
1328 Cache.setProperty(string + "SCREEN_X", jc.x + "");
1329 Cache.setProperty(string + "SCREEN_Y", jc.y + "");
1330 Cache.setProperty(string + "SCREEN_WIDTH", jc.width + "");
1331 Cache.setProperty(string + "SCREEN_HEIGHT", jc.height + "");
1341 public void aboutMenuItem_actionPerformed(ActionEvent e)
1343 new Thread(new Runnable()
1348 new SplashScreen(false);
1354 * Returns the html text for the About screen, including any available version
1355 * number, build details, author details and citation reference, but without
1356 * the enclosing {@code html} tags
1360 public String getAboutMessage()
1362 StringBuilder message = new StringBuilder(1024);
1363 message.append("<h1><strong>Version: ")
1364 .append(Cache.getProperty("VERSION")).append("</strong></h1>")
1365 .append("<strong>Built: <em>")
1366 .append(Cache.getDefault("BUILD_DATE", "unknown"))
1367 .append("</em> from ").append(Cache.getBuildDetailsForSplash())
1368 .append("</strong>");
1370 String latestVersion = Cache.getDefault("LATEST_VERSION", "Checking");
1371 if (latestVersion.equals("Checking"))
1373 // JBP removed this message for 2.11: May be reinstated in future version
1374 // message.append("<br>...Checking latest version...</br>");
1376 else if (!latestVersion.equals(Cache.getProperty("VERSION")))
1378 boolean red = false;
1379 if (Cache.getProperty("VERSION").toLowerCase()
1380 .indexOf("automated build") == -1)
1383 // Displayed when code version and jnlp version do not match and code
1384 // version is not a development build
1385 message.append("<div style=\"color: #FF0000;font-style: bold;\">");
1388 message.append("<br>!! Version ")
1389 .append(Cache.getDefault("LATEST_VERSION", "..Checking.."))
1390 .append(" is available for download from ")
1391 .append(Cache.getDefault("www.jalview.org",
1392 "http://www.jalview.org"))
1396 message.append("</div>");
1399 message.append("<br>Authors: ");
1400 message.append(Cache.getDefault("AUTHORFNAMES", DEFAULT_AUTHORS));
1401 message.append(CITATION);
1403 return message.toString();
1407 * Action on requesting Help documentation
1410 public void documentationMenuItem_actionPerformed()
1414 if (Platform.isJS())
1416 BrowserLauncher.openURL("http://www.jalview.org/help.html");
1425 Help.showHelpWindow();
1427 } catch (Exception ex)
1429 System.err.println("Error opening help: " + ex.getMessage());
1434 public void closeAll_actionPerformed(ActionEvent e)
1436 // TODO show a progress bar while closing?
1437 JInternalFrame[] frames = desktop.getAllFrames();
1438 for (int i = 0; i < frames.length; i++)
1442 frames[i].setClosed(true);
1443 } catch (java.beans.PropertyVetoException ex)
1447 Jalview.setCurrentAlignFrame(null);
1448 System.out.println("ALL CLOSED");
1451 * reset state of singleton objects as appropriate (clear down session state
1452 * when all windows are closed)
1454 StructureSelectionManager ssm = StructureSelectionManager
1455 .getStructureSelectionManager(this);
1463 public void raiseRelated_actionPerformed(ActionEvent e)
1465 reorderAssociatedWindows(false, false);
1469 public void minimizeAssociated_actionPerformed(ActionEvent e)
1471 reorderAssociatedWindows(true, false);
1474 void closeAssociatedWindows()
1476 reorderAssociatedWindows(false, true);
1482 * @seejalview.jbgui.GDesktop#garbageCollect_actionPerformed(java.awt.event.
1486 protected void garbageCollect_actionPerformed(ActionEvent e)
1488 // We simply collect the garbage
1489 Cache.log.debug("Collecting garbage...");
1491 Cache.log.debug("Finished garbage collection.");
1498 * jalview.jbgui.GDesktop#showMemusage_actionPerformed(java.awt.event.ActionEvent
1502 protected void showMemusage_actionPerformed(ActionEvent e)
1504 desktop.showMemoryUsage(showMemusage.isSelected());
1511 * jalview.jbgui.GDesktop#showConsole_actionPerformed(java.awt.event.ActionEvent
1515 protected void showConsole_actionPerformed(ActionEvent e)
1517 showConsole(showConsole.isSelected());
1520 Console jconsole = null;
1523 * control whether the java console is visible or not
1527 void showConsole(boolean selected)
1529 // TODO: decide if we should update properties file
1530 if (jconsole != null) // BH 2018
1532 showConsole.setSelected(selected);
1533 Cache.setProperty("SHOW_JAVA_CONSOLE",
1534 Boolean.valueOf(selected).toString());
1535 jconsole.setVisible(selected);
1539 void reorderAssociatedWindows(boolean minimize, boolean close)
1541 JInternalFrame[] frames = desktop.getAllFrames();
1542 if (frames == null || frames.length < 1)
1547 AlignmentViewport source = null, target = null;
1548 if (frames[0] instanceof AlignFrame)
1550 source = ((AlignFrame) frames[0]).getCurrentView();
1552 else if (frames[0] instanceof TreePanel)
1554 source = ((TreePanel) frames[0]).getViewPort();
1556 else if (frames[0] instanceof PCAPanel)
1558 source = ((PCAPanel) frames[0]).av;
1560 else if (frames[0].getContentPane() instanceof PairwiseAlignPanel)
1562 source = ((PairwiseAlignPanel) frames[0].getContentPane()).av;
1567 for (int i = 0; i < frames.length; i++)
1570 if (frames[i] == null)
1574 if (frames[i] instanceof AlignFrame)
1576 target = ((AlignFrame) frames[i]).getCurrentView();
1578 else if (frames[i] instanceof TreePanel)
1580 target = ((TreePanel) frames[i]).getViewPort();
1582 else if (frames[i] instanceof PCAPanel)
1584 target = ((PCAPanel) frames[i]).av;
1586 else if (frames[i].getContentPane() instanceof PairwiseAlignPanel)
1588 target = ((PairwiseAlignPanel) frames[i].getContentPane()).av;
1591 if (source == target)
1597 frames[i].setClosed(true);
1601 frames[i].setIcon(minimize);
1604 frames[i].toFront();
1608 } catch (java.beans.PropertyVetoException ex)
1623 protected void preferences_actionPerformed(ActionEvent e)
1629 * Prompts the user to choose a file and then saves the Jalview state as a
1630 * Jalview project file
1633 public void saveState_actionPerformed()
1635 saveState_actionPerformed(false);
1638 public void saveState_actionPerformed(boolean saveAs)
1640 java.io.File projectFile = getProjectFile();
1641 // autoSave indicates we already have a file and don't need to ask
1642 boolean autoSave = projectFile != null && !saveAs
1643 && BackupFiles.getEnabled();
1645 // System.out.println("autoSave="+autoSave+", projectFile='"+projectFile+"',
1646 // saveAs="+saveAs+", Backups
1647 // "+(BackupFiles.getEnabled()?"enabled":"disabled"));
1649 boolean approveSave = false;
1652 JalviewFileChooser chooser = new JalviewFileChooser("jvp",
1655 chooser.setFileView(new JalviewFileView());
1656 chooser.setDialogTitle(MessageManager.getString("label.save_state"));
1658 int value = chooser.showSaveDialog(this);
1660 if (value == JalviewFileChooser.APPROVE_OPTION)
1662 projectFile = chooser.getSelectedFile();
1663 setProjectFile(projectFile);
1668 if (approveSave || autoSave)
1670 final Desktop me = this;
1671 final java.io.File chosenFile = projectFile;
1672 new Thread(new Runnable()
1677 // TODO: refactor to Jalview desktop session controller action.
1678 setProgressBar(MessageManager.formatMessage(
1679 "label.saving_jalview_project", new Object[]
1680 { chosenFile.getName() }), chosenFile.hashCode());
1681 Cache.setProperty("LAST_DIRECTORY", chosenFile.getParent());
1682 // TODO catch and handle errors for savestate
1683 // TODO prevent user from messing with the Desktop whilst we're saving
1686 boolean doBackup = BackupFiles.getEnabled();
1687 BackupFiles backupfiles = doBackup ? new BackupFiles(chosenFile)
1690 new Jalview2XML().saveState(
1691 doBackup ? backupfiles.getTempFile() : chosenFile);
1695 backupfiles.setWriteSuccess(true);
1696 backupfiles.rollBackupsAndRenameTempFile();
1698 } catch (OutOfMemoryError oom)
1700 new OOMWarning("Whilst saving current state to "
1701 + chosenFile.getName(), oom);
1702 } catch (Exception ex)
1704 Cache.log.error("Problems whilst trying to save to "
1705 + chosenFile.getName(), ex);
1706 JvOptionPane.showMessageDialog(me,
1707 MessageManager.formatMessage(
1708 "label.error_whilst_saving_current_state_to",
1710 { chosenFile.getName() }),
1711 MessageManager.getString("label.couldnt_save_project"),
1712 JvOptionPane.WARNING_MESSAGE);
1714 setProgressBar(null, chosenFile.hashCode());
1721 public void saveAsState_actionPerformed(ActionEvent e)
1723 saveState_actionPerformed(true);
1726 private void setProjectFile(File choice)
1728 this.projectFile = choice;
1731 public File getProjectFile()
1733 return this.projectFile;
1737 * Shows a file chooser dialog and tries to read in the selected file as a
1741 public void loadState_actionPerformed()
1743 final String[] suffix = new String[] { "jvp", "jar" };
1744 final String[] desc = new String[] { "Jalview Project",
1745 "Jalview Project (old)" };
1746 JalviewFileChooser chooser = new JalviewFileChooser(
1747 Cache.getProperty("LAST_DIRECTORY"), suffix, desc,
1748 "Jalview Project", true, BackupFiles.getEnabled()); // last two
1752 chooser.setFileView(new JalviewFileView());
1753 chooser.setDialogTitle(MessageManager.getString("label.restore_state"));
1754 chooser.setResponseHandler(0, new Runnable()
1759 File selectedFile = chooser.getSelectedFile();
1760 setProjectFile(selectedFile);
1761 String choice = selectedFile.getAbsolutePath();
1762 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1763 new Thread(new Runnable()
1770 new Jalview2XML().loadJalviewAlign(selectedFile);
1771 } catch (OutOfMemoryError oom)
1773 new OOMWarning("Whilst loading project from " + choice, oom);
1774 } catch (Exception ex)
1777 "Problems whilst loading project from " + choice, ex);
1778 JvOptionPane.showMessageDialog(Desktop.desktop,
1779 MessageManager.formatMessage(
1780 "label.error_whilst_loading_project_from",
1784 .getString("label.couldnt_load_project"),
1785 JvOptionPane.WARNING_MESSAGE);
1788 }, "Project Loader").start();
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()) ? af.featureSettings : null;
1981 Rectangle fsBounds = af.getFeatureSettingsGeometry();
1982 for (int i = 0; i < size; i++)
1984 AlignmentPanel ap = af.alignPanels.get(i);
1986 AlignFrame newaf = new AlignFrame(ap);
1988 // transfer reference for existing feature settings to new alignFrame
1989 if (ap == af.alignPanel)
1991 if (viewFeatureSettings != null && viewFeatureSettings.fr.ap == ap)
1993 newaf.featureSettings = viewFeatureSettings;
1995 newaf.setFeatureSettingsGeometry(fsBounds);
1999 * Restore the view's last exploded frame geometry if known. Multiple
2000 * views from one exploded frame share and restore the same (frame)
2001 * position and size.
2003 Rectangle geometry = ap.av.getExplodedGeometry();
2004 if (geometry != null)
2006 newaf.setBounds(geometry);
2009 ap.av.setGatherViewsHere(false);
2011 addInternalFrame(newaf, af.getTitle(), AlignFrame.DEFAULT_WIDTH,
2012 AlignFrame.DEFAULT_HEIGHT);
2013 // and materialise a new feature settings dialog instance for the new
2015 // (closes the old as if 'OK' was pressed)
2016 if (ap == af.alignPanel && newaf.featureSettings != null
2017 && newaf.featureSettings.isOpen()
2018 && af.alignPanel.getAlignViewport().isShowSequenceFeatures())
2020 newaf.showFeatureSettingsUI();
2024 af.featureSettings = null;
2025 af.alignPanels.clear();
2026 af.closeMenuItem_actionPerformed(true);
2031 * Gather expanded views (separate AlignFrame's) with the same sequence set
2032 * identifier back in to this frame as additional views, and close the
2033 * expanded views. Note the expanded frames may themselves have multiple
2034 * views. We take the lot.
2038 public void gatherViews(AlignFrame source)
2040 source.viewport.setGatherViewsHere(true);
2041 source.viewport.setExplodedGeometry(source.getBounds());
2042 JInternalFrame[] frames = desktop.getAllFrames();
2043 String viewId = source.viewport.getSequenceSetId();
2044 for (int t = 0; t < frames.length; t++)
2046 if (frames[t] instanceof AlignFrame && frames[t] != source)
2048 AlignFrame af = (AlignFrame) frames[t];
2049 boolean gatherThis = false;
2050 for (int a = 0; a < af.alignPanels.size(); a++)
2052 AlignmentPanel ap = af.alignPanels.get(a);
2053 if (viewId.equals(ap.av.getSequenceSetId()))
2056 ap.av.setGatherViewsHere(false);
2057 ap.av.setExplodedGeometry(af.getBounds());
2058 source.addAlignmentPanel(ap, false);
2064 if (af.featureSettings != null && af.featureSettings.isOpen())
2066 if (source.featureSettings == null)
2068 // preserve the feature settings geometry for this frame
2069 source.featureSettings = af.featureSettings;
2070 source.setFeatureSettingsGeometry(
2071 af.getFeatureSettingsGeometry());
2075 // close it and forget
2076 af.featureSettings.close();
2079 af.alignPanels.clear();
2080 af.closeMenuItem_actionPerformed(true);
2085 // refresh the feature setting UI for the source frame if it exists
2086 if (source.featureSettings != null && source.featureSettings.isOpen())
2088 source.showFeatureSettingsUI();
2092 public JInternalFrame[] getAllFrames()
2094 return desktop.getAllFrames();
2098 * Checks the given url to see if it gives a response indicating that the user
2099 * should be informed of a new questionnaire.
2103 public void checkForQuestionnaire(String url)
2105 UserQuestionnaireCheck jvq = new UserQuestionnaireCheck(url);
2106 // javax.swing.SwingUtilities.invokeLater(jvq);
2107 new Thread(jvq).start();
2110 public void checkURLLinks()
2112 // Thread off the URL link checker
2113 addDialogThread(new Runnable()
2118 if (Cache.getDefault("CHECKURLLINKS", true))
2120 // check what the actual links are - if it's just the default don't
2121 // bother with the warning
2122 List<String> links = Preferences.sequenceUrlLinks
2125 // only need to check links if there is one with a
2126 // SEQUENCE_ID which is not the default EMBL_EBI link
2127 ListIterator<String> li = links.listIterator();
2128 boolean check = false;
2129 List<JLabel> urls = new ArrayList<>();
2130 while (li.hasNext())
2132 String link = li.next();
2133 if (link.contains(jalview.util.UrlConstants.SEQUENCE_ID)
2134 && !UrlConstants.isDefaultString(link))
2137 int barPos = link.indexOf("|");
2138 String urlMsg = barPos == -1 ? link
2139 : link.substring(0, barPos) + ": "
2140 + link.substring(barPos + 1);
2141 urls.add(new JLabel(urlMsg));
2149 // ask user to check in case URL links use old style tokens
2150 // ($SEQUENCE_ID$ for sequence id _or_ accession id)
2151 JPanel msgPanel = new JPanel();
2152 msgPanel.setLayout(new BoxLayout(msgPanel, BoxLayout.PAGE_AXIS));
2153 msgPanel.add(Box.createVerticalGlue());
2154 JLabel msg = new JLabel(MessageManager
2155 .getString("label.SEQUENCE_ID_for_DB_ACCESSION1"));
2156 JLabel msg2 = new JLabel(MessageManager
2157 .getString("label.SEQUENCE_ID_for_DB_ACCESSION2"));
2159 for (JLabel url : urls)
2165 final JCheckBox jcb = new JCheckBox(
2166 MessageManager.getString("label.do_not_display_again"));
2167 jcb.addActionListener(new ActionListener()
2170 public void actionPerformed(ActionEvent e)
2172 // update Cache settings for "don't show this again"
2173 boolean showWarningAgain = !jcb.isSelected();
2174 Cache.setProperty("CHECKURLLINKS",
2175 Boolean.valueOf(showWarningAgain).toString());
2180 JvOptionPane.showMessageDialog(Desktop.desktop, msgPanel,
2182 .getString("label.SEQUENCE_ID_no_longer_used"),
2183 JvOptionPane.WARNING_MESSAGE);
2190 * Proxy class for JDesktopPane which optionally displays the current memory
2191 * usage and highlights the desktop area with a red bar if free memory runs
2196 public class MyDesktopPane extends JDesktopPane implements Runnable
2198 private static final float ONE_MB = 1048576f;
2200 boolean showMemoryUsage = false;
2204 java.text.NumberFormat df;
2206 float maxMemory, allocatedMemory, freeMemory, totalFreeMemory,
2209 public MyDesktopPane(boolean showMemoryUsage)
2211 showMemoryUsage(showMemoryUsage);
2214 public void showMemoryUsage(boolean showMemory)
2216 this.showMemoryUsage = showMemory;
2219 Thread worker = new Thread(this);
2225 public boolean isShowMemoryUsage()
2227 return showMemoryUsage;
2233 df = java.text.NumberFormat.getNumberInstance();
2234 df.setMaximumFractionDigits(2);
2235 runtime = Runtime.getRuntime();
2237 while (showMemoryUsage)
2241 maxMemory = runtime.maxMemory() / ONE_MB;
2242 allocatedMemory = runtime.totalMemory() / ONE_MB;
2243 freeMemory = runtime.freeMemory() / ONE_MB;
2244 totalFreeMemory = freeMemory + (maxMemory - allocatedMemory);
2246 percentUsage = (totalFreeMemory / maxMemory) * 100;
2248 // if (percentUsage < 20)
2250 // border1 = BorderFactory.createMatteBorder(12, 12, 12, 12,
2252 // instance.set.setBorder(border1);
2255 // sleep after showing usage
2257 } catch (Exception ex)
2259 ex.printStackTrace();
2265 public void paintComponent(Graphics g)
2267 if (showMemoryUsage && g != null && df != null)
2269 if (percentUsage < 20)
2271 g.setColor(Color.red);
2273 FontMetrics fm = g.getFontMetrics();
2276 g.drawString(MessageManager.formatMessage("label.memory_stats",
2278 { df.format(totalFreeMemory), df.format(maxMemory),
2279 df.format(percentUsage) }),
2280 10, getHeight() - fm.getHeight());
2287 * Accessor method to quickly get all the AlignmentFrames loaded.
2289 * @return an array of AlignFrame, or null if none found
2291 public static AlignFrame[] getAlignFrames()
2293 if (Jalview.isHeadlessMode())
2295 // Desktop.desktop is null in headless mode
2296 return new AlignFrame[] { Jalview.currentAlignFrame };
2299 JInternalFrame[] frames = Desktop.desktop.getAllFrames();
2305 List<AlignFrame> avp = new ArrayList<>();
2307 for (int i = frames.length - 1; i > -1; i--)
2309 if (frames[i] instanceof AlignFrame)
2311 avp.add((AlignFrame) frames[i]);
2313 else if (frames[i] instanceof SplitFrame)
2316 * Also check for a split frame containing an AlignFrame
2318 GSplitFrame sf = (GSplitFrame) frames[i];
2319 if (sf.getTopFrame() instanceof AlignFrame)
2321 avp.add((AlignFrame) sf.getTopFrame());
2323 if (sf.getBottomFrame() instanceof AlignFrame)
2325 avp.add((AlignFrame) sf.getBottomFrame());
2329 if (avp.size() == 0)
2333 AlignFrame afs[] = avp.toArray(new AlignFrame[avp.size()]);
2338 * Returns an array of any AppJmol frames in the Desktop (or null if none).
2342 public GStructureViewer[] getJmols()
2344 JInternalFrame[] frames = Desktop.desktop.getAllFrames();
2350 List<GStructureViewer> avp = new ArrayList<>();
2352 for (int i = frames.length - 1; i > -1; i--)
2354 if (frames[i] instanceof AppJmol)
2356 GStructureViewer af = (GStructureViewer) frames[i];
2360 if (avp.size() == 0)
2364 GStructureViewer afs[] = avp.toArray(new GStructureViewer[avp.size()]);
2369 * Add Groovy Support to Jalview
2372 public void groovyShell_actionPerformed()
2376 openGroovyConsole();
2377 } catch (Exception ex)
2379 Cache.log.error("Groovy Shell Creation failed.", ex);
2380 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
2382 MessageManager.getString("label.couldnt_create_groovy_shell"),
2383 MessageManager.getString("label.groovy_support_failed"),
2384 JvOptionPane.ERROR_MESSAGE);
2389 * Open the Groovy console
2391 void openGroovyConsole()
2393 if (groovyConsole == null)
2395 groovyConsole = new groovy.ui.Console();
2396 groovyConsole.setVariable("Jalview", this);
2397 groovyConsole.run();
2400 * We allow only one console at a time, so that AlignFrame menu option
2401 * 'Calculate | Run Groovy script' is unambiguous.
2402 * Disable 'Groovy Console', and enable 'Run script', when the console is
2403 * opened, and the reverse when it is closed
2405 Window window = (Window) groovyConsole.getFrame();
2406 window.addWindowListener(new WindowAdapter()
2409 public void windowClosed(WindowEvent e)
2412 * rebind CMD-Q from Groovy Console to Jalview Quit
2415 enableExecuteGroovy(false);
2421 * show Groovy console window (after close and reopen)
2423 ((Window) groovyConsole.getFrame()).setVisible(true);
2426 * if we got this far, enable 'Run Groovy' in AlignFrame menus
2427 * and disable opening a second console
2429 enableExecuteGroovy(true);
2433 * Bind Ctrl/Cmd-Q to Quit - for reset as Groovy Console takes over this
2434 * binding when opened
2436 protected void addQuitHandler()
2439 .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
2441 .getKeyStroke(KeyEvent.VK_Q,
2442 jalview.util.ShortcutKeyMaskExWrapper
2443 .getMenuShortcutKeyMaskEx()),
2445 getRootPane().getActionMap().put("Quit", new AbstractAction()
2448 public void actionPerformed(ActionEvent e)
2456 * Enable or disable 'Run Groovy script' in AlignFrame calculate menus
2459 * true if Groovy console is open
2461 public void enableExecuteGroovy(boolean enabled)
2464 * disable opening a second Groovy console
2465 * (or re-enable when the console is closed)
2467 groovyShell.setEnabled(!enabled);
2469 AlignFrame[] alignFrames = getAlignFrames();
2470 if (alignFrames != null)
2472 for (AlignFrame af : alignFrames)
2474 af.setGroovyEnabled(enabled);
2480 * Progress bars managed by the IProgressIndicator method.
2482 private Hashtable<Long, JPanel> progressBars;
2484 private Hashtable<Long, IProgressIndicatorHandler> progressBarHandlers;
2489 * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
2492 public void setProgressBar(String message, long id)
2494 // Platform.timeCheck("Desktop " + message, Platform.TIME_MARK);
2496 if (progressBars == null)
2498 progressBars = new Hashtable<>();
2499 progressBarHandlers = new Hashtable<>();
2502 if (progressBars.get(Long.valueOf(id)) != null)
2504 JPanel panel = progressBars.remove(Long.valueOf(id));
2505 if (progressBarHandlers.contains(Long.valueOf(id)))
2507 progressBarHandlers.remove(Long.valueOf(id));
2509 removeProgressPanel(panel);
2513 progressBars.put(Long.valueOf(id), addProgressPanel(message));
2520 * @see jalview.gui.IProgressIndicator#registerHandler(long,
2521 * jalview.gui.IProgressIndicatorHandler)
2524 public void registerHandler(final long id,
2525 final IProgressIndicatorHandler handler)
2527 if (progressBarHandlers == null
2528 || !progressBars.containsKey(Long.valueOf(id)))
2530 throw new Error(MessageManager.getString(
2531 "error.call_setprogressbar_before_registering_handler"));
2533 progressBarHandlers.put(Long.valueOf(id), handler);
2534 final JPanel progressPanel = progressBars.get(Long.valueOf(id));
2535 if (handler.canCancel())
2537 JButton cancel = new JButton(
2538 MessageManager.getString("action.cancel"));
2539 final IProgressIndicator us = this;
2540 cancel.addActionListener(new ActionListener()
2544 public void actionPerformed(ActionEvent e)
2546 handler.cancelActivity(id);
2547 us.setProgressBar(MessageManager
2548 .formatMessage("label.cancelled_params", new Object[]
2549 { ((JLabel) progressPanel.getComponent(0)).getText() }),
2553 progressPanel.add(cancel, BorderLayout.EAST);
2559 * @return true if any progress bars are still active
2562 public boolean operationInProgress()
2564 if (progressBars != null && progressBars.size() > 0)
2572 * This will return the first AlignFrame holding the given viewport instance.
2573 * It will break if there are more than one AlignFrames viewing a particular
2577 * @return alignFrame for viewport
2579 public static AlignFrame getAlignFrameFor(AlignViewportI viewport)
2581 if (desktop != null)
2583 AlignmentPanel[] aps = getAlignmentPanels(
2584 viewport.getSequenceSetId());
2585 for (int panel = 0; aps != null && panel < aps.length; panel++)
2587 if (aps[panel] != null && aps[panel].av == viewport)
2589 return aps[panel].alignFrame;
2596 public VamsasApplication getVamsasApplication()
2598 // TODO: JAL-3311 remove remaining code from Jalview relating to VAMSAS
2604 * flag set if jalview GUI is being operated programmatically
2606 private boolean inBatchMode = false;
2609 * check if jalview GUI is being operated programmatically
2611 * @return inBatchMode
2613 public boolean isInBatchMode()
2619 * set flag if jalview GUI is being operated programmatically
2621 * @param inBatchMode
2623 public void setInBatchMode(boolean inBatchMode)
2625 this.inBatchMode = inBatchMode;
2628 public void startServiceDiscovery()
2630 startServiceDiscovery(false);
2633 public void startServiceDiscovery(boolean blocking)
2635 boolean alive = true;
2636 Thread t0 = null, t1 = null, t2 = null;
2637 // JAL-940 - JALVIEW 1 services are now being EOLed as of JABA 2.1 release
2640 // todo: changesupport handlers need to be transferred
2641 if (discoverer == null)
2643 discoverer = new jalview.ws.jws1.Discoverer();
2644 // register PCS handler for desktop.
2645 discoverer.addPropertyChangeListener(changeSupport);
2647 // JAL-940 - disabled JWS1 service configuration - always start discoverer
2648 // until we phase out completely
2649 (t0 = new Thread(discoverer)).start();
2652 if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
2654 t2 = jalview.ws.jws2.Jws2Discoverer.getDiscoverer()
2655 .startDiscoverer(changeSupport);
2659 // TODO: do rest service discovery
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
2952 * and 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
3126 // DataFlavor[] flavors = t.getTransferDataFlavors();
3127 // for (int i = 0; i < flavors.length; i++) {
3128 // if (flavors[i].isFlavorJavaFileListType()) {
3129 // evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
3130 // List<File> list = (List<File>) t.getTransferData(flavors[i]);
3131 // for (int j = 0; j < list.size(); j++) {
3132 // File file = (File) list.get(j);
3133 // byte[] data = getDroppedFileBytes(file);
3134 // fileName.setText(file.getName() + " - " + data.length + " " +
3135 // evt.getLocation());
3136 // JTextArea target = (JTextArea) ((DropTarget)
3137 // evt.getSource()).getComponent();
3138 // target.setText(new String(data));
3140 // dtde.dropComplete(true);
3145 DataFlavor uriListFlavor = new DataFlavor(
3146 "text/uri-list;class=java.lang.String"), urlFlavour = null;
3149 urlFlavour = new DataFlavor(
3150 "application/x-java-url; class=java.net.URL");
3151 } catch (ClassNotFoundException cfe)
3153 Cache.log.debug("Couldn't instantiate the URL dataflavor.", cfe);
3156 if (urlFlavour != null && t.isDataFlavorSupported(urlFlavour))
3161 java.net.URL url = (URL) t.getTransferData(urlFlavour);
3162 // nb: java 8 osx bug https://bugs.openjdk.java.net/browse/JDK-8156099
3163 // means url may be null.
3166 protocols.add(DataSourceType.URL);
3167 files.add(url.toString());
3168 Cache.log.debug("Drop handled as URL dataflavor "
3169 + files.get(files.size() - 1));
3174 if (Platform.isAMacAndNotJS())
3177 "Please ignore plist error - occurs due to problem with java 8 on OSX");
3180 } catch (Throwable ex)
3182 Cache.log.debug("URL drop handler failed.", ex);
3185 if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
3187 // Works on Windows and MacOSX
3188 Cache.log.debug("Drop handled as javaFileListFlavor");
3189 for (Object file : (List) t
3190 .getTransferData(DataFlavor.javaFileListFlavor))
3193 protocols.add(DataSourceType.FILE);
3198 // Unix like behaviour
3199 boolean added = false;
3201 if (t.isDataFlavorSupported(uriListFlavor))
3203 Cache.log.debug("Drop handled as uriListFlavor");
3204 // This is used by Unix drag system
3205 data = (String) t.getTransferData(uriListFlavor);
3209 // fallback to text: workaround - on OSX where there's a JVM bug
3210 Cache.log.debug("standard URIListFlavor failed. Trying text");
3211 // try text fallback
3212 DataFlavor textDf = new DataFlavor(
3213 "text/plain;class=java.lang.String");
3214 if (t.isDataFlavorSupported(textDf))
3216 data = (String) t.getTransferData(textDf);
3219 Cache.log.debug("Plain text drop content returned "
3220 + (data == null ? "Null - failed" : data));
3225 while (protocols.size() < files.size())
3227 Cache.log.debug("Adding missing FILE protocol for "
3228 + files.get(protocols.size()));
3229 protocols.add(DataSourceType.FILE);
3231 for (java.util.StringTokenizer st = new java.util.StringTokenizer(
3232 data, "\r\n"); st.hasMoreTokens();)
3235 String s = st.nextToken();
3236 if (s.startsWith("#"))
3238 // the line is a comment (as per the RFC 2483)
3241 java.net.URI uri = new java.net.URI(s);
3242 if (uri.getScheme().toLowerCase().startsWith("http"))
3244 protocols.add(DataSourceType.URL);
3245 files.add(uri.toString());
3249 // otherwise preserve old behaviour: catch all for file objects
3250 java.io.File file = new java.io.File(uri);
3251 protocols.add(DataSourceType.FILE);
3252 files.add(file.toString());
3257 if (Cache.log.isDebugEnabled())
3259 if (data == null || !added)
3262 if (t.getTransferDataFlavors() != null
3263 && t.getTransferDataFlavors().length > 0)
3266 "Couldn't resolve drop data. Here are the supported flavors:");
3267 for (DataFlavor fl : t.getTransferDataFlavors())
3270 "Supported transfer dataflavor: " + fl.toString());
3271 Object df = t.getTransferData(fl);
3274 Cache.log.debug("Retrieves: " + df);
3278 Cache.log.debug("Retrieved nothing");
3284 Cache.log.debug("Couldn't resolve dataflavor for drop: "
3290 if (Platform.isWindowsAndNotJS())
3292 Cache.log.debug("Scanning dropped content for Windows Link Files");
3294 // resolve any .lnk files in the file drop
3295 for (int f = 0; f < files.size(); f++)
3297 String source = files.get(f).toString().toLowerCase();
3298 if (protocols.get(f).equals(DataSourceType.FILE)
3299 && (source.endsWith(".lnk") || source.endsWith(".url")
3300 || source.endsWith(".site")))
3304 Object obj = files.get(f);
3305 File lf = (obj instanceof File ? (File) obj
3306 : new File((String) obj));
3307 // process link file to get a URL
3308 Cache.log.debug("Found potential link file: " + lf);
3309 WindowsShortcut wscfile = new WindowsShortcut(lf);
3310 String fullname = wscfile.getRealFilename();
3311 protocols.set(f, FormatAdapter.checkProtocol(fullname));
3312 files.set(f, fullname);
3313 Cache.log.debug("Parsed real filename " + fullname
3314 + " to extract protocol: " + protocols.get(f));
3315 } catch (Exception ex)
3318 "Couldn't parse " + files.get(f) + " as a link file.",
3327 * Sets the Preferences property for experimental features to True or False
3328 * depending on the state of the controlling menu item
3331 protected void showExperimental_actionPerformed(boolean selected)
3333 Cache.setProperty(EXPERIMENTAL_FEATURES, Boolean.toString(selected));
3337 * Answers a (possibly empty) list of any structure viewer frames (currently
3338 * for either Jmol or Chimera) which are currently open. This may optionally
3339 * be restricted to viewers of a specified class, or viewers linked to a
3340 * specified alignment panel.
3343 * if not null, only return viewers linked to this panel
3344 * @param structureViewerClass
3345 * if not null, only return viewers of this class
3348 public List<StructureViewerBase> getStructureViewers(
3349 AlignmentPanel apanel,
3350 Class<? extends StructureViewerBase> structureViewerClass)
3352 List<StructureViewerBase> result = new ArrayList<>();
3353 JInternalFrame[] frames = Desktop.instance.getAllFrames();
3355 for (JInternalFrame frame : frames)
3357 if (frame instanceof StructureViewerBase)
3359 if (structureViewerClass == null
3360 || structureViewerClass.isInstance(frame))
3363 || ((StructureViewerBase) frame).isLinkedWith(apanel))
3365 result.add((StructureViewerBase) frame);