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 static int openFrameCount = 0;
240 static final int xOffset = 30;
242 static final int yOffset = 30;
244 public static jalview.ws.jws1.Discoverer discoverer;
246 public static Object[] jalviewClipboard;
248 public static boolean internalCopy = false;
250 static int fileLoadingCount = 0;
252 class MyDesktopManager implements DesktopManager
255 private DesktopManager delegate;
257 public MyDesktopManager(DesktopManager delegate)
259 this.delegate = delegate;
263 public void activateFrame(JInternalFrame f)
267 delegate.activateFrame(f);
268 } catch (NullPointerException npe)
270 Point p = getMousePosition();
271 showPasteMenu(p.x, p.y);
276 public void beginDraggingFrame(JComponent f)
278 delegate.beginDraggingFrame(f);
282 public void beginResizingFrame(JComponent f, int direction)
284 delegate.beginResizingFrame(f, direction);
288 public void closeFrame(JInternalFrame f)
290 delegate.closeFrame(f);
294 public void deactivateFrame(JInternalFrame f)
296 delegate.deactivateFrame(f);
300 public void deiconifyFrame(JInternalFrame f)
302 delegate.deiconifyFrame(f);
306 public void dragFrame(JComponent f, int newX, int newY)
312 delegate.dragFrame(f, newX, newY);
316 public void endDraggingFrame(JComponent f)
318 delegate.endDraggingFrame(f);
319 desktopPane.repaint();
323 public void endResizingFrame(JComponent f)
325 delegate.endResizingFrame(f);
326 desktopPane.repaint();
330 public void iconifyFrame(JInternalFrame f)
332 delegate.iconifyFrame(f);
336 public void maximizeFrame(JInternalFrame f)
338 delegate.maximizeFrame(f);
342 public void minimizeFrame(JInternalFrame f)
344 delegate.minimizeFrame(f);
348 public void openFrame(JInternalFrame f)
350 delegate.openFrame(f);
354 public void resizeFrame(JComponent f, int newX, int newY, int newWidth,
361 delegate.resizeFrame(f, newX, newY, newWidth, newHeight);
365 public void setBoundsForFrame(JComponent f, int newX, int newY,
366 int newWidth, int newHeight)
368 delegate.setBoundsForFrame(f, newX, newY, newWidth, newHeight);
371 // All other methods, simply delegate
376 * Private constructor enforces singleton pattern. It is called by reflection
377 * from ApplicationSingletonProvider.getInstance().
385 * A note to implementors. It is ESSENTIAL that any activities that might
386 * block are spawned off as threads rather than waited for during this
390 doConfigureStructurePrefs();
391 setTitle("Jalview " + Cache.getProperty("VERSION"));
393 if (!Platform.isAMac())
395 // this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
399 this.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
405 APQHandlers.setAPQHandlers(this);
406 } catch (Throwable t)
408 System.out.println("Error setting APQHandlers: " + t.toString());
409 // t.printStackTrace();
412 addWindowListener(new WindowAdapter()
416 public void windowClosing(WindowEvent ev)
422 boolean selmemusage = Cache.getDefault("SHOW_MEMUSAGE", false);
424 boolean showjconsole = Cache.getDefault("SHOW_JAVA_CONSOLE", false);
425 desktopPane = new MyDesktopPane(selmemusage);
427 showMemusage.setSelected(selmemusage);
428 desktopPane.setBackground(Color.white);
430 getContentPane().setLayout(new BorderLayout());
431 // alternate config - have scrollbars - see notes in JAL-153
432 // JScrollPane sp = new JScrollPane();
433 // sp.getViewport().setView(desktop);
434 // getContentPane().add(sp, BorderLayout.CENTER);
436 // BH 2018 - just an experiment to try unclipped JInternalFrames.
439 getRootPane().putClientProperty("swingjs.overflow.hidden", "false");
442 getContentPane().add(desktopPane, BorderLayout.CENTER);
443 desktopPane.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
445 // This line prevents Windows Look&Feel resizing all new windows to
447 // if previous window was maximised
448 desktopPane.setDesktopManager(new MyDesktopManager(
449 (Platform.isWindowsAndNotJS() ? new DefaultDesktopManager()
450 : Platform.isAMacAndNotJS()
451 ? new AquaInternalFrameManager(
452 desktopPane.getDesktopManager())
453 : desktopPane.getDesktopManager())));
455 Rectangle dims = getLastKnownDimensions("");
462 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
463 int xPos = Math.max(5, (screenSize.width - 900) / 2);
464 int yPos = Math.max(5, (screenSize.height - 650) / 2);
465 setBounds(xPos, yPos, 900, 650);
468 if (!Platform.isJS())
475 jconsole = new Console(this, showjconsole);
476 jconsole.setHeader(Cache.getVersionDetailsForConsole());
477 showConsole(showjconsole);
479 showNews.setVisible(false);
481 experimentalFeatures.setSelected(showExperimental());
483 getIdentifiersOrgData();
487 // Spawn a thread that shows the splashscreen
489 SwingUtilities.invokeLater(new Runnable()
494 new SplashScreen(true);
498 // Thread off a new instance of the file chooser - this reduces the time
500 // takes to open it later on.
501 new Thread(new Runnable()
506 Cache.log.debug("Filechooser init thread started.");
507 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
508 JalviewFileChooser.forRead(Cache.getProperty("LAST_DIRECTORY"),
510 Cache.log.debug("Filechooser init thread finished.");
513 // Add the service change listener
514 changeSupport.addJalviewPropertyChangeListener("services",
515 new PropertyChangeListener()
519 public void propertyChange(PropertyChangeEvent evt)
521 Cache.log.debug("Firing service changed event for "
522 + evt.getNewValue());
523 JalviewServicesChanged(evt);
528 this.setDropTarget(new java.awt.dnd.DropTarget(desktopPane, this));
530 this.addWindowListener(new WindowAdapter()
533 public void windowClosing(WindowEvent evt)
540 this.addMouseListener(ma = new MouseAdapter()
543 public void mousePressed(MouseEvent evt)
545 if (evt.isPopupTrigger()) // Mac
547 showPasteMenu(evt.getX(), evt.getY());
552 public void mouseReleased(MouseEvent evt)
554 if (evt.isPopupTrigger()) // Windows
556 showPasteMenu(evt.getX(), evt.getY());
560 desktopPane.addMouseListener(ma);
561 } catch (Throwable t)
569 * Answers true if user preferences to enable experimental features is True
574 public boolean showExperimental()
576 String experimental = Cache.getDefault(EXPERIMENTAL_FEATURES,
577 Boolean.FALSE.toString());
578 return Boolean.valueOf(experimental).booleanValue();
581 public void doConfigureStructurePrefs()
583 // configure services
584 StructureSelectionManager ssm = StructureSelectionManager
585 .getStructureSelectionManager(this);
586 if (Cache.getDefault(Preferences.ADD_SS_ANN, true))
588 ssm.setAddTempFacAnnot(
589 Cache.getDefault(Preferences.ADD_TEMPFACT_ANN, true));
590 ssm.setProcessSecondaryStructure(
591 Cache.getDefault(Preferences.STRUCT_FROM_PDB, true));
592 ssm.setSecStructServices(
593 Cache.getDefault(Preferences.USE_RNAVIEW, true));
597 ssm.setAddTempFacAnnot(false);
598 ssm.setProcessSecondaryStructure(false);
599 ssm.setSecStructServices(false);
603 public void checkForNews()
605 final Desktop me = this;
606 // Thread off the news reader, in case there are connection problems.
607 new Thread(new Runnable()
612 Cache.log.debug("Starting news thread.");
613 jvnews = new BlogReader(me);
614 showNews.setVisible(true);
615 Cache.log.debug("Completed news thread.");
620 public void getIdentifiersOrgData()
622 // Thread off the identifiers fetcher
623 new Thread(new Runnable()
628 Cache.log.debug("Downloading data from identifiers.org");
631 UrlDownloadClient.download(IdOrgSettings.getUrl(),
632 IdOrgSettings.getDownloadLocation());
633 } catch (IOException e)
635 Cache.log.debug("Exception downloading identifiers.org data"
644 protected void showNews_actionPerformed(ActionEvent e)
646 showNews(showNews.isSelected());
649 void showNews(boolean visible)
651 Cache.log.debug((visible ? "Showing" : "Hiding") + " news.");
652 showNews.setSelected(visible);
653 if (visible && !jvnews.isVisible())
655 new Thread(new Runnable()
660 long now = System.currentTimeMillis();
662 MessageManager.getString("status.refreshing_news"), now);
663 jvnews.refreshNews();
664 setProgressBar(null, now);
672 * recover the last known dimensions for a jalview window
675 * - empty string is desktop, all other windows have unique prefix
676 * @return null or last known dimensions scaled to current geometry (if last
677 * window geom was known)
679 Rectangle getLastKnownDimensions(String windowName)
681 // TODO: lock aspect ratio for scaling desktop Bug #0058199
682 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
683 String x = Cache.getProperty(windowName + "SCREEN_X");
684 String y = Cache.getProperty(windowName + "SCREEN_Y");
685 String width = Cache.getProperty(windowName + "SCREEN_WIDTH");
686 String height = Cache.getProperty(windowName + "SCREEN_HEIGHT");
687 if ((x != null) && (y != null) && (width != null) && (height != null))
689 int ix = Integer.parseInt(x), iy = Integer.parseInt(y),
690 iw = Integer.parseInt(width), ih = Integer.parseInt(height);
691 if (Cache.getProperty("SCREENGEOMETRY_WIDTH") != null)
693 // attempt #1 - try to cope with change in screen geometry - this
694 // version doesn't preserve original jv aspect ratio.
695 // take ratio of current screen size vs original screen size.
696 double sw = ((1f * screenSize.width) / (1f * Integer
697 .parseInt(Cache.getProperty("SCREENGEOMETRY_WIDTH"))));
698 double sh = ((1f * screenSize.height) / (1f * Integer
699 .parseInt(Cache.getProperty("SCREENGEOMETRY_HEIGHT"))));
700 // rescale the bounds depending upon the current screen geometry.
701 ix = (int) (ix * sw);
702 iw = (int) (iw * sw);
703 iy = (int) (iy * sh);
704 ih = (int) (ih * sh);
705 while (ix >= screenSize.width)
708 "Window geometry location recall error: shifting horizontal to within screenbounds.");
709 ix -= screenSize.width;
711 while (iy >= screenSize.height)
714 "Window geometry location recall error: shifting vertical to within screenbounds.");
715 iy -= screenSize.height;
718 "Got last known dimensions for " + windowName + ": x:" + ix
719 + " y:" + iy + " width:" + iw + " height:" + ih);
721 // return dimensions for new instance
722 return new Rectangle(ix, iy, iw, ih);
727 void showPasteMenu(int x, int y)
729 JPopupMenu popup = new JPopupMenu();
730 JMenuItem item = new JMenuItem(
731 MessageManager.getString("label.paste_new_window"));
732 item.addActionListener(new ActionListener()
735 public void actionPerformed(ActionEvent evt)
742 popup.show(this, x, y);
749 Clipboard c = Toolkit.getDefaultToolkit().getSystemClipboard();
750 Transferable contents = c.getContents(this);
752 if (contents != null)
754 String file = (String) contents
755 .getTransferData(DataFlavor.stringFlavor);
757 FileFormatI format = new IdentifyFile().identify(file,
758 DataSourceType.PASTE);
760 new FileLoader().LoadFile(file, DataSourceType.PASTE, format);
763 } catch (Exception ex)
766 "Unable to paste alignment from system clipboard:\n" + ex);
771 * Adds and opens the given frame to the desktop
782 public static synchronized void addInternalFrame(
783 final JInternalFrame frame, String title, int w, int h)
785 addInternalFrame(frame, title, true, w, h, true, false);
789 * Add an internal frame to the Jalview desktop
796 * When true, display frame immediately, otherwise, caller must call
797 * setVisible themselves.
803 public static synchronized void addInternalFrame(
804 final JInternalFrame frame, String title, boolean makeVisible,
807 addInternalFrame(frame, title, makeVisible, w, h, true, false);
811 * Add an internal frame to the Jalview desktop and make it visible
824 public static synchronized void addInternalFrame(
825 final JInternalFrame frame, String title, int w, int h,
828 addInternalFrame(frame, title, true, w, h, resizable, false);
832 * Add an internal frame to the Jalview desktop
839 * When true, display frame immediately, otherwise, caller must call
840 * setVisible themselves.
847 * @param ignoreMinSize
848 * Do not set the default minimum size for frame
850 public static synchronized void addInternalFrame(
851 final JInternalFrame frame, String title, boolean makeVisible,
852 int w, int h, boolean resizable, boolean ignoreMinSize)
857 // TODO: allow callers to determine X and Y position of frame (eg. via
859 // TODO: consider fixing method to update entries in the window submenu with
860 // the current window title
862 frame.setTitle(title);
863 if (frame.getWidth() < 1 || frame.getHeight() < 1)
867 // THIS IS A PUBLIC STATIC METHOD, SO IT MAY BE CALLED EVEN IN
868 // A HEADLESS STATE WHEN NO DESKTOP EXISTS. MUST RETURN
869 // IF JALVIEW IS RUNNING HEADLESS
870 // ///////////////////////////////////////////////
871 if (Jalview.isHeadlessMode())
880 frame.setMinimumSize(
881 new Dimension(DEFAULT_MIN_WIDTH, DEFAULT_MIN_HEIGHT));
883 // Set default dimension for Alignment Frame window.
884 // The Alignment Frame window could be added from a number of places,
886 // I did this here in order not to miss out on any Alignment frame.
887 if (frame instanceof AlignFrame)
889 frame.setMinimumSize(new Dimension(ALIGN_FRAME_DEFAULT_MIN_WIDTH,
890 ALIGN_FRAME_DEFAULT_MIN_HEIGHT));
894 frame.setVisible(makeVisible);
895 frame.setClosable(true);
896 frame.setResizable(resizable);
897 frame.setMaximizable(resizable);
898 frame.setIconifiable(resizable);
899 frame.setOpaque(Platform.isJS());
900 boolean isEmbedded = (Platform.getDimIfEmbedded(frame, -1, -1) != null);
901 if (!isEmbedded && frame.getX() < 1 && frame.getY() < 1)
903 frame.setLocation(xOffset * openFrameCount,
904 yOffset * ((openFrameCount - 1) % 10) + yOffset);
908 * add an entry for the new frame in the Window menu
909 * (and remove it when the frame is closed)
911 final JMenuItem menuItem = new JMenuItem(title);
912 frame.addInternalFrameListener(new InternalFrameAdapter()
915 public void internalFrameActivated(InternalFrameEvent evt)
917 JInternalFrame itf = getDesktopPane().getSelectedFrame();
920 if (itf instanceof AlignFrame)
922 Jalview.setCurrentAlignFrame((AlignFrame) itf);
929 public void internalFrameClosed(InternalFrameEvent evt)
931 PaintRefresher.RemoveComponent(frame);
934 * defensive check to prevent frames being
935 * added half off the window
937 if (openFrameCount > 0)
943 * ensure no reference to alignFrame retained by menu item listener
945 if (menuItem.getActionListeners().length > 0)
947 menuItem.removeActionListener(menuItem.getActionListeners()[0]);
949 getInstance().windowMenu.remove(menuItem);
953 menuItem.addActionListener(new ActionListener()
956 public void actionPerformed(ActionEvent e)
960 frame.setSelected(true);
961 frame.setIcon(false);
962 } catch (java.beans.PropertyVetoException ex)
964 // System.err.println(ex.toString());
969 setKeyBindings(frame);
971 getDesktopPane().add(frame);
973 getInstance().windowMenu.add(menuItem);
978 frame.setSelected(true);
979 frame.requestFocus();
980 } catch (java.beans.PropertyVetoException ve)
982 } catch (java.lang.ClassCastException cex)
985 "Squashed a possible GUI implementation error. If you can recreate this, please look at http://issues.jalview.org/browse/JAL-869",
991 * Add key bindings to a JInternalFrame so that Ctrl-W and Cmd-W will close
996 private static void setKeyBindings(JInternalFrame frame)
998 @SuppressWarnings("serial")
999 final Action closeAction = new AbstractAction()
1002 public void actionPerformed(ActionEvent e)
1009 * set up key bindings for Ctrl-W and Cmd-W, with the same (Close) action
1011 KeyStroke ctrlWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
1012 InputEvent.CTRL_DOWN_MASK);
1013 KeyStroke cmdWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
1014 ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx());
1016 InputMap inputMap = frame
1017 .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
1018 String ctrlW = ctrlWKey.toString();
1019 inputMap.put(ctrlWKey, ctrlW);
1020 inputMap.put(cmdWKey, ctrlW);
1022 ActionMap actionMap = frame.getActionMap();
1023 actionMap.put(ctrlW, closeAction);
1027 public void lostOwnership(Clipboard clipboard, Transferable contents)
1031 jalviewClipboard = null;
1034 internalCopy = false;
1038 public void dragEnter(DropTargetDragEvent evt)
1043 public void dragExit(DropTargetEvent evt)
1048 public void dragOver(DropTargetDragEvent evt)
1053 public void dropActionChanged(DropTargetDragEvent evt)
1064 public void drop(DropTargetDropEvent evt)
1066 boolean success = true;
1067 // JAL-1552 - acceptDrop required before getTransferable call for
1068 // Java's Transferable for native dnd
1069 evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
1070 Transferable t = evt.getTransferable();
1071 List<Object> files = new ArrayList<>();
1072 List<DataSourceType> protocols = new ArrayList<>();
1076 transferFromDropTarget(files, protocols, evt, t);
1077 } catch (Exception e)
1079 e.printStackTrace();
1087 for (int i = 0; i < files.size(); i++)
1089 // BH 2018 File or String
1090 Object file = files.get(i);
1091 String fileName = file.toString();
1092 DataSourceType protocol = (protocols == null)
1093 ? DataSourceType.FILE
1095 FileFormatI format = null;
1097 if (fileName.endsWith(".jar"))
1099 format = FileFormat.Jalview;
1104 format = new IdentifyFile().identify(file, protocol);
1106 if (file instanceof File)
1108 Platform.cacheFileData((File) file);
1110 new FileLoader().LoadFile(null, file, protocol, format);
1113 } catch (Exception ex)
1118 evt.dropComplete(success); // need this to ensure input focus is properly
1119 // transfered to any new windows created
1129 public void inputLocalFileMenuItem_actionPerformed(AlignViewport viewport)
1131 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
1132 JalviewFileChooser chooser = JalviewFileChooser.forRead(
1133 Cache.getProperty("LAST_DIRECTORY"), fileFormat,
1134 BackupFiles.getEnabled());
1136 chooser.setFileView(new JalviewFileView());
1137 chooser.setDialogTitle(
1138 MessageManager.getString("label.open_local_file"));
1139 chooser.setToolTipText(MessageManager.getString("action.open"));
1141 chooser.setResponseHandler(0, new Runnable()
1146 File selectedFile = chooser.getSelectedFile();
1147 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1149 FileFormatI format = chooser.getSelectedFormat();
1152 * Call IdentifyFile to verify the file contains what its extension implies.
1153 * Skip this step for dynamically added file formats, because
1154 * IdentifyFile does not know how to recognise them.
1156 if (FileFormats.getInstance().isIdentifiable(format))
1160 format = new IdentifyFile().identify(selectedFile,
1161 DataSourceType.FILE);
1162 } catch (FileFormatException e)
1164 // format = null; //??
1168 new FileLoader().LoadFile(viewport, selectedFile,
1169 DataSourceType.FILE, format);
1172 chooser.showOpenDialog(this);
1176 * Shows a dialog for input of a URL at which to retrieve alignment data
1181 public void inputURLMenuItem_actionPerformed(AlignViewport viewport)
1183 // This construct allows us to have a wider textfield
1185 JLabel label = new JLabel(
1186 MessageManager.getString("label.input_file_url"));
1188 JPanel panel = new JPanel(new GridLayout(2, 1));
1192 * the URL to fetch is
1193 * Java: an editable combobox with history
1194 * JS: (pending JAL-3038) a plain text field
1197 String urlBase = "http://www.";
1198 if (Platform.isJS())
1200 history = new JTextField(urlBase, 35);
1209 JComboBox<String> asCombo = new JComboBox<>();
1210 asCombo.setPreferredSize(new Dimension(400, 20));
1211 asCombo.setEditable(true);
1212 asCombo.addItem(urlBase);
1213 String historyItems = Cache.getProperty("RECENT_URL");
1214 if (historyItems != null)
1216 for (String token : historyItems.split("\\t"))
1218 asCombo.addItem(token);
1225 Object[] options = new Object[] { MessageManager.getString("action.ok"),
1226 MessageManager.getString("action.cancel") };
1227 Runnable action = new Runnable()
1232 @SuppressWarnings("unchecked")
1233 String url = (history instanceof JTextField
1234 ? ((JTextField) history).getText()
1235 : ((JComboBox<String>) history).getSelectedItem()
1238 if (url.toLowerCase().endsWith(".jar"))
1240 if (viewport != null)
1242 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1243 FileFormat.Jalview);
1247 new FileLoader().LoadFile(url, DataSourceType.URL,
1248 FileFormat.Jalview);
1253 FileFormatI format = null;
1256 format = new IdentifyFile().identify(url, DataSourceType.URL);
1257 } catch (FileFormatException e)
1259 // TODO revise error handling, distinguish between
1260 // URL not found and response not valid
1265 String msg = MessageManager
1266 .formatMessage("label.couldnt_locate", url);
1267 JvOptionPane.showInternalMessageDialog(getDesktopPane(), msg,
1268 MessageManager.getString("label.url_not_found"),
1269 JvOptionPane.WARNING_MESSAGE);
1274 if (viewport != null)
1276 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1281 new FileLoader().LoadFile(url, DataSourceType.URL, format);
1286 String dialogOption = MessageManager
1287 .getString("label.input_alignment_from_url");
1288 JvOptionPane.newOptionDialog(desktopPane).setResponseHandler(0, action)
1289 .showInternalDialog(panel, dialogOption,
1290 JvOptionPane.YES_NO_CANCEL_OPTION,
1291 JvOptionPane.PLAIN_MESSAGE, null, options,
1292 MessageManager.getString("action.ok"));
1296 * Opens the CutAndPaste window for the user to paste an alignment in to
1299 * - if not null, the pasted alignment is added to the current
1300 * alignment; if null, to a new alignment window
1303 public void inputTextboxMenuItem_actionPerformed(
1304 AlignmentViewPanel viewPanel)
1306 CutAndPasteTransfer cap = new CutAndPasteTransfer();
1307 cap.setForInput(viewPanel);
1308 addInternalFrame(cap,
1309 MessageManager.getString("label.cut_paste_alignmen_file"), true,
1319 Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
1320 Cache.setProperty("SCREENGEOMETRY_WIDTH", screen.width + "");
1321 Cache.setProperty("SCREENGEOMETRY_HEIGHT", screen.height + "");
1322 storeLastKnownDimensions("", new Rectangle(getBounds().x, getBounds().y,
1323 getWidth(), getHeight()));
1325 if (jconsole != null)
1327 storeLastKnownDimensions("JAVA_CONSOLE_", jconsole.getBounds());
1328 jconsole.stopConsole();
1332 storeLastKnownDimensions("JALVIEW_RSS_WINDOW_", jvnews.getBounds());
1335 if (dialogExecutor != null)
1337 dialogExecutor.shutdownNow();
1339 closeAll_actionPerformed(null);
1341 if (groovyConsole != null)
1343 // suppress a possible repeat prompt to save script
1344 groovyConsole.setDirty(false);
1345 groovyConsole.exit();
1350 private void storeLastKnownDimensions(String string, Rectangle jc)
1352 Cache.log.debug("Storing last known dimensions for " + string + ": x:"
1353 + jc.x + " y:" + jc.y + " width:" + jc.width + " height:"
1356 Cache.setProperty(string + "SCREEN_X", jc.x + "");
1357 Cache.setProperty(string + "SCREEN_Y", jc.y + "");
1358 Cache.setProperty(string + "SCREEN_WIDTH", jc.width + "");
1359 Cache.setProperty(string + "SCREEN_HEIGHT", jc.height + "");
1369 public void aboutMenuItem_actionPerformed(ActionEvent e)
1371 new Thread(new Runnable()
1376 new SplashScreen(false);
1382 * Returns the html text for the About screen, including any available version
1383 * number, build details, author details and citation reference, but without
1384 * the enclosing {@code html} tags
1388 public String getAboutMessage()
1390 StringBuilder message = new StringBuilder(1024);
1391 message.append("<h1><strong>Version: ")
1392 .append(Cache.getProperty("VERSION")).append("</strong></h1>")
1393 .append("<strong>Built: <em>")
1394 .append(Cache.getDefault("BUILD_DATE", "unknown"))
1395 .append("</em> from ").append(Cache.getBuildDetailsForSplash())
1396 .append("</strong>");
1398 String latestVersion = Cache.getDefault("LATEST_VERSION", "Checking");
1399 if (latestVersion.equals("Checking"))
1401 // JBP removed this message for 2.11: May be reinstated in future version
1402 // message.append("<br>...Checking latest version...</br>");
1404 else if (!latestVersion.equals(Cache.getProperty("VERSION")))
1406 boolean red = false;
1407 if (Cache.getProperty("VERSION").toLowerCase()
1408 .indexOf("automated build") == -1)
1411 // Displayed when code version and jnlp version do not match and code
1412 // version is not a development build
1413 message.append("<div style=\"color: #FF0000;font-style: bold;\">");
1416 message.append("<br>!! Version ")
1417 .append(Cache.getDefault("LATEST_VERSION", "..Checking.."))
1418 .append(" is available for download from ")
1419 .append(Cache.getDefault("www.jalview.org",
1420 "http://www.jalview.org"))
1424 message.append("</div>");
1427 message.append("<br>Authors: ");
1428 message.append(Cache.getDefault("AUTHORFNAMES", DEFAULT_AUTHORS));
1429 message.append(CITATION);
1431 return message.toString();
1435 * Action on requesting Help documentation
1438 public void documentationMenuItem_actionPerformed()
1442 if (Platform.isJS())
1444 BrowserLauncher.openURL("http://www.jalview.org/help.html");
1453 Help.showHelpWindow();
1455 } catch (Exception ex)
1457 System.err.println("Error opening help: " + ex.getMessage());
1462 public void closeAll_actionPerformed(ActionEvent e)
1464 // TODO show a progress bar while closing?
1465 JInternalFrame[] frames = desktopPane.getAllFrames();
1466 for (int i = 0; i < frames.length; i++)
1470 frames[i].setClosed(true);
1471 } catch (java.beans.PropertyVetoException ex)
1475 Jalview.setCurrentAlignFrame(null);
1476 System.out.println("ALL CLOSED");
1479 * reset state of singleton objects as appropriate (clear down session state
1480 * when all windows are closed)
1482 StructureSelectionManager ssm = StructureSelectionManager
1483 .getStructureSelectionManager(this);
1491 public void raiseRelated_actionPerformed(ActionEvent e)
1493 reorderAssociatedWindows(false, false);
1497 public void minimizeAssociated_actionPerformed(ActionEvent e)
1499 reorderAssociatedWindows(true, false);
1502 void closeAssociatedWindows()
1504 reorderAssociatedWindows(false, true);
1510 * @seejalview.jbgui.GDesktop#garbageCollect_actionPerformed(java.awt.event.
1514 protected void garbageCollect_actionPerformed(ActionEvent e)
1516 // We simply collect the garbage
1517 Cache.log.debug("Collecting garbage...");
1519 Cache.log.debug("Finished garbage collection.");
1526 * jalview.jbgui.GDesktop#showMemusage_actionPerformed(java.awt.event.ActionEvent
1530 protected void showMemusage_actionPerformed(ActionEvent e)
1532 desktopPane.showMemoryUsage(showMemusage.isSelected());
1539 * jalview.jbgui.GDesktop#showConsole_actionPerformed(java.awt.event.ActionEvent
1543 protected void showConsole_actionPerformed(ActionEvent e)
1545 showConsole(showConsole.isSelected());
1548 Console jconsole = null;
1551 * control whether the java console is visible or not
1555 void showConsole(boolean selected)
1557 // TODO: decide if we should update properties file
1558 if (jconsole != null) // BH 2018
1560 showConsole.setSelected(selected);
1561 Cache.setProperty("SHOW_JAVA_CONSOLE",
1562 Boolean.valueOf(selected).toString());
1563 jconsole.setVisible(selected);
1567 void reorderAssociatedWindows(boolean minimize, boolean close)
1569 JInternalFrame[] frames = desktopPane.getAllFrames();
1570 if (frames == null || frames.length < 1)
1575 AlignmentViewport source = null, target = null;
1576 if (frames[0] instanceof AlignFrame)
1578 source = ((AlignFrame) frames[0]).getCurrentView();
1580 else if (frames[0] instanceof TreePanel)
1582 source = ((TreePanel) frames[0]).getViewPort();
1584 else if (frames[0] instanceof PCAPanel)
1586 source = ((PCAPanel) frames[0]).av;
1588 else if (frames[0].getContentPane() instanceof PairwiseAlignPanel)
1590 source = ((PairwiseAlignPanel) frames[0].getContentPane()).av;
1595 for (int i = 0; i < frames.length; i++)
1598 if (frames[i] == null)
1602 if (frames[i] instanceof AlignFrame)
1604 target = ((AlignFrame) frames[i]).getCurrentView();
1606 else if (frames[i] instanceof TreePanel)
1608 target = ((TreePanel) frames[i]).getViewPort();
1610 else if (frames[i] instanceof PCAPanel)
1612 target = ((PCAPanel) frames[i]).av;
1614 else if (frames[i].getContentPane() instanceof PairwiseAlignPanel)
1616 target = ((PairwiseAlignPanel) frames[i].getContentPane()).av;
1619 if (source == target)
1625 frames[i].setClosed(true);
1629 frames[i].setIcon(minimize);
1632 frames[i].toFront();
1636 } catch (java.beans.PropertyVetoException ex)
1651 protected void preferences_actionPerformed(ActionEvent e)
1657 * Prompts the user to choose a file and then saves the Jalview state as a
1658 * Jalview project file
1661 public void saveState_actionPerformed()
1663 saveState_actionPerformed(false);
1666 public void saveState_actionPerformed(boolean saveAs)
1668 java.io.File projectFile = getProjectFile();
1669 // autoSave indicates we already have a file and don't need to ask
1670 boolean autoSave = projectFile != null && !saveAs
1671 && BackupFiles.getEnabled();
1673 // System.out.println("autoSave="+autoSave+", projectFile='"+projectFile+"',
1674 // saveAs="+saveAs+", Backups
1675 // "+(BackupFiles.getEnabled()?"enabled":"disabled"));
1677 boolean approveSave = false;
1680 JalviewFileChooser chooser = new JalviewFileChooser("jvp",
1683 chooser.setFileView(new JalviewFileView());
1684 chooser.setDialogTitle(MessageManager.getString("label.save_state"));
1686 int value = chooser.showSaveDialog(this);
1688 if (value == JalviewFileChooser.APPROVE_OPTION)
1690 projectFile = chooser.getSelectedFile();
1691 setProjectFile(projectFile);
1696 if (approveSave || autoSave)
1698 final Desktop me = this;
1699 final java.io.File chosenFile = projectFile;
1700 new Thread(new Runnable()
1705 // TODO: refactor to Jalview desktop session controller action.
1706 setProgressBar(MessageManager.formatMessage(
1707 "label.saving_jalview_project", new Object[]
1708 { chosenFile.getName() }), chosenFile.hashCode());
1709 Cache.setProperty("LAST_DIRECTORY", chosenFile.getParent());
1710 // TODO catch and handle errors for savestate
1711 // TODO prevent user from messing with the Desktop whilst we're saving
1714 boolean doBackup = BackupFiles.getEnabled();
1715 BackupFiles backupfiles = doBackup ? new BackupFiles(chosenFile)
1718 new Jalview2XML().saveState(
1719 doBackup ? backupfiles.getTempFile() : chosenFile);
1723 backupfiles.setWriteSuccess(true);
1724 backupfiles.rollBackupsAndRenameTempFile();
1726 } catch (OutOfMemoryError oom)
1728 new OOMWarning("Whilst saving current state to "
1729 + chosenFile.getName(), oom);
1730 } catch (Exception ex)
1732 Cache.log.error("Problems whilst trying to save to "
1733 + chosenFile.getName(), ex);
1734 JvOptionPane.showMessageDialog(me,
1735 MessageManager.formatMessage(
1736 "label.error_whilst_saving_current_state_to",
1738 { chosenFile.getName() }),
1739 MessageManager.getString("label.couldnt_save_project"),
1740 JvOptionPane.WARNING_MESSAGE);
1742 setProgressBar(null, chosenFile.hashCode());
1749 public void saveAsState_actionPerformed(ActionEvent e)
1751 saveState_actionPerformed(true);
1754 private void setProjectFile(File choice)
1756 this.projectFile = choice;
1759 public File getProjectFile()
1761 return this.projectFile;
1765 * Shows a file chooser dialog and tries to read in the selected file as a
1769 public void loadState_actionPerformed()
1771 final String[] suffix = new String[] { "jvp", "jar" };
1772 final String[] desc = new String[] { "Jalview Project",
1773 "Jalview Project (old)" };
1774 JalviewFileChooser chooser = new JalviewFileChooser(
1775 Cache.getProperty("LAST_DIRECTORY"), suffix, desc,
1776 "Jalview Project", true, BackupFiles.getEnabled()); // last two
1780 chooser.setFileView(new JalviewFileView());
1781 chooser.setDialogTitle(MessageManager.getString("label.restore_state"));
1782 chooser.setResponseHandler(0, new Runnable()
1787 File selectedFile = chooser.getSelectedFile();
1788 setProjectFile(selectedFile);
1789 String choice = selectedFile.getAbsolutePath();
1790 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1791 new Thread(new Runnable()
1798 new Jalview2XML().loadJalviewAlign(selectedFile);
1799 } catch (OutOfMemoryError oom)
1801 new OOMWarning("Whilst loading project from " + choice, oom);
1802 } catch (Exception ex)
1805 "Problems whilst loading project from " + choice, ex);
1806 JvOptionPane.showMessageDialog( getDesktopPane(),
1807 MessageManager.formatMessage(
1808 "label.error_whilst_loading_project_from",
1812 .getString("label.couldnt_load_project"),
1813 JvOptionPane.WARNING_MESSAGE);
1820 chooser.showOpenDialog(this);
1824 public void inputSequence_actionPerformed(ActionEvent e)
1826 new SequenceFetcher(this);
1829 JPanel progressPanel;
1831 ArrayList<JPanel> fileLoadingPanels = new ArrayList<>();
1833 public void startLoading(final Object fileName)
1835 if (fileLoadingCount == 0)
1837 fileLoadingPanels.add(addProgressPanel(MessageManager
1838 .formatMessage("label.loading_file", new Object[]
1844 private JPanel addProgressPanel(String string)
1846 if (progressPanel == null)
1848 progressPanel = new JPanel(new GridLayout(1, 1));
1849 totalProgressCount = 0;
1850 getContentPane().add(progressPanel, BorderLayout.SOUTH);
1852 JPanel thisprogress = new JPanel(new BorderLayout(10, 5));
1853 JProgressBar progressBar = new JProgressBar();
1854 progressBar.setIndeterminate(true);
1856 thisprogress.add(new JLabel(string), BorderLayout.WEST);
1858 thisprogress.add(progressBar, BorderLayout.CENTER);
1859 progressPanel.add(thisprogress);
1860 ((GridLayout) progressPanel.getLayout()).setRows(
1861 ((GridLayout) progressPanel.getLayout()).getRows() + 1);
1862 ++totalProgressCount;
1864 return thisprogress;
1867 int totalProgressCount = 0;
1869 private void removeProgressPanel(JPanel progbar)
1871 if (progressPanel != null)
1873 synchronized (progressPanel)
1875 progressPanel.remove(progbar);
1876 GridLayout gl = (GridLayout) progressPanel.getLayout();
1877 gl.setRows(gl.getRows() - 1);
1878 if (--totalProgressCount < 1)
1880 this.getContentPane().remove(progressPanel);
1881 progressPanel = null;
1888 public void stopLoading()
1891 if (fileLoadingCount < 1)
1893 while (fileLoadingPanels.size() > 0)
1895 removeProgressPanel(fileLoadingPanels.remove(0));
1897 fileLoadingPanels.clear();
1898 fileLoadingCount = 0;
1903 public static int getViewCount(String alignmentId)
1905 AlignmentViewport[] aps = getViewports(alignmentId);
1906 return (aps == null) ? 0 : aps.length;
1911 * @param alignmentId
1912 * - if null, all sets are returned
1913 * @return all AlignmentPanels concerning the alignmentId sequence set
1915 public static AlignmentPanel[] getAlignmentPanels(String alignmentId)
1917 if (getDesktopPane() == null)
1919 // no frames created and in headless mode
1920 // TODO: verify that frames are recoverable when in headless mode
1923 List<AlignmentPanel> aps = new ArrayList<>();
1924 AlignFrame[] frames = getAlignFrames();
1929 for (AlignFrame af : frames)
1931 for (AlignmentPanel ap : af.alignPanels)
1933 if (alignmentId == null
1934 || alignmentId.equals(ap.av.getSequenceSetId()))
1940 if (aps.size() == 0)
1944 AlignmentPanel[] vap = aps.toArray(new AlignmentPanel[aps.size()]);
1949 * get all the viewports on an alignment.
1951 * @param sequenceSetId
1952 * unique alignment id (may be null - all viewports returned in that
1954 * @return all viewports on the alignment bound to sequenceSetId
1956 public static AlignmentViewport[] getViewports(String sequenceSetId)
1958 List<AlignmentViewport> viewp = new ArrayList<>();
1959 if ( getDesktopPane() != null)
1961 AlignFrame[] frames = getAlignFrames();
1963 for (AlignFrame afr : frames)
1965 if (sequenceSetId == null || afr.getViewport().getSequenceSetId()
1966 .equals(sequenceSetId))
1968 if (afr.alignPanels != null)
1970 for (AlignmentPanel ap : afr.alignPanels)
1972 if (sequenceSetId == null
1973 || sequenceSetId.equals(ap.av.getSequenceSetId()))
1981 viewp.add(afr.getViewport());
1985 if (viewp.size() > 0)
1987 return viewp.toArray(new AlignmentViewport[viewp.size()]);
1994 * Explode the views in the given frame into separate AlignFrame
1998 public static void explodeViews(AlignFrame af)
2000 int size = af.alignPanels.size();
2006 // FIXME: ideally should use UI interface API
2007 FeatureSettings viewFeatureSettings = (af.featureSettings != null
2008 && af.featureSettings.isOpen()) ? af.featureSettings : null;
2009 Rectangle fsBounds = af.getFeatureSettingsGeometry();
2010 for (int i = 0; i < size; i++)
2012 AlignmentPanel ap = af.alignPanels.get(i);
2014 AlignFrame newaf = new AlignFrame(ap);
2016 // transfer reference for existing feature settings to new alignFrame
2017 if (ap == af.alignPanel)
2019 if (viewFeatureSettings != null && viewFeatureSettings.fr.ap == ap)
2021 newaf.featureSettings = viewFeatureSettings;
2023 newaf.setFeatureSettingsGeometry(fsBounds);
2027 * Restore the view's last exploded frame geometry if known. Multiple
2028 * views from one exploded frame share and restore the same (frame)
2029 * position and size.
2031 Rectangle geometry = ap.av.getExplodedGeometry();
2032 if (geometry != null)
2034 newaf.setBounds(geometry);
2037 ap.av.setGatherViewsHere(false);
2039 addInternalFrame(newaf, af.getTitle(), AlignFrame.DEFAULT_WIDTH,
2040 AlignFrame.DEFAULT_HEIGHT);
2041 // and materialise a new feature settings dialog instance for the new
2043 // (closes the old as if 'OK' was pressed)
2044 if (ap == af.alignPanel && newaf.featureSettings != null
2045 && newaf.featureSettings.isOpen()
2046 && af.alignPanel.getAlignViewport().isShowSequenceFeatures())
2048 newaf.showFeatureSettingsUI();
2052 af.featureSettings = null;
2053 af.alignPanels.clear();
2054 af.closeMenuItem_actionPerformed(true);
2059 * Gather expanded views (separate AlignFrame's) with the same sequence set
2060 * identifier back in to this frame as additional views, and close the
2061 * expanded views. Note the expanded frames may themselves have multiple
2062 * views. We take the lot.
2066 public void gatherViews(AlignFrame source)
2068 source.viewport.setGatherViewsHere(true);
2069 source.viewport.setExplodedGeometry(source.getBounds());
2070 JInternalFrame[] frames = desktopPane.getAllFrames();
2071 String viewId = source.viewport.getSequenceSetId();
2072 for (int t = 0; t < frames.length; t++)
2074 if (frames[t] instanceof AlignFrame && frames[t] != source)
2076 AlignFrame af = (AlignFrame) frames[t];
2077 boolean gatherThis = false;
2078 for (int a = 0; a < af.alignPanels.size(); a++)
2080 AlignmentPanel ap = af.alignPanels.get(a);
2081 if (viewId.equals(ap.av.getSequenceSetId()))
2084 ap.av.setGatherViewsHere(false);
2085 ap.av.setExplodedGeometry(af.getBounds());
2086 source.addAlignmentPanel(ap, false);
2092 if (af.featureSettings != null && af.featureSettings.isOpen())
2094 if (source.featureSettings == null)
2096 // preserve the feature settings geometry for this frame
2097 source.featureSettings = af.featureSettings;
2098 source.setFeatureSettingsGeometry(
2099 af.getFeatureSettingsGeometry());
2103 // close it and forget
2104 af.featureSettings.close();
2107 af.alignPanels.clear();
2108 af.closeMenuItem_actionPerformed(true);
2113 // refresh the feature setting UI for the source frame if it exists
2114 if (source.featureSettings != null && source.featureSettings.isOpen())
2116 source.showFeatureSettingsUI();
2120 public JInternalFrame[] getAllFrames()
2122 return desktopPane.getAllFrames();
2126 * Checks the given url to see if it gives a response indicating that the user
2127 * should be informed of a new questionnaire.
2131 public void checkForQuestionnaire(String url)
2133 UserQuestionnaireCheck jvq = new UserQuestionnaireCheck(url);
2134 // javax.swing.SwingUtilities.invokeLater(jvq);
2135 new Thread(jvq).start();
2138 public void checkURLLinks()
2140 // Thread off the URL link checker
2141 addDialogThread(new Runnable()
2146 if (Cache.getDefault("CHECKURLLINKS", true))
2148 // check what the actual links are - if it's just the default don't
2149 // bother with the warning
2150 List<String> links = Preferences.sequenceUrlLinks
2153 // only need to check links if there is one with a
2154 // SEQUENCE_ID which is not the default EMBL_EBI link
2155 ListIterator<String> li = links.listIterator();
2156 boolean check = false;
2157 List<JLabel> urls = new ArrayList<>();
2158 while (li.hasNext())
2160 String link = li.next();
2161 if (link.contains(jalview.util.UrlConstants.SEQUENCE_ID)
2162 && !UrlConstants.isDefaultString(link))
2165 int barPos = link.indexOf("|");
2166 String urlMsg = barPos == -1 ? link
2167 : link.substring(0, barPos) + ": "
2168 + link.substring(barPos + 1);
2169 urls.add(new JLabel(urlMsg));
2177 // ask user to check in case URL links use old style tokens
2178 // ($SEQUENCE_ID$ for sequence id _or_ accession id)
2179 JPanel msgPanel = new JPanel();
2180 msgPanel.setLayout(new BoxLayout(msgPanel, BoxLayout.PAGE_AXIS));
2181 msgPanel.add(Box.createVerticalGlue());
2182 JLabel msg = new JLabel(MessageManager
2183 .getString("label.SEQUENCE_ID_for_DB_ACCESSION1"));
2184 JLabel msg2 = new JLabel(MessageManager
2185 .getString("label.SEQUENCE_ID_for_DB_ACCESSION2"));
2187 for (JLabel url : urls)
2193 final JCheckBox jcb = new JCheckBox(
2194 MessageManager.getString("label.do_not_display_again"));
2195 jcb.addActionListener(new ActionListener()
2198 public void actionPerformed(ActionEvent e)
2200 // update Cache settings for "don't show this again"
2201 boolean showWarningAgain = !jcb.isSelected();
2202 Cache.setProperty("CHECKURLLINKS",
2203 Boolean.valueOf(showWarningAgain).toString());
2208 JvOptionPane.showMessageDialog(desktopPane, msgPanel,
2210 .getString("label.SEQUENCE_ID_no_longer_used"),
2211 JvOptionPane.WARNING_MESSAGE);
2218 * Proxy class for JDesktopPane which optionally displays the current memory
2219 * usage and highlights the desktop area with a red bar if free memory runs
2224 public class MyDesktopPane extends JDesktopPane implements Runnable
2226 private static final float ONE_MB = 1048576f;
2228 boolean showMemoryUsage = false;
2232 java.text.NumberFormat df;
2234 float maxMemory, allocatedMemory, freeMemory, totalFreeMemory,
2237 public MyDesktopPane(boolean showMemoryUsage)
2239 showMemoryUsage(showMemoryUsage);
2242 public void showMemoryUsage(boolean showMemory)
2244 this.showMemoryUsage = showMemory;
2247 Thread worker = new Thread(this);
2253 public boolean isShowMemoryUsage()
2255 return showMemoryUsage;
2261 df = java.text.NumberFormat.getNumberInstance();
2262 df.setMaximumFractionDigits(2);
2263 runtime = Runtime.getRuntime();
2265 while (showMemoryUsage)
2269 maxMemory = runtime.maxMemory() / ONE_MB;
2270 allocatedMemory = runtime.totalMemory() / ONE_MB;
2271 freeMemory = runtime.freeMemory() / ONE_MB;
2272 totalFreeMemory = freeMemory + (maxMemory - allocatedMemory);
2274 percentUsage = (totalFreeMemory / maxMemory) * 100;
2276 // if (percentUsage < 20)
2278 // border1 = BorderFactory.createMatteBorder(12, 12, 12, 12,
2280 // instance.set.setBorder(border1);
2283 // sleep after showing usage
2285 } catch (Exception ex)
2287 ex.printStackTrace();
2293 public void paintComponent(Graphics g)
2295 if (showMemoryUsage && g != null && df != null)
2297 if (percentUsage < 20)
2299 g.setColor(Color.red);
2301 FontMetrics fm = g.getFontMetrics();
2304 g.drawString(MessageManager.formatMessage("label.memory_stats",
2306 { df.format(totalFreeMemory), df.format(maxMemory),
2307 df.format(percentUsage) }),
2308 10, getHeight() - fm.getHeight());
2315 * Accessor method to quickly get all the AlignmentFrames loaded.
2317 * @return an array of AlignFrame, or null if none found
2319 public static AlignFrame[] getAlignFrames()
2321 if (Jalview.isHeadlessMode())
2323 return new AlignFrame[] { Jalview.currentAlignFrame };
2326 JInternalFrame[] frames = getDesktopPane().getAllFrames();
2332 List<AlignFrame> avp = new ArrayList<>();
2334 for (int i = frames.length - 1; i > -1; i--)
2336 if (frames[i] instanceof AlignFrame)
2338 avp.add((AlignFrame) frames[i]);
2340 else if (frames[i] instanceof SplitFrame)
2343 * Also check for a split frame containing an AlignFrame
2345 GSplitFrame sf = (GSplitFrame) frames[i];
2346 if (sf.getTopFrame() instanceof AlignFrame)
2348 avp.add((AlignFrame) sf.getTopFrame());
2350 if (sf.getBottomFrame() instanceof AlignFrame)
2352 avp.add((AlignFrame) sf.getBottomFrame());
2356 if (avp.size() == 0)
2360 AlignFrame afs[] = avp.toArray(new AlignFrame[avp.size()]);
2365 * Returns an array of any AppJmol frames in the Desktop (or null if none).
2369 public GStructureViewer[] getJmols()
2371 JInternalFrame[] frames = desktopPane.getAllFrames();
2377 List<GStructureViewer> avp = new ArrayList<>();
2379 for (int i = frames.length - 1; i > -1; i--)
2381 if (frames[i] instanceof AppJmol)
2383 GStructureViewer af = (GStructureViewer) frames[i];
2387 if (avp.size() == 0)
2391 GStructureViewer afs[] = avp.toArray(new GStructureViewer[avp.size()]);
2396 * Add Groovy Support to Jalview
2399 public void groovyShell_actionPerformed()
2403 openGroovyConsole();
2404 } catch (Exception ex)
2406 Cache.log.error("Groovy Shell Creation failed.", ex);
2407 JvOptionPane.showInternalMessageDialog(desktopPane,
2409 MessageManager.getString("label.couldnt_create_groovy_shell"),
2410 MessageManager.getString("label.groovy_support_failed"),
2411 JvOptionPane.ERROR_MESSAGE);
2416 * Open the Groovy console
2418 void openGroovyConsole()
2420 if (groovyConsole == null)
2422 groovyConsole = new groovy.ui.Console();
2423 groovyConsole.setVariable("Jalview", this);
2424 groovyConsole.run();
2427 * We allow only one console at a time, so that AlignFrame menu option
2428 * 'Calculate | Run Groovy script' is unambiguous.
2429 * Disable 'Groovy Console', and enable 'Run script', when the console is
2430 * opened, and the reverse when it is closed
2432 Window window = (Window) groovyConsole.getFrame();
2433 window.addWindowListener(new WindowAdapter()
2436 public void windowClosed(WindowEvent e)
2439 * rebind CMD-Q from Groovy Console to Jalview Quit
2442 enableExecuteGroovy(false);
2448 * show Groovy console window (after close and reopen)
2450 ((Window) groovyConsole.getFrame()).setVisible(true);
2453 * if we got this far, enable 'Run Groovy' in AlignFrame menus
2454 * and disable opening a second console
2456 enableExecuteGroovy(true);
2460 * Bind Ctrl/Cmd-Q to Quit - for reset as Groovy Console takes over this
2461 * binding when opened
2463 protected void addQuitHandler()
2466 .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
2468 .getKeyStroke(KeyEvent.VK_Q,
2469 jalview.util.ShortcutKeyMaskExWrapper
2470 .getMenuShortcutKeyMaskEx()),
2472 getRootPane().getActionMap().put("Quit", new AbstractAction()
2475 public void actionPerformed(ActionEvent e)
2483 * Enable or disable 'Run Groovy script' in AlignFrame calculate menus
2486 * true if Groovy console is open
2488 public void enableExecuteGroovy(boolean enabled)
2491 * disable opening a second Groovy console
2492 * (or re-enable when the console is closed)
2494 groovyShell.setEnabled(!enabled);
2496 AlignFrame[] alignFrames = getAlignFrames();
2497 if (alignFrames != null)
2499 for (AlignFrame af : alignFrames)
2501 af.setGroovyEnabled(enabled);
2507 * Progress bars managed by the IProgressIndicator method.
2509 private Hashtable<Long, JPanel> progressBars;
2511 private Hashtable<Long, IProgressIndicatorHandler> progressBarHandlers;
2516 * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
2519 public void setProgressBar(String message, long id)
2521 // Platform.timeCheck("Desktop " + message, Platform.TIME_MARK);
2523 if (progressBars == null)
2525 progressBars = new Hashtable<>();
2526 progressBarHandlers = new Hashtable<>();
2529 if (progressBars.get(Long.valueOf(id)) != null)
2531 JPanel panel = progressBars.remove(Long.valueOf(id));
2532 if (progressBarHandlers.contains(Long.valueOf(id)))
2534 progressBarHandlers.remove(Long.valueOf(id));
2536 removeProgressPanel(panel);
2540 progressBars.put(Long.valueOf(id), addProgressPanel(message));
2547 * @see jalview.gui.IProgressIndicator#registerHandler(long,
2548 * jalview.gui.IProgressIndicatorHandler)
2551 public void registerHandler(final long id,
2552 final IProgressIndicatorHandler handler)
2554 if (progressBarHandlers == null
2555 || !progressBars.containsKey(Long.valueOf(id)))
2557 throw new Error(MessageManager.getString(
2558 "error.call_setprogressbar_before_registering_handler"));
2560 progressBarHandlers.put(Long.valueOf(id), handler);
2561 final JPanel progressPanel = progressBars.get(Long.valueOf(id));
2562 if (handler.canCancel())
2564 JButton cancel = new JButton(
2565 MessageManager.getString("action.cancel"));
2566 final IProgressIndicator us = this;
2567 cancel.addActionListener(new ActionListener()
2571 public void actionPerformed(ActionEvent e)
2573 handler.cancelActivity(id);
2574 us.setProgressBar(MessageManager
2575 .formatMessage("label.cancelled_params", new Object[]
2576 { ((JLabel) progressPanel.getComponent(0)).getText() }),
2580 progressPanel.add(cancel, BorderLayout.EAST);
2586 * @return true if any progress bars are still active
2589 public boolean operationInProgress()
2591 if (progressBars != null && progressBars.size() > 0)
2599 * This will return the first AlignFrame holding the given viewport instance.
2600 * It will break if there are more than one AlignFrames viewing a particular
2604 * @return alignFrame for viewport
2606 public static AlignFrame getAlignFrameFor(AlignViewportI viewport)
2608 if ( getDesktopPane() != null)
2610 AlignmentPanel[] aps = getAlignmentPanels(
2611 viewport.getSequenceSetId());
2612 for (int panel = 0; aps != null && panel < aps.length; panel++)
2614 if (aps[panel] != null && aps[panel].av == viewport)
2616 return aps[panel].alignFrame;
2623 public VamsasApplication getVamsasApplication()
2625 // TODO: JAL-3311 remove remaining code from Jalview relating to VAMSAS
2631 * flag set if jalview GUI is being operated programmatically
2633 private boolean inBatchMode = false;
2636 * check if jalview GUI is being operated programmatically
2638 * @return inBatchMode
2640 public boolean isInBatchMode()
2646 * set flag if jalview GUI is being operated programmatically
2648 * @param inBatchMode
2650 public void setInBatchMode(boolean inBatchMode)
2652 this.inBatchMode = inBatchMode;
2655 public void startServiceDiscovery()
2657 startServiceDiscovery(false);
2660 public void startServiceDiscovery(boolean blocking)
2662 boolean alive = true;
2663 Thread t0 = null, t1 = null, t2 = null;
2664 // JAL-940 - JALVIEW 1 services are now being EOLed as of JABA 2.1 release
2667 // todo: changesupport handlers need to be transferred
2668 if (discoverer == null)
2670 discoverer = new jalview.ws.jws1.Discoverer();
2671 // register PCS handler for desktop.
2672 discoverer.addPropertyChangeListener(changeSupport);
2674 // JAL-940 - disabled JWS1 service configuration - always start discoverer
2675 // until we phase out completely
2676 (t0 = new Thread(discoverer)).start();
2679 if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
2681 t2 = jalview.ws.jws2.Jws2Discoverer.getInstance()
2682 .startDiscoverer(changeSupport);
2686 // TODO: do rest service discovery
2695 } catch (Exception e)
2698 alive = (t1 != null && t1.isAlive()) || (t2 != null && t2.isAlive())
2699 || (t3 != null && t3.isAlive())
2700 || (t0 != null && t0.isAlive());
2706 * called to check if the service discovery process completed successfully.
2710 protected void JalviewServicesChanged(PropertyChangeEvent evt)
2712 if (evt.getNewValue() == null || evt.getNewValue() instanceof Vector)
2714 final String ermsg = jalview.ws.jws2.Jws2Discoverer.getInstance()
2715 .getErrorMessages();
2718 if (Cache.getDefault("SHOW_WSDISCOVERY_ERRORS", true))
2720 if (serviceChangedDialog == null)
2722 // only run if we aren't already displaying one of these.
2723 addDialogThread(serviceChangedDialog = new Runnable()
2730 * JalviewDialog jd =new JalviewDialog() {
2732 * @Override protected void cancelPressed() { // TODO
2733 * Auto-generated method stub
2735 * }@Override protected void okPressed() { // TODO
2736 * Auto-generated method stub
2738 * }@Override protected void raiseClosed() { // TODO
2739 * Auto-generated method stub
2741 * } }; jd.initDialogFrame(new
2742 * JLabel("<html><table width=\"450\"><tr><td>" + ermsg +
2743 * "<br/>It may be that you have invalid JABA URLs in your web service preferences,"
2744 * + " or mis-configured HTTP proxy settings.<br/>" +
2745 * "Check the <em>Connections</em> and <em>Web services</em> tab of the"
2747 * " Tools->Preferences dialog box to change them.</td></tr></table></html>"
2748 * ), true, true, "Web Service Configuration Problem", 450,
2751 * jd.waitForInput();
2753 JvOptionPane.showConfirmDialog(desktopPane,
2754 new JLabel("<html><table width=\"450\"><tr><td>"
2755 + ermsg + "</td></tr></table>"
2756 + "<p>It may be that you have invalid JABA URLs<br/>in your web service preferences,"
2757 + "<br>or as a command-line argument, or mis-configured HTTP proxy settings.</p>"
2758 + "<p>Check the <em>Connections</em> and <em>Web services</em> tab<br/>of the"
2759 + " Tools->Preferences dialog box to change them.</p></html>"),
2760 "Web Service Configuration Problem",
2761 JvOptionPane.DEFAULT_OPTION,
2762 JvOptionPane.ERROR_MESSAGE);
2763 serviceChangedDialog = null;
2772 "Errors reported by JABA discovery service. Check web services preferences.\n"
2779 private Runnable serviceChangedDialog = null;
2782 * start a thread to open a URL in the configured browser. Pops up a warning
2783 * dialog to the user if there is an exception when calling out to the browser
2788 public static void showUrl(final String url)
2790 showUrl(url, getInstance());
2794 * Like showUrl but allows progress handler to be specified
2798 * (null) or object implementing IProgressIndicator
2800 public static void showUrl(final String url,
2801 final IProgressIndicator progress)
2803 new Thread(new Runnable()
2810 if (progress != null)
2812 progress.setProgressBar(MessageManager
2813 .formatMessage("status.opening_params", new Object[]
2814 { url }), this.hashCode());
2816 jalview.util.BrowserLauncher.openURL(url);
2817 } catch (Exception ex)
2819 JvOptionPane.showInternalMessageDialog( getDesktopPane(),
2821 .getString("label.web_browser_not_found_unix"),
2822 MessageManager.getString("label.web_browser_not_found"),
2823 JvOptionPane.WARNING_MESSAGE);
2825 ex.printStackTrace();
2827 if (progress != null)
2829 progress.setProgressBar(null, this.hashCode());
2835 public static WsParamSetManager wsparamManager = null;
2837 public static ParamManager getUserParameterStore()
2839 if (wsparamManager == null)
2841 wsparamManager = new WsParamSetManager();
2843 return wsparamManager;
2847 * static hyperlink handler proxy method for use by Jalview's internal windows
2851 public static void hyperlinkUpdate(HyperlinkEvent e)
2853 if (e.getEventType() == EventType.ACTIVATED)
2858 url = e.getURL().toString();
2860 } catch (Exception x)
2864 if (Cache.log != null)
2866 Cache.log.error("Couldn't handle string " + url + " as a URL.");
2871 "Couldn't handle string " + url + " as a URL.");
2874 // ignore any exceptions due to dud links.
2881 * single thread that handles display of dialogs to user.
2883 ExecutorService dialogExecutor = Executors.newSingleThreadExecutor();
2886 * flag indicating if dialogExecutor should try to acquire a permit
2888 private volatile boolean dialogPause = true;
2893 private java.util.concurrent.Semaphore block = new Semaphore(0);
2895 private static groovy.ui.Console groovyConsole;
2898 * add another dialog thread to the queue
2902 public void addDialogThread(final Runnable prompter)
2904 dialogExecutor.submit(new Runnable()
2914 } catch (InterruptedException x)
2918 if (Jalview.isHeadlessMode())
2924 SwingUtilities.invokeAndWait(prompter);
2925 } catch (Exception q)
2927 Cache.log.warn("Unexpected Exception in dialog thread.", q);
2933 public void startDialogQueue()
2935 // set the flag so we don't pause waiting for another permit and semaphore
2936 // the current task to begin
2937 dialogPause = false;
2942 * Outputs an image of the desktop to file in EPS format, after prompting the
2943 * user for choice of Text or Lineart character rendering (unless a preference
2944 * has been set). The file name is generated as
2947 * Jalview_snapshot_nnnnn.eps where nnnnn is the current timestamp in milliseconds
2951 protected void snapShotWindow_actionPerformed(ActionEvent e)
2953 // currently the menu option to do this is not shown
2956 int width = getWidth();
2957 int height = getHeight();
2959 "Jalview_snapshot_" + System.currentTimeMillis() + ".eps");
2960 ImageWriterI writer = new ImageWriterI()
2963 public void exportImage(Graphics g) throws Exception
2966 Cache.log.info("Successfully written snapshot to file "
2967 + of.getAbsolutePath());
2970 String title = "View of desktop";
2971 ImageExporter exporter = new ImageExporter(writer, null, TYPE.EPS,
2973 exporter.doExport(of, this, width, height, title);
2977 * Explode the views in the given SplitFrame into separate SplitFrame windows.
2978 * This respects (remembers) any previous 'exploded geometry' i.e. the size
2979 * and location last time the view was expanded (if any). However it does not
2980 * remember the split pane divider location - this is set to match the
2981 * 'exploding' frame.
2985 public void explodeViews(SplitFrame sf)
2987 AlignFrame oldTopFrame = (AlignFrame) sf.getTopFrame();
2988 AlignFrame oldBottomFrame = (AlignFrame) sf.getBottomFrame();
2989 List<? extends AlignmentViewPanel> topPanels = oldTopFrame
2991 List<? extends AlignmentViewPanel> bottomPanels = oldBottomFrame
2993 int viewCount = topPanels.size();
3000 * Processing in reverse order works, forwards order leaves the first panels
3001 * not visible. I don't know why!
3003 for (int i = viewCount - 1; i >= 0; i--)
3006 * Make new top and bottom frames. These take over the respective
3007 * AlignmentPanel objects, including their AlignmentViewports, so the
3008 * cdna/protein relationships between the viewports is carried over to the
3011 * explodedGeometry holds the (x, y) position of the previously exploded
3012 * SplitFrame, and the (width, height) of the AlignFrame component
3014 AlignmentPanel topPanel = (AlignmentPanel) topPanels.get(i);
3015 AlignFrame newTopFrame = new AlignFrame(topPanel);
3016 newTopFrame.setSize(oldTopFrame.getSize());
3017 newTopFrame.setVisible(true);
3018 Rectangle geometry = ((AlignViewport) topPanel.getAlignViewport())
3019 .getExplodedGeometry();
3020 if (geometry != null)
3022 newTopFrame.setSize(geometry.getSize());
3025 AlignmentPanel bottomPanel = (AlignmentPanel) bottomPanels.get(i);
3026 AlignFrame newBottomFrame = new AlignFrame(bottomPanel);
3027 newBottomFrame.setSize(oldBottomFrame.getSize());
3028 newBottomFrame.setVisible(true);
3029 geometry = ((AlignViewport) bottomPanel.getAlignViewport())
3030 .getExplodedGeometry();
3031 if (geometry != null)
3033 newBottomFrame.setSize(geometry.getSize());
3036 topPanel.av.setGatherViewsHere(false);
3037 bottomPanel.av.setGatherViewsHere(false);
3038 JInternalFrame splitFrame = new SplitFrame(newTopFrame,
3040 if (geometry != null)
3042 splitFrame.setLocation(geometry.getLocation());
3044 addInternalFrame(splitFrame, sf.getTitle(), -1, -1);
3048 * Clear references to the panels (now relocated in the new SplitFrames)
3049 * before closing the old SplitFrame.
3052 bottomPanels.clear();
3057 * Gather expanded split frames, sharing the same pairs of sequence set ids,
3058 * back into the given SplitFrame as additional views. Note that the gathered
3059 * frames may themselves have multiple views.
3063 public void gatherViews(GSplitFrame source)
3066 * special handling of explodedGeometry for a view within a SplitFrame: - it
3067 * holds the (x, y) position of the enclosing SplitFrame, and the (width,
3068 * height) of the AlignFrame component
3070 AlignFrame myTopFrame = (AlignFrame) source.getTopFrame();
3071 AlignFrame myBottomFrame = (AlignFrame) source.getBottomFrame();
3072 myTopFrame.viewport.setExplodedGeometry(new Rectangle(source.getX(),
3073 source.getY(), myTopFrame.getWidth(), myTopFrame.getHeight()));
3074 myBottomFrame.viewport
3075 .setExplodedGeometry(new Rectangle(source.getX(), source.getY(),
3076 myBottomFrame.getWidth(), myBottomFrame.getHeight()));
3077 myTopFrame.viewport.setGatherViewsHere(true);
3078 myBottomFrame.viewport.setGatherViewsHere(true);
3079 String topViewId = myTopFrame.viewport.getSequenceSetId();
3080 String bottomViewId = myBottomFrame.viewport.getSequenceSetId();
3082 JInternalFrame[] frames = desktopPane.getAllFrames();
3083 for (JInternalFrame frame : frames)
3085 if (frame instanceof SplitFrame && frame != source)
3087 SplitFrame sf = (SplitFrame) frame;
3088 AlignFrame topFrame = (AlignFrame) sf.getTopFrame();
3089 AlignFrame bottomFrame = (AlignFrame) sf.getBottomFrame();
3090 boolean gatherThis = false;
3091 for (int a = 0; a < topFrame.alignPanels.size(); a++)
3093 AlignmentPanel topPanel = topFrame.alignPanels.get(a);
3094 AlignmentPanel bottomPanel = bottomFrame.alignPanels.get(a);
3095 if (topViewId.equals(topPanel.av.getSequenceSetId())
3096 && bottomViewId.equals(bottomPanel.av.getSequenceSetId()))
3099 topPanel.av.setGatherViewsHere(false);
3100 bottomPanel.av.setGatherViewsHere(false);
3101 topPanel.av.setExplodedGeometry(
3102 new Rectangle(sf.getLocation(), topFrame.getSize()));
3103 bottomPanel.av.setExplodedGeometry(
3104 new Rectangle(sf.getLocation(), bottomFrame.getSize()));
3105 myTopFrame.addAlignmentPanel(topPanel, false);
3106 myBottomFrame.addAlignmentPanel(bottomPanel, false);
3112 topFrame.getAlignPanels().clear();
3113 bottomFrame.getAlignPanels().clear();
3120 * The dust settles...give focus to the tab we did this from.
3122 myTopFrame.setDisplayedView(myTopFrame.alignPanel);
3125 public static groovy.ui.Console getGroovyConsole()
3127 return groovyConsole;
3131 * handles the payload of a drag and drop event.
3133 * TODO refactor to desktop utilities class
3136 * - Data source strings extracted from the drop event
3138 * - protocol for each data source extracted from the drop event
3142 * - the payload from the drop event
3145 public static void transferFromDropTarget(List<Object> files,
3146 List<DataSourceType> protocols, DropTargetDropEvent evt,
3147 Transferable t) throws Exception
3150 // BH 2018 changed List<String> to List<Object> to allow for File from
3153 // DataFlavor[] flavors = t.getTransferDataFlavors();
3154 // for (int i = 0; i < flavors.length; i++) {
3155 // if (flavors[i].isFlavorJavaFileListType()) {
3156 // evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
3157 // List<File> list = (List<File>) t.getTransferData(flavors[i]);
3158 // for (int j = 0; j < list.size(); j++) {
3159 // File file = (File) list.get(j);
3160 // byte[] data = getDroppedFileBytes(file);
3161 // fileName.setText(file.getName() + " - " + data.length + " " +
3162 // evt.getLocation());
3163 // JTextArea target = (JTextArea) ((DropTarget)
3164 // evt.getSource()).getComponent();
3165 // target.setText(new String(data));
3167 // dtde.dropComplete(true);
3172 DataFlavor uriListFlavor = new DataFlavor(
3173 "text/uri-list;class=java.lang.String"), urlFlavour = null;
3176 urlFlavour = new DataFlavor(
3177 "application/x-java-url; class=java.net.URL");
3178 } catch (ClassNotFoundException cfe)
3180 Cache.log.debug("Couldn't instantiate the URL dataflavor.", cfe);
3183 if (urlFlavour != null && t.isDataFlavorSupported(urlFlavour))
3188 java.net.URL url = (URL) t.getTransferData(urlFlavour);
3189 // nb: java 8 osx bug https://bugs.openjdk.java.net/browse/JDK-8156099
3190 // means url may be null.
3193 protocols.add(DataSourceType.URL);
3194 files.add(url.toString());
3195 Cache.log.debug("Drop handled as URL dataflavor "
3196 + files.get(files.size() - 1));
3201 if (Platform.isAMacAndNotJS())
3204 "Please ignore plist error - occurs due to problem with java 8 on OSX");
3207 } catch (Throwable ex)
3209 Cache.log.debug("URL drop handler failed.", ex);
3212 if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
3214 // Works on Windows and MacOSX
3215 Cache.log.debug("Drop handled as javaFileListFlavor");
3216 for (Object file : (List) t
3217 .getTransferData(DataFlavor.javaFileListFlavor))
3220 protocols.add(DataSourceType.FILE);
3225 // Unix like behaviour
3226 boolean added = false;
3228 if (t.isDataFlavorSupported(uriListFlavor))
3230 Cache.log.debug("Drop handled as uriListFlavor");
3231 // This is used by Unix drag system
3232 data = (String) t.getTransferData(uriListFlavor);
3236 // fallback to text: workaround - on OSX where there's a JVM bug
3237 Cache.log.debug("standard URIListFlavor failed. Trying text");
3238 // try text fallback
3239 DataFlavor textDf = new DataFlavor(
3240 "text/plain;class=java.lang.String");
3241 if (t.isDataFlavorSupported(textDf))
3243 data = (String) t.getTransferData(textDf);
3246 Cache.log.debug("Plain text drop content returned "
3247 + (data == null ? "Null - failed" : data));
3252 while (protocols.size() < files.size())
3254 Cache.log.debug("Adding missing FILE protocol for "
3255 + files.get(protocols.size()));
3256 protocols.add(DataSourceType.FILE);
3258 for (java.util.StringTokenizer st = new java.util.StringTokenizer(
3259 data, "\r\n"); st.hasMoreTokens();)
3262 String s = st.nextToken();
3263 if (s.startsWith("#"))
3265 // the line is a comment (as per the RFC 2483)
3268 java.net.URI uri = new java.net.URI(s);
3269 if (uri.getScheme().toLowerCase().startsWith("http"))
3271 protocols.add(DataSourceType.URL);
3272 files.add(uri.toString());
3276 // otherwise preserve old behaviour: catch all for file objects
3277 java.io.File file = new java.io.File(uri);
3278 protocols.add(DataSourceType.FILE);
3279 files.add(file.toString());
3284 if (Cache.log.isDebugEnabled())
3286 if (data == null || !added)
3289 if (t.getTransferDataFlavors() != null
3290 && t.getTransferDataFlavors().length > 0)
3293 "Couldn't resolve drop data. Here are the supported flavors:");
3294 for (DataFlavor fl : t.getTransferDataFlavors())
3297 "Supported transfer dataflavor: " + fl.toString());
3298 Object df = t.getTransferData(fl);
3301 Cache.log.debug("Retrieves: " + df);
3305 Cache.log.debug("Retrieved nothing");
3311 Cache.log.debug("Couldn't resolve dataflavor for drop: "
3317 if (Platform.isWindowsAndNotJS())
3319 Cache.log.debug("Scanning dropped content for Windows Link Files");
3321 // resolve any .lnk files in the file drop
3322 for (int f = 0; f < files.size(); f++)
3324 String source = files.get(f).toString().toLowerCase();
3325 if (protocols.get(f).equals(DataSourceType.FILE)
3326 && (source.endsWith(".lnk") || source.endsWith(".url")
3327 || source.endsWith(".site")))
3331 Object obj = files.get(f);
3332 File lf = (obj instanceof File ? (File) obj
3333 : new File((String) obj));
3334 // process link file to get a URL
3335 Cache.log.debug("Found potential link file: " + lf);
3336 WindowsShortcut wscfile = new WindowsShortcut(lf);
3337 String fullname = wscfile.getRealFilename();
3338 protocols.set(f, FormatAdapter.checkProtocol(fullname));
3339 files.set(f, fullname);
3340 Cache.log.debug("Parsed real filename " + fullname
3341 + " to extract protocol: " + protocols.get(f));
3342 } catch (Exception ex)
3345 "Couldn't parse " + files.get(f) + " as a link file.",
3354 * Sets the Preferences property for experimental features to True or False
3355 * depending on the state of the controlling menu item
3358 protected void showExperimental_actionPerformed(boolean selected)
3360 Cache.setProperty(EXPERIMENTAL_FEATURES, Boolean.toString(selected));
3364 * Answers a (possibly empty) list of any structure viewer frames (currently
3365 * for either Jmol or Chimera) which are currently open. This may optionally
3366 * be restricted to viewers of a specified class, or viewers linked to a
3367 * specified alignment panel.
3370 * if not null, only return viewers linked to this panel
3371 * @param structureViewerClass
3372 * if not null, only return viewers of this class
3375 public List<StructureViewerBase> getStructureViewers(
3376 AlignmentPanel apanel,
3377 Class<? extends StructureViewerBase> structureViewerClass)
3379 List<StructureViewerBase> result = new ArrayList<>();
3380 JInternalFrame[] frames = getAllFrames();
3382 for (JInternalFrame frame : frames)
3384 if (frame instanceof StructureViewerBase)
3386 if (structureViewerClass == null
3387 || structureViewerClass.isInstance(frame))
3390 || ((StructureViewerBase) frame).isLinkedWith(apanel))
3392 result.add((StructureViewerBase) frame);