Skip to content

ScreenSelector enhancements #75

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 0 additions & 96 deletions src/gov/nasa/worldwindx/examples/ScreenSelection.java
Original file line number Diff line number Diff line change
Expand Up @@ -111,102 +111,6 @@ public void actionPerformed(ActionEvent actionEvent)
}
}

/**
* Extends HighlightController to add the capability to highlight objects selected by a ScreenSelector. This tracks
* objects highlighted by both cursor rollover events and screen selection changes, and ensures that objects stay
* highlighted when they are either under cursor or in the ScreenSelector's selection rectangle.
*/
protected static class SelectionHighlightController extends HighlightController implements MessageListener
{
protected ScreenSelector screenSelector;
protected List<Highlightable> lastBoxHighlightObjects = new ArrayList<Highlightable>();

public SelectionHighlightController(WorldWindow wwd, ScreenSelector screenSelector)
{
super(wwd, SelectEvent.ROLLOVER);

this.screenSelector = screenSelector;
this.screenSelector.addMessageListener(this);
}

@Override
public void dispose()
{
super.dispose();

this.screenSelector.removeMessageListener(this);
}

public void onMessage(Message msg)
{
try
{
// Update the list of highlighted objects whenever the ScreenSelector's selection changes. We capture
// both the selection started and selection changed events to ensure that we clear the list of selected
// objects when the selection begins or re-starts, as well as update the list when it changes.
if (msg.getName().equals(ScreenSelector.SELECTION_STARTED)
|| msg.getName().equals(ScreenSelector.SELECTION_CHANGED))
{
this.highlightSelectedObjects(this.screenSelector.getSelectedObjects());
}
}
catch (Exception e)
{
// Wrap the handler in a try/catch to keep exceptions from bubbling up
Util.getLogger().warning(e.getMessage() != null ? e.getMessage() : e.toString());
}
}

protected void highlight(Object o)
{
// Determine if the highlighted object under the cursor has changed, but should remain highlighted because
// its in the selection box. In this case we assign the highlighted object under the cursor to null and
// return, and thereby avoid changing the highlight state of objects still highlighted by the selection box.
if (this.lastHighlightObject != o && this.lastBoxHighlightObjects.contains(this.lastHighlightObject))
{
this.lastHighlightObject = null;
return;
}

super.highlight(o);
}

protected void highlightSelectedObjects(List<?> list)
{
if (this.lastBoxHighlightObjects.equals(list))
return; // same thing selected

// Turn off highlight for the last set of selected objects, if any. Since one of these objects may still be
// highlighted due to a cursor rollover, we detect that object and avoid changing its highlight state.
for (Highlightable h : this.lastBoxHighlightObjects)
{
if (h != this.lastHighlightObject)
h.setHighlighted(false);
}
this.lastBoxHighlightObjects.clear();

if (list != null)
{
// Turn on highlight if object selected.
for (Object o : list)
{
if (o instanceof Highlightable)
{
((Highlightable) o).setHighlighted(true);
this.lastBoxHighlightObjects.add((Highlightable) o);
}
}
}

// We've potentially changed the highlight state of one or more objects. Request that the world window
// redraw itself in order to refresh these object's display. This is necessary because changes in the
// objects in the pick rectangle do not necessarily correspond to mouse movements. For example, the pick
// rectangle may be cleared when the user releases the mouse button at the end of a drag. In this case,
// there's no mouse movement to cause an automatic redraw.
this.wwd.redraw();
}
}

public static void main(String[] args)
{
start("World Wind Screen Selection", AppFrame.class);
Expand Down
185 changes: 148 additions & 37 deletions src/gov/nasa/worldwindx/examples/util/ScreenSelector.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import gov.nasa.worldwind.*;
import gov.nasa.worldwind.event.*;
import gov.nasa.worldwind.layers.*;
import gov.nasa.worldwind.pick.*;
import gov.nasa.worldwind.render.*;
import gov.nasa.worldwind.util.*;
import gov.nasa.worldwindx.applications.worldwindow.util.Util;
Expand Down Expand Up @@ -296,9 +297,16 @@ protected void drawOrderedRenderable(DrawContext dc)

protected WorldWindow wwd;
protected Layer layer;

protected SelectionRectangle selectionRect;
protected List<Object> selectedObjects = new ArrayList<Object>();

protected List<MessageListener> messageListeners = new ArrayList<MessageListener>();
protected List<SelectListener> selectListeners = new ArrayList<SelectListener>();

protected boolean enabled;
protected boolean autoEnable;
protected boolean autoDisable;
protected boolean armed;

public ScreenSelector(WorldWindow worldWindow)
Expand All @@ -315,6 +323,10 @@ public ScreenSelector(WorldWindow worldWindow)
this.layer.setPickEnabled(false); // The screen selector is not pickable.
this.selectionRect = this.createSelectionRectangle();
((RenderableLayer) this.layer).addRenderable(this.selectionRect);

// Listen for mouse input on the World Window.
this.wwd.getInputHandler().addMouseListener(this);
this.wwd.getInputHandler().addMouseMotionListener(this);
}

protected Layer createLayer()
Expand Down Expand Up @@ -356,6 +368,26 @@ public void setBorderColor(Color color)
{
this.selectionRect.setBorderColor(color);
}

public void setAutoEnable(boolean autoEnable)
{
this.autoEnable = autoEnable;
}

public boolean getAutoEnable()
{
return this.autoEnable;
}

public void setAutoDisable(boolean autoDisable)
{
this.autoDisable = autoDisable;
}

public boolean getAutoDisable()
{
return this.autoDisable;
}

public void enable()
{
Expand All @@ -373,9 +405,7 @@ public void enable()
if (!this.getLayer().isEnabled())
this.getLayer().setEnabled(true);

// Listen for mouse input on the World Window.
this.getWwd().getInputHandler().addMouseListener(this);
this.getWwd().getInputHandler().addMouseMotionListener(this);
this.enabled = true;
}

public void disable()
Expand All @@ -389,10 +419,8 @@ public void disable()

// Remove the layer that displays this ScreenSelector's selection rectangle.
this.getWwd().getModel().getLayers().remove(this.getLayer());

// Stop listening for mouse input on the world window.
this.getWwd().getInputHandler().removeMouseListener(this);
this.getWwd().getInputHandler().removeMouseMotionListener(this);

this.enabled = false;
}

public List<?> getSelectedObjects()
Expand Down Expand Up @@ -423,6 +451,30 @@ public void removeMessageListener(MessageListener listener)

this.messageListeners.remove(listener);
}

public void addSelectListener(SelectListener listener)
{
if (listener == null)
{
String msg = Logging.getMessage("nullValue.ListenerIsNull");
Logging.logger().severe(msg);
throw new IllegalArgumentException(msg);
}

this.selectListeners.add(listener);
}

public void removeSelectListener(SelectListener listener)
{
if (listener == null)
{
String msg = Logging.getMessage("nullValue.ListenerIsNull");
Logging.logger().severe(msg);
throw new IllegalArgumentException(msg);
}

this.selectListeners.remove(listener);
}

protected void sendMessage(Message message)
{
Expand All @@ -440,6 +492,23 @@ protected void sendMessage(Message message)
}
}
}

protected void sendSelectEvent(SelectEvent event)
{
for (SelectListener listener : this.selectListeners)
{
try
{
listener.selected(event);
}
catch (Exception e)
{
String msg = Logging.getMessage("generic.ExceptionInvokingMessageListener");
Logging.logger().severe(msg);
// Don't throw an exception, just log a severe message and continue to the next listener.
}
}
}

public void mouseClicked(MouseEvent mouseEvent)
{
Expand All @@ -448,28 +517,47 @@ public void mouseClicked(MouseEvent mouseEvent)

public void mousePressed(MouseEvent mouseEvent)
{
if (mouseEvent == null) // Ignore null events.
return;

if (MouseEvent.BUTTON1_DOWN_MASK != mouseEvent.getModifiersEx()) // Respond to button 1 down w/o modifiers.
return;
if (this.enabled)
{
if (mouseEvent == null) // Ignore null events.
return;

this.armed = true;
this.selectionStarted(mouseEvent);
mouseEvent.consume(); // Consume the mouse event to prevent the view from responding to it.
this.armed = true;
this.selectionStarted(mouseEvent);
mouseEvent.consume(); // Consume the mouse event to prevent the view from responding to it.
}
else if (this.autoEnable)
{
if (mouseEvent.isControlDown() || mouseEvent.isShiftDown())
{
enable();
this.armed = true;
this.selectionStarted(mouseEvent);
mouseEvent.consume(); // Consume the mouse event to prevent the view from responding to it.
}
}
}

public void mouseReleased(MouseEvent mouseEvent)
{
if (mouseEvent == null) // Ignore null events.
return;

if (!this.armed) // Respond to mouse released events when armed.
return;

this.armed = false;
this.selectionEnded(mouseEvent);
mouseEvent.consume(); // Consume the mouse event to prevent the view from responding to it.
if (this.enabled)
{
if (mouseEvent == null) // Ignore null events.
return;

if (!this.armed) // Respond to mouse released events when armed.
return;

this.armed = false;
this.selectionEnded(mouseEvent);
mouseEvent.consume(); // Consume the mouse event to prevent the view from responding to it.

// If auto-enable, then we auto-disable after selection.
if (this.autoDisable)
{
disable();
}
}
}

public void mouseEntered(MouseEvent mouseEvent)
Expand All @@ -484,14 +572,17 @@ public void mouseExited(MouseEvent mouseEvent)

public void mouseDragged(MouseEvent mouseEvent)
{
if (mouseEvent == null) // Ignore null events.
return;
if (this.enabled)
{
if (mouseEvent == null) // Ignore null events.
return;

if (!this.armed) // Respond to mouse dragged events when armed.
return;
if (!this.armed) // Respond to mouse dragged events when armed.
return;

this.selectionChanged(mouseEvent);
mouseEvent.consume(); // Consume the mouse event to prevent the view from responding to it.
this.selectionChanged(mouseEvent);
mouseEvent.consume(); // Consume the mouse event to prevent the view from responding to it.
}
}

public void mouseMoved(MouseEvent mouseEvent)
Expand All @@ -514,14 +605,34 @@ protected void selectionStarted(MouseEvent mouseEvent)
@SuppressWarnings({"UnusedParameters"})
protected void selectionEnded(MouseEvent mouseEvent)
{
this.selectionRect.clearSelection();
this.getWwd().getSceneController().setPickRectangle(null);
this.getWwd().removeSelectListener(this); // Stop listening for changes the pick rectangle selection.
this.getWwd().redraw();
Rectangle rectangle = this.selectionRect.getSelection();
if (rectangle.width > 0 && rectangle.height > 0)
{
this.selectionRect.clearSelection();
this.getWwd().getSceneController().setPickRectangle(null);
this.getWwd().removeSelectListener(this); // Stop listening for changes the pick rectangle selection.
this.getWwd().redraw();

// Send a message indicating that the user has completed their selection. We don't clear the list of selected
// objects in order to preserve the list of selected objects for the caller.
this.sendMessage(new Message(SELECTION_ENDED, this));
// Send a message indicating that the user has completed their selection. We don't clear the list of selected
// objects in order to preserve the list of selected objects for the caller.
this.sendMessage(new Message(SELECTION_ENDED, this));

PickedObjectList list = new PickedObjectList();
for (Object selected : this.getSelectedObjects())
{
PickedObject pickedObject = new PickedObject(0, selected);
pickedObject.setOnTop();
list.add(pickedObject);
}

String eventAction = SelectEvent.LEFT_CLICK;
if (mouseEvent.getButton() == MouseEvent.BUTTON2)
{
eventAction = SelectEvent.RIGHT_CLICK;
}

this.sendSelectEvent(new SelectEvent(this.getWwd(), eventAction, mouseEvent, list));
}
}

protected void selectionChanged(MouseEvent mouseEvent)
Expand Down
Loading