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());
485 if (Jalview.isInteractive())
487 // disabled for SeqCanvasTest
492 // Spawn a thread that shows the splashscreen
494 SwingUtilities.invokeLater(new Runnable()
499 new SplashScreen(true);
503 // Thread off a new instance of the file chooser - this reduces the
506 // takes to open it later on.
507 new Thread(new Runnable()
512 Cache.log.debug("Filechooser init thread started.");
513 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
514 JalviewFileChooser.forRead(
515 Cache.getProperty("LAST_DIRECTORY"), fileFormat);
516 Cache.log.debug("Filechooser init thread finished.");
519 // Add the service change listener
520 changeSupport.addJalviewPropertyChangeListener("services",
521 new PropertyChangeListener()
525 public void propertyChange(PropertyChangeEvent evt)
527 Cache.log.debug("Firing service changed event for "
528 + evt.getNewValue());
529 JalviewServicesChanged(evt);
534 this.setDropTarget(new java.awt.dnd.DropTarget(desktopPane, this));
536 this.addWindowListener(new WindowAdapter()
539 public void windowClosing(WindowEvent evt)
546 this.addMouseListener(ma = new MouseAdapter()
549 public void mousePressed(MouseEvent evt)
551 if (evt.isPopupTrigger()) // Mac
553 showPasteMenu(evt.getX(), evt.getY());
558 public void mouseReleased(MouseEvent evt)
560 if (evt.isPopupTrigger()) // Windows
562 showPasteMenu(evt.getX(), evt.getY());
566 desktopPane.addMouseListener(ma);
568 } catch (Throwable t)
576 * Answers true if user preferences to enable experimental features is True
581 public boolean showExperimental()
583 String experimental = Cache.getDefault(EXPERIMENTAL_FEATURES,
584 Boolean.FALSE.toString());
585 return Boolean.valueOf(experimental).booleanValue();
588 public void doConfigureStructurePrefs()
590 // configure services
591 StructureSelectionManager ssm = StructureSelectionManager
592 .getStructureSelectionManager(this);
593 if (Cache.getDefault(Preferences.ADD_SS_ANN, true))
595 ssm.setAddTempFacAnnot(
596 Cache.getDefault(Preferences.ADD_TEMPFACT_ANN, true));
597 ssm.setProcessSecondaryStructure(
598 Cache.getDefault(Preferences.STRUCT_FROM_PDB, true));
599 ssm.setSecStructServices(
600 Cache.getDefault(Preferences.USE_RNAVIEW, true));
604 ssm.setAddTempFacAnnot(false);
605 ssm.setProcessSecondaryStructure(false);
606 ssm.setSecStructServices(false);
610 public void checkForNews()
612 final Desktop me = this;
613 // Thread off the news reader, in case there are connection problems.
614 new Thread(new Runnable()
619 Cache.log.debug("Starting news thread.");
620 jvnews = new BlogReader(me);
621 showNews.setVisible(true);
622 Cache.log.debug("Completed news thread.");
627 public void getIdentifiersOrgData()
629 // Thread off the identifiers fetcher
630 new Thread(new Runnable()
635 Cache.log.debug("Downloading data from identifiers.org");
638 UrlDownloadClient.download(IdOrgSettings.getUrl(),
639 IdOrgSettings.getDownloadLocation());
640 } catch (IOException e)
642 Cache.log.debug("Exception downloading identifiers.org data"
651 protected void showNews_actionPerformed(ActionEvent e)
653 showNews(showNews.isSelected());
656 void showNews(boolean visible)
658 Cache.log.debug((visible ? "Showing" : "Hiding") + " news.");
659 showNews.setSelected(visible);
660 if (visible && !jvnews.isVisible())
662 new Thread(new Runnable()
667 long now = System.currentTimeMillis();
668 setProgressBar(MessageManager.getString("status.refreshing_news"),
670 jvnews.refreshNews();
671 setProgressBar(null, now);
679 * recover the last known dimensions for a jalview window
682 * - empty string is desktop, all other windows have unique prefix
683 * @return null or last known dimensions scaled to current geometry (if last
684 * window geom was known)
686 Rectangle getLastKnownDimensions(String windowName)
688 // TODO: lock aspect ratio for scaling desktop Bug #0058199
689 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
690 String x = Cache.getProperty(windowName + "SCREEN_X");
691 String y = Cache.getProperty(windowName + "SCREEN_Y");
692 String width = Cache.getProperty(windowName + "SCREEN_WIDTH");
693 String height = Cache.getProperty(windowName + "SCREEN_HEIGHT");
694 if ((x != null) && (y != null) && (width != null) && (height != null))
696 int ix = Integer.parseInt(x), iy = Integer.parseInt(y),
697 iw = Integer.parseInt(width), ih = Integer.parseInt(height);
698 if (Cache.getProperty("SCREENGEOMETRY_WIDTH") != null)
700 // attempt #1 - try to cope with change in screen geometry - this
701 // version doesn't preserve original jv aspect ratio.
702 // take ratio of current screen size vs original screen size.
703 double sw = ((1f * screenSize.width) / (1f * Integer
704 .parseInt(Cache.getProperty("SCREENGEOMETRY_WIDTH"))));
705 double sh = ((1f * screenSize.height) / (1f * Integer
706 .parseInt(Cache.getProperty("SCREENGEOMETRY_HEIGHT"))));
707 // rescale the bounds depending upon the current screen geometry.
708 ix = (int) (ix * sw);
709 iw = (int) (iw * sw);
710 iy = (int) (iy * sh);
711 ih = (int) (ih * sh);
712 while (ix >= screenSize.width)
715 "Window geometry location recall error: shifting horizontal to within screenbounds.");
716 ix -= screenSize.width;
718 while (iy >= screenSize.height)
721 "Window geometry location recall error: shifting vertical to within screenbounds.");
722 iy -= screenSize.height;
725 "Got last known dimensions for " + windowName + ": x:" + ix
726 + " y:" + iy + " width:" + iw + " height:" + ih);
728 // return dimensions for new instance
729 return new Rectangle(ix, iy, iw, ih);
734 void showPasteMenu(int x, int y)
736 JPopupMenu popup = new JPopupMenu();
737 JMenuItem item = new JMenuItem(
738 MessageManager.getString("label.paste_new_window"));
739 item.addActionListener(new ActionListener()
742 public void actionPerformed(ActionEvent evt)
749 popup.show(this, x, y);
756 Clipboard c = Toolkit.getDefaultToolkit().getSystemClipboard();
757 Transferable contents = c.getContents(this);
759 if (contents != null)
761 String file = (String) contents
762 .getTransferData(DataFlavor.stringFlavor);
764 FileFormatI format = new IdentifyFile().identify(file,
765 DataSourceType.PASTE);
767 new FileLoader().LoadFile(file, DataSourceType.PASTE, format);
770 } catch (Exception ex)
773 "Unable to paste alignment from system clipboard:\n" + ex);
778 // * Add an internal frame to the Jalview desktop that is allowed to be resized,
779 // * has a minimum size of 300px and might or might not be visible
785 // * @param makeVisible
786 // * When true, display frame immediately, otherwise, caller must call
787 // * setVisible themselves.
794 // public static synchronized void addInternalFrame(
795 // final JInternalFrame frame, String title, boolean makeVisible,
798 // // textbox, web services, sequenceFetcher, featureSettings
799 // getInstance().addFrame(frame, title, makeVisible, w, h,
800 // FRAME_ALLOW_RESIZE, FRAME_SET_MIN_SIZE_300);
804 // * Add an internal frame to the Jalview desktop that is visible, has a minimum
805 // * size of 300px, and may or may not be resizable
815 // * @param resizable
819 // public static synchronized void addInternalFrame(
820 // final JInternalFrame frame, String title, int w, int h,
821 // boolean resizable)
823 // // annotation, font, calculation, user-defined colors
824 // getInstance().addFrame(frame, title, FRAME_MAKE_VISIBLE, w, h,
825 // resizable, FRAME_SET_MIN_SIZE_300);
829 * Adds and opens the given frame to the desktop that is visible, allowed to
830 * resize, and has a 300px minimum width.
841 public static synchronized void addInternalFrame(
842 final JInternalFrame frame, String title, int w, int h)
845 getInstance().addFrame(frame, title, Desktop.FRAME_MAKE_VISIBLE, w, h,
846 FRAME_ALLOW_RESIZE, FRAME_SET_MIN_SIZE_300);
850 * Add an internal frame to the Jalview desktop that may optionally be
851 * visible, resizable, and allowed to be any size
858 * When true, display frame immediately, otherwise, caller must call
859 * setVisible themselves.
866 * @param ignoreMinSize
867 * Do not set the default minimum size for frame
869 public static synchronized void addInternalFrame(
870 final JInternalFrame frame, String title, boolean makeVisible,
871 int w, int h, boolean resizable, boolean ignoreMinSize)
874 getInstance().addFrame(frame, title, makeVisible, w, h, resizable,
878 // These can now by put into a single int flag, if desired:
880 public final static boolean FRAME_MAKE_VISIBLE = true;
882 public final static boolean FRAME_NOT_VISIBLE = false;
884 public final static boolean FRAME_ALLOW_RESIZE = true;
886 public final static boolean FRAME_NOT_RESIZABLE = false;
888 public final static boolean FRAME_ALLOW_ANY_SIZE = true;
890 public final static boolean FRAME_SET_MIN_SIZE_300 = false;
892 private void addFrame(JInternalFrame frame, String title,
893 boolean makeVisible, int w, int h, boolean resizable,
894 boolean ignoreMinSize)
896 // TODO: allow callers to determine X and Y position of frame (eg. via
898 // TODO: consider fixing method to update entries in the window submenu with
899 // the current window title
901 frame.setTitle(title);
902 if (frame.getWidth() < 1 || frame.getHeight() < 1)
906 // THIS IS A PUBLIC STATIC METHOD, SO IT MAY BE CALLED EVEN IN
907 // A HEADLESS STATE WHEN NO DESKTOP EXISTS. MUST RETURN
908 // IF JALVIEW IS RUNNING HEADLESS
909 // ///////////////////////////////////////////////
910 if (Jalview.isHeadlessMode())
917 boolean isEmbedded = (Platform.getEmbeddedAttribute(frame, "id") != null);
918 boolean hasEmbeddedSize = (Platform.getDimIfEmbedded(frame, -1, -1) != null);
919 // Web page embedding allows us to ignore minimum size
920 ignoreMinSize |= hasEmbeddedSize;
924 // Set default dimension for Alignment Frame window.
925 // The Alignment Frame window could be added from a number of places,
927 // I did this here in order not to miss out on any Alignment frame.
928 if (frame instanceof AlignFrame)
930 frame.setMinimumSize(new Dimension(ALIGN_FRAME_DEFAULT_MIN_WIDTH,
931 ALIGN_FRAME_DEFAULT_MIN_HEIGHT));
933 frame.setMinimumSize(
934 new Dimension(DEFAULT_MIN_WIDTH, DEFAULT_MIN_HEIGHT));
939 frame.setVisible(makeVisible);
940 frame.setClosable(true);
941 frame.setResizable(resizable);
942 frame.setMaximizable(resizable);
943 frame.setIconifiable(resizable);
944 frame.setOpaque(Platform.isJS());
945 if (!isEmbedded && frame.getX() < 1 && frame.getY() < 1)
947 frame.setLocation(xOffset * openFrameCount,
948 yOffset * ((openFrameCount - 1) % 10) + yOffset);
952 * add an entry for the new frame in the Window menu
953 * (and remove it when the frame is closed)
955 final JMenuItem menuItem = new JMenuItem(title);
956 frame.addInternalFrameListener(new InternalFrameAdapter()
959 public void internalFrameActivated(InternalFrameEvent evt)
961 JInternalFrame itf = getDesktopPane().getSelectedFrame();
964 if (itf instanceof AlignFrame)
966 Jalview.setCurrentAlignFrame((AlignFrame) itf);
973 public void internalFrameClosed(InternalFrameEvent evt)
975 PaintRefresher.RemoveComponent(frame);
978 * defensive check to prevent frames being
979 * added half off the window
981 if (openFrameCount > 0)
987 * ensure no reference to alignFrame retained by menu item listener
989 if (menuItem.getActionListeners().length > 0)
991 menuItem.removeActionListener(menuItem.getActionListeners()[0]);
993 getInstance().windowMenu.remove(menuItem);
997 menuItem.addActionListener(new ActionListener()
1000 public void actionPerformed(ActionEvent e)
1004 frame.setSelected(true);
1005 frame.setIcon(false);
1006 } catch (java.beans.PropertyVetoException ex)
1008 // System.err.println(ex.toString());
1013 setKeyBindings(frame);
1015 getDesktopPane().add(frame);
1017 getInstance().windowMenu.add(menuItem);
1022 frame.setSelected(true);
1023 frame.requestFocus();
1024 } catch (java.beans.PropertyVetoException ve)
1026 } catch (java.lang.ClassCastException cex)
1029 "Squashed a possible GUI implementation error. If you can recreate this, please look at http://issues.jalview.org/browse/JAL-869",
1035 * Add key bindings to a JInternalFrame so that Ctrl-W and Cmd-W will close
1040 private static void setKeyBindings(JInternalFrame frame)
1042 final Action closeAction = new AbstractAction()
1045 public void actionPerformed(ActionEvent e)
1052 * set up key bindings for Ctrl-W and Cmd-W, with the same (Close) action
1054 KeyStroke ctrlWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
1055 InputEvent.CTRL_DOWN_MASK);
1056 KeyStroke cmdWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
1057 ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx());
1059 InputMap inputMap = frame
1060 .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
1061 String ctrlW = ctrlWKey.toString();
1062 inputMap.put(ctrlWKey, ctrlW);
1063 inputMap.put(cmdWKey, ctrlW);
1065 ActionMap actionMap = frame.getActionMap();
1066 actionMap.put(ctrlW, closeAction);
1070 public void lostOwnership(Clipboard clipboard, Transferable contents)
1074 jalviewClipboard = null;
1077 internalCopy = false;
1081 public void dragEnter(DropTargetDragEvent evt)
1086 public void dragExit(DropTargetEvent evt)
1091 public void dragOver(DropTargetDragEvent evt)
1096 public void dropActionChanged(DropTargetDragEvent evt)
1107 public void drop(DropTargetDropEvent evt)
1109 boolean success = true;
1110 // JAL-1552 - acceptDrop required before getTransferable call for
1111 // Java's Transferable for native dnd
1112 evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
1113 Transferable t = evt.getTransferable();
1114 List<Object> files = new ArrayList<>();
1115 List<DataSourceType> protocols = new ArrayList<>();
1119 transferFromDropTarget(files, protocols, evt, t);
1120 } catch (Exception e)
1122 e.printStackTrace();
1130 for (int i = 0; i < files.size(); i++)
1132 // BH 2018 File or String
1133 Object file = files.get(i);
1134 String fileName = file.toString();
1135 DataSourceType protocol = (protocols == null)
1136 ? DataSourceType.FILE
1138 FileFormatI format = null;
1140 if (fileName.endsWith(".jar"))
1142 format = FileFormat.Jalview;
1147 format = new IdentifyFile().identify(file, protocol);
1149 if (file instanceof File)
1151 Platform.cacheFileData((File) file);
1153 new FileLoader().LoadFile(null, file, protocol, format);
1156 } catch (Exception ex)
1161 evt.dropComplete(success); // need this to ensure input focus is properly
1162 // transfered to any new windows created
1172 public void inputLocalFileMenuItem_actionPerformed(AlignViewport viewport)
1174 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
1175 JalviewFileChooser chooser = JalviewFileChooser.forRead(
1176 Cache.getProperty("LAST_DIRECTORY"), fileFormat,
1177 BackupFiles.getEnabled());
1179 chooser.setFileView(new JalviewFileView());
1180 chooser.setDialogTitle(
1181 MessageManager.getString("label.open_local_file"));
1182 chooser.setToolTipText(MessageManager.getString("action.open"));
1184 chooser.setResponseHandler(0, new Runnable()
1189 File selectedFile = chooser.getSelectedFile();
1190 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1192 FileFormatI format = chooser.getSelectedFormat();
1195 * Call IdentifyFile to verify the file contains what its extension implies.
1196 * Skip this step for dynamically added file formats, because
1197 * IdentifyFile does not know how to recognise them.
1199 if (FileFormats.getInstance().isIdentifiable(format))
1203 format = new IdentifyFile().identify(selectedFile,
1204 DataSourceType.FILE);
1205 } catch (FileFormatException e)
1207 // format = null; //??
1211 new FileLoader().LoadFile(viewport, selectedFile,
1212 DataSourceType.FILE, format);
1215 chooser.showOpenDialog(this);
1219 * Shows a dialog for input of a URL at which to retrieve alignment data
1224 public void inputURLMenuItem_actionPerformed(AlignViewport viewport)
1226 // This construct allows us to have a wider textfield
1228 JLabel label = new JLabel(
1229 MessageManager.getString("label.input_file_url"));
1231 JPanel panel = new JPanel(new GridLayout(2, 1));
1235 * the URL to fetch is
1236 * Java: an editable combobox with history
1237 * JS: (pending JAL-3038) a plain text field
1240 String urlBase = "http://www.";
1241 if (Platform.isJS())
1243 history = new JTextField(urlBase, 35);
1252 JComboBox<String> asCombo = new JComboBox<>();
1253 asCombo.setPreferredSize(new Dimension(400, 20));
1254 asCombo.setEditable(true);
1255 asCombo.addItem(urlBase);
1256 String historyItems = Cache.getProperty("RECENT_URL");
1257 if (historyItems != null)
1259 for (String token : historyItems.split("\\t"))
1261 asCombo.addItem(token);
1268 Object[] options = new Object[] { MessageManager.getString("action.ok"),
1269 MessageManager.getString("action.cancel") };
1270 Runnable action = new Runnable()
1275 @SuppressWarnings("unchecked")
1276 String url = (history instanceof JTextField
1277 ? ((JTextField) history).getText()
1278 : ((JComboBox<String>) history).getSelectedItem()
1281 if (url.toLowerCase().endsWith(".jar"))
1283 if (viewport != null)
1285 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1286 FileFormat.Jalview);
1290 new FileLoader().LoadFile(url, DataSourceType.URL,
1291 FileFormat.Jalview);
1296 FileFormatI format = null;
1299 format = new IdentifyFile().identify(url, DataSourceType.URL);
1300 } catch (FileFormatException e)
1302 // TODO revise error handling, distinguish between
1303 // URL not found and response not valid
1308 String msg = MessageManager
1309 .formatMessage("label.couldnt_locate", url);
1310 JvOptionPane.showInternalMessageDialog(getDesktopPane(), msg,
1311 MessageManager.getString("label.url_not_found"),
1312 JvOptionPane.WARNING_MESSAGE);
1317 if (viewport != null)
1319 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1324 new FileLoader().LoadFile(url, DataSourceType.URL, format);
1329 String dialogOption = MessageManager
1330 .getString("label.input_alignment_from_url");
1331 JvOptionPane.newOptionDialog(desktopPane).setResponseHandler(0, action)
1332 .showInternalDialog(panel, dialogOption,
1333 JvOptionPane.YES_NO_CANCEL_OPTION,
1334 JvOptionPane.PLAIN_MESSAGE, null, options,
1335 MessageManager.getString("action.ok"));
1339 * Opens the CutAndPaste window for the user to paste an alignment in to
1342 * - if not null, the pasted alignment is added to the current
1343 * alignment; if null, to a new alignment window
1346 public void inputTextboxMenuItem_actionPerformed(
1347 AlignmentViewPanel viewPanel)
1349 CutAndPasteTransfer cap = new CutAndPasteTransfer();
1350 cap.setForInput(viewPanel);
1351 addInternalFrame(cap,
1352 MessageManager.getString("label.cut_paste_alignmen_file"),
1353 FRAME_MAKE_VISIBLE, 600, 500, FRAME_ALLOW_RESIZE,
1354 FRAME_SET_MIN_SIZE_300);
1363 Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
1364 Cache.setProperty("SCREENGEOMETRY_WIDTH", screen.width + "");
1365 Cache.setProperty("SCREENGEOMETRY_HEIGHT", screen.height + "");
1366 storeLastKnownDimensions("", new Rectangle(getBounds().x, getBounds().y,
1367 getWidth(), getHeight()));
1369 if (jconsole != null)
1371 storeLastKnownDimensions("JAVA_CONSOLE_", jconsole.getBounds());
1372 jconsole.stopConsole();
1376 storeLastKnownDimensions("JALVIEW_RSS_WINDOW_", jvnews.getBounds());
1379 if (dialogExecutor != null)
1381 dialogExecutor.shutdownNow();
1383 closeAll_actionPerformed(null);
1385 if (groovyConsole != null)
1387 // suppress a possible repeat prompt to save script
1388 groovyConsole.setDirty(false);
1389 groovyConsole.exit();
1394 private void storeLastKnownDimensions(String string, Rectangle jc)
1396 Cache.log.debug("Storing last known dimensions for " + string + ": x:"
1397 + jc.x + " y:" + jc.y + " width:" + jc.width + " height:"
1400 Cache.setProperty(string + "SCREEN_X", jc.x + "");
1401 Cache.setProperty(string + "SCREEN_Y", jc.y + "");
1402 Cache.setProperty(string + "SCREEN_WIDTH", jc.width + "");
1403 Cache.setProperty(string + "SCREEN_HEIGHT", jc.height + "");
1413 public void aboutMenuItem_actionPerformed(ActionEvent e)
1415 new Thread(new Runnable()
1420 new SplashScreen(false);
1426 * Returns the html text for the About screen, including any available version
1427 * number, build details, author details and citation reference, but without
1428 * the enclosing {@code html} tags
1432 public String getAboutMessage()
1434 StringBuilder message = new StringBuilder(1024);
1435 message.append("<h1><strong>Version: ")
1436 .append(Cache.getProperty("VERSION")).append("</strong></h1>")
1437 .append("<strong>Built: <em>")
1438 .append(Cache.getDefault("BUILD_DATE", "unknown"))
1439 .append("</em> from ").append(Cache.getBuildDetailsForSplash())
1440 .append("</strong>");
1442 String latestVersion = Cache.getDefault("LATEST_VERSION", "Checking");
1443 if (latestVersion.equals("Checking"))
1445 // JBP removed this message for 2.11: May be reinstated in future version
1446 // message.append("<br>...Checking latest version...</br>");
1448 else if (!latestVersion.equals(Cache.getProperty("VERSION")))
1450 boolean red = false;
1451 if (Cache.getProperty("VERSION").toLowerCase()
1452 .indexOf("automated build") == -1)
1455 // Displayed when code version and jnlp version do not match and code
1456 // version is not a development build
1457 message.append("<div style=\"color: #FF0000;font-style: bold;\">");
1460 message.append("<br>!! Version ")
1461 .append(Cache.getDefault("LATEST_VERSION", "..Checking.."))
1462 .append(" is available for download from ")
1463 .append(Cache.getDefault("www.jalview.org",
1464 "http://www.jalview.org"))
1468 message.append("</div>");
1471 message.append("<br>Authors: ");
1472 message.append(Cache.getDefault("AUTHORFNAMES", DEFAULT_AUTHORS));
1473 message.append(CITATION);
1475 return message.toString();
1479 * Action on requesting Help documentation
1482 public void documentationMenuItem_actionPerformed()
1486 if (Platform.isJS())
1488 BrowserLauncher.openURL("http://www.jalview.org/help.html");
1497 Help.showHelpWindow();
1499 } catch (Exception ex)
1501 System.err.println("Error opening help: " + ex.getMessage());
1506 public void closeAll_actionPerformed(ActionEvent e)
1508 // TODO show a progress bar while closing?
1509 JInternalFrame[] frames = desktopPane.getAllFrames();
1510 for (int i = 0; i < frames.length; i++)
1514 frames[i].setClosed(true);
1515 } catch (java.beans.PropertyVetoException ex)
1519 Jalview.setCurrentAlignFrame(null);
1520 System.out.println("ALL CLOSED");
1523 * reset state of singleton objects as appropriate (clear down session state
1524 * when all windows are closed)
1526 StructureSelectionManager ssm = StructureSelectionManager
1527 .getStructureSelectionManager(this);
1535 public void raiseRelated_actionPerformed(ActionEvent e)
1537 reorderAssociatedWindows(false, false);
1541 public void minimizeAssociated_actionPerformed(ActionEvent e)
1543 reorderAssociatedWindows(true, false);
1546 void closeAssociatedWindows()
1548 reorderAssociatedWindows(false, true);
1554 * @seejalview.jbgui.GDesktop#garbageCollect_actionPerformed(java.awt.event.
1558 protected void garbageCollect_actionPerformed(ActionEvent e)
1560 // We simply collect the garbage
1561 Cache.log.debug("Collecting garbage...");
1563 Cache.log.debug("Finished garbage collection.");
1570 * jalview.jbgui.GDesktop#showMemusage_actionPerformed(java.awt.event.ActionEvent
1574 protected void showMemusage_actionPerformed(ActionEvent e)
1576 desktopPane.showMemoryUsage(showMemusage.isSelected());
1583 * jalview.jbgui.GDesktop#showConsole_actionPerformed(java.awt.event.ActionEvent
1587 protected void showConsole_actionPerformed(ActionEvent e)
1589 showConsole(showConsole.isSelected());
1592 Console jconsole = null;
1595 * control whether the java console is visible or not
1599 void showConsole(boolean selected)
1601 // TODO: decide if we should update properties file
1602 if (jconsole != null) // BH 2018
1604 showConsole.setSelected(selected);
1605 Cache.setProperty("SHOW_JAVA_CONSOLE",
1606 Boolean.valueOf(selected).toString());
1607 jconsole.setVisible(selected);
1611 void reorderAssociatedWindows(boolean minimize, boolean close)
1613 JInternalFrame[] frames = desktopPane.getAllFrames();
1614 if (frames == null || frames.length < 1)
1619 AlignmentViewport source = null, target = null;
1620 if (frames[0] instanceof AlignFrame)
1622 source = ((AlignFrame) frames[0]).getCurrentView();
1624 else if (frames[0] instanceof TreePanel)
1626 source = ((TreePanel) frames[0]).getViewPort();
1628 else if (frames[0] instanceof PCAPanel)
1630 source = ((PCAPanel) frames[0]).av;
1632 else if (frames[0].getContentPane() instanceof PairwiseAlignPanel)
1634 source = ((PairwiseAlignPanel) frames[0].getContentPane()).av;
1639 for (int i = 0; i < frames.length; i++)
1642 if (frames[i] == null)
1646 if (frames[i] instanceof AlignFrame)
1648 target = ((AlignFrame) frames[i]).getCurrentView();
1650 else if (frames[i] instanceof TreePanel)
1652 target = ((TreePanel) frames[i]).getViewPort();
1654 else if (frames[i] instanceof PCAPanel)
1656 target = ((PCAPanel) frames[i]).av;
1658 else if (frames[i].getContentPane() instanceof PairwiseAlignPanel)
1660 target = ((PairwiseAlignPanel) frames[i].getContentPane()).av;
1663 if (source == target)
1669 frames[i].setClosed(true);
1673 frames[i].setIcon(minimize);
1676 frames[i].toFront();
1680 } catch (java.beans.PropertyVetoException ex)
1695 protected void preferences_actionPerformed(ActionEvent e)
1701 * Prompts the user to choose a file and then saves the Jalview state as a
1702 * Jalview project file
1705 public void saveState_actionPerformed()
1707 saveState_actionPerformed(false);
1710 public void saveState_actionPerformed(boolean saveAs)
1712 java.io.File projectFile = getProjectFile();
1713 // autoSave indicates we already have a file and don't need to ask
1714 boolean autoSave = projectFile != null && !saveAs
1715 && BackupFiles.getEnabled();
1717 // System.out.println("autoSave="+autoSave+", projectFile='"+projectFile+"',
1718 // saveAs="+saveAs+", Backups
1719 // "+(BackupFiles.getEnabled()?"enabled":"disabled"));
1721 boolean approveSave = false;
1724 JalviewFileChooser chooser = new JalviewFileChooser("jvp",
1727 chooser.setFileView(new JalviewFileView());
1728 chooser.setDialogTitle(MessageManager.getString("label.save_state"));
1730 int value = chooser.showSaveDialog(this);
1732 if (value == JalviewFileChooser.APPROVE_OPTION)
1734 projectFile = chooser.getSelectedFile();
1735 setProjectFile(projectFile);
1740 if (approveSave || autoSave)
1742 final Desktop me = this;
1743 final java.io.File chosenFile = projectFile;
1744 new Thread(new Runnable()
1749 // TODO: refactor to Jalview desktop session controller action.
1750 setProgressBar(MessageManager.formatMessage(
1751 "label.saving_jalview_project", new Object[]
1752 { chosenFile.getName() }), chosenFile.hashCode());
1753 Cache.setProperty("LAST_DIRECTORY", chosenFile.getParent());
1754 // TODO catch and handle errors for savestate
1755 // TODO prevent user from messing with the Desktop whilst we're saving
1758 boolean doBackup = BackupFiles.getEnabled();
1759 BackupFiles backupfiles = doBackup ? new BackupFiles(chosenFile)
1762 new Jalview2XML().saveState(
1763 doBackup ? backupfiles.getTempFile() : chosenFile);
1767 backupfiles.setWriteSuccess(true);
1768 backupfiles.rollBackupsAndRenameTempFile();
1770 } catch (OutOfMemoryError oom)
1772 new OOMWarning("Whilst saving current state to "
1773 + chosenFile.getName(), oom);
1774 } catch (Exception ex)
1776 Cache.log.error("Problems whilst trying to save to "
1777 + chosenFile.getName(), ex);
1778 JvOptionPane.showMessageDialog(me,
1779 MessageManager.formatMessage(
1780 "label.error_whilst_saving_current_state_to",
1782 { chosenFile.getName() }),
1783 MessageManager.getString("label.couldnt_save_project"),
1784 JvOptionPane.WARNING_MESSAGE);
1786 setProgressBar(null, chosenFile.hashCode());
1793 public void saveAsState_actionPerformed(ActionEvent e)
1795 saveState_actionPerformed(true);
1798 private void setProjectFile(File choice)
1800 this.projectFile = choice;
1803 public File getProjectFile()
1805 return this.projectFile;
1809 * Shows a file chooser dialog and tries to read in the selected file as a
1813 public void loadState_actionPerformed()
1815 final String[] suffix = new String[] { "jvp", "jar" };
1816 final String[] desc = new String[] { "Jalview Project",
1817 "Jalview Project (old)" };
1818 JalviewFileChooser chooser = new JalviewFileChooser(
1819 Cache.getProperty("LAST_DIRECTORY"), suffix, desc,
1820 "Jalview Project", true, BackupFiles.getEnabled()); // last two
1824 chooser.setFileView(new JalviewFileView());
1825 chooser.setDialogTitle(MessageManager.getString("label.restore_state"));
1826 chooser.setResponseHandler(0, new Runnable()
1831 File selectedFile = chooser.getSelectedFile();
1832 setProjectFile(selectedFile);
1833 String choice = selectedFile.getAbsolutePath();
1834 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1835 new Thread(new Runnable()
1842 new Jalview2XML().loadJalviewAlign(selectedFile);
1843 } catch (OutOfMemoryError oom)
1845 new OOMWarning("Whilst loading project from " + choice, oom);
1846 } catch (Exception ex)
1849 "Problems whilst loading project from " + choice, ex);
1850 JvOptionPane.showMessageDialog(getDesktopPane(),
1851 MessageManager.formatMessage(
1852 "label.error_whilst_loading_project_from",
1856 .getString("label.couldnt_load_project"),
1857 JvOptionPane.WARNING_MESSAGE);
1864 chooser.showOpenDialog(this);
1868 public void inputSequence_actionPerformed(ActionEvent e)
1870 new SequenceFetcher(this);
1873 JPanel progressPanel;
1875 ArrayList<JPanel> fileLoadingPanels = new ArrayList<>();
1877 public void startLoading(final Object fileName)
1879 if (fileLoadingCount == 0)
1881 fileLoadingPanels.add(addProgressPanel(MessageManager
1882 .formatMessage("label.loading_file", new Object[]
1888 private JPanel addProgressPanel(String string)
1890 if (progressPanel == null)
1892 progressPanel = new JPanel(new GridLayout(1, 1));
1893 totalProgressCount = 0;
1894 getContentPane().add(progressPanel, BorderLayout.SOUTH);
1896 JPanel thisprogress = new JPanel(new BorderLayout(10, 5));
1897 JProgressBar progressBar = new JProgressBar();
1898 progressBar.setIndeterminate(true);
1900 thisprogress.add(new JLabel(string), BorderLayout.WEST);
1902 thisprogress.add(progressBar, BorderLayout.CENTER);
1903 progressPanel.add(thisprogress);
1904 ((GridLayout) progressPanel.getLayout()).setRows(
1905 ((GridLayout) progressPanel.getLayout()).getRows() + 1);
1906 ++totalProgressCount;
1908 return thisprogress;
1911 int totalProgressCount = 0;
1913 private void removeProgressPanel(JPanel progbar)
1915 if (progressPanel != null)
1917 synchronized (progressPanel)
1919 progressPanel.remove(progbar);
1920 GridLayout gl = (GridLayout) progressPanel.getLayout();
1921 gl.setRows(gl.getRows() - 1);
1922 if (--totalProgressCount < 1)
1924 this.getContentPane().remove(progressPanel);
1925 progressPanel = null;
1932 public void stopLoading()
1935 if (fileLoadingCount < 1)
1937 while (fileLoadingPanels.size() > 0)
1939 removeProgressPanel(fileLoadingPanels.remove(0));
1941 fileLoadingPanels.clear();
1942 fileLoadingCount = 0;
1947 public static int getViewCount(String alignmentId)
1949 AlignmentViewport[] aps = getViewports(alignmentId);
1950 return (aps == null) ? 0 : aps.length;
1955 * @param alignmentId
1956 * - if null, all sets are returned
1957 * @return all AlignmentPanels concerning the alignmentId sequence set
1959 public static AlignmentPanel[] getAlignmentPanels(String alignmentId)
1961 if (getDesktopPane() == null)
1963 // no frames created and in headless mode
1964 // TODO: verify that frames are recoverable when in headless mode
1967 List<AlignmentPanel> aps = new ArrayList<>();
1968 AlignFrame[] frames = getAlignFrames();
1973 for (AlignFrame af : frames)
1975 for (AlignmentPanel ap : af.alignPanels)
1977 if (alignmentId == null
1978 || alignmentId.equals(ap.av.getSequenceSetId()))
1984 if (aps.size() == 0)
1988 AlignmentPanel[] vap = aps.toArray(new AlignmentPanel[aps.size()]);
1993 * get all the viewports on an alignment.
1995 * @param sequenceSetId
1996 * unique alignment id (may be null - all viewports returned in that
1998 * @return all viewports on the alignment bound to sequenceSetId
2000 public static AlignmentViewport[] getViewports(String sequenceSetId)
2002 List<AlignmentViewport> viewp = new ArrayList<>();
2003 if (getDesktopPane() != null)
2005 AlignFrame[] frames = getAlignFrames();
2007 for (AlignFrame afr : frames)
2009 if (sequenceSetId == null || afr.getViewport().getSequenceSetId()
2010 .equals(sequenceSetId))
2012 if (afr.alignPanels != null)
2014 for (AlignmentPanel ap : afr.alignPanels)
2016 if (sequenceSetId == null
2017 || sequenceSetId.equals(ap.av.getSequenceSetId()))
2025 viewp.add(afr.getViewport());
2029 if (viewp.size() > 0)
2031 return viewp.toArray(new AlignmentViewport[viewp.size()]);
2038 * Explode the views in the given frame into separate AlignFrame
2042 public static void explodeViews(AlignFrame af)
2044 int size = af.alignPanels.size();
2050 // FIXME: ideally should use UI interface API
2051 FeatureSettings viewFeatureSettings = (af.featureSettings != null
2052 && af.featureSettings.isOpen()) ? af.featureSettings : null;
2053 Rectangle fsBounds = af.getFeatureSettingsGeometry();
2054 for (int i = 0; i < size; i++)
2056 AlignmentPanel ap = af.alignPanels.get(i);
2058 AlignFrame newaf = new AlignFrame(ap);
2060 // transfer reference for existing feature settings to new alignFrame
2061 if (ap == af.alignPanel)
2063 if (viewFeatureSettings != null && viewFeatureSettings.fr.ap == ap)
2065 newaf.featureSettings = viewFeatureSettings;
2067 newaf.setFeatureSettingsGeometry(fsBounds);
2071 * Restore the view's last exploded frame geometry if known. Multiple
2072 * views from one exploded frame share and restore the same (frame)
2073 * position and size.
2075 Rectangle geometry = ap.av.getExplodedGeometry();
2076 if (geometry != null)
2078 newaf.setBounds(geometry);
2081 ap.av.setGatherViewsHere(false);
2083 addInternalFrame(newaf, af.getTitle(), AlignFrame.DEFAULT_WIDTH,
2084 AlignFrame.DEFAULT_HEIGHT);
2085 // and materialise a new feature settings dialog instance for the new
2087 // (closes the old as if 'OK' was pressed)
2088 if (ap == af.alignPanel && newaf.featureSettings != null
2089 && newaf.featureSettings.isOpen()
2090 && af.alignPanel.getAlignViewport().isShowSequenceFeatures())
2092 newaf.showFeatureSettingsUI();
2096 af.featureSettings = null;
2097 af.alignPanels.clear();
2098 af.closeMenuItem_actionPerformed(true);
2103 * Gather expanded views (separate AlignFrame's) with the same sequence set
2104 * identifier back in to this frame as additional views, and close the
2105 * expanded views. Note the expanded frames may themselves have multiple
2106 * views. We take the lot.
2110 public void gatherViews(AlignFrame source)
2112 source.viewport.setGatherViewsHere(true);
2113 source.viewport.setExplodedGeometry(source.getBounds());
2114 JInternalFrame[] frames = desktopPane.getAllFrames();
2115 String viewId = source.viewport.getSequenceSetId();
2116 for (int t = 0; t < frames.length; t++)
2118 if (frames[t] instanceof AlignFrame && frames[t] != source)
2120 AlignFrame af = (AlignFrame) frames[t];
2121 boolean gatherThis = false;
2122 for (int a = 0; a < af.alignPanels.size(); a++)
2124 AlignmentPanel ap = af.alignPanels.get(a);
2125 if (viewId.equals(ap.av.getSequenceSetId()))
2128 ap.av.setGatherViewsHere(false);
2129 ap.av.setExplodedGeometry(af.getBounds());
2130 source.addAlignmentPanel(ap, false);
2136 if (af.featureSettings != null && af.featureSettings.isOpen())
2138 if (source.featureSettings == null)
2140 // preserve the feature settings geometry for this frame
2141 source.featureSettings = af.featureSettings;
2142 source.setFeatureSettingsGeometry(
2143 af.getFeatureSettingsGeometry());
2147 // close it and forget
2148 af.featureSettings.close();
2151 af.alignPanels.clear();
2152 af.closeMenuItem_actionPerformed(true);
2157 // refresh the feature setting UI for the source frame if it exists
2158 if (source.featureSettings != null && source.featureSettings.isOpen())
2160 source.showFeatureSettingsUI();
2164 public JInternalFrame[] getAllFrames()
2166 return desktopPane.getAllFrames();
2170 * Checks the given url to see if it gives a response indicating that the user
2171 * should be informed of a new questionnaire.
2175 public void checkForQuestionnaire(String url)
2177 UserQuestionnaireCheck jvq = new UserQuestionnaireCheck(url);
2178 // javax.swing.SwingUtilities.invokeLater(jvq);
2179 new Thread(jvq).start();
2182 public void checkURLLinks()
2184 // Thread off the URL link checker
2185 addDialogThread(new Runnable()
2190 if (Cache.getDefault("CHECKURLLINKS", true))
2192 // check what the actual links are - if it's just the default don't
2193 // bother with the warning
2194 List<String> links = Preferences.sequenceUrlLinks
2197 // only need to check links if there is one with a
2198 // SEQUENCE_ID which is not the default EMBL_EBI link
2199 ListIterator<String> li = links.listIterator();
2200 boolean check = false;
2201 List<JLabel> urls = new ArrayList<>();
2202 while (li.hasNext())
2204 String link = li.next();
2205 if (link.contains(jalview.util.UrlConstants.SEQUENCE_ID)
2206 && !UrlConstants.isDefaultString(link))
2209 int barPos = link.indexOf("|");
2210 String urlMsg = barPos == -1 ? link
2211 : link.substring(0, barPos) + ": "
2212 + link.substring(barPos + 1);
2213 urls.add(new JLabel(urlMsg));
2221 // ask user to check in case URL links use old style tokens
2222 // ($SEQUENCE_ID$ for sequence id _or_ accession id)
2223 JPanel msgPanel = new JPanel();
2224 msgPanel.setLayout(new BoxLayout(msgPanel, BoxLayout.PAGE_AXIS));
2225 msgPanel.add(Box.createVerticalGlue());
2226 JLabel msg = new JLabel(MessageManager
2227 .getString("label.SEQUENCE_ID_for_DB_ACCESSION1"));
2228 JLabel msg2 = new JLabel(MessageManager
2229 .getString("label.SEQUENCE_ID_for_DB_ACCESSION2"));
2231 for (JLabel url : urls)
2237 final JCheckBox jcb = new JCheckBox(
2238 MessageManager.getString("label.do_not_display_again"));
2239 jcb.addActionListener(new ActionListener()
2242 public void actionPerformed(ActionEvent e)
2244 // update Cache settings for "don't show this again"
2245 boolean showWarningAgain = !jcb.isSelected();
2246 Cache.setProperty("CHECKURLLINKS",
2247 Boolean.valueOf(showWarningAgain).toString());
2252 JvOptionPane.showMessageDialog(desktopPane, msgPanel,
2254 .getString("label.SEQUENCE_ID_no_longer_used"),
2255 JvOptionPane.WARNING_MESSAGE);
2262 * Proxy class for JDesktopPane which optionally displays the current memory
2263 * usage and highlights the desktop area with a red bar if free memory runs
2268 public class MyDesktopPane extends JDesktopPane implements Runnable
2270 private static final float ONE_MB = 1048576f;
2272 boolean showMemoryUsage = false;
2276 java.text.NumberFormat df;
2278 float maxMemory, allocatedMemory, freeMemory, totalFreeMemory,
2281 public MyDesktopPane(boolean showMemoryUsage)
2283 showMemoryUsage(showMemoryUsage);
2286 public void showMemoryUsage(boolean showMemory)
2288 this.showMemoryUsage = showMemory;
2291 Thread worker = new Thread(this);
2297 public boolean isShowMemoryUsage()
2299 return showMemoryUsage;
2305 df = java.text.NumberFormat.getNumberInstance();
2306 df.setMaximumFractionDigits(2);
2307 runtime = Runtime.getRuntime();
2309 while (showMemoryUsage)
2313 maxMemory = runtime.maxMemory() / ONE_MB;
2314 allocatedMemory = runtime.totalMemory() / ONE_MB;
2315 freeMemory = runtime.freeMemory() / ONE_MB;
2316 totalFreeMemory = freeMemory + (maxMemory - allocatedMemory);
2318 percentUsage = (totalFreeMemory / maxMemory) * 100;
2320 // if (percentUsage < 20)
2322 // border1 = BorderFactory.createMatteBorder(12, 12, 12, 12,
2324 // instance.set.setBorder(border1);
2327 // sleep after showing usage
2329 } catch (Exception ex)
2331 ex.printStackTrace();
2337 public void paintComponent(Graphics g)
2339 if (showMemoryUsage && g != null && df != null)
2341 if (percentUsage < 20)
2343 g.setColor(Color.red);
2345 FontMetrics fm = g.getFontMetrics();
2348 g.drawString(MessageManager.formatMessage("label.memory_stats",
2350 { df.format(totalFreeMemory), df.format(maxMemory),
2351 df.format(percentUsage) }),
2352 10, getHeight() - fm.getHeight());
2359 * Accessor method to quickly get all the AlignmentFrames loaded.
2361 * @return an array of AlignFrame, or null if none found
2363 public static AlignFrame[] getAlignFrames()
2365 if (Jalview.isHeadlessMode())
2367 return new AlignFrame[] { Jalview.getInstance().currentAlignFrame };
2370 JInternalFrame[] frames = getDesktopPane().getAllFrames();
2376 List<AlignFrame> avp = new ArrayList<>();
2378 for (int i = frames.length - 1; i > -1; i--)
2380 if (frames[i] instanceof AlignFrame)
2382 avp.add((AlignFrame) frames[i]);
2384 else if (frames[i] instanceof SplitFrame)
2387 * Also check for a split frame containing an AlignFrame
2389 GSplitFrame sf = (GSplitFrame) frames[i];
2390 if (sf.getTopFrame() instanceof AlignFrame)
2392 avp.add((AlignFrame) sf.getTopFrame());
2394 if (sf.getBottomFrame() instanceof AlignFrame)
2396 avp.add((AlignFrame) sf.getBottomFrame());
2400 if (avp.size() == 0)
2404 AlignFrame afs[] = avp.toArray(new AlignFrame[avp.size()]);
2409 * Returns an array of any AppJmol frames in the Desktop (or null if none).
2413 public GStructureViewer[] getJmols()
2415 JInternalFrame[] frames = desktopPane.getAllFrames();
2421 List<GStructureViewer> avp = new ArrayList<>();
2423 for (int i = frames.length - 1; i > -1; i--)
2425 if (frames[i] instanceof AppJmol)
2427 GStructureViewer af = (GStructureViewer) frames[i];
2431 if (avp.size() == 0)
2435 GStructureViewer afs[] = avp.toArray(new GStructureViewer[avp.size()]);
2440 * Add Groovy Support to Jalview
2443 public void groovyShell_actionPerformed()
2447 openGroovyConsole();
2448 } catch (Exception ex)
2450 Cache.log.error("Groovy Shell Creation failed.", ex);
2451 JvOptionPane.showInternalMessageDialog(desktopPane,
2453 MessageManager.getString("label.couldnt_create_groovy_shell"),
2454 MessageManager.getString("label.groovy_support_failed"),
2455 JvOptionPane.ERROR_MESSAGE);
2460 * Open the Groovy console
2462 void openGroovyConsole()
2464 if (groovyConsole == null)
2466 groovyConsole = new groovy.ui.Console();
2467 groovyConsole.setVariable("Jalview", this);
2468 groovyConsole.run();
2471 * We allow only one console at a time, so that AlignFrame menu option
2472 * 'Calculate | Run Groovy script' is unambiguous.
2473 * Disable 'Groovy Console', and enable 'Run script', when the console is
2474 * opened, and the reverse when it is closed
2476 Window window = (Window) groovyConsole.getFrame();
2477 window.addWindowListener(new WindowAdapter()
2480 public void windowClosed(WindowEvent e)
2483 * rebind CMD-Q from Groovy Console to Jalview Quit
2486 enableExecuteGroovy(false);
2492 * show Groovy console window (after close and reopen)
2494 ((Window) groovyConsole.getFrame()).setVisible(true);
2497 * if we got this far, enable 'Run Groovy' in AlignFrame menus
2498 * and disable opening a second console
2500 enableExecuteGroovy(true);
2504 * Bind Ctrl/Cmd-Q to Quit - for reset as Groovy Console takes over this
2505 * binding when opened
2507 protected void addQuitHandler()
2510 .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
2512 .getKeyStroke(KeyEvent.VK_Q,
2513 jalview.util.ShortcutKeyMaskExWrapper
2514 .getMenuShortcutKeyMaskEx()),
2516 getRootPane().getActionMap().put("Quit", new AbstractAction()
2519 public void actionPerformed(ActionEvent e)
2527 * Enable or disable 'Run Groovy script' in AlignFrame calculate menus
2530 * true if Groovy console is open
2532 public void enableExecuteGroovy(boolean enabled)
2535 * disable opening a second Groovy console
2536 * (or re-enable when the console is closed)
2538 groovyShell.setEnabled(!enabled);
2540 AlignFrame[] alignFrames = getAlignFrames();
2541 if (alignFrames != null)
2543 for (AlignFrame af : alignFrames)
2545 af.setGroovyEnabled(enabled);
2551 * Progress bars managed by the IProgressIndicator method.
2553 private Hashtable<Long, JPanel> progressBars;
2555 private Hashtable<Long, IProgressIndicatorHandler> progressBarHandlers;
2560 * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
2563 public void setProgressBar(String message, long id)
2565 // Platform.timeCheck("Desktop " + message, Platform.TIME_MARK);
2567 if (progressBars == null)
2569 progressBars = new Hashtable<>();
2570 progressBarHandlers = new Hashtable<>();
2573 if (progressBars.get(Long.valueOf(id)) != null)
2575 JPanel panel = progressBars.remove(Long.valueOf(id));
2576 if (progressBarHandlers.contains(Long.valueOf(id)))
2578 progressBarHandlers.remove(Long.valueOf(id));
2580 removeProgressPanel(panel);
2584 progressBars.put(Long.valueOf(id), addProgressPanel(message));
2591 * @see jalview.gui.IProgressIndicator#registerHandler(long,
2592 * jalview.gui.IProgressIndicatorHandler)
2595 public void registerHandler(final long id,
2596 final IProgressIndicatorHandler handler)
2598 if (progressBarHandlers == null
2599 || !progressBars.containsKey(Long.valueOf(id)))
2601 throw new Error(MessageManager.getString(
2602 "error.call_setprogressbar_before_registering_handler"));
2604 progressBarHandlers.put(Long.valueOf(id), handler);
2605 final JPanel progressPanel = progressBars.get(Long.valueOf(id));
2606 if (handler.canCancel())
2608 JButton cancel = new JButton(
2609 MessageManager.getString("action.cancel"));
2610 final IProgressIndicator us = this;
2611 cancel.addActionListener(new ActionListener()
2615 public void actionPerformed(ActionEvent e)
2617 handler.cancelActivity(id);
2618 us.setProgressBar(MessageManager
2619 .formatMessage("label.cancelled_params", new Object[]
2620 { ((JLabel) progressPanel.getComponent(0)).getText() }),
2624 progressPanel.add(cancel, BorderLayout.EAST);
2630 * @return true if any progress bars are still active
2633 public boolean operationInProgress()
2635 if (progressBars != null && progressBars.size() > 0)
2643 * This will return the first AlignFrame holding the given viewport instance.
2644 * It will break if there are more than one AlignFrames viewing a particular
2648 * @return alignFrame for viewport
2650 public static AlignFrame getAlignFrameFor(AlignViewportI viewport)
2652 if (getDesktopPane() != null)
2654 AlignmentPanel[] aps = getAlignmentPanels(
2655 viewport.getSequenceSetId());
2656 for (int panel = 0; aps != null && panel < aps.length; panel++)
2658 if (aps[panel] != null && aps[panel].av == viewport)
2660 return aps[panel].alignFrame;
2667 public VamsasApplication getVamsasApplication()
2669 // TODO: JAL-3311 remove remaining code from Jalview relating to VAMSAS
2675 * flag set if jalview GUI is being operated programmatically
2677 private boolean inBatchMode = false;
2680 * check if jalview GUI is being operated programmatically
2682 * @return inBatchMode
2684 public boolean isInBatchMode()
2690 * set flag if jalview GUI is being operated programmatically
2692 * @param inBatchMode
2694 public void setInBatchMode(boolean inBatchMode)
2696 this.inBatchMode = inBatchMode;
2699 public void startServiceDiscovery()
2701 startServiceDiscovery(false);
2704 public void startServiceDiscovery(boolean blocking)
2706 boolean alive = true;
2707 Thread t0 = null, t1 = null, t2 = null;
2708 // JAL-940 - JALVIEW 1 services are now being EOLed as of JABA 2.1 release
2711 // todo: changesupport handlers need to be transferred
2712 if (discoverer == null)
2714 discoverer = jalview.ws.jws1.Discoverer.getInstance();
2715 // register PCS handler for desktop.
2716 discoverer.addPropertyChangeListener(changeSupport);
2718 // JAL-940 - disabled JWS1 service configuration - always start discoverer
2719 // until we phase out completely
2720 (t0 = new Thread(discoverer)).start();
2723 if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
2725 t2 = jalview.ws.jws2.Jws2Discoverer.getInstance()
2726 .startDiscoverer(changeSupport);
2730 // TODO: do rest service discovery
2739 } catch (Exception e)
2742 alive = (t1 != null && t1.isAlive()) || (t2 != null && t2.isAlive())
2743 || (t3 != null && t3.isAlive())
2744 || (t0 != null && t0.isAlive());
2750 * called to check if the service discovery process completed successfully.
2754 protected void JalviewServicesChanged(PropertyChangeEvent evt)
2756 if (evt.getNewValue() == null || evt.getNewValue() instanceof Vector)
2758 final String ermsg = jalview.ws.jws2.Jws2Discoverer.getInstance()
2759 .getErrorMessages();
2762 if (Cache.getDefault("SHOW_WSDISCOVERY_ERRORS", true))
2764 if (serviceChangedDialog == null)
2766 // only run if we aren't already displaying one of these.
2767 addDialogThread(serviceChangedDialog = new Runnable()
2774 * JalviewDialog jd =new JalviewDialog() {
2776 * @Override protected void cancelPressed() { // TODO
2777 * Auto-generated method stub
2779 * }@Override protected void okPressed() { // TODO
2780 * Auto-generated method stub
2782 * }@Override protected void raiseClosed() { // TODO
2783 * Auto-generated method stub
2785 * } }; jd.initDialogFrame(new
2786 * JLabel("<html><table width=\"450\"><tr><td>" + ermsg +
2787 * "<br/>It may be that you have invalid JABA URLs in your web service preferences,"
2788 * + " or mis-configured HTTP proxy settings.<br/>" +
2789 * "Check the <em>Connections</em> and <em>Web services</em> tab of the"
2791 * " Tools->Preferences dialog box to change them.</td></tr></table></html>"
2792 * ), true, true, "Web Service Configuration Problem", 450,
2795 * jd.waitForInput();
2797 JvOptionPane.showConfirmDialog(desktopPane,
2798 new JLabel("<html><table width=\"450\"><tr><td>"
2799 + ermsg + "</td></tr></table>"
2800 + "<p>It may be that you have invalid JABA URLs<br/>in your web service preferences,"
2801 + "<br>or as a command-line argument, or mis-configured HTTP proxy settings.</p>"
2802 + "<p>Check the <em>Connections</em> and <em>Web services</em> tab<br/>of the"
2803 + " Tools->Preferences dialog box to change them.</p></html>"),
2804 "Web Service Configuration Problem",
2805 JvOptionPane.DEFAULT_OPTION,
2806 JvOptionPane.ERROR_MESSAGE);
2807 serviceChangedDialog = null;
2816 "Errors reported by JABA discovery service. Check web services preferences.\n"
2823 private Runnable serviceChangedDialog = null;
2826 * start a thread to open a URL in the configured browser. Pops up a warning
2827 * dialog to the user if there is an exception when calling out to the browser
2832 public static void showUrl(final String url)
2834 showUrl(url, getInstance());
2838 * Like showUrl but allows progress handler to be specified
2842 * (null) or object implementing IProgressIndicator
2844 public static void showUrl(final String url,
2845 final IProgressIndicator progress)
2847 new Thread(new Runnable()
2854 if (progress != null)
2856 progress.setProgressBar(MessageManager
2857 .formatMessage("status.opening_params", new Object[]
2858 { url }), this.hashCode());
2860 jalview.util.BrowserLauncher.openURL(url);
2861 } catch (Exception ex)
2863 JvOptionPane.showInternalMessageDialog(getDesktopPane(),
2865 .getString("label.web_browser_not_found_unix"),
2866 MessageManager.getString("label.web_browser_not_found"),
2867 JvOptionPane.WARNING_MESSAGE);
2869 ex.printStackTrace();
2871 if (progress != null)
2873 progress.setProgressBar(null, this.hashCode());
2879 public static WsParamSetManager wsparamManager = null;
2881 public static ParamManager getUserParameterStore()
2883 if (wsparamManager == null)
2885 wsparamManager = new WsParamSetManager();
2887 return wsparamManager;
2891 * static hyperlink handler proxy method for use by Jalview's internal windows
2895 public static void hyperlinkUpdate(HyperlinkEvent e)
2897 if (e.getEventType() == EventType.ACTIVATED)
2902 url = e.getURL().toString();
2904 } catch (Exception x)
2908 if (Cache.log != null)
2910 Cache.log.error("Couldn't handle string " + url + " as a URL.");
2915 "Couldn't handle string " + url + " as a URL.");
2918 // ignore any exceptions due to dud links.
2925 * single thread that handles display of dialogs to user.
2927 ExecutorService dialogExecutor = Executors.newSingleThreadExecutor();
2930 * flag indicating if dialogExecutor should try to acquire a permit
2932 private volatile boolean dialogPause = true;
2937 private java.util.concurrent.Semaphore block = new Semaphore(0);
2939 private static groovy.ui.Console groovyConsole;
2942 * add another dialog thread to the queue
2946 public void addDialogThread(final Runnable prompter)
2948 dialogExecutor.submit(new Runnable()
2958 } catch (InterruptedException x)
2962 if (Jalview.isHeadlessMode())
2968 SwingUtilities.invokeAndWait(prompter);
2969 } catch (Exception q)
2971 Cache.log.warn("Unexpected Exception in dialog thread.", q);
2977 public void startDialogQueue()
2979 // set the flag so we don't pause waiting for another permit and semaphore
2980 // the current task to begin
2981 dialogPause = false;
2986 * Outputs an image of the desktop to file in EPS format, after prompting the
2987 * user for choice of Text or Lineart character rendering (unless a preference
2988 * has been set). The file name is generated as
2991 * Jalview_snapshot_nnnnn.eps where nnnnn is the current timestamp in milliseconds
2995 protected void snapShotWindow_actionPerformed(ActionEvent e)
2997 // currently the menu option to do this is not shown
3000 int width = getWidth();
3001 int height = getHeight();
3003 "Jalview_snapshot_" + System.currentTimeMillis() + ".eps");
3004 ImageWriterI writer = new ImageWriterI()
3007 public void exportImage(Graphics g) throws Exception
3010 Cache.log.info("Successfully written snapshot to file "
3011 + of.getAbsolutePath());
3014 String title = "View of desktop";
3015 ImageExporter exporter = new ImageExporter(writer, null, TYPE.EPS,
3017 exporter.doExport(of, this, width, height, title);
3021 * Explode the views in the given SplitFrame into separate SplitFrame windows.
3022 * This respects (remembers) any previous 'exploded geometry' i.e. the size
3023 * and location last time the view was expanded (if any). However it does not
3024 * remember the split pane divider location - this is set to match the
3025 * 'exploding' frame.
3029 public void explodeViews(SplitFrame sf)
3031 AlignFrame oldTopFrame = (AlignFrame) sf.getTopFrame();
3032 AlignFrame oldBottomFrame = (AlignFrame) sf.getBottomFrame();
3033 List<? extends AlignmentViewPanel> topPanels = oldTopFrame
3035 List<? extends AlignmentViewPanel> bottomPanels = oldBottomFrame
3037 int viewCount = topPanels.size();
3044 * Processing in reverse order works, forwards order leaves the first panels
3045 * not visible. I don't know why!
3047 for (int i = viewCount - 1; i >= 0; i--)
3050 * Make new top and bottom frames. These take over the respective
3051 * AlignmentPanel objects, including their AlignmentViewports, so the
3052 * cdna/protein relationships between the viewports is carried over to the
3055 * explodedGeometry holds the (x, y) position of the previously exploded
3056 * SplitFrame, and the (width, height) of the AlignFrame component
3058 AlignmentPanel topPanel = (AlignmentPanel) topPanels.get(i);
3059 AlignFrame newTopFrame = new AlignFrame(topPanel);
3060 newTopFrame.setSize(oldTopFrame.getSize());
3061 newTopFrame.setVisible(true);
3062 Rectangle geometry = ((AlignViewport) topPanel.getAlignViewport())
3063 .getExplodedGeometry();
3064 if (geometry != null)
3066 newTopFrame.setSize(geometry.getSize());
3069 AlignmentPanel bottomPanel = (AlignmentPanel) bottomPanels.get(i);
3070 AlignFrame newBottomFrame = new AlignFrame(bottomPanel);
3071 newBottomFrame.setSize(oldBottomFrame.getSize());
3072 newBottomFrame.setVisible(true);
3073 geometry = ((AlignViewport) bottomPanel.getAlignViewport())
3074 .getExplodedGeometry();
3075 if (geometry != null)
3077 newBottomFrame.setSize(geometry.getSize());
3080 topPanel.av.setGatherViewsHere(false);
3081 bottomPanel.av.setGatherViewsHere(false);
3082 JInternalFrame splitFrame = new SplitFrame(newTopFrame,
3084 if (geometry != null)
3086 splitFrame.setLocation(geometry.getLocation());
3088 addInternalFrame(splitFrame, sf.getTitle(), -1, -1);
3092 * Clear references to the panels (now relocated in the new SplitFrames)
3093 * before closing the old SplitFrame.
3096 bottomPanels.clear();
3101 * Gather expanded split frames, sharing the same pairs of sequence set ids,
3102 * back into the given SplitFrame as additional views. Note that the gathered
3103 * frames may themselves have multiple views.
3107 public void gatherViews(GSplitFrame source)
3110 * special handling of explodedGeometry for a view within a SplitFrame: - it
3111 * holds the (x, y) position of the enclosing SplitFrame, and the (width,
3112 * height) of the AlignFrame component
3114 AlignFrame myTopFrame = (AlignFrame) source.getTopFrame();
3115 AlignFrame myBottomFrame = (AlignFrame) source.getBottomFrame();
3116 myTopFrame.viewport.setExplodedGeometry(new Rectangle(source.getX(),
3117 source.getY(), myTopFrame.getWidth(), myTopFrame.getHeight()));
3118 myBottomFrame.viewport
3119 .setExplodedGeometry(new Rectangle(source.getX(), source.getY(),
3120 myBottomFrame.getWidth(), myBottomFrame.getHeight()));
3121 myTopFrame.viewport.setGatherViewsHere(true);
3122 myBottomFrame.viewport.setGatherViewsHere(true);
3123 String topViewId = myTopFrame.viewport.getSequenceSetId();
3124 String bottomViewId = myBottomFrame.viewport.getSequenceSetId();
3126 JInternalFrame[] frames = desktopPane.getAllFrames();
3127 for (JInternalFrame frame : frames)
3129 if (frame instanceof SplitFrame && frame != source)
3131 SplitFrame sf = (SplitFrame) frame;
3132 AlignFrame topFrame = (AlignFrame) sf.getTopFrame();
3133 AlignFrame bottomFrame = (AlignFrame) sf.getBottomFrame();
3134 boolean gatherThis = false;
3135 for (int a = 0; a < topFrame.alignPanels.size(); a++)
3137 AlignmentPanel topPanel = topFrame.alignPanels.get(a);
3138 AlignmentPanel bottomPanel = bottomFrame.alignPanels.get(a);
3139 if (topViewId.equals(topPanel.av.getSequenceSetId())
3140 && bottomViewId.equals(bottomPanel.av.getSequenceSetId()))
3143 topPanel.av.setGatherViewsHere(false);
3144 bottomPanel.av.setGatherViewsHere(false);
3145 topPanel.av.setExplodedGeometry(
3146 new Rectangle(sf.getLocation(), topFrame.getSize()));
3147 bottomPanel.av.setExplodedGeometry(
3148 new Rectangle(sf.getLocation(), bottomFrame.getSize()));
3149 myTopFrame.addAlignmentPanel(topPanel, false);
3150 myBottomFrame.addAlignmentPanel(bottomPanel, false);
3156 topFrame.getAlignPanels().clear();
3157 bottomFrame.getAlignPanels().clear();
3164 * The dust settles...give focus to the tab we did this from.
3166 myTopFrame.setDisplayedView(myTopFrame.alignPanel);
3169 public static groovy.ui.Console getGroovyConsole()
3171 return groovyConsole;
3175 * handles the payload of a drag and drop event.
3177 * TODO refactor to desktop utilities class
3180 * - Data source strings extracted from the drop event
3182 * - protocol for each data source extracted from the drop event
3186 * - the payload from the drop event
3189 @SuppressWarnings("unchecked")
3190 public static void transferFromDropTarget(List<Object> files,
3191 List<DataSourceType> protocols, DropTargetDropEvent evt,
3192 Transferable t) throws Exception
3195 // BH 2018 changed List<String> to List<Object> to allow for File from
3198 // DataFlavor[] flavors = t.getTransferDataFlavors();
3199 // for (int i = 0; i < flavors.length; i++) {
3200 // if (flavors[i].isFlavorJavaFileListType()) {
3201 // evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
3202 // List<File> list = (List<File>) t.getTransferData(flavors[i]);
3203 // for (int j = 0; j < list.size(); j++) {
3204 // File file = (File) list.get(j);
3205 // byte[] data = getDroppedFileBytes(file);
3206 // fileName.setText(file.getName() + " - " + data.length + " " +
3207 // evt.getLocation());
3208 // JTextArea target = (JTextArea) ((DropTarget)
3209 // evt.getSource()).getComponent();
3210 // target.setText(new String(data));
3212 // dtde.dropComplete(true);
3217 DataFlavor uriListFlavor = new DataFlavor(
3218 "text/uri-list;class=java.lang.String"), urlFlavour = null;
3221 urlFlavour = new DataFlavor(
3222 "application/x-java-url; class=java.net.URL");
3223 } catch (ClassNotFoundException cfe)
3225 Cache.log.debug("Couldn't instantiate the URL dataflavor.", cfe);
3228 if (urlFlavour != null && t.isDataFlavorSupported(urlFlavour))
3233 java.net.URL url = (URL) t.getTransferData(urlFlavour);
3234 // nb: java 8 osx bug https://bugs.openjdk.java.net/browse/JDK-8156099
3235 // means url may be null.
3238 protocols.add(DataSourceType.URL);
3239 files.add(url.toString());
3240 Cache.log.debug("Drop handled as URL dataflavor "
3241 + files.get(files.size() - 1));
3246 if (Platform.isAMacAndNotJS())
3249 "Please ignore plist error - occurs due to problem with java 8 on OSX");
3252 } catch (Throwable ex)
3254 Cache.log.debug("URL drop handler failed.", ex);
3257 if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
3259 // Works on Windows and MacOSX
3260 Cache.log.debug("Drop handled as javaFileListFlavor");
3261 for (File file : (List<File>) t
3262 .getTransferData(DataFlavor.javaFileListFlavor))
3265 protocols.add(DataSourceType.FILE);
3270 // Unix like behaviour
3271 boolean added = false;
3273 if (t.isDataFlavorSupported(uriListFlavor))
3275 Cache.log.debug("Drop handled as uriListFlavor");
3276 // This is used by Unix drag system
3277 data = (String) t.getTransferData(uriListFlavor);
3281 // fallback to text: workaround - on OSX where there's a JVM bug
3282 Cache.log.debug("standard URIListFlavor failed. Trying text");
3283 // try text fallback
3284 DataFlavor textDf = new DataFlavor(
3285 "text/plain;class=java.lang.String");
3286 if (t.isDataFlavorSupported(textDf))
3288 data = (String) t.getTransferData(textDf);
3291 Cache.log.debug("Plain text drop content returned "
3292 + (data == null ? "Null - failed" : data));
3297 while (protocols.size() < files.size())
3299 Cache.log.debug("Adding missing FILE protocol for "
3300 + files.get(protocols.size()));
3301 protocols.add(DataSourceType.FILE);
3303 for (java.util.StringTokenizer st = new java.util.StringTokenizer(
3304 data, "\r\n"); st.hasMoreTokens();)
3307 String s = st.nextToken();
3308 if (s.startsWith("#"))
3310 // the line is a comment (as per the RFC 2483)
3313 java.net.URI uri = new java.net.URI(s);
3314 if (uri.getScheme().toLowerCase().startsWith("http"))
3316 protocols.add(DataSourceType.URL);
3317 files.add(uri.toString());
3321 // otherwise preserve old behaviour: catch all for file objects
3322 java.io.File file = new java.io.File(uri);
3323 protocols.add(DataSourceType.FILE);
3324 files.add(file.toString());
3329 if (Cache.log.isDebugEnabled())
3331 if (data == null || !added)
3334 if (t.getTransferDataFlavors() != null
3335 && t.getTransferDataFlavors().length > 0)
3338 "Couldn't resolve drop data. Here are the supported flavors:");
3339 for (DataFlavor fl : t.getTransferDataFlavors())
3342 "Supported transfer dataflavor: " + fl.toString());
3343 Object df = t.getTransferData(fl);
3346 Cache.log.debug("Retrieves: " + df);
3350 Cache.log.debug("Retrieved nothing");
3356 Cache.log.debug("Couldn't resolve dataflavor for drop: "
3362 if (Platform.isWindowsAndNotJS())
3364 Cache.log.debug("Scanning dropped content for Windows Link Files");
3366 // resolve any .lnk files in the file drop
3367 for (int f = 0; f < files.size(); f++)
3369 String source = files.get(f).toString().toLowerCase();
3370 if (protocols.get(f).equals(DataSourceType.FILE)
3371 && (source.endsWith(".lnk") || source.endsWith(".url")
3372 || source.endsWith(".site")))
3376 Object obj = files.get(f);
3377 File lf = (obj instanceof File ? (File) obj
3378 : new File((String) obj));
3379 // process link file to get a URL
3380 Cache.log.debug("Found potential link file: " + lf);
3381 WindowsShortcut wscfile = new WindowsShortcut(lf);
3382 String fullname = wscfile.getRealFilename();
3383 protocols.set(f, FormatAdapter.checkProtocol(fullname));
3384 files.set(f, fullname);
3385 Cache.log.debug("Parsed real filename " + fullname
3386 + " to extract protocol: " + protocols.get(f));
3387 } catch (Exception ex)
3390 "Couldn't parse " + files.get(f) + " as a link file.",
3399 * Sets the Preferences property for experimental features to True or False
3400 * depending on the state of the controlling menu item
3403 protected void showExperimental_actionPerformed(boolean selected)
3405 Cache.setProperty(EXPERIMENTAL_FEATURES, Boolean.toString(selected));
3409 * Answers a (possibly empty) list of any structure viewer frames (currently
3410 * for either Jmol or Chimera) which are currently open. This may optionally
3411 * be restricted to viewers of a specified class, or viewers linked to a
3412 * specified alignment panel.
3415 * if not null, only return viewers linked to this panel
3416 * @param structureViewerClass
3417 * if not null, only return viewers of this class
3420 public List<StructureViewerBase> getStructureViewers(
3421 AlignmentPanel apanel,
3422 Class<? extends StructureViewerBase> structureViewerClass)
3424 List<StructureViewerBase> result = new ArrayList<>();
3425 JInternalFrame[] frames = getAllFrames();
3427 for (JInternalFrame frame : frames)
3429 if (frame instanceof StructureViewerBase)
3431 if (structureViewerClass == null
3432 || structureViewerClass.isInstance(frame))
3435 || ((StructureViewerBase) frame).isLinkedWith(apanel))
3437 result.add((StructureViewerBase) frame);