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.UrlConstants;
125 import jalview.viewmodel.AlignmentViewport;
126 import jalview.ws.params.ParamManager;
127 import jalview.ws.utils.UrlDownloadClient;
134 * @version $Revision: 1.155 $
136 @SuppressWarnings("serial")
137 public class Desktop extends GDesktop
138 implements DropTargetListener, ClipboardOwner, IProgressIndicator,
139 StructureSelectionManagerProvider, ApplicationSingletonI
142 private static final String CITATION = "<br><br>Development managed by The Barton Group, University of Dundee, Scotland, UK.<br>"
143 + "<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"
144 + "<br><br>If you use Jalview, please cite:"
145 + "<br>Waterhouse, A.M., Procter, J.B., Martin, D.M.A, Clamp, M. and Barton, G. J. (2009)"
146 + "<br>Jalview Version 2 - a multiple sequence alignment editor and analysis workbench"
147 + "<br>Bioinformatics doi: 10.1093/bioinformatics/btp033";
149 private static final String DEFAULT_AUTHORS = "The Jalview Authors (See AUTHORS file for current list)";
151 private static int DEFAULT_MIN_WIDTH = 300;
153 private static int DEFAULT_MIN_HEIGHT = 250;
155 private static int ALIGN_FRAME_DEFAULT_MIN_WIDTH = 600;
157 private static int ALIGN_FRAME_DEFAULT_MIN_HEIGHT = 70;
159 private static final String EXPERIMENTAL_FEATURES = "EXPERIMENTAL_FEATURES";
161 protected static final String CONFIRM_KEYBOARD_QUIT = "CONFIRM_KEYBOARD_QUIT";
163 public static HashMap<String, FileWriter> savingFiles = new HashMap<String, FileWriter>();
165 private JalviewChangeSupport changeSupport = new JalviewChangeSupport();
168 * news reader - null if it was never started.
170 private BlogReader jvnews = null;
172 private File projectFile;
176 * @see jalview.gui.JalviewChangeSupport#addJalviewPropertyChangeListener(java.beans.PropertyChangeListener)
178 public void addJalviewPropertyChangeListener(
179 PropertyChangeListener listener)
181 changeSupport.addJalviewPropertyChangeListener(listener);
185 * @param propertyName
187 * @see jalview.gui.JalviewChangeSupport#addJalviewPropertyChangeListener(java.lang.String,
188 * java.beans.PropertyChangeListener)
190 public void addJalviewPropertyChangeListener(String propertyName,
191 PropertyChangeListener listener)
193 changeSupport.addJalviewPropertyChangeListener(propertyName, listener);
197 * @param propertyName
199 * @see jalview.gui.JalviewChangeSupport#removeJalviewPropertyChangeListener(java.lang.String,
200 * java.beans.PropertyChangeListener)
202 public void removeJalviewPropertyChangeListener(String propertyName,
203 PropertyChangeListener listener)
205 changeSupport.removeJalviewPropertyChangeListener(propertyName,
209 private MyDesktopPane desktopPane;
211 public static MyDesktopPane getDesktopPane()
213 Desktop desktop = getInstance();
214 return desktop == null ? null : desktop.desktopPane;
218 * Answers an 'application scope' singleton instance of this class. Separate
219 * SwingJS 'applets' running in the same browser page will each have a
220 * distinct instance of Desktop.
224 public static Desktop getInstance()
226 return Jalview.isHeadlessMode() ? null
227 : (Desktop) ApplicationSingletonProvider
228 .getInstance(Desktop.class);
231 public static StructureSelectionManager getStructureSelectionManager()
233 return StructureSelectionManager
234 .getStructureSelectionManager(getInstance());
237 int openFrameCount = 0;
239 final int xOffset = 30;
241 final int yOffset = 30;
243 public jalview.ws.jws1.Discoverer discoverer;
245 public Object[] jalviewClipboard;
247 public boolean internalCopy = false;
249 int fileLoadingCount = 0;
251 class MyDesktopManager implements DesktopManager
254 private DesktopManager delegate;
256 public MyDesktopManager(DesktopManager delegate)
258 this.delegate = delegate;
262 public void activateFrame(JInternalFrame f)
266 delegate.activateFrame(f);
267 } catch (NullPointerException npe)
269 Point p = getMousePosition();
270 showPasteMenu(p.x, p.y);
275 public void beginDraggingFrame(JComponent f)
277 delegate.beginDraggingFrame(f);
281 public void beginResizingFrame(JComponent f, int direction)
283 delegate.beginResizingFrame(f, direction);
287 public void closeFrame(JInternalFrame f)
289 delegate.closeFrame(f);
293 public void deactivateFrame(JInternalFrame f)
295 delegate.deactivateFrame(f);
299 public void deiconifyFrame(JInternalFrame f)
301 delegate.deiconifyFrame(f);
305 public void dragFrame(JComponent f, int newX, int newY)
311 delegate.dragFrame(f, newX, newY);
315 public void endDraggingFrame(JComponent f)
317 delegate.endDraggingFrame(f);
318 desktopPane.repaint();
322 public void endResizingFrame(JComponent f)
324 delegate.endResizingFrame(f);
325 desktopPane.repaint();
329 public void iconifyFrame(JInternalFrame f)
331 delegate.iconifyFrame(f);
335 public void maximizeFrame(JInternalFrame f)
337 delegate.maximizeFrame(f);
341 public void minimizeFrame(JInternalFrame f)
343 delegate.minimizeFrame(f);
347 public void openFrame(JInternalFrame f)
349 delegate.openFrame(f);
353 public void resizeFrame(JComponent f, int newX, int newY, int newWidth,
360 delegate.resizeFrame(f, newX, newY, newWidth, newHeight);
364 public void setBoundsForFrame(JComponent f, int newX, int newY,
365 int newWidth, int newHeight)
367 delegate.setBoundsForFrame(f, newX, newY, newWidth, newHeight);
370 // All other methods, simply delegate
375 * Private constructor enforces singleton pattern. It is called by reflection
376 * from ApplicationSingletonProvider.getInstance().
384 * A note to implementors. It is ESSENTIAL that any activities that might
385 * block are spawned off as threads rather than waited for during this
389 doConfigureStructurePrefs();
390 setTitle("Jalview " + Cache.getProperty("VERSION"));
392 if (!Platform.isAMac())
394 // this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
398 this.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
404 APQHandlers.setAPQHandlers(this);
405 } catch (Throwable t)
407 System.out.println("Error setting APQHandlers: " + t.toString());
408 // t.printStackTrace();
411 addWindowListener(new WindowAdapter()
415 public void windowClosing(WindowEvent ev)
421 boolean selmemusage = Cache.getDefault("SHOW_MEMUSAGE", false);
423 boolean showjconsole = Cache.getDefault("SHOW_JAVA_CONSOLE", false);
424 desktopPane = new MyDesktopPane(selmemusage);
426 showMemusage.setSelected(selmemusage);
427 desktopPane.setBackground(Color.white);
429 getContentPane().setLayout(new BorderLayout());
430 // alternate config - have scrollbars - see notes in JAL-153
431 // JScrollPane sp = new JScrollPane();
432 // sp.getViewport().setView(desktop);
433 // getContentPane().add(sp, BorderLayout.CENTER);
435 // BH 2018 - just an experiment to try unclipped JInternalFrames.
438 getRootPane().putClientProperty("swingjs.overflow.hidden", "false");
441 getContentPane().add(desktopPane, BorderLayout.CENTER);
442 desktopPane.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
444 // This line prevents Windows Look&Feel resizing all new windows to
446 // if previous window was maximised
447 desktopPane.setDesktopManager(new MyDesktopManager(
448 (Platform.isWindowsAndNotJS() ? new DefaultDesktopManager()
449 : Platform.isAMacAndNotJS()
450 ? new AquaInternalFrameManager(
451 desktopPane.getDesktopManager())
452 : desktopPane.getDesktopManager())));
454 Rectangle dims = getLastKnownDimensions("");
461 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
462 int xPos = Math.max(5, (screenSize.width - 900) / 2);
463 int yPos = Math.max(5, (screenSize.height - 650) / 2);
464 setBounds(xPos, yPos, 900, 650);
467 getIdentifiersOrgData();
469 if (!Platform.isJS())
476 jconsole = new Console(this, showjconsole);
477 jconsole.setHeader(Cache.getVersionDetailsForConsole());
478 showConsole(showjconsole);
480 showNews.setVisible(false);
482 experimentalFeatures.setSelected(showExperimental());
484 if (Jalview.isInteractive())
486 // disabled for SeqCanvasTest
489 // Spawn a thread that shows the splashscreen
491 SwingUtilities.invokeLater(new Runnable()
496 new SplashScreen(true);
500 // Thread off a new instance of the file chooser - this reduces the
503 // takes to open it later on.
504 new Thread(new Runnable()
509 Cache.log.debug("Filechooser init thread started.");
510 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
511 JalviewFileChooser.forRead(
512 Cache.getProperty("LAST_DIRECTORY"), fileFormat);
513 Cache.log.debug("Filechooser init thread finished.");
516 // Add the service change listener
517 changeSupport.addJalviewPropertyChangeListener("services",
518 new PropertyChangeListener()
522 public void propertyChange(PropertyChangeEvent evt)
524 Cache.log.debug("Firing service changed event for "
525 + evt.getNewValue());
526 JalviewServicesChanged(evt);
531 this.setDropTarget(new java.awt.dnd.DropTarget(desktopPane, this));
533 this.addWindowListener(new WindowAdapter()
536 public void windowClosing(WindowEvent evt)
543 this.addMouseListener(ma = new MouseAdapter()
546 public void mousePressed(MouseEvent evt)
548 if (evt.isPopupTrigger()) // Mac
550 showPasteMenu(evt.getX(), evt.getY());
555 public void mouseReleased(MouseEvent evt)
557 if (evt.isPopupTrigger()) // Windows
559 showPasteMenu(evt.getX(), evt.getY());
563 desktopPane.addMouseListener(ma);
564 } catch (Throwable t)
572 * Answers true if user preferences to enable experimental features is True
577 public boolean showExperimental()
579 String experimental = Cache.getDefault(EXPERIMENTAL_FEATURES,
580 Boolean.FALSE.toString());
581 return Boolean.valueOf(experimental).booleanValue();
584 public void doConfigureStructurePrefs()
586 // configure services
587 StructureSelectionManager ssm = StructureSelectionManager
588 .getStructureSelectionManager(this);
589 if (Cache.getDefault(Preferences.ADD_SS_ANN, true))
591 ssm.setAddTempFacAnnot(
592 Cache.getDefault(Preferences.ADD_TEMPFACT_ANN, true));
593 ssm.setProcessSecondaryStructure(
594 Cache.getDefault(Preferences.STRUCT_FROM_PDB, true));
595 ssm.setSecStructServices(
596 Cache.getDefault(Preferences.USE_RNAVIEW, true));
600 ssm.setAddTempFacAnnot(false);
601 ssm.setProcessSecondaryStructure(false);
602 ssm.setSecStructServices(false);
606 public void checkForNews()
608 final Desktop me = this;
609 // Thread off the news reader, in case there are connection problems.
610 new Thread(new Runnable()
615 Cache.log.debug("Starting news thread.");
616 jvnews = new BlogReader(me);
617 showNews.setVisible(true);
618 Cache.log.debug("Completed news thread.");
623 public void getIdentifiersOrgData()
625 // Thread off the identifiers fetcher
626 new Thread(new Runnable()
631 Cache.log.debug("Downloading data from identifiers.org");
634 UrlDownloadClient.download(IdOrgSettings.getUrl(),
635 IdOrgSettings.getDownloadLocation());
636 } catch (IOException e)
638 Cache.log.debug("Exception downloading identifiers.org data"
647 protected void showNews_actionPerformed(ActionEvent e)
649 showNews(showNews.isSelected());
652 void showNews(boolean visible)
654 Cache.log.debug((visible ? "Showing" : "Hiding") + " news.");
655 showNews.setSelected(visible);
656 if (visible && !jvnews.isVisible())
658 new Thread(new Runnable()
663 long now = System.currentTimeMillis();
664 setProgressBar(MessageManager.getString("status.refreshing_news"),
666 jvnews.refreshNews();
667 setProgressBar(null, now);
675 * recover the last known dimensions for a jalview window
678 * - empty string is desktop, all other windows have unique prefix
679 * @return null or last known dimensions scaled to current geometry (if last
680 * window geom was known)
682 Rectangle getLastKnownDimensions(String windowName)
684 // TODO: lock aspect ratio for scaling desktop Bug #0058199
685 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
686 String x = Cache.getProperty(windowName + "SCREEN_X");
687 String y = Cache.getProperty(windowName + "SCREEN_Y");
688 String width = Cache.getProperty(windowName + "SCREEN_WIDTH");
689 String height = Cache.getProperty(windowName + "SCREEN_HEIGHT");
690 if ((x != null) && (y != null) && (width != null) && (height != null))
692 int ix = Integer.parseInt(x), iy = Integer.parseInt(y),
693 iw = Integer.parseInt(width), ih = Integer.parseInt(height);
694 if (Cache.getProperty("SCREENGEOMETRY_WIDTH") != null)
696 // attempt #1 - try to cope with change in screen geometry - this
697 // version doesn't preserve original jv aspect ratio.
698 // take ratio of current screen size vs original screen size.
699 double sw = ((1f * screenSize.width) / (1f * Integer
700 .parseInt(Cache.getProperty("SCREENGEOMETRY_WIDTH"))));
701 double sh = ((1f * screenSize.height) / (1f * Integer
702 .parseInt(Cache.getProperty("SCREENGEOMETRY_HEIGHT"))));
703 // rescale the bounds depending upon the current screen geometry.
704 ix = (int) (ix * sw);
705 iw = (int) (iw * sw);
706 iy = (int) (iy * sh);
707 ih = (int) (ih * sh);
708 while (ix >= screenSize.width)
711 "Window geometry location recall error: shifting horizontal to within screenbounds.");
712 ix -= screenSize.width;
714 while (iy >= screenSize.height)
717 "Window geometry location recall error: shifting vertical to within screenbounds.");
718 iy -= screenSize.height;
721 "Got last known dimensions for " + windowName + ": x:" + ix
722 + " y:" + iy + " width:" + iw + " height:" + ih);
724 // return dimensions for new instance
725 return new Rectangle(ix, iy, iw, ih);
730 void showPasteMenu(int x, int y)
732 JPopupMenu popup = new JPopupMenu();
733 JMenuItem item = new JMenuItem(
734 MessageManager.getString("label.paste_new_window"));
735 item.addActionListener(new ActionListener()
738 public void actionPerformed(ActionEvent evt)
745 popup.show(this, x, y);
752 Clipboard c = Toolkit.getDefaultToolkit().getSystemClipboard();
753 Transferable contents = c.getContents(this);
755 if (contents != null)
757 String file = (String) contents
758 .getTransferData(DataFlavor.stringFlavor);
760 FileFormatI format = new IdentifyFile().identify(file,
761 DataSourceType.PASTE);
763 new FileLoader().LoadFile(file, DataSourceType.PASTE, format);
766 } catch (Exception ex)
769 "Unable to paste alignment from system clipboard:\n" + ex);
774 // * Add an internal frame to the Jalview desktop that is allowed to be resized,
775 // * has a minimum size of 300px and might or might not be visible
781 // * @param makeVisible
782 // * When true, display frame immediately, otherwise, caller must call
783 // * setVisible themselves.
790 // public static synchronized void addInternalFrame(
791 // final JInternalFrame frame, String title, boolean makeVisible,
794 // // textbox, web services, sequenceFetcher, featureSettings
795 // getInstance().addFrame(frame, title, makeVisible, w, h,
796 // FRAME_ALLOW_RESIZE, FRAME_SET_MIN_SIZE_300);
800 // * Add an internal frame to the Jalview desktop that is visible, has a minimum
801 // * size of 300px, and may or may not be resizable
811 // * @param resizable
815 // public static synchronized void addInternalFrame(
816 // final JInternalFrame frame, String title, int w, int h,
817 // boolean resizable)
819 // // annotation, font, calculation, user-defined colors
820 // getInstance().addFrame(frame, title, FRAME_MAKE_VISIBLE, w, h,
821 // resizable, FRAME_SET_MIN_SIZE_300);
825 * Adds and opens the given frame to the desktop that is visible, allowed to
826 * resize, and has a 300px minimum width.
837 public static synchronized void addInternalFrame(
838 final JInternalFrame frame, String title, int w, int h)
842 addInternalFrame(frame, title, Desktop.FRAME_MAKE_VISIBLE, w, h,
843 FRAME_ALLOW_RESIZE, FRAME_SET_MIN_SIZE_300);
847 * Add an internal frame to the Jalview desktop that may optionally be
848 * visible, resizable, and allowed to be any size
855 * When true, display frame immediately, otherwise, caller must call
856 * setVisible themselves.
863 * @param ignoreMinSize
864 * Do not set the default minimum size for frame
866 public static synchronized void addInternalFrame(
867 final JInternalFrame frame, String title, boolean makeVisible,
868 int w, int h, boolean resizable, boolean ignoreMinSize)
870 // 15 classes call this method directly.
872 // TODO: allow callers to determine X and Y position of frame (eg. via
874 // TODO: consider fixing method to update entries in the window submenu with
875 // the current window title
877 frame.setTitle(title);
878 if (frame.getWidth() < 1 || frame.getHeight() < 1)
882 if (getInstance() != null)
883 getInstance().addFrame(frame, makeVisible, resizable,
887 // These can now by put into a single int flag, if desired:
889 public final static boolean FRAME_MAKE_VISIBLE = true;
891 public final static boolean FRAME_NOT_VISIBLE = false;
893 public final static boolean FRAME_ALLOW_RESIZE = true;
895 public final static boolean FRAME_NOT_RESIZABLE = false;
897 public final static boolean FRAME_ALLOW_ANY_SIZE = true;
899 public final static boolean FRAME_SET_MIN_SIZE_300 = false;
901 private void addFrame(JInternalFrame frame,
902 boolean makeVisible, boolean resizable,
903 boolean ignoreMinSize)
908 boolean isEmbedded = (Platform.getEmbeddedAttribute(frame, "id") != null);
909 boolean hasEmbeddedSize = (Platform.getDimIfEmbedded(frame, -1, -1) != null);
910 // Web page embedding allows us to ignore minimum size
911 ignoreMinSize |= hasEmbeddedSize;
915 // Set default dimension for Alignment Frame window.
916 // The Alignment Frame window could be added from a number of places,
918 // I did this here in order not to miss out on any Alignment frame.
919 if (frame instanceof AlignFrame)
921 frame.setMinimumSize(new Dimension(ALIGN_FRAME_DEFAULT_MIN_WIDTH,
922 ALIGN_FRAME_DEFAULT_MIN_HEIGHT));
924 frame.setMinimumSize(
925 new Dimension(DEFAULT_MIN_WIDTH, DEFAULT_MIN_HEIGHT));
930 frame.setVisible(makeVisible);
931 frame.setClosable(true);
932 frame.setResizable(resizable);
933 frame.setMaximizable(resizable);
934 frame.setIconifiable(resizable);
935 frame.setOpaque(Platform.isJS());
936 if (!isEmbedded && frame.getX() < 1 && frame.getY() < 1)
938 frame.setLocation(xOffset * openFrameCount,
939 yOffset * ((openFrameCount - 1) % 10) + yOffset);
943 * add an entry for the new frame in the Window menu
944 * (and remove it when the frame is closed)
946 final JMenuItem menuItem = new JMenuItem(frame.getTitle());
947 frame.addInternalFrameListener(new InternalFrameAdapter()
950 public void internalFrameActivated(InternalFrameEvent evt)
952 JInternalFrame itf = getDesktopPane().getSelectedFrame();
955 if (itf instanceof AlignFrame)
957 Jalview.setCurrentAlignFrame((AlignFrame) itf);
964 public void internalFrameClosed(InternalFrameEvent evt)
966 PaintRefresher.RemoveComponent(frame);
969 * defensive check to prevent frames being
970 * added half off the window
972 if (openFrameCount > 0)
978 * ensure no reference to alignFrame retained by menu item listener
980 if (menuItem.getActionListeners().length > 0)
982 menuItem.removeActionListener(menuItem.getActionListeners()[0]);
984 getInstance().windowMenu.remove(menuItem);
988 menuItem.addActionListener(new ActionListener()
991 public void actionPerformed(ActionEvent e)
995 frame.setSelected(true);
996 frame.setIcon(false);
997 } catch (java.beans.PropertyVetoException ex)
999 // System.err.println(ex.toString());
1004 setKeyBindings(frame);
1006 getDesktopPane().add(frame);
1008 getInstance().windowMenu.add(menuItem);
1013 frame.setSelected(true);
1014 frame.requestFocus();
1015 } catch (java.beans.PropertyVetoException ve)
1017 } catch (java.lang.ClassCastException cex)
1020 "Squashed a possible GUI implementation error. If you can recreate this, please look at http://issues.jalview.org/browse/JAL-869",
1026 * Add key bindings to a JInternalFrame so that Ctrl-W and Cmd-W will close
1031 private static void setKeyBindings(JInternalFrame frame)
1033 final Action closeAction = new AbstractAction()
1036 public void actionPerformed(ActionEvent e)
1043 * set up key bindings for Ctrl-W and Cmd-W, with the same (Close) action
1045 KeyStroke ctrlWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
1046 InputEvent.CTRL_DOWN_MASK);
1047 KeyStroke cmdWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
1048 Platform.SHORTCUT_KEY_MASK);
1050 InputMap inputMap = frame
1051 .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
1052 String ctrlW = ctrlWKey.toString();
1053 inputMap.put(ctrlWKey, ctrlW);
1054 inputMap.put(cmdWKey, ctrlW);
1056 ActionMap actionMap = frame.getActionMap();
1057 actionMap.put(ctrlW, closeAction);
1061 public void lostOwnership(Clipboard clipboard, Transferable contents)
1065 jalviewClipboard = null;
1068 internalCopy = false;
1072 public void dragEnter(DropTargetDragEvent evt)
1077 public void dragExit(DropTargetEvent evt)
1082 public void dragOver(DropTargetDragEvent evt)
1087 public void dropActionChanged(DropTargetDragEvent evt)
1098 public void drop(DropTargetDropEvent evt)
1100 boolean success = true;
1101 // JAL-1552 - acceptDrop required before getTransferable call for
1102 // Java's Transferable for native dnd
1103 evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
1104 Transferable t = evt.getTransferable();
1105 List<Object> files = new ArrayList<>();
1106 List<DataSourceType> protocols = new ArrayList<>();
1110 transferFromDropTarget(files, protocols, evt, t);
1111 } catch (Exception e)
1113 e.printStackTrace();
1121 for (int i = 0; i < files.size(); i++)
1123 // BH 2018 File or String
1124 Object file = files.get(i);
1125 String fileName = file.toString();
1126 DataSourceType protocol = (protocols == null)
1127 ? DataSourceType.FILE
1129 FileFormatI format = null;
1131 if (fileName.endsWith(".jar"))
1133 format = FileFormat.Jalview;
1138 format = new IdentifyFile().identify(file, protocol);
1140 if (file instanceof File)
1142 Platform.cacheFileData((File) file);
1144 new FileLoader().LoadFile(null, file, protocol, format);
1147 } catch (Exception ex)
1152 evt.dropComplete(success); // need this to ensure input focus is properly
1153 // transfered to any new windows created
1163 public void inputLocalFileMenuItem_actionPerformed(AlignViewport viewport)
1165 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
1166 JalviewFileChooser chooser = JalviewFileChooser.forRead(
1167 Cache.getProperty("LAST_DIRECTORY"), fileFormat,
1168 BackupFiles.getEnabled());
1170 chooser.setFileView(new JalviewFileView());
1171 chooser.setDialogTitle(
1172 MessageManager.getString("label.open_local_file"));
1173 chooser.setToolTipText(MessageManager.getString("action.open"));
1175 chooser.setResponseHandler(0, new Runnable()
1180 File selectedFile = chooser.getSelectedFile();
1181 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1183 FileFormatI format = chooser.getSelectedFormat();
1186 * Call IdentifyFile to verify the file contains what its extension implies.
1187 * Skip this step for dynamically added file formats, because
1188 * IdentifyFile does not know how to recognise them.
1190 if (FileFormats.getInstance().isIdentifiable(format))
1194 format = new IdentifyFile().identify(selectedFile,
1195 DataSourceType.FILE);
1196 } catch (FileFormatException e)
1198 // format = null; //??
1202 new FileLoader().LoadFile(viewport, selectedFile,
1203 DataSourceType.FILE, format);
1206 chooser.showOpenDialog(this);
1210 * Shows a dialog for input of a URL at which to retrieve alignment data
1215 public void inputURLMenuItem_actionPerformed(AlignViewport viewport)
1217 // This construct allows us to have a wider textfield
1219 JLabel label = new JLabel(
1220 MessageManager.getString("label.input_file_url"));
1222 JPanel panel = new JPanel(new GridLayout(2, 1));
1226 * the URL to fetch is
1227 * Java: an editable combobox with history
1228 * JS: (pending JAL-3038) a plain text field
1231 String urlBase = "http://www.";
1232 if (Platform.isJS())
1234 history = new JTextField(urlBase, 35);
1243 JComboBox<String> asCombo = new JComboBox<>();
1244 asCombo.setPreferredSize(new Dimension(400, 20));
1245 asCombo.setEditable(true);
1246 asCombo.addItem(urlBase);
1247 String historyItems = Cache.getProperty("RECENT_URL");
1248 if (historyItems != null)
1250 for (String token : historyItems.split("\\t"))
1252 asCombo.addItem(token);
1259 Object[] options = new Object[] { MessageManager.getString("action.ok"),
1260 MessageManager.getString("action.cancel") };
1261 Runnable action = new Runnable()
1266 @SuppressWarnings("unchecked")
1267 String url = (history instanceof JTextField
1268 ? ((JTextField) history).getText()
1269 : ((JComboBox<String>) history).getSelectedItem()
1272 if (url.toLowerCase().endsWith(".jar"))
1274 if (viewport != null)
1276 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1277 FileFormat.Jalview);
1281 new FileLoader().LoadFile(url, DataSourceType.URL,
1282 FileFormat.Jalview);
1287 FileFormatI format = null;
1290 format = new IdentifyFile().identify(url, DataSourceType.URL);
1291 } catch (FileFormatException e)
1293 // TODO revise error handling, distinguish between
1294 // URL not found and response not valid
1299 String msg = MessageManager
1300 .formatMessage("label.couldnt_locate", url);
1301 JvOptionPane.showInternalMessageDialog(getDesktopPane(), msg,
1302 MessageManager.getString("label.url_not_found"),
1303 JvOptionPane.WARNING_MESSAGE);
1308 if (viewport != null)
1310 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1315 new FileLoader().LoadFile(url, DataSourceType.URL, format);
1320 String dialogOption = MessageManager
1321 .getString("label.input_alignment_from_url");
1322 JvOptionPane.newOptionDialog(desktopPane).setResponseHandler(0, action)
1323 .showInternalDialog(panel, dialogOption,
1324 JvOptionPane.YES_NO_CANCEL_OPTION,
1325 JvOptionPane.PLAIN_MESSAGE, null, options,
1326 MessageManager.getString("action.ok"));
1330 * Opens the CutAndPaste window for the user to paste an alignment in to
1333 * - if not null, the pasted alignment is added to the current
1334 * alignment; if null, to a new alignment window
1337 public void inputTextboxMenuItem_actionPerformed(
1338 AlignmentViewPanel viewPanel)
1340 CutAndPasteTransfer cap = new CutAndPasteTransfer();
1341 cap.setForInput(viewPanel);
1342 addInternalFrame(cap,
1343 MessageManager.getString("label.cut_paste_alignmen_file"),
1344 FRAME_MAKE_VISIBLE, 600, 500, FRAME_ALLOW_RESIZE,
1345 FRAME_SET_MIN_SIZE_300);
1354 Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
1355 Cache.setProperty("SCREENGEOMETRY_WIDTH", screen.width + "");
1356 Cache.setProperty("SCREENGEOMETRY_HEIGHT", screen.height + "");
1357 storeLastKnownDimensions("", new Rectangle(getBounds().x, getBounds().y,
1358 getWidth(), getHeight()));
1360 if (jconsole != null)
1362 storeLastKnownDimensions("JAVA_CONSOLE_", jconsole.getBounds());
1363 jconsole.stopConsole();
1367 storeLastKnownDimensions("JALVIEW_RSS_WINDOW_", jvnews.getBounds());
1370 if (dialogExecutor != null)
1372 dialogExecutor.shutdownNow();
1374 closeAll_actionPerformed(null);
1376 if (groovyConsole != null)
1378 // suppress a possible repeat prompt to save script
1379 groovyConsole.setDirty(false);
1380 groovyConsole.exit();
1385 private void storeLastKnownDimensions(String string, Rectangle jc)
1387 Cache.log.debug("Storing last known dimensions for " + string + ": x:"
1388 + jc.x + " y:" + jc.y + " width:" + jc.width + " height:"
1391 Cache.setProperty(string + "SCREEN_X", jc.x + "");
1392 Cache.setProperty(string + "SCREEN_Y", jc.y + "");
1393 Cache.setProperty(string + "SCREEN_WIDTH", jc.width + "");
1394 Cache.setProperty(string + "SCREEN_HEIGHT", jc.height + "");
1404 public void aboutMenuItem_actionPerformed(ActionEvent e)
1406 new Thread(new Runnable()
1411 new SplashScreen(false);
1417 * Returns the html text for the About screen, including any available version
1418 * number, build details, author details and citation reference, but without
1419 * the enclosing {@code html} tags
1423 public String getAboutMessage()
1425 StringBuilder message = new StringBuilder(1024);
1426 message.append("<h1><strong>Version: ")
1427 .append(Cache.getProperty("VERSION")).append("</strong></h1>")
1428 .append("<strong>Built: <em>")
1429 .append(Cache.getDefault("BUILD_DATE", "unknown"))
1430 .append("</em> from ").append(Cache.getBuildDetailsForSplash())
1431 .append("</strong>");
1433 String latestVersion = Cache.getDefault("LATEST_VERSION", "Checking");
1434 if (latestVersion.equals("Checking"))
1436 // JBP removed this message for 2.11: May be reinstated in future version
1437 // message.append("<br>...Checking latest version...</br>");
1439 else if (!latestVersion.equals(Cache.getProperty("VERSION")))
1441 boolean red = false;
1442 if (Cache.getProperty("VERSION").toLowerCase()
1443 .indexOf("automated build") == -1)
1446 // Displayed when code version and jnlp version do not match and code
1447 // version is not a development build
1448 message.append("<div style=\"color: #FF0000;font-style: bold;\">");
1451 message.append("<br>!! Version ")
1452 .append(Cache.getDefault("LATEST_VERSION", "..Checking.."))
1453 .append(" is available for download from ")
1454 .append(Cache.getDefault("www.jalview.org",
1455 "http://www.jalview.org"))
1459 message.append("</div>");
1462 message.append("<br>Authors: ");
1463 message.append(Cache.getDefault("AUTHORFNAMES", DEFAULT_AUTHORS));
1464 message.append(CITATION);
1466 return message.toString();
1470 * Action on requesting Help documentation
1473 public void documentationMenuItem_actionPerformed()
1477 if (Platform.isJS())
1479 BrowserLauncher.openURL("http://www.jalview.org/help.html");
1488 Help.showHelpWindow();
1490 } catch (Exception ex)
1492 System.err.println("Error opening help: " + ex.getMessage());
1497 public void closeAll_actionPerformed(ActionEvent e)
1499 // TODO show a progress bar while closing?
1500 JInternalFrame[] frames = desktopPane.getAllFrames();
1501 for (int i = 0; i < frames.length; i++)
1505 frames[i].setClosed(true);
1506 } catch (java.beans.PropertyVetoException ex)
1510 Jalview.setCurrentAlignFrame(null);
1511 System.out.println("ALL CLOSED");
1514 * reset state of singleton objects as appropriate (clear down session state
1515 * when all windows are closed)
1517 StructureSelectionManager ssm = StructureSelectionManager
1518 .getStructureSelectionManager(this);
1526 public void raiseRelated_actionPerformed(ActionEvent e)
1528 reorderAssociatedWindows(false, false);
1532 public void minimizeAssociated_actionPerformed(ActionEvent e)
1534 reorderAssociatedWindows(true, false);
1537 void closeAssociatedWindows()
1539 reorderAssociatedWindows(false, true);
1545 * @seejalview.jbgui.GDesktop#garbageCollect_actionPerformed(java.awt.event.
1549 protected void garbageCollect_actionPerformed(ActionEvent e)
1551 // We simply collect the garbage
1552 Cache.log.debug("Collecting garbage...");
1554 Cache.log.debug("Finished garbage collection.");
1561 * jalview.jbgui.GDesktop#showMemusage_actionPerformed(java.awt.event.ActionEvent
1565 protected void showMemusage_actionPerformed(ActionEvent e)
1567 desktopPane.showMemoryUsage(showMemusage.isSelected());
1574 * jalview.jbgui.GDesktop#showConsole_actionPerformed(java.awt.event.ActionEvent
1578 protected void showConsole_actionPerformed(ActionEvent e)
1580 showConsole(showConsole.isSelected());
1583 Console jconsole = null;
1586 * control whether the java console is visible or not
1590 void showConsole(boolean selected)
1592 // TODO: decide if we should update properties file
1593 if (jconsole != null) // BH 2018
1595 showConsole.setSelected(selected);
1596 Cache.setProperty("SHOW_JAVA_CONSOLE",
1597 Boolean.valueOf(selected).toString());
1598 jconsole.setVisible(selected);
1602 void reorderAssociatedWindows(boolean minimize, boolean close)
1604 JInternalFrame[] frames = desktopPane.getAllFrames();
1605 if (frames == null || frames.length < 1)
1610 AlignmentViewport source = null, target = null;
1611 if (frames[0] instanceof AlignFrame)
1613 source = ((AlignFrame) frames[0]).getCurrentView();
1615 else if (frames[0] instanceof TreePanel)
1617 source = ((TreePanel) frames[0]).getViewPort();
1619 else if (frames[0] instanceof PCAPanel)
1621 source = ((PCAPanel) frames[0]).av;
1623 else if (frames[0].getContentPane() instanceof PairwiseAlignPanel)
1625 source = ((PairwiseAlignPanel) frames[0].getContentPane()).av;
1630 for (int i = 0; i < frames.length; i++)
1633 if (frames[i] == null)
1637 if (frames[i] instanceof AlignFrame)
1639 target = ((AlignFrame) frames[i]).getCurrentView();
1641 else if (frames[i] instanceof TreePanel)
1643 target = ((TreePanel) frames[i]).getViewPort();
1645 else if (frames[i] instanceof PCAPanel)
1647 target = ((PCAPanel) frames[i]).av;
1649 else if (frames[i].getContentPane() instanceof PairwiseAlignPanel)
1651 target = ((PairwiseAlignPanel) frames[i].getContentPane()).av;
1654 if (source == target)
1660 frames[i].setClosed(true);
1664 frames[i].setIcon(minimize);
1667 frames[i].toFront();
1671 } catch (java.beans.PropertyVetoException ex)
1686 protected void preferences_actionPerformed(ActionEvent e)
1692 * Prompts the user to choose a file and then saves the Jalview state as a
1693 * Jalview project file
1696 public void saveState_actionPerformed()
1698 saveState_actionPerformed(false);
1701 public void saveState_actionPerformed(boolean saveAs)
1703 java.io.File projectFile = getProjectFile();
1704 // autoSave indicates we already have a file and don't need to ask
1705 boolean autoSave = projectFile != null && !saveAs
1706 && BackupFiles.getEnabled();
1708 // System.out.println("autoSave="+autoSave+", projectFile='"+projectFile+"',
1709 // saveAs="+saveAs+", Backups
1710 // "+(BackupFiles.getEnabled()?"enabled":"disabled"));
1712 boolean approveSave = false;
1715 JalviewFileChooser chooser = new JalviewFileChooser("jvp",
1718 chooser.setFileView(new JalviewFileView());
1719 chooser.setDialogTitle(MessageManager.getString("label.save_state"));
1721 int value = chooser.showSaveDialog(this);
1723 if (value == JalviewFileChooser.APPROVE_OPTION)
1725 projectFile = chooser.getSelectedFile();
1726 setProjectFile(projectFile);
1731 if (approveSave || autoSave)
1733 final Desktop me = this;
1734 final java.io.File chosenFile = projectFile;
1735 new Thread(new Runnable()
1740 // TODO: refactor to Jalview desktop session controller action.
1741 setProgressBar(MessageManager.formatMessage(
1742 "label.saving_jalview_project", new Object[]
1743 { chosenFile.getName() }), chosenFile.hashCode());
1744 Cache.setProperty("LAST_DIRECTORY", chosenFile.getParent());
1745 // TODO catch and handle errors for savestate
1746 // TODO prevent user from messing with the Desktop whilst we're saving
1749 boolean doBackup = BackupFiles.getEnabled();
1750 BackupFiles backupfiles = doBackup ? new BackupFiles(chosenFile)
1753 new Jalview2XML().saveState(
1754 doBackup ? backupfiles.getTempFile() : chosenFile);
1758 backupfiles.setWriteSuccess(true);
1759 backupfiles.rollBackupsAndRenameTempFile();
1761 } catch (OutOfMemoryError oom)
1763 new OOMWarning("Whilst saving current state to "
1764 + chosenFile.getName(), oom);
1765 } catch (Exception ex)
1767 Cache.log.error("Problems whilst trying to save to "
1768 + chosenFile.getName(), ex);
1769 JvOptionPane.showMessageDialog(me,
1770 MessageManager.formatMessage(
1771 "label.error_whilst_saving_current_state_to",
1773 { chosenFile.getName() }),
1774 MessageManager.getString("label.couldnt_save_project"),
1775 JvOptionPane.WARNING_MESSAGE);
1777 setProgressBar(null, chosenFile.hashCode());
1784 public void saveAsState_actionPerformed(ActionEvent e)
1786 saveState_actionPerformed(true);
1789 private void setProjectFile(File choice)
1791 this.projectFile = choice;
1794 public File getProjectFile()
1796 return this.projectFile;
1800 * Shows a file chooser dialog and tries to read in the selected file as a
1804 public void loadState_actionPerformed()
1806 final String[] suffix = new String[] { "jvp", "jar" };
1807 final String[] desc = new String[] { "Jalview Project",
1808 "Jalview Project (old)" };
1809 JalviewFileChooser chooser = new JalviewFileChooser(
1810 Cache.getProperty("LAST_DIRECTORY"), suffix, desc,
1811 "Jalview Project", true, BackupFiles.getEnabled()); // last two
1815 chooser.setFileView(new JalviewFileView());
1816 chooser.setDialogTitle(MessageManager.getString("label.restore_state"));
1817 chooser.setResponseHandler(0, new Runnable()
1822 File selectedFile = chooser.getSelectedFile();
1823 setProjectFile(selectedFile);
1824 String choice = selectedFile.getAbsolutePath();
1825 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1826 new Thread(new Runnable()
1833 new Jalview2XML().loadJalviewAlign(selectedFile);
1834 } catch (OutOfMemoryError oom)
1836 new OOMWarning("Whilst loading project from " + choice, oom);
1837 } catch (Exception ex)
1840 "Problems whilst loading project from " + choice, ex);
1841 JvOptionPane.showMessageDialog(getDesktopPane(),
1842 MessageManager.formatMessage(
1843 "label.error_whilst_loading_project_from",
1847 .getString("label.couldnt_load_project"),
1848 JvOptionPane.WARNING_MESSAGE);
1855 chooser.showOpenDialog(this);
1859 public void inputSequence_actionPerformed(ActionEvent e)
1861 new SequenceFetcher(this);
1864 JPanel progressPanel;
1866 ArrayList<JPanel> fileLoadingPanels = new ArrayList<>();
1868 public void startLoading(final Object fileName)
1870 if (fileLoadingCount == 0)
1872 fileLoadingPanels.add(addProgressPanel(MessageManager
1873 .formatMessage("label.loading_file", new Object[]
1879 private JPanel addProgressPanel(String string)
1881 if (progressPanel == null)
1883 progressPanel = new JPanel(new GridLayout(1, 1));
1884 totalProgressCount = 0;
1885 getContentPane().add(progressPanel, BorderLayout.SOUTH);
1887 JPanel thisprogress = new JPanel(new BorderLayout(10, 5));
1888 JProgressBar progressBar = new JProgressBar();
1889 progressBar.setIndeterminate(true);
1891 thisprogress.add(new JLabel(string), BorderLayout.WEST);
1893 thisprogress.add(progressBar, BorderLayout.CENTER);
1894 progressPanel.add(thisprogress);
1895 ((GridLayout) progressPanel.getLayout()).setRows(
1896 ((GridLayout) progressPanel.getLayout()).getRows() + 1);
1897 ++totalProgressCount;
1899 return thisprogress;
1902 int totalProgressCount = 0;
1904 private void removeProgressPanel(JPanel progbar)
1906 if (progressPanel != null)
1908 synchronized (progressPanel)
1910 progressPanel.remove(progbar);
1911 GridLayout gl = (GridLayout) progressPanel.getLayout();
1912 gl.setRows(gl.getRows() - 1);
1913 if (--totalProgressCount < 1)
1915 this.getContentPane().remove(progressPanel);
1916 progressPanel = null;
1923 public void stopLoading()
1926 if (fileLoadingCount < 1)
1928 while (fileLoadingPanels.size() > 0)
1930 removeProgressPanel(fileLoadingPanels.remove(0));
1932 fileLoadingPanels.clear();
1933 fileLoadingCount = 0;
1938 public static int getViewCount(String alignmentId)
1940 AlignmentViewport[] aps = getViewports(alignmentId);
1941 return (aps == null) ? 0 : aps.length;
1946 * @param alignmentId
1947 * - if null, all sets are returned
1948 * @return all AlignmentPanels concerning the alignmentId sequence set
1950 public static AlignmentPanel[] getAlignmentPanels(String alignmentId)
1952 if (getDesktopPane() == null)
1954 // no frames created and in headless mode
1955 // TODO: verify that frames are recoverable when in headless mode
1958 List<AlignmentPanel> aps = new ArrayList<>();
1959 AlignFrame[] frames = getAlignFrames();
1964 for (AlignFrame af : frames)
1966 for (AlignmentPanel ap : af.alignPanels)
1968 if (alignmentId == null
1969 || alignmentId.equals(ap.av.getSequenceSetId()))
1975 if (aps.size() == 0)
1979 AlignmentPanel[] vap = aps.toArray(new AlignmentPanel[aps.size()]);
1984 * get all the viewports on an alignment.
1986 * @param sequenceSetId
1987 * unique alignment id (may be null - all viewports returned in that
1989 * @return all viewports on the alignment bound to sequenceSetId
1991 public static AlignmentViewport[] getViewports(String sequenceSetId)
1993 List<AlignmentViewport> viewp = new ArrayList<>();
1994 if (getDesktopPane() != null)
1996 AlignFrame[] frames = getAlignFrames();
1998 for (AlignFrame afr : frames)
2000 if (sequenceSetId == null || afr.getViewport().getSequenceSetId()
2001 .equals(sequenceSetId))
2003 if (afr.alignPanels != null)
2005 for (AlignmentPanel ap : afr.alignPanels)
2007 if (sequenceSetId == null
2008 || sequenceSetId.equals(ap.av.getSequenceSetId()))
2016 viewp.add(afr.getViewport());
2020 if (viewp.size() > 0)
2022 return viewp.toArray(new AlignmentViewport[viewp.size()]);
2029 * Explode the views in the given frame into separate AlignFrame
2033 public static void explodeViews(AlignFrame af)
2035 int size = af.alignPanels.size();
2041 // FIXME: ideally should use UI interface API
2042 FeatureSettings viewFeatureSettings = (af.featureSettings != null
2043 && af.featureSettings.isOpen()) ? af.featureSettings : null;
2044 Rectangle fsBounds = af.getFeatureSettingsGeometry();
2045 for (int i = 0; i < size; i++)
2047 AlignmentPanel ap = af.alignPanels.get(i);
2049 AlignFrame newaf = new AlignFrame(ap);
2051 // transfer reference for existing feature settings to new alignFrame
2052 if (ap == af.alignPanel)
2054 if (viewFeatureSettings != null && viewFeatureSettings.fr.ap == ap)
2056 newaf.featureSettings = viewFeatureSettings;
2058 newaf.setFeatureSettingsGeometry(fsBounds);
2062 * Restore the view's last exploded frame geometry if known. Multiple
2063 * views from one exploded frame share and restore the same (frame)
2064 * position and size.
2066 Rectangle geometry = ap.av.getExplodedGeometry();
2067 if (geometry != null)
2069 newaf.setBounds(geometry);
2072 ap.av.setGatherViewsHere(false);
2074 addInternalFrame(newaf, af.getTitle(), AlignFrame.DEFAULT_WIDTH,
2075 AlignFrame.DEFAULT_HEIGHT);
2076 // and materialise a new feature settings dialog instance for the new
2078 // (closes the old as if 'OK' was pressed)
2079 if (ap == af.alignPanel && newaf.featureSettings != null
2080 && newaf.featureSettings.isOpen()
2081 && af.alignPanel.getAlignViewport().isShowSequenceFeatures())
2083 newaf.showFeatureSettingsUI();
2087 af.featureSettings = null;
2088 af.alignPanels.clear();
2089 af.closeMenuItem_actionPerformed(true);
2094 * Gather expanded views (separate AlignFrame's) with the same sequence set
2095 * identifier back in to this frame as additional views, and close the
2096 * expanded views. Note the expanded frames may themselves have multiple
2097 * views. We take the lot.
2101 public void gatherViews(AlignFrame source)
2103 source.viewport.setGatherViewsHere(true);
2104 source.viewport.setExplodedGeometry(source.getBounds());
2105 JInternalFrame[] frames = desktopPane.getAllFrames();
2106 String viewId = source.viewport.getSequenceSetId();
2107 for (int t = 0; t < frames.length; t++)
2109 if (frames[t] instanceof AlignFrame && frames[t] != source)
2111 AlignFrame af = (AlignFrame) frames[t];
2112 boolean gatherThis = false;
2113 for (int a = 0; a < af.alignPanels.size(); a++)
2115 AlignmentPanel ap = af.alignPanels.get(a);
2116 if (viewId.equals(ap.av.getSequenceSetId()))
2119 ap.av.setGatherViewsHere(false);
2120 ap.av.setExplodedGeometry(af.getBounds());
2121 source.addAlignmentPanel(ap, false);
2127 if (af.featureSettings != null && af.featureSettings.isOpen())
2129 if (source.featureSettings == null)
2131 // preserve the feature settings geometry for this frame
2132 source.featureSettings = af.featureSettings;
2133 source.setFeatureSettingsGeometry(
2134 af.getFeatureSettingsGeometry());
2138 // close it and forget
2139 af.featureSettings.close();
2142 af.alignPanels.clear();
2143 af.closeMenuItem_actionPerformed(true);
2148 // refresh the feature setting UI for the source frame if it exists
2149 if (source.featureSettings != null && source.featureSettings.isOpen())
2151 source.showFeatureSettingsUI();
2155 public JInternalFrame[] getAllFrames()
2157 return desktopPane.getAllFrames();
2161 * Checks the given url to see if it gives a response indicating that the user
2162 * should be informed of a new questionnaire.
2166 public void checkForQuestionnaire(String url)
2168 UserQuestionnaireCheck jvq = new UserQuestionnaireCheck(url);
2169 // javax.swing.SwingUtilities.invokeLater(jvq);
2170 new Thread(jvq).start();
2173 public void checkURLLinks()
2175 // Thread off the URL link checker
2176 addDialogThread(new Runnable()
2181 if (Cache.getDefault("CHECKURLLINKS", true))
2183 // check what the actual links are - if it's just the default don't
2184 // bother with the warning
2185 List<String> links = Preferences.sequenceUrlLinks
2188 // only need to check links if there is one with a
2189 // SEQUENCE_ID which is not the default EMBL_EBI link
2190 ListIterator<String> li = links.listIterator();
2191 boolean check = false;
2192 List<JLabel> urls = new ArrayList<>();
2193 while (li.hasNext())
2195 String link = li.next();
2196 if (link.contains(jalview.util.UrlConstants.SEQUENCE_ID)
2197 && !UrlConstants.isDefaultString(link))
2200 int barPos = link.indexOf("|");
2201 String urlMsg = barPos == -1 ? link
2202 : link.substring(0, barPos) + ": "
2203 + link.substring(barPos + 1);
2204 urls.add(new JLabel(urlMsg));
2212 // ask user to check in case URL links use old style tokens
2213 // ($SEQUENCE_ID$ for sequence id _or_ accession id)
2214 JPanel msgPanel = new JPanel();
2215 msgPanel.setLayout(new BoxLayout(msgPanel, BoxLayout.PAGE_AXIS));
2216 msgPanel.add(Box.createVerticalGlue());
2217 JLabel msg = new JLabel(MessageManager
2218 .getString("label.SEQUENCE_ID_for_DB_ACCESSION1"));
2219 JLabel msg2 = new JLabel(MessageManager
2220 .getString("label.SEQUENCE_ID_for_DB_ACCESSION2"));
2222 for (JLabel url : urls)
2228 final JCheckBox jcb = new JCheckBox(
2229 MessageManager.getString("label.do_not_display_again"));
2230 jcb.addActionListener(new ActionListener()
2233 public void actionPerformed(ActionEvent e)
2235 // update Cache settings for "don't show this again"
2236 boolean showWarningAgain = !jcb.isSelected();
2237 Cache.setProperty("CHECKURLLINKS",
2238 Boolean.valueOf(showWarningAgain).toString());
2243 JvOptionPane.showMessageDialog(desktopPane, msgPanel,
2245 .getString("label.SEQUENCE_ID_no_longer_used"),
2246 JvOptionPane.WARNING_MESSAGE);
2253 * Proxy class for JDesktopPane which optionally displays the current memory
2254 * usage and highlights the desktop area with a red bar if free memory runs
2259 public class MyDesktopPane extends JDesktopPane implements Runnable
2261 private static final float ONE_MB = 1048576f;
2263 boolean showMemoryUsage = false;
2267 java.text.NumberFormat df;
2269 float maxMemory, allocatedMemory, freeMemory, totalFreeMemory,
2272 public MyDesktopPane(boolean showMemoryUsage)
2274 showMemoryUsage(showMemoryUsage);
2277 public void showMemoryUsage(boolean showMemory)
2279 this.showMemoryUsage = showMemory;
2282 Thread worker = new Thread(this);
2288 public boolean isShowMemoryUsage()
2290 return showMemoryUsage;
2296 df = java.text.NumberFormat.getNumberInstance();
2297 df.setMaximumFractionDigits(2);
2298 runtime = Runtime.getRuntime();
2300 while (showMemoryUsage)
2304 maxMemory = runtime.maxMemory() / ONE_MB;
2305 allocatedMemory = runtime.totalMemory() / ONE_MB;
2306 freeMemory = runtime.freeMemory() / ONE_MB;
2307 totalFreeMemory = freeMemory + (maxMemory - allocatedMemory);
2309 percentUsage = (totalFreeMemory / maxMemory) * 100;
2311 // if (percentUsage < 20)
2313 // border1 = BorderFactory.createMatteBorder(12, 12, 12, 12,
2315 // instance.set.setBorder(border1);
2318 // sleep after showing usage
2320 } catch (Exception ex)
2322 ex.printStackTrace();
2328 public void paintComponent(Graphics g)
2330 if (showMemoryUsage && g != null && df != null)
2332 if (percentUsage < 20)
2334 g.setColor(Color.red);
2336 FontMetrics fm = g.getFontMetrics();
2339 g.drawString(MessageManager.formatMessage("label.memory_stats",
2341 { df.format(totalFreeMemory), df.format(maxMemory),
2342 df.format(percentUsage) }),
2343 10, getHeight() - fm.getHeight());
2350 * Accessor method to quickly get all the AlignmentFrames loaded.
2352 * @return an array of AlignFrame, or null if none found
2354 public static AlignFrame[] getAlignFrames()
2356 if (Jalview.isHeadlessMode())
2358 return new AlignFrame[] { Jalview.getInstance().currentAlignFrame };
2361 JInternalFrame[] frames = getDesktopPane().getAllFrames();
2367 List<AlignFrame> avp = new ArrayList<>();
2369 for (int i = frames.length - 1; i > -1; i--)
2371 if (frames[i] instanceof AlignFrame)
2373 avp.add((AlignFrame) frames[i]);
2375 else if (frames[i] instanceof SplitFrame)
2378 * Also check for a split frame containing an AlignFrame
2380 GSplitFrame sf = (GSplitFrame) frames[i];
2381 if (sf.getTopFrame() instanceof AlignFrame)
2383 avp.add((AlignFrame) sf.getTopFrame());
2385 if (sf.getBottomFrame() instanceof AlignFrame)
2387 avp.add((AlignFrame) sf.getBottomFrame());
2391 if (avp.size() == 0)
2395 AlignFrame afs[] = avp.toArray(new AlignFrame[avp.size()]);
2400 * Returns an array of any AppJmol frames in the Desktop (or null if none).
2404 public GStructureViewer[] getJmols()
2406 JInternalFrame[] frames = desktopPane.getAllFrames();
2412 List<GStructureViewer> avp = new ArrayList<>();
2414 for (int i = frames.length - 1; i > -1; i--)
2416 if (frames[i] instanceof AppJmol)
2418 GStructureViewer af = (GStructureViewer) frames[i];
2422 if (avp.size() == 0)
2426 GStructureViewer afs[] = avp.toArray(new GStructureViewer[avp.size()]);
2431 * Add Groovy Support to Jalview
2434 public void groovyShell_actionPerformed()
2438 openGroovyConsole();
2439 } catch (Exception ex)
2441 Cache.log.error("Groovy Shell Creation failed.", ex);
2442 JvOptionPane.showInternalMessageDialog(desktopPane,
2444 MessageManager.getString("label.couldnt_create_groovy_shell"),
2445 MessageManager.getString("label.groovy_support_failed"),
2446 JvOptionPane.ERROR_MESSAGE);
2451 * Open the Groovy console
2453 void openGroovyConsole()
2455 if (groovyConsole == null)
2457 groovyConsole = new groovy.ui.Console();
2458 groovyConsole.setVariable("Jalview", this);
2459 groovyConsole.run();
2462 * We allow only one console at a time, so that AlignFrame menu option
2463 * 'Calculate | Run Groovy script' is unambiguous.
2464 * Disable 'Groovy Console', and enable 'Run script', when the console is
2465 * opened, and the reverse when it is closed
2467 Window window = (Window) groovyConsole.getFrame();
2468 window.addWindowListener(new WindowAdapter()
2471 public void windowClosed(WindowEvent e)
2474 * rebind CMD-Q from Groovy Console to Jalview Quit
2477 enableExecuteGroovy(false);
2483 * show Groovy console window (after close and reopen)
2485 ((Window) groovyConsole.getFrame()).setVisible(true);
2488 * if we got this far, enable 'Run Groovy' in AlignFrame menus
2489 * and disable opening a second console
2491 enableExecuteGroovy(true);
2495 * Bind Ctrl/Cmd-Q to Quit - for reset as Groovy Console takes over this
2496 * binding when opened
2498 protected void addQuitHandler()
2501 getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
2502 .put(KeyStroke.getKeyStroke(KeyEvent.VK_Q,
2503 Platform.SHORTCUT_KEY_MASK),
2505 getRootPane().getActionMap().put("Quit", new AbstractAction()
2508 public void actionPerformed(ActionEvent e)
2516 * Enable or disable 'Run Groovy script' in AlignFrame calculate menus
2519 * true if Groovy console is open
2521 public void enableExecuteGroovy(boolean enabled)
2524 * disable opening a second Groovy console
2525 * (or re-enable when the console is closed)
2527 groovyShell.setEnabled(!enabled);
2529 AlignFrame[] alignFrames = getAlignFrames();
2530 if (alignFrames != null)
2532 for (AlignFrame af : alignFrames)
2534 af.setGroovyEnabled(enabled);
2540 * Progress bars managed by the IProgressIndicator method.
2542 private Hashtable<Long, JPanel> progressBars;
2544 private Hashtable<Long, IProgressIndicatorHandler> progressBarHandlers;
2549 * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
2552 public void setProgressBar(String message, long id)
2554 // Platform.timeCheck("Desktop " + message, Platform.TIME_MARK);
2556 if (progressBars == null)
2558 progressBars = new Hashtable<>();
2559 progressBarHandlers = new Hashtable<>();
2562 if (progressBars.get(Long.valueOf(id)) != null)
2564 JPanel panel = progressBars.remove(Long.valueOf(id));
2565 if (progressBarHandlers.contains(Long.valueOf(id)))
2567 progressBarHandlers.remove(Long.valueOf(id));
2569 removeProgressPanel(panel);
2573 progressBars.put(Long.valueOf(id), addProgressPanel(message));
2580 * @see jalview.gui.IProgressIndicator#registerHandler(long,
2581 * jalview.gui.IProgressIndicatorHandler)
2584 public void registerHandler(final long id,
2585 final IProgressIndicatorHandler handler)
2587 if (progressBarHandlers == null
2588 || !progressBars.containsKey(Long.valueOf(id)))
2590 throw new Error(MessageManager.getString(
2591 "error.call_setprogressbar_before_registering_handler"));
2593 progressBarHandlers.put(Long.valueOf(id), handler);
2594 final JPanel progressPanel = progressBars.get(Long.valueOf(id));
2595 if (handler.canCancel())
2597 JButton cancel = new JButton(
2598 MessageManager.getString("action.cancel"));
2599 final IProgressIndicator us = this;
2600 cancel.addActionListener(new ActionListener()
2604 public void actionPerformed(ActionEvent e)
2606 handler.cancelActivity(id);
2607 us.setProgressBar(MessageManager
2608 .formatMessage("label.cancelled_params", new Object[]
2609 { ((JLabel) progressPanel.getComponent(0)).getText() }),
2613 progressPanel.add(cancel, BorderLayout.EAST);
2619 * @return true if any progress bars are still active
2622 public boolean operationInProgress()
2624 if (progressBars != null && progressBars.size() > 0)
2632 * This will return the first AlignFrame holding the given viewport instance.
2633 * It will break if there are more than one AlignFrames viewing a particular
2637 * @return alignFrame for viewport
2639 public static AlignFrame getAlignFrameFor(AlignViewportI viewport)
2641 if (getDesktopPane() != null)
2643 AlignmentPanel[] aps = getAlignmentPanels(
2644 viewport.getSequenceSetId());
2645 for (int panel = 0; aps != null && panel < aps.length; panel++)
2647 if (aps[panel] != null && aps[panel].av == viewport)
2649 return aps[panel].alignFrame;
2656 public VamsasApplication getVamsasApplication()
2658 // TODO: JAL-3311 remove remaining code from Jalview relating to VAMSAS
2664 * flag set if jalview GUI is being operated programmatically
2666 private boolean inBatchMode = false;
2669 * check if jalview GUI is being operated programmatically
2671 * @return inBatchMode
2673 public boolean isInBatchMode()
2679 * set flag if jalview GUI is being operated programmatically
2681 * @param inBatchMode
2683 public void setInBatchMode(boolean inBatchMode)
2685 this.inBatchMode = inBatchMode;
2688 public void startServiceDiscovery()
2690 startServiceDiscovery(false);
2693 public void startServiceDiscovery(boolean blocking)
2695 boolean alive = true;
2696 Thread t0 = null, t1 = null, t2 = null;
2697 // JAL-940 - JALVIEW 1 services are now being EOLed as of JABA 2.1 release
2700 // todo: changesupport handlers need to be transferred
2701 if (discoverer == null)
2703 discoverer = jalview.ws.jws1.Discoverer.getInstance();
2704 // register PCS handler for desktop.
2705 discoverer.addPropertyChangeListener(changeSupport);
2707 // JAL-940 - disabled JWS1 service configuration - always start discoverer
2708 // until we phase out completely
2709 (t0 = new Thread(discoverer)).start();
2712 if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
2714 t2 = jalview.ws.jws2.Jws2Discoverer.getInstance()
2715 .startDiscoverer(changeSupport);
2719 // TODO: do rest service discovery
2728 } catch (Exception e)
2731 alive = (t1 != null && t1.isAlive()) || (t2 != null && t2.isAlive())
2732 || (t3 != null && t3.isAlive())
2733 || (t0 != null && t0.isAlive());
2739 * called to check if the service discovery process completed successfully.
2743 protected void JalviewServicesChanged(PropertyChangeEvent evt)
2745 if (evt.getNewValue() == null || evt.getNewValue() instanceof Vector)
2747 final String ermsg = jalview.ws.jws2.Jws2Discoverer.getInstance()
2748 .getErrorMessages();
2751 if (Cache.getDefault("SHOW_WSDISCOVERY_ERRORS", true))
2753 if (serviceChangedDialog == null)
2755 // only run if we aren't already displaying one of these.
2756 addDialogThread(serviceChangedDialog = new Runnable()
2763 * JalviewDialog jd =new JalviewDialog() {
2765 * @Override protected void cancelPressed() { // TODO
2766 * Auto-generated method stub
2768 * }@Override protected void okPressed() { // TODO
2769 * Auto-generated method stub
2771 * }@Override protected void raiseClosed() { // TODO
2772 * Auto-generated method stub
2774 * } }; jd.initDialogFrame(new
2775 * JLabel("<html><table width=\"450\"><tr><td>" + ermsg +
2776 * "<br/>It may be that you have invalid JABA URLs in your web service preferences,"
2777 * + " or mis-configured HTTP proxy settings.<br/>" +
2778 * "Check the <em>Connections</em> and <em>Web services</em> tab of the"
2780 * " Tools->Preferences dialog box to change them.</td></tr></table></html>"
2781 * ), true, true, "Web Service Configuration Problem", 450,
2784 * jd.waitForInput();
2786 JvOptionPane.showConfirmDialog(desktopPane,
2787 new JLabel("<html><table width=\"450\"><tr><td>"
2788 + ermsg + "</td></tr></table>"
2789 + "<p>It may be that you have invalid JABA URLs<br/>in your web service preferences,"
2790 + "<br>or as a command-line argument, or mis-configured HTTP proxy settings.</p>"
2791 + "<p>Check the <em>Connections</em> and <em>Web services</em> tab<br/>of the"
2792 + " Tools->Preferences dialog box to change them.</p></html>"),
2793 "Web Service Configuration Problem",
2794 JvOptionPane.DEFAULT_OPTION,
2795 JvOptionPane.ERROR_MESSAGE);
2796 serviceChangedDialog = null;
2805 "Errors reported by JABA discovery service. Check web services preferences.\n"
2812 private Runnable serviceChangedDialog = null;
2815 * start a thread to open a URL in the configured browser. Pops up a warning
2816 * dialog to the user if there is an exception when calling out to the browser
2821 public static void showUrl(final String url)
2823 showUrl(url, getInstance());
2827 * Like showUrl but allows progress handler to be specified
2831 * (null) or object implementing IProgressIndicator
2833 public static void showUrl(final String url,
2834 final IProgressIndicator progress)
2836 new Thread(new Runnable()
2843 if (progress != null)
2845 progress.setProgressBar(MessageManager
2846 .formatMessage("status.opening_params", new Object[]
2847 { url }), this.hashCode());
2849 jalview.util.BrowserLauncher.openURL(url);
2850 } catch (Exception ex)
2852 JvOptionPane.showInternalMessageDialog(getDesktopPane(),
2854 .getString("label.web_browser_not_found_unix"),
2855 MessageManager.getString("label.web_browser_not_found"),
2856 JvOptionPane.WARNING_MESSAGE);
2858 ex.printStackTrace();
2860 if (progress != null)
2862 progress.setProgressBar(null, this.hashCode());
2868 public static WsParamSetManager wsparamManager = null;
2870 public static ParamManager getUserParameterStore()
2872 if (wsparamManager == null)
2874 wsparamManager = new WsParamSetManager();
2876 return wsparamManager;
2880 * static hyperlink handler proxy method for use by Jalview's internal windows
2884 public static void hyperlinkUpdate(HyperlinkEvent e)
2886 if (e.getEventType() == EventType.ACTIVATED)
2891 url = e.getURL().toString();
2893 } catch (Exception x)
2897 if (Cache.log != null)
2899 Cache.log.error("Couldn't handle string " + url + " as a URL.");
2904 "Couldn't handle string " + url + " as a URL.");
2907 // ignore any exceptions due to dud links.
2914 * single thread that handles display of dialogs to user.
2916 ExecutorService dialogExecutor = Executors.newSingleThreadExecutor();
2919 * flag indicating if dialogExecutor should try to acquire a permit
2921 private volatile boolean dialogPause = true;
2926 private java.util.concurrent.Semaphore block = new Semaphore(0);
2928 private static groovy.ui.Console groovyConsole;
2931 * add another dialog thread to the queue
2935 public void addDialogThread(final Runnable prompter)
2937 dialogExecutor.submit(new Runnable()
2947 } catch (InterruptedException x)
2951 if (Jalview.isHeadlessMode())
2957 SwingUtilities.invokeAndWait(prompter);
2958 } catch (Exception q)
2960 Cache.log.warn("Unexpected Exception in dialog thread.", q);
2966 public void startDialogQueue()
2968 // set the flag so we don't pause waiting for another permit and semaphore
2969 // the current task to begin
2970 dialogPause = false;
2975 * Outputs an image of the desktop to file in EPS format, after prompting the
2976 * user for choice of Text or Lineart character rendering (unless a preference
2977 * has been set). The file name is generated as
2980 * Jalview_snapshot_nnnnn.eps where nnnnn is the current timestamp in milliseconds
2984 protected void snapShotWindow_actionPerformed(ActionEvent e)
2986 // currently the menu option to do this is not shown
2989 int width = getWidth();
2990 int height = getHeight();
2992 "Jalview_snapshot_" + System.currentTimeMillis() + ".eps");
2993 ImageWriterI writer = new ImageWriterI()
2996 public void exportImage(Graphics g) throws Exception
2999 Cache.log.info("Successfully written snapshot to file "
3000 + of.getAbsolutePath());
3003 String title = "View of desktop";
3004 ImageExporter exporter = new ImageExporter(writer, null, TYPE.EPS,
3006 exporter.doExport(of, this, width, height, title);
3010 * Explode the views in the given SplitFrame into separate SplitFrame windows.
3011 * This respects (remembers) any previous 'exploded geometry' i.e. the size
3012 * and location last time the view was expanded (if any). However it does not
3013 * remember the split pane divider location - this is set to match the
3014 * 'exploding' frame.
3018 public void explodeViews(SplitFrame sf)
3020 AlignFrame oldTopFrame = (AlignFrame) sf.getTopFrame();
3021 AlignFrame oldBottomFrame = (AlignFrame) sf.getBottomFrame();
3022 List<? extends AlignmentViewPanel> topPanels = oldTopFrame
3024 List<? extends AlignmentViewPanel> bottomPanels = oldBottomFrame
3026 int viewCount = topPanels.size();
3033 * Processing in reverse order works, forwards order leaves the first panels
3034 * not visible. I don't know why!
3036 for (int i = viewCount - 1; i >= 0; i--)
3039 * Make new top and bottom frames. These take over the respective
3040 * AlignmentPanel objects, including their AlignmentViewports, so the
3041 * cdna/protein relationships between the viewports is carried over to the
3044 * explodedGeometry holds the (x, y) position of the previously exploded
3045 * SplitFrame, and the (width, height) of the AlignFrame component
3047 AlignmentPanel topPanel = (AlignmentPanel) topPanels.get(i);
3048 AlignFrame newTopFrame = new AlignFrame(topPanel);
3049 newTopFrame.setSize(oldTopFrame.getSize());
3050 newTopFrame.setVisible(true);
3051 Rectangle geometry = ((AlignViewport) topPanel.getAlignViewport())
3052 .getExplodedGeometry();
3053 if (geometry != null)
3055 newTopFrame.setSize(geometry.getSize());
3058 AlignmentPanel bottomPanel = (AlignmentPanel) bottomPanels.get(i);
3059 AlignFrame newBottomFrame = new AlignFrame(bottomPanel);
3060 newBottomFrame.setSize(oldBottomFrame.getSize());
3061 newBottomFrame.setVisible(true);
3062 geometry = ((AlignViewport) bottomPanel.getAlignViewport())
3063 .getExplodedGeometry();
3064 if (geometry != null)
3066 newBottomFrame.setSize(geometry.getSize());
3069 topPanel.av.setGatherViewsHere(false);
3070 bottomPanel.av.setGatherViewsHere(false);
3071 JInternalFrame splitFrame = new SplitFrame(newTopFrame,
3073 if (geometry != null)
3075 splitFrame.setLocation(geometry.getLocation());
3077 addInternalFrame(splitFrame, sf.getTitle(), -1, -1);
3081 * Clear references to the panels (now relocated in the new SplitFrames)
3082 * before closing the old SplitFrame.
3085 bottomPanels.clear();
3090 * Gather expanded split frames, sharing the same pairs of sequence set ids,
3091 * back into the given SplitFrame as additional views. Note that the gathered
3092 * frames may themselves have multiple views.
3096 public void gatherViews(GSplitFrame source)
3099 * special handling of explodedGeometry for a view within a SplitFrame: - it
3100 * holds the (x, y) position of the enclosing SplitFrame, and the (width,
3101 * height) of the AlignFrame component
3103 AlignFrame myTopFrame = (AlignFrame) source.getTopFrame();
3104 AlignFrame myBottomFrame = (AlignFrame) source.getBottomFrame();
3105 myTopFrame.viewport.setExplodedGeometry(new Rectangle(source.getX(),
3106 source.getY(), myTopFrame.getWidth(), myTopFrame.getHeight()));
3107 myBottomFrame.viewport
3108 .setExplodedGeometry(new Rectangle(source.getX(), source.getY(),
3109 myBottomFrame.getWidth(), myBottomFrame.getHeight()));
3110 myTopFrame.viewport.setGatherViewsHere(true);
3111 myBottomFrame.viewport.setGatherViewsHere(true);
3112 String topViewId = myTopFrame.viewport.getSequenceSetId();
3113 String bottomViewId = myBottomFrame.viewport.getSequenceSetId();
3115 JInternalFrame[] frames = desktopPane.getAllFrames();
3116 for (JInternalFrame frame : frames)
3118 if (frame instanceof SplitFrame && frame != source)
3120 SplitFrame sf = (SplitFrame) frame;
3121 AlignFrame topFrame = (AlignFrame) sf.getTopFrame();
3122 AlignFrame bottomFrame = (AlignFrame) sf.getBottomFrame();
3123 boolean gatherThis = false;
3124 for (int a = 0; a < topFrame.alignPanels.size(); a++)
3126 AlignmentPanel topPanel = topFrame.alignPanels.get(a);
3127 AlignmentPanel bottomPanel = bottomFrame.alignPanels.get(a);
3128 if (topViewId.equals(topPanel.av.getSequenceSetId())
3129 && bottomViewId.equals(bottomPanel.av.getSequenceSetId()))
3132 topPanel.av.setGatherViewsHere(false);
3133 bottomPanel.av.setGatherViewsHere(false);
3134 topPanel.av.setExplodedGeometry(
3135 new Rectangle(sf.getLocation(), topFrame.getSize()));
3136 bottomPanel.av.setExplodedGeometry(
3137 new Rectangle(sf.getLocation(), bottomFrame.getSize()));
3138 myTopFrame.addAlignmentPanel(topPanel, false);
3139 myBottomFrame.addAlignmentPanel(bottomPanel, false);
3145 topFrame.getAlignPanels().clear();
3146 bottomFrame.getAlignPanels().clear();
3153 * The dust settles...give focus to the tab we did this from.
3155 myTopFrame.setDisplayedView(myTopFrame.alignPanel);
3158 public static groovy.ui.Console getGroovyConsole()
3160 return groovyConsole;
3164 * handles the payload of a drag and drop event.
3166 * TODO refactor to desktop utilities class
3169 * - Data source strings extracted from the drop event
3171 * - protocol for each data source extracted from the drop event
3175 * - the payload from the drop event
3178 @SuppressWarnings("unchecked")
3179 public static void transferFromDropTarget(List<Object> files,
3180 List<DataSourceType> protocols, DropTargetDropEvent evt,
3181 Transferable t) throws Exception
3184 // BH 2018 changed List<String> to List<Object> to allow for File from
3187 // DataFlavor[] flavors = t.getTransferDataFlavors();
3188 // for (int i = 0; i < flavors.length; i++) {
3189 // if (flavors[i].isFlavorJavaFileListType()) {
3190 // evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
3191 // List<File> list = (List<File>) t.getTransferData(flavors[i]);
3192 // for (int j = 0; j < list.size(); j++) {
3193 // File file = (File) list.get(j);
3194 // byte[] data = getDroppedFileBytes(file);
3195 // fileName.setText(file.getName() + " - " + data.length + " " +
3196 // evt.getLocation());
3197 // JTextArea target = (JTextArea) ((DropTarget)
3198 // evt.getSource()).getComponent();
3199 // target.setText(new String(data));
3201 // dtde.dropComplete(true);
3206 DataFlavor uriListFlavor = new DataFlavor(
3207 "text/uri-list;class=java.lang.String"), urlFlavour = null;
3210 urlFlavour = new DataFlavor(
3211 "application/x-java-url; class=java.net.URL");
3212 } catch (ClassNotFoundException cfe)
3214 Cache.log.debug("Couldn't instantiate the URL dataflavor.", cfe);
3217 if (urlFlavour != null && t.isDataFlavorSupported(urlFlavour))
3222 java.net.URL url = (URL) t.getTransferData(urlFlavour);
3223 // nb: java 8 osx bug https://bugs.openjdk.java.net/browse/JDK-8156099
3224 // means url may be null.
3227 protocols.add(DataSourceType.URL);
3228 files.add(url.toString());
3229 Cache.log.debug("Drop handled as URL dataflavor "
3230 + files.get(files.size() - 1));
3235 if (Platform.isAMacAndNotJS())
3238 "Please ignore plist error - occurs due to problem with java 8 on OSX");
3241 } catch (Throwable ex)
3243 Cache.log.debug("URL drop handler failed.", ex);
3246 if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
3248 // Works on Windows and MacOSX
3249 Cache.log.debug("Drop handled as javaFileListFlavor");
3250 for (File file : (List<File>) t
3251 .getTransferData(DataFlavor.javaFileListFlavor))
3254 protocols.add(DataSourceType.FILE);
3259 // Unix like behaviour
3260 boolean added = false;
3262 if (t.isDataFlavorSupported(uriListFlavor))
3264 Cache.log.debug("Drop handled as uriListFlavor");
3265 // This is used by Unix drag system
3266 data = (String) t.getTransferData(uriListFlavor);
3270 // fallback to text: workaround - on OSX where there's a JVM bug
3271 Cache.log.debug("standard URIListFlavor failed. Trying text");
3272 // try text fallback
3273 DataFlavor textDf = new DataFlavor(
3274 "text/plain;class=java.lang.String");
3275 if (t.isDataFlavorSupported(textDf))
3277 data = (String) t.getTransferData(textDf);
3280 Cache.log.debug("Plain text drop content returned "
3281 + (data == null ? "Null - failed" : data));
3286 while (protocols.size() < files.size())
3288 Cache.log.debug("Adding missing FILE protocol for "
3289 + files.get(protocols.size()));
3290 protocols.add(DataSourceType.FILE);
3292 for (java.util.StringTokenizer st = new java.util.StringTokenizer(
3293 data, "\r\n"); st.hasMoreTokens();)
3296 String s = st.nextToken();
3297 if (s.startsWith("#"))
3299 // the line is a comment (as per the RFC 2483)
3302 java.net.URI uri = new java.net.URI(s);
3303 if (uri.getScheme().toLowerCase().startsWith("http"))
3305 protocols.add(DataSourceType.URL);
3306 files.add(uri.toString());
3310 // otherwise preserve old behaviour: catch all for file objects
3311 java.io.File file = new java.io.File(uri);
3312 protocols.add(DataSourceType.FILE);
3313 files.add(file.toString());
3318 if (Cache.log.isDebugEnabled())
3320 if (data == null || !added)
3323 if (t.getTransferDataFlavors() != null
3324 && t.getTransferDataFlavors().length > 0)
3327 "Couldn't resolve drop data. Here are the supported flavors:");
3328 for (DataFlavor fl : t.getTransferDataFlavors())
3331 "Supported transfer dataflavor: " + fl.toString());
3332 Object df = t.getTransferData(fl);
3335 Cache.log.debug("Retrieves: " + df);
3339 Cache.log.debug("Retrieved nothing");
3345 Cache.log.debug("Couldn't resolve dataflavor for drop: "
3351 if (Platform.isWindowsAndNotJS())
3353 Cache.log.debug("Scanning dropped content for Windows Link Files");
3355 // resolve any .lnk files in the file drop
3356 for (int f = 0; f < files.size(); f++)
3358 String source = files.get(f).toString().toLowerCase();
3359 if (protocols.get(f).equals(DataSourceType.FILE)
3360 && (source.endsWith(".lnk") || source.endsWith(".url")
3361 || source.endsWith(".site")))
3365 Object obj = files.get(f);
3366 File lf = (obj instanceof File ? (File) obj
3367 : new File((String) obj));
3368 // process link file to get a URL
3369 Cache.log.debug("Found potential link file: " + lf);
3370 WindowsShortcut wscfile = new WindowsShortcut(lf);
3371 String fullname = wscfile.getRealFilename();
3372 protocols.set(f, FormatAdapter.checkProtocol(fullname));
3373 files.set(f, fullname);
3374 Cache.log.debug("Parsed real filename " + fullname
3375 + " to extract protocol: " + protocols.get(f));
3376 } catch (Exception ex)
3379 "Couldn't parse " + files.get(f) + " as a link file.",
3388 * Sets the Preferences property for experimental features to True or False
3389 * depending on the state of the controlling menu item
3392 protected void showExperimental_actionPerformed(boolean selected)
3394 Cache.setProperty(EXPERIMENTAL_FEATURES, Boolean.toString(selected));
3398 * Answers a (possibly empty) list of any structure viewer frames (currently
3399 * for either Jmol or Chimera) which are currently open. This may optionally
3400 * be restricted to viewers of a specified class, or viewers linked to a
3401 * specified alignment panel.
3404 * if not null, only return viewers linked to this panel
3405 * @param structureViewerClass
3406 * if not null, only return viewers of this class
3409 public List<StructureViewerBase> getStructureViewers(
3410 AlignmentPanel apanel,
3411 Class<? extends StructureViewerBase> structureViewerClass)
3413 List<StructureViewerBase> result = new ArrayList<>();
3414 JInternalFrame[] frames = getAllFrames();
3416 for (JInternalFrame frame : frames)
3418 if (frame instanceof StructureViewerBase)
3420 if (structureViewerClass == null
3421 || structureViewerClass.isInstance(frame))
3424 || ((StructureViewerBase) frame).isLinkedWith(apanel))
3426 result.add((StructureViewerBase) frame);