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-2013 the original author or authors.
*/
package org.fest.swing.input;

import static java.awt.AWTEvent.KEY_EVENT_MASK;
import static java.awt.AWTEvent.MOUSE_EVENT_MASK;
import static java.awt.AWTEvent.MOUSE_MOTION_EVENT_MASK;
import static javax.swing.SwingUtilities.getDeepestComponentAt;
import static org.fest.swing.awt.AWT.locationOnScreenOf;
import static org.fest.swing.input.MouseInfo.BUTTON_MASK;
import static org.fest.util.Preconditions.checkNotNull;

import java.awt.AWTEvent;
import java.awt.Component;
import java.awt.IllegalComponentStateException;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.event.AWTEventListener;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.concurrent.GuardedBy;
import javax.annotation.concurrent.ThreadSafe;

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

/**
* <p>
* Keeps track of a given input state. Includes mouse/pointer position and keyboard modifier key state.
* </p>
*
* <p>
* Synchronization assumes that any given instance might be called from more than one event dispatch thread (EDT.)
* </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(@Nonnull Toolkit toolkit) {
    long mask = MOUSE_MOTION_EVENT_MASK | MOUSE_EVENT_MASK | KEY_EVENT_MASK;
    AWTEventListener listener = new EventDispatchThreadedEventListener() {
      @Override
      protected void processEvent(@Nonnull 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(@Nonnull AWTEvent event) {
    if (event instanceof InputEvent) {
      InputEvent inputEvent = (InputEvent) event;
      if (inputEvent.getWhen() < lastEventTime()) {
        // event is old.
        return;
      }
      if (inputEvent instanceof KeyEvent) {
        synchronized (this) {
          lastEventTime(inputEvent);
          modifiers(inputEvent.getModifiers());
          // FIXME add state of individual keys
        }
      }
      if (inputEvent instanceof MouseEvent) {
        MouseEvent mouseEvent = (MouseEvent) inputEvent;
        Point eventScreenLocation = screenLocation(mouseEvent);
        synchronized (this) {
          lastEventTime(mouseEvent);
          dragDropInfo.update(mouseEvent);
          mouseInfo.modifiers(modifiers);
          mouseInfo.update(mouseEvent, eventScreenLocation);
          modifiers(mouseInfo.modifiers());
        }
      }
    }
  }

  private @Nullable Point screenLocation(@Nonnull MouseEvent event) {
    // childAt and locationOnScreenOf want the tree lock, so be careful not to use any additional locks at the same time
    // to avoid deadlock.
    // Determine the current mouse position in screen coordinates
    try {
      Component component = checkNotNull(event.getComponent());
      return locationOnScreenOf(component);
    } catch (IllegalComponentStateException e) {
      // component might be hidden by the time we process this event
    } catch (UnexpectedException e) {
      if (!(e.getCause() instanceof IllegalComponentStateException)) {
        throw e;
      }
    }
    return null;
  }

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

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

  /**
   * @return the most deeply nested AWT or Swing {@code Component} which currently contains the pointer.
   */
  public synchronized @Nullable Component deepestComponentUnderMousePointer() {
    Component c = mouseComponent();
    if (c != null) {
      Point mouseLocation = mouseLocation();
      if (mouseLocation != null) {
        c = childAt(c, mouseLocation);
      }
    }
    return c;
  }

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

  /**
   * <p>
   * Returns the AWT or Swing {@code Component} under the given coordinates in the given parent component. Events are
   * often generated only for the outermost {@code Container}, so we have to determine if the pointer is actually within
   * a child. Basically the same as {@code Component.getComponentAt}, but recurses to the lowest-level {@code Component}
   * instead of only one level. Point is in {@code Component} coordinates.
   * </p>
   *
   * <p>
   * The default {@code Component.getComponentAt} can return invisible {@code Component}s ({@code JRootPane} has an
   * invisible {@code JPanel} (glass pane?) which will otherwise swallow everything).
   * </p>
   *
   * <p>
   * NOTE: childAt grabs the {@code 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 {@code Component} under the given coordinates in the given parent {@code Component}.
   */
  public static Component childAt(@Nonnull Component parent, @Nonnull Point where) {
    return getDeepestComponentAt(parent, where.x, where.y);
  }

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

  /**
   * @return the AWT or Swing {@code Component} where a drag operation started.
   */
  public synchronized Component dragSource() {
    return dragDropInfo.source();
  }

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

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

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

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

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

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

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

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

  /**
   * Indicates whether there is a native drag/drop operation in progress.
   *
   * @return {@code true} if there is a native drag/drop operation in progress, {@code false} 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.