diff --git a/src/main/java/viewer/NavigationActions.java b/src/main/java/viewer/NavigationActions.java new file mode 100755 index 0000000000000000000000000000000000000000..788221c9accb7fa176eab1d4a7c7c9d03cfb1139 --- /dev/null +++ b/src/main/java/viewer/NavigationActions.java @@ -0,0 +1,142 @@ +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; + }; + } +} diff --git a/src/main/java/viewer/NavigationKeyHandler.java b/src/main/java/viewer/NavigationKeyHandler.java new file mode 100755 index 0000000000000000000000000000000000000000..6f468c4739e24d03b8bc2a3c25026a63becdb5a1 --- /dev/null +++ b/src/main/java/viewer/NavigationKeyHandler.java @@ -0,0 +1,128 @@ +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; + } +} diff --git a/src/main/java/viewer/SpimViewer.java b/src/main/java/viewer/SpimViewer.java index 0a3880ea8e28b4a894a04908a5b76d449ead6cfb..89920b0c529cef2b53de27fd0feb4ebbf682cc92 100644 --- a/src/main/java/viewer/SpimViewer.java +++ b/src/main/java/viewer/SpimViewer.java @@ -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;