| /* |
| * 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.wtk; |
| |
| import java.awt.Color; |
| import java.awt.Font; |
| import java.awt.Graphics2D; |
| import java.awt.Rectangle; |
| import java.awt.Shape; |
| import java.awt.geom.AffineTransform; |
| import java.lang.reflect.InvocationTargetException; |
| import java.util.Iterator; |
| |
| import org.apache.pivot.annotations.UnsupportedOperation; |
| import org.apache.pivot.beans.BeanAdapter; |
| import org.apache.pivot.beans.IDProperty; |
| import org.apache.pivot.beans.PropertyNotFoundException; |
| import org.apache.pivot.collections.ArrayList; |
| import org.apache.pivot.collections.Dictionary; |
| import org.apache.pivot.collections.HashMap; |
| import org.apache.pivot.collections.LinkedList; |
| import org.apache.pivot.collections.Map; |
| import org.apache.pivot.collections.Sequence; |
| import org.apache.pivot.json.JSONSerializer; |
| import org.apache.pivot.serialization.SerializationException; |
| import org.apache.pivot.util.ImmutableIterator; |
| import org.apache.pivot.util.ListenerList; |
| import org.apache.pivot.util.Utils; |
| import org.apache.pivot.wtk.effects.Decorator; |
| import org.apache.pivot.wtk.skin.ComponentSkin; |
| |
| /** |
| * Top level abstract base class for all components. In MVC terminology, a |
| * component represents the "controller". It has no inherent visual |
| * representation and acts as an intermediary between the component's data (the |
| * "model") and the skin, an implementation of {@link Skin} which serves as the |
| * "view". |
| */ |
| @IDProperty("name") |
| public abstract class Component implements ConstrainedVisual { |
| /** |
| * Style dictionary implementation. |
| */ |
| public final class StyleDictionary implements Dictionary<String, Object>, Iterable<String> { |
| private StyleDictionary() { |
| } |
| |
| public Object get(Style key) { |
| return styles.get(key.toString()); |
| } |
| |
| @Override |
| public Object get(String key) { |
| return styles.get(key); |
| } |
| |
| public Color getColor(Style key) { |
| return getColor(key.toString()); |
| } |
| |
| public Font getFont(Style key) { |
| return getFont(key.toString()); |
| } |
| |
| public int getInt(Style key) { |
| return getInt(key.toString()); |
| } |
| |
| public boolean getBoolean(Style key) { |
| return getBoolean(key.toString()); |
| } |
| |
| public Object put(Style key, Object value) { |
| return put(key.toString(), value); |
| } |
| |
| /** |
| * Stores the supplied value for the specified style.<br><br> |
| * <strong>NOTE</strong> The current implementation always returns |
| * <code>null</code> due to the use of BeanAdapter to set the the new |
| * value. (BeanAdapter does not look up the previous value for |
| * performance reasons)<br><br> This also means that the logic |
| * determining whether to fire the the event differs from other Pivot |
| * event firing code. The event will be fired each time this method is |
| * executed, regardless of whether the new value differs from the old |
| * value or not.<br><br> This behaviour may change in the future so |
| * should not be relied upon. |
| * |
| * @param key Style whose value will be overwritten |
| * @param value Value to be stored |
| * @return The previous value of the specified style (See note above) |
| * @see BeanAdapter#put(String, Object) |
| */ |
| @Override |
| public Object put(String key, Object value) { |
| Object previousValue = null; |
| |
| try { |
| previousValue = styles.put(key, value); |
| componentStyleListeners.styleUpdated(Component.this, key, previousValue); |
| } catch (PropertyNotFoundException exception) { |
| System.err.println("\"" + key + "\" is not a valid style for " + Component.this); |
| } |
| |
| return previousValue; |
| } |
| |
| /** |
| * Copy the named style from one style dictionary to this one. |
| * |
| * @param key Style value to be copied. |
| * @param source The source to copy from. |
| * @return The previous value in the target dictionary (but note |
| * the caveats from the {@link #put(String,Object)} method. |
| */ |
| public Object copy(Style key, Dictionary<String, Object> source) { |
| return copy(key.toString(), source); |
| } |
| |
| /** |
| * Not supported here. |
| * @throws UnsupportedOperationException always. |
| */ |
| @Override |
| @UnsupportedOperation |
| public Object remove(String key) { |
| throw new UnsupportedOperationException(); |
| } |
| |
| public boolean containsKey(Style key) { |
| return styles.containsKey(key.toString()); |
| } |
| |
| @Override |
| public boolean containsKey(String key) { |
| return styles.containsKey(key); |
| } |
| |
| public boolean isReadOnly(Style key) { |
| return styles.isReadOnly(key.toString()); |
| } |
| |
| public boolean isReadOnly(String key) { |
| return styles.isReadOnly(key); |
| } |
| |
| public Class<?> getType(Style key) { |
| return styles.getType(key.toString()); |
| } |
| |
| public Class<?> getType(String key) { |
| return styles.getType(key); |
| } |
| |
| @Override |
| public Iterator<String> iterator() { |
| return new ImmutableIterator<>(styles.iterator()); |
| } |
| } |
| |
| /** |
| * User data dictionary implementation. |
| */ |
| public final class UserDataDictionary implements Dictionary<String, Object>, Iterable<String> { |
| private UserDataDictionary() { |
| } |
| |
| @Override |
| public Object get(String key) { |
| return userData.get(key); |
| } |
| |
| @Override |
| public Object put(String key, Object value) { |
| boolean update = userData.containsKey(key); |
| Object previousValue = userData.put(key, value); |
| |
| if (update) { |
| componentDataListeners.valueUpdated(Component.this, key, previousValue); |
| } else { |
| componentDataListeners.valueAdded(Component.this, key); |
| } |
| |
| return previousValue; |
| } |
| |
| @Override |
| public Object remove(String key) { |
| Object previousValue; |
| if (userData.containsKey(key)) { |
| previousValue = userData.remove(key); |
| componentDataListeners.valueRemoved(Component.this, key, previousValue); |
| } else { |
| previousValue = null; |
| } |
| |
| return previousValue; |
| } |
| |
| @Override |
| public boolean containsKey(String key) { |
| return userData.containsKey(key); |
| } |
| |
| @Override |
| public Iterator<String> iterator() { |
| return new ImmutableIterator<>(userData.iterator()); |
| } |
| } |
| |
| /** |
| * Decorator sequence implementation. |
| */ |
| public final class DecoratorSequence implements Sequence<Decorator>, Iterable<Decorator> { |
| @Override |
| public int add(Decorator decorator) { |
| int index = getLength(); |
| insert(decorator, index); |
| |
| return index; |
| } |
| |
| @Override |
| public void insert(Decorator decorator, int index) { |
| Utils.checkNull(decorator, "decorator"); |
| |
| // Repaint the the component's previous decorated region |
| if (parent != null) { |
| parent.repaint(getDecoratedBounds()); |
| } |
| |
| decorators.insert(decorator, index); |
| |
| // Repaint the the component's current decorated region |
| if (parent != null) { |
| parent.repaint(getDecoratedBounds()); |
| } |
| |
| componentDecoratorListeners.decoratorInserted(Component.this, index); |
| } |
| |
| @Override |
| public Decorator update(int index, Decorator decorator) { |
| Utils.checkNull(decorator, "decorator"); |
| |
| // Repaint the the component's previous decorated region |
| if (parent != null) { |
| parent.repaint(getDecoratedBounds()); |
| } |
| |
| Decorator previousDecorator = decorators.update(index, decorator); |
| |
| // Repaint the the component's current decorated region |
| if (parent != null) { |
| parent.repaint(getDecoratedBounds()); |
| } |
| |
| componentDecoratorListeners.decoratorUpdated(Component.this, index, previousDecorator); |
| |
| return previousDecorator; |
| } |
| |
| @Override |
| public int remove(Decorator decorator) { |
| int index = indexOf(decorator); |
| if (index != -1) { |
| remove(index, 1); |
| } |
| |
| return index; |
| } |
| |
| @Override |
| public Sequence<Decorator> remove(int index, int count) { |
| if (count > 0) { |
| // Repaint the the component's previous decorated region |
| if (parent != null) { |
| parent.repaint(getDecoratedBounds()); |
| } |
| } |
| |
| Sequence<Decorator> removed = decorators.remove(index, count); |
| |
| if (count > 0) { |
| if (parent != null) { |
| // Repaint the the component's current decorated region |
| parent.repaint(getDecoratedBounds()); |
| } |
| |
| componentDecoratorListeners.decoratorsRemoved(Component.this, index, removed); |
| } |
| |
| return removed; |
| } |
| |
| public Sequence<Decorator> removeAll() { |
| return remove(0, getLength()); |
| } |
| |
| @Override |
| public Decorator get(int index) { |
| return decorators.get(index); |
| } |
| |
| @Override |
| public int indexOf(Decorator decorator) { |
| return decorators.indexOf(decorator); |
| } |
| |
| @Override |
| public int getLength() { |
| return decorators.getLength(); |
| } |
| |
| @Override |
| public Iterator<Decorator> iterator() { |
| return new ImmutableIterator<>(decorators.iterator()); |
| } |
| } |
| |
| // The currently installed skin, or null if no skin is installed |
| private Skin skin = null; |
| |
| // Preferred width and height values explicitly set by the user |
| private int preferredWidth = -1; |
| private int preferredHeight = -1; |
| |
| // Bounds on preferred size |
| private int minimumWidth = 0; |
| private int maximumWidth = Integer.MAX_VALUE; |
| private int minimumHeight = 0; |
| private int maximumHeight = Integer.MAX_VALUE; |
| |
| // Calculated preferred size value |
| private Dimensions preferredSize = null; |
| |
| // Calculated baseline for current size |
| private int baseline = -1; |
| |
| // The component's parent container, or null if the component does not have |
| // a parent |
| private Container parent = null; |
| |
| // The component's layout-valid state |
| private boolean valid = false; |
| |
| // The component's location, relative to the parent's origin |
| private int x = 0; |
| private int y = 0; |
| |
| // The component's visible flag |
| private boolean visible = true; |
| |
| // The component's decorators |
| private ArrayList<Decorator> decorators = new ArrayList<>(); |
| private DecoratorSequence decoratorSequence = new DecoratorSequence(); |
| |
| // The component's enabled flag |
| private boolean enabled = true; |
| |
| // The current mouse location |
| private Point mouseLocation = null; |
| |
| // The cursor that is displayed over the component |
| private Cursor cursor = null; |
| |
| // The tooltip text, delay, trigger callback, flag for wrapping its text |
| private String tooltipText = null; |
| private int tooltipDelay = 1000; |
| private ApplicationContext.ScheduledCallback triggerTooltipCallback = null; |
| private boolean tooltipWrapText = true; |
| |
| // The component's drag source |
| private DragSource dragSource = null; |
| |
| // The component's drop target |
| private DropTarget dropTarget = null; |
| |
| // The component's menu handler |
| private MenuHandler menuHandler = null; |
| |
| // The component's name |
| private String name = null; |
| |
| // The component's styles |
| private BeanAdapter styles = null; |
| private StyleDictionary styleDictionary = new StyleDictionary(); |
| |
| // User data |
| private HashMap<String, Object> userData = new HashMap<>(); |
| private UserDataDictionary userDataDictionary = new UserDataDictionary(); |
| |
| // Container attributes |
| private HashMap<? extends Enum<?>, Object> attributes = null; |
| |
| // The component's automation ID |
| private String automationID; |
| |
| // Event listener lists |
| private ComponentListener.Listeners componentListeners = new ComponentListener.Listeners(); |
| private ComponentStateListener.Listeners componentStateListeners = new ComponentStateListener.Listeners(); |
| private ComponentDecoratorListener.Listeners componentDecoratorListeners = |
| new ComponentDecoratorListener.Listeners(); |
| private ComponentStyleListener.Listeners componentStyleListeners = new ComponentStyleListener.Listeners(); |
| private ComponentMouseListener.Listeners componentMouseListeners = new ComponentMouseListener.Listeners(); |
| private ComponentMouseButtonListener.Listeners componentMouseButtonListeners = |
| new ComponentMouseButtonListener.Listeners(); |
| private ComponentMouseWheelListener.Listeners componentMouseWheelListeners = |
| new ComponentMouseWheelListener.Listeners(); |
| private ComponentKeyListener.Listeners componentKeyListeners = new ComponentKeyListener.Listeners(); |
| private ComponentTooltipListener.Listeners componentTooltipListeners = new ComponentTooltipListener.Listeners(); |
| private ComponentDataListener.Listeners componentDataListeners = new ComponentDataListener.Listeners(); |
| |
| // The component that currently has the focus |
| private static Component focusedComponent = null; |
| |
| // Typed and named styles |
| private static HashMap<Class<? extends Component>, Map<String, ?>> typedStyles = new HashMap<>(); |
| private static HashMap<String, Map<String, ?>> namedStyles = new HashMap<>(); |
| |
| // Class event listeners |
| private static ComponentClassListener.Listeners componentClassListeners = new ComponentClassListener.Listeners(); |
| |
| /** |
| * Returns the component's automation ID. |
| * |
| * @return The component's automation ID, or <tt>null</tt> if the component |
| * does not have an automation ID. |
| */ |
| public String getAutomationID() { |
| return automationID; |
| } |
| |
| /** |
| * Sets the component's automation ID. This value can be used to obtain a |
| * reference to the component via {@link Automation#get(String)} when the |
| * component is attached to a component hierarchy. |
| * |
| * @param automationID The automation ID to use for the component, or |
| * <tt>null</tt> to clear the automation ID. |
| */ |
| public void setAutomationID(String automationID) { |
| String previousAutomationID = this.automationID; |
| this.automationID = automationID; |
| |
| if (getDisplay() != null) { |
| if (previousAutomationID != null) { |
| Automation.remove(previousAutomationID); |
| } |
| |
| if (automationID != null) { |
| Automation.add(automationID, this); |
| } |
| } |
| } |
| |
| /** |
| * Set the automation ID via an enum value. |
| * |
| * @param <E> The enum type that will be used here. |
| * @param enumID The enum value to use as the automation ID for this |
| * component, or <tt>null</tt> to clear the automation ID. |
| * @see #setAutomationID(String) |
| */ |
| public <E extends Enum<E>> void setAutomationID(E enumID) { |
| setAutomationID(enumID.toString()); |
| } |
| |
| /** |
| * Returns the currently installed skin. |
| * |
| * @return The currently installed skin. |
| */ |
| protected Skin getSkin() { |
| return skin; |
| } |
| |
| /** |
| * Sets the skin, replacing any previous skin. |
| * |
| * @param skin The new skin. |
| */ |
| @SuppressWarnings("unchecked") |
| protected void setSkin(Skin skin) { |
| Utils.checkNull(skin, "skin"); |
| |
| if (this.skin != null) { |
| throw new IllegalStateException("Skin is already installed."); |
| } |
| |
| this.skin = skin; |
| styles = new BeanAdapter(skin); |
| skin.install(this); |
| |
| // Apply any defined type styles |
| LinkedList<Class<?>> styleTypes = new LinkedList<>(); |
| |
| Class<?> type = getClass(); |
| while (type != Object.class) { |
| styleTypes.insert(type, 0); |
| type = type.getSuperclass(); |
| } |
| |
| for (Class<?> styleType : styleTypes) { |
| Map<String, ?> stylesMap = typedStyles.get((Class<? extends Component>) styleType); |
| |
| if (stylesMap != null) { |
| setStyles(stylesMap); |
| } |
| } |
| |
| invalidate(); |
| repaint(); |
| } |
| |
| /** |
| * Check if the given skin is correct with respect to the necessary skin class. |
| * <p> Meant to be called from the subclass' {@link #setSkin} method. |
| * |
| * @param skin The skin object to check. |
| * @param expectedClass What the skin class should be. |
| * @throws IllegalArgumentException if the skin object doesn't implement the given skin interface. |
| */ |
| protected final void checkSkin(Skin skin, Class<?> expectedClass) { |
| if (!expectedClass.isInstance(skin)) { |
| throw new IllegalArgumentException("Skin class must implement " |
| + expectedClass.getName()); |
| } |
| } |
| |
| /** |
| * Installs the skin for the given component class, as defined by the |
| * current theme. |
| * |
| * @param componentClass Pivot component class for which to install the skin. |
| */ |
| @SuppressWarnings("unchecked") |
| protected void installSkin(Class<? extends Component> componentClass) { |
| // Walk up component hierarchy from this type; if we find a match |
| // and the super class equals the given component class, install |
| // the skin. Otherwise, ignore - it will be installed later by a |
| // subclass of the component class. |
| Class<?> type = getClass(); |
| |
| Theme theme = Theme.getTheme(); |
| Class<? extends org.apache.pivot.wtk.Skin> skinClass = theme.getSkinClass((Class<? extends Component>) type); |
| |
| while (skinClass == null && type != componentClass && type != Component.class) { |
| type = type.getSuperclass(); |
| |
| if (type != Component.class) { |
| skinClass = theme.getSkinClass((Class<? extends Component>) type); |
| } |
| } |
| |
| if (type == Component.class) { |
| throw new IllegalArgumentException(componentClass.getName() + " is not an ancestor of " |
| + getClass().getName()); |
| } |
| |
| if (skinClass == null) { |
| throw new IllegalArgumentException("No skin mapping for " + componentClass.getName() |
| + " found."); |
| } |
| |
| if (type == componentClass) { |
| try { |
| setSkin(skinClass.getDeclaredConstructor().newInstance()); |
| } catch (InstantiationException | IllegalAccessException | NoSuchMethodException |
| | InvocationTargetException exception) { |
| throw new IllegalArgumentException(exception); |
| } |
| } |
| } |
| |
| public Container getParent() { |
| return parent; |
| } |
| |
| protected void setParent(Container parent) { |
| // If this component is being removed from the component hierarchy |
| // and is currently focused, clear the focus |
| if (parent == null && isFocused()) { |
| clearFocus(); |
| } |
| |
| Container previousParent = this.parent; |
| this.parent = parent; |
| |
| if (previousParent != null) { |
| previousParent.descendantRemoved(this); |
| } |
| |
| if (parent != null) { |
| parent.descendantAdded(this); |
| } |
| |
| componentListeners.parentChanged(this, previousParent); |
| } |
| |
| public Window getWindow() { |
| return (Window) getAncestor(Window.class); |
| } |
| |
| public Display getDisplay() { |
| return (Display) getAncestor(Display.class); |
| } |
| |
| public Container getAncestor(Class<? extends Container> ancestorType) { |
| Component component = this; |
| |
| while (component != null && !(ancestorType.isInstance(component))) { |
| component = component.getParent(); |
| } |
| |
| return (Container) component; |
| } |
| |
| @SuppressWarnings("unchecked") |
| public Container getAncestor(String ancestorTypeName) throws ClassNotFoundException { |
| Utils.checkNull(ancestorTypeName, "ancestorTypeName"); |
| |
| return getAncestor((Class<? extends Container>) Class.forName(ancestorTypeName)); |
| } |
| |
| @Override |
| public int getWidth() { |
| return skin.getWidth(); |
| } |
| |
| public void setWidth(int width) { |
| setSize(width, getHeight()); |
| } |
| |
| @Override |
| public int getHeight() { |
| return skin.getHeight(); |
| } |
| |
| public void setHeight(int height) { |
| setSize(getWidth(), height); |
| } |
| |
| public Dimensions getSize() { |
| return new Dimensions(this.getWidth(), this.getHeight()); |
| } |
| |
| public final void setSize(Dimensions size) { |
| Utils.checkNull(size, "size"); |
| |
| setSize(size.width, size.height); |
| } |
| |
| /** |
| * NOTE This method should only be called during layout. Callers should use |
| * {@link #setPreferredSize(int, int)}. |
| * |
| * @param width Final computed width |
| * @param height Final computed height |
| */ |
| @Override |
| public void setSize(int width, int height) { |
| Utils.checkNonNegative(width, "width"); |
| Utils.checkNonNegative(height, "height"); |
| |
| int previousWidth = getWidth(); |
| int previousHeight = getHeight(); |
| |
| if (width != previousWidth || height != previousHeight) { |
| // This component'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 component |
| if (parent != null) { |
| parent.repaint(getDecoratedBounds()); |
| } |
| |
| // Set the size of the skin |
| skin.setSize(width, height); |
| |
| // Redraw the region currently occupied by this component |
| if (parent != null) { |
| parent.repaint(getDecoratedBounds()); |
| } |
| |
| componentListeners.sizeChanged(this, previousWidth, previousHeight); |
| } |
| } |
| |
| /** |
| * Returns the component's unconstrained preferred width. |
| * @return The unconstrained preferred width. |
| */ |
| public int getPreferredWidth() { |
| return getPreferredWidth(-1); |
| } |
| |
| /** |
| * Returns the component's constrained preferred width. |
| * |
| * @param height The height value by which the preferred width should be |
| * constrained, or <tt>-1</tt> for no constraint. |
| * @return The constrained preferred width. |
| */ |
| @Override |
| public int getPreferredWidth(int height) { |
| int preferredWidthLocal; |
| |
| if (this.preferredWidth == -1) { |
| if (height == -1) { |
| preferredWidthLocal = getPreferredSize().width; |
| } else { |
| if (preferredSize != null && preferredSize.height == height) { |
| preferredWidthLocal = preferredSize.width; |
| } else { |
| Limits widthLimits = getWidthLimits(); |
| preferredWidthLocal = widthLimits.constrain(skin.getPreferredWidth(height)); |
| } |
| } |
| } else { |
| preferredWidthLocal = this.preferredWidth; |
| } |
| |
| return preferredWidthLocal; |
| } |
| |
| /** |
| * Sets the component's preferred width. |
| * |
| * @param preferredWidth The preferred width value, or <tt>-1</tt> to use |
| * the default value determined by the skin. |
| */ |
| public void setPreferredWidth(int preferredWidth) { |
| setPreferredSize(preferredWidth, preferredHeight); |
| } |
| |
| /** |
| * Returns a flag indicating whether the preferred width was explicitly set |
| * by the caller or is the default value determined by the skin. |
| * |
| * @return <tt>true</tt> if the preferred width was explicitly set; |
| * <tt>false</tt>, otherwise. |
| */ |
| public boolean isPreferredWidthSet() { |
| return (preferredWidth != -1); |
| } |
| |
| /** |
| * Returns the component's unconstrained preferred height. |
| * @return The unconstrained preferred height. |
| */ |
| public int getPreferredHeight() { |
| return getPreferredHeight(-1); |
| } |
| |
| /** |
| * Returns the component's constrained preferred height. |
| * |
| * @param width The width value by which the preferred height should be |
| * constrained, or <tt>-1</tt> for no constraint. |
| * @return The constrained preferred height. |
| */ |
| @Override |
| public int getPreferredHeight(int width) { |
| int preferredHeightLocal; |
| |
| if (this.preferredHeight == -1) { |
| if (width == -1) { |
| preferredHeightLocal = getPreferredSize().height; |
| } else { |
| if (preferredSize != null && preferredSize.width == width) { |
| preferredHeightLocal = preferredSize.height; |
| } else { |
| Limits heightLimits = getHeightLimits(); |
| preferredHeightLocal = heightLimits.constrain(skin.getPreferredHeight(width)); |
| } |
| } |
| } else { |
| preferredHeightLocal = this.preferredHeight; |
| } |
| |
| return preferredHeightLocal; |
| } |
| |
| /** |
| * Sets the component's preferred height. |
| * |
| * @param preferredHeight The preferred height value, or <tt>-1</tt> to use |
| * the default value determined by the skin. |
| */ |
| public void setPreferredHeight(int preferredHeight) { |
| setPreferredSize(preferredWidth, preferredHeight); |
| } |
| |
| /** |
| * Returns a flag indicating whether the preferred height was explicitly set |
| * by the caller or is the default value determined by the skin. |
| * |
| * @return <tt>true</tt> if the preferred height was explicitly set; |
| * <tt>false</tt>, otherwise. |
| */ |
| public boolean isPreferredHeightSet() { |
| return (preferredHeight != -1); |
| } |
| |
| /** |
| * Gets the component's unconstrained preferred size. |
| */ |
| @Override |
| public Dimensions getPreferredSize() { |
| if (preferredSize == null) { |
| Dimensions preferredSizeLocal; |
| if (preferredWidth == -1 && preferredHeight == -1) { |
| preferredSizeLocal = skin.getPreferredSize(); |
| } else if (preferredWidth == -1) { |
| preferredSizeLocal = new Dimensions(skin.getPreferredWidth(preferredHeight), |
| preferredHeight); |
| } else if (preferredHeight == -1) { |
| preferredSizeLocal = new Dimensions(preferredWidth, |
| skin.getPreferredHeight(preferredWidth)); |
| } else { |
| preferredSizeLocal = new Dimensions(preferredWidth, preferredHeight); |
| } |
| |
| Limits widthLimits = getWidthLimits(); |
| Limits heightLimits = getHeightLimits(); |
| |
| int preferredWidthLocal = widthLimits.constrain(preferredSizeLocal.width); |
| int preferredHeightLocal = heightLimits.constrain(preferredSizeLocal.height); |
| |
| if (preferredSizeLocal.width > preferredWidthLocal) { |
| preferredHeightLocal = heightLimits.constrain(skin.getPreferredHeight(preferredWidthLocal)); |
| } |
| |
| if (preferredSizeLocal.height > preferredHeightLocal) { |
| preferredWidthLocal = widthLimits.constrain(skin.getPreferredWidth(preferredHeightLocal)); |
| } |
| |
| this.preferredSize = new Dimensions(preferredWidthLocal, preferredHeightLocal); |
| } |
| |
| return preferredSize; |
| } |
| |
| public final void setPreferredSize(Dimensions preferredSize) { |
| Utils.checkNull(preferredSize, "preferredSize"); |
| |
| setPreferredSize(preferredSize.width, preferredSize.height); |
| } |
| |
| /** |
| * Sets the component's preferred size. |
| * |
| * @param preferredWidth The preferred width value, or <tt>-1</tt> to use |
| * the default value determined by the skin. |
| * @param preferredHeight The preferred height value, or <tt>-1</tt> to use |
| * the default value determined by the skin. |
| */ |
| public void setPreferredSize(int preferredWidth, int preferredHeight) { |
| if (preferredWidth < -1) { |
| throw new IllegalArgumentException(preferredWidth |
| + " is not a valid value for preferredWidth."); |
| } |
| |
| if (preferredHeight < -1) { |
| throw new IllegalArgumentException(preferredHeight |
| + " is not a valid value for preferredHeight."); |
| } |
| |
| int previousPreferredWidth = this.preferredWidth; |
| int previousPreferredHeight = this.preferredHeight; |
| |
| if (previousPreferredWidth != preferredWidth || previousPreferredHeight != preferredHeight) { |
| this.preferredWidth = preferredWidth; |
| this.preferredHeight = preferredHeight; |
| |
| invalidate(); |
| |
| componentListeners.preferredSizeChanged(this, previousPreferredWidth, |
| previousPreferredHeight); |
| } |
| } |
| |
| /** |
| * Returns a flag indicating whether the preferred size was explicitly set |
| * by the caller or is the default value determined by the skin. |
| * |
| * @return <tt>true</tt> if the preferred size was explicitly set; |
| * <tt>false</tt>, otherwise. |
| */ |
| public boolean isPreferredSizeSet() { |
| return isPreferredWidthSet() && isPreferredHeightSet(); |
| } |
| |
| /** |
| * Returns the minimum width of this component. |
| * @return The given minimum width of this component. |
| */ |
| public int getMinimumWidth() { |
| return minimumWidth; |
| } |
| |
| /** |
| * Sets the minimum width of this component. |
| * |
| * @param minimumWidth The new minimum width for this component. |
| */ |
| public void setMinimumWidth(int minimumWidth) { |
| setWidthLimits(minimumWidth, getMaximumWidth()); |
| } |
| |
| /** |
| * Returns the maximum width of this component. |
| * @return The given maximum width of this component. |
| */ |
| public int getMaximumWidth() { |
| return maximumWidth; |
| } |
| |
| /** |
| * Sets the maximum width of this component. |
| * |
| * @param maximumWidth The new maximum width of this component. |
| */ |
| public void setMaximumWidth(int maximumWidth) { |
| setWidthLimits(getMinimumWidth(), maximumWidth); |
| } |
| |
| /** |
| * Returns the width limits for this component. |
| * @return The current width limits (min and max). |
| */ |
| public Limits getWidthLimits() { |
| return new Limits(minimumWidth, maximumWidth); |
| } |
| |
| /** |
| * Sets the width limits for this component. |
| * |
| * @param minimumWidth The new minimum width. |
| * @param maximumWidth The new maximum width. |
| */ |
| public void setWidthLimits(int minimumWidth, int maximumWidth) { |
| int previousMinimumWidth = this.minimumWidth; |
| int previousMaximumWidth = this.maximumWidth; |
| |
| if (previousMinimumWidth != minimumWidth || previousMaximumWidth != maximumWidth) { |
| Utils.checkNonNegative(minimumWidth, "minimumWidth"); |
| |
| if (minimumWidth > maximumWidth) { |
| throw new IllegalArgumentException("minimumWidth is greater than maximumWidth."); |
| } |
| |
| this.minimumWidth = minimumWidth; |
| this.maximumWidth = maximumWidth; |
| |
| invalidate(); |
| |
| componentListeners.widthLimitsChanged(this, previousMinimumWidth, previousMaximumWidth); |
| } |
| } |
| |
| /** |
| * Sets the width limits for this component. |
| * |
| * @param widthLimits The new width limits (min and max). |
| */ |
| public final void setWidthLimits(Limits widthLimits) { |
| Utils.checkNull(widthLimits, "widthLimits"); |
| |
| setWidthLimits(widthLimits.minimum, widthLimits.maximum); |
| } |
| |
| /** |
| * Returns the minimum height of this component. |
| * @return The given minimum height of this component. |
| */ |
| public int getMinimumHeight() { |
| return minimumHeight; |
| } |
| |
| /** |
| * Sets the minimum height of this component. |
| * |
| * @param minimumHeight The new minimum height. |
| */ |
| public void setMinimumHeight(int minimumHeight) { |
| setHeightLimits(minimumHeight, getMaximumHeight()); |
| } |
| |
| /** |
| * Returns the maximum height of this component. |
| * @return The given maximum height of this component. |
| */ |
| public int getMaximumHeight() { |
| return maximumHeight; |
| } |
| |
| /** |
| * Sets the maximum height of this component. |
| * |
| * @param maximumHeight The new maximum height. |
| */ |
| public void setMaximumHeight(int maximumHeight) { |
| setHeightLimits(getMinimumHeight(), maximumHeight); |
| } |
| |
| /** |
| * Returns the height limits for this component. |
| * @return The current height limits (min and max). |
| */ |
| public Limits getHeightLimits() { |
| return new Limits(minimumHeight, maximumHeight); |
| } |
| |
| /** |
| * Sets the height limits for this component. |
| * |
| * @param minimumHeight The new minimum height. |
| * @param maximumHeight The new maximum height. |
| */ |
| public void setHeightLimits(int minimumHeight, int maximumHeight) { |
| int previousMinimumHeight = this.minimumHeight; |
| int previousMaximumHeight = this.maximumHeight; |
| |
| if (previousMinimumHeight != minimumHeight || previousMaximumHeight != maximumHeight) { |
| Utils.checkNonNegative(minimumHeight, "minimumHeight"); |
| |
| if (minimumHeight > maximumHeight) { |
| throw new IllegalArgumentException("minimumHeight is greater than maximumHeight."); |
| } |
| |
| this.minimumHeight = minimumHeight; |
| this.maximumHeight = maximumHeight; |
| |
| invalidate(); |
| |
| componentListeners.heightLimitsChanged(this, previousMinimumHeight, |
| previousMaximumHeight); |
| } |
| } |
| |
| /** |
| * Sets the height limits for this component. |
| * |
| * @param heightLimits The new height limits (min and max). |
| */ |
| public final void setHeightLimits(Limits heightLimits) { |
| Utils.checkNull(heightLimits, "heightLimits"); |
| |
| setHeightLimits(heightLimits.minimum, heightLimits.maximum); |
| } |
| |
| /** |
| * Returns the component's x-coordinate. |
| * |
| * @return The component's horizontal position relative to the origin of the |
| * parent container. |
| */ |
| public int getX() { |
| return x; |
| } |
| |
| /** |
| * Sets the component's x-coordinate. |
| * |
| * @param x The component's horizontal position relative to the origin of |
| * the parent container. |
| */ |
| public void setX(int x) { |
| setLocation(x, getY()); |
| } |
| |
| /** |
| * Returns the component's y-coordinate. |
| * |
| * @return The component's vertical position relative to the origin of the |
| * parent container. |
| */ |
| public int getY() { |
| return y; |
| } |
| |
| /** |
| * Sets the component's y-coordinate. |
| * |
| * @param y The component's vertical position relative to the origin of the |
| * parent container. |
| */ |
| public void setY(int y) { |
| setLocation(getX(), y); |
| } |
| |
| /** |
| * Returns the component's location. |
| * |
| * @return A point value containing the component's horizontal and vertical |
| * position relative to the origin of the parent container. |
| */ |
| public Point getLocation() { |
| return new Point(getX(), getY()); |
| } |
| |
| /** |
| * Sets the component's location. NOTE This method should only be called |
| * when performing layout. However, since some containers do not reposition |
| * components during layout, it is valid for callers to invoke this method |
| * directly when such containers. |
| * |
| * @param x The component's horizontal position relative to the origin of |
| * the parent container. |
| * @param y The component's vertical position relative to the origin of the |
| * parent container. |
| */ |
| 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 component |
| if (parent != null) { |
| parent.repaint(getDecoratedBounds()); |
| } |
| |
| // Set the new coordinates |
| this.x = x; |
| this.y = y; |
| |
| // Redraw the region currently occupied by this component |
| if (parent != null) { |
| parent.repaint(getDecoratedBounds()); |
| } |
| |
| componentListeners.locationChanged(this, previousX, previousY); |
| } |
| } |
| |
| /** |
| * Sets the component's location. |
| * |
| * @param location A point value containing the component's horizontal and |
| * vertical position relative to the origin of the parent container. |
| * @see #setLocation(int, int) |
| */ |
| public final void setLocation(Point location) { |
| Utils.checkNull(location, "location"); |
| |
| setLocation(location.x, location.y); |
| } |
| |
| /** |
| * Returns the component's baseline. |
| * |
| * @return The baseline relative to the origin of this component, or |
| * <tt>-1</tt> if this component does not have a baseline. |
| */ |
| @Override |
| public int getBaseline() { |
| if (baseline == -1) { |
| baseline = skin.getBaseline(); |
| } |
| |
| return baseline; |
| } |
| |
| /** |
| * Returns the component's baseline for a given width and height. |
| * |
| * @return The baseline relative to the origin of this component, or |
| * <tt>-1</tt> if this component does not have a baseline. |
| */ |
| @Override |
| public int getBaseline(int width, int height) { |
| return skin.getBaseline(width, height); |
| } |
| |
| /** |
| * Returns the component's bounding area. |
| * |
| * @return The component's bounding area. The <tt>x</tt> and <tt>y</tt> |
| * values are relative to the parent container. |
| */ |
| public Bounds getBounds() { |
| return new Bounds(x, y, getWidth(), getHeight()); |
| } |
| |
| /** |
| * Returns the component's bounding area including decorators. |
| * |
| * @return The decorated bounding area. The <tt>x</tt> and <tt>y</tt> values |
| * are relative to the parent container. |
| */ |
| public Bounds getDecoratedBounds() { |
| Bounds decoratedBounds = new Bounds(0, 0, getWidth(), getHeight()); |
| |
| for (Decorator decorator : decorators) { |
| decoratedBounds = decoratedBounds.union(decorator.getBounds(this)); |
| } |
| |
| return new Bounds(decoratedBounds.x + x, decoratedBounds.y + y, |
| decoratedBounds.width, decoratedBounds.height); |
| } |
| |
| /** |
| * Returns the component's bounding area in screen coordinates. |
| * <p> The result is the result of {@link #getBounds} offset by all the |
| * parent containers of this component, and offset by the application's |
| * {@link Display} on the screen. |
| * |
| * @return The component's bounding area relative to the entire screen. |
| */ |
| public Bounds getScreenBounds() { |
| Display display = getDisplay(); |
| |
| Point displayLocation = mapPointToAncestor(display, 0, 0); |
| ApplicationContext.DisplayHost displayHost = display.getDisplayHost(); |
| java.awt.Point hostLocation = display.getHostWindow().getLocationOnScreen(); |
| |
| return new Bounds(displayLocation.x + displayHost.getX() + hostLocation.x, |
| displayLocation.y + displayHost.getY() + hostLocation.y, |
| getWidth(), getHeight()); |
| } |
| |
| /** |
| * Convert and return a new {@link Rectangle} from component-relative |
| * coordinates to screen-relative. Uses the {@link #getScreenBounds} |
| * method to accomplish the mapping. |
| * |
| * @param clientRectangle A rectangle in component-relative coordinates. |
| * @return A new object in screen-relative coordinates. |
| */ |
| public Rectangle offsetToScreen(Rectangle clientRectangle) { |
| Bounds screenBounds = getScreenBounds(); |
| return new Rectangle(clientRectangle.x + screenBounds.x, |
| clientRectangle.y + screenBounds.y, |
| clientRectangle.width, clientRectangle.height); |
| } |
| |
| /** |
| * Determines if the component contains a given location. This method |
| * facilitates mouse interaction with non-rectangular components. |
| * |
| * @param xValue Horizontal location to check. |
| * @param yValue Vertical location to check. |
| * @return <tt>true</tt> if the component's shape contains the given |
| * location; <tt>false</tt>, otherwise. |
| * @throws UnsupportedOperationException This method is not currently |
| * implemented. |
| */ |
| @UnsupportedOperation |
| public boolean contains(int xValue, int yValue) { |
| // TODO |
| throw new UnsupportedOperationException(); |
| } |
| |
| /** |
| * Returns the component's visibility. |
| * |
| * @return <tt>true</tt> if the component will be painted; <tt>false</tt>, |
| * otherwise. |
| */ |
| public boolean isVisible() { |
| return visible; |
| } |
| |
| /** |
| * Sets the component's visibility. |
| * |
| * @param visible <tt>true</tt> if the component should be painted; |
| * <tt>false</tt>, otherwise. |
| */ |
| public void setVisible(boolean visible) { |
| if (this.visible != visible) { |
| // If this component 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()) { |
| mouseOut(); |
| } |
| } |
| |
| // Redraw the region formerly occupied by this component |
| if (parent != null) { |
| parent.repaint(getDecoratedBounds()); |
| } |
| |
| this.visible = visible; |
| |
| // Redraw the region currently occupied by this component |
| if (parent != null) { |
| parent.repaint(getDecoratedBounds()); |
| } |
| |
| // Ensure the layout is valid |
| if (visible && !valid) { |
| validate(); |
| } |
| |
| // Invalidate the parent |
| if (parent != null) { |
| parent.invalidate(); |
| } |
| |
| componentListeners.visibleChanged(this); |
| } |
| } |
| |
| /** |
| * Returns the component's decorator sequence. |
| * |
| * @return The component's decorator sequence |
| */ |
| public DecoratorSequence getDecorators() { |
| return decoratorSequence; |
| } |
| |
| /** |
| * Maps a point in this component's coordinate system to the specified |
| * ancestor's coordinate space. |
| * |
| * @param ancestor The ancestor container of this component. |
| * @param xValue The x-coordinate in this component's coordinate space. |
| * @param yValue The y-coordinate in this component's coordinate space. |
| * @return A point containing the translated coordinates, or <tt>null</tt> if |
| * the component is not a descendant of the specified ancestor. |
| */ |
| public Point mapPointToAncestor(final Container ancestor, int xValue, int yValue) { |
| Utils.checkNull(ancestor, "ancestor"); |
| |
| int xLocation = xValue; |
| int yLocation = yValue; |
| |
| Point coordinates = null; |
| |
| for (Component component = this; component != null; component = component.getParent()) { |
| if (component == ancestor) { |
| coordinates = new Point(xLocation, yLocation); |
| break; |
| } else { |
| xLocation += component.x; |
| yLocation += component.y; |
| } |
| } |
| |
| return coordinates; |
| } |
| |
| /** |
| * Maps a point in this component's coordinate system to the specified |
| * ancestor's coordinate space. |
| * |
| * @param ancestor The ancestor container of this component. |
| * @param location The coordinates in this component's coordinate space. |
| * @return A point containing the translated coordinates, or <tt>null</tt> if |
| * the component is not a descendant of the specified ancestor. |
| */ |
| public Point mapPointToAncestor(Container ancestor, Point location) { |
| Utils.checkNull(location, "location"); |
| |
| return mapPointToAncestor(ancestor, location.x, location.y); |
| } |
| |
| /** |
| * Maps a point in the specified ancestor's coordinate space to this |
| * component's coordinate system. |
| * |
| * @param ancestor The ancestor container of this component. |
| * @param xValue The x-coordinate in the ancestors's coordinate space. |
| * @param yValue The y-coordinate in the ancestor's coordinate space. |
| * @return A point containing the translated coordinates, or <tt>null</tt> if |
| * the component is not a descendant of the specified ancestor. |
| */ |
| public Point mapPointFromAncestor(final Container ancestor, int xValue, int yValue) { |
| Utils.checkNull(ancestor, "ancestor"); |
| |
| int xLocation = xValue; |
| int yLocation = yValue; |
| |
| Point coordinates = null; |
| |
| for (Component component = this; component != null; component = component.getParent()) { |
| if (component == ancestor) { |
| coordinates = new Point(xLocation, yLocation); |
| break; |
| } else { |
| xLocation -= component.x; |
| yLocation -= component.y; |
| } |
| } |
| |
| return coordinates; |
| } |
| |
| public Point mapPointFromAncestor(Container ancestor, Point location) { |
| Utils.checkNull(location, "location"); |
| |
| return mapPointFromAncestor(ancestor, location.x, location.y); |
| } |
| |
| /** |
| * Determines if this component is showing. To be showing, the component and |
| * all of its ancestors must be visible and attached to a display. |
| * |
| * @return <tt>true</tt> if this component is showing; <tt>false</tt> |
| * otherwise. |
| */ |
| public boolean isShowing() { |
| Component component = this; |
| |
| while (component != null && component.isVisible() && !(component instanceof Display)) { |
| component = component.getParent(); |
| } |
| |
| return (component != null && component.isVisible()); |
| } |
| |
| /** |
| * Determines the visible area of a component. The visible area is defined |
| * as the intersection of the component's area with the visible area of its |
| * ancestors, or, in the case of a Viewport, the viewport bounds. |
| * |
| * @return The visible area of the component in the component's coordinate |
| * space, or <tt>null</tt> if the component is either not showing or not |
| * part of the component hierarchy. |
| */ |
| public Bounds getVisibleArea() { |
| return getVisibleArea(0, 0, getWidth(), getHeight()); |
| } |
| |
| /** |
| * Determines the visible portion of a given area. The visible area is defined |
| * as the intersection of the component's area with the visible area of its |
| * ancestors, or, in the case of a Viewport, the viewport bounds. |
| * |
| * @param area The area to check its visibility. |
| * @return The visible part of the given area in the component's coordinate |
| * space, or <tt>null</tt> if the component is either not showing or not |
| * part of the component hierarchy. |
| */ |
| public Bounds getVisibleArea(Bounds area) { |
| Utils.checkNull(area, "area"); |
| |
| return getVisibleArea(area.x, area.y, area.width, area.height); |
| } |
| |
| /** |
| * Determines the visible area of the given rectangle. The visible area is defined |
| * as the intersection of the component's area with the visible area of its |
| * ancestors, or, in the case of a Viewport, the viewport bounds. |
| * |
| * @param xValue The x-coordinate of the area. |
| * @param yValue The y-coordinate of the area. |
| * @param width The width of the area. |
| * @param height The height of the area. |
| * @return The visible part of the given area in the component's coordinate |
| * space, or <tt>null</tt> if the component is either not showing or not |
| * part of the component hierarchy. |
| */ |
| public Bounds getVisibleArea(int xValue, int yValue, int width, int height) { |
| Bounds visibleArea = null; |
| |
| Component component = this; |
| |
| int top = yValue; |
| int left = xValue; |
| int bottom = yValue + height - 1; |
| int right = xValue + width - 1; |
| |
| int xOffset = 0; |
| int yOffset = 0; |
| |
| while (component != null && component.isVisible()) { |
| int minTop = 0; |
| int minLeft = 0; |
| int maxBottom = component.getHeight() - 1; |
| int maxRight = component.getWidth() - 1; |
| |
| if (component instanceof Viewport) { |
| Viewport viewport = (Viewport) component; |
| Bounds bounds = viewport.getViewportBounds(); |
| minTop = bounds.y; |
| minLeft = bounds.x; |
| maxBottom = bounds.y + bounds.height - 1; |
| maxRight = bounds.x + bounds.width - 1; |
| } |
| |
| top = component.y + Math.max(top, minTop); |
| left = component.x + Math.max(left, minLeft); |
| bottom = component.y + Math.max(Math.min(bottom, maxBottom), -1); |
| right = component.x + Math.max(Math.min(right, maxRight), -1); |
| |
| xOffset += component.x; |
| yOffset += component.y; |
| |
| if (component instanceof Display) { |
| visibleArea = new Bounds(left - xOffset, top - yOffset, |
| right - left + 1, bottom - top + 1); |
| } |
| |
| component = component.getParent(); |
| } |
| |
| return visibleArea; |
| } |
| |
| /** |
| * Ensures that the given area of a component is visible within the |
| * viewports of all applicable ancestors. |
| * |
| * @param area The area to be made visible. |
| */ |
| public void scrollAreaToVisible(Bounds area) { |
| Utils.checkNull(area, "area"); |
| |
| scrollAreaToVisible(area.x, area.y, area.width, area.height); |
| } |
| |
| /** |
| * Ensures that the given area of a component is visible within the |
| * viewports of all applicable ancestors. |
| * |
| * @param xValue The x-coordinate of the area to be made visible. |
| * @param yValue The y-coordinate of the area. |
| * @param width The width of the area to be shown. |
| * @param height The height of the area. |
| */ |
| public void scrollAreaToVisible(int xValue, int yValue, int width, int height) { |
| int xLocation = xValue; |
| int yLocation = yValue; |
| int widthValue = width; |
| int heightValue = height; |
| |
| Component component = this; |
| |
| while (component != null) { |
| if (component instanceof Viewport) { |
| Viewport viewport = (Viewport) component; |
| Component view = viewport.getView(); |
| |
| try { |
| Bounds viewportBounds = viewport.getViewportBounds(); |
| |
| int deltaX = 0; |
| |
| int leftDisplacement = xLocation - viewportBounds.x; |
| int rightDisplacement = (xLocation + widthValue) |
| - (viewportBounds.x + viewportBounds.width); |
| |
| if ((leftDisplacement & rightDisplacement) < 0) { |
| // Both leftDisplacement and rightDisplacement are |
| // negative; the area lies to the left of our viewport |
| // bounds |
| deltaX = Math.max(leftDisplacement, rightDisplacement); |
| } else if ((leftDisplacement | rightDisplacement) > 0) { |
| // Both leftDisplacement and rightDisplacement are |
| // positive; the area lies to the right of our viewport |
| // bounds |
| deltaX = Math.min(leftDisplacement, rightDisplacement); |
| } |
| |
| if (deltaX != 0) { |
| int viewWidth = (view == null) ? 0 : view.getWidth(); |
| int scrollLeft = viewport.getScrollLeft(); |
| scrollLeft = Math.min(Math.max(scrollLeft + deltaX, 0), |
| Math.max(viewWidth - viewportBounds.width, 0)); |
| viewport.setScrollLeft(scrollLeft); |
| |
| xLocation -= deltaX; |
| } |
| |
| xLocation = Math.max(xLocation, viewportBounds.x); |
| widthValue = Math.min(widthValue, |
| Math.max(viewportBounds.width - (xLocation - viewportBounds.x), 0)); |
| |
| int deltaY = 0; |
| |
| int topDisplacement = yLocation - viewportBounds.y; |
| int bottomDisplacement = (yLocation + heightValue) |
| - (viewportBounds.y + viewportBounds.height); |
| |
| if ((topDisplacement & bottomDisplacement) < 0) { |
| // Both topDisplacement and bottomDisplacement are |
| // negative; the area lies above our viewport bounds |
| deltaY = Math.max(topDisplacement, bottomDisplacement); |
| } else if ((topDisplacement | bottomDisplacement) > 0) { |
| // Both topDisplacement and bottomDisplacement are |
| // positive; the area lies below our viewport bounds |
| deltaY = Math.min(topDisplacement, bottomDisplacement); |
| } |
| |
| if (deltaY != 0) { |
| int viewHeight = (view == null) ? 0 : view.getHeight(); |
| int scrollTop = viewport.getScrollTop(); |
| scrollTop = Math.min(Math.max(scrollTop + deltaY, 0), |
| Math.max(viewHeight - viewportBounds.height, 0)); |
| viewport.setScrollTop(scrollTop); |
| |
| yLocation -= deltaY; |
| } |
| |
| yLocation = Math.max(yLocation, viewportBounds.y); |
| heightValue = Math.min(heightValue, |
| Math.max(viewportBounds.height - (yLocation - viewportBounds.y), 0)); |
| } catch (UnsupportedOperationException ex) { |
| // If the viewport doesn't support getting the viewport |
| // bounds, we simply act as we would have had the viewport |
| // been any other type of component; namely, we do nothing |
| // and proceed to its parent |
| } |
| } |
| |
| xLocation += component.x; |
| yLocation += component.y; |
| |
| component = component.getParent(); |
| } |
| } |
| |
| /** |
| * Returns the component's valid state. |
| * @return Whether or not the component is valid. |
| */ |
| public boolean isValid() { |
| return valid; |
| } |
| |
| /** |
| * Flags the component's hierarchy as invalid, and clears any cached |
| * preferred size. |
| */ |
| public void invalidate() { |
| Container.assertEventDispatchThread(this); |
| valid = false; |
| |
| // Clear the preferred size and baseline |
| preferredSize = null; |
| baseline = -1; |
| |
| if (parent != null) { |
| parent.invalidate(); |
| } |
| } |
| |
| /** |
| * Lays out the component by calling {@link Skin#layout()}. |
| */ |
| public void validate() { |
| if (!valid && visible) { |
| layout(); |
| valid = true; |
| } |
| } |
| |
| /** |
| * Called to lay out the component. |
| */ |
| protected void layout() { |
| skin.layout(); |
| } |
| |
| /** |
| * Flags the entire component as needing to be repainted. |
| */ |
| public final void repaint() { |
| repaint(false); |
| } |
| |
| /** |
| * Flags the entire component as needing to be repainted. |
| * |
| * @param immediate Whether to repaint immediately. |
| */ |
| public final void repaint(boolean immediate) { |
| repaint(0, 0, getWidth(), getHeight(), immediate); |
| } |
| |
| /** |
| * Flags an area as needing to be repainted. |
| * |
| * @param area The area that needs to be repainted. |
| */ |
| public final void repaint(Bounds area) { |
| repaint(area, false); |
| } |
| |
| /** |
| * Flags an area as needing to be repainted or repaints the rectangle |
| * immediately. |
| * |
| * @param area The area to be repainted. |
| * @param immediate Whether or not the area needs immediate painting. |
| */ |
| public final void repaint(Bounds area, boolean immediate) { |
| Utils.checkNull(area, "area"); |
| |
| repaint(area.x, area.y, area.width, area.height, immediate); |
| } |
| |
| /** |
| * Flags an area as needing to be repainted. |
| * |
| * @param xValue Starting x-coordinate of area to paint. |
| * @param yValue Starting y-coordinate. |
| * @param width Width of area to repaint. |
| * @param height Height of the area. |
| */ |
| public final void repaint(int xValue, int yValue, int width, int height) { |
| repaint(xValue, yValue, width, height, false); |
| } |
| |
| /** |
| * Flags an area as needing to be repainted. |
| * |
| * @param xValue Starting x-coordinate of area to repaint. |
| * @param yValue Starting y-coordinate. |
| * @param width Width of area to repaint. |
| * @param height Height of area. |
| * @param immediate Whether repaint should be done immediately. |
| */ |
| public void repaint(int xValue, int yValue, int width, int height, boolean immediate) { |
| Container.assertEventDispatchThread(this); |
| if (parent != null) { |
| int xLocation = xValue; |
| int yLocation = yValue; |
| int widthValue = width; |
| int heightValue = height; |
| |
| // Constrain the repaint area to this component's bounds |
| int top = yLocation; |
| int left = xLocation; |
| int bottom = top + heightValue - 1; |
| int right = left + widthValue - 1; |
| |
| xLocation = Math.max(left, 0); |
| yLocation = Math.max(top, 0); |
| widthValue = Math.min(right, getWidth() - 1) - xLocation + 1; |
| heightValue = Math.min(bottom, getHeight() - 1) - yLocation + 1; |
| |
| if (widthValue > 0 && heightValue > 0) { |
| // Notify the parent that the region needs updating |
| parent.repaint(xLocation + this.x, yLocation + this.y, |
| widthValue, heightValue, immediate); |
| |
| // Repaint any affected decorators |
| for (Decorator decorator : decorators) { |
| AffineTransform transform = decorator.getTransform(this); |
| |
| if (!transform.isIdentity()) { |
| // Apply the decorator's transform to the repaint area |
| Rectangle area = new Rectangle(xLocation, yLocation, |
| widthValue, heightValue); |
| Shape transformedShape = transform.createTransformedShape(area); |
| Bounds tranformedBounds = new Bounds(transformedShape.getBounds()); |
| |
| // Limit the transformed area to the decorator's bounds |
| tranformedBounds = tranformedBounds.intersect(decorator.getBounds(this)); |
| |
| // Add the bounded area to the repaint region |
| parent.repaint(tranformedBounds.x + this.x, tranformedBounds.y + this.y, |
| tranformedBounds.width, tranformedBounds.height, immediate); |
| } |
| } |
| } |
| } |
| } |
| |
| /** |
| * Paints the component. Delegates to the skin. |
| * @param graphics The graphics context to paint into. |
| */ |
| @Override |
| public void paint(Graphics2D graphics) { |
| skin.paint(graphics); |
| } |
| |
| /** |
| * Creates a graphics context for this component. 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 component, or <tt>null</tt> if this |
| * component is not showing. |
| * @see #isShowing() |
| */ |
| public Graphics2D getGraphics() { |
| Graphics2D graphics = null; |
| |
| int xValue = 0; |
| int yValue = 0; |
| |
| Component component = this; |
| |
| while (component != null && component.isVisible() && !(component instanceof Display)) { |
| xValue += component.x; |
| yValue += component.y; |
| |
| component = component.getParent(); |
| } |
| |
| if (component != null && component.isVisible()) { |
| Display display = (Display) component; |
| graphics = (Graphics2D) display.getDisplayHost().getGraphics(); |
| |
| double scale = display.getDisplayHost().getScale(); |
| if (scale != 1) { |
| graphics.scale(scale, scale); |
| } |
| |
| graphics.translate(xValue, yValue); |
| graphics.clipRect(0, 0, getWidth(), getHeight()); |
| } |
| |
| return graphics; |
| } |
| |
| /** |
| * Returns the component's enabled state. |
| * |
| * @return <tt>true</tt> if the component is enabled; <tt>false</tt>, |
| * otherwise. |
| */ |
| public boolean isEnabled() { |
| return enabled; |
| } |
| |
| /** |
| * Sets the component's enabled state. Enabled components respond to user |
| * input events; disabled components do not. |
| * |
| * @param enabled <tt>true</tt> if the component is enabled; <tt>false</tt>, |
| * otherwise. |
| */ |
| public void setEnabled(boolean enabled) { |
| if (this.enabled != enabled) { |
| if (!enabled) { |
| // If this component has the focus, clear it |
| if (isFocused()) { |
| clearFocus(); |
| } |
| |
| // Ensure that the mouse out event is processed |
| if (isMouseOver()) { |
| mouseOut(); |
| } |
| } |
| |
| this.enabled = enabled; |
| |
| componentStateListeners.enabledChanged(this); |
| } |
| } |
| |
| /** |
| * Determines if this component is blocked. A component is blocked if the |
| * component or any of its ancestors is disabled. |
| * |
| * @return <tt>true</tt> if the component is blocked; <tt>false</tt>, |
| * otherwise. |
| */ |
| public boolean isBlocked() { |
| boolean blocked = false; |
| |
| Component component = this; |
| |
| while (component != null && !blocked) { |
| blocked = !component.isEnabled(); |
| component = component.getParent(); |
| } |
| |
| return blocked; |
| } |
| |
| /** |
| * Determines if the mouse is positioned over this component. |
| * |
| * @return <tt>true</tt> if the mouse is currently located over this |
| * component; <tt>false</tt>, otherwise. |
| */ |
| public boolean isMouseOver() { |
| return (mouseLocation != null); |
| } |
| |
| /** |
| * Returns the current mouse location in the component's coordinate space. |
| * |
| * @return The current mouse location, or <tt>null</tt> if the mouse is not |
| * currently positioned over this component. |
| */ |
| public Point getMouseLocation() { |
| return mouseLocation; |
| } |
| |
| /** |
| * Returns the cursor that is displayed when the mouse pointer is over this |
| * component. |
| * |
| * @return The cursor that is displayed over the component. |
| */ |
| public Cursor getCursor() { |
| return cursor; |
| } |
| |
| /** |
| * Sets the cursor that is displayed when the mouse pointer is over this |
| * component. |
| * |
| * @param cursor The cursor to display over the component, or <tt>null</tt> |
| * to inherit the cursor of the parent container. |
| */ |
| public void setCursor(Cursor cursor) { |
| Cursor previousCursor = this.cursor; |
| |
| if (previousCursor != cursor) { |
| this.cursor = cursor; |
| |
| if (isMouseOver()) { |
| Mouse.setCursor(this); |
| } |
| |
| componentListeners.cursorChanged(this, previousCursor); |
| } |
| } |
| |
| /** |
| * Returns the component's tooltip text. |
| * |
| * @return The component's tooltip text, or <tt>null</tt> if no tooltip is |
| * specified. |
| */ |
| public String getTooltipText() { |
| return tooltipText; |
| } |
| |
| /** |
| * Sets the component's tooltip text. |
| * |
| * @param tooltipText The component's tooltip text, or <tt>null</tt> for no |
| * tooltip. |
| */ |
| public void setTooltipText(String tooltipText) { |
| String previousTooltipText = this.tooltipText; |
| |
| if (previousTooltipText != tooltipText) { |
| this.tooltipText = tooltipText; |
| componentListeners.tooltipTextChanged(this, previousTooltipText); |
| } |
| } |
| |
| /** |
| * Returns the component's tooltip delay. |
| * |
| * @return The tooltip delay, in milliseconds. |
| */ |
| public int getTooltipDelay() { |
| return tooltipDelay; |
| } |
| |
| /** |
| * Sets the component's tooltip delay. |
| * |
| * @param tooltipDelay The tooltip delay, in milliseconds. |
| */ |
| public void setTooltipDelay(int tooltipDelay) { |
| int previousTooltipDelay = this.tooltipDelay; |
| |
| if (previousTooltipDelay != tooltipDelay) { |
| this.tooltipDelay = tooltipDelay; |
| componentListeners.tooltipDelayChanged(this, previousTooltipDelay); |
| } |
| } |
| |
| /** |
| * Returns the tooltip's mode for wrapping its text. |
| * |
| * @return <tt>true</tt> if the tooltip text wrap mode is enabled; |
| * <tt>false</tt> if not. |
| */ |
| public boolean getTooltipWrapText() { |
| return tooltipWrapText; |
| } |
| |
| /** |
| * Sets the tooltip's text wrapping mode. |
| * |
| * @param tooltipWrapText The component's tooltip text wrap mode. |
| */ |
| public void setTooltipWrapText(boolean tooltipWrapText) { |
| boolean previousTooltipWrapText = this.tooltipWrapText; |
| |
| if (previousTooltipWrapText != tooltipWrapText) { |
| this.tooltipWrapText = tooltipWrapText; |
| // componentListeners.tooltipTextWrapChanged(this, |
| // previousTooltipWrapText); // verify if/when to implement it ... |
| } |
| } |
| |
| /** |
| * Tells whether or not this component is fully opaque when painted. |
| * |
| * @return <tt>true</tt> if this component is opaque; <tt>false</tt> if any |
| * part of it is transparent or translucent. |
| */ |
| public boolean isOpaque() { |
| return skin.isOpaque(); |
| } |
| |
| /** |
| * Returns this component's focusability. A focusable component is capable |
| * of receiving the focus only when it is showing, unblocked, and its window |
| * is not closing. |
| * |
| * @return <tt>true</tt> if the component is capable of receiving the focus; |
| * <tt>false</tt>, otherwise. |
| */ |
| public boolean isFocusable() { |
| boolean focusable = skin.isFocusable(); |
| |
| if (focusable) { |
| Component component = this; |
| |
| while (focusable && component != null && !(component instanceof Window)) { |
| focusable = component.isVisible() && isEnabled(); |
| |
| component = component.getParent(); |
| focusable &= component != null; |
| } |
| |
| if (focusable) { |
| Window window = (Window) component; |
| if (window != null) { |
| focusable = window.isVisible() && window.isEnabled() && window.isOpen() |
| && !window.isClosing(); |
| } else { |
| focusable = false; |
| } |
| } |
| } |
| |
| return focusable; |
| } |
| |
| /** |
| * Returns the component's focused state. |
| * |
| * @return <tt>true</tt> if the component has the input focus; |
| * <tt>false</tt> otherwise. |
| */ |
| public boolean isFocused() { |
| return (focusedComponent == this); |
| } |
| |
| /** |
| * Called to notify a component that its focus state has changed. |
| * |
| * @param focused <tt>true</tt> if the component has received the input |
| * focus; <tt>false</tt> if the component has lost the focus. |
| * @param obverseComponent If <tt>focused</tt> is true, the component that |
| * has lost the focus; otherwise, the component that has gained the focus. |
| */ |
| protected void setFocused(boolean focused, Component obverseComponent) { |
| if (focused) { |
| parent.descendantGainedFocus(this, obverseComponent); |
| } else { |
| parent.descendantLostFocus(this); |
| } |
| |
| componentStateListeners.focusedChanged(this, obverseComponent); |
| } |
| |
| /** |
| * Requests that focus be given to this component. |
| * |
| * @return <tt>true</tt> if the component gained the focus; <tt>false</tt> |
| * otherwise. |
| */ |
| public boolean requestFocus() { |
| if (isFocusable()) { |
| setFocusedComponent(this); |
| |
| ApplicationContext.DisplayHost displayHost = getDisplay().getDisplayHost(); |
| if (!displayHost.isFocusOwner()) { |
| displayHost.requestFocusInWindow(); |
| } |
| } |
| |
| return isFocused(); |
| } |
| |
| /** |
| * Transfers focus to the next focusable component in the given direction. |
| * |
| * @param direction The direction in which to transfer focus. |
| * @return The new component that has received the focus or <tt>null</tt> |
| * if no component is focused. |
| */ |
| public Component transferFocus(FocusTraversalDirection direction) { |
| Component component = null; |
| |
| Container parentValue = getParent(); |
| if (parentValue != null) { |
| component = parentValue.transferFocus(this, direction); |
| } |
| |
| return component; |
| } |
| |
| /** |
| * Returns the currently focused component. |
| * |
| * @return The component that currently has the focus, or <tt>null</tt> if |
| * no component is focused. |
| */ |
| public static Component getFocusedComponent() { |
| return focusedComponent; |
| } |
| |
| /** |
| * Sets the focused component. |
| * |
| * @param focusedComponent The component to focus, or <tt>null</tt> to clear |
| * the focus. |
| */ |
| private static void setFocusedComponent(Component focusedComponent) { |
| Component previousFocusedComponent = Component.focusedComponent; |
| |
| if (previousFocusedComponent != focusedComponent) { |
| Component.focusedComponent = focusedComponent; |
| |
| if (previousFocusedComponent != null) { |
| previousFocusedComponent.setFocused(false, focusedComponent); |
| } |
| |
| if (focusedComponent != null) { |
| focusedComponent.setFocused(true, previousFocusedComponent); |
| } |
| |
| componentClassListeners.focusedComponentChanged(previousFocusedComponent); |
| } |
| } |
| |
| /** |
| * Clears the focus. |
| */ |
| public static void clearFocus() { |
| setFocusedComponent(null); |
| } |
| |
| /** |
| * Copies bound values from the bind context to the component. This |
| * functionality must be provided by the subclass; the base implementation |
| * is a no-op. |
| * |
| * @param context The object to load the bound values from. |
| */ |
| public void load(Object context) { |
| // empty block |
| } |
| |
| /** |
| * Copies bound values from the component to the bind context. This |
| * functionality must be provided by the subclass; the base implementation |
| * is a no-op. |
| * |
| * @param context The object to store the bound values into. |
| */ |
| public void store(Object context) { |
| // empty block |
| } |
| |
| /** |
| * Clears any bound values in the component. This functionality must |
| * be provided by the subclass; the base implementation is a no-op. |
| */ |
| public void clear() { |
| // empty block |
| } |
| |
| public DragSource getDragSource() { |
| return dragSource; |
| } |
| |
| public void setDragSource(DragSource dragSource) { |
| DragSource previousDragSource = this.dragSource; |
| |
| if (previousDragSource != dragSource) { |
| this.dragSource = dragSource; |
| componentListeners.dragSourceChanged(this, previousDragSource); |
| } |
| } |
| |
| public DropTarget getDropTarget() { |
| return dropTarget; |
| } |
| |
| public void setDropTarget(DropTarget dropTarget) { |
| DropTarget previousDropTarget = this.dropTarget; |
| |
| if (previousDropTarget != dropTarget) { |
| this.dropTarget = dropTarget; |
| componentListeners.dropTargetChanged(this, previousDropTarget); |
| } |
| } |
| |
| public MenuHandler getMenuHandler() { |
| return menuHandler; |
| } |
| |
| public void setMenuHandler(MenuHandler menuHandler) { |
| MenuHandler previousMenuHandler = this.menuHandler; |
| |
| if (previousMenuHandler != menuHandler) { |
| this.menuHandler = menuHandler; |
| componentListeners.menuHandlerChanged(this, previousMenuHandler); |
| } |
| } |
| |
| /** |
| * Returns the component's name. |
| * @return The name of the component. |
| */ |
| public String getName() { |
| return name; |
| } |
| |
| /** |
| * Sets the component's name. |
| * |
| * @param name Name to be given to this component. |
| */ |
| public void setName(String name) { |
| String previousName = this.name; |
| |
| if (previousName != name) { |
| this.name = name; |
| componentListeners.nameChanged(this, previousName); |
| } |
| } |
| |
| /** |
| * Returns the component's style dictionary. |
| * @return The style dictionary for this component. |
| */ |
| public final StyleDictionary getStyles() { |
| return styleDictionary; |
| } |
| |
| /** |
| * Applies a set of styles. |
| * |
| * @param styles A map containing the styles to apply. |
| */ |
| public void setStyles(Map<String, ?> styles) { |
| Utils.checkNull(styles, "styles"); |
| |
| for (String key : styles) { |
| getStyles().put(key, styles.get(key)); |
| } |
| } |
| |
| /** |
| * Applies a set of styles. |
| * |
| * @param styles The styles encoded as a JSON map. |
| * @throws SerializationException if the string doesn't conform to JSON standards. |
| */ |
| public void setStyles(String styles) throws SerializationException { |
| Utils.checkNull(styles, "styles"); |
| |
| setStyles(JSONSerializer.parseMap(styles)); |
| } |
| |
| /** |
| * Returns the typed style dictionary. |
| * @return The typed style dictionary for this component. |
| */ |
| public static Map<Class<? extends Component>, Map<String, ?>> getTypedStyles() { |
| return typedStyles; |
| } |
| |
| /** |
| * Returns the named style dictionary. |
| * @return The named style dictionary for this component. |
| */ |
| public static Map<String, Map<String, ?>> getNamedStyles() { |
| return namedStyles; |
| } |
| |
| /** |
| * Applies a named style to this component. |
| * |
| * @param styleName The name of an already loaded style to apply. |
| */ |
| public void setStyleName(String styleName) { |
| Utils.checkNull(styleName, "styleName"); |
| |
| Map<String, ?> stylesMap = namedStyles.get(styleName); |
| |
| if (stylesMap == null) { |
| System.err.println("Named style \"" + styleName + "\" does not exist."); |
| } else { |
| setStyles(stylesMap); |
| } |
| } |
| |
| /** |
| * Applies a set of named styles. |
| * |
| * @param styleNames List of style names to apply to this component. |
| */ |
| public void setStyleNames(Sequence<String> styleNames) { |
| Utils.checkNull(styleNames, "styleNames"); |
| |
| for (int i = 0, n = styleNames.getLength(); i < n; i++) { |
| setStyleName(styleNames.get(i)); |
| } |
| } |
| |
| /** |
| * Applies a set of named styles. |
| * |
| * @param styleNames Comma-delimited list of style names to apply. |
| */ |
| public void setStyleNames(String styleNames) { |
| Utils.checkNull(styleNames, "styleNames"); |
| |
| for (String styleName : styleNames.split(",")) { |
| setStyleName(styleName.trim()); |
| } |
| } |
| |
| /** |
| * @return The user data dictionary for this component. |
| */ |
| public UserDataDictionary getUserData() { |
| return userDataDictionary; |
| } |
| |
| /** |
| * Gets the specified component attribute. While attributes can be used to |
| * store arbitrary data, they are intended to be used by containers to store |
| * layout-related metadata in their child components. |
| * |
| * @param <T> The enum type of the attribute key. |
| * @param key The attribute key |
| * @return The attribute value, or <tt>null</tt> if no such attribute exists |
| */ |
| @SuppressWarnings("unchecked") |
| public <T extends Enum<T>> Object getAttribute(T key) { |
| Object attribute = null; |
| |
| if (attributes != null) { |
| attribute = ((HashMap<T, Object>) attributes).get(key); |
| } |
| |
| return attribute; |
| } |
| |
| /** |
| * Sets the specified component attribute. While attributes can be used to |
| * store arbitrary data, they are intended to be used by containers to store |
| * layout-related metadata in their child components. |
| * |
| * @param <T> The enum type of the attribute key. |
| * @param key The attribute key |
| * @param value The attribute value, or <tt>null</tt> to clear the attribute |
| * @return The previous value of the attribute, or <tt>null</tt> if the |
| * attribute was unset |
| */ |
| @SuppressWarnings("unchecked") |
| public <T extends Enum<T>> Object setAttribute(T key, Object value) { |
| if (attributes == null) { |
| attributes = new HashMap<>(); |
| } |
| |
| Object previousValue; |
| |
| if (value != null) { |
| previousValue = ((HashMap<T, Object>) attributes).put(key, value); |
| } else { |
| previousValue = ((HashMap<T, Object>) attributes).remove(key); |
| } |
| |
| return previousValue; |
| } |
| |
| /** |
| * If the mouse is currently over the component, causes the component to |
| * fire <tt>mouseOut()</tt> and a <tt>mouseMove()</tt> at the current mouse |
| * location. <p> This method is primarily useful when consuming container |
| * mouse motion events, since it allows a caller to reset the mouse state |
| * based on the event consumption logic. |
| */ |
| public void reenterMouse() { |
| if (isMouseOver()) { |
| mouseOut(); |
| |
| Display display = getDisplay(); |
| Point location = display.getMouseLocation(); |
| location = mapPointFromAncestor(display, x, y); |
| mouseMove(location.x, location.y); |
| } |
| } |
| |
| protected boolean mouseMove(int xValue, int yValue) { |
| boolean consumed = false; |
| |
| if (isEnabled()) { |
| mouseLocation = new Point(xValue, yValue); |
| |
| if (triggerTooltipCallback != null) { |
| triggerTooltipCallback.cancel(); |
| triggerTooltipCallback = null; |
| } |
| |
| triggerTooltipCallback = ApplicationContext.scheduleCallback(new Runnable() { |
| @Override |
| public void run() { |
| Point mouseLocationValue = getMouseLocation(); |
| if (mouseLocationValue != null) { |
| componentTooltipListeners.tooltipTriggered(Component.this, |
| mouseLocationValue.x, mouseLocationValue.y); |
| } |
| } |
| }, tooltipDelay); |
| |
| consumed = componentMouseListeners.mouseMove(this, xValue, yValue); |
| } |
| |
| return consumed; |
| } |
| |
| protected void mouseOver() { |
| if (isEnabled()) { |
| mouseLocation = new Point(-1, -1); |
| |
| componentMouseListeners.mouseOver(this); |
| } |
| } |
| |
| protected void mouseOut() { |
| if (isEnabled()) { |
| mouseLocation = null; |
| |
| if (triggerTooltipCallback != null) { |
| triggerTooltipCallback.cancel(); |
| triggerTooltipCallback = null; |
| } |
| |
| componentMouseListeners.mouseOut(this); |
| } |
| } |
| |
| protected boolean mouseDown(Mouse.Button button, int xValue, int yValue) { |
| boolean consumed = false; |
| |
| if (isEnabled()) { |
| if (triggerTooltipCallback != null) { |
| triggerTooltipCallback.cancel(); |
| triggerTooltipCallback = null; |
| } |
| |
| consumed = componentMouseButtonListeners.mouseDown(this, button, xValue, yValue); |
| } |
| |
| return consumed; |
| } |
| |
| protected boolean mouseUp(Mouse.Button button, int xValue, int yValue) { |
| boolean consumed = false; |
| |
| if (isEnabled()) { |
| consumed = componentMouseButtonListeners.mouseUp(this, button, xValue, yValue); |
| } |
| |
| return consumed; |
| } |
| |
| protected boolean mouseClick(Mouse.Button button, int xValue, int yValue, int count) { |
| boolean consumed = false; |
| |
| if (isEnabled()) { |
| consumed = componentMouseButtonListeners.mouseClick(this, button, xValue, yValue, |
| count); |
| } |
| |
| return consumed; |
| } |
| |
| protected boolean mouseWheel(Mouse.ScrollType scrollType, int scrollAmount, int wheelRotation, |
| int xValue, int yValue) { |
| boolean consumed = false; |
| |
| if (isEnabled()) { |
| consumed = componentMouseWheelListeners.mouseWheel(this, scrollType, scrollAmount, |
| wheelRotation, xValue, yValue); |
| } |
| |
| return consumed; |
| } |
| |
| protected boolean keyTyped(char character) { |
| boolean consumed = false; |
| |
| if (isEnabled()) { |
| consumed = componentKeyListeners.keyTyped(this, character); |
| |
| if (!consumed && parent != null) { |
| consumed = parent.keyTyped(character); |
| } |
| } |
| |
| return consumed; |
| } |
| |
| protected boolean keyPressed(int keyCode, Keyboard.KeyLocation keyLocation) { |
| boolean consumed = false; |
| |
| if (isEnabled()) { |
| consumed = componentKeyListeners.keyPressed(this, keyCode, keyLocation); |
| |
| if (!consumed && parent != null) { |
| consumed = parent.keyPressed(keyCode, keyLocation); |
| } |
| } |
| |
| return consumed; |
| } |
| |
| protected boolean keyReleased(int keyCode, Keyboard.KeyLocation keyLocation) { |
| boolean consumed = false; |
| |
| if (isEnabled()) { |
| consumed = componentKeyListeners.keyReleased(this, keyCode, keyLocation); |
| |
| if (!consumed && parent != null) { |
| consumed = parent.keyReleased(keyCode, keyLocation); |
| } |
| } |
| |
| return consumed; |
| } |
| |
| /** |
| * Returns the input method listener for this component, |
| * which will reside in the skin, so defer to the skin class. |
| * |
| * @return The input method listener (if any) for this |
| * component. |
| */ |
| public TextInputMethodListener getTextInputMethodListener() { |
| return ((ComponentSkin) getSkin()).getTextInputMethodListener(); |
| } |
| |
| @Override |
| public String toString() { |
| String s; |
| |
| if (automationID != null) { |
| s = this.getClass().getName() + "#" + automationID; |
| } else { |
| s = super.toString(); |
| } |
| |
| return s; |
| } |
| |
| public ListenerList<ComponentListener> getComponentListeners() { |
| return componentListeners; |
| } |
| |
| public ListenerList<ComponentStateListener> getComponentStateListeners() { |
| return componentStateListeners; |
| } |
| |
| public ListenerList<ComponentDecoratorListener> getComponentDecoratorListeners() { |
| return componentDecoratorListeners; |
| } |
| |
| public ListenerList<ComponentStyleListener> getComponentStyleListeners() { |
| return componentStyleListeners; |
| } |
| |
| public ListenerList<ComponentMouseListener> getComponentMouseListeners() { |
| return componentMouseListeners; |
| } |
| |
| public ListenerList<ComponentMouseButtonListener> getComponentMouseButtonListeners() { |
| return componentMouseButtonListeners; |
| } |
| |
| public ListenerList<ComponentMouseWheelListener> getComponentMouseWheelListeners() { |
| return componentMouseWheelListeners; |
| } |
| |
| public ListenerList<ComponentKeyListener> getComponentKeyListeners() { |
| return componentKeyListeners; |
| } |
| |
| public ListenerList<ComponentTooltipListener> getComponentTooltipListeners() { |
| return componentTooltipListeners; |
| } |
| |
| public ListenerList<ComponentDataListener> getComponentDataListeners() { |
| return componentDataListeners; |
| } |
| |
| public static ListenerList<ComponentClassListener> getComponentClassListeners() { |
| return componentClassListeners; |
| } |
| |
| /** |
| * Check an index value against the provided bounds and throw a nicely formatted |
| * exception, including the index name, for out of range values. |
| * |
| * @param indexName The name of the index to be checked. |
| * @param index Index to be checked against the bounds. |
| * @param min Minimum allowed value of the index. |
| * @param max Maximum allowed value of the index. |
| * @throws IndexOutOfBoundsException if index is out of range. |
| */ |
| protected static final void indexBoundsCheck(String indexName, int index, int min, int max) |
| throws IndexOutOfBoundsException { |
| if (max < min) { |
| throw new IllegalArgumentException("max (" + max + ") < " + "min (" + min + ")"); |
| } |
| if (index < min) { |
| throw new IndexOutOfBoundsException(indexName + ": index (" + index + ") < min (" + min + ")"); |
| } |
| if (index > max) { |
| throw new IndexOutOfBoundsException(indexName + ": index (" + index + ") > max (" + max + ")"); |
| } |
| } |
| } |