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.Graphics2D;
29 import java.awt.GridLayout;
30 import java.awt.Point;
31 import java.awt.Rectangle;
32 import java.awt.Toolkit;
33 import java.awt.Window;
34 import java.awt.datatransfer.Clipboard;
35 import java.awt.datatransfer.ClipboardOwner;
36 import java.awt.datatransfer.DataFlavor;
37 import java.awt.datatransfer.Transferable;
38 import java.awt.dnd.DnDConstants;
39 import java.awt.dnd.DropTargetDragEvent;
40 import java.awt.dnd.DropTargetDropEvent;
41 import java.awt.dnd.DropTargetEvent;
42 import java.awt.dnd.DropTargetListener;
43 import java.awt.event.ActionEvent;
44 import java.awt.event.ActionListener;
45 import java.awt.event.InputEvent;
46 import java.awt.event.KeyEvent;
47 import java.awt.event.MouseAdapter;
48 import java.awt.event.MouseEvent;
49 import java.awt.event.WindowAdapter;
50 import java.awt.event.WindowEvent;
51 import java.awt.geom.AffineTransform;
52 import java.beans.PropertyChangeEvent;
53 import java.beans.PropertyChangeListener;
55 import java.io.FileWriter;
56 import java.io.IOException;
58 import java.util.ArrayList;
59 import java.util.HashMap;
60 import java.util.Hashtable;
61 import java.util.List;
62 import java.util.ListIterator;
63 import java.util.Vector;
64 import java.util.concurrent.ExecutorService;
65 import java.util.concurrent.Executors;
66 import java.util.concurrent.Semaphore;
68 import javax.swing.AbstractAction;
69 import javax.swing.Action;
70 import javax.swing.ActionMap;
71 import javax.swing.Box;
72 import javax.swing.BoxLayout;
73 import javax.swing.DefaultDesktopManager;
74 import javax.swing.DesktopManager;
75 import javax.swing.InputMap;
76 import javax.swing.JButton;
77 import javax.swing.JCheckBox;
78 import javax.swing.JComboBox;
79 import javax.swing.JComponent;
80 import javax.swing.JDesktopPane;
81 import javax.swing.JInternalFrame;
82 import javax.swing.JLabel;
83 import javax.swing.JMenuItem;
84 import javax.swing.JPanel;
85 import javax.swing.JPopupMenu;
86 import javax.swing.JProgressBar;
87 import javax.swing.JTextField;
88 import javax.swing.KeyStroke;
89 import javax.swing.SwingUtilities;
90 import javax.swing.event.HyperlinkEvent;
91 import javax.swing.event.HyperlinkEvent.EventType;
92 import javax.swing.event.InternalFrameAdapter;
93 import javax.swing.event.InternalFrameEvent;
95 import org.stackoverflowusers.file.WindowsShortcut;
97 import jalview.api.AlignViewportI;
98 import jalview.api.AlignmentViewPanel;
99 import jalview.bin.Cache;
100 import jalview.bin.Jalview;
101 import jalview.gui.ImageExporter.ImageWriterI;
102 import jalview.io.BackupFiles;
103 import jalview.io.DataSourceType;
104 import jalview.io.FileFormat;
105 import jalview.io.FileFormatException;
106 import jalview.io.FileFormatI;
107 import jalview.io.FileFormats;
108 import jalview.io.FileLoader;
109 import jalview.io.FormatAdapter;
110 import jalview.io.IdentifyFile;
111 import jalview.io.JalviewFileChooser;
112 import jalview.io.JalviewFileView;
113 import jalview.jbgui.GSplitFrame;
114 import jalview.jbgui.GStructureViewer;
115 import jalview.project.Jalview2XML;
116 import jalview.structure.StructureSelectionManager;
117 import jalview.urls.IdOrgSettings;
118 import jalview.util.BrowserLauncher;
119 import jalview.util.ChannelProperties;
120 import jalview.util.ImageMaker.TYPE;
121 import jalview.util.MessageManager;
122 import jalview.util.Platform;
123 import jalview.util.ShortcutKeyMaskExWrapper;
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 public class Desktop extends jalview.jbgui.GDesktop
137 implements DropTargetListener, ClipboardOwner, IProgressIndicator,
138 jalview.api.StructureSelectionManagerProvider
140 private static final String CITATION = "<br><br>Development managed by The Barton Group, University of Dundee, Scotland, UK.<br>"
141 + "<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"
142 + "<br><br>If you use Jalview, please cite:"
143 + "<br>Waterhouse, A.M., Procter, J.B., Martin, D.M.A, Clamp, M. and Barton, G. J. (2009)"
144 + "<br>Jalview Version 2 - a multiple sequence alignment editor and analysis workbench"
145 + "<br>Bioinformatics doi: 10.1093/bioinformatics/btp033";
147 private static final String DEFAULT_AUTHORS = "The Jalview Authors (See AUTHORS file for current list)";
149 private static int DEFAULT_MIN_WIDTH = 300;
151 private static int DEFAULT_MIN_HEIGHT = 250;
153 private static int ALIGN_FRAME_DEFAULT_MIN_WIDTH = 600;
155 private static int ALIGN_FRAME_DEFAULT_MIN_HEIGHT = 70;
157 private static final String EXPERIMENTAL_FEATURES = "EXPERIMENTAL_FEATURES";
159 protected static final String CONFIRM_KEYBOARD_QUIT = "CONFIRM_KEYBOARD_QUIT";
161 public static HashMap<String, FileWriter> savingFiles = new HashMap<String, FileWriter>();
163 private JalviewChangeSupport changeSupport = new JalviewChangeSupport();
165 public static boolean nosplash = false;
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 /** Singleton Desktop instance */
210 public static Desktop instance;
212 public static MyDesktopPane desktop;
214 public static MyDesktopPane getDesktop()
216 // BH 2018 could use currentThread() here as a reference to a
217 // Hashtable<Thread, MyDesktopPane> in JavaScript
221 static int openFrameCount = 0;
223 static final int xOffset = 30;
225 static final int yOffset = 30;
227 public static jalview.ws.jws1.Discoverer discoverer;
229 public static Object[] jalviewClipboard;
231 public static boolean internalCopy = false;
233 static int fileLoadingCount = 0;
235 class MyDesktopManager implements DesktopManager
238 private DesktopManager delegate;
240 public MyDesktopManager(DesktopManager delegate)
242 this.delegate = delegate;
246 public void activateFrame(JInternalFrame f)
250 delegate.activateFrame(f);
251 } catch (NullPointerException npe)
253 Point p = getMousePosition();
254 instance.showPasteMenu(p.x, p.y);
259 public void beginDraggingFrame(JComponent f)
261 delegate.beginDraggingFrame(f);
265 public void beginResizingFrame(JComponent f, int direction)
267 delegate.beginResizingFrame(f, direction);
271 public void closeFrame(JInternalFrame f)
273 delegate.closeFrame(f);
277 public void deactivateFrame(JInternalFrame f)
279 delegate.deactivateFrame(f);
283 public void deiconifyFrame(JInternalFrame f)
285 delegate.deiconifyFrame(f);
289 public void dragFrame(JComponent f, int newX, int newY)
295 delegate.dragFrame(f, newX, newY);
299 public void endDraggingFrame(JComponent f)
301 delegate.endDraggingFrame(f);
306 public void endResizingFrame(JComponent f)
308 delegate.endResizingFrame(f);
313 public void iconifyFrame(JInternalFrame f)
315 delegate.iconifyFrame(f);
319 public void maximizeFrame(JInternalFrame f)
321 delegate.maximizeFrame(f);
325 public void minimizeFrame(JInternalFrame f)
327 delegate.minimizeFrame(f);
331 public void openFrame(JInternalFrame f)
333 delegate.openFrame(f);
337 public void resizeFrame(JComponent f, int newX, int newY, int newWidth,
344 delegate.resizeFrame(f, newX, newY, newWidth, newHeight);
348 public void setBoundsForFrame(JComponent f, int newX, int newY,
349 int newWidth, int newHeight)
351 delegate.setBoundsForFrame(f, newX, newY, newWidth, newHeight);
354 // All other methods, simply delegate
359 * Creates a new Desktop object.
365 * A note to implementors. It is ESSENTIAL that any activities that might
366 * block are spawned off as threads rather than waited for during this
371 doConfigureStructurePrefs();
372 setTitle(ChannelProperties.getProperty("app_name") + " "
373 + Cache.getProperty("VERSION"));
375 if (!Platform.isAMac())
377 // this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
381 this.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
387 APQHandlers.setAPQHandlers(this);
388 } catch (Throwable t)
390 System.out.println("Error setting APQHandlers: " + t.toString());
391 // t.printStackTrace();
394 addWindowListener(new WindowAdapter()
398 public void windowClosing(WindowEvent ev)
404 boolean selmemusage = Cache.getDefault("SHOW_MEMUSAGE", false);
406 boolean showjconsole = Cache.getDefault("SHOW_JAVA_CONSOLE", false);
407 desktop = new MyDesktopPane(selmemusage);
409 showMemusage.setSelected(selmemusage);
410 desktop.setBackground(Color.white);
412 getContentPane().setLayout(new BorderLayout());
413 // alternate config - have scrollbars - see notes in JAL-153
414 // JScrollPane sp = new JScrollPane();
415 // sp.getViewport().setView(desktop);
416 // getContentPane().add(sp, BorderLayout.CENTER);
418 // BH 2018 - just an experiment to try unclipped JInternalFrames.
421 getRootPane().putClientProperty("swingjs.overflow.hidden", "false");
424 getContentPane().add(desktop, BorderLayout.CENTER);
425 desktop.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
427 // This line prevents Windows Look&Feel resizing all new windows to maximum
428 // if previous window was maximised
429 desktop.setDesktopManager(new MyDesktopManager(
430 (Platform.isWindowsAndNotJS() ? new DefaultDesktopManager()
431 : Platform.isAMacAndNotJS()
432 ? new AquaInternalFrameManager(
433 desktop.getDesktopManager())
434 : desktop.getDesktopManager())));
436 Rectangle dims = getLastKnownDimensions("");
443 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
444 int xPos = Math.max(5, (screenSize.width - 900) / 2);
445 int yPos = Math.max(5, (screenSize.height - 650) / 2);
446 setBounds(xPos, yPos, 900, 650);
449 if (!Platform.isJS())
456 jconsole = new Console(this, showjconsole);
457 jconsole.setHeader(Cache.getVersionDetailsForConsole());
458 showConsole(showjconsole);
460 showNews.setVisible(false);
462 experimentalFeatures.setSelected(showExperimental());
464 getIdentifiersOrgData();
468 // Spawn a thread that shows the splashscreen
471 SwingUtilities.invokeLater(new Runnable()
476 new SplashScreen(true);
481 // Thread off a new instance of the file chooser - this reduces the time
483 // takes to open it later on.
484 new Thread(new Runnable()
489 Cache.log.debug("Filechooser init thread started.");
490 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
491 JalviewFileChooser.forRead(Cache.getProperty("LAST_DIRECTORY"),
493 Cache.log.debug("Filechooser init thread finished.");
496 // Add the service change listener
497 changeSupport.addJalviewPropertyChangeListener("services",
498 new PropertyChangeListener()
502 public void propertyChange(PropertyChangeEvent evt)
504 Cache.log.debug("Firing service changed event for "
505 + evt.getNewValue());
506 JalviewServicesChanged(evt);
511 this.setDropTarget(new java.awt.dnd.DropTarget(desktop, this));
513 this.addWindowListener(new WindowAdapter()
516 public void windowClosing(WindowEvent evt)
523 this.addMouseListener(ma = new MouseAdapter()
526 public void mousePressed(MouseEvent evt)
528 if (evt.isPopupTrigger()) // Mac
530 showPasteMenu(evt.getX(), evt.getY());
535 public void mouseReleased(MouseEvent evt)
537 if (evt.isPopupTrigger()) // Windows
539 showPasteMenu(evt.getX(), evt.getY());
543 desktop.addMouseListener(ma);
547 * Answers true if user preferences to enable experimental features is True
552 public boolean showExperimental()
554 String experimental = Cache.getDefault(EXPERIMENTAL_FEATURES,
555 Boolean.FALSE.toString());
556 return Boolean.valueOf(experimental).booleanValue();
559 public void doConfigureStructurePrefs()
561 // configure services
562 StructureSelectionManager ssm = StructureSelectionManager
563 .getStructureSelectionManager(this);
564 if (Cache.getDefault(Preferences.ADD_SS_ANN, true))
566 ssm.setAddTempFacAnnot(
567 Cache.getDefault(Preferences.ADD_TEMPFACT_ANN, true));
568 ssm.setProcessSecondaryStructure(
569 Cache.getDefault(Preferences.STRUCT_FROM_PDB, true));
570 ssm.setSecStructServices(
571 Cache.getDefault(Preferences.USE_RNAVIEW, true));
575 ssm.setAddTempFacAnnot(false);
576 ssm.setProcessSecondaryStructure(false);
577 ssm.setSecStructServices(false);
581 public void checkForNews()
583 final Desktop me = this;
584 // Thread off the news reader, in case there are connection problems.
585 new Thread(new Runnable()
590 Cache.log.debug("Starting news thread.");
591 jvnews = new BlogReader(me);
592 showNews.setVisible(true);
593 Cache.log.debug("Completed news thread.");
598 public void getIdentifiersOrgData()
600 // Thread off the identifiers fetcher
601 new Thread(new Runnable()
606 Cache.log.debug("Downloading data from identifiers.org");
609 UrlDownloadClient.download(IdOrgSettings.getUrl(),
610 IdOrgSettings.getDownloadLocation());
611 } catch (IOException e)
613 Cache.log.debug("Exception downloading identifiers.org data"
622 protected void showNews_actionPerformed(ActionEvent e)
624 showNews(showNews.isSelected());
627 void showNews(boolean visible)
629 Cache.log.debug((visible ? "Showing" : "Hiding") + " news.");
630 showNews.setSelected(visible);
631 if (visible && !jvnews.isVisible())
633 new Thread(new Runnable()
638 long now = System.currentTimeMillis();
639 Desktop.instance.setProgressBar(
640 MessageManager.getString("status.refreshing_news"), now);
641 jvnews.refreshNews();
642 Desktop.instance.setProgressBar(null, now);
650 * recover the last known dimensions for a jalview window
653 * - empty string is desktop, all other windows have unique prefix
654 * @return null or last known dimensions scaled to current geometry (if last
655 * window geom was known)
657 Rectangle getLastKnownDimensions(String windowName)
659 // TODO: lock aspect ratio for scaling desktop Bug #0058199
660 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
661 String x = Cache.getProperty(windowName + "SCREEN_X");
662 String y = Cache.getProperty(windowName + "SCREEN_Y");
663 String width = Cache.getProperty(windowName + "SCREEN_WIDTH");
664 String height = Cache.getProperty(windowName + "SCREEN_HEIGHT");
665 if ((x != null) && (y != null) && (width != null) && (height != null))
667 int ix = Integer.parseInt(x), iy = Integer.parseInt(y),
668 iw = Integer.parseInt(width), ih = Integer.parseInt(height);
669 if (Cache.getProperty("SCREENGEOMETRY_WIDTH") != null)
671 // attempt #1 - try to cope with change in screen geometry - this
672 // version doesn't preserve original jv aspect ratio.
673 // take ratio of current screen size vs original screen size.
674 double sw = ((1f * screenSize.width) / (1f * Integer
675 .parseInt(Cache.getProperty("SCREENGEOMETRY_WIDTH"))));
676 double sh = ((1f * screenSize.height) / (1f * Integer
677 .parseInt(Cache.getProperty("SCREENGEOMETRY_HEIGHT"))));
678 // rescale the bounds depending upon the current screen geometry.
679 ix = (int) (ix * sw);
680 iw = (int) (iw * sw);
681 iy = (int) (iy * sh);
682 ih = (int) (ih * sh);
683 while (ix >= screenSize.width)
686 "Window geometry location recall error: shifting horizontal to within screenbounds.");
687 ix -= screenSize.width;
689 while (iy >= screenSize.height)
692 "Window geometry location recall error: shifting vertical to within screenbounds.");
693 iy -= screenSize.height;
696 "Got last known dimensions for " + windowName + ": x:" + ix
697 + " y:" + iy + " width:" + iw + " height:" + ih);
699 // return dimensions for new instance
700 return new Rectangle(ix, iy, iw, ih);
705 void showPasteMenu(int x, int y)
707 JPopupMenu popup = new JPopupMenu();
708 JMenuItem item = new JMenuItem(
709 MessageManager.getString("label.paste_new_window"));
710 item.addActionListener(new ActionListener()
713 public void actionPerformed(ActionEvent evt)
720 popup.show(this, x, y);
727 Clipboard c = Toolkit.getDefaultToolkit().getSystemClipboard();
728 Transferable contents = c.getContents(this);
730 if (contents != null)
732 String file = (String) contents
733 .getTransferData(DataFlavor.stringFlavor);
735 FileFormatI format = new IdentifyFile().identify(file,
736 DataSourceType.PASTE);
738 new FileLoader().LoadFile(file, DataSourceType.PASTE, format);
741 } catch (Exception ex)
744 "Unable to paste alignment from system clipboard:\n" + ex);
749 * Adds and opens the given frame to the desktop
760 public static synchronized void addInternalFrame(
761 final JInternalFrame frame, String title, int w, int h)
763 addInternalFrame(frame, title, true, w, h, true, false);
767 * Add an internal frame to the Jalview desktop
774 * When true, display frame immediately, otherwise, caller must call
775 * setVisible themselves.
781 public static synchronized void addInternalFrame(
782 final JInternalFrame frame, String title, boolean makeVisible,
785 addInternalFrame(frame, title, makeVisible, w, h, true, false);
789 * Add an internal frame to the Jalview desktop and make it visible
802 public static synchronized void addInternalFrame(
803 final JInternalFrame frame, String title, int w, int h,
806 addInternalFrame(frame, title, true, w, h, resizable, false);
810 * Add an internal frame to the Jalview desktop
817 * When true, display frame immediately, otherwise, caller must call
818 * setVisible themselves.
825 * @param ignoreMinSize
826 * Do not set the default minimum size for frame
828 public static synchronized void addInternalFrame(
829 final JInternalFrame frame, String title, boolean makeVisible,
830 int w, int h, boolean resizable, boolean ignoreMinSize)
833 // TODO: allow callers to determine X and Y position of frame (eg. via
835 // TODO: consider fixing method to update entries in the window submenu with
836 // the current window title
838 frame.setTitle(title);
839 if (frame.getWidth() < 1 || frame.getHeight() < 1)
843 // THIS IS A PUBLIC STATIC METHOD, SO IT MAY BE CALLED EVEN IN
844 // A HEADLESS STATE WHEN NO DESKTOP EXISTS. MUST RETURN
845 // IF JALVIEW IS RUNNING HEADLESS
846 // ///////////////////////////////////////////////
847 if (instance == null || (System.getProperty("java.awt.headless") != null
848 && System.getProperty("java.awt.headless").equals("true")))
857 frame.setMinimumSize(
858 new Dimension(DEFAULT_MIN_WIDTH, DEFAULT_MIN_HEIGHT));
860 // Set default dimension for Alignment Frame window.
861 // The Alignment Frame window could be added from a number of places,
863 // I did this here in order not to miss out on any Alignment frame.
864 if (frame instanceof AlignFrame)
866 frame.setMinimumSize(new Dimension(ALIGN_FRAME_DEFAULT_MIN_WIDTH,
867 ALIGN_FRAME_DEFAULT_MIN_HEIGHT));
871 frame.setVisible(makeVisible);
872 frame.setClosable(true);
873 frame.setResizable(resizable);
874 frame.setMaximizable(resizable);
875 frame.setIconifiable(resizable);
876 frame.setOpaque(Platform.isJS());
878 if (frame.getX() < 1 && frame.getY() < 1)
880 frame.setLocation(xOffset * openFrameCount,
881 yOffset * ((openFrameCount - 1) % 10) + yOffset);
885 * add an entry for the new frame in the Window menu
886 * (and remove it when the frame is closed)
888 final JMenuItem menuItem = new JMenuItem(title);
889 frame.addInternalFrameListener(new InternalFrameAdapter()
892 public void internalFrameActivated(InternalFrameEvent evt)
894 JInternalFrame itf = desktop.getSelectedFrame();
897 if (itf instanceof AlignFrame)
899 Jalview.setCurrentAlignFrame((AlignFrame) itf);
906 public void internalFrameClosed(InternalFrameEvent evt)
908 PaintRefresher.RemoveComponent(frame);
911 * defensive check to prevent frames being
912 * added half off the window
914 if (openFrameCount > 0)
920 * ensure no reference to alignFrame retained by menu item listener
922 if (menuItem.getActionListeners().length > 0)
924 menuItem.removeActionListener(menuItem.getActionListeners()[0]);
926 windowMenu.remove(menuItem);
930 menuItem.addActionListener(new ActionListener()
933 public void actionPerformed(ActionEvent e)
937 frame.setSelected(true);
938 frame.setIcon(false);
939 } catch (java.beans.PropertyVetoException ex)
941 // System.err.println(ex.toString());
946 setKeyBindings(frame);
950 windowMenu.add(menuItem);
955 frame.setSelected(true);
956 frame.requestFocus();
957 } catch (java.beans.PropertyVetoException ve)
959 } catch (java.lang.ClassCastException cex)
962 "Squashed a possible GUI implementation error. If you can recreate this, please look at http://issues.jalview.org/browse/JAL-869",
968 * Add key bindings to a JInternalFrame so that Ctrl-W and Cmd-W will close
973 private static void setKeyBindings(JInternalFrame frame)
975 @SuppressWarnings("serial")
976 final Action closeAction = new AbstractAction()
979 public void actionPerformed(ActionEvent e)
986 * set up key bindings for Ctrl-W and Cmd-W, with the same (Close) action
988 KeyStroke ctrlWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
989 InputEvent.CTRL_DOWN_MASK);
990 KeyStroke cmdWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
991 ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx());
993 InputMap inputMap = frame
994 .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
995 String ctrlW = ctrlWKey.toString();
996 inputMap.put(ctrlWKey, ctrlW);
997 inputMap.put(cmdWKey, ctrlW);
999 ActionMap actionMap = frame.getActionMap();
1000 actionMap.put(ctrlW, closeAction);
1004 public void lostOwnership(Clipboard clipboard, Transferable contents)
1008 Desktop.jalviewClipboard = null;
1011 internalCopy = false;
1015 public void dragEnter(DropTargetDragEvent evt)
1020 public void dragExit(DropTargetEvent evt)
1025 public void dragOver(DropTargetDragEvent evt)
1030 public void dropActionChanged(DropTargetDragEvent evt)
1041 public void drop(DropTargetDropEvent evt)
1043 boolean success = true;
1044 // JAL-1552 - acceptDrop required before getTransferable call for
1045 // Java's Transferable for native dnd
1046 evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
1047 Transferable t = evt.getTransferable();
1048 List<Object> files = new ArrayList<>();
1049 List<DataSourceType> protocols = new ArrayList<>();
1053 Desktop.transferFromDropTarget(files, protocols, evt, t);
1054 } catch (Exception e)
1056 e.printStackTrace();
1064 for (int i = 0; i < files.size(); i++)
1066 // BH 2018 File or String
1067 Object file = files.get(i);
1068 String fileName = file.toString();
1069 DataSourceType protocol = (protocols == null)
1070 ? DataSourceType.FILE
1072 FileFormatI format = null;
1074 if (fileName.endsWith(".jar"))
1076 format = FileFormat.Jalview;
1081 format = new IdentifyFile().identify(file, protocol);
1083 if (file instanceof File)
1085 Platform.cacheFileData((File) file);
1087 new FileLoader().LoadFile(null, file, protocol, format);
1090 } catch (Exception ex)
1095 evt.dropComplete(success); // need this to ensure input focus is properly
1096 // transfered to any new windows created
1106 public void inputLocalFileMenuItem_actionPerformed(AlignViewport viewport)
1108 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
1109 JalviewFileChooser chooser = JalviewFileChooser.forRead(
1110 Cache.getProperty("LAST_DIRECTORY"), fileFormat,
1111 BackupFiles.getEnabled());
1113 chooser.setFileView(new JalviewFileView());
1114 chooser.setDialogTitle(
1115 MessageManager.getString("label.open_local_file"));
1116 chooser.setToolTipText(MessageManager.getString("action.open"));
1118 chooser.setResponseHandler(0, new Runnable()
1123 File selectedFile = chooser.getSelectedFile();
1124 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1126 FileFormatI format = chooser.getSelectedFormat();
1129 * Call IdentifyFile to verify the file contains what its extension implies.
1130 * Skip this step for dynamically added file formats, because
1131 * IdentifyFile does not know how to recognise them.
1133 if (FileFormats.getInstance().isIdentifiable(format))
1137 format = new IdentifyFile().identify(selectedFile,
1138 DataSourceType.FILE);
1139 } catch (FileFormatException e)
1141 // format = null; //??
1145 new FileLoader().LoadFile(viewport, selectedFile,
1146 DataSourceType.FILE, format);
1149 chooser.showOpenDialog(this);
1153 * Shows a dialog for input of a URL at which to retrieve alignment data
1158 public void inputURLMenuItem_actionPerformed(AlignViewport viewport)
1160 // This construct allows us to have a wider textfield
1162 JLabel label = new JLabel(
1163 MessageManager.getString("label.input_file_url"));
1165 JPanel panel = new JPanel(new GridLayout(2, 1));
1169 * the URL to fetch is
1170 * Java: an editable combobox with history
1171 * JS: (pending JAL-3038) a plain text field
1174 String urlBase = "http://www.";
1175 if (Platform.isJS())
1177 history = new JTextField(urlBase, 35);
1186 JComboBox<String> asCombo = new JComboBox<>();
1187 asCombo.setPreferredSize(new Dimension(400, 20));
1188 asCombo.setEditable(true);
1189 asCombo.addItem(urlBase);
1190 String historyItems = Cache.getProperty("RECENT_URL");
1191 if (historyItems != null)
1193 for (String token : historyItems.split("\\t"))
1195 asCombo.addItem(token);
1202 Object[] options = new Object[] { MessageManager.getString("action.ok"),
1203 MessageManager.getString("action.cancel") };
1204 Runnable action = new Runnable()
1209 @SuppressWarnings("unchecked")
1210 String url = (history instanceof JTextField
1211 ? ((JTextField) history).getText()
1212 : ((JComboBox<String>) history).getSelectedItem()
1215 if (url.toLowerCase().endsWith(".jar"))
1217 if (viewport != null)
1219 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1220 FileFormat.Jalview);
1224 new FileLoader().LoadFile(url, DataSourceType.URL,
1225 FileFormat.Jalview);
1230 FileFormatI format = null;
1233 format = new IdentifyFile().identify(url, DataSourceType.URL);
1234 } catch (FileFormatException e)
1236 // TODO revise error handling, distinguish between
1237 // URL not found and response not valid
1242 String msg = MessageManager
1243 .formatMessage("label.couldnt_locate", url);
1244 JvOptionPane.showInternalMessageDialog(Desktop.desktop, msg,
1245 MessageManager.getString("label.url_not_found"),
1246 JvOptionPane.WARNING_MESSAGE);
1251 if (viewport != null)
1253 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1258 new FileLoader().LoadFile(url, DataSourceType.URL, format);
1263 String dialogOption = MessageManager
1264 .getString("label.input_alignment_from_url");
1265 JvOptionPane.newOptionDialog(desktop).setResponseHandler(0, action)
1266 .showInternalDialog(panel, dialogOption,
1267 JvOptionPane.YES_NO_CANCEL_OPTION,
1268 JvOptionPane.PLAIN_MESSAGE, null, options,
1269 MessageManager.getString("action.ok"));
1273 * Opens the CutAndPaste window for the user to paste an alignment in to
1276 * - if not null, the pasted alignment is added to the current
1277 * alignment; if null, to a new alignment window
1280 public void inputTextboxMenuItem_actionPerformed(
1281 AlignmentViewPanel viewPanel)
1283 CutAndPasteTransfer cap = new CutAndPasteTransfer();
1284 cap.setForInput(viewPanel);
1285 Desktop.addInternalFrame(cap,
1286 MessageManager.getString("label.cut_paste_alignmen_file"), true,
1296 Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
1297 Cache.setProperty("SCREENGEOMETRY_WIDTH", screen.width + "");
1298 Cache.setProperty("SCREENGEOMETRY_HEIGHT", screen.height + "");
1299 storeLastKnownDimensions("", new Rectangle(getBounds().x, getBounds().y,
1300 getWidth(), getHeight()));
1302 if (jconsole != null)
1304 storeLastKnownDimensions("JAVA_CONSOLE_", jconsole.getBounds());
1305 jconsole.stopConsole();
1309 storeLastKnownDimensions("JALVIEW_RSS_WINDOW_", jvnews.getBounds());
1312 if (dialogExecutor != null)
1314 dialogExecutor.shutdownNow();
1316 closeAll_actionPerformed(null);
1318 if (groovyConsole != null)
1320 // suppress a possible repeat prompt to save script
1321 groovyConsole.setDirty(false);
1322 groovyConsole.exit();
1327 private void storeLastKnownDimensions(String string, Rectangle jc)
1329 Cache.log.debug("Storing last known dimensions for " + string + ": x:"
1330 + jc.x + " y:" + jc.y + " width:" + jc.width + " height:"
1333 Cache.setProperty(string + "SCREEN_X", jc.x + "");
1334 Cache.setProperty(string + "SCREEN_Y", jc.y + "");
1335 Cache.setProperty(string + "SCREEN_WIDTH", jc.width + "");
1336 Cache.setProperty(string + "SCREEN_HEIGHT", jc.height + "");
1346 public void aboutMenuItem_actionPerformed(ActionEvent e)
1348 new Thread(new Runnable()
1353 new SplashScreen(false);
1359 * Returns the html text for the About screen, including any available version
1360 * number, build details, author details and citation reference, but without
1361 * the enclosing {@code html} tags
1365 public String getAboutMessage()
1367 StringBuilder message = new StringBuilder(1024);
1368 message.append("<h1><strong>Version: ")
1369 .append(Cache.getProperty("VERSION")).append("</strong></h1>")
1370 .append("<strong>Built: <em>")
1371 .append(Cache.getDefault("BUILD_DATE", "unknown"))
1372 .append("</em> from ").append(Cache.getBuildDetailsForSplash())
1373 .append("</strong>");
1375 String latestVersion = Cache.getDefault("LATEST_VERSION", "Checking");
1376 if (latestVersion.equals("Checking"))
1378 // JBP removed this message for 2.11: May be reinstated in future version
1379 // message.append("<br>...Checking latest version...</br>");
1381 else if (!latestVersion.equals(Cache.getProperty("VERSION")))
1383 boolean red = false;
1384 if (Cache.getProperty("VERSION").toLowerCase()
1385 .indexOf("automated build") == -1)
1388 // Displayed when code version and jnlp version do not match and code
1389 // version is not a development build
1390 message.append("<div style=\"color: #FF0000;font-style: bold;\">");
1393 message.append("<br>!! Version ")
1394 .append(Cache.getDefault("LATEST_VERSION", "..Checking.."))
1395 .append(" is available for download from ")
1396 .append(Cache.getDefault("www.jalview.org",
1397 "http://www.jalview.org"))
1401 message.append("</div>");
1404 message.append("<br>Authors: ");
1405 message.append(Cache.getDefault("AUTHORFNAMES", DEFAULT_AUTHORS));
1406 message.append(CITATION);
1408 return message.toString();
1412 * Action on requesting Help documentation
1415 public void documentationMenuItem_actionPerformed()
1419 if (Platform.isJS())
1421 BrowserLauncher.openURL("http://www.jalview.org/help.html");
1430 Help.showHelpWindow();
1432 } catch (Exception ex)
1434 System.err.println("Error opening help: " + ex.getMessage());
1439 public void closeAll_actionPerformed(ActionEvent e)
1441 // TODO show a progress bar while closing?
1442 JInternalFrame[] frames = desktop.getAllFrames();
1443 for (int i = 0; i < frames.length; i++)
1447 frames[i].setClosed(true);
1448 } catch (java.beans.PropertyVetoException ex)
1452 Jalview.setCurrentAlignFrame(null);
1453 System.out.println("ALL CLOSED");
1456 * reset state of singleton objects as appropriate (clear down session state
1457 * when all windows are closed)
1459 StructureSelectionManager ssm = StructureSelectionManager
1460 .getStructureSelectionManager(this);
1468 public void raiseRelated_actionPerformed(ActionEvent e)
1470 reorderAssociatedWindows(false, false);
1474 public void minimizeAssociated_actionPerformed(ActionEvent e)
1476 reorderAssociatedWindows(true, false);
1479 void closeAssociatedWindows()
1481 reorderAssociatedWindows(false, true);
1487 * @seejalview.jbgui.GDesktop#garbageCollect_actionPerformed(java.awt.event.
1491 protected void garbageCollect_actionPerformed(ActionEvent e)
1493 // We simply collect the garbage
1494 Cache.log.debug("Collecting garbage...");
1496 Cache.log.debug("Finished garbage collection.");
1503 * jalview.jbgui.GDesktop#showMemusage_actionPerformed(java.awt.event.ActionEvent
1507 protected void showMemusage_actionPerformed(ActionEvent e)
1509 desktop.showMemoryUsage(showMemusage.isSelected());
1516 * jalview.jbgui.GDesktop#showConsole_actionPerformed(java.awt.event.ActionEvent
1520 protected void showConsole_actionPerformed(ActionEvent e)
1522 showConsole(showConsole.isSelected());
1525 Console jconsole = null;
1528 * control whether the java console is visible or not
1532 void showConsole(boolean selected)
1534 // TODO: decide if we should update properties file
1535 if (jconsole != null) // BH 2018
1537 showConsole.setSelected(selected);
1538 Cache.setProperty("SHOW_JAVA_CONSOLE",
1539 Boolean.valueOf(selected).toString());
1540 jconsole.setVisible(selected);
1544 void reorderAssociatedWindows(boolean minimize, boolean close)
1546 JInternalFrame[] frames = desktop.getAllFrames();
1547 if (frames == null || frames.length < 1)
1552 AlignmentViewport source = null, target = null;
1553 if (frames[0] instanceof AlignFrame)
1555 source = ((AlignFrame) frames[0]).getCurrentView();
1557 else if (frames[0] instanceof TreePanel)
1559 source = ((TreePanel) frames[0]).getViewPort();
1561 else if (frames[0] instanceof PCAPanel)
1563 source = ((PCAPanel) frames[0]).av;
1565 else if (frames[0].getContentPane() instanceof PairwiseAlignPanel)
1567 source = ((PairwiseAlignPanel) frames[0].getContentPane()).av;
1572 for (int i = 0; i < frames.length; i++)
1575 if (frames[i] == null)
1579 if (frames[i] instanceof AlignFrame)
1581 target = ((AlignFrame) frames[i]).getCurrentView();
1583 else if (frames[i] instanceof TreePanel)
1585 target = ((TreePanel) frames[i]).getViewPort();
1587 else if (frames[i] instanceof PCAPanel)
1589 target = ((PCAPanel) frames[i]).av;
1591 else if (frames[i].getContentPane() instanceof PairwiseAlignPanel)
1593 target = ((PairwiseAlignPanel) frames[i].getContentPane()).av;
1596 if (source == target)
1602 frames[i].setClosed(true);
1606 frames[i].setIcon(minimize);
1609 frames[i].toFront();
1613 } catch (java.beans.PropertyVetoException ex)
1628 protected void preferences_actionPerformed(ActionEvent e)
1630 Preferences.openPreferences();
1634 * Prompts the user to choose a file and then saves the Jalview state as a
1635 * Jalview project file
1638 public void saveState_actionPerformed()
1640 saveState_actionPerformed(false);
1643 public void saveState_actionPerformed(boolean saveAs)
1645 java.io.File projectFile = getProjectFile();
1646 // autoSave indicates we already have a file and don't need to ask
1647 boolean autoSave = projectFile != null && !saveAs
1648 && BackupFiles.getEnabled();
1650 // System.out.println("autoSave="+autoSave+", projectFile='"+projectFile+"',
1651 // saveAs="+saveAs+", Backups
1652 // "+(BackupFiles.getEnabled()?"enabled":"disabled"));
1654 boolean approveSave = false;
1657 JalviewFileChooser chooser = new JalviewFileChooser("jvp",
1660 chooser.setFileView(new JalviewFileView());
1661 chooser.setDialogTitle(MessageManager.getString("label.save_state"));
1663 int value = chooser.showSaveDialog(this);
1665 if (value == JalviewFileChooser.APPROVE_OPTION)
1667 projectFile = chooser.getSelectedFile();
1668 setProjectFile(projectFile);
1673 if (approveSave || autoSave)
1675 final Desktop me = this;
1676 final java.io.File chosenFile = projectFile;
1677 new Thread(new Runnable()
1682 // TODO: refactor to Jalview desktop session controller action.
1683 setProgressBar(MessageManager.formatMessage(
1684 "label.saving_jalview_project", new Object[]
1685 { chosenFile.getName() }), chosenFile.hashCode());
1686 Cache.setProperty("LAST_DIRECTORY", chosenFile.getParent());
1687 // TODO catch and handle errors for savestate
1688 // TODO prevent user from messing with the Desktop whilst we're saving
1691 boolean doBackup = BackupFiles.getEnabled();
1692 BackupFiles backupfiles = doBackup ? new BackupFiles(chosenFile)
1695 new Jalview2XML().saveState(
1696 doBackup ? backupfiles.getTempFile() : chosenFile);
1700 backupfiles.setWriteSuccess(true);
1701 backupfiles.rollBackupsAndRenameTempFile();
1703 } catch (OutOfMemoryError oom)
1705 new OOMWarning("Whilst saving current state to "
1706 + chosenFile.getName(), oom);
1707 } catch (Exception ex)
1709 Cache.log.error("Problems whilst trying to save to "
1710 + chosenFile.getName(), ex);
1711 JvOptionPane.showMessageDialog(me,
1712 MessageManager.formatMessage(
1713 "label.error_whilst_saving_current_state_to",
1715 { chosenFile.getName() }),
1716 MessageManager.getString("label.couldnt_save_project"),
1717 JvOptionPane.WARNING_MESSAGE);
1719 setProgressBar(null, chosenFile.hashCode());
1726 public void saveAsState_actionPerformed(ActionEvent e)
1728 saveState_actionPerformed(true);
1731 private void setProjectFile(File choice)
1733 this.projectFile = choice;
1736 public File getProjectFile()
1738 return this.projectFile;
1742 * Shows a file chooser dialog and tries to read in the selected file as a
1746 public void loadState_actionPerformed()
1748 final String[] suffix = new String[] { "jvp", "jar" };
1749 final String[] desc = new String[] { "Jalview Project",
1750 "Jalview Project (old)" };
1751 JalviewFileChooser chooser = new JalviewFileChooser(
1752 Cache.getProperty("LAST_DIRECTORY"), suffix, desc,
1753 "Jalview Project", true, BackupFiles.getEnabled()); // last two
1757 chooser.setFileView(new JalviewFileView());
1758 chooser.setDialogTitle(MessageManager.getString("label.restore_state"));
1759 chooser.setResponseHandler(0, new Runnable()
1764 File selectedFile = chooser.getSelectedFile();
1765 setProjectFile(selectedFile);
1766 String choice = selectedFile.getAbsolutePath();
1767 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1768 new Thread(new Runnable()
1775 new Jalview2XML().loadJalviewAlign(selectedFile);
1776 } catch (OutOfMemoryError oom)
1778 new OOMWarning("Whilst loading project from " + choice, oom);
1779 } catch (Exception ex)
1782 "Problems whilst loading project from " + choice, ex);
1783 JvOptionPane.showMessageDialog(Desktop.desktop,
1784 MessageManager.formatMessage(
1785 "label.error_whilst_loading_project_from",
1789 .getString("label.couldnt_load_project"),
1790 JvOptionPane.WARNING_MESSAGE);
1793 }, "Project Loader").start();
1797 chooser.showOpenDialog(this);
1801 public void inputSequence_actionPerformed(ActionEvent e)
1803 new SequenceFetcher(this);
1806 JPanel progressPanel;
1808 ArrayList<JPanel> fileLoadingPanels = new ArrayList<>();
1810 public void startLoading(final Object fileName)
1812 if (fileLoadingCount == 0)
1814 fileLoadingPanels.add(addProgressPanel(MessageManager
1815 .formatMessage("label.loading_file", new Object[]
1821 private JPanel addProgressPanel(String string)
1823 if (progressPanel == null)
1825 progressPanel = new JPanel(new GridLayout(1, 1));
1826 totalProgressCount = 0;
1827 instance.getContentPane().add(progressPanel, BorderLayout.SOUTH);
1829 JPanel thisprogress = new JPanel(new BorderLayout(10, 5));
1830 JProgressBar progressBar = new JProgressBar();
1831 progressBar.setIndeterminate(true);
1833 thisprogress.add(new JLabel(string), BorderLayout.WEST);
1835 thisprogress.add(progressBar, BorderLayout.CENTER);
1836 progressPanel.add(thisprogress);
1837 ((GridLayout) progressPanel.getLayout()).setRows(
1838 ((GridLayout) progressPanel.getLayout()).getRows() + 1);
1839 ++totalProgressCount;
1840 instance.validate();
1841 return thisprogress;
1844 int totalProgressCount = 0;
1846 private void removeProgressPanel(JPanel progbar)
1848 if (progressPanel != null)
1850 synchronized (progressPanel)
1852 progressPanel.remove(progbar);
1853 GridLayout gl = (GridLayout) progressPanel.getLayout();
1854 gl.setRows(gl.getRows() - 1);
1855 if (--totalProgressCount < 1)
1857 this.getContentPane().remove(progressPanel);
1858 progressPanel = null;
1865 public void stopLoading()
1868 if (fileLoadingCount < 1)
1870 while (fileLoadingPanels.size() > 0)
1872 removeProgressPanel(fileLoadingPanels.remove(0));
1874 fileLoadingPanels.clear();
1875 fileLoadingCount = 0;
1880 public static int getViewCount(String alignmentId)
1882 AlignmentViewport[] aps = getViewports(alignmentId);
1883 return (aps == null) ? 0 : aps.length;
1888 * @param alignmentId
1889 * - if null, all sets are returned
1890 * @return all AlignmentPanels concerning the alignmentId sequence set
1892 public static AlignmentPanel[] getAlignmentPanels(String alignmentId)
1894 if (Desktop.desktop == null)
1896 // no frames created and in headless mode
1897 // TODO: verify that frames are recoverable when in headless mode
1900 List<AlignmentPanel> aps = new ArrayList<>();
1901 AlignFrame[] frames = getAlignFrames();
1906 for (AlignFrame af : frames)
1908 for (AlignmentPanel ap : af.alignPanels)
1910 if (alignmentId == null
1911 || alignmentId.equals(ap.av.getSequenceSetId()))
1917 if (aps.size() == 0)
1921 AlignmentPanel[] vap = aps.toArray(new AlignmentPanel[aps.size()]);
1926 * get all the viewports on an alignment.
1928 * @param sequenceSetId
1929 * unique alignment id (may be null - all viewports returned in that
1931 * @return all viewports on the alignment bound to sequenceSetId
1933 public static AlignmentViewport[] getViewports(String sequenceSetId)
1935 List<AlignmentViewport> viewp = new ArrayList<>();
1936 if (desktop != null)
1938 AlignFrame[] frames = Desktop.getAlignFrames();
1940 for (AlignFrame afr : frames)
1942 if (sequenceSetId == null || afr.getViewport().getSequenceSetId()
1943 .equals(sequenceSetId))
1945 if (afr.alignPanels != null)
1947 for (AlignmentPanel ap : afr.alignPanels)
1949 if (sequenceSetId == null
1950 || sequenceSetId.equals(ap.av.getSequenceSetId()))
1958 viewp.add(afr.getViewport());
1962 if (viewp.size() > 0)
1964 return viewp.toArray(new AlignmentViewport[viewp.size()]);
1971 * Explode the views in the given frame into separate AlignFrame
1975 public static void explodeViews(AlignFrame af)
1977 int size = af.alignPanels.size();
1983 // FIXME: ideally should use UI interface API
1984 FeatureSettings viewFeatureSettings = (af.featureSettings != null
1985 && af.featureSettings.isOpen()) ? af.featureSettings : null;
1986 Rectangle fsBounds = af.getFeatureSettingsGeometry();
1987 for (int i = 0; i < size; i++)
1989 AlignmentPanel ap = af.alignPanels.get(i);
1991 AlignFrame newaf = new AlignFrame(ap);
1993 // transfer reference for existing feature settings to new alignFrame
1994 if (ap == af.alignPanel)
1996 if (viewFeatureSettings != null && viewFeatureSettings.fr.ap == ap)
1998 newaf.featureSettings = viewFeatureSettings;
2000 newaf.setFeatureSettingsGeometry(fsBounds);
2004 * Restore the view's last exploded frame geometry if known. Multiple
2005 * views from one exploded frame share and restore the same (frame)
2006 * position and size.
2008 Rectangle geometry = ap.av.getExplodedGeometry();
2009 if (geometry != null)
2011 newaf.setBounds(geometry);
2014 ap.av.setGatherViewsHere(false);
2016 addInternalFrame(newaf, af.getTitle(), AlignFrame.DEFAULT_WIDTH,
2017 AlignFrame.DEFAULT_HEIGHT);
2018 // and materialise a new feature settings dialog instance for the new
2020 // (closes the old as if 'OK' was pressed)
2021 if (ap == af.alignPanel && newaf.featureSettings != null
2022 && newaf.featureSettings.isOpen()
2023 && af.alignPanel.getAlignViewport().isShowSequenceFeatures())
2025 newaf.showFeatureSettingsUI();
2029 af.featureSettings = null;
2030 af.alignPanels.clear();
2031 af.closeMenuItem_actionPerformed(true);
2036 * Gather expanded views (separate AlignFrame's) with the same sequence set
2037 * identifier back in to this frame as additional views, and close the
2038 * expanded views. Note the expanded frames may themselves have multiple
2039 * views. We take the lot.
2043 public void gatherViews(AlignFrame source)
2045 source.viewport.setGatherViewsHere(true);
2046 source.viewport.setExplodedGeometry(source.getBounds());
2047 JInternalFrame[] frames = desktop.getAllFrames();
2048 String viewId = source.viewport.getSequenceSetId();
2049 for (int t = 0; t < frames.length; t++)
2051 if (frames[t] instanceof AlignFrame && frames[t] != source)
2053 AlignFrame af = (AlignFrame) frames[t];
2054 boolean gatherThis = false;
2055 for (int a = 0; a < af.alignPanels.size(); a++)
2057 AlignmentPanel ap = af.alignPanels.get(a);
2058 if (viewId.equals(ap.av.getSequenceSetId()))
2061 ap.av.setGatherViewsHere(false);
2062 ap.av.setExplodedGeometry(af.getBounds());
2063 source.addAlignmentPanel(ap, false);
2069 if (af.featureSettings != null && af.featureSettings.isOpen())
2071 if (source.featureSettings == null)
2073 // preserve the feature settings geometry for this frame
2074 source.featureSettings = af.featureSettings;
2075 source.setFeatureSettingsGeometry(
2076 af.getFeatureSettingsGeometry());
2080 // close it and forget
2081 af.featureSettings.close();
2084 af.alignPanels.clear();
2085 af.closeMenuItem_actionPerformed(true);
2090 // refresh the feature setting UI for the source frame if it exists
2091 if (source.featureSettings != null && source.featureSettings.isOpen())
2093 source.showFeatureSettingsUI();
2097 public JInternalFrame[] getAllFrames()
2099 return desktop.getAllFrames();
2103 * Checks the given url to see if it gives a response indicating that the user
2104 * should be informed of a new questionnaire.
2108 public void checkForQuestionnaire(String url)
2110 UserQuestionnaireCheck jvq = new UserQuestionnaireCheck(url);
2111 // javax.swing.SwingUtilities.invokeLater(jvq);
2112 new Thread(jvq).start();
2115 public void checkURLLinks()
2117 // Thread off the URL link checker
2118 addDialogThread(new Runnable()
2123 if (Cache.getDefault("CHECKURLLINKS", true))
2125 // check what the actual links are - if it's just the default don't
2126 // bother with the warning
2127 List<String> links = Preferences.sequenceUrlLinks
2130 // only need to check links if there is one with a
2131 // SEQUENCE_ID which is not the default EMBL_EBI link
2132 ListIterator<String> li = links.listIterator();
2133 boolean check = false;
2134 List<JLabel> urls = new ArrayList<>();
2135 while (li.hasNext())
2137 String link = li.next();
2138 if (link.contains(jalview.util.UrlConstants.SEQUENCE_ID)
2139 && !UrlConstants.isDefaultString(link))
2142 int barPos = link.indexOf("|");
2143 String urlMsg = barPos == -1 ? link
2144 : link.substring(0, barPos) + ": "
2145 + link.substring(barPos + 1);
2146 urls.add(new JLabel(urlMsg));
2154 // ask user to check in case URL links use old style tokens
2155 // ($SEQUENCE_ID$ for sequence id _or_ accession id)
2156 JPanel msgPanel = new JPanel();
2157 msgPanel.setLayout(new BoxLayout(msgPanel, BoxLayout.PAGE_AXIS));
2158 msgPanel.add(Box.createVerticalGlue());
2159 JLabel msg = new JLabel(MessageManager
2160 .getString("label.SEQUENCE_ID_for_DB_ACCESSION1"));
2161 JLabel msg2 = new JLabel(MessageManager
2162 .getString("label.SEQUENCE_ID_for_DB_ACCESSION2"));
2164 for (JLabel url : urls)
2170 final JCheckBox jcb = new JCheckBox(
2171 MessageManager.getString("label.do_not_display_again"));
2172 jcb.addActionListener(new ActionListener()
2175 public void actionPerformed(ActionEvent e)
2177 // update Cache settings for "don't show this again"
2178 boolean showWarningAgain = !jcb.isSelected();
2179 Cache.setProperty("CHECKURLLINKS",
2180 Boolean.valueOf(showWarningAgain).toString());
2185 JvOptionPane.showMessageDialog(Desktop.desktop, msgPanel,
2187 .getString("label.SEQUENCE_ID_no_longer_used"),
2188 JvOptionPane.WARNING_MESSAGE);
2195 * Proxy class for JDesktopPane which optionally displays the current memory
2196 * usage and highlights the desktop area with a red bar if free memory runs
2201 public class MyDesktopPane extends JDesktopPane implements Runnable
2203 private static final float ONE_MB = 1048576f;
2205 boolean showMemoryUsage = false;
2209 java.text.NumberFormat df;
2211 float maxMemory, allocatedMemory, freeMemory, totalFreeMemory,
2214 public MyDesktopPane(boolean showMemoryUsage)
2216 showMemoryUsage(showMemoryUsage);
2219 public void showMemoryUsage(boolean showMemory)
2221 this.showMemoryUsage = showMemory;
2224 Thread worker = new Thread(this);
2230 public boolean isShowMemoryUsage()
2232 return showMemoryUsage;
2238 df = java.text.NumberFormat.getNumberInstance();
2239 df.setMaximumFractionDigits(2);
2240 runtime = Runtime.getRuntime();
2242 while (showMemoryUsage)
2246 maxMemory = runtime.maxMemory() / ONE_MB;
2247 allocatedMemory = runtime.totalMemory() / ONE_MB;
2248 freeMemory = runtime.freeMemory() / ONE_MB;
2249 totalFreeMemory = freeMemory + (maxMemory - allocatedMemory);
2251 percentUsage = (totalFreeMemory / maxMemory) * 100;
2253 // if (percentUsage < 20)
2255 // border1 = BorderFactory.createMatteBorder(12, 12, 12, 12,
2257 // instance.set.setBorder(border1);
2260 // sleep after showing usage
2262 } catch (Exception ex)
2264 ex.printStackTrace();
2270 public void paintComponent(Graphics g)
2272 if (showMemoryUsage && g != null && df != null)
2274 if (percentUsage < 20)
2276 g.setColor(Color.red);
2278 FontMetrics fm = g.getFontMetrics();
2281 g.drawString(MessageManager.formatMessage("label.memory_stats",
2283 { df.format(totalFreeMemory), df.format(maxMemory),
2284 df.format(percentUsage) }),
2285 10, getHeight() - fm.getHeight());
2289 // output debug scale message. Important for jalview.bin.HiDPISettingTest2
2290 Desktop.debugScaleMessage(Desktop.getDesktop().getGraphics());
2295 * Accessor method to quickly get all the AlignmentFrames loaded.
2297 * @return an array of AlignFrame, or null if none found
2299 public static AlignFrame[] getAlignFrames()
2301 if (Jalview.isHeadlessMode())
2303 // Desktop.desktop is null in headless mode
2304 return new AlignFrame[] { Jalview.currentAlignFrame };
2307 JInternalFrame[] frames = Desktop.desktop.getAllFrames();
2313 List<AlignFrame> avp = new ArrayList<>();
2315 for (int i = frames.length - 1; i > -1; i--)
2317 if (frames[i] instanceof AlignFrame)
2319 avp.add((AlignFrame) frames[i]);
2321 else if (frames[i] instanceof SplitFrame)
2324 * Also check for a split frame containing an AlignFrame
2326 GSplitFrame sf = (GSplitFrame) frames[i];
2327 if (sf.getTopFrame() instanceof AlignFrame)
2329 avp.add((AlignFrame) sf.getTopFrame());
2331 if (sf.getBottomFrame() instanceof AlignFrame)
2333 avp.add((AlignFrame) sf.getBottomFrame());
2337 if (avp.size() == 0)
2341 AlignFrame afs[] = avp.toArray(new AlignFrame[avp.size()]);
2346 * Returns an array of any AppJmol frames in the Desktop (or null if none).
2350 public GStructureViewer[] getJmols()
2352 JInternalFrame[] frames = Desktop.desktop.getAllFrames();
2358 List<GStructureViewer> avp = new ArrayList<>();
2360 for (int i = frames.length - 1; i > -1; i--)
2362 if (frames[i] instanceof AppJmol)
2364 GStructureViewer af = (GStructureViewer) frames[i];
2368 if (avp.size() == 0)
2372 GStructureViewer afs[] = avp.toArray(new GStructureViewer[avp.size()]);
2377 * Add Groovy Support to Jalview
2380 public void groovyShell_actionPerformed()
2384 openGroovyConsole();
2385 } catch (Exception ex)
2387 Cache.log.error("Groovy Shell Creation failed.", ex);
2388 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
2390 MessageManager.getString("label.couldnt_create_groovy_shell"),
2391 MessageManager.getString("label.groovy_support_failed"),
2392 JvOptionPane.ERROR_MESSAGE);
2397 * Open the Groovy console
2399 void openGroovyConsole()
2401 if (groovyConsole == null)
2403 groovyConsole = new groovy.ui.Console();
2404 groovyConsole.setVariable("Jalview", this);
2405 groovyConsole.run();
2408 * We allow only one console at a time, so that AlignFrame menu option
2409 * 'Calculate | Run Groovy script' is unambiguous.
2410 * Disable 'Groovy Console', and enable 'Run script', when the console is
2411 * opened, and the reverse when it is closed
2413 Window window = (Window) groovyConsole.getFrame();
2414 window.addWindowListener(new WindowAdapter()
2417 public void windowClosed(WindowEvent e)
2420 * rebind CMD-Q from Groovy Console to Jalview Quit
2423 enableExecuteGroovy(false);
2429 * show Groovy console window (after close and reopen)
2431 ((Window) groovyConsole.getFrame()).setVisible(true);
2434 * if we got this far, enable 'Run Groovy' in AlignFrame menus
2435 * and disable opening a second console
2437 enableExecuteGroovy(true);
2441 * Bind Ctrl/Cmd-Q to Quit - for reset as Groovy Console takes over this
2442 * binding when opened
2444 protected void addQuitHandler()
2447 .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
2449 .getKeyStroke(KeyEvent.VK_Q,
2450 jalview.util.ShortcutKeyMaskExWrapper
2451 .getMenuShortcutKeyMaskEx()),
2453 getRootPane().getActionMap().put("Quit", new AbstractAction()
2456 public void actionPerformed(ActionEvent e)
2464 * Enable or disable 'Run Groovy script' in AlignFrame calculate menus
2467 * true if Groovy console is open
2469 public void enableExecuteGroovy(boolean enabled)
2472 * disable opening a second Groovy console
2473 * (or re-enable when the console is closed)
2475 groovyShell.setEnabled(!enabled);
2477 AlignFrame[] alignFrames = getAlignFrames();
2478 if (alignFrames != null)
2480 for (AlignFrame af : alignFrames)
2482 af.setGroovyEnabled(enabled);
2488 * Progress bars managed by the IProgressIndicator method.
2490 private Hashtable<Long, JPanel> progressBars;
2492 private Hashtable<Long, IProgressIndicatorHandler> progressBarHandlers;
2497 * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
2500 public void setProgressBar(String message, long id)
2502 // Platform.timeCheck("Desktop " + message, Platform.TIME_MARK);
2504 if (progressBars == null)
2506 progressBars = new Hashtable<>();
2507 progressBarHandlers = new Hashtable<>();
2510 if (progressBars.get(Long.valueOf(id)) != null)
2512 JPanel panel = progressBars.remove(Long.valueOf(id));
2513 if (progressBarHandlers.contains(Long.valueOf(id)))
2515 progressBarHandlers.remove(Long.valueOf(id));
2517 removeProgressPanel(panel);
2521 progressBars.put(Long.valueOf(id), addProgressPanel(message));
2528 * @see jalview.gui.IProgressIndicator#registerHandler(long,
2529 * jalview.gui.IProgressIndicatorHandler)
2532 public void registerHandler(final long id,
2533 final IProgressIndicatorHandler handler)
2535 if (progressBarHandlers == null
2536 || !progressBars.containsKey(Long.valueOf(id)))
2538 throw new Error(MessageManager.getString(
2539 "error.call_setprogressbar_before_registering_handler"));
2541 progressBarHandlers.put(Long.valueOf(id), handler);
2542 final JPanel progressPanel = progressBars.get(Long.valueOf(id));
2543 if (handler.canCancel())
2545 JButton cancel = new JButton(
2546 MessageManager.getString("action.cancel"));
2547 final IProgressIndicator us = this;
2548 cancel.addActionListener(new ActionListener()
2552 public void actionPerformed(ActionEvent e)
2554 handler.cancelActivity(id);
2555 us.setProgressBar(MessageManager
2556 .formatMessage("label.cancelled_params", new Object[]
2557 { ((JLabel) progressPanel.getComponent(0)).getText() }),
2561 progressPanel.add(cancel, BorderLayout.EAST);
2567 * @return true if any progress bars are still active
2570 public boolean operationInProgress()
2572 if (progressBars != null && progressBars.size() > 0)
2580 * This will return the first AlignFrame holding the given viewport instance.
2581 * It will break if there are more than one AlignFrames viewing a particular
2585 * @return alignFrame for viewport
2587 public static AlignFrame getAlignFrameFor(AlignViewportI viewport)
2589 if (desktop != null)
2591 AlignmentPanel[] aps = getAlignmentPanels(
2592 viewport.getSequenceSetId());
2593 for (int panel = 0; aps != null && panel < aps.length; panel++)
2595 if (aps[panel] != null && aps[panel].av == viewport)
2597 return aps[panel].alignFrame;
2604 public VamsasApplication getVamsasApplication()
2606 // TODO: JAL-3311 remove remaining code from Jalview relating to VAMSAS
2612 * flag set if jalview GUI is being operated programmatically
2614 private boolean inBatchMode = false;
2617 * check if jalview GUI is being operated programmatically
2619 * @return inBatchMode
2621 public boolean isInBatchMode()
2627 * set flag if jalview GUI is being operated programmatically
2629 * @param inBatchMode
2631 public void setInBatchMode(boolean inBatchMode)
2633 this.inBatchMode = inBatchMode;
2636 public void startServiceDiscovery()
2638 startServiceDiscovery(false);
2641 public void startServiceDiscovery(boolean blocking)
2643 boolean alive = true;
2644 Thread t0 = null, t1 = null, t2 = null;
2645 // JAL-940 - JALVIEW 1 services are now being EOLed as of JABA 2.1 release
2648 // todo: changesupport handlers need to be transferred
2649 if (discoverer == null)
2651 discoverer = new jalview.ws.jws1.Discoverer();
2652 // register PCS handler for desktop.
2653 discoverer.addPropertyChangeListener(changeSupport);
2655 // JAL-940 - disabled JWS1 service configuration - always start discoverer
2656 // until we phase out completely
2657 (t0 = new Thread(discoverer)).start();
2660 if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
2662 t2 = jalview.ws.jws2.Jws2Discoverer.getDiscoverer()
2663 .startDiscoverer(changeSupport);
2667 // TODO: do rest service discovery
2676 } catch (Exception e)
2679 alive = (t1 != null && t1.isAlive()) || (t2 != null && t2.isAlive())
2680 || (t3 != null && t3.isAlive())
2681 || (t0 != null && t0.isAlive());
2687 * called to check if the service discovery process completed successfully.
2691 protected void JalviewServicesChanged(PropertyChangeEvent evt)
2693 if (evt.getNewValue() == null || evt.getNewValue() instanceof Vector)
2695 final String ermsg = jalview.ws.jws2.Jws2Discoverer.getDiscoverer()
2696 .getErrorMessages();
2699 if (Cache.getDefault("SHOW_WSDISCOVERY_ERRORS", true))
2701 if (serviceChangedDialog == null)
2703 // only run if we aren't already displaying one of these.
2704 addDialogThread(serviceChangedDialog = new Runnable()
2711 * JalviewDialog jd =new JalviewDialog() {
2713 * @Override protected void cancelPressed() { // TODO
2714 * Auto-generated method stub
2716 * }@Override protected void okPressed() { // TODO
2717 * Auto-generated method stub
2719 * }@Override protected void raiseClosed() { // TODO
2720 * Auto-generated method stub
2722 * } }; jd.initDialogFrame(new
2723 * JLabel("<html><table width=\"450\"><tr><td>" + ermsg +
2724 * "<br/>It may be that you have invalid JABA URLs in your web service preferences,"
2725 * + " or mis-configured HTTP proxy settings.<br/>" +
2726 * "Check the <em>Connections</em> and <em>Web services</em> tab of the"
2728 * " Tools->Preferences dialog box to change them.</td></tr></table></html>"
2729 * ), true, true, "Web Service Configuration Problem", 450,
2732 * jd.waitForInput();
2734 JvOptionPane.showConfirmDialog(Desktop.desktop,
2735 new JLabel("<html><table width=\"450\"><tr><td>"
2736 + ermsg + "</td></tr></table>"
2737 + "<p>It may be that you have invalid JABA URLs<br/>in your web service preferences,"
2738 + "<br>or as a command-line argument, or mis-configured HTTP proxy settings.</p>"
2739 + "<p>Check the <em>Connections</em> and <em>Web services</em> tab<br/>of the"
2740 + " Tools->Preferences dialog box to change them.</p></html>"),
2741 "Web Service Configuration Problem",
2742 JvOptionPane.DEFAULT_OPTION,
2743 JvOptionPane.ERROR_MESSAGE);
2744 serviceChangedDialog = null;
2753 "Errors reported by JABA discovery service. Check web services preferences.\n"
2760 private Runnable serviceChangedDialog = null;
2763 * start a thread to open a URL in the configured browser. Pops up a warning
2764 * dialog to the user if there is an exception when calling out to the browser
2769 public static void showUrl(final String url)
2771 showUrl(url, Desktop.instance);
2775 * Like showUrl but allows progress handler to be specified
2779 * (null) or object implementing IProgressIndicator
2781 public static void showUrl(final String url,
2782 final IProgressIndicator progress)
2784 new Thread(new Runnable()
2791 if (progress != null)
2793 progress.setProgressBar(MessageManager
2794 .formatMessage("status.opening_params", new Object[]
2795 { url }), this.hashCode());
2797 jalview.util.BrowserLauncher.openURL(url);
2798 } catch (Exception ex)
2800 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
2802 .getString("label.web_browser_not_found_unix"),
2803 MessageManager.getString("label.web_browser_not_found"),
2804 JvOptionPane.WARNING_MESSAGE);
2806 ex.printStackTrace();
2808 if (progress != null)
2810 progress.setProgressBar(null, this.hashCode());
2816 public static WsParamSetManager wsparamManager = null;
2818 public static ParamManager getUserParameterStore()
2820 if (wsparamManager == null)
2822 wsparamManager = new WsParamSetManager();
2824 return wsparamManager;
2828 * static hyperlink handler proxy method for use by Jalview's internal windows
2832 public static void hyperlinkUpdate(HyperlinkEvent e)
2834 if (e.getEventType() == EventType.ACTIVATED)
2839 url = e.getURL().toString();
2840 Desktop.showUrl(url);
2841 } catch (Exception x)
2845 if (Cache.log != null)
2847 Cache.log.error("Couldn't handle string " + url + " as a URL.");
2852 "Couldn't handle string " + url + " as a URL.");
2855 // ignore any exceptions due to dud links.
2862 * single thread that handles display of dialogs to user.
2864 ExecutorService dialogExecutor = Executors.newSingleThreadExecutor();
2867 * flag indicating if dialogExecutor should try to acquire a permit
2869 private volatile boolean dialogPause = true;
2874 private java.util.concurrent.Semaphore block = new Semaphore(0);
2876 private static groovy.ui.Console groovyConsole;
2879 * add another dialog thread to the queue
2883 public void addDialogThread(final Runnable prompter)
2885 dialogExecutor.submit(new Runnable()
2895 } catch (InterruptedException x)
2899 if (instance == null)
2905 SwingUtilities.invokeAndWait(prompter);
2906 } catch (Exception q)
2908 Cache.log.warn("Unexpected Exception in dialog thread.", q);
2914 public void startDialogQueue()
2916 // set the flag so we don't pause waiting for another permit and semaphore
2917 // the current task to begin
2918 dialogPause = false;
2923 * Outputs an image of the desktop to file in EPS format, after prompting the
2924 * user for choice of Text or Lineart character rendering (unless a preference
2925 * has been set). The file name is generated as
2928 * Jalview_snapshot_nnnnn.eps where nnnnn is the current timestamp in milliseconds
2932 protected void snapShotWindow_actionPerformed(ActionEvent e)
2934 // currently the menu option to do this is not shown
2937 int width = getWidth();
2938 int height = getHeight();
2940 "Jalview_snapshot_" + System.currentTimeMillis() + ".eps");
2941 ImageWriterI writer = new ImageWriterI()
2944 public void exportImage(Graphics g) throws Exception
2947 Cache.log.info("Successfully written snapshot to file "
2948 + of.getAbsolutePath());
2951 String title = "View of desktop";
2952 ImageExporter exporter = new ImageExporter(writer, null, TYPE.EPS,
2954 exporter.doExport(of, this, width, height, title);
2958 * Explode the views in the given SplitFrame into separate SplitFrame windows.
2959 * This respects (remembers) any previous 'exploded geometry' i.e. the size
2960 * and location last time the view was expanded (if any). However it does not
2961 * remember the split pane divider location - this is set to match the
2962 * 'exploding' frame.
2966 public void explodeViews(SplitFrame sf)
2968 AlignFrame oldTopFrame = (AlignFrame) sf.getTopFrame();
2969 AlignFrame oldBottomFrame = (AlignFrame) sf.getBottomFrame();
2970 List<? extends AlignmentViewPanel> topPanels = oldTopFrame
2972 List<? extends AlignmentViewPanel> bottomPanels = oldBottomFrame
2974 int viewCount = topPanels.size();
2981 * Processing in reverse order works, forwards order leaves the first panels
2982 * not visible. I don't know why!
2984 for (int i = viewCount - 1; i >= 0; i--)
2987 * Make new top and bottom frames. These take over the respective
2988 * AlignmentPanel objects, including their AlignmentViewports, so the
2989 * cdna/protein relationships between the viewports is carried over to the
2992 * explodedGeometry holds the (x, y) position of the previously exploded
2993 * SplitFrame, and the (width, height) of the AlignFrame component
2995 AlignmentPanel topPanel = (AlignmentPanel) topPanels.get(i);
2996 AlignFrame newTopFrame = new AlignFrame(topPanel);
2997 newTopFrame.setSize(oldTopFrame.getSize());
2998 newTopFrame.setVisible(true);
2999 Rectangle geometry = ((AlignViewport) topPanel.getAlignViewport())
3000 .getExplodedGeometry();
3001 if (geometry != null)
3003 newTopFrame.setSize(geometry.getSize());
3006 AlignmentPanel bottomPanel = (AlignmentPanel) bottomPanels.get(i);
3007 AlignFrame newBottomFrame = new AlignFrame(bottomPanel);
3008 newBottomFrame.setSize(oldBottomFrame.getSize());
3009 newBottomFrame.setVisible(true);
3010 geometry = ((AlignViewport) bottomPanel.getAlignViewport())
3011 .getExplodedGeometry();
3012 if (geometry != null)
3014 newBottomFrame.setSize(geometry.getSize());
3017 topPanel.av.setGatherViewsHere(false);
3018 bottomPanel.av.setGatherViewsHere(false);
3019 JInternalFrame splitFrame = new SplitFrame(newTopFrame,
3021 if (geometry != null)
3023 splitFrame.setLocation(geometry.getLocation());
3025 Desktop.addInternalFrame(splitFrame, sf.getTitle(), -1, -1);
3029 * Clear references to the panels (now relocated in the new SplitFrames)
3030 * before closing the old SplitFrame.
3033 bottomPanels.clear();
3038 * Gather expanded split frames, sharing the same pairs of sequence set ids,
3039 * back into the given SplitFrame as additional views. Note that the gathered
3040 * frames may themselves have multiple views.
3044 public void gatherViews(GSplitFrame source)
3047 * special handling of explodedGeometry for a view within a SplitFrame: - it
3048 * holds the (x, y) position of the enclosing SplitFrame, and the (width,
3049 * height) of the AlignFrame component
3051 AlignFrame myTopFrame = (AlignFrame) source.getTopFrame();
3052 AlignFrame myBottomFrame = (AlignFrame) source.getBottomFrame();
3053 myTopFrame.viewport.setExplodedGeometry(new Rectangle(source.getX(),
3054 source.getY(), myTopFrame.getWidth(), myTopFrame.getHeight()));
3055 myBottomFrame.viewport
3056 .setExplodedGeometry(new Rectangle(source.getX(), source.getY(),
3057 myBottomFrame.getWidth(), myBottomFrame.getHeight()));
3058 myTopFrame.viewport.setGatherViewsHere(true);
3059 myBottomFrame.viewport.setGatherViewsHere(true);
3060 String topViewId = myTopFrame.viewport.getSequenceSetId();
3061 String bottomViewId = myBottomFrame.viewport.getSequenceSetId();
3063 JInternalFrame[] frames = desktop.getAllFrames();
3064 for (JInternalFrame frame : frames)
3066 if (frame instanceof SplitFrame && frame != source)
3068 SplitFrame sf = (SplitFrame) frame;
3069 AlignFrame topFrame = (AlignFrame) sf.getTopFrame();
3070 AlignFrame bottomFrame = (AlignFrame) sf.getBottomFrame();
3071 boolean gatherThis = false;
3072 for (int a = 0; a < topFrame.alignPanels.size(); a++)
3074 AlignmentPanel topPanel = topFrame.alignPanels.get(a);
3075 AlignmentPanel bottomPanel = bottomFrame.alignPanels.get(a);
3076 if (topViewId.equals(topPanel.av.getSequenceSetId())
3077 && bottomViewId.equals(bottomPanel.av.getSequenceSetId()))
3080 topPanel.av.setGatherViewsHere(false);
3081 bottomPanel.av.setGatherViewsHere(false);
3082 topPanel.av.setExplodedGeometry(
3083 new Rectangle(sf.getLocation(), topFrame.getSize()));
3084 bottomPanel.av.setExplodedGeometry(
3085 new Rectangle(sf.getLocation(), bottomFrame.getSize()));
3086 myTopFrame.addAlignmentPanel(topPanel, false);
3087 myBottomFrame.addAlignmentPanel(bottomPanel, false);
3093 topFrame.getAlignPanels().clear();
3094 bottomFrame.getAlignPanels().clear();
3101 * The dust settles...give focus to the tab we did this from.
3103 myTopFrame.setDisplayedView(myTopFrame.alignPanel);
3106 public static groovy.ui.Console getGroovyConsole()
3108 return groovyConsole;
3112 * handles the payload of a drag and drop event.
3114 * TODO refactor to desktop utilities class
3117 * - Data source strings extracted from the drop event
3119 * - protocol for each data source extracted from the drop event
3123 * - the payload from the drop event
3126 public static void transferFromDropTarget(List<Object> files,
3127 List<DataSourceType> protocols, DropTargetDropEvent evt,
3128 Transferable t) throws Exception
3131 // BH 2018 changed List<String> to List<Object> to allow for File from
3134 // DataFlavor[] flavors = t.getTransferDataFlavors();
3135 // for (int i = 0; i < flavors.length; i++) {
3136 // if (flavors[i].isFlavorJavaFileListType()) {
3137 // evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
3138 // List<File> list = (List<File>) t.getTransferData(flavors[i]);
3139 // for (int j = 0; j < list.size(); j++) {
3140 // File file = (File) list.get(j);
3141 // byte[] data = getDroppedFileBytes(file);
3142 // fileName.setText(file.getName() + " - " + data.length + " " +
3143 // evt.getLocation());
3144 // JTextArea target = (JTextArea) ((DropTarget)
3145 // evt.getSource()).getComponent();
3146 // target.setText(new String(data));
3148 // dtde.dropComplete(true);
3153 DataFlavor uriListFlavor = new DataFlavor(
3154 "text/uri-list;class=java.lang.String"), urlFlavour = null;
3157 urlFlavour = new DataFlavor(
3158 "application/x-java-url; class=java.net.URL");
3159 } catch (ClassNotFoundException cfe)
3161 Cache.log.debug("Couldn't instantiate the URL dataflavor.", cfe);
3164 if (urlFlavour != null && t.isDataFlavorSupported(urlFlavour))
3169 java.net.URL url = (URL) t.getTransferData(urlFlavour);
3170 // nb: java 8 osx bug https://bugs.openjdk.java.net/browse/JDK-8156099
3171 // means url may be null.
3174 protocols.add(DataSourceType.URL);
3175 files.add(url.toString());
3176 Cache.log.debug("Drop handled as URL dataflavor "
3177 + files.get(files.size() - 1));
3182 if (Platform.isAMacAndNotJS())
3185 "Please ignore plist error - occurs due to problem with java 8 on OSX");
3188 } catch (Throwable ex)
3190 Cache.log.debug("URL drop handler failed.", ex);
3193 if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
3195 // Works on Windows and MacOSX
3196 Cache.log.debug("Drop handled as javaFileListFlavor");
3197 for (Object file : (List) t
3198 .getTransferData(DataFlavor.javaFileListFlavor))
3201 protocols.add(DataSourceType.FILE);
3206 // Unix like behaviour
3207 boolean added = false;
3209 if (t.isDataFlavorSupported(uriListFlavor))
3211 Cache.log.debug("Drop handled as uriListFlavor");
3212 // This is used by Unix drag system
3213 data = (String) t.getTransferData(uriListFlavor);
3217 // fallback to text: workaround - on OSX where there's a JVM bug
3218 Cache.log.debug("standard URIListFlavor failed. Trying text");
3219 // try text fallback
3220 DataFlavor textDf = new DataFlavor(
3221 "text/plain;class=java.lang.String");
3222 if (t.isDataFlavorSupported(textDf))
3224 data = (String) t.getTransferData(textDf);
3227 Cache.log.debug("Plain text drop content returned "
3228 + (data == null ? "Null - failed" : data));
3233 while (protocols.size() < files.size())
3235 Cache.log.debug("Adding missing FILE protocol for "
3236 + files.get(protocols.size()));
3237 protocols.add(DataSourceType.FILE);
3239 for (java.util.StringTokenizer st = new java.util.StringTokenizer(
3240 data, "\r\n"); st.hasMoreTokens();)
3243 String s = st.nextToken();
3244 if (s.startsWith("#"))
3246 // the line is a comment (as per the RFC 2483)
3249 java.net.URI uri = new java.net.URI(s);
3250 if (uri.getScheme().toLowerCase().startsWith("http"))
3252 protocols.add(DataSourceType.URL);
3253 files.add(uri.toString());
3257 // otherwise preserve old behaviour: catch all for file objects
3258 java.io.File file = new java.io.File(uri);
3259 protocols.add(DataSourceType.FILE);
3260 files.add(file.toString());
3265 if (Cache.log.isDebugEnabled())
3267 if (data == null || !added)
3270 if (t.getTransferDataFlavors() != null
3271 && t.getTransferDataFlavors().length > 0)
3274 "Couldn't resolve drop data. Here are the supported flavors:");
3275 for (DataFlavor fl : t.getTransferDataFlavors())
3278 "Supported transfer dataflavor: " + fl.toString());
3279 Object df = t.getTransferData(fl);
3282 Cache.log.debug("Retrieves: " + df);
3286 Cache.log.debug("Retrieved nothing");
3292 Cache.log.debug("Couldn't resolve dataflavor for drop: "
3298 if (Platform.isWindowsAndNotJS())
3300 Cache.log.debug("Scanning dropped content for Windows Link Files");
3302 // resolve any .lnk files in the file drop
3303 for (int f = 0; f < files.size(); f++)
3305 String source = files.get(f).toString().toLowerCase();
3306 if (protocols.get(f).equals(DataSourceType.FILE)
3307 && (source.endsWith(".lnk") || source.endsWith(".url")
3308 || source.endsWith(".site")))
3312 Object obj = files.get(f);
3313 File lf = (obj instanceof File ? (File) obj
3314 : new File((String) obj));
3315 // process link file to get a URL
3316 Cache.log.debug("Found potential link file: " + lf);
3317 WindowsShortcut wscfile = new WindowsShortcut(lf);
3318 String fullname = wscfile.getRealFilename();
3319 protocols.set(f, FormatAdapter.checkProtocol(fullname));
3320 files.set(f, fullname);
3321 Cache.log.debug("Parsed real filename " + fullname
3322 + " to extract protocol: " + protocols.get(f));
3323 } catch (Exception ex)
3326 "Couldn't parse " + files.get(f) + " as a link file.",
3335 * Sets the Preferences property for experimental features to True or False
3336 * depending on the state of the controlling menu item
3339 protected void showExperimental_actionPerformed(boolean selected)
3341 Cache.setProperty(EXPERIMENTAL_FEATURES, Boolean.toString(selected));
3345 * Answers a (possibly empty) list of any structure viewer frames (currently
3346 * for either Jmol or Chimera) which are currently open. This may optionally
3347 * be restricted to viewers of a specified class, or viewers linked to a
3348 * specified alignment panel.
3351 * if not null, only return viewers linked to this panel
3352 * @param structureViewerClass
3353 * if not null, only return viewers of this class
3356 public List<StructureViewerBase> getStructureViewers(
3357 AlignmentPanel apanel,
3358 Class<? extends StructureViewerBase> structureViewerClass)
3360 List<StructureViewerBase> result = new ArrayList<>();
3361 JInternalFrame[] frames = Desktop.instance.getAllFrames();
3363 for (JInternalFrame frame : frames)
3365 if (frame instanceof StructureViewerBase)
3367 if (structureViewerClass == null
3368 || structureViewerClass.isInstance(frame))
3371 || ((StructureViewerBase) frame).isLinkedWith(apanel))
3373 result.add((StructureViewerBase) frame);
3381 public static final String debugScaleMessage = "Desktop graphics transform scale=";
3383 private static boolean debugScaleMessageDone = false;
3385 public static void debugScaleMessage(Graphics g)
3387 if (debugScaleMessageDone)
3391 // output used by tests to check HiDPI scaling settings in action
3394 Graphics2D gg = (Graphics2D) g;
3397 AffineTransform t = gg.getTransform();
3398 double scaleX = t.getScaleX();
3399 double scaleY = t.getScaleY();
3400 Cache.debug(debugScaleMessage + scaleX + " (X)");
3401 Cache.debug(debugScaleMessage + scaleY + " (Y)");
3402 debugScaleMessageDone = true;
3406 Cache.debug("Desktop graphics null");
3408 } catch (Exception e)
3410 Cache.debug(Cache.getStackTraceString(e));