gecko-dev/widget/qt/nsWindow.cpp

1989 lines
52 KiB
C++

/* -*- 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 <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>
#include "gfxXlibSurface.h"
#endif //MOZ_X11
#include "nsXULAppAPI.h"
#include "prlink.h"
#include "nsWindow.h"
#include "mozqwidget.h"
#include "nsIdleService.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);
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;
}
TemporaryRef<DrawTarget>
nsWindow::StartRemoteDrawing()
{
if (!mWidget) {
return nullptr;
}
#ifdef MOZ_X11
Display* dpy = gfxQtPlatform::GetXDisplay(mWidget);
Screen* screen = DefaultScreenOfDisplay(dpy);
Visual* defaultVisual = DefaultVisualOfScreen(screen);
gfxASurface* surf = new gfxXlibSurface(dpy, mWidget->winId(), defaultVisual,
gfxIntSize(mWidget->width(),
mWidget->height()));
IntSize size(surf->GetSize().width, surf->GetSize().height);
if (size.width <= 0 || size.height <= 0) {
return nullptr;
}
return gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(surf, size);
#else
return nullptr;
#endif
}
NS_IMETHODIMP
nsWindow::SetCursor(nsCursor aCursor)
{
if (mCursor == aCursor && !mUpdateCursor) {
return NS_OK;
}
mUpdateCursor = false;
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;
}
static void
InitMouseEvent(WidgetMouseEvent& aMouseEvent, QMouseEvent* aEvent,
int aClickCount)
{
aMouseEvent.refPoint.x = nscoord(aEvent->pos().x());
aMouseEvent.refPoint.y = nscoord(aEvent->pos().y());
aMouseEvent.InitBasicModifiers(aEvent->modifiers() & Qt::ControlModifier,
aEvent->modifiers() & Qt::AltModifier,
aEvent->modifiers() & Qt::ShiftModifier,
aEvent->modifiers() & Qt::MetaModifier);
aMouseEvent.clickCount = aClickCount;
switch (aEvent->button()) {
case Qt::LeftButton:
aMouseEvent.button = WidgetMouseEvent::eLeftButton;
break;
case Qt::RightButton:
aMouseEvent.button = WidgetMouseEvent::eRightButton;
break;
case Qt::MiddleButton:
aMouseEvent.button = WidgetMouseEvent::eMiddleButton;
break;
default:
break;
}
}
static bool
IsAcceptedButton(Qt::MouseButton button)
{
switch (button) {
case Qt::LeftButton:
case Qt::RightButton:
case Qt::MiddleButton:
return true;
default:
return false;
}
}
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;
if (!IsAcceptedButton(aEvent->button())) {
if (aEvent->button() == Qt::BackButton)
return DispatchCommandEvent(nsGkAtoms::Back);
if (aEvent->button() == Qt::ForwardButton)
return DispatchCommandEvent(nsGkAtoms::Forward);
return nsEventStatus_eIgnore;
}
WidgetMouseEvent event(true, NS_MOUSE_BUTTON_DOWN, this,
WidgetMouseEvent::eReal);
InitMouseEvent(event, aEvent, 1);
nsEventStatus status = DispatchEvent(&event);
// Right click on linux should also pop up a context menu.
if (event.button == WidgetMouseEvent::eRightButton &&
MOZ_LIKELY(!mIsDestroyed)) {
WidgetMouseEvent contextMenuEvent(true, NS_CONTEXTMENU, this,
WidgetMouseEvent::eReal);
InitMouseEvent(contextMenuEvent, aEvent, 1);
DispatchEvent(&contextMenuEvent, status);
}
return status;
}
nsEventStatus
nsWindow::mouseReleaseEvent(QMouseEvent* aEvent)
{
// The user has done something.
UserActivity();
if (!IsAcceptedButton(aEvent->button()))
return nsEventStatus_eIgnore;
WidgetMouseEvent event(true, NS_MOUSE_BUTTON_UP, this,
WidgetMouseEvent::eReal);
InitMouseEvent(event, aEvent, 1);
return DispatchEvent(&event);
}
nsEventStatus
nsWindow::mouseDoubleClickEvent(QMouseEvent* aEvent)
{
// The user has done something.
UserActivity();
if (!IsAcceptedButton(aEvent->button()))
return nsEventStatus_eIgnore;
WidgetMouseEvent event(true, NS_MOUSE_DOUBLECLICK, this,
WidgetMouseEvent::eReal);
InitMouseEvent(event, aEvent, 2);
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;
}
static bool
IsContextMenuKeyEvent(const QKeyEvent* aQEvent)
{
if (aQEvent->modifiers() & (Qt::ControlModifier |
Qt::AltModifier |
Qt::MetaModifier)) {
return false;
}
bool isShift = aQEvent->modifiers() & Qt::ShiftModifier;
uint32_t keyCode = QtKeyCodeToDOMKeyCode(aQEvent->key());
return (keyCode == NS_VK_F10 && isShift) ||
(keyCode == 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);
}
if (aQEvent->text().length() && aQEvent->text()[0].isPrint()) {
aEvent.charCode = (int32_t) aQEvent->text()[0].unicode();
aEvent.keyCode = 0;
aEvent.mKeyNameIndex = KEY_NAME_INDEX_PrintableKey;
} else {
aEvent.charCode = 0;
aEvent.keyCode = QtKeyCodeToDOMKeyCode(aQEvent->key());
aEvent.mKeyNameIndex = QtKeyCodeToDOMKeyNameIndex(aQEvent->key());
}
aEvent.mCodeNameIndex = ScanCodeToDOMCodeNameIndex(aQEvent->nativeScanCode());
// 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.mPluginEvent.Copy(*aQEvent);
}
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;
}
// 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);
return DispatchEvent(&contextMenuEvent);
}
//:TODO: fix shortcuts hebrew for non X11,
//see Bug 562195##51
uint32_t domKeyCode = QtKeyCodeToDOMKeyCode(aEvent->key());
if (!aEvent->isAutoRepeat() && !IsKeyDown(domKeyCode)) {
SetKeyDownFlag(domKeyCode);
WidgetKeyboardEvent downEvent(true, NS_KEY_DOWN, this);
InitKeyEvent(downEvent, aEvent);
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);
// Seend the key press event
return DispatchEvent(&event);
}
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;
}
// 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;
}
// 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
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 ///////////////////
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(MOZ_UTF16("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;
}