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 if (!Platform.isJS())
475 jconsole = new Console(this, showjconsole);
476 jconsole.setHeader(Cache.getVersionDetailsForConsole());
477 showConsole(showjconsole);
479 showNews.setVisible(false);
481 experimentalFeatures.setSelected(showExperimental());
483 getIdentifiersOrgData();
487 // Spawn a thread that shows the splashscreen
489 SwingUtilities.invokeLater(new Runnable()
494 new SplashScreen(true);
498 // Thread off a new instance of the file chooser - this reduces the time
500 // takes to open it later on.
501 new Thread(new Runnable()
506 Cache.log.debug("Filechooser init thread started.");
507 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
508 JalviewFileChooser.forRead(Cache.getProperty("LAST_DIRECTORY"),
510 Cache.log.debug("Filechooser init thread finished.");
513 // Add the service change listener
514 changeSupport.addJalviewPropertyChangeListener("services",
515 new PropertyChangeListener()
519 public void propertyChange(PropertyChangeEvent evt)
521 Cache.log.debug("Firing service changed event for "
522 + evt.getNewValue());
523 JalviewServicesChanged(evt);
528 this.setDropTarget(new java.awt.dnd.DropTarget(desktopPane, this));
530 this.addWindowListener(new WindowAdapter()
533 public void windowClosing(WindowEvent evt)
540 this.addMouseListener(ma = new MouseAdapter()
543 public void mousePressed(MouseEvent evt)
545 if (evt.isPopupTrigger()) // Mac
547 showPasteMenu(evt.getX(), evt.getY());
552 public void mouseReleased(MouseEvent evt)
554 if (evt.isPopupTrigger()) // Windows
556 showPasteMenu(evt.getX(), evt.getY());
560 desktopPane.addMouseListener(ma);
561 } catch (Throwable t)
569 * Answers true if user preferences to enable experimental features is True
574 public boolean showExperimental()
576 String experimental = Cache.getDefault(EXPERIMENTAL_FEATURES,
577 Boolean.FALSE.toString());
578 return Boolean.valueOf(experimental).booleanValue();
581 public void doConfigureStructurePrefs()
583 // configure services
584 StructureSelectionManager ssm = StructureSelectionManager
585 .getStructureSelectionManager(this);
586 if (Cache.getDefault(Preferences.ADD_SS_ANN, true))
588 ssm.setAddTempFacAnnot(
589 Cache.getDefault(Preferences.ADD_TEMPFACT_ANN, true));
590 ssm.setProcessSecondaryStructure(
591 Cache.getDefault(Preferences.STRUCT_FROM_PDB, true));
592 ssm.setSecStructServices(
593 Cache.getDefault(Preferences.USE_RNAVIEW, true));
597 ssm.setAddTempFacAnnot(false);
598 ssm.setProcessSecondaryStructure(false);
599 ssm.setSecStructServices(false);
603 public void checkForNews()
605 final Desktop me = this;
606 // Thread off the news reader, in case there are connection problems.
607 new Thread(new Runnable()
612 Cache.log.debug("Starting news thread.");
613 jvnews = new BlogReader(me);
614 showNews.setVisible(true);
615 Cache.log.debug("Completed news thread.");
620 public void getIdentifiersOrgData()
622 // Thread off the identifiers fetcher
623 new Thread(new Runnable()
628 Cache.log.debug("Downloading data from identifiers.org");
631 UrlDownloadClient.download(IdOrgSettings.getUrl(),
632 IdOrgSettings.getDownloadLocation());
633 } catch (IOException e)
635 Cache.log.debug("Exception downloading identifiers.org data"
644 protected void showNews_actionPerformed(ActionEvent e)
646 showNews(showNews.isSelected());
649 void showNews(boolean visible)
651 Cache.log.debug((visible ? "Showing" : "Hiding") + " news.");
652 showNews.setSelected(visible);
653 if (visible && !jvnews.isVisible())
655 new Thread(new Runnable()
660 long now = System.currentTimeMillis();
662 MessageManager.getString("status.refreshing_news"), now);
663 jvnews.refreshNews();
664 setProgressBar(null, now);
672 * recover the last known dimensions for a jalview window
675 * - empty string is desktop, all other windows have unique prefix
676 * @return null or last known dimensions scaled to current geometry (if last
677 * window geom was known)
679 Rectangle getLastKnownDimensions(String windowName)
681 // TODO: lock aspect ratio for scaling desktop Bug #0058199
682 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
683 String x = Cache.getProperty(windowName + "SCREEN_X");
684 String y = Cache.getProperty(windowName + "SCREEN_Y");
685 String width = Cache.getProperty(windowName + "SCREEN_WIDTH");
686 String height = Cache.getProperty(windowName + "SCREEN_HEIGHT");
687 if ((x != null) && (y != null) && (width != null) && (height != null))
689 int ix = Integer.parseInt(x), iy = Integer.parseInt(y),
690 iw = Integer.parseInt(width), ih = Integer.parseInt(height);
691 if (Cache.getProperty("SCREENGEOMETRY_WIDTH") != null)
693 // attempt #1 - try to cope with change in screen geometry - this
694 // version doesn't preserve original jv aspect ratio.
695 // take ratio of current screen size vs original screen size.
696 double sw = ((1f * screenSize.width) / (1f * Integer
697 .parseInt(Cache.getProperty("SCREENGEOMETRY_WIDTH"))));
698 double sh = ((1f * screenSize.height) / (1f * Integer
699 .parseInt(Cache.getProperty("SCREENGEOMETRY_HEIGHT"))));
700 // rescale the bounds depending upon the current screen geometry.
701 ix = (int) (ix * sw);
702 iw = (int) (iw * sw);
703 iy = (int) (iy * sh);
704 ih = (int) (ih * sh);
705 while (ix >= screenSize.width)
708 "Window geometry location recall error: shifting horizontal to within screenbounds.");
709 ix -= screenSize.width;
711 while (iy >= screenSize.height)
714 "Window geometry location recall error: shifting vertical to within screenbounds.");
715 iy -= screenSize.height;
718 "Got last known dimensions for " + windowName + ": x:" + ix
719 + " y:" + iy + " width:" + iw + " height:" + ih);
721 // return dimensions for new instance
722 return new Rectangle(ix, iy, iw, ih);
727 void showPasteMenu(int x, int y)
729 JPopupMenu popup = new JPopupMenu();
730 JMenuItem item = new JMenuItem(
731 MessageManager.getString("label.paste_new_window"));
732 item.addActionListener(new ActionListener()
735 public void actionPerformed(ActionEvent evt)
742 popup.show(this, x, y);
749 Clipboard c = Toolkit.getDefaultToolkit().getSystemClipboard();
750 Transferable contents = c.getContents(this);
752 if (contents != null)
754 String file = (String) contents
755 .getTransferData(DataFlavor.stringFlavor);
757 FileFormatI format = new IdentifyFile().identify(file,
758 DataSourceType.PASTE);
760 new FileLoader().LoadFile(file, DataSourceType.PASTE, format);
763 } catch (Exception ex)
766 "Unable to paste alignment from system clipboard:\n" + ex);
771 * Adds and opens the given frame to the desktop
782 public static synchronized void addInternalFrame(
783 final JInternalFrame frame, String title, int w, int h)
785 addInternalFrame(frame, title, true, w, h, true, false);
789 * Add an internal frame to the Jalview desktop
796 * When true, display frame immediately, otherwise, caller must call
797 * setVisible themselves.
803 public static synchronized void addInternalFrame(
804 final JInternalFrame frame, String title, boolean makeVisible,
807 addInternalFrame(frame, title, makeVisible, w, h, true, false);
811 * Add an internal frame to the Jalview desktop and make it visible
824 public static synchronized void addInternalFrame(
825 final JInternalFrame frame, String title, int w, int h,
828 addInternalFrame(frame, title, true, w, h, resizable, false);
832 * Add an internal frame to the Jalview desktop
839 * When true, display frame immediately, otherwise, caller must call
840 * setVisible themselves.
847 * @param ignoreMinSize
848 * Do not set the default minimum size for frame
850 public static synchronized void addInternalFrame(
851 final JInternalFrame frame, String title, boolean makeVisible,
852 int w, int h, boolean resizable, boolean ignoreMinSize)
855 getInstance().addFrame(frame, title, makeVisible, w, h, resizable, ignoreMinSize);
858 private void addFrame(JInternalFrame frame, String title,
859 boolean makeVisible, int w, int h, boolean resizable,
860 boolean ignoreMinSize)
862 // TODO: allow callers to determine X and Y position of frame (eg. via
864 // TODO: consider fixing method to update entries in the window submenu with
865 // the current window title
867 frame.setTitle(title);
868 if (frame.getWidth() < 1 || frame.getHeight() < 1)
872 // THIS IS A PUBLIC STATIC METHOD, SO IT MAY BE CALLED EVEN IN
873 // A HEADLESS STATE WHEN NO DESKTOP EXISTS. MUST RETURN
874 // IF JALVIEW IS RUNNING HEADLESS
875 // ///////////////////////////////////////////////
876 if (Jalview.isHeadlessMode())
885 frame.setMinimumSize(
886 new Dimension(DEFAULT_MIN_WIDTH, DEFAULT_MIN_HEIGHT));
888 // Set default dimension for Alignment Frame window.
889 // The Alignment Frame window could be added from a number of places,
891 // I did this here in order not to miss out on any Alignment frame.
892 if (frame instanceof AlignFrame)
894 frame.setMinimumSize(new Dimension(ALIGN_FRAME_DEFAULT_MIN_WIDTH,
895 ALIGN_FRAME_DEFAULT_MIN_HEIGHT));
899 frame.setVisible(makeVisible);
900 frame.setClosable(true);
901 frame.setResizable(resizable);
902 frame.setMaximizable(resizable);
903 frame.setIconifiable(resizable);
904 frame.setOpaque(Platform.isJS());
905 boolean isEmbedded = (Platform.getDimIfEmbedded(frame, -1, -1) != null);
906 if (!isEmbedded && frame.getX() < 1 && frame.getY() < 1)
908 frame.setLocation(xOffset * openFrameCount,
909 yOffset * ((openFrameCount - 1) % 10) + yOffset);
913 * add an entry for the new frame in the Window menu
914 * (and remove it when the frame is closed)
916 final JMenuItem menuItem = new JMenuItem(title);
917 frame.addInternalFrameListener(new InternalFrameAdapter()
920 public void internalFrameActivated(InternalFrameEvent evt)
922 JInternalFrame itf = getDesktopPane().getSelectedFrame();
925 if (itf instanceof AlignFrame)
927 Jalview.setCurrentAlignFrame((AlignFrame) itf);
934 public void internalFrameClosed(InternalFrameEvent evt)
936 PaintRefresher.RemoveComponent(frame);
939 * defensive check to prevent frames being
940 * added half off the window
942 if (openFrameCount > 0)
948 * ensure no reference to alignFrame retained by menu item listener
950 if (menuItem.getActionListeners().length > 0)
952 menuItem.removeActionListener(menuItem.getActionListeners()[0]);
954 getInstance().windowMenu.remove(menuItem);
958 menuItem.addActionListener(new ActionListener()
961 public void actionPerformed(ActionEvent e)
965 frame.setSelected(true);
966 frame.setIcon(false);
967 } catch (java.beans.PropertyVetoException ex)
969 // System.err.println(ex.toString());
974 setKeyBindings(frame);
976 getDesktopPane().add(frame);
978 getInstance().windowMenu.add(menuItem);
983 frame.setSelected(true);
984 frame.requestFocus();
985 } catch (java.beans.PropertyVetoException ve)
987 } catch (java.lang.ClassCastException cex)
990 "Squashed a possible GUI implementation error. If you can recreate this, please look at http://issues.jalview.org/browse/JAL-869",
996 * Add key bindings to a JInternalFrame so that Ctrl-W and Cmd-W will close
1001 private static void setKeyBindings(JInternalFrame frame)
1003 final Action closeAction = new AbstractAction()
1006 public void actionPerformed(ActionEvent e)
1013 * set up key bindings for Ctrl-W and Cmd-W, with the same (Close) action
1015 KeyStroke ctrlWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
1016 InputEvent.CTRL_DOWN_MASK);
1017 KeyStroke cmdWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
1018 ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx());
1020 InputMap inputMap = frame
1021 .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
1022 String ctrlW = ctrlWKey.toString();
1023 inputMap.put(ctrlWKey, ctrlW);
1024 inputMap.put(cmdWKey, ctrlW);
1026 ActionMap actionMap = frame.getActionMap();
1027 actionMap.put(ctrlW, closeAction);
1031 public void lostOwnership(Clipboard clipboard, Transferable contents)
1035 jalviewClipboard = null;
1038 internalCopy = false;
1042 public void dragEnter(DropTargetDragEvent evt)
1047 public void dragExit(DropTargetEvent evt)
1052 public void dragOver(DropTargetDragEvent evt)
1057 public void dropActionChanged(DropTargetDragEvent evt)
1068 public void drop(DropTargetDropEvent evt)
1070 boolean success = true;
1071 // JAL-1552 - acceptDrop required before getTransferable call for
1072 // Java's Transferable for native dnd
1073 evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
1074 Transferable t = evt.getTransferable();
1075 List<Object> files = new ArrayList<>();
1076 List<DataSourceType> protocols = new ArrayList<>();
1080 transferFromDropTarget(files, protocols, evt, t);
1081 } catch (Exception e)
1083 e.printStackTrace();
1091 for (int i = 0; i < files.size(); i++)
1093 // BH 2018 File or String
1094 Object file = files.get(i);
1095 String fileName = file.toString();
1096 DataSourceType protocol = (protocols == null)
1097 ? DataSourceType.FILE
1099 FileFormatI format = null;
1101 if (fileName.endsWith(".jar"))
1103 format = FileFormat.Jalview;
1108 format = new IdentifyFile().identify(file, protocol);
1110 if (file instanceof File)
1112 Platform.cacheFileData((File) file);
1114 new FileLoader().LoadFile(null, file, protocol, format);
1117 } catch (Exception ex)
1122 evt.dropComplete(success); // need this to ensure input focus is properly
1123 // transfered to any new windows created
1133 public void inputLocalFileMenuItem_actionPerformed(AlignViewport viewport)
1135 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
1136 JalviewFileChooser chooser = JalviewFileChooser.forRead(
1137 Cache.getProperty("LAST_DIRECTORY"), fileFormat,
1138 BackupFiles.getEnabled());
1140 chooser.setFileView(new JalviewFileView());
1141 chooser.setDialogTitle(
1142 MessageManager.getString("label.open_local_file"));
1143 chooser.setToolTipText(MessageManager.getString("action.open"));
1145 chooser.setResponseHandler(0, new Runnable()
1150 File selectedFile = chooser.getSelectedFile();
1151 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1153 FileFormatI format = chooser.getSelectedFormat();
1156 * Call IdentifyFile to verify the file contains what its extension implies.
1157 * Skip this step for dynamically added file formats, because
1158 * IdentifyFile does not know how to recognise them.
1160 if (FileFormats.getInstance().isIdentifiable(format))
1164 format = new IdentifyFile().identify(selectedFile,
1165 DataSourceType.FILE);
1166 } catch (FileFormatException e)
1168 // format = null; //??
1172 new FileLoader().LoadFile(viewport, selectedFile,
1173 DataSourceType.FILE, format);
1176 chooser.showOpenDialog(this);
1180 * Shows a dialog for input of a URL at which to retrieve alignment data
1185 public void inputURLMenuItem_actionPerformed(AlignViewport viewport)
1187 // This construct allows us to have a wider textfield
1189 JLabel label = new JLabel(
1190 MessageManager.getString("label.input_file_url"));
1192 JPanel panel = new JPanel(new GridLayout(2, 1));
1196 * the URL to fetch is
1197 * Java: an editable combobox with history
1198 * JS: (pending JAL-3038) a plain text field
1201 String urlBase = "http://www.";
1202 if (Platform.isJS())
1204 history = new JTextField(urlBase, 35);
1213 JComboBox<String> asCombo = new JComboBox<>();
1214 asCombo.setPreferredSize(new Dimension(400, 20));
1215 asCombo.setEditable(true);
1216 asCombo.addItem(urlBase);
1217 String historyItems = Cache.getProperty("RECENT_URL");
1218 if (historyItems != null)
1220 for (String token : historyItems.split("\\t"))
1222 asCombo.addItem(token);
1229 Object[] options = new Object[] { MessageManager.getString("action.ok"),
1230 MessageManager.getString("action.cancel") };
1231 Runnable action = new Runnable()
1236 @SuppressWarnings("unchecked")
1237 String url = (history instanceof JTextField
1238 ? ((JTextField) history).getText()
1239 : ((JComboBox<String>) history).getSelectedItem()
1242 if (url.toLowerCase().endsWith(".jar"))
1244 if (viewport != null)
1246 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1247 FileFormat.Jalview);
1251 new FileLoader().LoadFile(url, DataSourceType.URL,
1252 FileFormat.Jalview);
1257 FileFormatI format = null;
1260 format = new IdentifyFile().identify(url, DataSourceType.URL);
1261 } catch (FileFormatException e)
1263 // TODO revise error handling, distinguish between
1264 // URL not found and response not valid
1269 String msg = MessageManager
1270 .formatMessage("label.couldnt_locate", url);
1271 JvOptionPane.showInternalMessageDialog(getDesktopPane(), msg,
1272 MessageManager.getString("label.url_not_found"),
1273 JvOptionPane.WARNING_MESSAGE);
1278 if (viewport != null)
1280 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1285 new FileLoader().LoadFile(url, DataSourceType.URL, format);
1290 String dialogOption = MessageManager
1291 .getString("label.input_alignment_from_url");
1292 JvOptionPane.newOptionDialog(desktopPane).setResponseHandler(0, action)
1293 .showInternalDialog(panel, dialogOption,
1294 JvOptionPane.YES_NO_CANCEL_OPTION,
1295 JvOptionPane.PLAIN_MESSAGE, null, options,
1296 MessageManager.getString("action.ok"));
1300 * Opens the CutAndPaste window for the user to paste an alignment in to
1303 * - if not null, the pasted alignment is added to the current
1304 * alignment; if null, to a new alignment window
1307 public void inputTextboxMenuItem_actionPerformed(
1308 AlignmentViewPanel viewPanel)
1310 CutAndPasteTransfer cap = new CutAndPasteTransfer();
1311 cap.setForInput(viewPanel);
1312 addInternalFrame(cap,
1313 MessageManager.getString("label.cut_paste_alignmen_file"), true,
1323 Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
1324 Cache.setProperty("SCREENGEOMETRY_WIDTH", screen.width + "");
1325 Cache.setProperty("SCREENGEOMETRY_HEIGHT", screen.height + "");
1326 storeLastKnownDimensions("", new Rectangle(getBounds().x, getBounds().y,
1327 getWidth(), getHeight()));
1329 if (jconsole != null)
1331 storeLastKnownDimensions("JAVA_CONSOLE_", jconsole.getBounds());
1332 jconsole.stopConsole();
1336 storeLastKnownDimensions("JALVIEW_RSS_WINDOW_", jvnews.getBounds());
1339 if (dialogExecutor != null)
1341 dialogExecutor.shutdownNow();
1343 closeAll_actionPerformed(null);
1345 if (groovyConsole != null)
1347 // suppress a possible repeat prompt to save script
1348 groovyConsole.setDirty(false);
1349 groovyConsole.exit();
1354 private void storeLastKnownDimensions(String string, Rectangle jc)
1356 Cache.log.debug("Storing last known dimensions for " + string + ": x:"
1357 + jc.x + " y:" + jc.y + " width:" + jc.width + " height:"
1360 Cache.setProperty(string + "SCREEN_X", jc.x + "");
1361 Cache.setProperty(string + "SCREEN_Y", jc.y + "");
1362 Cache.setProperty(string + "SCREEN_WIDTH", jc.width + "");
1363 Cache.setProperty(string + "SCREEN_HEIGHT", jc.height + "");
1373 public void aboutMenuItem_actionPerformed(ActionEvent e)
1375 new Thread(new Runnable()
1380 new SplashScreen(false);
1386 * Returns the html text for the About screen, including any available version
1387 * number, build details, author details and citation reference, but without
1388 * the enclosing {@code html} tags
1392 public String getAboutMessage()
1394 StringBuilder message = new StringBuilder(1024);
1395 message.append("<h1><strong>Version: ")
1396 .append(Cache.getProperty("VERSION")).append("</strong></h1>")
1397 .append("<strong>Built: <em>")
1398 .append(Cache.getDefault("BUILD_DATE", "unknown"))
1399 .append("</em> from ").append(Cache.getBuildDetailsForSplash())
1400 .append("</strong>");
1402 String latestVersion = Cache.getDefault("LATEST_VERSION", "Checking");
1403 if (latestVersion.equals("Checking"))
1405 // JBP removed this message for 2.11: May be reinstated in future version
1406 // message.append("<br>...Checking latest version...</br>");
1408 else if (!latestVersion.equals(Cache.getProperty("VERSION")))
1410 boolean red = false;
1411 if (Cache.getProperty("VERSION").toLowerCase()
1412 .indexOf("automated build") == -1)
1415 // Displayed when code version and jnlp version do not match and code
1416 // version is not a development build
1417 message.append("<div style=\"color: #FF0000;font-style: bold;\">");
1420 message.append("<br>!! Version ")
1421 .append(Cache.getDefault("LATEST_VERSION", "..Checking.."))
1422 .append(" is available for download from ")
1423 .append(Cache.getDefault("www.jalview.org",
1424 "http://www.jalview.org"))
1428 message.append("</div>");
1431 message.append("<br>Authors: ");
1432 message.append(Cache.getDefault("AUTHORFNAMES", DEFAULT_AUTHORS));
1433 message.append(CITATION);
1435 return message.toString();
1439 * Action on requesting Help documentation
1442 public void documentationMenuItem_actionPerformed()
1446 if (Platform.isJS())
1448 BrowserLauncher.openURL("http://www.jalview.org/help.html");
1457 Help.showHelpWindow();
1459 } catch (Exception ex)
1461 System.err.println("Error opening help: " + ex.getMessage());
1466 public void closeAll_actionPerformed(ActionEvent e)
1468 // TODO show a progress bar while closing?
1469 JInternalFrame[] frames = desktopPane.getAllFrames();
1470 for (int i = 0; i < frames.length; i++)
1474 frames[i].setClosed(true);
1475 } catch (java.beans.PropertyVetoException ex)
1479 Jalview.setCurrentAlignFrame(null);
1480 System.out.println("ALL CLOSED");
1483 * reset state of singleton objects as appropriate (clear down session state
1484 * when all windows are closed)
1486 StructureSelectionManager ssm = StructureSelectionManager
1487 .getStructureSelectionManager(this);
1495 public void raiseRelated_actionPerformed(ActionEvent e)
1497 reorderAssociatedWindows(false, false);
1501 public void minimizeAssociated_actionPerformed(ActionEvent e)
1503 reorderAssociatedWindows(true, false);
1506 void closeAssociatedWindows()
1508 reorderAssociatedWindows(false, true);
1514 * @seejalview.jbgui.GDesktop#garbageCollect_actionPerformed(java.awt.event.
1518 protected void garbageCollect_actionPerformed(ActionEvent e)
1520 // We simply collect the garbage
1521 Cache.log.debug("Collecting garbage...");
1523 Cache.log.debug("Finished garbage collection.");
1530 * jalview.jbgui.GDesktop#showMemusage_actionPerformed(java.awt.event.ActionEvent
1534 protected void showMemusage_actionPerformed(ActionEvent e)
1536 desktopPane.showMemoryUsage(showMemusage.isSelected());
1543 * jalview.jbgui.GDesktop#showConsole_actionPerformed(java.awt.event.ActionEvent
1547 protected void showConsole_actionPerformed(ActionEvent e)
1549 showConsole(showConsole.isSelected());
1552 Console jconsole = null;
1555 * control whether the java console is visible or not
1559 void showConsole(boolean selected)
1561 // TODO: decide if we should update properties file
1562 if (jconsole != null) // BH 2018
1564 showConsole.setSelected(selected);
1565 Cache.setProperty("SHOW_JAVA_CONSOLE",
1566 Boolean.valueOf(selected).toString());
1567 jconsole.setVisible(selected);
1571 void reorderAssociatedWindows(boolean minimize, boolean close)
1573 JInternalFrame[] frames = desktopPane.getAllFrames();
1574 if (frames == null || frames.length < 1)
1579 AlignmentViewport source = null, target = null;
1580 if (frames[0] instanceof AlignFrame)
1582 source = ((AlignFrame) frames[0]).getCurrentView();
1584 else if (frames[0] instanceof TreePanel)
1586 source = ((TreePanel) frames[0]).getViewPort();
1588 else if (frames[0] instanceof PCAPanel)
1590 source = ((PCAPanel) frames[0]).av;
1592 else if (frames[0].getContentPane() instanceof PairwiseAlignPanel)
1594 source = ((PairwiseAlignPanel) frames[0].getContentPane()).av;
1599 for (int i = 0; i < frames.length; i++)
1602 if (frames[i] == null)
1606 if (frames[i] instanceof AlignFrame)
1608 target = ((AlignFrame) frames[i]).getCurrentView();
1610 else if (frames[i] instanceof TreePanel)
1612 target = ((TreePanel) frames[i]).getViewPort();
1614 else if (frames[i] instanceof PCAPanel)
1616 target = ((PCAPanel) frames[i]).av;
1618 else if (frames[i].getContentPane() instanceof PairwiseAlignPanel)
1620 target = ((PairwiseAlignPanel) frames[i].getContentPane()).av;
1623 if (source == target)
1629 frames[i].setClosed(true);
1633 frames[i].setIcon(minimize);
1636 frames[i].toFront();
1640 } catch (java.beans.PropertyVetoException ex)
1655 protected void preferences_actionPerformed(ActionEvent e)
1661 * Prompts the user to choose a file and then saves the Jalview state as a
1662 * Jalview project file
1665 public void saveState_actionPerformed()
1667 saveState_actionPerformed(false);
1670 public void saveState_actionPerformed(boolean saveAs)
1672 java.io.File projectFile = getProjectFile();
1673 // autoSave indicates we already have a file and don't need to ask
1674 boolean autoSave = projectFile != null && !saveAs
1675 && BackupFiles.getEnabled();
1677 // System.out.println("autoSave="+autoSave+", projectFile='"+projectFile+"',
1678 // saveAs="+saveAs+", Backups
1679 // "+(BackupFiles.getEnabled()?"enabled":"disabled"));
1681 boolean approveSave = false;
1684 JalviewFileChooser chooser = new JalviewFileChooser("jvp",
1687 chooser.setFileView(new JalviewFileView());
1688 chooser.setDialogTitle(MessageManager.getString("label.save_state"));
1690 int value = chooser.showSaveDialog(this);
1692 if (value == JalviewFileChooser.APPROVE_OPTION)
1694 projectFile = chooser.getSelectedFile();
1695 setProjectFile(projectFile);
1700 if (approveSave || autoSave)
1702 final Desktop me = this;
1703 final java.io.File chosenFile = projectFile;
1704 new Thread(new Runnable()
1709 // TODO: refactor to Jalview desktop session controller action.
1710 setProgressBar(MessageManager.formatMessage(
1711 "label.saving_jalview_project", new Object[]
1712 { chosenFile.getName() }), chosenFile.hashCode());
1713 Cache.setProperty("LAST_DIRECTORY", chosenFile.getParent());
1714 // TODO catch and handle errors for savestate
1715 // TODO prevent user from messing with the Desktop whilst we're saving
1718 boolean doBackup = BackupFiles.getEnabled();
1719 BackupFiles backupfiles = doBackup ? new BackupFiles(chosenFile)
1722 new Jalview2XML().saveState(
1723 doBackup ? backupfiles.getTempFile() : chosenFile);
1727 backupfiles.setWriteSuccess(true);
1728 backupfiles.rollBackupsAndRenameTempFile();
1730 } catch (OutOfMemoryError oom)
1732 new OOMWarning("Whilst saving current state to "
1733 + chosenFile.getName(), oom);
1734 } catch (Exception ex)
1736 Cache.log.error("Problems whilst trying to save to "
1737 + chosenFile.getName(), ex);
1738 JvOptionPane.showMessageDialog(me,
1739 MessageManager.formatMessage(
1740 "label.error_whilst_saving_current_state_to",
1742 { chosenFile.getName() }),
1743 MessageManager.getString("label.couldnt_save_project"),
1744 JvOptionPane.WARNING_MESSAGE);
1746 setProgressBar(null, chosenFile.hashCode());
1753 public void saveAsState_actionPerformed(ActionEvent e)
1755 saveState_actionPerformed(true);
1758 private void setProjectFile(File choice)
1760 this.projectFile = choice;
1763 public File getProjectFile()
1765 return this.projectFile;
1769 * Shows a file chooser dialog and tries to read in the selected file as a
1773 public void loadState_actionPerformed()
1775 final String[] suffix = new String[] { "jvp", "jar" };
1776 final String[] desc = new String[] { "Jalview Project",
1777 "Jalview Project (old)" };
1778 JalviewFileChooser chooser = new JalviewFileChooser(
1779 Cache.getProperty("LAST_DIRECTORY"), suffix, desc,
1780 "Jalview Project", true, BackupFiles.getEnabled()); // last two
1784 chooser.setFileView(new JalviewFileView());
1785 chooser.setDialogTitle(MessageManager.getString("label.restore_state"));
1786 chooser.setResponseHandler(0, new Runnable()
1791 File selectedFile = chooser.getSelectedFile();
1792 setProjectFile(selectedFile);
1793 String choice = selectedFile.getAbsolutePath();
1794 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1795 new Thread(new Runnable()
1802 new Jalview2XML().loadJalviewAlign(selectedFile);
1803 } catch (OutOfMemoryError oom)
1805 new OOMWarning("Whilst loading project from " + choice, oom);
1806 } catch (Exception ex)
1809 "Problems whilst loading project from " + choice, ex);
1810 JvOptionPane.showMessageDialog( getDesktopPane(),
1811 MessageManager.formatMessage(
1812 "label.error_whilst_loading_project_from",
1816 .getString("label.couldnt_load_project"),
1817 JvOptionPane.WARNING_MESSAGE);
1824 chooser.showOpenDialog(this);
1828 public void inputSequence_actionPerformed(ActionEvent e)
1830 new SequenceFetcher(this);
1833 JPanel progressPanel;
1835 ArrayList<JPanel> fileLoadingPanels = new ArrayList<>();
1837 public void startLoading(final Object fileName)
1839 if (fileLoadingCount == 0)
1841 fileLoadingPanels.add(addProgressPanel(MessageManager
1842 .formatMessage("label.loading_file", new Object[]
1848 private JPanel addProgressPanel(String string)
1850 if (progressPanel == null)
1852 progressPanel = new JPanel(new GridLayout(1, 1));
1853 totalProgressCount = 0;
1854 getContentPane().add(progressPanel, BorderLayout.SOUTH);
1856 JPanel thisprogress = new JPanel(new BorderLayout(10, 5));
1857 JProgressBar progressBar = new JProgressBar();
1858 progressBar.setIndeterminate(true);
1860 thisprogress.add(new JLabel(string), BorderLayout.WEST);
1862 thisprogress.add(progressBar, BorderLayout.CENTER);
1863 progressPanel.add(thisprogress);
1864 ((GridLayout) progressPanel.getLayout()).setRows(
1865 ((GridLayout) progressPanel.getLayout()).getRows() + 1);
1866 ++totalProgressCount;
1868 return thisprogress;
1871 int totalProgressCount = 0;
1873 private void removeProgressPanel(JPanel progbar)
1875 if (progressPanel != null)
1877 synchronized (progressPanel)
1879 progressPanel.remove(progbar);
1880 GridLayout gl = (GridLayout) progressPanel.getLayout();
1881 gl.setRows(gl.getRows() - 1);
1882 if (--totalProgressCount < 1)
1884 this.getContentPane().remove(progressPanel);
1885 progressPanel = null;
1892 public void stopLoading()
1895 if (fileLoadingCount < 1)
1897 while (fileLoadingPanels.size() > 0)
1899 removeProgressPanel(fileLoadingPanels.remove(0));
1901 fileLoadingPanels.clear();
1902 fileLoadingCount = 0;
1907 public static int getViewCount(String alignmentId)
1909 AlignmentViewport[] aps = getViewports(alignmentId);
1910 return (aps == null) ? 0 : aps.length;
1915 * @param alignmentId
1916 * - if null, all sets are returned
1917 * @return all AlignmentPanels concerning the alignmentId sequence set
1919 public static AlignmentPanel[] getAlignmentPanels(String alignmentId)
1921 if (getDesktopPane() == null)
1923 // no frames created and in headless mode
1924 // TODO: verify that frames are recoverable when in headless mode
1927 List<AlignmentPanel> aps = new ArrayList<>();
1928 AlignFrame[] frames = getAlignFrames();
1933 for (AlignFrame af : frames)
1935 for (AlignmentPanel ap : af.alignPanels)
1937 if (alignmentId == null
1938 || alignmentId.equals(ap.av.getSequenceSetId()))
1944 if (aps.size() == 0)
1948 AlignmentPanel[] vap = aps.toArray(new AlignmentPanel[aps.size()]);
1953 * get all the viewports on an alignment.
1955 * @param sequenceSetId
1956 * unique alignment id (may be null - all viewports returned in that
1958 * @return all viewports on the alignment bound to sequenceSetId
1960 public static AlignmentViewport[] getViewports(String sequenceSetId)
1962 List<AlignmentViewport> viewp = new ArrayList<>();
1963 if ( getDesktopPane() != null)
1965 AlignFrame[] frames = getAlignFrames();
1967 for (AlignFrame afr : frames)
1969 if (sequenceSetId == null || afr.getViewport().getSequenceSetId()
1970 .equals(sequenceSetId))
1972 if (afr.alignPanels != null)
1974 for (AlignmentPanel ap : afr.alignPanels)
1976 if (sequenceSetId == null
1977 || sequenceSetId.equals(ap.av.getSequenceSetId()))
1985 viewp.add(afr.getViewport());
1989 if (viewp.size() > 0)
1991 return viewp.toArray(new AlignmentViewport[viewp.size()]);
1998 * Explode the views in the given frame into separate AlignFrame
2002 public static void explodeViews(AlignFrame af)
2004 int size = af.alignPanels.size();
2010 // FIXME: ideally should use UI interface API
2011 FeatureSettings viewFeatureSettings = (af.featureSettings != null
2012 && af.featureSettings.isOpen()) ? af.featureSettings : null;
2013 Rectangle fsBounds = af.getFeatureSettingsGeometry();
2014 for (int i = 0; i < size; i++)
2016 AlignmentPanel ap = af.alignPanels.get(i);
2018 AlignFrame newaf = new AlignFrame(ap);
2020 // transfer reference for existing feature settings to new alignFrame
2021 if (ap == af.alignPanel)
2023 if (viewFeatureSettings != null && viewFeatureSettings.fr.ap == ap)
2025 newaf.featureSettings = viewFeatureSettings;
2027 newaf.setFeatureSettingsGeometry(fsBounds);
2031 * Restore the view's last exploded frame geometry if known. Multiple
2032 * views from one exploded frame share and restore the same (frame)
2033 * position and size.
2035 Rectangle geometry = ap.av.getExplodedGeometry();
2036 if (geometry != null)
2038 newaf.setBounds(geometry);
2041 ap.av.setGatherViewsHere(false);
2043 addInternalFrame(newaf, af.getTitle(), AlignFrame.DEFAULT_WIDTH,
2044 AlignFrame.DEFAULT_HEIGHT);
2045 // and materialise a new feature settings dialog instance for the new
2047 // (closes the old as if 'OK' was pressed)
2048 if (ap == af.alignPanel && newaf.featureSettings != null
2049 && newaf.featureSettings.isOpen()
2050 && af.alignPanel.getAlignViewport().isShowSequenceFeatures())
2052 newaf.showFeatureSettingsUI();
2056 af.featureSettings = null;
2057 af.alignPanels.clear();
2058 af.closeMenuItem_actionPerformed(true);
2063 * Gather expanded views (separate AlignFrame's) with the same sequence set
2064 * identifier back in to this frame as additional views, and close the
2065 * expanded views. Note the expanded frames may themselves have multiple
2066 * views. We take the lot.
2070 public void gatherViews(AlignFrame source)
2072 source.viewport.setGatherViewsHere(true);
2073 source.viewport.setExplodedGeometry(source.getBounds());
2074 JInternalFrame[] frames = desktopPane.getAllFrames();
2075 String viewId = source.viewport.getSequenceSetId();
2076 for (int t = 0; t < frames.length; t++)
2078 if (frames[t] instanceof AlignFrame && frames[t] != source)
2080 AlignFrame af = (AlignFrame) frames[t];
2081 boolean gatherThis = false;
2082 for (int a = 0; a < af.alignPanels.size(); a++)
2084 AlignmentPanel ap = af.alignPanels.get(a);
2085 if (viewId.equals(ap.av.getSequenceSetId()))
2088 ap.av.setGatherViewsHere(false);
2089 ap.av.setExplodedGeometry(af.getBounds());
2090 source.addAlignmentPanel(ap, false);
2096 if (af.featureSettings != null && af.featureSettings.isOpen())
2098 if (source.featureSettings == null)
2100 // preserve the feature settings geometry for this frame
2101 source.featureSettings = af.featureSettings;
2102 source.setFeatureSettingsGeometry(
2103 af.getFeatureSettingsGeometry());
2107 // close it and forget
2108 af.featureSettings.close();
2111 af.alignPanels.clear();
2112 af.closeMenuItem_actionPerformed(true);
2117 // refresh the feature setting UI for the source frame if it exists
2118 if (source.featureSettings != null && source.featureSettings.isOpen())
2120 source.showFeatureSettingsUI();
2124 public JInternalFrame[] getAllFrames()
2126 return desktopPane.getAllFrames();
2130 * Checks the given url to see if it gives a response indicating that the user
2131 * should be informed of a new questionnaire.
2135 public void checkForQuestionnaire(String url)
2137 UserQuestionnaireCheck jvq = new UserQuestionnaireCheck(url);
2138 // javax.swing.SwingUtilities.invokeLater(jvq);
2139 new Thread(jvq).start();
2142 public void checkURLLinks()
2144 // Thread off the URL link checker
2145 addDialogThread(new Runnable()
2150 if (Cache.getDefault("CHECKURLLINKS", true))
2152 // check what the actual links are - if it's just the default don't
2153 // bother with the warning
2154 List<String> links = Preferences.sequenceUrlLinks
2157 // only need to check links if there is one with a
2158 // SEQUENCE_ID which is not the default EMBL_EBI link
2159 ListIterator<String> li = links.listIterator();
2160 boolean check = false;
2161 List<JLabel> urls = new ArrayList<>();
2162 while (li.hasNext())
2164 String link = li.next();
2165 if (link.contains(jalview.util.UrlConstants.SEQUENCE_ID)
2166 && !UrlConstants.isDefaultString(link))
2169 int barPos = link.indexOf("|");
2170 String urlMsg = barPos == -1 ? link
2171 : link.substring(0, barPos) + ": "
2172 + link.substring(barPos + 1);
2173 urls.add(new JLabel(urlMsg));
2181 // ask user to check in case URL links use old style tokens
2182 // ($SEQUENCE_ID$ for sequence id _or_ accession id)
2183 JPanel msgPanel = new JPanel();
2184 msgPanel.setLayout(new BoxLayout(msgPanel, BoxLayout.PAGE_AXIS));
2185 msgPanel.add(Box.createVerticalGlue());
2186 JLabel msg = new JLabel(MessageManager
2187 .getString("label.SEQUENCE_ID_for_DB_ACCESSION1"));
2188 JLabel msg2 = new JLabel(MessageManager
2189 .getString("label.SEQUENCE_ID_for_DB_ACCESSION2"));
2191 for (JLabel url : urls)
2197 final JCheckBox jcb = new JCheckBox(
2198 MessageManager.getString("label.do_not_display_again"));
2199 jcb.addActionListener(new ActionListener()
2202 public void actionPerformed(ActionEvent e)
2204 // update Cache settings for "don't show this again"
2205 boolean showWarningAgain = !jcb.isSelected();
2206 Cache.setProperty("CHECKURLLINKS",
2207 Boolean.valueOf(showWarningAgain).toString());
2212 JvOptionPane.showMessageDialog(desktopPane, msgPanel,
2214 .getString("label.SEQUENCE_ID_no_longer_used"),
2215 JvOptionPane.WARNING_MESSAGE);
2222 * Proxy class for JDesktopPane which optionally displays the current memory
2223 * usage and highlights the desktop area with a red bar if free memory runs
2228 public class MyDesktopPane extends JDesktopPane implements Runnable
2230 private static final float ONE_MB = 1048576f;
2232 boolean showMemoryUsage = false;
2236 java.text.NumberFormat df;
2238 float maxMemory, allocatedMemory, freeMemory, totalFreeMemory,
2241 public MyDesktopPane(boolean showMemoryUsage)
2243 showMemoryUsage(showMemoryUsage);
2246 public void showMemoryUsage(boolean showMemory)
2248 this.showMemoryUsage = showMemory;
2251 Thread worker = new Thread(this);
2257 public boolean isShowMemoryUsage()
2259 return showMemoryUsage;
2265 df = java.text.NumberFormat.getNumberInstance();
2266 df.setMaximumFractionDigits(2);
2267 runtime = Runtime.getRuntime();
2269 while (showMemoryUsage)
2273 maxMemory = runtime.maxMemory() / ONE_MB;
2274 allocatedMemory = runtime.totalMemory() / ONE_MB;
2275 freeMemory = runtime.freeMemory() / ONE_MB;
2276 totalFreeMemory = freeMemory + (maxMemory - allocatedMemory);
2278 percentUsage = (totalFreeMemory / maxMemory) * 100;
2280 // if (percentUsage < 20)
2282 // border1 = BorderFactory.createMatteBorder(12, 12, 12, 12,
2284 // instance.set.setBorder(border1);
2287 // sleep after showing usage
2289 } catch (Exception ex)
2291 ex.printStackTrace();
2297 public void paintComponent(Graphics g)
2299 if (showMemoryUsage && g != null && df != null)
2301 if (percentUsage < 20)
2303 g.setColor(Color.red);
2305 FontMetrics fm = g.getFontMetrics();
2308 g.drawString(MessageManager.formatMessage("label.memory_stats",
2310 { df.format(totalFreeMemory), df.format(maxMemory),
2311 df.format(percentUsage) }),
2312 10, getHeight() - fm.getHeight());
2319 * Accessor method to quickly get all the AlignmentFrames loaded.
2321 * @return an array of AlignFrame, or null if none found
2323 public static AlignFrame[] getAlignFrames()
2325 if (Jalview.isHeadlessMode())
2327 return new AlignFrame[] { Jalview.getInstance().currentAlignFrame };
2330 JInternalFrame[] frames = getDesktopPane().getAllFrames();
2336 List<AlignFrame> avp = new ArrayList<>();
2338 for (int i = frames.length - 1; i > -1; i--)
2340 if (frames[i] instanceof AlignFrame)
2342 avp.add((AlignFrame) frames[i]);
2344 else if (frames[i] instanceof SplitFrame)
2347 * Also check for a split frame containing an AlignFrame
2349 GSplitFrame sf = (GSplitFrame) frames[i];
2350 if (sf.getTopFrame() instanceof AlignFrame)
2352 avp.add((AlignFrame) sf.getTopFrame());
2354 if (sf.getBottomFrame() instanceof AlignFrame)
2356 avp.add((AlignFrame) sf.getBottomFrame());
2360 if (avp.size() == 0)
2364 AlignFrame afs[] = avp.toArray(new AlignFrame[avp.size()]);
2369 * Returns an array of any AppJmol frames in the Desktop (or null if none).
2373 public GStructureViewer[] getJmols()
2375 JInternalFrame[] frames = desktopPane.getAllFrames();
2381 List<GStructureViewer> avp = new ArrayList<>();
2383 for (int i = frames.length - 1; i > -1; i--)
2385 if (frames[i] instanceof AppJmol)
2387 GStructureViewer af = (GStructureViewer) frames[i];
2391 if (avp.size() == 0)
2395 GStructureViewer afs[] = avp.toArray(new GStructureViewer[avp.size()]);
2400 * Add Groovy Support to Jalview
2403 public void groovyShell_actionPerformed()
2407 openGroovyConsole();
2408 } catch (Exception ex)
2410 Cache.log.error("Groovy Shell Creation failed.", ex);
2411 JvOptionPane.showInternalMessageDialog(desktopPane,
2413 MessageManager.getString("label.couldnt_create_groovy_shell"),
2414 MessageManager.getString("label.groovy_support_failed"),
2415 JvOptionPane.ERROR_MESSAGE);
2420 * Open the Groovy console
2422 void openGroovyConsole()
2424 if (groovyConsole == null)
2426 groovyConsole = new groovy.ui.Console();
2427 groovyConsole.setVariable("Jalview", this);
2428 groovyConsole.run();
2431 * We allow only one console at a time, so that AlignFrame menu option
2432 * 'Calculate | Run Groovy script' is unambiguous.
2433 * Disable 'Groovy Console', and enable 'Run script', when the console is
2434 * opened, and the reverse when it is closed
2436 Window window = (Window) groovyConsole.getFrame();
2437 window.addWindowListener(new WindowAdapter()
2440 public void windowClosed(WindowEvent e)
2443 * rebind CMD-Q from Groovy Console to Jalview Quit
2446 enableExecuteGroovy(false);
2452 * show Groovy console window (after close and reopen)
2454 ((Window) groovyConsole.getFrame()).setVisible(true);
2457 * if we got this far, enable 'Run Groovy' in AlignFrame menus
2458 * and disable opening a second console
2460 enableExecuteGroovy(true);
2464 * Bind Ctrl/Cmd-Q to Quit - for reset as Groovy Console takes over this
2465 * binding when opened
2467 protected void addQuitHandler()
2470 .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
2472 .getKeyStroke(KeyEvent.VK_Q,
2473 jalview.util.ShortcutKeyMaskExWrapper
2474 .getMenuShortcutKeyMaskEx()),
2476 getRootPane().getActionMap().put("Quit", new AbstractAction()
2479 public void actionPerformed(ActionEvent e)
2487 * Enable or disable 'Run Groovy script' in AlignFrame calculate menus
2490 * true if Groovy console is open
2492 public void enableExecuteGroovy(boolean enabled)
2495 * disable opening a second Groovy console
2496 * (or re-enable when the console is closed)
2498 groovyShell.setEnabled(!enabled);
2500 AlignFrame[] alignFrames = getAlignFrames();
2501 if (alignFrames != null)
2503 for (AlignFrame af : alignFrames)
2505 af.setGroovyEnabled(enabled);
2511 * Progress bars managed by the IProgressIndicator method.
2513 private Hashtable<Long, JPanel> progressBars;
2515 private Hashtable<Long, IProgressIndicatorHandler> progressBarHandlers;
2520 * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
2523 public void setProgressBar(String message, long id)
2525 // Platform.timeCheck("Desktop " + message, Platform.TIME_MARK);
2527 if (progressBars == null)
2529 progressBars = new Hashtable<>();
2530 progressBarHandlers = new Hashtable<>();
2533 if (progressBars.get(Long.valueOf(id)) != null)
2535 JPanel panel = progressBars.remove(Long.valueOf(id));
2536 if (progressBarHandlers.contains(Long.valueOf(id)))
2538 progressBarHandlers.remove(Long.valueOf(id));
2540 removeProgressPanel(panel);
2544 progressBars.put(Long.valueOf(id), addProgressPanel(message));
2551 * @see jalview.gui.IProgressIndicator#registerHandler(long,
2552 * jalview.gui.IProgressIndicatorHandler)
2555 public void registerHandler(final long id,
2556 final IProgressIndicatorHandler handler)
2558 if (progressBarHandlers == null
2559 || !progressBars.containsKey(Long.valueOf(id)))
2561 throw new Error(MessageManager.getString(
2562 "error.call_setprogressbar_before_registering_handler"));
2564 progressBarHandlers.put(Long.valueOf(id), handler);
2565 final JPanel progressPanel = progressBars.get(Long.valueOf(id));
2566 if (handler.canCancel())
2568 JButton cancel = new JButton(
2569 MessageManager.getString("action.cancel"));
2570 final IProgressIndicator us = this;
2571 cancel.addActionListener(new ActionListener()
2575 public void actionPerformed(ActionEvent e)
2577 handler.cancelActivity(id);
2578 us.setProgressBar(MessageManager
2579 .formatMessage("label.cancelled_params", new Object[]
2580 { ((JLabel) progressPanel.getComponent(0)).getText() }),
2584 progressPanel.add(cancel, BorderLayout.EAST);
2590 * @return true if any progress bars are still active
2593 public boolean operationInProgress()
2595 if (progressBars != null && progressBars.size() > 0)
2603 * This will return the first AlignFrame holding the given viewport instance.
2604 * It will break if there are more than one AlignFrames viewing a particular
2608 * @return alignFrame for viewport
2610 public static AlignFrame getAlignFrameFor(AlignViewportI viewport)
2612 if ( getDesktopPane() != null)
2614 AlignmentPanel[] aps = getAlignmentPanels(
2615 viewport.getSequenceSetId());
2616 for (int panel = 0; aps != null && panel < aps.length; panel++)
2618 if (aps[panel] != null && aps[panel].av == viewport)
2620 return aps[panel].alignFrame;
2627 public VamsasApplication getVamsasApplication()
2629 // TODO: JAL-3311 remove remaining code from Jalview relating to VAMSAS
2635 * flag set if jalview GUI is being operated programmatically
2637 private boolean inBatchMode = false;
2640 * check if jalview GUI is being operated programmatically
2642 * @return inBatchMode
2644 public boolean isInBatchMode()
2650 * set flag if jalview GUI is being operated programmatically
2652 * @param inBatchMode
2654 public void setInBatchMode(boolean inBatchMode)
2656 this.inBatchMode = inBatchMode;
2659 public void startServiceDiscovery()
2661 startServiceDiscovery(false);
2664 public void startServiceDiscovery(boolean blocking)
2666 boolean alive = true;
2667 Thread t0 = null, t1 = null, t2 = null;
2668 // JAL-940 - JALVIEW 1 services are now being EOLed as of JABA 2.1 release
2671 // todo: changesupport handlers need to be transferred
2672 if (discoverer == null)
2674 discoverer = jalview.ws.jws1.Discoverer.getInstance();
2675 // register PCS handler for desktop.
2676 discoverer.addPropertyChangeListener(changeSupport);
2678 // JAL-940 - disabled JWS1 service configuration - always start discoverer
2679 // until we phase out completely
2680 (t0 = new Thread(discoverer)).start();
2683 if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
2685 t2 = jalview.ws.jws2.Jws2Discoverer.getInstance()
2686 .startDiscoverer(changeSupport);
2690 // TODO: do rest service discovery
2699 } catch (Exception e)
2702 alive = (t1 != null && t1.isAlive()) || (t2 != null && t2.isAlive())
2703 || (t3 != null && t3.isAlive())
2704 || (t0 != null && t0.isAlive());
2710 * called to check if the service discovery process completed successfully.
2714 protected void JalviewServicesChanged(PropertyChangeEvent evt)
2716 if (evt.getNewValue() == null || evt.getNewValue() instanceof Vector)
2718 final String ermsg = jalview.ws.jws2.Jws2Discoverer.getInstance()
2719 .getErrorMessages();
2722 if (Cache.getDefault("SHOW_WSDISCOVERY_ERRORS", true))
2724 if (serviceChangedDialog == null)
2726 // only run if we aren't already displaying one of these.
2727 addDialogThread(serviceChangedDialog = new Runnable()
2734 * JalviewDialog jd =new JalviewDialog() {
2736 * @Override protected void cancelPressed() { // TODO
2737 * Auto-generated method stub
2739 * }@Override protected void okPressed() { // TODO
2740 * Auto-generated method stub
2742 * }@Override protected void raiseClosed() { // TODO
2743 * Auto-generated method stub
2745 * } }; jd.initDialogFrame(new
2746 * JLabel("<html><table width=\"450\"><tr><td>" + ermsg +
2747 * "<br/>It may be that you have invalid JABA URLs in your web service preferences,"
2748 * + " or mis-configured HTTP proxy settings.<br/>" +
2749 * "Check the <em>Connections</em> and <em>Web services</em> tab of the"
2751 * " Tools->Preferences dialog box to change them.</td></tr></table></html>"
2752 * ), true, true, "Web Service Configuration Problem", 450,
2755 * jd.waitForInput();
2757 JvOptionPane.showConfirmDialog(desktopPane,
2758 new JLabel("<html><table width=\"450\"><tr><td>"
2759 + ermsg + "</td></tr></table>"
2760 + "<p>It may be that you have invalid JABA URLs<br/>in your web service preferences,"
2761 + "<br>or as a command-line argument, or mis-configured HTTP proxy settings.</p>"
2762 + "<p>Check the <em>Connections</em> and <em>Web services</em> tab<br/>of the"
2763 + " Tools->Preferences dialog box to change them.</p></html>"),
2764 "Web Service Configuration Problem",
2765 JvOptionPane.DEFAULT_OPTION,
2766 JvOptionPane.ERROR_MESSAGE);
2767 serviceChangedDialog = null;
2776 "Errors reported by JABA discovery service. Check web services preferences.\n"
2783 private Runnable serviceChangedDialog = null;
2786 * start a thread to open a URL in the configured browser. Pops up a warning
2787 * dialog to the user if there is an exception when calling out to the browser
2792 public static void showUrl(final String url)
2794 showUrl(url, getInstance());
2798 * Like showUrl but allows progress handler to be specified
2802 * (null) or object implementing IProgressIndicator
2804 public static void showUrl(final String url,
2805 final IProgressIndicator progress)
2807 new Thread(new Runnable()
2814 if (progress != null)
2816 progress.setProgressBar(MessageManager
2817 .formatMessage("status.opening_params", new Object[]
2818 { url }), this.hashCode());
2820 jalview.util.BrowserLauncher.openURL(url);
2821 } catch (Exception ex)
2823 JvOptionPane.showInternalMessageDialog( getDesktopPane(),
2825 .getString("label.web_browser_not_found_unix"),
2826 MessageManager.getString("label.web_browser_not_found"),
2827 JvOptionPane.WARNING_MESSAGE);
2829 ex.printStackTrace();
2831 if (progress != null)
2833 progress.setProgressBar(null, this.hashCode());
2839 public static WsParamSetManager wsparamManager = null;
2841 public static ParamManager getUserParameterStore()
2843 if (wsparamManager == null)
2845 wsparamManager = new WsParamSetManager();
2847 return wsparamManager;
2851 * static hyperlink handler proxy method for use by Jalview's internal windows
2855 public static void hyperlinkUpdate(HyperlinkEvent e)
2857 if (e.getEventType() == EventType.ACTIVATED)
2862 url = e.getURL().toString();
2864 } catch (Exception x)
2868 if (Cache.log != null)
2870 Cache.log.error("Couldn't handle string " + url + " as a URL.");
2875 "Couldn't handle string " + url + " as a URL.");
2878 // ignore any exceptions due to dud links.
2885 * single thread that handles display of dialogs to user.
2887 ExecutorService dialogExecutor = Executors.newSingleThreadExecutor();
2890 * flag indicating if dialogExecutor should try to acquire a permit
2892 private volatile boolean dialogPause = true;
2897 private java.util.concurrent.Semaphore block = new Semaphore(0);
2899 private static groovy.ui.Console groovyConsole;
2902 * add another dialog thread to the queue
2906 public void addDialogThread(final Runnable prompter)
2908 dialogExecutor.submit(new Runnable()
2918 } catch (InterruptedException x)
2922 if (Jalview.isHeadlessMode())
2928 SwingUtilities.invokeAndWait(prompter);
2929 } catch (Exception q)
2931 Cache.log.warn("Unexpected Exception in dialog thread.", q);
2937 public void startDialogQueue()
2939 // set the flag so we don't pause waiting for another permit and semaphore
2940 // the current task to begin
2941 dialogPause = false;
2946 * Outputs an image of the desktop to file in EPS format, after prompting the
2947 * user for choice of Text or Lineart character rendering (unless a preference
2948 * has been set). The file name is generated as
2951 * Jalview_snapshot_nnnnn.eps where nnnnn is the current timestamp in milliseconds
2955 protected void snapShotWindow_actionPerformed(ActionEvent e)
2957 // currently the menu option to do this is not shown
2960 int width = getWidth();
2961 int height = getHeight();
2963 "Jalview_snapshot_" + System.currentTimeMillis() + ".eps");
2964 ImageWriterI writer = new ImageWriterI()
2967 public void exportImage(Graphics g) throws Exception
2970 Cache.log.info("Successfully written snapshot to file "
2971 + of.getAbsolutePath());
2974 String title = "View of desktop";
2975 ImageExporter exporter = new ImageExporter(writer, null, TYPE.EPS,
2977 exporter.doExport(of, this, width, height, title);
2981 * Explode the views in the given SplitFrame into separate SplitFrame windows.
2982 * This respects (remembers) any previous 'exploded geometry' i.e. the size
2983 * and location last time the view was expanded (if any). However it does not
2984 * remember the split pane divider location - this is set to match the
2985 * 'exploding' frame.
2989 public void explodeViews(SplitFrame sf)
2991 AlignFrame oldTopFrame = (AlignFrame) sf.getTopFrame();
2992 AlignFrame oldBottomFrame = (AlignFrame) sf.getBottomFrame();
2993 List<? extends AlignmentViewPanel> topPanels = oldTopFrame
2995 List<? extends AlignmentViewPanel> bottomPanels = oldBottomFrame
2997 int viewCount = topPanels.size();
3004 * Processing in reverse order works, forwards order leaves the first panels
3005 * not visible. I don't know why!
3007 for (int i = viewCount - 1; i >= 0; i--)
3010 * Make new top and bottom frames. These take over the respective
3011 * AlignmentPanel objects, including their AlignmentViewports, so the
3012 * cdna/protein relationships between the viewports is carried over to the
3015 * explodedGeometry holds the (x, y) position of the previously exploded
3016 * SplitFrame, and the (width, height) of the AlignFrame component
3018 AlignmentPanel topPanel = (AlignmentPanel) topPanels.get(i);
3019 AlignFrame newTopFrame = new AlignFrame(topPanel);
3020 newTopFrame.setSize(oldTopFrame.getSize());
3021 newTopFrame.setVisible(true);
3022 Rectangle geometry = ((AlignViewport) topPanel.getAlignViewport())
3023 .getExplodedGeometry();
3024 if (geometry != null)
3026 newTopFrame.setSize(geometry.getSize());
3029 AlignmentPanel bottomPanel = (AlignmentPanel) bottomPanels.get(i);
3030 AlignFrame newBottomFrame = new AlignFrame(bottomPanel);
3031 newBottomFrame.setSize(oldBottomFrame.getSize());
3032 newBottomFrame.setVisible(true);
3033 geometry = ((AlignViewport) bottomPanel.getAlignViewport())
3034 .getExplodedGeometry();
3035 if (geometry != null)
3037 newBottomFrame.setSize(geometry.getSize());
3040 topPanel.av.setGatherViewsHere(false);
3041 bottomPanel.av.setGatherViewsHere(false);
3042 JInternalFrame splitFrame = new SplitFrame(newTopFrame,
3044 if (geometry != null)
3046 splitFrame.setLocation(geometry.getLocation());
3048 addInternalFrame(splitFrame, sf.getTitle(), -1, -1);
3052 * Clear references to the panels (now relocated in the new SplitFrames)
3053 * before closing the old SplitFrame.
3056 bottomPanels.clear();
3061 * Gather expanded split frames, sharing the same pairs of sequence set ids,
3062 * back into the given SplitFrame as additional views. Note that the gathered
3063 * frames may themselves have multiple views.
3067 public void gatherViews(GSplitFrame source)
3070 * special handling of explodedGeometry for a view within a SplitFrame: - it
3071 * holds the (x, y) position of the enclosing SplitFrame, and the (width,
3072 * height) of the AlignFrame component
3074 AlignFrame myTopFrame = (AlignFrame) source.getTopFrame();
3075 AlignFrame myBottomFrame = (AlignFrame) source.getBottomFrame();
3076 myTopFrame.viewport.setExplodedGeometry(new Rectangle(source.getX(),
3077 source.getY(), myTopFrame.getWidth(), myTopFrame.getHeight()));
3078 myBottomFrame.viewport
3079 .setExplodedGeometry(new Rectangle(source.getX(), source.getY(),
3080 myBottomFrame.getWidth(), myBottomFrame.getHeight()));
3081 myTopFrame.viewport.setGatherViewsHere(true);
3082 myBottomFrame.viewport.setGatherViewsHere(true);
3083 String topViewId = myTopFrame.viewport.getSequenceSetId();
3084 String bottomViewId = myBottomFrame.viewport.getSequenceSetId();
3086 JInternalFrame[] frames = desktopPane.getAllFrames();
3087 for (JInternalFrame frame : frames)
3089 if (frame instanceof SplitFrame && frame != source)
3091 SplitFrame sf = (SplitFrame) frame;
3092 AlignFrame topFrame = (AlignFrame) sf.getTopFrame();
3093 AlignFrame bottomFrame = (AlignFrame) sf.getBottomFrame();
3094 boolean gatherThis = false;
3095 for (int a = 0; a < topFrame.alignPanels.size(); a++)
3097 AlignmentPanel topPanel = topFrame.alignPanels.get(a);
3098 AlignmentPanel bottomPanel = bottomFrame.alignPanels.get(a);
3099 if (topViewId.equals(topPanel.av.getSequenceSetId())
3100 && bottomViewId.equals(bottomPanel.av.getSequenceSetId()))
3103 topPanel.av.setGatherViewsHere(false);
3104 bottomPanel.av.setGatherViewsHere(false);
3105 topPanel.av.setExplodedGeometry(
3106 new Rectangle(sf.getLocation(), topFrame.getSize()));
3107 bottomPanel.av.setExplodedGeometry(
3108 new Rectangle(sf.getLocation(), bottomFrame.getSize()));
3109 myTopFrame.addAlignmentPanel(topPanel, false);
3110 myBottomFrame.addAlignmentPanel(bottomPanel, false);
3116 topFrame.getAlignPanels().clear();
3117 bottomFrame.getAlignPanels().clear();
3124 * The dust settles...give focus to the tab we did this from.
3126 myTopFrame.setDisplayedView(myTopFrame.alignPanel);
3129 public static groovy.ui.Console getGroovyConsole()
3131 return groovyConsole;
3135 * handles the payload of a drag and drop event.
3137 * TODO refactor to desktop utilities class
3140 * - Data source strings extracted from the drop event
3142 * - protocol for each data source extracted from the drop event
3146 * - the payload from the drop event
3149 @SuppressWarnings("unchecked")
3150 public static void transferFromDropTarget(List<Object> files,
3151 List<DataSourceType> protocols, DropTargetDropEvent evt,
3152 Transferable t) throws Exception
3155 // BH 2018 changed List<String> to List<Object> to allow for File from
3158 // DataFlavor[] flavors = t.getTransferDataFlavors();
3159 // for (int i = 0; i < flavors.length; i++) {
3160 // if (flavors[i].isFlavorJavaFileListType()) {
3161 // evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
3162 // List<File> list = (List<File>) t.getTransferData(flavors[i]);
3163 // for (int j = 0; j < list.size(); j++) {
3164 // File file = (File) list.get(j);
3165 // byte[] data = getDroppedFileBytes(file);
3166 // fileName.setText(file.getName() + " - " + data.length + " " +
3167 // evt.getLocation());
3168 // JTextArea target = (JTextArea) ((DropTarget)
3169 // evt.getSource()).getComponent();
3170 // target.setText(new String(data));
3172 // dtde.dropComplete(true);
3177 DataFlavor uriListFlavor = new DataFlavor(
3178 "text/uri-list;class=java.lang.String"), urlFlavour = null;
3181 urlFlavour = new DataFlavor(
3182 "application/x-java-url; class=java.net.URL");
3183 } catch (ClassNotFoundException cfe)
3185 Cache.log.debug("Couldn't instantiate the URL dataflavor.", cfe);
3188 if (urlFlavour != null && t.isDataFlavorSupported(urlFlavour))
3193 java.net.URL url = (URL) t.getTransferData(urlFlavour);
3194 // nb: java 8 osx bug https://bugs.openjdk.java.net/browse/JDK-8156099
3195 // means url may be null.
3198 protocols.add(DataSourceType.URL);
3199 files.add(url.toString());
3200 Cache.log.debug("Drop handled as URL dataflavor "
3201 + files.get(files.size() - 1));
3206 if (Platform.isAMacAndNotJS())
3209 "Please ignore plist error - occurs due to problem with java 8 on OSX");
3212 } catch (Throwable ex)
3214 Cache.log.debug("URL drop handler failed.", ex);
3217 if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
3219 // Works on Windows and MacOSX
3220 Cache.log.debug("Drop handled as javaFileListFlavor");
3221 for (File file : (List<File>) t
3222 .getTransferData(DataFlavor.javaFileListFlavor))
3225 protocols.add(DataSourceType.FILE);
3230 // Unix like behaviour
3231 boolean added = false;
3233 if (t.isDataFlavorSupported(uriListFlavor))
3235 Cache.log.debug("Drop handled as uriListFlavor");
3236 // This is used by Unix drag system
3237 data = (String) t.getTransferData(uriListFlavor);
3241 // fallback to text: workaround - on OSX where there's a JVM bug
3242 Cache.log.debug("standard URIListFlavor failed. Trying text");
3243 // try text fallback
3244 DataFlavor textDf = new DataFlavor(
3245 "text/plain;class=java.lang.String");
3246 if (t.isDataFlavorSupported(textDf))
3248 data = (String) t.getTransferData(textDf);
3251 Cache.log.debug("Plain text drop content returned "
3252 + (data == null ? "Null - failed" : data));
3257 while (protocols.size() < files.size())
3259 Cache.log.debug("Adding missing FILE protocol for "
3260 + files.get(protocols.size()));
3261 protocols.add(DataSourceType.FILE);
3263 for (java.util.StringTokenizer st = new java.util.StringTokenizer(
3264 data, "\r\n"); st.hasMoreTokens();)
3267 String s = st.nextToken();
3268 if (s.startsWith("#"))
3270 // the line is a comment (as per the RFC 2483)
3273 java.net.URI uri = new java.net.URI(s);
3274 if (uri.getScheme().toLowerCase().startsWith("http"))
3276 protocols.add(DataSourceType.URL);
3277 files.add(uri.toString());
3281 // otherwise preserve old behaviour: catch all for file objects
3282 java.io.File file = new java.io.File(uri);
3283 protocols.add(DataSourceType.FILE);
3284 files.add(file.toString());
3289 if (Cache.log.isDebugEnabled())
3291 if (data == null || !added)
3294 if (t.getTransferDataFlavors() != null
3295 && t.getTransferDataFlavors().length > 0)
3298 "Couldn't resolve drop data. Here are the supported flavors:");
3299 for (DataFlavor fl : t.getTransferDataFlavors())
3302 "Supported transfer dataflavor: " + fl.toString());
3303 Object df = t.getTransferData(fl);
3306 Cache.log.debug("Retrieves: " + df);
3310 Cache.log.debug("Retrieved nothing");
3316 Cache.log.debug("Couldn't resolve dataflavor for drop: "
3322 if (Platform.isWindowsAndNotJS())
3324 Cache.log.debug("Scanning dropped content for Windows Link Files");
3326 // resolve any .lnk files in the file drop
3327 for (int f = 0; f < files.size(); f++)
3329 String source = files.get(f).toString().toLowerCase();
3330 if (protocols.get(f).equals(DataSourceType.FILE)
3331 && (source.endsWith(".lnk") || source.endsWith(".url")
3332 || source.endsWith(".site")))
3336 Object obj = files.get(f);
3337 File lf = (obj instanceof File ? (File) obj
3338 : new File((String) obj));
3339 // process link file to get a URL
3340 Cache.log.debug("Found potential link file: " + lf);
3341 WindowsShortcut wscfile = new WindowsShortcut(lf);
3342 String fullname = wscfile.getRealFilename();
3343 protocols.set(f, FormatAdapter.checkProtocol(fullname));
3344 files.set(f, fullname);
3345 Cache.log.debug("Parsed real filename " + fullname
3346 + " to extract protocol: " + protocols.get(f));
3347 } catch (Exception ex)
3350 "Couldn't parse " + files.get(f) + " as a link file.",
3359 * Sets the Preferences property for experimental features to True or False
3360 * depending on the state of the controlling menu item
3363 protected void showExperimental_actionPerformed(boolean selected)
3365 Cache.setProperty(EXPERIMENTAL_FEATURES, Boolean.toString(selected));
3369 * Answers a (possibly empty) list of any structure viewer frames (currently
3370 * for either Jmol or Chimera) which are currently open. This may optionally
3371 * be restricted to viewers of a specified class, or viewers linked to a
3372 * specified alignment panel.
3375 * if not null, only return viewers linked to this panel
3376 * @param structureViewerClass
3377 * if not null, only return viewers of this class
3380 public List<StructureViewerBase> getStructureViewers(
3381 AlignmentPanel apanel,
3382 Class<? extends StructureViewerBase> structureViewerClass)
3384 List<StructureViewerBase> result = new ArrayList<>();
3385 JInternalFrame[] frames = getAllFrames();
3387 for (JInternalFrame frame : frames)
3389 if (frame instanceof StructureViewerBase)
3391 if (structureViewerClass == null
3392 || structureViewerClass.isInstance(frame))
3395 || ((StructureViewerBase) frame).isLinkedWith(apanel))
3397 result.add((StructureViewerBase) frame);