Skip to content

Commit

Permalink
ttools: refactor navigation mouse listener code
Browse files Browse the repository at this point in the history
Add a new class NavigationListener which translates mouse gestures into
surface re-aspecting requests.  This can now be used in both topcat
and stilts, avoiding duplicated code.
  • Loading branch information
mbtaylor committed Oct 30, 2013
1 parent b174565 commit 0828b52
Show file tree
Hide file tree
Showing 3 changed files with 196 additions and 197 deletions.
147 changes: 37 additions & 110 deletions topcat/src/main/uk/ac/starlink/topcat/plot2/StackPlotWindow.java
Expand Up @@ -9,9 +9,9 @@
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.awt.event.MouseMotionAdapter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
Expand All @@ -33,8 +33,6 @@
import javax.swing.OverlayLayout;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.MouseInputAdapter;
import javax.swing.event.MouseInputListener;
import uk.ac.starlink.table.StarTable;
import uk.ac.starlink.table.Tables;
import uk.ac.starlink.topcat.AuxWindow;
Expand All @@ -51,6 +49,7 @@
import uk.ac.starlink.ttools.plot.Range;
import uk.ac.starlink.ttools.plot2.AuxScale;
import uk.ac.starlink.ttools.plot2.DataGeom;
import uk.ac.starlink.ttools.plot2.NavigationListener;
import uk.ac.starlink.ttools.plot2.Navigator;
import uk.ac.starlink.ttools.plot2.PlotLayer;
import uk.ac.starlink.ttools.plot2.PlotPlacement;
Expand Down Expand Up @@ -157,21 +156,44 @@ public float[] getItem() {
axisControl_.addActionListener( plotPanel_ );
shaderControl.addActionListener( plotPanel_ );

/* Arrange for user gestures (zoom, pan, click) on the plot panel
* itself to result in appropriate actions. */
plotPanel_.setFocusable( true );
plotPanel_.addMouseWheelListener( new MouseWheelListener() {
public void mouseWheelMoved( MouseWheelEvent evt ) {
StackPlotWindow.this.wheel( evt );
/* Arrange for user navigation actions to adjust the view. */
new NavigationListener<A>() {
public Surface getSurface() {
return plotPanel_.getLatestSurface();
}
} );
addMouseInputListener( plotPanel_, new PanListener() );
public Navigator<A> getNavigator() {
return axisControl_.getNavigator();
}
public Iterable<double[]> createDataPosIterable() {
return new PointCloud( plotPanel_.getPlotLayers(), true )
.createDataPosIterable( plotPanel_.getDataStore() );
}
public void setAspect( A aspect ) {
axisControl_.setAspect( aspect );
plotPanel_.replot();
}
}.addListeners( plotPanel_ );

/* Arrange for user clicks to identify points. */
plotPanel_.addMouseListener( new IdentifyListener() );

/* Prepare a panel that reports current cursor position. */
posLabel_ = new JLabel();
JComponent posLine = new LineBox( "Position", posLabel_ );
posLine.setBorder( BorderFactory.createEtchedBorder() );
addMouseInputListener( plotPanel_, new PosListener() );
plotPanel_.addMouseListener( new MouseAdapter() {
public void mouseEntered( MouseEvent evt ) {
displayPosition( evt.getPoint() );
}
public void mouseExited( MouseEvent evt ) {
displayPosition( null );
}
} );
plotPanel_.addMouseMotionListener( new MouseMotionAdapter() {
public void mouseMoved( MouseEvent evt ) {
displayPosition( evt.getPoint() );
}
} );

/* Prepare a panel that reports visible point count. */
countLabel_ = new JLabel();
Expand Down Expand Up @@ -202,7 +224,6 @@ protected void blobCompleted( Shape blob ) {
addMaskSubsets( createBlobMasker( blob ).getItem() );
}
};
blobPanel.setFocusable( true ); // necessary to transmit focus to plot
stackModel_.addPlotActionListener( new ActionListener() {
public void actionPerformed( ActionEvent evt ) {
blobPanel.setActive( false );
Expand Down Expand Up @@ -406,18 +427,6 @@ public void dispose() {
plotPanel_.clearData();
}

/**
* Sets the plot surface aspect to a given new value.
*
* @param aspect new aspect
*/
private void fixAspect( A aspect ) {
if ( aspect != null ) {
axisControl_.setAspect( aspect );
plotPanel_.replot();
}
}

/**
* Acquires the list of requested plot layers from the GUI.
*
Expand Down Expand Up @@ -808,33 +817,6 @@ private void displayPosition( Point point ) {
posLabel_.setText( pos );
}

/**
* Delivers a mouse wheel gesture to the currently installed navigator,
* and updates the plot aspect accordingly.
*
* @param evt mouse wheel event
*/
private void wheel( MouseWheelEvent evt ) {
Point point = evt.getPoint();
Surface surface = plotPanel_.getLatestSurface();
if ( surface != null ) {
fixAspect( getNavigator().wheel( surface, evt ) );
}
}

/**
* Utility method to add a mouse input listener as both a MouseListener
* and a MouseMotionListener to a component.
*
* @param comp target component
* @param lnr listener for mouse button and motion events
*/
private static void addMouseInputListener( JComponent comp,
MouseInputListener lnr ) {
comp.addMouseListener( lnr );
comp.addMouseMotionListener( lnr );
}

/**
* Indicates whether any of the submitted list of plot layers
* makes use of a colour scale.
Expand All @@ -853,36 +835,9 @@ private static boolean hasShadedLayers( PlotLayer[] layers ) {
}

/**
* Mouse listener which implements dragging the plot around,
* clicking to identify points, and re-centering (right click).
* Mouse listener which listens for click events that identify a point.
*/
private class PanListener extends MouseInputAdapter {
private Surface dragSurface_;
private Point startPoint_;

@Override
public void mousePressed( MouseEvent evt ) {
Surface surface = plotPanel_.getLatestSurface();
if ( surface != null ) {
dragSurface_ = surface;
startPoint_ = evt.getPoint();
}
}

@Override
public void mouseDragged( MouseEvent evt ) {
if ( dragSurface_ != null ) {
fixAspect( getNavigator()
.drag( dragSurface_, evt, startPoint_ ) );
}
}

@Override
public void mouseReleased( MouseEvent evt ) {
dragSurface_ = null;
startPoint_ = null;
}

private class IdentifyListener extends MouseAdapter {
@Override
public void mouseClicked( MouseEvent evt ) {
int iButt = evt.getButton();
Expand All @@ -893,34 +848,6 @@ public void mouseClicked( MouseEvent evt ) {
evt.isShiftDown() ) ) {
identifyPoint( evt.getPoint() );
}
else {
Surface surface = plotPanel_.getSurface();
Iterable<double[]> dpIt =
new PointCloud( plotPanel_.getPlotLayers(), true )
.createDataPosIterable( plotPanel_.getDataStore() );
fixAspect( getNavigator().click( surface, evt, dpIt ) );
}
}
}

/**
* Mouse listener which causes update of the cursor position status
* panel whenever the mouse is moved.
*/
private class PosListener extends MouseInputAdapter {
@Override
public void mouseEntered( MouseEvent evt ) {
displayPosition( evt.getPoint() );
}

@Override
public void mouseMoved( MouseEvent evt ) {
displayPosition( evt.getPoint() );
}

@Override
public void mouseExited( MouseEvent evt ) {
displayPosition( null );
}
}
}
142 changes: 142 additions & 0 deletions ttools/src/main/uk/ac/starlink/ttools/plot2/NavigationListener.java
@@ -0,0 +1,142 @@
package uk.ac.starlink.ttools.plot2;

import java.awt.Component;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;

/**
* Listener that receives mouse events and uses them in conjunction with
* a supplied navigator to updae the aspect of a plot surface.
*
* @author Mark Taylor
* @since 30 Oct 2013
*/
public abstract class NavigationListener<A>
implements MouseListener, MouseMotionListener, MouseWheelListener {
private Surface dragSurface_;
private Point startPoint_;

/**
* Returns a plotting surface which provides the context for navigation
* actions.
*
* @return current plotting surface, may be null
*/
public abstract Surface getSurface();

/**
* Returns a navigator which is used to convert mouse gestures into
* updated surface aspects.
*
* @return current navigator, may be null
*/
public abstract Navigator<A> getNavigator();

/**
* Returns an iterable over a sequence of data space positions,
* which may be required to make sense of a click action.
*
* @return iterable over data positions, may be null
* @see Navigator#click
*/
public abstract Iterable<double[]> createDataPosIterable();

/**
* Receives a new aspect requested by user interface actions in
* conjunction with this object.
*
* @param aspect definition of requested plot surface
*/
public abstract void setAspect( A aspect );

public void mousePressed( MouseEvent evt ) {

/* Start a drag gesture. */
startPoint_ = evt.getPoint();
dragSurface_ = getSurface();
}

public void mouseDragged( MouseEvent evt ) {

/* Reposition surface midway through drag gesture. */
if ( dragSurface_ != null ) {
Navigator<A> navigator = getNavigator();
if ( navigator != null ) {
A aspect = navigator.drag( dragSurface_, evt, startPoint_ );
if ( aspect != null ) {
setAspect( aspect );
}
}
}
}

public void mouseReleased( MouseEvent evt ) {

/* Terminate any current drag gesture. */
dragSurface_ = null;
startPoint_ = null;
}

public void mouseClicked( MouseEvent evt ) {
int iButt = evt.getButton();
if ( iButt == MouseEvent.BUTTON3 ) {
Navigator<A> navigator = getNavigator();
Surface surface = getSurface();
if ( navigator != null && surface != null ) {
A aspect = navigator
.click( surface, evt, createDataPosIterable() );
if ( aspect != null ) {
setAspect( aspect );
}
}
}
}

public void mouseWheelMoved( MouseWheelEvent evt ) {
Navigator<A> navigator = getNavigator();
Surface surface = getSurface();
if ( navigator != null && surface != null ) {
A aspect = navigator.wheel( surface, evt );
if ( aspect != null ) {
setAspect( aspect );
}
}
}

public void mouseMoved( MouseEvent evt ) {
}
public void mouseEntered( MouseEvent evt ) {
}
public void mouseExited( MouseEvent evt ) {
}

/**
* Convenience method to install this listener on a graphical component.
* This currently just calls
* <code>addMouseListener</code>,
* <code>addMouseMotionListener</code> and
* <code>addMouseWheelListener</code>.
*
* @param component component to which this object should listen
*/
public void addListeners( Component component ) {
component.addMouseListener( this );
component.addMouseMotionListener( this );
component.addMouseWheelListener( this );
}

/**
* Reverses the effect of {@link #addListeners addListeners}.
*
* @param component component to which this listener was previously added
*/
public void removeListeners( Component component ) {
component.removeMouseListener( this );
component.removeMouseMotionListener( this );
component.removeMouseWheelListener( this );
}
}

0 comments on commit 0828b52

Please sign in to comment.