3 import jalview.jbgui.GAlignFrame;
4 import jalview.jbgui.GSplitFrame;
5 import jalview.structure.StructureSelectionManager;
7 import java.awt.Component;
8 import java.awt.MouseInfo;
10 import java.awt.Rectangle;
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;
28 public class SplitFrame extends GSplitFrame
30 private static final long serialVersionUID = 1L;
32 public SplitFrame(GAlignFrame top, GAlignFrame bottom)
39 * Initialise this frame.
43 setSize(AlignFrame.DEFAULT_WIDTH, Desktop.instance.getHeight() - 20);
45 addCloseFrameListener();
53 * Add a listener to tidy up when the frame is closed.
55 protected void addCloseFrameListener()
57 addInternalFrameListener(new InternalFrameAdapter()
60 public void internalFrameClosed(InternalFrameEvent evt)
62 if (getTopFrame() instanceof AlignFrame)
64 ((AlignFrame) getTopFrame())
65 .closeMenuItem_actionPerformed(true);
67 if (getBottomFrame() instanceof AlignFrame)
69 ((AlignFrame) getBottomFrame())
70 .closeMenuItem_actionPerformed(true);
77 * Add a key listener that delegates to whichever split component the mouse is
78 * in (or does nothing if neither).
80 protected void addKeyListener()
82 addKeyListener(new KeyAdapter() {
85 public void keyPressed(KeyEvent e)
87 AlignFrame af = (AlignFrame) getFrameAtMouse();
90 * Intercept and override any keys here if wanted.
92 if (!overrideKey(e, af))
96 for (KeyListener kl : af.getKeyListeners())
105 public void keyReleased(KeyEvent e)
107 Component c = getFrameAtMouse();
110 for (KeyListener kl : c.getKeyListeners())
121 * Returns true if the key event is overriden and actioned (or ignored) here,
122 * else returns false, indicating it should be delegated to the AlignFrame's
125 * We can't handle Cmd-Key combinations here, instead this is done by
126 * overriding key bindings.
128 * @see addKeyOverrides
133 protected boolean overrideKey(KeyEvent e, AlignFrame af)
135 boolean actioned = false;
136 int keyCode = e.getKeyCode();
139 case KeyEvent.VK_DOWN:
140 if (e.isAltDown() || !af.viewport.cursorMode)
143 * Key down (or Alt-key-down in cursor mode) - move selected sequences
145 ((AlignFrame) getTopFrame()).moveSelectedSequences(false);
146 ((AlignFrame) getBottomFrame()).moveSelectedSequences(false);
152 if (e.isAltDown() || !af.viewport.cursorMode)
155 * Key up (or Alt-key-up in cursor mode) - move selected sequences
157 ((AlignFrame) getTopFrame()).moveSelectedSequences(true);
158 ((AlignFrame) getBottomFrame()).moveSelectedSequences(true);
168 * Returns the split pane component the mouse is in, or null if neither.
172 protected GAlignFrame getFrameAtMouse()
174 Point loc = MouseInfo.getPointerInfo().getLocation();
176 if (isIn(loc, getTopFrame()))
178 return getTopFrame();
180 else if (isIn(loc, getBottomFrame()))
182 return getBottomFrame();
187 private boolean isIn(Point loc, JComponent comp)
189 Point p = comp.getLocationOnScreen();
190 Rectangle r = new Rectangle(p.x, p.y, comp.getWidth(), comp.getHeight());
191 return r.contains(loc);
195 * Set key bindings (recommended for Swing over key accelerators).
197 private void addKeyBindings()
199 overrideDelegatedKeyBindings();
201 overrideImplementedKeyBindings();
205 * Override key bindings with alternative action methods implemented in this
208 protected void overrideImplementedKeyBindings()
212 overrideExpandViews();
213 overrideGatherViews();
217 * Replace Cmd-W close view action with our version.
219 protected void overrideCloseView()
221 AbstractAction action;
223 * Ctrl-W / Cmd-W - close view or window
225 KeyStroke key_cmdW = KeyStroke.getKeyStroke(KeyEvent.VK_W, Toolkit
226 .getDefaultToolkit().getMenuShortcutKeyMask(), false);
227 action = new AbstractAction()
230 public void actionPerformed(ActionEvent e)
232 closeView_actionPerformed();
235 overrideKeyBinding(key_cmdW, action);
239 * Replace Cmd-T new view action with our version.
241 protected void overrideNewView()
244 * Ctrl-T / Cmd-T open new view
246 KeyStroke key_cmdT = KeyStroke.getKeyStroke(KeyEvent.VK_T, Toolkit
247 .getDefaultToolkit().getMenuShortcutKeyMask(), false);
248 AbstractAction action = new AbstractAction()
251 public void actionPerformed(ActionEvent e)
253 newView_actionPerformed();
256 overrideKeyBinding(key_cmdT, action);
260 * For now, delegates key events to the corresponding key accelerator for the
261 * AlignFrame that the mouse is in. Hopefully can be simplified in future if
262 * AlignFrame is changed to use key bindings rather than accelerators.
264 protected void overrideDelegatedKeyBindings()
266 if (getTopFrame() instanceof AlignFrame)
269 * Get all accelerator keys in the top frame (the bottom should be
270 * identical) and override each one.
272 for (Entry<KeyStroke, JMenuItem> acc : ((AlignFrame) getTopFrame())
273 .getAccelerators().entrySet())
275 overrideKeyBinding(acc);
281 * Overrides an AlignFrame key accelerator with our version which delegates to
282 * the action listener in whichever frame has the mouse (and does nothing if
287 private void overrideKeyBinding(Entry<KeyStroke, JMenuItem> acc)
289 final KeyStroke ks = acc.getKey();
290 InputMap inputMap = this.getInputMap(JComponent.WHEN_FOCUSED);
291 inputMap.put(ks, ks);
292 this.getActionMap().put(ks, new AbstractAction()
295 public void actionPerformed(ActionEvent e)
297 Component c = getFrameAtMouse();
298 if (c != null && c instanceof AlignFrame)
300 for (ActionListener a : ((AlignFrame) c).getAccelerators()
301 .get(ks).getActionListeners())
303 a.actionPerformed(null);
311 * Replace an accelerator key's action with the specified action.
315 protected void overrideKeyBinding(KeyStroke ks, AbstractAction action)
317 this.getActionMap().put(ks, action);
318 overrideMenuItem(ks, action);
322 * Create and link new views (with matching names) in both panes.
324 * Note this is _not_ multiple tabs, each hosting a split pane view, rather it
325 * is a single split pane with each split holding multiple tabs which are
328 protected void newView_actionPerformed()
330 System.out.println("newView " + this.hashCode());
331 AlignFrame topFrame = (AlignFrame) getTopFrame();
332 AlignFrame bottomFrame = (AlignFrame) getBottomFrame();
334 AlignmentPanel newTopPanel = topFrame.newView(null, true);
335 AlignmentPanel newBottomPanel = bottomFrame.newView(null, true);
338 * This currently (for the first new view only) leaves the top pane on tab 0
339 * but the bottom on tab 1. This results from 'setInitialTabVisible' echoing
340 * from the bottom back to the first frame. Next line is a fudge to work
341 * around this. TODO find a better way.
343 if (topFrame.getTabIndex() != bottomFrame.getTabIndex())
345 topFrame.setDisplayedView(newTopPanel);
348 newBottomPanel.av.viewName = newTopPanel.av.viewName;
349 newTopPanel.av.setCodingComplement(newBottomPanel.av);
351 final StructureSelectionManager ssm = StructureSelectionManager
352 .getStructureSelectionManager(Desktop.instance);
353 ssm.addCommandListener(newTopPanel.av);
354 ssm.addCommandListener(newBottomPanel.av);
358 * Close the currently selected view in both panes. If there is only one view,
359 * close this split frame.
361 protected void closeView_actionPerformed()
363 int viewCount = ((AlignFrame) getTopFrame()).getAlignPanels().size();
370 AlignmentPanel topPanel = ((AlignFrame) getTopFrame()).alignPanel;
371 AlignmentPanel bottomPanel = ((AlignFrame) getBottomFrame()).alignPanel;
373 ((AlignFrame) getTopFrame()).closeView(topPanel);
374 ((AlignFrame) getBottomFrame()).closeView(bottomPanel);
379 * Close child frames and this split frame.
383 ((AlignFrame) getTopFrame()).closeMenuItem_actionPerformed(true);
384 ((AlignFrame) getBottomFrame()).closeMenuItem_actionPerformed(true);
387 this.setClosed(true);
388 } catch (PropertyVetoException e)
395 * Replace AlignFrame 'expand views' action with SplitFrame version.
397 protected void overrideExpandViews()
399 KeyStroke key_X = KeyStroke.getKeyStroke(KeyEvent.VK_X, 0, false);
400 AbstractAction action = new AbstractAction()
403 public void actionPerformed(ActionEvent e)
405 expandViews_actionPerformed();
408 overrideMenuItem(key_X, action);
412 * Replace AlignFrame 'gather views' action with SplitFrame version.
414 protected void overrideGatherViews()
416 KeyStroke key_G = KeyStroke.getKeyStroke(KeyEvent.VK_G, 0, false);
417 AbstractAction action = new AbstractAction()
420 public void actionPerformed(ActionEvent e)
422 gatherViews_actionPerformed();
425 overrideMenuItem(key_G, action);
429 * Override the menu action associated with the keystroke in the child frames,
430 * replacing it with the given action.
435 private void overrideMenuItem(KeyStroke ks, AbstractAction action)
437 overrideMenuItem(ks, action, getTopFrame());
438 overrideMenuItem(ks, action, getBottomFrame());
442 * Override the menu action associated with the keystroke in one child frame,
443 * replacing it with the given action. Mwahahahaha.
449 private void overrideMenuItem(KeyStroke key, final AbstractAction action,
452 if (comp instanceof AlignFrame)
454 JMenuItem mi = ((AlignFrame) comp).getAccelerators().get(key);
457 for (ActionListener al : mi.getActionListeners())
459 mi.removeActionListener(al);
461 mi.addActionListener(new ActionListener()
464 public void actionPerformed(ActionEvent e)
466 action.actionPerformed(e);
474 * Expand any multiple views (which are always in pairs) into separate split
477 protected void expandViews_actionPerformed()
479 Desktop.instance.explodeViews(this);
483 * Gather any other SplitFrame views of this alignment back in as multiple
484 * (pairs of) views in this SplitFrame.
486 protected void gatherViews_actionPerformed()
488 Desktop.instance.gatherViews(this);