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.ShortcutKeyMaskExWrapper;
125 import jalview.util.UrlConstants;
126 import jalview.viewmodel.AlignmentViewport;
127 import jalview.ws.params.ParamManager;
128 import jalview.ws.utils.UrlDownloadClient;
135 * @version $Revision: 1.155 $
137 @SuppressWarnings("serial")
138 public class Desktop extends GDesktop
139 implements DropTargetListener, ClipboardOwner, IProgressIndicator,
140 StructureSelectionManagerProvider, ApplicationSingletonI
143 private static final String CITATION = "<br><br>Development managed by The Barton Group, University of Dundee, Scotland, UK.<br>"
144 + "<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"
145 + "<br><br>If you use Jalview, please cite:"
146 + "<br>Waterhouse, A.M., Procter, J.B., Martin, D.M.A, Clamp, M. and Barton, G. J. (2009)"
147 + "<br>Jalview Version 2 - a multiple sequence alignment editor and analysis workbench"
148 + "<br>Bioinformatics doi: 10.1093/bioinformatics/btp033";
150 private static final String DEFAULT_AUTHORS = "The Jalview Authors (See AUTHORS file for current list)";
152 private static int DEFAULT_MIN_WIDTH = 300;
154 private static int DEFAULT_MIN_HEIGHT = 250;
156 private static int ALIGN_FRAME_DEFAULT_MIN_WIDTH = 600;
158 private static int ALIGN_FRAME_DEFAULT_MIN_HEIGHT = 70;
160 private static final String EXPERIMENTAL_FEATURES = "EXPERIMENTAL_FEATURES";
162 protected static final String CONFIRM_KEYBOARD_QUIT = "CONFIRM_KEYBOARD_QUIT";
164 public static HashMap<String, FileWriter> savingFiles = new HashMap<String, FileWriter>();
166 private JalviewChangeSupport changeSupport = new JalviewChangeSupport();
169 * news reader - null if it was never started.
171 private BlogReader jvnews = null;
173 private File projectFile;
177 * @see jalview.gui.JalviewChangeSupport#addJalviewPropertyChangeListener(java.beans.PropertyChangeListener)
179 public void addJalviewPropertyChangeListener(
180 PropertyChangeListener listener)
182 changeSupport.addJalviewPropertyChangeListener(listener);
186 * @param propertyName
188 * @see jalview.gui.JalviewChangeSupport#addJalviewPropertyChangeListener(java.lang.String,
189 * java.beans.PropertyChangeListener)
191 public void addJalviewPropertyChangeListener(String propertyName,
192 PropertyChangeListener listener)
194 changeSupport.addJalviewPropertyChangeListener(propertyName, listener);
198 * @param propertyName
200 * @see jalview.gui.JalviewChangeSupport#removeJalviewPropertyChangeListener(java.lang.String,
201 * java.beans.PropertyChangeListener)
203 public void removeJalviewPropertyChangeListener(String propertyName,
204 PropertyChangeListener listener)
206 changeSupport.removeJalviewPropertyChangeListener(propertyName,
210 private MyDesktopPane desktopPane;
212 public static MyDesktopPane getDesktopPane()
214 Desktop desktop = getInstance();
215 return desktop == null ? null : desktop.desktopPane;
219 * Answers an 'application scope' singleton instance of this class. Separate
220 * SwingJS 'applets' running in the same browser page will each have a
221 * distinct instance of Desktop.
225 public static Desktop getInstance()
227 return Jalview.isHeadlessMode() ? null
228 : (Desktop) ApplicationSingletonProvider
229 .getInstance(Desktop.class);
232 public static StructureSelectionManager getStructureSelectionManager()
234 return StructureSelectionManager
235 .getStructureSelectionManager(getInstance());
238 int openFrameCount = 0;
240 final int xOffset = 30;
242 final int yOffset = 30;
244 public jalview.ws.jws1.Discoverer discoverer;
246 public Object[] jalviewClipboard;
248 public boolean internalCopy = false;
250 int fileLoadingCount = 0;
252 class MyDesktopManager implements DesktopManager
255 private DesktopManager delegate;
257 public MyDesktopManager(DesktopManager delegate)
259 this.delegate = delegate;
263 public void activateFrame(JInternalFrame f)
267 delegate.activateFrame(f);
268 } catch (NullPointerException npe)
270 Point p = getMousePosition();
271 showPasteMenu(p.x, p.y);
276 public void beginDraggingFrame(JComponent f)
278 delegate.beginDraggingFrame(f);
282 public void beginResizingFrame(JComponent f, int direction)
284 delegate.beginResizingFrame(f, direction);
288 public void closeFrame(JInternalFrame f)
290 delegate.closeFrame(f);
294 public void deactivateFrame(JInternalFrame f)
296 delegate.deactivateFrame(f);
300 public void deiconifyFrame(JInternalFrame f)
302 delegate.deiconifyFrame(f);
306 public void dragFrame(JComponent f, int newX, int newY)
312 delegate.dragFrame(f, newX, newY);
316 public void endDraggingFrame(JComponent f)
318 delegate.endDraggingFrame(f);
319 desktopPane.repaint();
323 public void endResizingFrame(JComponent f)
325 delegate.endResizingFrame(f);
326 desktopPane.repaint();
330 public void iconifyFrame(JInternalFrame f)
332 delegate.iconifyFrame(f);
336 public void maximizeFrame(JInternalFrame f)
338 delegate.maximizeFrame(f);
342 public void minimizeFrame(JInternalFrame f)
344 delegate.minimizeFrame(f);
348 public void openFrame(JInternalFrame f)
350 delegate.openFrame(f);
354 public void resizeFrame(JComponent f, int newX, int newY, int newWidth,
361 delegate.resizeFrame(f, newX, newY, newWidth, newHeight);
365 public void setBoundsForFrame(JComponent f, int newX, int newY,
366 int newWidth, int newHeight)
368 delegate.setBoundsForFrame(f, newX, newY, newWidth, newHeight);
371 // All other methods, simply delegate
376 * Private constructor enforces singleton pattern. It is called by reflection
377 * from ApplicationSingletonProvider.getInstance().
385 * A note to implementors. It is ESSENTIAL that any activities that might
386 * block are spawned off as threads rather than waited for during this
390 doConfigureStructurePrefs();
391 setTitle("Jalview " + Cache.getProperty("VERSION"));
393 if (!Platform.isAMac())
395 // this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
399 this.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
405 APQHandlers.setAPQHandlers(this);
406 } catch (Throwable t)
408 System.out.println("Error setting APQHandlers: " + t.toString());
409 // t.printStackTrace();
412 addWindowListener(new WindowAdapter()
416 public void windowClosing(WindowEvent ev)
422 boolean selmemusage = Cache.getDefault("SHOW_MEMUSAGE", false);
424 boolean showjconsole = Cache.getDefault("SHOW_JAVA_CONSOLE", false);
425 desktopPane = new MyDesktopPane(selmemusage);
427 showMemusage.setSelected(selmemusage);
428 desktopPane.setBackground(Color.white);
430 getContentPane().setLayout(new BorderLayout());
431 // alternate config - have scrollbars - see notes in JAL-153
432 // JScrollPane sp = new JScrollPane();
433 // sp.getViewport().setView(desktop);
434 // getContentPane().add(sp, BorderLayout.CENTER);
436 // BH 2018 - just an experiment to try unclipped JInternalFrames.
439 getRootPane().putClientProperty("swingjs.overflow.hidden", "false");
442 getContentPane().add(desktopPane, BorderLayout.CENTER);
443 desktopPane.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
445 // This line prevents Windows Look&Feel resizing all new windows to
447 // if previous window was maximised
448 desktopPane.setDesktopManager(new MyDesktopManager(
449 (Platform.isWindowsAndNotJS() ? new DefaultDesktopManager()
450 : Platform.isAMacAndNotJS()
451 ? new AquaInternalFrameManager(
452 desktopPane.getDesktopManager())
453 : desktopPane.getDesktopManager())));
455 Rectangle dims = getLastKnownDimensions("");
462 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
463 int xPos = Math.max(5, (screenSize.width - 900) / 2);
464 int yPos = Math.max(5, (screenSize.height - 650) / 2);
465 setBounds(xPos, yPos, 900, 650);
468 getIdentifiersOrgData();
470 if (!Platform.isJS())
477 jconsole = new Console(this, showjconsole);
478 jconsole.setHeader(Cache.getVersionDetailsForConsole());
479 showConsole(showjconsole);
481 showNews.setVisible(false);
483 experimentalFeatures.setSelected(showExperimental());
487 // Spawn a thread that shows the splashscreen
489 SwingUtilities.invokeLater(new Runnable()
494 new SplashScreen(true);
498 // Thread off a new instance of the file chooser - this reduces the time
500 // takes to open it later on.
501 new Thread(new Runnable()
506 Cache.log.debug("Filechooser init thread started.");
507 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
508 JalviewFileChooser.forRead(Cache.getProperty("LAST_DIRECTORY"),
510 Cache.log.debug("Filechooser init thread finished.");
513 // Add the service change listener
514 changeSupport.addJalviewPropertyChangeListener("services",
515 new PropertyChangeListener()
519 public void propertyChange(PropertyChangeEvent evt)
521 Cache.log.debug("Firing service changed event for "
522 + evt.getNewValue());
523 JalviewServicesChanged(evt);
528 this.setDropTarget(new java.awt.dnd.DropTarget(desktopPane, this));
530 this.addWindowListener(new WindowAdapter()
533 public void windowClosing(WindowEvent evt)
540 this.addMouseListener(ma = new MouseAdapter()
543 public void mousePressed(MouseEvent evt)
545 if (evt.isPopupTrigger()) // Mac
547 showPasteMenu(evt.getX(), evt.getY());
552 public void mouseReleased(MouseEvent evt)
554 if (evt.isPopupTrigger()) // Windows
556 showPasteMenu(evt.getX(), evt.getY());
560 desktopPane.addMouseListener(ma);
561 } catch (Throwable t)
569 * Answers true if user preferences to enable experimental features is True
574 public boolean showExperimental()
576 String experimental = Cache.getDefault(EXPERIMENTAL_FEATURES,
577 Boolean.FALSE.toString());
578 return Boolean.valueOf(experimental).booleanValue();
581 public void doConfigureStructurePrefs()
583 // configure services
584 StructureSelectionManager ssm = StructureSelectionManager
585 .getStructureSelectionManager(this);
586 if (Cache.getDefault(Preferences.ADD_SS_ANN, true))
588 ssm.setAddTempFacAnnot(
589 Cache.getDefault(Preferences.ADD_TEMPFACT_ANN, true));
590 ssm.setProcessSecondaryStructure(
591 Cache.getDefault(Preferences.STRUCT_FROM_PDB, true));
592 ssm.setSecStructServices(
593 Cache.getDefault(Preferences.USE_RNAVIEW, true));
597 ssm.setAddTempFacAnnot(false);
598 ssm.setProcessSecondaryStructure(false);
599 ssm.setSecStructServices(false);
603 public void checkForNews()
605 final Desktop me = this;
606 // Thread off the news reader, in case there are connection problems.
607 new Thread(new Runnable()
612 Cache.log.debug("Starting news thread.");
613 jvnews = new BlogReader(me);
614 showNews.setVisible(true);
615 Cache.log.debug("Completed news thread.");
620 public void getIdentifiersOrgData()
622 // Thread off the identifiers fetcher
623 new Thread(new Runnable()
628 Cache.log.debug("Downloading data from identifiers.org");
631 UrlDownloadClient.download(IdOrgSettings.getUrl(),
632 IdOrgSettings.getDownloadLocation());
633 } catch (IOException e)
635 Cache.log.debug("Exception downloading identifiers.org data"
644 protected void showNews_actionPerformed(ActionEvent e)
646 showNews(showNews.isSelected());
649 void showNews(boolean visible)
651 Cache.log.debug((visible ? "Showing" : "Hiding") + " news.");
652 showNews.setSelected(visible);
653 if (visible && !jvnews.isVisible())
655 new Thread(new Runnable()
660 long now = System.currentTimeMillis();
661 setProgressBar(MessageManager.getString("status.refreshing_news"),
663 jvnews.refreshNews();
664 setProgressBar(null, now);
672 * recover the last known dimensions for a jalview window
675 * - empty string is desktop, all other windows have unique prefix
676 * @return null or last known dimensions scaled to current geometry (if last
677 * window geom was known)
679 Rectangle getLastKnownDimensions(String windowName)
681 // TODO: lock aspect ratio for scaling desktop Bug #0058199
682 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
683 String x = Cache.getProperty(windowName + "SCREEN_X");
684 String y = Cache.getProperty(windowName + "SCREEN_Y");
685 String width = Cache.getProperty(windowName + "SCREEN_WIDTH");
686 String height = Cache.getProperty(windowName + "SCREEN_HEIGHT");
687 if ((x != null) && (y != null) && (width != null) && (height != null))
689 int ix = Integer.parseInt(x), iy = Integer.parseInt(y),
690 iw = Integer.parseInt(width), ih = Integer.parseInt(height);
691 if (Cache.getProperty("SCREENGEOMETRY_WIDTH") != null)
693 // attempt #1 - try to cope with change in screen geometry - this
694 // version doesn't preserve original jv aspect ratio.
695 // take ratio of current screen size vs original screen size.
696 double sw = ((1f * screenSize.width) / (1f * Integer
697 .parseInt(Cache.getProperty("SCREENGEOMETRY_WIDTH"))));
698 double sh = ((1f * screenSize.height) / (1f * Integer
699 .parseInt(Cache.getProperty("SCREENGEOMETRY_HEIGHT"))));
700 // rescale the bounds depending upon the current screen geometry.
701 ix = (int) (ix * sw);
702 iw = (int) (iw * sw);
703 iy = (int) (iy * sh);
704 ih = (int) (ih * sh);
705 while (ix >= screenSize.width)
708 "Window geometry location recall error: shifting horizontal to within screenbounds.");
709 ix -= screenSize.width;
711 while (iy >= screenSize.height)
714 "Window geometry location recall error: shifting vertical to within screenbounds.");
715 iy -= screenSize.height;
718 "Got last known dimensions for " + windowName + ": x:" + ix
719 + " y:" + iy + " width:" + iw + " height:" + ih);
721 // return dimensions for new instance
722 return new Rectangle(ix, iy, iw, ih);
727 void showPasteMenu(int x, int y)
729 JPopupMenu popup = new JPopupMenu();
730 JMenuItem item = new JMenuItem(
731 MessageManager.getString("label.paste_new_window"));
732 item.addActionListener(new ActionListener()
735 public void actionPerformed(ActionEvent evt)
742 popup.show(this, x, y);
749 Clipboard c = Toolkit.getDefaultToolkit().getSystemClipboard();
750 Transferable contents = c.getContents(this);
752 if (contents != null)
754 String file = (String) contents
755 .getTransferData(DataFlavor.stringFlavor);
757 FileFormatI format = new IdentifyFile().identify(file,
758 DataSourceType.PASTE);
760 new FileLoader().LoadFile(file, DataSourceType.PASTE, format);
763 } catch (Exception ex)
766 "Unable to paste alignment from system clipboard:\n" + ex);
771 // * Add an internal frame to the Jalview desktop that is allowed to be resized,
772 // * has a minimum size of 300px and might or might not be visible
778 // * @param makeVisible
779 // * When true, display frame immediately, otherwise, caller must call
780 // * setVisible themselves.
787 // public static synchronized void addInternalFrame(
788 // final JInternalFrame frame, String title, boolean makeVisible,
791 // // textbox, web services, sequenceFetcher, featureSettings
792 // getInstance().addFrame(frame, title, makeVisible, w, h,
793 // FRAME_ALLOW_RESIZE, FRAME_SET_MIN_SIZE_300);
797 // * Add an internal frame to the Jalview desktop that is visible, has a minimum
798 // * size of 300px, and may or may not be resizable
808 // * @param resizable
812 // public static synchronized void addInternalFrame(
813 // final JInternalFrame frame, String title, int w, int h,
814 // boolean resizable)
816 // // annotation, font, calculation, user-defined colors
817 // getInstance().addFrame(frame, title, FRAME_MAKE_VISIBLE, w, h,
818 // resizable, FRAME_SET_MIN_SIZE_300);
822 * Adds and opens the given frame to the desktop that is visible, allowed to
823 * resize, and has a 300px minimum width.
834 public static synchronized void addInternalFrame(
835 final JInternalFrame frame, String title, int w, int h)
838 getInstance().addFrame(frame, title, Desktop.FRAME_MAKE_VISIBLE, w, h,
839 FRAME_ALLOW_RESIZE, FRAME_SET_MIN_SIZE_300);
843 * Add an internal frame to the Jalview desktop that may optionally be
844 * visible, resizable, and allowed to be any size
851 * When true, display frame immediately, otherwise, caller must call
852 * setVisible themselves.
859 * @param ignoreMinSize
860 * Do not set the default minimum size for frame
862 public static synchronized void addInternalFrame(
863 final JInternalFrame frame, String title, boolean makeVisible,
864 int w, int h, boolean resizable, boolean ignoreMinSize)
867 getInstance().addFrame(frame, title, makeVisible, w, h, resizable,
871 // These can now by put into a single int flag, if desired:
873 public final static boolean FRAME_MAKE_VISIBLE = true;
875 public final static boolean FRAME_NOT_VISIBLE = false;
877 public final static boolean FRAME_ALLOW_RESIZE = true;
879 public final static boolean FRAME_NOT_RESIZABLE = false;
881 public final static boolean FRAME_ALLOW_ANY_SIZE = true;
883 public final static boolean FRAME_SET_MIN_SIZE_300 = false;
885 private void addFrame(JInternalFrame frame, String title,
886 boolean makeVisible, int w, int h, boolean resizable,
887 boolean ignoreMinSize)
889 // TODO: allow callers to determine X and Y position of frame (eg. via
891 // TODO: consider fixing method to update entries in the window submenu with
892 // the current window title
894 frame.setTitle(title);
895 if (frame.getWidth() < 1 || frame.getHeight() < 1)
899 // THIS IS A PUBLIC STATIC METHOD, SO IT MAY BE CALLED EVEN IN
900 // A HEADLESS STATE WHEN NO DESKTOP EXISTS. MUST RETURN
901 // IF JALVIEW IS RUNNING HEADLESS
902 // ///////////////////////////////////////////////
903 if (Jalview.isHeadlessMode())
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(title);
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 ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx());
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 .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
2505 .getKeyStroke(KeyEvent.VK_Q,
2506 jalview.util.ShortcutKeyMaskExWrapper
2507 .getMenuShortcutKeyMaskEx()),
2509 getRootPane().getActionMap().put("Quit", new AbstractAction()
2512 public void actionPerformed(ActionEvent e)
2520 * Enable or disable 'Run Groovy script' in AlignFrame calculate menus
2523 * true if Groovy console is open
2525 public void enableExecuteGroovy(boolean enabled)
2528 * disable opening a second Groovy console
2529 * (or re-enable when the console is closed)
2531 groovyShell.setEnabled(!enabled);
2533 AlignFrame[] alignFrames = getAlignFrames();
2534 if (alignFrames != null)
2536 for (AlignFrame af : alignFrames)
2538 af.setGroovyEnabled(enabled);
2544 * Progress bars managed by the IProgressIndicator method.
2546 private Hashtable<Long, JPanel> progressBars;
2548 private Hashtable<Long, IProgressIndicatorHandler> progressBarHandlers;
2553 * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
2556 public void setProgressBar(String message, long id)
2558 // Platform.timeCheck("Desktop " + message, Platform.TIME_MARK);
2560 if (progressBars == null)
2562 progressBars = new Hashtable<>();
2563 progressBarHandlers = new Hashtable<>();
2566 if (progressBars.get(Long.valueOf(id)) != null)
2568 JPanel panel = progressBars.remove(Long.valueOf(id));
2569 if (progressBarHandlers.contains(Long.valueOf(id)))
2571 progressBarHandlers.remove(Long.valueOf(id));
2573 removeProgressPanel(panel);
2577 progressBars.put(Long.valueOf(id), addProgressPanel(message));
2584 * @see jalview.gui.IProgressIndicator#registerHandler(long,
2585 * jalview.gui.IProgressIndicatorHandler)
2588 public void registerHandler(final long id,
2589 final IProgressIndicatorHandler handler)
2591 if (progressBarHandlers == null
2592 || !progressBars.containsKey(Long.valueOf(id)))
2594 throw new Error(MessageManager.getString(
2595 "error.call_setprogressbar_before_registering_handler"));
2597 progressBarHandlers.put(Long.valueOf(id), handler);
2598 final JPanel progressPanel = progressBars.get(Long.valueOf(id));
2599 if (handler.canCancel())
2601 JButton cancel = new JButton(
2602 MessageManager.getString("action.cancel"));
2603 final IProgressIndicator us = this;
2604 cancel.addActionListener(new ActionListener()
2608 public void actionPerformed(ActionEvent e)
2610 handler.cancelActivity(id);
2611 us.setProgressBar(MessageManager
2612 .formatMessage("label.cancelled_params", new Object[]
2613 { ((JLabel) progressPanel.getComponent(0)).getText() }),
2617 progressPanel.add(cancel, BorderLayout.EAST);
2623 * @return true if any progress bars are still active
2626 public boolean operationInProgress()
2628 if (progressBars != null && progressBars.size() > 0)
2636 * This will return the first AlignFrame holding the given viewport instance.
2637 * It will break if there are more than one AlignFrames viewing a particular
2641 * @return alignFrame for viewport
2643 public static AlignFrame getAlignFrameFor(AlignViewportI viewport)
2645 if (getDesktopPane() != null)
2647 AlignmentPanel[] aps = getAlignmentPanels(
2648 viewport.getSequenceSetId());
2649 for (int panel = 0; aps != null && panel < aps.length; panel++)
2651 if (aps[panel] != null && aps[panel].av == viewport)
2653 return aps[panel].alignFrame;
2660 public VamsasApplication getVamsasApplication()
2662 // TODO: JAL-3311 remove remaining code from Jalview relating to VAMSAS
2668 * flag set if jalview GUI is being operated programmatically
2670 private boolean inBatchMode = false;
2673 * check if jalview GUI is being operated programmatically
2675 * @return inBatchMode
2677 public boolean isInBatchMode()
2683 * set flag if jalview GUI is being operated programmatically
2685 * @param inBatchMode
2687 public void setInBatchMode(boolean inBatchMode)
2689 this.inBatchMode = inBatchMode;
2692 public void startServiceDiscovery()
2694 startServiceDiscovery(false);
2697 public void startServiceDiscovery(boolean blocking)
2699 boolean alive = true;
2700 Thread t0 = null, t1 = null, t2 = null;
2701 // JAL-940 - JALVIEW 1 services are now being EOLed as of JABA 2.1 release
2704 // todo: changesupport handlers need to be transferred
2705 if (discoverer == null)
2707 discoverer = jalview.ws.jws1.Discoverer.getInstance();
2708 // register PCS handler for desktop.
2709 discoverer.addPropertyChangeListener(changeSupport);
2711 // JAL-940 - disabled JWS1 service configuration - always start discoverer
2712 // until we phase out completely
2713 (t0 = new Thread(discoverer)).start();
2716 if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
2718 t2 = jalview.ws.jws2.Jws2Discoverer.getInstance()
2719 .startDiscoverer(changeSupport);
2723 // TODO: do rest service discovery
2732 } catch (Exception e)
2735 alive = (t1 != null && t1.isAlive()) || (t2 != null && t2.isAlive())
2736 || (t3 != null && t3.isAlive())
2737 || (t0 != null && t0.isAlive());
2743 * called to check if the service discovery process completed successfully.
2747 protected void JalviewServicesChanged(PropertyChangeEvent evt)
2749 if (evt.getNewValue() == null || evt.getNewValue() instanceof Vector)
2751 final String ermsg = jalview.ws.jws2.Jws2Discoverer.getInstance()
2752 .getErrorMessages();
2755 if (Cache.getDefault("SHOW_WSDISCOVERY_ERRORS", true))
2757 if (serviceChangedDialog == null)
2759 // only run if we aren't already displaying one of these.
2760 addDialogThread(serviceChangedDialog = new Runnable()
2767 * JalviewDialog jd =new JalviewDialog() {
2769 * @Override protected void cancelPressed() { // TODO
2770 * Auto-generated method stub
2772 * }@Override protected void okPressed() { // TODO
2773 * Auto-generated method stub
2775 * }@Override protected void raiseClosed() { // TODO
2776 * Auto-generated method stub
2778 * } }; jd.initDialogFrame(new
2779 * JLabel("<html><table width=\"450\"><tr><td>" + ermsg +
2780 * "<br/>It may be that you have invalid JABA URLs in your web service preferences,"
2781 * + " or mis-configured HTTP proxy settings.<br/>" +
2782 * "Check the <em>Connections</em> and <em>Web services</em> tab of the"
2784 * " Tools->Preferences dialog box to change them.</td></tr></table></html>"
2785 * ), true, true, "Web Service Configuration Problem", 450,
2788 * jd.waitForInput();
2790 JvOptionPane.showConfirmDialog(desktopPane,
2791 new JLabel("<html><table width=\"450\"><tr><td>"
2792 + ermsg + "</td></tr></table>"
2793 + "<p>It may be that you have invalid JABA URLs<br/>in your web service preferences,"
2794 + "<br>or as a command-line argument, or mis-configured HTTP proxy settings.</p>"
2795 + "<p>Check the <em>Connections</em> and <em>Web services</em> tab<br/>of the"
2796 + " Tools->Preferences dialog box to change them.</p></html>"),
2797 "Web Service Configuration Problem",
2798 JvOptionPane.DEFAULT_OPTION,
2799 JvOptionPane.ERROR_MESSAGE);
2800 serviceChangedDialog = null;
2809 "Errors reported by JABA discovery service. Check web services preferences.\n"
2816 private Runnable serviceChangedDialog = null;
2819 * start a thread to open a URL in the configured browser. Pops up a warning
2820 * dialog to the user if there is an exception when calling out to the browser
2825 public static void showUrl(final String url)
2827 showUrl(url, getInstance());
2831 * Like showUrl but allows progress handler to be specified
2835 * (null) or object implementing IProgressIndicator
2837 public static void showUrl(final String url,
2838 final IProgressIndicator progress)
2840 new Thread(new Runnable()
2847 if (progress != null)
2849 progress.setProgressBar(MessageManager
2850 .formatMessage("status.opening_params", new Object[]
2851 { url }), this.hashCode());
2853 jalview.util.BrowserLauncher.openURL(url);
2854 } catch (Exception ex)
2856 JvOptionPane.showInternalMessageDialog(getDesktopPane(),
2858 .getString("label.web_browser_not_found_unix"),
2859 MessageManager.getString("label.web_browser_not_found"),
2860 JvOptionPane.WARNING_MESSAGE);
2862 ex.printStackTrace();
2864 if (progress != null)
2866 progress.setProgressBar(null, this.hashCode());
2872 public static WsParamSetManager wsparamManager = null;
2874 public static ParamManager getUserParameterStore()
2876 if (wsparamManager == null)
2878 wsparamManager = new WsParamSetManager();
2880 return wsparamManager;
2884 * static hyperlink handler proxy method for use by Jalview's internal windows
2888 public static void hyperlinkUpdate(HyperlinkEvent e)
2890 if (e.getEventType() == EventType.ACTIVATED)
2895 url = e.getURL().toString();
2897 } catch (Exception x)
2901 if (Cache.log != null)
2903 Cache.log.error("Couldn't handle string " + url + " as a URL.");
2908 "Couldn't handle string " + url + " as a URL.");
2911 // ignore any exceptions due to dud links.
2918 * single thread that handles display of dialogs to user.
2920 ExecutorService dialogExecutor = Executors.newSingleThreadExecutor();
2923 * flag indicating if dialogExecutor should try to acquire a permit
2925 private volatile boolean dialogPause = true;
2930 private java.util.concurrent.Semaphore block = new Semaphore(0);
2932 private static groovy.ui.Console groovyConsole;
2935 * add another dialog thread to the queue
2939 public void addDialogThread(final Runnable prompter)
2941 dialogExecutor.submit(new Runnable()
2951 } catch (InterruptedException x)
2955 if (Jalview.isHeadlessMode())
2961 SwingUtilities.invokeAndWait(prompter);
2962 } catch (Exception q)
2964 Cache.log.warn("Unexpected Exception in dialog thread.", q);
2970 public void startDialogQueue()
2972 // set the flag so we don't pause waiting for another permit and semaphore
2973 // the current task to begin
2974 dialogPause = false;
2979 * Outputs an image of the desktop to file in EPS format, after prompting the
2980 * user for choice of Text or Lineart character rendering (unless a preference
2981 * has been set). The file name is generated as
2984 * Jalview_snapshot_nnnnn.eps where nnnnn is the current timestamp in milliseconds
2988 protected void snapShotWindow_actionPerformed(ActionEvent e)
2990 // currently the menu option to do this is not shown
2993 int width = getWidth();
2994 int height = getHeight();
2996 "Jalview_snapshot_" + System.currentTimeMillis() + ".eps");
2997 ImageWriterI writer = new ImageWriterI()
3000 public void exportImage(Graphics g) throws Exception
3003 Cache.log.info("Successfully written snapshot to file "
3004 + of.getAbsolutePath());
3007 String title = "View of desktop";
3008 ImageExporter exporter = new ImageExporter(writer, null, TYPE.EPS,
3010 exporter.doExport(of, this, width, height, title);
3014 * Explode the views in the given SplitFrame into separate SplitFrame windows.
3015 * This respects (remembers) any previous 'exploded geometry' i.e. the size
3016 * and location last time the view was expanded (if any). However it does not
3017 * remember the split pane divider location - this is set to match the
3018 * 'exploding' frame.
3022 public void explodeViews(SplitFrame sf)
3024 AlignFrame oldTopFrame = (AlignFrame) sf.getTopFrame();
3025 AlignFrame oldBottomFrame = (AlignFrame) sf.getBottomFrame();
3026 List<? extends AlignmentViewPanel> topPanels = oldTopFrame
3028 List<? extends AlignmentViewPanel> bottomPanels = oldBottomFrame
3030 int viewCount = topPanels.size();
3037 * Processing in reverse order works, forwards order leaves the first panels
3038 * not visible. I don't know why!
3040 for (int i = viewCount - 1; i >= 0; i--)
3043 * Make new top and bottom frames. These take over the respective
3044 * AlignmentPanel objects, including their AlignmentViewports, so the
3045 * cdna/protein relationships between the viewports is carried over to the
3048 * explodedGeometry holds the (x, y) position of the previously exploded
3049 * SplitFrame, and the (width, height) of the AlignFrame component
3051 AlignmentPanel topPanel = (AlignmentPanel) topPanels.get(i);
3052 AlignFrame newTopFrame = new AlignFrame(topPanel);
3053 newTopFrame.setSize(oldTopFrame.getSize());
3054 newTopFrame.setVisible(true);
3055 Rectangle geometry = ((AlignViewport) topPanel.getAlignViewport())
3056 .getExplodedGeometry();
3057 if (geometry != null)
3059 newTopFrame.setSize(geometry.getSize());
3062 AlignmentPanel bottomPanel = (AlignmentPanel) bottomPanels.get(i);
3063 AlignFrame newBottomFrame = new AlignFrame(bottomPanel);
3064 newBottomFrame.setSize(oldBottomFrame.getSize());
3065 newBottomFrame.setVisible(true);
3066 geometry = ((AlignViewport) bottomPanel.getAlignViewport())
3067 .getExplodedGeometry();
3068 if (geometry != null)
3070 newBottomFrame.setSize(geometry.getSize());
3073 topPanel.av.setGatherViewsHere(false);
3074 bottomPanel.av.setGatherViewsHere(false);
3075 JInternalFrame splitFrame = new SplitFrame(newTopFrame,
3077 if (geometry != null)
3079 splitFrame.setLocation(geometry.getLocation());
3081 addInternalFrame(splitFrame, sf.getTitle(), -1, -1);
3085 * Clear references to the panels (now relocated in the new SplitFrames)
3086 * before closing the old SplitFrame.
3089 bottomPanels.clear();
3094 * Gather expanded split frames, sharing the same pairs of sequence set ids,
3095 * back into the given SplitFrame as additional views. Note that the gathered
3096 * frames may themselves have multiple views.
3100 public void gatherViews(GSplitFrame source)
3103 * special handling of explodedGeometry for a view within a SplitFrame: - it
3104 * holds the (x, y) position of the enclosing SplitFrame, and the (width,
3105 * height) of the AlignFrame component
3107 AlignFrame myTopFrame = (AlignFrame) source.getTopFrame();
3108 AlignFrame myBottomFrame = (AlignFrame) source.getBottomFrame();
3109 myTopFrame.viewport.setExplodedGeometry(new Rectangle(source.getX(),
3110 source.getY(), myTopFrame.getWidth(), myTopFrame.getHeight()));
3111 myBottomFrame.viewport
3112 .setExplodedGeometry(new Rectangle(source.getX(), source.getY(),
3113 myBottomFrame.getWidth(), myBottomFrame.getHeight()));
3114 myTopFrame.viewport.setGatherViewsHere(true);
3115 myBottomFrame.viewport.setGatherViewsHere(true);
3116 String topViewId = myTopFrame.viewport.getSequenceSetId();
3117 String bottomViewId = myBottomFrame.viewport.getSequenceSetId();
3119 JInternalFrame[] frames = desktopPane.getAllFrames();
3120 for (JInternalFrame frame : frames)
3122 if (frame instanceof SplitFrame && frame != source)
3124 SplitFrame sf = (SplitFrame) frame;
3125 AlignFrame topFrame = (AlignFrame) sf.getTopFrame();
3126 AlignFrame bottomFrame = (AlignFrame) sf.getBottomFrame();
3127 boolean gatherThis = false;
3128 for (int a = 0; a < topFrame.alignPanels.size(); a++)
3130 AlignmentPanel topPanel = topFrame.alignPanels.get(a);
3131 AlignmentPanel bottomPanel = bottomFrame.alignPanels.get(a);
3132 if (topViewId.equals(topPanel.av.getSequenceSetId())
3133 && bottomViewId.equals(bottomPanel.av.getSequenceSetId()))
3136 topPanel.av.setGatherViewsHere(false);
3137 bottomPanel.av.setGatherViewsHere(false);
3138 topPanel.av.setExplodedGeometry(
3139 new Rectangle(sf.getLocation(), topFrame.getSize()));
3140 bottomPanel.av.setExplodedGeometry(
3141 new Rectangle(sf.getLocation(), bottomFrame.getSize()));
3142 myTopFrame.addAlignmentPanel(topPanel, false);
3143 myBottomFrame.addAlignmentPanel(bottomPanel, false);
3149 topFrame.getAlignPanels().clear();
3150 bottomFrame.getAlignPanels().clear();
3157 * The dust settles...give focus to the tab we did this from.
3159 myTopFrame.setDisplayedView(myTopFrame.alignPanel);
3162 public static groovy.ui.Console getGroovyConsole()
3164 return groovyConsole;
3168 * handles the payload of a drag and drop event.
3170 * TODO refactor to desktop utilities class
3173 * - Data source strings extracted from the drop event
3175 * - protocol for each data source extracted from the drop event
3179 * - the payload from the drop event
3182 @SuppressWarnings("unchecked")
3183 public static void transferFromDropTarget(List<Object> files,
3184 List<DataSourceType> protocols, DropTargetDropEvent evt,
3185 Transferable t) throws Exception
3188 // BH 2018 changed List<String> to List<Object> to allow for File from
3191 // DataFlavor[] flavors = t.getTransferDataFlavors();
3192 // for (int i = 0; i < flavors.length; i++) {
3193 // if (flavors[i].isFlavorJavaFileListType()) {
3194 // evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
3195 // List<File> list = (List<File>) t.getTransferData(flavors[i]);
3196 // for (int j = 0; j < list.size(); j++) {
3197 // File file = (File) list.get(j);
3198 // byte[] data = getDroppedFileBytes(file);
3199 // fileName.setText(file.getName() + " - " + data.length + " " +
3200 // evt.getLocation());
3201 // JTextArea target = (JTextArea) ((DropTarget)
3202 // evt.getSource()).getComponent();
3203 // target.setText(new String(data));
3205 // dtde.dropComplete(true);
3210 DataFlavor uriListFlavor = new DataFlavor(
3211 "text/uri-list;class=java.lang.String"), urlFlavour = null;
3214 urlFlavour = new DataFlavor(
3215 "application/x-java-url; class=java.net.URL");
3216 } catch (ClassNotFoundException cfe)
3218 Cache.log.debug("Couldn't instantiate the URL dataflavor.", cfe);
3221 if (urlFlavour != null && t.isDataFlavorSupported(urlFlavour))
3226 java.net.URL url = (URL) t.getTransferData(urlFlavour);
3227 // nb: java 8 osx bug https://bugs.openjdk.java.net/browse/JDK-8156099
3228 // means url may be null.
3231 protocols.add(DataSourceType.URL);
3232 files.add(url.toString());
3233 Cache.log.debug("Drop handled as URL dataflavor "
3234 + files.get(files.size() - 1));
3239 if (Platform.isAMacAndNotJS())
3242 "Please ignore plist error - occurs due to problem with java 8 on OSX");
3245 } catch (Throwable ex)
3247 Cache.log.debug("URL drop handler failed.", ex);
3250 if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
3252 // Works on Windows and MacOSX
3253 Cache.log.debug("Drop handled as javaFileListFlavor");
3254 for (File file : (List<File>) t
3255 .getTransferData(DataFlavor.javaFileListFlavor))
3258 protocols.add(DataSourceType.FILE);
3263 // Unix like behaviour
3264 boolean added = false;
3266 if (t.isDataFlavorSupported(uriListFlavor))
3268 Cache.log.debug("Drop handled as uriListFlavor");
3269 // This is used by Unix drag system
3270 data = (String) t.getTransferData(uriListFlavor);
3274 // fallback to text: workaround - on OSX where there's a JVM bug
3275 Cache.log.debug("standard URIListFlavor failed. Trying text");
3276 // try text fallback
3277 DataFlavor textDf = new DataFlavor(
3278 "text/plain;class=java.lang.String");
3279 if (t.isDataFlavorSupported(textDf))
3281 data = (String) t.getTransferData(textDf);
3284 Cache.log.debug("Plain text drop content returned "
3285 + (data == null ? "Null - failed" : data));
3290 while (protocols.size() < files.size())
3292 Cache.log.debug("Adding missing FILE protocol for "
3293 + files.get(protocols.size()));
3294 protocols.add(DataSourceType.FILE);
3296 for (java.util.StringTokenizer st = new java.util.StringTokenizer(
3297 data, "\r\n"); st.hasMoreTokens();)
3300 String s = st.nextToken();
3301 if (s.startsWith("#"))
3303 // the line is a comment (as per the RFC 2483)
3306 java.net.URI uri = new java.net.URI(s);
3307 if (uri.getScheme().toLowerCase().startsWith("http"))
3309 protocols.add(DataSourceType.URL);
3310 files.add(uri.toString());
3314 // otherwise preserve old behaviour: catch all for file objects
3315 java.io.File file = new java.io.File(uri);
3316 protocols.add(DataSourceType.FILE);
3317 files.add(file.toString());
3322 if (Cache.log.isDebugEnabled())
3324 if (data == null || !added)
3327 if (t.getTransferDataFlavors() != null
3328 && t.getTransferDataFlavors().length > 0)
3331 "Couldn't resolve drop data. Here are the supported flavors:");
3332 for (DataFlavor fl : t.getTransferDataFlavors())
3335 "Supported transfer dataflavor: " + fl.toString());
3336 Object df = t.getTransferData(fl);
3339 Cache.log.debug("Retrieves: " + df);
3343 Cache.log.debug("Retrieved nothing");
3349 Cache.log.debug("Couldn't resolve dataflavor for drop: "
3355 if (Platform.isWindowsAndNotJS())
3357 Cache.log.debug("Scanning dropped content for Windows Link Files");
3359 // resolve any .lnk files in the file drop
3360 for (int f = 0; f < files.size(); f++)
3362 String source = files.get(f).toString().toLowerCase();
3363 if (protocols.get(f).equals(DataSourceType.FILE)
3364 && (source.endsWith(".lnk") || source.endsWith(".url")
3365 || source.endsWith(".site")))
3369 Object obj = files.get(f);
3370 File lf = (obj instanceof File ? (File) obj
3371 : new File((String) obj));
3372 // process link file to get a URL
3373 Cache.log.debug("Found potential link file: " + lf);
3374 WindowsShortcut wscfile = new WindowsShortcut(lf);
3375 String fullname = wscfile.getRealFilename();
3376 protocols.set(f, FormatAdapter.checkProtocol(fullname));
3377 files.set(f, fullname);
3378 Cache.log.debug("Parsed real filename " + fullname
3379 + " to extract protocol: " + protocols.get(f));
3380 } catch (Exception ex)
3383 "Couldn't parse " + files.get(f) + " as a link file.",
3392 * Sets the Preferences property for experimental features to True or False
3393 * depending on the state of the controlling menu item
3396 protected void showExperimental_actionPerformed(boolean selected)
3398 Cache.setProperty(EXPERIMENTAL_FEATURES, Boolean.toString(selected));
3402 * Answers a (possibly empty) list of any structure viewer frames (currently
3403 * for either Jmol or Chimera) which are currently open. This may optionally
3404 * be restricted to viewers of a specified class, or viewers linked to a
3405 * specified alignment panel.
3408 * if not null, only return viewers linked to this panel
3409 * @param structureViewerClass
3410 * if not null, only return viewers of this class
3413 public List<StructureViewerBase> getStructureViewers(
3414 AlignmentPanel apanel,
3415 Class<? extends StructureViewerBase> structureViewerClass)
3417 List<StructureViewerBase> result = new ArrayList<>();
3418 JInternalFrame[] frames = getAllFrames();
3420 for (JInternalFrame frame : frames)
3422 if (frame instanceof StructureViewerBase)
3424 if (structureViewerClass == null
3425 || structureViewerClass.isInstance(frame))
3428 || ((StructureViewerBase) frame).isLinkedWith(apanel))
3430 result.add((StructureViewerBase) frame);