Files
tubestation/widget/src/qt/nsWindow.cpp
romashin 0043be283b Major reimplementation of nsWindow class
Full step by step porting of Gtk2 version
Fixes: some problems with widget resize...
New problems: SetCursor does not work
2008-04-19 18:37:51 +03:00

3032 lines
86 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim:expandtab:shiftwidth=4:tabstop=4:
*/
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is Christopher Blizzard
* <blizzard@mozilla.org>. Portions created by the Initial Developer
* are Copyright (C) 2001 the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Mats Palmgren <mats.palmgren@bredband.net>
* Masayuki Nakano <masayuki@d-toybox.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "prlink.h"
#include <QtGui>
#include <qcursor.h>
#include "nsWindow.h"
#include "nsToolkit.h"
#include "nsIDeviceContext.h"
#include "nsIRenderingContext.h"
#include "nsIRegion.h"
#include "nsIRollupListener.h"
#include "nsIMenuRollup.h"
#include "nsIDOMNode.h"
#include "nsWidgetsCID.h"
#include "nsIDragService.h"
#include "nsQtKeyUtils.h"
#include <X11/XF86keysym.h>
#include "nsWidgetAtoms.h"
#ifdef MOZ_ENABLE_STARTUP_NOTIFICATION
#define SN_API_NOT_YET_FROZEN
#include <startup-notification-1.0/libsn/sn.h>
#endif
#include "nsIPrefService.h"
#include "nsIPrefBranch.h"
#include "nsIServiceManager.h"
#include "nsIStringBundle.h"
#include "nsGfxCIID.h"
/* For SetIcon */
#include "nsAppDirectoryServiceDefs.h"
#include "nsXPIDLString.h"
#include "nsIFile.h"
#include "nsILocalFile.h"
/* SetCursor(imgIContainer*) */
#include "imgIContainer.h"
#include "gfxIImageFrame.h"
#include "nsGfxCIID.h"
#include "nsIImage.h"
#include "nsIInterfaceRequestorUtils.h"
#include "nsAutoPtr.h"
#include "gfxPlatformQt.h"
#include "gfxXlibSurface.h"
#include "gfxContext.h"
#include "gfxImageSurface.h"
#ifdef MOZ_ENABLE_GLITZ
#include "gfxGlitzSurface.h"
#include "glitz-glx.h"
#endif
#include "mozqwidget.h"
/* For PrepareNativeWidget */
static NS_DEFINE_IID(kDeviceContextCID, NS_DEVICE_CONTEXT_CID);
/* utility functions */
static PRBool is_mouse_in_window(QWidget* aWindow,
double aMouseX, double aMouseY);
// initialization static functions
static nsresult initialize_prefs (void);
static NS_DEFINE_IID(kCDragServiceCID, NS_DRAGSERVICE_CID);
#define NS_WINDOW_TITLE_MAX_LENGTH 4095
#define kWindowPositionSlop 20
// QT
static const int WHEEL_DELTA = 120;
static PRBool gGlobalsInitialized = PR_FALSE;
static nsWindow * get_window_for_qt_widget(QWidget *widget);
static bool ignoreEvent(nsEventStatus aStatus)
{
return aStatus == nsEventStatus_eConsumeNoDefault;
}
static PRBool
isContextMenuKey(const nsKeyEvent &aKeyEvent)
{
return ((aKeyEvent.keyCode == NS_VK_F10 && aKeyEvent.isShift &&
!aKeyEvent.isControl && !aKeyEvent.isMeta && !aKeyEvent.isAlt) ||
(aKeyEvent.keyCode == NS_VK_CONTEXT_MENU && !aKeyEvent.isShift &&
!aKeyEvent.isControl && !aKeyEvent.isMeta && !aKeyEvent.isAlt));
}
static void
keyEventToContextMenuEvent(const nsKeyEvent* aKeyEvent,
nsMouseEvent* aCMEvent)
{
memcpy(aCMEvent, aKeyEvent, sizeof(nsInputEvent));
// aCMEvent->message = NS_CONTEXTMENU_KEY;
aCMEvent->isShift = aCMEvent->isControl = PR_FALSE;
aCMEvent->isControl = PR_FALSE;
aCMEvent->isAlt = aCMEvent->isMeta = PR_FALSE;
aCMEvent->isMeta = PR_FALSE;
aCMEvent->clickCount = 0;
aCMEvent->acceptActivation = PR_FALSE;
}
nsWindow::nsWindow()
{
mDrawingarea = nsnull;
mIsVisible = PR_FALSE;
mRetryPointerGrab = PR_FALSE;
mRetryKeyboardGrab = PR_FALSE;
mActivatePending = PR_FALSE;
mWindowType = eWindowType_child;
mSizeState = nsSizeMode_Normal;
mPluginType = PluginType_NONE;
mQCursor = Qt::ArrowCursor;
if (!gGlobalsInitialized) {
gGlobalsInitialized = PR_TRUE;
// It's OK if either of these fail, but it may not be one day.
initialize_prefs();
}
memset(mKeyDownFlags, 0, sizeof(mKeyDownFlags));
mIsTransparent = PR_FALSE;
mTransparencyBitmap = nsnull;
mTransparencyBitmapWidth = 0;
mTransparencyBitmapHeight = 0;
mCursor = eCursor_standard;
}
nsWindow::~nsWindow()
{
LOG(("nsWindow::~nsWindow() [%p]\n", (void *)this));
delete[] mTransparencyBitmap;
mTransparencyBitmap = nsnull;
Destroy();
}
void
nsWindow::Initialize(QWidget *widget)
{
Q_ASSERT(widget);
mDrawingarea = widget;
mDrawingarea->setMouseTracking(PR_TRUE);
mDrawingarea->setFocusPolicy(Qt::WheelFocus);
}
/* static */ void
nsWindow::ReleaseGlobals()
{
}
NS_IMPL_ISUPPORTS_INHERITED1(nsWindow, nsCommonWidget,
nsISupportsWeakReference)
NS_IMETHODIMP
nsWindow::Create(nsIWidget *aParent,
const nsRect &aRect,
EVENT_CALLBACK aHandleEventFunction,
nsIDeviceContext *aContext,
nsIAppShell *aAppShell,
nsIToolkit *aToolkit,
nsWidgetInitData *aInitData)
{
nsresult rv = NativeCreate(aParent, nsnull, aRect, aHandleEventFunction,
aContext, aAppShell, aToolkit, aInitData);
return rv;
}
NS_IMETHODIMP
nsWindow::Create(nsNativeWidget aParent,
const nsRect &aRect,
EVENT_CALLBACK aHandleEventFunction,
nsIDeviceContext *aContext,
nsIAppShell *aAppShell,
nsIToolkit *aToolkit,
nsWidgetInitData *aInitData)
{
nsresult rv = NativeCreate(nsnull, aParent, aRect, aHandleEventFunction,
aContext, aAppShell, aToolkit, aInitData);
return rv;
}
NS_IMETHODIMP
nsWindow::Destroy(void)
{
if (mIsDestroyed || !mCreated)
return NS_OK;
LOG(("nsWindow::Destroy [%p]\n", (void *)this));
mIsDestroyed = PR_TRUE;
mCreated = PR_FALSE;
NativeShow(PR_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.
mThebesSurface = nsnull;
if (mDrawingarea) {
delete mDrawingarea;
mDrawingarea = nsnull;
}
OnDestroy();
return NS_OK;
}
NS_IMETHODIMP
nsWindow::SetParent(nsIWidget *aNewParent)
{
NS_ENSURE_ARG_POINTER(aNewParent);
QWidget* newParentWindow =
static_cast<QWidget*>(aNewParent->GetNativeData(NS_NATIVE_WINDOW));
NS_ASSERTION(newParentWindow, "Parent widget has a null native window handle");
if (mDrawingarea) {
qDebug("FIXME:>>>>>>Func:%s::%d\n", __PRETTY_FUNCTION__, __LINE__);
// moz_drawingarea_reparent(mDrawingarea, newParentWindow);
} else {
NS_NOTREACHED("nsWindow::SetParent - reparenting a non-child window");
}
return NS_OK;
}
NS_IMETHODIMP
nsWindow::SetModal(PRBool aModal)
{
LOG(("nsWindow::SetModal [%p] %d, widget[%p]\n", (void *)this, aModal, mDrawingarea));
MozQWidget *mozWidget = static_cast<MozQWidget*>(mDrawingarea);
if (mozWidget)
mozWidget->setModal(aModal);
return NS_OK;
}
NS_IMETHODIMP
nsWindow::IsVisible(PRBool & aState)
{
aState = mDrawingarea?mDrawingarea->isVisible():PR_FALSE;
return NS_OK;
}
NS_IMETHODIMP
nsWindow::ConstrainPosition(PRBool aAllowSlop, PRInt32 *aX, PRInt32 *aY)
{
if (mDrawingarea) {
PRInt32 screenWidth = QApplication::desktop()->width();
PRInt32 screenHeight = QApplication::desktop()->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(PRInt32 aX, PRInt32 aY)
{
LOG(("nsWindow::Move [%p] %d %d\n", (void *)this,
aX, aY));
mPlaced = PR_TRUE;
// Since a popup window's x/y coordinates are in relation to to
// the parent, the parent might have moved so we always move a
// popup window.
//bool popup = mDrawingarea ? mDrawingarea->windowType() == Qt::Popup : false;
if (aX == mBounds.x && aY == mBounds.y &&
mWindowType != eWindowType_popup)
return NS_OK;
// XXX Should we do some AreBoundsSane check here?
if (!mDrawingarea)
return NS_OK;
QPoint pos(aX, aY);
if (mDrawingarea) {
if (mParent && mDrawingarea->windowType() == Qt::Popup) {
nsRect oldrect, newrect;
oldrect.x = aX;
oldrect.y = aY;
mParent->WidgetToScreen(oldrect, newrect);
pos = QPoint(newrect.x, newrect.y);
#ifdef DEBUG_WIDGETS
qDebug("pos is [%d,%d]", pos.x(), pos.y());
#endif
} else {
qDebug("Widget within another? (%p)", (void*)mDrawingarea);
}
}
mBounds.x = pos.x();
mBounds.y = pos.y();
if (!mCreated)
return NS_OK;
if (mIsTopLevel) {
mDrawingarea->move(pos);
}
else if (mDrawingarea) {
mDrawingarea->move(pos);
}
return NS_OK;
}
NS_IMETHODIMP
nsWindow::PlaceBehind(nsTopLevelWidgetZPlacement aPlacement,
nsIWidget *aWidget,
PRBool aActivate)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsWindow::SetZIndex(PRInt32 aZIndex)
{
nsIWidget* oldPrev = GetPrevSibling();
nsBaseWidget::SetZIndex(aZIndex);
if (GetPrevSibling() == oldPrev) {
return NS_OK;
}
NS_ASSERTION(!mDrawingarea, "Expected Mozilla child widget");
// We skip the nsWindows that don't have mDrawingareas.
// These are probably in the process of being destroyed.
if (!GetNextSibling()) {
// We're to be on top.
if (mDrawingarea) {
qDebug("FIXME:>>>>>>Func:%s::%d\n", __PRETTY_FUNCTION__, __LINE__);
// gdk_window_raise(mDrawingarea->clip_window);
}
} else {
// All the siblings before us need to be below our widget.
for (nsWindow* w = this; w;
w = static_cast<nsWindow*>(w->GetPrevSibling())) {
if (w->mDrawingarea) {
qDebug("FIXME:>>>>>>Func:%s::%d\n", __PRETTY_FUNCTION__, __LINE__);
// gdk_window_lower(w->mDrawingarea->clip_window);
}
}
}
return NS_OK;
}
NS_IMETHODIMP
nsWindow::SetSizeMode(PRInt32 aMode)
{
nsresult rv;
LOG(("nsWindow::SetSizeMode [%p] %d\n", (void *)this, aMode));
// 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 (!mDrawingarea || mSizeState == mSizeMode) {
return rv;
}
switch (aMode) {
case nsSizeMode_Maximized:
mDrawingarea->showMaximized();
break;
case nsSizeMode_Minimized:
mDrawingarea->showMinimized();
break;
default:
// nsSizeMode_Normal, really.
mDrawingarea->showNormal ();
// KILLME
//if (mSizeState == nsSizeMode_Minimized)
// gtk_window_deiconify(GTK_WINDOW(mDrawingarea));
//else if (mSizeState == nsSizeMode_Maximized)
// gtk_window_unmaximize(GTK_WINDOW(mDrawingarea));
break;
}
mSizeState = mSizeMode;
return rv;
}
NS_IMETHODIMP
nsWindow::Enable(PRBool aState)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
typedef void (* SetUserTimeFunc)(QWidget* aWindow, quint32 aTimestamp);
// This will become obsolete when new GTK APIs are widely supported,
// as described here: http://bugzilla.gnome.org/show_bug.cgi?id=347375
static void
SetUserTimeAndStartupIDForActivatedWindow(QWidget* aWindow)
{
nsCOMPtr<nsIToolkit> toolkit;
NS_GetCurrentToolkit(getter_AddRefs(toolkit));
if (!toolkit)
return;
nsToolkit* QTToolkit = static_cast<nsToolkit*>
(static_cast<nsIToolkit*>(toolkit));
nsCAutoString desktopStartupID;
QTToolkit->GetDesktopStartupID(&desktopStartupID);
if (desktopStartupID.IsEmpty()) {
// We don't have the data we need. Fall back to an
// approximation ... using the timestamp of the remote command
// being received as a guess for the timestamp of the user event
// that triggered it.
PRUint32 timestamp = QTToolkit->GetFocusTimestamp();
if (timestamp) {
aWindow->focusWidget ();
// gdk_window_focus(aWindow->window, timestamp);
QTToolkit->SetFocusTimestamp(0);
}
return;
}
QTToolkit->SetDesktopStartupID(EmptyCString());
}
NS_IMETHODIMP
nsWindow::SetFocus(PRBool 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 (!mDrawingarea)
return NS_ERROR_FAILURE;
if (aRaise)
mDrawingarea->raise();
mDrawingarea->setFocus();
// If there is already a focused child window, dispatch a LOSTFOCUS
// event from that widget and unset its got focus flag.
LOGFOCUS((" widget now has focus - dispatching events [%p]\n",
(void *)this));
DispatchGotFocusEvent();
LOGFOCUS((" done dispatching events in SetFocus() [%p]\n",
(void *)this));
return NS_OK;
}
NS_IMETHODIMP
nsWindow::GetScreenBounds(nsRect &aRect)
{
nsRect origin(0, 0, mBounds.width, mBounds.height);
WidgetToScreen(origin, aRect);
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::SetForegroundColor(const nscolor &aColor)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsWindow::SetBackgroundColor(const nscolor &aColor)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsWindow::SetCursor(nsCursor aCursor)
{
mCursor = aCursor;
switch(mCursor) {
case eCursor_standard:
mQCursor = Qt::ArrowCursor;
break;
case eCursor_wait:
mQCursor = Qt::WaitCursor;
break;
case eCursor_select:
mQCursor = Qt::IBeamCursor;
break;
case eCursor_hyperlink:
mQCursor = Qt::PointingHandCursor;
break;
case eCursor_ew_resize:
mQCursor = Qt::SplitHCursor;
break;
case eCursor_ns_resize:
mQCursor = Qt::SplitVCursor;
break;
case eCursor_nw_resize:
case eCursor_se_resize:
mQCursor = Qt::SizeBDiagCursor;
break;
case eCursor_ne_resize:
case eCursor_sw_resize:
mQCursor = Qt::SizeFDiagCursor;
break;
case eCursor_crosshair:
case eCursor_move:
mQCursor = Qt::SizeAllCursor;
break;
case eCursor_help:
mQCursor = Qt::WhatsThisCursor;
break;
case eCursor_copy:
case eCursor_alias:
break;
case eCursor_context_menu:
case eCursor_cell:
case eCursor_grab:
case eCursor_grabbing:
case eCursor_spinning:
case eCursor_zoom_in:
case eCursor_zoom_out:
default:
break;
}
return NS_OK;
// FIXME after reimplementation of whole nsWindow SetCursor cause lot of errors
if (mDrawingarea)
mDrawingarea->setCursor(mQCursor);
return NS_OK;
}
static
PRUint8* Data32BitTo1Bit(PRUint8* aImageData,
PRUint32 aImageBytesPerRow,
PRUint32 aWidth, PRUint32 aHeight)
{
PRUint32 outBpr = (aWidth + 7) / 8;
PRUint8* outData = new PRUint8[outBpr * aHeight];
if (!outData)
return NULL;
PRUint8 *outRow = outData,
*imageRow = aImageData;
for (PRUint32 curRow = 0; curRow < aHeight; curRow++) {
PRUint8 *irow = imageRow;
PRUint8 *orow = outRow;
PRUint8 imagePixels = 0;
PRUint8 offset = 0;
for (PRUint32 curCol = 0; curCol < aWidth; curCol++) {
PRUint8 r = *imageRow++,
g = *imageRow++,
b = *imageRow++;
/* a = * */imageRow++;
if ((r + b + g) < 3 * 128)
imagePixels |= (1 << offset);
if (offset == 7) {
*outRow++ = imagePixels;
offset = 0;
imagePixels = 0;
} else {
offset++;
}
}
if (offset != 0)
*outRow++ = imagePixels;
imageRow = irow + aImageBytesPerRow;
outRow = orow + outBpr;
}
return outData;
}
NS_IMETHODIMP
nsWindow::SetCursor(imgIContainer* aCursor,
PRUint32 aHotspotX, PRUint32 aHotspotY)
{
nsresult rv = NS_ERROR_OUT_OF_MEMORY;
qDebug("FIXME:>>>>>>Func:%s::%d\n", __PRETTY_FUNCTION__, __LINE__);
return rv;
}
NS_IMETHODIMP
nsWindow::Validate()
{
// Get the update for this window and, well, just drop it on the
// floor.
if (!mDrawingarea)
return NS_OK;
qDebug("FIXME:>>>>>>Func:%s::%d\n", __PRETTY_FUNCTION__, __LINE__);
return NS_OK;
}
NS_IMETHODIMP
nsWindow::Invalidate(PRBool aIsSynchronous)
{
LOGDRAW(("Invalidate (all) [%p]: \n", (void *)this));
if (!mDrawingarea)
return NS_OK;
if (aIsSynchronous)
mDrawingarea->repaint();
else
mDrawingarea->update();
return NS_OK;
}
NS_IMETHODIMP
nsWindow::Invalidate(const nsRect &aRect,
PRBool aIsSynchronous)
{
LOGDRAW(("Invalidate (rect) [%p]: %d %d %d %d (sync: %d)\n", (void *)this,
aRect.x, aRect.y, aRect.width, aRect.height, aIsSynchronous));
if (!mDrawingarea)
return NS_OK;
if (aIsSynchronous)
mDrawingarea->repaint(aRect.x, aRect.y, aRect.width, aRect.height);
else
mDrawingarea->update(aRect.x, aRect.y, aRect.width, aRect.height);
return NS_OK;
}
NS_IMETHODIMP
nsWindow::InvalidateRegion(const nsIRegion* aRegion,
PRBool aIsSynchronous)
{
QRegion *region = nsnull;
aRegion->GetNativeRegion((void *&)region);
if (region && mDrawingarea) {
QRect rect = region->boundingRect();
// LOGDRAW(("Invalidate (region) [%p]: %d %d %d %d (sync: %d)\n",
// (void *)this,
// rect.x, rect.y, rect.width, rect.height, aIsSynchronous));
if (aIsSynchronous)
mDrawingarea->repaint(*region);
else
mDrawingarea->update(*region);
}
else {
qDebug("FIXME:>>>>>>Func:%s::%d\n", __PRETTY_FUNCTION__, __LINE__);
LOGDRAW(("Invalidate (region) [%p] with empty region\n",
(void *)this));
}
return NS_OK;
}
NS_IMETHODIMP
nsWindow::Update()
{
if (!mDrawingarea)
return NS_OK;
mDrawingarea->update();
return NS_OK;
}
NS_IMETHODIMP
nsWindow::SetColorMap(nsColorMap *aColorMap)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsWindow::Scroll(PRInt32 aDx,
PRInt32 aDy,
nsRect *aClipRect)
{
if (!mDrawingarea)
return NS_OK;
mDrawingarea->scroll(aDx, aDy);
// Update bounds on our child windows
for (nsIWidget* kid = mFirstChild; kid; kid = kid->GetNextSibling()) {
nsRect bounds;
kid->GetBounds(bounds);
bounds.x += aDx;
bounds.y += aDy;
static_cast<nsBaseWidget*>(kid)->SetBounds(bounds);
}
return NS_OK;
}
NS_IMETHODIMP
nsWindow::ScrollWidgets(PRInt32 aDx,
PRInt32 aDy)
{
if (!mDrawingarea)
return NS_OK;
mDrawingarea->scroll(aDx, aDy);
return NS_OK;
}
NS_IMETHODIMP
nsWindow::ScrollRect(nsRect &aSrcRect,
PRInt32 aDx,
PRInt32 aDy)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
void*
nsWindow::GetNativeData(PRUint32 aDataType)
{
switch (aDataType) {
case NS_NATIVE_WINDOW:
case NS_NATIVE_WIDGET: {
if (!mDrawingarea)
return nsnull;
return mDrawingarea;
break;
}
case NS_NATIVE_PLUGIN_PORT:
return SetupPluginPort();
break;
case NS_NATIVE_DISPLAY:
return mDrawingarea->x11Info().display();
break;
case NS_NATIVE_GRAPHIC: {
NS_ASSERTION(nsnull != mToolkit, "NULL toolkit, unable to get a GC");
return (void *)static_cast<nsToolkit *>(mToolkit)->GetSharedGC();
break;
}
case NS_NATIVE_SHELLWIDGET:
return (void *) mDrawingarea;
default:
NS_WARNING("nsWindow::GetNativeData called with bad value");
return nsnull;
}
}
NS_IMETHODIMP
nsWindow::SetBorderStyle(nsBorderStyle aBorderStyle)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsWindow::SetTitle(const nsAString& aTitle)
{
if (!mDrawingarea)
return NS_OK;
nsAString::const_iterator it;
QString qStr((QChar*)aTitle.BeginReading(it).get(), -1);
if (mDrawingarea)
mDrawingarea->setWindowTitle(qStr);
return NS_OK;
}
NS_IMETHODIMP
nsWindow::SetIcon(const nsAString& aIconSpec)
{
if (!mDrawingarea)
return NS_OK;
nsCOMPtr<nsILocalFile> iconFile;
nsCAutoString path;
nsCStringArray 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 (PRUint32 i = 0; i < NS_ARRAY_LENGTH(extensions); i++) {
// Don't bother looking for XPM versions if we found a PNG.
if (i == NS_ARRAY_LENGTH(extensions) - 2 && iconList.Count())
break;
nsAutoString extension;
extension.AppendASCII(extensions[i]);
ResolveIconName(aIconSpec, extension, getter_AddRefs(iconFile));
if (iconFile) {
iconFile->GetNativePath(path);
iconList.AppendCString(path);
}
}
// leave the default icon intact if no matching icons were found
if (iconList.Count() == 0)
return NS_OK;
return SetWindowIconList(iconList);
}
NS_IMETHODIMP
nsWindow::SetMenuBar(nsIMenuBar * aMenuBar)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsWindow::ShowMenuBar(PRBool aShow)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsWindow::WidgetToScreen(const nsRect& aOldRect, nsRect& aNewRect)
{
NS_ENSURE_TRUE(mDrawingarea, NS_OK);
PRInt32 X,Y;
QPoint offset(0,0);
offset = mDrawingarea->mapFromGlobal(offset);
X = offset.x();
Y = offset.y();
LOG(("WidgetToScreen (container) %d %d\n", X, Y));
aNewRect.x = aOldRect.x + X;
aNewRect.y = aOldRect.y + Y;
aNewRect.width = aOldRect.width;
aNewRect.height = aOldRect.height;
return NS_OK;
}
NS_IMETHODIMP
nsWindow::ScreenToWidget(const nsRect& aOldRect, nsRect& aNewRect)
{
NS_ENSURE_TRUE(mDrawingarea, NS_OK);
PRInt32 X,Y;
QPoint offset(0,0);
offset = mDrawingarea->mapFromGlobal(offset);
X = offset.x();
Y = offset.y();
LOG(("WidgetToScreen (container) %d %d\n", X, Y));
aNewRect.x = aOldRect.x - X;
aNewRect.y = aOldRect.y - Y;
aNewRect.width = aOldRect.width;
aNewRect.height = aOldRect.height;
return NS_OK;
}
NS_IMETHODIMP
nsWindow::BeginResizingChildren(void)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsWindow::EndResizingChildren(void)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsWindow::EnableDragDrop(PRBool aEnable)
{
return NS_OK;
}
void
nsWindow::ConvertToDeviceCoordinates(nscoord &aX,
nscoord &aY)
{
}
NS_IMETHODIMP
nsWindow::PreCreateWidget(nsWidgetInitData *aWidgetInitData)
{
if (nsnull != aWidgetInitData) {
mWindowType = aWidgetInitData->mWindowType;
mBorderStyle = aWidgetInitData->mBorderStyle;
return NS_OK;
}
return NS_ERROR_FAILURE;
}
NS_IMETHODIMP
nsWindow::CaptureMouse(PRBool aCapture)
{
LOG(("CaptureMouse %p\n", (void *)this));
if (!mDrawingarea)
return NS_OK;
/*
if (aCapture) {
GrabPointer();
}
else {
ReleaseGrabs();
}
*/
return NS_OK;
}
NS_IMETHODIMP
nsWindow::CaptureRollupEvents(nsIRollupListener *aListener,
PRBool aDoCapture,
PRBool aConsumeRollupEvent)
{
if (!mDrawingarea)
return NS_OK;
LOG(("CaptureRollupEvents %p\n", (void *)this));
/*
if (aDoCapture) {
GrabPointer();
GrabKeyboard();
}
else {
ReleaseGrabs();
}
*/
return NS_OK;
}
NS_IMETHODIMP
nsWindow::GetAttention(PRInt32 aCycleCount)
{
LOG(("nsWindow::GetAttention [%p]\n", (void *)this));
SetUrgencyHint(mDrawingarea, PR_TRUE);
return NS_OK;
}
void
nsWindow::LoseFocus(void)
{
// make sure that we reset our key down counter so the next keypress
// for this widget will get the down event
memset(mKeyDownFlags, 0, sizeof(mKeyDownFlags));
// Dispatch a lostfocus event
DispatchLostFocusEvent();
LOGFOCUS((" widget lost focus [%p]\n", (void *)this));
}
bool
nsWindow::OnExposeEvent(QPaintEvent *aEvent)
{
if (mIsDestroyed) {
LOG(("Expose event on destroyed window [%p] window %p\n",
(void *)this, mDrawingarea));
return FALSE;
}
if (!mDrawingarea)
return FALSE;
static NS_DEFINE_CID(kRegionCID, NS_REGION_CID);
nsCOMPtr<nsIRegion> updateRegion = do_CreateInstance(kRegionCID);
if (!updateRegion)
return FALSE;
updateRegion->Init();
QVector<QRect> rects = aEvent->region().rects();
LOGDRAW(("sending expose event [%p] %p 0x%lx (rects follow):\n",
(void *)this, (void *)aEvent, 0));
for (int i = 0; i < rects.size(); ++i) {
QRect r = rects.at(i);
updateRegion->Union(r.x(), r.y(), r.width(), r.height());
LOGDRAW(("\t%d %d %d %d\n", r.x(), r.y(), r.width(), r.height()));
}
nsCOMPtr<nsIRenderingContext> rc = getter_AddRefs(GetRenderingContext());
if (NS_UNLIKELY(!rc)) {
return FALSE;
}
PRBool translucent;
GetHasTransparentBackground(translucent);
nsIntRect boundsRect;
QPixmap* bufferPixmap = nsnull;
nsRefPtr<gfxXlibSurface> bufferPixmapSurface;
updateRegion->GetBoundingBox(&boundsRect.x, &boundsRect.y,
&boundsRect.width, &boundsRect.height);
// do double-buffering and clipping here
nsRefPtr<gfxContext> ctx = rc->ThebesContext();
ctx->Save();
ctx->NewPath();
if (translucent) {
// Collapse update area to the bounding box. This is so we only have to
// call UpdateTranslucentWindowAlpha once. After we have dropped
// support for non-Thebes graphics, UpdateTranslucentWindowAlpha will be
// our private interface so we can rework things to avoid this.
ctx->Rectangle(gfxRect(boundsRect.x, boundsRect.y,
boundsRect.width, boundsRect.height));
} else {
for (int i = 0; i < rects.size(); ++i) {
QRect r = rects.at(i);
ctx->Rectangle(gfxRect(r.x(), r.y(), r.width(), r.height()));
}
}
ctx->Clip();
// double buffer
if (translucent) {
ctx->PushGroup(gfxASurface::CONTENT_COLOR_ALPHA);
} else {
#ifdef MOZ_ENABLE_GLITZ
ctx->PushGroup(gfxASurface::CONTENT_COLOR);
#else // MOZ_ENABLE_GLITZ
// Instead of just doing PushGroup we're going to do a little dance
// to ensure that GDK creates the pixmap, so it doesn't go all
// XGetGeometry on us in gdk_pixmap_foreign_new_for_display when we
// paint native themes
// GdkDrawable* d = Qt::Key_DRAWABLE(mDrawingarea->inner_window);
// qint32 depth = gdk_drawable_get_depth(d);
// bufferPixmap = new QPixmap(d, boundsRect.width, boundsRect.height);
if (bufferPixmap) {
#if 0
GdkVisual* visual = gdk_drawable_get_visual(Qt::Key_DRAWABLE(bufferPixmap));
Visual* XVisual = gdk_x11_visual_get_xvisual(visual);
Display* display = gdk_x11_drawable_get_xdisplay(Qt::Key_DRAWABLE(bufferPixmap));
Drawable drawable = gdk_x11_drawable_get_xid(Qt::Key_DRAWABLE(bufferPixmap));
bufferPixmapSurface =
new gfxXlibSurface(display, drawable, XVisual,
gfxIntSize(boundsRect.width, boundsRect.height));
if (bufferPixmapSurface) {
bufferPixmapSurface->SetDeviceOffset(gfxPoint(-boundsRect.x, -boundsRect.y));
nsCOMPtr<nsIRenderingContext> newRC;
nsresult rv = GetDeviceContext()->
CreateRenderingContextInstance(*getter_AddRefs(newRC));
if (NS_FAILED(rv)) {
bufferPixmapSurface = nsnull;
} else {
rv = newRC->Init(GetDeviceContext(), bufferPixmapSurface);
if (NS_FAILED(rv)) {
bufferPixmapSurface = nsnull;
} else {
rc = newRC;
}
}
}
#endif
}
#endif // MOZ_ENABLE_GLITZ
}
#if 0
// NOTE: Paint flashing region would be wrong for cairo, since
// cairo inflates the update region, etc. So don't paint flash
// for cairo.
#ifdef DEBUG
if (WANT_PAINT_FLASHING && aEvent->window)
gdk_window_flash(aEvent->window, 1, 100, aEvent->region);
#endif
#endif
nsPaintEvent event(PR_TRUE, NS_PAINT, this);
QRect r = aEvent->rect();
if (!r.isValid())
r = mDrawingarea->rect();
nsRect rect(r.x(), r.y(), r.width(), r.height());
event.refPoint.x = aEvent->rect().x();
event.refPoint.y = aEvent->rect().y();
event.rect = &rect; // was null FIXME
event.region = updateRegion;
event.renderingContext = rc;
nsEventStatus status;
DispatchEvent(&event, status);
// DispatchEvent can Destroy us (bug 378273), avoid doing any paint
// operations below if that happened - it will lead to XError and exit().
if (NS_LIKELY(!mIsDestroyed)) {
if (status != nsEventStatus_eIgnore) {
if (translucent) {
nsRefPtr<gfxPattern> pattern = ctx->PopGroup();
ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
ctx->SetPattern(pattern);
ctx->Paint();
nsRefPtr<gfxImageSurface> img =
new gfxImageSurface(gfxIntSize(boundsRect.width, boundsRect.height),
gfxImageSurface::ImageFormatA8);
if (img && !img->CairoStatus()) {
img->SetDeviceOffset(gfxPoint(-boundsRect.x, -boundsRect.y));
nsRefPtr<gfxContext> imgCtx = new gfxContext(img);
if (imgCtx) {
imgCtx->SetPattern(pattern);
imgCtx->SetOperator(gfxContext::OPERATOR_SOURCE);
imgCtx->Paint();
}
UpdateTranslucentWindowAlphaInternal(nsRect(boundsRect.x, boundsRect.y,
boundsRect.width, boundsRect.height),
img->Data(), img->Stride());
}
} else {
#ifdef MOZ_ENABLE_GLITZ
ctx->PopGroupToSource();
ctx->Paint();
#else // MOZ_ENABLE_GLITZ
if (bufferPixmapSurface) {
ctx->SetSource(bufferPixmapSurface);
ctx->Paint();
}
#endif // MOZ_ENABLE_GLITZ
}
} else {
// ignore
if (translucent) {
ctx->PopGroup();
} else {
#ifdef MOZ_ENABLE_GLITZ
ctx->PopGroup();
#endif // MOZ_ENABLE_GLITZ
}
}
if (bufferPixmap) {
delete bufferPixmap;
}
ctx->Restore();
}
// check the return value!
return ignoreEvent(status);
}
bool
nsWindow::OnConfigureEvent(QMoveEvent *aEvent)
{
LOG(("configure event [%p] %d %d\n", (void *)this,
aEvent->pos().x(), aEvent->pos().y()));
// can we shortcut?
if (!mDrawingarea
|| (mBounds.x == aEvent->pos().x()
&& mBounds.y == aEvent->pos().y()))
return FALSE;
// Toplevel windows need to have their bounds set so that we can
// keep track of our location. It's not often that the x,y is set
// by the layout engine. Width and height are set elsewhere.
QPoint pos = aEvent->pos();
if (mIsTopLevel) {
mPlaced = PR_TRUE;
// Need to translate this into the right coordinates
nsRect oldrect, newrect;
WidgetToScreen(oldrect, newrect);
mBounds.x = newrect.x;
mBounds.y = newrect.y;
}
nsGUIEvent event(PR_TRUE, NS_MOVE, this);
event.refPoint.x = pos.x();
event.refPoint.y = pos.y();
// XXX mozilla will invalidate the entire window after this move
// complete. wtf?
nsEventStatus status;
DispatchEvent(&event, status);
return ignoreEvent(status);
}
bool
nsWindow::OnSizeAllocate(QResizeEvent *e)
{
nsRect rect;
// Generate XPFE resize event
GetBounds(rect);
rect.width = e->size().width();
rect.height = e->size().height();
LOG(("size_allocate [%p] %d %d\n",
(void *)this, rect.width, rect.height));
ResizeTransparencyBitmap(rect.width, rect.height);
mBounds.width = rect.width;
mBounds.height = rect.height;
#ifdef DEBUG_WIDGETS
qDebug("resizeEvent: mDrawingarea=%p, aWidth=%d, aHeight=%d, aX = %d, aY = %d", (void*)mDrawingarea,
rect.width, rect.height, rect.x, rect.y);
#endif
if (mTransparencyBitmap) {
ApplyTransparencyBitmap();
}
if (mDrawingarea)
mDrawingarea->resize(rect.width, rect.height);
nsEventStatus status;
DispatchResizeEvent(rect, status);
return ignoreEvent(status);
}
bool
nsWindow::OnDeleteEvent(QCloseEvent *aEvent)
{
nsGUIEvent event(PR_TRUE, NS_XUL_CLOSE, this);
event.refPoint.x = 0;
event.refPoint.y = 0;
nsEventStatus status;
DispatchEvent(&event, status);
return ignoreEvent(status);
}
bool
nsWindow::OnEnterNotifyEvent(QEvent *aEvent)
{
nsMouseEvent event(PR_TRUE, NS_MOUSE_ENTER, this, nsMouseEvent::eReal);
QPoint pt = QCursor::pos();
event.refPoint.x = nscoord(pt.x());
event.refPoint.y = nscoord(pt.y());
LOG(("OnEnterNotify: %p\n", (void *)this));
nsEventStatus status;
DispatchEvent(&event, status);
return FALSE;
}
bool
nsWindow::OnLeaveNotifyEvent(QEvent *aEvent)
{
nsMouseEvent event(PR_TRUE, NS_MOUSE_EXIT, this, nsMouseEvent::eReal);
QPoint pt = QCursor::pos();
event.refPoint.x = nscoord(pt.x());
event.refPoint.y = nscoord(pt.y());
LOG(("OnLeaveNotify: %p\n", (void *)this));
nsEventStatus status;
DispatchEvent(&event, status);
return FALSE;
}
bool
nsWindow::OnMotionNotifyEvent(QMouseEvent *aEvent)
{
// when we receive this, it must be that the gtk dragging is over,
// it is dropped either in or out of mozilla, clear the flag
//mDrawingarea->setCursor(mQCursor);
nsMouseEvent event(PR_TRUE, NS_MOUSE_MOVE, this, nsMouseEvent::eReal);
event.refPoint.x = nscoord(aEvent->x());
event.refPoint.y = nscoord(aEvent->y());
event.isShift = aEvent->modifiers() & Qt::ShiftModifier;
event.isControl = aEvent->modifiers() & Qt::ControlModifier;
event.isAlt = aEvent->modifiers() & Qt::AltModifier;
event.isMeta = aEvent->modifiers() & Qt::MetaModifier;
event.clickCount = 0;
nsEventStatus status;
DispatchEvent(&event, status);
return ignoreEvent(status);
}
void
nsWindow::InitButtonEvent(nsMouseEvent &event,
QMouseEvent *aEvent, int aClickCount)
{
event.refPoint.x = nscoord(aEvent->x());
event.refPoint.y = nscoord(aEvent->y());
event.isShift = aEvent->modifiers() & Qt::ShiftModifier;
event.isControl = aEvent->modifiers() & Qt::ControlModifier;
event.isAlt = aEvent->modifiers() & Qt::AltModifier;
event.isMeta = aEvent->modifiers() & Qt::MetaModifier;
event.clickCount = aClickCount;
}
bool
nsWindow::OnButtonPressEvent(QMouseEvent *aEvent)
{
mDrawingarea->setCursor(mQCursor);
PRUint16 domButton;
switch (aEvent->button()) {
case Qt::MidButton:
domButton = nsMouseEvent::eMiddleButton;
break;
case Qt::RightButton:
domButton = nsMouseEvent::eRightButton;
break;
default:
domButton = nsMouseEvent::eLeftButton;
break;
}
nsMouseEvent event(PR_TRUE, NS_MOUSE_BUTTON_DOWN, this, nsMouseEvent::eReal);
event.button = domButton;
InitButtonEvent(event, aEvent, 1);
nsEventStatus status;
DispatchEvent(&event, status);
// right menu click on linux should also pop up a context menu
if (domButton == nsMouseEvent::eRightButton &&
NS_LIKELY(!mIsDestroyed)) {
nsMouseEvent contextMenuEvent(PR_TRUE, NS_CONTEXTMENU, this,
nsMouseEvent::eReal);
InitButtonEvent(contextMenuEvent, aEvent);
DispatchEvent(&contextMenuEvent, status);
}
return ignoreEvent(status);
}
bool
nsWindow::OnButtonReleaseEvent(QMouseEvent *aEvent)
{
PRUint16 domButton;
// mLastButtonReleaseTime = aEvent->time;
switch (aEvent->button()) {
case Qt::MidButton:
domButton = nsMouseEvent::eMiddleButton;
break;
case Qt::RightButton:
domButton = nsMouseEvent::eRightButton;
break;
default:
domButton = nsMouseEvent::eLeftButton;
break;
}
nsMouseEvent event(PR_TRUE, NS_MOUSE_BUTTON_UP, this, nsMouseEvent::eReal);
event.button = domButton;
InitButtonEvent(event, aEvent);
nsEventStatus status;
DispatchEvent(&event, status);
return ignoreEvent(status);
}
bool
nsWindow::mouseDoubleClickEvent(QMouseEvent *e)
{
PRUint32 eventType;
switch (e->button()) {
case Qt::MidButton:
eventType = nsMouseEvent::eMiddleButton;
break;
case Qt::RightButton:
eventType = nsMouseEvent::eRightButton;
break;
default:
eventType = nsMouseEvent::eLeftButton;
break;
}
nsMouseEvent event(PR_TRUE, NS_MOUSE_DOUBLECLICK, this, nsMouseEvent::eReal);
event.button = eventType;
InitButtonEvent(event, e, 2);
//pressed
nsEventStatus status;
DispatchEvent(&event, status);
return ignoreEvent(status);
}
bool
nsWindow::OnContainerFocusInEvent(QFocusEvent *aEvent)
{
LOGFOCUS(("OnContainerFocusInEvent [%p]\n", (void *)this));
// Return if someone has blocked events for this widget. This will
// happen if someone has called gtk_widget_grab_focus() from
// nsWindow::SetFocus() and will prevent recursion.
if (mIsTopLevel)
mActivatePending = PR_TRUE;
// Unset the urgency hint, if possible
// SetUrgencyHint(top_window, PR_FALSE);
// dispatch a got focus event
DispatchGotFocusEvent();
// send the activate event if it wasn't already sent via any
// SetFocus() calls that were the result of the GOTFOCUS event
// above.
if (mActivatePending) {
mActivatePending = PR_FALSE;
DispatchActivateEvent();
}
LOGFOCUS(("Events sent from focus in event [%p]\n", (void *)this));
return FALSE;
}
bool
nsWindow::OnContainerFocusOutEvent(QFocusEvent *aEvent)
{
LOGFOCUS(("OnContainerFocusOutEvent [%p]\n", (void *)this));
DispatchLostFocusEvent();
if (mDrawingarea)
DispatchDeactivateEvent();
mActivatePending = PR_FALSE;
LOGFOCUS(("Done with container focus out [%p]\n", (void *)this));
return FALSE;
}
inline PRBool
is_latin_shortcut_key(quint32 aKeyval)
{
return ((Qt::Key_0 <= aKeyval && aKeyval <= Qt::Key_9) ||
(Qt::Key_A <= aKeyval && aKeyval <= Qt::Key_Z));
}
PRBool
nsWindow::DispatchCommandEvent(nsIAtom* aCommand)
{
nsEventStatus status;
nsCommandEvent event(PR_TRUE, nsWidgetAtoms::onAppCommand, aCommand, this);
DispatchEvent(&event, status);
return TRUE;
}
bool
nsWindow::OnKeyPressEvent(QKeyEvent *aEvent)
{
LOGFOCUS(("OnKeyPressEvent [%p]\n", (void *)this));
#ifdef USE_XIM
// if we are in the middle of composing text, XIM gets to see it
// before mozilla does.
LOGIM(("key press [%p]: composing %d val %d\n",
(void *)this, IMEComposingWindow() != nsnull, aEvent->key()));
if (IMEFilterEvent(aEvent))
return TRUE;
LOGIM(("sending as regular key press event\n"));
#endif
nsEventStatus status;
nsCOMPtr<nsIWidget> kungFuDeathGrip = this;
// If the key down flag isn't set then set it so we don't send
// another key down event on the next key press -- DOM events are
// key down, key press and key up. X only has key press and key
// release. gtk2 already filters the extra key release events for
// us.
PRBool isKeyDownCancelled = PR_FALSE;
PRUint32 domVirtualKeyCode = QtKeyCodeToDOMKeyCode(aEvent->key());
if (!IsKeyDown(domVirtualKeyCode)) {
SetKeyDownFlag(domVirtualKeyCode);
// send the key down event
nsKeyEvent downEvent(PR_TRUE, NS_KEY_DOWN, this);
InitKeyEvent(downEvent, aEvent);
DispatchEvent(&downEvent, status);
if (NS_UNLIKELY(mIsDestroyed))
return PR_TRUE;
isKeyDownCancelled = (status == nsEventStatus_eConsumeNoDefault);
}
// Don't pass modifiers as NS_KEY_PRESS events.
// TODO: Instead of selectively excluding some keys from NS_KEY_PRESS events,
// we should instead selectively include (as per MSDN spec; no official
// spec covers KeyPress events).
/*
if (aEvent->key() == Qt::Key_Shift_L
|| aEvent->key() == Qt::Key_Shift_R
|| aEvent->key() == Qt::Key_Control_L
|| aEvent->key() == Qt::Key_Control_R
|| aEvent->key() == Qt::Key_Alt_L
|| aEvent->key() == Qt::Key_Alt_R
|| aEvent->key() == Qt::Key_Meta_L
|| aEvent->key() == Qt::Key_Meta_R) {
return TRUE;
}
*/
// Look for specialized app-command keys
switch (aEvent->key()) {
case XF86XK_Back:
return DispatchCommandEvent(nsWidgetAtoms::Back);
case XF86XK_Forward:
return DispatchCommandEvent(nsWidgetAtoms::Forward);
case XF86XK_Refresh:
return DispatchCommandEvent(nsWidgetAtoms::Reload);
case XF86XK_Stop:
return DispatchCommandEvent(nsWidgetAtoms::Stop);
case XF86XK_Search:
return DispatchCommandEvent(nsWidgetAtoms::Search);
case XF86XK_Favorites:
return DispatchCommandEvent(nsWidgetAtoms::Bookmarks);
case XF86XK_HomePage:
return DispatchCommandEvent(nsWidgetAtoms::Home);
}
nsKeyEvent event(PR_TRUE, NS_KEY_PRESS, this);
InitKeyEvent(event, aEvent);
if (isKeyDownCancelled) {
// If prevent default set for onkeydown, do the same for onkeypress
event.flags |= NS_EVENT_FLAG_NO_DEFAULT;
}
event.charCode = nsConvertCharCodeToUnicode(aEvent);
if (event.charCode) {
event.keyCode = 0;
// if the control, meta, or alt key is down, then we should leave
// the isShift flag alone (probably not a printable character)
// if none of the other modifier keys are pressed then we need to
// clear isShift so the character can be inserted in the editor
if (event.isControl || event.isAlt || event.isMeta) {
QKeyEvent tmpEvent = *aEvent;
// Fix for bug 69230:
// if modifier key is pressed and key pressed is not latin character,
// we should try other keyboard layouts to find out correct latin
// character corresponding to pressed key;
// that way shortcuts like Ctrl+C will work no matter what
// keyboard layout is selected
// We don't try to fix up punctuation accelerators here,
// because their location differs between latin layouts
/*
if (!is_latin_shortcut_key(event.charCode)) {
// We have a non-latin char, try other keyboard groups
GdkKeymapKey *keys;
quint32 *keyvals;
qint32 n_entries;
PRUint32 latinCharCode;
qint32 level;
if (gdk_keymap_translate_keyboard_state(NULL,
tmpEvent.hardware_keycode,
(GdkModifierType)tmpEvent.state,
tmpEvent.group,
NULL, NULL, &level, NULL)
&& gdk_keymap_get_entries_for_keycode(NULL,
tmpEvent.hardware_keycode,
&keys, &keyvals,
&n_entries)) {
qint32 n;
for (n=0; n<n_entries; n++) {
if (keys[n].group == tmpEvent.group) {
// Skip keys from the same group
continue;
}
if (keys[n].level != level) {
// Allow only same level keys
continue;
}
if (is_latin_shortcut_key(keyvals[n])) {
// Latin character found
if (event.isShift)
tmpEvent.keyval = gdk_keyval_to_upper(keyvals[n]);
else
tmpEvent.keyval = gdk_keyval_to_lower(keyvals[n]);
tmpEvent.group = keys[n].group;
latinCharCode = nsConvertCharCodeToUnicode(&tmpEvent);
if (latinCharCode) {
event.charCode = latinCharCode;
break;
}
}
}
g_free(keys);
g_free(keyvals);
}
}
*/
// make Ctrl+uppercase functional as same as Ctrl+lowercase
// when Ctrl+uppercase(eg.Ctrl+C) is pressed,convert the charCode
// from uppercase to lowercase(eg.Ctrl+c),so do Alt and Meta Key
// It is hack code for bug 61355, there is same code snip for
// Windows platform in widget/src/windows/nsWindow.cpp: See bug 16486
// Note: if Shift is pressed at the same time, do not to_lower()
// Because Ctrl+Shift has different function with Ctrl
/*
if (!event.isShift &&
event.charCode >= Qt::Key_A &&
event.charCode <= Qt::Key_Z)
event.charCode = gdk_keyval_to_lower(event.charCode);
*/
// Keep the characters unshifted for shortcuts and accesskeys and
// make sure that numbers are always passed as such (among others:
// bugs 50255 and 351310)
/*
if (!event.isControl && event.isShift &&
(event.charCode < Qt::Key_0 || event.charCode > Qt::Key_9)) {
GdkKeymapKey k = { tmpEvent.hardware_keycode, tmpEvent.group, 0 };
tmpEvent.keyval = gdk_keymap_lookup_key(gdk_keymap_get_default(), &k);
PRUint32 unshiftedCharCode = nsConvertCharCodeToUnicode(&tmpEvent);
if (unshiftedCharCode)
event.charCode = unshiftedCharCode;
} */
}
}
// before we dispatch a key, check if it's the context menu key.
// If so, send a context menu key event instead.
if (isContextMenuKey(event)) {
nsMouseEvent contextMenuEvent(PR_TRUE, NS_CONTEXTMENU, this,
nsMouseEvent::eReal,
nsMouseEvent::eContextMenuKey);
keyEventToContextMenuEvent(&event, &contextMenuEvent);
DispatchEvent(&contextMenuEvent, status);
}
else {
// send the key press event
DispatchEvent(&event, status);
}
// If the event was consumed, return.
LOGIM(("status %d\n", status));
if (status == nsEventStatus_eConsumeNoDefault) {
LOGIM(("key press consumed\n"));
return TRUE;
}
return FALSE;
}
bool
nsWindow::OnKeyReleaseEvent(QKeyEvent *aEvent)
{
LOGFOCUS(("OnKeyReleaseEvent [%p]\n", (void *)this));
#ifdef USE_XIM
if (IMEFilterEvent(aEvent))
return TRUE;
#endif
// send the key event as a key up event
nsKeyEvent event(PR_TRUE, NS_KEY_UP, this);
InitKeyEvent(event, aEvent);
// unset the key down flag
ClearKeyDownFlag(event.keyCode);
nsEventStatus status;
DispatchEvent(&event, status);
// If the event was consumed, return.
if (status == nsEventStatus_eConsumeNoDefault) {
LOGIM(("key release consumed\n"));
return TRUE;
}
return FALSE;
}
bool
nsWindow::OnScrollEvent(QWheelEvent *aEvent)
{
// check to see if we should rollup
nsMouseScrollEvent event(PR_TRUE, NS_MOUSE_SCROLL, this);
switch (aEvent->orientation()) {
case Qt::Vertical:
event.scrollFlags = nsMouseScrollEvent::kIsVertical;
break;
case Qt::Horizontal:
event.scrollFlags = nsMouseScrollEvent::kIsHorizontal;
break;
default:
Q_ASSERT(0);
break;
}
event.delta = (int)((aEvent->delta() / WHEEL_DELTA) * -3);
event.refPoint.x = nscoord(aEvent->x());
event.refPoint.y = nscoord(aEvent->y());
event.isShift = aEvent->modifiers() & Qt::ShiftModifier;
event.isControl = aEvent->modifiers() & Qt::ControlModifier;
event.isAlt = aEvent->modifiers() & Qt::AltModifier;
event.isMeta = aEvent->modifiers() & Qt::MetaModifier;
event.time = 0;
nsEventStatus status;
DispatchEvent(&event, status);
return ignoreEvent(status);
}
bool
nsWindow::showEvent(QShowEvent *)
{
LOG(("%s [%p]\n", __PRETTY_FUNCTION__,(void *)this));
qDebug("FIXME:>>>>>>Func:%s::%d\n", __PRETTY_FUNCTION__, __LINE__);
/*
QRect r = mDrawingarea->rect();
nsRect rect(r.x(), r.y(), r.width(), r.height());
nsCOMPtr<nsIRenderingContext> rc = getter_AddRefs(GetRenderingContext());
// Generate XPFE paint event
nsPaintEvent event(PR_TRUE, NS_PAINT, this);
event.refPoint.x = 0;
event.refPoint.y = 0;
event.rect = &rect;
// XXX fix this!
event.region = nsnull;
// XXX fix this!
event.renderingContext = rc;
nsEventStatus status;
DispatchEvent(&event, status);
*/
mIsVisible = PR_TRUE;
return false;
}
bool
nsWindow::hideEvent(QHideEvent *)
{
LOG(("%s [%p]\n", __PRETTY_FUNCTION__,(void *)this));
mIsVisible = PR_FALSE;
return false;
}
bool
nsWindow::OnWindowStateEvent(QEvent *aEvent)
{
qDebug("FIXME:>>>>>>Func:%s::%d\n", __PRETTY_FUNCTION__, __LINE__);
nsSizeModeEvent event(PR_TRUE, NS_SIZEMODE, this);
nsEventStatus status;
DispatchEvent(&event, status);
return ignoreEvent(status);
}
void
nsWindow::ThemeChanged()
{
nsGUIEvent event(PR_TRUE, NS_THEMECHANGED, this);
nsEventStatus status = nsEventStatus_eIgnore;
DispatchEvent(&event, status);
if (!mDrawingarea || NS_UNLIKELY(mIsDestroyed))
return;
qDebug("FIXME:>>>>>>Func:%s::%d\n", __PRETTY_FUNCTION__, __LINE__);
return;
}
bool
nsWindow::OnDragMotionEvent(QDragMoveEvent *e)
{
LOG(("nsWindow::OnDragMotionSignal\n"));
nsMouseEvent event(PR_TRUE, NS_DRAGDROP_OVER, 0,
nsMouseEvent::eReal);
return TRUE;
}
bool
nsWindow::OnDragLeaveEvent(QDragLeaveEvent *e)
{
// XXX Do we want to pass this on only if the event's subwindow is null?
LOG(("nsWindow::OnDragLeaveSignal(%p)\n", this));
nsMouseEvent event(PR_TRUE, NS_DRAGDROP_EXIT, this, nsMouseEvent::eReal);
nsEventStatus status;
DispatchEvent(&event, status);
return ignoreEvent(status);
}
bool
nsWindow::OnDragDropEvent(QDropEvent *e)
{
LOG(("nsWindow::OnDragDropSignal\n"));
nsMouseEvent event(PR_TRUE, NS_DRAGDROP_OVER, 0,
nsMouseEvent::eReal);
return TRUE;
}
bool
nsWindow::OnDragEnter(QDragEnterEvent *)
{
// XXX Do we want to pass this on only if the event's subwindow is null?
LOG(("nsWindow::OnDragEnter(%p)\n", this));
nsMouseEvent event(PR_TRUE, NS_DRAGDROP_ENTER, this, nsMouseEvent::eReal);
nsEventStatus status;
DispatchEvent(&event, status);
return ignoreEvent(status);
}
static void
GetBrandName(nsXPIDLString& brandName)
{
nsCOMPtr<nsIStringBundleService> bundleService =
do_GetService(NS_STRINGBUNDLE_CONTRACTID);
nsCOMPtr<nsIStringBundle> bundle;
if (bundleService)
bundleService->CreateBundle(
"chrome://branding/locale/brand.properties",
getter_AddRefs(bundle));
if (bundle)
bundle->GetStringFromName(
NS_LITERAL_STRING("brandShortName").get(),
getter_Copies(brandName));
if (brandName.IsEmpty())
brandName.Assign(NS_LITERAL_STRING("Mozilla"));
}
nsresult
nsWindow::NativeCreate(nsIWidget *aParent,
nsNativeWidget aNativeParent,
const nsRect &aRect,
EVENT_CALLBACK aHandleEventFunction,
nsIDeviceContext *aContext,
nsIAppShell *aAppShell,
nsIToolkit *aToolkit,
nsWidgetInitData *aInitData)
{
// only set the base parent if we're going to be a dialog or a
// toplevel
nsIWidget *baseParent = aInitData &&
(aInitData->mWindowType == eWindowType_dialog ||
aInitData->mWindowType == eWindowType_toplevel ||
aInitData->mWindowType == eWindowType_invisible) ?
nsnull : aParent;
// initialize all the common bits of this class
BaseCreate(baseParent, aRect, aHandleEventFunction, aContext,
aAppShell, aToolkit, aInitData);
// Do we need to listen for resizes?
PRBool listenForResizes = PR_FALSE;;
if (aNativeParent || (aInitData && aInitData->mListenForResizes))
listenForResizes = PR_TRUE;
// and do our common creation
CommonCreate(aParent, listenForResizes);
// save our bounds
mBounds = aRect;
if (mWindowType != eWindowType_child) {
// The window manager might place us. Indicate that if we're
// shown, we want to go through
// nsWindow::NativeResize(x,y,w,h) to maybe set our own
// position.
mNeedsMove = PR_TRUE;
}
// figure out our parent window
QWidget *parent = nsnull;
if (aParent != nsnull)
parent = (QWidget*)aParent->GetNativeData(NS_NATIVE_WIDGET);
else
parent = (QWidget*)aNativeParent;
#ifdef MOZ_ENABLE_GLITZ
GdkVisual* visual = nsnull;
if (gfxPlatform::UseGlitz()) {
nsCOMPtr<nsIDeviceContext> dc = aContext;
if (!dc) {
nsCOMPtr<nsIDeviceContext> dc = do_CreateInstance(kDeviceContextCID);
// no parent widget to initialize with
dc->Init(nsnull);
}
Display* dpy = ;
int defaultScreen = gdk_x11_get_default_screen();
glitz_drawable_format_t* format = glitz_glx_find_window_format (dpy, defaultScreen,
0, NULL, 0);
if (format) {
XVisualInfo* vinfo = glitz_glx_get_visual_info_from_format(dpy, defaultScreen, format);
GdkScreen* screen = gdk_display_get_screen(gdk_x11_lookup_xdisplay(dpy), defaultScreen);
visual = gdk_x11_screen_lookup_visual(screen, vinfo->visualid);
} else {
// couldn't find a GLX visual; force Glitz off
gfxPlatform::SetUseGlitz(PR_FALSE);
}
}
#endif
// ok, create our windows
mDrawingarea = createQWidget(parent, aInitData);
Initialize(mDrawingarea);
LOG(("nsWindow [%p]\n", (void *)this));
if (mDrawingarea) {
LOG(("\tmDrawingarea %p %p %p %lx %lx\n", (void *)mDrawingarea));
}
// resize so that everything is set to the right dimensions
if (!mIsTopLevel)
Resize(mBounds.x, mBounds.y, mBounds.width, mBounds.height, PR_FALSE);
return NS_OK;
}
NS_IMETHODIMP
nsWindow::SetWindowClass(const nsAString &xulWinType)
{
if (!mDrawingarea)
return NS_ERROR_FAILURE;
nsXPIDLString brandName;
GetBrandName(brandName);
XClassHint *class_hint = XAllocClassHint();
if (!class_hint)
return NS_ERROR_OUT_OF_MEMORY;
const char *role = NULL;
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;
// gdk_window_set_role(GTK_WIDGET(mDrawingarea)->window, role);
qDebug("FIXME:>>>>>>Func:%s::%d\n", __PRETTY_FUNCTION__, __LINE__);
// Can't use gtk_window_set_wmclass() for this; it prints
// a warning & refuses to make the change.
XSetClassHint(mDrawingarea->x11Info().display(),
mDrawingarea->handle(),
class_hint);
nsMemory::Free(class_hint->res_class);
nsMemory::Free(class_hint->res_name);
XFree(class_hint);
return NS_OK;
}
void
nsWindow::NativeResize(PRInt32 aWidth, PRInt32 aHeight, PRBool aRepaint)
{
LOG(("nsWindow::NativeResize [%p] %d %d\n", (void *)this,
aWidth, aHeight));
ResizeTransparencyBitmap(aWidth, aHeight);
// clear our resize flag
mNeedsResize = PR_FALSE;
mDrawingarea->resize( aWidth, aHeight);
if (aRepaint) {
if (mDrawingarea->isVisible())
mDrawingarea->repaint();
}
}
void
nsWindow::NativeResize(PRInt32 aX, PRInt32 aY,
PRInt32 aWidth, PRInt32 aHeight,
PRBool aRepaint)
{
mNeedsResize = PR_FALSE;
mNeedsMove = PR_FALSE;
LOG(("nsWindow::NativeResize [%p] %d %d %d %d\n", (void *)this,
aX, aY, aWidth, aHeight));
ResizeTransparencyBitmap(aWidth, aHeight);
QPoint pos(aX, aY);
if (mDrawingarea)
{
if (mParent && mDrawingarea->windowType() == Qt::Popup) {
nsRect oldrect, newrect;
oldrect.x = aX;
oldrect.y = aY;
mParent->WidgetToScreen(oldrect, newrect);
pos = QPoint(newrect.x, newrect.y);
#ifdef DEBUG_WIDGETS
qDebug("pos is [%d,%d]", pos.x(), pos.y());
#endif
} else {
#ifdef DEBUG_WIDGETS
qDebug("Widget with original position? (%p)", mDrawingarea);
#endif
}
}
mDrawingarea->setGeometry(pos.x(), pos.y(), aWidth, aHeight);
if (aRepaint) {
if (mDrawingarea->isVisible())
mDrawingarea->repaint();
}
}
void
nsWindow::NativeShow (PRBool aAction)
{
if (aAction) {
// GTK wants us to set the window mask before we show the window
// for the first time, or setting the mask later won't work.
// GTK also wants us to NOT set the window mask if we're not really
// going to need it, because GTK won't let us unset the mask properly
// later.
// So, we delay setting the mask until the last moment: when the window
// is shown.
// XXX that may or may not be true for GTK+ 2.x
if (mTransparencyBitmap) {
ApplyTransparencyBitmap();
}
// unset our flag now that our window has been shown
mNeedsShow = PR_FALSE;
}
if (!mDrawingarea) {
//XXX: apperently can be null during the printing, check whether
// that's true
qDebug("nsCommon::Show : widget empty");
return;
}
mDrawingarea->setShown(aAction);
}
void
nsWindow::EnsureGrabs(void)
{
if (mRetryPointerGrab)
GrabPointer();
if (mRetryKeyboardGrab)
GrabKeyboard();
}
NS_IMETHODIMP
nsWindow::SetHasTransparentBackground(PRBool aTransparent)
{
// if (!mDrawingarea) {
// Pass the request to the toplevel window
// return topWindow->SetHasTransparentBackground(aTransparent);
// }
if (mIsTransparent == aTransparent)
return NS_OK;
if (!aTransparent) {
if (mTransparencyBitmap) {
delete[] mTransparencyBitmap;
mTransparencyBitmap = nsnull;
mTransparencyBitmapWidth = 0;
mTransparencyBitmapHeight = 0;
// gtk_widget_reset_shapes(mDrawingarea);
}
} // else the new default alpha values are "all 1", so we don't
// need to change anything yet
mIsTransparent = aTransparent;
return NS_OK;
}
NS_IMETHODIMP
nsWindow::GetHasTransparentBackground(PRBool& aTransparent)
{
if (!mDrawingarea) {
// Pass the request to the toplevel window
// QWidget *topWidget = nsnull;
// GetToplevelWidget(&topWidget);
// if (!topWidget) {
// aTransparent = PR_FALSE;
// return NS_ERROR_FAILURE;
// }
// if (!topWindow) {
// aTransparent = PR_FALSE;
// return NS_ERROR_FAILURE;
// }
// return topWindow->GetHasTransparentBackground(aTransparent);
}
aTransparent = mIsTransparent;
return NS_OK;
}
void
nsWindow::ResizeTransparencyBitmap(PRInt32 aNewWidth, PRInt32 aNewHeight)
{
if (!mTransparencyBitmap)
return;
if (aNewWidth == mTransparencyBitmapWidth &&
aNewHeight == mTransparencyBitmapHeight)
return;
PRInt32 newSize = ((aNewWidth+7)/8)*aNewHeight;
char* newBits = new char[newSize];
if (!newBits) {
delete[] mTransparencyBitmap;
mTransparencyBitmap = nsnull;
mTransparencyBitmapWidth = 0;
mTransparencyBitmapHeight = 0;
return;
}
// fill new mask with "opaque", first
memset(newBits, 255, newSize);
// Now copy the intersection of the old and new areas into the new mask
PRInt32 copyWidth = PR_MIN(aNewWidth, mTransparencyBitmapWidth);
PRInt32 copyHeight = PR_MIN(aNewHeight, mTransparencyBitmapHeight);
PRInt32 oldRowBytes = (mTransparencyBitmapWidth+7)/8;
PRInt32 newRowBytes = (aNewWidth+7)/8;
PRInt32 copyBytes = (copyWidth+7)/8;
PRInt32 i;
char* fromPtr = mTransparencyBitmap;
char* toPtr = newBits;
for (i = 0; i < copyHeight; i++) {
memcpy(toPtr, fromPtr, copyBytes);
fromPtr += oldRowBytes;
toPtr += newRowBytes;
}
delete[] mTransparencyBitmap;
mTransparencyBitmap = newBits;
mTransparencyBitmapWidth = aNewWidth;
mTransparencyBitmapHeight = aNewHeight;
}
static PRBool
ChangedMaskBits(char* aMaskBits, PRInt32 aMaskWidth, PRInt32 aMaskHeight,
const nsRect& aRect, PRUint8* aAlphas, PRInt32 aStride)
{
PRInt32 x, y, xMax = aRect.XMost(), yMax = aRect.YMost();
PRInt32 maskBytesPerRow = (aMaskWidth + 7)/8;
for (y = aRect.y; y < yMax; y++) {
char* maskBytes = aMaskBits + y*maskBytesPerRow;
PRUint8* alphas = aAlphas;
for (x = aRect.x; x < xMax; x++) {
PRBool newBit = *alphas > 0;
alphas++;
char maskByte = maskBytes[x >> 3];
PRBool maskBit = (maskByte & (1 << (x & 7))) != 0;
if (maskBit != newBit) {
return PR_TRUE;
}
}
aAlphas += aStride;
}
return PR_FALSE;
}
static
void UpdateMaskBits(char* aMaskBits, PRInt32 aMaskWidth, PRInt32 aMaskHeight,
const nsRect& aRect, PRUint8* aAlphas, PRInt32 aStride)
{
PRInt32 x, y, xMax = aRect.XMost(), yMax = aRect.YMost();
PRInt32 maskBytesPerRow = (aMaskWidth + 7)/8;
for (y = aRect.y; y < yMax; y++) {
char* maskBytes = aMaskBits + y*maskBytesPerRow;
PRUint8* alphas = aAlphas;
for (x = aRect.x; x < xMax; x++) {
PRBool newBit = *alphas > 0;
alphas++;
char mask = 1 << (x & 7);
char maskByte = maskBytes[x >> 3];
// Note: '-newBit' turns 0 into 00...00 and 1 into 11...11
maskBytes[x >> 3] = (maskByte & ~mask) | (-newBit & mask);
}
aAlphas += aStride;
}
}
void
nsWindow::ApplyTransparencyBitmap()
{
qDebug("FIXME:>>>>>>Func:%s::%d\n", __PRETTY_FUNCTION__, __LINE__);
/*
gtk_widget_reset_shapes(mDrawingarea);
GdkBitmap* maskBitmap = gdk_bitmap_create_from_data(mDrawingarea->window,
mTransparencyBitmap,
mTransparencyBitmapWidth, mTransparencyBitmapHeight);
if (!maskBitmap)
return;
gtk_widget_shape_combine_mask(mDrawingarea, maskBitmap, 0, 0);
gdk_bitmap_unref(maskBitmap);
*/
}
nsresult
nsWindow::UpdateTranslucentWindowAlphaInternal(const nsRect& aRect,
PRUint8* aAlphas, PRInt32 aStride)
{
if (!mDrawingarea) {
// Pass the request to the toplevel window
// return topWindow->UpdateTranslucentWindowAlphaInternal(aRect, aAlphas, aStride);
return NS_ERROR_FAILURE;
}
NS_ASSERTION(mIsTransparent, "Window is not transparent");
if (mTransparencyBitmap == nsnull) {
PRInt32 size = ((mBounds.width+7)/8)*mBounds.height;
mTransparencyBitmap = new char[size];
if (mTransparencyBitmap == nsnull)
return NS_ERROR_FAILURE;
memset(mTransparencyBitmap, 255, size);
mTransparencyBitmapWidth = mBounds.width;
mTransparencyBitmapHeight = mBounds.height;
}
NS_ASSERTION(aRect.x >= 0 && aRect.y >= 0
&& aRect.XMost() <= mBounds.width && aRect.YMost() <= mBounds.height,
"Rect is out of window bounds");
if (!ChangedMaskBits(mTransparencyBitmap, mBounds.width, mBounds.height,
aRect, aAlphas, aStride))
// skip the expensive stuff if the mask bits haven't changed; hopefully
// this is the common case
return NS_OK;
UpdateMaskBits(mTransparencyBitmap, mBounds.width, mBounds.height,
aRect, aAlphas, aStride);
if (!mNeedsShow) {
ApplyTransparencyBitmap();
}
return NS_OK;
}
void
nsWindow::GrabPointer(void)
{
LOG(("GrabPointer %d\n", mRetryPointerGrab));
mRetryPointerGrab = PR_FALSE;
// If the window isn't visible, just set the flag to retry the
// grab. When this window becomes visible, the grab will be
// retried.
PRBool visibility = PR_TRUE;
IsVisible(visibility);
if (!visibility) {
LOG(("GrabPointer: window not visible\n"));
mRetryPointerGrab = PR_TRUE;
return;
}
if (!mDrawingarea)
return;
mDrawingarea->grabMouse();
}
void
nsWindow::GrabKeyboard(void)
{
LOG(("GrabKeyboard %d\n", mRetryKeyboardGrab));
mRetryKeyboardGrab = PR_FALSE;
// If the window isn't visible, just set the flag to retry the
// grab. When this window becomes visible, the grab will be
// retried.
PRBool visibility = PR_TRUE;
IsVisible(visibility);
if (!visibility) {
LOG(("GrabKeyboard: window not visible\n"));
mRetryKeyboardGrab = PR_TRUE;
return;
}
if (!mDrawingarea)
return;
mDrawingarea->grabKeyboard();
}
void
nsWindow::ReleaseGrabs(void)
{
LOG(("ReleaseGrabs\n"));
mRetryPointerGrab = PR_FALSE;
mRetryKeyboardGrab = PR_FALSE;
// gdk_pointer_ungrab(Qt::Key_CURRENT_TIME);
// gdk_keyboard_ungrab(Qt::Key_CURRENT_TIME);
}
void
nsWindow::GetToplevelWidget(QWidget **aWidget)
{
*aWidget = nsnull;
if (mDrawingarea) {
*aWidget = mDrawingarea;
return;
}
}
void
nsWindow::SetUrgencyHint(QWidget *top_window, PRBool state)
{
if (!top_window)
return;
qDebug("FIXME:>>>>>>Func:%s::%d\n", __PRETTY_FUNCTION__, __LINE__);
/*
// Try to get a pointer to gdk_window_set_urgency_hint
PRLibrary* lib;
_gdk_window_set_urgency_hint_fn _gdk_window_set_urgency_hint = nsnull;
_gdk_window_set_urgency_hint = (_gdk_window_set_urgency_hint_fn)
PR_FindFunctionSymbolAndLibrary("gdk_window_set_urgency_hint", &lib);
if (_gdk_window_set_urgency_hint) {
_gdk_window_set_urgency_hint(top_window->window, state);
PR_UnloadLibrary(lib);
}
else if (state) {
gdk_window_show_unraised(top_window->window);
}
*/
}
void *
nsWindow::SetupPluginPort(void)
{
if (!mDrawingarea)
return nsnull;
qDebug("FIXME:>>>>>>Func:%s::%d\n", __PRETTY_FUNCTION__, __LINE__);
/*
// we have to flush the X queue here so that any plugins that
// might be running on separate X connections will be able to use
// this window in case it was just created
XWindowAttributes xattrs;
XGetWindowAttributes(Qt::Key_DISPLAY (),
Qt::Key_WINDOW_XWINDOW(mDrawingarea->inner_window),
&xattrs);
XSelectInput (Qt::Key_DISPLAY (),
Qt::Key_WINDOW_XWINDOW(mDrawingarea->inner_window),
xattrs.your_event_mask |
SubstructureNotifyMask);
gdk_window_add_filter(mDrawingarea->inner_window,
plugin_window_filter_func,
this);
XSync(Qt::Key_DISPLAY(), False);
return (void *)Qt::Key_WINDOW_XWINDOW(mDrawingarea->inner_window);
*/
return nsnull;
}
nsresult
nsWindow::SetWindowIconList(const nsCStringArray &aIconList)
{
qDebug("FIXME:>>>>>>Func:%s::%d\n", __PRETTY_FUNCTION__, __LINE__);
return NS_OK;
}
void
nsWindow::SetDefaultIcon(void)
{
SetIcon(NS_LITERAL_STRING("default"));
}
void
nsWindow::SetPluginType(PluginType aPluginType)
{
mPluginType = aPluginType;
}
void
nsWindow::SetNonXEmbedPluginFocus()
{
qDebug("FIXME:>>>>>>Func:%s::%d\n", __PRETTY_FUNCTION__, __LINE__);
}
void
nsWindow::LoseNonXEmbedPluginFocus()
{
qDebug("FIXME:>>>>>>Func:%s::%d\n", __PRETTY_FUNCTION__, __LINE__);
LOGFOCUS(("nsWindow::LoseNonXEmbedPluginFocus\n"));
LOGFOCUS(("nsWindow::LoseNonXEmbedPluginFocus end\n"));
}
qint32
nsWindow::ConvertBorderStyles(nsBorderStyle aStyle)
{
qint32 w = 0;
if (aStyle == eBorderStyle_default)
return -1;
qDebug("FIXME:>>>>>>Func:%s::%d\n", __PRETTY_FUNCTION__, __LINE__);
/*
if (aStyle & eBorderStyle_all)
w |= Qt::Key_DECOR_ALL;
if (aStyle & eBorderStyle_border)
w |= Qt::Key_DECOR_BORDER;
if (aStyle & eBorderStyle_resizeh)
w |= Qt::Key_DECOR_RESIZEH;
if (aStyle & eBorderStyle_title)
w |= Qt::Key_DECOR_TITLE;
if (aStyle & eBorderStyle_menu)
w |= Qt::Key_DECOR_MENU;
if (aStyle & eBorderStyle_minimize)
w |= Qt::Key_DECOR_MINIMIZE;
if (aStyle & eBorderStyle_maximize)
w |= Qt::Key_DECOR_MAXIMIZE;
if (aStyle & eBorderStyle_close) {
#ifdef DEBUG
printf("we don't handle eBorderStyle_close yet... please fix me\n");
#endif
}
*/
return w;
}
NS_IMETHODIMP
nsWindow::MakeFullScreen(PRBool aFullScreen)
{
/*
#if GTK_CHECK_VERSION(2,2,0)
if (aFullScreen)
gdk_window_fullscreen (mDrawingarea->window);
else
gdk_window_unfullscreen (mDrawingarea->window);
return NS_OK;
#else
*/
return nsBaseWidget::MakeFullScreen(aFullScreen);
//#endif
}
NS_IMETHODIMP
nsWindow::HideWindowChrome(PRBool aShouldHide)
{
if (!mDrawingarea) {
// Pass the request to the toplevel window
QWidget *topWidget = nsnull;
GetToplevelWidget(&topWidget);
// return topWindow->HideWindowChrome(aShouldHide);
return NS_ERROR_FAILURE;
}
// Sawfish, metacity, and presumably other window managers get
// confused if we change the window decorations while the window
// is visible.
PRBool wasVisible = PR_FALSE;
if (mDrawingarea->isVisible()) {
mDrawingarea->hide();
wasVisible = PR_TRUE;
}
qint32 wmd;
if (aShouldHide)
wmd = 0;
else
wmd = ConvertBorderStyles(mBorderStyle);
// gdk_window_set_decorations(mDrawingarea->window, (GdkWMDecoration) wmd);
if (wasVisible) {
mDrawingarea->show();
}
// For some window managers, adding or removing window decorations
// requires unmapping and remapping our toplevel window. Go ahead
// and flush the queue here so that we don't end up with a BadWindow
// error later when this happens (when the persistence timer fires
// and GetWindowPos is called)
XSync(mDrawingarea->x11Info().display(), False);
return NS_OK;
}
/* static */
PRBool
is_mouse_in_window (QWidget* aWindow, double aMouseX, double aMouseY)
{
qint32 x = 0;
qint32 y = 0;
qint32 w, h;
qint32 offsetX = 0;
qint32 offsetY = 0;
QWidget *window;
window = aWindow;
while (window) {
qint32 tmpX = window->pos().x();
qint32 tmpY = window->pos().y();
// if this is a window, compute x and y given its origin and our
// offset
x = tmpX + offsetX;
y = tmpY + offsetY;
break;
offsetX += tmpX;
offsetY += tmpY;
}
w = window->size().width();
h = window->size().height();
if (aMouseX > x && aMouseX < x + w &&
aMouseY > y && aMouseY < y + h)
return PR_TRUE;
return PR_FALSE;
}
/* static */
nsWindow *
get_window_for_qt_widget(QWidget *widget)
{
MozQWidget *mozWidget = static_cast<MozQWidget*>(widget);
return mozWidget->getReciever();
}
//////////////////////////////////////////////////////////////////////
// These are all of our drag and drop operations
void
nsWindow::InitDragEvent(nsMouseEvent &aEvent)
{
// set the keyboard modifiers
/*
qint32 x, y;
GdkModifierType state = (GdkModifierType)0;
gdk_window_get_pointer(NULL, &x, &y, &state);
aEvent.isShift = (state & Qt::Key_SHIFT_MASK) ? PR_TRUE : PR_FALSE;
aEvent.isControl = (state & Qt::Key_CONTROL_MASK) ? PR_TRUE : PR_FALSE;
aEvent.isAlt = (state & Qt::Key_MOD1_MASK) ? PR_TRUE : PR_FALSE;
aEvent.isMeta = PR_FALSE; // GTK+ doesn't support the meta key
*/
}
// This will update the drag action based on the information in the
// drag context. Gtk gets this from a combination of the key settings
// and what the source is offering.
/* static */
nsresult
initialize_prefs(void)
{
// check to see if we should set our raise pref
nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
if (!prefs)
return NS_OK;
PRBool val = PR_TRUE;
nsresult rv;
rv = prefs->GetBoolPref("mozilla.widget.raise-on-setfocus", &val);
return NS_OK;
}
inline PRBool
is_context_menu_key(const nsKeyEvent& aKeyEvent)
{
return ((aKeyEvent.keyCode == NS_VK_F10 && aKeyEvent.isShift &&
!aKeyEvent.isControl && !aKeyEvent.isMeta && !aKeyEvent.isAlt) ||
(aKeyEvent.keyCode == NS_VK_CONTEXT_MENU && !aKeyEvent.isShift &&
!aKeyEvent.isControl && !aKeyEvent.isMeta && !aKeyEvent.isAlt));
}
void
key_event_to_context_menu_event(nsMouseEvent &aEvent,
QKeyEvent *aGdkEvent)
{
aEvent.refPoint = nsPoint(0, 0);
aEvent.isShift = PR_FALSE;
aEvent.isControl = PR_FALSE;
aEvent.isAlt = PR_FALSE;
aEvent.isMeta = PR_FALSE;
aEvent.time = 0;
aEvent.clickCount = 1;
}
static PRBool
gdk_keyboard_get_modmap_masks(Display* aDisplay,
PRUint32* aCapsLockMask,
PRUint32* aNumLockMask,
PRUint32* aScrollLockMask)
{
*aCapsLockMask = 0;
*aNumLockMask = 0;
*aScrollLockMask = 0;
int min_keycode = 0;
int max_keycode = 0;
XDisplayKeycodes(aDisplay, &min_keycode, &max_keycode);
int keysyms_per_keycode = 0;
KeySym* xkeymap = XGetKeyboardMapping(aDisplay, min_keycode,
max_keycode - min_keycode + 1,
&keysyms_per_keycode);
if (!xkeymap) {
return PR_FALSE;
}
XModifierKeymap* xmodmap = XGetModifierMapping(aDisplay);
if (!xmodmap) {
XFree(xkeymap);
return PR_FALSE;
}
/*
The modifiermap member of the XModifierKeymap structure contains 8 sets
of max_keypermod KeyCodes, one for each modifier in the order Shift,
Lock, Control, Mod1, Mod2, Mod3, Mod4, and Mod5.
Only nonzero KeyCodes have meaning in each set, and zero KeyCodes are ignored.
*/
const unsigned int map_size = 8 * xmodmap->max_keypermod;
for (unsigned int i = 0; i < map_size; i++) {
KeyCode keycode = xmodmap->modifiermap[i];
if (!keycode || keycode < min_keycode || keycode > max_keycode)
continue;
const KeySym* syms = xkeymap + (keycode - min_keycode) * keysyms_per_keycode;
const unsigned int mask = 1 << (i / xmodmap->max_keypermod);
for (int j = 0; j < keysyms_per_keycode; j++) {
switch (syms[j]) {
case Qt::Key_CapsLock: *aCapsLockMask |= mask; break;
case Qt::Key_NumLock: *aNumLockMask |= mask; break;
case Qt::Key_ScrollLock: *aScrollLockMask |= mask; break;
}
}
}
XFreeModifiermap(xmodmap);
XFree(xkeymap);
return PR_TRUE;
}
// nsChildWindow class
nsChildWindow::nsChildWindow()
{
}
nsChildWindow::~nsChildWindow()
{
}
nsPopupWindow::nsPopupWindow()
{
qDebug("===================== popup!");
}
nsPopupWindow::~nsPopupWindow()
{
}
QWidget*
nsWindow::createQWidget(QWidget *parent, nsWidgetInitData *aInitData)
{
Qt::WFlags flags = Qt::Widget;
#ifdef DEBUG_WIDGETS
qDebug("NEW WIDGET\n\tparent is %p (%s)", (void*)parent,
parent ? qPrintable(parent->objectName()) : "null");
#endif
// ok, create our windows
switch (mWindowType) {
case eWindowType_dialog:
case eWindowType_popup:
case eWindowType_toplevel:
case eWindowType_invisible: {
mIsTopLevel = PR_TRUE;
nsXPIDLString brandName;
GetBrandName(brandName);
NS_ConvertUTF16toUTF8 cBrand(brandName);
if (mWindowType == eWindowType_dialog) {
//gtk_window_set_wmclass(GTK_WINDOW(mDrawingarea), "Dialog", cBrand.get());
//gtk_window_set_type_hint(GTK_WINDOW(mDrawingarea),
// Qt::Key_WINDOW_TYPE_HINT_DIALOG);
flags |= Qt::Dialog;
mDrawingarea = new MozQWidget(this, parent, "topLevelDialog", flags);
qDebug("\t\t#### dialog (%p)", (void*)mDrawingarea);
//SetDefaultIcon();
}
else if (mWindowType == eWindowType_popup) {
flags |= Qt::Popup;
// gtk_window_set_wmclass(GTK_WINDOW(mDrawingarea), "Toplevel", cBrand.get());
//gtk_window_set_decorated(GTK_WINDOW(mDrawingarea), FALSE);
mDrawingarea = new MozQWidget(this, parent, "topLevelPopup", flags);
qDebug("\t\t#### popup (%p)", (void*)mDrawingarea);
mDrawingarea->setFocusPolicy(Qt::WheelFocus);
}
else { // must be eWindowType_toplevel
flags |= Qt::Window;
mDrawingarea = new MozQWidget(this, parent, "topLevelWindow", flags);
qDebug("\t\t#### toplevel (%p)", (void*)mDrawingarea);
//SetDefaultIcon();
}
if (mWindowType == eWindowType_popup) {
// gdk does not automatically set the cursor for "temporary"
// windows, which are what gtk uses for popups.
mCursor = eCursor_wait; // force SetCursor to actually set the
// cursor, even though our internal state
// indicates that we already have the
// standard cursor.
SetCursor(eCursor_standard);
}
}
break;
case eWindowType_child: {
mDrawingarea = new MozQWidget(this, parent, "paintArea", 0);
qDebug("\t\t#### child (%p)", (void*)mDrawingarea);
}
break;
default:
break;
}
mDrawingarea->setAttribute(Qt::WA_StaticContents);
mDrawingarea->setAttribute(Qt::WA_OpaquePaintEvent); // Transparent Widget Background
// Disable the double buffer because it will make the caret crazy
// For bug#153805 (Gtk2 double buffer makes carets misbehave)
mDrawingarea->setAttribute(Qt::WA_NoSystemBackground);
mDrawingarea->setAttribute(Qt::WA_PaintOnScreen);
return mDrawingarea;
}
// return the gfxASurface for rendering to this widget
gfxASurface*
nsWindow::GetThebesSurface()
{
// XXXvlad always create a new thebes surface for now,
// because the old clip doesn't get cleared otherwise.
// we should fix this at some point, and just reset
// the clip.
mThebesSurface = nsnull;
if (!mThebesSurface) {
qint32 x_offset = 0, y_offset = 0;
qint32 width = mDrawingarea->width(), height = mDrawingarea->height();
// Owen Taylor says this is the right thing to do!
width = PR_MIN(32767, width);
height = PR_MIN(32767, height);
if (!gfxPlatform::UseGlitz()) {
mThebesSurface = new gfxXlibSurface
(mDrawingarea->x11Info().display(),
(Drawable)mDrawingarea->handle(),
static_cast<Visual*>(mDrawingarea->x11Info().visual()),
gfxIntSize(width, height));
// if the surface creation is reporting an error, then
// we don't have a surface to give back
if (mThebesSurface && mThebesSurface->CairoStatus() != 0)
mThebesSurface = nsnull;
} else {
#ifdef MOZ_ENABLE_GLITZ
glitz_surface_t *gsurf;
glitz_drawable_t *gdraw;
glitz_drawable_format_t *gdformat = glitz_glx_find_window_format (Qt::Key_DISPLAY(),
gdk_x11_get_default_screen(),
0, NULL, 0);
if (!gdformat)
NS_ERROR("Failed to find glitz drawable format");
Display* dpy = Qt::Key_WINDOW_XDISPLAY(d);
Window wnd = Qt::Key_WINDOW_XWINDOW(d);
gdraw =
glitz_glx_create_drawable_for_window (dpy,
DefaultScreen(dpy),
gdformat,
wnd,
width,
height);
glitz_format_t *gformat =
glitz_find_standard_format (gdraw, GLITZ_STANDARD_RGB24);
gsurf =
glitz_surface_create (gdraw,
gformat,
width,
height,
0,
NULL);
glitz_surface_attach (gsurf, gdraw, GLITZ_DRAWABLE_BUFFER_FRONT_COLOR);
//fprintf (stderr, "## nsThebesDrawingSurface::Init Glitz DRAWABLE %p (DC: %p)\n", aWidget, aDC);
mThebesSurface = new gfxGlitzSurface (gdraw, gsurf, PR_TRUE);
#endif
}
if (mThebesSurface) {
mThebesSurface->SetDeviceOffset(gfxPoint(-x_offset, -y_offset));
}
}
return mThebesSurface;
}
NS_IMETHODIMP
nsWindow::BeginResizeDrag(nsGUIEvent* aEvent, PRInt32 aHorizontal, PRInt32 aVertical)
{
NS_ENSURE_ARG_POINTER(aEvent);
if (aEvent->eventStructType != NS_MOUSE_EVENT) {
// you can only begin a resize drag with a mouse event
return NS_ERROR_INVALID_ARG;
}
nsMouseEvent* mouse_event = static_cast<nsMouseEvent*>(aEvent);
if (mouse_event->button != nsMouseEvent::eLeftButton) {
// you can only begin a resize drag with the left mouse button
return NS_ERROR_INVALID_ARG;
}
qDebug("FIXME:>>>>>>Func:%s::%d\n", __PRETTY_FUNCTION__, __LINE__);
return NS_OK;
}
bool
nsWindow::contextMenuEvent(QContextMenuEvent *)
{
//qDebug("context menu");
return false;
}
bool
nsWindow::imStartEvent(QEvent *)
{
qWarning("XXX imStartEvent");
return false;
}
bool
nsWindow::imComposeEvent(QEvent *)
{
qWarning("XXX imComposeEvent");
return false;
}
bool
nsWindow::imEndEvent(QEvent * )
{
qWarning("XXX imComposeEvent");
return false;
}