Package org.fest.swing.input

Source Code of org.fest.swing.input.InputState

/*
* Created on Mar 28, 2008
*
* Licensed 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.
*
* Copyright @2008-2010 the original author or authors.
*/
package org.fest.swing.input;

import static java.awt.AWTEvent.*;
import static javax.swing.SwingUtilities.getDeepestComponentAt;
import static org.fest.swing.awt.AWT.locationOnScreenOf;
import static org.fest.swing.input.MouseInfo.BUTTON_MASK;

import java.awt.*;
import java.awt.event.*;

import net.jcip.annotations.GuardedBy;
import net.jcip.annotations.ThreadSafe;

import org.fest.swing.exception.UnexpectedException;
import org.fest.swing.listener.EventDispatchThreadedEventListener;

/**
* Class to keep track of a given input state. Includes mouse/pointer position and keyboard modifier key state.
* <p>
* Synchronization assumes that any given instance might be called from more than one event dispatch thread.
* </p>
*/
// TODO: add a BitSet with the full keyboard key press state
@ThreadSafe
public class InputState {

  @GuardedBy("this") private final MouseInfo mouseInfo = new MouseInfo();
  @GuardedBy("this") private final DragDropInfo dragDropInfo = new DragDropInfo();

  @GuardedBy("this") private int modifiers;
  @GuardedBy("this") private long lastEventTime;

  private EventNormalizer normalizer;

  public InputState(Toolkit toolkit) {
    long mask = MOUSE_MOTION_EVENT_MASK | MOUSE_EVENT_MASK | KEY_EVENT_MASK;
    AWTEventListener listener = new EventDispatchThreadedEventListener() {
      protected void processEvent(AWTEvent event) {
        update(event);
      }
    };
    normalizer = new EventNormalizer();
    normalizer.startListening(toolkit, listener, mask);
  }

  public synchronized void clear() {
    mouseInfo.clear();
    dragDropInfo.clear();
    modifiers = 0;
    lastEventTime = 0;
  }

  public void dispose() {
    normalizer.stopListening();
    normalizer = null;
  }

  /**
   * Explicitly update the internal state.
   * @param event the event to use to update the internal state.
   */
  public void update(AWTEvent event) {
    if (event instanceof KeyEvent) updateState((KeyEvent) event);
    if (event instanceof MouseEvent) updateState((MouseEvent) event);
  }

  private void updateState(KeyEvent event) {
    if (isOld(event)) return;
    synchronized (this) {
      lastEventTime(event);
      modifiers(event.getModifiers());
      // FIXME add state of individual keys
    }
  }

  private void updateState(MouseEvent event) {
    if (isOld(event)) return;
    // childAt and locationOnScreenOf want the tree lock, so be careful not to use any additional locks at the same time
    // to avoid deadlock.
    Point eventScreenLocation = null;
    // Determine the current mouse position in screen coordinates
    try {
      eventScreenLocation = locationOnScreenOf(event.getComponent());
    } catch (IllegalComponentStateException e) {
      // component might be hidden by the time we process this event
    } catch (UnexpectedException e) {
      if (!(e.getCause() instanceof IllegalComponentStateException)) throw e;
    }
    synchronized (this) {
      lastEventTime(event);
      dragDropInfo.update(event);
      mouseInfo.modifiers(modifiers);
      mouseInfo.update(event, eventScreenLocation);
      modifiers(mouseInfo.modifiers());
    }
  }

  private boolean isOld(InputEvent event) {
    return event.getWhen() < lastEventTime();
  }

  private void lastEventTime(InputEvent event) {
    lastEventTime = event.getWhen();
  }

  private void modifiers(int newModifiers) {
    modifiers = newModifiers;
  }

  /**
   * Returns the most deeply nested component which currently contains the pointer.
   * @return the most deeply nested component which currently contains the pointer.
   */
  public synchronized Component deepestComponentUnderMousePointer() {
    Component c = mouseComponent();
    if (c != null) c = childAt(c, mouseLocation());
    return c;
  }

  /**
   * Returns the last known Component to contain the pointer, or <code>null</code> if none. Note that this may not
   * correspond to the component that actually shows up in AWTEvents.
   * @return the last known Component to contain the pointer, or <code>null</code> if none.
   */
  public synchronized Component mouseComponent() {
    return mouseInfo.component();
  }

  /**
   * Returns the component under the given coordinates in the given parent component. Events are often generated only
   * for the outermost container, so we have to determine if the pointer is actually within a child. Basically the same
   * as Component.getComponentAt, but recurses to the lowest-level component instead of only one level. Point is in
   * component coordinates.
   * <p>
   * The default Component.getComponentAt can return invisible components (JRootPane has an invisible JPanel (glass
   * pane?) which will otherwise swallow everything).
   * <p>
   * NOTE: childAt grabs the TreeLock, so this should *only* be invoked on the event dispatch thread, preferably with no
   * other locks held. Use it elsewhere at your own risk.
   * <p>
   * @param parent the given parent.
   * @param where the given coordinates.
   * @return the component under the given coordinates in the given parent component.
   */
  public static Component childAt(Component parent, Point where) {
    return getDeepestComponentAt(parent, where.x, where.y);
  }

  /**
   * Indicates there is a drag operation in progress.
   * @return <code>true</code> if there is a drag operation in progress, <code>false</code> otherwise.
   */
  public synchronized boolean dragInProgress() {
    return dragDropInfo.isDragging();
  }

  /**
   * Returns the <code>{@link Component}</code> where a drag operation started.
   * @return the <code>Component</code> where a drag operation started.
   */
  public synchronized Component dragSource() {
    return dragDropInfo.source();
  }

  /**
   * Returns the coordinates where a drag operation started.
   * @return the coordinates where a drag operation started.
   */
  public synchronized Point dragOrigin() {
    return dragDropInfo.origin();
  }

  /**
   * Indicates the number of times a mouse button was clicked.
   * @return the number of times a mouse button was clicked.
   */
  public synchronized int clickCount() {
    return mouseInfo.clickCount();
  }

  /**
   * Returns the time when the last input event occurred.
   * @return the time when the last input event occurred.
   */
  public synchronized long lastEventTime() {
    return lastEventTime;
  }

  /**
   * Returns all currently active modifiers.
   * @return all currently active modifiers.
   */
  public synchronized int modifiers() {
    return modifiers;
  }

  /**
   * Returns the currently pressed key modifiers.
   * @return the currently pressed key modifiers.
   */
  public synchronized int keyModifiers() {
    return modifiers & ~BUTTON_MASK;
  }

  /**
   * Returns the mouse buttons used in the last input event.
   * @return the mouse buttons used in the last input event.
   */
  public synchronized int buttons() {
    return mouseInfo.buttons();
  }

  /**
   * Returns the mouse location relative to the component that currently contains the pointer, or <code>null</code> if
   * outside all components.
   * @return the mouse location relative to the component that currently contains the pointer, or <code>null</code> if
   *         outside all components.
   */
  public synchronized Point mouseLocation() {
    return mouseInfo.location();
  }

  /**
   * Returns the last known mouse location.
   * @return the last known mouse location.
   */
  public synchronized Point mouseLocationOnScreen() {
    return mouseInfo.locationOnScreen();
  }

  /**
   * Indicates whether there is a native drag/drop operation in progress.
   * @return <code>true</code> if there is a native drag/drop operation in progress, <code>false</code> otherwise.
   */
  public boolean isNativeDragActive() {
    return dragDropInfo.isNativeDragActive();
  }
}
TOP

Related Classes of org.fest.swing.input.InputState

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.