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.WSDiscovererI;
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("Jalview " + Cache.getProperty("VERSION"));
370 if (!Platform.isAMac())
372 // this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
376 this.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
382 APQHandlers.setAPQHandlers(this);
383 } catch (Throwable t)
385 System.out.println("Error setting APQHandlers: " + t.toString());
386 // t.printStackTrace();
389 addWindowListener(new WindowAdapter()
393 public void windowClosing(WindowEvent ev)
399 boolean selmemusage = Cache.getDefault("SHOW_MEMUSAGE",
402 boolean showjconsole = Cache.getDefault("SHOW_JAVA_CONSOLE",
404 desktop = new MyDesktopPane(selmemusage);
406 showMemusage.setSelected(selmemusage);
407 desktop.setBackground(Color.white);
409 getContentPane().setLayout(new BorderLayout());
410 // alternate config - have scrollbars - see notes in JAL-153
411 // JScrollPane sp = new JScrollPane();
412 // sp.getViewport().setView(desktop);
413 // getContentPane().add(sp, BorderLayout.CENTER);
415 // BH 2018 - just an experiment to try unclipped JInternalFrames.
418 getRootPane().putClientProperty("swingjs.overflow.hidden", "false");
421 getContentPane().add(desktop, BorderLayout.CENTER);
422 desktop.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
424 // This line prevents Windows Look&Feel resizing all new windows to maximum
425 // if previous window was maximised
426 desktop.setDesktopManager(new MyDesktopManager(
427 (Platform.isWindowsAndNotJS() ? new DefaultDesktopManager()
428 : Platform.isAMacAndNotJS()
429 ? new AquaInternalFrameManager(
430 desktop.getDesktopManager())
431 : desktop.getDesktopManager())));
433 Rectangle dims = getLastKnownDimensions("");
440 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
441 int xPos = Math.max(5, (screenSize.width - 900) / 2);
442 int yPos = Math.max(5, (screenSize.height - 650) / 2);
443 setBounds(xPos, yPos, 900, 650);
446 if (!Platform.isJS())
453 jconsole = new Console(this, showjconsole);
454 jconsole.setHeader(Cache.getVersionDetailsForConsole());
455 showConsole(showjconsole);
457 showNews.setVisible(false);
459 experimentalFeatures.setSelected(showExperimental());
461 getIdentifiersOrgData();
465 // Spawn a thread that shows the splashscreen
467 SwingUtilities.invokeLater(new Runnable()
472 new SplashScreen(true);
476 // Thread off a new instance of the file chooser - this reduces the time
478 // takes to open it later on.
479 new Thread(new Runnable()
484 Cache.log.debug("Filechooser init thread started.");
485 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
486 JalviewFileChooser.forRead(Cache.getProperty("LAST_DIRECTORY"),
488 Cache.log.debug("Filechooser init thread finished.");
491 // Add the service change listener
492 changeSupport.addJalviewPropertyChangeListener("services",
493 new PropertyChangeListener()
497 public void propertyChange(PropertyChangeEvent evt)
499 Cache.log.debug("Firing service changed event for "
500 + evt.getNewValue());
501 JalviewServicesChanged(evt);
506 this.setDropTarget(new java.awt.dnd.DropTarget(desktop, this));
508 this.addWindowListener(new WindowAdapter()
511 public void windowClosing(WindowEvent evt)
518 this.addMouseListener(ma = new MouseAdapter()
521 public void mousePressed(MouseEvent evt)
523 if (evt.isPopupTrigger()) // Mac
525 showPasteMenu(evt.getX(), evt.getY());
530 public void mouseReleased(MouseEvent evt)
532 if (evt.isPopupTrigger()) // Windows
534 showPasteMenu(evt.getX(), evt.getY());
538 desktop.addMouseListener(ma);
543 * Answers true if user preferences to enable experimental features is True
548 public boolean showExperimental()
550 String experimental = Cache.getDefault(EXPERIMENTAL_FEATURES,
551 Boolean.FALSE.toString());
552 return Boolean.valueOf(experimental).booleanValue();
555 public void doConfigureStructurePrefs()
557 // configure services
558 StructureSelectionManager ssm = StructureSelectionManager
559 .getStructureSelectionManager(this);
560 if (Cache.getDefault(Preferences.ADD_SS_ANN, true))
562 ssm.setAddTempFacAnnot(Cache
563 .getDefault(Preferences.ADD_TEMPFACT_ANN, true));
564 ssm.setProcessSecondaryStructure(Cache
565 .getDefault(Preferences.STRUCT_FROM_PDB, true));
566 ssm.setSecStructServices(
567 Cache.getDefault(Preferences.USE_RNAVIEW, true));
571 ssm.setAddTempFacAnnot(false);
572 ssm.setProcessSecondaryStructure(false);
573 ssm.setSecStructServices(false);
577 public void checkForNews()
579 final Desktop me = this;
580 // Thread off the news reader, in case there are connection problems.
581 new Thread(new Runnable()
586 Cache.log.debug("Starting news thread.");
587 jvnews = new BlogReader(me);
588 showNews.setVisible(true);
589 Cache.log.debug("Completed news thread.");
594 public void getIdentifiersOrgData()
596 // Thread off the identifiers fetcher
597 new Thread(new Runnable()
602 Cache.log.debug("Downloading data from identifiers.org");
605 UrlDownloadClient.download(IdOrgSettings.getUrl(),
606 IdOrgSettings.getDownloadLocation());
607 } catch (IOException e)
609 Cache.log.debug("Exception downloading identifiers.org data"
618 protected void showNews_actionPerformed(ActionEvent e)
620 showNews(showNews.isSelected());
623 void showNews(boolean visible)
625 Cache.log.debug((visible ? "Showing" : "Hiding") + " news.");
626 showNews.setSelected(visible);
627 if (visible && !jvnews.isVisible())
629 new Thread(new Runnable()
634 long now = System.currentTimeMillis();
635 Desktop.instance.setProgressBar(
636 MessageManager.getString("status.refreshing_news"), now);
637 jvnews.refreshNews();
638 Desktop.instance.setProgressBar(null, now);
646 * recover the last known dimensions for a jalview window
649 * - empty string is desktop, all other windows have unique prefix
650 * @return null or last known dimensions scaled to current geometry (if last
651 * window geom was known)
653 Rectangle getLastKnownDimensions(String windowName)
655 // TODO: lock aspect ratio for scaling desktop Bug #0058199
656 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
657 String x = Cache.getProperty(windowName + "SCREEN_X");
658 String y = Cache.getProperty(windowName + "SCREEN_Y");
660 .getProperty(windowName + "SCREEN_WIDTH");
661 String height = Cache
662 .getProperty(windowName + "SCREEN_HEIGHT");
663 if ((x != null) && (y != null) && (width != null) && (height != null))
665 int ix = Integer.parseInt(x), iy = Integer.parseInt(y),
666 iw = Integer.parseInt(width), ih = Integer.parseInt(height);
667 if (Cache.getProperty("SCREENGEOMETRY_WIDTH") != null)
669 // attempt #1 - try to cope with change in screen geometry - this
670 // version doesn't preserve original jv aspect ratio.
671 // take ratio of current screen size vs original screen size.
672 double sw = ((1f * screenSize.width) / (1f * Integer.parseInt(
673 Cache.getProperty("SCREENGEOMETRY_WIDTH"))));
674 double sh = ((1f * screenSize.height) / (1f * Integer.parseInt(
675 Cache.getProperty("SCREENGEOMETRY_HEIGHT"))));
676 // rescale the bounds depending upon the current screen geometry.
677 ix = (int) (ix * sw);
678 iw = (int) (iw * sw);
679 iy = (int) (iy * sh);
680 ih = (int) (ih * sh);
681 while (ix >= screenSize.width)
684 "Window geometry location recall error: shifting horizontal to within screenbounds.");
685 ix -= screenSize.width;
687 while (iy >= screenSize.height)
690 "Window geometry location recall error: shifting vertical to within screenbounds.");
691 iy -= screenSize.height;
694 "Got last known dimensions for " + windowName + ": x:" + ix
695 + " y:" + iy + " width:" + iw + " height:" + ih);
697 // return dimensions for new instance
698 return new Rectangle(ix, iy, iw, ih);
703 void showPasteMenu(int x, int y)
705 JPopupMenu popup = new JPopupMenu();
706 JMenuItem item = new JMenuItem(
707 MessageManager.getString("label.paste_new_window"));
708 item.addActionListener(new ActionListener()
711 public void actionPerformed(ActionEvent evt)
718 popup.show(this, x, y);
725 Clipboard c = Toolkit.getDefaultToolkit().getSystemClipboard();
726 Transferable contents = c.getContents(this);
728 if (contents != null)
730 String file = (String) contents
731 .getTransferData(DataFlavor.stringFlavor);
733 FileFormatI format = new IdentifyFile().identify(file,
734 DataSourceType.PASTE);
736 new FileLoader().LoadFile(file, DataSourceType.PASTE, format);
739 } catch (Exception ex)
742 "Unable to paste alignment from system clipboard:\n" + ex);
747 * Adds and opens the given frame to the desktop
758 public static synchronized void addInternalFrame(
759 final JInternalFrame frame, String title, int w, int h)
761 addInternalFrame(frame, title, true, w, h, true, false);
765 * Add an internal frame to the Jalview desktop
772 * When true, display frame immediately, otherwise, caller must call
773 * setVisible themselves.
779 public static synchronized void addInternalFrame(
780 final JInternalFrame frame, String title, boolean makeVisible,
783 addInternalFrame(frame, title, makeVisible, w, h, true, false);
787 * Add an internal frame to the Jalview desktop and make it visible
800 public static synchronized void addInternalFrame(
801 final JInternalFrame frame, String title, int w, int h,
804 addInternalFrame(frame, title, true, w, h, resizable, false);
808 * Add an internal frame to the Jalview desktop
815 * When true, display frame immediately, otherwise, caller must call
816 * setVisible themselves.
823 * @param ignoreMinSize
824 * Do not set the default minimum size for frame
826 public static synchronized void addInternalFrame(
827 final JInternalFrame frame, String title, boolean makeVisible,
828 int w, int h, boolean resizable, boolean ignoreMinSize)
831 // TODO: allow callers to determine X and Y position of frame (eg. via
833 // TODO: consider fixing method to update entries in the window submenu with
834 // the current window title
836 frame.setTitle(title);
837 if (frame.getWidth() < 1 || frame.getHeight() < 1)
841 // THIS IS A PUBLIC STATIC METHOD, SO IT MAY BE CALLED EVEN IN
842 // A HEADLESS STATE WHEN NO DESKTOP EXISTS. MUST RETURN
843 // IF JALVIEW IS RUNNING HEADLESS
844 // ///////////////////////////////////////////////
845 if (instance == null || (System.getProperty("java.awt.headless") != null
846 && System.getProperty("java.awt.headless").equals("true")))
855 frame.setMinimumSize(
856 new Dimension(DEFAULT_MIN_WIDTH, DEFAULT_MIN_HEIGHT));
858 // Set default dimension for Alignment Frame window.
859 // The Alignment Frame window could be added from a number of places,
861 // I did this here in order not to miss out on any Alignment frame.
862 if (frame instanceof AlignFrame)
864 frame.setMinimumSize(new Dimension(ALIGN_FRAME_DEFAULT_MIN_WIDTH,
865 ALIGN_FRAME_DEFAULT_MIN_HEIGHT));
869 frame.setVisible(makeVisible);
870 frame.setClosable(true);
871 frame.setResizable(resizable);
872 frame.setMaximizable(resizable);
873 frame.setIconifiable(resizable);
874 frame.setOpaque(Platform.isJS());
876 if (frame.getX() < 1 && frame.getY() < 1)
878 frame.setLocation(xOffset * openFrameCount,
879 yOffset * ((openFrameCount - 1) % 10) + yOffset);
883 * add an entry for the new frame in the Window menu
884 * (and remove it when the frame is closed)
886 final JMenuItem menuItem = new JMenuItem(title);
887 frame.addInternalFrameListener(new InternalFrameAdapter()
890 public void internalFrameActivated(InternalFrameEvent evt)
892 JInternalFrame itf = desktop.getSelectedFrame();
895 if (itf instanceof AlignFrame)
897 Jalview.setCurrentAlignFrame((AlignFrame) itf);
904 public void internalFrameClosed(InternalFrameEvent evt)
906 PaintRefresher.RemoveComponent(frame);
909 * defensive check to prevent frames being
910 * added half off the window
912 if (openFrameCount > 0)
918 * ensure no reference to alignFrame retained by menu item listener
920 if (menuItem.getActionListeners().length > 0)
922 menuItem.removeActionListener(menuItem.getActionListeners()[0]);
924 windowMenu.remove(menuItem);
928 menuItem.addActionListener(new ActionListener()
931 public void actionPerformed(ActionEvent e)
935 frame.setSelected(true);
936 frame.setIcon(false);
937 } catch (java.beans.PropertyVetoException ex)
939 // System.err.println(ex.toString());
944 setKeyBindings(frame);
948 windowMenu.add(menuItem);
953 frame.setSelected(true);
954 frame.requestFocus();
955 } catch (java.beans.PropertyVetoException ve)
957 } catch (java.lang.ClassCastException cex)
960 "Squashed a possible GUI implementation error. If you can recreate this, please look at http://issues.jalview.org/browse/JAL-869",
966 * Add key bindings to a JInternalFrame so that Ctrl-W and Cmd-W will close the
971 private static void setKeyBindings(JInternalFrame frame)
973 @SuppressWarnings("serial")
974 final Action closeAction = new AbstractAction()
977 public void actionPerformed(ActionEvent e)
984 * set up key bindings for Ctrl-W and Cmd-W, with the same (Close) action
986 KeyStroke ctrlWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
987 InputEvent.CTRL_DOWN_MASK);
988 KeyStroke cmdWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
989 ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx());
991 InputMap inputMap = frame
992 .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
993 String ctrlW = ctrlWKey.toString();
994 inputMap.put(ctrlWKey, ctrlW);
995 inputMap.put(cmdWKey, ctrlW);
997 ActionMap actionMap = frame.getActionMap();
998 actionMap.put(ctrlW, closeAction);
1002 public void lostOwnership(Clipboard clipboard, Transferable contents)
1006 Desktop.jalviewClipboard = null;
1009 internalCopy = false;
1013 public void dragEnter(DropTargetDragEvent evt)
1018 public void dragExit(DropTargetEvent evt)
1023 public void dragOver(DropTargetDragEvent evt)
1028 public void dropActionChanged(DropTargetDragEvent evt)
1039 public void drop(DropTargetDropEvent evt)
1041 boolean success = true;
1042 // JAL-1552 - acceptDrop required before getTransferable call for
1043 // Java's Transferable for native dnd
1044 evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
1045 Transferable t = evt.getTransferable();
1046 List<Object> files = new ArrayList<>();
1047 List<DataSourceType> protocols = new ArrayList<>();
1051 Desktop.transferFromDropTarget(files, protocols, evt, t);
1052 } catch (Exception e)
1054 e.printStackTrace();
1062 for (int i = 0; i < files.size(); i++)
1064 // BH 2018 File or String
1065 Object file = files.get(i);
1066 String fileName = file.toString();
1067 DataSourceType protocol = (protocols == null)
1068 ? DataSourceType.FILE
1070 FileFormatI format = null;
1072 if (fileName.endsWith(".jar"))
1074 format = FileFormat.Jalview;
1079 format = new IdentifyFile().identify(file, protocol);
1081 if (file instanceof File)
1083 Platform.cacheFileData((File) file);
1085 new FileLoader().LoadFile(null, file, protocol, format);
1088 } catch (Exception ex)
1093 evt.dropComplete(success); // need this to ensure input focus is properly
1094 // transfered to any new windows created
1104 public void inputLocalFileMenuItem_actionPerformed(AlignViewport viewport)
1106 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
1107 JalviewFileChooser chooser = JalviewFileChooser
1108 .forRead(Cache.getProperty("LAST_DIRECTORY"), fileFormat, BackupFiles.getEnabled());
1110 chooser.setFileView(new JalviewFileView());
1111 chooser.setDialogTitle(
1112 MessageManager.getString("label.open_local_file"));
1113 chooser.setToolTipText(MessageManager.getString("action.open"));
1115 chooser.setResponseHandler(0, new Runnable()
1120 File selectedFile = chooser.getSelectedFile();
1121 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1123 FileFormatI format = chooser.getSelectedFormat();
1126 * Call IdentifyFile to verify the file contains what its extension implies.
1127 * Skip this step for dynamically added file formats, because
1128 * IdentifyFile does not know how to recognise them.
1130 if (FileFormats.getInstance().isIdentifiable(format))
1134 format = new IdentifyFile().identify(selectedFile,
1135 DataSourceType.FILE);
1136 } catch (FileFormatException e)
1138 // format = null; //??
1142 new FileLoader().LoadFile(viewport, selectedFile,
1143 DataSourceType.FILE, format);
1146 chooser.showOpenDialog(this);
1150 * Shows a dialog for input of a URL at which to retrieve alignment data
1155 public void inputURLMenuItem_actionPerformed(AlignViewport viewport)
1157 // This construct allows us to have a wider textfield
1159 JLabel label = new JLabel(
1160 MessageManager.getString("label.input_file_url"));
1162 JPanel panel = new JPanel(new GridLayout(2, 1));
1166 * the URL to fetch is
1167 * Java: an editable combobox with history
1168 * JS: (pending JAL-3038) a plain text field
1171 String urlBase = "http://www.";
1172 if (Platform.isJS())
1174 history = new JTextField(urlBase, 35);
1183 JComboBox<String> asCombo = new JComboBox<>();
1184 asCombo.setPreferredSize(new Dimension(400, 20));
1185 asCombo.setEditable(true);
1186 asCombo.addItem(urlBase);
1187 String historyItems = Cache.getProperty("RECENT_URL");
1188 if (historyItems != null)
1190 for (String token : historyItems.split("\\t"))
1192 asCombo.addItem(token);
1199 Object[] options = new Object[] { MessageManager.getString("action.ok"),
1200 MessageManager.getString("action.cancel") };
1201 Runnable action = new Runnable()
1206 @SuppressWarnings("unchecked")
1207 String url = (history instanceof JTextField
1208 ? ((JTextField) history).getText()
1209 : ((JComboBox<String>) history).getSelectedItem()
1212 if (url.toLowerCase().endsWith(".jar"))
1214 if (viewport != null)
1216 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1217 FileFormat.Jalview);
1221 new FileLoader().LoadFile(url, DataSourceType.URL,
1222 FileFormat.Jalview);
1227 FileFormatI format = null;
1230 format = new IdentifyFile().identify(url, DataSourceType.URL);
1231 } catch (FileFormatException e)
1233 // TODO revise error handling, distinguish between
1234 // URL not found and response not valid
1239 String msg = MessageManager
1240 .formatMessage("label.couldnt_locate", url);
1241 JvOptionPane.showInternalMessageDialog(Desktop.desktop, msg,
1242 MessageManager.getString("label.url_not_found"),
1243 JvOptionPane.WARNING_MESSAGE);
1248 if (viewport != null)
1250 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1255 new FileLoader().LoadFile(url, DataSourceType.URL, format);
1260 String dialogOption = MessageManager
1261 .getString("label.input_alignment_from_url");
1262 JvOptionPane.newOptionDialog(desktop).setResponseHandler(0, action)
1263 .showInternalDialog(panel, dialogOption,
1264 JvOptionPane.YES_NO_CANCEL_OPTION,
1265 JvOptionPane.PLAIN_MESSAGE, null, options,
1266 MessageManager.getString("action.ok"));
1270 * Opens the CutAndPaste window for the user to paste an alignment in to
1273 * - if not null, the pasted alignment is added to the current
1274 * alignment; if null, to a new alignment window
1277 public void inputTextboxMenuItem_actionPerformed(
1278 AlignmentViewPanel viewPanel)
1280 CutAndPasteTransfer cap = new CutAndPasteTransfer();
1281 cap.setForInput(viewPanel);
1282 Desktop.addInternalFrame(cap,
1283 MessageManager.getString("label.cut_paste_alignmen_file"), true,
1293 Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
1294 Cache.setProperty("SCREENGEOMETRY_WIDTH",
1296 Cache.setProperty("SCREENGEOMETRY_HEIGHT",
1297 screen.height + "");
1298 storeLastKnownDimensions("", new Rectangle(getBounds().x, getBounds().y,
1299 getWidth(), getHeight()));
1301 if (jconsole != null)
1303 storeLastKnownDimensions("JAVA_CONSOLE_", jconsole.getBounds());
1304 jconsole.stopConsole();
1308 storeLastKnownDimensions("JALVIEW_RSS_WINDOW_", jvnews.getBounds());
1311 if (dialogExecutor != null)
1313 dialogExecutor.shutdownNow();
1315 closeAll_actionPerformed(null);
1317 if (groovyConsole != null)
1319 // suppress a possible repeat prompt to save script
1320 groovyConsole.setDirty(false);
1321 groovyConsole.exit();
1326 private void storeLastKnownDimensions(String string, Rectangle jc)
1328 Cache.log.debug("Storing last known dimensions for "
1329 + string + ": x:" + jc.x + " y:" + jc.y + " width:" + jc.width
1330 + " height:" + jc.height);
1332 Cache.setProperty(string + "SCREEN_X", jc.x + "");
1333 Cache.setProperty(string + "SCREEN_Y", jc.y + "");
1334 Cache.setProperty(string + "SCREEN_WIDTH", jc.width + "");
1335 Cache.setProperty(string + "SCREEN_HEIGHT", jc.height + "");
1345 public void aboutMenuItem_actionPerformed(ActionEvent e)
1347 new Thread(new Runnable()
1352 new SplashScreen(false);
1358 * Returns the html text for the About screen, including any available version
1359 * number, build details, author details and citation reference, but without
1360 * the enclosing {@code html} tags
1364 public String getAboutMessage()
1366 StringBuilder message = new StringBuilder(1024);
1367 message.append("<h1><strong>Version: ")
1368 .append(Cache.getProperty("VERSION")).append("</strong></h1>")
1369 .append("<strong>Built: <em>")
1370 .append(Cache.getDefault("BUILD_DATE", "unknown"))
1371 .append("</em> from ").append(Cache.getBuildDetailsForSplash())
1372 .append("</strong>");
1374 String latestVersion = Cache.getDefault("LATEST_VERSION", "Checking");
1375 if (latestVersion.equals("Checking"))
1377 // JBP removed this message for 2.11: May be reinstated in future version
1378 // message.append("<br>...Checking latest version...</br>");
1380 else if (!latestVersion.equals(Cache.getProperty("VERSION")))
1382 boolean red = false;
1383 if (Cache.getProperty("VERSION").toLowerCase()
1384 .indexOf("automated build") == -1)
1387 // Displayed when code version and jnlp version do not match and code
1388 // version is not a development build
1389 message.append("<div style=\"color: #FF0000;font-style: bold;\">");
1392 message.append("<br>!! Version ")
1393 .append(Cache.getDefault("LATEST_VERSION", "..Checking.."))
1394 .append(" is available for download from ")
1395 .append(Cache.getDefault("www.jalview.org",
1396 "http://www.jalview.org"))
1400 message.append("</div>");
1403 message.append("<br>Authors: ");
1404 message.append(Cache.getDefault("AUTHORFNAMES", DEFAULT_AUTHORS));
1405 message.append(CITATION);
1407 return message.toString();
1411 * Action on requesting Help documentation
1414 public void documentationMenuItem_actionPerformed()
1418 if (Platform.isJS())
1420 BrowserLauncher.openURL("http://www.jalview.org/help.html");
1429 Help.showHelpWindow();
1431 } catch (Exception ex)
1433 System.err.println("Error opening help: " + ex.getMessage());
1438 public void closeAll_actionPerformed(ActionEvent e)
1440 // TODO show a progress bar while closing?
1441 JInternalFrame[] frames = desktop.getAllFrames();
1442 for (int i = 0; i < frames.length; i++)
1446 frames[i].setClosed(true);
1447 } catch (java.beans.PropertyVetoException ex)
1451 Jalview.setCurrentAlignFrame(null);
1452 System.out.println("ALL CLOSED");
1455 * reset state of singleton objects as appropriate (clear down session state
1456 * when all windows are closed)
1458 StructureSelectionManager ssm = StructureSelectionManager
1459 .getStructureSelectionManager(this);
1467 public void raiseRelated_actionPerformed(ActionEvent e)
1469 reorderAssociatedWindows(false, false);
1473 public void minimizeAssociated_actionPerformed(ActionEvent e)
1475 reorderAssociatedWindows(true, false);
1478 void closeAssociatedWindows()
1480 reorderAssociatedWindows(false, true);
1486 * @seejalview.jbgui.GDesktop#garbageCollect_actionPerformed(java.awt.event.
1490 protected void garbageCollect_actionPerformed(ActionEvent e)
1492 // We simply collect the garbage
1493 Cache.log.debug("Collecting garbage...");
1495 Cache.log.debug("Finished garbage collection.");
1502 * jalview.jbgui.GDesktop#showMemusage_actionPerformed(java.awt.event.ActionEvent
1506 protected void showMemusage_actionPerformed(ActionEvent e)
1508 desktop.showMemoryUsage(showMemusage.isSelected());
1515 * jalview.jbgui.GDesktop#showConsole_actionPerformed(java.awt.event.ActionEvent
1519 protected void showConsole_actionPerformed(ActionEvent e)
1521 showConsole(showConsole.isSelected());
1524 Console jconsole = null;
1527 * control whether the java console is visible or not
1531 void showConsole(boolean selected)
1533 // TODO: decide if we should update properties file
1534 if (jconsole != null) // BH 2018
1536 showConsole.setSelected(selected);
1537 Cache.setProperty("SHOW_JAVA_CONSOLE",
1538 Boolean.valueOf(selected).toString());
1539 jconsole.setVisible(selected);
1543 void reorderAssociatedWindows(boolean minimize, boolean close)
1545 JInternalFrame[] frames = desktop.getAllFrames();
1546 if (frames == null || frames.length < 1)
1551 AlignViewportI source = null;
1552 AlignViewportI target = null;
1553 if (frames[0] instanceof AlignFrame)
1555 source = ((AlignFrame) frames[0]).getCurrentView();
1557 else if (frames[0] instanceof TreePanel)
1559 source = ((TreePanel) frames[0]).getViewPort();
1561 else if (frames[0] instanceof PCAPanel)
1563 source = ((PCAPanel) frames[0]).av;
1565 else if (frames[0].getContentPane() instanceof PairwiseAlignPanel)
1567 source = ((PairwiseAlignPanel) frames[0].getContentPane()).av;
1572 for (int i = 0; i < frames.length; i++)
1575 if (frames[i] == null)
1579 if (frames[i] instanceof AlignFrame)
1581 target = ((AlignFrame) frames[i]).getCurrentView();
1583 else if (frames[i] instanceof TreePanel)
1585 target = ((TreePanel) frames[i]).getViewPort();
1587 else if (frames[i] instanceof PCAPanel)
1589 target = ((PCAPanel) frames[i]).av;
1591 else if (frames[i].getContentPane() instanceof PairwiseAlignPanel)
1593 target = ((PairwiseAlignPanel) frames[i].getContentPane()).av;
1596 if (source == target)
1602 frames[i].setClosed(true);
1606 frames[i].setIcon(minimize);
1609 frames[i].toFront();
1613 } catch (java.beans.PropertyVetoException ex)
1628 protected void preferences_actionPerformed(ActionEvent e)
1634 * Prompts the user to choose a file and then saves the Jalview state as a
1635 * Jalview project file
1638 public void saveState_actionPerformed()
1640 saveState_actionPerformed(false);
1643 public void saveState_actionPerformed(boolean saveAs)
1645 java.io.File projectFile = getProjectFile();
1646 // autoSave indicates we already have a file and don't need to ask
1647 boolean autoSave = projectFile != null && !saveAs
1648 && BackupFiles.getEnabled();
1650 // System.out.println("autoSave="+autoSave+", projectFile='"+projectFile+"',
1651 // saveAs="+saveAs+", Backups
1652 // "+(BackupFiles.getEnabled()?"enabled":"disabled"));
1654 boolean approveSave = false;
1657 JalviewFileChooser chooser = new JalviewFileChooser("jvp",
1660 chooser.setFileView(new JalviewFileView());
1661 chooser.setDialogTitle(MessageManager.getString("label.save_state"));
1663 int value = chooser.showSaveDialog(this);
1665 if (value == JalviewFileChooser.APPROVE_OPTION)
1667 projectFile = chooser.getSelectedFile();
1668 setProjectFile(projectFile);
1673 if (approveSave || autoSave)
1675 final Desktop me = this;
1676 final java.io.File chosenFile = projectFile;
1677 new Thread(new Runnable()
1682 // TODO: refactor to Jalview desktop session controller action.
1683 setProgressBar(MessageManager.formatMessage(
1684 "label.saving_jalview_project", new Object[]
1685 { chosenFile.getName() }), chosenFile.hashCode());
1686 Cache.setProperty("LAST_DIRECTORY",
1687 chosenFile.getParent());
1688 // TODO catch and handle errors for savestate
1689 // TODO prevent user from messing with the Desktop whilst we're saving
1692 boolean doBackup = BackupFiles.getEnabled();
1693 BackupFiles backupfiles = doBackup ? new BackupFiles(chosenFile) : null;
1695 new Jalview2XML().saveState(doBackup ? backupfiles.getTempFile() : chosenFile);
1699 backupfiles.setWriteSuccess(true);
1700 backupfiles.rollBackupsAndRenameTempFile();
1702 } catch (OutOfMemoryError oom)
1704 new OOMWarning("Whilst saving current state to "
1705 + chosenFile.getName(), oom);
1706 } catch (Exception ex)
1708 Cache.log.error("Problems whilst trying to save to "
1709 + chosenFile.getName(), ex);
1710 JvOptionPane.showMessageDialog(me,
1711 MessageManager.formatMessage(
1712 "label.error_whilst_saving_current_state_to",
1714 { chosenFile.getName() }),
1715 MessageManager.getString("label.couldnt_save_project"),
1716 JvOptionPane.WARNING_MESSAGE);
1718 setProgressBar(null, chosenFile.hashCode());
1725 public void saveAsState_actionPerformed(ActionEvent e)
1727 saveState_actionPerformed(true);
1730 private void setProjectFile(File choice)
1732 this.projectFile = choice;
1735 public File getProjectFile()
1737 return this.projectFile;
1741 * Shows a file chooser dialog and tries to read in the selected file as a
1745 public void loadState_actionPerformed()
1747 final String[] suffix = new String[] { "jvp", "jar" };
1748 final String[] desc = new String[] { "Jalview Project",
1749 "Jalview Project (old)" };
1750 JalviewFileChooser chooser = new JalviewFileChooser(
1751 Cache.getProperty("LAST_DIRECTORY"), suffix, desc,
1752 "Jalview Project", true, BackupFiles.getEnabled()); // last two booleans: allFiles,
1754 chooser.setFileView(new JalviewFileView());
1755 chooser.setDialogTitle(MessageManager.getString("label.restore_state"));
1756 chooser.setResponseHandler(0, new Runnable()
1761 File selectedFile = chooser.getSelectedFile();
1762 setProjectFile(selectedFile);
1763 String choice = selectedFile.getAbsolutePath();
1764 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1765 new Thread(new Runnable()
1772 new Jalview2XML().loadJalviewAlign(selectedFile);
1773 } catch (OutOfMemoryError oom)
1775 new OOMWarning("Whilst loading project from " + choice, oom);
1776 } catch (Exception ex)
1779 "Problems whilst loading project from " + choice, ex);
1780 JvOptionPane.showMessageDialog(Desktop.desktop,
1781 MessageManager.formatMessage(
1782 "label.error_whilst_loading_project_from",
1785 MessageManager.getString("label.couldnt_load_project"),
1786 JvOptionPane.WARNING_MESSAGE);
1793 chooser.showOpenDialog(this);
1797 public void inputSequence_actionPerformed(ActionEvent e)
1799 new SequenceFetcher(this);
1802 JPanel progressPanel;
1804 ArrayList<JPanel> fileLoadingPanels = new ArrayList<>();
1806 public void startLoading(final Object fileName)
1808 if (fileLoadingCount == 0)
1810 fileLoadingPanels.add(addProgressPanel(MessageManager
1811 .formatMessage("label.loading_file", new Object[]
1817 private JPanel addProgressPanel(String string)
1819 if (progressPanel == null)
1821 progressPanel = new JPanel(new GridLayout(1, 1));
1822 totalProgressCount = 0;
1823 instance.getContentPane().add(progressPanel, BorderLayout.SOUTH);
1825 JPanel thisprogress = new JPanel(new BorderLayout(10, 5));
1826 JProgressBar progressBar = new JProgressBar();
1827 progressBar.setIndeterminate(true);
1829 thisprogress.add(new JLabel(string), BorderLayout.WEST);
1831 thisprogress.add(progressBar, BorderLayout.CENTER);
1832 progressPanel.add(thisprogress);
1833 ((GridLayout) progressPanel.getLayout()).setRows(
1834 ((GridLayout) progressPanel.getLayout()).getRows() + 1);
1835 ++totalProgressCount;
1836 instance.validate();
1837 return thisprogress;
1840 int totalProgressCount = 0;
1842 private void removeProgressPanel(JPanel progbar)
1844 if (progressPanel != null)
1846 synchronized (progressPanel)
1848 progressPanel.remove(progbar);
1849 GridLayout gl = (GridLayout) progressPanel.getLayout();
1850 gl.setRows(gl.getRows() - 1);
1851 if (--totalProgressCount < 1)
1853 this.getContentPane().remove(progressPanel);
1854 progressPanel = null;
1861 public void stopLoading()
1864 if (fileLoadingCount < 1)
1866 while (fileLoadingPanels.size() > 0)
1868 removeProgressPanel(fileLoadingPanels.remove(0));
1870 fileLoadingPanels.clear();
1871 fileLoadingCount = 0;
1876 public static int getViewCount(String alignmentId)
1878 AlignmentViewport[] aps = getViewports(alignmentId);
1879 return (aps == null) ? 0 : aps.length;
1884 * @param alignmentId
1885 * - if null, all sets are returned
1886 * @return all AlignmentPanels concerning the alignmentId sequence set
1888 public static AlignmentPanel[] getAlignmentPanels(String alignmentId)
1890 if (Desktop.desktop == null)
1892 // no frames created and in headless mode
1893 // TODO: verify that frames are recoverable when in headless mode
1896 List<AlignmentPanel> aps = new ArrayList<>();
1897 AlignFrame[] frames = getAlignFrames();
1902 for (AlignFrame af : frames)
1904 for (AlignmentPanel ap : af.alignPanels)
1906 if (alignmentId == null
1907 || alignmentId.equals(ap.av.getSequenceSetId()))
1913 if (aps.size() == 0)
1917 AlignmentPanel[] vap = aps.toArray(new AlignmentPanel[aps.size()]);
1922 * get all the viewports on an alignment.
1924 * @param sequenceSetId
1925 * unique alignment id (may be null - all viewports returned in that
1927 * @return all viewports on the alignment bound to sequenceSetId
1929 public static AlignmentViewport[] getViewports(String sequenceSetId)
1931 List<AlignmentViewport> viewp = new ArrayList<>();
1932 if (desktop != null)
1934 AlignFrame[] frames = Desktop.getAlignFrames();
1936 for (AlignFrame afr : frames)
1938 if (sequenceSetId == null || afr.getViewport().getSequenceSetId()
1939 .equals(sequenceSetId))
1941 if (afr.alignPanels != null)
1943 for (AlignmentPanel ap : afr.alignPanels)
1945 if (sequenceSetId == null
1946 || sequenceSetId.equals(ap.av.getSequenceSetId()))
1954 viewp.add(afr.getViewport());
1958 if (viewp.size() > 0)
1960 return viewp.toArray(new AlignmentViewport[viewp.size()]);
1967 * Explode the views in the given frame into separate AlignFrame
1971 public static void explodeViews(AlignFrame af)
1973 int size = af.alignPanels.size();
1979 // FIXME: ideally should use UI interface API
1980 FeatureSettings viewFeatureSettings = (af.featureSettings != null
1981 && af.featureSettings.isOpen())
1982 ? af.featureSettings
1984 Rectangle fsBounds = af.getFeatureSettingsGeometry();
1985 for (int i = 0; i < size; i++)
1987 AlignmentPanel ap = af.alignPanels.get(i);
1989 AlignFrame newaf = new AlignFrame(ap);
1991 // transfer reference for existing feature settings to new alignFrame
1992 if (ap == af.alignPanel)
1994 if (viewFeatureSettings != null && viewFeatureSettings.fr.ap == ap)
1996 newaf.featureSettings = viewFeatureSettings;
1998 newaf.setFeatureSettingsGeometry(fsBounds);
2002 * Restore the view's last exploded frame geometry if known. Multiple
2003 * views from one exploded frame share and restore the same (frame)
2004 * position and size.
2006 Rectangle geometry = ap.av.getExplodedGeometry();
2007 if (geometry != null)
2009 newaf.setBounds(geometry);
2012 ap.av.setGatherViewsHere(false);
2014 addInternalFrame(newaf, af.getTitle(), AlignFrame.DEFAULT_WIDTH,
2015 AlignFrame.DEFAULT_HEIGHT);
2016 // and materialise a new feature settings dialog instance for the new alignframe
2017 // (closes the old as if 'OK' was pressed)
2018 if (ap == af.alignPanel && newaf.featureSettings != null
2019 && newaf.featureSettings.isOpen()
2020 && af.alignPanel.getAlignViewport().isShowSequenceFeatures())
2022 newaf.showFeatureSettingsUI();
2026 af.featureSettings = null;
2027 af.alignPanels.clear();
2028 af.closeMenuItem_actionPerformed(true);
2033 * Gather expanded views (separate AlignFrame's) with the same sequence set
2034 * identifier back in to this frame as additional views, and close the expanded
2035 * views. Note the expanded frames may themselves have multiple views. We take
2040 public void gatherViews(AlignFrame source)
2042 source.viewport.setGatherViewsHere(true);
2043 source.viewport.setExplodedGeometry(source.getBounds());
2044 JInternalFrame[] frames = desktop.getAllFrames();
2045 String viewId = source.viewport.getSequenceSetId();
2046 for (int t = 0; t < frames.length; t++)
2048 if (frames[t] instanceof AlignFrame && frames[t] != source)
2050 AlignFrame af = (AlignFrame) frames[t];
2051 boolean gatherThis = false;
2052 for (int a = 0; a < af.alignPanels.size(); a++)
2054 AlignmentPanel ap = af.alignPanels.get(a);
2055 if (viewId.equals(ap.av.getSequenceSetId()))
2058 ap.av.setGatherViewsHere(false);
2059 ap.av.setExplodedGeometry(af.getBounds());
2060 source.addAlignmentPanel(ap, false);
2066 if (af.featureSettings != null && af.featureSettings.isOpen())
2068 if (source.featureSettings == null)
2070 // preserve the feature settings geometry for this frame
2071 source.featureSettings = af.featureSettings;
2072 source.setFeatureSettingsGeometry(
2073 af.getFeatureSettingsGeometry());
2077 // close it and forget
2078 af.featureSettings.close();
2081 af.alignPanels.clear();
2082 af.closeMenuItem_actionPerformed(true);
2087 // refresh the feature setting UI for the source frame if it exists
2088 if (source.featureSettings != null
2089 && source.featureSettings.isOpen())
2091 source.showFeatureSettingsUI();
2095 public JInternalFrame[] getAllFrames()
2097 return desktop.getAllFrames();
2101 * Checks the given url to see if it gives a response indicating that the user
2102 * should be informed of a new questionnaire.
2106 public void checkForQuestionnaire(String url)
2108 UserQuestionnaireCheck jvq = new UserQuestionnaireCheck(url);
2109 // javax.swing.SwingUtilities.invokeLater(jvq);
2110 new Thread(jvq).start();
2113 public void checkURLLinks()
2115 // Thread off the URL link checker
2116 addDialogThread(new Runnable()
2121 if (Cache.getDefault("CHECKURLLINKS", true))
2123 // check what the actual links are - if it's just the default don't
2124 // bother with the warning
2125 List<String> links = Preferences.sequenceUrlLinks
2128 // only need to check links if there is one with a
2129 // SEQUENCE_ID which is not the default EMBL_EBI link
2130 ListIterator<String> li = links.listIterator();
2131 boolean check = false;
2132 List<JLabel> urls = new ArrayList<>();
2133 while (li.hasNext())
2135 String link = li.next();
2136 if (link.contains(jalview.util.UrlConstants.SEQUENCE_ID)
2137 && !UrlConstants.isDefaultString(link))
2140 int barPos = link.indexOf("|");
2141 String urlMsg = barPos == -1 ? link
2142 : link.substring(0, barPos) + ": "
2143 + link.substring(barPos + 1);
2144 urls.add(new JLabel(urlMsg));
2152 // ask user to check in case URL links use old style tokens
2153 // ($SEQUENCE_ID$ for sequence id _or_ accession id)
2154 JPanel msgPanel = new JPanel();
2155 msgPanel.setLayout(new BoxLayout(msgPanel, BoxLayout.PAGE_AXIS));
2156 msgPanel.add(Box.createVerticalGlue());
2157 JLabel msg = new JLabel(MessageManager
2158 .getString("label.SEQUENCE_ID_for_DB_ACCESSION1"));
2159 JLabel msg2 = new JLabel(MessageManager
2160 .getString("label.SEQUENCE_ID_for_DB_ACCESSION2"));
2162 for (JLabel url : urls)
2168 final JCheckBox jcb = new JCheckBox(
2169 MessageManager.getString("label.do_not_display_again"));
2170 jcb.addActionListener(new ActionListener()
2173 public void actionPerformed(ActionEvent e)
2175 // update Cache settings for "don't show this again"
2176 boolean showWarningAgain = !jcb.isSelected();
2177 Cache.setProperty("CHECKURLLINKS",
2178 Boolean.valueOf(showWarningAgain).toString());
2183 JvOptionPane.showMessageDialog(Desktop.desktop, msgPanel,
2185 .getString("label.SEQUENCE_ID_no_longer_used"),
2186 JvOptionPane.WARNING_MESSAGE);
2193 * Proxy class for JDesktopPane which optionally displays the current memory
2194 * usage and highlights the desktop area with a red bar if free memory runs low.
2198 public class MyDesktopPane extends JDesktopPane
2201 private static final float ONE_MB = 1048576f;
2203 boolean showMemoryUsage = false;
2207 java.text.NumberFormat df;
2209 float maxMemory, allocatedMemory, freeMemory, totalFreeMemory,
2212 public MyDesktopPane(boolean showMemoryUsage)
2214 showMemoryUsage(showMemoryUsage);
2217 public void showMemoryUsage(boolean showMemory)
2219 this.showMemoryUsage = showMemory;
2222 Thread worker = new Thread(this);
2228 public boolean isShowMemoryUsage()
2230 return showMemoryUsage;
2236 df = java.text.NumberFormat.getNumberInstance();
2237 df.setMaximumFractionDigits(2);
2238 runtime = Runtime.getRuntime();
2240 while (showMemoryUsage)
2244 maxMemory = runtime.maxMemory() / ONE_MB;
2245 allocatedMemory = runtime.totalMemory() / ONE_MB;
2246 freeMemory = runtime.freeMemory() / ONE_MB;
2247 totalFreeMemory = freeMemory + (maxMemory - allocatedMemory);
2249 percentUsage = (totalFreeMemory / maxMemory) * 100;
2251 // if (percentUsage < 20)
2253 // border1 = BorderFactory.createMatteBorder(12, 12, 12, 12,
2255 // instance.set.setBorder(border1);
2258 // sleep after showing usage
2260 } catch (Exception ex)
2262 ex.printStackTrace();
2268 public void paintComponent(Graphics g)
2270 if (showMemoryUsage && g != null && df != null)
2272 if (percentUsage < 20)
2274 g.setColor(Color.red);
2276 FontMetrics fm = g.getFontMetrics();
2279 g.drawString(MessageManager.formatMessage("label.memory_stats",
2281 { df.format(totalFreeMemory), df.format(maxMemory),
2282 df.format(percentUsage) }),
2283 10, getHeight() - fm.getHeight());
2290 * Accessor method to quickly get all the AlignmentFrames loaded.
2292 * @return an array of AlignFrame, or null if none found
2294 public static AlignFrame[] getAlignFrames()
2296 if (Jalview.isHeadlessMode())
2298 // Desktop.desktop is null in headless mode
2299 return new AlignFrame[] { Jalview.currentAlignFrame };
2302 JInternalFrame[] frames = Desktop.desktop.getAllFrames();
2308 List<AlignFrame> avp = new ArrayList<>();
2310 for (int i = frames.length - 1; i > -1; i--)
2312 if (frames[i] instanceof AlignFrame)
2314 avp.add((AlignFrame) frames[i]);
2316 else if (frames[i] instanceof SplitFrame)
2319 * Also check for a split frame containing an AlignFrame
2321 GSplitFrame sf = (GSplitFrame) frames[i];
2322 if (sf.getTopFrame() instanceof AlignFrame)
2324 avp.add((AlignFrame) sf.getTopFrame());
2326 if (sf.getBottomFrame() instanceof AlignFrame)
2328 avp.add((AlignFrame) sf.getBottomFrame());
2332 if (avp.size() == 0)
2336 AlignFrame afs[] = avp.toArray(new AlignFrame[avp.size()]);
2341 * Returns an array of any AppJmol frames in the Desktop (or null if none).
2345 public GStructureViewer[] getJmols()
2347 JInternalFrame[] frames = Desktop.desktop.getAllFrames();
2353 List<GStructureViewer> avp = new ArrayList<>();
2355 for (int i = frames.length - 1; i > -1; i--)
2357 if (frames[i] instanceof AppJmol)
2359 GStructureViewer af = (GStructureViewer) frames[i];
2363 if (avp.size() == 0)
2367 GStructureViewer afs[] = avp.toArray(new GStructureViewer[avp.size()]);
2372 * Add Groovy Support to Jalview
2375 public void groovyShell_actionPerformed()
2379 openGroovyConsole();
2380 } catch (Exception ex)
2382 Cache.log.error("Groovy Shell Creation failed.", ex);
2383 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
2385 MessageManager.getString("label.couldnt_create_groovy_shell"),
2386 MessageManager.getString("label.groovy_support_failed"),
2387 JvOptionPane.ERROR_MESSAGE);
2392 * Open the Groovy console
2394 void openGroovyConsole()
2396 if (groovyConsole == null)
2398 groovyConsole = new groovy.ui.Console();
2399 groovyConsole.setVariable("Jalview", this);
2400 groovyConsole.run();
2403 * We allow only one console at a time, so that AlignFrame menu option
2404 * 'Calculate | Run Groovy script' is unambiguous.
2405 * Disable 'Groovy Console', and enable 'Run script', when the console is
2406 * opened, and the reverse when it is closed
2408 Window window = (Window) groovyConsole.getFrame();
2409 window.addWindowListener(new WindowAdapter()
2412 public void windowClosed(WindowEvent e)
2415 * rebind CMD-Q from Groovy Console to Jalview Quit
2418 enableExecuteGroovy(false);
2424 * show Groovy console window (after close and reopen)
2426 ((Window) groovyConsole.getFrame()).setVisible(true);
2429 * if we got this far, enable 'Run Groovy' in AlignFrame menus
2430 * and disable opening a second console
2432 enableExecuteGroovy(true);
2436 * Bind Ctrl/Cmd-Q to Quit - for reset as Groovy Console takes over this binding
2439 protected void addQuitHandler()
2441 getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
2442 .put(KeyStroke.getKeyStroke(KeyEvent.VK_Q,
2443 jalview.util.ShortcutKeyMaskExWrapper.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. It
2573 * will break if there are more than one AlignFrames viewing a particular av.
2576 * @return alignFrame for viewport
2578 public static AlignFrame getAlignFrameFor(AlignViewportI viewport)
2580 if (desktop != null)
2582 AlignmentPanel[] aps = getAlignmentPanels(
2583 viewport.getSequenceSetId());
2584 for (int panel = 0; aps != null && panel < aps.length; panel++)
2586 if (aps[panel] != null && aps[panel].av == viewport)
2588 return aps[panel].alignFrame;
2595 public VamsasApplication getVamsasApplication()
2597 // TODO: JAL-3311 remove remaining code from Jalview relating to VAMSAS
2603 * flag set if jalview GUI is being operated programmatically
2605 private boolean inBatchMode = false;
2608 * check if jalview GUI is being operated programmatically
2610 * @return inBatchMode
2612 public boolean isInBatchMode()
2618 * set flag if jalview GUI is being operated programmatically
2620 * @param inBatchMode
2622 public void setInBatchMode(boolean inBatchMode)
2624 this.inBatchMode = inBatchMode;
2627 public void startServiceDiscovery()
2629 startServiceDiscovery(false);
2632 public void startServiceDiscovery(boolean blocking)
2634 boolean alive = true;
2635 Thread t0 = null, t1 = null, t2 = null, t3 = null;
2636 // JAL-940 - JALVIEW 1 services are now being EOLed as of JABA 2.1 release
2639 // todo: changesupport handlers need to be transferred
2640 if (discoverer == null)
2642 discoverer = new jalview.ws.jws1.Discoverer();
2643 // register PCS handler for desktop.
2644 discoverer.addPropertyChangeListener(changeSupport);
2646 // JAL-940 - disabled JWS1 service configuration - always start discoverer
2647 // until we phase out completely
2648 (t0 = new Thread(discoverer)).start();
2651 if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
2653 t2 = startServiceDiscovery(
2654 jalview.ws.jws2.Jws2Discoverer.getDiscoverer(), false);
2656 if (Cache.getDefault("SHOW_SLIVKA_SERVICES", true))
2658 // start slivka discovery
2659 t3 = startServiceDiscovery(
2660 jalview.ws.slivkaws.SlivkaWSDiscoverer.getInstance(), false);
2669 } catch (Exception e)
2672 // FIXME: Condition should check the discoverer's isRunning rather than
2674 alive = (t1 != null && t1.isAlive()) || (t2 != null && t2.isAlive())
2675 || (t3 != null && t3.isAlive()) || (t0 != null && t0.isAlive());
2680 public Thread startServiceDiscovery(WSDiscovererI discoverer,
2683 Thread thread = discoverer.startDiscoverer(changeSupport);
2689 } catch (InterruptedException e)
2691 e.printStackTrace();
2698 * called to check if the service discovery process completed successfully.
2702 protected void JalviewServicesChanged(PropertyChangeEvent evt)
2704 if (evt.getNewValue() == null || evt.getNewValue() instanceof Vector)
2706 final WSDiscovererI discoverer = jalview.ws.jws2.Jws2Discoverer
2708 final String ermsg = discoverer.getErrorMessages();
2711 if (Cache.getDefault("SHOW_WSDISCOVERY_ERRORS", true))
2713 if (serviceChangedDialog == null)
2715 // only run if we aren't already displaying one of these.
2716 addDialogThread(serviceChangedDialog = new Runnable()
2723 * JalviewDialog jd =new JalviewDialog() {
2725 * @Override protected void cancelPressed() { // TODO
2726 * Auto-generated method stub
2728 * }@Override protected void okPressed() { // TODO
2729 * Auto-generated method stub
2731 * }@Override protected void raiseClosed() { // TODO
2732 * Auto-generated method stub
2734 * } }; jd.initDialogFrame(new
2735 * JLabel("<html><table width=\"450\"><tr><td>" + ermsg +
2736 * "<br/>It may be that you have invalid JABA URLs in your web service preferences,"
2737 * + " or mis-configured HTTP proxy settings.<br/>" +
2738 * "Check the <em>Connections</em> and <em>Web services</em> tab of the"
2740 * " Tools->Preferences dialog box to change them.</td></tr></table></html>"
2741 * ), true, true, "Web Service Configuration Problem", 450,
2744 * jd.waitForInput();
2746 JvOptionPane.showConfirmDialog(Desktop.desktop,
2747 new JLabel("<html><table width=\"450\"><tr><td>"
2748 + ermsg + "</td></tr></table>"
2749 + "<p>It may be that you have invalid JABA URLs<br/>in your web service preferences,"
2750 + "<br>or as a command-line argument, or mis-configured HTTP proxy settings.</p>"
2751 + "<p>Check the <em>Connections</em> and <em>Web services</em> tab<br/>of the"
2752 + " Tools->Preferences dialog box to change them.</p></html>"),
2753 "Web Service Configuration Problem",
2754 JvOptionPane.DEFAULT_OPTION,
2755 JvOptionPane.ERROR_MESSAGE);
2756 serviceChangedDialog = null;
2765 "Errors reported by JABA discovery service. Check web services preferences.\n"
2772 private Runnable serviceChangedDialog = null;
2775 * start a thread to open a URL in the configured browser. Pops up a warning
2776 * dialog to the user if there is an exception when calling out to the browser
2781 public static void showUrl(final String url)
2783 showUrl(url, Desktop.instance);
2787 * Like showUrl but allows progress handler to be specified
2791 * (null) or object implementing IProgressIndicator
2793 public static void showUrl(final String url,
2794 final IProgressIndicator progress)
2796 new Thread(new Runnable()
2803 if (progress != null)
2805 progress.setProgressBar(MessageManager
2806 .formatMessage("status.opening_params", new Object[]
2807 { url }), this.hashCode());
2809 jalview.util.BrowserLauncher.openURL(url);
2810 } catch (Exception ex)
2812 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
2814 .getString("label.web_browser_not_found_unix"),
2815 MessageManager.getString("label.web_browser_not_found"),
2816 JvOptionPane.WARNING_MESSAGE);
2818 ex.printStackTrace();
2820 if (progress != null)
2822 progress.setProgressBar(null, this.hashCode());
2828 public static WsParamSetManager wsparamManager = null;
2830 public static ParamManager getUserParameterStore()
2832 if (wsparamManager == null)
2834 wsparamManager = new WsParamSetManager();
2836 return wsparamManager;
2840 * static hyperlink handler proxy method for use by Jalview's internal windows
2844 public static void hyperlinkUpdate(HyperlinkEvent e)
2846 if (e.getEventType() == EventType.ACTIVATED)
2851 url = e.getURL().toString();
2852 Desktop.showUrl(url);
2853 } catch (Exception x)
2857 if (Cache.log != null)
2859 Cache.log.error("Couldn't handle string " + url + " as a URL.");
2864 "Couldn't handle string " + url + " as a URL.");
2867 // ignore any exceptions due to dud links.
2874 * single thread that handles display of dialogs to user.
2876 ExecutorService dialogExecutor = Executors.newSingleThreadExecutor();
2879 * flag indicating if dialogExecutor should try to acquire a permit
2881 private volatile boolean dialogPause = true;
2886 private java.util.concurrent.Semaphore block = new Semaphore(0);
2888 private static groovy.ui.Console groovyConsole;
2891 * add another dialog thread to the queue
2895 public void addDialogThread(final Runnable prompter)
2897 dialogExecutor.submit(new Runnable()
2907 } catch (InterruptedException x)
2911 if (instance == null)
2917 SwingUtilities.invokeAndWait(prompter);
2918 } catch (Exception q)
2920 Cache.log.warn("Unexpected Exception in dialog thread.", q);
2926 public void startDialogQueue()
2928 // set the flag so we don't pause waiting for another permit and semaphore
2929 // the current task to begin
2930 dialogPause = false;
2935 * Outputs an image of the desktop to file in EPS format, after prompting the
2936 * user for choice of Text or Lineart character rendering (unless a preference
2937 * has been set). The file name is generated as
2940 * Jalview_snapshot_nnnnn.eps where nnnnn is the current timestamp in milliseconds
2944 protected void snapShotWindow_actionPerformed(ActionEvent e)
2946 // currently the menu option to do this is not shown
2949 int width = getWidth();
2950 int height = getHeight();
2952 "Jalview_snapshot_" + System.currentTimeMillis() + ".eps");
2953 ImageWriterI writer = new ImageWriterI()
2956 public void exportImage(Graphics g) throws Exception
2959 Cache.log.info("Successfully written snapshot to file "
2960 + of.getAbsolutePath());
2963 String title = "View of desktop";
2964 ImageExporter exporter = new ImageExporter(writer, null, TYPE.EPS,
2966 exporter.doExport(of, this, width, height, title);
2970 * Explode the views in the given SplitFrame into separate SplitFrame windows.
2971 * This respects (remembers) any previous 'exploded geometry' i.e. the size and
2972 * location last time the view was expanded (if any). However it does not
2973 * remember the split pane divider location - this is set to match the
2974 * 'exploding' frame.
2978 public void explodeViews(SplitFrame sf)
2980 AlignFrame oldTopFrame = (AlignFrame) sf.getTopFrame();
2981 AlignFrame oldBottomFrame = (AlignFrame) sf.getBottomFrame();
2982 List<? extends AlignmentViewPanel> topPanels = oldTopFrame
2984 List<? extends AlignmentViewPanel> bottomPanels = oldBottomFrame
2986 int viewCount = topPanels.size();
2993 * Processing in reverse order works, forwards order leaves the first panels
2994 * not visible. I don't know why!
2996 for (int i = viewCount - 1; i >= 0; i--)
2999 * Make new top and bottom frames. These take over the respective
3000 * AlignmentPanel objects, including their AlignmentViewports, so the
3001 * cdna/protein relationships between the viewports is carried over to the
3004 * explodedGeometry holds the (x, y) position of the previously exploded
3005 * SplitFrame, and the (width, height) of the AlignFrame component
3007 AlignmentPanel topPanel = (AlignmentPanel) topPanels.get(i);
3008 AlignFrame newTopFrame = new AlignFrame(topPanel);
3009 newTopFrame.setSize(oldTopFrame.getSize());
3010 newTopFrame.setVisible(true);
3011 Rectangle geometry = ((AlignViewport) topPanel.getAlignViewport())
3012 .getExplodedGeometry();
3013 if (geometry != null)
3015 newTopFrame.setSize(geometry.getSize());
3018 AlignmentPanel bottomPanel = (AlignmentPanel) bottomPanels.get(i);
3019 AlignFrame newBottomFrame = new AlignFrame(bottomPanel);
3020 newBottomFrame.setSize(oldBottomFrame.getSize());
3021 newBottomFrame.setVisible(true);
3022 geometry = ((AlignViewport) bottomPanel.getAlignViewport())
3023 .getExplodedGeometry();
3024 if (geometry != null)
3026 newBottomFrame.setSize(geometry.getSize());
3029 topPanel.av.setGatherViewsHere(false);
3030 bottomPanel.av.setGatherViewsHere(false);
3031 JInternalFrame splitFrame = new SplitFrame(newTopFrame,
3033 if (geometry != null)
3035 splitFrame.setLocation(geometry.getLocation());
3037 Desktop.addInternalFrame(splitFrame, sf.getTitle(), -1, -1);
3041 * Clear references to the panels (now relocated in the new SplitFrames)
3042 * before closing the old SplitFrame.
3045 bottomPanels.clear();
3050 * Gather expanded split frames, sharing the same pairs of sequence set ids,
3051 * back into the given SplitFrame as additional views. Note that the gathered
3052 * frames may themselves have multiple views.
3056 public void gatherViews(GSplitFrame source)
3059 * special handling of explodedGeometry for a view within a SplitFrame: - it
3060 * holds the (x, y) position of the enclosing SplitFrame, and the (width,
3061 * height) of the AlignFrame component
3063 AlignFrame myTopFrame = (AlignFrame) source.getTopFrame();
3064 AlignFrame myBottomFrame = (AlignFrame) source.getBottomFrame();
3065 myTopFrame.viewport.setExplodedGeometry(new Rectangle(source.getX(),
3066 source.getY(), myTopFrame.getWidth(), myTopFrame.getHeight()));
3067 myBottomFrame.viewport
3068 .setExplodedGeometry(new Rectangle(source.getX(), source.getY(),
3069 myBottomFrame.getWidth(), myBottomFrame.getHeight()));
3070 myTopFrame.viewport.setGatherViewsHere(true);
3071 myBottomFrame.viewport.setGatherViewsHere(true);
3072 String topViewId = myTopFrame.viewport.getSequenceSetId();
3073 String bottomViewId = myBottomFrame.viewport.getSequenceSetId();
3075 JInternalFrame[] frames = desktop.getAllFrames();
3076 for (JInternalFrame frame : frames)
3078 if (frame instanceof SplitFrame && frame != source)
3080 SplitFrame sf = (SplitFrame) frame;
3081 AlignFrame topFrame = (AlignFrame) sf.getTopFrame();
3082 AlignFrame bottomFrame = (AlignFrame) sf.getBottomFrame();
3083 boolean gatherThis = false;
3084 for (int a = 0; a < topFrame.alignPanels.size(); a++)
3086 AlignmentPanel topPanel = topFrame.alignPanels.get(a);
3087 AlignmentPanel bottomPanel = bottomFrame.alignPanels.get(a);
3088 if (topViewId.equals(topPanel.av.getSequenceSetId())
3089 && bottomViewId.equals(bottomPanel.av.getSequenceSetId()))
3092 topPanel.av.setGatherViewsHere(false);
3093 bottomPanel.av.setGatherViewsHere(false);
3094 topPanel.av.setExplodedGeometry(
3095 new Rectangle(sf.getLocation(), topFrame.getSize()));
3096 bottomPanel.av.setExplodedGeometry(
3097 new Rectangle(sf.getLocation(), bottomFrame.getSize()));
3098 myTopFrame.addAlignmentPanel(topPanel, false);
3099 myBottomFrame.addAlignmentPanel(bottomPanel, false);
3105 topFrame.getAlignPanels().clear();
3106 bottomFrame.getAlignPanels().clear();
3113 * The dust settles...give focus to the tab we did this from.
3115 myTopFrame.setDisplayedView(myTopFrame.alignPanel);
3118 public static groovy.ui.Console getGroovyConsole()
3120 return groovyConsole;
3124 * handles the payload of a drag and drop event.
3126 * TODO refactor to desktop utilities class
3129 * - Data source strings extracted from the drop event
3131 * - protocol for each data source extracted from the drop event
3135 * - the payload from the drop event
3138 public static void transferFromDropTarget(List<Object> files,
3139 List<DataSourceType> protocols, DropTargetDropEvent evt,
3140 Transferable t) throws Exception
3143 // BH 2018 changed List<String> to List<Object> to allow for File from SwingJS
3145 // DataFlavor[] flavors = t.getTransferDataFlavors();
3146 // for (int i = 0; i < flavors.length; i++) {
3147 // if (flavors[i].isFlavorJavaFileListType()) {
3148 // evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
3149 // List<File> list = (List<File>) t.getTransferData(flavors[i]);
3150 // for (int j = 0; j < list.size(); j++) {
3151 // File file = (File) list.get(j);
3152 // byte[] data = getDroppedFileBytes(file);
3153 // fileName.setText(file.getName() + " - " + data.length + " " +
3154 // evt.getLocation());
3155 // JTextArea target = (JTextArea) ((DropTarget) evt.getSource()).getComponent();
3156 // target.setText(new String(data));
3158 // dtde.dropComplete(true);
3163 DataFlavor uriListFlavor = new DataFlavor(
3164 "text/uri-list;class=java.lang.String"), urlFlavour = null;
3167 urlFlavour = new DataFlavor(
3168 "application/x-java-url; class=java.net.URL");
3169 } catch (ClassNotFoundException cfe)
3171 Cache.log.debug("Couldn't instantiate the URL dataflavor.", cfe);
3174 if (urlFlavour != null && t.isDataFlavorSupported(urlFlavour))
3179 java.net.URL url = (URL) t.getTransferData(urlFlavour);
3180 // nb: java 8 osx bug https://bugs.openjdk.java.net/browse/JDK-8156099
3181 // means url may be null.
3184 protocols.add(DataSourceType.URL);
3185 files.add(url.toString());
3186 Cache.log.debug("Drop handled as URL dataflavor "
3187 + files.get(files.size() - 1));
3192 if (Platform.isAMacAndNotJS())
3195 "Please ignore plist error - occurs due to problem with java 8 on OSX");
3198 } catch (Throwable ex)
3200 Cache.log.debug("URL drop handler failed.", ex);
3203 if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
3205 // Works on Windows and MacOSX
3206 Cache.log.debug("Drop handled as javaFileListFlavor");
3207 for (Object file : (List) t
3208 .getTransferData(DataFlavor.javaFileListFlavor))
3211 protocols.add(DataSourceType.FILE);
3216 // Unix like behaviour
3217 boolean added = false;
3219 if (t.isDataFlavorSupported(uriListFlavor))
3221 Cache.log.debug("Drop handled as uriListFlavor");
3222 // This is used by Unix drag system
3223 data = (String) t.getTransferData(uriListFlavor);
3227 // fallback to text: workaround - on OSX where there's a JVM bug
3228 Cache.log.debug("standard URIListFlavor failed. Trying text");
3229 // try text fallback
3230 DataFlavor textDf = new DataFlavor(
3231 "text/plain;class=java.lang.String");
3232 if (t.isDataFlavorSupported(textDf))
3234 data = (String) t.getTransferData(textDf);
3237 Cache.log.debug("Plain text drop content returned "
3238 + (data == null ? "Null - failed" : data));
3243 while (protocols.size() < files.size())
3245 Cache.log.debug("Adding missing FILE protocol for "
3246 + files.get(protocols.size()));
3247 protocols.add(DataSourceType.FILE);
3249 for (java.util.StringTokenizer st = new java.util.StringTokenizer(
3250 data, "\r\n"); st.hasMoreTokens();)
3253 String s = st.nextToken();
3254 if (s.startsWith("#"))
3256 // the line is a comment (as per the RFC 2483)
3259 java.net.URI uri = new java.net.URI(s);
3260 if (uri.getScheme().toLowerCase().startsWith("http"))
3262 protocols.add(DataSourceType.URL);
3263 files.add(uri.toString());
3267 // otherwise preserve old behaviour: catch all for file objects
3268 java.io.File file = new java.io.File(uri);
3269 protocols.add(DataSourceType.FILE);
3270 files.add(file.toString());
3275 if (Cache.log.isDebugEnabled())
3277 if (data == null || !added)
3280 if (t.getTransferDataFlavors() != null
3281 && t.getTransferDataFlavors().length > 0)
3284 "Couldn't resolve drop data. Here are the supported flavors:");
3285 for (DataFlavor fl : t.getTransferDataFlavors())
3288 "Supported transfer dataflavor: " + fl.toString());
3289 Object df = t.getTransferData(fl);
3292 Cache.log.debug("Retrieves: " + df);
3296 Cache.log.debug("Retrieved nothing");
3302 Cache.log.debug("Couldn't resolve dataflavor for drop: "
3308 if (Platform.isWindowsAndNotJS())
3310 Cache.log.debug("Scanning dropped content for Windows Link Files");
3312 // resolve any .lnk files in the file drop
3313 for (int f = 0; f < files.size(); f++)
3315 String source = files.get(f).toString().toLowerCase();
3316 if (protocols.get(f).equals(DataSourceType.FILE)
3317 && (source.endsWith(".lnk") || source.endsWith(".url")
3318 || source.endsWith(".site")))
3322 Object obj = files.get(f);
3323 File lf = (obj instanceof File ? (File) obj
3324 : new File((String) obj));
3325 // process link file to get a URL
3326 Cache.log.debug("Found potential link file: " + lf);
3327 WindowsShortcut wscfile = new WindowsShortcut(lf);
3328 String fullname = wscfile.getRealFilename();
3329 protocols.set(f, FormatAdapter.checkProtocol(fullname));
3330 files.set(f, fullname);
3331 Cache.log.debug("Parsed real filename " + fullname
3332 + " to extract protocol: " + protocols.get(f));
3333 } catch (Exception ex)
3336 "Couldn't parse " + files.get(f) + " as a link file.",
3345 * Sets the Preferences property for experimental features to True or False
3346 * depending on the state of the controlling menu item
3349 protected void showExperimental_actionPerformed(boolean selected)
3351 Cache.setProperty(EXPERIMENTAL_FEATURES, Boolean.toString(selected));
3355 * Answers a (possibly empty) list of any structure viewer frames (currently for
3356 * either Jmol or Chimera) which are currently open. This may optionally be
3357 * restricted to viewers of a specified class, or viewers linked to a specified
3361 * if not null, only return viewers linked to this panel
3362 * @param structureViewerClass
3363 * if not null, only return viewers of this class
3366 public List<StructureViewerBase> getStructureViewers(
3367 AlignmentPanel apanel,
3368 Class<? extends StructureViewerBase> structureViewerClass)
3370 List<StructureViewerBase> result = new ArrayList<>();
3371 JInternalFrame[] frames = Desktop.instance.getAllFrames();
3373 for (JInternalFrame frame : frames)
3375 if (frame instanceof StructureViewerBase)
3377 if (structureViewerClass == null
3378 || structureViewerClass.isInstance(frame))
3381 || ((StructureViewerBase) frame).isLinkedWith(apanel))
3383 result.add((StructureViewerBase) frame);