Skip to content

Commit

Permalink
Added documentation to local controls classes in fastPlot.
Browse files Browse the repository at this point in the history
Package gov.nasa.arc.mct.fastplot.bridge.controls introduces
several classes/interfaces for dealing with local control
elements in a uniform way; added documentation of the role
of these classes.
  • Loading branch information
VWoeltjen committed Jan 23, 2013
1 parent 4485683 commit 5488f78
Show file tree
Hide file tree
Showing 10 changed files with 147 additions and 60 deletions.
Expand Up @@ -30,7 +30,7 @@
import gov.nasa.arc.mct.fastplot.bridge.PlotConstants.YAxisMaximumLocationSetting;
import gov.nasa.arc.mct.fastplot.bridge.controls.CornerResetButton;
import gov.nasa.arc.mct.fastplot.bridge.controls.LocalControlKeyEventDispatcher;
import gov.nasa.arc.mct.fastplot.bridge.controls.ObservableAxis;
import gov.nasa.arc.mct.fastplot.bridge.controls.ControllableAxis;
import gov.nasa.arc.mct.fastplot.bridge.controls.PanControls;
import gov.nasa.arc.mct.fastplot.bridge.controls.ZoomControls;
import gov.nasa.arc.mct.fastplot.settings.LineSettings;
Expand Down Expand Up @@ -772,6 +772,7 @@ public void run() {
timer.schedule(updateTimeBoundsTask, 0, 1000);
}
});
/* Make sure key events get dispatched to local controls (which enable for Alt/Ctrl/etc...) */
plotPanel.addAncestorListener(new LocalControlKeyEventDispatcher(this));
GridBagLayout layout = new StackPlotLayout(this);
plotPanel.setLayout(layout);
Expand Down Expand Up @@ -803,17 +804,17 @@ public void run() {
this, plotLabelingAlgorithm);

// TODO: Move control attachment elsewhere
List<ObservableAxis> observableAxes = new ArrayList<ObservableAxis>();
List<ControllableAxis> observableAxes = new ArrayList<ControllableAxis>();
for (AbstractAxis axis : newPlot.getAxes()) {
if (axis != null && axis.getVisibleOrientation() != null) {
ObservableAxis a = new ObservableAxis(newPlot, axis);
ControllableAxis a = new ControllableAxis(newPlot, axis);
observableAxes.add(a);
newPlot.attachLocalControl(new PanControls(a));
newPlot.attachLocalControl(new ZoomControls(a));
newPlot.attachLocalControl(new CornerResetButton(a));
}
}
newPlot.attachLocalControl(new CornerResetButton(observableAxes.toArray(new ObservableAxis[observableAxes.size()])));
newPlot.attachLocalControl(new CornerResetButton(observableAxes.toArray(new ControllableAxis[observableAxes.size()])));

newPlot.setPlotLabelingAlgorithm(plotLabelingAlgorithm);
subPlots.add(newPlot);
Expand Down
@@ -1,14 +1,12 @@
package gov.nasa.arc.mct.fastplot.bridge.controls;

import gov.nasa.arc.mct.fastplot.bridge.AbstractAxis;
import gov.nasa.arc.mct.fastplot.bridge.PlotConstants;
import gov.nasa.arc.mct.fastplot.bridge.PlotObserver;
import gov.nasa.arc.mct.fastplot.bridge.AbstractAxis.AxisVisibleOrientation;
import gov.nasa.arc.mct.fastplot.bridge.PlotObserver;
import gov.nasa.arc.mct.fastplot.view.IconLoader.Icons;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
Expand Down Expand Up @@ -40,6 +38,11 @@ public abstract class AbstractPanZoomControls extends AbstractPlotLocalControl i
private boolean keyState = false;
private boolean mouseState = false;

/**
* Create controls for the specified plot axis. The axis will be used to determine the orientation
* of controls (horizontal or vertical), and will receive updates when controls are clicked.
* @param axis
*/
public AbstractPanZoomControls(AbstractAxis axis) {
super();
this.axis = axis;
Expand Down Expand Up @@ -67,10 +70,16 @@ public AbstractPanZoomControls(AbstractAxis axis) {

/**
* Apply whatever adjustment this control performs (panning or zooming)
* The "less" argument indicates whether or not this
* The "less" argument indicates whether or not this adjustment goes toward the bottom-left.
*
* Note that currentSpan is presumed to be calculated as getEnd-getStart for the axis; so,
* the sign will be negative if the axes have been inverted (i.e. "max at left"). Implementing
* classes may use this sign (implicitly or explicitly) to adjust the axis in the correct
* direction.
*
* @param axis the axis to update
* @param currentSpan the current axis span
* @param less true if the "les"
* @param less
*/
public abstract void adjustAxis(AbstractAxis axis, double currentSpan, boolean less);

Expand Down
Expand Up @@ -7,44 +7,35 @@
import gov.nasa.arc.mct.fastplot.view.IconLoader;

import java.awt.event.ActionListener;
import java.util.Arrays;
import java.util.Collection;
import java.util.ResourceBundle;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.SpringLayout;
import javax.swing.border.Border;

/**
* Base class for plot local controls. These are controls which will appear as part of the
* plot view, used to make immediate changes to what is being shown (as opposed to broader
* changes to the view).
*
* @see gov.nasa.arc.mct.fastplot.bridge.AbstractPlottingPackage#attachLocalControl(AbstractPlotLocalControl)
*
* @author vwoeltje
*/
public abstract class AbstractPlotLocalControl extends JPanel {
private static final long serialVersionUID = 5762300765204572813L;
private static final ResourceBundle BUNDLE =
ResourceBundle.getBundle(PlotLocalControlsManager.class.getName().substring(0,
PlotLocalControlsManager.class.getName().lastIndexOf("."))+".Bundle");



private static final Border BUTTON_BORDER = BorderFactory.createEmptyBorder(
PlotConstants.ARROW_BUTTON_BORDER_STYLE_BOTTOM,
PlotConstants.ARROW_BUTTON_BORDER_STYLE_LEFT,
PlotConstants.ARROW_BUTTON_BORDER_STYLE_BOTTOM,
PlotConstants.ARROW_BUTTON_BORDER_STYLE_RIGHT);

public static final Collection<AttachmentLocation> FILL_ATTACHMENT =
Arrays.<AttachmentLocation>asList(
new AttachmentLocation(SpringLayout.WEST, SpringLayout.WEST, 0),
new AttachmentLocation(SpringLayout.EAST, SpringLayout.EAST, 0),
new AttachmentLocation(SpringLayout.NORTH, SpringLayout.NORTH, 0),
new AttachmentLocation(SpringLayout.SOUTH, SpringLayout.SOUTH, 0)
);

/**
* Controls are attached to one specific plot, represented by this object
*/
Expand Down
@@ -1,6 +1,31 @@
package gov.nasa.arc.mct.fastplot.bridge.controls;

public interface AbstractPlotLocalControlsManager {
/**
* Manages the local controls for a given plot. Primarily used to communicate user
* interactions to specific controls when the control is not able to listen for them
* locally in the Swing hierarchy (specifically, many controls are interested in either
* whether or not the mouse is over the plot area, or need to listen for key events
* even when they don't have focus.)
*
* Note: May consider removing this and simply return a Collection<AbstractPlotLocalControl>
* from AbstractPlottingPackage to simplify API. (The current implementation
* permits some cross-compatibility with the older PlotLocalControlManager.)
*
* @see gov.nasa.arc.mct.fastplot.bridge.AbstractPlottingPackage#getLocalControlsManager()
*
* @author vwoeltje
*/
public interface AbstractPlotLocalControlsManager {
/**
* Notify local controls that a specific key has been pressed or released.
* @param key the key code pressed/released (one of KeyEvent.VK_*)
* @param pressed true if press; false if released
*/
public void informKeyState(int key, boolean pressed);

/**
* Notify local controls of changes to the mouse position with regard to the plot
* @param inPlotArea true if hovering over plot; false if mouse has left
*/
public void informMouseHover(boolean inPlotArea);
}
Expand Up @@ -10,15 +10,15 @@
* @author vwoeltje
*
*/
public class ObservableAxis implements AbstractAxis {
public class ControllableAxis implements AbstractAxis {
private AbstractAxis axis;
private PlotSubject subject;

private boolean dirty = false;
private double recordedStart;
private double recordedEnd;

public ObservableAxis(PlotSubject subject, AbstractAxis axis) {
public ControllableAxis(PlotSubject subject, AbstractAxis axis) {
super();
this.axis = axis;
this.subject = subject;
Expand Down
Expand Up @@ -16,12 +16,33 @@

import javax.swing.SpringLayout;

/**
* A CornerResetButton checks for changes made by local controls, and presents a button
* for the user to click in order to undo these changes and restore the previous axis
* state.
*
* @author vwoeltje
*
*/
public class CornerResetButton extends AbstractPlotLocalControl implements PlotObserver, ActionListener {
private static final long serialVersionUID = -348727120749498680L;

private Collection<ObservableAxis> managedAxes = new ArrayList<ObservableAxis>();
/**
* The axes that this reset button looks at and may reset.
*/
private Collection<ControllableAxis> managedAxes = new ArrayList<ControllableAxis>();

public CornerResetButton(ObservableAxis... axes) {
/**
* Create a new corner reset button; this will monitor the provided axes for local
* changes and make a button visible as appropriate; when clicked, this button will reset
* these all monitored axes.
*
* Note that the shape and position of this button will be inferred by examining
* the list of provided axes.
*
* @param axes the axes to be monitored and reset by this button
*/
public CornerResetButton(ControllableAxis... axes) {
super();
setLayout(new GridLayout());
Collections.addAll(managedAxes, axes);
Expand All @@ -30,6 +51,13 @@ public CornerResetButton(ObservableAxis... axes) {
setBorder(null);
}

/**
* Get the attachment location for this control.
*
* X-Axis reset button appears bottom-right;
* Y-Axis reset button appears top-left;
* X & Y axis reset button appears bottom-left.
*/
@Override
public Collection<AttachmentLocation> getDesiredAttachmentLocations() {
String verticalEdge = isManaged(AxisVisibleOrientation.VERTICAL) ?
Expand All @@ -49,43 +77,58 @@ public PlotObserver getPlotObserver() {

@Override
public void updateTimeAxis(PlotSubject subject, long startTime, long endTime) {
// TODO Auto-generated method stub

}

@Override
public void plotAxisChanged(PlotSubject subject, AbstractAxis axis) {
// When the plot axis has changed, check to see if those changes are local
boolean dirty = true;
for (ObservableAxis a : managedAxes) {
for (ControllableAxis a : managedAxes) {
// Only display if ALL axes have changed
dirty &= a.isDirty();
}
setVisible(dirty);
}

@Override
public void actionPerformed(ActionEvent e) {
for (ObservableAxis a : managedAxes) {
// Reset all axes when clicked
for (ControllableAxis a : managedAxes) {
a.reset();
}
}

private boolean isManaged(AxisVisibleOrientation o) {
for (ObservableAxis axis : managedAxes) {
/**
* Utility method to determine if a given orientation (horizontal or vertical) is
* among the axes being managed.
* @param o
* @return
*/
private boolean isManaged(AxisVisibleOrientation o) {
for (ControllableAxis axis : managedAxes) {
if (axis.getVisibleOrientation() == o) {
return true;
}
}
return false;
}

/**
* Choose the icon for this button, based on the orientation of the axes managed.
* @return
*/
private Icons chooseIcon() {
Icons[] icons = { Icons.PLOT_CORNER_RESET_BUTTON_TOP_LEFT_GREY, Icons.PLOT_CORNER_RESET_BUTTON_TOP_RIGHT_GREY,
Icons.PLOT_CORNER_RESET_BUTTON_BOTTOM_LEFT_GREY, Icons.PLOT_CORNER_RESET_BUTTON_BOTTOM_RIGHT_GREY };
int index = (isManaged(AxisVisibleOrientation.HORIZONTAL) ? 2 : 0) +
(isManaged(AxisVisibleOrientation.VERTICAL ) ? 0 : 1);
return icons[index];
}


/**
* Choose the name for this button, based on the orientation of the axes managed.
* @return
*/
private String chooseName() {
String[] names = { "TopLeftCornerButton", "TopRightCornerButton",
"BottomLeftCornerButton", "BottomRightCornerButton" };
Expand Down
Expand Up @@ -10,6 +10,22 @@
import javax.swing.event.AncestorEvent;
import javax.swing.event.AncestorListener;

/**
* Dispatches key events directly to plot local controls, regardless of focus (but only if the
* mouse is hovering over the plot). This is an alternative to other keyboard input strategies
* in Swing (KeyListener, InputMap) which only apply when components have focus, whose usage
* may require a lot of extra code to make sure that the plot has focus whenever the user might
* expect it to.
*
* Usage note: It is advisable to attach this as an AncestorListener to a relevant JComponent
* instead of registering it as a KeyEventDispatcher directly. When acting as an
* AncestorListener, this will register and remove itself as a KeyEventDispatcher automatically
* when the component is added or removed from the Swing hierarchy. This helps ensure that
* stale references do not remain in the KeyboardFocusManager, which is global (so out-dated
* dispatchers which are not removed present memory leaks.)
*
* @author vwoeltje
*/
public class LocalControlKeyEventDispatcher implements KeyEventDispatcher, AncestorListener {
private PlotAbstraction abstraction;

Expand All @@ -21,6 +37,7 @@ public LocalControlKeyEventDispatcher(PlotAbstraction abstraction) {
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
int id = event.getID();
/* Any PRESSED or RELEASED events should be reported to local controls */
if (id == KeyEvent.KEY_PRESSED || id == KeyEvent.KEY_RELEASED) {
boolean pressed = id == KeyEvent.KEY_PRESSED;
for (AbstractPlottingPackage p : abstraction.getSubPlots()) {
Expand All @@ -31,6 +48,13 @@ public boolean dispatchKeyEvent(KeyEvent event) {
}
}
return false;
/*
* Note that this is not particularly efficient; all key presses get forwarded to all
* plots, which is potentially a lot of plots. It may make more sense to build a map
* of keycodes -> interested local control managers at the time of instantiation.
* But, this risks bugs if the map becomes out-of-date, and may also require additions
* to related interfaces to allow these key bindings to be inferred or registered.
*/
}


Expand All @@ -48,8 +72,6 @@ public void ancestorAdded(AncestorEvent e) {

@Override
public void ancestorMoved(AncestorEvent arg0) {
// TODO Auto-generated method stub

}

}
Expand Up @@ -7,15 +7,25 @@

import java.awt.event.KeyEvent;

/**
* Buttons to pan along an axis.
*
* @author vwoeltje
*
*/
public class PanControls extends AbstractPanZoomControls {
private static final long serialVersionUID = 3970100144412350694L;

/**
* Create pan controls for the specified axis.
* @param axis
*/
public PanControls(AbstractAxis axis) {
super(axis);
}

@Override
public void adjustAxis(AbstractAxis axis, double currentSpan, boolean less) {
public void adjustAxis(AbstractAxis axis, double currentSpan, boolean less) {
axis.shift(currentSpan * (PlotConstants.PANNING_PERCENTAGE / 100.) * (less ? -1 : 1));
}

Expand Down

0 comments on commit 5488f78

Please sign in to comment.