Skip to content
Snippets Groups Projects
Commit 14832e2d authored by Jean-Yves Tinevez's avatar Jean-Yves Tinevez
Browse files

Externalize keystrokes handling


Keystroks and bound actions are handled in a separate
class, that can load a key mapping from a file. The goal
is to have keybindings that can be toggled on/off depending
on the mode we are in.

Signed-off-by: default avatarJean-Yves Tinevez <jeanyves.tinevez@gmail.com>
parent caa8f5e4
No related branches found
No related tags found
No related merge requests found
package viewer;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.Action;
import viewer.SpimViewer.AlignPlane;
public class NavigationActions
{
private NavigationActions()
{}
public static final Action getToggleManualTransformationAction( final SpimViewer viewer )
{
return new AbstractAction( "toggle manual transformation" )
{
@Override
public void actionPerformed( final ActionEvent e )
{
viewer.toggleManualTransformation();
}
private static final long serialVersionUID = 1L;
};
}
public static final Action getToggleInterpolationAction( final SpimViewer viewer )
{
return new AbstractAction( "toggle interpolation" )
{
@Override
public void actionPerformed( final ActionEvent e )
{
viewer.toggleInterpolation();
}
private static final long serialVersionUID = 1L;
};
}
public static final Action getToggleFusedModeAction( final SpimViewer viewer )
{
return new AbstractAction( "toggle fused mode" )
{
@Override
public void actionPerformed( final ActionEvent e )
{
viewer.visibilityAndGrouping.setFusedEnabled( !viewer.visibilityAndGrouping.isFusedEnabled() );
}
private static final long serialVersionUID = 1L;
};
}
public static final Action getToggleGroupingAction( final SpimViewer viewer )
{
return new AbstractAction( "toggle grouping" )
{
@Override
public void actionPerformed( final ActionEvent e )
{
viewer.visibilityAndGrouping.setGroupingEnabled( !viewer.visibilityAndGrouping.isGroupingEnabled() );
}
private static final long serialVersionUID = 1L;
};
}
public static final Action getSetCurrentSource( final SpimViewer viewer, final int sourceIndex )
{
return new AbstractAction( "set current source " + sourceIndex )
{
@Override
public void actionPerformed( final ActionEvent e )
{
viewer.setCurrentGroupOrSource( sourceIndex );
}
private static final long serialVersionUID = 1L;
};
}
public static final Action getToggleSourceVisibilityAction( final SpimViewer viewer, final int sourceIndex )
{
return new AbstractAction( "toggle source visibility " + sourceIndex )
{
@Override
public void actionPerformed( final ActionEvent e )
{
viewer.toggleActiveGroupOrSource( sourceIndex );
}
private static final long serialVersionUID = 1L;
};
}
public static final Action getAlignPlaneAction( final SpimViewer viewer, final AlignPlane plane )
{
return new AbstractAction( "align XY plane" )
{
@Override
public void actionPerformed( final ActionEvent e )
{
viewer.align( plane );
}
private static final long serialVersionUID = 1L;
};
}
public static final Action getNextTimePointAction( final SpimViewer viewer )
{
return new AbstractAction( "next timepoint" )
{
@Override
public void actionPerformed( final ActionEvent e )
{
viewer.sliderTime.setValue( viewer.sliderTime.getValue() + 1 );
}
private static final long serialVersionUID = 1L;
};
}
public static Action getPreviousTimePointAction( final SpimViewer viewer )
{
return new AbstractAction( "next timepoint" )
{
@Override
public void actionPerformed( final ActionEvent e )
{
viewer.sliderTime.setValue( viewer.sliderTime.getValue() - 1 );
}
private static final long serialVersionUID = 1L;
};
}
}
package viewer;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JComponent;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import viewer.SpimViewer.AlignPlane;
public class NavigationKeyHandler
{
private static final Properties DEFAULT_KEYBINGS = new Properties();
static
{
DEFAULT_KEYBINGS.setProperty( "T", "toggle manual transformation" );
DEFAULT_KEYBINGS.setProperty( "I", "toggle interpolation" );
DEFAULT_KEYBINGS.setProperty( "F", "toggle fused mode" );
DEFAULT_KEYBINGS.setProperty( "G", "toggle grouping" );
final String[] numkeys = new String[] { "1", "2", "3", "4", "5", "6", "7", "8", "9", "0" };
for ( int i = 0; i < numkeys.length; ++i )
{
DEFAULT_KEYBINGS.setProperty( numkeys[ i ], "set current source " + i );
DEFAULT_KEYBINGS.setProperty( "shift " + numkeys[ i ], "toggle source visibility " + i );
}
DEFAULT_KEYBINGS.setProperty( "shift Z", "align XY plane" );
DEFAULT_KEYBINGS.setProperty( "shift X", "align ZY plane" );
DEFAULT_KEYBINGS.setProperty( "shift Y", "align XZ plane" );
DEFAULT_KEYBINGS.setProperty( "shift A", "align XZ plane" );
DEFAULT_KEYBINGS.setProperty( "CLOSE_BRACKET", "next timepoint" );
DEFAULT_KEYBINGS.setProperty( "M", "next timepoint" );
DEFAULT_KEYBINGS.setProperty( "OPEN_BRACKET", "previous timepoint" );
DEFAULT_KEYBINGS.setProperty( "N", "previous timepoint" );
}
private final SpimViewer viewer;
public NavigationKeyHandler( final SpimViewer viewer )
{
this.viewer = viewer;
installKeyboardActions( viewer.frame.getRootPane() );
}
/**
* Unused, yet.
*
* @return
*/
protected InputMap readPropertyFile()
{
Properties config;
final File file = new File( "spimviewer.properties" );
try
{
final InputStream stream = new FileInputStream( file );
config = new Properties();
config.load( stream );
}
catch ( final IOException e )
{
System.out.println( "Cannot find the config file :" + file + ". Using default key bindings." );
System.out.println( e.getMessage() );
config = DEFAULT_KEYBINGS;
}
return generateMapFrom( config );
}
private InputMap generateMapFrom( final Properties config )
{
final InputMap map = new InputMap();
for ( final Object obj : config.keySet() )
{
final String key = ( String ) obj;
final String command = config.getProperty( key );
map.put( KeyStroke.getKeyStroke( key ), command );
}
return map;
}
protected void installKeyboardActions( final JComponent component )
{
final InputMap inputMap = readPropertyFile();
SwingUtilities.replaceUIActionMap( component, createActionMap() );
SwingUtilities.replaceUIInputMap( component, JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, inputMap );
// SwingUtilities.replaceUIInputMap( component, JComponent.WHEN_FOCUSED,
// inputMap );
}
protected ActionMap createActionMap()
{
final ActionMap map = new ActionMap();
map.put( "toggle manual transformation", NavigationActions.getToggleManualTransformationAction( viewer ) );
map.put( "toggle interpolation", NavigationActions.getToggleInterpolationAction( viewer ) );
map.put( "toggle fused mode", NavigationActions.getToggleFusedModeAction( viewer ) );
map.put( "toggle grouping", NavigationActions.getToggleGroupingAction( viewer ) );
for ( int i = 0; i < 10; ++i )
{
map.put( "set current source " + i, NavigationActions.getSetCurrentSource( viewer, i ) );
map.put( "toggle source visibility " + i, NavigationActions.getToggleSourceVisibilityAction( viewer, i ) );
}
map.put( "align XY plane", NavigationActions.getAlignPlaneAction( viewer, AlignPlane.XY ) );
map.put( "align ZY plane", NavigationActions.getAlignPlaneAction( viewer, AlignPlane.ZY ) );
map.put( "align XZ plane", NavigationActions.getAlignPlaneAction( viewer, AlignPlane.XZ ) );
map.put( "next timepoint", NavigationActions.getNextTimePointAction( viewer ) );
map.put( "previous timepoint", NavigationActions.getPreviousTimePointAction( viewer ) );
return map;
}
}
......@@ -13,8 +13,6 @@ import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
......@@ -24,7 +22,6 @@ import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ActionMap;
import javax.swing.InputMap;
......@@ -99,8 +96,6 @@ public class SpimViewer implements OverlayRenderer, TransformListener< AffineTra
final protected MouseCoordinateListener mouseCoordinates;
final protected ArrayList< Pair< KeyStroke, Action > > keysActions;
protected AbstractTransformAnimator currentAnimator = null;
final protected VisibilityAndGrouping visibilityAndGrouping;
......@@ -114,8 +109,12 @@ public class SpimViewer implements OverlayRenderer, TransformListener< AffineTra
*/
final protected CopyOnWriteArrayList< TransformListener< AffineTransform3D > > transformListeners;
private final NavigationKeyHandler navigationKeyHandler;
final protected ArrayList< Pair< KeyStroke, Action > > keysActions;
/**
*
*
* @param width
* width of the display window.
* @param height
......@@ -162,7 +161,9 @@ public class SpimViewer implements OverlayRenderer, TransformListener< AffineTra
state = new ViewerState( transformedSources, groups, numTimePoints );
if ( !sources.isEmpty() )
{
state.setCurrentSource( 0 );
}
multiBoxOverlayRenderer = new MultiBoxOverlayRenderer( width, height );
sourceInfoOverlayRenderer = new SourceInfoOverlayRenderer();
......@@ -198,7 +199,9 @@ public class SpimViewer implements OverlayRenderer, TransformListener< AffineTra
public void stateChanged( final ChangeEvent e )
{
if ( e.getSource().equals( sliderTime ) )
{
updateTimepoint( sliderTime.getValue() );
}
}
} );
......@@ -207,7 +210,8 @@ public class SpimViewer implements OverlayRenderer, TransformListener< AffineTra
transformListeners = new CopyOnWriteArrayList< TransformListener< AffineTransform3D > >();
// final GraphicsConfiguration gc = GuiUtil.getSuitableGraphicsConfiguration( GuiUtil.ARGB_COLOR_MODEL );
// final GraphicsConfiguration gc =
// GuiUtil.getSuitableGraphicsConfiguration( GuiUtil.ARGB_COLOR_MODEL );
final GraphicsConfiguration gc = GuiUtil.getSuitableGraphicsConfiguration( GuiUtil.RGB_COLOR_MODEL );
frame = new JFrame( "multi-angle viewer", gc );
frame.getRootPane().setDoubleBuffered( true );
......@@ -227,9 +231,11 @@ public class SpimViewer implements OverlayRenderer, TransformListener< AffineTra
frame.setVisible( true );
renderTarget.setCanvasSize( display.getWidth(), display.getHeight() );
// Hnadle navigation keys
navigationKeyHandler = new NavigationKeyHandler( this );
// Old way to handle key presses. We leave it here to add extra key
// bindings in a simple way
keysActions = new ArrayList< Pair< KeyStroke, Action > >();
createKeyActions();
installKeyActions( frame );
painterThread.start();
......@@ -240,7 +246,9 @@ public class SpimViewer implements OverlayRenderer, TransformListener< AffineTra
{
display.addHandler( handler );
if ( KeyListener.class.isInstance( handler ) )
{
frame.addKeyListener( ( KeyListener ) handler );
}
}
public void getGlobalMouseCoordinates( final RealPositionable gPos )
......@@ -251,6 +259,30 @@ public class SpimViewer implements OverlayRenderer, TransformListener< AffineTra
viewerTransform.applyInverse( gPos, lPos );
}
public void addKeyAction( final KeyStroke keystroke, final Action action )
{
keysActions.add( new ValuePair< KeyStroke, Action >( keystroke, action ) );
installKeyActions( frame );
}
/**
* Add Keystrokes and corresponding Actions from {@link #keysActions} to a
* container.
*/
public void installKeyActions( final RootPaneContainer container )
{
final JRootPane rootpane = container.getRootPane();
final ActionMap am = rootpane.getActionMap();
final InputMap im = rootpane.getInputMap( JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT );
for ( final Pair< KeyStroke, Action > keyAction : keysActions )
{
final KeyStroke key = keyAction.getA();
final Action action = keyAction.getB();
im.put( key, action.getValue( Action.NAME ) );
am.put( action.getValue( Action.NAME ), action );
}
}
@Override
public void paint()
{
......@@ -265,7 +297,9 @@ public class SpimViewer implements OverlayRenderer, TransformListener< AffineTra
handler.setTransform( transform );
transformChanged( transform );
if ( currentAnimator.isComplete() )
{
currentAnimator = null;
}
}
}
......@@ -300,12 +334,18 @@ public class SpimViewer implements OverlayRenderer, TransformListener< AffineTra
{
animatedOverlay.paint( ( Graphics2D ) g, System.currentTimeMillis() );
if ( animatedOverlay.isComplete() )
{
animatedOverlay = null;
}
else
{
display.repaint();
}
}
if ( multiBoxOverlayRenderer.isHighlightInProgress() )
{
display.repaint();
}
}
@Override
......@@ -314,7 +354,9 @@ public class SpimViewer implements OverlayRenderer, TransformListener< AffineTra
viewerTransform.set( transform );
state.setViewerTransform( transform );
for ( final TransformListener< AffineTransform3D > l : transformListeners )
{
l.transformChanged( viewerTransform );
}
requestRepaint();
}
......@@ -335,10 +377,12 @@ public class SpimViewer implements OverlayRenderer, TransformListener< AffineTra
display.repaint();
break;
case SOURCE_ACTVITY_CHANGED:
// TODO multiBoxOverlayRenderer.highlight() all sources that became visible
// TODO multiBoxOverlayRenderer.highlight() all sources that became
// visible
break;
case GROUP_ACTIVITY_CHANGED:
// TODO multiBoxOverlayRenderer.highlight() all sources that became visible
// TODO multiBoxOverlayRenderer.highlight() all sources that became
// visible
break;
case VISIBILITY_CHANGED:
requestRepaint();
......@@ -381,7 +425,8 @@ public class SpimViewer implements OverlayRenderer, TransformListener< AffineTra
Affine3DHelpers.extractApproximateRotationAffine( sourceTransform, qSource, 0 );
LinAlgHelpers.quaternionMultiply( qSource, qAlignZY, qTmpSource );
}
else // if ( plane == AlignPlane.XZ )
else
// if ( plane == AlignPlane.XZ )
{
Affine3DHelpers.extractApproximateRotationAffine( sourceTransform, qSource, 1 );
LinAlgHelpers.quaternionMultiply( qSource, qAlignXZ, qTmpSource );
......@@ -433,17 +478,25 @@ public class SpimViewer implements OverlayRenderer, TransformListener< AffineTra
protected void setCurrentGroupOrSource( final int index )
{
if ( visibilityAndGrouping.isGroupingEnabled() )
{
visibilityAndGrouping.setCurrentGroup( index );
}
else
{
visibilityAndGrouping.setCurrentSource( index );
}
}
protected void toggleActiveGroupOrSource( final int index )
{
if ( visibilityAndGrouping.isGroupingEnabled() )
{
visibilityAndGrouping.setGroupActive( index, !visibilityAndGrouping.isGroupActive( index ) );
}
else
{
visibilityAndGrouping.setSourceActive( index, !visibilityAndGrouping.isSourceActive( index ) );
}
}
/**
......@@ -473,7 +526,7 @@ public class SpimViewer implements OverlayRenderer, TransformListener< AffineTra
/**
* Get a copy of the current {@link ViewerState}.
*
*
* @return a copy of the current {@link ViewerState}.
*/
public synchronized ViewerState getState()
......@@ -483,7 +536,7 @@ public class SpimViewer implements OverlayRenderer, TransformListener< AffineTra
/**
* Get the viewer canvas.
*
*
* @return the viewer canvas.
*/
public InteractiveDisplayCanvasComponent< AffineTransform3D > getDisplay()
......@@ -495,7 +548,7 @@ public class SpimViewer implements OverlayRenderer, TransformListener< AffineTra
* Add a {@link TransformListener} to notify about viewer transformation
* changes. Listeners will be notified <em>before</em> calling
* {@link #requestRepaint()} so they have the chance to interfere.
*
*
* @param listener
* the transform listener to add.
*/
......@@ -508,7 +561,7 @@ public class SpimViewer implements OverlayRenderer, TransformListener< AffineTra
* Add a {@link TransformListener} to notify about viewer transformation
* changes. Listeners will be notified <em>before</em> calling
* {@link #requestRepaint()} so they have the chance to interfere.
*
*
* @param listener
* the transform listener to add.
* @param index
......@@ -525,7 +578,7 @@ public class SpimViewer implements OverlayRenderer, TransformListener< AffineTra
/**
* Remove a {@link TransformListener}.
*
*
* @param listener
* the transform listener to remove.
*/
......@@ -537,194 +590,6 @@ public class SpimViewer implements OverlayRenderer, TransformListener< AffineTra
}
}
/**
* Create Keystrokes and corresponding Actions.
*
* @return list of KeyStroke-Action-pairs.
*/
protected void createKeyActions()
{
KeyStroke key = KeyStroke.getKeyStroke( KeyEvent.VK_T, 0 );
Action action = new AbstractAction( "toggle manual transformation" )
{
@Override
public void actionPerformed( final ActionEvent e )
{
toggleManualTransformation();
}
private static final long serialVersionUID = 1L;
};
keysActions.add( new ValuePair< KeyStroke, Action >( key, action ) );
key = KeyStroke.getKeyStroke( KeyEvent.VK_I, 0 );
action = new AbstractAction( "toggle interpolation" )
{
@Override
public void actionPerformed( final ActionEvent e )
{
toggleInterpolation();
}
private static final long serialVersionUID = 1L;
};
keysActions.add( new ValuePair< KeyStroke, Action >( key, action ) );
key = KeyStroke.getKeyStroke( KeyEvent.VK_F, 0 );
action = new AbstractAction( "toggle fused mode" )
{
@Override
public void actionPerformed( final ActionEvent e )
{
visibilityAndGrouping.setFusedEnabled( !visibilityAndGrouping.isFusedEnabled() );
}
private static final long serialVersionUID = 1L;
};
keysActions.add( new ValuePair< KeyStroke, Action >( key, action ) );
key = KeyStroke.getKeyStroke( KeyEvent.VK_G, 0 );
action = new AbstractAction( "toggle grouping" )
{
@Override
public void actionPerformed( final ActionEvent e )
{
visibilityAndGrouping.setGroupingEnabled( !visibilityAndGrouping.isGroupingEnabled() );
}
private static final long serialVersionUID = 1L;
};
keysActions.add( new ValuePair< KeyStroke, Action >( key, action ) );
final int[] numkeys = new int[] { KeyEvent.VK_1, KeyEvent.VK_2, KeyEvent.VK_3, KeyEvent.VK_4, KeyEvent.VK_5, KeyEvent.VK_6, KeyEvent.VK_7, KeyEvent.VK_8, KeyEvent.VK_9, KeyEvent.VK_0 };
for ( int i = 0; i < numkeys.length; ++i )
{
final int index = i;
key = KeyStroke.getKeyStroke( numkeys[ i ], 0 );
action = new AbstractAction( "set current source " + i )
{
@Override
public void actionPerformed( final ActionEvent e )
{
setCurrentGroupOrSource( index );
}
private static final long serialVersionUID = 1L;
};
keysActions.add( new ValuePair< KeyStroke, Action >( key, action ) );
key = KeyStroke.getKeyStroke( numkeys[ i ], KeyEvent.SHIFT_DOWN_MASK );
action = new AbstractAction( "toggle source visibility " + i )
{
@Override
public void actionPerformed( final ActionEvent e )
{
toggleActiveGroupOrSource( index );
}
private static final long serialVersionUID = 1L;
};
keysActions.add( new ValuePair< KeyStroke, Action >( key, action ) );
}
key = KeyStroke.getKeyStroke( KeyEvent.VK_Z, KeyEvent.SHIFT_DOWN_MASK );
action = new AbstractAction( "align XY plane" )
{
@Override
public void actionPerformed( final ActionEvent e )
{
align( AlignPlane.XY );
}
private static final long serialVersionUID = 1L;
};
keysActions.add( new ValuePair< KeyStroke, Action >( key, action ) );
key = KeyStroke.getKeyStroke( KeyEvent.VK_X, KeyEvent.SHIFT_DOWN_MASK );
action = new AbstractAction( "align ZY plane" )
{
@Override
public void actionPerformed( final ActionEvent e )
{
align( AlignPlane.ZY );
}
private static final long serialVersionUID = 1L;
};
keysActions.add( new ValuePair< KeyStroke, Action >( key, action ) );
key = KeyStroke.getKeyStroke( KeyEvent.VK_Y, KeyEvent.SHIFT_DOWN_MASK );
action = new AbstractAction( "align XZ plane" )
{
@Override
public void actionPerformed( final ActionEvent e )
{
align( AlignPlane.XZ );
}
private static final long serialVersionUID = 1L;
};
keysActions.add( new ValuePair< KeyStroke, Action >( key, action ) );
key = KeyStroke.getKeyStroke( KeyEvent.VK_A, KeyEvent.SHIFT_DOWN_MASK );
keysActions.add( new ValuePair< KeyStroke, Action >( key, action ) );
key = KeyStroke.getKeyStroke( KeyEvent.VK_CLOSE_BRACKET, 0, false );
action = new AbstractAction( "next timepoint" )
{
@Override
public void actionPerformed( final ActionEvent e )
{
sliderTime.setValue( sliderTime.getValue() + 1 );
}
private static final long serialVersionUID = 1L;
};
keysActions.add( new ValuePair< KeyStroke, Action >( key, action ) );
key = KeyStroke.getKeyStroke( KeyEvent.VK_M, 0 );
keysActions.add( new ValuePair< KeyStroke, Action >( key, action ) );
key = KeyStroke.getKeyStroke( KeyEvent.VK_OPEN_BRACKET, 0, false );
action = new AbstractAction( "previous timepoint" )
{
@Override
public void actionPerformed( final ActionEvent e )
{
sliderTime.setValue( sliderTime.getValue() - 1 );
}
private static final long serialVersionUID = 1L;
};
keysActions.add( new ValuePair< KeyStroke, Action >( key, action ) );
key = KeyStroke.getKeyStroke( KeyEvent.VK_N, 0 );
keysActions.add( new ValuePair< KeyStroke, Action >( key, action ) );
}
public void addKeyAction( final KeyStroke keystroke, final Action action )
{
keysActions.add( new ValuePair< KeyStroke, Action >( keystroke, action ) );
installKeyActions( frame );
}
/**
* Add Keystrokes and corresponding Actions from {@link #keysActions} to a
* container.
*/
public void installKeyActions( final RootPaneContainer container )
{
final JRootPane rootpane = container.getRootPane();
final ActionMap am = rootpane.getActionMap();
final InputMap im = rootpane.getInputMap( JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT );
for ( final Pair< KeyStroke, Action > keyAction : keysActions )
{
final KeyStroke key = keyAction.getA();
final Action action = keyAction.getB();
im.put( key, action.getValue( Action.NAME ) );
am.put( action.getValue( Action.NAME ), action );
}
}
protected class MouseCoordinateListener implements MouseMotionListener
{
private int x;
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment