/* * Copyright 2015 Brandon Borkholder * * 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. */ package org.jogamp.glg2d.event; import static java.lang.Math.abs; import java.awt.AWTEvent; import java.awt.Component; import java.awt.EventQueue; import java.awt.Point; import java.awt.Toolkit; import java.awt.event.MouseEvent; import java.awt.event.MouseWheelEvent; import java.awt.geom.AffineTransform; import java.awt.geom.Point2D; import javax.swing.SwingUtilities; /** * Here is an explanation of terms used to name the multiple, interacting * components. * * <ul> * <li> * origin - the component that originally received the event</li> * <li> * target - the top-most component that is getting redirected events, usually * also the top-most component that is being drawn in GLG2D</li> * <li> * source - the component that will receive the event, always a descendant of * the target component</li> */ public class MouseEventTranslator { protected EventQueue queue; protected AffineTransform originToTargetTransform; protected Component target; protected Component lastUnderCursor; protected Component lastSource; protected MouseEvent dragging; public MouseEventTranslator(Component target) { this.target = target; queue = Toolkit.getDefaultToolkit().getSystemEventQueue(); originToTargetTransform = new AffineTransform(); } /** * Sets the {@code AffineTransform} that is used to transform a point relative * to the origin component into a point relative to the target component. */ public void setOriginToTargetTransform(AffineTransform xform) { originToTargetTransform = new AffineTransform(xform); } /** * Gets the {@code AffineTransform} that is used to transform a point relative * to the origin component into a point relative to the target component. * * <p> * Note: the returned {@code AffineTransform} is a copy. * </p> */ public AffineTransform getOriginToTargetTransform() { return new AffineTransform(originToTargetTransform); } public void publishMouseEvent(Component underCursor, int id, long when, int modifiers, Point point, int clickCount, int button) { Point lastSrcPt = lastSource == null ? point : SwingUtilities.convertPoint(underCursor, point, lastSource); boolean published = false; // always publish mouse exiting event first if (id == MouseEvent.MOUSE_EXITED) { publish(new MouseEvent(underCursor, MouseEvent.MOUSE_EXITED, when, modifiers, point.x, point.y, 0, 0, clickCount, false, button)); published = true; } // take special action if dragging if (dragging != null) { if (id != MouseEvent.MOUSE_DRAGGED) { // not dragging anymore publish(new MouseEvent(lastSource, MouseEvent.MOUSE_RELEASED, when, dragging.getModifiers(), lastSrcPt.x, lastSrcPt.y, 0, 0, dragging.getClickCount(), false, dragging.getButton())); } else if (lastUnderCursor == lastSource && lastUnderCursor != underCursor) { // mouse was on the last source, but isn't now publish(new MouseEvent(lastSource, MouseEvent.MOUSE_EXITED, when, modifiers, lastSrcPt.x, lastSrcPt.y, 0, 0, clickCount, false, button)); } else if (underCursor == lastSource && lastUnderCursor != underCursor) { // mouse is now on the last source, but wasn't before publish(new MouseEvent(lastSource, MouseEvent.MOUSE_ENTERED, when, modifiers, lastSrcPt.x, lastSrcPt.y, 0, 0, clickCount, false, button)); } published = true; } // publish to last source component if (id == MouseEvent.MOUSE_DRAGGED) { MouseEvent e = new MouseEvent(lastSource, MouseEvent.MOUSE_DRAGGED, when, modifiers, lastSrcPt.x, lastSrcPt.y, 0, 0, clickCount, false, button); publish(e); dragging = e; published = true; } else { dragging = null; } // not dragging, but mouse moved from one over one component to over another if (dragging == null && lastSource != null && lastSource != underCursor) { publish(new MouseEvent(lastSource, MouseEvent.MOUSE_EXITED, when, modifiers, lastSrcPt.x, lastSrcPt.y, 0, 0, clickCount, false, button)); publish(new MouseEvent(underCursor, MouseEvent.MOUSE_ENTERED, when, modifiers, point.x, point.y, 0, 0, clickCount, false, button)); } if (!published) { publish(new MouseEvent(underCursor, id, when, modifiers, point.x, point.y, 0, 0, clickCount, false, button)); } assert dragging == null || lastUnderCursor != null; assert dragging == null || lastSource != null; assert (lastSource == null) == (lastUnderCursor == null); lastUnderCursor = underCursor; lastSource = id == MouseEvent.MOUSE_DRAGGED ? lastSource : underCursor; } public void publishMouseEvent(int id, long when, int modifiers, int clickCount, int button, Point clickedOnOrigin) { Point clickedOnTarget = originPointToTarget(clickedOnOrigin); Component source = getSourceComponent(id, clickedOnTarget); if (source == null) { return; } Point clickedOnSource = targetPointToSource(source, clickedOnTarget); publishMouseEvent(source, id, when, modifiers, clickedOnSource, clickCount, button); } public MouseWheelEvent publishMouseWheelEvent(Component source, int id, long when, int modifiers, Point sourcePoint, int clickCount, int scrollType, int scrollAmount, int wheelRotation) { // TODO how to determine this? boolean isPopupTrigger = false; MouseWheelEvent e = new MouseWheelEvent(source, id, when, modifiers, sourcePoint.x, sourcePoint.y, 0, 0, clickCount, isPopupTrigger, scrollType, scrollAmount, wheelRotation); publish(e); return e; } public MouseWheelEvent publishMouseWheelEvent(int id, long when, int modifiers, int wheelRotation, Point mouseOnOrigin) { Point clickedOnTarget = originPointToTarget(mouseOnOrigin); Component source = getSourceComponent(id, clickedOnTarget); if (source == null) { return null; } Point clickedOnSource = targetPointToSource(source, clickedOnTarget); return publishMouseWheelEvent(source, id, when, modifiers, clickedOnSource, 0, MouseWheelEvent.WHEEL_UNIT_SCROLL, abs(wheelRotation), wheelRotation); } /** * Converts a point that is relative to the origin component into a point that * is relative to the target component. */ protected Point originPointToTarget(Point pt) { Point2D newPt = originToTargetTransform.transform(pt, new Point()); return new Point((int) newPt.getX(), (int) newPt.getY()); } /** * Converts a point that is relative to the target into a point that is * relative to the source component. */ protected Point targetPointToSource(Component source, Point pt) { return SwingUtilities.convertPoint(target, pt, source); } /** * Gets the source component at the given target-relative point. */ protected Component getSourceComponent(int eventId, Point pt) { return SwingUtilities.getDeepestComponentAt(target, pt.x, pt.y); } protected void publish(AWTEvent e) { queue.postEvent(e); } }