gecko-dev/widget/qt/nsWindow.cpp

2270 строки
64 KiB
C++
Исходник Обычный вид История

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim:expandtab:shiftwidth=4:tabstop=4:
*/
2012-05-21 15:12:37 +04:00
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/ArrayUtils.h"
#include "mozilla/MiscEvents.h"
#include "mozilla/MouseEvents.h"
#include "mozilla/TextEvents.h"
#include "mozilla/TouchEvents.h"
#include <QGuiApplication>
#include <QtGui/QCursor>
#include <QIcon>
#include <QMouseEvent>
#include <QWheelEvent>
#include <QResizeEvent>
#include <QPaintEngine>
#include <QMimeData>
#include <QScreen>
#include <QtCore/QDebug>
#include <QtCore/QEvent>
#include <QtCore/QVariant>
#include <algorithm>
#ifdef MOZ_X11
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#endif //MOZ_X11
#include "nsXULAppAPI.h"
#include "prlink.h"
#include "nsWindow.h"
#include "mozqwidget.h"
#include "nsIdleService.h"
#include "nsRenderingContext.h"
#include "nsIRollupListener.h"
#include "nsWidgetsCID.h"
#include "nsQtKeyUtils.h"
#include "mozilla/Services.h"
#include "mozilla/Preferences.h"
#include "mozilla/Likely.h"
#include "mozilla/layers/LayersTypes.h"
#include "nsIWidgetListener.h"
#include "ClientLayerManager.h"
#include "BasicLayers.h"
#include "nsIStringBundle.h"
#include "nsGfxCIID.h"
#include "imgIContainer.h"
#include "nsGfxCIID.h"
#include "nsIInterfaceRequestorUtils.h"
#include "nsAutoPtr.h"
#include "gfxQtPlatform.h"
#include "nsIDOMWheelEvent.h"
#include "GLContext.h"
#ifdef MOZ_X11
#include "keysym2ucs.h"
#endif
#include "Layers.h"
#include "GLContextProvider.h"
using namespace mozilla;
using namespace mozilla::gl;
using namespace mozilla::widget;
using namespace mozilla::gfx;
using namespace mozilla::layers;
using mozilla::gl::GLContext;
#define kWindowPositionSlop 20
// Qt
static const int WHEEL_DELTA = 120;
static bool gGlobalsInitialized = false;
static bool sAltGrModifier = false;
static void find_first_visible_parent(QWindow* aItem, QWindow*& aVisibleItem);
static bool is_mouse_in_window (MozQWidget* aWindow, double aMouseX, double aMouseY);
static bool isContextMenuKeyEvent(const QKeyEvent *qe);
static void InitKeyEvent(WidgetKeyboardEvent &aEvent, QKeyEvent *aQEvent);
nsWindow::nsWindow()
{
LOG(("%s [%p]\n", __PRETTY_FUNCTION__, (void *)this));
mIsTopLevel = false;
mIsDestroyed = false;
mIsShown = false;
mEnabled = true;
mWidget = nullptr;
mVisible = false;
mActivatePending = false;
mWindowType = eWindowType_child;
mSizeState = nsSizeMode_Normal;
mLastSizeMode = nsSizeMode_Normal;
mQCursor = Qt::ArrowCursor;
mNeedsResize = false;
mNeedsMove = false;
mListenForResizes = false;
mNeedsShow = false;
mTimerStarted = false;
mMoveEvent.needDispatch = false;
if (!gGlobalsInitialized) {
gfxPlatform::GetPlatform();
gGlobalsInitialized = true;
}
memset(mKeyDownFlags, 0, sizeof(mKeyDownFlags));
mIsTransparent = false;
mCursor = eCursor_standard;
}
nsWindow::~nsWindow()
{
LOG(("%s [%p]\n", __PRETTY_FUNCTION__, (void *)this));
Destroy();
}
nsresult
nsWindow::Create(nsIWidget *aParent,
nsNativeWidget aNativeParent,
const nsIntRect &aRect,
nsDeviceContext *aContext,
nsWidgetInitData *aInitData)
{
// only set the base parent if we're not going to be a dialog or a
// toplevel
nsIWidget *baseParent = aParent;
// initialize all the common bits of this class
BaseCreate(baseParent, aRect, aContext, aInitData);
mVisible = true;
// and do our common creation
mParent = (nsWindow *)aParent;
// save our bounds
mBounds = aRect;
// find native parent
MozQWidget *parent = nullptr;
if (aParent != nullptr) {
parent = static_cast<MozQWidget*>(aParent->GetNativeData(NS_NATIVE_WIDGET));
} else if (aNativeParent != nullptr) {
parent = static_cast<MozQWidget*>(aNativeParent);
if (parent && mParent == nullptr) {
mParent = parent->getReceiver();
}
}
LOG(("Create: nsWindow [%p] mWidget:[%p] parent:[%p], natPar:[%p] mParent:%p\n", (void *)this, (void*)mWidget, parent, aNativeParent, mParent));
// ok, create our QGraphicsWidget
mWidget = createQWidget(parent, aInitData);
if (!mWidget) {
return NS_ERROR_OUT_OF_MEMORY;
}
// resize so that everything is set to the right dimensions
Resize(mBounds.x, mBounds.y, mBounds.width, mBounds.height, false);
// check if we should listen for resizes
mListenForResizes = (aNativeParent ||
(aInitData && aInitData->mListenForResizes));
return NS_OK;
}
MozQWidget*
nsWindow::createQWidget(MozQWidget* parent,
nsWidgetInitData* aInitData)
{
const char *windowName = nullptr;
Qt::WindowFlags flags = Qt::Widget;
// ok, create our windows
switch (mWindowType) {
case eWindowType_dialog:
windowName = "topLevelDialog";
flags = Qt::Dialog;
break;
case eWindowType_popup:
windowName = "topLevelPopup";
flags = Qt::Popup;
break;
case eWindowType_toplevel:
windowName = "topLevelWindow";
flags = Qt::Window;
break;
case eWindowType_invisible:
windowName = "topLevelInvisible";
break;
case eWindowType_child:
case eWindowType_plugin:
default: // sheet
windowName = "paintArea";
break;
}
MozQWidget* widget = new MozQWidget(this, parent);
if (!widget) {
return nullptr;
}
widget->setObjectName(QString(windowName));
if (mWindowType == eWindowType_invisible) {
#if (QT_VERSION >= QT_VERSION_CHECK(5, 1, 0))
widget->setVisibility(QWindow::Hidden);
#else
widget->hide();
#endif
}
if (mWindowType == eWindowType_dialog) {
widget->setModality(Qt::WindowModal);
}
widget->create();
// create a QGraphicsView if this is a new toplevel window
LOG(("nsWindow::%s [%p] Created Window: %s, widget:%p, par:%p\n", __FUNCTION__, (void *)this, windowName, widget, parent));
return widget;
}
NS_IMETHODIMP
nsWindow::Destroy(void)
{
if (mIsDestroyed || !mWidget) {
return NS_OK;
}
LOG(("nsWindow::Destroy [%p]\n", (void *)this));
mIsDestroyed = true;
/** Need to clean our LayerManager up while still alive */
if (mLayerManager) {
mLayerManager->Destroy();
}
mLayerManager = nullptr;
// It is safe to call DestroyeCompositor several times (here and
// in the parent class) since it will take effect only once.
// The reason we call it here is because on gtk platforms we need
// to destroy the compositor before we destroy the gdk window (which
// destroys the the gl context attached to it).
DestroyCompositor();
ClearCachedResources();
nsIRollupListener* rollupListener = nsBaseWidget::GetActiveRollupListener();
if (rollupListener) {
nsCOMPtr<nsIWidget> rollupWidget = rollupListener->GetRollupWidget();
if (static_cast<nsIWidget *>(this) == rollupWidget) {
rollupListener->Rollup(0, nullptr, nullptr);
}
}
Show(false);
// walk the list of children and call destroy on them. Have to be
// careful, though -- calling destroy on a kid may actually remove
// it from our child list, losing its sibling links.
for (nsIWidget* kid = mFirstChild; kid; ) {
nsIWidget* next = kid->GetNextSibling();
kid->Destroy();
kid = next;
}
// Destroy thebes surface now. Badness can happen if we destroy
// the surface after its X Window.
if (mWidget) {
mWidget->dropReceiver();
// Call deleteLater instead of delete; Qt still needs the object
// to be valid even after sending it a Close event. We could
// also set WA_DeleteOnClose, but this gives us more control.
mWidget->deleteLater();
}
mWidget = nullptr;
OnDestroy();
return NS_OK;
}
NS_IMETHODIMP
nsWindow::Show(bool aState)
{
LOG(("nsWindow::Show [%p] state %d\n", (void *)this, aState));
if (aState == mIsShown) {
return NS_OK;
}
// Clear our cached resources when the window is hidden.
if (mIsShown && !aState) {
ClearCachedResources();
}
mIsShown = aState;
if ((aState && !AreBoundsSane()) || !mWidget) {
LOG(("\tbounds are insane or window hasn't been created yet\n"));
mNeedsShow = true;
return NS_OK;
}
if (aState) {
if (mNeedsMove) {
NativeResize(mBounds.x, mBounds.y, mBounds.width, mBounds.height,
false);
} else if (mNeedsResize) {
NativeResize(mBounds.width, mBounds.height, false);
}
}
else {
// If someone is hiding this widget, clear any needing show flag.
mNeedsShow = false;
}
NativeShow(aState);
return NS_OK;
}
bool
nsWindow::IsVisible() const
{
return mIsShown;
}
NS_IMETHODIMP
nsWindow::ConstrainPosition(bool aAllowSlop, int32_t *aX, int32_t *aY)
{
if (!mWidget) {
return NS_ERROR_FAILURE;
}
int32_t screenWidth = qApp->primaryScreen()->size().width();
int32_t screenHeight = qApp->primaryScreen()->size().height();
if (aAllowSlop) {
if (*aX < (kWindowPositionSlop - mBounds.width))
*aX = kWindowPositionSlop - mBounds.width;
if (*aX > (screenWidth - kWindowPositionSlop))
*aX = screenWidth - kWindowPositionSlop;
if (*aY < (kWindowPositionSlop - mBounds.height))
*aY = kWindowPositionSlop - mBounds.height;
if (*aY > (screenHeight - kWindowPositionSlop))
*aY = screenHeight - kWindowPositionSlop;
} else {
if (*aX < 0)
*aX = 0;
if (*aX > (screenWidth - mBounds.width))
*aX = screenWidth - mBounds.width;
if (*aY < 0)
*aY = 0;
if (*aY > (screenHeight - mBounds.height))
*aY = screenHeight - mBounds.height;
}
return NS_OK;
}
NS_IMETHODIMP
nsWindow::Move(double aX, double aY)
{
LOG(("nsWindow::Move [%p] %f %f\n", (void *)this,
aX, aY));
int32_t x = NSToIntRound(aX);
int32_t y = NSToIntRound(aY);
if (mIsTopLevel) {
SetSizeMode(nsSizeMode_Normal);
}
if (x == mBounds.x && y == mBounds.y) {
return NS_OK;
}
mNeedsMove = false;
// update the bounds
QPoint pos(x, y);
if (mIsTopLevel) {
mWidget->setPosition(x, y);
}
else if (mWidget) {
// the position of the widget is set relative to the parent
// so we map the coordinates accordingly
pos = mWidget->mapToGlobal(pos);
mWidget->setPosition(pos);
}
mBounds.x = pos.x();
mBounds.y = pos.y();
NotifyRollupGeometryChange();
return NS_OK;
}
NS_IMETHODIMP
nsWindow::Resize(double aWidth, double aHeight, bool aRepaint)
{
mBounds.width = NSToIntRound(aWidth);
mBounds.height = NSToIntRound(aHeight);
if (!mWidget)
return NS_OK;
if (mIsShown) {
if (AreBoundsSane()) {
if (mIsTopLevel || mNeedsShow)
NativeResize(mBounds.x, mBounds.y,
mBounds.width, mBounds.height, aRepaint);
else
NativeResize(mBounds.width, mBounds.height, aRepaint);
// Does it need to be shown because it was previously insane?
if (mNeedsShow) {
NativeShow(true);
}
}
else {
// If someone has set this so that the needs show flag is false
// and it needs to be hidden, update the flag and hide the
// window. This flag will be cleared the next time someone
// hides the window or shows it. It also prevents us from
// calling NativeShow(false) excessively on the window which
// causes unneeded X traffic.
if (!mNeedsShow) {
mNeedsShow = true;
NativeShow(false);
}
}
}
else if (AreBoundsSane() && mListenForResizes) {
// For widgets that we listen for resizes for (widgets created
// with native parents) we apparently _always_ have to resize. I
// dunno why, but apparently we're lame like that.
NativeResize(mBounds.width, mBounds.height, aRepaint);
}
else {
mNeedsResize = true;
}
// synthesize a resize event if this isn't a toplevel
if (mIsTopLevel || mListenForResizes) {
nsEventStatus status;
DispatchResizeEvent(mBounds, status);
}
NotifyRollupGeometryChange();
return NS_OK;
}
NS_IMETHODIMP
nsWindow::Resize(double aX, double aY, double aWidth, double aHeight,
bool aRepaint)
{
mBounds.x = NSToIntRound(aX);
mBounds.y = NSToIntRound(aY);
mBounds.width = NSToIntRound(aWidth);
mBounds.height = NSToIntRound(aHeight);
mPlaced = true;
if (!mWidget) {
return NS_OK;
}
// Has this widget been set to visible?
if (mIsShown) {
// Are the bounds sane?
if (AreBoundsSane()) {
// Yep? Resize the window
NativeResize(mBounds.x, mBounds.y, mBounds.width, mBounds.height,
aRepaint);
// Does it need to be shown because it was previously insane?
if (mNeedsShow)
NativeShow(true);
}
else {
// If someone has set this so that the needs show flag is false
// and it needs to be hidden, update the flag and hide the
// window. This flag will be cleared the next time someone
// hides the window or shows it. It also prevents us from
// calling NativeShow(false) excessively on the window which
// causes unneeded X traffic.
if (!mNeedsShow) {
mNeedsShow = true;
NativeShow(false);
}
}
}
// If the widget hasn't been shown, mark the widget as needing to be
// resized before it is shown
else if (AreBoundsSane() && mListenForResizes) {
// For widgets that we listen for resizes for (widgets created
// with native parents) we apparently _always_ have to resize. I
// dunno why, but apparently we're lame like that.
NativeResize(mBounds.x, mBounds.y, mBounds.width, mBounds.height,
aRepaint);
}
else {
mNeedsResize = true;
mNeedsMove = true;
}
if (mIsTopLevel || mListenForResizes) {
// synthesize a resize event
nsEventStatus status;
DispatchResizeEvent(mBounds, status);
}
if (aRepaint) {
mWidget->renderLater();
}
NotifyRollupGeometryChange();
return NS_OK;
}
NS_IMETHODIMP
nsWindow::Enable(bool aState)
{
mEnabled = aState;
return NS_OK;
}
bool
nsWindow::IsEnabled() const
{
return mEnabled;
}
NS_IMETHODIMP
nsWindow::SetFocus(bool aRaise)
{
// Make sure that our owning widget has focus. If it doesn't try to
// grab it. Note that we don't set our focus flag in this case.
LOGFOCUS((" SetFocus [%p]\n", (void *)this));
if (!mWidget) {
return NS_ERROR_FAILURE;
}
if (mWidget->focusObject()) {
return NS_OK;
}
// Because QGraphicsItem cannot get the focus if they are
// invisible, we look up the chain, for the lowest visible
// parent and focus that one
QWindow* realFocusItem = nullptr;
find_first_visible_parent(mWidget, realFocusItem);
if (!realFocusItem || realFocusItem->focusObject()) {
return NS_OK;
}
if (aRaise && mWidget) {
// the raising has to happen on the view widget
mWidget->raise();
}
// XXXndeakin why is this here? It should dispatch only when the OS
// notifies us.
DispatchActivateEvent();
return NS_OK;
}
NS_IMETHODIMP
nsWindow::ConfigureChildren(const nsTArray<nsIWidget::Configuration>& aConfigurations)
{
for (uint32_t i = 0; i < aConfigurations.Length(); ++i) {
const Configuration& configuration = aConfigurations[i];
nsWindow* w = static_cast<nsWindow*>(configuration.mChild);
NS_ASSERTION(w->GetParent() == this,
"Configured widget is not a child");
if (w->mBounds.Size() != configuration.mBounds.Size()) {
w->Resize(configuration.mBounds.x, configuration.mBounds.y,
configuration.mBounds.width, configuration.mBounds.height,
true);
} else if (w->mBounds.TopLeft() != configuration.mBounds.TopLeft()) {
w->Move(configuration.mBounds.x, configuration.mBounds.y);
}
}
return NS_OK;
}
NS_IMETHODIMP
nsWindow::Invalidate(const nsIntRect &aRect)
{
LOGDRAW(("Invalidate (rect) [%p,%p]: %d %d %d %d\n", (void *)this,
(void*)mWidget,aRect.x, aRect.y, aRect.width, aRect.height));
if (!mWidget) {
return NS_OK;
}
mWidget->renderLater();
return NS_OK;
}
nsIntPoint
nsWindow::WidgetToScreenOffset()
{
NS_ENSURE_TRUE(mWidget, nsIntPoint(0,0));
QPoint origin(0, 0);
origin = mWidget->mapToGlobal(origin);
return nsIntPoint(origin.x(), origin.y());
}
void*
nsWindow::GetNativeData(uint32_t aDataType)
{
switch (aDataType) {
case NS_NATIVE_WINDOW:
case NS_NATIVE_WIDGET: {
return mWidget;
}
case NS_NATIVE_SHAREABLE_WINDOW: {
return mWidget ? (void*)mWidget->winId() : nullptr;
}
case NS_NATIVE_DISPLAY: {
#ifdef MOZ_X11
return gfxQtPlatform::GetXDisplay(mWidget);
#endif
break;
}
case NS_NATIVE_PLUGIN_PORT:
case NS_NATIVE_GRAPHIC:
case NS_NATIVE_SHELLWIDGET: {
break;
}
default:
NS_WARNING("nsWindow::GetNativeData called with bad value");
return nullptr;
}
LOG(("nsWindow::%s [%p] aDataType:%i\n", __FUNCTION__, (void *)this, aDataType));
return nullptr;
}
NS_IMETHODIMP
nsWindow::DispatchEvent(WidgetGUIEvent* aEvent, nsEventStatus& aStatus)
{
#ifdef DEBUG
debug_DumpEvent(stdout, aEvent->widget, aEvent,
nsAutoCString("something"), 0);
#endif
aStatus = nsEventStatus_eIgnore;
// send it to the standard callback
if (mWidgetListener) {
aStatus = mWidgetListener->HandleEvent(aEvent, mUseAttachedEvents);
}
return NS_OK;
}
NS_IMETHODIMP_(void)
nsWindow::SetInputContext(const InputContext& aContext,
const InputContextAction& aAction)
{
NS_ENSURE_TRUE_VOID(mWidget);
// SetSoftwareKeyboardState uses mInputContext,
// so, before calling that, record aContext in mInputContext.
mInputContext = aContext;
switch (mInputContext.mIMEState.mEnabled) {
case IMEState::ENABLED:
case IMEState::PASSWORD:
case IMEState::PLUGIN:
SetSoftwareKeyboardState(true, aAction);
break;
default:
SetSoftwareKeyboardState(false, aAction);
break;
}
}
NS_IMETHODIMP_(InputContext)
nsWindow::GetInputContext()
{
mInputContext.mIMEState.mOpen = IMEState::OPEN_STATE_NOT_SUPPORTED;
// Our qt widget looks like using only one context per process.
// However, it's better to set the context's pointer.
mInputContext.mNativeIMEContext = qApp->inputMethod();
return mInputContext;
}
NS_IMETHODIMP
nsWindow::ReparentNativeWidget(nsIWidget *aNewParent)
{
NS_PRECONDITION(aNewParent, "");
MozQWidget* newParent = static_cast<MozQWidget*>(aNewParent->GetNativeData(NS_NATIVE_WINDOW));
NS_ASSERTION(newParent, "Parent widget has a null native window handle");
if (mWidget) {
mWidget->setParent(newParent);
}
return NS_OK;
}
NS_IMETHODIMP
nsWindow::MakeFullScreen(bool aFullScreen)
{
NS_ENSURE_TRUE(mWidget, NS_ERROR_FAILURE);
if (aFullScreen) {
if (mSizeMode != nsSizeMode_Fullscreen) {
mLastSizeMode = mSizeMode;
}
mSizeMode = nsSizeMode_Fullscreen;
mWidget->showFullScreen();
}
else {
mSizeMode = mLastSizeMode;
switch (mSizeMode) {
case nsSizeMode_Maximized:
mWidget->showMaximized();
break;
case nsSizeMode_Minimized:
mWidget->showMinimized();
break;
case nsSizeMode_Normal:
mWidget->showNormal();
break;
default:
mWidget->showNormal();
break;
}
}
NS_ASSERTION(mLastSizeMode != nsSizeMode_Fullscreen,
"mLastSizeMode should never be fullscreen");
return nsBaseWidget::MakeFullScreen(aFullScreen);
}
LayerManager*
nsWindow::GetLayerManager(PLayerTransactionChild* aShadowManager,
LayersBackend aBackendHint,
LayerManagerPersistence aPersistence,
bool* aAllowRetaining)
{
if (!mLayerManager && eTransparencyTransparent == GetTransparencyMode()) {
mLayerManager = CreateBasicLayerManager();
}
return nsBaseWidget::GetLayerManager(aShadowManager, aBackendHint,
aPersistence, aAllowRetaining);
}
void
nsWindow::UserActivity()
{
if (!mIdleService) {
mIdleService = do_GetService("@mozilla.org/widget/idleservice;1");
}
if (mIdleService) {
mIdleService->ResetIdleTimeOut(0);
}
}
uint32_t
nsWindow::GetGLFrameBufferFormat()
{
if (mLayerManager &&
mLayerManager->GetBackendType() == mozilla::layers::LayersBackend::LAYERS_OPENGL) {
return LOCAL_GL_RGB;
}
return LOCAL_GL_NONE;
}
NS_IMETHODIMP
nsWindow::SetCursor(nsCursor aCursor)
{
if (mCursor == aCursor) {
return NS_OK;
}
mCursor = aCursor;
if (mWidget) {
mWidget->SetCursor(mCursor);
}
return NS_OK;
}
NS_IMETHODIMP
nsWindow::SetTitle(const nsAString& aTitle)
{
QString qStr(QString::fromUtf16((const ushort*)aTitle.BeginReading(), aTitle.Length()));
mWidget->setTitle(qStr);
return NS_OK;
}
// EVENTS
void
nsWindow::OnPaint()
{
LOGDRAW(("nsWindow::%s [%p]\n", __FUNCTION__, (void *)this));
nsIWidgetListener* listener =
mAttachedWidgetListener ? mAttachedWidgetListener : mWidgetListener;
if (!listener) {
return;
}
listener->WillPaintWindow(this);
switch (GetLayerManager()->GetBackendType()) {
case mozilla::layers::LayersBackend::LAYERS_CLIENT: {
nsIntRegion region(nsIntRect(0, 0, mWidget->width(), mWidget->height()));
listener->PaintWindow(this, region);
break;
}
default:
NS_ERROR("Invalid layer manager");
}
listener->DidPaintWindow();
}
nsEventStatus
nsWindow::moveEvent(QMoveEvent* aEvent)
{
LOG(("configure event [%p] %d %d\n", (void *)this,
aEvent->pos().x(), aEvent->pos().y()));
// can we shortcut?
if (!mWidget || !mWidgetListener)
return nsEventStatus_eIgnore;
if ((mBounds.x == aEvent->pos().x() &&
mBounds.y == aEvent->pos().y()))
{
return nsEventStatus_eIgnore;
}
NotifyWindowMoved(aEvent->pos().x(), aEvent->pos().y());
return nsEventStatus_eConsumeNoDefault;
}
nsEventStatus
nsWindow::resizeEvent(QResizeEvent* aEvent)
{
nsIntRect rect;
// Generate XPFE resize event
GetBounds(rect);
rect.width = aEvent->size().width();
rect.height = aEvent->size().height();
mBounds.width = rect.width;
mBounds.height = rect.height;
nsEventStatus status;
DispatchResizeEvent(rect, status);
return status;
}
nsEventStatus
nsWindow::mouseMoveEvent(QMouseEvent* aEvent)
{
UserActivity();
mMoveEvent.pos = aEvent->pos();
mMoveEvent.modifiers = aEvent->modifiers();
mMoveEvent.needDispatch = true;
DispatchMotionToMainThread();
return nsEventStatus_eIgnore;
}
nsEventStatus
nsWindow::mousePressEvent(QMouseEvent* aEvent)
{
// The user has done something.
UserActivity();
QPoint pos = aEvent->pos();
// we check against the widgets geometry, so use parent coordinates
// for the check
if (mWidget)
pos = mWidget->mapToGlobal(pos);
if (CheckForRollup( pos.x(), pos.y(), false))
return nsEventStatus_eIgnore;
uint16_t domButton;
switch (aEvent->button()) {
case Qt::MidButton:
domButton = WidgetMouseEvent::eMiddleButton;
break;
case Qt::RightButton:
domButton = WidgetMouseEvent::eRightButton;
break;
default:
domButton = WidgetMouseEvent::eLeftButton;
break;
}
WidgetMouseEvent event(true, NS_MOUSE_BUTTON_DOWN, this,
WidgetMouseEvent::eReal);
event.button = domButton;
InitButtonEvent(event, aEvent, 1);
LOG(("%s [%p] button: %d\n", __PRETTY_FUNCTION__, (void*)this, domButton));
nsEventStatus status = DispatchEvent(&event);
// right menu click on linux should also pop up a context menu
if (domButton == WidgetMouseEvent::eRightButton &&
MOZ_LIKELY(!mIsDestroyed)) {
WidgetMouseEvent contextMenuEvent(true, NS_CONTEXTMENU, this,
WidgetMouseEvent::eReal);
2008-04-19 19:38:12 +04:00
InitButtonEvent(contextMenuEvent, aEvent, 1);
DispatchEvent(&contextMenuEvent, status);
}
return status;
}
nsEventStatus
nsWindow::mouseReleaseEvent(QMouseEvent* aEvent)
{
// The user has done something.
UserActivity();
uint16_t domButton;
switch (aEvent->button()) {
case Qt::MidButton:
domButton = WidgetMouseEvent::eMiddleButton;
break;
case Qt::RightButton:
domButton = WidgetMouseEvent::eRightButton;
break;
default:
domButton = WidgetMouseEvent::eLeftButton;
break;
}
LOG(("%s [%p] button: %d\n", __PRETTY_FUNCTION__, (void*)this, domButton));
WidgetMouseEvent event(true, NS_MOUSE_BUTTON_UP, this,
WidgetMouseEvent::eReal);
event.button = domButton;
2008-04-19 19:38:12 +04:00
InitButtonEvent(event, aEvent, 1);
nsEventStatus status = DispatchEvent(&event);
return status;
}
nsEventStatus
nsWindow::mouseDoubleClickEvent(QMouseEvent* aEvent)
{
uint32_t eventType;
switch (aEvent->button()) {
case Qt::MidButton:
eventType = WidgetMouseEvent::eMiddleButton;
break;
case Qt::RightButton:
eventType = WidgetMouseEvent::eRightButton;
break;
default:
eventType = WidgetMouseEvent::eLeftButton;
break;
}
WidgetMouseEvent event(true, NS_MOUSE_DOUBLECLICK, this,
WidgetMouseEvent::eReal);
event.button = eventType;
InitButtonEvent(event, aEvent, 2);
//pressed
return DispatchEvent(&event);
}
nsEventStatus
nsWindow::focusInEvent(QFocusEvent* aEvent)
{
LOGFOCUS(("OnFocusInEvent [%p]\n", (void *)this));
if (!mWidget) {
return nsEventStatus_eIgnore;
}
DispatchActivateEventOnTopLevelWindow();
LOGFOCUS(("Events sent from focus in event [%p]\n", (void *)this));
return nsEventStatus_eIgnore;
}
nsEventStatus
nsWindow::focusOutEvent(QFocusEvent* aEvent)
{
LOGFOCUS(("OnFocusOutEvent [%p]\n", (void *)this));
if (!mWidget) {
return nsEventStatus_eIgnore;
}
DispatchDeactivateEventOnTopLevelWindow();
LOGFOCUS(("Done with container focus out [%p]\n", (void *)this));
return nsEventStatus_eIgnore;
}
nsEventStatus
nsWindow::keyPressEvent(QKeyEvent* aEvent)
{
LOGFOCUS(("OnKeyPressEvent [%p]\n", (void *)this));
// The user has done something.
UserActivity();
if (aEvent->key() == Qt::Key_AltGr) {
sAltGrModifier = true;
}
CodeNameIndex codeNameIndex =
ScanCodeToDOMCodeNameIndex(aEvent->nativeScanCode());
#ifdef MOZ_X11
// before we dispatch a key, check if it's the context menu key.
// If so, send a context menu key event instead.
if (isContextMenuKeyEvent(aEvent)) {
WidgetMouseEvent contextMenuEvent(true, NS_CONTEXTMENU, this,
WidgetMouseEvent::eReal,
WidgetMouseEvent::eContextMenuKey);
//keyEventToContextMenuEvent(&event, &contextMenuEvent);
return DispatchEvent(&contextMenuEvent);
}
uint32_t domCharCode = 0;
uint32_t domKeyCode = QtKeyCodeToDOMKeyCode(aEvent->key());
// get keymap and modifier map from the Xserver
Display *display = gfxQtPlatform::GetXDisplay(mWidget);
int x_min_keycode = 0, x_max_keycode = 0, xkeysyms_per_keycode;
XDisplayKeycodes(display, &x_min_keycode, &x_max_keycode);
XModifierKeymap *xmodmap = XGetModifierMapping(display);
if (!xmodmap)
return nsEventStatus_eIgnore;
KeySym *xkeymap = XGetKeyboardMapping(display, x_min_keycode, x_max_keycode - x_min_keycode,
&xkeysyms_per_keycode);
if (!xkeymap) {
XFreeModifiermap(xmodmap);
return nsEventStatus_eIgnore;
}
// create modifier masks
qint32 shift_mask = 0, shift_lock_mask = 0, caps_lock_mask = 0, num_lock_mask = 0;
for (int i = 0; i < 8 * xmodmap->max_keypermod; ++i) {
qint32 maskbit = 1 << (i / xmodmap->max_keypermod);
KeyCode modkeycode = xmodmap->modifiermap[i];
if (modkeycode == NoSymbol) {
continue;
}
quint32 mapindex = (modkeycode - x_min_keycode) * xkeysyms_per_keycode;
for (int j = 0; j < xkeysyms_per_keycode; ++j) {
KeySym modkeysym = xkeymap[mapindex + j];
switch (modkeysym) {
case XK_Num_Lock:
num_lock_mask |= maskbit;
break;
case XK_Caps_Lock:
caps_lock_mask |= maskbit;
break;
case XK_Shift_Lock:
shift_lock_mask |= maskbit;
break;
case XK_Shift_L:
case XK_Shift_R:
shift_mask |= maskbit;
break;
}
}
}
// indicate whether is down or not
bool shift_state = ((shift_mask & aEvent->nativeModifiers()) != 0) ^
(bool)(shift_lock_mask & aEvent->nativeModifiers());
bool capslock_state = (bool)(caps_lock_mask & aEvent->nativeModifiers());
// try to find a keysym that we can translate to a DOMKeyCode
// this is needed because some of Qt's keycodes cannot be translated
// TODO: use US keyboard keymap instead of localised keymap
if (!domKeyCode &&
aEvent->nativeScanCode() >= (quint32)x_min_keycode &&
aEvent->nativeScanCode() <= (quint32)x_max_keycode) {
int index = (aEvent->nativeScanCode() - x_min_keycode) * xkeysyms_per_keycode;
for(int i = 0; (i < xkeysyms_per_keycode) && (domKeyCode == (quint32)NoSymbol); ++i) {
domKeyCode = QtKeyCodeToDOMKeyCode(xkeymap[index + i]);
}
}
// store character in domCharCode
if (aEvent->text().length() && aEvent->text()[0].isPrint())
domCharCode = (int32_t) aEvent->text()[0].unicode();
KeyNameIndex keyNameIndex =
domCharCode ? KEY_NAME_INDEX_PrintableKey :
QtKeyCodeToDOMKeyNameIndex(aEvent->key());
// If the key isn't autorepeat, we need to send the initial down event
if (!aEvent->isAutoRepeat() && !IsKeyDown(domKeyCode)) {
// send the key down event
SetKeyDownFlag(domKeyCode);
WidgetKeyboardEvent downEvent(true, NS_KEY_DOWN, this);
InitKeyEvent(downEvent, aEvent);
downEvent.keyCode = domKeyCode;
downEvent.mKeyNameIndex = keyNameIndex;
downEvent.mCodeNameIndex = codeNameIndex;
nsEventStatus status = DispatchEvent(&downEvent);
// DispatchEvent can Destroy us (bug 378273)
if (MOZ_UNLIKELY(mIsDestroyed)) {
qWarning() << "Returning[" << __LINE__ << "]: " << "Window destroyed";
return status;
}
// If prevent default on keydown, don't dispatch keypress event
if (status == nsEventStatus_eConsumeNoDefault) {
return nsEventStatus_eConsumeNoDefault;
}
}
// Don't pass modifiers as NS_KEY_PRESS events.
// Instead of selectively excluding some keys from NS_KEY_PRESS events,
// we instead selectively include (as per MSDN spec
// ( http://msdn.microsoft.com/en-us/library/system.windows.forms.control.keypress%28VS.71%29.aspx );
// no official spec covers KeyPress events).
if (aEvent->key() == Qt::Key_Shift ||
aEvent->key() == Qt::Key_Control ||
aEvent->key() == Qt::Key_Meta ||
aEvent->key() == Qt::Key_Alt ||
aEvent->key() == Qt::Key_AltGr) {
return nsEventStatus_eIgnore;
}
// Look for specialized app-command keys
switch (aEvent->key()) {
case Qt::Key_Back:
return DispatchCommandEvent(nsGkAtoms::Back);
case Qt::Key_Forward:
return DispatchCommandEvent(nsGkAtoms::Forward);
case Qt::Key_Refresh:
return DispatchCommandEvent(nsGkAtoms::Reload);
case Qt::Key_Stop:
return DispatchCommandEvent(nsGkAtoms::Stop);
case Qt::Key_Search:
return DispatchCommandEvent(nsGkAtoms::Search);
case Qt::Key_Favorites:
return DispatchCommandEvent(nsGkAtoms::Bookmarks);
case Qt::Key_HomePage:
return DispatchCommandEvent(nsGkAtoms::Home);
case Qt::Key_Copy:
case Qt::Key_F16: // F16, F20, F18, F14 are old keysyms for Copy Cut Paste Undo
return DispatchContentCommandEvent(NS_CONTENT_COMMAND_COPY);
case Qt::Key_Cut:
case Qt::Key_F20:
return DispatchContentCommandEvent(NS_CONTENT_COMMAND_CUT);
case Qt::Key_Paste:
case Qt::Key_F18:
case Qt::Key_F9:
return DispatchContentCommandEvent(NS_CONTENT_COMMAND_PASTE);
case Qt::Key_F14:
return DispatchContentCommandEvent(NS_CONTENT_COMMAND_UNDO);
}
// Qt::Key_Redo and Qt::Key_Undo are not available yet.
if (aEvent->nativeVirtualKey() == 0xff66) {
return DispatchContentCommandEvent(NS_CONTENT_COMMAND_REDO);
}
if (aEvent->nativeVirtualKey() == 0xff65) {
return DispatchContentCommandEvent(NS_CONTENT_COMMAND_UNDO);
}
WidgetKeyboardEvent event(true, NS_KEY_PRESS, this);
InitKeyEvent(event, aEvent);
// If there is no charcode attainable from the text, try to
// generate it from the keycode. Check shift state for case
// Also replace the charcode if ControlModifier is the only
// pressed Modifier
if ((!domCharCode) &&
(QGuiApplication::keyboardModifiers() &
(Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier))) {
// get a character from X11 key map
KeySym keysym = aEvent->nativeVirtualKey();
if (keysym) {
domCharCode = (uint32_t) keysym2ucs(keysym);
if (domCharCode == -1 || !QChar((quint32)domCharCode).isPrint()) {
domCharCode = 0;
}
}
// if Ctrl is pressed and domCharCode is not a ASCII character
if (domCharCode > 0xFF && (QGuiApplication::keyboardModifiers() & Qt::ControlModifier)) {
// replace Unicode character
int index = (aEvent->nativeScanCode() - x_min_keycode) * xkeysyms_per_keycode;
for (int i = 0; i < xkeysyms_per_keycode; ++i) {
if (xkeymap[index + i] <= 0xFF && !shift_state) {
domCharCode = (uint32_t) QChar::toLower((uint) xkeymap[index + i]);
break;
}
}
}
} else { // The key event should cause a character input.
// At that time, we need to reset the modifiers
// because nsEditor will not accept a key event
// for text input if one or more modifiers are set.
event.modifiers &= ~(MODIFIER_CONTROL |
MODIFIER_ALT |
MODIFIER_META);
}
KeySym keysym = NoSymbol;
int index = (aEvent->nativeScanCode() - x_min_keycode) * xkeysyms_per_keycode;
for (int i = 0; i < xkeysyms_per_keycode; ++i) {
if (xkeymap[index + i] == aEvent->nativeVirtualKey()) {
if ((i % 2) == 0) { // shifted char
keysym = xkeymap[index + i + 1];
break;
} else { // unshifted char
keysym = xkeymap[index + i - 1];
break;
}
}
if (xkeysyms_per_keycode - 1 == i) {
qWarning() << "Symbol '" << aEvent->nativeVirtualKey() << "' not found";
}
}
QChar unshiftedChar(domCharCode);
long ucs = keysym2ucs(keysym);
ucs = ucs == -1 ? 0 : ucs;
QChar shiftedChar((uint)ucs);
// append alternativeCharCodes if modifier is pressed
// append an additional alternativeCharCodes if domCharCode is not a Latin character
// and if one of these modifiers is pressed (i.e. Ctrl, Alt, Meta)
if (domCharCode &&
(QGuiApplication::keyboardModifiers() &
(Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier))) {
event.charCode = domCharCode;
event.keyCode = 0;
AlternativeCharCode altCharCode(0, 0);
// if character has a lower and upper representation
if ((unshiftedChar.isUpper() || unshiftedChar.isLower()) &&
unshiftedChar.toLower() == shiftedChar.toLower()) {
if (shift_state ^ capslock_state) {
altCharCode.mUnshiftedCharCode = (uint32_t) QChar::toUpper((uint)domCharCode);
altCharCode.mShiftedCharCode = (uint32_t) QChar::toLower((uint)domCharCode);
} else {
altCharCode.mUnshiftedCharCode = (uint32_t) QChar::toLower((uint)domCharCode);
altCharCode.mShiftedCharCode = (uint32_t) QChar::toUpper((uint)domCharCode);
}
} else {
altCharCode.mUnshiftedCharCode = (uint32_t) unshiftedChar.unicode();
altCharCode.mShiftedCharCode = (uint32_t) shiftedChar.unicode();
}
// append alternative char code to event
if ((altCharCode.mUnshiftedCharCode && altCharCode.mUnshiftedCharCode != domCharCode) ||
(altCharCode.mShiftedCharCode && altCharCode.mShiftedCharCode != domCharCode)) {
event.alternativeCharCodes.AppendElement(altCharCode);
}
// check if the alternative char codes are latin-1
if (altCharCode.mUnshiftedCharCode > 0xFF || altCharCode.mShiftedCharCode > 0xFF) {
altCharCode.mUnshiftedCharCode = altCharCode.mShiftedCharCode = 0;
// find latin char for keycode
KeySym keysym = NoSymbol;
int index = (aEvent->nativeScanCode() - x_min_keycode) * xkeysyms_per_keycode;
// find first shifted and unshifted Latin-Char in XKeyMap
for (int i = 0; i < xkeysyms_per_keycode; ++i) {
keysym = xkeymap[index + i];
if (keysym && keysym <= 0xFF) {
if ((shift_state && (i % 2 == 1)) ||
(!shift_state && (i % 2 == 0))) {
altCharCode.mUnshiftedCharCode = altCharCode.mUnshiftedCharCode ?
altCharCode.mUnshiftedCharCode :
keysym;
} else {
altCharCode.mShiftedCharCode = altCharCode.mShiftedCharCode ?
altCharCode.mShiftedCharCode :
keysym;
}
if (altCharCode.mUnshiftedCharCode && altCharCode.mShiftedCharCode) {
break;
}
}
}
if (altCharCode.mUnshiftedCharCode || altCharCode.mShiftedCharCode) {
event.alternativeCharCodes.AppendElement(altCharCode);
}
}
} else {
event.charCode = domCharCode;
}
if (xmodmap) {
XFreeModifiermap(xmodmap);
}
if (xkeymap) {
XFree(xkeymap);
}
event.keyCode = domCharCode ? 0 : domKeyCode;
event.mKeyNameIndex = keyNameIndex;
event.mCodeNameIndex = codeNameIndex;
// send the key press event
return DispatchEvent(&event);
#else
//:TODO: fix shortcuts hebrew for non X11,
//see Bug 562195##51
// before we dispatch a key, check if it's the context menu key.
// If so, send a context menu key event instead.
if (isContextMenuKeyEvent(aEvent)) {
WidgetMouseEvent contextMenuEvent(true, NS_CONTEXTMENU, this,
WidgetMouseEvent::eReal,
WidgetMouseEvent::eContextMenuKey);
//keyEventToContextMenuEvent(&event, &contextMenuEvent);
return DispatchEvent(&contextMenuEvent);
}
uint32_t domCharCode = 0;
uint32_t domKeyCode = QtKeyCodeToDOMKeyCode(aEvent->key());
if (aEvent->text().length() && aEvent->text()[0].isPrint()) {
domCharCode = (int32_t) aEvent->text()[0].unicode();
}
KeyNameIndex keyNameIndex =
domCharCode ? KEY_NAME_INDEX_PrintableKey :
QtKeyCodeToDOMKeyNameIndex(aEvent->key());
// If the key isn't autorepeat, we need to send the initial down event
if (!aEvent->isAutoRepeat() && !IsKeyDown(domKeyCode)) {
// send the key down event
SetKeyDownFlag(domKeyCode);
WidgetKeyboardEvent downEvent(true, NS_KEY_DOWN, this);
InitKeyEvent(downEvent, aEvent);
downEvent.keyCode = domKeyCode;
downEvent.mKeyNameIndex = keyNameIndex;
downEvent.mCodeNameIndex = codeNameIndex;
nsEventStatus status = DispatchEvent(&downEvent);
// If prevent default on keydown, don't dispatch keypress event
if (status == nsEventStatus_eConsumeNoDefault) {
return nsEventStatus_eConsumeNoDefault;
}
}
WidgetKeyboardEvent event(true, NS_KEY_PRESS, this);
InitKeyEvent(event, aEvent);
event.charCode = domCharCode;
event.keyCode = domCharCode ? 0 : domKeyCode;
event.mKeyNameIndex = keyNameIndex;
event.mCodeNameIndex = codeNameIndex;
// send the key press event
return DispatchEvent(&event);
#endif
}
nsEventStatus
nsWindow::keyReleaseEvent(QKeyEvent* aEvent)
{
LOGFOCUS(("OnKeyReleaseEvent [%p]\n", (void *)this));
// The user has done something.
UserActivity();
if (isContextMenuKeyEvent(aEvent)) {
// er, what do we do here? DoDefault or NoDefault?
return nsEventStatus_eConsumeDoDefault;
}
uint32_t domKeyCode = QtKeyCodeToDOMKeyCode(aEvent->key());
#ifdef MOZ_X11
if (!domKeyCode) {
// get keymap from the Xserver
Display *display = gfxQtPlatform::GetXDisplay(mWidget);
int x_min_keycode = 0, x_max_keycode = 0, xkeysyms_per_keycode;
XDisplayKeycodes(display, &x_min_keycode, &x_max_keycode);
KeySym *xkeymap = XGetKeyboardMapping(display, x_min_keycode, x_max_keycode - x_min_keycode,
&xkeysyms_per_keycode);
if (aEvent->nativeScanCode() >= (quint32)x_min_keycode &&
aEvent->nativeScanCode() <= (quint32)x_max_keycode) {
int index = (aEvent->nativeScanCode() - x_min_keycode) * xkeysyms_per_keycode;
for(int i = 0; (i < xkeysyms_per_keycode) && (domKeyCode == (quint32)NoSymbol); ++i) {
domKeyCode = QtKeyCodeToDOMKeyCode(xkeymap[index + i]);
}
}
if (xkeymap) {
XFree(xkeymap);
}
}
#endif // MOZ_X11
// send the key event as a key up event
WidgetKeyboardEvent event(true, NS_KEY_UP, this);
InitKeyEvent(event, aEvent);
if (aEvent->key() == Qt::Key_AltGr) {
sAltGrModifier = false;
}
event.keyCode = domKeyCode;
event.mKeyNameIndex =
(aEvent->text().length() && aEvent->text()[0].isPrint()) ?
KEY_NAME_INDEX_PrintableKey :
QtKeyCodeToDOMKeyNameIndex(aEvent->key());
event.mCodeNameIndex =
ScanCodeToDOMCodeNameIndex(aEvent->nativeScanCode());
// unset the key down flag
ClearKeyDownFlag(event.keyCode);
return DispatchEvent(&event);
}
nsEventStatus
nsWindow::wheelEvent(QWheelEvent* aEvent)
{
// check to see if we should rollup
WidgetWheelEvent wheelEvent(true, NS_WHEEL_WHEEL, this);
wheelEvent.deltaMode = nsIDOMWheelEvent::DOM_DELTA_LINE;
// negative values for aEvent->delta indicate downward scrolling;
// this is opposite Gecko usage.
// TODO: Store the unused delta values due to fraction round and add it
// to next event. The stored values should be reset by other
// direction scroll event.
int32_t delta = (int)(aEvent->delta() / WHEEL_DELTA) * -3;
switch (aEvent->orientation()) {
case Qt::Vertical:
wheelEvent.deltaY = wheelEvent.lineOrPageDeltaY = delta;
break;
case Qt::Horizontal:
wheelEvent.deltaX = wheelEvent.lineOrPageDeltaX = delta;
break;
default:
Q_ASSERT(0);
break;
}
wheelEvent.refPoint.x = nscoord(aEvent->pos().x());
wheelEvent.refPoint.y = nscoord(aEvent->pos().y());
wheelEvent.InitBasicModifiers(aEvent->modifiers() & Qt::ControlModifier,
aEvent->modifiers() & Qt::AltModifier,
aEvent->modifiers() & Qt::ShiftModifier,
aEvent->modifiers() & Qt::MetaModifier);
wheelEvent.time = 0;
return DispatchEvent(&wheelEvent);
}
nsEventStatus
nsWindow::showEvent(QShowEvent *)
{
LOG(("%s [%p]\n", __PRETTY_FUNCTION__,(void *)this));
mVisible = true;
return nsEventStatus_eConsumeDoDefault;
}
nsEventStatus
nsWindow::hideEvent(QHideEvent *)
{
LOG(("%s [%p]\n", __PRETTY_FUNCTION__,(void *)this));
mVisible = false;
return nsEventStatus_eConsumeDoDefault;
}
nsEventStatus nsWindow::touchEvent(QTouchEvent* aEvent)
{
return nsEventStatus_eIgnore;
}
nsEventStatus
nsWindow::tabletEvent(QTabletEvent* aEvent)
{
LOGFOCUS(("nsWindow::%s [%p]\n", __FUNCTION__, (void *)this));
return nsEventStatus_eIgnore;
}
// Helpers
void
nsWindow::InitButtonEvent(WidgetMouseEvent& aMoveEvent,
QMouseEvent* aEvent,
int aClickCount)
{
aMoveEvent.refPoint.x = nscoord(aEvent->pos().x());
aMoveEvent.refPoint.y = nscoord(aEvent->pos().y());
aMoveEvent.InitBasicModifiers(aEvent->modifiers() & Qt::ControlModifier,
aEvent->modifiers() & Qt::AltModifier,
aEvent->modifiers() & Qt::ShiftModifier,
aEvent->modifiers() & Qt::MetaModifier);
aMoveEvent.clickCount = aClickCount;
}
nsEventStatus
nsWindow::DispatchEvent(WidgetGUIEvent* aEvent)
{
nsEventStatus status;
DispatchEvent(aEvent, status);
return status;
}
void
nsWindow::DispatchActivateEvent(void)
{
if (mWidgetListener) {
mWidgetListener->WindowActivated();
}
}
void
nsWindow::DispatchDeactivateEvent(void)
{
if (mWidgetListener) {
mWidgetListener->WindowDeactivated();
}
}
void
nsWindow::DispatchActivateEventOnTopLevelWindow(void)
{
nsWindow* topLevelWindow = static_cast<nsWindow*>(GetTopLevelWidget());
if (topLevelWindow != nullptr) {
topLevelWindow->DispatchActivateEvent();
}
}
void
nsWindow::DispatchDeactivateEventOnTopLevelWindow(void)
{
nsWindow* topLevelWindow = static_cast<nsWindow*>(GetTopLevelWidget());
if (topLevelWindow != nullptr) {
topLevelWindow->DispatchDeactivateEvent();
}
}
void
nsWindow::DispatchResizeEvent(nsIntRect &aRect, nsEventStatus &aStatus)
{
aStatus = nsEventStatus_eIgnore;
if (mWidgetListener &&
mWidgetListener->WindowResized(this, aRect.width, aRect.height)) {
aStatus = nsEventStatus_eConsumeNoDefault;
}
}
///////////////////////////////////// OLD GECKO ECENTS need to Sort ///////////////////
/* static */ bool
isContextMenuKeyEvent(const QKeyEvent *qe)
{
uint32_t kc = QtKeyCodeToDOMKeyCode(qe->key());
if (qe->modifiers() & (Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier)) {
return false;
}
bool isShift = qe->modifiers() & Qt::ShiftModifier;
return (kc == NS_VK_F10 && isShift) ||
(kc == NS_VK_CONTEXT_MENU && !isShift);
}
/* static */void
InitKeyEvent(WidgetKeyboardEvent &aEvent, QKeyEvent *aQEvent)
{
aEvent.InitBasicModifiers(aQEvent->modifiers() & Qt::ControlModifier,
aQEvent->modifiers() & Qt::AltModifier,
aQEvent->modifiers() & Qt::ShiftModifier,
aQEvent->modifiers() & Qt::MetaModifier);
aEvent.mIsRepeat =
(aEvent.message == NS_KEY_DOWN || aEvent.message == NS_KEY_PRESS) &&
aQEvent->isAutoRepeat();
aEvent.time = 0;
if (sAltGrModifier) {
aEvent.modifiers |= (MODIFIER_CONTROL | MODIFIER_ALT);
}
// The transformations above and in qt for the keyval are not invertible
// so link to the QKeyEvent (which will vanish soon after return from the
// event callback) to give plugins access to hardware_keycode and state.
// (An XEvent would be nice but the QKeyEvent is good enough.)
aEvent.pluginEvent = (void *)aQEvent;
}
NS_IMPL_ISUPPORTS_INHERITED(nsWindow, nsBaseWidget, nsISupportsWeakReference)
void
nsWindow::ClearCachedResources()
{
if (mLayerManager &&
mLayerManager->GetBackendType() == mozilla::layers::LayersBackend::LAYERS_BASIC) {
mLayerManager->ClearCachedResources();
}
for (nsIWidget* kid = mFirstChild; kid; ) {
nsIWidget* next = kid->GetNextSibling();
static_cast<nsWindow*>(kid)->ClearCachedResources();
kid = next;
}
}
NS_IMETHODIMP
nsWindow::SetParent(nsIWidget *aNewParent)
{
NS_ENSURE_ARG_POINTER(aNewParent);
nsCOMPtr<nsIWidget> kungFuDeathGrip(this);
nsIWidget* parent = GetParent();
if (parent) {
parent->RemoveChild(this);
}
ReparentNativeWidget(aNewParent);
aNewParent->AddChild(this);
return NS_OK;
}
NS_IMETHODIMP
nsWindow::SetModal(bool aModal)
{
LOG(("nsWindow::SetModal [%p] %d, widget[%p]\n", (void *)this, aModal, mWidget));
if (mWidget) {
mWidget->setModality(aModal ? Qt::WindowModal : Qt::NonModal);
}
return NS_OK;
}
NS_IMETHODIMP
nsWindow::PlaceBehind(nsTopLevelWidgetZPlacement aPlacement,
nsIWidget *aWidget,
bool aActivate)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsWindow::SetSizeMode(int32_t aMode)
{
nsresult rv;
LOG(("nsWindow::SetSizeMode [%p] %d\n", (void *)this, aMode));
if (aMode != nsSizeMode_Minimized) {
mWidget->requestActivate();
}
// Save the requested state.
rv = nsBaseWidget::SetSizeMode(aMode);
// return if there's no shell or our current state is the same as
// the mode we were just set to.
if (!mWidget || mSizeState == mSizeMode) {
return rv;
}
switch (aMode) {
case nsSizeMode_Maximized:
mWidget->showMaximized();
break;
case nsSizeMode_Minimized:
mWidget->showMinimized();
break;
case nsSizeMode_Fullscreen:
mWidget->showFullScreen();
break;
default:
// nsSizeMode_Normal, really.
mWidget->show();
break;
}
mSizeState = mSizeMode;
return rv;
}
// Helper function to recursively find the first parent item that
// is still visible (QGraphicsItem can be hidden even if they are
// set to visible if one of their ancestors is invisible)
/* static */
void find_first_visible_parent(QWindow* aItem, QWindow*& aVisibleItem)
{
NS_ENSURE_TRUE_VOID(aItem);
aVisibleItem = nullptr;
QWindow* parItem = nullptr;
while (!aVisibleItem) {
if (aItem->isVisible()) {
aVisibleItem = aItem;
}
else {
parItem = aItem->parent();
if (parItem) {
aItem = parItem;
}
else {
aItem->setVisible(true);
aVisibleItem = aItem;
}
}
}
}
NS_IMETHODIMP
nsWindow::GetScreenBounds(nsIntRect &aRect)
{
aRect = nsIntRect(nsIntPoint(0, 0), mBounds.Size());
if (mIsTopLevel) {
QPoint pos = mWidget->position();
aRect.MoveTo(pos.x(), pos.y());
}
else {
aRect.MoveTo(WidgetToScreenOffset());
}
LOG(("GetScreenBounds %d %d | %d %d | %d %d\n",
aRect.x, aRect.y,
mBounds.width, mBounds.height,
aRect.width, aRect.height));
return NS_OK;
}
NS_IMETHODIMP
nsWindow::SetIcon(const nsAString& aIconSpec)
{
if (!mWidget)
return NS_OK;
nsCOMPtr<nsIFile> iconFile;
nsAutoCString path;
nsTArray<nsCString> iconList;
// Look for icons with the following suffixes appended to the base name.
// The last two entries (for the old XPM format) will be ignored unless
// no icons are found using the other suffixes. XPM icons are depricated.
const char extensions[6][7] = { ".png", "16.png", "32.png", "48.png",
".xpm", "16.xpm" };
for (uint32_t i = 0; i < ArrayLength(extensions); i++) {
// Don't bother looking for XPM versions if we found a PNG.
if (i == ArrayLength(extensions) - 2 && iconList.Length())
break;
nsAutoString extension;
extension.AppendASCII(extensions[i]);
ResolveIconName(aIconSpec, extension, getter_AddRefs(iconFile));
if (iconFile) {
iconFile->GetNativePath(path);
iconList.AppendElement(path);
}
}
// leave the default icon intact if no matching icons were found
if (iconList.Length() == 0)
return NS_OK;
return SetWindowIconList(iconList);
}
NS_IMETHODIMP
nsWindow::CaptureMouse(bool aCapture)
{
LOG(("CaptureMouse %p\n", (void *)this));
if (!mWidget)
return NS_OK;
mWidget->setMouseGrabEnabled(aCapture);
return NS_OK;
}
bool
nsWindow::CheckForRollup(double aMouseX, double aMouseY,
bool aIsWheel)
{
nsIRollupListener* rollupListener = GetActiveRollupListener();
nsCOMPtr<nsIWidget> rollupWidget;
if (rollupListener) {
rollupWidget = rollupListener->GetRollupWidget();
}
if (!rollupWidget) {
nsBaseWidget::gRollupListener = nullptr;
return false;
}
bool retVal = false;
MozQWidget *currentPopup =
(MozQWidget *)rollupWidget->GetNativeData(NS_NATIVE_WINDOW);
if (!is_mouse_in_window(currentPopup, aMouseX, aMouseY)) {
bool rollup = true;
if (aIsWheel) {
rollup = rollupListener->ShouldRollupOnMouseWheelEvent();
retVal = true;
}
// if we're dealing with menus, we probably have submenus and
// we don't want to rollup if the clickis in a parent menu of
// the current submenu
uint32_t popupsToRollup = UINT32_MAX;
if (rollupListener) {
nsAutoTArray<nsIWidget*, 5> widgetChain;
uint32_t sameTypeCount = rollupListener->GetSubmenuWidgetChain(&widgetChain);
for (uint32_t i=0; i<widgetChain.Length(); ++i) {
nsIWidget* widget = widgetChain[i];
MozQWidget* currWindow =
(MozQWidget*) widget->GetNativeData(NS_NATIVE_WINDOW);
if (is_mouse_in_window(currWindow, aMouseX, aMouseY)) {
if (i < sameTypeCount) {
rollup = false;
}
else {
popupsToRollup = sameTypeCount;
}
break;
}
} // foreach parent menu widget
} // if rollup listener knows about menus
// if we've determined that we should still rollup, do it.
if (rollup) {
nsIntPoint pos(aMouseX, aMouseY);
retVal = rollupListener->Rollup(popupsToRollup, &pos, nullptr);
}
}
return retVal;
}
/* static */
bool
is_mouse_in_window (MozQWidget* aWindow, double aMouseX, double aMouseY)
{
return aWindow->geometry().contains(aMouseX, aMouseY);
}
NS_IMETHODIMP
nsWindow::GetAttention(int32_t aCycleCount)
{
LOG(("nsWindow::GetAttention [%p]\n", (void *)this));
return NS_ERROR_NOT_IMPLEMENTED;
}
nsEventStatus
nsWindow::OnCloseEvent(QCloseEvent *aEvent)
{
if (!mWidgetListener)
return nsEventStatus_eIgnore;
mWidgetListener->RequestWindowClose(this);
return nsEventStatus_eConsumeNoDefault;
}
inline bool
is_latin_shortcut_key(quint32 aKeyval)
{
return ((Qt::Key_0 <= aKeyval && aKeyval <= Qt::Key_9) ||
(Qt::Key_A <= aKeyval && aKeyval <= Qt::Key_Z));
}
nsEventStatus
nsWindow::DispatchCommandEvent(nsIAtom* aCommand)
{
WidgetCommandEvent event(true, nsGkAtoms::onAppCommand, aCommand, this);
nsEventStatus status;
DispatchEvent(&event, status);
return status;
}
nsEventStatus
nsWindow::DispatchContentCommandEvent(int32_t aMsg)
{
WidgetContentCommandEvent event(true, aMsg, this);
nsEventStatus status;
DispatchEvent(&event, status);
return status;
}
static void
GetBrandName(nsXPIDLString& brandName)
{
nsCOMPtr<nsIStringBundleService> bundleService =
mozilla::services::GetStringBundleService();
nsCOMPtr<nsIStringBundle> bundle;
if (bundleService) {
bundleService->CreateBundle(
"chrome://branding/locale/brand.properties",
getter_AddRefs(bundle));
}
if (bundle) {
bundle->GetStringFromName(
MOZ_UTF16("brandShortName"),
getter_Copies(brandName));
}
if (brandName.IsEmpty()) {
brandName.AssignLiteral("Mozilla");
}
}
NS_IMETHODIMP
nsWindow::SetWindowClass(const nsAString &xulWinType)
{
if (!mWidget) {
return NS_ERROR_FAILURE;
}
nsXPIDLString brandName;
GetBrandName(brandName);
#ifdef MOZ_X11
XClassHint *class_hint = XAllocClassHint();
if (!class_hint) {
return NS_ERROR_OUT_OF_MEMORY;
}
const char *role = nullptr;
class_hint->res_name = ToNewCString(xulWinType);
if (!class_hint->res_name) {
XFree(class_hint);
return NS_ERROR_OUT_OF_MEMORY;
}
class_hint->res_class = ToNewCString(brandName);
if (!class_hint->res_class) {
nsMemory::Free(class_hint->res_name);
XFree(class_hint);
return NS_ERROR_OUT_OF_MEMORY;
}
// Parse res_name into a name and role. Characters other than
// [A-Za-z0-9_-] are converted to '_'. Anything after the first
// colon is assigned to role; if there's no colon, assign the
// whole thing to both role and res_name.
for (char *c = class_hint->res_name; *c; c++) {
if (':' == *c) {
*c = 0;
role = c + 1;
}
else if (!isascii(*c) || (!isalnum(*c) && ('_' != *c) && ('-' != *c)))
*c = '_';
}
class_hint->res_name[0] = toupper(class_hint->res_name[0]);
if (!role) role = class_hint->res_name;
QWindow *widget = mWidget;
// If widget not show, handle might be null
if (widget && widget->winId()) {
XSetClassHint(gfxQtPlatform::GetXDisplay(widget),
widget->winId(),
class_hint);
}
nsMemory::Free(class_hint->res_class);
nsMemory::Free(class_hint->res_name);
XFree(class_hint);
#endif
return NS_OK;
}
void
nsWindow::NativeResize(int32_t aWidth, int32_t aHeight, bool aRepaint)
{
LOG(("nsWindow::NativeResize [%p] %d %d\n", (void *)this,
aWidth, aHeight));
mNeedsResize = false;
mWidget->resize(aWidth, aHeight);
if (aRepaint) {
mWidget->renderLater();
}
}
void
nsWindow::NativeResize(int32_t aX, int32_t aY,
int32_t aWidth, int32_t aHeight,
bool aRepaint)
{
LOG(("nsWindow::NativeResize [%p] %d %d %d %d\n", (void *)this,
aX, aY, aWidth, aHeight));
mNeedsResize = false;
mNeedsMove = false;
mWidget->setGeometry(aX, aY, aWidth, aHeight);
if (aRepaint) {
mWidget->renderLater();
}
}
void
nsWindow::NativeShow(bool aAction)
{
if (aAction) {
// On e10s, we never want the child process or plugin process
// to go fullscreen because if we do the window because visible
// do to disabled Qt-Xembed
mWidget->show();
// unset our flag now that our window has been shown
mNeedsShow = false;
}
else {
mWidget->hide();
}
}
NS_IMETHODIMP
nsWindow::SetHasTransparentBackground(bool aTransparent)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsWindow::GetHasTransparentBackground(bool& aTransparent)
{
aTransparent = mIsTransparent;
return NS_OK;
}
void *
nsWindow::SetupPluginPort(void)
{
NS_WARNING("Not implemented");
return nullptr;
}
nsresult
nsWindow::SetWindowIconList(const nsTArray<nsCString> &aIconList)
{
QIcon icon;
for (uint32_t i = 0; i < aIconList.Length(); ++i) {
const char *path = aIconList[i].get();
LOG(("window [%p] Loading icon from %s\n", (void *)this, path));
icon.addFile(path);
}
mWidget->setIcon(icon);
return NS_OK;
}
void
nsWindow::SetDefaultIcon(void)
{
SetIcon(NS_LITERAL_STRING("default"));
}
void nsWindow::QWidgetDestroyed()
{
mWidget = nullptr;
}
NS_IMETHODIMP
nsWindow::HideWindowChrome(bool aShouldHide)
{
if (!mWidget) {
// Nothing to hide
return NS_ERROR_FAILURE;
}
// Sawfish, metacity, and presumably other window managers get
// confused if we change the window decorations while the window
// is visible.
bool wasVisible = false;
if (mWidget->isVisible()) {
NativeShow(false);
wasVisible = true;
}
if (wasVisible) {
NativeShow(true);
}
return NS_OK;
}
//////////////////////////////////////////////////////////////////////
NS_IMETHODIMP_(bool)
nsWindow::HasGLContext()
{
return false;
}
nsIWidget*
nsWindow::GetParent(void)
{
return mParent;
}
float
nsWindow::GetDPI()
{
return qApp->primaryScreen()->logicalDotsPerInch();
}
void
nsWindow::OnDestroy(void)
{
if (mOnDestroyCalled) {
return;
}
mOnDestroyCalled = true;
// release references to children and device context
nsBaseWidget::OnDestroy();
// let go of our parent
mParent = nullptr;
nsCOMPtr<nsIWidget> kungFuDeathGrip = this;
NotifyWindowDestroyed();
}
bool
nsWindow::AreBoundsSane(void)
{
if (mBounds.width > 0 && mBounds.height > 0) {
return true;
}
return false;
}
void
nsWindow::SetSoftwareKeyboardState(bool aOpen,
const InputContextAction& aAction)
{
if (aOpen) {
NS_ENSURE_TRUE_VOID(mInputContext.mIMEState.mEnabled !=
IMEState::DISABLED);
// Ensure that opening the virtual keyboard is allowed for this specific
// InputContext depending on the content.ime.strict.policy pref
if (mInputContext.mIMEState.mEnabled != IMEState::PLUGIN &&
Preferences::GetBool("content.ime.strict_policy", false) &&
!aAction.ContentGotFocusByTrustedCause() &&
!aAction.UserMightRequestOpenVKB()) {
return;
}
}
if (aOpen) {
qApp->inputMethod()->show();
} else {
qApp->inputMethod()->hide();
}
return;
}
void
nsWindow::ProcessMotionEvent()
{
if (mMoveEvent.needDispatch) {
WidgetMouseEvent event(true, NS_MOUSE_MOVE, this,
WidgetMouseEvent::eReal);
event.refPoint.x = nscoord(mMoveEvent.pos.x());
event.refPoint.y = nscoord(mMoveEvent.pos.y());
event.InitBasicModifiers(mMoveEvent.modifiers & Qt::ControlModifier,
mMoveEvent.modifiers & Qt::AltModifier,
mMoveEvent.modifiers & Qt::ShiftModifier,
mMoveEvent.modifiers & Qt::MetaModifier);
event.clickCount = 0;
DispatchEvent(&event);
mMoveEvent.needDispatch = false;
}
mTimerStarted = false;
}