3 import jalview.jbgui.GAlignFrame;
4 import jalview.jbgui.GSplitFrame;
5 import jalview.structure.StructureSelectionManager;
7 import java.awt.Component;
8 import java.awt.Toolkit;
9 import java.awt.event.ActionEvent;
10 import java.awt.event.ActionListener;
11 import java.awt.event.KeyAdapter;
12 import java.awt.event.KeyEvent;
13 import java.awt.event.KeyListener;
14 import java.beans.PropertyVetoException;
15 import java.util.Map.Entry;
17 import javax.swing.AbstractAction;
18 import javax.swing.InputMap;
19 import javax.swing.JComponent;
20 import javax.swing.JMenuItem;
21 import javax.swing.KeyStroke;
22 import javax.swing.event.InternalFrameAdapter;
23 import javax.swing.event.InternalFrameEvent;
26 * An internal frame on the desktop that hosts a horizontally split view of
27 * linked DNA and Protein alignments. Additional views can be created in linked
28 * pairs, expanded to separate split frames, or regathered into a single frame.
30 * (Some) operations on each alignment are automatically mirrored on the other.
31 * These include mouseover (highlighting), sequence and column selection,
32 * sequence ordering and sorting, and grouping, colouring and sorting by tree.
37 public class SplitFrame extends GSplitFrame
39 private static final long serialVersionUID = 1L;
41 public SplitFrame(GAlignFrame top, GAlignFrame bottom)
48 * Initialise this frame.
52 setSize(AlignFrame.DEFAULT_WIDTH, Desktop.instance.getHeight() - 20);
54 addCloseFrameListener();
62 * Add a listener to tidy up when the frame is closed.
64 protected void addCloseFrameListener()
66 addInternalFrameListener(new InternalFrameAdapter()
69 public void internalFrameClosed(InternalFrameEvent evt)
71 if (getTopFrame() instanceof AlignFrame)
73 ((AlignFrame) getTopFrame())
74 .closeMenuItem_actionPerformed(true);
76 if (getBottomFrame() instanceof AlignFrame)
78 ((AlignFrame) getBottomFrame())
79 .closeMenuItem_actionPerformed(true);
86 * Add a key listener that delegates to whichever split component the mouse is
87 * in (or does nothing if neither).
89 protected void addKeyListener()
91 addKeyListener(new KeyAdapter() {
94 public void keyPressed(KeyEvent e)
96 AlignFrame af = (AlignFrame) getFrameAtMouse();
99 * Intercept and override any keys here if wanted.
101 if (!overrideKey(e, af))
105 for (KeyListener kl : af.getKeyListeners())
114 public void keyReleased(KeyEvent e)
116 Component c = getFrameAtMouse();
119 for (KeyListener kl : c.getKeyListeners())
130 * Returns true if the key event is overriden and actioned (or ignored) here,
131 * else returns false, indicating it should be delegated to the AlignFrame's
134 * We can't handle Cmd-Key combinations here, instead this is done by
135 * overriding key bindings.
137 * @see addKeyOverrides
142 protected boolean overrideKey(KeyEvent e, AlignFrame af)
144 boolean actioned = false;
145 int keyCode = e.getKeyCode();
148 case KeyEvent.VK_DOWN:
149 if (e.isAltDown() || !af.viewport.cursorMode)
152 * Key down (or Alt-key-down in cursor mode) - move selected sequences
154 ((AlignFrame) getTopFrame()).moveSelectedSequences(false);
155 ((AlignFrame) getBottomFrame()).moveSelectedSequences(false);
161 if (e.isAltDown() || !af.viewport.cursorMode)
164 * Key up (or Alt-key-up in cursor mode) - move selected sequences
166 ((AlignFrame) getTopFrame()).moveSelectedSequences(true);
167 ((AlignFrame) getBottomFrame()).moveSelectedSequences(true);
177 * Set key bindings (recommended for Swing over key accelerators).
179 private void addKeyBindings()
181 overrideDelegatedKeyBindings();
183 overrideImplementedKeyBindings();
187 * Override key bindings with alternative action methods implemented in this
190 protected void overrideImplementedKeyBindings()
194 overrideExpandViews();
195 overrideGatherViews();
199 * Replace Cmd-W close view action with our version.
201 protected void overrideCloseView()
203 AbstractAction action;
205 * Ctrl-W / Cmd-W - close view or window
207 KeyStroke key_cmdW = KeyStroke.getKeyStroke(KeyEvent.VK_W, Toolkit
208 .getDefaultToolkit().getMenuShortcutKeyMask(), false);
209 action = new AbstractAction()
212 public void actionPerformed(ActionEvent e)
214 closeView_actionPerformed();
217 overrideKeyBinding(key_cmdW, action);
221 * Replace Cmd-T new view action with our version.
223 protected void overrideNewView()
226 * Ctrl-T / Cmd-T open new view
228 KeyStroke key_cmdT = KeyStroke.getKeyStroke(KeyEvent.VK_T, Toolkit
229 .getDefaultToolkit().getMenuShortcutKeyMask(), false);
230 AbstractAction action = new AbstractAction()
233 public void actionPerformed(ActionEvent e)
235 newView_actionPerformed();
238 overrideKeyBinding(key_cmdT, action);
242 * For now, delegates key events to the corresponding key accelerator for the
243 * AlignFrame that the mouse is in. Hopefully can be simplified in future if
244 * AlignFrame is changed to use key bindings rather than accelerators.
246 protected void overrideDelegatedKeyBindings()
248 if (getTopFrame() instanceof AlignFrame)
251 * Get all accelerator keys in the top frame (the bottom should be
252 * identical) and override each one.
254 for (Entry<KeyStroke, JMenuItem> acc : ((AlignFrame) getTopFrame())
255 .getAccelerators().entrySet())
257 overrideKeyBinding(acc);
263 * Overrides an AlignFrame key accelerator with our version which delegates to
264 * the action listener in whichever frame has the mouse (and does nothing if
269 private void overrideKeyBinding(Entry<KeyStroke, JMenuItem> acc)
271 final KeyStroke ks = acc.getKey();
272 InputMap inputMap = this.getInputMap(JComponent.WHEN_FOCUSED);
273 inputMap.put(ks, ks);
274 this.getActionMap().put(ks, new AbstractAction()
277 public void actionPerformed(ActionEvent e)
279 Component c = getFrameAtMouse();
280 if (c != null && c instanceof AlignFrame)
282 for (ActionListener a : ((AlignFrame) c).getAccelerators()
283 .get(ks).getActionListeners())
285 a.actionPerformed(null);
293 * Replace an accelerator key's action with the specified action.
297 protected void overrideKeyBinding(KeyStroke ks, AbstractAction action)
299 this.getActionMap().put(ks, action);
300 overrideMenuItem(ks, action);
304 * Create and link new views (with matching names) in both panes.
306 * Note this is _not_ multiple tabs, each hosting a split pane view, rather it
307 * is a single split pane with each split holding multiple tabs which are
310 * TODO implement instead with a tabbed holder in the SplitView, each tab
311 * holding a single JSplitPane. Would avoid a duplicated tab, at the cost of
312 * some additional coding.
314 protected void newView_actionPerformed()
316 AlignFrame topFrame = (AlignFrame) getTopFrame();
317 AlignFrame bottomFrame = (AlignFrame) getBottomFrame();
319 AlignmentPanel newTopPanel = topFrame.newView(null, true);
320 AlignmentPanel newBottomPanel = bottomFrame.newView(null, true);
323 * This currently (for the first new view only) leaves the top pane on tab 0
324 * but the bottom on tab 1. This results from 'setInitialTabVisible' echoing
325 * from the bottom back to the first frame. Next line is a fudge to work
326 * around this. TODO find a better way.
328 if (topFrame.getTabIndex() != bottomFrame.getTabIndex())
330 topFrame.setDisplayedView(newTopPanel);
333 newBottomPanel.av.viewName = newTopPanel.av.viewName;
334 newTopPanel.av.setCodingComplement(newBottomPanel.av);
336 final StructureSelectionManager ssm = StructureSelectionManager
337 .getStructureSelectionManager(Desktop.instance);
338 ssm.addCommandListener(newTopPanel.av);
339 ssm.addCommandListener(newBottomPanel.av);
343 * Close the currently selected view in both panes. If there is only one view,
344 * close this split frame.
346 protected void closeView_actionPerformed()
348 int viewCount = ((AlignFrame) getTopFrame()).getAlignPanels().size();
355 AlignmentPanel topPanel = ((AlignFrame) getTopFrame()).alignPanel;
356 AlignmentPanel bottomPanel = ((AlignFrame) getBottomFrame()).alignPanel;
358 ((AlignFrame) getTopFrame()).closeView(topPanel);
359 ((AlignFrame) getBottomFrame()).closeView(bottomPanel);
364 * Close child frames and this split frame.
368 ((AlignFrame) getTopFrame()).closeMenuItem_actionPerformed(true);
369 ((AlignFrame) getBottomFrame()).closeMenuItem_actionPerformed(true);
372 this.setClosed(true);
373 } catch (PropertyVetoException e)
380 * Replace AlignFrame 'expand views' action with SplitFrame version.
382 protected void overrideExpandViews()
384 KeyStroke key_X = KeyStroke.getKeyStroke(KeyEvent.VK_X, 0, false);
385 AbstractAction action = new AbstractAction()
388 public void actionPerformed(ActionEvent e)
390 expandViews_actionPerformed();
393 overrideMenuItem(key_X, action);
397 * Replace AlignFrame 'gather views' action with SplitFrame version.
399 protected void overrideGatherViews()
401 KeyStroke key_G = KeyStroke.getKeyStroke(KeyEvent.VK_G, 0, false);
402 AbstractAction action = new AbstractAction()
405 public void actionPerformed(ActionEvent e)
407 gatherViews_actionPerformed();
410 overrideMenuItem(key_G, action);
414 * Override the menu action associated with the keystroke in the child frames,
415 * replacing it with the given action.
420 private void overrideMenuItem(KeyStroke ks, AbstractAction action)
422 overrideMenuItem(ks, action, getTopFrame());
423 overrideMenuItem(ks, action, getBottomFrame());
427 * Override the menu action associated with the keystroke in one child frame,
428 * replacing it with the given action. Mwahahahaha.
434 private void overrideMenuItem(KeyStroke key, final AbstractAction action,
437 if (comp instanceof AlignFrame)
439 JMenuItem mi = ((AlignFrame) comp).getAccelerators().get(key);
442 for (ActionListener al : mi.getActionListeners())
444 mi.removeActionListener(al);
446 mi.addActionListener(new ActionListener()
449 public void actionPerformed(ActionEvent e)
451 action.actionPerformed(e);
459 * Expand any multiple views (which are always in pairs) into separate split
462 protected void expandViews_actionPerformed()
464 Desktop.instance.explodeViews(this);
468 * Gather any other SplitFrame views of this alignment back in as multiple
469 * (pairs of) views in this SplitFrame.
471 protected void gatherViews_actionPerformed()
473 Desktop.instance.gatherViews(this);