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())
912 frame.setMinimumSize(
913 new Dimension(DEFAULT_MIN_WIDTH, DEFAULT_MIN_HEIGHT));
915 // Set default dimension for Alignment Frame window.
916 // The Alignment Frame window could be added from a number of places,
918 // I did this here in order not to miss out on any Alignment frame.
919 if (frame instanceof AlignFrame)
921 frame.setMinimumSize(new Dimension(ALIGN_FRAME_DEFAULT_MIN_WIDTH,
922 ALIGN_FRAME_DEFAULT_MIN_HEIGHT));
926 frame.setVisible(makeVisible);
927 frame.setClosable(true);
928 frame.setResizable(resizable);
929 frame.setMaximizable(resizable);
930 frame.setIconifiable(resizable);
931 frame.setOpaque(Platform.isJS());
932 boolean isEmbedded = (Platform.getDimIfEmbedded(frame, -1, -1) != null);
933 if (!isEmbedded && frame.getX() < 1 && frame.getY() < 1)
935 frame.setLocation(xOffset * openFrameCount,
936 yOffset * ((openFrameCount - 1) % 10) + yOffset);
940 * add an entry for the new frame in the Window menu
941 * (and remove it when the frame is closed)
943 final JMenuItem menuItem = new JMenuItem(title);
944 frame.addInternalFrameListener(new InternalFrameAdapter()
947 public void internalFrameActivated(InternalFrameEvent evt)
949 JInternalFrame itf = getDesktopPane().getSelectedFrame();
952 if (itf instanceof AlignFrame)
954 Jalview.setCurrentAlignFrame((AlignFrame) itf);
961 public void internalFrameClosed(InternalFrameEvent evt)
963 PaintRefresher.RemoveComponent(frame);
966 * defensive check to prevent frames being
967 * added half off the window
969 if (openFrameCount > 0)
975 * ensure no reference to alignFrame retained by menu item listener
977 if (menuItem.getActionListeners().length > 0)
979 menuItem.removeActionListener(menuItem.getActionListeners()[0]);
981 getInstance().windowMenu.remove(menuItem);
985 menuItem.addActionListener(new ActionListener()
988 public void actionPerformed(ActionEvent e)
992 frame.setSelected(true);
993 frame.setIcon(false);
994 } catch (java.beans.PropertyVetoException ex)
996 // System.err.println(ex.toString());
1001 setKeyBindings(frame);
1003 getDesktopPane().add(frame);
1005 getInstance().windowMenu.add(menuItem);
1010 frame.setSelected(true);
1011 frame.requestFocus();
1012 } catch (java.beans.PropertyVetoException ve)
1014 } catch (java.lang.ClassCastException cex)
1017 "Squashed a possible GUI implementation error. If you can recreate this, please look at http://issues.jalview.org/browse/JAL-869",
1023 * Add key bindings to a JInternalFrame so that Ctrl-W and Cmd-W will close
1028 private static void setKeyBindings(JInternalFrame frame)
1030 final Action closeAction = new AbstractAction()
1033 public void actionPerformed(ActionEvent e)
1040 * set up key bindings for Ctrl-W and Cmd-W, with the same (Close) action
1042 KeyStroke ctrlWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
1043 InputEvent.CTRL_DOWN_MASK);
1044 KeyStroke cmdWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
1045 ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx());
1047 InputMap inputMap = frame
1048 .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
1049 String ctrlW = ctrlWKey.toString();
1050 inputMap.put(ctrlWKey, ctrlW);
1051 inputMap.put(cmdWKey, ctrlW);
1053 ActionMap actionMap = frame.getActionMap();
1054 actionMap.put(ctrlW, closeAction);
1058 public void lostOwnership(Clipboard clipboard, Transferable contents)
1062 jalviewClipboard = null;
1065 internalCopy = false;
1069 public void dragEnter(DropTargetDragEvent evt)
1074 public void dragExit(DropTargetEvent evt)
1079 public void dragOver(DropTargetDragEvent evt)
1084 public void dropActionChanged(DropTargetDragEvent evt)
1095 public void drop(DropTargetDropEvent evt)
1097 boolean success = true;
1098 // JAL-1552 - acceptDrop required before getTransferable call for
1099 // Java's Transferable for native dnd
1100 evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
1101 Transferable t = evt.getTransferable();
1102 List<Object> files = new ArrayList<>();
1103 List<DataSourceType> protocols = new ArrayList<>();
1107 transferFromDropTarget(files, protocols, evt, t);
1108 } catch (Exception e)
1110 e.printStackTrace();
1118 for (int i = 0; i < files.size(); i++)
1120 // BH 2018 File or String
1121 Object file = files.get(i);
1122 String fileName = file.toString();
1123 DataSourceType protocol = (protocols == null)
1124 ? DataSourceType.FILE
1126 FileFormatI format = null;
1128 if (fileName.endsWith(".jar"))
1130 format = FileFormat.Jalview;
1135 format = new IdentifyFile().identify(file, protocol);
1137 if (file instanceof File)
1139 Platform.cacheFileData((File) file);
1141 new FileLoader().LoadFile(null, file, protocol, format);
1144 } catch (Exception ex)
1149 evt.dropComplete(success); // need this to ensure input focus is properly
1150 // transfered to any new windows created
1160 public void inputLocalFileMenuItem_actionPerformed(AlignViewport viewport)
1162 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
1163 JalviewFileChooser chooser = JalviewFileChooser.forRead(
1164 Cache.getProperty("LAST_DIRECTORY"), fileFormat,
1165 BackupFiles.getEnabled());
1167 chooser.setFileView(new JalviewFileView());
1168 chooser.setDialogTitle(
1169 MessageManager.getString("label.open_local_file"));
1170 chooser.setToolTipText(MessageManager.getString("action.open"));
1172 chooser.setResponseHandler(0, new Runnable()
1177 File selectedFile = chooser.getSelectedFile();
1178 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1180 FileFormatI format = chooser.getSelectedFormat();
1183 * Call IdentifyFile to verify the file contains what its extension implies.
1184 * Skip this step for dynamically added file formats, because
1185 * IdentifyFile does not know how to recognise them.
1187 if (FileFormats.getInstance().isIdentifiable(format))
1191 format = new IdentifyFile().identify(selectedFile,
1192 DataSourceType.FILE);
1193 } catch (FileFormatException e)
1195 // format = null; //??
1199 new FileLoader().LoadFile(viewport, selectedFile,
1200 DataSourceType.FILE, format);
1203 chooser.showOpenDialog(this);
1207 * Shows a dialog for input of a URL at which to retrieve alignment data
1212 public void inputURLMenuItem_actionPerformed(AlignViewport viewport)
1214 // This construct allows us to have a wider textfield
1216 JLabel label = new JLabel(
1217 MessageManager.getString("label.input_file_url"));
1219 JPanel panel = new JPanel(new GridLayout(2, 1));
1223 * the URL to fetch is
1224 * Java: an editable combobox with history
1225 * JS: (pending JAL-3038) a plain text field
1228 String urlBase = "http://www.";
1229 if (Platform.isJS())
1231 history = new JTextField(urlBase, 35);
1240 JComboBox<String> asCombo = new JComboBox<>();
1241 asCombo.setPreferredSize(new Dimension(400, 20));
1242 asCombo.setEditable(true);
1243 asCombo.addItem(urlBase);
1244 String historyItems = Cache.getProperty("RECENT_URL");
1245 if (historyItems != null)
1247 for (String token : historyItems.split("\\t"))
1249 asCombo.addItem(token);
1256 Object[] options = new Object[] { MessageManager.getString("action.ok"),
1257 MessageManager.getString("action.cancel") };
1258 Runnable action = new Runnable()
1263 @SuppressWarnings("unchecked")
1264 String url = (history instanceof JTextField
1265 ? ((JTextField) history).getText()
1266 : ((JComboBox<String>) history).getSelectedItem()
1269 if (url.toLowerCase().endsWith(".jar"))
1271 if (viewport != null)
1273 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1274 FileFormat.Jalview);
1278 new FileLoader().LoadFile(url, DataSourceType.URL,
1279 FileFormat.Jalview);
1284 FileFormatI format = null;
1287 format = new IdentifyFile().identify(url, DataSourceType.URL);
1288 } catch (FileFormatException e)
1290 // TODO revise error handling, distinguish between
1291 // URL not found and response not valid
1296 String msg = MessageManager
1297 .formatMessage("label.couldnt_locate", url);
1298 JvOptionPane.showInternalMessageDialog(getDesktopPane(), msg,
1299 MessageManager.getString("label.url_not_found"),
1300 JvOptionPane.WARNING_MESSAGE);
1305 if (viewport != null)
1307 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1312 new FileLoader().LoadFile(url, DataSourceType.URL, format);
1317 String dialogOption = MessageManager
1318 .getString("label.input_alignment_from_url");
1319 JvOptionPane.newOptionDialog(desktopPane).setResponseHandler(0, action)
1320 .showInternalDialog(panel, dialogOption,
1321 JvOptionPane.YES_NO_CANCEL_OPTION,
1322 JvOptionPane.PLAIN_MESSAGE, null, options,
1323 MessageManager.getString("action.ok"));
1327 * Opens the CutAndPaste window for the user to paste an alignment in to
1330 * - if not null, the pasted alignment is added to the current
1331 * alignment; if null, to a new alignment window
1334 public void inputTextboxMenuItem_actionPerformed(
1335 AlignmentViewPanel viewPanel)
1337 CutAndPasteTransfer cap = new CutAndPasteTransfer();
1338 cap.setForInput(viewPanel);
1339 addInternalFrame(cap,
1340 MessageManager.getString("label.cut_paste_alignmen_file"),
1341 FRAME_MAKE_VISIBLE, 600, 500, FRAME_ALLOW_RESIZE,
1342 FRAME_SET_MIN_SIZE_300);
1351 Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
1352 Cache.setProperty("SCREENGEOMETRY_WIDTH", screen.width + "");
1353 Cache.setProperty("SCREENGEOMETRY_HEIGHT", screen.height + "");
1354 storeLastKnownDimensions("", new Rectangle(getBounds().x, getBounds().y,
1355 getWidth(), getHeight()));
1357 if (jconsole != null)
1359 storeLastKnownDimensions("JAVA_CONSOLE_", jconsole.getBounds());
1360 jconsole.stopConsole();
1364 storeLastKnownDimensions("JALVIEW_RSS_WINDOW_", jvnews.getBounds());
1367 if (dialogExecutor != null)
1369 dialogExecutor.shutdownNow();
1371 closeAll_actionPerformed(null);
1373 if (groovyConsole != null)
1375 // suppress a possible repeat prompt to save script
1376 groovyConsole.setDirty(false);
1377 groovyConsole.exit();
1382 private void storeLastKnownDimensions(String string, Rectangle jc)
1384 Cache.log.debug("Storing last known dimensions for " + string + ": x:"
1385 + jc.x + " y:" + jc.y + " width:" + jc.width + " height:"
1388 Cache.setProperty(string + "SCREEN_X", jc.x + "");
1389 Cache.setProperty(string + "SCREEN_Y", jc.y + "");
1390 Cache.setProperty(string + "SCREEN_WIDTH", jc.width + "");
1391 Cache.setProperty(string + "SCREEN_HEIGHT", jc.height + "");
1401 public void aboutMenuItem_actionPerformed(ActionEvent e)
1403 new Thread(new Runnable()
1408 new SplashScreen(false);
1414 * Returns the html text for the About screen, including any available version
1415 * number, build details, author details and citation reference, but without
1416 * the enclosing {@code html} tags
1420 public String getAboutMessage()
1422 StringBuilder message = new StringBuilder(1024);
1423 message.append("<h1><strong>Version: ")
1424 .append(Cache.getProperty("VERSION")).append("</strong></h1>")
1425 .append("<strong>Built: <em>")
1426 .append(Cache.getDefault("BUILD_DATE", "unknown"))
1427 .append("</em> from ").append(Cache.getBuildDetailsForSplash())
1428 .append("</strong>");
1430 String latestVersion = Cache.getDefault("LATEST_VERSION", "Checking");
1431 if (latestVersion.equals("Checking"))
1433 // JBP removed this message for 2.11: May be reinstated in future version
1434 // message.append("<br>...Checking latest version...</br>");
1436 else if (!latestVersion.equals(Cache.getProperty("VERSION")))
1438 boolean red = false;
1439 if (Cache.getProperty("VERSION").toLowerCase()
1440 .indexOf("automated build") == -1)
1443 // Displayed when code version and jnlp version do not match and code
1444 // version is not a development build
1445 message.append("<div style=\"color: #FF0000;font-style: bold;\">");
1448 message.append("<br>!! Version ")
1449 .append(Cache.getDefault("LATEST_VERSION", "..Checking.."))
1450 .append(" is available for download from ")
1451 .append(Cache.getDefault("www.jalview.org",
1452 "http://www.jalview.org"))
1456 message.append("</div>");
1459 message.append("<br>Authors: ");
1460 message.append(Cache.getDefault("AUTHORFNAMES", DEFAULT_AUTHORS));
1461 message.append(CITATION);
1463 return message.toString();
1467 * Action on requesting Help documentation
1470 public void documentationMenuItem_actionPerformed()
1474 if (Platform.isJS())
1476 BrowserLauncher.openURL("http://www.jalview.org/help.html");
1485 Help.showHelpWindow();
1487 } catch (Exception ex)
1489 System.err.println("Error opening help: " + ex.getMessage());
1494 public void closeAll_actionPerformed(ActionEvent e)
1496 // TODO show a progress bar while closing?
1497 JInternalFrame[] frames = desktopPane.getAllFrames();
1498 for (int i = 0; i < frames.length; i++)
1502 frames[i].setClosed(true);
1503 } catch (java.beans.PropertyVetoException ex)
1507 Jalview.setCurrentAlignFrame(null);
1508 System.out.println("ALL CLOSED");
1511 * reset state of singleton objects as appropriate (clear down session state
1512 * when all windows are closed)
1514 StructureSelectionManager ssm = StructureSelectionManager
1515 .getStructureSelectionManager(this);
1523 public void raiseRelated_actionPerformed(ActionEvent e)
1525 reorderAssociatedWindows(false, false);
1529 public void minimizeAssociated_actionPerformed(ActionEvent e)
1531 reorderAssociatedWindows(true, false);
1534 void closeAssociatedWindows()
1536 reorderAssociatedWindows(false, true);
1542 * @seejalview.jbgui.GDesktop#garbageCollect_actionPerformed(java.awt.event.
1546 protected void garbageCollect_actionPerformed(ActionEvent e)
1548 // We simply collect the garbage
1549 Cache.log.debug("Collecting garbage...");
1551 Cache.log.debug("Finished garbage collection.");
1558 * jalview.jbgui.GDesktop#showMemusage_actionPerformed(java.awt.event.ActionEvent
1562 protected void showMemusage_actionPerformed(ActionEvent e)
1564 desktopPane.showMemoryUsage(showMemusage.isSelected());
1571 * jalview.jbgui.GDesktop#showConsole_actionPerformed(java.awt.event.ActionEvent
1575 protected void showConsole_actionPerformed(ActionEvent e)
1577 showConsole(showConsole.isSelected());
1580 Console jconsole = null;
1583 * control whether the java console is visible or not
1587 void showConsole(boolean selected)
1589 // TODO: decide if we should update properties file
1590 if (jconsole != null) // BH 2018
1592 showConsole.setSelected(selected);
1593 Cache.setProperty("SHOW_JAVA_CONSOLE",
1594 Boolean.valueOf(selected).toString());
1595 jconsole.setVisible(selected);
1599 void reorderAssociatedWindows(boolean minimize, boolean close)
1601 JInternalFrame[] frames = desktopPane.getAllFrames();
1602 if (frames == null || frames.length < 1)
1607 AlignmentViewport source = null, target = null;
1608 if (frames[0] instanceof AlignFrame)
1610 source = ((AlignFrame) frames[0]).getCurrentView();
1612 else if (frames[0] instanceof TreePanel)
1614 source = ((TreePanel) frames[0]).getViewPort();
1616 else if (frames[0] instanceof PCAPanel)
1618 source = ((PCAPanel) frames[0]).av;
1620 else if (frames[0].getContentPane() instanceof PairwiseAlignPanel)
1622 source = ((PairwiseAlignPanel) frames[0].getContentPane()).av;
1627 for (int i = 0; i < frames.length; i++)
1630 if (frames[i] == null)
1634 if (frames[i] instanceof AlignFrame)
1636 target = ((AlignFrame) frames[i]).getCurrentView();
1638 else if (frames[i] instanceof TreePanel)
1640 target = ((TreePanel) frames[i]).getViewPort();
1642 else if (frames[i] instanceof PCAPanel)
1644 target = ((PCAPanel) frames[i]).av;
1646 else if (frames[i].getContentPane() instanceof PairwiseAlignPanel)
1648 target = ((PairwiseAlignPanel) frames[i].getContentPane()).av;
1651 if (source == target)
1657 frames[i].setClosed(true);
1661 frames[i].setIcon(minimize);
1664 frames[i].toFront();
1668 } catch (java.beans.PropertyVetoException ex)
1683 protected void preferences_actionPerformed(ActionEvent e)
1689 * Prompts the user to choose a file and then saves the Jalview state as a
1690 * Jalview project file
1693 public void saveState_actionPerformed()
1695 saveState_actionPerformed(false);
1698 public void saveState_actionPerformed(boolean saveAs)
1700 java.io.File projectFile = getProjectFile();
1701 // autoSave indicates we already have a file and don't need to ask
1702 boolean autoSave = projectFile != null && !saveAs
1703 && BackupFiles.getEnabled();
1705 // System.out.println("autoSave="+autoSave+", projectFile='"+projectFile+"',
1706 // saveAs="+saveAs+", Backups
1707 // "+(BackupFiles.getEnabled()?"enabled":"disabled"));
1709 boolean approveSave = false;
1712 JalviewFileChooser chooser = new JalviewFileChooser("jvp",
1715 chooser.setFileView(new JalviewFileView());
1716 chooser.setDialogTitle(MessageManager.getString("label.save_state"));
1718 int value = chooser.showSaveDialog(this);
1720 if (value == JalviewFileChooser.APPROVE_OPTION)
1722 projectFile = chooser.getSelectedFile();
1723 setProjectFile(projectFile);
1728 if (approveSave || autoSave)
1730 final Desktop me = this;
1731 final java.io.File chosenFile = projectFile;
1732 new Thread(new Runnable()
1737 // TODO: refactor to Jalview desktop session controller action.
1738 setProgressBar(MessageManager.formatMessage(
1739 "label.saving_jalview_project", new Object[]
1740 { chosenFile.getName() }), chosenFile.hashCode());
1741 Cache.setProperty("LAST_DIRECTORY", chosenFile.getParent());
1742 // TODO catch and handle errors for savestate
1743 // TODO prevent user from messing with the Desktop whilst we're saving
1746 boolean doBackup = BackupFiles.getEnabled();
1747 BackupFiles backupfiles = doBackup ? new BackupFiles(chosenFile)
1750 new Jalview2XML().saveState(
1751 doBackup ? backupfiles.getTempFile() : chosenFile);
1755 backupfiles.setWriteSuccess(true);
1756 backupfiles.rollBackupsAndRenameTempFile();
1758 } catch (OutOfMemoryError oom)
1760 new OOMWarning("Whilst saving current state to "
1761 + chosenFile.getName(), oom);
1762 } catch (Exception ex)
1764 Cache.log.error("Problems whilst trying to save to "
1765 + chosenFile.getName(), ex);
1766 JvOptionPane.showMessageDialog(me,
1767 MessageManager.formatMessage(
1768 "label.error_whilst_saving_current_state_to",
1770 { chosenFile.getName() }),
1771 MessageManager.getString("label.couldnt_save_project"),
1772 JvOptionPane.WARNING_MESSAGE);
1774 setProgressBar(null, chosenFile.hashCode());
1781 public void saveAsState_actionPerformed(ActionEvent e)
1783 saveState_actionPerformed(true);
1786 private void setProjectFile(File choice)
1788 this.projectFile = choice;
1791 public File getProjectFile()
1793 return this.projectFile;
1797 * Shows a file chooser dialog and tries to read in the selected file as a
1801 public void loadState_actionPerformed()
1803 final String[] suffix = new String[] { "jvp", "jar" };
1804 final String[] desc = new String[] { "Jalview Project",
1805 "Jalview Project (old)" };
1806 JalviewFileChooser chooser = new JalviewFileChooser(
1807 Cache.getProperty("LAST_DIRECTORY"), suffix, desc,
1808 "Jalview Project", true, BackupFiles.getEnabled()); // last two
1812 chooser.setFileView(new JalviewFileView());
1813 chooser.setDialogTitle(MessageManager.getString("label.restore_state"));
1814 chooser.setResponseHandler(0, new Runnable()
1819 File selectedFile = chooser.getSelectedFile();
1820 setProjectFile(selectedFile);
1821 String choice = selectedFile.getAbsolutePath();
1822 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1823 new Thread(new Runnable()
1830 new Jalview2XML().loadJalviewAlign(selectedFile);
1831 } catch (OutOfMemoryError oom)
1833 new OOMWarning("Whilst loading project from " + choice, oom);
1834 } catch (Exception ex)
1837 "Problems whilst loading project from " + choice, ex);
1838 JvOptionPane.showMessageDialog(getDesktopPane(),
1839 MessageManager.formatMessage(
1840 "label.error_whilst_loading_project_from",
1844 .getString("label.couldnt_load_project"),
1845 JvOptionPane.WARNING_MESSAGE);
1852 chooser.showOpenDialog(this);
1856 public void inputSequence_actionPerformed(ActionEvent e)
1858 new SequenceFetcher(this);
1861 JPanel progressPanel;
1863 ArrayList<JPanel> fileLoadingPanels = new ArrayList<>();
1865 public void startLoading(final Object fileName)
1867 if (fileLoadingCount == 0)
1869 fileLoadingPanels.add(addProgressPanel(MessageManager
1870 .formatMessage("label.loading_file", new Object[]
1876 private JPanel addProgressPanel(String string)
1878 if (progressPanel == null)
1880 progressPanel = new JPanel(new GridLayout(1, 1));
1881 totalProgressCount = 0;
1882 getContentPane().add(progressPanel, BorderLayout.SOUTH);
1884 JPanel thisprogress = new JPanel(new BorderLayout(10, 5));
1885 JProgressBar progressBar = new JProgressBar();
1886 progressBar.setIndeterminate(true);
1888 thisprogress.add(new JLabel(string), BorderLayout.WEST);
1890 thisprogress.add(progressBar, BorderLayout.CENTER);
1891 progressPanel.add(thisprogress);
1892 ((GridLayout) progressPanel.getLayout()).setRows(
1893 ((GridLayout) progressPanel.getLayout()).getRows() + 1);
1894 ++totalProgressCount;
1896 return thisprogress;
1899 int totalProgressCount = 0;
1901 private void removeProgressPanel(JPanel progbar)
1903 if (progressPanel != null)
1905 synchronized (progressPanel)
1907 progressPanel.remove(progbar);
1908 GridLayout gl = (GridLayout) progressPanel.getLayout();
1909 gl.setRows(gl.getRows() - 1);
1910 if (--totalProgressCount < 1)
1912 this.getContentPane().remove(progressPanel);
1913 progressPanel = null;
1920 public void stopLoading()
1923 if (fileLoadingCount < 1)
1925 while (fileLoadingPanels.size() > 0)
1927 removeProgressPanel(fileLoadingPanels.remove(0));
1929 fileLoadingPanels.clear();
1930 fileLoadingCount = 0;
1935 public static int getViewCount(String alignmentId)
1937 AlignmentViewport[] aps = getViewports(alignmentId);
1938 return (aps == null) ? 0 : aps.length;
1943 * @param alignmentId
1944 * - if null, all sets are returned
1945 * @return all AlignmentPanels concerning the alignmentId sequence set
1947 public static AlignmentPanel[] getAlignmentPanels(String alignmentId)
1949 if (getDesktopPane() == null)
1951 // no frames created and in headless mode
1952 // TODO: verify that frames are recoverable when in headless mode
1955 List<AlignmentPanel> aps = new ArrayList<>();
1956 AlignFrame[] frames = getAlignFrames();
1961 for (AlignFrame af : frames)
1963 for (AlignmentPanel ap : af.alignPanels)
1965 if (alignmentId == null
1966 || alignmentId.equals(ap.av.getSequenceSetId()))
1972 if (aps.size() == 0)
1976 AlignmentPanel[] vap = aps.toArray(new AlignmentPanel[aps.size()]);
1981 * get all the viewports on an alignment.
1983 * @param sequenceSetId
1984 * unique alignment id (may be null - all viewports returned in that
1986 * @return all viewports on the alignment bound to sequenceSetId
1988 public static AlignmentViewport[] getViewports(String sequenceSetId)
1990 List<AlignmentViewport> viewp = new ArrayList<>();
1991 if (getDesktopPane() != null)
1993 AlignFrame[] frames = getAlignFrames();
1995 for (AlignFrame afr : frames)
1997 if (sequenceSetId == null || afr.getViewport().getSequenceSetId()
1998 .equals(sequenceSetId))
2000 if (afr.alignPanels != null)
2002 for (AlignmentPanel ap : afr.alignPanels)
2004 if (sequenceSetId == null
2005 || sequenceSetId.equals(ap.av.getSequenceSetId()))
2013 viewp.add(afr.getViewport());
2017 if (viewp.size() > 0)
2019 return viewp.toArray(new AlignmentViewport[viewp.size()]);
2026 * Explode the views in the given frame into separate AlignFrame
2030 public static void explodeViews(AlignFrame af)
2032 int size = af.alignPanels.size();
2038 // FIXME: ideally should use UI interface API
2039 FeatureSettings viewFeatureSettings = (af.featureSettings != null
2040 && af.featureSettings.isOpen()) ? af.featureSettings : null;
2041 Rectangle fsBounds = af.getFeatureSettingsGeometry();
2042 for (int i = 0; i < size; i++)
2044 AlignmentPanel ap = af.alignPanels.get(i);
2046 AlignFrame newaf = new AlignFrame(ap);
2048 // transfer reference for existing feature settings to new alignFrame
2049 if (ap == af.alignPanel)
2051 if (viewFeatureSettings != null && viewFeatureSettings.fr.ap == ap)
2053 newaf.featureSettings = viewFeatureSettings;
2055 newaf.setFeatureSettingsGeometry(fsBounds);
2059 * Restore the view's last exploded frame geometry if known. Multiple
2060 * views from one exploded frame share and restore the same (frame)
2061 * position and size.
2063 Rectangle geometry = ap.av.getExplodedGeometry();
2064 if (geometry != null)
2066 newaf.setBounds(geometry);
2069 ap.av.setGatherViewsHere(false);
2071 addInternalFrame(newaf, af.getTitle(), AlignFrame.DEFAULT_WIDTH,
2072 AlignFrame.DEFAULT_HEIGHT);
2073 // and materialise a new feature settings dialog instance for the new
2075 // (closes the old as if 'OK' was pressed)
2076 if (ap == af.alignPanel && newaf.featureSettings != null
2077 && newaf.featureSettings.isOpen()
2078 && af.alignPanel.getAlignViewport().isShowSequenceFeatures())
2080 newaf.showFeatureSettingsUI();
2084 af.featureSettings = null;
2085 af.alignPanels.clear();
2086 af.closeMenuItem_actionPerformed(true);
2091 * Gather expanded views (separate AlignFrame's) with the same sequence set
2092 * identifier back in to this frame as additional views, and close the
2093 * expanded views. Note the expanded frames may themselves have multiple
2094 * views. We take the lot.
2098 public void gatherViews(AlignFrame source)
2100 source.viewport.setGatherViewsHere(true);
2101 source.viewport.setExplodedGeometry(source.getBounds());
2102 JInternalFrame[] frames = desktopPane.getAllFrames();
2103 String viewId = source.viewport.getSequenceSetId();
2104 for (int t = 0; t < frames.length; t++)
2106 if (frames[t] instanceof AlignFrame && frames[t] != source)
2108 AlignFrame af = (AlignFrame) frames[t];
2109 boolean gatherThis = false;
2110 for (int a = 0; a < af.alignPanels.size(); a++)
2112 AlignmentPanel ap = af.alignPanels.get(a);
2113 if (viewId.equals(ap.av.getSequenceSetId()))
2116 ap.av.setGatherViewsHere(false);
2117 ap.av.setExplodedGeometry(af.getBounds());
2118 source.addAlignmentPanel(ap, false);
2124 if (af.featureSettings != null && af.featureSettings.isOpen())
2126 if (source.featureSettings == null)
2128 // preserve the feature settings geometry for this frame
2129 source.featureSettings = af.featureSettings;
2130 source.setFeatureSettingsGeometry(
2131 af.getFeatureSettingsGeometry());
2135 // close it and forget
2136 af.featureSettings.close();
2139 af.alignPanels.clear();
2140 af.closeMenuItem_actionPerformed(true);
2145 // refresh the feature setting UI for the source frame if it exists
2146 if (source.featureSettings != null && source.featureSettings.isOpen())
2148 source.showFeatureSettingsUI();
2152 public JInternalFrame[] getAllFrames()
2154 return desktopPane.getAllFrames();
2158 * Checks the given url to see if it gives a response indicating that the user
2159 * should be informed of a new questionnaire.
2163 public void checkForQuestionnaire(String url)
2165 UserQuestionnaireCheck jvq = new UserQuestionnaireCheck(url);
2166 // javax.swing.SwingUtilities.invokeLater(jvq);
2167 new Thread(jvq).start();
2170 public void checkURLLinks()
2172 // Thread off the URL link checker
2173 addDialogThread(new Runnable()
2178 if (Cache.getDefault("CHECKURLLINKS", true))
2180 // check what the actual links are - if it's just the default don't
2181 // bother with the warning
2182 List<String> links = Preferences.sequenceUrlLinks
2185 // only need to check links if there is one with a
2186 // SEQUENCE_ID which is not the default EMBL_EBI link
2187 ListIterator<String> li = links.listIterator();
2188 boolean check = false;
2189 List<JLabel> urls = new ArrayList<>();
2190 while (li.hasNext())
2192 String link = li.next();
2193 if (link.contains(jalview.util.UrlConstants.SEQUENCE_ID)
2194 && !UrlConstants.isDefaultString(link))
2197 int barPos = link.indexOf("|");
2198 String urlMsg = barPos == -1 ? link
2199 : link.substring(0, barPos) + ": "
2200 + link.substring(barPos + 1);
2201 urls.add(new JLabel(urlMsg));
2209 // ask user to check in case URL links use old style tokens
2210 // ($SEQUENCE_ID$ for sequence id _or_ accession id)
2211 JPanel msgPanel = new JPanel();
2212 msgPanel.setLayout(new BoxLayout(msgPanel, BoxLayout.PAGE_AXIS));
2213 msgPanel.add(Box.createVerticalGlue());
2214 JLabel msg = new JLabel(MessageManager
2215 .getString("label.SEQUENCE_ID_for_DB_ACCESSION1"));
2216 JLabel msg2 = new JLabel(MessageManager
2217 .getString("label.SEQUENCE_ID_for_DB_ACCESSION2"));
2219 for (JLabel url : urls)
2225 final JCheckBox jcb = new JCheckBox(
2226 MessageManager.getString("label.do_not_display_again"));
2227 jcb.addActionListener(new ActionListener()
2230 public void actionPerformed(ActionEvent e)
2232 // update Cache settings for "don't show this again"
2233 boolean showWarningAgain = !jcb.isSelected();
2234 Cache.setProperty("CHECKURLLINKS",
2235 Boolean.valueOf(showWarningAgain).toString());
2240 JvOptionPane.showMessageDialog(desktopPane, msgPanel,
2242 .getString("label.SEQUENCE_ID_no_longer_used"),
2243 JvOptionPane.WARNING_MESSAGE);
2250 * Proxy class for JDesktopPane which optionally displays the current memory
2251 * usage and highlights the desktop area with a red bar if free memory runs
2256 public class MyDesktopPane extends JDesktopPane implements Runnable
2258 private static final float ONE_MB = 1048576f;
2260 boolean showMemoryUsage = false;
2264 java.text.NumberFormat df;
2266 float maxMemory, allocatedMemory, freeMemory, totalFreeMemory,
2269 public MyDesktopPane(boolean showMemoryUsage)
2271 showMemoryUsage(showMemoryUsage);
2274 public void showMemoryUsage(boolean showMemory)
2276 this.showMemoryUsage = showMemory;
2279 Thread worker = new Thread(this);
2285 public boolean isShowMemoryUsage()
2287 return showMemoryUsage;
2293 df = java.text.NumberFormat.getNumberInstance();
2294 df.setMaximumFractionDigits(2);
2295 runtime = Runtime.getRuntime();
2297 while (showMemoryUsage)
2301 maxMemory = runtime.maxMemory() / ONE_MB;
2302 allocatedMemory = runtime.totalMemory() / ONE_MB;
2303 freeMemory = runtime.freeMemory() / ONE_MB;
2304 totalFreeMemory = freeMemory + (maxMemory - allocatedMemory);
2306 percentUsage = (totalFreeMemory / maxMemory) * 100;
2308 // if (percentUsage < 20)
2310 // border1 = BorderFactory.createMatteBorder(12, 12, 12, 12,
2312 // instance.set.setBorder(border1);
2315 // sleep after showing usage
2317 } catch (Exception ex)
2319 ex.printStackTrace();
2325 public void paintComponent(Graphics g)
2327 if (showMemoryUsage && g != null && df != null)
2329 if (percentUsage < 20)
2331 g.setColor(Color.red);
2333 FontMetrics fm = g.getFontMetrics();
2336 g.drawString(MessageManager.formatMessage("label.memory_stats",
2338 { df.format(totalFreeMemory), df.format(maxMemory),
2339 df.format(percentUsage) }),
2340 10, getHeight() - fm.getHeight());
2347 * Accessor method to quickly get all the AlignmentFrames loaded.
2349 * @return an array of AlignFrame, or null if none found
2351 public static AlignFrame[] getAlignFrames()
2353 if (Jalview.isHeadlessMode())
2355 return new AlignFrame[] { Jalview.getInstance().currentAlignFrame };
2358 JInternalFrame[] frames = getDesktopPane().getAllFrames();
2364 List<AlignFrame> avp = new ArrayList<>();
2366 for (int i = frames.length - 1; i > -1; i--)
2368 if (frames[i] instanceof AlignFrame)
2370 avp.add((AlignFrame) frames[i]);
2372 else if (frames[i] instanceof SplitFrame)
2375 * Also check for a split frame containing an AlignFrame
2377 GSplitFrame sf = (GSplitFrame) frames[i];
2378 if (sf.getTopFrame() instanceof AlignFrame)
2380 avp.add((AlignFrame) sf.getTopFrame());
2382 if (sf.getBottomFrame() instanceof AlignFrame)
2384 avp.add((AlignFrame) sf.getBottomFrame());
2388 if (avp.size() == 0)
2392 AlignFrame afs[] = avp.toArray(new AlignFrame[avp.size()]);
2397 * Returns an array of any AppJmol frames in the Desktop (or null if none).
2401 public GStructureViewer[] getJmols()
2403 JInternalFrame[] frames = desktopPane.getAllFrames();
2409 List<GStructureViewer> avp = new ArrayList<>();
2411 for (int i = frames.length - 1; i > -1; i--)
2413 if (frames[i] instanceof AppJmol)
2415 GStructureViewer af = (GStructureViewer) frames[i];
2419 if (avp.size() == 0)
2423 GStructureViewer afs[] = avp.toArray(new GStructureViewer[avp.size()]);
2428 * Add Groovy Support to Jalview
2431 public void groovyShell_actionPerformed()
2435 openGroovyConsole();
2436 } catch (Exception ex)
2438 Cache.log.error("Groovy Shell Creation failed.", ex);
2439 JvOptionPane.showInternalMessageDialog(desktopPane,
2441 MessageManager.getString("label.couldnt_create_groovy_shell"),
2442 MessageManager.getString("label.groovy_support_failed"),
2443 JvOptionPane.ERROR_MESSAGE);
2448 * Open the Groovy console
2450 void openGroovyConsole()
2452 if (groovyConsole == null)
2454 groovyConsole = new groovy.ui.Console();
2455 groovyConsole.setVariable("Jalview", this);
2456 groovyConsole.run();
2459 * We allow only one console at a time, so that AlignFrame menu option
2460 * 'Calculate | Run Groovy script' is unambiguous.
2461 * Disable 'Groovy Console', and enable 'Run script', when the console is
2462 * opened, and the reverse when it is closed
2464 Window window = (Window) groovyConsole.getFrame();
2465 window.addWindowListener(new WindowAdapter()
2468 public void windowClosed(WindowEvent e)
2471 * rebind CMD-Q from Groovy Console to Jalview Quit
2474 enableExecuteGroovy(false);
2480 * show Groovy console window (after close and reopen)
2482 ((Window) groovyConsole.getFrame()).setVisible(true);
2485 * if we got this far, enable 'Run Groovy' in AlignFrame menus
2486 * and disable opening a second console
2488 enableExecuteGroovy(true);
2492 * Bind Ctrl/Cmd-Q to Quit - for reset as Groovy Console takes over this
2493 * binding when opened
2495 protected void addQuitHandler()
2498 .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
2500 .getKeyStroke(KeyEvent.VK_Q,
2501 jalview.util.ShortcutKeyMaskExWrapper
2502 .getMenuShortcutKeyMaskEx()),
2504 getRootPane().getActionMap().put("Quit", new AbstractAction()
2507 public void actionPerformed(ActionEvent e)
2515 * Enable or disable 'Run Groovy script' in AlignFrame calculate menus
2518 * true if Groovy console is open
2520 public void enableExecuteGroovy(boolean enabled)
2523 * disable opening a second Groovy console
2524 * (or re-enable when the console is closed)
2526 groovyShell.setEnabled(!enabled);
2528 AlignFrame[] alignFrames = getAlignFrames();
2529 if (alignFrames != null)
2531 for (AlignFrame af : alignFrames)
2533 af.setGroovyEnabled(enabled);
2539 * Progress bars managed by the IProgressIndicator method.
2541 private Hashtable<Long, JPanel> progressBars;
2543 private Hashtable<Long, IProgressIndicatorHandler> progressBarHandlers;
2548 * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
2551 public void setProgressBar(String message, long id)
2553 // Platform.timeCheck("Desktop " + message, Platform.TIME_MARK);
2555 if (progressBars == null)
2557 progressBars = new Hashtable<>();
2558 progressBarHandlers = new Hashtable<>();
2561 if (progressBars.get(Long.valueOf(id)) != null)
2563 JPanel panel = progressBars.remove(Long.valueOf(id));
2564 if (progressBarHandlers.contains(Long.valueOf(id)))
2566 progressBarHandlers.remove(Long.valueOf(id));
2568 removeProgressPanel(panel);
2572 progressBars.put(Long.valueOf(id), addProgressPanel(message));
2579 * @see jalview.gui.IProgressIndicator#registerHandler(long,
2580 * jalview.gui.IProgressIndicatorHandler)
2583 public void registerHandler(final long id,
2584 final IProgressIndicatorHandler handler)
2586 if (progressBarHandlers == null
2587 || !progressBars.containsKey(Long.valueOf(id)))
2589 throw new Error(MessageManager.getString(
2590 "error.call_setprogressbar_before_registering_handler"));
2592 progressBarHandlers.put(Long.valueOf(id), handler);
2593 final JPanel progressPanel = progressBars.get(Long.valueOf(id));
2594 if (handler.canCancel())
2596 JButton cancel = new JButton(
2597 MessageManager.getString("action.cancel"));
2598 final IProgressIndicator us = this;
2599 cancel.addActionListener(new ActionListener()
2603 public void actionPerformed(ActionEvent e)
2605 handler.cancelActivity(id);
2606 us.setProgressBar(MessageManager
2607 .formatMessage("label.cancelled_params", new Object[]
2608 { ((JLabel) progressPanel.getComponent(0)).getText() }),
2612 progressPanel.add(cancel, BorderLayout.EAST);
2618 * @return true if any progress bars are still active
2621 public boolean operationInProgress()
2623 if (progressBars != null && progressBars.size() > 0)
2631 * This will return the first AlignFrame holding the given viewport instance.
2632 * It will break if there are more than one AlignFrames viewing a particular
2636 * @return alignFrame for viewport
2638 public static AlignFrame getAlignFrameFor(AlignViewportI viewport)
2640 if (getDesktopPane() != null)
2642 AlignmentPanel[] aps = getAlignmentPanels(
2643 viewport.getSequenceSetId());
2644 for (int panel = 0; aps != null && panel < aps.length; panel++)
2646 if (aps[panel] != null && aps[panel].av == viewport)
2648 return aps[panel].alignFrame;
2655 public VamsasApplication getVamsasApplication()
2657 // TODO: JAL-3311 remove remaining code from Jalview relating to VAMSAS
2663 * flag set if jalview GUI is being operated programmatically
2665 private boolean inBatchMode = false;
2668 * check if jalview GUI is being operated programmatically
2670 * @return inBatchMode
2672 public boolean isInBatchMode()
2678 * set flag if jalview GUI is being operated programmatically
2680 * @param inBatchMode
2682 public void setInBatchMode(boolean inBatchMode)
2684 this.inBatchMode = inBatchMode;
2687 public void startServiceDiscovery()
2689 startServiceDiscovery(false);
2692 public void startServiceDiscovery(boolean blocking)
2694 boolean alive = true;
2695 Thread t0 = null, t1 = null, t2 = null;
2696 // JAL-940 - JALVIEW 1 services are now being EOLed as of JABA 2.1 release
2699 // todo: changesupport handlers need to be transferred
2700 if (discoverer == null)
2702 discoverer = jalview.ws.jws1.Discoverer.getInstance();
2703 // register PCS handler for desktop.
2704 discoverer.addPropertyChangeListener(changeSupport);
2706 // JAL-940 - disabled JWS1 service configuration - always start discoverer
2707 // until we phase out completely
2708 (t0 = new Thread(discoverer)).start();
2711 if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
2713 t2 = jalview.ws.jws2.Jws2Discoverer.getInstance()
2714 .startDiscoverer(changeSupport);
2718 // TODO: do rest service discovery
2727 } catch (Exception e)
2730 alive = (t1 != null && t1.isAlive()) || (t2 != null && t2.isAlive())
2731 || (t3 != null && t3.isAlive())
2732 || (t0 != null && t0.isAlive());
2738 * called to check if the service discovery process completed successfully.
2742 protected void JalviewServicesChanged(PropertyChangeEvent evt)
2744 if (evt.getNewValue() == null || evt.getNewValue() instanceof Vector)
2746 final String ermsg = jalview.ws.jws2.Jws2Discoverer.getInstance()
2747 .getErrorMessages();
2750 if (Cache.getDefault("SHOW_WSDISCOVERY_ERRORS", true))
2752 if (serviceChangedDialog == null)
2754 // only run if we aren't already displaying one of these.
2755 addDialogThread(serviceChangedDialog = new Runnable()
2762 * JalviewDialog jd =new JalviewDialog() {
2764 * @Override protected void cancelPressed() { // TODO
2765 * Auto-generated method stub
2767 * }@Override protected void okPressed() { // TODO
2768 * Auto-generated method stub
2770 * }@Override protected void raiseClosed() { // TODO
2771 * Auto-generated method stub
2773 * } }; jd.initDialogFrame(new
2774 * JLabel("<html><table width=\"450\"><tr><td>" + ermsg +
2775 * "<br/>It may be that you have invalid JABA URLs in your web service preferences,"
2776 * + " or mis-configured HTTP proxy settings.<br/>" +
2777 * "Check the <em>Connections</em> and <em>Web services</em> tab of the"
2779 * " Tools->Preferences dialog box to change them.</td></tr></table></html>"
2780 * ), true, true, "Web Service Configuration Problem", 450,
2783 * jd.waitForInput();
2785 JvOptionPane.showConfirmDialog(desktopPane,
2786 new JLabel("<html><table width=\"450\"><tr><td>"
2787 + ermsg + "</td></tr></table>"
2788 + "<p>It may be that you have invalid JABA URLs<br/>in your web service preferences,"
2789 + "<br>or as a command-line argument, or mis-configured HTTP proxy settings.</p>"
2790 + "<p>Check the <em>Connections</em> and <em>Web services</em> tab<br/>of the"
2791 + " Tools->Preferences dialog box to change them.</p></html>"),
2792 "Web Service Configuration Problem",
2793 JvOptionPane.DEFAULT_OPTION,
2794 JvOptionPane.ERROR_MESSAGE);
2795 serviceChangedDialog = null;
2804 "Errors reported by JABA discovery service. Check web services preferences.\n"
2811 private Runnable serviceChangedDialog = null;
2814 * start a thread to open a URL in the configured browser. Pops up a warning
2815 * dialog to the user if there is an exception when calling out to the browser
2820 public static void showUrl(final String url)
2822 showUrl(url, getInstance());
2826 * Like showUrl but allows progress handler to be specified
2830 * (null) or object implementing IProgressIndicator
2832 public static void showUrl(final String url,
2833 final IProgressIndicator progress)
2835 new Thread(new Runnable()
2842 if (progress != null)
2844 progress.setProgressBar(MessageManager
2845 .formatMessage("status.opening_params", new Object[]
2846 { url }), this.hashCode());
2848 jalview.util.BrowserLauncher.openURL(url);
2849 } catch (Exception ex)
2851 JvOptionPane.showInternalMessageDialog(getDesktopPane(),
2853 .getString("label.web_browser_not_found_unix"),
2854 MessageManager.getString("label.web_browser_not_found"),
2855 JvOptionPane.WARNING_MESSAGE);
2857 ex.printStackTrace();
2859 if (progress != null)
2861 progress.setProgressBar(null, this.hashCode());
2867 public static WsParamSetManager wsparamManager = null;
2869 public static ParamManager getUserParameterStore()
2871 if (wsparamManager == null)
2873 wsparamManager = new WsParamSetManager();
2875 return wsparamManager;
2879 * static hyperlink handler proxy method for use by Jalview's internal windows
2883 public static void hyperlinkUpdate(HyperlinkEvent e)
2885 if (e.getEventType() == EventType.ACTIVATED)
2890 url = e.getURL().toString();
2892 } catch (Exception x)
2896 if (Cache.log != null)
2898 Cache.log.error("Couldn't handle string " + url + " as a URL.");
2903 "Couldn't handle string " + url + " as a URL.");
2906 // ignore any exceptions due to dud links.
2913 * single thread that handles display of dialogs to user.
2915 ExecutorService dialogExecutor = Executors.newSingleThreadExecutor();
2918 * flag indicating if dialogExecutor should try to acquire a permit
2920 private volatile boolean dialogPause = true;
2925 private java.util.concurrent.Semaphore block = new Semaphore(0);
2927 private static groovy.ui.Console groovyConsole;
2930 * add another dialog thread to the queue
2934 public void addDialogThread(final Runnable prompter)
2936 dialogExecutor.submit(new Runnable()
2946 } catch (InterruptedException x)
2950 if (Jalview.isHeadlessMode())
2956 SwingUtilities.invokeAndWait(prompter);
2957 } catch (Exception q)
2959 Cache.log.warn("Unexpected Exception in dialog thread.", q);
2965 public void startDialogQueue()
2967 // set the flag so we don't pause waiting for another permit and semaphore
2968 // the current task to begin
2969 dialogPause = false;
2974 * Outputs an image of the desktop to file in EPS format, after prompting the
2975 * user for choice of Text or Lineart character rendering (unless a preference
2976 * has been set). The file name is generated as
2979 * Jalview_snapshot_nnnnn.eps where nnnnn is the current timestamp in milliseconds
2983 protected void snapShotWindow_actionPerformed(ActionEvent e)
2985 // currently the menu option to do this is not shown
2988 int width = getWidth();
2989 int height = getHeight();
2991 "Jalview_snapshot_" + System.currentTimeMillis() + ".eps");
2992 ImageWriterI writer = new ImageWriterI()
2995 public void exportImage(Graphics g) throws Exception
2998 Cache.log.info("Successfully written snapshot to file "
2999 + of.getAbsolutePath());
3002 String title = "View of desktop";
3003 ImageExporter exporter = new ImageExporter(writer, null, TYPE.EPS,
3005 exporter.doExport(of, this, width, height, title);
3009 * Explode the views in the given SplitFrame into separate SplitFrame windows.
3010 * This respects (remembers) any previous 'exploded geometry' i.e. the size
3011 * and location last time the view was expanded (if any). However it does not
3012 * remember the split pane divider location - this is set to match the
3013 * 'exploding' frame.
3017 public void explodeViews(SplitFrame sf)
3019 AlignFrame oldTopFrame = (AlignFrame) sf.getTopFrame();
3020 AlignFrame oldBottomFrame = (AlignFrame) sf.getBottomFrame();
3021 List<? extends AlignmentViewPanel> topPanels = oldTopFrame
3023 List<? extends AlignmentViewPanel> bottomPanels = oldBottomFrame
3025 int viewCount = topPanels.size();
3032 * Processing in reverse order works, forwards order leaves the first panels
3033 * not visible. I don't know why!
3035 for (int i = viewCount - 1; i >= 0; i--)
3038 * Make new top and bottom frames. These take over the respective
3039 * AlignmentPanel objects, including their AlignmentViewports, so the
3040 * cdna/protein relationships between the viewports is carried over to the
3043 * explodedGeometry holds the (x, y) position of the previously exploded
3044 * SplitFrame, and the (width, height) of the AlignFrame component
3046 AlignmentPanel topPanel = (AlignmentPanel) topPanels.get(i);
3047 AlignFrame newTopFrame = new AlignFrame(topPanel);
3048 newTopFrame.setSize(oldTopFrame.getSize());
3049 newTopFrame.setVisible(true);
3050 Rectangle geometry = ((AlignViewport) topPanel.getAlignViewport())
3051 .getExplodedGeometry();
3052 if (geometry != null)
3054 newTopFrame.setSize(geometry.getSize());
3057 AlignmentPanel bottomPanel = (AlignmentPanel) bottomPanels.get(i);
3058 AlignFrame newBottomFrame = new AlignFrame(bottomPanel);
3059 newBottomFrame.setSize(oldBottomFrame.getSize());
3060 newBottomFrame.setVisible(true);
3061 geometry = ((AlignViewport) bottomPanel.getAlignViewport())
3062 .getExplodedGeometry();
3063 if (geometry != null)
3065 newBottomFrame.setSize(geometry.getSize());
3068 topPanel.av.setGatherViewsHere(false);
3069 bottomPanel.av.setGatherViewsHere(false);
3070 JInternalFrame splitFrame = new SplitFrame(newTopFrame,
3072 if (geometry != null)
3074 splitFrame.setLocation(geometry.getLocation());
3076 addInternalFrame(splitFrame, sf.getTitle(), -1, -1);
3080 * Clear references to the panels (now relocated in the new SplitFrames)
3081 * before closing the old SplitFrame.
3084 bottomPanels.clear();
3089 * Gather expanded split frames, sharing the same pairs of sequence set ids,
3090 * back into the given SplitFrame as additional views. Note that the gathered
3091 * frames may themselves have multiple views.
3095 public void gatherViews(GSplitFrame source)
3098 * special handling of explodedGeometry for a view within a SplitFrame: - it
3099 * holds the (x, y) position of the enclosing SplitFrame, and the (width,
3100 * height) of the AlignFrame component
3102 AlignFrame myTopFrame = (AlignFrame) source.getTopFrame();
3103 AlignFrame myBottomFrame = (AlignFrame) source.getBottomFrame();
3104 myTopFrame.viewport.setExplodedGeometry(new Rectangle(source.getX(),
3105 source.getY(), myTopFrame.getWidth(), myTopFrame.getHeight()));
3106 myBottomFrame.viewport
3107 .setExplodedGeometry(new Rectangle(source.getX(), source.getY(),
3108 myBottomFrame.getWidth(), myBottomFrame.getHeight()));
3109 myTopFrame.viewport.setGatherViewsHere(true);
3110 myBottomFrame.viewport.setGatherViewsHere(true);
3111 String topViewId = myTopFrame.viewport.getSequenceSetId();
3112 String bottomViewId = myBottomFrame.viewport.getSequenceSetId();
3114 JInternalFrame[] frames = desktopPane.getAllFrames();
3115 for (JInternalFrame frame : frames)
3117 if (frame instanceof SplitFrame && frame != source)
3119 SplitFrame sf = (SplitFrame) frame;
3120 AlignFrame topFrame = (AlignFrame) sf.getTopFrame();
3121 AlignFrame bottomFrame = (AlignFrame) sf.getBottomFrame();
3122 boolean gatherThis = false;
3123 for (int a = 0; a < topFrame.alignPanels.size(); a++)
3125 AlignmentPanel topPanel = topFrame.alignPanels.get(a);
3126 AlignmentPanel bottomPanel = bottomFrame.alignPanels.get(a);
3127 if (topViewId.equals(topPanel.av.getSequenceSetId())
3128 && bottomViewId.equals(bottomPanel.av.getSequenceSetId()))
3131 topPanel.av.setGatherViewsHere(false);
3132 bottomPanel.av.setGatherViewsHere(false);
3133 topPanel.av.setExplodedGeometry(
3134 new Rectangle(sf.getLocation(), topFrame.getSize()));
3135 bottomPanel.av.setExplodedGeometry(
3136 new Rectangle(sf.getLocation(), bottomFrame.getSize()));
3137 myTopFrame.addAlignmentPanel(topPanel, false);
3138 myBottomFrame.addAlignmentPanel(bottomPanel, false);
3144 topFrame.getAlignPanels().clear();
3145 bottomFrame.getAlignPanels().clear();
3152 * The dust settles...give focus to the tab we did this from.
3154 myTopFrame.setDisplayedView(myTopFrame.alignPanel);
3157 public static groovy.ui.Console getGroovyConsole()
3159 return groovyConsole;
3163 * handles the payload of a drag and drop event.
3165 * TODO refactor to desktop utilities class
3168 * - Data source strings extracted from the drop event
3170 * - protocol for each data source extracted from the drop event
3174 * - the payload from the drop event
3177 @SuppressWarnings("unchecked")
3178 public static void transferFromDropTarget(List<Object> files,
3179 List<DataSourceType> protocols, DropTargetDropEvent evt,
3180 Transferable t) throws Exception
3183 // BH 2018 changed List<String> to List<Object> to allow for File from
3186 // DataFlavor[] flavors = t.getTransferDataFlavors();
3187 // for (int i = 0; i < flavors.length; i++) {
3188 // if (flavors[i].isFlavorJavaFileListType()) {
3189 // evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
3190 // List<File> list = (List<File>) t.getTransferData(flavors[i]);
3191 // for (int j = 0; j < list.size(); j++) {
3192 // File file = (File) list.get(j);
3193 // byte[] data = getDroppedFileBytes(file);
3194 // fileName.setText(file.getName() + " - " + data.length + " " +
3195 // evt.getLocation());
3196 // JTextArea target = (JTextArea) ((DropTarget)
3197 // evt.getSource()).getComponent();
3198 // target.setText(new String(data));
3200 // dtde.dropComplete(true);
3205 DataFlavor uriListFlavor = new DataFlavor(
3206 "text/uri-list;class=java.lang.String"), urlFlavour = null;
3209 urlFlavour = new DataFlavor(
3210 "application/x-java-url; class=java.net.URL");
3211 } catch (ClassNotFoundException cfe)
3213 Cache.log.debug("Couldn't instantiate the URL dataflavor.", cfe);
3216 if (urlFlavour != null && t.isDataFlavorSupported(urlFlavour))
3221 java.net.URL url = (URL) t.getTransferData(urlFlavour);
3222 // nb: java 8 osx bug https://bugs.openjdk.java.net/browse/JDK-8156099
3223 // means url may be null.
3226 protocols.add(DataSourceType.URL);
3227 files.add(url.toString());
3228 Cache.log.debug("Drop handled as URL dataflavor "
3229 + files.get(files.size() - 1));
3234 if (Platform.isAMacAndNotJS())
3237 "Please ignore plist error - occurs due to problem with java 8 on OSX");
3240 } catch (Throwable ex)
3242 Cache.log.debug("URL drop handler failed.", ex);
3245 if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
3247 // Works on Windows and MacOSX
3248 Cache.log.debug("Drop handled as javaFileListFlavor");
3249 for (File file : (List<File>) t
3250 .getTransferData(DataFlavor.javaFileListFlavor))
3253 protocols.add(DataSourceType.FILE);
3258 // Unix like behaviour
3259 boolean added = false;
3261 if (t.isDataFlavorSupported(uriListFlavor))
3263 Cache.log.debug("Drop handled as uriListFlavor");
3264 // This is used by Unix drag system
3265 data = (String) t.getTransferData(uriListFlavor);
3269 // fallback to text: workaround - on OSX where there's a JVM bug
3270 Cache.log.debug("standard URIListFlavor failed. Trying text");
3271 // try text fallback
3272 DataFlavor textDf = new DataFlavor(
3273 "text/plain;class=java.lang.String");
3274 if (t.isDataFlavorSupported(textDf))
3276 data = (String) t.getTransferData(textDf);
3279 Cache.log.debug("Plain text drop content returned "
3280 + (data == null ? "Null - failed" : data));
3285 while (protocols.size() < files.size())
3287 Cache.log.debug("Adding missing FILE protocol for "
3288 + files.get(protocols.size()));
3289 protocols.add(DataSourceType.FILE);
3291 for (java.util.StringTokenizer st = new java.util.StringTokenizer(
3292 data, "\r\n"); st.hasMoreTokens();)
3295 String s = st.nextToken();
3296 if (s.startsWith("#"))
3298 // the line is a comment (as per the RFC 2483)
3301 java.net.URI uri = new java.net.URI(s);
3302 if (uri.getScheme().toLowerCase().startsWith("http"))
3304 protocols.add(DataSourceType.URL);
3305 files.add(uri.toString());
3309 // otherwise preserve old behaviour: catch all for file objects
3310 java.io.File file = new java.io.File(uri);
3311 protocols.add(DataSourceType.FILE);
3312 files.add(file.toString());
3317 if (Cache.log.isDebugEnabled())
3319 if (data == null || !added)
3322 if (t.getTransferDataFlavors() != null
3323 && t.getTransferDataFlavors().length > 0)
3326 "Couldn't resolve drop data. Here are the supported flavors:");
3327 for (DataFlavor fl : t.getTransferDataFlavors())
3330 "Supported transfer dataflavor: " + fl.toString());
3331 Object df = t.getTransferData(fl);
3334 Cache.log.debug("Retrieves: " + df);
3338 Cache.log.debug("Retrieved nothing");
3344 Cache.log.debug("Couldn't resolve dataflavor for drop: "
3350 if (Platform.isWindowsAndNotJS())
3352 Cache.log.debug("Scanning dropped content for Windows Link Files");
3354 // resolve any .lnk files in the file drop
3355 for (int f = 0; f < files.size(); f++)
3357 String source = files.get(f).toString().toLowerCase();
3358 if (protocols.get(f).equals(DataSourceType.FILE)
3359 && (source.endsWith(".lnk") || source.endsWith(".url")
3360 || source.endsWith(".site")))
3364 Object obj = files.get(f);
3365 File lf = (obj instanceof File ? (File) obj
3366 : new File((String) obj));
3367 // process link file to get a URL
3368 Cache.log.debug("Found potential link file: " + lf);
3369 WindowsShortcut wscfile = new WindowsShortcut(lf);
3370 String fullname = wscfile.getRealFilename();
3371 protocols.set(f, FormatAdapter.checkProtocol(fullname));
3372 files.set(f, fullname);
3373 Cache.log.debug("Parsed real filename " + fullname
3374 + " to extract protocol: " + protocols.get(f));
3375 } catch (Exception ex)
3378 "Couldn't parse " + files.get(f) + " as a link file.",
3387 * Sets the Preferences property for experimental features to True or False
3388 * depending on the state of the controlling menu item
3391 protected void showExperimental_actionPerformed(boolean selected)
3393 Cache.setProperty(EXPERIMENTAL_FEATURES, Boolean.toString(selected));
3397 * Answers a (possibly empty) list of any structure viewer frames (currently
3398 * for either Jmol or Chimera) which are currently open. This may optionally
3399 * be restricted to viewers of a specified class, or viewers linked to a
3400 * specified alignment panel.
3403 * if not null, only return viewers linked to this panel
3404 * @param structureViewerClass
3405 * if not null, only return viewers of this class
3408 public List<StructureViewerBase> getStructureViewers(
3409 AlignmentPanel apanel,
3410 Class<? extends StructureViewerBase> structureViewerClass)
3412 List<StructureViewerBase> result = new ArrayList<>();
3413 JInternalFrame[] frames = getAllFrames();
3415 for (JInternalFrame frame : frames)
3417 if (frame instanceof StructureViewerBase)
3419 if (structureViewerClass == null
3420 || structureViewerClass.isInstance(frame))
3423 || ((StructureViewerBase) frame).isLinkedWith(apanel))
3425 result.add((StructureViewerBase) frame);