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.api.StructureSelectionManagerProvider;
98 import jalview.bin.ApplicationSingletonProvider;
99 import jalview.bin.ApplicationSingletonProvider.ApplicationSingletonI;
100 import jalview.bin.Cache;
101 import jalview.bin.Jalview;
102 import jalview.gui.ImageExporter.ImageWriterI;
103 import jalview.io.BackupFiles;
104 import jalview.io.DataSourceType;
105 import jalview.io.FileFormat;
106 import jalview.io.FileFormatException;
107 import jalview.io.FileFormatI;
108 import jalview.io.FileFormats;
109 import jalview.io.FileLoader;
110 import jalview.io.FormatAdapter;
111 import jalview.io.IdentifyFile;
112 import jalview.io.JalviewFileChooser;
113 import jalview.io.JalviewFileView;
114 import jalview.jbgui.GDesktop;
115 import jalview.jbgui.GSplitFrame;
116 import jalview.jbgui.GStructureViewer;
117 import jalview.project.Jalview2XML;
118 import jalview.structure.StructureSelectionManager;
119 import jalview.urls.IdOrgSettings;
120 import jalview.util.BrowserLauncher;
121 import jalview.util.ImageMaker.TYPE;
122 import jalview.util.MessageManager;
123 import jalview.util.Platform;
124 import jalview.util.UrlConstants;
125 import jalview.viewmodel.AlignmentViewport;
126 import jalview.ws.params.ParamManager;
127 import jalview.ws.utils.UrlDownloadClient;
134 * @version $Revision: 1.155 $
136 @SuppressWarnings("serial")
137 public class Desktop extends GDesktop
138 implements DropTargetListener, ClipboardOwner, IProgressIndicator,
139 StructureSelectionManagerProvider, ApplicationSingletonI
142 private static final String CITATION = "<br><br>Development managed by The Barton Group, University of Dundee, Scotland, UK.<br>"
143 + "<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"
144 + "<br><br>If you use Jalview, please cite:"
145 + "<br>Waterhouse, A.M., Procter, J.B., Martin, D.M.A, Clamp, M. and Barton, G. J. (2009)"
146 + "<br>Jalview Version 2 - a multiple sequence alignment editor and analysis workbench"
147 + "<br>Bioinformatics doi: 10.1093/bioinformatics/btp033";
149 private static final String DEFAULT_AUTHORS = "The Jalview Authors (See AUTHORS file for current list)";
151 private static int DEFAULT_MIN_WIDTH = 300;
153 private static int DEFAULT_MIN_HEIGHT = 250;
155 private static int ALIGN_FRAME_DEFAULT_MIN_WIDTH = 600;
157 private static int ALIGN_FRAME_DEFAULT_MIN_HEIGHT = 70;
159 private static final String EXPERIMENTAL_FEATURES = "EXPERIMENTAL_FEATURES";
161 protected static final String CONFIRM_KEYBOARD_QUIT = "CONFIRM_KEYBOARD_QUIT";
163 public static HashMap<String, FileWriter> savingFiles = new HashMap<String, FileWriter>();
165 private JalviewChangeSupport changeSupport = new JalviewChangeSupport();
168 * news reader - null if it was never started.
170 private BlogReader jvnews = null;
172 private File projectFile;
176 * @see jalview.gui.JalviewChangeSupport#addJalviewPropertyChangeListener(java.beans.PropertyChangeListener)
178 public void addJalviewPropertyChangeListener(
179 PropertyChangeListener listener)
181 changeSupport.addJalviewPropertyChangeListener(listener);
185 * @param propertyName
187 * @see jalview.gui.JalviewChangeSupport#addJalviewPropertyChangeListener(java.lang.String,
188 * java.beans.PropertyChangeListener)
190 public void addJalviewPropertyChangeListener(String propertyName,
191 PropertyChangeListener listener)
193 changeSupport.addJalviewPropertyChangeListener(propertyName, listener);
197 * @param propertyName
199 * @see jalview.gui.JalviewChangeSupport#removeJalviewPropertyChangeListener(java.lang.String,
200 * java.beans.PropertyChangeListener)
202 public void removeJalviewPropertyChangeListener(String propertyName,
203 PropertyChangeListener listener)
205 changeSupport.removeJalviewPropertyChangeListener(propertyName,
209 private MyDesktopPane desktopPane;
211 public static MyDesktopPane getDesktopPane()
213 Desktop desktop = getInstance();
214 return desktop == null ? null : desktop.desktopPane;
218 * Answers an 'application scope' singleton instance of this class. Separate
219 * SwingJS 'applets' running in the same browser page will each have a
220 * distinct instance of Desktop.
224 public static Desktop getInstance()
226 return Jalview.isHeadlessMode() ? null
227 : (Desktop) ApplicationSingletonProvider
228 .getInstance(Desktop.class);
231 public static StructureSelectionManager getStructureSelectionManager()
233 return StructureSelectionManager
234 .getStructureSelectionManager(getInstance());
237 int openFrameCount = 0;
239 final int xOffset = 30;
241 final int yOffset = 30;
243 public jalview.ws.jws1.Discoverer discoverer;
245 public Object[] jalviewClipboard;
247 public boolean internalCopy = false;
249 int fileLoadingCount = 0;
251 class MyDesktopManager implements DesktopManager
254 private DesktopManager delegate;
256 public MyDesktopManager(DesktopManager delegate)
258 this.delegate = delegate;
262 public void activateFrame(JInternalFrame f)
266 delegate.activateFrame(f);
267 } catch (NullPointerException npe)
269 Point p = getMousePosition();
270 showPasteMenu(p.x, p.y);
275 public void beginDraggingFrame(JComponent f)
277 delegate.beginDraggingFrame(f);
281 public void beginResizingFrame(JComponent f, int direction)
283 delegate.beginResizingFrame(f, direction);
287 public void closeFrame(JInternalFrame f)
289 delegate.closeFrame(f);
293 public void deactivateFrame(JInternalFrame f)
295 delegate.deactivateFrame(f);
299 public void deiconifyFrame(JInternalFrame f)
301 delegate.deiconifyFrame(f);
305 public void dragFrame(JComponent f, int newX, int newY)
311 delegate.dragFrame(f, newX, newY);
315 public void endDraggingFrame(JComponent f)
317 delegate.endDraggingFrame(f);
318 desktopPane.repaint();
322 public void endResizingFrame(JComponent f)
324 delegate.endResizingFrame(f);
325 desktopPane.repaint();
329 public void iconifyFrame(JInternalFrame f)
331 delegate.iconifyFrame(f);
335 public void maximizeFrame(JInternalFrame f)
337 delegate.maximizeFrame(f);
341 public void minimizeFrame(JInternalFrame f)
343 delegate.minimizeFrame(f);
347 public void openFrame(JInternalFrame f)
349 delegate.openFrame(f);
353 public void resizeFrame(JComponent f, int newX, int newY, int newWidth,
360 delegate.resizeFrame(f, newX, newY, newWidth, newHeight);
364 public void setBoundsForFrame(JComponent f, int newX, int newY,
365 int newWidth, int newHeight)
367 delegate.setBoundsForFrame(f, newX, newY, newWidth, newHeight);
370 // All other methods, simply delegate
375 * Private constructor enforces singleton pattern. It is called by reflection
376 * from ApplicationSingletonProvider.getInstance().
384 * A note to implementors. It is ESSENTIAL that any activities that might
385 * block are spawned off as threads rather than waited for during this
389 doConfigureStructurePrefs();
390 setTitle("Jalview " + Cache.getProperty("VERSION"));
392 if (!Platform.isAMac())
394 // this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
398 this.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
404 APQHandlers.setAPQHandlers(this);
405 } catch (Throwable t)
407 System.out.println("Error setting APQHandlers: " + t.toString());
408 // t.printStackTrace();
411 addWindowListener(new WindowAdapter()
415 public void windowClosing(WindowEvent ev)
421 boolean selmemusage = Cache.getDefault("SHOW_MEMUSAGE", false);
423 boolean showjconsole = Cache.getDefault("SHOW_JAVA_CONSOLE", false);
424 desktopPane = new MyDesktopPane(selmemusage);
426 showMemusage.setSelected(selmemusage);
427 desktopPane.setBackground(Color.white);
429 getContentPane().setLayout(new BorderLayout());
430 // alternate config - have scrollbars - see notes in JAL-153
431 // JScrollPane sp = new JScrollPane();
432 // sp.getViewport().setView(desktop);
433 // getContentPane().add(sp, BorderLayout.CENTER);
435 // BH 2018 - just an experiment to try unclipped JInternalFrames.
438 getRootPane().putClientProperty("swingjs.overflow.hidden", "false");
441 getContentPane().add(desktopPane, BorderLayout.CENTER);
442 desktopPane.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
444 // This line prevents Windows Look&Feel resizing all new windows to
446 // if previous window was maximised
447 desktopPane.setDesktopManager(new MyDesktopManager(
448 (Platform.isWindowsAndNotJS() ? new DefaultDesktopManager()
449 : Platform.isAMacAndNotJS()
450 ? new AquaInternalFrameManager(
451 desktopPane.getDesktopManager())
452 : desktopPane.getDesktopManager())));
454 Rectangle dims = getLastKnownDimensions("");
461 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
462 int xPos = Math.max(5, (screenSize.width - 900) / 2);
463 int yPos = Math.max(5, (screenSize.height - 650) / 2);
464 setBounds(xPos, yPos, 900, 650);
467 getIdentifiersOrgData();
469 if (!Platform.isJS())
476 jconsole = new Console(this, showjconsole);
477 jconsole.setHeader(Cache.getVersionDetailsForConsole());
478 showConsole(showjconsole);
480 showNews.setVisible(false);
482 experimentalFeatures.setSelected(showExperimental());
484 if (Jalview.isInteractive())
486 // disabled for SeqCanvasTest
491 // Spawn a thread that shows the splashscreen
493 SwingUtilities.invokeLater(new Runnable()
498 new SplashScreen(true);
502 // Thread off a new instance of the file chooser - this reduces the
505 // takes to open it later on.
506 new Thread(new Runnable()
511 Cache.log.debug("Filechooser init thread started.");
512 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
513 JalviewFileChooser.forRead(
514 Cache.getProperty("LAST_DIRECTORY"), fileFormat);
515 Cache.log.debug("Filechooser init thread finished.");
518 // Add the service change listener
519 changeSupport.addJalviewPropertyChangeListener("services",
520 new PropertyChangeListener()
524 public void propertyChange(PropertyChangeEvent evt)
526 Cache.log.debug("Firing service changed event for "
527 + evt.getNewValue());
528 JalviewServicesChanged(evt);
533 this.setDropTarget(new java.awt.dnd.DropTarget(desktopPane, this));
535 this.addWindowListener(new WindowAdapter()
538 public void windowClosing(WindowEvent evt)
545 this.addMouseListener(ma = new MouseAdapter()
548 public void mousePressed(MouseEvent evt)
550 if (evt.isPopupTrigger()) // Mac
552 showPasteMenu(evt.getX(), evt.getY());
557 public void mouseReleased(MouseEvent evt)
559 if (evt.isPopupTrigger()) // Windows
561 showPasteMenu(evt.getX(), evt.getY());
565 desktopPane.addMouseListener(ma);
566 } catch (Throwable t)
574 * Answers true if user preferences to enable experimental features is True
579 public boolean showExperimental()
581 String experimental = Cache.getDefault(EXPERIMENTAL_FEATURES,
582 Boolean.FALSE.toString());
583 return Boolean.valueOf(experimental).booleanValue();
586 public void doConfigureStructurePrefs()
588 // configure services
589 StructureSelectionManager ssm = StructureSelectionManager
590 .getStructureSelectionManager(this);
591 if (Cache.getDefault(Preferences.ADD_SS_ANN, true))
593 ssm.setAddTempFacAnnot(
594 Cache.getDefault(Preferences.ADD_TEMPFACT_ANN, true));
595 ssm.setProcessSecondaryStructure(
596 Cache.getDefault(Preferences.STRUCT_FROM_PDB, true));
597 ssm.setSecStructServices(
598 Cache.getDefault(Preferences.USE_RNAVIEW, true));
602 ssm.setAddTempFacAnnot(false);
603 ssm.setProcessSecondaryStructure(false);
604 ssm.setSecStructServices(false);
608 public void checkForNews()
610 final Desktop me = this;
611 // Thread off the news reader, in case there are connection problems.
612 new Thread(new Runnable()
617 Cache.log.debug("Starting news thread.");
618 jvnews = new BlogReader(me);
619 showNews.setVisible(true);
620 Cache.log.debug("Completed news thread.");
625 public void getIdentifiersOrgData()
627 // Thread off the identifiers fetcher
628 new Thread(new Runnable()
633 Cache.log.debug("Downloading data from identifiers.org");
636 UrlDownloadClient.download(IdOrgSettings.getUrl(),
637 IdOrgSettings.getDownloadLocation());
638 } catch (IOException e)
640 Cache.log.debug("Exception downloading identifiers.org data"
649 protected void showNews_actionPerformed(ActionEvent e)
651 showNews(showNews.isSelected());
654 void showNews(boolean visible)
656 Cache.log.debug((visible ? "Showing" : "Hiding") + " news.");
657 showNews.setSelected(visible);
658 if (visible && !jvnews.isVisible())
660 new Thread(new Runnable()
665 long now = System.currentTimeMillis();
666 setProgressBar(MessageManager.getString("status.refreshing_news"),
668 jvnews.refreshNews();
669 setProgressBar(null, now);
677 * recover the last known dimensions for a jalview window
680 * - empty string is desktop, all other windows have unique prefix
681 * @return null or last known dimensions scaled to current geometry (if last
682 * window geom was known)
684 Rectangle getLastKnownDimensions(String windowName)
686 // TODO: lock aspect ratio for scaling desktop Bug #0058199
687 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
688 String x = Cache.getProperty(windowName + "SCREEN_X");
689 String y = Cache.getProperty(windowName + "SCREEN_Y");
690 String width = Cache.getProperty(windowName + "SCREEN_WIDTH");
691 String height = Cache.getProperty(windowName + "SCREEN_HEIGHT");
692 if ((x != null) && (y != null) && (width != null) && (height != null))
694 int ix = Integer.parseInt(x), iy = Integer.parseInt(y),
695 iw = Integer.parseInt(width), ih = Integer.parseInt(height);
696 if (Cache.getProperty("SCREENGEOMETRY_WIDTH") != null)
698 // attempt #1 - try to cope with change in screen geometry - this
699 // version doesn't preserve original jv aspect ratio.
700 // take ratio of current screen size vs original screen size.
701 double sw = ((1f * screenSize.width) / (1f * Integer
702 .parseInt(Cache.getProperty("SCREENGEOMETRY_WIDTH"))));
703 double sh = ((1f * screenSize.height) / (1f * Integer
704 .parseInt(Cache.getProperty("SCREENGEOMETRY_HEIGHT"))));
705 // rescale the bounds depending upon the current screen geometry.
706 ix = (int) (ix * sw);
707 iw = (int) (iw * sw);
708 iy = (int) (iy * sh);
709 ih = (int) (ih * sh);
710 while (ix >= screenSize.width)
713 "Window geometry location recall error: shifting horizontal to within screenbounds.");
714 ix -= screenSize.width;
716 while (iy >= screenSize.height)
719 "Window geometry location recall error: shifting vertical to within screenbounds.");
720 iy -= screenSize.height;
723 "Got last known dimensions for " + windowName + ": x:" + ix
724 + " y:" + iy + " width:" + iw + " height:" + ih);
726 // return dimensions for new instance
727 return new Rectangle(ix, iy, iw, ih);
732 void showPasteMenu(int x, int y)
734 JPopupMenu popup = new JPopupMenu();
735 JMenuItem item = new JMenuItem(
736 MessageManager.getString("label.paste_new_window"));
737 item.addActionListener(new ActionListener()
740 public void actionPerformed(ActionEvent evt)
747 popup.show(this, x, y);
754 Clipboard c = Toolkit.getDefaultToolkit().getSystemClipboard();
755 Transferable contents = c.getContents(this);
757 if (contents != null)
759 String file = (String) contents
760 .getTransferData(DataFlavor.stringFlavor);
762 FileFormatI format = new IdentifyFile().identify(file,
763 DataSourceType.PASTE);
765 new FileLoader().LoadFile(file, DataSourceType.PASTE, format);
768 } catch (Exception ex)
771 "Unable to paste alignment from system clipboard:\n" + ex);
776 // * Add an internal frame to the Jalview desktop that is allowed to be resized,
777 // * has a minimum size of 300px and might or might not be visible
783 // * @param makeVisible
784 // * When true, display frame immediately, otherwise, caller must call
785 // * setVisible themselves.
792 // public static synchronized void addInternalFrame(
793 // final JInternalFrame frame, String title, boolean makeVisible,
796 // // textbox, web services, sequenceFetcher, featureSettings
797 // getInstance().addFrame(frame, title, makeVisible, w, h,
798 // FRAME_ALLOW_RESIZE, FRAME_SET_MIN_SIZE_300);
802 // * Add an internal frame to the Jalview desktop that is visible, has a minimum
803 // * size of 300px, and may or may not be resizable
813 // * @param resizable
817 // public static synchronized void addInternalFrame(
818 // final JInternalFrame frame, String title, int w, int h,
819 // boolean resizable)
821 // // annotation, font, calculation, user-defined colors
822 // getInstance().addFrame(frame, title, FRAME_MAKE_VISIBLE, w, h,
823 // resizable, FRAME_SET_MIN_SIZE_300);
827 * Adds and opens the given frame to the desktop that is visible, allowed to
828 * resize, and has a 300px minimum width.
839 public static synchronized void addInternalFrame(
840 final JInternalFrame frame, String title, int w, int h)
844 addInternalFrame(frame, title, Desktop.FRAME_MAKE_VISIBLE, w, h,
845 FRAME_ALLOW_RESIZE, FRAME_SET_MIN_SIZE_300);
849 * Add an internal frame to the Jalview desktop that may optionally be
850 * visible, resizable, and allowed to be any size
857 * When true, display frame immediately, otherwise, caller must call
858 * setVisible themselves.
865 * @param ignoreMinSize
866 * Do not set the default minimum size for frame
868 public static synchronized void addInternalFrame(
869 final JInternalFrame frame, String title, boolean makeVisible,
870 int w, int h, boolean resizable, boolean ignoreMinSize)
872 // 15 classes call this method directly.
874 // TODO: allow callers to determine X and Y position of frame (eg. via
876 // TODO: consider fixing method to update entries in the window submenu with
877 // the current window title
879 frame.setTitle(title);
880 if (frame.getWidth() < 1 || frame.getHeight() < 1)
884 if (getInstance() != null)
885 getInstance().addFrame(frame, makeVisible, resizable,
889 // These can now by put into a single int flag, if desired:
891 public final static boolean FRAME_MAKE_VISIBLE = true;
893 public final static boolean FRAME_NOT_VISIBLE = false;
895 public final static boolean FRAME_ALLOW_RESIZE = true;
897 public final static boolean FRAME_NOT_RESIZABLE = false;
899 public final static boolean FRAME_ALLOW_ANY_SIZE = true;
901 public final static boolean FRAME_SET_MIN_SIZE_300 = false;
903 private void addFrame(JInternalFrame frame,
904 boolean makeVisible, boolean resizable,
905 boolean ignoreMinSize)
910 boolean isEmbedded = (Platform.getEmbeddedAttribute(frame, "id") != null);
911 boolean hasEmbeddedSize = (Platform.getDimIfEmbedded(frame, -1, -1) != null);
912 // Web page embedding allows us to ignore minimum size
913 ignoreMinSize |= hasEmbeddedSize;
917 // Set default dimension for Alignment Frame window.
918 // The Alignment Frame window could be added from a number of places,
920 // I did this here in order not to miss out on any Alignment frame.
921 if (frame instanceof AlignFrame)
923 frame.setMinimumSize(new Dimension(ALIGN_FRAME_DEFAULT_MIN_WIDTH,
924 ALIGN_FRAME_DEFAULT_MIN_HEIGHT));
926 frame.setMinimumSize(
927 new Dimension(DEFAULT_MIN_WIDTH, DEFAULT_MIN_HEIGHT));
932 frame.setVisible(makeVisible);
933 frame.setClosable(true);
934 frame.setResizable(resizable);
935 frame.setMaximizable(resizable);
936 frame.setIconifiable(resizable);
937 frame.setOpaque(Platform.isJS());
938 if (!isEmbedded && frame.getX() < 1 && frame.getY() < 1)
940 frame.setLocation(xOffset * openFrameCount,
941 yOffset * ((openFrameCount - 1) % 10) + yOffset);
945 * add an entry for the new frame in the Window menu
946 * (and remove it when the frame is closed)
948 final JMenuItem menuItem = new JMenuItem(frame.getTitle());
949 frame.addInternalFrameListener(new InternalFrameAdapter()
952 public void internalFrameActivated(InternalFrameEvent evt)
954 JInternalFrame itf = getDesktopPane().getSelectedFrame();
957 if (itf instanceof AlignFrame)
959 Jalview.setCurrentAlignFrame((AlignFrame) itf);
966 public void internalFrameClosed(InternalFrameEvent evt)
968 PaintRefresher.RemoveComponent(frame);
971 * defensive check to prevent frames being
972 * added half off the window
974 if (openFrameCount > 0)
980 * ensure no reference to alignFrame retained by menu item listener
982 if (menuItem.getActionListeners().length > 0)
984 menuItem.removeActionListener(menuItem.getActionListeners()[0]);
986 getInstance().windowMenu.remove(menuItem);
990 menuItem.addActionListener(new ActionListener()
993 public void actionPerformed(ActionEvent e)
997 frame.setSelected(true);
998 frame.setIcon(false);
999 } catch (java.beans.PropertyVetoException ex)
1001 // System.err.println(ex.toString());
1006 setKeyBindings(frame);
1008 getDesktopPane().add(frame);
1010 getInstance().windowMenu.add(menuItem);
1015 frame.setSelected(true);
1016 frame.requestFocus();
1017 } catch (java.beans.PropertyVetoException ve)
1019 } catch (java.lang.ClassCastException cex)
1022 "Squashed a possible GUI implementation error. If you can recreate this, please look at http://issues.jalview.org/browse/JAL-869",
1028 * Add key bindings to a JInternalFrame so that Ctrl-W and Cmd-W will close
1033 private static void setKeyBindings(JInternalFrame frame)
1035 final Action closeAction = new AbstractAction()
1038 public void actionPerformed(ActionEvent e)
1045 * set up key bindings for Ctrl-W and Cmd-W, with the same (Close) action
1047 KeyStroke ctrlWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
1048 InputEvent.CTRL_DOWN_MASK);
1049 KeyStroke cmdWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
1050 Platform.SHORTCUT_KEY_MASK);
1052 InputMap inputMap = frame
1053 .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
1054 String ctrlW = ctrlWKey.toString();
1055 inputMap.put(ctrlWKey, ctrlW);
1056 inputMap.put(cmdWKey, ctrlW);
1058 ActionMap actionMap = frame.getActionMap();
1059 actionMap.put(ctrlW, closeAction);
1063 public void lostOwnership(Clipboard clipboard, Transferable contents)
1067 jalviewClipboard = null;
1070 internalCopy = false;
1074 public void dragEnter(DropTargetDragEvent evt)
1079 public void dragExit(DropTargetEvent evt)
1084 public void dragOver(DropTargetDragEvent evt)
1089 public void dropActionChanged(DropTargetDragEvent evt)
1100 public void drop(DropTargetDropEvent evt)
1102 boolean success = true;
1103 // JAL-1552 - acceptDrop required before getTransferable call for
1104 // Java's Transferable for native dnd
1105 evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
1106 Transferable t = evt.getTransferable();
1107 List<Object> files = new ArrayList<>();
1108 List<DataSourceType> protocols = new ArrayList<>();
1112 transferFromDropTarget(files, protocols, evt, t);
1113 } catch (Exception e)
1115 e.printStackTrace();
1123 for (int i = 0; i < files.size(); i++)
1125 // BH 2018 File or String
1126 Object file = files.get(i);
1127 String fileName = file.toString();
1128 DataSourceType protocol = (protocols == null)
1129 ? DataSourceType.FILE
1131 FileFormatI format = null;
1133 if (fileName.endsWith(".jar"))
1135 format = FileFormat.Jalview;
1140 format = new IdentifyFile().identify(file, protocol);
1142 if (file instanceof File)
1144 Platform.cacheFileData((File) file);
1146 new FileLoader().LoadFile(null, file, protocol, format);
1149 } catch (Exception ex)
1154 evt.dropComplete(success); // need this to ensure input focus is properly
1155 // transfered to any new windows created
1165 public void inputLocalFileMenuItem_actionPerformed(AlignViewport viewport)
1167 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
1168 JalviewFileChooser chooser = JalviewFileChooser.forRead(
1169 Cache.getProperty("LAST_DIRECTORY"), fileFormat,
1170 BackupFiles.getEnabled());
1172 chooser.setFileView(new JalviewFileView());
1173 chooser.setDialogTitle(
1174 MessageManager.getString("label.open_local_file"));
1175 chooser.setToolTipText(MessageManager.getString("action.open"));
1177 chooser.setResponseHandler(0, new Runnable()
1182 File selectedFile = chooser.getSelectedFile();
1183 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1185 FileFormatI format = chooser.getSelectedFormat();
1188 * Call IdentifyFile to verify the file contains what its extension implies.
1189 * Skip this step for dynamically added file formats, because
1190 * IdentifyFile does not know how to recognise them.
1192 if (FileFormats.getInstance().isIdentifiable(format))
1196 format = new IdentifyFile().identify(selectedFile,
1197 DataSourceType.FILE);
1198 } catch (FileFormatException e)
1200 // format = null; //??
1204 new FileLoader().LoadFile(viewport, selectedFile,
1205 DataSourceType.FILE, format);
1208 chooser.showOpenDialog(this);
1212 * Shows a dialog for input of a URL at which to retrieve alignment data
1217 public void inputURLMenuItem_actionPerformed(AlignViewport viewport)
1219 // This construct allows us to have a wider textfield
1221 JLabel label = new JLabel(
1222 MessageManager.getString("label.input_file_url"));
1224 JPanel panel = new JPanel(new GridLayout(2, 1));
1228 * the URL to fetch is
1229 * Java: an editable combobox with history
1230 * JS: (pending JAL-3038) a plain text field
1233 String urlBase = "http://www.";
1234 if (Platform.isJS())
1236 history = new JTextField(urlBase, 35);
1245 JComboBox<String> asCombo = new JComboBox<>();
1246 asCombo.setPreferredSize(new Dimension(400, 20));
1247 asCombo.setEditable(true);
1248 asCombo.addItem(urlBase);
1249 String historyItems = Cache.getProperty("RECENT_URL");
1250 if (historyItems != null)
1252 for (String token : historyItems.split("\\t"))
1254 asCombo.addItem(token);
1261 Object[] options = new Object[] { MessageManager.getString("action.ok"),
1262 MessageManager.getString("action.cancel") };
1263 Runnable action = new Runnable()
1268 @SuppressWarnings("unchecked")
1269 String url = (history instanceof JTextField
1270 ? ((JTextField) history).getText()
1271 : ((JComboBox<String>) history).getSelectedItem()
1274 if (url.toLowerCase().endsWith(".jar"))
1276 if (viewport != null)
1278 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1279 FileFormat.Jalview);
1283 new FileLoader().LoadFile(url, DataSourceType.URL,
1284 FileFormat.Jalview);
1289 FileFormatI format = null;
1292 format = new IdentifyFile().identify(url, DataSourceType.URL);
1293 } catch (FileFormatException e)
1295 // TODO revise error handling, distinguish between
1296 // URL not found and response not valid
1301 String msg = MessageManager
1302 .formatMessage("label.couldnt_locate", url);
1303 JvOptionPane.showInternalMessageDialog(getDesktopPane(), msg,
1304 MessageManager.getString("label.url_not_found"),
1305 JvOptionPane.WARNING_MESSAGE);
1310 if (viewport != null)
1312 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1317 new FileLoader().LoadFile(url, DataSourceType.URL, format);
1322 String dialogOption = MessageManager
1323 .getString("label.input_alignment_from_url");
1324 JvOptionPane.newOptionDialog(desktopPane).setResponseHandler(0, action)
1325 .showInternalDialog(panel, dialogOption,
1326 JvOptionPane.YES_NO_CANCEL_OPTION,
1327 JvOptionPane.PLAIN_MESSAGE, null, options,
1328 MessageManager.getString("action.ok"));
1332 * Opens the CutAndPaste window for the user to paste an alignment in to
1335 * - if not null, the pasted alignment is added to the current
1336 * alignment; if null, to a new alignment window
1339 public void inputTextboxMenuItem_actionPerformed(
1340 AlignmentViewPanel viewPanel)
1342 CutAndPasteTransfer cap = new CutAndPasteTransfer();
1343 cap.setForInput(viewPanel);
1344 addInternalFrame(cap,
1345 MessageManager.getString("label.cut_paste_alignmen_file"),
1346 FRAME_MAKE_VISIBLE, 600, 500, FRAME_ALLOW_RESIZE,
1347 FRAME_SET_MIN_SIZE_300);
1356 Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
1357 Cache.setProperty("SCREENGEOMETRY_WIDTH", screen.width + "");
1358 Cache.setProperty("SCREENGEOMETRY_HEIGHT", screen.height + "");
1359 storeLastKnownDimensions("", new Rectangle(getBounds().x, getBounds().y,
1360 getWidth(), getHeight()));
1362 if (jconsole != null)
1364 storeLastKnownDimensions("JAVA_CONSOLE_", jconsole.getBounds());
1365 jconsole.stopConsole();
1369 storeLastKnownDimensions("JALVIEW_RSS_WINDOW_", jvnews.getBounds());
1372 if (dialogExecutor != null)
1374 dialogExecutor.shutdownNow();
1376 closeAll_actionPerformed(null);
1378 if (groovyConsole != null)
1380 // suppress a possible repeat prompt to save script
1381 groovyConsole.setDirty(false);
1382 groovyConsole.exit();
1387 private void storeLastKnownDimensions(String string, Rectangle jc)
1389 Cache.log.debug("Storing last known dimensions for " + string + ": x:"
1390 + jc.x + " y:" + jc.y + " width:" + jc.width + " height:"
1393 Cache.setProperty(string + "SCREEN_X", jc.x + "");
1394 Cache.setProperty(string + "SCREEN_Y", jc.y + "");
1395 Cache.setProperty(string + "SCREEN_WIDTH", jc.width + "");
1396 Cache.setProperty(string + "SCREEN_HEIGHT", jc.height + "");
1406 public void aboutMenuItem_actionPerformed(ActionEvent e)
1408 new Thread(new Runnable()
1413 new SplashScreen(false);
1419 * Returns the html text for the About screen, including any available version
1420 * number, build details, author details and citation reference, but without
1421 * the enclosing {@code html} tags
1425 public String getAboutMessage()
1427 StringBuilder message = new StringBuilder(1024);
1428 message.append("<h1><strong>Version: ")
1429 .append(Cache.getProperty("VERSION")).append("</strong></h1>")
1430 .append("<strong>Built: <em>")
1431 .append(Cache.getDefault("BUILD_DATE", "unknown"))
1432 .append("</em> from ").append(Cache.getBuildDetailsForSplash())
1433 .append("</strong>");
1435 String latestVersion = Cache.getDefault("LATEST_VERSION", "Checking");
1436 if (latestVersion.equals("Checking"))
1438 // JBP removed this message for 2.11: May be reinstated in future version
1439 // message.append("<br>...Checking latest version...</br>");
1441 else if (!latestVersion.equals(Cache.getProperty("VERSION")))
1443 boolean red = false;
1444 if (Cache.getProperty("VERSION").toLowerCase()
1445 .indexOf("automated build") == -1)
1448 // Displayed when code version and jnlp version do not match and code
1449 // version is not a development build
1450 message.append("<div style=\"color: #FF0000;font-style: bold;\">");
1453 message.append("<br>!! Version ")
1454 .append(Cache.getDefault("LATEST_VERSION", "..Checking.."))
1455 .append(" is available for download from ")
1456 .append(Cache.getDefault("www.jalview.org",
1457 "http://www.jalview.org"))
1461 message.append("</div>");
1464 message.append("<br>Authors: ");
1465 message.append(Cache.getDefault("AUTHORFNAMES", DEFAULT_AUTHORS));
1466 message.append(CITATION);
1468 return message.toString();
1472 * Action on requesting Help documentation
1475 public void documentationMenuItem_actionPerformed()
1479 if (Platform.isJS())
1481 BrowserLauncher.openURL("http://www.jalview.org/help.html");
1490 Help.showHelpWindow();
1492 } catch (Exception ex)
1494 System.err.println("Error opening help: " + ex.getMessage());
1499 public void closeAll_actionPerformed(ActionEvent e)
1501 // TODO show a progress bar while closing?
1502 JInternalFrame[] frames = desktopPane.getAllFrames();
1503 for (int i = 0; i < frames.length; i++)
1507 frames[i].setClosed(true);
1508 } catch (java.beans.PropertyVetoException ex)
1512 Jalview.setCurrentAlignFrame(null);
1513 System.out.println("ALL CLOSED");
1516 * reset state of singleton objects as appropriate (clear down session state
1517 * when all windows are closed)
1519 StructureSelectionManager ssm = StructureSelectionManager
1520 .getStructureSelectionManager(this);
1528 public void raiseRelated_actionPerformed(ActionEvent e)
1530 reorderAssociatedWindows(false, false);
1534 public void minimizeAssociated_actionPerformed(ActionEvent e)
1536 reorderAssociatedWindows(true, false);
1539 void closeAssociatedWindows()
1541 reorderAssociatedWindows(false, true);
1547 * @seejalview.jbgui.GDesktop#garbageCollect_actionPerformed(java.awt.event.
1551 protected void garbageCollect_actionPerformed(ActionEvent e)
1553 // We simply collect the garbage
1554 Cache.log.debug("Collecting garbage...");
1556 Cache.log.debug("Finished garbage collection.");
1563 * jalview.jbgui.GDesktop#showMemusage_actionPerformed(java.awt.event.ActionEvent
1567 protected void showMemusage_actionPerformed(ActionEvent e)
1569 desktopPane.showMemoryUsage(showMemusage.isSelected());
1576 * jalview.jbgui.GDesktop#showConsole_actionPerformed(java.awt.event.ActionEvent
1580 protected void showConsole_actionPerformed(ActionEvent e)
1582 showConsole(showConsole.isSelected());
1585 Console jconsole = null;
1588 * control whether the java console is visible or not
1592 void showConsole(boolean selected)
1594 // TODO: decide if we should update properties file
1595 if (jconsole != null) // BH 2018
1597 showConsole.setSelected(selected);
1598 Cache.setProperty("SHOW_JAVA_CONSOLE",
1599 Boolean.valueOf(selected).toString());
1600 jconsole.setVisible(selected);
1604 void reorderAssociatedWindows(boolean minimize, boolean close)
1606 JInternalFrame[] frames = desktopPane.getAllFrames();
1607 if (frames == null || frames.length < 1)
1612 AlignmentViewport source = null, target = null;
1613 if (frames[0] instanceof AlignFrame)
1615 source = ((AlignFrame) frames[0]).getCurrentView();
1617 else if (frames[0] instanceof TreePanel)
1619 source = ((TreePanel) frames[0]).getViewPort();
1621 else if (frames[0] instanceof PCAPanel)
1623 source = ((PCAPanel) frames[0]).av;
1625 else if (frames[0].getContentPane() instanceof PairwiseAlignPanel)
1627 source = ((PairwiseAlignPanel) frames[0].getContentPane()).av;
1632 for (int i = 0; i < frames.length; i++)
1635 if (frames[i] == null)
1639 if (frames[i] instanceof AlignFrame)
1641 target = ((AlignFrame) frames[i]).getCurrentView();
1643 else if (frames[i] instanceof TreePanel)
1645 target = ((TreePanel) frames[i]).getViewPort();
1647 else if (frames[i] instanceof PCAPanel)
1649 target = ((PCAPanel) frames[i]).av;
1651 else if (frames[i].getContentPane() instanceof PairwiseAlignPanel)
1653 target = ((PairwiseAlignPanel) frames[i].getContentPane()).av;
1656 if (source == target)
1662 frames[i].setClosed(true);
1666 frames[i].setIcon(minimize);
1669 frames[i].toFront();
1673 } catch (java.beans.PropertyVetoException ex)
1688 protected void preferences_actionPerformed(ActionEvent e)
1694 * Prompts the user to choose a file and then saves the Jalview state as a
1695 * Jalview project file
1698 public void saveState_actionPerformed()
1700 saveState_actionPerformed(false);
1703 public void saveState_actionPerformed(boolean saveAs)
1705 java.io.File projectFile = getProjectFile();
1706 // autoSave indicates we already have a file and don't need to ask
1707 boolean autoSave = projectFile != null && !saveAs
1708 && BackupFiles.getEnabled();
1710 // System.out.println("autoSave="+autoSave+", projectFile='"+projectFile+"',
1711 // saveAs="+saveAs+", Backups
1712 // "+(BackupFiles.getEnabled()?"enabled":"disabled"));
1714 boolean approveSave = false;
1717 JalviewFileChooser chooser = new JalviewFileChooser("jvp",
1720 chooser.setFileView(new JalviewFileView());
1721 chooser.setDialogTitle(MessageManager.getString("label.save_state"));
1723 int value = chooser.showSaveDialog(this);
1725 if (value == JalviewFileChooser.APPROVE_OPTION)
1727 projectFile = chooser.getSelectedFile();
1728 setProjectFile(projectFile);
1733 if (approveSave || autoSave)
1735 final Desktop me = this;
1736 final java.io.File chosenFile = projectFile;
1737 new Thread(new Runnable()
1742 // TODO: refactor to Jalview desktop session controller action.
1743 setProgressBar(MessageManager.formatMessage(
1744 "label.saving_jalview_project", new Object[]
1745 { chosenFile.getName() }), chosenFile.hashCode());
1746 Cache.setProperty("LAST_DIRECTORY", chosenFile.getParent());
1747 // TODO catch and handle errors for savestate
1748 // TODO prevent user from messing with the Desktop whilst we're saving
1751 boolean doBackup = BackupFiles.getEnabled();
1752 BackupFiles backupfiles = doBackup ? new BackupFiles(chosenFile)
1755 new Jalview2XML().saveState(
1756 doBackup ? backupfiles.getTempFile() : chosenFile);
1760 backupfiles.setWriteSuccess(true);
1761 backupfiles.rollBackupsAndRenameTempFile();
1763 } catch (OutOfMemoryError oom)
1765 new OOMWarning("Whilst saving current state to "
1766 + chosenFile.getName(), oom);
1767 } catch (Exception ex)
1769 Cache.log.error("Problems whilst trying to save to "
1770 + chosenFile.getName(), ex);
1771 JvOptionPane.showMessageDialog(me,
1772 MessageManager.formatMessage(
1773 "label.error_whilst_saving_current_state_to",
1775 { chosenFile.getName() }),
1776 MessageManager.getString("label.couldnt_save_project"),
1777 JvOptionPane.WARNING_MESSAGE);
1779 setProgressBar(null, chosenFile.hashCode());
1786 public void saveAsState_actionPerformed(ActionEvent e)
1788 saveState_actionPerformed(true);
1791 private void setProjectFile(File choice)
1793 this.projectFile = choice;
1796 public File getProjectFile()
1798 return this.projectFile;
1802 * Shows a file chooser dialog and tries to read in the selected file as a
1806 public void loadState_actionPerformed()
1808 final String[] suffix = new String[] { "jvp", "jar" };
1809 final String[] desc = new String[] { "Jalview Project",
1810 "Jalview Project (old)" };
1811 JalviewFileChooser chooser = new JalviewFileChooser(
1812 Cache.getProperty("LAST_DIRECTORY"), suffix, desc,
1813 "Jalview Project", true, BackupFiles.getEnabled()); // last two
1817 chooser.setFileView(new JalviewFileView());
1818 chooser.setDialogTitle(MessageManager.getString("label.restore_state"));
1819 chooser.setResponseHandler(0, new Runnable()
1824 File selectedFile = chooser.getSelectedFile();
1825 setProjectFile(selectedFile);
1826 String choice = selectedFile.getAbsolutePath();
1827 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1828 new Thread(new Runnable()
1835 new Jalview2XML().loadJalviewAlign(selectedFile);
1836 } catch (OutOfMemoryError oom)
1838 new OOMWarning("Whilst loading project from " + choice, oom);
1839 } catch (Exception ex)
1842 "Problems whilst loading project from " + choice, ex);
1843 JvOptionPane.showMessageDialog(getDesktopPane(),
1844 MessageManager.formatMessage(
1845 "label.error_whilst_loading_project_from",
1849 .getString("label.couldnt_load_project"),
1850 JvOptionPane.WARNING_MESSAGE);
1857 chooser.showOpenDialog(this);
1861 public void inputSequence_actionPerformed(ActionEvent e)
1863 new SequenceFetcher(this);
1866 JPanel progressPanel;
1868 ArrayList<JPanel> fileLoadingPanels = new ArrayList<>();
1870 public void startLoading(final Object fileName)
1872 if (fileLoadingCount == 0)
1874 fileLoadingPanels.add(addProgressPanel(MessageManager
1875 .formatMessage("label.loading_file", new Object[]
1881 private JPanel addProgressPanel(String string)
1883 if (progressPanel == null)
1885 progressPanel = new JPanel(new GridLayout(1, 1));
1886 totalProgressCount = 0;
1887 getContentPane().add(progressPanel, BorderLayout.SOUTH);
1889 JPanel thisprogress = new JPanel(new BorderLayout(10, 5));
1890 JProgressBar progressBar = new JProgressBar();
1891 progressBar.setIndeterminate(true);
1893 thisprogress.add(new JLabel(string), BorderLayout.WEST);
1895 thisprogress.add(progressBar, BorderLayout.CENTER);
1896 progressPanel.add(thisprogress);
1897 ((GridLayout) progressPanel.getLayout()).setRows(
1898 ((GridLayout) progressPanel.getLayout()).getRows() + 1);
1899 ++totalProgressCount;
1901 return thisprogress;
1904 int totalProgressCount = 0;
1906 private void removeProgressPanel(JPanel progbar)
1908 if (progressPanel != null)
1910 synchronized (progressPanel)
1912 progressPanel.remove(progbar);
1913 GridLayout gl = (GridLayout) progressPanel.getLayout();
1914 gl.setRows(gl.getRows() - 1);
1915 if (--totalProgressCount < 1)
1917 this.getContentPane().remove(progressPanel);
1918 progressPanel = null;
1925 public void stopLoading()
1928 if (fileLoadingCount < 1)
1930 while (fileLoadingPanels.size() > 0)
1932 removeProgressPanel(fileLoadingPanels.remove(0));
1934 fileLoadingPanels.clear();
1935 fileLoadingCount = 0;
1940 public static int getViewCount(String alignmentId)
1942 AlignmentViewport[] aps = getViewports(alignmentId);
1943 return (aps == null) ? 0 : aps.length;
1948 * @param alignmentId
1949 * - if null, all sets are returned
1950 * @return all AlignmentPanels concerning the alignmentId sequence set
1952 public static AlignmentPanel[] getAlignmentPanels(String alignmentId)
1954 if (getDesktopPane() == null)
1956 // no frames created and in headless mode
1957 // TODO: verify that frames are recoverable when in headless mode
1960 List<AlignmentPanel> aps = new ArrayList<>();
1961 AlignFrame[] frames = getAlignFrames();
1966 for (AlignFrame af : frames)
1968 for (AlignmentPanel ap : af.alignPanels)
1970 if (alignmentId == null
1971 || alignmentId.equals(ap.av.getSequenceSetId()))
1977 if (aps.size() == 0)
1981 AlignmentPanel[] vap = aps.toArray(new AlignmentPanel[aps.size()]);
1986 * get all the viewports on an alignment.
1988 * @param sequenceSetId
1989 * unique alignment id (may be null - all viewports returned in that
1991 * @return all viewports on the alignment bound to sequenceSetId
1993 public static AlignmentViewport[] getViewports(String sequenceSetId)
1995 List<AlignmentViewport> viewp = new ArrayList<>();
1996 if (getDesktopPane() != null)
1998 AlignFrame[] frames = getAlignFrames();
2000 for (AlignFrame afr : frames)
2002 if (sequenceSetId == null || afr.getViewport().getSequenceSetId()
2003 .equals(sequenceSetId))
2005 if (afr.alignPanels != null)
2007 for (AlignmentPanel ap : afr.alignPanels)
2009 if (sequenceSetId == null
2010 || sequenceSetId.equals(ap.av.getSequenceSetId()))
2018 viewp.add(afr.getViewport());
2022 if (viewp.size() > 0)
2024 return viewp.toArray(new AlignmentViewport[viewp.size()]);
2031 * Explode the views in the given frame into separate AlignFrame
2035 public static void explodeViews(AlignFrame af)
2037 int size = af.alignPanels.size();
2043 // FIXME: ideally should use UI interface API
2044 FeatureSettings viewFeatureSettings = (af.featureSettings != null
2045 && af.featureSettings.isOpen()) ? af.featureSettings : null;
2046 Rectangle fsBounds = af.getFeatureSettingsGeometry();
2047 for (int i = 0; i < size; i++)
2049 AlignmentPanel ap = af.alignPanels.get(i);
2051 AlignFrame newaf = new AlignFrame(ap);
2053 // transfer reference for existing feature settings to new alignFrame
2054 if (ap == af.alignPanel)
2056 if (viewFeatureSettings != null && viewFeatureSettings.fr.ap == ap)
2058 newaf.featureSettings = viewFeatureSettings;
2060 newaf.setFeatureSettingsGeometry(fsBounds);
2064 * Restore the view's last exploded frame geometry if known. Multiple
2065 * views from one exploded frame share and restore the same (frame)
2066 * position and size.
2068 Rectangle geometry = ap.av.getExplodedGeometry();
2069 if (geometry != null)
2071 newaf.setBounds(geometry);
2074 ap.av.setGatherViewsHere(false);
2076 addInternalFrame(newaf, af.getTitle(), AlignFrame.DEFAULT_WIDTH,
2077 AlignFrame.DEFAULT_HEIGHT);
2078 // and materialise a new feature settings dialog instance for the new
2080 // (closes the old as if 'OK' was pressed)
2081 if (ap == af.alignPanel && newaf.featureSettings != null
2082 && newaf.featureSettings.isOpen()
2083 && af.alignPanel.getAlignViewport().isShowSequenceFeatures())
2085 newaf.showFeatureSettingsUI();
2089 af.featureSettings = null;
2090 af.alignPanels.clear();
2091 af.closeMenuItem_actionPerformed(true);
2096 * Gather expanded views (separate AlignFrame's) with the same sequence set
2097 * identifier back in to this frame as additional views, and close the
2098 * expanded views. Note the expanded frames may themselves have multiple
2099 * views. We take the lot.
2103 public void gatherViews(AlignFrame source)
2105 source.viewport.setGatherViewsHere(true);
2106 source.viewport.setExplodedGeometry(source.getBounds());
2107 JInternalFrame[] frames = desktopPane.getAllFrames();
2108 String viewId = source.viewport.getSequenceSetId();
2109 for (int t = 0; t < frames.length; t++)
2111 if (frames[t] instanceof AlignFrame && frames[t] != source)
2113 AlignFrame af = (AlignFrame) frames[t];
2114 boolean gatherThis = false;
2115 for (int a = 0; a < af.alignPanels.size(); a++)
2117 AlignmentPanel ap = af.alignPanels.get(a);
2118 if (viewId.equals(ap.av.getSequenceSetId()))
2121 ap.av.setGatherViewsHere(false);
2122 ap.av.setExplodedGeometry(af.getBounds());
2123 source.addAlignmentPanel(ap, false);
2129 if (af.featureSettings != null && af.featureSettings.isOpen())
2131 if (source.featureSettings == null)
2133 // preserve the feature settings geometry for this frame
2134 source.featureSettings = af.featureSettings;
2135 source.setFeatureSettingsGeometry(
2136 af.getFeatureSettingsGeometry());
2140 // close it and forget
2141 af.featureSettings.close();
2144 af.alignPanels.clear();
2145 af.closeMenuItem_actionPerformed(true);
2150 // refresh the feature setting UI for the source frame if it exists
2151 if (source.featureSettings != null && source.featureSettings.isOpen())
2153 source.showFeatureSettingsUI();
2157 public JInternalFrame[] getAllFrames()
2159 return desktopPane.getAllFrames();
2163 * Checks the given url to see if it gives a response indicating that the user
2164 * should be informed of a new questionnaire.
2168 public void checkForQuestionnaire(String url)
2170 UserQuestionnaireCheck jvq = new UserQuestionnaireCheck(url);
2171 // javax.swing.SwingUtilities.invokeLater(jvq);
2172 new Thread(jvq).start();
2175 public void checkURLLinks()
2177 // Thread off the URL link checker
2178 addDialogThread(new Runnable()
2183 if (Cache.getDefault("CHECKURLLINKS", true))
2185 // check what the actual links are - if it's just the default don't
2186 // bother with the warning
2187 List<String> links = Preferences.sequenceUrlLinks
2190 // only need to check links if there is one with a
2191 // SEQUENCE_ID which is not the default EMBL_EBI link
2192 ListIterator<String> li = links.listIterator();
2193 boolean check = false;
2194 List<JLabel> urls = new ArrayList<>();
2195 while (li.hasNext())
2197 String link = li.next();
2198 if (link.contains(jalview.util.UrlConstants.SEQUENCE_ID)
2199 && !UrlConstants.isDefaultString(link))
2202 int barPos = link.indexOf("|");
2203 String urlMsg = barPos == -1 ? link
2204 : link.substring(0, barPos) + ": "
2205 + link.substring(barPos + 1);
2206 urls.add(new JLabel(urlMsg));
2214 // ask user to check in case URL links use old style tokens
2215 // ($SEQUENCE_ID$ for sequence id _or_ accession id)
2216 JPanel msgPanel = new JPanel();
2217 msgPanel.setLayout(new BoxLayout(msgPanel, BoxLayout.PAGE_AXIS));
2218 msgPanel.add(Box.createVerticalGlue());
2219 JLabel msg = new JLabel(MessageManager
2220 .getString("label.SEQUENCE_ID_for_DB_ACCESSION1"));
2221 JLabel msg2 = new JLabel(MessageManager
2222 .getString("label.SEQUENCE_ID_for_DB_ACCESSION2"));
2224 for (JLabel url : urls)
2230 final JCheckBox jcb = new JCheckBox(
2231 MessageManager.getString("label.do_not_display_again"));
2232 jcb.addActionListener(new ActionListener()
2235 public void actionPerformed(ActionEvent e)
2237 // update Cache settings for "don't show this again"
2238 boolean showWarningAgain = !jcb.isSelected();
2239 Cache.setProperty("CHECKURLLINKS",
2240 Boolean.valueOf(showWarningAgain).toString());
2245 JvOptionPane.showMessageDialog(desktopPane, msgPanel,
2247 .getString("label.SEQUENCE_ID_no_longer_used"),
2248 JvOptionPane.WARNING_MESSAGE);
2255 * Proxy class for JDesktopPane which optionally displays the current memory
2256 * usage and highlights the desktop area with a red bar if free memory runs
2261 public class MyDesktopPane extends JDesktopPane implements Runnable
2263 private static final float ONE_MB = 1048576f;
2265 boolean showMemoryUsage = false;
2269 java.text.NumberFormat df;
2271 float maxMemory, allocatedMemory, freeMemory, totalFreeMemory,
2274 public MyDesktopPane(boolean showMemoryUsage)
2276 showMemoryUsage(showMemoryUsage);
2279 public void showMemoryUsage(boolean showMemory)
2281 this.showMemoryUsage = showMemory;
2284 Thread worker = new Thread(this);
2290 public boolean isShowMemoryUsage()
2292 return showMemoryUsage;
2298 df = java.text.NumberFormat.getNumberInstance();
2299 df.setMaximumFractionDigits(2);
2300 runtime = Runtime.getRuntime();
2302 while (showMemoryUsage)
2306 maxMemory = runtime.maxMemory() / ONE_MB;
2307 allocatedMemory = runtime.totalMemory() / ONE_MB;
2308 freeMemory = runtime.freeMemory() / ONE_MB;
2309 totalFreeMemory = freeMemory + (maxMemory - allocatedMemory);
2311 percentUsage = (totalFreeMemory / maxMemory) * 100;
2313 // if (percentUsage < 20)
2315 // border1 = BorderFactory.createMatteBorder(12, 12, 12, 12,
2317 // instance.set.setBorder(border1);
2320 // sleep after showing usage
2322 } catch (Exception ex)
2324 ex.printStackTrace();
2330 public void paintComponent(Graphics g)
2332 if (showMemoryUsage && g != null && df != null)
2334 if (percentUsage < 20)
2336 g.setColor(Color.red);
2338 FontMetrics fm = g.getFontMetrics();
2341 g.drawString(MessageManager.formatMessage("label.memory_stats",
2343 { df.format(totalFreeMemory), df.format(maxMemory),
2344 df.format(percentUsage) }),
2345 10, getHeight() - fm.getHeight());
2352 * Accessor method to quickly get all the AlignmentFrames loaded.
2354 * @return an array of AlignFrame, or null if none found
2356 public static AlignFrame[] getAlignFrames()
2358 if (Jalview.isHeadlessMode())
2360 return new AlignFrame[] { Jalview.getInstance().currentAlignFrame };
2363 JInternalFrame[] frames = getDesktopPane().getAllFrames();
2369 List<AlignFrame> avp = new ArrayList<>();
2371 for (int i = frames.length - 1; i > -1; i--)
2373 if (frames[i] instanceof AlignFrame)
2375 avp.add((AlignFrame) frames[i]);
2377 else if (frames[i] instanceof SplitFrame)
2380 * Also check for a split frame containing an AlignFrame
2382 GSplitFrame sf = (GSplitFrame) frames[i];
2383 if (sf.getTopFrame() instanceof AlignFrame)
2385 avp.add((AlignFrame) sf.getTopFrame());
2387 if (sf.getBottomFrame() instanceof AlignFrame)
2389 avp.add((AlignFrame) sf.getBottomFrame());
2393 if (avp.size() == 0)
2397 AlignFrame afs[] = avp.toArray(new AlignFrame[avp.size()]);
2402 * Returns an array of any AppJmol frames in the Desktop (or null if none).
2406 public GStructureViewer[] getJmols()
2408 JInternalFrame[] frames = desktopPane.getAllFrames();
2414 List<GStructureViewer> avp = new ArrayList<>();
2416 for (int i = frames.length - 1; i > -1; i--)
2418 if (frames[i] instanceof AppJmol)
2420 GStructureViewer af = (GStructureViewer) frames[i];
2424 if (avp.size() == 0)
2428 GStructureViewer afs[] = avp.toArray(new GStructureViewer[avp.size()]);
2433 * Add Groovy Support to Jalview
2436 public void groovyShell_actionPerformed()
2440 openGroovyConsole();
2441 } catch (Exception ex)
2443 Cache.log.error("Groovy Shell Creation failed.", ex);
2444 JvOptionPane.showInternalMessageDialog(desktopPane,
2446 MessageManager.getString("label.couldnt_create_groovy_shell"),
2447 MessageManager.getString("label.groovy_support_failed"),
2448 JvOptionPane.ERROR_MESSAGE);
2453 * Open the Groovy console
2455 void openGroovyConsole()
2457 if (groovyConsole == null)
2459 groovyConsole = new groovy.ui.Console();
2460 groovyConsole.setVariable("Jalview", this);
2461 groovyConsole.run();
2464 * We allow only one console at a time, so that AlignFrame menu option
2465 * 'Calculate | Run Groovy script' is unambiguous.
2466 * Disable 'Groovy Console', and enable 'Run script', when the console is
2467 * opened, and the reverse when it is closed
2469 Window window = (Window) groovyConsole.getFrame();
2470 window.addWindowListener(new WindowAdapter()
2473 public void windowClosed(WindowEvent e)
2476 * rebind CMD-Q from Groovy Console to Jalview Quit
2479 enableExecuteGroovy(false);
2485 * show Groovy console window (after close and reopen)
2487 ((Window) groovyConsole.getFrame()).setVisible(true);
2490 * if we got this far, enable 'Run Groovy' in AlignFrame menus
2491 * and disable opening a second console
2493 enableExecuteGroovy(true);
2497 * Bind Ctrl/Cmd-Q to Quit - for reset as Groovy Console takes over this
2498 * binding when opened
2500 protected void addQuitHandler()
2503 getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
2504 .put(KeyStroke.getKeyStroke(KeyEvent.VK_Q,
2505 Platform.SHORTCUT_KEY_MASK),
2507 getRootPane().getActionMap().put("Quit", new AbstractAction()
2510 public void actionPerformed(ActionEvent e)
2518 * Enable or disable 'Run Groovy script' in AlignFrame calculate menus
2521 * true if Groovy console is open
2523 public void enableExecuteGroovy(boolean enabled)
2526 * disable opening a second Groovy console
2527 * (or re-enable when the console is closed)
2529 groovyShell.setEnabled(!enabled);
2531 AlignFrame[] alignFrames = getAlignFrames();
2532 if (alignFrames != null)
2534 for (AlignFrame af : alignFrames)
2536 af.setGroovyEnabled(enabled);
2542 * Progress bars managed by the IProgressIndicator method.
2544 private Hashtable<Long, JPanel> progressBars;
2546 private Hashtable<Long, IProgressIndicatorHandler> progressBarHandlers;
2551 * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
2554 public void setProgressBar(String message, long id)
2556 // Platform.timeCheck("Desktop " + message, Platform.TIME_MARK);
2558 if (progressBars == null)
2560 progressBars = new Hashtable<>();
2561 progressBarHandlers = new Hashtable<>();
2564 if (progressBars.get(Long.valueOf(id)) != null)
2566 JPanel panel = progressBars.remove(Long.valueOf(id));
2567 if (progressBarHandlers.contains(Long.valueOf(id)))
2569 progressBarHandlers.remove(Long.valueOf(id));
2571 removeProgressPanel(panel);
2575 progressBars.put(Long.valueOf(id), addProgressPanel(message));
2582 * @see jalview.gui.IProgressIndicator#registerHandler(long,
2583 * jalview.gui.IProgressIndicatorHandler)
2586 public void registerHandler(final long id,
2587 final IProgressIndicatorHandler handler)
2589 if (progressBarHandlers == null
2590 || !progressBars.containsKey(Long.valueOf(id)))
2592 throw new Error(MessageManager.getString(
2593 "error.call_setprogressbar_before_registering_handler"));
2595 progressBarHandlers.put(Long.valueOf(id), handler);
2596 final JPanel progressPanel = progressBars.get(Long.valueOf(id));
2597 if (handler.canCancel())
2599 JButton cancel = new JButton(
2600 MessageManager.getString("action.cancel"));
2601 final IProgressIndicator us = this;
2602 cancel.addActionListener(new ActionListener()
2606 public void actionPerformed(ActionEvent e)
2608 handler.cancelActivity(id);
2609 us.setProgressBar(MessageManager
2610 .formatMessage("label.cancelled_params", new Object[]
2611 { ((JLabel) progressPanel.getComponent(0)).getText() }),
2615 progressPanel.add(cancel, BorderLayout.EAST);
2621 * @return true if any progress bars are still active
2624 public boolean operationInProgress()
2626 if (progressBars != null && progressBars.size() > 0)
2634 * This will return the first AlignFrame holding the given viewport instance.
2635 * It will break if there are more than one AlignFrames viewing a particular
2639 * @return alignFrame for viewport
2641 public static AlignFrame getAlignFrameFor(AlignViewportI viewport)
2643 if (getDesktopPane() != null)
2645 AlignmentPanel[] aps = getAlignmentPanels(
2646 viewport.getSequenceSetId());
2647 for (int panel = 0; aps != null && panel < aps.length; panel++)
2649 if (aps[panel] != null && aps[panel].av == viewport)
2651 return aps[panel].alignFrame;
2658 public VamsasApplication getVamsasApplication()
2660 // TODO: JAL-3311 remove remaining code from Jalview relating to VAMSAS
2666 * flag set if jalview GUI is being operated programmatically
2668 private boolean inBatchMode = false;
2671 * check if jalview GUI is being operated programmatically
2673 * @return inBatchMode
2675 public boolean isInBatchMode()
2681 * set flag if jalview GUI is being operated programmatically
2683 * @param inBatchMode
2685 public void setInBatchMode(boolean inBatchMode)
2687 this.inBatchMode = inBatchMode;
2690 public void startServiceDiscovery()
2692 startServiceDiscovery(false);
2695 public void startServiceDiscovery(boolean blocking)
2697 boolean alive = true;
2698 Thread t0 = null, t1 = null, t2 = null;
2699 // JAL-940 - JALVIEW 1 services are now being EOLed as of JABA 2.1 release
2702 // todo: changesupport handlers need to be transferred
2703 if (discoverer == null)
2705 discoverer = jalview.ws.jws1.Discoverer.getInstance();
2706 // register PCS handler for desktop.
2707 discoverer.addPropertyChangeListener(changeSupport);
2709 // JAL-940 - disabled JWS1 service configuration - always start discoverer
2710 // until we phase out completely
2711 (t0 = new Thread(discoverer)).start();
2714 if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
2716 t2 = jalview.ws.jws2.Jws2Discoverer.getInstance()
2717 .startDiscoverer(changeSupport);
2721 // TODO: do rest service discovery
2730 } catch (Exception e)
2733 alive = (t1 != null && t1.isAlive()) || (t2 != null && t2.isAlive())
2734 || (t3 != null && t3.isAlive())
2735 || (t0 != null && t0.isAlive());
2741 * called to check if the service discovery process completed successfully.
2745 protected void JalviewServicesChanged(PropertyChangeEvent evt)
2747 if (evt.getNewValue() == null || evt.getNewValue() instanceof Vector)
2749 final String ermsg = jalview.ws.jws2.Jws2Discoverer.getInstance()
2750 .getErrorMessages();
2753 if (Cache.getDefault("SHOW_WSDISCOVERY_ERRORS", true))
2755 if (serviceChangedDialog == null)
2757 // only run if we aren't already displaying one of these.
2758 addDialogThread(serviceChangedDialog = new Runnable()
2765 * JalviewDialog jd =new JalviewDialog() {
2767 * @Override protected void cancelPressed() { // TODO
2768 * Auto-generated method stub
2770 * }@Override protected void okPressed() { // TODO
2771 * Auto-generated method stub
2773 * }@Override protected void raiseClosed() { // TODO
2774 * Auto-generated method stub
2776 * } }; jd.initDialogFrame(new
2777 * JLabel("<html><table width=\"450\"><tr><td>" + ermsg +
2778 * "<br/>It may be that you have invalid JABA URLs in your web service preferences,"
2779 * + " or mis-configured HTTP proxy settings.<br/>" +
2780 * "Check the <em>Connections</em> and <em>Web services</em> tab of the"
2782 * " Tools->Preferences dialog box to change them.</td></tr></table></html>"
2783 * ), true, true, "Web Service Configuration Problem", 450,
2786 * jd.waitForInput();
2788 JvOptionPane.showConfirmDialog(desktopPane,
2789 new JLabel("<html><table width=\"450\"><tr><td>"
2790 + ermsg + "</td></tr></table>"
2791 + "<p>It may be that you have invalid JABA URLs<br/>in your web service preferences,"
2792 + "<br>or as a command-line argument, or mis-configured HTTP proxy settings.</p>"
2793 + "<p>Check the <em>Connections</em> and <em>Web services</em> tab<br/>of the"
2794 + " Tools->Preferences dialog box to change them.</p></html>"),
2795 "Web Service Configuration Problem",
2796 JvOptionPane.DEFAULT_OPTION,
2797 JvOptionPane.ERROR_MESSAGE);
2798 serviceChangedDialog = null;
2807 "Errors reported by JABA discovery service. Check web services preferences.\n"
2814 private Runnable serviceChangedDialog = null;
2817 * start a thread to open a URL in the configured browser. Pops up a warning
2818 * dialog to the user if there is an exception when calling out to the browser
2823 public static void showUrl(final String url)
2825 showUrl(url, getInstance());
2829 * Like showUrl but allows progress handler to be specified
2833 * (null) or object implementing IProgressIndicator
2835 public static void showUrl(final String url,
2836 final IProgressIndicator progress)
2838 new Thread(new Runnable()
2845 if (progress != null)
2847 progress.setProgressBar(MessageManager
2848 .formatMessage("status.opening_params", new Object[]
2849 { url }), this.hashCode());
2851 jalview.util.BrowserLauncher.openURL(url);
2852 } catch (Exception ex)
2854 JvOptionPane.showInternalMessageDialog(getDesktopPane(),
2856 .getString("label.web_browser_not_found_unix"),
2857 MessageManager.getString("label.web_browser_not_found"),
2858 JvOptionPane.WARNING_MESSAGE);
2860 ex.printStackTrace();
2862 if (progress != null)
2864 progress.setProgressBar(null, this.hashCode());
2870 public static WsParamSetManager wsparamManager = null;
2872 public static ParamManager getUserParameterStore()
2874 if (wsparamManager == null)
2876 wsparamManager = new WsParamSetManager();
2878 return wsparamManager;
2882 * static hyperlink handler proxy method for use by Jalview's internal windows
2886 public static void hyperlinkUpdate(HyperlinkEvent e)
2888 if (e.getEventType() == EventType.ACTIVATED)
2893 url = e.getURL().toString();
2895 } catch (Exception x)
2899 if (Cache.log != null)
2901 Cache.log.error("Couldn't handle string " + url + " as a URL.");
2906 "Couldn't handle string " + url + " as a URL.");
2909 // ignore any exceptions due to dud links.
2916 * single thread that handles display of dialogs to user.
2918 ExecutorService dialogExecutor = Executors.newSingleThreadExecutor();
2921 * flag indicating if dialogExecutor should try to acquire a permit
2923 private volatile boolean dialogPause = true;
2928 private java.util.concurrent.Semaphore block = new Semaphore(0);
2930 private static groovy.ui.Console groovyConsole;
2933 * add another dialog thread to the queue
2937 public void addDialogThread(final Runnable prompter)
2939 dialogExecutor.submit(new Runnable()
2949 } catch (InterruptedException x)
2953 if (Jalview.isHeadlessMode())
2959 SwingUtilities.invokeAndWait(prompter);
2960 } catch (Exception q)
2962 Cache.log.warn("Unexpected Exception in dialog thread.", q);
2968 public void startDialogQueue()
2970 // set the flag so we don't pause waiting for another permit and semaphore
2971 // the current task to begin
2972 dialogPause = false;
2977 * Outputs an image of the desktop to file in EPS format, after prompting the
2978 * user for choice of Text or Lineart character rendering (unless a preference
2979 * has been set). The file name is generated as
2982 * Jalview_snapshot_nnnnn.eps where nnnnn is the current timestamp in milliseconds
2986 protected void snapShotWindow_actionPerformed(ActionEvent e)
2988 // currently the menu option to do this is not shown
2991 int width = getWidth();
2992 int height = getHeight();
2994 "Jalview_snapshot_" + System.currentTimeMillis() + ".eps");
2995 ImageWriterI writer = new ImageWriterI()
2998 public void exportImage(Graphics g) throws Exception
3001 Cache.log.info("Successfully written snapshot to file "
3002 + of.getAbsolutePath());
3005 String title = "View of desktop";
3006 ImageExporter exporter = new ImageExporter(writer, null, TYPE.EPS,
3008 exporter.doExport(of, this, width, height, title);
3012 * Explode the views in the given SplitFrame into separate SplitFrame windows.
3013 * This respects (remembers) any previous 'exploded geometry' i.e. the size
3014 * and location last time the view was expanded (if any). However it does not
3015 * remember the split pane divider location - this is set to match the
3016 * 'exploding' frame.
3020 public void explodeViews(SplitFrame sf)
3022 AlignFrame oldTopFrame = (AlignFrame) sf.getTopFrame();
3023 AlignFrame oldBottomFrame = (AlignFrame) sf.getBottomFrame();
3024 List<? extends AlignmentViewPanel> topPanels = oldTopFrame
3026 List<? extends AlignmentViewPanel> bottomPanels = oldBottomFrame
3028 int viewCount = topPanels.size();
3035 * Processing in reverse order works, forwards order leaves the first panels
3036 * not visible. I don't know why!
3038 for (int i = viewCount - 1; i >= 0; i--)
3041 * Make new top and bottom frames. These take over the respective
3042 * AlignmentPanel objects, including their AlignmentViewports, so the
3043 * cdna/protein relationships between the viewports is carried over to the
3046 * explodedGeometry holds the (x, y) position of the previously exploded
3047 * SplitFrame, and the (width, height) of the AlignFrame component
3049 AlignmentPanel topPanel = (AlignmentPanel) topPanels.get(i);
3050 AlignFrame newTopFrame = new AlignFrame(topPanel);
3051 newTopFrame.setSize(oldTopFrame.getSize());
3052 newTopFrame.setVisible(true);
3053 Rectangle geometry = ((AlignViewport) topPanel.getAlignViewport())
3054 .getExplodedGeometry();
3055 if (geometry != null)
3057 newTopFrame.setSize(geometry.getSize());
3060 AlignmentPanel bottomPanel = (AlignmentPanel) bottomPanels.get(i);
3061 AlignFrame newBottomFrame = new AlignFrame(bottomPanel);
3062 newBottomFrame.setSize(oldBottomFrame.getSize());
3063 newBottomFrame.setVisible(true);
3064 geometry = ((AlignViewport) bottomPanel.getAlignViewport())
3065 .getExplodedGeometry();
3066 if (geometry != null)
3068 newBottomFrame.setSize(geometry.getSize());
3071 topPanel.av.setGatherViewsHere(false);
3072 bottomPanel.av.setGatherViewsHere(false);
3073 JInternalFrame splitFrame = new SplitFrame(newTopFrame,
3075 if (geometry != null)
3077 splitFrame.setLocation(geometry.getLocation());
3079 addInternalFrame(splitFrame, sf.getTitle(), -1, -1);
3083 * Clear references to the panels (now relocated in the new SplitFrames)
3084 * before closing the old SplitFrame.
3087 bottomPanels.clear();
3092 * Gather expanded split frames, sharing the same pairs of sequence set ids,
3093 * back into the given SplitFrame as additional views. Note that the gathered
3094 * frames may themselves have multiple views.
3098 public void gatherViews(GSplitFrame source)
3101 * special handling of explodedGeometry for a view within a SplitFrame: - it
3102 * holds the (x, y) position of the enclosing SplitFrame, and the (width,
3103 * height) of the AlignFrame component
3105 AlignFrame myTopFrame = (AlignFrame) source.getTopFrame();
3106 AlignFrame myBottomFrame = (AlignFrame) source.getBottomFrame();
3107 myTopFrame.viewport.setExplodedGeometry(new Rectangle(source.getX(),
3108 source.getY(), myTopFrame.getWidth(), myTopFrame.getHeight()));
3109 myBottomFrame.viewport
3110 .setExplodedGeometry(new Rectangle(source.getX(), source.getY(),
3111 myBottomFrame.getWidth(), myBottomFrame.getHeight()));
3112 myTopFrame.viewport.setGatherViewsHere(true);
3113 myBottomFrame.viewport.setGatherViewsHere(true);
3114 String topViewId = myTopFrame.viewport.getSequenceSetId();
3115 String bottomViewId = myBottomFrame.viewport.getSequenceSetId();
3117 JInternalFrame[] frames = desktopPane.getAllFrames();
3118 for (JInternalFrame frame : frames)
3120 if (frame instanceof SplitFrame && frame != source)
3122 SplitFrame sf = (SplitFrame) frame;
3123 AlignFrame topFrame = (AlignFrame) sf.getTopFrame();
3124 AlignFrame bottomFrame = (AlignFrame) sf.getBottomFrame();
3125 boolean gatherThis = false;
3126 for (int a = 0; a < topFrame.alignPanels.size(); a++)
3128 AlignmentPanel topPanel = topFrame.alignPanels.get(a);
3129 AlignmentPanel bottomPanel = bottomFrame.alignPanels.get(a);
3130 if (topViewId.equals(topPanel.av.getSequenceSetId())
3131 && bottomViewId.equals(bottomPanel.av.getSequenceSetId()))
3134 topPanel.av.setGatherViewsHere(false);
3135 bottomPanel.av.setGatherViewsHere(false);
3136 topPanel.av.setExplodedGeometry(
3137 new Rectangle(sf.getLocation(), topFrame.getSize()));
3138 bottomPanel.av.setExplodedGeometry(
3139 new Rectangle(sf.getLocation(), bottomFrame.getSize()));
3140 myTopFrame.addAlignmentPanel(topPanel, false);
3141 myBottomFrame.addAlignmentPanel(bottomPanel, false);
3147 topFrame.getAlignPanels().clear();
3148 bottomFrame.getAlignPanels().clear();
3155 * The dust settles...give focus to the tab we did this from.
3157 myTopFrame.setDisplayedView(myTopFrame.alignPanel);
3160 public static groovy.ui.Console getGroovyConsole()
3162 return groovyConsole;
3166 * handles the payload of a drag and drop event.
3168 * TODO refactor to desktop utilities class
3171 * - Data source strings extracted from the drop event
3173 * - protocol for each data source extracted from the drop event
3177 * - the payload from the drop event
3180 @SuppressWarnings("unchecked")
3181 public static void transferFromDropTarget(List<Object> files,
3182 List<DataSourceType> protocols, DropTargetDropEvent evt,
3183 Transferable t) throws Exception
3186 // BH 2018 changed List<String> to List<Object> to allow for File from
3189 // DataFlavor[] flavors = t.getTransferDataFlavors();
3190 // for (int i = 0; i < flavors.length; i++) {
3191 // if (flavors[i].isFlavorJavaFileListType()) {
3192 // evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
3193 // List<File> list = (List<File>) t.getTransferData(flavors[i]);
3194 // for (int j = 0; j < list.size(); j++) {
3195 // File file = (File) list.get(j);
3196 // byte[] data = getDroppedFileBytes(file);
3197 // fileName.setText(file.getName() + " - " + data.length + " " +
3198 // evt.getLocation());
3199 // JTextArea target = (JTextArea) ((DropTarget)
3200 // evt.getSource()).getComponent();
3201 // target.setText(new String(data));
3203 // dtde.dropComplete(true);
3208 DataFlavor uriListFlavor = new DataFlavor(
3209 "text/uri-list;class=java.lang.String"), urlFlavour = null;
3212 urlFlavour = new DataFlavor(
3213 "application/x-java-url; class=java.net.URL");
3214 } catch (ClassNotFoundException cfe)
3216 Cache.log.debug("Couldn't instantiate the URL dataflavor.", cfe);
3219 if (urlFlavour != null && t.isDataFlavorSupported(urlFlavour))
3224 java.net.URL url = (URL) t.getTransferData(urlFlavour);
3225 // nb: java 8 osx bug https://bugs.openjdk.java.net/browse/JDK-8156099
3226 // means url may be null.
3229 protocols.add(DataSourceType.URL);
3230 files.add(url.toString());
3231 Cache.log.debug("Drop handled as URL dataflavor "
3232 + files.get(files.size() - 1));
3237 if (Platform.isAMacAndNotJS())
3240 "Please ignore plist error - occurs due to problem with java 8 on OSX");
3243 } catch (Throwable ex)
3245 Cache.log.debug("URL drop handler failed.", ex);
3248 if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
3250 // Works on Windows and MacOSX
3251 Cache.log.debug("Drop handled as javaFileListFlavor");
3252 for (File file : (List<File>) t
3253 .getTransferData(DataFlavor.javaFileListFlavor))
3256 protocols.add(DataSourceType.FILE);
3261 // Unix like behaviour
3262 boolean added = false;
3264 if (t.isDataFlavorSupported(uriListFlavor))
3266 Cache.log.debug("Drop handled as uriListFlavor");
3267 // This is used by Unix drag system
3268 data = (String) t.getTransferData(uriListFlavor);
3272 // fallback to text: workaround - on OSX where there's a JVM bug
3273 Cache.log.debug("standard URIListFlavor failed. Trying text");
3274 // try text fallback
3275 DataFlavor textDf = new DataFlavor(
3276 "text/plain;class=java.lang.String");
3277 if (t.isDataFlavorSupported(textDf))
3279 data = (String) t.getTransferData(textDf);
3282 Cache.log.debug("Plain text drop content returned "
3283 + (data == null ? "Null - failed" : data));
3288 while (protocols.size() < files.size())
3290 Cache.log.debug("Adding missing FILE protocol for "
3291 + files.get(protocols.size()));
3292 protocols.add(DataSourceType.FILE);
3294 for (java.util.StringTokenizer st = new java.util.StringTokenizer(
3295 data, "\r\n"); st.hasMoreTokens();)
3298 String s = st.nextToken();
3299 if (s.startsWith("#"))
3301 // the line is a comment (as per the RFC 2483)
3304 java.net.URI uri = new java.net.URI(s);
3305 if (uri.getScheme().toLowerCase().startsWith("http"))
3307 protocols.add(DataSourceType.URL);
3308 files.add(uri.toString());
3312 // otherwise preserve old behaviour: catch all for file objects
3313 java.io.File file = new java.io.File(uri);
3314 protocols.add(DataSourceType.FILE);
3315 files.add(file.toString());
3320 if (Cache.log.isDebugEnabled())
3322 if (data == null || !added)
3325 if (t.getTransferDataFlavors() != null
3326 && t.getTransferDataFlavors().length > 0)
3329 "Couldn't resolve drop data. Here are the supported flavors:");
3330 for (DataFlavor fl : t.getTransferDataFlavors())
3333 "Supported transfer dataflavor: " + fl.toString());
3334 Object df = t.getTransferData(fl);
3337 Cache.log.debug("Retrieves: " + df);
3341 Cache.log.debug("Retrieved nothing");
3347 Cache.log.debug("Couldn't resolve dataflavor for drop: "
3353 if (Platform.isWindowsAndNotJS())
3355 Cache.log.debug("Scanning dropped content for Windows Link Files");
3357 // resolve any .lnk files in the file drop
3358 for (int f = 0; f < files.size(); f++)
3360 String source = files.get(f).toString().toLowerCase();
3361 if (protocols.get(f).equals(DataSourceType.FILE)
3362 && (source.endsWith(".lnk") || source.endsWith(".url")
3363 || source.endsWith(".site")))
3367 Object obj = files.get(f);
3368 File lf = (obj instanceof File ? (File) obj
3369 : new File((String) obj));
3370 // process link file to get a URL
3371 Cache.log.debug("Found potential link file: " + lf);
3372 WindowsShortcut wscfile = new WindowsShortcut(lf);
3373 String fullname = wscfile.getRealFilename();
3374 protocols.set(f, FormatAdapter.checkProtocol(fullname));
3375 files.set(f, fullname);
3376 Cache.log.debug("Parsed real filename " + fullname
3377 + " to extract protocol: " + protocols.get(f));
3378 } catch (Exception ex)
3381 "Couldn't parse " + files.get(f) + " as a link file.",
3390 * Sets the Preferences property for experimental features to True or False
3391 * depending on the state of the controlling menu item
3394 protected void showExperimental_actionPerformed(boolean selected)
3396 Cache.setProperty(EXPERIMENTAL_FEATURES, Boolean.toString(selected));
3400 * Answers a (possibly empty) list of any structure viewer frames (currently
3401 * for either Jmol or Chimera) which are currently open. This may optionally
3402 * be restricted to viewers of a specified class, or viewers linked to a
3403 * specified alignment panel.
3406 * if not null, only return viewers linked to this panel
3407 * @param structureViewerClass
3408 * if not null, only return viewers of this class
3411 public List<StructureViewerBase> getStructureViewers(
3412 AlignmentPanel apanel,
3413 Class<? extends StructureViewerBase> structureViewerClass)
3415 List<StructureViewerBase> result = new ArrayList<>();
3416 JInternalFrame[] frames = getAllFrames();
3418 for (JInternalFrame frame : frames)
3420 if (frame instanceof StructureViewerBase)
3422 if (structureViewerClass == null
3423 || structureViewerClass.isInstance(frame))
3426 || ((StructureViewerBase) frame).isLinkedWith(apanel))
3428 result.add((StructureViewerBase) frame);