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;
55 import java.lang.reflect.Field;
57 import java.util.ArrayList;
58 import java.util.HashMap;
59 import java.util.Hashtable;
60 import java.util.List;
61 import java.util.ListIterator;
62 import java.util.StringTokenizer;
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.KeyStroke;
88 import javax.swing.SwingUtilities;
89 import javax.swing.event.HyperlinkEvent;
90 import javax.swing.event.HyperlinkEvent.EventType;
91 import javax.swing.event.InternalFrameAdapter;
92 import javax.swing.event.InternalFrameEvent;
94 import org.stackoverflowusers.file.WindowsShortcut;
96 import jalview.api.AlignViewportI;
97 import jalview.api.AlignmentViewPanel;
98 import jalview.bin.Cache;
99 import jalview.bin.Jalview;
100 import jalview.io.BackupFiles;
101 import jalview.io.DataSourceType;
102 import jalview.io.FileFormat;
103 import jalview.io.FileFormatException;
104 import jalview.io.FileFormatI;
105 import jalview.io.FileFormats;
106 import jalview.io.FileLoader;
107 import jalview.io.FormatAdapter;
108 import jalview.io.IdentifyFile;
109 import jalview.io.JalviewFileChooser;
110 import jalview.io.JalviewFileView;
111 import jalview.jbgui.GSplitFrame;
112 import jalview.jbgui.GStructureViewer;
113 import jalview.project.Jalview2XML;
114 import jalview.structure.StructureSelectionManager;
115 import jalview.urls.IdOrgSettings;
116 import jalview.util.ImageMaker;
117 import jalview.util.MessageManager;
118 import jalview.util.Platform;
119 import jalview.util.UrlConstants;
120 import jalview.viewmodel.AlignmentViewport;
121 import jalview.ws.params.ParamManager;
122 import jalview.ws.utils.UrlDownloadClient;
129 * @version $Revision: 1.155 $
131 public class Desktop extends jalview.jbgui.GDesktop
132 implements DropTargetListener, ClipboardOwner, IProgressIndicator,
133 jalview.api.StructureSelectionManagerProvider
135 private static int DEFAULT_MIN_WIDTH = 300;
137 private static int DEFAULT_MIN_HEIGHT = 250;
139 private static int ALIGN_FRAME_DEFAULT_MIN_WIDTH = 600;
141 private static int ALIGN_FRAME_DEFAULT_MIN_HEIGHT = 70;
143 private static final String EXPERIMENTAL_FEATURES = "EXPERIMENTAL_FEATURES";
145 protected static final String CONFIRM_KEYBOARD_QUIT = "CONFIRM_KEYBOARD_QUIT";
147 public static HashMap<String, FileWriter> savingFiles = new HashMap<String, FileWriter>();
149 private JalviewChangeSupport changeSupport = new JalviewChangeSupport();
152 * news reader - null if it was never started.
154 private BlogReader jvnews = null;
156 private File projectFile;
160 * @see jalview.gui.JalviewChangeSupport#addJalviewPropertyChangeListener(java.beans.PropertyChangeListener)
162 public void addJalviewPropertyChangeListener(
163 PropertyChangeListener listener)
165 changeSupport.addJalviewPropertyChangeListener(listener);
169 * @param propertyName
171 * @see jalview.gui.JalviewChangeSupport#addJalviewPropertyChangeListener(java.lang.String,
172 * java.beans.PropertyChangeListener)
174 public void addJalviewPropertyChangeListener(String propertyName,
175 PropertyChangeListener listener)
177 changeSupport.addJalviewPropertyChangeListener(propertyName, listener);
181 * @param propertyName
183 * @see jalview.gui.JalviewChangeSupport#removeJalviewPropertyChangeListener(java.lang.String,
184 * java.beans.PropertyChangeListener)
186 public void removeJalviewPropertyChangeListener(String propertyName,
187 PropertyChangeListener listener)
189 changeSupport.removeJalviewPropertyChangeListener(propertyName,
193 /** Singleton Desktop instance */
194 public static Desktop instance;
196 public static MyDesktopPane desktop;
198 static int openFrameCount = 0;
200 static final int xOffset = 30;
202 static final int yOffset = 30;
204 public static jalview.ws.jws1.Discoverer discoverer;
206 public static Object[] jalviewClipboard;
208 public static boolean internalCopy = false;
210 static int fileLoadingCount = 0;
212 class MyDesktopManager implements DesktopManager
215 private DesktopManager delegate;
217 public MyDesktopManager(DesktopManager delegate)
219 this.delegate = delegate;
223 public void activateFrame(JInternalFrame f)
227 delegate.activateFrame(f);
228 } catch (NullPointerException npe)
230 Point p = getMousePosition();
231 instance.showPasteMenu(p.x, p.y);
236 public void beginDraggingFrame(JComponent f)
238 delegate.beginDraggingFrame(f);
242 public void beginResizingFrame(JComponent f, int direction)
244 delegate.beginResizingFrame(f, direction);
248 public void closeFrame(JInternalFrame f)
250 delegate.closeFrame(f);
254 public void deactivateFrame(JInternalFrame f)
256 delegate.deactivateFrame(f);
260 public void deiconifyFrame(JInternalFrame f)
262 delegate.deiconifyFrame(f);
266 public void dragFrame(JComponent f, int newX, int newY)
272 delegate.dragFrame(f, newX, newY);
276 public void endDraggingFrame(JComponent f)
278 delegate.endDraggingFrame(f);
283 public void endResizingFrame(JComponent f)
285 delegate.endResizingFrame(f);
290 public void iconifyFrame(JInternalFrame f)
292 delegate.iconifyFrame(f);
296 public void maximizeFrame(JInternalFrame f)
298 delegate.maximizeFrame(f);
302 public void minimizeFrame(JInternalFrame f)
304 delegate.minimizeFrame(f);
308 public void openFrame(JInternalFrame f)
310 delegate.openFrame(f);
314 public void resizeFrame(JComponent f, int newX, int newY, int newWidth,
321 delegate.resizeFrame(f, newX, newY, newWidth, newHeight);
325 public void setBoundsForFrame(JComponent f, int newX, int newY,
326 int newWidth, int newHeight)
328 delegate.setBoundsForFrame(f, newX, newY, newWidth, newHeight);
331 // All other methods, simply delegate
336 * Creates a new Desktop object.
341 * A note to implementors. It is ESSENTIAL that any activities that might
342 * block are spawned off as threads rather than waited for during this
347 doConfigureStructurePrefs();
348 String title = "Jalview " + Cache.getProperty("VERSION");
351 if (!Platform.isAMac())
353 // this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
357 this.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
363 APQHandlers.setAPQHandlers(this);
364 } catch (Exception e)
366 System.out.println("Cannot set APQHandlers");
367 // e.printStackTrace();
368 } catch (Throwable t)
370 System.out.println("Cannot set APQHandlers");
371 // t.printStackTrace();
374 if (Platform.isLinux())
378 Toolkit xToolkit = Toolkit.getDefaultToolkit();
379 Field awtAppClassNameField = xToolkit.getClass()
380 .getDeclaredField("awtAppClassName");
381 awtAppClassNameField.setAccessible(true);
382 awtAppClassNameField.set(xToolkit, "Jalview");
383 } catch (Exception e)
385 Cache.log.debug("Could not set awtAppClassName");
389 addWindowListener(new WindowAdapter()
393 public void windowClosing(WindowEvent ev)
399 boolean selmemusage = jalview.bin.Cache.getDefault("SHOW_MEMUSAGE",
402 boolean showjconsole = jalview.bin.Cache.getDefault("SHOW_JAVA_CONSOLE",
404 desktop = new MyDesktopPane(selmemusage);
405 showMemusage.setSelected(selmemusage);
406 desktop.setBackground(Color.white);
408 getContentPane().setLayout(new BorderLayout());
409 // alternate config - have scrollbars - see notes in JAL-153
410 // JScrollPane sp = new JScrollPane();
411 // sp.getViewport().setView(desktop);
412 // getContentPane().add(sp, BorderLayout.CENTER);
413 getContentPane().add(desktop, BorderLayout.CENTER);
414 desktop.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
416 // This line prevents Windows Look&Feel resizing all new windows to maximum
417 // if previous window was maximised
418 desktop.setDesktopManager(new MyDesktopManager(
419 (Platform.isWindows() ? new DefaultDesktopManager()
421 ? new AquaInternalFrameManager(
422 desktop.getDesktopManager())
423 : desktop.getDesktopManager())));
425 Rectangle dims = getLastKnownDimensions("");
432 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
433 setBounds((screenSize.width - 900) / 2, (screenSize.height - 650) / 2,
436 jconsole = new Console(this, showjconsole);
437 // add essential build information
438 jconsole.setHeader(jalview.bin.Cache.getVersionDetailsForConsole());
440 showConsole(showjconsole);
442 showNews.setVisible(false);
444 experimentalFeatures.setSelected(showExperimental());
446 getIdentifiersOrgData();
450 this.addWindowListener(new WindowAdapter()
453 public void windowClosing(WindowEvent evt)
460 this.addMouseListener(ma = new MouseAdapter()
463 public void mousePressed(MouseEvent evt)
465 if (evt.isPopupTrigger()) // Mac
467 showPasteMenu(evt.getX(), evt.getY());
472 public void mouseReleased(MouseEvent evt)
474 if (evt.isPopupTrigger()) // Windows
476 showPasteMenu(evt.getX(), evt.getY());
480 desktop.addMouseListener(ma);
482 this.setDropTarget(new java.awt.dnd.DropTarget(desktop, this));
483 // Spawn a thread that shows the splashscreen
484 SwingUtilities.invokeLater(new Runnable()
493 // Thread off a new instance of the file chooser - this reduces the time it
494 // takes to open it later on.
495 new Thread(new Runnable()
500 Cache.log.debug("Filechooser init thread started.");
501 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
502 JalviewFileChooser.forRead(Cache.getProperty("LAST_DIRECTORY"),
504 Cache.log.debug("Filechooser init thread finished.");
507 // Add the service change listener
508 changeSupport.addJalviewPropertyChangeListener("services",
509 new PropertyChangeListener()
513 public void propertyChange(PropertyChangeEvent evt)
515 Cache.log.debug("Firing service changed event for "
516 + evt.getNewValue());
517 JalviewServicesChanged(evt);
524 * Answers true if user preferences to enable experimental features is True
529 public boolean showExperimental()
531 String experimental = Cache.getDefault(EXPERIMENTAL_FEATURES,
532 Boolean.FALSE.toString());
533 return Boolean.valueOf(experimental).booleanValue();
536 public void doConfigureStructurePrefs()
538 // configure services
539 StructureSelectionManager ssm = StructureSelectionManager
540 .getStructureSelectionManager(this);
541 if (jalview.bin.Cache.getDefault(Preferences.ADD_SS_ANN, true))
543 ssm.setAddTempFacAnnot(jalview.bin.Cache
544 .getDefault(Preferences.ADD_TEMPFACT_ANN, true));
545 ssm.setProcessSecondaryStructure(jalview.bin.Cache
546 .getDefault(Preferences.STRUCT_FROM_PDB, true));
547 ssm.setSecStructServices(
548 jalview.bin.Cache.getDefault(Preferences.USE_RNAVIEW, true));
552 ssm.setAddTempFacAnnot(false);
553 ssm.setProcessSecondaryStructure(false);
554 ssm.setSecStructServices(false);
558 public void checkForNews()
560 final Desktop me = this;
561 // Thread off the news reader, in case there are connection problems.
562 new Thread(new Runnable()
567 Cache.log.debug("Starting news thread.");
569 jvnews = new BlogReader(me);
570 showNews.setVisible(true);
571 Cache.log.debug("Completed news thread.");
576 public void getIdentifiersOrgData()
578 if (Cache.getProperty("NOIDENTIFIERSSERVICE") == null)
579 {// Thread off the identifiers fetcher
580 new Thread(new Runnable()
585 Cache.log.debug("Downloading data from identifiers.org");
588 UrlDownloadClient.download(IdOrgSettings.getUrl(),
589 IdOrgSettings.getDownloadLocation());
590 } catch (IOException e)
592 Cache.log.debug("Exception downloading identifiers.org data"
602 protected void showNews_actionPerformed(ActionEvent e)
604 showNews(showNews.isSelected());
607 void showNews(boolean visible)
610 Cache.log.debug((visible ? "Showing" : "Hiding") + " news.");
611 showNews.setSelected(visible);
612 if (visible && !jvnews.isVisible())
614 new Thread(new Runnable()
619 long now = System.currentTimeMillis();
620 Desktop.instance.setProgressBar(
621 MessageManager.getString("status.refreshing_news"),
623 jvnews.refreshNews();
624 Desktop.instance.setProgressBar(null, now);
633 * recover the last known dimensions for a jalview window
636 * - empty string is desktop, all other windows have unique prefix
637 * @return null or last known dimensions scaled to current geometry (if last
638 * window geom was known)
640 Rectangle getLastKnownDimensions(String windowName)
642 // TODO: lock aspect ratio for scaling desktop Bug #0058199
643 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
644 String x = jalview.bin.Cache.getProperty(windowName + "SCREEN_X");
645 String y = jalview.bin.Cache.getProperty(windowName + "SCREEN_Y");
646 String width = jalview.bin.Cache
647 .getProperty(windowName + "SCREEN_WIDTH");
648 String height = jalview.bin.Cache
649 .getProperty(windowName + "SCREEN_HEIGHT");
650 if ((x != null) && (y != null) && (width != null) && (height != null))
652 int ix = Integer.parseInt(x), iy = Integer.parseInt(y),
653 iw = Integer.parseInt(width), ih = Integer.parseInt(height);
654 if (jalview.bin.Cache.getProperty("SCREENGEOMETRY_WIDTH") != null)
656 // attempt #1 - try to cope with change in screen geometry - this
657 // version doesn't preserve original jv aspect ratio.
658 // take ratio of current screen size vs original screen size.
659 double sw = ((1f * screenSize.width) / (1f * Integer.parseInt(
660 jalview.bin.Cache.getProperty("SCREENGEOMETRY_WIDTH"))));
661 double sh = ((1f * screenSize.height) / (1f * Integer.parseInt(
662 jalview.bin.Cache.getProperty("SCREENGEOMETRY_HEIGHT"))));
663 // rescale the bounds depending upon the current screen geometry.
664 ix = (int) (ix * sw);
665 iw = (int) (iw * sw);
666 iy = (int) (iy * sh);
667 ih = (int) (ih * sh);
668 while (ix >= screenSize.width)
670 jalview.bin.Cache.log.debug(
671 "Window geometry location recall error: shifting horizontal to within screenbounds.");
672 ix -= screenSize.width;
674 while (iy >= screenSize.height)
676 jalview.bin.Cache.log.debug(
677 "Window geometry location recall error: shifting vertical to within screenbounds.");
678 iy -= screenSize.height;
680 jalview.bin.Cache.log.debug(
681 "Got last known dimensions for " + windowName + ": x:" + ix
682 + " y:" + iy + " width:" + iw + " height:" + ih);
684 // return dimensions for new instance
685 return new Rectangle(ix, iy, iw, ih);
690 void showPasteMenu(int x, int y)
692 JPopupMenu popup = new JPopupMenu();
693 JMenuItem item = new JMenuItem(
694 MessageManager.getString("label.paste_new_window"));
695 item.addActionListener(new ActionListener()
698 public void actionPerformed(ActionEvent evt)
705 popup.show(this, x, y);
712 Clipboard c = Toolkit.getDefaultToolkit().getSystemClipboard();
713 Transferable contents = c.getContents(this);
715 if (contents != null)
717 String file = (String) contents
718 .getTransferData(DataFlavor.stringFlavor);
720 FileFormatI format = new IdentifyFile().identify(file,
721 DataSourceType.PASTE);
723 new FileLoader().LoadFile(file, DataSourceType.PASTE, format);
726 } catch (Exception ex)
729 "Unable to paste alignment from system clipboard:\n" + ex);
734 * Adds and opens the given frame to the desktop
745 public static synchronized void addInternalFrame(
746 final JInternalFrame frame, String title, int w, int h)
748 addInternalFrame(frame, title, true, w, h, true, false);
752 * Add an internal frame to the Jalview desktop
759 * When true, display frame immediately, otherwise, caller must call
760 * setVisible themselves.
766 public static synchronized void addInternalFrame(
767 final JInternalFrame frame, String title, boolean makeVisible,
770 addInternalFrame(frame, title, makeVisible, w, h, true, false);
774 * Add an internal frame to the Jalview desktop and make it visible
787 public static synchronized void addInternalFrame(
788 final JInternalFrame frame, String title, int w, int h,
791 addInternalFrame(frame, title, true, w, h, resizable, false);
795 * Add an internal frame to the Jalview desktop
802 * When true, display frame immediately, otherwise, caller must call
803 * setVisible themselves.
810 * @param ignoreMinSize
811 * Do not set the default minimum size for frame
813 public static synchronized void addInternalFrame(
814 final JInternalFrame frame, String title, boolean makeVisible,
815 int w, int h, boolean resizable, boolean ignoreMinSize)
818 // TODO: allow callers to determine X and Y position of frame (eg. via
820 // TODO: consider fixing method to update entries in the window submenu with
821 // the current window title
823 frame.setTitle(title);
824 if (frame.getWidth() < 1 || frame.getHeight() < 1)
828 // THIS IS A PUBLIC STATIC METHOD, SO IT MAY BE CALLED EVEN IN
829 // A HEADLESS STATE WHEN NO DESKTOP EXISTS. MUST RETURN
830 // IF JALVIEW IS RUNNING HEADLESS
831 // ///////////////////////////////////////////////
832 if (instance == null || (System.getProperty("java.awt.headless") != null
833 && System.getProperty("java.awt.headless").equals("true")))
842 frame.setMinimumSize(
843 new Dimension(DEFAULT_MIN_WIDTH, DEFAULT_MIN_HEIGHT));
845 // Set default dimension for Alignment Frame window.
846 // The Alignment Frame window could be added from a number of places,
848 // I did this here in order not to miss out on any Alignment frame.
849 if (frame instanceof AlignFrame)
851 frame.setMinimumSize(new Dimension(ALIGN_FRAME_DEFAULT_MIN_WIDTH,
852 ALIGN_FRAME_DEFAULT_MIN_HEIGHT));
856 frame.setVisible(makeVisible);
857 frame.setClosable(true);
858 frame.setResizable(resizable);
859 frame.setMaximizable(resizable);
860 frame.setIconifiable(resizable);
861 frame.setOpaque(false);
863 if (frame.getX() < 1 && frame.getY() < 1)
865 frame.setLocation(xOffset * openFrameCount,
866 yOffset * ((openFrameCount - 1) % 10) + yOffset);
870 * add an entry for the new frame in the Window menu
871 * (and remove it when the frame is closed)
873 final JMenuItem menuItem = new JMenuItem(title);
874 frame.addInternalFrameListener(new InternalFrameAdapter()
877 public void internalFrameActivated(InternalFrameEvent evt)
879 JInternalFrame itf = desktop.getSelectedFrame();
882 if (itf instanceof AlignFrame)
884 Jalview.setCurrentAlignFrame((AlignFrame) itf);
891 public void internalFrameClosed(InternalFrameEvent evt)
893 PaintRefresher.RemoveComponent(frame);
896 * defensive check to prevent frames being
897 * added half off the window
899 if (openFrameCount > 0)
905 * ensure no reference to alignFrame retained by menu item listener
907 if (menuItem.getActionListeners().length > 0)
909 menuItem.removeActionListener(menuItem.getActionListeners()[0]);
911 windowMenu.remove(menuItem);
915 menuItem.addActionListener(new ActionListener()
918 public void actionPerformed(ActionEvent e)
922 frame.setSelected(true);
923 frame.setIcon(false);
924 } catch (java.beans.PropertyVetoException ex)
931 setKeyBindings(frame);
935 windowMenu.add(menuItem);
940 frame.setSelected(true);
941 frame.requestFocus();
942 } catch (java.beans.PropertyVetoException ve)
944 } catch (java.lang.ClassCastException cex)
947 "Squashed a possible GUI implementation error. If you can recreate this, please look at https://issues.jalview.org/browse/JAL-869",
953 * Add key bindings to a JInternalFrame so that Ctrl-W and Cmd-W will close
958 private static void setKeyBindings(JInternalFrame frame)
960 @SuppressWarnings("serial")
961 final Action closeAction = new AbstractAction()
964 public void actionPerformed(ActionEvent e)
971 * set up key bindings for Ctrl-W and Cmd-W, with the same (Close) action
973 KeyStroke ctrlWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
974 InputEvent.CTRL_DOWN_MASK);
975 KeyStroke cmdWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
976 jalview.util.ShortcutKeyMaskExWrapper
977 .getMenuShortcutKeyMaskEx());
979 InputMap inputMap = frame
980 .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
981 String ctrlW = ctrlWKey.toString();
982 inputMap.put(ctrlWKey, ctrlW);
983 inputMap.put(cmdWKey, ctrlW);
985 ActionMap actionMap = frame.getActionMap();
986 actionMap.put(ctrlW, closeAction);
990 public void lostOwnership(Clipboard clipboard, Transferable contents)
994 Desktop.jalviewClipboard = null;
997 internalCopy = false;
1001 public void dragEnter(DropTargetDragEvent evt)
1006 public void dragExit(DropTargetEvent evt)
1011 public void dragOver(DropTargetDragEvent evt)
1016 public void dropActionChanged(DropTargetDragEvent evt)
1027 public void drop(DropTargetDropEvent evt)
1029 boolean success = true;
1030 // JAL-1552 - acceptDrop required before getTransferable call for
1031 // Java's Transferable for native dnd
1032 evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
1033 Transferable t = evt.getTransferable();
1034 List<String> files = new ArrayList<>();
1035 List<DataSourceType> protocols = new ArrayList<>();
1039 Desktop.transferFromDropTarget(files, protocols, evt, t);
1040 } catch (Exception e)
1042 e.printStackTrace();
1050 for (int i = 0; i < files.size(); i++)
1052 String file = files.get(i).toString();
1053 DataSourceType protocol = (protocols == null)
1054 ? DataSourceType.FILE
1056 FileFormatI format = null;
1058 if (file.endsWith(".jar"))
1060 format = FileFormat.Jalview;
1065 format = new IdentifyFile().identify(file, protocol);
1068 new FileLoader().LoadFile(file, protocol, format);
1071 } catch (Exception ex)
1076 evt.dropComplete(success); // need this to ensure input focus is properly
1077 // transfered to any new windows created
1087 public void inputLocalFileMenuItem_actionPerformed(AlignViewport viewport)
1089 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
1090 JalviewFileChooser chooser = JalviewFileChooser.forRead(
1091 Cache.getProperty("LAST_DIRECTORY"), fileFormat,
1092 BackupFiles.getEnabled());
1094 chooser.setFileView(new JalviewFileView());
1095 chooser.setDialogTitle(
1096 MessageManager.getString("label.open_local_file"));
1097 chooser.setToolTipText(MessageManager.getString("action.open"));
1099 int value = chooser.showOpenDialog(this);
1101 if (value == JalviewFileChooser.APPROVE_OPTION)
1103 String choice = chooser.getSelectedFile().getPath();
1104 Cache.setProperty("LAST_DIRECTORY",
1105 chooser.getSelectedFile().getParent());
1107 FileFormatI format = chooser.getSelectedFormat();
1110 * Call IdentifyFile to verify the file contains what its extension implies.
1111 * Skip this step for dynamically added file formats, because
1112 * IdentifyFile does not know how to recognise them.
1114 if (FileFormats.getInstance().isIdentifiable(format))
1118 format = new IdentifyFile().identify(choice, DataSourceType.FILE);
1119 } catch (FileFormatException e)
1121 // format = null; //??
1125 if (viewport != null)
1127 new FileLoader().LoadFile(viewport, choice, DataSourceType.FILE,
1132 new FileLoader().LoadFile(choice, DataSourceType.FILE, format);
1144 public void inputURLMenuItem_actionPerformed(AlignViewport viewport)
1146 // This construct allows us to have a wider textfield
1148 JLabel label = new JLabel(
1149 MessageManager.getString("label.input_file_url"));
1150 final JComboBox history = new JComboBox();
1152 JPanel panel = new JPanel(new GridLayout(2, 1));
1155 history.setPreferredSize(new Dimension(400, 20));
1156 history.setEditable(true);
1157 history.addItem("https://www.");
1159 String historyItems = jalview.bin.Cache.getProperty("RECENT_URL");
1163 if (historyItems != null)
1165 st = new StringTokenizer(historyItems, "\t");
1167 while (st.hasMoreTokens())
1169 history.addItem(st.nextElement());
1173 int reply = JvOptionPane.showInternalConfirmDialog(desktop, panel,
1174 MessageManager.getString("label.input_alignment_from_url"),
1175 JvOptionPane.OK_CANCEL_OPTION);
1177 if (reply != JvOptionPane.OK_OPTION)
1182 String url = history.getSelectedItem().toString();
1184 if (url.toLowerCase().endsWith(".jar"))
1186 if (viewport != null)
1188 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1189 FileFormat.Jalview);
1193 new FileLoader().LoadFile(url, DataSourceType.URL,
1194 FileFormat.Jalview);
1199 FileFormatI format = null;
1202 format = new IdentifyFile().identify(url, DataSourceType.URL);
1203 } catch (FileFormatException e)
1205 // TODO revise error handling, distinguish between
1206 // URL not found and response not valid
1211 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
1212 MessageManager.formatMessage("label.couldnt_locate",
1215 MessageManager.getString("label.url_not_found"),
1216 JvOptionPane.WARNING_MESSAGE);
1221 if (viewport != null)
1223 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1228 new FileLoader().LoadFile(url, DataSourceType.URL, format);
1234 * Opens the CutAndPaste window for the user to paste an alignment in to
1237 * - if not null, the pasted alignment is added to the current
1238 * alignment; if null, to a new alignment window
1241 public void inputTextboxMenuItem_actionPerformed(
1242 AlignmentViewPanel viewPanel)
1244 CutAndPasteTransfer cap = new CutAndPasteTransfer();
1245 cap.setForInput(viewPanel);
1246 Desktop.addInternalFrame(cap,
1247 MessageManager.getString("label.cut_paste_alignmen_file"), true,
1257 Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
1258 jalview.bin.Cache.setProperty("SCREENGEOMETRY_WIDTH",
1260 jalview.bin.Cache.setProperty("SCREENGEOMETRY_HEIGHT",
1261 screen.height + "");
1262 storeLastKnownDimensions("", new Rectangle(getBounds().x, getBounds().y,
1263 getWidth(), getHeight()));
1265 if (jconsole != null)
1267 storeLastKnownDimensions("JAVA_CONSOLE_", jconsole.getBounds());
1268 jconsole.stopConsole();
1272 storeLastKnownDimensions("JALVIEW_RSS_WINDOW_", jvnews.getBounds());
1275 if (dialogExecutor != null)
1277 dialogExecutor.shutdownNow();
1279 closeAll_actionPerformed(null);
1281 if (groovyConsole != null)
1283 // suppress a possible repeat prompt to save script
1284 groovyConsole.setDirty(false);
1285 groovyConsole.exit();
1290 private void storeLastKnownDimensions(String string, Rectangle jc)
1292 jalview.bin.Cache.log.debug("Storing last known dimensions for "
1293 + string + ": x:" + jc.x + " y:" + jc.y + " width:" + jc.width
1294 + " height:" + jc.height);
1296 jalview.bin.Cache.setProperty(string + "SCREEN_X", jc.x + "");
1297 jalview.bin.Cache.setProperty(string + "SCREEN_Y", jc.y + "");
1298 jalview.bin.Cache.setProperty(string + "SCREEN_WIDTH", jc.width + "");
1299 jalview.bin.Cache.setProperty(string + "SCREEN_HEIGHT", jc.height + "");
1309 public void aboutMenuItem_actionPerformed(ActionEvent e)
1311 // StringBuffer message = getAboutMessage(false);
1312 // JvOptionPane.showInternalMessageDialog(Desktop.desktop,
1314 // message.toString(), "About Jalview", JvOptionPane.INFORMATION_MESSAGE);
1315 new Thread(new Runnable()
1320 new SplashScreen(true);
1325 public StringBuffer getAboutMessage(boolean shortv)
1327 StringBuffer message = new StringBuffer();
1328 message.append("<html>");
1331 message.append("<h1><strong>Version: "
1332 + jalview.bin.Cache.getProperty("VERSION")
1333 + "</strong></h1>");
1334 message.append("<strong>Built: <em>"
1335 + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown")
1336 + "</em> from " + jalview.bin.Cache.getBuildDetailsForSplash()
1343 message.append("<strong>Version "
1344 + jalview.bin.Cache.getProperty("VERSION")
1345 + "; last updated: "
1346 + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown"));
1349 if (jalview.bin.Cache.getDefault("LATEST_VERSION", "Checking")
1350 .equals("Checking"))
1352 // JBP removed this message for 2.11: May be reinstated in future version
1353 // message.append("<br>...Checking latest version...</br>");
1355 else if (!jalview.bin.Cache.getDefault("LATEST_VERSION", "Checking")
1356 .equals(jalview.bin.Cache.getProperty("VERSION")))
1358 boolean red = false;
1359 if (jalview.bin.Cache.getProperty("VERSION").toLowerCase()
1360 .indexOf("automated build") == -1)
1363 // Displayed when code version and jnlp version do not match and code
1364 // version is not a development build
1365 message.append("<div style=\"color: #FF0000;font-style: bold;\">");
1368 message.append("<br>!! Version "
1369 + jalview.bin.Cache.getDefault("LATEST_VERSION",
1371 + " is available for download from "
1372 + jalview.bin.Cache.getDefault("www.jalview.org",
1373 "https://www.jalview.org")
1377 message.append("</div>");
1380 message.append("<br>Authors: " + jalview.bin.Cache.getDefault(
1382 "The Jalview Authors (See AUTHORS file for current list)")
1383 + "<br><br>Development managed by The Barton Group, University of Dundee, Scotland, UK.<br>"
1384 + "<br><br>For help, see the FAQ at <a href=\"https://www.jalview.org/faq\">www.jalview.org/faq</a> and/or join the jalview-discuss@jalview.org mailing list"
1385 + "<br><br>If you use Jalview, please cite:"
1386 + "<br>Waterhouse, A.M., Procter, J.B., Martin, D.M.A, Clamp, M. and Barton, G. J. (2009)"
1387 + "<br>Jalview Version 2 - a multiple sequence alignment editor and analysis workbench"
1388 + "<br>Bioinformatics doi: 10.1093/bioinformatics/btp033"
1400 public void documentationMenuItem_actionPerformed(ActionEvent e)
1404 Help.showHelpWindow();
1405 } catch (Exception ex)
1411 public void closeAll_actionPerformed(ActionEvent e)
1413 // TODO show a progress bar while closing?
1414 JInternalFrame[] frames = desktop.getAllFrames();
1415 for (int i = 0; i < frames.length; i++)
1419 frames[i].setClosed(true);
1420 } catch (java.beans.PropertyVetoException ex)
1424 Jalview.setCurrentAlignFrame(null);
1425 System.out.println("ALL CLOSED");
1428 * reset state of singleton objects as appropriate (clear down session state
1429 * when all windows are closed)
1431 StructureSelectionManager ssm = StructureSelectionManager
1432 .getStructureSelectionManager(this);
1440 public void raiseRelated_actionPerformed(ActionEvent e)
1442 reorderAssociatedWindows(false, false);
1446 public void minimizeAssociated_actionPerformed(ActionEvent e)
1448 reorderAssociatedWindows(true, false);
1451 void closeAssociatedWindows()
1453 reorderAssociatedWindows(false, true);
1459 * @seejalview.jbgui.GDesktop#garbageCollect_actionPerformed(java.awt.event.
1463 protected void garbageCollect_actionPerformed(ActionEvent e)
1465 // We simply collect the garbage
1466 jalview.bin.Cache.log.debug("Collecting garbage...");
1468 jalview.bin.Cache.log.debug("Finished garbage collection.");
1475 * jalview.jbgui.GDesktop#showMemusage_actionPerformed(java.awt.event.ActionEvent
1479 protected void showMemusage_actionPerformed(ActionEvent e)
1481 desktop.showMemoryUsage(showMemusage.isSelected());
1488 * jalview.jbgui.GDesktop#showConsole_actionPerformed(java.awt.event.ActionEvent
1492 protected void showConsole_actionPerformed(ActionEvent e)
1494 showConsole(showConsole.isSelected());
1497 Console jconsole = null;
1500 * control whether the java console is visible or not
1504 void showConsole(boolean selected)
1506 showConsole.setSelected(selected);
1507 // TODO: decide if we should update properties file
1508 Cache.setProperty("SHOW_JAVA_CONSOLE",
1509 Boolean.valueOf(selected).toString());
1510 jconsole.setVisible(selected);
1513 void reorderAssociatedWindows(boolean minimize, boolean close)
1515 JInternalFrame[] frames = desktop.getAllFrames();
1516 if (frames == null || frames.length < 1)
1521 AlignmentViewport source = null, target = null;
1522 if (frames[0] instanceof AlignFrame)
1524 source = ((AlignFrame) frames[0]).getCurrentView();
1526 else if (frames[0] instanceof TreePanel)
1528 source = ((TreePanel) frames[0]).getViewPort();
1530 else if (frames[0] instanceof PCAPanel)
1532 source = ((PCAPanel) frames[0]).av;
1534 else if (frames[0].getContentPane() instanceof PairwiseAlignPanel)
1536 source = ((PairwiseAlignPanel) frames[0].getContentPane()).av;
1541 for (int i = 0; i < frames.length; i++)
1544 if (frames[i] == null)
1548 if (frames[i] instanceof AlignFrame)
1550 target = ((AlignFrame) frames[i]).getCurrentView();
1552 else if (frames[i] instanceof TreePanel)
1554 target = ((TreePanel) frames[i]).getViewPort();
1556 else if (frames[i] instanceof PCAPanel)
1558 target = ((PCAPanel) frames[i]).av;
1560 else if (frames[i].getContentPane() instanceof PairwiseAlignPanel)
1562 target = ((PairwiseAlignPanel) frames[i].getContentPane()).av;
1565 if (source == target)
1571 frames[i].setClosed(true);
1575 frames[i].setIcon(minimize);
1578 frames[i].toFront();
1582 } catch (java.beans.PropertyVetoException ex)
1597 protected void preferences_actionPerformed(ActionEvent e)
1603 * Shows a file chooser dialog and writes out the current session as a Jalview
1607 public void saveState_actionPerformed()
1609 saveState_actionPerformed(false);
1612 public void saveState_actionPerformed(boolean saveAs)
1614 java.io.File projectFile = getProjectFile();
1615 // autoSave indicates we already have a file and don't need to ask
1616 boolean autoSave = projectFile != null && !saveAs
1617 && BackupFiles.getEnabled();
1619 // System.out.println("autoSave="+autoSave+", projectFile='"+projectFile+"',
1620 // saveAs="+saveAs+", Backups
1621 // "+(BackupFiles.getEnabled()?"enabled":"disabled"));
1623 boolean approveSave = false;
1626 JalviewFileChooser chooser = new JalviewFileChooser("jvp",
1629 chooser.setFileView(new JalviewFileView());
1630 chooser.setDialogTitle(MessageManager.getString("label.save_state"));
1632 int value = chooser.showSaveDialog(this);
1634 if (value == JalviewFileChooser.APPROVE_OPTION)
1636 projectFile = chooser.getSelectedFile();
1637 setProjectFile(projectFile);
1642 if (approveSave || autoSave)
1644 final Desktop me = this;
1645 final java.io.File chosenFile = projectFile;
1646 new Thread(new Runnable()
1651 // TODO: refactor to Jalview desktop session controller action.
1652 setProgressBar(MessageManager.formatMessage(
1653 "label.saving_jalview_project", new Object[]
1654 { chosenFile.getName() }), chosenFile.hashCode());
1655 jalview.bin.Cache.setProperty("LAST_DIRECTORY",
1656 chosenFile.getParent());
1657 // TODO catch and handle errors for savestate
1658 // TODO prevent user from messing with the Desktop whilst we're saving
1661 BackupFiles backupfiles = new BackupFiles(chosenFile);
1663 new Jalview2XML().saveState(backupfiles.getTempFile());
1665 backupfiles.setWriteSuccess(true);
1666 backupfiles.rollBackupsAndRenameTempFile();
1667 } catch (OutOfMemoryError oom)
1669 new OOMWarning("Whilst saving current state to "
1670 + chosenFile.getName(), oom);
1671 } catch (Exception ex)
1673 Cache.log.error("Problems whilst trying to save to "
1674 + chosenFile.getName(), ex);
1675 JvOptionPane.showMessageDialog(me,
1676 MessageManager.formatMessage(
1677 "label.error_whilst_saving_current_state_to",
1679 { chosenFile.getName() }),
1680 MessageManager.getString("label.couldnt_save_project"),
1681 JvOptionPane.WARNING_MESSAGE);
1683 setProgressBar(null, chosenFile.hashCode());
1690 public void saveAsState_actionPerformed(ActionEvent e)
1692 saveState_actionPerformed(true);
1695 private void setProjectFile(File choice)
1697 this.projectFile = choice;
1700 public File getProjectFile()
1702 return this.projectFile;
1706 * Shows a file chooser dialog and tries to read in the selected file as a
1710 public void loadState_actionPerformed()
1712 final String[] suffix = new String[] { "jvp", "jar" };
1713 final String[] desc = new String[] { "Jalview Project",
1714 "Jalview Project (old)" };
1715 JalviewFileChooser chooser = new JalviewFileChooser(
1716 Cache.getProperty("LAST_DIRECTORY"), suffix, desc,
1717 "Jalview Project", true, BackupFiles.getEnabled()); // last two
1721 chooser.setFileView(new JalviewFileView());
1722 chooser.setDialogTitle(MessageManager.getString("label.restore_state"));
1724 int value = chooser.showOpenDialog(this);
1726 if (value == JalviewFileChooser.APPROVE_OPTION)
1728 final File selectedFile = chooser.getSelectedFile();
1729 setProjectFile(selectedFile);
1730 final String choice = selectedFile.getAbsolutePath();
1731 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1732 new Thread(new Runnable()
1737 setProgressBar(MessageManager.formatMessage(
1738 "label.loading_jalview_project", new Object[]
1739 { choice }), choice.hashCode());
1742 new Jalview2XML().loadJalviewAlign(choice);
1743 } catch (OutOfMemoryError oom)
1745 new OOMWarning("Whilst loading project from " + choice, oom);
1746 } catch (Exception ex)
1749 "Problems whilst loading project from " + choice, ex);
1750 JvOptionPane.showMessageDialog(Desktop.desktop,
1751 MessageManager.formatMessage(
1752 "label.error_whilst_loading_project_from",
1755 MessageManager.getString("label.couldnt_load_project"),
1756 JvOptionPane.WARNING_MESSAGE);
1758 setProgressBar(null, choice.hashCode());
1765 public void inputSequence_actionPerformed(ActionEvent e)
1767 new SequenceFetcher(this);
1770 JPanel progressPanel;
1772 ArrayList<JPanel> fileLoadingPanels = new ArrayList<>();
1774 public void startLoading(final String fileName)
1776 if (fileLoadingCount == 0)
1778 fileLoadingPanels.add(addProgressPanel(MessageManager
1779 .formatMessage("label.loading_file", new Object[]
1785 private JPanel addProgressPanel(String string)
1787 if (progressPanel == null)
1789 progressPanel = new JPanel(new GridLayout(1, 1));
1790 totalProgressCount = 0;
1791 instance.getContentPane().add(progressPanel, BorderLayout.SOUTH);
1793 JPanel thisprogress = new JPanel(new BorderLayout(10, 5));
1794 JProgressBar progressBar = new JProgressBar();
1795 progressBar.setIndeterminate(true);
1797 thisprogress.add(new JLabel(string), BorderLayout.WEST);
1799 thisprogress.add(progressBar, BorderLayout.CENTER);
1800 progressPanel.add(thisprogress);
1801 ((GridLayout) progressPanel.getLayout()).setRows(
1802 ((GridLayout) progressPanel.getLayout()).getRows() + 1);
1803 ++totalProgressCount;
1804 instance.validate();
1805 return thisprogress;
1808 int totalProgressCount = 0;
1810 private void removeProgressPanel(JPanel progbar)
1812 if (progressPanel != null)
1814 synchronized (progressPanel)
1816 progressPanel.remove(progbar);
1817 GridLayout gl = (GridLayout) progressPanel.getLayout();
1818 gl.setRows(gl.getRows() - 1);
1819 if (--totalProgressCount < 1)
1821 this.getContentPane().remove(progressPanel);
1822 progressPanel = null;
1829 public void stopLoading()
1832 if (fileLoadingCount < 1)
1834 while (fileLoadingPanels.size() > 0)
1836 removeProgressPanel(fileLoadingPanels.remove(0));
1838 fileLoadingPanels.clear();
1839 fileLoadingCount = 0;
1844 public static int getViewCount(String alignmentId)
1846 AlignmentViewport[] aps = getViewports(alignmentId);
1847 return (aps == null) ? 0 : aps.length;
1852 * @param alignmentId
1853 * - if null, all sets are returned
1854 * @return all AlignmentPanels concerning the alignmentId sequence set
1856 public static AlignmentPanel[] getAlignmentPanels(String alignmentId)
1858 if (Desktop.desktop == null)
1860 // no frames created and in headless mode
1861 // TODO: verify that frames are recoverable when in headless mode
1864 List<AlignmentPanel> aps = new ArrayList<>();
1865 AlignFrame[] frames = getAlignFrames();
1870 for (AlignFrame af : frames)
1872 for (AlignmentPanel ap : af.alignPanels)
1874 if (alignmentId == null
1875 || alignmentId.equals(ap.av.getSequenceSetId()))
1881 if (aps.size() == 0)
1885 AlignmentPanel[] vap = aps.toArray(new AlignmentPanel[aps.size()]);
1890 * get all the viewports on an alignment.
1892 * @param sequenceSetId
1893 * unique alignment id (may be null - all viewports returned in that
1895 * @return all viewports on the alignment bound to sequenceSetId
1897 public static AlignmentViewport[] getViewports(String sequenceSetId)
1899 List<AlignmentViewport> viewp = new ArrayList<>();
1900 if (desktop != null)
1902 AlignFrame[] frames = Desktop.getAlignFrames();
1904 for (AlignFrame afr : frames)
1906 if (sequenceSetId == null || afr.getViewport().getSequenceSetId()
1907 .equals(sequenceSetId))
1909 if (afr.alignPanels != null)
1911 for (AlignmentPanel ap : afr.alignPanels)
1913 if (sequenceSetId == null
1914 || sequenceSetId.equals(ap.av.getSequenceSetId()))
1922 viewp.add(afr.getViewport());
1926 if (viewp.size() > 0)
1928 return viewp.toArray(new AlignmentViewport[viewp.size()]);
1935 * Explode the views in the given frame into separate AlignFrame
1939 public static void explodeViews(AlignFrame af)
1941 int size = af.alignPanels.size();
1947 // FIXME: ideally should use UI interface API
1948 FeatureSettings viewFeatureSettings = (af.featureSettings != null
1949 && af.featureSettings.isOpen()) ? af.featureSettings : null;
1950 Rectangle fsBounds = af.getFeatureSettingsGeometry();
1951 for (int i = 0; i < size; i++)
1953 AlignmentPanel ap = af.alignPanels.get(i);
1955 AlignFrame newaf = new AlignFrame(ap);
1957 // transfer reference for existing feature settings to new alignFrame
1958 if (ap == af.alignPanel)
1960 if (viewFeatureSettings != null && viewFeatureSettings.fr.ap == ap)
1962 newaf.featureSettings = viewFeatureSettings;
1964 newaf.setFeatureSettingsGeometry(fsBounds);
1968 * Restore the view's last exploded frame geometry if known. Multiple
1969 * views from one exploded frame share and restore the same (frame)
1970 * position and size.
1972 Rectangle geometry = ap.av.getExplodedGeometry();
1973 if (geometry != null)
1975 newaf.setBounds(geometry);
1978 ap.av.setGatherViewsHere(false);
1980 addInternalFrame(newaf, af.getTitle(), AlignFrame.DEFAULT_WIDTH,
1981 AlignFrame.DEFAULT_HEIGHT);
1982 // and materialise a new feature settings dialog instance for the new
1984 // (closes the old as if 'OK' was pressed)
1985 if (ap == af.alignPanel && newaf.featureSettings != null
1986 && newaf.featureSettings.isOpen()
1987 && af.alignPanel.getAlignViewport().isShowSequenceFeatures())
1989 newaf.showFeatureSettingsUI();
1993 af.featureSettings = null;
1994 af.alignPanels.clear();
1995 af.closeMenuItem_actionPerformed(true);
2000 * Gather expanded views (separate AlignFrame's) with the same sequence set
2001 * identifier back in to this frame as additional views, and close the
2002 * expanded views. Note the expanded frames may themselves have multiple
2003 * views. We take the lot.
2007 public void gatherViews(AlignFrame source)
2009 source.viewport.setGatherViewsHere(true);
2010 source.viewport.setExplodedGeometry(source.getBounds());
2011 JInternalFrame[] frames = desktop.getAllFrames();
2012 String viewId = source.viewport.getSequenceSetId();
2013 for (int t = 0; t < frames.length; t++)
2015 if (frames[t] instanceof AlignFrame && frames[t] != source)
2017 AlignFrame af = (AlignFrame) frames[t];
2018 boolean gatherThis = false;
2019 for (int a = 0; a < af.alignPanels.size(); a++)
2021 AlignmentPanel ap = af.alignPanels.get(a);
2022 if (viewId.equals(ap.av.getSequenceSetId()))
2025 ap.av.setGatherViewsHere(false);
2026 ap.av.setExplodedGeometry(af.getBounds());
2027 source.addAlignmentPanel(ap, false);
2033 if (af.featureSettings != null && af.featureSettings.isOpen())
2035 if (source.featureSettings == null)
2037 // preserve the feature settings geometry for this frame
2038 source.featureSettings = af.featureSettings;
2039 source.setFeatureSettingsGeometry(
2040 af.getFeatureSettingsGeometry());
2044 // close it and forget
2045 af.featureSettings.close();
2048 af.alignPanels.clear();
2049 af.closeMenuItem_actionPerformed(true);
2053 // refresh the feature setting UI for the source frame if it exists
2054 if (source.featureSettings != null && source.featureSettings.isOpen())
2056 source.showFeatureSettingsUI();
2061 public JInternalFrame[] getAllFrames()
2063 return desktop.getAllFrames();
2067 * Checks the given url to see if it gives a response indicating that the user
2068 * should be informed of a new questionnaire.
2072 public void checkForQuestionnaire(String url)
2074 UserQuestionnaireCheck jvq = new UserQuestionnaireCheck(url);
2075 // javax.swing.SwingUtilities.invokeLater(jvq);
2076 new Thread(jvq).start();
2079 public void checkURLLinks()
2081 // Thread off the URL link checker
2082 addDialogThread(new Runnable()
2087 if (Cache.getDefault("CHECKURLLINKS", true))
2089 // check what the actual links are - if it's just the default don't
2090 // bother with the warning
2091 List<String> links = Preferences.sequenceUrlLinks
2094 // only need to check links if there is one with a
2095 // SEQUENCE_ID which is not the default EMBL_EBI link
2096 ListIterator<String> li = links.listIterator();
2097 boolean check = false;
2098 List<JLabel> urls = new ArrayList<>();
2099 while (li.hasNext())
2101 String link = li.next();
2102 if (link.contains(jalview.util.UrlConstants.SEQUENCE_ID)
2103 && !UrlConstants.isDefaultString(link))
2106 int barPos = link.indexOf("|");
2107 String urlMsg = barPos == -1 ? link
2108 : link.substring(0, barPos) + ": "
2109 + link.substring(barPos + 1);
2110 urls.add(new JLabel(urlMsg));
2118 // ask user to check in case URL links use old style tokens
2119 // ($SEQUENCE_ID$ for sequence id _or_ accession id)
2120 JPanel msgPanel = new JPanel();
2121 msgPanel.setLayout(new BoxLayout(msgPanel, BoxLayout.PAGE_AXIS));
2122 msgPanel.add(Box.createVerticalGlue());
2123 JLabel msg = new JLabel(MessageManager
2124 .getString("label.SEQUENCE_ID_for_DB_ACCESSION1"));
2125 JLabel msg2 = new JLabel(MessageManager
2126 .getString("label.SEQUENCE_ID_for_DB_ACCESSION2"));
2128 for (JLabel url : urls)
2134 final JCheckBox jcb = new JCheckBox(
2135 MessageManager.getString("label.do_not_display_again"));
2136 jcb.addActionListener(new ActionListener()
2139 public void actionPerformed(ActionEvent e)
2141 // update Cache settings for "don't show this again"
2142 boolean showWarningAgain = !jcb.isSelected();
2143 Cache.setProperty("CHECKURLLINKS",
2144 Boolean.valueOf(showWarningAgain).toString());
2149 JvOptionPane.showMessageDialog(Desktop.desktop, msgPanel,
2151 .getString("label.SEQUENCE_ID_no_longer_used"),
2152 JvOptionPane.WARNING_MESSAGE);
2159 * Proxy class for JDesktopPane which optionally displays the current memory
2160 * usage and highlights the desktop area with a red bar if free memory runs
2165 public class MyDesktopPane extends JDesktopPane implements Runnable
2168 private static final float ONE_MB = 1048576f;
2170 boolean showMemoryUsage = false;
2174 java.text.NumberFormat df;
2176 float maxMemory, allocatedMemory, freeMemory, totalFreeMemory,
2179 public MyDesktopPane(boolean showMemoryUsage)
2181 showMemoryUsage(showMemoryUsage);
2184 public void showMemoryUsage(boolean showMemory)
2186 this.showMemoryUsage = showMemory;
2189 Thread worker = new Thread(this);
2195 public boolean isShowMemoryUsage()
2197 return showMemoryUsage;
2203 df = java.text.NumberFormat.getNumberInstance();
2204 df.setMaximumFractionDigits(2);
2205 runtime = Runtime.getRuntime();
2207 while (showMemoryUsage)
2211 maxMemory = runtime.maxMemory() / ONE_MB;
2212 allocatedMemory = runtime.totalMemory() / ONE_MB;
2213 freeMemory = runtime.freeMemory() / ONE_MB;
2214 totalFreeMemory = freeMemory + (maxMemory - allocatedMemory);
2216 percentUsage = (totalFreeMemory / maxMemory) * 100;
2218 // if (percentUsage < 20)
2220 // border1 = BorderFactory.createMatteBorder(12, 12, 12, 12,
2222 // instance.set.setBorder(border1);
2225 // sleep after showing usage
2227 } catch (Exception ex)
2229 ex.printStackTrace();
2235 public void paintComponent(Graphics g)
2237 if (showMemoryUsage && g != null && df != null)
2239 if (percentUsage < 20)
2241 g.setColor(Color.red);
2243 FontMetrics fm = g.getFontMetrics();
2246 g.drawString(MessageManager.formatMessage("label.memory_stats",
2248 { df.format(totalFreeMemory), df.format(maxMemory),
2249 df.format(percentUsage) }),
2250 10, getHeight() - fm.getHeight());
2257 * Accessor method to quickly get all the AlignmentFrames loaded.
2259 * @return an array of AlignFrame, or null if none found
2261 public static AlignFrame[] getAlignFrames()
2263 if (Jalview.isHeadlessMode())
2265 // Desktop.desktop is null in headless mode
2266 return new AlignFrame[] { Jalview.currentAlignFrame };
2269 JInternalFrame[] frames = Desktop.desktop.getAllFrames();
2275 List<AlignFrame> avp = new ArrayList<>();
2277 for (int i = frames.length - 1; i > -1; i--)
2279 if (frames[i] instanceof AlignFrame)
2281 avp.add((AlignFrame) frames[i]);
2283 else if (frames[i] instanceof SplitFrame)
2286 * Also check for a split frame containing an AlignFrame
2288 GSplitFrame sf = (GSplitFrame) frames[i];
2289 if (sf.getTopFrame() instanceof AlignFrame)
2291 avp.add((AlignFrame) sf.getTopFrame());
2293 if (sf.getBottomFrame() instanceof AlignFrame)
2295 avp.add((AlignFrame) sf.getBottomFrame());
2299 if (avp.size() == 0)
2303 AlignFrame afs[] = avp.toArray(new AlignFrame[avp.size()]);
2308 * Returns an array of any AppJmol frames in the Desktop (or null if none).
2312 public GStructureViewer[] getJmols()
2314 JInternalFrame[] frames = Desktop.desktop.getAllFrames();
2320 List<GStructureViewer> avp = new ArrayList<>();
2322 for (int i = frames.length - 1; i > -1; i--)
2324 if (frames[i] instanceof AppJmol)
2326 GStructureViewer af = (GStructureViewer) frames[i];
2330 if (avp.size() == 0)
2334 GStructureViewer afs[] = avp.toArray(new GStructureViewer[avp.size()]);
2339 * Add Groovy Support to Jalview
2342 public void groovyShell_actionPerformed()
2346 openGroovyConsole();
2347 } catch (Exception ex)
2349 jalview.bin.Cache.log.error("Groovy Shell Creation failed.", ex);
2350 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
2352 MessageManager.getString("label.couldnt_create_groovy_shell"),
2353 MessageManager.getString("label.groovy_support_failed"),
2354 JvOptionPane.ERROR_MESSAGE);
2359 * Open the Groovy console
2361 void openGroovyConsole()
2363 if (groovyConsole == null)
2365 groovyConsole = new groovy.ui.Console();
2366 groovyConsole.setVariable("Jalview", this);
2367 groovyConsole.run();
2370 * We allow only one console at a time, so that AlignFrame menu option
2371 * 'Calculate | Run Groovy script' is unambiguous.
2372 * Disable 'Groovy Console', and enable 'Run script', when the console is
2373 * opened, and the reverse when it is closed
2375 Window window = (Window) groovyConsole.getFrame();
2376 window.addWindowListener(new WindowAdapter()
2379 public void windowClosed(WindowEvent e)
2382 * rebind CMD-Q from Groovy Console to Jalview Quit
2385 enableExecuteGroovy(false);
2391 * show Groovy console window (after close and reopen)
2393 ((Window) groovyConsole.getFrame()).setVisible(true);
2396 * if we got this far, enable 'Run Groovy' in AlignFrame menus
2397 * and disable opening a second console
2399 enableExecuteGroovy(true);
2403 * Bind Ctrl/Cmd-Q to Quit - for reset as Groovy Console takes over this
2404 * binding when opened
2406 protected void addQuitHandler()
2409 .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
2411 .getKeyStroke(KeyEvent.VK_Q,
2412 jalview.util.ShortcutKeyMaskExWrapper
2413 .getMenuShortcutKeyMaskEx()),
2415 getRootPane().getActionMap().put("Quit", new AbstractAction()
2418 public void actionPerformed(ActionEvent e)
2426 * Enable or disable 'Run Groovy script' in AlignFrame calculate menus
2429 * true if Groovy console is open
2431 public void enableExecuteGroovy(boolean enabled)
2434 * disable opening a second Groovy console
2435 * (or re-enable when the console is closed)
2437 groovyShell.setEnabled(!enabled);
2439 AlignFrame[] alignFrames = getAlignFrames();
2440 if (alignFrames != null)
2442 for (AlignFrame af : alignFrames)
2444 af.setGroovyEnabled(enabled);
2450 * Progress bars managed by the IProgressIndicator method.
2452 private Hashtable<Long, JPanel> progressBars;
2454 private Hashtable<Long, IProgressIndicatorHandler> progressBarHandlers;
2459 * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
2462 public void setProgressBar(String message, long id)
2464 if (progressBars == null)
2466 progressBars = new Hashtable<>();
2467 progressBarHandlers = new Hashtable<>();
2470 if (progressBars.get(Long.valueOf(id)) != null)
2472 JPanel panel = progressBars.remove(Long.valueOf(id));
2473 if (progressBarHandlers.contains(Long.valueOf(id)))
2475 progressBarHandlers.remove(Long.valueOf(id));
2477 removeProgressPanel(panel);
2481 progressBars.put(Long.valueOf(id), addProgressPanel(message));
2488 * @see jalview.gui.IProgressIndicator#registerHandler(long,
2489 * jalview.gui.IProgressIndicatorHandler)
2492 public void registerHandler(final long id,
2493 final IProgressIndicatorHandler handler)
2495 if (progressBarHandlers == null
2496 || !progressBars.containsKey(Long.valueOf(id)))
2498 throw new Error(MessageManager.getString(
2499 "error.call_setprogressbar_before_registering_handler"));
2501 progressBarHandlers.put(Long.valueOf(id), handler);
2502 final JPanel progressPanel = progressBars.get(Long.valueOf(id));
2503 if (handler.canCancel())
2505 JButton cancel = new JButton(
2506 MessageManager.getString("action.cancel"));
2507 final IProgressIndicator us = this;
2508 cancel.addActionListener(new ActionListener()
2512 public void actionPerformed(ActionEvent e)
2514 handler.cancelActivity(id);
2515 us.setProgressBar(MessageManager
2516 .formatMessage("label.cancelled_params", new Object[]
2517 { ((JLabel) progressPanel.getComponent(0)).getText() }),
2521 progressPanel.add(cancel, BorderLayout.EAST);
2527 * @return true if any progress bars are still active
2530 public boolean operationInProgress()
2532 if (progressBars != null && progressBars.size() > 0)
2540 * This will return the first AlignFrame holding the given viewport instance.
2541 * It will break if there are more than one AlignFrames viewing a particular
2545 * @return alignFrame for viewport
2547 public static AlignFrame getAlignFrameFor(AlignViewportI viewport)
2549 if (desktop != null)
2551 AlignmentPanel[] aps = getAlignmentPanels(
2552 viewport.getSequenceSetId());
2553 for (int panel = 0; aps != null && panel < aps.length; panel++)
2555 if (aps[panel] != null && aps[panel].av == viewport)
2557 return aps[panel].alignFrame;
2564 public VamsasApplication getVamsasApplication()
2566 // TODO: JAL-3311 remove remaining code from Jalview relating to VAMSAS
2572 * flag set if jalview GUI is being operated programmatically
2574 private boolean inBatchMode = false;
2577 * check if jalview GUI is being operated programmatically
2579 * @return inBatchMode
2581 public boolean isInBatchMode()
2587 * set flag if jalview GUI is being operated programmatically
2589 * @param inBatchMode
2591 public void setInBatchMode(boolean inBatchMode)
2593 this.inBatchMode = inBatchMode;
2597 * start service discovery and wait till it is done
2599 public void startServiceDiscovery()
2601 startServiceDiscovery(false);
2605 * start service discovery threads - blocking or non-blocking
2609 public void startServiceDiscovery(boolean blocking)
2611 startServiceDiscovery(blocking, false);
2615 * start service discovery threads
2618 * - false means call returns immediately
2619 * @param ignore_SHOW_JWS2_SERVICES_preference
2620 * - when true JABA services are discovered regardless of user's JWS2
2621 * discovery preference setting
2623 public void startServiceDiscovery(boolean blocking,
2624 boolean ignore_SHOW_JWS2_SERVICES_preference)
2626 boolean alive = true;
2627 Thread t0 = null, t1 = null, t2 = null;
2628 // JAL-940 - JALVIEW 1 services are now being EOLed as of JABA 2.1 release
2631 // todo: changesupport handlers need to be transferred
2632 if (discoverer == null)
2634 discoverer = new jalview.ws.jws1.Discoverer();
2635 // register PCS handler for desktop.
2636 discoverer.addPropertyChangeListener(changeSupport);
2638 // JAL-940 - disabled JWS1 service configuration - always start discoverer
2639 // until we phase out completely
2640 (t0 = new Thread(discoverer)).start();
2643 if (ignore_SHOW_JWS2_SERVICES_preference
2644 || Cache.getDefault("SHOW_JWS2_SERVICES", true))
2646 t2 = jalview.ws.jws2.Jws2Discoverer.getDiscoverer()
2647 .startDiscoverer(changeSupport);
2651 // TODO: do rest service discovery
2660 } catch (Exception e)
2663 alive = (t1 != null && t1.isAlive()) || (t2 != null && t2.isAlive())
2664 || (t3 != null && t3.isAlive())
2665 || (t0 != null && t0.isAlive());
2671 * called to check if the service discovery process completed successfully.
2675 protected void JalviewServicesChanged(PropertyChangeEvent evt)
2677 if (evt.getNewValue() == null || evt.getNewValue() instanceof Vector)
2679 final String ermsg = jalview.ws.jws2.Jws2Discoverer.getDiscoverer()
2680 .getErrorMessages();
2683 if (Cache.getDefault("SHOW_WSDISCOVERY_ERRORS", true))
2685 if (serviceChangedDialog == null)
2687 // only run if we aren't already displaying one of these.
2688 addDialogThread(serviceChangedDialog = new Runnable()
2695 * JalviewDialog jd =new JalviewDialog() {
2697 * @Override protected void cancelPressed() { // TODO
2698 * Auto-generated method stub
2700 * }@Override protected void okPressed() { // TODO
2701 * Auto-generated method stub
2703 * }@Override protected void raiseClosed() { // TODO
2704 * Auto-generated method stub
2706 * } }; jd.initDialogFrame(new
2707 * JLabel("<html><table width=\"450\"><tr><td>" + ermsg +
2708 * "<br/>It may be that you have invalid JABA URLs in your web service preferences,"
2709 * + " or mis-configured HTTP proxy settings.<br/>" +
2710 * "Check the <em>Connections</em> and <em>Web services</em> tab of the"
2712 * " Tools->Preferences dialog box to change them.</td></tr></table></html>"
2713 * ), true, true, "Web Service Configuration Problem", 450,
2716 * jd.waitForInput();
2718 JvOptionPane.showConfirmDialog(Desktop.desktop,
2719 new JLabel("<html><table width=\"450\"><tr><td>"
2720 + ermsg + "</td></tr></table>"
2721 + "<p>It may be that you have invalid JABA URLs<br/>in your web service preferences,"
2722 + "<br>or as a command-line argument, or mis-configured HTTP proxy settings.</p>"
2723 + "<p>Check the <em>Connections</em> and <em>Web services</em> tab<br/>of the"
2724 + " Tools->Preferences dialog box to change them.</p></html>"),
2725 "Web Service Configuration Problem",
2726 JvOptionPane.DEFAULT_OPTION,
2727 JvOptionPane.ERROR_MESSAGE);
2728 serviceChangedDialog = null;
2737 "Errors reported by JABA discovery service. Check web services preferences.\n"
2744 private Runnable serviceChangedDialog = null;
2747 * start a thread to open a URL in the configured browser. Pops up a warning
2748 * dialog to the user if there is an exception when calling out to the browser
2753 public static void showUrl(final String url)
2755 showUrl(url, Desktop.instance);
2759 * Like showUrl but allows progress handler to be specified
2763 * (null) or object implementing IProgressIndicator
2765 public static void showUrl(final String url,
2766 final IProgressIndicator progress)
2768 new Thread(new Runnable()
2775 if (progress != null)
2777 progress.setProgressBar(MessageManager
2778 .formatMessage("status.opening_params", new Object[]
2779 { url }), this.hashCode());
2781 jalview.util.BrowserLauncher.openURL(url);
2782 } catch (Exception ex)
2784 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
2786 .getString("label.web_browser_not_found_unix"),
2787 MessageManager.getString("label.web_browser_not_found"),
2788 JvOptionPane.WARNING_MESSAGE);
2790 ex.printStackTrace();
2792 if (progress != null)
2794 progress.setProgressBar(null, this.hashCode());
2800 public static WsParamSetManager wsparamManager = null;
2802 public static ParamManager getUserParameterStore()
2804 if (wsparamManager == null)
2806 wsparamManager = new WsParamSetManager();
2808 return wsparamManager;
2812 * static hyperlink handler proxy method for use by Jalview's internal windows
2816 public static void hyperlinkUpdate(HyperlinkEvent e)
2818 if (e.getEventType() == EventType.ACTIVATED)
2823 url = e.getURL().toString();
2824 Desktop.showUrl(url);
2825 } catch (Exception x)
2829 if (Cache.log != null)
2831 Cache.log.error("Couldn't handle string " + url + " as a URL.");
2836 "Couldn't handle string " + url + " as a URL.");
2839 // ignore any exceptions due to dud links.
2846 * single thread that handles display of dialogs to user.
2848 ExecutorService dialogExecutor = Executors.newSingleThreadExecutor();
2851 * flag indicating if dialogExecutor should try to acquire a permit
2853 private volatile boolean dialogPause = true;
2858 private java.util.concurrent.Semaphore block = new Semaphore(0);
2860 private static groovy.ui.Console groovyConsole;
2863 * add another dialog thread to the queue
2867 public void addDialogThread(final Runnable prompter)
2869 dialogExecutor.submit(new Runnable()
2879 } catch (InterruptedException x)
2884 if (instance == null)
2890 SwingUtilities.invokeAndWait(prompter);
2891 } catch (Exception q)
2893 Cache.log.warn("Unexpected Exception in dialog thread.", q);
2899 public void startDialogQueue()
2901 // set the flag so we don't pause waiting for another permit and semaphore
2902 // the current task to begin
2903 dialogPause = false;
2908 protected void snapShotWindow_actionPerformed(ActionEvent e)
2912 ImageMaker im = new jalview.util.ImageMaker(
2913 this, ImageMaker.TYPE.EPS, "View of Desktop", getWidth(),
2914 getHeight(), of = new File("Jalview_snapshot"
2915 + System.currentTimeMillis() + ".eps"),
2916 "View of desktop", null, 0, false);
2919 paintAll(im.getGraphics());
2921 } catch (Exception q)
2923 Cache.log.error("Couldn't write snapshot to " + of.getAbsolutePath(),
2927 Cache.log.info("Successfully written snapshot to file "
2928 + of.getAbsolutePath());
2932 * Explode the views in the given SplitFrame into separate SplitFrame windows.
2933 * This respects (remembers) any previous 'exploded geometry' i.e. the size
2934 * and location last time the view was expanded (if any). However it does not
2935 * remember the split pane divider location - this is set to match the
2936 * 'exploding' frame.
2940 public void explodeViews(SplitFrame sf)
2942 AlignFrame oldTopFrame = (AlignFrame) sf.getTopFrame();
2943 AlignFrame oldBottomFrame = (AlignFrame) sf.getBottomFrame();
2944 List<? extends AlignmentViewPanel> topPanels = oldTopFrame
2946 List<? extends AlignmentViewPanel> bottomPanels = oldBottomFrame
2948 int viewCount = topPanels.size();
2955 * Processing in reverse order works, forwards order leaves the first panels
2956 * not visible. I don't know why!
2958 for (int i = viewCount - 1; i >= 0; i--)
2961 * Make new top and bottom frames. These take over the respective
2962 * AlignmentPanel objects, including their AlignmentViewports, so the
2963 * cdna/protein relationships between the viewports is carried over to the
2966 * explodedGeometry holds the (x, y) position of the previously exploded
2967 * SplitFrame, and the (width, height) of the AlignFrame component
2969 AlignmentPanel topPanel = (AlignmentPanel) topPanels.get(i);
2970 AlignFrame newTopFrame = new AlignFrame(topPanel);
2971 newTopFrame.setSize(oldTopFrame.getSize());
2972 newTopFrame.setVisible(true);
2973 Rectangle geometry = ((AlignViewport) topPanel.getAlignViewport())
2974 .getExplodedGeometry();
2975 if (geometry != null)
2977 newTopFrame.setSize(geometry.getSize());
2980 AlignmentPanel bottomPanel = (AlignmentPanel) bottomPanels.get(i);
2981 AlignFrame newBottomFrame = new AlignFrame(bottomPanel);
2982 newBottomFrame.setSize(oldBottomFrame.getSize());
2983 newBottomFrame.setVisible(true);
2984 geometry = ((AlignViewport) bottomPanel.getAlignViewport())
2985 .getExplodedGeometry();
2986 if (geometry != null)
2988 newBottomFrame.setSize(geometry.getSize());
2991 topPanel.av.setGatherViewsHere(false);
2992 bottomPanel.av.setGatherViewsHere(false);
2993 JInternalFrame splitFrame = new SplitFrame(newTopFrame,
2995 if (geometry != null)
2997 splitFrame.setLocation(geometry.getLocation());
2999 Desktop.addInternalFrame(splitFrame, sf.getTitle(), -1, -1);
3003 * Clear references to the panels (now relocated in the new SplitFrames)
3004 * before closing the old SplitFrame.
3007 bottomPanels.clear();
3012 * Gather expanded split frames, sharing the same pairs of sequence set ids,
3013 * back into the given SplitFrame as additional views. Note that the gathered
3014 * frames may themselves have multiple views.
3018 public void gatherViews(GSplitFrame source)
3021 * special handling of explodedGeometry for a view within a SplitFrame: - it
3022 * holds the (x, y) position of the enclosing SplitFrame, and the (width,
3023 * height) of the AlignFrame component
3025 AlignFrame myTopFrame = (AlignFrame) source.getTopFrame();
3026 AlignFrame myBottomFrame = (AlignFrame) source.getBottomFrame();
3027 myTopFrame.viewport.setExplodedGeometry(new Rectangle(source.getX(),
3028 source.getY(), myTopFrame.getWidth(), myTopFrame.getHeight()));
3029 myBottomFrame.viewport
3030 .setExplodedGeometry(new Rectangle(source.getX(), source.getY(),
3031 myBottomFrame.getWidth(), myBottomFrame.getHeight()));
3032 myTopFrame.viewport.setGatherViewsHere(true);
3033 myBottomFrame.viewport.setGatherViewsHere(true);
3034 String topViewId = myTopFrame.viewport.getSequenceSetId();
3035 String bottomViewId = myBottomFrame.viewport.getSequenceSetId();
3037 JInternalFrame[] frames = desktop.getAllFrames();
3038 for (JInternalFrame frame : frames)
3040 if (frame instanceof SplitFrame && frame != source)
3042 SplitFrame sf = (SplitFrame) frame;
3043 AlignFrame topFrame = (AlignFrame) sf.getTopFrame();
3044 AlignFrame bottomFrame = (AlignFrame) sf.getBottomFrame();
3045 boolean gatherThis = false;
3046 for (int a = 0; a < topFrame.alignPanels.size(); a++)
3048 AlignmentPanel topPanel = topFrame.alignPanels.get(a);
3049 AlignmentPanel bottomPanel = bottomFrame.alignPanels.get(a);
3050 if (topViewId.equals(topPanel.av.getSequenceSetId())
3051 && bottomViewId.equals(bottomPanel.av.getSequenceSetId()))
3054 topPanel.av.setGatherViewsHere(false);
3055 bottomPanel.av.setGatherViewsHere(false);
3056 topPanel.av.setExplodedGeometry(
3057 new Rectangle(sf.getLocation(), topFrame.getSize()));
3058 bottomPanel.av.setExplodedGeometry(
3059 new Rectangle(sf.getLocation(), bottomFrame.getSize()));
3060 myTopFrame.addAlignmentPanel(topPanel, false);
3061 myBottomFrame.addAlignmentPanel(bottomPanel, false);
3067 topFrame.getAlignPanels().clear();
3068 bottomFrame.getAlignPanels().clear();
3075 * The dust settles...give focus to the tab we did this from.
3077 myTopFrame.setDisplayedView(myTopFrame.alignPanel);
3080 public static groovy.ui.Console getGroovyConsole()
3082 return groovyConsole;
3086 * handles the payload of a drag and drop event.
3088 * TODO refactor to desktop utilities class
3091 * - Data source strings extracted from the drop event
3093 * - protocol for each data source extracted from the drop event
3097 * - the payload from the drop event
3100 public static void transferFromDropTarget(List<String> files,
3101 List<DataSourceType> protocols, DropTargetDropEvent evt,
3102 Transferable t) throws Exception
3105 DataFlavor uriListFlavor = new DataFlavor(
3106 "text/uri-list;class=java.lang.String"), urlFlavour = null;
3109 urlFlavour = new DataFlavor(
3110 "application/x-java-url; class=java.net.URL");
3111 } catch (ClassNotFoundException cfe)
3113 Cache.log.debug("Couldn't instantiate the URL dataflavor.", cfe);
3116 if (urlFlavour != null && t.isDataFlavorSupported(urlFlavour))
3121 java.net.URL url = (URL) t.getTransferData(urlFlavour);
3122 // nb: java 8 osx bug https://bugs.openjdk.java.net/browse/JDK-8156099
3123 // means url may be null.
3126 protocols.add(DataSourceType.URL);
3127 files.add(url.toString());
3128 Cache.log.debug("Drop handled as URL dataflavor "
3129 + files.get(files.size() - 1));
3134 if (Platform.isAMac())
3137 "Please ignore plist error - occurs due to problem with java 8 on OSX");
3141 } catch (Throwable ex)
3143 Cache.log.debug("URL drop handler failed.", ex);
3146 if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
3148 // Works on Windows and MacOSX
3149 Cache.log.debug("Drop handled as javaFileListFlavor");
3150 for (Object file : (List) t
3151 .getTransferData(DataFlavor.javaFileListFlavor))
3153 files.add(((File) file).toString());
3154 protocols.add(DataSourceType.FILE);
3159 // Unix like behaviour
3160 boolean added = false;
3162 if (t.isDataFlavorSupported(uriListFlavor))
3164 Cache.log.debug("Drop handled as uriListFlavor");
3165 // This is used by Unix drag system
3166 data = (String) t.getTransferData(uriListFlavor);
3170 // fallback to text: workaround - on OSX where there's a JVM bug
3171 Cache.log.debug("standard URIListFlavor failed. Trying text");
3172 // try text fallback
3173 DataFlavor textDf = new DataFlavor(
3174 "text/plain;class=java.lang.String");
3175 if (t.isDataFlavorSupported(textDf))
3177 data = (String) t.getTransferData(textDf);
3180 Cache.log.debug("Plain text drop content returned "
3181 + (data == null ? "Null - failed" : data));
3186 while (protocols.size() < files.size())
3188 Cache.log.debug("Adding missing FILE protocol for "
3189 + files.get(protocols.size()));
3190 protocols.add(DataSourceType.FILE);
3192 for (java.util.StringTokenizer st = new java.util.StringTokenizer(
3193 data, "\r\n"); st.hasMoreTokens();)
3196 String s = st.nextToken();
3197 if (s.startsWith("#"))
3199 // the line is a comment (as per the RFC 2483)
3202 java.net.URI uri = new java.net.URI(s);
3203 if (uri.getScheme().toLowerCase().startsWith("http"))
3205 protocols.add(DataSourceType.URL);
3206 files.add(uri.toString());
3210 // otherwise preserve old behaviour: catch all for file objects
3211 java.io.File file = new java.io.File(uri);
3212 protocols.add(DataSourceType.FILE);
3213 files.add(file.toString());
3218 if (Cache.log.isDebugEnabled())
3220 if (data == null || !added)
3223 if (t.getTransferDataFlavors() != null
3224 && t.getTransferDataFlavors().length > 0)
3227 "Couldn't resolve drop data. Here are the supported flavors:");
3228 for (DataFlavor fl : t.getTransferDataFlavors())
3231 "Supported transfer dataflavor: " + fl.toString());
3232 Object df = t.getTransferData(fl);
3235 Cache.log.debug("Retrieves: " + df);
3239 Cache.log.debug("Retrieved nothing");
3245 Cache.log.debug("Couldn't resolve dataflavor for drop: "
3251 if (Platform.isWindows())
3254 Cache.log.debug("Scanning dropped content for Windows Link Files");
3256 // resolve any .lnk files in the file drop
3257 for (int f = 0; f < files.size(); f++)
3259 String source = files.get(f).toLowerCase();
3260 if (protocols.get(f).equals(DataSourceType.FILE)
3261 && (source.endsWith(".lnk") || source.endsWith(".url")
3262 || source.endsWith(".site")))
3266 File lf = new File(files.get(f));
3267 // process link file to get a URL
3268 Cache.log.debug("Found potential link file: " + lf);
3269 WindowsShortcut wscfile = new WindowsShortcut(lf);
3270 String fullname = wscfile.getRealFilename();
3271 protocols.set(f, FormatAdapter.checkProtocol(fullname));
3272 files.set(f, fullname);
3273 Cache.log.debug("Parsed real filename " + fullname
3274 + " to extract protocol: " + protocols.get(f));
3275 } catch (Exception ex)
3278 "Couldn't parse " + files.get(f) + " as a link file.",
3287 * Sets the Preferences property for experimental features to True or False
3288 * depending on the state of the controlling menu item
3291 protected void showExperimental_actionPerformed(boolean selected)
3293 Cache.setProperty(EXPERIMENTAL_FEATURES, Boolean.toString(selected));
3297 * Answers a (possibly empty) list of any structure viewer frames (currently
3298 * for either Jmol or Chimera) which are currently open. This may optionally
3299 * be restricted to viewers of a specified class, or viewers linked to a
3300 * specified alignment panel.
3303 * if not null, only return viewers linked to this panel
3304 * @param structureViewerClass
3305 * if not null, only return viewers of this class
3308 public List<StructureViewerBase> getStructureViewers(
3309 AlignmentPanel apanel,
3310 Class<? extends StructureViewerBase> structureViewerClass)
3312 List<StructureViewerBase> result = new ArrayList<>();
3313 JInternalFrame[] frames = Desktop.instance.getAllFrames();
3315 for (JInternalFrame frame : frames)
3317 if (frame instanceof StructureViewerBase)
3319 if (structureViewerClass == null
3320 || structureViewerClass.isInstance(frame))
3323 || ((StructureViewerBase) frame).isLinkedWith(apanel))
3325 result.add((StructureViewerBase) frame);