diff -r 000000000000 -r 6474c204b198 widget/qt/nsWindow.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/widget/qt/nsWindow.cpp Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,2256 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim:expandtab:shiftwidth=4:tabstop=4: + */ +/* 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#ifdef MOZ_X11 +#include +#include +#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(aParent->GetNativeData(NS_NATIVE_WIDGET)); + } else if (aNativeParent != nullptr) { + parent = static_cast(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) { + widget->setVisibility(QWindow::Hidden); + } + 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 rollupWidget = rollupListener->GetRollupWidget(); + if (static_cast(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& aConfigurations) +{ + for (uint32_t i = 0; i < aConfigurations.Length(); ++i) { + const Configuration& configuration = aConfigurations[i]; + + nsWindow* w = static_cast(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(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); + 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; + 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; + } + +#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; + + 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; + // 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; + + 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; + + // 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()); + + // 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(GetTopLevelWidget()); + if (topLevelWindow != nullptr) { + topLevelWindow->DispatchActivateEvent(); + } +} + +void +nsWindow::DispatchDeactivateEventOnTopLevelWindow(void) +{ + nsWindow* topLevelWindow = static_cast(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(kid)->ClearCachedResources(); + kid = next; + } +} + +NS_IMETHODIMP +nsWindow::SetParent(nsIWidget *aNewParent) +{ + NS_ENSURE_ARG_POINTER(aNewParent); + + nsCOMPtr 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 iconFile; + nsAutoCString path; + nsTArray 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 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 widgetChain; + uint32_t sameTypeCount = rollupListener->GetSubmenuWidgetChain(&widgetChain); + for (uint32_t i=0; iGetNativeData(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 bundleService = + mozilla::services::GetStringBundleService(); + + nsCOMPtr 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.Assign(NS_LITERAL_STRING("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 &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 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; +} +