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 Platform.SHORTCUT_KEY_MASK);
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 getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
2511 .put(KeyStroke.getKeyStroke(KeyEvent.VK_Q,
2512 Platform.SHORTCUT_KEY_MASK),
2514 getRootPane().getActionMap().put("Quit", new AbstractAction()
2517 public void actionPerformed(ActionEvent e)
2525 * Enable or disable 'Run Groovy script' in AlignFrame calculate menus
2528 * true if Groovy console is open
2530 public void enableExecuteGroovy(boolean enabled)
2533 * disable opening a second Groovy console
2534 * (or re-enable when the console is closed)
2536 groovyShell.setEnabled(!enabled);
2538 AlignFrame[] alignFrames = getAlignFrames();
2539 if (alignFrames != null)
2541 for (AlignFrame af : alignFrames)
2543 af.setGroovyEnabled(enabled);
2549 * Progress bars managed by the IProgressIndicator method.
2551 private Hashtable<Long, JPanel> progressBars;
2553 private Hashtable<Long, IProgressIndicatorHandler> progressBarHandlers;
2558 * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
2561 public void setProgressBar(String message, long id)
2563 // Platform.timeCheck("Desktop " + message, Platform.TIME_MARK);
2565 if (progressBars == null)
2567 progressBars = new Hashtable<>();
2568 progressBarHandlers = new Hashtable<>();
2571 if (progressBars.get(Long.valueOf(id)) != null)
2573 JPanel panel = progressBars.remove(Long.valueOf(id));
2574 if (progressBarHandlers.contains(Long.valueOf(id)))
2576 progressBarHandlers.remove(Long.valueOf(id));
2578 removeProgressPanel(panel);
2582 progressBars.put(Long.valueOf(id), addProgressPanel(message));
2589 * @see jalview.gui.IProgressIndicator#registerHandler(long,
2590 * jalview.gui.IProgressIndicatorHandler)
2593 public void registerHandler(final long id,
2594 final IProgressIndicatorHandler handler)
2596 if (progressBarHandlers == null
2597 || !progressBars.containsKey(Long.valueOf(id)))
2599 throw new Error(MessageManager.getString(
2600 "error.call_setprogressbar_before_registering_handler"));
2602 progressBarHandlers.put(Long.valueOf(id), handler);
2603 final JPanel progressPanel = progressBars.get(Long.valueOf(id));
2604 if (handler.canCancel())
2606 JButton cancel = new JButton(
2607 MessageManager.getString("action.cancel"));
2608 final IProgressIndicator us = this;
2609 cancel.addActionListener(new ActionListener()
2613 public void actionPerformed(ActionEvent e)
2615 handler.cancelActivity(id);
2616 us.setProgressBar(MessageManager
2617 .formatMessage("label.cancelled_params", new Object[]
2618 { ((JLabel) progressPanel.getComponent(0)).getText() }),
2622 progressPanel.add(cancel, BorderLayout.EAST);
2628 * @return true if any progress bars are still active
2631 public boolean operationInProgress()
2633 if (progressBars != null && progressBars.size() > 0)
2641 * This will return the first AlignFrame holding the given viewport instance.
2642 * It will break if there are more than one AlignFrames viewing a particular
2646 * @return alignFrame for viewport
2648 public static AlignFrame getAlignFrameFor(AlignViewportI viewport)
2650 if (getDesktopPane() != null)
2652 AlignmentPanel[] aps = getAlignmentPanels(
2653 viewport.getSequenceSetId());
2654 for (int panel = 0; aps != null && panel < aps.length; panel++)
2656 if (aps[panel] != null && aps[panel].av == viewport)
2658 return aps[panel].alignFrame;
2665 public VamsasApplication getVamsasApplication()
2667 // TODO: JAL-3311 remove remaining code from Jalview relating to VAMSAS
2673 * flag set if jalview GUI is being operated programmatically
2675 private boolean inBatchMode = false;
2678 * check if jalview GUI is being operated programmatically
2680 * @return inBatchMode
2682 public boolean isInBatchMode()
2688 * set flag if jalview GUI is being operated programmatically
2690 * @param inBatchMode
2692 public void setInBatchMode(boolean inBatchMode)
2694 this.inBatchMode = inBatchMode;
2697 public void startServiceDiscovery()
2699 startServiceDiscovery(false);
2702 public void startServiceDiscovery(boolean blocking)
2704 boolean alive = true;
2705 Thread t0 = null, t1 = null, t2 = null;
2706 // JAL-940 - JALVIEW 1 services are now being EOLed as of JABA 2.1 release
2709 // todo: changesupport handlers need to be transferred
2710 if (discoverer == null)
2712 discoverer = jalview.ws.jws1.Discoverer.getInstance();
2713 // register PCS handler for desktop.
2714 discoverer.addPropertyChangeListener(changeSupport);
2716 // JAL-940 - disabled JWS1 service configuration - always start discoverer
2717 // until we phase out completely
2718 (t0 = new Thread(discoverer)).start();
2721 if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
2723 t2 = jalview.ws.jws2.Jws2Discoverer.getInstance()
2724 .startDiscoverer(changeSupport);
2728 // TODO: do rest service discovery
2737 } catch (Exception e)
2740 alive = (t1 != null && t1.isAlive()) || (t2 != null && t2.isAlive())
2741 || (t3 != null && t3.isAlive())
2742 || (t0 != null && t0.isAlive());
2748 * called to check if the service discovery process completed successfully.
2752 protected void JalviewServicesChanged(PropertyChangeEvent evt)
2754 if (evt.getNewValue() == null || evt.getNewValue() instanceof Vector)
2756 final String ermsg = jalview.ws.jws2.Jws2Discoverer.getInstance()
2757 .getErrorMessages();
2760 if (Cache.getDefault("SHOW_WSDISCOVERY_ERRORS", true))
2762 if (serviceChangedDialog == null)
2764 // only run if we aren't already displaying one of these.
2765 addDialogThread(serviceChangedDialog = new Runnable()
2772 * JalviewDialog jd =new JalviewDialog() {
2774 * @Override protected void cancelPressed() { // TODO
2775 * Auto-generated method stub
2777 * }@Override protected void okPressed() { // TODO
2778 * Auto-generated method stub
2780 * }@Override protected void raiseClosed() { // TODO
2781 * Auto-generated method stub
2783 * } }; jd.initDialogFrame(new
2784 * JLabel("<html><table width=\"450\"><tr><td>" + ermsg +
2785 * "<br/>It may be that you have invalid JABA URLs in your web service preferences,"
2786 * + " or mis-configured HTTP proxy settings.<br/>" +
2787 * "Check the <em>Connections</em> and <em>Web services</em> tab of the"
2789 * " Tools->Preferences dialog box to change them.</td></tr></table></html>"
2790 * ), true, true, "Web Service Configuration Problem", 450,
2793 * jd.waitForInput();
2795 JvOptionPane.showConfirmDialog(desktopPane,
2796 new JLabel("<html><table width=\"450\"><tr><td>"
2797 + ermsg + "</td></tr></table>"
2798 + "<p>It may be that you have invalid JABA URLs<br/>in your web service preferences,"
2799 + "<br>or as a command-line argument, or mis-configured HTTP proxy settings.</p>"
2800 + "<p>Check the <em>Connections</em> and <em>Web services</em> tab<br/>of the"
2801 + " Tools->Preferences dialog box to change them.</p></html>"),
2802 "Web Service Configuration Problem",
2803 JvOptionPane.DEFAULT_OPTION,
2804 JvOptionPane.ERROR_MESSAGE);
2805 serviceChangedDialog = null;
2814 "Errors reported by JABA discovery service. Check web services preferences.\n"
2821 private Runnable serviceChangedDialog = null;
2824 * start a thread to open a URL in the configured browser. Pops up a warning
2825 * dialog to the user if there is an exception when calling out to the browser
2830 public static void showUrl(final String url)
2832 showUrl(url, getInstance());
2836 * Like showUrl but allows progress handler to be specified
2840 * (null) or object implementing IProgressIndicator
2842 public static void showUrl(final String url,
2843 final IProgressIndicator progress)
2845 new Thread(new Runnable()
2852 if (progress != null)
2854 progress.setProgressBar(MessageManager
2855 .formatMessage("status.opening_params", new Object[]
2856 { url }), this.hashCode());
2858 jalview.util.BrowserLauncher.openURL(url);
2859 } catch (Exception ex)
2861 JvOptionPane.showInternalMessageDialog(getDesktopPane(),
2863 .getString("label.web_browser_not_found_unix"),
2864 MessageManager.getString("label.web_browser_not_found"),
2865 JvOptionPane.WARNING_MESSAGE);
2867 ex.printStackTrace();
2869 if (progress != null)
2871 progress.setProgressBar(null, this.hashCode());
2877 public static WsParamSetManager wsparamManager = null;
2879 public static ParamManager getUserParameterStore()
2881 if (wsparamManager == null)
2883 wsparamManager = new WsParamSetManager();
2885 return wsparamManager;
2889 * static hyperlink handler proxy method for use by Jalview's internal windows
2893 public static void hyperlinkUpdate(HyperlinkEvent e)
2895 if (e.getEventType() == EventType.ACTIVATED)
2900 url = e.getURL().toString();
2902 } catch (Exception x)
2906 if (Cache.log != null)
2908 Cache.log.error("Couldn't handle string " + url + " as a URL.");
2913 "Couldn't handle string " + url + " as a URL.");
2916 // ignore any exceptions due to dud links.
2923 * single thread that handles display of dialogs to user.
2925 ExecutorService dialogExecutor = Executors.newSingleThreadExecutor();
2928 * flag indicating if dialogExecutor should try to acquire a permit
2930 private volatile boolean dialogPause = true;
2935 private java.util.concurrent.Semaphore block = new Semaphore(0);
2937 private static groovy.ui.Console groovyConsole;
2940 * add another dialog thread to the queue
2944 public void addDialogThread(final Runnable prompter)
2946 dialogExecutor.submit(new Runnable()
2956 } catch (InterruptedException x)
2960 if (Jalview.isHeadlessMode())
2966 SwingUtilities.invokeAndWait(prompter);
2967 } catch (Exception q)
2969 Cache.log.warn("Unexpected Exception in dialog thread.", q);
2975 public void startDialogQueue()
2977 // set the flag so we don't pause waiting for another permit and semaphore
2978 // the current task to begin
2979 dialogPause = false;
2984 * Outputs an image of the desktop to file in EPS format, after prompting the
2985 * user for choice of Text or Lineart character rendering (unless a preference
2986 * has been set). The file name is generated as
2989 * Jalview_snapshot_nnnnn.eps where nnnnn is the current timestamp in milliseconds
2993 protected void snapShotWindow_actionPerformed(ActionEvent e)
2995 // currently the menu option to do this is not shown
2998 int width = getWidth();
2999 int height = getHeight();
3001 "Jalview_snapshot_" + System.currentTimeMillis() + ".eps");
3002 ImageWriterI writer = new ImageWriterI()
3005 public void exportImage(Graphics g) throws Exception
3008 Cache.log.info("Successfully written snapshot to file "
3009 + of.getAbsolutePath());
3012 String title = "View of desktop";
3013 ImageExporter exporter = new ImageExporter(writer, null, TYPE.EPS,
3015 exporter.doExport(of, this, width, height, title);
3019 * Explode the views in the given SplitFrame into separate SplitFrame windows.
3020 * This respects (remembers) any previous 'exploded geometry' i.e. the size
3021 * and location last time the view was expanded (if any). However it does not
3022 * remember the split pane divider location - this is set to match the
3023 * 'exploding' frame.
3027 public void explodeViews(SplitFrame sf)
3029 AlignFrame oldTopFrame = (AlignFrame) sf.getTopFrame();
3030 AlignFrame oldBottomFrame = (AlignFrame) sf.getBottomFrame();
3031 List<? extends AlignmentViewPanel> topPanels = oldTopFrame
3033 List<? extends AlignmentViewPanel> bottomPanels = oldBottomFrame
3035 int viewCount = topPanels.size();
3042 * Processing in reverse order works, forwards order leaves the first panels
3043 * not visible. I don't know why!
3045 for (int i = viewCount - 1; i >= 0; i--)
3048 * Make new top and bottom frames. These take over the respective
3049 * AlignmentPanel objects, including their AlignmentViewports, so the
3050 * cdna/protein relationships between the viewports is carried over to the
3053 * explodedGeometry holds the (x, y) position of the previously exploded
3054 * SplitFrame, and the (width, height) of the AlignFrame component
3056 AlignmentPanel topPanel = (AlignmentPanel) topPanels.get(i);
3057 AlignFrame newTopFrame = new AlignFrame(topPanel);
3058 newTopFrame.setSize(oldTopFrame.getSize());
3059 newTopFrame.setVisible(true);
3060 Rectangle geometry = ((AlignViewport) topPanel.getAlignViewport())
3061 .getExplodedGeometry();
3062 if (geometry != null)
3064 newTopFrame.setSize(geometry.getSize());
3067 AlignmentPanel bottomPanel = (AlignmentPanel) bottomPanels.get(i);
3068 AlignFrame newBottomFrame = new AlignFrame(bottomPanel);
3069 newBottomFrame.setSize(oldBottomFrame.getSize());
3070 newBottomFrame.setVisible(true);
3071 geometry = ((AlignViewport) bottomPanel.getAlignViewport())
3072 .getExplodedGeometry();
3073 if (geometry != null)
3075 newBottomFrame.setSize(geometry.getSize());
3078 topPanel.av.setGatherViewsHere(false);
3079 bottomPanel.av.setGatherViewsHere(false);
3080 JInternalFrame splitFrame = new SplitFrame(newTopFrame,
3082 if (geometry != null)
3084 splitFrame.setLocation(geometry.getLocation());
3086 addInternalFrame(splitFrame, sf.getTitle(), -1, -1);
3090 * Clear references to the panels (now relocated in the new SplitFrames)
3091 * before closing the old SplitFrame.
3094 bottomPanels.clear();
3099 * Gather expanded split frames, sharing the same pairs of sequence set ids,
3100 * back into the given SplitFrame as additional views. Note that the gathered
3101 * frames may themselves have multiple views.
3105 public void gatherViews(GSplitFrame source)
3108 * special handling of explodedGeometry for a view within a SplitFrame: - it
3109 * holds the (x, y) position of the enclosing SplitFrame, and the (width,
3110 * height) of the AlignFrame component
3112 AlignFrame myTopFrame = (AlignFrame) source.getTopFrame();
3113 AlignFrame myBottomFrame = (AlignFrame) source.getBottomFrame();
3114 myTopFrame.viewport.setExplodedGeometry(new Rectangle(source.getX(),
3115 source.getY(), myTopFrame.getWidth(), myTopFrame.getHeight()));
3116 myBottomFrame.viewport
3117 .setExplodedGeometry(new Rectangle(source.getX(), source.getY(),
3118 myBottomFrame.getWidth(), myBottomFrame.getHeight()));
3119 myTopFrame.viewport.setGatherViewsHere(true);
3120 myBottomFrame.viewport.setGatherViewsHere(true);
3121 String topViewId = myTopFrame.viewport.getSequenceSetId();
3122 String bottomViewId = myBottomFrame.viewport.getSequenceSetId();
3124 JInternalFrame[] frames = desktopPane.getAllFrames();
3125 for (JInternalFrame frame : frames)
3127 if (frame instanceof SplitFrame && frame != source)
3129 SplitFrame sf = (SplitFrame) frame;
3130 AlignFrame topFrame = (AlignFrame) sf.getTopFrame();
3131 AlignFrame bottomFrame = (AlignFrame) sf.getBottomFrame();
3132 boolean gatherThis = false;
3133 for (int a = 0; a < topFrame.alignPanels.size(); a++)
3135 AlignmentPanel topPanel = topFrame.alignPanels.get(a);
3136 AlignmentPanel bottomPanel = bottomFrame.alignPanels.get(a);
3137 if (topViewId.equals(topPanel.av.getSequenceSetId())
3138 && bottomViewId.equals(bottomPanel.av.getSequenceSetId()))
3141 topPanel.av.setGatherViewsHere(false);
3142 bottomPanel.av.setGatherViewsHere(false);
3143 topPanel.av.setExplodedGeometry(
3144 new Rectangle(sf.getLocation(), topFrame.getSize()));
3145 bottomPanel.av.setExplodedGeometry(
3146 new Rectangle(sf.getLocation(), bottomFrame.getSize()));
3147 myTopFrame.addAlignmentPanel(topPanel, false);
3148 myBottomFrame.addAlignmentPanel(bottomPanel, false);
3154 topFrame.getAlignPanels().clear();
3155 bottomFrame.getAlignPanels().clear();
3162 * The dust settles...give focus to the tab we did this from.
3164 myTopFrame.setDisplayedView(myTopFrame.alignPanel);
3167 public static groovy.ui.Console getGroovyConsole()
3169 return groovyConsole;
3173 * handles the payload of a drag and drop event.
3175 * TODO refactor to desktop utilities class
3178 * - Data source strings extracted from the drop event
3180 * - protocol for each data source extracted from the drop event
3184 * - the payload from the drop event
3187 @SuppressWarnings("unchecked")
3188 public static void transferFromDropTarget(List<Object> files,
3189 List<DataSourceType> protocols, DropTargetDropEvent evt,
3190 Transferable t) throws Exception
3193 // BH 2018 changed List<String> to List<Object> to allow for File from
3196 // DataFlavor[] flavors = t.getTransferDataFlavors();
3197 // for (int i = 0; i < flavors.length; i++) {
3198 // if (flavors[i].isFlavorJavaFileListType()) {
3199 // evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
3200 // List<File> list = (List<File>) t.getTransferData(flavors[i]);
3201 // for (int j = 0; j < list.size(); j++) {
3202 // File file = (File) list.get(j);
3203 // byte[] data = getDroppedFileBytes(file);
3204 // fileName.setText(file.getName() + " - " + data.length + " " +
3205 // evt.getLocation());
3206 // JTextArea target = (JTextArea) ((DropTarget)
3207 // evt.getSource()).getComponent();
3208 // target.setText(new String(data));
3210 // dtde.dropComplete(true);
3215 DataFlavor uriListFlavor = new DataFlavor(
3216 "text/uri-list;class=java.lang.String"), urlFlavour = null;
3219 urlFlavour = new DataFlavor(
3220 "application/x-java-url; class=java.net.URL");
3221 } catch (ClassNotFoundException cfe)
3223 Cache.log.debug("Couldn't instantiate the URL dataflavor.", cfe);
3226 if (urlFlavour != null && t.isDataFlavorSupported(urlFlavour))
3231 java.net.URL url = (URL) t.getTransferData(urlFlavour);
3232 // nb: java 8 osx bug https://bugs.openjdk.java.net/browse/JDK-8156099
3233 // means url may be null.
3236 protocols.add(DataSourceType.URL);
3237 files.add(url.toString());
3238 Cache.log.debug("Drop handled as URL dataflavor "
3239 + files.get(files.size() - 1));
3244 if (Platform.isAMacAndNotJS())
3247 "Please ignore plist error - occurs due to problem with java 8 on OSX");
3250 } catch (Throwable ex)
3252 Cache.log.debug("URL drop handler failed.", ex);
3255 if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
3257 // Works on Windows and MacOSX
3258 Cache.log.debug("Drop handled as javaFileListFlavor");
3259 for (File file : (List<File>) t
3260 .getTransferData(DataFlavor.javaFileListFlavor))
3263 protocols.add(DataSourceType.FILE);
3268 // Unix like behaviour
3269 boolean added = false;
3271 if (t.isDataFlavorSupported(uriListFlavor))
3273 Cache.log.debug("Drop handled as uriListFlavor");
3274 // This is used by Unix drag system
3275 data = (String) t.getTransferData(uriListFlavor);
3279 // fallback to text: workaround - on OSX where there's a JVM bug
3280 Cache.log.debug("standard URIListFlavor failed. Trying text");
3281 // try text fallback
3282 DataFlavor textDf = new DataFlavor(
3283 "text/plain;class=java.lang.String");
3284 if (t.isDataFlavorSupported(textDf))
3286 data = (String) t.getTransferData(textDf);
3289 Cache.log.debug("Plain text drop content returned "
3290 + (data == null ? "Null - failed" : data));
3295 while (protocols.size() < files.size())
3297 Cache.log.debug("Adding missing FILE protocol for "
3298 + files.get(protocols.size()));
3299 protocols.add(DataSourceType.FILE);
3301 for (java.util.StringTokenizer st = new java.util.StringTokenizer(
3302 data, "\r\n"); st.hasMoreTokens();)
3305 String s = st.nextToken();
3306 if (s.startsWith("#"))
3308 // the line is a comment (as per the RFC 2483)
3311 java.net.URI uri = new java.net.URI(s);
3312 if (uri.getScheme().toLowerCase().startsWith("http"))
3314 protocols.add(DataSourceType.URL);
3315 files.add(uri.toString());
3319 // otherwise preserve old behaviour: catch all for file objects
3320 java.io.File file = new java.io.File(uri);
3321 protocols.add(DataSourceType.FILE);
3322 files.add(file.toString());
3327 if (Cache.log.isDebugEnabled())
3329 if (data == null || !added)
3332 if (t.getTransferDataFlavors() != null
3333 && t.getTransferDataFlavors().length > 0)
3336 "Couldn't resolve drop data. Here are the supported flavors:");
3337 for (DataFlavor fl : t.getTransferDataFlavors())
3340 "Supported transfer dataflavor: " + fl.toString());
3341 Object df = t.getTransferData(fl);
3344 Cache.log.debug("Retrieves: " + df);
3348 Cache.log.debug("Retrieved nothing");
3354 Cache.log.debug("Couldn't resolve dataflavor for drop: "
3360 if (Platform.isWindowsAndNotJS())
3362 Cache.log.debug("Scanning dropped content for Windows Link Files");
3364 // resolve any .lnk files in the file drop
3365 for (int f = 0; f < files.size(); f++)
3367 String source = files.get(f).toString().toLowerCase();
3368 if (protocols.get(f).equals(DataSourceType.FILE)
3369 && (source.endsWith(".lnk") || source.endsWith(".url")
3370 || source.endsWith(".site")))
3374 Object obj = files.get(f);
3375 File lf = (obj instanceof File ? (File) obj
3376 : new File((String) obj));
3377 // process link file to get a URL
3378 Cache.log.debug("Found potential link file: " + lf);
3379 WindowsShortcut wscfile = new WindowsShortcut(lf);
3380 String fullname = wscfile.getRealFilename();
3381 protocols.set(f, FormatAdapter.checkProtocol(fullname));
3382 files.set(f, fullname);
3383 Cache.log.debug("Parsed real filename " + fullname
3384 + " to extract protocol: " + protocols.get(f));
3385 } catch (Exception ex)
3388 "Couldn't parse " + files.get(f) + " as a link file.",
3397 * Sets the Preferences property for experimental features to True or False
3398 * depending on the state of the controlling menu item
3401 protected void showExperimental_actionPerformed(boolean selected)
3403 Cache.setProperty(EXPERIMENTAL_FEATURES, Boolean.toString(selected));
3407 * Answers a (possibly empty) list of any structure viewer frames (currently
3408 * for either Jmol or Chimera) which are currently open. This may optionally
3409 * be restricted to viewers of a specified class, or viewers linked to a
3410 * specified alignment panel.
3413 * if not null, only return viewers linked to this panel
3414 * @param structureViewerClass
3415 * if not null, only return viewers of this class
3418 public List<StructureViewerBase> getStructureViewers(
3419 AlignmentPanel apanel,
3420 Class<? extends StructureViewerBase> structureViewerClass)
3422 List<StructureViewerBase> result = new ArrayList<>();
3423 JInternalFrame[] frames = getAllFrames();
3425 for (JInternalFrame frame : frames)
3427 if (frame instanceof StructureViewerBase)
3429 if (structureViewerClass == null
3430 || structureViewerClass.isInstance(frame))
3433 || ((StructureViewerBase) frame).isLinkedWith(apanel))
3435 result.add((StructureViewerBase) frame);