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());
488 // Spawn a thread that shows the splashscreen
490 SwingUtilities.invokeLater(new Runnable()
495 new SplashScreen(true);
499 // Thread off a new instance of the file chooser - this reduces the time
501 // takes to open it later on.
502 new Thread(new Runnable()
507 Cache.log.debug("Filechooser init thread started.");
508 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
509 JalviewFileChooser.forRead(Cache.getProperty("LAST_DIRECTORY"),
511 Cache.log.debug("Filechooser init thread finished.");
514 // Add the service change listener
515 changeSupport.addJalviewPropertyChangeListener("services",
516 new PropertyChangeListener()
520 public void propertyChange(PropertyChangeEvent evt)
522 Cache.log.debug("Firing service changed event for "
523 + evt.getNewValue());
524 JalviewServicesChanged(evt);
529 this.setDropTarget(new java.awt.dnd.DropTarget(desktopPane, this));
531 this.addWindowListener(new WindowAdapter()
534 public void windowClosing(WindowEvent evt)
541 this.addMouseListener(ma = new MouseAdapter()
544 public void mousePressed(MouseEvent evt)
546 if (evt.isPopupTrigger()) // Mac
548 showPasteMenu(evt.getX(), evt.getY());
553 public void mouseReleased(MouseEvent evt)
555 if (evt.isPopupTrigger()) // Windows
557 showPasteMenu(evt.getX(), evt.getY());
561 desktopPane.addMouseListener(ma);
562 } catch (Throwable t)
570 * Answers true if user preferences to enable experimental features is True
575 public boolean showExperimental()
577 String experimental = Cache.getDefault(EXPERIMENTAL_FEATURES,
578 Boolean.FALSE.toString());
579 return Boolean.valueOf(experimental).booleanValue();
582 public void doConfigureStructurePrefs()
584 // configure services
585 StructureSelectionManager ssm = StructureSelectionManager
586 .getStructureSelectionManager(this);
587 if (Cache.getDefault(Preferences.ADD_SS_ANN, true))
589 ssm.setAddTempFacAnnot(
590 Cache.getDefault(Preferences.ADD_TEMPFACT_ANN, true));
591 ssm.setProcessSecondaryStructure(
592 Cache.getDefault(Preferences.STRUCT_FROM_PDB, true));
593 ssm.setSecStructServices(
594 Cache.getDefault(Preferences.USE_RNAVIEW, true));
598 ssm.setAddTempFacAnnot(false);
599 ssm.setProcessSecondaryStructure(false);
600 ssm.setSecStructServices(false);
604 public void checkForNews()
606 final Desktop me = this;
607 // Thread off the news reader, in case there are connection problems.
608 new Thread(new Runnable()
613 Cache.log.debug("Starting news thread.");
614 jvnews = new BlogReader(me);
615 showNews.setVisible(true);
616 Cache.log.debug("Completed news thread.");
621 public void getIdentifiersOrgData()
623 // Thread off the identifiers fetcher
624 new Thread(new Runnable()
629 Cache.log.debug("Downloading data from identifiers.org");
632 UrlDownloadClient.download(IdOrgSettings.getUrl(),
633 IdOrgSettings.getDownloadLocation());
634 } catch (IOException e)
636 Cache.log.debug("Exception downloading identifiers.org data"
645 protected void showNews_actionPerformed(ActionEvent e)
647 showNews(showNews.isSelected());
650 void showNews(boolean visible)
652 Cache.log.debug((visible ? "Showing" : "Hiding") + " news.");
653 showNews.setSelected(visible);
654 if (visible && !jvnews.isVisible())
656 new Thread(new Runnable()
661 long now = System.currentTimeMillis();
663 MessageManager.getString("status.refreshing_news"), now);
664 jvnews.refreshNews();
665 setProgressBar(null, now);
673 * recover the last known dimensions for a jalview window
676 * - empty string is desktop, all other windows have unique prefix
677 * @return null or last known dimensions scaled to current geometry (if last
678 * window geom was known)
680 Rectangle getLastKnownDimensions(String windowName)
682 // TODO: lock aspect ratio for scaling desktop Bug #0058199
683 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
684 String x = Cache.getProperty(windowName + "SCREEN_X");
685 String y = Cache.getProperty(windowName + "SCREEN_Y");
686 String width = Cache.getProperty(windowName + "SCREEN_WIDTH");
687 String height = Cache.getProperty(windowName + "SCREEN_HEIGHT");
688 if ((x != null) && (y != null) && (width != null) && (height != null))
690 int ix = Integer.parseInt(x), iy = Integer.parseInt(y),
691 iw = Integer.parseInt(width), ih = Integer.parseInt(height);
692 if (Cache.getProperty("SCREENGEOMETRY_WIDTH") != null)
694 // attempt #1 - try to cope with change in screen geometry - this
695 // version doesn't preserve original jv aspect ratio.
696 // take ratio of current screen size vs original screen size.
697 double sw = ((1f * screenSize.width) / (1f * Integer
698 .parseInt(Cache.getProperty("SCREENGEOMETRY_WIDTH"))));
699 double sh = ((1f * screenSize.height) / (1f * Integer
700 .parseInt(Cache.getProperty("SCREENGEOMETRY_HEIGHT"))));
701 // rescale the bounds depending upon the current screen geometry.
702 ix = (int) (ix * sw);
703 iw = (int) (iw * sw);
704 iy = (int) (iy * sh);
705 ih = (int) (ih * sh);
706 while (ix >= screenSize.width)
709 "Window geometry location recall error: shifting horizontal to within screenbounds.");
710 ix -= screenSize.width;
712 while (iy >= screenSize.height)
715 "Window geometry location recall error: shifting vertical to within screenbounds.");
716 iy -= screenSize.height;
719 "Got last known dimensions for " + windowName + ": x:" + ix
720 + " y:" + iy + " width:" + iw + " height:" + ih);
722 // return dimensions for new instance
723 return new Rectangle(ix, iy, iw, ih);
728 void showPasteMenu(int x, int y)
730 JPopupMenu popup = new JPopupMenu();
731 JMenuItem item = new JMenuItem(
732 MessageManager.getString("label.paste_new_window"));
733 item.addActionListener(new ActionListener()
736 public void actionPerformed(ActionEvent evt)
743 popup.show(this, x, y);
750 Clipboard c = Toolkit.getDefaultToolkit().getSystemClipboard();
751 Transferable contents = c.getContents(this);
753 if (contents != null)
755 String file = (String) contents
756 .getTransferData(DataFlavor.stringFlavor);
758 FileFormatI format = new IdentifyFile().identify(file,
759 DataSourceType.PASTE);
761 new FileLoader().LoadFile(file, DataSourceType.PASTE, format);
764 } catch (Exception ex)
767 "Unable to paste alignment from system clipboard:\n" + ex);
772 * Adds and opens the given frame to the desktop
783 public static synchronized void addInternalFrame(
784 final JInternalFrame frame, String title, int w, int h)
786 addInternalFrame(frame, title, true, w, h, true, false);
790 * Add an internal frame to the Jalview desktop
797 * When true, display frame immediately, otherwise, caller must call
798 * setVisible themselves.
804 public static synchronized void addInternalFrame(
805 final JInternalFrame frame, String title, boolean makeVisible,
808 addInternalFrame(frame, title, makeVisible, w, h, true, false);
812 * Add an internal frame to the Jalview desktop and make it visible
825 public static synchronized void addInternalFrame(
826 final JInternalFrame frame, String title, int w, int h,
829 addInternalFrame(frame, title, true, w, h, resizable, false);
833 * Add an internal frame to the Jalview desktop
840 * When true, display frame immediately, otherwise, caller must call
841 * setVisible themselves.
848 * @param ignoreMinSize
849 * Do not set the default minimum size for frame
851 public static synchronized void addInternalFrame(
852 final JInternalFrame frame, String title, boolean makeVisible,
853 int w, int h, boolean resizable, boolean ignoreMinSize)
856 getInstance().addFrame(frame, title, makeVisible, w, h, resizable, ignoreMinSize);
859 private void addFrame(JInternalFrame frame, String title,
860 boolean makeVisible, int w, int h, boolean resizable,
861 boolean ignoreMinSize)
863 // TODO: allow callers to determine X and Y position of frame (eg. via
865 // TODO: consider fixing method to update entries in the window submenu with
866 // the current window title
868 frame.setTitle(title);
869 if (frame.getWidth() < 1 || frame.getHeight() < 1)
873 // THIS IS A PUBLIC STATIC METHOD, SO IT MAY BE CALLED EVEN IN
874 // A HEADLESS STATE WHEN NO DESKTOP EXISTS. MUST RETURN
875 // IF JALVIEW IS RUNNING HEADLESS
876 // ///////////////////////////////////////////////
877 if (Jalview.isHeadlessMode())
886 frame.setMinimumSize(
887 new Dimension(DEFAULT_MIN_WIDTH, DEFAULT_MIN_HEIGHT));
889 // Set default dimension for Alignment Frame window.
890 // The Alignment Frame window could be added from a number of places,
892 // I did this here in order not to miss out on any Alignment frame.
893 if (frame instanceof AlignFrame)
895 frame.setMinimumSize(new Dimension(ALIGN_FRAME_DEFAULT_MIN_WIDTH,
896 ALIGN_FRAME_DEFAULT_MIN_HEIGHT));
900 frame.setVisible(makeVisible);
901 frame.setClosable(true);
902 frame.setResizable(resizable);
903 frame.setMaximizable(resizable);
904 frame.setIconifiable(resizable);
905 frame.setOpaque(Platform.isJS());
906 boolean isEmbedded = (Platform.getDimIfEmbedded(frame, -1, -1) != null);
907 if (!isEmbedded && frame.getX() < 1 && frame.getY() < 1)
909 frame.setLocation(xOffset * openFrameCount,
910 yOffset * ((openFrameCount - 1) % 10) + yOffset);
914 * add an entry for the new frame in the Window menu
915 * (and remove it when the frame is closed)
917 final JMenuItem menuItem = new JMenuItem(title);
918 frame.addInternalFrameListener(new InternalFrameAdapter()
921 public void internalFrameActivated(InternalFrameEvent evt)
923 JInternalFrame itf = getDesktopPane().getSelectedFrame();
926 if (itf instanceof AlignFrame)
928 Jalview.setCurrentAlignFrame((AlignFrame) itf);
935 public void internalFrameClosed(InternalFrameEvent evt)
937 PaintRefresher.RemoveComponent(frame);
940 * defensive check to prevent frames being
941 * added half off the window
943 if (openFrameCount > 0)
949 * ensure no reference to alignFrame retained by menu item listener
951 if (menuItem.getActionListeners().length > 0)
953 menuItem.removeActionListener(menuItem.getActionListeners()[0]);
955 getInstance().windowMenu.remove(menuItem);
959 menuItem.addActionListener(new ActionListener()
962 public void actionPerformed(ActionEvent e)
966 frame.setSelected(true);
967 frame.setIcon(false);
968 } catch (java.beans.PropertyVetoException ex)
970 // System.err.println(ex.toString());
975 setKeyBindings(frame);
977 getDesktopPane().add(frame);
979 getInstance().windowMenu.add(menuItem);
984 frame.setSelected(true);
985 frame.requestFocus();
986 } catch (java.beans.PropertyVetoException ve)
988 } catch (java.lang.ClassCastException cex)
991 "Squashed a possible GUI implementation error. If you can recreate this, please look at http://issues.jalview.org/browse/JAL-869",
997 * Add key bindings to a JInternalFrame so that Ctrl-W and Cmd-W will close
1002 private static void setKeyBindings(JInternalFrame frame)
1004 final Action closeAction = new AbstractAction()
1007 public void actionPerformed(ActionEvent e)
1014 * set up key bindings for Ctrl-W and Cmd-W, with the same (Close) action
1016 KeyStroke ctrlWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
1017 InputEvent.CTRL_DOWN_MASK);
1018 KeyStroke cmdWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
1019 ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx());
1021 InputMap inputMap = frame
1022 .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
1023 String ctrlW = ctrlWKey.toString();
1024 inputMap.put(ctrlWKey, ctrlW);
1025 inputMap.put(cmdWKey, ctrlW);
1027 ActionMap actionMap = frame.getActionMap();
1028 actionMap.put(ctrlW, closeAction);
1032 public void lostOwnership(Clipboard clipboard, Transferable contents)
1036 jalviewClipboard = null;
1039 internalCopy = false;
1043 public void dragEnter(DropTargetDragEvent evt)
1048 public void dragExit(DropTargetEvent evt)
1053 public void dragOver(DropTargetDragEvent evt)
1058 public void dropActionChanged(DropTargetDragEvent evt)
1069 public void drop(DropTargetDropEvent evt)
1071 boolean success = true;
1072 // JAL-1552 - acceptDrop required before getTransferable call for
1073 // Java's Transferable for native dnd
1074 evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
1075 Transferable t = evt.getTransferable();
1076 List<Object> files = new ArrayList<>();
1077 List<DataSourceType> protocols = new ArrayList<>();
1081 transferFromDropTarget(files, protocols, evt, t);
1082 } catch (Exception e)
1084 e.printStackTrace();
1092 for (int i = 0; i < files.size(); i++)
1094 // BH 2018 File or String
1095 Object file = files.get(i);
1096 String fileName = file.toString();
1097 DataSourceType protocol = (protocols == null)
1098 ? DataSourceType.FILE
1100 FileFormatI format = null;
1102 if (fileName.endsWith(".jar"))
1104 format = FileFormat.Jalview;
1109 format = new IdentifyFile().identify(file, protocol);
1111 if (file instanceof File)
1113 Platform.cacheFileData((File) file);
1115 new FileLoader().LoadFile(null, file, protocol, format);
1118 } catch (Exception ex)
1123 evt.dropComplete(success); // need this to ensure input focus is properly
1124 // transfered to any new windows created
1134 public void inputLocalFileMenuItem_actionPerformed(AlignViewport viewport)
1136 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
1137 JalviewFileChooser chooser = JalviewFileChooser.forRead(
1138 Cache.getProperty("LAST_DIRECTORY"), fileFormat,
1139 BackupFiles.getEnabled());
1141 chooser.setFileView(new JalviewFileView());
1142 chooser.setDialogTitle(
1143 MessageManager.getString("label.open_local_file"));
1144 chooser.setToolTipText(MessageManager.getString("action.open"));
1146 chooser.setResponseHandler(0, new Runnable()
1151 File selectedFile = chooser.getSelectedFile();
1152 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1154 FileFormatI format = chooser.getSelectedFormat();
1157 * Call IdentifyFile to verify the file contains what its extension implies.
1158 * Skip this step for dynamically added file formats, because
1159 * IdentifyFile does not know how to recognise them.
1161 if (FileFormats.getInstance().isIdentifiable(format))
1165 format = new IdentifyFile().identify(selectedFile,
1166 DataSourceType.FILE);
1167 } catch (FileFormatException e)
1169 // format = null; //??
1173 new FileLoader().LoadFile(viewport, selectedFile,
1174 DataSourceType.FILE, format);
1177 chooser.showOpenDialog(this);
1181 * Shows a dialog for input of a URL at which to retrieve alignment data
1186 public void inputURLMenuItem_actionPerformed(AlignViewport viewport)
1188 // This construct allows us to have a wider textfield
1190 JLabel label = new JLabel(
1191 MessageManager.getString("label.input_file_url"));
1193 JPanel panel = new JPanel(new GridLayout(2, 1));
1197 * the URL to fetch is
1198 * Java: an editable combobox with history
1199 * JS: (pending JAL-3038) a plain text field
1202 String urlBase = "http://www.";
1203 if (Platform.isJS())
1205 history = new JTextField(urlBase, 35);
1214 JComboBox<String> asCombo = new JComboBox<>();
1215 asCombo.setPreferredSize(new Dimension(400, 20));
1216 asCombo.setEditable(true);
1217 asCombo.addItem(urlBase);
1218 String historyItems = Cache.getProperty("RECENT_URL");
1219 if (historyItems != null)
1221 for (String token : historyItems.split("\\t"))
1223 asCombo.addItem(token);
1230 Object[] options = new Object[] { MessageManager.getString("action.ok"),
1231 MessageManager.getString("action.cancel") };
1232 Runnable action = new Runnable()
1237 @SuppressWarnings("unchecked")
1238 String url = (history instanceof JTextField
1239 ? ((JTextField) history).getText()
1240 : ((JComboBox<String>) history).getSelectedItem()
1243 if (url.toLowerCase().endsWith(".jar"))
1245 if (viewport != null)
1247 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1248 FileFormat.Jalview);
1252 new FileLoader().LoadFile(url, DataSourceType.URL,
1253 FileFormat.Jalview);
1258 FileFormatI format = null;
1261 format = new IdentifyFile().identify(url, DataSourceType.URL);
1262 } catch (FileFormatException e)
1264 // TODO revise error handling, distinguish between
1265 // URL not found and response not valid
1270 String msg = MessageManager
1271 .formatMessage("label.couldnt_locate", url);
1272 JvOptionPane.showInternalMessageDialog(getDesktopPane(), msg,
1273 MessageManager.getString("label.url_not_found"),
1274 JvOptionPane.WARNING_MESSAGE);
1279 if (viewport != null)
1281 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1286 new FileLoader().LoadFile(url, DataSourceType.URL, format);
1291 String dialogOption = MessageManager
1292 .getString("label.input_alignment_from_url");
1293 JvOptionPane.newOptionDialog(desktopPane).setResponseHandler(0, action)
1294 .showInternalDialog(panel, dialogOption,
1295 JvOptionPane.YES_NO_CANCEL_OPTION,
1296 JvOptionPane.PLAIN_MESSAGE, null, options,
1297 MessageManager.getString("action.ok"));
1301 * Opens the CutAndPaste window for the user to paste an alignment in to
1304 * - if not null, the pasted alignment is added to the current
1305 * alignment; if null, to a new alignment window
1308 public void inputTextboxMenuItem_actionPerformed(
1309 AlignmentViewPanel viewPanel)
1311 CutAndPasteTransfer cap = new CutAndPasteTransfer();
1312 cap.setForInput(viewPanel);
1313 addInternalFrame(cap,
1314 MessageManager.getString("label.cut_paste_alignmen_file"), true,
1324 Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
1325 Cache.setProperty("SCREENGEOMETRY_WIDTH", screen.width + "");
1326 Cache.setProperty("SCREENGEOMETRY_HEIGHT", screen.height + "");
1327 storeLastKnownDimensions("", new Rectangle(getBounds().x, getBounds().y,
1328 getWidth(), getHeight()));
1330 if (jconsole != null)
1332 storeLastKnownDimensions("JAVA_CONSOLE_", jconsole.getBounds());
1333 jconsole.stopConsole();
1337 storeLastKnownDimensions("JALVIEW_RSS_WINDOW_", jvnews.getBounds());
1340 if (dialogExecutor != null)
1342 dialogExecutor.shutdownNow();
1344 closeAll_actionPerformed(null);
1346 if (groovyConsole != null)
1348 // suppress a possible repeat prompt to save script
1349 groovyConsole.setDirty(false);
1350 groovyConsole.exit();
1355 private void storeLastKnownDimensions(String string, Rectangle jc)
1357 Cache.log.debug("Storing last known dimensions for " + string + ": x:"
1358 + jc.x + " y:" + jc.y + " width:" + jc.width + " height:"
1361 Cache.setProperty(string + "SCREEN_X", jc.x + "");
1362 Cache.setProperty(string + "SCREEN_Y", jc.y + "");
1363 Cache.setProperty(string + "SCREEN_WIDTH", jc.width + "");
1364 Cache.setProperty(string + "SCREEN_HEIGHT", jc.height + "");
1374 public void aboutMenuItem_actionPerformed(ActionEvent e)
1376 new Thread(new Runnable()
1381 new SplashScreen(false);
1387 * Returns the html text for the About screen, including any available version
1388 * number, build details, author details and citation reference, but without
1389 * the enclosing {@code html} tags
1393 public String getAboutMessage()
1395 StringBuilder message = new StringBuilder(1024);
1396 message.append("<h1><strong>Version: ")
1397 .append(Cache.getProperty("VERSION")).append("</strong></h1>")
1398 .append("<strong>Built: <em>")
1399 .append(Cache.getDefault("BUILD_DATE", "unknown"))
1400 .append("</em> from ").append(Cache.getBuildDetailsForSplash())
1401 .append("</strong>");
1403 String latestVersion = Cache.getDefault("LATEST_VERSION", "Checking");
1404 if (latestVersion.equals("Checking"))
1406 // JBP removed this message for 2.11: May be reinstated in future version
1407 // message.append("<br>...Checking latest version...</br>");
1409 else if (!latestVersion.equals(Cache.getProperty("VERSION")))
1411 boolean red = false;
1412 if (Cache.getProperty("VERSION").toLowerCase()
1413 .indexOf("automated build") == -1)
1416 // Displayed when code version and jnlp version do not match and code
1417 // version is not a development build
1418 message.append("<div style=\"color: #FF0000;font-style: bold;\">");
1421 message.append("<br>!! Version ")
1422 .append(Cache.getDefault("LATEST_VERSION", "..Checking.."))
1423 .append(" is available for download from ")
1424 .append(Cache.getDefault("www.jalview.org",
1425 "http://www.jalview.org"))
1429 message.append("</div>");
1432 message.append("<br>Authors: ");
1433 message.append(Cache.getDefault("AUTHORFNAMES", DEFAULT_AUTHORS));
1434 message.append(CITATION);
1436 return message.toString();
1440 * Action on requesting Help documentation
1443 public void documentationMenuItem_actionPerformed()
1447 if (Platform.isJS())
1449 BrowserLauncher.openURL("http://www.jalview.org/help.html");
1458 Help.showHelpWindow();
1460 } catch (Exception ex)
1462 System.err.println("Error opening help: " + ex.getMessage());
1467 public void closeAll_actionPerformed(ActionEvent e)
1469 // TODO show a progress bar while closing?
1470 JInternalFrame[] frames = desktopPane.getAllFrames();
1471 for (int i = 0; i < frames.length; i++)
1475 frames[i].setClosed(true);
1476 } catch (java.beans.PropertyVetoException ex)
1480 Jalview.setCurrentAlignFrame(null);
1481 System.out.println("ALL CLOSED");
1484 * reset state of singleton objects as appropriate (clear down session state
1485 * when all windows are closed)
1487 StructureSelectionManager ssm = StructureSelectionManager
1488 .getStructureSelectionManager(this);
1496 public void raiseRelated_actionPerformed(ActionEvent e)
1498 reorderAssociatedWindows(false, false);
1502 public void minimizeAssociated_actionPerformed(ActionEvent e)
1504 reorderAssociatedWindows(true, false);
1507 void closeAssociatedWindows()
1509 reorderAssociatedWindows(false, true);
1515 * @seejalview.jbgui.GDesktop#garbageCollect_actionPerformed(java.awt.event.
1519 protected void garbageCollect_actionPerformed(ActionEvent e)
1521 // We simply collect the garbage
1522 Cache.log.debug("Collecting garbage...");
1524 Cache.log.debug("Finished garbage collection.");
1531 * jalview.jbgui.GDesktop#showMemusage_actionPerformed(java.awt.event.ActionEvent
1535 protected void showMemusage_actionPerformed(ActionEvent e)
1537 desktopPane.showMemoryUsage(showMemusage.isSelected());
1544 * jalview.jbgui.GDesktop#showConsole_actionPerformed(java.awt.event.ActionEvent
1548 protected void showConsole_actionPerformed(ActionEvent e)
1550 showConsole(showConsole.isSelected());
1553 Console jconsole = null;
1556 * control whether the java console is visible or not
1560 void showConsole(boolean selected)
1562 // TODO: decide if we should update properties file
1563 if (jconsole != null) // BH 2018
1565 showConsole.setSelected(selected);
1566 Cache.setProperty("SHOW_JAVA_CONSOLE",
1567 Boolean.valueOf(selected).toString());
1568 jconsole.setVisible(selected);
1572 void reorderAssociatedWindows(boolean minimize, boolean close)
1574 JInternalFrame[] frames = desktopPane.getAllFrames();
1575 if (frames == null || frames.length < 1)
1580 AlignmentViewport source = null, target = null;
1581 if (frames[0] instanceof AlignFrame)
1583 source = ((AlignFrame) frames[0]).getCurrentView();
1585 else if (frames[0] instanceof TreePanel)
1587 source = ((TreePanel) frames[0]).getViewPort();
1589 else if (frames[0] instanceof PCAPanel)
1591 source = ((PCAPanel) frames[0]).av;
1593 else if (frames[0].getContentPane() instanceof PairwiseAlignPanel)
1595 source = ((PairwiseAlignPanel) frames[0].getContentPane()).av;
1600 for (int i = 0; i < frames.length; i++)
1603 if (frames[i] == null)
1607 if (frames[i] instanceof AlignFrame)
1609 target = ((AlignFrame) frames[i]).getCurrentView();
1611 else if (frames[i] instanceof TreePanel)
1613 target = ((TreePanel) frames[i]).getViewPort();
1615 else if (frames[i] instanceof PCAPanel)
1617 target = ((PCAPanel) frames[i]).av;
1619 else if (frames[i].getContentPane() instanceof PairwiseAlignPanel)
1621 target = ((PairwiseAlignPanel) frames[i].getContentPane()).av;
1624 if (source == target)
1630 frames[i].setClosed(true);
1634 frames[i].setIcon(minimize);
1637 frames[i].toFront();
1641 } catch (java.beans.PropertyVetoException ex)
1656 protected void preferences_actionPerformed(ActionEvent e)
1662 * Prompts the user to choose a file and then saves the Jalview state as a
1663 * Jalview project file
1666 public void saveState_actionPerformed()
1668 saveState_actionPerformed(false);
1671 public void saveState_actionPerformed(boolean saveAs)
1673 java.io.File projectFile = getProjectFile();
1674 // autoSave indicates we already have a file and don't need to ask
1675 boolean autoSave = projectFile != null && !saveAs
1676 && BackupFiles.getEnabled();
1678 // System.out.println("autoSave="+autoSave+", projectFile='"+projectFile+"',
1679 // saveAs="+saveAs+", Backups
1680 // "+(BackupFiles.getEnabled()?"enabled":"disabled"));
1682 boolean approveSave = false;
1685 JalviewFileChooser chooser = new JalviewFileChooser("jvp",
1688 chooser.setFileView(new JalviewFileView());
1689 chooser.setDialogTitle(MessageManager.getString("label.save_state"));
1691 int value = chooser.showSaveDialog(this);
1693 if (value == JalviewFileChooser.APPROVE_OPTION)
1695 projectFile = chooser.getSelectedFile();
1696 setProjectFile(projectFile);
1701 if (approveSave || autoSave)
1703 final Desktop me = this;
1704 final java.io.File chosenFile = projectFile;
1705 new Thread(new Runnable()
1710 // TODO: refactor to Jalview desktop session controller action.
1711 setProgressBar(MessageManager.formatMessage(
1712 "label.saving_jalview_project", new Object[]
1713 { chosenFile.getName() }), chosenFile.hashCode());
1714 Cache.setProperty("LAST_DIRECTORY", chosenFile.getParent());
1715 // TODO catch and handle errors for savestate
1716 // TODO prevent user from messing with the Desktop whilst we're saving
1719 boolean doBackup = BackupFiles.getEnabled();
1720 BackupFiles backupfiles = doBackup ? new BackupFiles(chosenFile)
1723 new Jalview2XML().saveState(
1724 doBackup ? backupfiles.getTempFile() : chosenFile);
1728 backupfiles.setWriteSuccess(true);
1729 backupfiles.rollBackupsAndRenameTempFile();
1731 } catch (OutOfMemoryError oom)
1733 new OOMWarning("Whilst saving current state to "
1734 + chosenFile.getName(), oom);
1735 } catch (Exception ex)
1737 Cache.log.error("Problems whilst trying to save to "
1738 + chosenFile.getName(), ex);
1739 JvOptionPane.showMessageDialog(me,
1740 MessageManager.formatMessage(
1741 "label.error_whilst_saving_current_state_to",
1743 { chosenFile.getName() }),
1744 MessageManager.getString("label.couldnt_save_project"),
1745 JvOptionPane.WARNING_MESSAGE);
1747 setProgressBar(null, chosenFile.hashCode());
1754 public void saveAsState_actionPerformed(ActionEvent e)
1756 saveState_actionPerformed(true);
1759 private void setProjectFile(File choice)
1761 this.projectFile = choice;
1764 public File getProjectFile()
1766 return this.projectFile;
1770 * Shows a file chooser dialog and tries to read in the selected file as a
1774 public void loadState_actionPerformed()
1776 final String[] suffix = new String[] { "jvp", "jar" };
1777 final String[] desc = new String[] { "Jalview Project",
1778 "Jalview Project (old)" };
1779 JalviewFileChooser chooser = new JalviewFileChooser(
1780 Cache.getProperty("LAST_DIRECTORY"), suffix, desc,
1781 "Jalview Project", true, BackupFiles.getEnabled()); // last two
1785 chooser.setFileView(new JalviewFileView());
1786 chooser.setDialogTitle(MessageManager.getString("label.restore_state"));
1787 chooser.setResponseHandler(0, new Runnable()
1792 File selectedFile = chooser.getSelectedFile();
1793 setProjectFile(selectedFile);
1794 String choice = selectedFile.getAbsolutePath();
1795 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1796 new Thread(new Runnable()
1803 new Jalview2XML().loadJalviewAlign(selectedFile);
1804 } catch (OutOfMemoryError oom)
1806 new OOMWarning("Whilst loading project from " + choice, oom);
1807 } catch (Exception ex)
1810 "Problems whilst loading project from " + choice, ex);
1811 JvOptionPane.showMessageDialog( getDesktopPane(),
1812 MessageManager.formatMessage(
1813 "label.error_whilst_loading_project_from",
1817 .getString("label.couldnt_load_project"),
1818 JvOptionPane.WARNING_MESSAGE);
1825 chooser.showOpenDialog(this);
1829 public void inputSequence_actionPerformed(ActionEvent e)
1831 new SequenceFetcher(this);
1834 JPanel progressPanel;
1836 ArrayList<JPanel> fileLoadingPanels = new ArrayList<>();
1838 public void startLoading(final Object fileName)
1840 if (fileLoadingCount == 0)
1842 fileLoadingPanels.add(addProgressPanel(MessageManager
1843 .formatMessage("label.loading_file", new Object[]
1849 private JPanel addProgressPanel(String string)
1851 if (progressPanel == null)
1853 progressPanel = new JPanel(new GridLayout(1, 1));
1854 totalProgressCount = 0;
1855 getContentPane().add(progressPanel, BorderLayout.SOUTH);
1857 JPanel thisprogress = new JPanel(new BorderLayout(10, 5));
1858 JProgressBar progressBar = new JProgressBar();
1859 progressBar.setIndeterminate(true);
1861 thisprogress.add(new JLabel(string), BorderLayout.WEST);
1863 thisprogress.add(progressBar, BorderLayout.CENTER);
1864 progressPanel.add(thisprogress);
1865 ((GridLayout) progressPanel.getLayout()).setRows(
1866 ((GridLayout) progressPanel.getLayout()).getRows() + 1);
1867 ++totalProgressCount;
1869 return thisprogress;
1872 int totalProgressCount = 0;
1874 private void removeProgressPanel(JPanel progbar)
1876 if (progressPanel != null)
1878 synchronized (progressPanel)
1880 progressPanel.remove(progbar);
1881 GridLayout gl = (GridLayout) progressPanel.getLayout();
1882 gl.setRows(gl.getRows() - 1);
1883 if (--totalProgressCount < 1)
1885 this.getContentPane().remove(progressPanel);
1886 progressPanel = null;
1893 public void stopLoading()
1896 if (fileLoadingCount < 1)
1898 while (fileLoadingPanels.size() > 0)
1900 removeProgressPanel(fileLoadingPanels.remove(0));
1902 fileLoadingPanels.clear();
1903 fileLoadingCount = 0;
1908 public static int getViewCount(String alignmentId)
1910 AlignmentViewport[] aps = getViewports(alignmentId);
1911 return (aps == null) ? 0 : aps.length;
1916 * @param alignmentId
1917 * - if null, all sets are returned
1918 * @return all AlignmentPanels concerning the alignmentId sequence set
1920 public static AlignmentPanel[] getAlignmentPanels(String alignmentId)
1922 if (getDesktopPane() == null)
1924 // no frames created and in headless mode
1925 // TODO: verify that frames are recoverable when in headless mode
1928 List<AlignmentPanel> aps = new ArrayList<>();
1929 AlignFrame[] frames = getAlignFrames();
1934 for (AlignFrame af : frames)
1936 for (AlignmentPanel ap : af.alignPanels)
1938 if (alignmentId == null
1939 || alignmentId.equals(ap.av.getSequenceSetId()))
1945 if (aps.size() == 0)
1949 AlignmentPanel[] vap = aps.toArray(new AlignmentPanel[aps.size()]);
1954 * get all the viewports on an alignment.
1956 * @param sequenceSetId
1957 * unique alignment id (may be null - all viewports returned in that
1959 * @return all viewports on the alignment bound to sequenceSetId
1961 public static AlignmentViewport[] getViewports(String sequenceSetId)
1963 List<AlignmentViewport> viewp = new ArrayList<>();
1964 if ( getDesktopPane() != null)
1966 AlignFrame[] frames = getAlignFrames();
1968 for (AlignFrame afr : frames)
1970 if (sequenceSetId == null || afr.getViewport().getSequenceSetId()
1971 .equals(sequenceSetId))
1973 if (afr.alignPanels != null)
1975 for (AlignmentPanel ap : afr.alignPanels)
1977 if (sequenceSetId == null
1978 || sequenceSetId.equals(ap.av.getSequenceSetId()))
1986 viewp.add(afr.getViewport());
1990 if (viewp.size() > 0)
1992 return viewp.toArray(new AlignmentViewport[viewp.size()]);
1999 * Explode the views in the given frame into separate AlignFrame
2003 public static void explodeViews(AlignFrame af)
2005 int size = af.alignPanels.size();
2011 // FIXME: ideally should use UI interface API
2012 FeatureSettings viewFeatureSettings = (af.featureSettings != null
2013 && af.featureSettings.isOpen()) ? af.featureSettings : null;
2014 Rectangle fsBounds = af.getFeatureSettingsGeometry();
2015 for (int i = 0; i < size; i++)
2017 AlignmentPanel ap = af.alignPanels.get(i);
2019 AlignFrame newaf = new AlignFrame(ap);
2021 // transfer reference for existing feature settings to new alignFrame
2022 if (ap == af.alignPanel)
2024 if (viewFeatureSettings != null && viewFeatureSettings.fr.ap == ap)
2026 newaf.featureSettings = viewFeatureSettings;
2028 newaf.setFeatureSettingsGeometry(fsBounds);
2032 * Restore the view's last exploded frame geometry if known. Multiple
2033 * views from one exploded frame share and restore the same (frame)
2034 * position and size.
2036 Rectangle geometry = ap.av.getExplodedGeometry();
2037 if (geometry != null)
2039 newaf.setBounds(geometry);
2042 ap.av.setGatherViewsHere(false);
2044 addInternalFrame(newaf, af.getTitle(), AlignFrame.DEFAULT_WIDTH,
2045 AlignFrame.DEFAULT_HEIGHT);
2046 // and materialise a new feature settings dialog instance for the new
2048 // (closes the old as if 'OK' was pressed)
2049 if (ap == af.alignPanel && newaf.featureSettings != null
2050 && newaf.featureSettings.isOpen()
2051 && af.alignPanel.getAlignViewport().isShowSequenceFeatures())
2053 newaf.showFeatureSettingsUI();
2057 af.featureSettings = null;
2058 af.alignPanels.clear();
2059 af.closeMenuItem_actionPerformed(true);
2064 * Gather expanded views (separate AlignFrame's) with the same sequence set
2065 * identifier back in to this frame as additional views, and close the
2066 * expanded views. Note the expanded frames may themselves have multiple
2067 * views. We take the lot.
2071 public void gatherViews(AlignFrame source)
2073 source.viewport.setGatherViewsHere(true);
2074 source.viewport.setExplodedGeometry(source.getBounds());
2075 JInternalFrame[] frames = desktopPane.getAllFrames();
2076 String viewId = source.viewport.getSequenceSetId();
2077 for (int t = 0; t < frames.length; t++)
2079 if (frames[t] instanceof AlignFrame && frames[t] != source)
2081 AlignFrame af = (AlignFrame) frames[t];
2082 boolean gatherThis = false;
2083 for (int a = 0; a < af.alignPanels.size(); a++)
2085 AlignmentPanel ap = af.alignPanels.get(a);
2086 if (viewId.equals(ap.av.getSequenceSetId()))
2089 ap.av.setGatherViewsHere(false);
2090 ap.av.setExplodedGeometry(af.getBounds());
2091 source.addAlignmentPanel(ap, false);
2097 if (af.featureSettings != null && af.featureSettings.isOpen())
2099 if (source.featureSettings == null)
2101 // preserve the feature settings geometry for this frame
2102 source.featureSettings = af.featureSettings;
2103 source.setFeatureSettingsGeometry(
2104 af.getFeatureSettingsGeometry());
2108 // close it and forget
2109 af.featureSettings.close();
2112 af.alignPanels.clear();
2113 af.closeMenuItem_actionPerformed(true);
2118 // refresh the feature setting UI for the source frame if it exists
2119 if (source.featureSettings != null && source.featureSettings.isOpen())
2121 source.showFeatureSettingsUI();
2125 public JInternalFrame[] getAllFrames()
2127 return desktopPane.getAllFrames();
2131 * Checks the given url to see if it gives a response indicating that the user
2132 * should be informed of a new questionnaire.
2136 public void checkForQuestionnaire(String url)
2138 UserQuestionnaireCheck jvq = new UserQuestionnaireCheck(url);
2139 // javax.swing.SwingUtilities.invokeLater(jvq);
2140 new Thread(jvq).start();
2143 public void checkURLLinks()
2145 // Thread off the URL link checker
2146 addDialogThread(new Runnable()
2151 if (Cache.getDefault("CHECKURLLINKS", true))
2153 // check what the actual links are - if it's just the default don't
2154 // bother with the warning
2155 List<String> links = Preferences.sequenceUrlLinks
2158 // only need to check links if there is one with a
2159 // SEQUENCE_ID which is not the default EMBL_EBI link
2160 ListIterator<String> li = links.listIterator();
2161 boolean check = false;
2162 List<JLabel> urls = new ArrayList<>();
2163 while (li.hasNext())
2165 String link = li.next();
2166 if (link.contains(jalview.util.UrlConstants.SEQUENCE_ID)
2167 && !UrlConstants.isDefaultString(link))
2170 int barPos = link.indexOf("|");
2171 String urlMsg = barPos == -1 ? link
2172 : link.substring(0, barPos) + ": "
2173 + link.substring(barPos + 1);
2174 urls.add(new JLabel(urlMsg));
2182 // ask user to check in case URL links use old style tokens
2183 // ($SEQUENCE_ID$ for sequence id _or_ accession id)
2184 JPanel msgPanel = new JPanel();
2185 msgPanel.setLayout(new BoxLayout(msgPanel, BoxLayout.PAGE_AXIS));
2186 msgPanel.add(Box.createVerticalGlue());
2187 JLabel msg = new JLabel(MessageManager
2188 .getString("label.SEQUENCE_ID_for_DB_ACCESSION1"));
2189 JLabel msg2 = new JLabel(MessageManager
2190 .getString("label.SEQUENCE_ID_for_DB_ACCESSION2"));
2192 for (JLabel url : urls)
2198 final JCheckBox jcb = new JCheckBox(
2199 MessageManager.getString("label.do_not_display_again"));
2200 jcb.addActionListener(new ActionListener()
2203 public void actionPerformed(ActionEvent e)
2205 // update Cache settings for "don't show this again"
2206 boolean showWarningAgain = !jcb.isSelected();
2207 Cache.setProperty("CHECKURLLINKS",
2208 Boolean.valueOf(showWarningAgain).toString());
2213 JvOptionPane.showMessageDialog(desktopPane, msgPanel,
2215 .getString("label.SEQUENCE_ID_no_longer_used"),
2216 JvOptionPane.WARNING_MESSAGE);
2223 * Proxy class for JDesktopPane which optionally displays the current memory
2224 * usage and highlights the desktop area with a red bar if free memory runs
2229 public class MyDesktopPane extends JDesktopPane implements Runnable
2231 private static final float ONE_MB = 1048576f;
2233 boolean showMemoryUsage = false;
2237 java.text.NumberFormat df;
2239 float maxMemory, allocatedMemory, freeMemory, totalFreeMemory,
2242 public MyDesktopPane(boolean showMemoryUsage)
2244 showMemoryUsage(showMemoryUsage);
2247 public void showMemoryUsage(boolean showMemory)
2249 this.showMemoryUsage = showMemory;
2252 Thread worker = new Thread(this);
2258 public boolean isShowMemoryUsage()
2260 return showMemoryUsage;
2266 df = java.text.NumberFormat.getNumberInstance();
2267 df.setMaximumFractionDigits(2);
2268 runtime = Runtime.getRuntime();
2270 while (showMemoryUsage)
2274 maxMemory = runtime.maxMemory() / ONE_MB;
2275 allocatedMemory = runtime.totalMemory() / ONE_MB;
2276 freeMemory = runtime.freeMemory() / ONE_MB;
2277 totalFreeMemory = freeMemory + (maxMemory - allocatedMemory);
2279 percentUsage = (totalFreeMemory / maxMemory) * 100;
2281 // if (percentUsage < 20)
2283 // border1 = BorderFactory.createMatteBorder(12, 12, 12, 12,
2285 // instance.set.setBorder(border1);
2288 // sleep after showing usage
2290 } catch (Exception ex)
2292 ex.printStackTrace();
2298 public void paintComponent(Graphics g)
2300 if (showMemoryUsage && g != null && df != null)
2302 if (percentUsage < 20)
2304 g.setColor(Color.red);
2306 FontMetrics fm = g.getFontMetrics();
2309 g.drawString(MessageManager.formatMessage("label.memory_stats",
2311 { df.format(totalFreeMemory), df.format(maxMemory),
2312 df.format(percentUsage) }),
2313 10, getHeight() - fm.getHeight());
2320 * Accessor method to quickly get all the AlignmentFrames loaded.
2322 * @return an array of AlignFrame, or null if none found
2324 public static AlignFrame[] getAlignFrames()
2326 if (Jalview.isHeadlessMode())
2328 return new AlignFrame[] { Jalview.getInstance().currentAlignFrame };
2331 JInternalFrame[] frames = getDesktopPane().getAllFrames();
2337 List<AlignFrame> avp = new ArrayList<>();
2339 for (int i = frames.length - 1; i > -1; i--)
2341 if (frames[i] instanceof AlignFrame)
2343 avp.add((AlignFrame) frames[i]);
2345 else if (frames[i] instanceof SplitFrame)
2348 * Also check for a split frame containing an AlignFrame
2350 GSplitFrame sf = (GSplitFrame) frames[i];
2351 if (sf.getTopFrame() instanceof AlignFrame)
2353 avp.add((AlignFrame) sf.getTopFrame());
2355 if (sf.getBottomFrame() instanceof AlignFrame)
2357 avp.add((AlignFrame) sf.getBottomFrame());
2361 if (avp.size() == 0)
2365 AlignFrame afs[] = avp.toArray(new AlignFrame[avp.size()]);
2370 * Returns an array of any AppJmol frames in the Desktop (or null if none).
2374 public GStructureViewer[] getJmols()
2376 JInternalFrame[] frames = desktopPane.getAllFrames();
2382 List<GStructureViewer> avp = new ArrayList<>();
2384 for (int i = frames.length - 1; i > -1; i--)
2386 if (frames[i] instanceof AppJmol)
2388 GStructureViewer af = (GStructureViewer) frames[i];
2392 if (avp.size() == 0)
2396 GStructureViewer afs[] = avp.toArray(new GStructureViewer[avp.size()]);
2401 * Add Groovy Support to Jalview
2404 public void groovyShell_actionPerformed()
2408 openGroovyConsole();
2409 } catch (Exception ex)
2411 Cache.log.error("Groovy Shell Creation failed.", ex);
2412 JvOptionPane.showInternalMessageDialog(desktopPane,
2414 MessageManager.getString("label.couldnt_create_groovy_shell"),
2415 MessageManager.getString("label.groovy_support_failed"),
2416 JvOptionPane.ERROR_MESSAGE);
2421 * Open the Groovy console
2423 void openGroovyConsole()
2425 if (groovyConsole == null)
2427 groovyConsole = new groovy.ui.Console();
2428 groovyConsole.setVariable("Jalview", this);
2429 groovyConsole.run();
2432 * We allow only one console at a time, so that AlignFrame menu option
2433 * 'Calculate | Run Groovy script' is unambiguous.
2434 * Disable 'Groovy Console', and enable 'Run script', when the console is
2435 * opened, and the reverse when it is closed
2437 Window window = (Window) groovyConsole.getFrame();
2438 window.addWindowListener(new WindowAdapter()
2441 public void windowClosed(WindowEvent e)
2444 * rebind CMD-Q from Groovy Console to Jalview Quit
2447 enableExecuteGroovy(false);
2453 * show Groovy console window (after close and reopen)
2455 ((Window) groovyConsole.getFrame()).setVisible(true);
2458 * if we got this far, enable 'Run Groovy' in AlignFrame menus
2459 * and disable opening a second console
2461 enableExecuteGroovy(true);
2465 * Bind Ctrl/Cmd-Q to Quit - for reset as Groovy Console takes over this
2466 * binding when opened
2468 protected void addQuitHandler()
2471 .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
2473 .getKeyStroke(KeyEvent.VK_Q,
2474 jalview.util.ShortcutKeyMaskExWrapper
2475 .getMenuShortcutKeyMaskEx()),
2477 getRootPane().getActionMap().put("Quit", new AbstractAction()
2480 public void actionPerformed(ActionEvent e)
2488 * Enable or disable 'Run Groovy script' in AlignFrame calculate menus
2491 * true if Groovy console is open
2493 public void enableExecuteGroovy(boolean enabled)
2496 * disable opening a second Groovy console
2497 * (or re-enable when the console is closed)
2499 groovyShell.setEnabled(!enabled);
2501 AlignFrame[] alignFrames = getAlignFrames();
2502 if (alignFrames != null)
2504 for (AlignFrame af : alignFrames)
2506 af.setGroovyEnabled(enabled);
2512 * Progress bars managed by the IProgressIndicator method.
2514 private Hashtable<Long, JPanel> progressBars;
2516 private Hashtable<Long, IProgressIndicatorHandler> progressBarHandlers;
2521 * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
2524 public void setProgressBar(String message, long id)
2526 // Platform.timeCheck("Desktop " + message, Platform.TIME_MARK);
2528 if (progressBars == null)
2530 progressBars = new Hashtable<>();
2531 progressBarHandlers = new Hashtable<>();
2534 if (progressBars.get(Long.valueOf(id)) != null)
2536 JPanel panel = progressBars.remove(Long.valueOf(id));
2537 if (progressBarHandlers.contains(Long.valueOf(id)))
2539 progressBarHandlers.remove(Long.valueOf(id));
2541 removeProgressPanel(panel);
2545 progressBars.put(Long.valueOf(id), addProgressPanel(message));
2552 * @see jalview.gui.IProgressIndicator#registerHandler(long,
2553 * jalview.gui.IProgressIndicatorHandler)
2556 public void registerHandler(final long id,
2557 final IProgressIndicatorHandler handler)
2559 if (progressBarHandlers == null
2560 || !progressBars.containsKey(Long.valueOf(id)))
2562 throw new Error(MessageManager.getString(
2563 "error.call_setprogressbar_before_registering_handler"));
2565 progressBarHandlers.put(Long.valueOf(id), handler);
2566 final JPanel progressPanel = progressBars.get(Long.valueOf(id));
2567 if (handler.canCancel())
2569 JButton cancel = new JButton(
2570 MessageManager.getString("action.cancel"));
2571 final IProgressIndicator us = this;
2572 cancel.addActionListener(new ActionListener()
2576 public void actionPerformed(ActionEvent e)
2578 handler.cancelActivity(id);
2579 us.setProgressBar(MessageManager
2580 .formatMessage("label.cancelled_params", new Object[]
2581 { ((JLabel) progressPanel.getComponent(0)).getText() }),
2585 progressPanel.add(cancel, BorderLayout.EAST);
2591 * @return true if any progress bars are still active
2594 public boolean operationInProgress()
2596 if (progressBars != null && progressBars.size() > 0)
2604 * This will return the first AlignFrame holding the given viewport instance.
2605 * It will break if there are more than one AlignFrames viewing a particular
2609 * @return alignFrame for viewport
2611 public static AlignFrame getAlignFrameFor(AlignViewportI viewport)
2613 if ( getDesktopPane() != null)
2615 AlignmentPanel[] aps = getAlignmentPanels(
2616 viewport.getSequenceSetId());
2617 for (int panel = 0; aps != null && panel < aps.length; panel++)
2619 if (aps[panel] != null && aps[panel].av == viewport)
2621 return aps[panel].alignFrame;
2628 public VamsasApplication getVamsasApplication()
2630 // TODO: JAL-3311 remove remaining code from Jalview relating to VAMSAS
2636 * flag set if jalview GUI is being operated programmatically
2638 private boolean inBatchMode = false;
2641 * check if jalview GUI is being operated programmatically
2643 * @return inBatchMode
2645 public boolean isInBatchMode()
2651 * set flag if jalview GUI is being operated programmatically
2653 * @param inBatchMode
2655 public void setInBatchMode(boolean inBatchMode)
2657 this.inBatchMode = inBatchMode;
2660 public void startServiceDiscovery()
2662 startServiceDiscovery(false);
2665 public void startServiceDiscovery(boolean blocking)
2667 boolean alive = true;
2668 Thread t0 = null, t1 = null, t2 = null;
2669 // JAL-940 - JALVIEW 1 services are now being EOLed as of JABA 2.1 release
2672 // todo: changesupport handlers need to be transferred
2673 if (discoverer == null)
2675 discoverer = jalview.ws.jws1.Discoverer.getInstance();
2676 // register PCS handler for desktop.
2677 discoverer.addPropertyChangeListener(changeSupport);
2679 // JAL-940 - disabled JWS1 service configuration - always start discoverer
2680 // until we phase out completely
2681 (t0 = new Thread(discoverer)).start();
2684 if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
2686 t2 = jalview.ws.jws2.Jws2Discoverer.getInstance()
2687 .startDiscoverer(changeSupport);
2691 // TODO: do rest service discovery
2700 } catch (Exception e)
2703 alive = (t1 != null && t1.isAlive()) || (t2 != null && t2.isAlive())
2704 || (t3 != null && t3.isAlive())
2705 || (t0 != null && t0.isAlive());
2711 * called to check if the service discovery process completed successfully.
2715 protected void JalviewServicesChanged(PropertyChangeEvent evt)
2717 if (evt.getNewValue() == null || evt.getNewValue() instanceof Vector)
2719 final String ermsg = jalview.ws.jws2.Jws2Discoverer.getInstance()
2720 .getErrorMessages();
2723 if (Cache.getDefault("SHOW_WSDISCOVERY_ERRORS", true))
2725 if (serviceChangedDialog == null)
2727 // only run if we aren't already displaying one of these.
2728 addDialogThread(serviceChangedDialog = new Runnable()
2735 * JalviewDialog jd =new JalviewDialog() {
2737 * @Override protected void cancelPressed() { // TODO
2738 * Auto-generated method stub
2740 * }@Override protected void okPressed() { // TODO
2741 * Auto-generated method stub
2743 * }@Override protected void raiseClosed() { // TODO
2744 * Auto-generated method stub
2746 * } }; jd.initDialogFrame(new
2747 * JLabel("<html><table width=\"450\"><tr><td>" + ermsg +
2748 * "<br/>It may be that you have invalid JABA URLs in your web service preferences,"
2749 * + " or mis-configured HTTP proxy settings.<br/>" +
2750 * "Check the <em>Connections</em> and <em>Web services</em> tab of the"
2752 * " Tools->Preferences dialog box to change them.</td></tr></table></html>"
2753 * ), true, true, "Web Service Configuration Problem", 450,
2756 * jd.waitForInput();
2758 JvOptionPane.showConfirmDialog(desktopPane,
2759 new JLabel("<html><table width=\"450\"><tr><td>"
2760 + ermsg + "</td></tr></table>"
2761 + "<p>It may be that you have invalid JABA URLs<br/>in your web service preferences,"
2762 + "<br>or as a command-line argument, or mis-configured HTTP proxy settings.</p>"
2763 + "<p>Check the <em>Connections</em> and <em>Web services</em> tab<br/>of the"
2764 + " Tools->Preferences dialog box to change them.</p></html>"),
2765 "Web Service Configuration Problem",
2766 JvOptionPane.DEFAULT_OPTION,
2767 JvOptionPane.ERROR_MESSAGE);
2768 serviceChangedDialog = null;
2777 "Errors reported by JABA discovery service. Check web services preferences.\n"
2784 private Runnable serviceChangedDialog = null;
2787 * start a thread to open a URL in the configured browser. Pops up a warning
2788 * dialog to the user if there is an exception when calling out to the browser
2793 public static void showUrl(final String url)
2795 showUrl(url, getInstance());
2799 * Like showUrl but allows progress handler to be specified
2803 * (null) or object implementing IProgressIndicator
2805 public static void showUrl(final String url,
2806 final IProgressIndicator progress)
2808 new Thread(new Runnable()
2815 if (progress != null)
2817 progress.setProgressBar(MessageManager
2818 .formatMessage("status.opening_params", new Object[]
2819 { url }), this.hashCode());
2821 jalview.util.BrowserLauncher.openURL(url);
2822 } catch (Exception ex)
2824 JvOptionPane.showInternalMessageDialog( getDesktopPane(),
2826 .getString("label.web_browser_not_found_unix"),
2827 MessageManager.getString("label.web_browser_not_found"),
2828 JvOptionPane.WARNING_MESSAGE);
2830 ex.printStackTrace();
2832 if (progress != null)
2834 progress.setProgressBar(null, this.hashCode());
2840 public static WsParamSetManager wsparamManager = null;
2842 public static ParamManager getUserParameterStore()
2844 if (wsparamManager == null)
2846 wsparamManager = new WsParamSetManager();
2848 return wsparamManager;
2852 * static hyperlink handler proxy method for use by Jalview's internal windows
2856 public static void hyperlinkUpdate(HyperlinkEvent e)
2858 if (e.getEventType() == EventType.ACTIVATED)
2863 url = e.getURL().toString();
2865 } catch (Exception x)
2869 if (Cache.log != null)
2871 Cache.log.error("Couldn't handle string " + url + " as a URL.");
2876 "Couldn't handle string " + url + " as a URL.");
2879 // ignore any exceptions due to dud links.
2886 * single thread that handles display of dialogs to user.
2888 ExecutorService dialogExecutor = Executors.newSingleThreadExecutor();
2891 * flag indicating if dialogExecutor should try to acquire a permit
2893 private volatile boolean dialogPause = true;
2898 private java.util.concurrent.Semaphore block = new Semaphore(0);
2900 private static groovy.ui.Console groovyConsole;
2903 * add another dialog thread to the queue
2907 public void addDialogThread(final Runnable prompter)
2909 dialogExecutor.submit(new Runnable()
2919 } catch (InterruptedException x)
2923 if (Jalview.isHeadlessMode())
2929 SwingUtilities.invokeAndWait(prompter);
2930 } catch (Exception q)
2932 Cache.log.warn("Unexpected Exception in dialog thread.", q);
2938 public void startDialogQueue()
2940 // set the flag so we don't pause waiting for another permit and semaphore
2941 // the current task to begin
2942 dialogPause = false;
2947 * Outputs an image of the desktop to file in EPS format, after prompting the
2948 * user for choice of Text or Lineart character rendering (unless a preference
2949 * has been set). The file name is generated as
2952 * Jalview_snapshot_nnnnn.eps where nnnnn is the current timestamp in milliseconds
2956 protected void snapShotWindow_actionPerformed(ActionEvent e)
2958 // currently the menu option to do this is not shown
2961 int width = getWidth();
2962 int height = getHeight();
2964 "Jalview_snapshot_" + System.currentTimeMillis() + ".eps");
2965 ImageWriterI writer = new ImageWriterI()
2968 public void exportImage(Graphics g) throws Exception
2971 Cache.log.info("Successfully written snapshot to file "
2972 + of.getAbsolutePath());
2975 String title = "View of desktop";
2976 ImageExporter exporter = new ImageExporter(writer, null, TYPE.EPS,
2978 exporter.doExport(of, this, width, height, title);
2982 * Explode the views in the given SplitFrame into separate SplitFrame windows.
2983 * This respects (remembers) any previous 'exploded geometry' i.e. the size
2984 * and location last time the view was expanded (if any). However it does not
2985 * remember the split pane divider location - this is set to match the
2986 * 'exploding' frame.
2990 public void explodeViews(SplitFrame sf)
2992 AlignFrame oldTopFrame = (AlignFrame) sf.getTopFrame();
2993 AlignFrame oldBottomFrame = (AlignFrame) sf.getBottomFrame();
2994 List<? extends AlignmentViewPanel> topPanels = oldTopFrame
2996 List<? extends AlignmentViewPanel> bottomPanels = oldBottomFrame
2998 int viewCount = topPanels.size();
3005 * Processing in reverse order works, forwards order leaves the first panels
3006 * not visible. I don't know why!
3008 for (int i = viewCount - 1; i >= 0; i--)
3011 * Make new top and bottom frames. These take over the respective
3012 * AlignmentPanel objects, including their AlignmentViewports, so the
3013 * cdna/protein relationships between the viewports is carried over to the
3016 * explodedGeometry holds the (x, y) position of the previously exploded
3017 * SplitFrame, and the (width, height) of the AlignFrame component
3019 AlignmentPanel topPanel = (AlignmentPanel) topPanels.get(i);
3020 AlignFrame newTopFrame = new AlignFrame(topPanel);
3021 newTopFrame.setSize(oldTopFrame.getSize());
3022 newTopFrame.setVisible(true);
3023 Rectangle geometry = ((AlignViewport) topPanel.getAlignViewport())
3024 .getExplodedGeometry();
3025 if (geometry != null)
3027 newTopFrame.setSize(geometry.getSize());
3030 AlignmentPanel bottomPanel = (AlignmentPanel) bottomPanels.get(i);
3031 AlignFrame newBottomFrame = new AlignFrame(bottomPanel);
3032 newBottomFrame.setSize(oldBottomFrame.getSize());
3033 newBottomFrame.setVisible(true);
3034 geometry = ((AlignViewport) bottomPanel.getAlignViewport())
3035 .getExplodedGeometry();
3036 if (geometry != null)
3038 newBottomFrame.setSize(geometry.getSize());
3041 topPanel.av.setGatherViewsHere(false);
3042 bottomPanel.av.setGatherViewsHere(false);
3043 JInternalFrame splitFrame = new SplitFrame(newTopFrame,
3045 if (geometry != null)
3047 splitFrame.setLocation(geometry.getLocation());
3049 addInternalFrame(splitFrame, sf.getTitle(), -1, -1);
3053 * Clear references to the panels (now relocated in the new SplitFrames)
3054 * before closing the old SplitFrame.
3057 bottomPanels.clear();
3062 * Gather expanded split frames, sharing the same pairs of sequence set ids,
3063 * back into the given SplitFrame as additional views. Note that the gathered
3064 * frames may themselves have multiple views.
3068 public void gatherViews(GSplitFrame source)
3071 * special handling of explodedGeometry for a view within a SplitFrame: - it
3072 * holds the (x, y) position of the enclosing SplitFrame, and the (width,
3073 * height) of the AlignFrame component
3075 AlignFrame myTopFrame = (AlignFrame) source.getTopFrame();
3076 AlignFrame myBottomFrame = (AlignFrame) source.getBottomFrame();
3077 myTopFrame.viewport.setExplodedGeometry(new Rectangle(source.getX(),
3078 source.getY(), myTopFrame.getWidth(), myTopFrame.getHeight()));
3079 myBottomFrame.viewport
3080 .setExplodedGeometry(new Rectangle(source.getX(), source.getY(),
3081 myBottomFrame.getWidth(), myBottomFrame.getHeight()));
3082 myTopFrame.viewport.setGatherViewsHere(true);
3083 myBottomFrame.viewport.setGatherViewsHere(true);
3084 String topViewId = myTopFrame.viewport.getSequenceSetId();
3085 String bottomViewId = myBottomFrame.viewport.getSequenceSetId();
3087 JInternalFrame[] frames = desktopPane.getAllFrames();
3088 for (JInternalFrame frame : frames)
3090 if (frame instanceof SplitFrame && frame != source)
3092 SplitFrame sf = (SplitFrame) frame;
3093 AlignFrame topFrame = (AlignFrame) sf.getTopFrame();
3094 AlignFrame bottomFrame = (AlignFrame) sf.getBottomFrame();
3095 boolean gatherThis = false;
3096 for (int a = 0; a < topFrame.alignPanels.size(); a++)
3098 AlignmentPanel topPanel = topFrame.alignPanels.get(a);
3099 AlignmentPanel bottomPanel = bottomFrame.alignPanels.get(a);
3100 if (topViewId.equals(topPanel.av.getSequenceSetId())
3101 && bottomViewId.equals(bottomPanel.av.getSequenceSetId()))
3104 topPanel.av.setGatherViewsHere(false);
3105 bottomPanel.av.setGatherViewsHere(false);
3106 topPanel.av.setExplodedGeometry(
3107 new Rectangle(sf.getLocation(), topFrame.getSize()));
3108 bottomPanel.av.setExplodedGeometry(
3109 new Rectangle(sf.getLocation(), bottomFrame.getSize()));
3110 myTopFrame.addAlignmentPanel(topPanel, false);
3111 myBottomFrame.addAlignmentPanel(bottomPanel, false);
3117 topFrame.getAlignPanels().clear();
3118 bottomFrame.getAlignPanels().clear();
3125 * The dust settles...give focus to the tab we did this from.
3127 myTopFrame.setDisplayedView(myTopFrame.alignPanel);
3130 public static groovy.ui.Console getGroovyConsole()
3132 return groovyConsole;
3136 * handles the payload of a drag and drop event.
3138 * TODO refactor to desktop utilities class
3141 * - Data source strings extracted from the drop event
3143 * - protocol for each data source extracted from the drop event
3147 * - the payload from the drop event
3150 @SuppressWarnings("unchecked")
3151 public static void transferFromDropTarget(List<Object> files,
3152 List<DataSourceType> protocols, DropTargetDropEvent evt,
3153 Transferable t) throws Exception
3156 // BH 2018 changed List<String> to List<Object> to allow for File from
3159 // DataFlavor[] flavors = t.getTransferDataFlavors();
3160 // for (int i = 0; i < flavors.length; i++) {
3161 // if (flavors[i].isFlavorJavaFileListType()) {
3162 // evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
3163 // List<File> list = (List<File>) t.getTransferData(flavors[i]);
3164 // for (int j = 0; j < list.size(); j++) {
3165 // File file = (File) list.get(j);
3166 // byte[] data = getDroppedFileBytes(file);
3167 // fileName.setText(file.getName() + " - " + data.length + " " +
3168 // evt.getLocation());
3169 // JTextArea target = (JTextArea) ((DropTarget)
3170 // evt.getSource()).getComponent();
3171 // target.setText(new String(data));
3173 // dtde.dropComplete(true);
3178 DataFlavor uriListFlavor = new DataFlavor(
3179 "text/uri-list;class=java.lang.String"), urlFlavour = null;
3182 urlFlavour = new DataFlavor(
3183 "application/x-java-url; class=java.net.URL");
3184 } catch (ClassNotFoundException cfe)
3186 Cache.log.debug("Couldn't instantiate the URL dataflavor.", cfe);
3189 if (urlFlavour != null && t.isDataFlavorSupported(urlFlavour))
3194 java.net.URL url = (URL) t.getTransferData(urlFlavour);
3195 // nb: java 8 osx bug https://bugs.openjdk.java.net/browse/JDK-8156099
3196 // means url may be null.
3199 protocols.add(DataSourceType.URL);
3200 files.add(url.toString());
3201 Cache.log.debug("Drop handled as URL dataflavor "
3202 + files.get(files.size() - 1));
3207 if (Platform.isAMacAndNotJS())
3210 "Please ignore plist error - occurs due to problem with java 8 on OSX");
3213 } catch (Throwable ex)
3215 Cache.log.debug("URL drop handler failed.", ex);
3218 if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
3220 // Works on Windows and MacOSX
3221 Cache.log.debug("Drop handled as javaFileListFlavor");
3222 for (File file : (List<File>) t
3223 .getTransferData(DataFlavor.javaFileListFlavor))
3226 protocols.add(DataSourceType.FILE);
3231 // Unix like behaviour
3232 boolean added = false;
3234 if (t.isDataFlavorSupported(uriListFlavor))
3236 Cache.log.debug("Drop handled as uriListFlavor");
3237 // This is used by Unix drag system
3238 data = (String) t.getTransferData(uriListFlavor);
3242 // fallback to text: workaround - on OSX where there's a JVM bug
3243 Cache.log.debug("standard URIListFlavor failed. Trying text");
3244 // try text fallback
3245 DataFlavor textDf = new DataFlavor(
3246 "text/plain;class=java.lang.String");
3247 if (t.isDataFlavorSupported(textDf))
3249 data = (String) t.getTransferData(textDf);
3252 Cache.log.debug("Plain text drop content returned "
3253 + (data == null ? "Null - failed" : data));
3258 while (protocols.size() < files.size())
3260 Cache.log.debug("Adding missing FILE protocol for "
3261 + files.get(protocols.size()));
3262 protocols.add(DataSourceType.FILE);
3264 for (java.util.StringTokenizer st = new java.util.StringTokenizer(
3265 data, "\r\n"); st.hasMoreTokens();)
3268 String s = st.nextToken();
3269 if (s.startsWith("#"))
3271 // the line is a comment (as per the RFC 2483)
3274 java.net.URI uri = new java.net.URI(s);
3275 if (uri.getScheme().toLowerCase().startsWith("http"))
3277 protocols.add(DataSourceType.URL);
3278 files.add(uri.toString());
3282 // otherwise preserve old behaviour: catch all for file objects
3283 java.io.File file = new java.io.File(uri);
3284 protocols.add(DataSourceType.FILE);
3285 files.add(file.toString());
3290 if (Cache.log.isDebugEnabled())
3292 if (data == null || !added)
3295 if (t.getTransferDataFlavors() != null
3296 && t.getTransferDataFlavors().length > 0)
3299 "Couldn't resolve drop data. Here are the supported flavors:");
3300 for (DataFlavor fl : t.getTransferDataFlavors())
3303 "Supported transfer dataflavor: " + fl.toString());
3304 Object df = t.getTransferData(fl);
3307 Cache.log.debug("Retrieves: " + df);
3311 Cache.log.debug("Retrieved nothing");
3317 Cache.log.debug("Couldn't resolve dataflavor for drop: "
3323 if (Platform.isWindowsAndNotJS())
3325 Cache.log.debug("Scanning dropped content for Windows Link Files");
3327 // resolve any .lnk files in the file drop
3328 for (int f = 0; f < files.size(); f++)
3330 String source = files.get(f).toString().toLowerCase();
3331 if (protocols.get(f).equals(DataSourceType.FILE)
3332 && (source.endsWith(".lnk") || source.endsWith(".url")
3333 || source.endsWith(".site")))
3337 Object obj = files.get(f);
3338 File lf = (obj instanceof File ? (File) obj
3339 : new File((String) obj));
3340 // process link file to get a URL
3341 Cache.log.debug("Found potential link file: " + lf);
3342 WindowsShortcut wscfile = new WindowsShortcut(lf);
3343 String fullname = wscfile.getRealFilename();
3344 protocols.set(f, FormatAdapter.checkProtocol(fullname));
3345 files.set(f, fullname);
3346 Cache.log.debug("Parsed real filename " + fullname
3347 + " to extract protocol: " + protocols.get(f));
3348 } catch (Exception ex)
3351 "Couldn't parse " + files.get(f) + " as a link file.",
3360 * Sets the Preferences property for experimental features to True or False
3361 * depending on the state of the controlling menu item
3364 protected void showExperimental_actionPerformed(boolean selected)
3366 Cache.setProperty(EXPERIMENTAL_FEATURES, Boolean.toString(selected));
3370 * Answers a (possibly empty) list of any structure viewer frames (currently
3371 * for either Jmol or Chimera) which are currently open. This may optionally
3372 * be restricted to viewers of a specified class, or viewers linked to a
3373 * specified alignment panel.
3376 * if not null, only return viewers linked to this panel
3377 * @param structureViewerClass
3378 * if not null, only return viewers of this class
3381 public List<StructureViewerBase> getStructureViewers(
3382 AlignmentPanel apanel,
3383 Class<? extends StructureViewerBase> structureViewerClass)
3385 List<StructureViewerBase> result = new ArrayList<>();
3386 JInternalFrame[] frames = getAllFrames();
3388 for (JInternalFrame frame : frames)
3390 if (frame instanceof StructureViewerBase)
3392 if (structureViewerClass == null
3393 || structureViewerClass.isInstance(frame))
3396 || ((StructureViewerBase) frame).isLinkedWith(apanel))
3398 result.add((StructureViewerBase) frame);