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)
845 setFrame(frame, title, w, h);
847 // Headless operation has no Desktop instance, only the static class.
849 if (getInstance() != null)
850 getInstance().addFrame(frame, Desktop.FRAME_MAKE_VISIBLE,
851 FRAME_ALLOW_RESIZE, FRAME_SET_MIN_SIZE_300);
855 * Add an internal frame to the Jalview desktop that may optionally be
856 * visible, resizable, and allowed to be any size
863 * When true, display frame immediately, otherwise, caller must call
864 * setVisible themselves.
871 * @param ignoreMinSize
872 * Do not set the default minimum size for frame
874 public static synchronized void addInternalFrame(
875 final JInternalFrame frame, String title, boolean makeVisible,
876 int w, int h, boolean resizable, boolean ignoreMinSize)
879 setFrame(frame, title, w, h);
881 if (getInstance() != null)
882 getInstance().addFrame(frame, makeVisible, resizable,
886 // These can now by put into a single int flag, if desired:
888 public final static boolean FRAME_MAKE_VISIBLE = true;
890 public final static boolean FRAME_NOT_VISIBLE = false;
892 public final static boolean FRAME_ALLOW_RESIZE = true;
894 public final static boolean FRAME_NOT_RESIZABLE = false;
896 public final static boolean FRAME_ALLOW_ANY_SIZE = true;
898 public final static boolean FRAME_SET_MIN_SIZE_300 = false;
900 private static void setFrame(JInternalFrame frame, String title,
903 // TODO: allow callers to determine X and Y position of frame (eg. via
905 // TODO: consider fixing method to update entries in the window submenu with
906 // the current window title
908 frame.setTitle(title);
909 if (frame.getWidth() < 1 || frame.getHeight() < 1)
915 private void addFrame(JInternalFrame frame,
916 boolean makeVisible, boolean resizable,
917 boolean ignoreMinSize)
922 boolean isEmbedded = (Platform.getEmbeddedAttribute(frame, "id") != null);
923 boolean hasEmbeddedSize = (Platform.getDimIfEmbedded(frame, -1, -1) != null);
924 // Web page embedding allows us to ignore minimum size
925 ignoreMinSize |= hasEmbeddedSize;
929 // Set default dimension for Alignment Frame window.
930 // The Alignment Frame window could be added from a number of places,
932 // I did this here in order not to miss out on any Alignment frame.
933 if (frame instanceof AlignFrame)
935 frame.setMinimumSize(new Dimension(ALIGN_FRAME_DEFAULT_MIN_WIDTH,
936 ALIGN_FRAME_DEFAULT_MIN_HEIGHT));
938 frame.setMinimumSize(
939 new Dimension(DEFAULT_MIN_WIDTH, DEFAULT_MIN_HEIGHT));
944 frame.setVisible(makeVisible);
945 frame.setClosable(true);
946 frame.setResizable(resizable);
947 frame.setMaximizable(resizable);
948 frame.setIconifiable(resizable);
949 frame.setOpaque(Platform.isJS());
950 if (!isEmbedded && frame.getX() < 1 && frame.getY() < 1)
952 frame.setLocation(xOffset * openFrameCount,
953 yOffset * ((openFrameCount - 1) % 10) + yOffset);
957 * add an entry for the new frame in the Window menu
958 * (and remove it when the frame is closed)
960 final JMenuItem menuItem = new JMenuItem(frame.getTitle());
961 frame.addInternalFrameListener(new InternalFrameAdapter()
964 public void internalFrameActivated(InternalFrameEvent evt)
966 JInternalFrame itf = getDesktopPane().getSelectedFrame();
969 if (itf instanceof AlignFrame)
971 Jalview.setCurrentAlignFrame((AlignFrame) itf);
978 public void internalFrameClosed(InternalFrameEvent evt)
980 PaintRefresher.RemoveComponent(frame);
983 * defensive check to prevent frames being
984 * added half off the window
986 if (openFrameCount > 0)
992 * ensure no reference to alignFrame retained by menu item listener
994 if (menuItem.getActionListeners().length > 0)
996 menuItem.removeActionListener(menuItem.getActionListeners()[0]);
998 getInstance().windowMenu.remove(menuItem);
1002 menuItem.addActionListener(new ActionListener()
1005 public void actionPerformed(ActionEvent e)
1009 frame.setSelected(true);
1010 frame.setIcon(false);
1011 } catch (java.beans.PropertyVetoException ex)
1013 // System.err.println(ex.toString());
1018 setKeyBindings(frame);
1020 getDesktopPane().add(frame);
1022 getInstance().windowMenu.add(menuItem);
1027 frame.setSelected(true);
1028 frame.requestFocus();
1029 } catch (java.beans.PropertyVetoException ve)
1031 } catch (java.lang.ClassCastException cex)
1034 "Squashed a possible GUI implementation error. If you can recreate this, please look at http://issues.jalview.org/browse/JAL-869",
1040 * Add key bindings to a JInternalFrame so that Ctrl-W and Cmd-W will close
1045 private static void setKeyBindings(JInternalFrame frame)
1047 final Action closeAction = new AbstractAction()
1050 public void actionPerformed(ActionEvent e)
1057 * set up key bindings for Ctrl-W and Cmd-W, with the same (Close) action
1059 KeyStroke ctrlWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
1060 InputEvent.CTRL_DOWN_MASK);
1061 KeyStroke cmdWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
1062 Platform.SHORTCUT_KEY_MASK);
1064 InputMap inputMap = frame
1065 .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
1066 String ctrlW = ctrlWKey.toString();
1067 inputMap.put(ctrlWKey, ctrlW);
1068 inputMap.put(cmdWKey, ctrlW);
1070 ActionMap actionMap = frame.getActionMap();
1071 actionMap.put(ctrlW, closeAction);
1075 public void lostOwnership(Clipboard clipboard, Transferable contents)
1079 jalviewClipboard = null;
1082 internalCopy = false;
1086 public void dragEnter(DropTargetDragEvent evt)
1091 public void dragExit(DropTargetEvent evt)
1096 public void dragOver(DropTargetDragEvent evt)
1101 public void dropActionChanged(DropTargetDragEvent evt)
1112 public void drop(DropTargetDropEvent evt)
1114 boolean success = true;
1115 // JAL-1552 - acceptDrop required before getTransferable call for
1116 // Java's Transferable for native dnd
1117 evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
1118 Transferable t = evt.getTransferable();
1119 List<Object> files = new ArrayList<>();
1120 List<DataSourceType> protocols = new ArrayList<>();
1124 transferFromDropTarget(files, protocols, evt, t);
1125 } catch (Exception e)
1127 e.printStackTrace();
1135 for (int i = 0; i < files.size(); i++)
1137 // BH 2018 File or String
1138 Object file = files.get(i);
1139 String fileName = file.toString();
1140 DataSourceType protocol = (protocols == null)
1141 ? DataSourceType.FILE
1143 FileFormatI format = null;
1145 if (fileName.endsWith(".jar"))
1147 format = FileFormat.Jalview;
1152 format = new IdentifyFile().identify(file, protocol);
1154 if (file instanceof File)
1156 Platform.cacheFileData((File) file);
1158 new FileLoader().LoadFile(null, file, protocol, format);
1161 } catch (Exception ex)
1166 evt.dropComplete(success); // need this to ensure input focus is properly
1167 // transfered to any new windows created
1177 public void inputLocalFileMenuItem_actionPerformed(AlignViewport viewport)
1179 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
1180 JalviewFileChooser chooser = JalviewFileChooser.forRead(
1181 Cache.getProperty("LAST_DIRECTORY"), fileFormat,
1182 BackupFiles.getEnabled());
1184 chooser.setFileView(new JalviewFileView());
1185 chooser.setDialogTitle(
1186 MessageManager.getString("label.open_local_file"));
1187 chooser.setToolTipText(MessageManager.getString("action.open"));
1189 chooser.setResponseHandler(0, new Runnable()
1194 File selectedFile = chooser.getSelectedFile();
1195 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1197 FileFormatI format = chooser.getSelectedFormat();
1200 * Call IdentifyFile to verify the file contains what its extension implies.
1201 * Skip this step for dynamically added file formats, because
1202 * IdentifyFile does not know how to recognise them.
1204 if (FileFormats.getInstance().isIdentifiable(format))
1208 format = new IdentifyFile().identify(selectedFile,
1209 DataSourceType.FILE);
1210 } catch (FileFormatException e)
1212 // format = null; //??
1216 new FileLoader().LoadFile(viewport, selectedFile,
1217 DataSourceType.FILE, format);
1220 chooser.showOpenDialog(this);
1224 * Shows a dialog for input of a URL at which to retrieve alignment data
1229 public void inputURLMenuItem_actionPerformed(AlignViewport viewport)
1231 // This construct allows us to have a wider textfield
1233 JLabel label = new JLabel(
1234 MessageManager.getString("label.input_file_url"));
1236 JPanel panel = new JPanel(new GridLayout(2, 1));
1240 * the URL to fetch is
1241 * Java: an editable combobox with history
1242 * JS: (pending JAL-3038) a plain text field
1245 String urlBase = "http://www.";
1246 if (Platform.isJS())
1248 history = new JTextField(urlBase, 35);
1257 JComboBox<String> asCombo = new JComboBox<>();
1258 asCombo.setPreferredSize(new Dimension(400, 20));
1259 asCombo.setEditable(true);
1260 asCombo.addItem(urlBase);
1261 String historyItems = Cache.getProperty("RECENT_URL");
1262 if (historyItems != null)
1264 for (String token : historyItems.split("\\t"))
1266 asCombo.addItem(token);
1273 Object[] options = new Object[] { MessageManager.getString("action.ok"),
1274 MessageManager.getString("action.cancel") };
1275 Runnable action = new Runnable()
1280 @SuppressWarnings("unchecked")
1281 String url = (history instanceof JTextField
1282 ? ((JTextField) history).getText()
1283 : ((JComboBox<String>) history).getSelectedItem()
1286 if (url.toLowerCase().endsWith(".jar"))
1288 if (viewport != null)
1290 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1291 FileFormat.Jalview);
1295 new FileLoader().LoadFile(url, DataSourceType.URL,
1296 FileFormat.Jalview);
1301 FileFormatI format = null;
1304 format = new IdentifyFile().identify(url, DataSourceType.URL);
1305 } catch (FileFormatException e)
1307 // TODO revise error handling, distinguish between
1308 // URL not found and response not valid
1313 String msg = MessageManager
1314 .formatMessage("label.couldnt_locate", url);
1315 JvOptionPane.showInternalMessageDialog(getDesktopPane(), msg,
1316 MessageManager.getString("label.url_not_found"),
1317 JvOptionPane.WARNING_MESSAGE);
1322 if (viewport != null)
1324 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1329 new FileLoader().LoadFile(url, DataSourceType.URL, format);
1334 String dialogOption = MessageManager
1335 .getString("label.input_alignment_from_url");
1336 JvOptionPane.newOptionDialog(desktopPane).setResponseHandler(0, action)
1337 .showInternalDialog(panel, dialogOption,
1338 JvOptionPane.YES_NO_CANCEL_OPTION,
1339 JvOptionPane.PLAIN_MESSAGE, null, options,
1340 MessageManager.getString("action.ok"));
1344 * Opens the CutAndPaste window for the user to paste an alignment in to
1347 * - if not null, the pasted alignment is added to the current
1348 * alignment; if null, to a new alignment window
1351 public void inputTextboxMenuItem_actionPerformed(
1352 AlignmentViewPanel viewPanel)
1354 CutAndPasteTransfer cap = new CutAndPasteTransfer();
1355 cap.setForInput(viewPanel);
1356 addInternalFrame(cap,
1357 MessageManager.getString("label.cut_paste_alignmen_file"),
1358 FRAME_MAKE_VISIBLE, 600, 500, FRAME_ALLOW_RESIZE,
1359 FRAME_SET_MIN_SIZE_300);
1368 Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
1369 Cache.setProperty("SCREENGEOMETRY_WIDTH", screen.width + "");
1370 Cache.setProperty("SCREENGEOMETRY_HEIGHT", screen.height + "");
1371 storeLastKnownDimensions("", new Rectangle(getBounds().x, getBounds().y,
1372 getWidth(), getHeight()));
1374 if (jconsole != null)
1376 storeLastKnownDimensions("JAVA_CONSOLE_", jconsole.getBounds());
1377 jconsole.stopConsole();
1381 storeLastKnownDimensions("JALVIEW_RSS_WINDOW_", jvnews.getBounds());
1384 if (dialogExecutor != null)
1386 dialogExecutor.shutdownNow();
1388 closeAll_actionPerformed(null);
1390 if (groovyConsole != null)
1392 // suppress a possible repeat prompt to save script
1393 groovyConsole.setDirty(false);
1394 groovyConsole.exit();
1399 private void storeLastKnownDimensions(String string, Rectangle jc)
1401 Cache.log.debug("Storing last known dimensions for " + string + ": x:"
1402 + jc.x + " y:" + jc.y + " width:" + jc.width + " height:"
1405 Cache.setProperty(string + "SCREEN_X", jc.x + "");
1406 Cache.setProperty(string + "SCREEN_Y", jc.y + "");
1407 Cache.setProperty(string + "SCREEN_WIDTH", jc.width + "");
1408 Cache.setProperty(string + "SCREEN_HEIGHT", jc.height + "");
1418 public void aboutMenuItem_actionPerformed(ActionEvent e)
1420 new Thread(new Runnable()
1425 new SplashScreen(false);
1431 * Returns the html text for the About screen, including any available version
1432 * number, build details, author details and citation reference, but without
1433 * the enclosing {@code html} tags
1437 public String getAboutMessage()
1439 StringBuilder message = new StringBuilder(1024);
1440 message.append("<h1><strong>Version: ")
1441 .append(Cache.getProperty("VERSION")).append("</strong></h1>")
1442 .append("<strong>Built: <em>")
1443 .append(Cache.getDefault("BUILD_DATE", "unknown"))
1444 .append("</em> from ").append(Cache.getBuildDetailsForSplash())
1445 .append("</strong>");
1447 String latestVersion = Cache.getDefault("LATEST_VERSION", "Checking");
1448 if (latestVersion.equals("Checking"))
1450 // JBP removed this message for 2.11: May be reinstated in future version
1451 // message.append("<br>...Checking latest version...</br>");
1453 else if (!latestVersion.equals(Cache.getProperty("VERSION")))
1455 boolean red = false;
1456 if (Cache.getProperty("VERSION").toLowerCase()
1457 .indexOf("automated build") == -1)
1460 // Displayed when code version and jnlp version do not match and code
1461 // version is not a development build
1462 message.append("<div style=\"color: #FF0000;font-style: bold;\">");
1465 message.append("<br>!! Version ")
1466 .append(Cache.getDefault("LATEST_VERSION", "..Checking.."))
1467 .append(" is available for download from ")
1468 .append(Cache.getDefault("www.jalview.org",
1469 "http://www.jalview.org"))
1473 message.append("</div>");
1476 message.append("<br>Authors: ");
1477 message.append(Cache.getDefault("AUTHORFNAMES", DEFAULT_AUTHORS));
1478 message.append(CITATION);
1480 return message.toString();
1484 * Action on requesting Help documentation
1487 public void documentationMenuItem_actionPerformed()
1491 if (Platform.isJS())
1493 BrowserLauncher.openURL("http://www.jalview.org/help.html");
1502 Help.showHelpWindow();
1504 } catch (Exception ex)
1506 System.err.println("Error opening help: " + ex.getMessage());
1511 public void closeAll_actionPerformed(ActionEvent e)
1513 // TODO show a progress bar while closing?
1514 JInternalFrame[] frames = desktopPane.getAllFrames();
1515 for (int i = 0; i < frames.length; i++)
1519 frames[i].setClosed(true);
1520 } catch (java.beans.PropertyVetoException ex)
1524 Jalview.setCurrentAlignFrame(null);
1525 System.out.println("ALL CLOSED");
1528 * reset state of singleton objects as appropriate (clear down session state
1529 * when all windows are closed)
1531 StructureSelectionManager ssm = StructureSelectionManager
1532 .getStructureSelectionManager(this);
1540 public void raiseRelated_actionPerformed(ActionEvent e)
1542 reorderAssociatedWindows(false, false);
1546 public void minimizeAssociated_actionPerformed(ActionEvent e)
1548 reorderAssociatedWindows(true, false);
1551 void closeAssociatedWindows()
1553 reorderAssociatedWindows(false, true);
1559 * @seejalview.jbgui.GDesktop#garbageCollect_actionPerformed(java.awt.event.
1563 protected void garbageCollect_actionPerformed(ActionEvent e)
1565 // We simply collect the garbage
1566 Cache.log.debug("Collecting garbage...");
1568 Cache.log.debug("Finished garbage collection.");
1575 * jalview.jbgui.GDesktop#showMemusage_actionPerformed(java.awt.event.ActionEvent
1579 protected void showMemusage_actionPerformed(ActionEvent e)
1581 desktopPane.showMemoryUsage(showMemusage.isSelected());
1588 * jalview.jbgui.GDesktop#showConsole_actionPerformed(java.awt.event.ActionEvent
1592 protected void showConsole_actionPerformed(ActionEvent e)
1594 showConsole(showConsole.isSelected());
1597 Console jconsole = null;
1600 * control whether the java console is visible or not
1604 void showConsole(boolean selected)
1606 // TODO: decide if we should update properties file
1607 if (jconsole != null) // BH 2018
1609 showConsole.setSelected(selected);
1610 Cache.setProperty("SHOW_JAVA_CONSOLE",
1611 Boolean.valueOf(selected).toString());
1612 jconsole.setVisible(selected);
1616 void reorderAssociatedWindows(boolean minimize, boolean close)
1618 JInternalFrame[] frames = desktopPane.getAllFrames();
1619 if (frames == null || frames.length < 1)
1624 AlignmentViewport source = null, target = null;
1625 if (frames[0] instanceof AlignFrame)
1627 source = ((AlignFrame) frames[0]).getCurrentView();
1629 else if (frames[0] instanceof TreePanel)
1631 source = ((TreePanel) frames[0]).getViewPort();
1633 else if (frames[0] instanceof PCAPanel)
1635 source = ((PCAPanel) frames[0]).av;
1637 else if (frames[0].getContentPane() instanceof PairwiseAlignPanel)
1639 source = ((PairwiseAlignPanel) frames[0].getContentPane()).av;
1644 for (int i = 0; i < frames.length; i++)
1647 if (frames[i] == null)
1651 if (frames[i] instanceof AlignFrame)
1653 target = ((AlignFrame) frames[i]).getCurrentView();
1655 else if (frames[i] instanceof TreePanel)
1657 target = ((TreePanel) frames[i]).getViewPort();
1659 else if (frames[i] instanceof PCAPanel)
1661 target = ((PCAPanel) frames[i]).av;
1663 else if (frames[i].getContentPane() instanceof PairwiseAlignPanel)
1665 target = ((PairwiseAlignPanel) frames[i].getContentPane()).av;
1668 if (source == target)
1674 frames[i].setClosed(true);
1678 frames[i].setIcon(minimize);
1681 frames[i].toFront();
1685 } catch (java.beans.PropertyVetoException ex)
1700 protected void preferences_actionPerformed(ActionEvent e)
1706 * Prompts the user to choose a file and then saves the Jalview state as a
1707 * Jalview project file
1710 public void saveState_actionPerformed()
1712 saveState_actionPerformed(false);
1715 public void saveState_actionPerformed(boolean saveAs)
1717 java.io.File projectFile = getProjectFile();
1718 // autoSave indicates we already have a file and don't need to ask
1719 boolean autoSave = projectFile != null && !saveAs
1720 && BackupFiles.getEnabled();
1722 // System.out.println("autoSave="+autoSave+", projectFile='"+projectFile+"',
1723 // saveAs="+saveAs+", Backups
1724 // "+(BackupFiles.getEnabled()?"enabled":"disabled"));
1726 boolean approveSave = false;
1729 JalviewFileChooser chooser = new JalviewFileChooser("jvp",
1732 chooser.setFileView(new JalviewFileView());
1733 chooser.setDialogTitle(MessageManager.getString("label.save_state"));
1735 int value = chooser.showSaveDialog(this);
1737 if (value == JalviewFileChooser.APPROVE_OPTION)
1739 projectFile = chooser.getSelectedFile();
1740 setProjectFile(projectFile);
1745 if (approveSave || autoSave)
1747 final Desktop me = this;
1748 final java.io.File chosenFile = projectFile;
1749 new Thread(new Runnable()
1754 // TODO: refactor to Jalview desktop session controller action.
1755 setProgressBar(MessageManager.formatMessage(
1756 "label.saving_jalview_project", new Object[]
1757 { chosenFile.getName() }), chosenFile.hashCode());
1758 Cache.setProperty("LAST_DIRECTORY", chosenFile.getParent());
1759 // TODO catch and handle errors for savestate
1760 // TODO prevent user from messing with the Desktop whilst we're saving
1763 boolean doBackup = BackupFiles.getEnabled();
1764 BackupFiles backupfiles = doBackup ? new BackupFiles(chosenFile)
1767 new Jalview2XML().saveState(
1768 doBackup ? backupfiles.getTempFile() : chosenFile);
1772 backupfiles.setWriteSuccess(true);
1773 backupfiles.rollBackupsAndRenameTempFile();
1775 } catch (OutOfMemoryError oom)
1777 new OOMWarning("Whilst saving current state to "
1778 + chosenFile.getName(), oom);
1779 } catch (Exception ex)
1781 Cache.log.error("Problems whilst trying to save to "
1782 + chosenFile.getName(), ex);
1783 JvOptionPane.showMessageDialog(me,
1784 MessageManager.formatMessage(
1785 "label.error_whilst_saving_current_state_to",
1787 { chosenFile.getName() }),
1788 MessageManager.getString("label.couldnt_save_project"),
1789 JvOptionPane.WARNING_MESSAGE);
1791 setProgressBar(null, chosenFile.hashCode());
1798 public void saveAsState_actionPerformed(ActionEvent e)
1800 saveState_actionPerformed(true);
1803 private void setProjectFile(File choice)
1805 this.projectFile = choice;
1808 public File getProjectFile()
1810 return this.projectFile;
1814 * Shows a file chooser dialog and tries to read in the selected file as a
1818 public void loadState_actionPerformed()
1820 final String[] suffix = new String[] { "jvp", "jar" };
1821 final String[] desc = new String[] { "Jalview Project",
1822 "Jalview Project (old)" };
1823 JalviewFileChooser chooser = new JalviewFileChooser(
1824 Cache.getProperty("LAST_DIRECTORY"), suffix, desc,
1825 "Jalview Project", true, BackupFiles.getEnabled()); // last two
1829 chooser.setFileView(new JalviewFileView());
1830 chooser.setDialogTitle(MessageManager.getString("label.restore_state"));
1831 chooser.setResponseHandler(0, new Runnable()
1836 File selectedFile = chooser.getSelectedFile();
1837 setProjectFile(selectedFile);
1838 String choice = selectedFile.getAbsolutePath();
1839 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1840 new Thread(new Runnable()
1847 new Jalview2XML().loadJalviewAlign(selectedFile);
1848 } catch (OutOfMemoryError oom)
1850 new OOMWarning("Whilst loading project from " + choice, oom);
1851 } catch (Exception ex)
1854 "Problems whilst loading project from " + choice, ex);
1855 JvOptionPane.showMessageDialog(getDesktopPane(),
1856 MessageManager.formatMessage(
1857 "label.error_whilst_loading_project_from",
1861 .getString("label.couldnt_load_project"),
1862 JvOptionPane.WARNING_MESSAGE);
1869 chooser.showOpenDialog(this);
1873 public void inputSequence_actionPerformed(ActionEvent e)
1875 new SequenceFetcher(this);
1878 JPanel progressPanel;
1880 ArrayList<JPanel> fileLoadingPanels = new ArrayList<>();
1882 public void startLoading(final Object fileName)
1884 if (fileLoadingCount == 0)
1886 fileLoadingPanels.add(addProgressPanel(MessageManager
1887 .formatMessage("label.loading_file", new Object[]
1893 private JPanel addProgressPanel(String string)
1895 if (progressPanel == null)
1897 progressPanel = new JPanel(new GridLayout(1, 1));
1898 totalProgressCount = 0;
1899 getContentPane().add(progressPanel, BorderLayout.SOUTH);
1901 JPanel thisprogress = new JPanel(new BorderLayout(10, 5));
1902 JProgressBar progressBar = new JProgressBar();
1903 progressBar.setIndeterminate(true);
1905 thisprogress.add(new JLabel(string), BorderLayout.WEST);
1907 thisprogress.add(progressBar, BorderLayout.CENTER);
1908 progressPanel.add(thisprogress);
1909 ((GridLayout) progressPanel.getLayout()).setRows(
1910 ((GridLayout) progressPanel.getLayout()).getRows() + 1);
1911 ++totalProgressCount;
1913 return thisprogress;
1916 int totalProgressCount = 0;
1918 private void removeProgressPanel(JPanel progbar)
1920 if (progressPanel != null)
1922 synchronized (progressPanel)
1924 progressPanel.remove(progbar);
1925 GridLayout gl = (GridLayout) progressPanel.getLayout();
1926 gl.setRows(gl.getRows() - 1);
1927 if (--totalProgressCount < 1)
1929 this.getContentPane().remove(progressPanel);
1930 progressPanel = null;
1937 public void stopLoading()
1940 if (fileLoadingCount < 1)
1942 while (fileLoadingPanels.size() > 0)
1944 removeProgressPanel(fileLoadingPanels.remove(0));
1946 fileLoadingPanels.clear();
1947 fileLoadingCount = 0;
1952 public static int getViewCount(String alignmentId)
1954 AlignmentViewport[] aps = getViewports(alignmentId);
1955 return (aps == null) ? 0 : aps.length;
1960 * @param alignmentId
1961 * - if null, all sets are returned
1962 * @return all AlignmentPanels concerning the alignmentId sequence set
1964 public static AlignmentPanel[] getAlignmentPanels(String alignmentId)
1966 if (getDesktopPane() == null)
1968 // no frames created and in headless mode
1969 // TODO: verify that frames are recoverable when in headless mode
1972 List<AlignmentPanel> aps = new ArrayList<>();
1973 AlignFrame[] frames = getAlignFrames();
1978 for (AlignFrame af : frames)
1980 for (AlignmentPanel ap : af.alignPanels)
1982 if (alignmentId == null
1983 || alignmentId.equals(ap.av.getSequenceSetId()))
1989 if (aps.size() == 0)
1993 AlignmentPanel[] vap = aps.toArray(new AlignmentPanel[aps.size()]);
1998 * get all the viewports on an alignment.
2000 * @param sequenceSetId
2001 * unique alignment id (may be null - all viewports returned in that
2003 * @return all viewports on the alignment bound to sequenceSetId
2005 public static AlignmentViewport[] getViewports(String sequenceSetId)
2007 List<AlignmentViewport> viewp = new ArrayList<>();
2008 if (getDesktopPane() != null)
2010 AlignFrame[] frames = getAlignFrames();
2012 for (AlignFrame afr : frames)
2014 if (sequenceSetId == null || afr.getViewport().getSequenceSetId()
2015 .equals(sequenceSetId))
2017 if (afr.alignPanels != null)
2019 for (AlignmentPanel ap : afr.alignPanels)
2021 if (sequenceSetId == null
2022 || sequenceSetId.equals(ap.av.getSequenceSetId()))
2030 viewp.add(afr.getViewport());
2034 if (viewp.size() > 0)
2036 return viewp.toArray(new AlignmentViewport[viewp.size()]);
2043 * Explode the views in the given frame into separate AlignFrame
2047 public static void explodeViews(AlignFrame af)
2049 int size = af.alignPanels.size();
2055 // FIXME: ideally should use UI interface API
2056 FeatureSettings viewFeatureSettings = (af.featureSettings != null
2057 && af.featureSettings.isOpen()) ? af.featureSettings : null;
2058 Rectangle fsBounds = af.getFeatureSettingsGeometry();
2059 for (int i = 0; i < size; i++)
2061 AlignmentPanel ap = af.alignPanels.get(i);
2063 AlignFrame newaf = new AlignFrame(ap);
2065 // transfer reference for existing feature settings to new alignFrame
2066 if (ap == af.alignPanel)
2068 if (viewFeatureSettings != null && viewFeatureSettings.fr.ap == ap)
2070 newaf.featureSettings = viewFeatureSettings;
2072 newaf.setFeatureSettingsGeometry(fsBounds);
2076 * Restore the view's last exploded frame geometry if known. Multiple
2077 * views from one exploded frame share and restore the same (frame)
2078 * position and size.
2080 Rectangle geometry = ap.av.getExplodedGeometry();
2081 if (geometry != null)
2083 newaf.setBounds(geometry);
2086 ap.av.setGatherViewsHere(false);
2088 addInternalFrame(newaf, af.getTitle(), AlignFrame.DEFAULT_WIDTH,
2089 AlignFrame.DEFAULT_HEIGHT);
2090 // and materialise a new feature settings dialog instance for the new
2092 // (closes the old as if 'OK' was pressed)
2093 if (ap == af.alignPanel && newaf.featureSettings != null
2094 && newaf.featureSettings.isOpen()
2095 && af.alignPanel.getAlignViewport().isShowSequenceFeatures())
2097 newaf.showFeatureSettingsUI();
2101 af.featureSettings = null;
2102 af.alignPanels.clear();
2103 af.closeMenuItem_actionPerformed(true);
2108 * Gather expanded views (separate AlignFrame's) with the same sequence set
2109 * identifier back in to this frame as additional views, and close the
2110 * expanded views. Note the expanded frames may themselves have multiple
2111 * views. We take the lot.
2115 public void gatherViews(AlignFrame source)
2117 source.viewport.setGatherViewsHere(true);
2118 source.viewport.setExplodedGeometry(source.getBounds());
2119 JInternalFrame[] frames = desktopPane.getAllFrames();
2120 String viewId = source.viewport.getSequenceSetId();
2121 for (int t = 0; t < frames.length; t++)
2123 if (frames[t] instanceof AlignFrame && frames[t] != source)
2125 AlignFrame af = (AlignFrame) frames[t];
2126 boolean gatherThis = false;
2127 for (int a = 0; a < af.alignPanels.size(); a++)
2129 AlignmentPanel ap = af.alignPanels.get(a);
2130 if (viewId.equals(ap.av.getSequenceSetId()))
2133 ap.av.setGatherViewsHere(false);
2134 ap.av.setExplodedGeometry(af.getBounds());
2135 source.addAlignmentPanel(ap, false);
2141 if (af.featureSettings != null && af.featureSettings.isOpen())
2143 if (source.featureSettings == null)
2145 // preserve the feature settings geometry for this frame
2146 source.featureSettings = af.featureSettings;
2147 source.setFeatureSettingsGeometry(
2148 af.getFeatureSettingsGeometry());
2152 // close it and forget
2153 af.featureSettings.close();
2156 af.alignPanels.clear();
2157 af.closeMenuItem_actionPerformed(true);
2162 // refresh the feature setting UI for the source frame if it exists
2163 if (source.featureSettings != null && source.featureSettings.isOpen())
2165 source.showFeatureSettingsUI();
2169 public JInternalFrame[] getAllFrames()
2171 return desktopPane.getAllFrames();
2175 * Checks the given url to see if it gives a response indicating that the user
2176 * should be informed of a new questionnaire.
2180 public void checkForQuestionnaire(String url)
2182 UserQuestionnaireCheck jvq = new UserQuestionnaireCheck(url);
2183 // javax.swing.SwingUtilities.invokeLater(jvq);
2184 new Thread(jvq).start();
2187 public void checkURLLinks()
2189 // Thread off the URL link checker
2190 addDialogThread(new Runnable()
2195 if (Cache.getDefault("CHECKURLLINKS", true))
2197 // check what the actual links are - if it's just the default don't
2198 // bother with the warning
2199 List<String> links = Preferences.sequenceUrlLinks
2202 // only need to check links if there is one with a
2203 // SEQUENCE_ID which is not the default EMBL_EBI link
2204 ListIterator<String> li = links.listIterator();
2205 boolean check = false;
2206 List<JLabel> urls = new ArrayList<>();
2207 while (li.hasNext())
2209 String link = li.next();
2210 if (link.contains(jalview.util.UrlConstants.SEQUENCE_ID)
2211 && !UrlConstants.isDefaultString(link))
2214 int barPos = link.indexOf("|");
2215 String urlMsg = barPos == -1 ? link
2216 : link.substring(0, barPos) + ": "
2217 + link.substring(barPos + 1);
2218 urls.add(new JLabel(urlMsg));
2226 // ask user to check in case URL links use old style tokens
2227 // ($SEQUENCE_ID$ for sequence id _or_ accession id)
2228 JPanel msgPanel = new JPanel();
2229 msgPanel.setLayout(new BoxLayout(msgPanel, BoxLayout.PAGE_AXIS));
2230 msgPanel.add(Box.createVerticalGlue());
2231 JLabel msg = new JLabel(MessageManager
2232 .getString("label.SEQUENCE_ID_for_DB_ACCESSION1"));
2233 JLabel msg2 = new JLabel(MessageManager
2234 .getString("label.SEQUENCE_ID_for_DB_ACCESSION2"));
2236 for (JLabel url : urls)
2242 final JCheckBox jcb = new JCheckBox(
2243 MessageManager.getString("label.do_not_display_again"));
2244 jcb.addActionListener(new ActionListener()
2247 public void actionPerformed(ActionEvent e)
2249 // update Cache settings for "don't show this again"
2250 boolean showWarningAgain = !jcb.isSelected();
2251 Cache.setProperty("CHECKURLLINKS",
2252 Boolean.valueOf(showWarningAgain).toString());
2257 JvOptionPane.showMessageDialog(desktopPane, msgPanel,
2259 .getString("label.SEQUENCE_ID_no_longer_used"),
2260 JvOptionPane.WARNING_MESSAGE);
2267 * Proxy class for JDesktopPane which optionally displays the current memory
2268 * usage and highlights the desktop area with a red bar if free memory runs
2273 public class MyDesktopPane extends JDesktopPane implements Runnable
2275 private static final float ONE_MB = 1048576f;
2277 boolean showMemoryUsage = false;
2281 java.text.NumberFormat df;
2283 float maxMemory, allocatedMemory, freeMemory, totalFreeMemory,
2286 public MyDesktopPane(boolean showMemoryUsage)
2288 showMemoryUsage(showMemoryUsage);
2291 public void showMemoryUsage(boolean showMemory)
2293 this.showMemoryUsage = showMemory;
2296 Thread worker = new Thread(this);
2302 public boolean isShowMemoryUsage()
2304 return showMemoryUsage;
2310 df = java.text.NumberFormat.getNumberInstance();
2311 df.setMaximumFractionDigits(2);
2312 runtime = Runtime.getRuntime();
2314 while (showMemoryUsage)
2318 maxMemory = runtime.maxMemory() / ONE_MB;
2319 allocatedMemory = runtime.totalMemory() / ONE_MB;
2320 freeMemory = runtime.freeMemory() / ONE_MB;
2321 totalFreeMemory = freeMemory + (maxMemory - allocatedMemory);
2323 percentUsage = (totalFreeMemory / maxMemory) * 100;
2325 // if (percentUsage < 20)
2327 // border1 = BorderFactory.createMatteBorder(12, 12, 12, 12,
2329 // instance.set.setBorder(border1);
2332 // sleep after showing usage
2334 } catch (Exception ex)
2336 ex.printStackTrace();
2342 public void paintComponent(Graphics g)
2344 if (showMemoryUsage && g != null && df != null)
2346 if (percentUsage < 20)
2348 g.setColor(Color.red);
2350 FontMetrics fm = g.getFontMetrics();
2353 g.drawString(MessageManager.formatMessage("label.memory_stats",
2355 { df.format(totalFreeMemory), df.format(maxMemory),
2356 df.format(percentUsage) }),
2357 10, getHeight() - fm.getHeight());
2364 * Accessor method to quickly get all the AlignmentFrames loaded.
2366 * @return an array of AlignFrame, or null if none found
2368 public static AlignFrame[] getAlignFrames()
2370 if (Jalview.isHeadlessMode())
2372 return new AlignFrame[] { Jalview.getInstance().currentAlignFrame };
2375 JInternalFrame[] frames = getDesktopPane().getAllFrames();
2381 List<AlignFrame> avp = new ArrayList<>();
2383 for (int i = frames.length - 1; i > -1; i--)
2385 if (frames[i] instanceof AlignFrame)
2387 avp.add((AlignFrame) frames[i]);
2389 else if (frames[i] instanceof SplitFrame)
2392 * Also check for a split frame containing an AlignFrame
2394 GSplitFrame sf = (GSplitFrame) frames[i];
2395 if (sf.getTopFrame() instanceof AlignFrame)
2397 avp.add((AlignFrame) sf.getTopFrame());
2399 if (sf.getBottomFrame() instanceof AlignFrame)
2401 avp.add((AlignFrame) sf.getBottomFrame());
2405 if (avp.size() == 0)
2409 AlignFrame afs[] = avp.toArray(new AlignFrame[avp.size()]);
2414 * Returns an array of any AppJmol frames in the Desktop (or null if none).
2418 public GStructureViewer[] getJmols()
2420 JInternalFrame[] frames = desktopPane.getAllFrames();
2426 List<GStructureViewer> avp = new ArrayList<>();
2428 for (int i = frames.length - 1; i > -1; i--)
2430 if (frames[i] instanceof AppJmol)
2432 GStructureViewer af = (GStructureViewer) frames[i];
2436 if (avp.size() == 0)
2440 GStructureViewer afs[] = avp.toArray(new GStructureViewer[avp.size()]);
2445 * Add Groovy Support to Jalview
2448 public void groovyShell_actionPerformed()
2452 openGroovyConsole();
2453 } catch (Exception ex)
2455 Cache.log.error("Groovy Shell Creation failed.", ex);
2456 JvOptionPane.showInternalMessageDialog(desktopPane,
2458 MessageManager.getString("label.couldnt_create_groovy_shell"),
2459 MessageManager.getString("label.groovy_support_failed"),
2460 JvOptionPane.ERROR_MESSAGE);
2465 * Open the Groovy console
2467 void openGroovyConsole()
2469 if (groovyConsole == null)
2471 groovyConsole = new groovy.ui.Console();
2472 groovyConsole.setVariable("Jalview", this);
2473 groovyConsole.run();
2476 * We allow only one console at a time, so that AlignFrame menu option
2477 * 'Calculate | Run Groovy script' is unambiguous.
2478 * Disable 'Groovy Console', and enable 'Run script', when the console is
2479 * opened, and the reverse when it is closed
2481 Window window = (Window) groovyConsole.getFrame();
2482 window.addWindowListener(new WindowAdapter()
2485 public void windowClosed(WindowEvent e)
2488 * rebind CMD-Q from Groovy Console to Jalview Quit
2491 enableExecuteGroovy(false);
2497 * show Groovy console window (after close and reopen)
2499 ((Window) groovyConsole.getFrame()).setVisible(true);
2502 * if we got this far, enable 'Run Groovy' in AlignFrame menus
2503 * and disable opening a second console
2505 enableExecuteGroovy(true);
2509 * Bind Ctrl/Cmd-Q to Quit - for reset as Groovy Console takes over this
2510 * binding when opened
2512 protected void addQuitHandler()
2515 getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
2516 .put(KeyStroke.getKeyStroke(KeyEvent.VK_Q,
2517 Platform.SHORTCUT_KEY_MASK),
2519 getRootPane().getActionMap().put("Quit", new AbstractAction()
2522 public void actionPerformed(ActionEvent e)
2530 * Enable or disable 'Run Groovy script' in AlignFrame calculate menus
2533 * true if Groovy console is open
2535 public void enableExecuteGroovy(boolean enabled)
2538 * disable opening a second Groovy console
2539 * (or re-enable when the console is closed)
2541 groovyShell.setEnabled(!enabled);
2543 AlignFrame[] alignFrames = getAlignFrames();
2544 if (alignFrames != null)
2546 for (AlignFrame af : alignFrames)
2548 af.setGroovyEnabled(enabled);
2554 * Progress bars managed by the IProgressIndicator method.
2556 private Hashtable<Long, JPanel> progressBars;
2558 private Hashtable<Long, IProgressIndicatorHandler> progressBarHandlers;
2563 * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
2566 public void setProgressBar(String message, long id)
2568 // Platform.timeCheck("Desktop " + message, Platform.TIME_MARK);
2570 if (progressBars == null)
2572 progressBars = new Hashtable<>();
2573 progressBarHandlers = new Hashtable<>();
2576 if (progressBars.get(Long.valueOf(id)) != null)
2578 JPanel panel = progressBars.remove(Long.valueOf(id));
2579 if (progressBarHandlers.contains(Long.valueOf(id)))
2581 progressBarHandlers.remove(Long.valueOf(id));
2583 removeProgressPanel(panel);
2587 progressBars.put(Long.valueOf(id), addProgressPanel(message));
2594 * @see jalview.gui.IProgressIndicator#registerHandler(long,
2595 * jalview.gui.IProgressIndicatorHandler)
2598 public void registerHandler(final long id,
2599 final IProgressIndicatorHandler handler)
2601 if (progressBarHandlers == null
2602 || !progressBars.containsKey(Long.valueOf(id)))
2604 throw new Error(MessageManager.getString(
2605 "error.call_setprogressbar_before_registering_handler"));
2607 progressBarHandlers.put(Long.valueOf(id), handler);
2608 final JPanel progressPanel = progressBars.get(Long.valueOf(id));
2609 if (handler.canCancel())
2611 JButton cancel = new JButton(
2612 MessageManager.getString("action.cancel"));
2613 final IProgressIndicator us = this;
2614 cancel.addActionListener(new ActionListener()
2618 public void actionPerformed(ActionEvent e)
2620 handler.cancelActivity(id);
2621 us.setProgressBar(MessageManager
2622 .formatMessage("label.cancelled_params", new Object[]
2623 { ((JLabel) progressPanel.getComponent(0)).getText() }),
2627 progressPanel.add(cancel, BorderLayout.EAST);
2633 * @return true if any progress bars are still active
2636 public boolean operationInProgress()
2638 if (progressBars != null && progressBars.size() > 0)
2646 * This will return the first AlignFrame holding the given viewport instance.
2647 * It will break if there are more than one AlignFrames viewing a particular
2651 * @return alignFrame for viewport
2653 public static AlignFrame getAlignFrameFor(AlignViewportI viewport)
2655 if (getDesktopPane() != null)
2657 AlignmentPanel[] aps = getAlignmentPanels(
2658 viewport.getSequenceSetId());
2659 for (int panel = 0; aps != null && panel < aps.length; panel++)
2661 if (aps[panel] != null && aps[panel].av == viewport)
2663 return aps[panel].alignFrame;
2670 public VamsasApplication getVamsasApplication()
2672 // TODO: JAL-3311 remove remaining code from Jalview relating to VAMSAS
2678 * flag set if jalview GUI is being operated programmatically
2680 private boolean inBatchMode = false;
2683 * check if jalview GUI is being operated programmatically
2685 * @return inBatchMode
2687 public boolean isInBatchMode()
2693 * set flag if jalview GUI is being operated programmatically
2695 * @param inBatchMode
2697 public void setInBatchMode(boolean inBatchMode)
2699 this.inBatchMode = inBatchMode;
2702 public void startServiceDiscovery()
2704 startServiceDiscovery(false);
2707 public void startServiceDiscovery(boolean blocking)
2709 boolean alive = true;
2710 Thread t0 = null, t1 = null, t2 = null;
2711 // JAL-940 - JALVIEW 1 services are now being EOLed as of JABA 2.1 release
2714 // todo: changesupport handlers need to be transferred
2715 if (discoverer == null)
2717 discoverer = jalview.ws.jws1.Discoverer.getInstance();
2718 // register PCS handler for desktop.
2719 discoverer.addPropertyChangeListener(changeSupport);
2721 // JAL-940 - disabled JWS1 service configuration - always start discoverer
2722 // until we phase out completely
2723 (t0 = new Thread(discoverer)).start();
2726 if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
2728 t2 = jalview.ws.jws2.Jws2Discoverer.getInstance()
2729 .startDiscoverer(changeSupport);
2733 // TODO: do rest service discovery
2742 } catch (Exception e)
2745 alive = (t1 != null && t1.isAlive()) || (t2 != null && t2.isAlive())
2746 || (t3 != null && t3.isAlive())
2747 || (t0 != null && t0.isAlive());
2753 * called to check if the service discovery process completed successfully.
2757 protected void JalviewServicesChanged(PropertyChangeEvent evt)
2759 if (evt.getNewValue() == null || evt.getNewValue() instanceof Vector)
2761 final String ermsg = jalview.ws.jws2.Jws2Discoverer.getInstance()
2762 .getErrorMessages();
2765 if (Cache.getDefault("SHOW_WSDISCOVERY_ERRORS", true))
2767 if (serviceChangedDialog == null)
2769 // only run if we aren't already displaying one of these.
2770 addDialogThread(serviceChangedDialog = new Runnable()
2777 * JalviewDialog jd =new JalviewDialog() {
2779 * @Override protected void cancelPressed() { // TODO
2780 * Auto-generated method stub
2782 * }@Override protected void okPressed() { // TODO
2783 * Auto-generated method stub
2785 * }@Override protected void raiseClosed() { // TODO
2786 * Auto-generated method stub
2788 * } }; jd.initDialogFrame(new
2789 * JLabel("<html><table width=\"450\"><tr><td>" + ermsg +
2790 * "<br/>It may be that you have invalid JABA URLs in your web service preferences,"
2791 * + " or mis-configured HTTP proxy settings.<br/>" +
2792 * "Check the <em>Connections</em> and <em>Web services</em> tab of the"
2794 * " Tools->Preferences dialog box to change them.</td></tr></table></html>"
2795 * ), true, true, "Web Service Configuration Problem", 450,
2798 * jd.waitForInput();
2800 JvOptionPane.showConfirmDialog(desktopPane,
2801 new JLabel("<html><table width=\"450\"><tr><td>"
2802 + ermsg + "</td></tr></table>"
2803 + "<p>It may be that you have invalid JABA URLs<br/>in your web service preferences,"
2804 + "<br>or as a command-line argument, or mis-configured HTTP proxy settings.</p>"
2805 + "<p>Check the <em>Connections</em> and <em>Web services</em> tab<br/>of the"
2806 + " Tools->Preferences dialog box to change them.</p></html>"),
2807 "Web Service Configuration Problem",
2808 JvOptionPane.DEFAULT_OPTION,
2809 JvOptionPane.ERROR_MESSAGE);
2810 serviceChangedDialog = null;
2819 "Errors reported by JABA discovery service. Check web services preferences.\n"
2826 private Runnable serviceChangedDialog = null;
2829 * start a thread to open a URL in the configured browser. Pops up a warning
2830 * dialog to the user if there is an exception when calling out to the browser
2835 public static void showUrl(final String url)
2837 showUrl(url, getInstance());
2841 * Like showUrl but allows progress handler to be specified
2845 * (null) or object implementing IProgressIndicator
2847 public static void showUrl(final String url,
2848 final IProgressIndicator progress)
2850 new Thread(new Runnable()
2857 if (progress != null)
2859 progress.setProgressBar(MessageManager
2860 .formatMessage("status.opening_params", new Object[]
2861 { url }), this.hashCode());
2863 jalview.util.BrowserLauncher.openURL(url);
2864 } catch (Exception ex)
2866 JvOptionPane.showInternalMessageDialog(getDesktopPane(),
2868 .getString("label.web_browser_not_found_unix"),
2869 MessageManager.getString("label.web_browser_not_found"),
2870 JvOptionPane.WARNING_MESSAGE);
2872 ex.printStackTrace();
2874 if (progress != null)
2876 progress.setProgressBar(null, this.hashCode());
2882 public static WsParamSetManager wsparamManager = null;
2884 public static ParamManager getUserParameterStore()
2886 if (wsparamManager == null)
2888 wsparamManager = new WsParamSetManager();
2890 return wsparamManager;
2894 * static hyperlink handler proxy method for use by Jalview's internal windows
2898 public static void hyperlinkUpdate(HyperlinkEvent e)
2900 if (e.getEventType() == EventType.ACTIVATED)
2905 url = e.getURL().toString();
2907 } catch (Exception x)
2911 if (Cache.log != null)
2913 Cache.log.error("Couldn't handle string " + url + " as a URL.");
2918 "Couldn't handle string " + url + " as a URL.");
2921 // ignore any exceptions due to dud links.
2928 * single thread that handles display of dialogs to user.
2930 ExecutorService dialogExecutor = Executors.newSingleThreadExecutor();
2933 * flag indicating if dialogExecutor should try to acquire a permit
2935 private volatile boolean dialogPause = true;
2940 private java.util.concurrent.Semaphore block = new Semaphore(0);
2942 private static groovy.ui.Console groovyConsole;
2945 * add another dialog thread to the queue
2949 public void addDialogThread(final Runnable prompter)
2951 dialogExecutor.submit(new Runnable()
2961 } catch (InterruptedException x)
2965 if (Jalview.isHeadlessMode())
2971 SwingUtilities.invokeAndWait(prompter);
2972 } catch (Exception q)
2974 Cache.log.warn("Unexpected Exception in dialog thread.", q);
2980 public void startDialogQueue()
2982 // set the flag so we don't pause waiting for another permit and semaphore
2983 // the current task to begin
2984 dialogPause = false;
2989 * Outputs an image of the desktop to file in EPS format, after prompting the
2990 * user for choice of Text or Lineart character rendering (unless a preference
2991 * has been set). The file name is generated as
2994 * Jalview_snapshot_nnnnn.eps where nnnnn is the current timestamp in milliseconds
2998 protected void snapShotWindow_actionPerformed(ActionEvent e)
3000 // currently the menu option to do this is not shown
3003 int width = getWidth();
3004 int height = getHeight();
3006 "Jalview_snapshot_" + System.currentTimeMillis() + ".eps");
3007 ImageWriterI writer = new ImageWriterI()
3010 public void exportImage(Graphics g) throws Exception
3013 Cache.log.info("Successfully written snapshot to file "
3014 + of.getAbsolutePath());
3017 String title = "View of desktop";
3018 ImageExporter exporter = new ImageExporter(writer, null, TYPE.EPS,
3020 exporter.doExport(of, this, width, height, title);
3024 * Explode the views in the given SplitFrame into separate SplitFrame windows.
3025 * This respects (remembers) any previous 'exploded geometry' i.e. the size
3026 * and location last time the view was expanded (if any). However it does not
3027 * remember the split pane divider location - this is set to match the
3028 * 'exploding' frame.
3032 public void explodeViews(SplitFrame sf)
3034 AlignFrame oldTopFrame = (AlignFrame) sf.getTopFrame();
3035 AlignFrame oldBottomFrame = (AlignFrame) sf.getBottomFrame();
3036 List<? extends AlignmentViewPanel> topPanels = oldTopFrame
3038 List<? extends AlignmentViewPanel> bottomPanels = oldBottomFrame
3040 int viewCount = topPanels.size();
3047 * Processing in reverse order works, forwards order leaves the first panels
3048 * not visible. I don't know why!
3050 for (int i = viewCount - 1; i >= 0; i--)
3053 * Make new top and bottom frames. These take over the respective
3054 * AlignmentPanel objects, including their AlignmentViewports, so the
3055 * cdna/protein relationships between the viewports is carried over to the
3058 * explodedGeometry holds the (x, y) position of the previously exploded
3059 * SplitFrame, and the (width, height) of the AlignFrame component
3061 AlignmentPanel topPanel = (AlignmentPanel) topPanels.get(i);
3062 AlignFrame newTopFrame = new AlignFrame(topPanel);
3063 newTopFrame.setSize(oldTopFrame.getSize());
3064 newTopFrame.setVisible(true);
3065 Rectangle geometry = ((AlignViewport) topPanel.getAlignViewport())
3066 .getExplodedGeometry();
3067 if (geometry != null)
3069 newTopFrame.setSize(geometry.getSize());
3072 AlignmentPanel bottomPanel = (AlignmentPanel) bottomPanels.get(i);
3073 AlignFrame newBottomFrame = new AlignFrame(bottomPanel);
3074 newBottomFrame.setSize(oldBottomFrame.getSize());
3075 newBottomFrame.setVisible(true);
3076 geometry = ((AlignViewport) bottomPanel.getAlignViewport())
3077 .getExplodedGeometry();
3078 if (geometry != null)
3080 newBottomFrame.setSize(geometry.getSize());
3083 topPanel.av.setGatherViewsHere(false);
3084 bottomPanel.av.setGatherViewsHere(false);
3085 JInternalFrame splitFrame = new SplitFrame(newTopFrame,
3087 if (geometry != null)
3089 splitFrame.setLocation(geometry.getLocation());
3091 addInternalFrame(splitFrame, sf.getTitle(), -1, -1);
3095 * Clear references to the panels (now relocated in the new SplitFrames)
3096 * before closing the old SplitFrame.
3099 bottomPanels.clear();
3104 * Gather expanded split frames, sharing the same pairs of sequence set ids,
3105 * back into the given SplitFrame as additional views. Note that the gathered
3106 * frames may themselves have multiple views.
3110 public void gatherViews(GSplitFrame source)
3113 * special handling of explodedGeometry for a view within a SplitFrame: - it
3114 * holds the (x, y) position of the enclosing SplitFrame, and the (width,
3115 * height) of the AlignFrame component
3117 AlignFrame myTopFrame = (AlignFrame) source.getTopFrame();
3118 AlignFrame myBottomFrame = (AlignFrame) source.getBottomFrame();
3119 myTopFrame.viewport.setExplodedGeometry(new Rectangle(source.getX(),
3120 source.getY(), myTopFrame.getWidth(), myTopFrame.getHeight()));
3121 myBottomFrame.viewport
3122 .setExplodedGeometry(new Rectangle(source.getX(), source.getY(),
3123 myBottomFrame.getWidth(), myBottomFrame.getHeight()));
3124 myTopFrame.viewport.setGatherViewsHere(true);
3125 myBottomFrame.viewport.setGatherViewsHere(true);
3126 String topViewId = myTopFrame.viewport.getSequenceSetId();
3127 String bottomViewId = myBottomFrame.viewport.getSequenceSetId();
3129 JInternalFrame[] frames = desktopPane.getAllFrames();
3130 for (JInternalFrame frame : frames)
3132 if (frame instanceof SplitFrame && frame != source)
3134 SplitFrame sf = (SplitFrame) frame;
3135 AlignFrame topFrame = (AlignFrame) sf.getTopFrame();
3136 AlignFrame bottomFrame = (AlignFrame) sf.getBottomFrame();
3137 boolean gatherThis = false;
3138 for (int a = 0; a < topFrame.alignPanels.size(); a++)
3140 AlignmentPanel topPanel = topFrame.alignPanels.get(a);
3141 AlignmentPanel bottomPanel = bottomFrame.alignPanels.get(a);
3142 if (topViewId.equals(topPanel.av.getSequenceSetId())
3143 && bottomViewId.equals(bottomPanel.av.getSequenceSetId()))
3146 topPanel.av.setGatherViewsHere(false);
3147 bottomPanel.av.setGatherViewsHere(false);
3148 topPanel.av.setExplodedGeometry(
3149 new Rectangle(sf.getLocation(), topFrame.getSize()));
3150 bottomPanel.av.setExplodedGeometry(
3151 new Rectangle(sf.getLocation(), bottomFrame.getSize()));
3152 myTopFrame.addAlignmentPanel(topPanel, false);
3153 myBottomFrame.addAlignmentPanel(bottomPanel, false);
3159 topFrame.getAlignPanels().clear();
3160 bottomFrame.getAlignPanels().clear();
3167 * The dust settles...give focus to the tab we did this from.
3169 myTopFrame.setDisplayedView(myTopFrame.alignPanel);
3172 public static groovy.ui.Console getGroovyConsole()
3174 return groovyConsole;
3178 * handles the payload of a drag and drop event.
3180 * TODO refactor to desktop utilities class
3183 * - Data source strings extracted from the drop event
3185 * - protocol for each data source extracted from the drop event
3189 * - the payload from the drop event
3192 @SuppressWarnings("unchecked")
3193 public static void transferFromDropTarget(List<Object> files,
3194 List<DataSourceType> protocols, DropTargetDropEvent evt,
3195 Transferable t) throws Exception
3198 // BH 2018 changed List<String> to List<Object> to allow for File from
3201 // DataFlavor[] flavors = t.getTransferDataFlavors();
3202 // for (int i = 0; i < flavors.length; i++) {
3203 // if (flavors[i].isFlavorJavaFileListType()) {
3204 // evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
3205 // List<File> list = (List<File>) t.getTransferData(flavors[i]);
3206 // for (int j = 0; j < list.size(); j++) {
3207 // File file = (File) list.get(j);
3208 // byte[] data = getDroppedFileBytes(file);
3209 // fileName.setText(file.getName() + " - " + data.length + " " +
3210 // evt.getLocation());
3211 // JTextArea target = (JTextArea) ((DropTarget)
3212 // evt.getSource()).getComponent();
3213 // target.setText(new String(data));
3215 // dtde.dropComplete(true);
3220 DataFlavor uriListFlavor = new DataFlavor(
3221 "text/uri-list;class=java.lang.String"), urlFlavour = null;
3224 urlFlavour = new DataFlavor(
3225 "application/x-java-url; class=java.net.URL");
3226 } catch (ClassNotFoundException cfe)
3228 Cache.log.debug("Couldn't instantiate the URL dataflavor.", cfe);
3231 if (urlFlavour != null && t.isDataFlavorSupported(urlFlavour))
3236 java.net.URL url = (URL) t.getTransferData(urlFlavour);
3237 // nb: java 8 osx bug https://bugs.openjdk.java.net/browse/JDK-8156099
3238 // means url may be null.
3241 protocols.add(DataSourceType.URL);
3242 files.add(url.toString());
3243 Cache.log.debug("Drop handled as URL dataflavor "
3244 + files.get(files.size() - 1));
3249 if (Platform.isAMacAndNotJS())
3252 "Please ignore plist error - occurs due to problem with java 8 on OSX");
3255 } catch (Throwable ex)
3257 Cache.log.debug("URL drop handler failed.", ex);
3260 if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
3262 // Works on Windows and MacOSX
3263 Cache.log.debug("Drop handled as javaFileListFlavor");
3264 for (File file : (List<File>) t
3265 .getTransferData(DataFlavor.javaFileListFlavor))
3268 protocols.add(DataSourceType.FILE);
3273 // Unix like behaviour
3274 boolean added = false;
3276 if (t.isDataFlavorSupported(uriListFlavor))
3278 Cache.log.debug("Drop handled as uriListFlavor");
3279 // This is used by Unix drag system
3280 data = (String) t.getTransferData(uriListFlavor);
3284 // fallback to text: workaround - on OSX where there's a JVM bug
3285 Cache.log.debug("standard URIListFlavor failed. Trying text");
3286 // try text fallback
3287 DataFlavor textDf = new DataFlavor(
3288 "text/plain;class=java.lang.String");
3289 if (t.isDataFlavorSupported(textDf))
3291 data = (String) t.getTransferData(textDf);
3294 Cache.log.debug("Plain text drop content returned "
3295 + (data == null ? "Null - failed" : data));
3300 while (protocols.size() < files.size())
3302 Cache.log.debug("Adding missing FILE protocol for "
3303 + files.get(protocols.size()));
3304 protocols.add(DataSourceType.FILE);
3306 for (java.util.StringTokenizer st = new java.util.StringTokenizer(
3307 data, "\r\n"); st.hasMoreTokens();)
3310 String s = st.nextToken();
3311 if (s.startsWith("#"))
3313 // the line is a comment (as per the RFC 2483)
3316 java.net.URI uri = new java.net.URI(s);
3317 if (uri.getScheme().toLowerCase().startsWith("http"))
3319 protocols.add(DataSourceType.URL);
3320 files.add(uri.toString());
3324 // otherwise preserve old behaviour: catch all for file objects
3325 java.io.File file = new java.io.File(uri);
3326 protocols.add(DataSourceType.FILE);
3327 files.add(file.toString());
3332 if (Cache.log.isDebugEnabled())
3334 if (data == null || !added)
3337 if (t.getTransferDataFlavors() != null
3338 && t.getTransferDataFlavors().length > 0)
3341 "Couldn't resolve drop data. Here are the supported flavors:");
3342 for (DataFlavor fl : t.getTransferDataFlavors())
3345 "Supported transfer dataflavor: " + fl.toString());
3346 Object df = t.getTransferData(fl);
3349 Cache.log.debug("Retrieves: " + df);
3353 Cache.log.debug("Retrieved nothing");
3359 Cache.log.debug("Couldn't resolve dataflavor for drop: "
3365 if (Platform.isWindowsAndNotJS())
3367 Cache.log.debug("Scanning dropped content for Windows Link Files");
3369 // resolve any .lnk files in the file drop
3370 for (int f = 0; f < files.size(); f++)
3372 String source = files.get(f).toString().toLowerCase();
3373 if (protocols.get(f).equals(DataSourceType.FILE)
3374 && (source.endsWith(".lnk") || source.endsWith(".url")
3375 || source.endsWith(".site")))
3379 Object obj = files.get(f);
3380 File lf = (obj instanceof File ? (File) obj
3381 : new File((String) obj));
3382 // process link file to get a URL
3383 Cache.log.debug("Found potential link file: " + lf);
3384 WindowsShortcut wscfile = new WindowsShortcut(lf);
3385 String fullname = wscfile.getRealFilename();
3386 protocols.set(f, FormatAdapter.checkProtocol(fullname));
3387 files.set(f, fullname);
3388 Cache.log.debug("Parsed real filename " + fullname
3389 + " to extract protocol: " + protocols.get(f));
3390 } catch (Exception ex)
3393 "Couldn't parse " + files.get(f) + " as a link file.",
3402 * Sets the Preferences property for experimental features to True or False
3403 * depending on the state of the controlling menu item
3406 protected void showExperimental_actionPerformed(boolean selected)
3408 Cache.setProperty(EXPERIMENTAL_FEATURES, Boolean.toString(selected));
3412 * Answers a (possibly empty) list of any structure viewer frames (currently
3413 * for either Jmol or Chimera) which are currently open. This may optionally
3414 * be restricted to viewers of a specified class, or viewers linked to a
3415 * specified alignment panel.
3418 * if not null, only return viewers linked to this panel
3419 * @param structureViewerClass
3420 * if not null, only return viewers of this class
3423 public List<StructureViewerBase> getStructureViewers(
3424 AlignmentPanel apanel,
3425 Class<? extends StructureViewerBase> structureViewerClass)
3427 List<StructureViewerBase> result = new ArrayList<>();
3428 JInternalFrame[] frames = getAllFrames();
3430 for (JInternalFrame frame : frames)
3432 if (frame instanceof StructureViewerBase)
3434 if (structureViewerClass == null
3435 || structureViewerClass.isInstance(frame))
3438 || ((StructureViewerBase) frame).isLinkedWith(apanel))
3440 result.add((StructureViewerBase) frame);