blob: 960b6c7f022596369fab1f4849b92ebd6c18171e [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.pivot.scene;
import org.apache.pivot.scene.Keyboard.KeyLocation;
import org.apache.pivot.scene.Mouse.Button;
import org.apache.pivot.scene.effect.Decorator;
import org.apache.pivot.util.ListenerList;
import org.apache.pivot.util.ObservableList;
import org.apache.pivot.util.ObservableListAdapter;
/**
* Abstract base class for nodes.
*/
public abstract class Node implements Visual {
private static class NodeListenerList extends ListenerList<NodeListener>
implements NodeListener {
@Override
public void locationChanged(Node node, int previousX, int previousY) {
for (NodeListener listener : listeners()) {
listener.locationChanged(node, previousX, previousY);
}
}
@Override
public void sizeChanged(Node node, int previousWidth, int previousHeight) {
for (NodeListener listener : listeners()) {
listener.sizeChanged(node, previousWidth, previousHeight);
}
}
@Override
public void visibleChanged(Node node) {
for (NodeListener listener : listeners()) {
listener.visibleChanged(node);
}
}
@Override
public void clipChanged(Node node) {
for (NodeListener listener : listeners()) {
listener.clipChanged(node);
}
}
}
private static class NodeStateListenerList extends ListenerList<NodeStateListener>
implements NodeStateListener {
@Override
public void enabledChanged(Node node) {
for (NodeStateListener listener : listeners()) {
listener.enabledChanged(node);
}
}
@Override
public void focusedChanged(Node node, Node obverseNode) {
for (NodeStateListener listener : listeners()) {
listener.focusedChanged(node, obverseNode);
}
}
}
private static class NodeMouseListenerList extends ListenerList<NodeMouseListener>
implements NodeMouseListener {
@Override
public void mouseEntered(Node node) {
for (NodeMouseListener listener : listeners()) {
listener.mouseEntered(node);
}
}
@Override
public void mouseExited(Node node) {
for (NodeMouseListener listener : listeners()) {
listener.mouseExited(node);
}
}
@Override
public boolean mouseMoved(Node node, int x, int y, boolean captured) {
boolean consumed = false;
for (NodeMouseListener listener : listeners()) {
consumed |= listener.mouseMoved(node, x, y, captured);
}
return consumed;
}
}
private static class NodeMouseButtonListenerList extends ListenerList<NodeMouseButtonListener>
implements NodeMouseButtonListener {
@Override
public boolean mousePressed(Node node, Button button, int x, int y) {
boolean consumed = false;
for (NodeMouseButtonListener listener : listeners()) {
consumed |= listener.mousePressed(node, button, x, y);
}
return consumed;
}
@Override
public boolean mouseReleased(Node node, Button button, int x, int y) {
boolean consumed = false;
for (NodeMouseButtonListener listener : listeners()) {
consumed |= listener.mouseReleased(node, button, x, y);
}
return consumed;
}
@Override
public boolean mouseClicked(Node node, Button button, int x, int y, int count) {
boolean consumed = false;
for (NodeMouseButtonListener listener : listeners()) {
consumed |= listener.mouseClicked(node, button, x, y, count);
}
return consumed;
}
}
private static class NodeMouseWheelListenerList extends ListenerList<NodeMouseWheelListener>
implements NodeMouseWheelListener {
@Override
public boolean mouseWheelScrolled(Node node, Mouse.ScrollType scrollType,
int scrollAmount, int wheelRotation, int x, int y) {
boolean consumed = false;
for (NodeMouseWheelListener listener : listeners()) {
consumed |= listener.mouseWheelScrolled(node, scrollType, scrollAmount,
wheelRotation, x, y);
}
return consumed;
}
}
private static class NodeKeyListenerList extends ListenerList<NodeKeyListener>
implements NodeKeyListener {
@Override
public boolean keyPressed(Node node, int keyCode, KeyLocation keyLocation) {
boolean consumed = false;
for (NodeKeyListener listener : listeners()) {
consumed |= listener.keyPressed(node, keyCode, keyLocation);
}
return consumed;
}
@Override
public boolean keyReleased(Node node, int keyCode, KeyLocation keyLocation) {
boolean consumed = false;
for (NodeKeyListener listener : listeners()) {
consumed |= listener.keyReleased(node, keyCode, keyLocation);
}
return consumed;
}
@Override
public boolean keyTyped(Node node, char character) {
boolean consumed = false;
for (NodeKeyListener listener : listeners()) {
consumed |= listener.keyTyped(node, character);
}
return consumed;
}
}
private static class NodeClassListenerList extends ListenerList<NodeClassListener>
implements NodeClassListener {
@Override
public void focusedNodeChanged(Node previousFocusedNode) {
for (NodeClassListener listener : listeners()) {
listener.focusedNodeChanged(previousFocusedNode);
}
}
}
// The node's parent, or null if the node does not have a parent
private Group group = null;
// The node's location, relative to the parent's origin
private int x = 0;
private int y = 0;
// The node's size
private int width = 0;
private int height = 0;
// The node's visible flag
private boolean visible = false;
// The node's clip flag
private boolean clip = false;
// The node's valid state
private boolean valid = false;
// The node's decorators
// TODO Use an inner class that will call repaint() as needed
private ObservableList<Decorator> decorators = ObservableListAdapter.observableArrayList();
// The node's enabled flag
private boolean enabled = true;
// The current mouse location
private Point mouseLocation = null;
// Instance listener lists
private NodeListenerList nodeListeners = new NodeListenerList();
private NodeStateListenerList nodeStateListeners = new NodeStateListenerList();
private NodeMouseListenerList nodeMouseListeners = new NodeMouseListenerList();
private NodeMouseButtonListenerList nodeMouseButtonListeners = new NodeMouseButtonListenerList();
private NodeMouseWheelListenerList nodeMouseWheelListeners = new NodeMouseWheelListenerList();
private NodeKeyListenerList nodeKeyListeners = new NodeKeyListenerList();
// The currently focused node
private static Node focusedNode = null;
// Class listener list
private static NodeClassListenerList nodeClassListeners = new NodeClassListenerList();
/**
* Returns the group that contains this node.
*
* @return
* The group that contains the node, or <tt>null</tt> if this node is
* not currently attached to a group.
*/
public Group getGroup() {
return group;
}
/**
* Sets the group that will contain this node.
*
* @param group
* The group that will contain the node, or <tt>null</tt> to remove
* the node from a group.
*/
protected void setGroup(Group group) {
// If this node is being removed from the scene graph
// and is currently focused, clear the focus
if (group == null
&& isFocused()) {
clearFocus();
}
Group previousGroup = this.group;
this.group = group;
if (previousGroup != null) {
previousGroup.descendantRemoved(this);
}
if (group != null) {
group.descendantAdded(this);
}
}
/**
* Returns the stage that is hosting this node.
*
* @return
* The stage that is hosting the node, or <tt>null</tt> if this node
* is not currently attached to a stage.
*/
public Stage getStage() {
return (Stage)getAncestor(Stage.class);
}
public Group getAncestor(Class<? extends Group> ancestorType) {
Node node = this;
while (node != null
&& !(ancestorType.isInstance(node))) {
node = node.getGroup();
}
return (Group)node;
}
@SuppressWarnings("unchecked")
public Group getAncestor(String ancestorTypeName) throws ClassNotFoundException {
if (ancestorTypeName == null) {
throw new IllegalArgumentException();
}
return getAncestor((Class<? extends Group>)Class.forName(ancestorTypeName));
}
/**
* Returns the node's x-coordinate.
*
* @return
* The node's horizontal position relative to the origin of the
* parent group.
*/
public int getX() {
return x;
}
/**
* Sets the node's x-coordinate.
*
* @param x
* The node's horizontal position relative to the origin of the
* parent group.
*/
public void setX(int x) {
setLocation(x, getY());
}
/**
* Returns the node's y-coordinate.
*
* @return
* The node's vertical position relative to the origin of the
* parent group.
*/
public int getY() {
return y;
}
/**
* Sets the node's y-coordinate.
*
* @param y
* The node's vertical position relative to the origin of the
* parent group.
*/
public void setY(int y) {
setLocation(getX(), y);
}
/**
* Returns the node's location.
*
* @return
* A point value containing the node's horizontal and vertical
* position relative to the origin of the parent group.
*/
public Point getLocation() {
return new Point(getX(), getY());
}
/**
* Sets the node's location.
*
* @param x
* The node's horizontal position relative to the origin of the
* parent group.
*
* @param y
* The node's vertical position relative to the origin of the
* parent group.
*/
public void setLocation(int x, int y) {
int previousX = this.x;
int previousY = this.y;
if (previousX != x
|| previousY != y) {
// Redraw the region formerly occupied by this node
if (group != null) {
group.repaint(getDecoratedBounds());
}
// Set the new coordinates
this.x = x;
this.y = y;
// Redraw the region currently occupied by this node
if (group != null) {
group.repaint(getDecoratedBounds());
}
nodeListeners.locationChanged(this, previousX, previousY);
}
}
/**
* Sets the node's location.
*
* @param location
* A point value containing the node's horizontal and vertical
* position relative to the origin of the parent group.
*
* @see #setLocation(int, int)
*/
public final void setLocation(Point location) {
if (location == null) {
throw new IllegalArgumentException("location cannot be null.");
}
setLocation(location.x, location.y);
}
@Override
public int getWidth() {
return width;
}
public void setWidth(int width) {
setSize(width, getHeight());
}
@Override
public int getHeight() {
return height;
}
public void setHeight(int height) {
setSize(getWidth(), height);
}
public Dimensions getSize() {
return new Dimensions(this.getWidth(), this.getHeight());
}
public final void setSize(Dimensions size) {
if (size == null) {
throw new IllegalArgumentException("size is null.");
}
setSize(size.width, size.height);
}
@Override
public void setSize(int width, int height) {
if (width < 0) {
throw new IllegalArgumentException("width is negative.");
}
if (height < 0) {
throw new IllegalArgumentException("height is negative.");
}
int previousWidth = getWidth();
int previousHeight = getHeight();
if (width != previousWidth
|| height != previousHeight) {
// This node's size changed, most likely as a result
// of being laid out; it must be flagged as invalid to ensure
// that layout is propagated downward when validate() is
// called on it
invalidate();
// Redraw the region formerly occupied by this node
if (group != null) {
group.repaint(getDecoratedBounds());
}
// Set the size
this.width = width;
this.height = height;
// Redraw the region currently occupied by this node
if (group != null) {
group.repaint(getDecoratedBounds());
}
nodeListeners.sizeChanged(this, previousWidth, previousHeight);
}
}
/**
* Returns the node's bounding area.
*
* @return
* The node's bounding area. The <tt>x</tt> and <tt>y</tt> values are
* relative to the parent group.
*/
public Bounds getBounds() {
// TODO This is the transformed bounds of the node, in the parent's
// coordinate space. It is determined by transforming the node's
// extents and mapping to parent coordinates. If clip is true, it
// will also be constrained to the untransformed bounds of the node.
// TODO If null, recalculate (set to null in invalidate())
return new Bounds(x, y, getWidth(), getHeight());
}
/**
* Returns the node's decorated bounding area.
*
* @return
* TODO
*/
public Bounds getDecoratedBounds() {
// TODO
return getBounds();
}
/**
* Returns the node's extents. These are the maximum and minimum x and y values
* in the node's coordinate space.
* <p>
* This method will only be called as needed, so implementations do not need to
* cache the value.
*/
public abstract Extents getExtents();
/**
* Determines if the node contains a given location. This method facilitates
* mouse interaction with non-rectangular nodes.
*
* @param x
* @param y
*
* @return
* <tt>true</tt> if the node's shape contains the given location; <tt>false</tt>,
* otherwise.
*
* @throws UnsupportedOperationException
* This method is not currently implemented.
*/
public abstract boolean contains(int x, int y);
/**
* Returns the node's visibility.
*
* @return
* <tt>true</tt> if the node will be painted; <tt>false</tt>,
* otherwise.
*/
public boolean isVisible() {
return visible;
}
/**
* Sets the node's visibility.
*
* @param visible
* <tt>true</tt> if the node should be painted; <tt>false</tt>,
* otherwise.
*/
public void setVisible(boolean visible) {
if (this.visible != visible) {
// If this node is being hidden and has the focus, clear
// the focus
if (!visible) {
if (isFocused()) {
clearFocus();
}
// Ensure that the mouse out event is processed
if (isMouseOver()) {
mouseExited();
}
}
// Redraw the region formerly occupied by this node
if (group != null) {
group.repaint(getDecoratedBounds());
}
this.visible = visible;
// Redraw the region currently occupied by this node
if (group != null) {
group.repaint(getDecoratedBounds());
}
// Ensure the layout is valid
if (visible
&& !valid) {
validate();
}
// Invalidate the parent
if (group != null) {
group.invalidate();
}
nodeListeners.visibleChanged(this);
}
}
/**
* Determines if this node is showing. To be showing, the node
* and all of its ancestors must be visible and attached to a display.
*
* @return
* <tt>true</tt> if this node is showing; <tt>false</tt> otherwise.
*/
public boolean isShowing() {
Node node = this;
while (node != null
&& node.isVisible()
&& !(node instanceof Stage)) {
node = node.getGroup();
}
return (node != null
&& node.isVisible());
}
/**
* Returns the node's clip flag.
*
* @return
* <tt>true</tt> if the node should be clipped when painted;
* <tt>false</tt>, otherwise.
*/
public boolean getClip() {
return clip;
}
/**
* Sets the node's clip flag.
*
* @param clip
* <tt>true</tt> if the node should be clipped when painted;
* <tt>false</tt>, otherwise.
*/
public void setClip(boolean clip) {
if (this.clip != clip) {
this.clip = clip;
invalidate();
nodeListeners.clipChanged(this);
}
}
/**
* Returns the node's decorator list.
*/
public ObservableList<Decorator> getDecorators() {
return decorators;
}
/**
* Returns the node's enabled state.
*
* @return
* <tt>true</tt> if the node is enabled; <tt>false</tt>, otherwise.
*/
public boolean isEnabled() {
return enabled;
}
/**
* Sets the node's enabled state. Enabled nodes respond to user
* input events; disabled nodes do not.
*
* @param enabled
* <tt>true</tt> if the node is enabled; <tt>false</tt>, otherwise.
*/
public void setEnabled(boolean enabled) {
if (this.enabled != enabled) {
if (!enabled) {
// If this node has the focus, clear it
if (isFocused()) {
clearFocus();
}
// Ensure that the mouse out event is processed
if (isMouseOver()) {
mouseExited();
}
}
this.enabled = enabled;
nodeStateListeners.enabledChanged(this);
}
}
/**
* Determines if this node is blocked. A node is blocked if the
* node or any of its ancestors is disabled.
*
* @return
* <tt>true</tt> if the node is blocked; <tt>false</tt>, otherwise.
*/
public boolean isBlocked() {
boolean blocked = false;
Node node = this;
while (node != null
&& !blocked) {
blocked = !node.isEnabled();
node = node.getGroup();
}
return blocked;
}
/**
* Determines if the mouse is positioned over this node.
*
* @return
* <tt>true</tt> if the mouse is currently located over this node;
* <tt>false</tt>, otherwise.
*/
public boolean isMouseOver() {
return (mouseLocation != null);
}
/**
* Returns the current mouse location in the node's coordinate space.
*
* @return
* The current mouse location, or <tt>null</tt> if the mouse is not
* currently positioned over this node.
*/
public Point getMouseLocation() {
return mouseLocation;
}
/**
* Maps a point in this node's coordinate system to the specified
* ancestor's coordinate space.
*
* @param x
* The x-coordinate in this node's coordinate space
*
* @param y
* The y-coordinate in this node's coordinate space
*
* @return
* A point containing the translated coordinates, or <tt>null</tt> if the
* node is not a descendant of the specified ancestor.
*/
public Point mapPointToAncestor(Group ancestor, int x, int y) {
if (ancestor == null) {
throw new IllegalArgumentException("ancestor is null");
}
Point coordinates = null;
Node node = this;
while (node != null
&& coordinates == null) {
if (node == ancestor) {
coordinates = new Point(x, y);
} else {
x += node.x;
y += node.y;
node = node.getGroup();
}
}
return coordinates;
}
public Point mapPointToAncestor(Group ancestor, Point location) {
if (location == null) {
throw new IllegalArgumentException();
}
return mapPointToAncestor(ancestor, location.x, location.y);
}
/**
* Maps a point in the specified ancestor's coordinate space to this
* node's coordinate system.
*
* @param x
* The x-coordinate in the ancestors's coordinate space.
*
* @param y
* The y-coordinate in the ancestor's coordinate space.
*
* @return
* A point containing the translated coordinates, or <tt>null</tt> if the
* node is not a descendant of the specified ancestor.
*/
public Point mapPointFromAncestor(Group ancestor, int x, int y) {
if (ancestor == null) {
throw new IllegalArgumentException("ancestor is null");
}
Point coordinates = null;
Node node = this;
while (node != null
&& coordinates == null) {
if (node == ancestor) {
coordinates = new Point(x, y);
} else {
x -= node.x;
y -= node.y;
node = node.getGroup();
}
}
return coordinates;
}
public Point mapPointFromAncestor(Group ancestor, Point location) {
if (location == null) {
throw new IllegalArgumentException();
}
return mapPointFromAncestor(ancestor, location.x, location.y);
}
/**
* Returns this node's focusability.
*
* @return
* <tt>true</tt> if the node is capable of receiving the focus;
* <tt>false</tt>, otherwise.
*/
public abstract boolean isFocusable();
/**
* Returns the node's focused state.
*
* @return
* <tt>true</tt> if the node has the input focus; <tt>false</tt>
* otherwise.
*/
public boolean isFocused() {
return (focusedNode == this);
}
/**
* Called to notify a node that its focus state has changed.
*
* @param focused
* <tt>true</tt> if the node has received the input focus;
* <tt>false</tt> if the node has lost the focus.
*
* @param obverseComponent
* If <tt>focused</tt> is true, the node that has lost the focus;
* otherwise, the node that has gained the focus.
*/
protected void setFocused(boolean focused, Node obverseNode) {
if (focused) {
group.descendantGainedFocus(this);
} else {
group.descendantLostFocus(this);
}
nodeStateListeners.focusedChanged(this, obverseNode);
}
/**
* Requests that focus be given to this node. In order to receive the focus,
* the node must be focusable, showing, and unblocked.
*
* @return
* <tt>true</tt> if the node gained the focus; <tt>false</tt>
* otherwise.
*/
public boolean requestFocus() {
boolean focusable = isFocusable();
if (focusable) {
Node node = this;
while (focusable
&& node != null
&& !(node instanceof Stage)) {
focusable = node.isVisible()
&& isEnabled();
node = node.getGroup();
focusable &= node != null;
}
}
if (focusable) {
setFocusedNode(this);
getStage().requestNativeFocus();
}
return isFocused();
}
/**
* Transfers focus to the next focusable node.
*
* @param direction
* The direction in which to transfer focus.
*/
public Node transferFocus(FocusTraversalDirection direction) {
Node node = null;
Group group = getGroup();
if (group != null) {
node = group.transferFocus(this, direction);
}
return node;
}
/**
* Lays out the node's contents.
*/
public abstract void layout();
/**
* Returns the node's valid state.
*/
public boolean isValid() {
return valid;
}
/**
* Repaints the node and flags the node's hierarchy as invalid.
*/
public void invalidate() {
valid = false;
// Repaint the area this node previously occupied
repaint();
if (group != null) {
group.invalidate();
}
}
/**
* Repaints and lays out the node.
*/
public void validate() {
if (!valid
&& visible) {
// Repaint the area this node currently occupies
repaint();
layout();
valid = true;
}
}
/**
* Flags the entire node as needing to be repainted.
*/
public final void repaint() {
repaint(false);
}
/**
* Flags the entire node as needing to be repainted.
*
* @param immediate
*/
public final void repaint(boolean immediate) {
repaint(getBounds(), immediate);
}
/**
* Flags an area as needing to be repainted.
*
* @param area
*/
public final void repaint(Bounds area) {
repaint(area, false);
}
/**
* Flags an area as needing to be repainted or repaints the rectangle
* immediately.
*
* @param area
* @param immediate
*/
public final void repaint(Bounds area, boolean immediate) {
if (area == null) {
throw new IllegalArgumentException("area is null.");
}
repaint(area.x, area.y, area.width, area.height, immediate);
}
/**
* Flags an area as needing to be repainted.
*
* @param x
* @param y
* @param width
* @param height
*/
public final void repaint(int x, int y, int width, int height) {
repaint(x, y, width, height, false);
}
/**
* Flags an area as needing to be repainted.
*
* @param x
* @param y
* @param width
* @param height
* @param immediate
*/
public void repaint(int x, int y, int width, int height, boolean immediate) {
if (group != null) {
// Constrain the repaint area to this node's bounds
// TODO Only constrain if clipped
int top = y;
int left = x;
int bottom = top + height - 1;
int right = left + width - 1;
x = Math.max(left, 0);
y = Math.max(top, 0);
width = Math.min(right, getWidth() - 1) - x + 1;
height = Math.min(bottom, getHeight() - 1) - y + 1;
if (width > 0
&& height > 0) {
// Notify the parent that the region needs updating
// TODO Apply node's transform before propagating to parent
group.repaint(x + this.x, y + this.y, width, height, immediate);
// Repaint any affected decorators
for (Decorator decorator : decorators) {
Transform transform = decorator.getTransform(this);
if (!transform.isIdentity()) {
// TODO
}
}
}
}
}
/**
* Creates a graphics context for this node. This graphics context
* will not be double buffered. In other words, drawing operations on it
* will operate directly on the video RAM.
*
* @return
* A graphics context for this node, or <tt>null</tt> if this
* node is not showing.
*
* @see #isShowing()
*/
public Graphics getGraphics() {
// TODO Get host graphics and translate to the bounds of this node;
// clip if necessary
return null;
}
/**
* If the mouse is currently over the node, causes the node to
* fire <tt>mouseOut()</tt> and a <tt>mouseMove()</tt> at the current mouse
* location.
* <p>
* This method is primarily useful when consuming group mouse motion
* events, since it allows a caller to reset the mouse state based on the
* event consumption logic.
*/
public void reenterMouse() {
if (isMouseOver()) {
mouseExited();
Stage stage = getStage();
Point location = stage.getMouseLocation();
location = mapPointFromAncestor(stage, x, y);
mouseMoved(location.x, location.y, false);
}
}
protected void mouseEntered() {
if (isEnabled()) {
mouseLocation = new Point(-1, -1);
nodeMouseListeners.mouseEntered(this);
}
}
protected void mouseExited() {
if (isEnabled()) {
mouseLocation = null;
nodeMouseListeners.mouseExited(this);
}
}
protected boolean mouseMoved(int x, int y, boolean captured) {
boolean consumed = false;
if (isEnabled()) {
mouseLocation = new Point(x, y);
consumed = nodeMouseListeners.mouseMoved(this, x, y, captured);
}
return consumed;
}
protected boolean mousePressed(Mouse.Button button, int x, int y) {
boolean consumed = false;
if (isEnabled()) {
consumed = nodeMouseButtonListeners.mousePressed(this, button, x, y);
}
return consumed;
}
protected boolean mouseReleased(Mouse.Button button, int x, int y) {
boolean consumed = false;
if (isEnabled()) {
consumed = nodeMouseButtonListeners.mouseReleased(this, button, x, y);
}
return consumed;
}
protected boolean mouseClicked(Mouse.Button button, int x, int y, int count) {
boolean consumed = false;
if (isEnabled()) {
consumed = nodeMouseButtonListeners.mouseClicked(this, button, x, y, count);
}
return consumed;
}
protected boolean mouseWheelScrolled(Mouse.ScrollType scrollType,
int scrollAmount, int wheelRotation, int x, int y) {
boolean consumed = false;
if (isEnabled()) {
consumed = nodeMouseWheelListeners.mouseWheelScrolled(this, scrollType, scrollAmount,
wheelRotation, x, y);
}
return consumed;
}
protected boolean keyPressed(int keyCode, Keyboard.KeyLocation keyLocation) {
boolean consumed = false;
if (isEnabled()) {
consumed = nodeKeyListeners.keyPressed(this, keyCode, keyLocation);
if (!consumed && group != null) {
consumed = group.keyPressed(keyCode, keyLocation);
}
}
return consumed;
}
protected boolean keyReleased(int keyCode, Keyboard.KeyLocation keyLocation) {
boolean consumed = false;
if (isEnabled()) {
consumed = nodeKeyListeners.keyReleased(this, keyCode, keyLocation);
if (!consumed && group != null) {
consumed = group.keyReleased(keyCode, keyLocation);
}
}
return consumed;
}
protected boolean keyTyped(char character) {
boolean consumed = false;
if (isEnabled()) {
consumed = nodeKeyListeners.keyTyped(this, character);
if (!consumed && group != null) {
consumed = group.keyTyped(character);
}
}
return consumed;
}
@Override
public String toString() {
return getClass().getName();
}
public ListenerList<NodeListener> getNodeListeners() {
return nodeListeners;
}
public ListenerList<NodeStateListener> getNodeStateListeners() {
return nodeStateListeners;
}
public ListenerList<NodeMouseListener> getNodeMouseListeners() {
return nodeMouseListeners;
}
public ListenerList<NodeMouseButtonListener> getNodeMouseButtonListeners() {
return nodeMouseButtonListeners;
}
public ListenerList<NodeMouseWheelListener> getNodeMouseWheelListeners() {
return nodeMouseWheelListeners;
}
public ListenerList<NodeKeyListener> getNodeKeyListeners() {
return nodeKeyListeners;
}
public static ListenerList<NodeClassListener> getNodeClassListeners() {
return nodeClassListeners;
}
/**
* Returns the currently focused node.
*
* @return
* The node that currently has the focus, or <tt>null</tt> if no
* node is focused.
*/
public static Node getFocusedNode() {
return focusedNode;
}
/**
* Sets the focused node.
*
* @param focusedComponent
* The node to focus, or <tt>null</tt> to clear the focus.
*/
private static void setFocusedNode(Node focusedNode) {
Node previousFocusedNode = Node.focusedNode;
if (previousFocusedNode != focusedNode) {
Node.focusedNode = focusedNode;
if (previousFocusedNode != null) {
previousFocusedNode.setFocused(false, focusedNode);
}
if (focusedNode != null) {
focusedNode.setFocused(true, previousFocusedNode);
}
nodeClassListeners.focusedNodeChanged(previousFocusedNode);
}
}
/**
* Clears the focus.
*/
public static void clearFocus() {
setFocusedNode(null);
}
}