3 import jalview.api.ViewStyleI;
4 import jalview.datamodel.AlignmentI;
5 import jalview.jbgui.GAlignFrame;
6 import jalview.jbgui.GSplitFrame;
7 import jalview.structure.StructureSelectionManager;
8 import jalview.viewmodel.AlignmentViewport;
10 import java.awt.Component;
11 import java.awt.Toolkit;
12 import java.awt.event.ActionEvent;
13 import java.awt.event.ActionListener;
14 import java.awt.event.KeyAdapter;
15 import java.awt.event.KeyEvent;
16 import java.awt.event.KeyListener;
17 import java.beans.PropertyVetoException;
18 import java.util.Map.Entry;
20 import javax.swing.AbstractAction;
21 import javax.swing.InputMap;
22 import javax.swing.JComponent;
23 import javax.swing.JMenuItem;
24 import javax.swing.KeyStroke;
25 import javax.swing.event.InternalFrameAdapter;
26 import javax.swing.event.InternalFrameEvent;
29 * An internal frame on the desktop that hosts a horizontally split view of
30 * linked DNA and Protein alignments. Additional views can be created in linked
31 * pairs, expanded to separate split frames, or regathered into a single frame.
33 * (Some) operations on each alignment are automatically mirrored on the other.
34 * These include mouseover (highlighting), sequence and column selection,
35 * sequence ordering and sorting, and grouping, colouring and sorting by tree.
40 public class SplitFrame extends GSplitFrame
42 private static final long serialVersionUID = 1L;
44 public SplitFrame(GAlignFrame top, GAlignFrame bottom)
51 * Initialise this frame.
55 getTopFrame().setSplitFrame(this);
56 getBottomFrame().setSplitFrame(this);
57 getTopFrame().setVisible(true);
58 getBottomFrame().setVisible(true);
60 ((AlignFrame) getTopFrame()).getViewport().setCodingComplement(
61 ((AlignFrame) getBottomFrame()).getViewport());
63 setSize(AlignFrame.DEFAULT_WIDTH, Desktop.instance.getHeight() - 20);
67 addCloseFrameListener();
73 addCommandListeners();
77 * Set the top and bottom frames to listen to each others Commands (e.g. Edit,
80 protected void addCommandListeners()
82 // TODO if CommandListener is only ever 1:1 for complementary views,
83 // may change broadcast pattern to direct messaging (more efficient)
84 final StructureSelectionManager ssm = StructureSelectionManager
85 .getStructureSelectionManager(Desktop.instance);
86 ssm.addCommandListener(((AlignFrame) getTopFrame()).getViewport());
87 ssm.addCommandListener(((AlignFrame) getBottomFrame()).getViewport());
91 * Do any tweaking and twerking of the layout wanted.
93 private void adjustLayout()
96 * Ensure sequence ids are the same width for good alignment.
98 int w1 = ((AlignFrame) getTopFrame()).getViewport().getIdWidth();
99 int w2 = ((AlignFrame) getBottomFrame()).getViewport().getIdWidth();
100 int w3 = Math.max(w1, w2);
103 ((AlignFrame) getTopFrame()).getViewport().setIdWidth(w3);
107 ((AlignFrame) getBottomFrame()).getViewport().setIdWidth(w3);
111 * Set the character width for protein to 3 times that for dna.
113 final AlignViewport topViewport = ((AlignFrame) getTopFrame()).viewport;
114 final AlignViewport bottomViewport = ((AlignFrame) getBottomFrame()).viewport;
115 final AlignmentI topAlignment = topViewport.getAlignment();
116 final AlignmentI bottomAlignment = bottomViewport.getAlignment();
117 AlignmentViewport cdna = topAlignment.isNucleotide() ? topViewport
118 : (bottomAlignment.isNucleotide() ? bottomViewport : null);
119 AlignmentViewport protein = !topAlignment.isNucleotide() ? topViewport
120 : (!bottomAlignment.isNucleotide() ? bottomViewport : null);
121 if (protein != null && cdna != null)
123 ViewStyleI vs = cdna.getViewStyle();
124 vs.setCharWidth(3 * vs.getCharWidth());
125 protein.setViewStyle(vs);
130 * Add a listener to tidy up when the frame is closed.
132 protected void addCloseFrameListener()
134 addInternalFrameListener(new InternalFrameAdapter()
137 public void internalFrameClosed(InternalFrameEvent evt)
139 if (getTopFrame() instanceof AlignFrame)
141 ((AlignFrame) getTopFrame())
142 .closeMenuItem_actionPerformed(true);
144 if (getBottomFrame() instanceof AlignFrame)
146 ((AlignFrame) getBottomFrame())
147 .closeMenuItem_actionPerformed(true);
154 * Add a key listener that delegates to whichever split component the mouse is
155 * in (or does nothing if neither).
157 protected void addKeyListener()
159 addKeyListener(new KeyAdapter() {
162 public void keyPressed(KeyEvent e)
164 AlignFrame af = (AlignFrame) getFrameAtMouse();
167 * Intercept and override any keys here if wanted.
169 if (!overrideKey(e, af))
173 for (KeyListener kl : af.getKeyListeners())
182 public void keyReleased(KeyEvent e)
184 Component c = getFrameAtMouse();
187 for (KeyListener kl : c.getKeyListeners())
198 * Returns true if the key event is overriden and actioned (or ignored) here,
199 * else returns false, indicating it should be delegated to the AlignFrame's
202 * We can't handle Cmd-Key combinations here, instead this is done by
203 * overriding key bindings.
205 * @see addKeyOverrides
210 protected boolean overrideKey(KeyEvent e, AlignFrame af)
212 boolean actioned = false;
213 int keyCode = e.getKeyCode();
216 case KeyEvent.VK_DOWN:
217 if (e.isAltDown() || !af.viewport.cursorMode)
220 * Key down (or Alt-key-down in cursor mode) - move selected sequences
222 ((AlignFrame) getTopFrame()).moveSelectedSequences(false);
223 ((AlignFrame) getBottomFrame()).moveSelectedSequences(false);
229 if (e.isAltDown() || !af.viewport.cursorMode)
232 * Key up (or Alt-key-up in cursor mode) - move selected sequences
234 ((AlignFrame) getTopFrame()).moveSelectedSequences(true);
235 ((AlignFrame) getBottomFrame()).moveSelectedSequences(true);
245 * Set key bindings (recommended for Swing over key accelerators).
247 private void addKeyBindings()
249 overrideDelegatedKeyBindings();
251 overrideImplementedKeyBindings();
255 * Override key bindings with alternative action methods implemented in this
258 protected void overrideImplementedKeyBindings()
262 overrideExpandViews();
263 overrideGatherViews();
267 * Replace Cmd-W close view action with our version.
269 protected void overrideCloseView()
271 AbstractAction action;
273 * Ctrl-W / Cmd-W - close view or window
275 KeyStroke key_cmdW = KeyStroke.getKeyStroke(KeyEvent.VK_W, Toolkit
276 .getDefaultToolkit().getMenuShortcutKeyMask(), false);
277 action = new AbstractAction()
280 public void actionPerformed(ActionEvent e)
282 closeView_actionPerformed();
285 overrideKeyBinding(key_cmdW, action);
289 * Replace Cmd-T new view action with our version.
291 protected void overrideNewView()
294 * Ctrl-T / Cmd-T open new view
296 KeyStroke key_cmdT = KeyStroke.getKeyStroke(KeyEvent.VK_T, Toolkit
297 .getDefaultToolkit().getMenuShortcutKeyMask(), false);
298 AbstractAction action = new AbstractAction()
301 public void actionPerformed(ActionEvent e)
303 newView_actionPerformed();
306 overrideKeyBinding(key_cmdT, action);
310 * For now, delegates key events to the corresponding key accelerator for the
311 * AlignFrame that the mouse is in. Hopefully can be simplified in future if
312 * AlignFrame is changed to use key bindings rather than accelerators.
314 protected void overrideDelegatedKeyBindings()
316 if (getTopFrame() instanceof AlignFrame)
319 * Get all accelerator keys in the top frame (the bottom should be
320 * identical) and override each one.
322 for (Entry<KeyStroke, JMenuItem> acc : ((AlignFrame) getTopFrame())
323 .getAccelerators().entrySet())
325 overrideKeyBinding(acc);
331 * Overrides an AlignFrame key accelerator with our version which delegates to
332 * the action listener in whichever frame has the mouse (and does nothing if
337 private void overrideKeyBinding(Entry<KeyStroke, JMenuItem> acc)
339 final KeyStroke ks = acc.getKey();
340 InputMap inputMap = this.getInputMap(JComponent.WHEN_FOCUSED);
341 inputMap.put(ks, ks);
342 this.getActionMap().put(ks, new AbstractAction()
345 public void actionPerformed(ActionEvent e)
347 Component c = getFrameAtMouse();
348 if (c != null && c instanceof AlignFrame)
350 for (ActionListener a : ((AlignFrame) c).getAccelerators()
351 .get(ks).getActionListeners())
353 a.actionPerformed(null);
361 * Replace an accelerator key's action with the specified action.
365 protected void overrideKeyBinding(KeyStroke ks, AbstractAction action)
367 this.getActionMap().put(ks, action);
368 overrideMenuItem(ks, action);
372 * Create and link new views (with matching names) in both panes.
374 * Note this is _not_ multiple tabs, each hosting a split pane view, rather it
375 * is a single split pane with each split holding multiple tabs which are
378 * TODO implement instead with a tabbed holder in the SplitView, each tab
379 * holding a single JSplitPane. Would avoid a duplicated tab, at the cost of
380 * some additional coding.
382 protected void newView_actionPerformed()
384 AlignFrame topFrame = (AlignFrame) getTopFrame();
385 AlignFrame bottomFrame = (AlignFrame) getBottomFrame();
387 AlignmentPanel newTopPanel = topFrame.newView(null, true);
388 AlignmentPanel newBottomPanel = bottomFrame.newView(null, true);
391 * This currently (for the first new view only) leaves the top pane on tab 0
392 * but the bottom on tab 1. This results from 'setInitialTabVisible' echoing
393 * from the bottom back to the first frame. Next line is a fudge to work
394 * around this. TODO find a better way.
396 if (topFrame.getTabIndex() != bottomFrame.getTabIndex())
398 topFrame.setDisplayedView(newTopPanel);
401 newBottomPanel.av.viewName = newTopPanel.av.viewName;
402 newTopPanel.av.setCodingComplement(newBottomPanel.av);
404 final StructureSelectionManager ssm = StructureSelectionManager
405 .getStructureSelectionManager(Desktop.instance);
406 ssm.addCommandListener(newTopPanel.av);
407 ssm.addCommandListener(newBottomPanel.av);
411 * Close the currently selected view in both panes. If there is only one view,
412 * close this split frame.
414 protected void closeView_actionPerformed()
416 int viewCount = ((AlignFrame) getTopFrame()).getAlignPanels().size();
423 AlignmentPanel topPanel = ((AlignFrame) getTopFrame()).alignPanel;
424 AlignmentPanel bottomPanel = ((AlignFrame) getBottomFrame()).alignPanel;
426 ((AlignFrame) getTopFrame()).closeView(topPanel);
427 ((AlignFrame) getBottomFrame()).closeView(bottomPanel);
432 * Close child frames and this split frame.
436 ((AlignFrame) getTopFrame()).closeMenuItem_actionPerformed(true);
437 ((AlignFrame) getBottomFrame()).closeMenuItem_actionPerformed(true);
440 this.setClosed(true);
441 } catch (PropertyVetoException e)
448 * Replace AlignFrame 'expand views' action with SplitFrame version.
450 protected void overrideExpandViews()
452 KeyStroke key_X = KeyStroke.getKeyStroke(KeyEvent.VK_X, 0, false);
453 AbstractAction action = new AbstractAction()
456 public void actionPerformed(ActionEvent e)
458 expandViews_actionPerformed();
461 overrideMenuItem(key_X, action);
465 * Replace AlignFrame 'gather views' action with SplitFrame version.
467 protected void overrideGatherViews()
469 KeyStroke key_G = KeyStroke.getKeyStroke(KeyEvent.VK_G, 0, false);
470 AbstractAction action = new AbstractAction()
473 public void actionPerformed(ActionEvent e)
475 gatherViews_actionPerformed();
478 overrideMenuItem(key_G, action);
482 * Override the menu action associated with the keystroke in the child frames,
483 * replacing it with the given action.
488 private void overrideMenuItem(KeyStroke ks, AbstractAction action)
490 overrideMenuItem(ks, action, getTopFrame());
491 overrideMenuItem(ks, action, getBottomFrame());
495 * Override the menu action associated with the keystroke in one child frame,
496 * replacing it with the given action. Mwahahahaha.
502 private void overrideMenuItem(KeyStroke key, final AbstractAction action,
505 if (comp instanceof AlignFrame)
507 JMenuItem mi = ((AlignFrame) comp).getAccelerators().get(key);
510 for (ActionListener al : mi.getActionListeners())
512 mi.removeActionListener(al);
514 mi.addActionListener(new ActionListener()
517 public void actionPerformed(ActionEvent e)
519 action.actionPerformed(e);
527 * Expand any multiple views (which are always in pairs) into separate split
530 protected void expandViews_actionPerformed()
532 Desktop.instance.explodeViews(this);
536 * Gather any other SplitFrame views of this alignment back in as multiple
537 * (pairs of) views in this SplitFrame.
539 protected void gatherViews_actionPerformed()
541 Desktop.instance.gatherViews(this);