/*
* MouseTracker.java
*
* Copyright (C) 2009-17 by RStudio, Inc.
*
* Unless you have received this program directly from RStudio pursuant
* to the terms of a commercial license agreement with RStudio, then
* this program is licensed to you under the terms of version 3 of the
* GNU Affero General Public License. This program is distributed WITHOUT
* ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
* AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
*
*/
package org.rstudio.core.client;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.Event.NativePreviewEvent;
import com.google.gwt.user.client.Event.NativePreviewHandler;
import com.google.inject.Singleton;
@Singleton
public class MouseTracker
{
public MouseTracker()
{
Event.addNativePreviewHandler(new NativePreviewHandler()
{
@Override
public void onPreviewNativeEvent(NativePreviewEvent preview)
{
int type = preview.getTypeInt();
if ((type & Event.MOUSEEVENTS) == 0)
{
lastEventType_ = type;
return;
}
NativeEvent event = preview.getNativeEvent();
// Safari triggers certain mouse events when modifier keys are
// pressed. Suppress those as they can cause unexpected behavior
// when e.g. typing in a textbox while the mouse happens to lie
// over a popup panel (e.g. C++ completions, Find in Files)
if (isSafariModifierKeyCausingMouseEvent(event, type))
{
event.stopPropagation();
event.preventDefault();
}
lastEventType_ = type;
lastMouseX_ = event.getClientX();
lastMouseY_ = event.getClientY();
}
});
}
private boolean isSafariModifierKeyCausingMouseEvent(NativeEvent event,
int type)
{
if (!IS_SAFARI)
return false;
// If the last event was a key event, and we just got a mouse event,
// suppress it. (The intention here is to suppress a 'mouseout' or
// 'mouseover' event synthesized in respond to a modifier keypress.
//
// Note that these events can be emitted in response to a keyup as
// well; in such a case the associated modifier key will no longer
// be held down and so we just globally suppress 'mouseover' and
// 'mouseout' following a keyevent.
if ((lastEventType_ & Event.KEYEVENTS) != 0 &&
(type == Event.ONMOUSEOVER || type == Event.ONMOUSEOUT))
{
suppressing_ = true;
return true;
}
// Stop suppressing once we get a new non-mouseover/mouseout event.
else if (type != Event.ONMOUSEOVER && type != Event.ONMOUSEOUT)
{
suppressing_ = false;
return false;
}
// For other intermediate events, return status of suppressing flag.
return suppressing_;
}
public int getLastMouseX()
{
return lastMouseX_;
}
public int getLastMouseY()
{
return lastMouseY_;
}
private int lastMouseX_;
private int lastMouseY_;
private int lastEventType_;
private boolean suppressing_;
private static final boolean IS_SAFARI =
BrowseCap.isSafari() || BrowseCap.isMacintoshDesktop();
}