/** * 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 2012-2015 the original author or authors. */ package org.assertj.swing.input; import static java.awt.event.InputEvent.BUTTON1_MASK; import static java.awt.event.InputEvent.BUTTON2_MASK; import static java.awt.event.InputEvent.BUTTON3_MASK; import static java.awt.event.MouseEvent.MOUSE_ENTERED; import static java.awt.event.MouseEvent.MOUSE_EXITED; import static java.awt.event.MouseEvent.MOUSE_PRESSED; import static java.awt.event.MouseEvent.MOUSE_RELEASED; import static org.assertj.swing.query.ComponentShowingQuery.isShowing; import java.awt.Component; import java.awt.Point; import java.awt.event.MouseEvent; import java.lang.ref.WeakReference; import java.util.Stack; import javax.annotation.Nonnull; import javax.annotation.Nullable; /** * A description mouse-related operations. * * @author Alex Ruiz */ class MouseInfo { static final int BUTTON_MASK = BUTTON1_MASK | BUTTON2_MASK | BUTTON3_MASK; /** Current mouse position, in component coordinates. */ private Point location = new Point(0, 0); /** Current mouse position, in screen coordinates. */ private Point locationOnScreen = new Point(0, 0); private final Stack<WeakReference<Component>> componentStack = new Stack<WeakReference<Component>>(); private final Stack<Point> locationStack = new Stack<Point>(); private final Stack<Point> screenLocationStack = new Stack<Point>(); private int buttons; private int modifiers; private int clickCount; void clear() { buttons = modifiers = clickCount = 0; componentStack.clear(); locationStack.clear(); screenLocationStack.clear(); } void update(@Nonnull MouseEvent event, @Nullable Point eventScreenLocation) { // When a button is released, only that button appears in the modifier mask clickCount(event.getClickCount()); updateOnMousePressed(event); updateOnMouseReleased(event); updateOnMouseEntered(event, eventScreenLocation); updateOnMouseExited(event); if (eventScreenLocation != null) { Point where = event.getPoint(); location = componentStack.empty() ? null : new Point(where); locationOnScreen.setLocation(eventScreenLocation); locationOnScreen.translate(where.x, where.y); } } private void updateOnMousePressed(@Nonnull MouseEvent event) { if (event.getID() != MOUSE_PRESSED) { return; } int buttonUsed = buttonUsed(event); buttons |= buttonUsed; modifiers |= buttonUsed; } private void updateOnMouseReleased(@Nonnull MouseEvent event) { if (event.getID() != MOUSE_RELEASED) { return; } int buttonUsed = buttonUsed(event); buttons &= ~buttonUsed; modifiers &= ~buttonUsed; } private int buttonUsed(@Nonnull MouseEvent event) { return event.getModifiers() & BUTTON_MASK; } private void updateOnMouseEntered(@Nonnull MouseEvent event, @Nullable Point eventScreenLocation) { if (event.getID() != MOUSE_ENTERED) { return; } componentStack.push(new WeakReference<Component>(event.getComponent())); Point eventPoint = event.getPoint(); locationStack.push(eventPoint); screenLocationStack.push(eventScreenLocation != null ? eventScreenLocation : eventPoint); } private void updateOnMouseExited(@Nonnull MouseEvent event) { if (event.getID() != MOUSE_EXITED || componentStack.empty()) { return; } componentStack.pop(); locationStack.pop(); screenLocationStack.pop(); } public @Nullable Component component() { if (componentStack.empty()) { return null; } Component c = componentStack.peek().get(); // Make sure we don't return a component that has gone away. if (c != null && isShowing(c)) { return c; } componentStack.pop(); locationStack.pop(); screenLocationStack.pop(); c = component(); if (c != null) { location = locationStack.peek(); locationOnScreen = screenLocationStack.peek(); } return c; } int buttons() { return buttons; } void buttons(int newButtons) { buttons = newButtons; } int modifiers() { return modifiers; } void modifiers(int newModifiers) { modifiers = newModifiers; } int clickCount() { return clickCount; } void clickCount(int newClickCount) { clickCount = newClickCount; } @Nullable Point location() { return pointFrom(location); } @Nullable Point locationOnScreen() { return pointFrom(locationOnScreen); } private @Nullable Point pointFrom(Point source) { return source != null ? new Point(source) : null; } }