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);
567 } catch (Throwable t)
575 * Answers true if user preferences to enable experimental features is True
580 public boolean showExperimental()
582 String experimental = Cache.getDefault(EXPERIMENTAL_FEATURES,
583 Boolean.FALSE.toString());
584 return Boolean.valueOf(experimental).booleanValue();
587 public void doConfigureStructurePrefs()
589 // configure services
590 StructureSelectionManager ssm = StructureSelectionManager
591 .getStructureSelectionManager(this);
592 if (Cache.getDefault(Preferences.ADD_SS_ANN, true))
594 ssm.setAddTempFacAnnot(
595 Cache.getDefault(Preferences.ADD_TEMPFACT_ANN, true));
596 ssm.setProcessSecondaryStructure(
597 Cache.getDefault(Preferences.STRUCT_FROM_PDB, true));
598 ssm.setSecStructServices(
599 Cache.getDefault(Preferences.USE_RNAVIEW, true));
603 ssm.setAddTempFacAnnot(false);
604 ssm.setProcessSecondaryStructure(false);
605 ssm.setSecStructServices(false);
609 public void checkForNews()
611 final Desktop me = this;
612 // Thread off the news reader, in case there are connection problems.
613 new Thread(new Runnable()
618 Cache.log.debug("Starting news thread.");
619 jvnews = new BlogReader(me);
620 showNews.setVisible(true);
621 Cache.log.debug("Completed news thread.");
626 public void getIdentifiersOrgData()
628 // Thread off the identifiers fetcher
629 new Thread(new Runnable()
634 Cache.log.debug("Downloading data from identifiers.org");
637 UrlDownloadClient.download(IdOrgSettings.getUrl(),
638 IdOrgSettings.getDownloadLocation());
639 } catch (IOException e)
641 Cache.log.debug("Exception downloading identifiers.org data"
650 protected void showNews_actionPerformed(ActionEvent e)
652 showNews(showNews.isSelected());
655 void showNews(boolean visible)
657 Cache.log.debug((visible ? "Showing" : "Hiding") + " news.");
658 showNews.setSelected(visible);
659 if (visible && !jvnews.isVisible())
661 new Thread(new Runnable()
666 long now = System.currentTimeMillis();
667 setProgressBar(MessageManager.getString("status.refreshing_news"),
669 jvnews.refreshNews();
670 setProgressBar(null, now);
678 * recover the last known dimensions for a jalview window
681 * - empty string is desktop, all other windows have unique prefix
682 * @return null or last known dimensions scaled to current geometry (if last
683 * window geom was known)
685 Rectangle getLastKnownDimensions(String windowName)
687 // TODO: lock aspect ratio for scaling desktop Bug #0058199
688 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
689 String x = Cache.getProperty(windowName + "SCREEN_X");
690 String y = Cache.getProperty(windowName + "SCREEN_Y");
691 String width = Cache.getProperty(windowName + "SCREEN_WIDTH");
692 String height = Cache.getProperty(windowName + "SCREEN_HEIGHT");
693 if ((x != null) && (y != null) && (width != null) && (height != null))
695 int ix = Integer.parseInt(x), iy = Integer.parseInt(y),
696 iw = Integer.parseInt(width), ih = Integer.parseInt(height);
697 if (Cache.getProperty("SCREENGEOMETRY_WIDTH") != null)
699 // attempt #1 - try to cope with change in screen geometry - this
700 // version doesn't preserve original jv aspect ratio.
701 // take ratio of current screen size vs original screen size.
702 double sw = ((1f * screenSize.width) / (1f * Integer
703 .parseInt(Cache.getProperty("SCREENGEOMETRY_WIDTH"))));
704 double sh = ((1f * screenSize.height) / (1f * Integer
705 .parseInt(Cache.getProperty("SCREENGEOMETRY_HEIGHT"))));
706 // rescale the bounds depending upon the current screen geometry.
707 ix = (int) (ix * sw);
708 iw = (int) (iw * sw);
709 iy = (int) (iy * sh);
710 ih = (int) (ih * sh);
711 while (ix >= screenSize.width)
714 "Window geometry location recall error: shifting horizontal to within screenbounds.");
715 ix -= screenSize.width;
717 while (iy >= screenSize.height)
720 "Window geometry location recall error: shifting vertical to within screenbounds.");
721 iy -= screenSize.height;
724 "Got last known dimensions for " + windowName + ": x:" + ix
725 + " y:" + iy + " width:" + iw + " height:" + ih);
727 // return dimensions for new instance
728 return new Rectangle(ix, iy, iw, ih);
733 void showPasteMenu(int x, int y)
735 JPopupMenu popup = new JPopupMenu();
736 JMenuItem item = new JMenuItem(
737 MessageManager.getString("label.paste_new_window"));
738 item.addActionListener(new ActionListener()
741 public void actionPerformed(ActionEvent evt)
748 popup.show(this, x, y);
755 Clipboard c = Toolkit.getDefaultToolkit().getSystemClipboard();
756 Transferable contents = c.getContents(this);
758 if (contents != null)
760 String file = (String) contents
761 .getTransferData(DataFlavor.stringFlavor);
763 FileFormatI format = new IdentifyFile().identify(file,
764 DataSourceType.PASTE);
766 new FileLoader().LoadFile(file, DataSourceType.PASTE, format);
769 } catch (Exception ex)
772 "Unable to paste alignment from system clipboard:\n" + ex);
777 // * Add an internal frame to the Jalview desktop that is allowed to be resized,
778 // * has a minimum size of 300px and might or might not be visible
784 // * @param makeVisible
785 // * When true, display frame immediately, otherwise, caller must call
786 // * setVisible themselves.
793 // public static synchronized void addInternalFrame(
794 // final JInternalFrame frame, String title, boolean makeVisible,
797 // // textbox, web services, sequenceFetcher, featureSettings
798 // getInstance().addFrame(frame, title, makeVisible, w, h,
799 // FRAME_ALLOW_RESIZE, FRAME_SET_MIN_SIZE_300);
803 // * Add an internal frame to the Jalview desktop that is visible, has a minimum
804 // * size of 300px, and may or may not be resizable
814 // * @param resizable
818 // public static synchronized void addInternalFrame(
819 // final JInternalFrame frame, String title, int w, int h,
820 // boolean resizable)
822 // // annotation, font, calculation, user-defined colors
823 // getInstance().addFrame(frame, title, FRAME_MAKE_VISIBLE, w, h,
824 // resizable, FRAME_SET_MIN_SIZE_300);
828 * Adds and opens the given frame to the desktop that is visible, allowed to
829 * resize, and has a 300px minimum width.
840 public static synchronized void addInternalFrame(
841 final JInternalFrame frame, String title, int w, int h)
844 getInstance().addFrame(frame, title, Desktop.FRAME_MAKE_VISIBLE, w, h,
845 FRAME_ALLOW_RESIZE, FRAME_SET_MIN_SIZE_300);
849 * Add an internal frame to the Jalview desktop that may optionally be
850 * visible, resizable, and allowed to be any size
857 * When true, display frame immediately, otherwise, caller must call
858 * setVisible themselves.
865 * @param ignoreMinSize
866 * Do not set the default minimum size for frame
868 public static synchronized void addInternalFrame(
869 final JInternalFrame frame, String title, boolean makeVisible,
870 int w, int h, boolean resizable, boolean ignoreMinSize)
873 getInstance().addFrame(frame, title, makeVisible, w, h, resizable,
877 // These can now by put into a single int flag, if desired:
879 public final static boolean FRAME_MAKE_VISIBLE = true;
881 public final static boolean FRAME_NOT_VISIBLE = false;
883 public final static boolean FRAME_ALLOW_RESIZE = true;
885 public final static boolean FRAME_NOT_RESIZABLE = false;
887 public final static boolean FRAME_ALLOW_ANY_SIZE = true;
889 public final static boolean FRAME_SET_MIN_SIZE_300 = false;
891 private void addFrame(JInternalFrame frame, String title,
892 boolean makeVisible, int w, int h, boolean resizable,
893 boolean ignoreMinSize)
895 // TODO: allow callers to determine X and Y position of frame (eg. via
897 // TODO: consider fixing method to update entries in the window submenu with
898 // the current window title
900 frame.setTitle(title);
901 if (frame.getWidth() < 1 || frame.getHeight() < 1)
905 // THIS IS A PUBLIC STATIC METHOD, SO IT MAY BE CALLED EVEN IN
906 // A HEADLESS STATE WHEN NO DESKTOP EXISTS. MUST RETURN
907 // IF JALVIEW IS RUNNING HEADLESS
908 // ///////////////////////////////////////////////
909 if (Jalview.isHeadlessMode())
916 boolean isEmbedded = (Platform.getEmbeddedAttribute(frame, "id") != null);
917 boolean hasEmbeddedSize = (Platform.getDimIfEmbedded(frame, -1, -1) != null);
918 // Web page embedding allows us to ignore minimum size
919 ignoreMinSize |= hasEmbeddedSize;
923 // Set default dimension for Alignment Frame window.
924 // The Alignment Frame window could be added from a number of places,
926 // I did this here in order not to miss out on any Alignment frame.
927 if (frame instanceof AlignFrame)
929 frame.setMinimumSize(new Dimension(ALIGN_FRAME_DEFAULT_MIN_WIDTH,
930 ALIGN_FRAME_DEFAULT_MIN_HEIGHT));
932 frame.setMinimumSize(
933 new Dimension(DEFAULT_MIN_WIDTH, DEFAULT_MIN_HEIGHT));
938 frame.setVisible(makeVisible);
939 frame.setClosable(true);
940 frame.setResizable(resizable);
941 frame.setMaximizable(resizable);
942 frame.setIconifiable(resizable);
943 frame.setOpaque(Platform.isJS());
944 if (!isEmbedded && frame.getX() < 1 && frame.getY() < 1)
946 frame.setLocation(xOffset * openFrameCount,
947 yOffset * ((openFrameCount - 1) % 10) + yOffset);
951 * add an entry for the new frame in the Window menu
952 * (and remove it when the frame is closed)
954 final JMenuItem menuItem = new JMenuItem(title);
955 frame.addInternalFrameListener(new InternalFrameAdapter()
958 public void internalFrameActivated(InternalFrameEvent evt)
960 JInternalFrame itf = getDesktopPane().getSelectedFrame();
963 if (itf instanceof AlignFrame)
965 Jalview.setCurrentAlignFrame((AlignFrame) itf);
972 public void internalFrameClosed(InternalFrameEvent evt)
974 PaintRefresher.RemoveComponent(frame);
977 * defensive check to prevent frames being
978 * added half off the window
980 if (openFrameCount > 0)
986 * ensure no reference to alignFrame retained by menu item listener
988 if (menuItem.getActionListeners().length > 0)
990 menuItem.removeActionListener(menuItem.getActionListeners()[0]);
992 getInstance().windowMenu.remove(menuItem);
996 menuItem.addActionListener(new ActionListener()
999 public void actionPerformed(ActionEvent e)
1003 frame.setSelected(true);
1004 frame.setIcon(false);
1005 } catch (java.beans.PropertyVetoException ex)
1007 // System.err.println(ex.toString());
1012 setKeyBindings(frame);
1014 getDesktopPane().add(frame);
1016 getInstance().windowMenu.add(menuItem);
1021 frame.setSelected(true);
1022 frame.requestFocus();
1023 } catch (java.beans.PropertyVetoException ve)
1025 } catch (java.lang.ClassCastException cex)
1028 "Squashed a possible GUI implementation error. If you can recreate this, please look at http://issues.jalview.org/browse/JAL-869",
1034 * Add key bindings to a JInternalFrame so that Ctrl-W and Cmd-W will close
1039 private static void setKeyBindings(JInternalFrame frame)
1041 final Action closeAction = new AbstractAction()
1044 public void actionPerformed(ActionEvent e)
1051 * set up key bindings for Ctrl-W and Cmd-W, with the same (Close) action
1053 KeyStroke ctrlWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
1054 InputEvent.CTRL_DOWN_MASK);
1055 KeyStroke cmdWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
1056 Platform.SHORTCUT_KEY_MASK);
1058 InputMap inputMap = frame
1059 .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
1060 String ctrlW = ctrlWKey.toString();
1061 inputMap.put(ctrlWKey, ctrlW);
1062 inputMap.put(cmdWKey, ctrlW);
1064 ActionMap actionMap = frame.getActionMap();
1065 actionMap.put(ctrlW, closeAction);
1069 public void lostOwnership(Clipboard clipboard, Transferable contents)
1073 jalviewClipboard = null;
1076 internalCopy = false;
1080 public void dragEnter(DropTargetDragEvent evt)
1085 public void dragExit(DropTargetEvent evt)
1090 public void dragOver(DropTargetDragEvent evt)
1095 public void dropActionChanged(DropTargetDragEvent evt)
1106 public void drop(DropTargetDropEvent evt)
1108 boolean success = true;
1109 // JAL-1552 - acceptDrop required before getTransferable call for
1110 // Java's Transferable for native dnd
1111 evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
1112 Transferable t = evt.getTransferable();
1113 List<Object> files = new ArrayList<>();
1114 List<DataSourceType> protocols = new ArrayList<>();
1118 transferFromDropTarget(files, protocols, evt, t);
1119 } catch (Exception e)
1121 e.printStackTrace();
1129 for (int i = 0; i < files.size(); i++)
1131 // BH 2018 File or String
1132 Object file = files.get(i);
1133 String fileName = file.toString();
1134 DataSourceType protocol = (protocols == null)
1135 ? DataSourceType.FILE
1137 FileFormatI format = null;
1139 if (fileName.endsWith(".jar"))
1141 format = FileFormat.Jalview;
1146 format = new IdentifyFile().identify(file, protocol);
1148 if (file instanceof File)
1150 Platform.cacheFileData((File) file);
1152 new FileLoader().LoadFile(null, file, protocol, format);
1155 } catch (Exception ex)
1160 evt.dropComplete(success); // need this to ensure input focus is properly
1161 // transfered to any new windows created
1171 public void inputLocalFileMenuItem_actionPerformed(AlignViewport viewport)
1173 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
1174 JalviewFileChooser chooser = JalviewFileChooser.forRead(
1175 Cache.getProperty("LAST_DIRECTORY"), fileFormat,
1176 BackupFiles.getEnabled());
1178 chooser.setFileView(new JalviewFileView());
1179 chooser.setDialogTitle(
1180 MessageManager.getString("label.open_local_file"));
1181 chooser.setToolTipText(MessageManager.getString("action.open"));
1183 chooser.setResponseHandler(0, new Runnable()
1188 File selectedFile = chooser.getSelectedFile();
1189 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1191 FileFormatI format = chooser.getSelectedFormat();
1194 * Call IdentifyFile to verify the file contains what its extension implies.
1195 * Skip this step for dynamically added file formats, because
1196 * IdentifyFile does not know how to recognise them.
1198 if (FileFormats.getInstance().isIdentifiable(format))
1202 format = new IdentifyFile().identify(selectedFile,
1203 DataSourceType.FILE);
1204 } catch (FileFormatException e)
1206 // format = null; //??
1210 new FileLoader().LoadFile(viewport, selectedFile,
1211 DataSourceType.FILE, format);
1214 chooser.showOpenDialog(this);
1218 * Shows a dialog for input of a URL at which to retrieve alignment data
1223 public void inputURLMenuItem_actionPerformed(AlignViewport viewport)
1225 // This construct allows us to have a wider textfield
1227 JLabel label = new JLabel(
1228 MessageManager.getString("label.input_file_url"));
1230 JPanel panel = new JPanel(new GridLayout(2, 1));
1234 * the URL to fetch is
1235 * Java: an editable combobox with history
1236 * JS: (pending JAL-3038) a plain text field
1239 String urlBase = "http://www.";
1240 if (Platform.isJS())
1242 history = new JTextField(urlBase, 35);
1251 JComboBox<String> asCombo = new JComboBox<>();
1252 asCombo.setPreferredSize(new Dimension(400, 20));
1253 asCombo.setEditable(true);
1254 asCombo.addItem(urlBase);
1255 String historyItems = Cache.getProperty("RECENT_URL");
1256 if (historyItems != null)
1258 for (String token : historyItems.split("\\t"))
1260 asCombo.addItem(token);
1267 Object[] options = new Object[] { MessageManager.getString("action.ok"),
1268 MessageManager.getString("action.cancel") };
1269 Runnable action = new Runnable()
1274 @SuppressWarnings("unchecked")
1275 String url = (history instanceof JTextField
1276 ? ((JTextField) history).getText()
1277 : ((JComboBox<String>) history).getSelectedItem()
1280 if (url.toLowerCase().endsWith(".jar"))
1282 if (viewport != null)
1284 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1285 FileFormat.Jalview);
1289 new FileLoader().LoadFile(url, DataSourceType.URL,
1290 FileFormat.Jalview);
1295 FileFormatI format = null;
1298 format = new IdentifyFile().identify(url, DataSourceType.URL);
1299 } catch (FileFormatException e)
1301 // TODO revise error handling, distinguish between
1302 // URL not found and response not valid
1307 String msg = MessageManager
1308 .formatMessage("label.couldnt_locate", url);
1309 JvOptionPane.showInternalMessageDialog(getDesktopPane(), msg,
1310 MessageManager.getString("label.url_not_found"),
1311 JvOptionPane.WARNING_MESSAGE);
1316 if (viewport != null)
1318 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1323 new FileLoader().LoadFile(url, DataSourceType.URL, format);
1328 String dialogOption = MessageManager
1329 .getString("label.input_alignment_from_url");
1330 JvOptionPane.newOptionDialog(desktopPane).setResponseHandler(0, action)
1331 .showInternalDialog(panel, dialogOption,
1332 JvOptionPane.YES_NO_CANCEL_OPTION,
1333 JvOptionPane.PLAIN_MESSAGE, null, options,
1334 MessageManager.getString("action.ok"));
1338 * Opens the CutAndPaste window for the user to paste an alignment in to
1341 * - if not null, the pasted alignment is added to the current
1342 * alignment; if null, to a new alignment window
1345 public void inputTextboxMenuItem_actionPerformed(
1346 AlignmentViewPanel viewPanel)
1348 CutAndPasteTransfer cap = new CutAndPasteTransfer();
1349 cap.setForInput(viewPanel);
1350 addInternalFrame(cap,
1351 MessageManager.getString("label.cut_paste_alignmen_file"),
1352 FRAME_MAKE_VISIBLE, 600, 500, FRAME_ALLOW_RESIZE,
1353 FRAME_SET_MIN_SIZE_300);
1362 Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
1363 Cache.setProperty("SCREENGEOMETRY_WIDTH", screen.width + "");
1364 Cache.setProperty("SCREENGEOMETRY_HEIGHT", screen.height + "");
1365 storeLastKnownDimensions("", new Rectangle(getBounds().x, getBounds().y,
1366 getWidth(), getHeight()));
1368 if (jconsole != null)
1370 storeLastKnownDimensions("JAVA_CONSOLE_", jconsole.getBounds());
1371 jconsole.stopConsole();
1375 storeLastKnownDimensions("JALVIEW_RSS_WINDOW_", jvnews.getBounds());
1378 if (dialogExecutor != null)
1380 dialogExecutor.shutdownNow();
1382 closeAll_actionPerformed(null);
1384 if (groovyConsole != null)
1386 // suppress a possible repeat prompt to save script
1387 groovyConsole.setDirty(false);
1388 groovyConsole.exit();
1393 private void storeLastKnownDimensions(String string, Rectangle jc)
1395 Cache.log.debug("Storing last known dimensions for " + string + ": x:"
1396 + jc.x + " y:" + jc.y + " width:" + jc.width + " height:"
1399 Cache.setProperty(string + "SCREEN_X", jc.x + "");
1400 Cache.setProperty(string + "SCREEN_Y", jc.y + "");
1401 Cache.setProperty(string + "SCREEN_WIDTH", jc.width + "");
1402 Cache.setProperty(string + "SCREEN_HEIGHT", jc.height + "");
1412 public void aboutMenuItem_actionPerformed(ActionEvent e)
1414 new Thread(new Runnable()
1419 new SplashScreen(false);
1425 * Returns the html text for the About screen, including any available version
1426 * number, build details, author details and citation reference, but without
1427 * the enclosing {@code html} tags
1431 public String getAboutMessage()
1433 StringBuilder message = new StringBuilder(1024);
1434 message.append("<h1><strong>Version: ")
1435 .append(Cache.getProperty("VERSION")).append("</strong></h1>")
1436 .append("<strong>Built: <em>")
1437 .append(Cache.getDefault("BUILD_DATE", "unknown"))
1438 .append("</em> from ").append(Cache.getBuildDetailsForSplash())
1439 .append("</strong>");
1441 String latestVersion = Cache.getDefault("LATEST_VERSION", "Checking");
1442 if (latestVersion.equals("Checking"))
1444 // JBP removed this message for 2.11: May be reinstated in future version
1445 // message.append("<br>...Checking latest version...</br>");
1447 else if (!latestVersion.equals(Cache.getProperty("VERSION")))
1449 boolean red = false;
1450 if (Cache.getProperty("VERSION").toLowerCase()
1451 .indexOf("automated build") == -1)
1454 // Displayed when code version and jnlp version do not match and code
1455 // version is not a development build
1456 message.append("<div style=\"color: #FF0000;font-style: bold;\">");
1459 message.append("<br>!! Version ")
1460 .append(Cache.getDefault("LATEST_VERSION", "..Checking.."))
1461 .append(" is available for download from ")
1462 .append(Cache.getDefault("www.jalview.org",
1463 "http://www.jalview.org"))
1467 message.append("</div>");
1470 message.append("<br>Authors: ");
1471 message.append(Cache.getDefault("AUTHORFNAMES", DEFAULT_AUTHORS));
1472 message.append(CITATION);
1474 return message.toString();
1478 * Action on requesting Help documentation
1481 public void documentationMenuItem_actionPerformed()
1485 if (Platform.isJS())
1487 BrowserLauncher.openURL("http://www.jalview.org/help.html");
1496 Help.showHelpWindow();
1498 } catch (Exception ex)
1500 System.err.println("Error opening help: " + ex.getMessage());
1505 public void closeAll_actionPerformed(ActionEvent e)
1507 // TODO show a progress bar while closing?
1508 JInternalFrame[] frames = desktopPane.getAllFrames();
1509 for (int i = 0; i < frames.length; i++)
1513 frames[i].setClosed(true);
1514 } catch (java.beans.PropertyVetoException ex)
1518 Jalview.setCurrentAlignFrame(null);
1519 System.out.println("ALL CLOSED");
1522 * reset state of singleton objects as appropriate (clear down session state
1523 * when all windows are closed)
1525 StructureSelectionManager ssm = StructureSelectionManager
1526 .getStructureSelectionManager(this);
1534 public void raiseRelated_actionPerformed(ActionEvent e)
1536 reorderAssociatedWindows(false, false);
1540 public void minimizeAssociated_actionPerformed(ActionEvent e)
1542 reorderAssociatedWindows(true, false);
1545 void closeAssociatedWindows()
1547 reorderAssociatedWindows(false, true);
1553 * @seejalview.jbgui.GDesktop#garbageCollect_actionPerformed(java.awt.event.
1557 protected void garbageCollect_actionPerformed(ActionEvent e)
1559 // We simply collect the garbage
1560 Cache.log.debug("Collecting garbage...");
1562 Cache.log.debug("Finished garbage collection.");
1569 * jalview.jbgui.GDesktop#showMemusage_actionPerformed(java.awt.event.ActionEvent
1573 protected void showMemusage_actionPerformed(ActionEvent e)
1575 desktopPane.showMemoryUsage(showMemusage.isSelected());
1582 * jalview.jbgui.GDesktop#showConsole_actionPerformed(java.awt.event.ActionEvent
1586 protected void showConsole_actionPerformed(ActionEvent e)
1588 showConsole(showConsole.isSelected());
1591 Console jconsole = null;
1594 * control whether the java console is visible or not
1598 void showConsole(boolean selected)
1600 // TODO: decide if we should update properties file
1601 if (jconsole != null) // BH 2018
1603 showConsole.setSelected(selected);
1604 Cache.setProperty("SHOW_JAVA_CONSOLE",
1605 Boolean.valueOf(selected).toString());
1606 jconsole.setVisible(selected);
1610 void reorderAssociatedWindows(boolean minimize, boolean close)
1612 JInternalFrame[] frames = desktopPane.getAllFrames();
1613 if (frames == null || frames.length < 1)
1618 AlignmentViewport source = null, target = null;
1619 if (frames[0] instanceof AlignFrame)
1621 source = ((AlignFrame) frames[0]).getCurrentView();
1623 else if (frames[0] instanceof TreePanel)
1625 source = ((TreePanel) frames[0]).getViewPort();
1627 else if (frames[0] instanceof PCAPanel)
1629 source = ((PCAPanel) frames[0]).av;
1631 else if (frames[0].getContentPane() instanceof PairwiseAlignPanel)
1633 source = ((PairwiseAlignPanel) frames[0].getContentPane()).av;
1638 for (int i = 0; i < frames.length; i++)
1641 if (frames[i] == null)
1645 if (frames[i] instanceof AlignFrame)
1647 target = ((AlignFrame) frames[i]).getCurrentView();
1649 else if (frames[i] instanceof TreePanel)
1651 target = ((TreePanel) frames[i]).getViewPort();
1653 else if (frames[i] instanceof PCAPanel)
1655 target = ((PCAPanel) frames[i]).av;
1657 else if (frames[i].getContentPane() instanceof PairwiseAlignPanel)
1659 target = ((PairwiseAlignPanel) frames[i].getContentPane()).av;
1662 if (source == target)
1668 frames[i].setClosed(true);
1672 frames[i].setIcon(minimize);
1675 frames[i].toFront();
1679 } catch (java.beans.PropertyVetoException ex)
1694 protected void preferences_actionPerformed(ActionEvent e)
1700 * Prompts the user to choose a file and then saves the Jalview state as a
1701 * Jalview project file
1704 public void saveState_actionPerformed()
1706 saveState_actionPerformed(false);
1709 public void saveState_actionPerformed(boolean saveAs)
1711 java.io.File projectFile = getProjectFile();
1712 // autoSave indicates we already have a file and don't need to ask
1713 boolean autoSave = projectFile != null && !saveAs
1714 && BackupFiles.getEnabled();
1716 // System.out.println("autoSave="+autoSave+", projectFile='"+projectFile+"',
1717 // saveAs="+saveAs+", Backups
1718 // "+(BackupFiles.getEnabled()?"enabled":"disabled"));
1720 boolean approveSave = false;
1723 JalviewFileChooser chooser = new JalviewFileChooser("jvp",
1726 chooser.setFileView(new JalviewFileView());
1727 chooser.setDialogTitle(MessageManager.getString("label.save_state"));
1729 int value = chooser.showSaveDialog(this);
1731 if (value == JalviewFileChooser.APPROVE_OPTION)
1733 projectFile = chooser.getSelectedFile();
1734 setProjectFile(projectFile);
1739 if (approveSave || autoSave)
1741 final Desktop me = this;
1742 final java.io.File chosenFile = projectFile;
1743 new Thread(new Runnable()
1748 // TODO: refactor to Jalview desktop session controller action.
1749 setProgressBar(MessageManager.formatMessage(
1750 "label.saving_jalview_project", new Object[]
1751 { chosenFile.getName() }), chosenFile.hashCode());
1752 Cache.setProperty("LAST_DIRECTORY", chosenFile.getParent());
1753 // TODO catch and handle errors for savestate
1754 // TODO prevent user from messing with the Desktop whilst we're saving
1757 boolean doBackup = BackupFiles.getEnabled();
1758 BackupFiles backupfiles = doBackup ? new BackupFiles(chosenFile)
1761 new Jalview2XML().saveState(
1762 doBackup ? backupfiles.getTempFile() : chosenFile);
1766 backupfiles.setWriteSuccess(true);
1767 backupfiles.rollBackupsAndRenameTempFile();
1769 } catch (OutOfMemoryError oom)
1771 new OOMWarning("Whilst saving current state to "
1772 + chosenFile.getName(), oom);
1773 } catch (Exception ex)
1775 Cache.log.error("Problems whilst trying to save to "
1776 + chosenFile.getName(), ex);
1777 JvOptionPane.showMessageDialog(me,
1778 MessageManager.formatMessage(
1779 "label.error_whilst_saving_current_state_to",
1781 { chosenFile.getName() }),
1782 MessageManager.getString("label.couldnt_save_project"),
1783 JvOptionPane.WARNING_MESSAGE);
1785 setProgressBar(null, chosenFile.hashCode());
1792 public void saveAsState_actionPerformed(ActionEvent e)
1794 saveState_actionPerformed(true);
1797 private void setProjectFile(File choice)
1799 this.projectFile = choice;
1802 public File getProjectFile()
1804 return this.projectFile;
1808 * Shows a file chooser dialog and tries to read in the selected file as a
1812 public void loadState_actionPerformed()
1814 final String[] suffix = new String[] { "jvp", "jar" };
1815 final String[] desc = new String[] { "Jalview Project",
1816 "Jalview Project (old)" };
1817 JalviewFileChooser chooser = new JalviewFileChooser(
1818 Cache.getProperty("LAST_DIRECTORY"), suffix, desc,
1819 "Jalview Project", true, BackupFiles.getEnabled()); // last two
1823 chooser.setFileView(new JalviewFileView());
1824 chooser.setDialogTitle(MessageManager.getString("label.restore_state"));
1825 chooser.setResponseHandler(0, new Runnable()
1830 File selectedFile = chooser.getSelectedFile();
1831 setProjectFile(selectedFile);
1832 String choice = selectedFile.getAbsolutePath();
1833 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1834 new Thread(new Runnable()
1841 new Jalview2XML().loadJalviewAlign(selectedFile);
1842 } catch (OutOfMemoryError oom)
1844 new OOMWarning("Whilst loading project from " + choice, oom);
1845 } catch (Exception ex)
1848 "Problems whilst loading project from " + choice, ex);
1849 JvOptionPane.showMessageDialog(getDesktopPane(),
1850 MessageManager.formatMessage(
1851 "label.error_whilst_loading_project_from",
1855 .getString("label.couldnt_load_project"),
1856 JvOptionPane.WARNING_MESSAGE);
1863 chooser.showOpenDialog(this);
1867 public void inputSequence_actionPerformed(ActionEvent e)
1869 new SequenceFetcher(this);
1872 JPanel progressPanel;
1874 ArrayList<JPanel> fileLoadingPanels = new ArrayList<>();
1876 public void startLoading(final Object fileName)
1878 if (fileLoadingCount == 0)
1880 fileLoadingPanels.add(addProgressPanel(MessageManager
1881 .formatMessage("label.loading_file", new Object[]
1887 private JPanel addProgressPanel(String string)
1889 if (progressPanel == null)
1891 progressPanel = new JPanel(new GridLayout(1, 1));
1892 totalProgressCount = 0;
1893 getContentPane().add(progressPanel, BorderLayout.SOUTH);
1895 JPanel thisprogress = new JPanel(new BorderLayout(10, 5));
1896 JProgressBar progressBar = new JProgressBar();
1897 progressBar.setIndeterminate(true);
1899 thisprogress.add(new JLabel(string), BorderLayout.WEST);
1901 thisprogress.add(progressBar, BorderLayout.CENTER);
1902 progressPanel.add(thisprogress);
1903 ((GridLayout) progressPanel.getLayout()).setRows(
1904 ((GridLayout) progressPanel.getLayout()).getRows() + 1);
1905 ++totalProgressCount;
1907 return thisprogress;
1910 int totalProgressCount = 0;
1912 private void removeProgressPanel(JPanel progbar)
1914 if (progressPanel != null)
1916 synchronized (progressPanel)
1918 progressPanel.remove(progbar);
1919 GridLayout gl = (GridLayout) progressPanel.getLayout();
1920 gl.setRows(gl.getRows() - 1);
1921 if (--totalProgressCount < 1)
1923 this.getContentPane().remove(progressPanel);
1924 progressPanel = null;
1931 public void stopLoading()
1934 if (fileLoadingCount < 1)
1936 while (fileLoadingPanels.size() > 0)
1938 removeProgressPanel(fileLoadingPanels.remove(0));
1940 fileLoadingPanels.clear();
1941 fileLoadingCount = 0;
1946 public static int getViewCount(String alignmentId)
1948 AlignmentViewport[] aps = getViewports(alignmentId);
1949 return (aps == null) ? 0 : aps.length;
1954 * @param alignmentId
1955 * - if null, all sets are returned
1956 * @return all AlignmentPanels concerning the alignmentId sequence set
1958 public static AlignmentPanel[] getAlignmentPanels(String alignmentId)
1960 if (getDesktopPane() == null)
1962 // no frames created and in headless mode
1963 // TODO: verify that frames are recoverable when in headless mode
1966 List<AlignmentPanel> aps = new ArrayList<>();
1967 AlignFrame[] frames = getAlignFrames();
1972 for (AlignFrame af : frames)
1974 for (AlignmentPanel ap : af.alignPanels)
1976 if (alignmentId == null
1977 || alignmentId.equals(ap.av.getSequenceSetId()))
1983 if (aps.size() == 0)
1987 AlignmentPanel[] vap = aps.toArray(new AlignmentPanel[aps.size()]);
1992 * get all the viewports on an alignment.
1994 * @param sequenceSetId
1995 * unique alignment id (may be null - all viewports returned in that
1997 * @return all viewports on the alignment bound to sequenceSetId
1999 public static AlignmentViewport[] getViewports(String sequenceSetId)
2001 List<AlignmentViewport> viewp = new ArrayList<>();
2002 if (getDesktopPane() != null)
2004 AlignFrame[] frames = getAlignFrames();
2006 for (AlignFrame afr : frames)
2008 if (sequenceSetId == null || afr.getViewport().getSequenceSetId()
2009 .equals(sequenceSetId))
2011 if (afr.alignPanels != null)
2013 for (AlignmentPanel ap : afr.alignPanels)
2015 if (sequenceSetId == null
2016 || sequenceSetId.equals(ap.av.getSequenceSetId()))
2024 viewp.add(afr.getViewport());
2028 if (viewp.size() > 0)
2030 return viewp.toArray(new AlignmentViewport[viewp.size()]);
2037 * Explode the views in the given frame into separate AlignFrame
2041 public static void explodeViews(AlignFrame af)
2043 int size = af.alignPanels.size();
2049 // FIXME: ideally should use UI interface API
2050 FeatureSettings viewFeatureSettings = (af.featureSettings != null
2051 && af.featureSettings.isOpen()) ? af.featureSettings : null;
2052 Rectangle fsBounds = af.getFeatureSettingsGeometry();
2053 for (int i = 0; i < size; i++)
2055 AlignmentPanel ap = af.alignPanels.get(i);
2057 AlignFrame newaf = new AlignFrame(ap);
2059 // transfer reference for existing feature settings to new alignFrame
2060 if (ap == af.alignPanel)
2062 if (viewFeatureSettings != null && viewFeatureSettings.fr.ap == ap)
2064 newaf.featureSettings = viewFeatureSettings;
2066 newaf.setFeatureSettingsGeometry(fsBounds);
2070 * Restore the view's last exploded frame geometry if known. Multiple
2071 * views from one exploded frame share and restore the same (frame)
2072 * position and size.
2074 Rectangle geometry = ap.av.getExplodedGeometry();
2075 if (geometry != null)
2077 newaf.setBounds(geometry);
2080 ap.av.setGatherViewsHere(false);
2082 addInternalFrame(newaf, af.getTitle(), AlignFrame.DEFAULT_WIDTH,
2083 AlignFrame.DEFAULT_HEIGHT);
2084 // and materialise a new feature settings dialog instance for the new
2086 // (closes the old as if 'OK' was pressed)
2087 if (ap == af.alignPanel && newaf.featureSettings != null
2088 && newaf.featureSettings.isOpen()
2089 && af.alignPanel.getAlignViewport().isShowSequenceFeatures())
2091 newaf.showFeatureSettingsUI();
2095 af.featureSettings = null;
2096 af.alignPanels.clear();
2097 af.closeMenuItem_actionPerformed(true);
2102 * Gather expanded views (separate AlignFrame's) with the same sequence set
2103 * identifier back in to this frame as additional views, and close the
2104 * expanded views. Note the expanded frames may themselves have multiple
2105 * views. We take the lot.
2109 public void gatherViews(AlignFrame source)
2111 source.viewport.setGatherViewsHere(true);
2112 source.viewport.setExplodedGeometry(source.getBounds());
2113 JInternalFrame[] frames = desktopPane.getAllFrames();
2114 String viewId = source.viewport.getSequenceSetId();
2115 for (int t = 0; t < frames.length; t++)
2117 if (frames[t] instanceof AlignFrame && frames[t] != source)
2119 AlignFrame af = (AlignFrame) frames[t];
2120 boolean gatherThis = false;
2121 for (int a = 0; a < af.alignPanels.size(); a++)
2123 AlignmentPanel ap = af.alignPanels.get(a);
2124 if (viewId.equals(ap.av.getSequenceSetId()))
2127 ap.av.setGatherViewsHere(false);
2128 ap.av.setExplodedGeometry(af.getBounds());
2129 source.addAlignmentPanel(ap, false);
2135 if (af.featureSettings != null && af.featureSettings.isOpen())
2137 if (source.featureSettings == null)
2139 // preserve the feature settings geometry for this frame
2140 source.featureSettings = af.featureSettings;
2141 source.setFeatureSettingsGeometry(
2142 af.getFeatureSettingsGeometry());
2146 // close it and forget
2147 af.featureSettings.close();
2150 af.alignPanels.clear();
2151 af.closeMenuItem_actionPerformed(true);
2156 // refresh the feature setting UI for the source frame if it exists
2157 if (source.featureSettings != null && source.featureSettings.isOpen())
2159 source.showFeatureSettingsUI();
2163 public JInternalFrame[] getAllFrames()
2165 return desktopPane.getAllFrames();
2169 * Checks the given url to see if it gives a response indicating that the user
2170 * should be informed of a new questionnaire.
2174 public void checkForQuestionnaire(String url)
2176 UserQuestionnaireCheck jvq = new UserQuestionnaireCheck(url);
2177 // javax.swing.SwingUtilities.invokeLater(jvq);
2178 new Thread(jvq).start();
2181 public void checkURLLinks()
2183 // Thread off the URL link checker
2184 addDialogThread(new Runnable()
2189 if (Cache.getDefault("CHECKURLLINKS", true))
2191 // check what the actual links are - if it's just the default don't
2192 // bother with the warning
2193 List<String> links = Preferences.sequenceUrlLinks
2196 // only need to check links if there is one with a
2197 // SEQUENCE_ID which is not the default EMBL_EBI link
2198 ListIterator<String> li = links.listIterator();
2199 boolean check = false;
2200 List<JLabel> urls = new ArrayList<>();
2201 while (li.hasNext())
2203 String link = li.next();
2204 if (link.contains(jalview.util.UrlConstants.SEQUENCE_ID)
2205 && !UrlConstants.isDefaultString(link))
2208 int barPos = link.indexOf("|");
2209 String urlMsg = barPos == -1 ? link
2210 : link.substring(0, barPos) + ": "
2211 + link.substring(barPos + 1);
2212 urls.add(new JLabel(urlMsg));
2220 // ask user to check in case URL links use old style tokens
2221 // ($SEQUENCE_ID$ for sequence id _or_ accession id)
2222 JPanel msgPanel = new JPanel();
2223 msgPanel.setLayout(new BoxLayout(msgPanel, BoxLayout.PAGE_AXIS));
2224 msgPanel.add(Box.createVerticalGlue());
2225 JLabel msg = new JLabel(MessageManager
2226 .getString("label.SEQUENCE_ID_for_DB_ACCESSION1"));
2227 JLabel msg2 = new JLabel(MessageManager
2228 .getString("label.SEQUENCE_ID_for_DB_ACCESSION2"));
2230 for (JLabel url : urls)
2236 final JCheckBox jcb = new JCheckBox(
2237 MessageManager.getString("label.do_not_display_again"));
2238 jcb.addActionListener(new ActionListener()
2241 public void actionPerformed(ActionEvent e)
2243 // update Cache settings for "don't show this again"
2244 boolean showWarningAgain = !jcb.isSelected();
2245 Cache.setProperty("CHECKURLLINKS",
2246 Boolean.valueOf(showWarningAgain).toString());
2251 JvOptionPane.showMessageDialog(desktopPane, msgPanel,
2253 .getString("label.SEQUENCE_ID_no_longer_used"),
2254 JvOptionPane.WARNING_MESSAGE);
2261 * Proxy class for JDesktopPane which optionally displays the current memory
2262 * usage and highlights the desktop area with a red bar if free memory runs
2267 public class MyDesktopPane extends JDesktopPane implements Runnable
2269 private static final float ONE_MB = 1048576f;
2271 boolean showMemoryUsage = false;
2275 java.text.NumberFormat df;
2277 float maxMemory, allocatedMemory, freeMemory, totalFreeMemory,
2280 public MyDesktopPane(boolean showMemoryUsage)
2282 showMemoryUsage(showMemoryUsage);
2285 public void showMemoryUsage(boolean showMemory)
2287 this.showMemoryUsage = showMemory;
2290 Thread worker = new Thread(this);
2296 public boolean isShowMemoryUsage()
2298 return showMemoryUsage;
2304 df = java.text.NumberFormat.getNumberInstance();
2305 df.setMaximumFractionDigits(2);
2306 runtime = Runtime.getRuntime();
2308 while (showMemoryUsage)
2312 maxMemory = runtime.maxMemory() / ONE_MB;
2313 allocatedMemory = runtime.totalMemory() / ONE_MB;
2314 freeMemory = runtime.freeMemory() / ONE_MB;
2315 totalFreeMemory = freeMemory + (maxMemory - allocatedMemory);
2317 percentUsage = (totalFreeMemory / maxMemory) * 100;
2319 // if (percentUsage < 20)
2321 // border1 = BorderFactory.createMatteBorder(12, 12, 12, 12,
2323 // instance.set.setBorder(border1);
2326 // sleep after showing usage
2328 } catch (Exception ex)
2330 ex.printStackTrace();
2336 public void paintComponent(Graphics g)
2338 if (showMemoryUsage && g != null && df != null)
2340 if (percentUsage < 20)
2342 g.setColor(Color.red);
2344 FontMetrics fm = g.getFontMetrics();
2347 g.drawString(MessageManager.formatMessage("label.memory_stats",
2349 { df.format(totalFreeMemory), df.format(maxMemory),
2350 df.format(percentUsage) }),
2351 10, getHeight() - fm.getHeight());
2358 * Accessor method to quickly get all the AlignmentFrames loaded.
2360 * @return an array of AlignFrame, or null if none found
2362 public static AlignFrame[] getAlignFrames()
2364 if (Jalview.isHeadlessMode())
2366 return new AlignFrame[] { Jalview.getInstance().currentAlignFrame };
2369 JInternalFrame[] frames = getDesktopPane().getAllFrames();
2375 List<AlignFrame> avp = new ArrayList<>();
2377 for (int i = frames.length - 1; i > -1; i--)
2379 if (frames[i] instanceof AlignFrame)
2381 avp.add((AlignFrame) frames[i]);
2383 else if (frames[i] instanceof SplitFrame)
2386 * Also check for a split frame containing an AlignFrame
2388 GSplitFrame sf = (GSplitFrame) frames[i];
2389 if (sf.getTopFrame() instanceof AlignFrame)
2391 avp.add((AlignFrame) sf.getTopFrame());
2393 if (sf.getBottomFrame() instanceof AlignFrame)
2395 avp.add((AlignFrame) sf.getBottomFrame());
2399 if (avp.size() == 0)
2403 AlignFrame afs[] = avp.toArray(new AlignFrame[avp.size()]);
2408 * Returns an array of any AppJmol frames in the Desktop (or null if none).
2412 public GStructureViewer[] getJmols()
2414 JInternalFrame[] frames = desktopPane.getAllFrames();
2420 List<GStructureViewer> avp = new ArrayList<>();
2422 for (int i = frames.length - 1; i > -1; i--)
2424 if (frames[i] instanceof AppJmol)
2426 GStructureViewer af = (GStructureViewer) frames[i];
2430 if (avp.size() == 0)
2434 GStructureViewer afs[] = avp.toArray(new GStructureViewer[avp.size()]);
2439 * Add Groovy Support to Jalview
2442 public void groovyShell_actionPerformed()
2446 openGroovyConsole();
2447 } catch (Exception ex)
2449 Cache.log.error("Groovy Shell Creation failed.", ex);
2450 JvOptionPane.showInternalMessageDialog(desktopPane,
2452 MessageManager.getString("label.couldnt_create_groovy_shell"),
2453 MessageManager.getString("label.groovy_support_failed"),
2454 JvOptionPane.ERROR_MESSAGE);
2459 * Open the Groovy console
2461 void openGroovyConsole()
2463 if (groovyConsole == null)
2465 groovyConsole = new groovy.ui.Console();
2466 groovyConsole.setVariable("Jalview", this);
2467 groovyConsole.run();
2470 * We allow only one console at a time, so that AlignFrame menu option
2471 * 'Calculate | Run Groovy script' is unambiguous.
2472 * Disable 'Groovy Console', and enable 'Run script', when the console is
2473 * opened, and the reverse when it is closed
2475 Window window = (Window) groovyConsole.getFrame();
2476 window.addWindowListener(new WindowAdapter()
2479 public void windowClosed(WindowEvent e)
2482 * rebind CMD-Q from Groovy Console to Jalview Quit
2485 enableExecuteGroovy(false);
2491 * show Groovy console window (after close and reopen)
2493 ((Window) groovyConsole.getFrame()).setVisible(true);
2496 * if we got this far, enable 'Run Groovy' in AlignFrame menus
2497 * and disable opening a second console
2499 enableExecuteGroovy(true);
2503 * Bind Ctrl/Cmd-Q to Quit - for reset as Groovy Console takes over this
2504 * binding when opened
2506 protected void addQuitHandler()
2509 getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
2510 .put(KeyStroke.getKeyStroke(KeyEvent.VK_Q,
2511 Platform.SHORTCUT_KEY_MASK),
2513 getRootPane().getActionMap().put("Quit", new AbstractAction()
2516 public void actionPerformed(ActionEvent e)
2524 * Enable or disable 'Run Groovy script' in AlignFrame calculate menus
2527 * true if Groovy console is open
2529 public void enableExecuteGroovy(boolean enabled)
2532 * disable opening a second Groovy console
2533 * (or re-enable when the console is closed)
2535 groovyShell.setEnabled(!enabled);
2537 AlignFrame[] alignFrames = getAlignFrames();
2538 if (alignFrames != null)
2540 for (AlignFrame af : alignFrames)
2542 af.setGroovyEnabled(enabled);
2548 * Progress bars managed by the IProgressIndicator method.
2550 private Hashtable<Long, JPanel> progressBars;
2552 private Hashtable<Long, IProgressIndicatorHandler> progressBarHandlers;
2557 * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
2560 public void setProgressBar(String message, long id)
2562 // Platform.timeCheck("Desktop " + message, Platform.TIME_MARK);
2564 if (progressBars == null)
2566 progressBars = new Hashtable<>();
2567 progressBarHandlers = new Hashtable<>();
2570 if (progressBars.get(Long.valueOf(id)) != null)
2572 JPanel panel = progressBars.remove(Long.valueOf(id));
2573 if (progressBarHandlers.contains(Long.valueOf(id)))
2575 progressBarHandlers.remove(Long.valueOf(id));
2577 removeProgressPanel(panel);
2581 progressBars.put(Long.valueOf(id), addProgressPanel(message));
2588 * @see jalview.gui.IProgressIndicator#registerHandler(long,
2589 * jalview.gui.IProgressIndicatorHandler)
2592 public void registerHandler(final long id,
2593 final IProgressIndicatorHandler handler)
2595 if (progressBarHandlers == null
2596 || !progressBars.containsKey(Long.valueOf(id)))
2598 throw new Error(MessageManager.getString(
2599 "error.call_setprogressbar_before_registering_handler"));
2601 progressBarHandlers.put(Long.valueOf(id), handler);
2602 final JPanel progressPanel = progressBars.get(Long.valueOf(id));
2603 if (handler.canCancel())
2605 JButton cancel = new JButton(
2606 MessageManager.getString("action.cancel"));
2607 final IProgressIndicator us = this;
2608 cancel.addActionListener(new ActionListener()
2612 public void actionPerformed(ActionEvent e)
2614 handler.cancelActivity(id);
2615 us.setProgressBar(MessageManager
2616 .formatMessage("label.cancelled_params", new Object[]
2617 { ((JLabel) progressPanel.getComponent(0)).getText() }),
2621 progressPanel.add(cancel, BorderLayout.EAST);
2627 * @return true if any progress bars are still active
2630 public boolean operationInProgress()
2632 if (progressBars != null && progressBars.size() > 0)
2640 * This will return the first AlignFrame holding the given viewport instance.
2641 * It will break if there are more than one AlignFrames viewing a particular
2645 * @return alignFrame for viewport
2647 public static AlignFrame getAlignFrameFor(AlignViewportI viewport)
2649 if (getDesktopPane() != null)
2651 AlignmentPanel[] aps = getAlignmentPanels(
2652 viewport.getSequenceSetId());
2653 for (int panel = 0; aps != null && panel < aps.length; panel++)
2655 if (aps[panel] != null && aps[panel].av == viewport)
2657 return aps[panel].alignFrame;
2664 public VamsasApplication getVamsasApplication()
2666 // TODO: JAL-3311 remove remaining code from Jalview relating to VAMSAS
2672 * flag set if jalview GUI is being operated programmatically
2674 private boolean inBatchMode = false;
2677 * check if jalview GUI is being operated programmatically
2679 * @return inBatchMode
2681 public boolean isInBatchMode()
2687 * set flag if jalview GUI is being operated programmatically
2689 * @param inBatchMode
2691 public void setInBatchMode(boolean inBatchMode)
2693 this.inBatchMode = inBatchMode;
2696 public void startServiceDiscovery()
2698 startServiceDiscovery(false);
2701 public void startServiceDiscovery(boolean blocking)
2703 boolean alive = true;
2704 Thread t0 = null, t1 = null, t2 = null;
2705 // JAL-940 - JALVIEW 1 services are now being EOLed as of JABA 2.1 release
2708 // todo: changesupport handlers need to be transferred
2709 if (discoverer == null)
2711 discoverer = jalview.ws.jws1.Discoverer.getInstance();
2712 // register PCS handler for desktop.
2713 discoverer.addPropertyChangeListener(changeSupport);
2715 // JAL-940 - disabled JWS1 service configuration - always start discoverer
2716 // until we phase out completely
2717 (t0 = new Thread(discoverer)).start();
2720 if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
2722 t2 = jalview.ws.jws2.Jws2Discoverer.getInstance()
2723 .startDiscoverer(changeSupport);
2727 // TODO: do rest service discovery
2736 } catch (Exception e)
2739 alive = (t1 != null && t1.isAlive()) || (t2 != null && t2.isAlive())
2740 || (t3 != null && t3.isAlive())
2741 || (t0 != null && t0.isAlive());
2747 * called to check if the service discovery process completed successfully.
2751 protected void JalviewServicesChanged(PropertyChangeEvent evt)
2753 if (evt.getNewValue() == null || evt.getNewValue() instanceof Vector)
2755 final String ermsg = jalview.ws.jws2.Jws2Discoverer.getInstance()
2756 .getErrorMessages();
2759 if (Cache.getDefault("SHOW_WSDISCOVERY_ERRORS", true))
2761 if (serviceChangedDialog == null)
2763 // only run if we aren't already displaying one of these.
2764 addDialogThread(serviceChangedDialog = new Runnable()
2771 * JalviewDialog jd =new JalviewDialog() {
2773 * @Override protected void cancelPressed() { // TODO
2774 * Auto-generated method stub
2776 * }@Override protected void okPressed() { // TODO
2777 * Auto-generated method stub
2779 * }@Override protected void raiseClosed() { // TODO
2780 * Auto-generated method stub
2782 * } }; jd.initDialogFrame(new
2783 * JLabel("<html><table width=\"450\"><tr><td>" + ermsg +
2784 * "<br/>It may be that you have invalid JABA URLs in your web service preferences,"
2785 * + " or mis-configured HTTP proxy settings.<br/>" +
2786 * "Check the <em>Connections</em> and <em>Web services</em> tab of the"
2788 * " Tools->Preferences dialog box to change them.</td></tr></table></html>"
2789 * ), true, true, "Web Service Configuration Problem", 450,
2792 * jd.waitForInput();
2794 JvOptionPane.showConfirmDialog(desktopPane,
2795 new JLabel("<html><table width=\"450\"><tr><td>"
2796 + ermsg + "</td></tr></table>"
2797 + "<p>It may be that you have invalid JABA URLs<br/>in your web service preferences,"
2798 + "<br>or as a command-line argument, or mis-configured HTTP proxy settings.</p>"
2799 + "<p>Check the <em>Connections</em> and <em>Web services</em> tab<br/>of the"
2800 + " Tools->Preferences dialog box to change them.</p></html>"),
2801 "Web Service Configuration Problem",
2802 JvOptionPane.DEFAULT_OPTION,
2803 JvOptionPane.ERROR_MESSAGE);
2804 serviceChangedDialog = null;
2813 "Errors reported by JABA discovery service. Check web services preferences.\n"
2820 private Runnable serviceChangedDialog = null;
2823 * start a thread to open a URL in the configured browser. Pops up a warning
2824 * dialog to the user if there is an exception when calling out to the browser
2829 public static void showUrl(final String url)
2831 showUrl(url, getInstance());
2835 * Like showUrl but allows progress handler to be specified
2839 * (null) or object implementing IProgressIndicator
2841 public static void showUrl(final String url,
2842 final IProgressIndicator progress)
2844 new Thread(new Runnable()
2851 if (progress != null)
2853 progress.setProgressBar(MessageManager
2854 .formatMessage("status.opening_params", new Object[]
2855 { url }), this.hashCode());
2857 jalview.util.BrowserLauncher.openURL(url);
2858 } catch (Exception ex)
2860 JvOptionPane.showInternalMessageDialog(getDesktopPane(),
2862 .getString("label.web_browser_not_found_unix"),
2863 MessageManager.getString("label.web_browser_not_found"),
2864 JvOptionPane.WARNING_MESSAGE);
2866 ex.printStackTrace();
2868 if (progress != null)
2870 progress.setProgressBar(null, this.hashCode());
2876 public static WsParamSetManager wsparamManager = null;
2878 public static ParamManager getUserParameterStore()
2880 if (wsparamManager == null)
2882 wsparamManager = new WsParamSetManager();
2884 return wsparamManager;
2888 * static hyperlink handler proxy method for use by Jalview's internal windows
2892 public static void hyperlinkUpdate(HyperlinkEvent e)
2894 if (e.getEventType() == EventType.ACTIVATED)
2899 url = e.getURL().toString();
2901 } catch (Exception x)
2905 if (Cache.log != null)
2907 Cache.log.error("Couldn't handle string " + url + " as a URL.");
2912 "Couldn't handle string " + url + " as a URL.");
2915 // ignore any exceptions due to dud links.
2922 * single thread that handles display of dialogs to user.
2924 ExecutorService dialogExecutor = Executors.newSingleThreadExecutor();
2927 * flag indicating if dialogExecutor should try to acquire a permit
2929 private volatile boolean dialogPause = true;
2934 private java.util.concurrent.Semaphore block = new Semaphore(0);
2936 private static groovy.ui.Console groovyConsole;
2939 * add another dialog thread to the queue
2943 public void addDialogThread(final Runnable prompter)
2945 dialogExecutor.submit(new Runnable()
2955 } catch (InterruptedException x)
2959 if (Jalview.isHeadlessMode())
2965 SwingUtilities.invokeAndWait(prompter);
2966 } catch (Exception q)
2968 Cache.log.warn("Unexpected Exception in dialog thread.", q);
2974 public void startDialogQueue()
2976 // set the flag so we don't pause waiting for another permit and semaphore
2977 // the current task to begin
2978 dialogPause = false;
2983 * Outputs an image of the desktop to file in EPS format, after prompting the
2984 * user for choice of Text or Lineart character rendering (unless a preference
2985 * has been set). The file name is generated as
2988 * Jalview_snapshot_nnnnn.eps where nnnnn is the current timestamp in milliseconds
2992 protected void snapShotWindow_actionPerformed(ActionEvent e)
2994 // currently the menu option to do this is not shown
2997 int width = getWidth();
2998 int height = getHeight();
3000 "Jalview_snapshot_" + System.currentTimeMillis() + ".eps");
3001 ImageWriterI writer = new ImageWriterI()
3004 public void exportImage(Graphics g) throws Exception
3007 Cache.log.info("Successfully written snapshot to file "
3008 + of.getAbsolutePath());
3011 String title = "View of desktop";
3012 ImageExporter exporter = new ImageExporter(writer, null, TYPE.EPS,
3014 exporter.doExport(of, this, width, height, title);
3018 * Explode the views in the given SplitFrame into separate SplitFrame windows.
3019 * This respects (remembers) any previous 'exploded geometry' i.e. the size
3020 * and location last time the view was expanded (if any). However it does not
3021 * remember the split pane divider location - this is set to match the
3022 * 'exploding' frame.
3026 public void explodeViews(SplitFrame sf)
3028 AlignFrame oldTopFrame = (AlignFrame) sf.getTopFrame();
3029 AlignFrame oldBottomFrame = (AlignFrame) sf.getBottomFrame();
3030 List<? extends AlignmentViewPanel> topPanels = oldTopFrame
3032 List<? extends AlignmentViewPanel> bottomPanels = oldBottomFrame
3034 int viewCount = topPanels.size();
3041 * Processing in reverse order works, forwards order leaves the first panels
3042 * not visible. I don't know why!
3044 for (int i = viewCount - 1; i >= 0; i--)
3047 * Make new top and bottom frames. These take over the respective
3048 * AlignmentPanel objects, including their AlignmentViewports, so the
3049 * cdna/protein relationships between the viewports is carried over to the
3052 * explodedGeometry holds the (x, y) position of the previously exploded
3053 * SplitFrame, and the (width, height) of the AlignFrame component
3055 AlignmentPanel topPanel = (AlignmentPanel) topPanels.get(i);
3056 AlignFrame newTopFrame = new AlignFrame(topPanel);
3057 newTopFrame.setSize(oldTopFrame.getSize());
3058 newTopFrame.setVisible(true);
3059 Rectangle geometry = ((AlignViewport) topPanel.getAlignViewport())
3060 .getExplodedGeometry();
3061 if (geometry != null)
3063 newTopFrame.setSize(geometry.getSize());
3066 AlignmentPanel bottomPanel = (AlignmentPanel) bottomPanels.get(i);
3067 AlignFrame newBottomFrame = new AlignFrame(bottomPanel);
3068 newBottomFrame.setSize(oldBottomFrame.getSize());
3069 newBottomFrame.setVisible(true);
3070 geometry = ((AlignViewport) bottomPanel.getAlignViewport())
3071 .getExplodedGeometry();
3072 if (geometry != null)
3074 newBottomFrame.setSize(geometry.getSize());
3077 topPanel.av.setGatherViewsHere(false);
3078 bottomPanel.av.setGatherViewsHere(false);
3079 JInternalFrame splitFrame = new SplitFrame(newTopFrame,
3081 if (geometry != null)
3083 splitFrame.setLocation(geometry.getLocation());
3085 addInternalFrame(splitFrame, sf.getTitle(), -1, -1);
3089 * Clear references to the panels (now relocated in the new SplitFrames)
3090 * before closing the old SplitFrame.
3093 bottomPanels.clear();
3098 * Gather expanded split frames, sharing the same pairs of sequence set ids,
3099 * back into the given SplitFrame as additional views. Note that the gathered
3100 * frames may themselves have multiple views.
3104 public void gatherViews(GSplitFrame source)
3107 * special handling of explodedGeometry for a view within a SplitFrame: - it
3108 * holds the (x, y) position of the enclosing SplitFrame, and the (width,
3109 * height) of the AlignFrame component
3111 AlignFrame myTopFrame = (AlignFrame) source.getTopFrame();
3112 AlignFrame myBottomFrame = (AlignFrame) source.getBottomFrame();
3113 myTopFrame.viewport.setExplodedGeometry(new Rectangle(source.getX(),
3114 source.getY(), myTopFrame.getWidth(), myTopFrame.getHeight()));
3115 myBottomFrame.viewport
3116 .setExplodedGeometry(new Rectangle(source.getX(), source.getY(),
3117 myBottomFrame.getWidth(), myBottomFrame.getHeight()));
3118 myTopFrame.viewport.setGatherViewsHere(true);
3119 myBottomFrame.viewport.setGatherViewsHere(true);
3120 String topViewId = myTopFrame.viewport.getSequenceSetId();
3121 String bottomViewId = myBottomFrame.viewport.getSequenceSetId();
3123 JInternalFrame[] frames = desktopPane.getAllFrames();
3124 for (JInternalFrame frame : frames)
3126 if (frame instanceof SplitFrame && frame != source)
3128 SplitFrame sf = (SplitFrame) frame;
3129 AlignFrame topFrame = (AlignFrame) sf.getTopFrame();
3130 AlignFrame bottomFrame = (AlignFrame) sf.getBottomFrame();
3131 boolean gatherThis = false;
3132 for (int a = 0; a < topFrame.alignPanels.size(); a++)
3134 AlignmentPanel topPanel = topFrame.alignPanels.get(a);
3135 AlignmentPanel bottomPanel = bottomFrame.alignPanels.get(a);
3136 if (topViewId.equals(topPanel.av.getSequenceSetId())
3137 && bottomViewId.equals(bottomPanel.av.getSequenceSetId()))
3140 topPanel.av.setGatherViewsHere(false);
3141 bottomPanel.av.setGatherViewsHere(false);
3142 topPanel.av.setExplodedGeometry(
3143 new Rectangle(sf.getLocation(), topFrame.getSize()));
3144 bottomPanel.av.setExplodedGeometry(
3145 new Rectangle(sf.getLocation(), bottomFrame.getSize()));
3146 myTopFrame.addAlignmentPanel(topPanel, false);
3147 myBottomFrame.addAlignmentPanel(bottomPanel, false);
3153 topFrame.getAlignPanels().clear();
3154 bottomFrame.getAlignPanels().clear();
3161 * The dust settles...give focus to the tab we did this from.
3163 myTopFrame.setDisplayedView(myTopFrame.alignPanel);
3166 public static groovy.ui.Console getGroovyConsole()
3168 return groovyConsole;
3172 * handles the payload of a drag and drop event.
3174 * TODO refactor to desktop utilities class
3177 * - Data source strings extracted from the drop event
3179 * - protocol for each data source extracted from the drop event
3183 * - the payload from the drop event
3186 @SuppressWarnings("unchecked")
3187 public static void transferFromDropTarget(List<Object> files,
3188 List<DataSourceType> protocols, DropTargetDropEvent evt,
3189 Transferable t) throws Exception
3192 // BH 2018 changed List<String> to List<Object> to allow for File from
3195 // DataFlavor[] flavors = t.getTransferDataFlavors();
3196 // for (int i = 0; i < flavors.length; i++) {
3197 // if (flavors[i].isFlavorJavaFileListType()) {
3198 // evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
3199 // List<File> list = (List<File>) t.getTransferData(flavors[i]);
3200 // for (int j = 0; j < list.size(); j++) {
3201 // File file = (File) list.get(j);
3202 // byte[] data = getDroppedFileBytes(file);
3203 // fileName.setText(file.getName() + " - " + data.length + " " +
3204 // evt.getLocation());
3205 // JTextArea target = (JTextArea) ((DropTarget)
3206 // evt.getSource()).getComponent();
3207 // target.setText(new String(data));
3209 // dtde.dropComplete(true);
3214 DataFlavor uriListFlavor = new DataFlavor(
3215 "text/uri-list;class=java.lang.String"), urlFlavour = null;
3218 urlFlavour = new DataFlavor(
3219 "application/x-java-url; class=java.net.URL");
3220 } catch (ClassNotFoundException cfe)
3222 Cache.log.debug("Couldn't instantiate the URL dataflavor.", cfe);
3225 if (urlFlavour != null && t.isDataFlavorSupported(urlFlavour))
3230 java.net.URL url = (URL) t.getTransferData(urlFlavour);
3231 // nb: java 8 osx bug https://bugs.openjdk.java.net/browse/JDK-8156099
3232 // means url may be null.
3235 protocols.add(DataSourceType.URL);
3236 files.add(url.toString());
3237 Cache.log.debug("Drop handled as URL dataflavor "
3238 + files.get(files.size() - 1));
3243 if (Platform.isAMacAndNotJS())
3246 "Please ignore plist error - occurs due to problem with java 8 on OSX");
3249 } catch (Throwable ex)
3251 Cache.log.debug("URL drop handler failed.", ex);
3254 if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
3256 // Works on Windows and MacOSX
3257 Cache.log.debug("Drop handled as javaFileListFlavor");
3258 for (File file : (List<File>) t
3259 .getTransferData(DataFlavor.javaFileListFlavor))
3262 protocols.add(DataSourceType.FILE);
3267 // Unix like behaviour
3268 boolean added = false;
3270 if (t.isDataFlavorSupported(uriListFlavor))
3272 Cache.log.debug("Drop handled as uriListFlavor");
3273 // This is used by Unix drag system
3274 data = (String) t.getTransferData(uriListFlavor);
3278 // fallback to text: workaround - on OSX where there's a JVM bug
3279 Cache.log.debug("standard URIListFlavor failed. Trying text");
3280 // try text fallback
3281 DataFlavor textDf = new DataFlavor(
3282 "text/plain;class=java.lang.String");
3283 if (t.isDataFlavorSupported(textDf))
3285 data = (String) t.getTransferData(textDf);
3288 Cache.log.debug("Plain text drop content returned "
3289 + (data == null ? "Null - failed" : data));
3294 while (protocols.size() < files.size())
3296 Cache.log.debug("Adding missing FILE protocol for "
3297 + files.get(protocols.size()));
3298 protocols.add(DataSourceType.FILE);
3300 for (java.util.StringTokenizer st = new java.util.StringTokenizer(
3301 data, "\r\n"); st.hasMoreTokens();)
3304 String s = st.nextToken();
3305 if (s.startsWith("#"))
3307 // the line is a comment (as per the RFC 2483)
3310 java.net.URI uri = new java.net.URI(s);
3311 if (uri.getScheme().toLowerCase().startsWith("http"))
3313 protocols.add(DataSourceType.URL);
3314 files.add(uri.toString());
3318 // otherwise preserve old behaviour: catch all for file objects
3319 java.io.File file = new java.io.File(uri);
3320 protocols.add(DataSourceType.FILE);
3321 files.add(file.toString());
3326 if (Cache.log.isDebugEnabled())
3328 if (data == null || !added)
3331 if (t.getTransferDataFlavors() != null
3332 && t.getTransferDataFlavors().length > 0)
3335 "Couldn't resolve drop data. Here are the supported flavors:");
3336 for (DataFlavor fl : t.getTransferDataFlavors())
3339 "Supported transfer dataflavor: " + fl.toString());
3340 Object df = t.getTransferData(fl);
3343 Cache.log.debug("Retrieves: " + df);
3347 Cache.log.debug("Retrieved nothing");
3353 Cache.log.debug("Couldn't resolve dataflavor for drop: "
3359 if (Platform.isWindowsAndNotJS())
3361 Cache.log.debug("Scanning dropped content for Windows Link Files");
3363 // resolve any .lnk files in the file drop
3364 for (int f = 0; f < files.size(); f++)
3366 String source = files.get(f).toString().toLowerCase();
3367 if (protocols.get(f).equals(DataSourceType.FILE)
3368 && (source.endsWith(".lnk") || source.endsWith(".url")
3369 || source.endsWith(".site")))
3373 Object obj = files.get(f);
3374 File lf = (obj instanceof File ? (File) obj
3375 : new File((String) obj));
3376 // process link file to get a URL
3377 Cache.log.debug("Found potential link file: " + lf);
3378 WindowsShortcut wscfile = new WindowsShortcut(lf);
3379 String fullname = wscfile.getRealFilename();
3380 protocols.set(f, FormatAdapter.checkProtocol(fullname));
3381 files.set(f, fullname);
3382 Cache.log.debug("Parsed real filename " + fullname
3383 + " to extract protocol: " + protocols.get(f));
3384 } catch (Exception ex)
3387 "Couldn't parse " + files.get(f) + " as a link file.",
3396 * Sets the Preferences property for experimental features to True or False
3397 * depending on the state of the controlling menu item
3400 protected void showExperimental_actionPerformed(boolean selected)
3402 Cache.setProperty(EXPERIMENTAL_FEATURES, Boolean.toString(selected));
3406 * Answers a (possibly empty) list of any structure viewer frames (currently
3407 * for either Jmol or Chimera) which are currently open. This may optionally
3408 * be restricted to viewers of a specified class, or viewers linked to a
3409 * specified alignment panel.
3412 * if not null, only return viewers linked to this panel
3413 * @param structureViewerClass
3414 * if not null, only return viewers of this class
3417 public List<StructureViewerBase> getStructureViewers(
3418 AlignmentPanel apanel,
3419 Class<? extends StructureViewerBase> structureViewerClass)
3421 List<StructureViewerBase> result = new ArrayList<>();
3422 JInternalFrame[] frames = getAllFrames();
3424 for (JInternalFrame frame : frames)
3426 if (frame instanceof StructureViewerBase)
3428 if (structureViewerClass == null
3429 || structureViewerClass.isInstance(frame))
3432 || ((StructureViewerBase) frame).isLinkedWith(apanel))
3434 result.add((StructureViewerBase) frame);