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;
25 public class SplitFrame extends GSplitFrame
27 private static final long serialVersionUID = 1L;
29 public SplitFrame(GAlignFrame top, GAlignFrame bottom)
36 * Initialise this frame.
40 setSize(AlignFrame.DEFAULT_WIDTH, Desktop.instance.getHeight() - 20);
42 addCloseFrameListener();
50 * Add a listener to tidy up when the frame is closed.
52 protected void addCloseFrameListener()
54 addInternalFrameListener(new InternalFrameAdapter()
57 public void internalFrameClosed(InternalFrameEvent evt)
59 if (getTopFrame() instanceof AlignFrame)
61 ((AlignFrame) getTopFrame())
62 .closeMenuItem_actionPerformed(true);
64 if (getBottomFrame() instanceof AlignFrame)
66 ((AlignFrame) getBottomFrame())
67 .closeMenuItem_actionPerformed(true);
74 * Add a key listener that delegates to whichever split component the mouse is
75 * in (or does nothing if neither).
77 protected void addKeyListener()
79 addKeyListener(new KeyAdapter() {
82 public void keyPressed(KeyEvent e)
84 AlignFrame af = (AlignFrame) getFrameAtMouse();
87 * Intercept and override any keys here if wanted.
89 if (!overrideKey(e, af))
93 for (KeyListener kl : af.getKeyListeners())
102 public void keyReleased(KeyEvent e)
104 Component c = getFrameAtMouse();
107 for (KeyListener kl : c.getKeyListeners())
118 * Returns true if the key event is overriden and actioned (or ignored) here,
119 * else returns false, indicating it should be delegated to the AlignFrame's
122 * We can't handle Cmd-Key combinations here, instead this is done by
123 * overriding key bindings.
125 * @see addKeyOverrides
130 protected boolean overrideKey(KeyEvent e, AlignFrame af)
132 boolean actioned = false;
133 int keyCode = e.getKeyCode();
136 case KeyEvent.VK_DOWN:
137 if (e.isAltDown() || !af.viewport.cursorMode)
140 * Key down (or Alt-key-down in cursor mode) - move selected sequences
142 ((AlignFrame) getTopFrame()).moveSelectedSequences(false);
143 ((AlignFrame) getBottomFrame()).moveSelectedSequences(false);
149 if (e.isAltDown() || !af.viewport.cursorMode)
152 * Key up (or Alt-key-up in cursor mode) - move selected sequences
154 ((AlignFrame) getTopFrame()).moveSelectedSequences(true);
155 ((AlignFrame) getBottomFrame()).moveSelectedSequences(true);
165 * Set key bindings (recommended for Swing over key accelerators).
167 private void addKeyBindings()
169 overrideDelegatedKeyBindings();
171 overrideImplementedKeyBindings();
175 * Override key bindings with alternative action methods implemented in this
178 protected void overrideImplementedKeyBindings()
182 overrideExpandViews();
183 overrideGatherViews();
187 * Replace Cmd-W close view action with our version.
189 protected void overrideCloseView()
191 AbstractAction action;
193 * Ctrl-W / Cmd-W - close view or window
195 KeyStroke key_cmdW = KeyStroke.getKeyStroke(KeyEvent.VK_W, Toolkit
196 .getDefaultToolkit().getMenuShortcutKeyMask(), false);
197 action = new AbstractAction()
200 public void actionPerformed(ActionEvent e)
202 closeView_actionPerformed();
205 overrideKeyBinding(key_cmdW, action);
209 * Replace Cmd-T new view action with our version.
211 protected void overrideNewView()
214 * Ctrl-T / Cmd-T open new view
216 KeyStroke key_cmdT = KeyStroke.getKeyStroke(KeyEvent.VK_T, Toolkit
217 .getDefaultToolkit().getMenuShortcutKeyMask(), false);
218 AbstractAction action = new AbstractAction()
221 public void actionPerformed(ActionEvent e)
223 newView_actionPerformed();
226 overrideKeyBinding(key_cmdT, action);
230 * For now, delegates key events to the corresponding key accelerator for the
231 * AlignFrame that the mouse is in. Hopefully can be simplified in future if
232 * AlignFrame is changed to use key bindings rather than accelerators.
234 protected void overrideDelegatedKeyBindings()
236 if (getTopFrame() instanceof AlignFrame)
239 * Get all accelerator keys in the top frame (the bottom should be
240 * identical) and override each one.
242 for (Entry<KeyStroke, JMenuItem> acc : ((AlignFrame) getTopFrame())
243 .getAccelerators().entrySet())
245 overrideKeyBinding(acc);
251 * Overrides an AlignFrame key accelerator with our version which delegates to
252 * the action listener in whichever frame has the mouse (and does nothing if
257 private void overrideKeyBinding(Entry<KeyStroke, JMenuItem> acc)
259 final KeyStroke ks = acc.getKey();
260 InputMap inputMap = this.getInputMap(JComponent.WHEN_FOCUSED);
261 inputMap.put(ks, ks);
262 this.getActionMap().put(ks, new AbstractAction()
265 public void actionPerformed(ActionEvent e)
267 Component c = getFrameAtMouse();
268 if (c != null && c instanceof AlignFrame)
270 for (ActionListener a : ((AlignFrame) c).getAccelerators()
271 .get(ks).getActionListeners())
273 a.actionPerformed(null);
281 * Replace an accelerator key's action with the specified action.
285 protected void overrideKeyBinding(KeyStroke ks, AbstractAction action)
287 this.getActionMap().put(ks, action);
288 overrideMenuItem(ks, action);
292 * Create and link new views (with matching names) in both panes.
294 * Note this is _not_ multiple tabs, each hosting a split pane view, rather it
295 * is a single split pane with each split holding multiple tabs which are
298 * TODO implement instead with a tabbed holder in the SplitView, each tab
299 * holding a single JSplitPane. Would avoid a duplicated tab, at the cost of
300 * some additional coding.
302 protected void newView_actionPerformed()
304 AlignFrame topFrame = (AlignFrame) getTopFrame();
305 AlignFrame bottomFrame = (AlignFrame) getBottomFrame();
307 AlignmentPanel newTopPanel = topFrame.newView(null, true);
308 AlignmentPanel newBottomPanel = bottomFrame.newView(null, true);
311 * This currently (for the first new view only) leaves the top pane on tab 0
312 * but the bottom on tab 1. This results from 'setInitialTabVisible' echoing
313 * from the bottom back to the first frame. Next line is a fudge to work
314 * around this. TODO find a better way.
316 if (topFrame.getTabIndex() != bottomFrame.getTabIndex())
318 topFrame.setDisplayedView(newTopPanel);
321 newBottomPanel.av.viewName = newTopPanel.av.viewName;
322 newTopPanel.av.setCodingComplement(newBottomPanel.av);
324 final StructureSelectionManager ssm = StructureSelectionManager
325 .getStructureSelectionManager(Desktop.instance);
326 ssm.addCommandListener(newTopPanel.av);
327 ssm.addCommandListener(newBottomPanel.av);
331 * Close the currently selected view in both panes. If there is only one view,
332 * close this split frame.
334 protected void closeView_actionPerformed()
336 int viewCount = ((AlignFrame) getTopFrame()).getAlignPanels().size();
343 AlignmentPanel topPanel = ((AlignFrame) getTopFrame()).alignPanel;
344 AlignmentPanel bottomPanel = ((AlignFrame) getBottomFrame()).alignPanel;
346 ((AlignFrame) getTopFrame()).closeView(topPanel);
347 ((AlignFrame) getBottomFrame()).closeView(bottomPanel);
352 * Close child frames and this split frame.
356 ((AlignFrame) getTopFrame()).closeMenuItem_actionPerformed(true);
357 ((AlignFrame) getBottomFrame()).closeMenuItem_actionPerformed(true);
360 this.setClosed(true);
361 } catch (PropertyVetoException e)
368 * Replace AlignFrame 'expand views' action with SplitFrame version.
370 protected void overrideExpandViews()
372 KeyStroke key_X = KeyStroke.getKeyStroke(KeyEvent.VK_X, 0, false);
373 AbstractAction action = new AbstractAction()
376 public void actionPerformed(ActionEvent e)
378 expandViews_actionPerformed();
381 overrideMenuItem(key_X, action);
385 * Replace AlignFrame 'gather views' action with SplitFrame version.
387 protected void overrideGatherViews()
389 KeyStroke key_G = KeyStroke.getKeyStroke(KeyEvent.VK_G, 0, false);
390 AbstractAction action = new AbstractAction()
393 public void actionPerformed(ActionEvent e)
395 gatherViews_actionPerformed();
398 overrideMenuItem(key_G, action);
402 * Override the menu action associated with the keystroke in the child frames,
403 * replacing it with the given action.
408 private void overrideMenuItem(KeyStroke ks, AbstractAction action)
410 overrideMenuItem(ks, action, getTopFrame());
411 overrideMenuItem(ks, action, getBottomFrame());
415 * Override the menu action associated with the keystroke in one child frame,
416 * replacing it with the given action. Mwahahahaha.
422 private void overrideMenuItem(KeyStroke key, final AbstractAction action,
425 if (comp instanceof AlignFrame)
427 JMenuItem mi = ((AlignFrame) comp).getAccelerators().get(key);
430 for (ActionListener al : mi.getActionListeners())
432 mi.removeActionListener(al);
434 mi.addActionListener(new ActionListener()
437 public void actionPerformed(ActionEvent e)
439 action.actionPerformed(e);
447 * Expand any multiple views (which are always in pairs) into separate split
450 protected void expandViews_actionPerformed()
452 Desktop.instance.explodeViews(this);
456 * Gather any other SplitFrame views of this alignment back in as multiple
457 * (pairs of) views in this SplitFrame.
459 protected void gatherViews_actionPerformed()
461 Desktop.instance.gatherViews(this);