/**************************************************************************** ** ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). ** Contact: Qt Software Information (qt-info@nokia.com) ** ** Copyright (C) 2009 netlabs.org. OS/2 parts. ** ** This file is part of the QtGui module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial Usage ** Licensees holding valid Qt Commercial licenses may use this file in ** accordance with the Qt Commercial License Agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Nokia. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain ** additional rights. These rights are described in the Nokia Qt LGPL ** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this ** package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** If you are unsure which license is appropriate for your use, please ** contact the sales department at qt-sales@nokia.com. ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qt_os2.h" #include "qdebug.h" #include "qapplication.h" #include "qapplication_p.h" #include "qwidget.h" #include "qpointer.h" #include "qcolormap.h" #include "qpixmapcache.h" #include "qdesktopwidget.h" #include "qset.h" #include "private/qeventdispatcher_pm_p.h" #include "private/qbackingstore_p.h" #include "qwidget_p.h" #include "qkeymapper_p.h" #include "qcursor_p.h" #define QT_DEBUGMSGFLOW QT_BEGIN_NAMESPACE /***************************************************************************** Internal variables and functions *****************************************************************************/ static HWND curWin = 0; // current window static HPS displayPS = 0; // display presentation space #if !defined (QT_NO_SESSIONMANAGER) // Session management static bool sm_blockUserInput = FALSE; //#define DEBUG_SESSIONMANAGER #endif static bool replayPopupMouseEvent = false; // replay handling when popups close // ignore the next release event if return from a modal widget static bool ignoreNextMouseReleaseEvent = false; #if defined(QT_DEBUG) static bool appNoGrab = false; // mouse/keyboard grabbing #endif static bool app_do_modal = false; // modal mode extern QWidgetList *qt_modal_stack; extern QDesktopWidget *qt_desktopWidget; static QPointer popupButtonFocus; static bool qt_try_modal(QWidget*, QMSG*, MRESULT&); QWidget *qt_button_down = 0; // widget got last button-down QPointer qt_last_mouse_receiver = 0; static HWND autoCaptureWnd = NULLHANDLE; static bool autoCaptureReleased = FALSE; static void setAutoCapture(HWND); // automatic capture static void releaseAutoCapture(); extern QCursor *qt_grab_cursor(); extern void qt_WinQueryClipRegionOrRect(HWND hwnd, HRGN hrgn); // qwidget_pm.cpp extern QRegion qt_dirtyRegion(QWidget *); // qbackingstore.cpp MRESULT EXPENTRY QtWndProc(HWND, ULONG, MPARAM, MPARAM); class QETWidget : public QWidget // event translator widget { public: QWExtra *xtra() { return d_func()->extraData(); } QTLWExtra *topData() { return d_func()->topData(); } // @todo later // QTLWExtra *maybeTopData() { return d_func()->maybeTopData(); } // void syncBackingStore(const QRegion &rgn) { d_func()->syncBackingStore(rgn); } // void syncBackingStore() { d_func()->syncBackingStore(); } QWidgetData *dataPtr() { return data; } QWidgetPrivate *dptr() { return d_func(); } // QRect frameStrut() const { return d_func()->frameStrut(); } bool pmEvent(QMSG *m, MRESULT *r) { return QWidget::pmEvent(m, r); } // void markFrameStrutDirty() { data->fstrut_dirty = 1; } bool translateMouseEvent(const QMSG &qmsg); #ifndef QT_NO_WHEELEVENT bool translateWheelEvent(const QMSG &qmsg); #endif bool translatePaintEvent(const QMSG &qmsg); bool translateConfigEvent(const QMSG &qmsg); bool translateCloseEvent(const QMSG &qmsg); // void repolishStyle(QStyle &style); // inline void showChildren(bool spontaneous) { d_func()->showChildren(spontaneous); } // inline void hideChildren(bool spontaneous) { d_func()->hideChildren(spontaneous); } // inline void validateObstacles() { d_func()->validateObstacles(); } // inline uint testWindowState(uint teststate){ return dataPtr()->window_state & teststate; } // inline void forceUpdate() { // QTLWExtra *tlwExtra = window()->d_func()->maybeTopData(); // if (tlwExtra && tlwExtra->backingStore) // tlwExtra->backingStore->markDirty(rect(), this, true, true); // } }; static void qt_set_pm_resources() { // @todo later: take colors, fonts, etc. from the system theme }; /***************************************************************************** qt_init() - initializes Qt for PM *****************************************************************************/ void qt_init(QApplicationPrivate *priv, int) { int argc = priv->argc; char **argv = priv->argv; int i, j; // Get command line params j = argc ? 1 : 0; for (i=1; iargc) { priv->argv[j] = 0; priv->argc = j; } // initialize key mapper QKeyMapper::changeKeyboard(); QColormap::initialize(); QFont::initialize(); #ifndef QT_NO_CURSOR QCursorData::initialize(); #endif qApp->setObjectName(priv->appName()); // default font QApplicationPrivate::setSystemFont( QFont(QLatin1String("System Proportional"), 10)); // QFont::locale_init(); ### Uncomment when it does something on OS/2 if (QApplication::desktopSettingsAware()) qt_set_pm_resources(); } /***************************************************************************** qt_cleanup() - cleans up when the application is finished *****************************************************************************/ void qt_cleanup() { QPixmapCache::clear(); #ifndef QT_NO_CURSOR QCursorData::cleanup(); #endif QFont::cleanup(); QColormap::cleanup(); if (displayPS) { WinReleasePS(displayPS); displayPS = 0; } } /***************************************************************************** Platform specific global and internal functions *****************************************************************************/ Q_GUI_EXPORT HPS qt_display_ps() { Q_ASSERT(qApp && qApp->thread() == QThread::currentThread()); if (!displayPS) displayPS = WinGetScreenPS(HWND_DESKTOP); return displayPS; } // application no-grab option bool qt_nograb() { #if defined(QT_DEBUG) return appNoGrab; #else return false; #endif } /***************************************************************************** Safe configuration (move,resize,setGeometry) mechanism to avoid recursion when processing messages. *****************************************************************************/ struct QPMConfigRequest { WId id; // widget to be configured int req; // 0=move, 1=resize, 2=setGeo int x, y, w, h; // request parameters }; Q_GLOBAL_STATIC(QList, configRequests); void qPMRequestConfig(WId id, int req, int x, int y, int w, int h) { QPMConfigRequest *r = new QPMConfigRequest; r->id = id; r->req = req; r->x = x; r->y = y; r->w = w; r->h = h; configRequests()->append(r); } /***************************************************************************** GUI event dispatcher *****************************************************************************/ class QGuiEventDispatcherPM : public QEventDispatcherPM { public: QGuiEventDispatcherPM(QObject *parent = 0); bool processEvents(QEventLoop::ProcessEventsFlags flags); }; QGuiEventDispatcherPM::QGuiEventDispatcherPM(QObject *parent) : QEventDispatcherPM(parent) { // pre-create the message queue early as we'll need it anyway in GUI mode createMsgQueue(); } bool QGuiEventDispatcherPM::processEvents(QEventLoop::ProcessEventsFlags flags) { if (!QEventDispatcherPM::processEvents(flags)) return false; QPMConfigRequest *r; for (;;) { if (configRequests()->isEmpty()) break; r = configRequests()->takeLast(); QWidget *w = QWidget::find(r->id); QRect rect(r->x, r->y, r->w, r->h); int req = r->req; delete r; if (w) { // widget exists if (w->testAttribute(Qt::WA_WState_ConfigPending)) break; // biting our tail if (req == 0) w->move(rect.topLeft()); else if (req == 1) w->resize(rect.size()); else w->setGeometry(rect); } } return true; } void QApplicationPrivate::createEventDispatcher() { Q_Q(QApplication); if (q->type() != QApplication::Tty) eventDispatcher = new QGuiEventDispatcherPM(q); else eventDispatcher = new QEventDispatcherPM(q); } /***************************************************************************** Platform specific QApplication members *****************************************************************************/ void QApplicationPrivate::initializeWidgetPaletteHash() { } QString QApplicationPrivate::appName() const { return QCoreApplicationPrivate::appName(); } void QApplication::setCursorFlashTime(int msecs) { WinSetSysValue(HWND_DESKTOP, SV_CURSORRATE, msecs / 2); QApplicationPrivate::cursor_flash_time = msecs; } int QApplication::cursorFlashTime() { int blink = (int)WinQuerySysValue(HWND_DESKTOP, SV_CURSORRATE); if (!blink) return QApplicationPrivate::cursor_flash_time; if (blink > 0) return 2 * blink; return 0; } void QApplication::setDoubleClickInterval(int ms) { WinSetSysValue(HWND_DESKTOP, SV_DBLCLKTIME, ms); QApplicationPrivate::mouse_double_click_time = ms; } int QApplication::doubleClickInterval() { int ms = (int) WinQuerySysValue(HWND_DESKTOP, SV_DBLCLKTIME); if (ms != 0) return ms; return QApplicationPrivate::mouse_double_click_time; } void QApplication::setKeyboardInputInterval(int ms) { QApplicationPrivate::keyboard_input_time = ms; } int QApplication::keyboardInputInterval() { // FIXME: get from the system return QApplicationPrivate::keyboard_input_time; } #ifndef QT_NO_WHEELEVENT void QApplication::setWheelScrollLines(int n) { QApplicationPrivate::wheel_scroll_lines = n; } int QApplication::wheelScrollLines() { return QApplicationPrivate::wheel_scroll_lines; } #endif //QT_NO_WHEELEVENT void QApplication::setEffectEnabled(Qt::UIEffect effect, bool enable) { // @todo implement } bool QApplication::isEffectEnabled(Qt::UIEffect effect) { // @todo implement return false; } void QApplication::beep() { WinAlarm(HWND_DESKTOP, WA_WARNING); } void QApplication::alert(QWidget *widget, int duration) { // @todo implement } /***************************************************************************** QApplication cursor stack *****************************************************************************/ #ifndef QT_NO_CURSOR void QApplication::setOverrideCursor(const QCursor &cursor) { // @todo implement } void QApplication::restoreOverrideCursor() { // @todo implement } #endif /***************************************************************************** Routines to find a Qt widget from a screen position *****************************************************************************/ QWidget *QApplication::topLevelAt(const QPoint &pos) { // @todo implement return 0; } /***************************************************************************** Main event loop *****************************************************************************/ // sent to hwnd that has been entered to by a mouse pointer. // FID_CLIENT also receives enter messages of its WC_FRAME. // mp1 = hwnd that is entered, mp2 = hwnd that is left #define WM_U_MOUSEENTER 0x41E // sent to hwnd that has been left by a mouse pointer. // FID_CLIENT also receives leave messages of its WC_FRAME. // mp1 = hwnd that is left, mp2 = hwnd that is entered #define WM_U_MOUSELEAVE 0x41F // some undocumented system values #define SV_WORKAREA_YTOP 51 #define SV_WORKAREA_YBOTTOM 52 #define SV_WORKAREA_XRIGHT 53 #define SV_WORKAREA_XLEFT 54 // QtWndProc() receives all messages from the main event loop MRESULT EXPENTRY QtWndProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2) { do { if (!qApp) // unstable app state break; #if 0 // make sure we show widgets (e.g. scrollbars) when the user resizes if (qApp->loopLevel()) qApp->sendPostedEvents(0, QEvent::ShowWindowRequest); #endif MRESULT rc = (MRESULT) FALSE; QETWidget *widget = 0; bool isTranslatableMouseEvent = (msg >= WM_MOUSEFIRST && msg <= WM_MOUSELAST) || (msg >= WM_EXTMOUSEFIRST && msg <= WM_EXTMOUSELAST); QMSG qmsg; // create QMSG structure qmsg.hwnd = hwnd; qmsg.msg = msg; qmsg.mp1 = mp1; qmsg.mp2 = mp2; qmsg.time = WinQueryMsgTime(0); if (isTranslatableMouseEvent || msg == WM_CONTEXTMENU) { qmsg.ptl.x = (short)SHORT1FROMMP(mp1); qmsg.ptl.y = (short)SHORT2FROMMP(mp1); WinMapWindowPoints(qmsg.hwnd, HWND_DESKTOP, &qmsg.ptl, 1); } else { WinQueryMsgPos(0, &qmsg.ptl); } // flip y coordinate qmsg.ptl.y = QApplication::desktop()->height() - (qmsg.ptl.y + 1); #if defined(QT_DEBUGMSGFLOW) { QString str = qStrQMSG(qmsg); if (!str.isEmpty()) qDebug() << "*** [W]" << str.toUtf8().constData(); } #endif // send through app filter if (qApp->filterEvent(&qmsg, reinterpret_cast(&rc))) return rc; switch(msg) { case WM_BUTTON1DOWN: case WM_BUTTON2DOWN: case WM_BUTTON3DOWN: if (ignoreNextMouseReleaseEvent) ignoreNextMouseReleaseEvent = false; break; case WM_BUTTON1UP: case WM_BUTTON2UP: case WM_BUTTON3UP: if (ignoreNextMouseReleaseEvent) { ignoreNextMouseReleaseEvent = false; if (qt_button_down && qt_button_down->internalWinId() == autoCaptureWnd) { releaseAutoCapture(); qt_button_down = 0; } return (MRESULT)TRUE; } break; default: break; } if (!widget) widget = (QETWidget*)QWidget::find(hwnd); if (!widget) // don't know this widget break; if (app_do_modal) { // modal event handling if (!qt_try_modal(widget, &qmsg, rc)) return rc; } if (widget->pmEvent(&qmsg, &rc)) // send through widget filter return rc; if (isTranslatableMouseEvent) { if (qApp->activePopupWidget() != 0) { // in popup mode QWidget *w = QApplication::widgetAt(qmsg.ptl.x, qmsg.ptl.y); if (w) { POINTL ptl = { SHORT1FROMMP(qmsg.mp1), SHORT2FROMMP(qmsg.mp1) }; WinMapWindowPoints(qmsg.hwnd, w->winId(), &ptl, 1); qmsg.mp1 = MPFROM2SHORT(ptl.x, ptl.y); widget = (QETWidget*)w; } } if (widget->translateMouseEvent(qmsg)) // mouse event return (MRESULT)TRUE; #ifndef QT_NO_WHEELEVENT } else if (msg == WM_VSCROLL || msg == WM_HSCROLL) { if (widget->translateWheelEvent(qmsg)) return (MRESULT)TRUE; #endif #ifndef QT_NO_DRAGANDDROP } else if (msg >= WM_DRAGFIRST && msg <= WM_DRAGLAST) { return qt_dispatchDragAndDrop(widget, qmsg); #endif } else { switch(msg) { case WM_PAINT: { // paint event if (widget->translatePaintEvent(qmsg)) return (MRESULT)TRUE; break; } case WM_ERASEBACKGROUND: { // erase window background // flush WM_PAINT messages here to update window contents // instantly while tracking the resize frame (normally these // messages are delivered after the user has stopped resizing // for some time). this slows down resizing slightly but gives a // better look (no invalid window contents can be seen during // resize). the alternative could be to erase the background only, // but we need to do it for every non-toplevel window, which can // also be time-consuming (WM_ERASEBACKGROUND is sent to WC_FRAME // clients only, so we would have to do all calculations ourselves). WinUpdateWindow(widget->effectiveWinId()); return FALSE; } case WM_CALCVALIDRECTS: { // we must always return this value here to cause PM to reposition // our children accordingly (othwerwise we would have to do it // ourselves to keep them top-left aligned). return (MRESULT)(CVR_ALIGNLEFT | CVR_ALIGNTOP); } case WM_SIZE: { // resize window if (widget->translateConfigEvent(qmsg)) return (MRESULT)TRUE; break; } case WM_SHOW: { // @todo there is some more processing in Qt4, see // WM_SHOWWINDOW in qapplication_win.cpp if (!SHORT1FROMMP(mp1) && autoCaptureWnd == widget->internalWinId()) releaseAutoCapture(); break; } case WM_CLOSE: { // close window widget->translateCloseEvent(qmsg); return (MRESULT)TRUE; } case WM_DESTROY: { // destroy window if (hwnd == curWin) { QWidget *enter = QWidget::mouseGrabber(); if (enter == widget) enter = 0; QApplicationPrivate::dispatchEnterLeave(enter, widget); curWin = enter ? enter->effectiveWinId() : 0; qt_last_mouse_receiver = enter; } if (widget == popupButtonFocus) popupButtonFocus = 0; break; } #ifndef QT_NO_CONTEXTMENU case WM_CONTEXTMENU: { if (SHORT2FROMMP(mp2)) { // keyboard event QWidget *fw = qApp->focusWidget(); if (fw && fw->isEnabled()) { QContextMenuEvent e(QContextMenuEvent::Keyboard, QPoint(5, 5), fw->mapToGlobal(QPoint(5, 5)), 0); if (qt_sendSpontaneousEvent(fw, &e)) return (MRESULT)TRUE; } } else { // mouse event if (widget->translateMouseEvent(qmsg)) return (MRESULT)TRUE; } break; } #endif case WM_U_MOUSELEAVE: { // We receive a mouse leave for curWin, meaning // the mouse was moved outside our widgets if (widget->internalWinId() == curWin) { bool dispatch = !widget->underMouse(); // hasMouse is updated when dispatching enter/leave, // so test if it is actually up-to-date if (!dispatch) { QRect geom = widget->geometry(); if (widget->parentWidget() && !widget->isWindow()) { QPoint gp = widget->parentWidget()->mapToGlobal(widget->pos()); geom.setX(gp.x()); geom.setY(gp.y()); } QPoint cpos = QCursor::pos(); dispatch = !geom.contains(cpos); if ( !dispatch && !QWidget::mouseGrabber()) { QWidget *hittest = QApplication::widgetAt(cpos); dispatch = !hittest || hittest->internalWinId() != curWin; } if (!dispatch) { HPS hps = qt_display_ps(); HRGN hrgn = GpiCreateRegion(hps, 0, NULL); qt_WinQueryClipRegionOrRect(hwnd, hrgn); QPoint lcpos = widget->mapFromGlobal(cpos); // flip y coordinate POINTL pt = { lcpos.x(), widget->height() - (lcpos.y() + 1) }; dispatch = !GpiPtInRegion(hps, hrgn, &pt); GpiDestroyRegion(hps, hrgn); } } if (dispatch) { if (qt_last_mouse_receiver && !qt_last_mouse_receiver->internalWinId()) QApplicationPrivate::dispatchEnterLeave(0, qt_last_mouse_receiver); else QApplicationPrivate::dispatchEnterLeave(0, QWidget::find((WId)curWin)); curWin = 0; qt_last_mouse_receiver = 0; } } break; } case WM_MINMAXFRAME: { PSWP pswp = (PSWP)mp1; bool window_state_change = false; Qt::WindowStates oldstate = Qt::WindowStates(widget->dataPtr()->window_state); if (pswp->fl & SWP_MINIMIZE) { window_state_change = true; widget->dataPtr()->window_state |= Qt::WindowMinimized; if (widget->isVisible()) { QHideEvent e; qt_sendSpontaneousEvent(widget, &e); widget->dptr()->hideChildren(true); const QString title = widget->windowIconText(); if (!title.isEmpty()) widget->dptr()->setWindowTitle_helper(title); } } else if (pswp->fl & (SWP_MAXIMIZE | SWP_RESTORE)) { window_state_change = true; if (pswp->fl & SWP_MAXIMIZE) { widget->topData()->normalGeometry = widget->geometry(); widget->dataPtr()->window_state |= Qt::WindowMaximized; } else if (!widget->isMinimized()) { widget->dataPtr()->window_state &= ~Qt::WindowMaximized; } if (widget->isMinimized()) { widget->dataPtr()->window_state &= ~Qt::WindowMinimized; widget->dptr()->showChildren(true); QShowEvent e; qt_sendSpontaneousEvent(widget, &e); const QString title = widget->windowTitle(); if (!title.isEmpty()) widget->dptr()->setWindowTitle_helper(title); } } if (window_state_change) { QWindowStateChangeEvent e(oldstate); qt_sendSpontaneousEvent(widget, &e); } break; } default: break; } } } while(0); return WinDefWindowProc(hwnd, msg, mp1, mp2); } PFNWP QtOldFrameProc = 0; MRESULT EXPENTRY QtFrameProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2) { do { if (!qApp) // unstable app state break; #if 0 // make sure we show widgets (e.g. scrollbars) when the user resizes if (qApp->loopLevel()) qApp->sendPostedEvents(0, QEvent::ShowWindowRequest); #endif HWND hwndC = WinWindowFromID(hwnd, FID_CLIENT); QETWidget *widget = (QETWidget*)QWidget::find(hwndC); if (!widget) // don't know this widget break; Q_ASSERT(widget->isWindow()); QMSG qmsg; // create QMSG structure qmsg.hwnd = hwnd; qmsg.msg = msg; qmsg.mp1 = mp1; qmsg.mp2 = mp2; #if defined(QT_DEBUGMSGFLOW) { QString str = qStrQMSG(qmsg); if (!str.isEmpty()) qDebug() << "*** [F]" << str.toUtf8().constData(); } #endif switch(msg) { case WM_MOVE: { // move window // note that we handle it nere instead of having CS_MOVENOTIFY // on the client and handling in QtWndProc because we don't want // client inside frame window move messages that would appear during // minimize/maximize otherwise if (widget->translateConfigEvent(qmsg)) return (MRESULT)TRUE; break; } default: break; } } while(0); return QtOldFrameProc(hwnd, msg, mp1, mp2); } /***************************************************************************** Modal widgets; We have implemented our own modal widget mechanism to get total control. A modal widget without a parent becomes application-modal. A modal widget with a parent becomes modal to its parent and grandparents.. QApplicationPrivate::enterModal() Enters modal state Arguments: QWidget *widget A modal widget QApplicationPrivate::leaveModal() Leaves modal state for a widget Arguments: QWidget *widget A modal widget *****************************************************************************/ bool QApplicationPrivate::modalState() { return app_do_modal; } void QApplicationPrivate::enterModal_sys(QWidget *widget) { if (!qt_modal_stack) qt_modal_stack = new QWidgetList; releaseAutoCapture(); QWidget *leave = qt_last_mouse_receiver; if (!leave) leave = QWidget::find(curWin); QApplicationPrivate::dispatchEnterLeave(0, leave); qt_modal_stack->insert(0, widget); app_do_modal = true; curWin = 0; qt_last_mouse_receiver = 0; ignoreNextMouseReleaseEvent = false; } void QApplicationPrivate::leaveModal_sys(QWidget *widget) { if (qt_modal_stack && qt_modal_stack->removeAll(widget)) { if (qt_modal_stack->isEmpty()) { delete qt_modal_stack; qt_modal_stack = 0; QPoint p(QCursor::pos()); app_do_modal = false; // necessary, we may get recursively into qt_try_modal below QWidget* w = QApplication::widgetAt(p.x(), p.y()); QWidget *leave = qt_last_mouse_receiver; if (!leave) leave = QWidget::find(curWin); if (QWidget *grabber = QWidget::mouseGrabber()) { w = grabber; if (leave == w) leave = 0; } QApplicationPrivate::dispatchEnterLeave(w, leave); // send synthetic enter event curWin = w ? w->effectiveWinId() : 0; qt_last_mouse_receiver = w; } ignoreNextMouseReleaseEvent = true; } app_do_modal = qt_modal_stack != 0; } bool qt_try_modal(QWidget *widget, QMSG *qmsg, MRESULT &rc) { QWidget *top = 0; if (QApplicationPrivate::tryModalHelper(widget, &top)) return true; int type = qmsg->msg; bool block_event = false; if ((type >= WM_MOUSEFIRST && type <= WM_MOUSELAST) || (type >= WM_EXTMOUSEFIRST && type <= WM_EXTMOUSELAST) || type == WM_VSCROLL || type == WM_HSCROLL || type == WM_U_MOUSELEAVE || type == WM_CHAR) { if (type == WM_MOUSEMOVE) { #ifndef QT_NO_CURSOR QCursor *c = qt_grab_cursor(); if (!c) c = QApplication::overrideCursor(); if (c) // application cursor defined WinSetPointer(HWND_DESKTOP, c->handle()); else WinSetPointer(HWND_DESKTOP, QCursor(Qt::ArrowCursor).handle()); #endif // QT_NO_CURSOR } else if (type == WM_BUTTON1DOWN || type == type == WM_BUTTON2DOWN || type == WM_BUTTON3DOWN) { if (!top->isActiveWindow()) { top->activateWindow(); } else { QApplication::beep(); } } block_event = true; } else if (type == WM_CLOSE) { block_event = true; } else if (type == WM_SYSCOMMAND) { if (!(SHORT1FROMMP(qmsg->mp1) == SC_RESTORE && widget->isMinimized())) block_event = true; } return !block_event; } /***************************************************************************** Popup widget mechanism openPopup() Adds a widget to the list of popup widgets Arguments: QWidget *widget The popup widget to be added closePopup() Removes a widget from the list of popup widgets Arguments: QWidget *widget The popup widget to be removed *****************************************************************************/ void QApplicationPrivate::openPopup(QWidget *popup) { // @todo implement } void QApplicationPrivate::closePopup(QWidget *popup) { // @todo implement } /***************************************************************************** Event translation; translates PM events to Qt events *****************************************************************************/ // State holder for LWIN/RWIN and ALTGr keys // (ALTGr is also necessary since OS/2 doesn't report ALTGr as KC_ALT) static int qt_extraKeyState = 0; static int mouseButtonState() { int state = 0; if (WinGetKeyState(HWND_DESKTOP, VK_BUTTON1) & 0x8000) state |= Qt::LeftButton; if (WinGetKeyState(HWND_DESKTOP, VK_BUTTON2) & 0x8000) state |= Qt::RightButton; if (WinGetKeyState(HWND_DESKTOP, VK_BUTTON3) & 0x8000) state |= Qt::MidButton; return state; } // // Auto-capturing for mouse press and mouse release // static void setAutoCapture(HWND h) { if (autoCaptureWnd) releaseAutoCapture(); autoCaptureWnd = h; if (!mouseButtonState()) { // all buttons released, we don't actually capture the mouse // (see QWidget::translateMouseEvent()) autoCaptureReleased = true; } else { autoCaptureReleased = false; WinSetCapture(HWND_DESKTOP, h); } } static void releaseAutoCapture() { if (autoCaptureWnd) { if (!autoCaptureReleased) { WinSetCapture(HWND_DESKTOP, NULLHANDLE); autoCaptureReleased = true; } autoCaptureWnd = NULLHANDLE; } } // // Mouse event translation // static ushort mouseTbl[] = { WM_MOUSEMOVE, QEvent::MouseMove, 0, WM_BUTTON1DOWN, QEvent::MouseButtonPress, Qt::LeftButton, WM_BUTTON1UP, QEvent::MouseButtonRelease, Qt::LeftButton, WM_BUTTON1DBLCLK, QEvent::MouseButtonDblClick, Qt::LeftButton, WM_BUTTON2DOWN, QEvent::MouseButtonPress, Qt::RightButton, WM_BUTTON2UP, QEvent::MouseButtonRelease, Qt::RightButton, WM_BUTTON2DBLCLK, QEvent::MouseButtonDblClick, Qt::RightButton, WM_BUTTON3DOWN, QEvent::MouseButtonPress, Qt::MidButton, WM_BUTTON3UP, QEvent::MouseButtonRelease, Qt::MidButton, WM_BUTTON3DBLCLK, QEvent::MouseButtonDblClick, Qt::MidButton, WM_CONTEXTMENU, QEvent::ContextMenu, 0, 0, 0, 0 }; static int translateButtonState(USHORT s, int type, int button) { Q_UNUSED(button); int bst = mouseButtonState(); if (type == QEvent::ContextMenu) { if (WinGetKeyState(HWND_DESKTOP, VK_SHIFT) & 0x8000) bst |= Qt::ShiftModifier; if (WinGetKeyState(HWND_DESKTOP, VK_ALT) & 0x8000) bst |= Qt::AltModifier; if (WinGetKeyState(HWND_DESKTOP, VK_CTRL) & 0x8000) bst |= Qt::ControlModifier; } else { if (s & KC_SHIFT) bst |= Qt::ShiftModifier; if ((s & KC_ALT)) bst |= Qt::AltModifier; if (s & KC_CTRL) bst |= Qt::ControlModifier; } if ((qt_extraKeyState & Qt::AltModifier)) bst |= Qt::AltModifier; if (qt_extraKeyState & Qt::MetaModifier) bst |= Qt::MetaModifier; return bst; } bool QETWidget::translateMouseEvent(const QMSG &qmsg) { #if 0 static const char *msgNames[] = { // 11 items "WM_MOUSEMOVE", "WM_BUTTON1DOWN", "WM_BUTTON1UP", "WM_BUTTON1DBLCLK", "WM_BUTTON2DOWN", "WM_BUTTON2UP", "WM_BUTTON2DBLCLK", "WM_BUTTON3DOWN", "WM_BUTTON3UP", "WM_BUTTON3DBLCLK", "WM_???" }; int msgIdx = qmsg.msg - WM_MOUSEMOVE; if (msgIdx < 0 || msgIdx > 9) msgIdx = 10; qDebug("%s (%04lX): [%08lX/%p:%s] %04hd,%04hd hit=%04hX fl=%04hX", msgNames[msgIdx], qmsg.msg, qmsg.hwnd, this, widgetName(this), SHORT1FROMMP(qmsg.mp1), SHORT2FROMMP(qmsg.mp1), SHORT1FROMMP(qmsg.mp2), SHORT2FROMMP(qmsg.mp2)); #endif if (!isWindow() && testAttribute(Qt::WA_NativeWindow)) Q_ASSERT(internalWinId() != NULLHANDLE); static QPoint pos; // window pos (y flipped) static POINTL gpos = { -1, -1 }; // global pos (y flipped) QEvent::Type type; // event parameters int button; int state; int i; // candidate for the double click event static HWND dblClickCandidateWin = 0; #if !defined (QT_NO_SESSIONMANAGER) if (sm_blockUserInput) //block user interaction during session management return true; #endif // Compress mouse move events if (qmsg.msg == WM_MOUSEMOVE) { QMSG mouseMsg; mouseMsg.msg = WM_NULL; while (WinPeekMsg(0, &mouseMsg, qmsg.hwnd, WM_MOUSEMOVE, WM_MOUSEMOVE, PM_NOREMOVE)) { if (mouseMsg.mp2 != qmsg.mp2) break; // leave the message in the queue because // the key state has changed // Remove the mouse move message WinPeekMsg(0, &mouseMsg, qmsg.hwnd, WM_MOUSEMOVE, WM_MOUSEMOVE, PM_REMOVE); } // Update the passed in QMSG structure with the // most recent one. if (mouseMsg.msg != WM_NULL) { PQMSG pqmsg = (PQMSG)&qmsg; pqmsg->mp1 = mouseMsg.mp1; pqmsg->mp2 = mouseMsg.mp2; pqmsg->time = mouseMsg.time; pqmsg->ptl.x = (short)SHORT1FROMMP(mouseMsg.mp1); pqmsg->ptl.y = (short)SHORT2FROMMP(mouseMsg.mp1); WinMapWindowPoints(pqmsg->hwnd, HWND_DESKTOP, &pqmsg->ptl, 1); // flip y coordinate pqmsg->ptl.y = QApplication::desktop()->height() - (pqmsg->ptl.y + 1); } } for (i = 0; mouseTbl[i] && (ULONG)mouseTbl[i] != qmsg.msg; i += 3) ; if (!mouseTbl[i]) return true; type = (QEvent::Type)mouseTbl[++i]; // event type button = mouseTbl[++i]; // which button state = translateButtonState(SHORT2FROMMP(qmsg.mp2), type, button); // button state // It seems, that PM remembers only the WM_BUTTONxDOWN message (instead of // the WM_BUTTONxDOWN + WM_BUTTONxUP pair) to detect whether the next button // press should be converted to WM_BUTTONxDBLCLK or not. As a result, the // window gets WM_BUTTONxDBLCLK even if it didn't receive the preceeding // WM_BUTTONxUP (this happens if we issue WinSetCapture() on the first // WM_BUTTONxDOWN), which is obviously wrong and makes problems for QWorkspace // and QTitleBar system menu handlers that don't expect a double click after // they opened a popup menu. dblClickCandidateWin is reset to 0 (see a *** // remmark below) when WinSetCapture is issued that directs messages // to a window other than one received the first WM_BUTTONxDOWN, // so we can fix it here. Note that if there is more than one popup window, // WinSetCapture is issued only for the first of them, so this code doesn't // prevent MouseButtonDblClick from being delivered to a popup when another // popup gets closed on the first WM_BUTTONxDOWN (Qt/Win32 behaves in the // same way, so it's left for compatibility). if (type == QEvent::MouseButtonPress) { dblClickCandidateWin = qmsg.hwnd; } else if (type == QEvent::MouseButtonDblClick) { if (dblClickCandidateWin != qmsg.hwnd) type = QEvent::MouseButtonPress; dblClickCandidateWin = 0; } const QPoint widgetPos = mapFromGlobal(QPoint(qmsg.ptl.x, qmsg.ptl.y)); QWidget *alienWidget = !internalWinId() ? this : childAt(widgetPos); if (alienWidget && alienWidget->internalWinId()) alienWidget = 0; if (type == QEvent::MouseMove) { if (!(state & Qt::MouseButtonMask)) qt_button_down = 0; #ifndef QT_NO_CURSOR QCursor *c = qt_grab_cursor(); if (!c) c = QApplication::overrideCursor(); if (c) // application cursor defined WinSetPointer(HWND_DESKTOP, c->handle()); else if (!qt_button_down) { QWidget *w = alienWidget ? alienWidget : this; while (!w->isWindow() && !w->isEnabled()) w = w->parentWidget(); WinSetPointer(HWND_DESKTOP, w->cursor().handle()); } #else // pass the msg to the default proc to let it change the pointer shape WinDefWindowProc(qmsg.hwnd, qmsg.msg, qmsg.mp1, qmsg.mp2); #endif HWND id = effectiveWinId(); QWidget *mouseGrabber = QWidget::mouseGrabber(); QWidget *activePopupWidget = qApp->activePopupWidget(); if (mouseGrabber) { if (!activePopupWidget || (activePopupWidget == this && !rect().contains(widgetPos))) id = mouseGrabber->effectiveWinId(); } else if (type == QEvent::NonClientAreaMouseMove) { id = 0; } if (curWin != id) { // new current window // @todo // add CS_HITTEST to our window classes and handle WM_HITTEST, // otherwise disabled windows will not get mouse events? if (id == 0) { QWidget *leave = qt_last_mouse_receiver; if (!leave) leave = QWidget::find(curWin); QApplicationPrivate::dispatchEnterLeave(0, leave); qt_last_mouse_receiver = 0; curWin = 0; } else { QWidget *leave = 0; if (curWin && qt_last_mouse_receiver) leave = qt_last_mouse_receiver; else leave = QWidget::find(curWin); QWidget *enter = alienWidget ? alienWidget : this; if (mouseGrabber && activePopupWidget) { if (leave != mouseGrabber) enter = mouseGrabber; else enter = activePopupWidget == this ? this : mouseGrabber; } QApplicationPrivate::dispatchEnterLeave(enter, leave); qt_last_mouse_receiver = enter; curWin = enter ? enter->effectiveWinId() : 0; } } // *** PM posts a dummy WM_MOUSEMOVE message (with the same, uncahnged // pointer coordinates) after every WinSetCapture that actually changes // the capture target. I.e., if the argument of WinSetCapture is // NULLHANDLE, a window under the mouse pointer gets this message, // otherwise the specified window gets it unless it is already under the // pointer. We use this info to check whether the window can be a double // click candidate (see above). if (qmsg.ptl.x == gpos.x && qmsg.ptl.y == gpos.y) { if (dblClickCandidateWin != qmsg.hwnd) dblClickCandidateWin = 0; return true; } gpos = qmsg.ptl; Q_ASSERT(testAttribute(Qt::WA_WState_Created)); POINTL curPos = gpos; WinMapWindowPoints(internalWinId(), HWND_DESKTOP, &gpos, 1); pos.rx() = curPos.x; pos.ry() = curPos.y; pos = d_func()->mapFromWS(pos); } else { if (type == QEvent::MouseButtonPress && !isActiveWindow()) setActiveWindow(); gpos = qmsg.ptl; pos = mapFromGlobal(QPoint(gpos.x, gpos.y)); // mouse button pressed if (!qt_button_down && (type == QEvent::MouseButtonPress || type == QEvent::MouseButtonDblClick)) { QWidget *tlw = window(); if (QWidget *child = tlw->childAt(mapTo(tlw, pos))) qt_button_down = child; else qt_button_down = this; } } // detect special button states enum { Other, SinglePressed, AllReleased } btnState = Other; int bs = state & Qt::MouseButtonMask; if ((type == QEvent::MouseButtonPress || type == QEvent::MouseButtonDblClick) && bs == 0) { btnState = SinglePressed; } else if (type == QEvent::MouseButtonRelease && bs == button) { btnState = AllReleased; } bool res = false; if (qApp->d_func()->inPopupMode()) { // in popup mode if (!autoCaptureReleased && btnState == AllReleased) { // in order to give non-Qt windows the opportunity to see mouse // messages while our popups are active we need to release the // mouse capture which is absolute in OS/2. we do it directly // (not through releaseAutoCapture()) in order to keep // autoCaptureWnd nonzero to keep forwarding mouse move events // (actually sent to one of Qt widgets) to the active popup. autoCaptureReleased = true; WinSetCapture(HWND_DESKTOP, 0); } else if (autoCaptureReleased && btnState == SinglePressed) { // set the mouse capture back if a button is pressed. if ( autoCaptureWnd ) { autoCaptureReleased = false; WinSetCapture(HWND_DESKTOP, autoCaptureWnd); } } replayPopupMouseEvent = false; QWidget* activePopupWidget = qApp->activePopupWidget(); QWidget *target = activePopupWidget; const QPoint globalPos(gpos.x, gpos.y); if (target != this) { if ((windowType() == Qt::Popup) && rect().contains(pos) && 0) target = this; else // send to last popup pos = target->mapFromGlobal(globalPos); } QWidget *popupChild = target->childAt(pos); bool releaseAfter = false; switch (type) { case QEvent::MouseButtonPress: case QEvent::MouseButtonDblClick: popupButtonFocus = popupChild; break; case QEvent::MouseButtonRelease: releaseAfter = true; break; default: break; // nothing for mouse move } if (target->isEnabled()) { if (popupButtonFocus) { target = popupButtonFocus; } else if (popupChild) { // forward mouse events to the popup child. mouse move events // are only forwarded to popup children that enable mouse tracking. if (type != QEvent::MouseMove || popupChild->hasMouseTracking()) target = popupChild; } pos = target->mapFromGlobal(globalPos); #ifndef QT_NO_CONTEXTMENU if (type == QEvent::ContextMenu) { QContextMenuEvent e(QContextMenuEvent::Mouse, pos, globalPos, state); res = QApplication::sendSpontaneousEvent(target, &e); res = res && e.isAccepted(); } else #endif { QMouseEvent e(type, pos, globalPos, Qt::MouseButton(button), Qt::MouseButtons(state & Qt::MouseButtonMask), Qt::KeyboardModifiers(state & Qt::KeyboardModifierMask)); res = QApplicationPrivate::sendMouseEvent(target, &e, alienWidget, this, &qt_button_down, qt_last_mouse_receiver); res = res && e.isAccepted(); } } else { // close disabled popups when a mouse button is pressed or released switch (type) { case QEvent::MouseButtonPress: case QEvent::MouseButtonDblClick: case QEvent::MouseButtonRelease: target->close(); break; default: break; } } if (releaseAfter) { popupButtonFocus = 0; qt_button_down = 0; } if (type == QEvent::MouseButtonPress && qApp->activePopupWidget() != activePopupWidget && replayPopupMouseEvent) { // the popup dissappeared. Replay the event QWidget* w = QApplication::widgetAt(gpos.x, gpos.y); if (w && !QApplicationPrivate::isBlockedByModal(w)) { Q_ASSERT(w->testAttribute(Qt::WA_WState_Created)); HWND hwndTarget = w->effectiveWinId(); if (QWidget::mouseGrabber() == 0) setAutoCapture(hwndTarget); if (!w->isActiveWindow()) w->activateWindow(); POINTL pt = gpos; WinMapWindowPoints(HWND_DESKTOP, hwndTarget, &pt, 1); // flip y coordinate pt.y = w->height() - (pt.y + 1); WinPostMsg(hwndTarget, qmsg.msg, MPFROM2SHORT(pt.x, pt.y), qmsg.mp2); } } } else { // not popup mode if (btnState == SinglePressed && QWidget::mouseGrabber() == 0) { Q_ASSERT(testAttribute(Qt::WA_WState_Created)); setAutoCapture(internalWinId()); } else if (btnState == AllReleased && QWidget::mouseGrabber() == 0) { releaseAutoCapture(); } const QPoint globalPos(gpos.x,gpos.y); QWidget *widget = QApplicationPrivate::pickMouseReceiver(this, globalPos, pos, type, Qt::MouseButtons(bs), qt_button_down, alienWidget); if (!widget) return false; // don't send event #ifndef QT_NO_CONTEXTMENU if (type == QEvent::ContextMenu) { QContextMenuEvent e(QContextMenuEvent::Mouse, pos, globalPos, state); res = QApplication::sendSpontaneousEvent(widget, &e); res = res && e.isAccepted(); } else #endif { QMouseEvent e(type, pos, globalPos, Qt::MouseButton(button), Qt::MouseButtons(state & Qt::MouseButtonMask), Qt::KeyboardModifiers(state & Qt::KeyboardModifierMask)); res = QApplicationPrivate::sendMouseEvent(widget, &e, alienWidget, this, &qt_button_down, qt_last_mouse_receiver); res = res && e.isAccepted(); } if (type != QEvent::MouseMove) pos.rx() = pos.ry() = -9999; // init for move compression } return res; } #ifndef QT_NO_WHEELEVENT bool QETWidget::translateWheelEvent(const QMSG &qmsg) { enum { WHEEL_DELTA = 120 }; #ifndef QT_NO_SESSIONMANAGER if (sm_blockUserInput) // block user interaction during session management return true; #endif // consume duplicate wheel events sent by the AMouse driver to emulate // multiline scrolls. we need this since currently Qt (QScrollBar, for // instance) maintains the number of lines to scroll per wheel rotation // (including the special handling of CTRL and SHIFT modifiers) on its own // and doesn't have a setting to tell it to be aware of system settings // for the mouse wheel. if we had processed events as they are, we would // get a confusing behavior (too many lines scrolled etc.). { int devh = QApplication::desktop()->height(); QMSG wheelMsg; while (WinPeekMsg(0, &wheelMsg, qmsg.hwnd, qmsg.msg, qmsg.msg, PM_NOREMOVE)) { // PM bug: ptl contains SHORT coordinates although fields are LONG wheelMsg.ptl.x = (short) wheelMsg.ptl.x; wheelMsg.ptl.y = (short) wheelMsg.ptl.y; // flip y coordinate wheelMsg.ptl.y = devh - (wheelMsg.ptl.y + 1); if (wheelMsg.mp1 != qmsg.mp1 || wheelMsg.mp2 != qmsg.mp2 || wheelMsg.ptl.x != qmsg.ptl.x || wheelMsg.ptl.y != qmsg.ptl.y) break; WinPeekMsg(0, &wheelMsg, qmsg.hwnd, qmsg.msg, qmsg.msg, PM_REMOVE); } } int delta; USHORT cmd = SHORT2FROMMP(qmsg.mp2); switch (cmd) { case SB_LINEUP: case SB_PAGEUP: delta = WHEEL_DELTA; break; case SB_LINEDOWN: case SB_PAGEDOWN: delta = -WHEEL_DELTA; break; default: return false; } int state = 0; if (WinGetKeyState(HWND_DESKTOP, VK_SHIFT ) & 0x8000) state |= Qt::ShiftModifier; if (WinGetKeyState(HWND_DESKTOP, VK_ALT) & 0x8000 || (qt_extraKeyState & Qt::AltModifier)) state |= Qt::AltModifier; if (WinGetKeyState(HWND_DESKTOP, VK_CTRL) & 0x8000) state |= Qt::ControlModifier; if (qt_extraKeyState & Qt::MetaModifier) state |= Qt::MetaModifier; Qt::Orientation orient; // Alt inverts scroll orientation (Qt/Win32 behavior) if (state & Qt::AltModifier) orient = qmsg.msg == WM_VSCROLL ? Qt::Horizontal : Qt::Vertical; else orient = qmsg.msg == WM_VSCROLL ? Qt::Vertical : Qt::Horizontal; QPoint globalPos(qmsg.ptl.x, qmsg.ptl.y); // if there is a widget under the mouse and it is not shadowed // by modality, we send the event to it first MRESULT rc = FALSE; QWidget* w = QApplication::widgetAt(globalPos); if (!w || !qt_try_modal(w, (QMSG*)&qmsg, rc)) { //synaptics touchpad shows its own widget at this position //so widgetAt() will fail with that HWND, try child of this widget w = this->childAt(this->mapFromGlobal(globalPos)); if (!w) w = this; } // send the event to the widget or its ancestors { QWidget* popup = qApp->activePopupWidget(); if (popup && w->window() != popup) popup->close(); #ifndef QT_NO_WHEELEVENT QWheelEvent e(w->mapFromGlobal(globalPos), globalPos, delta, Qt::MouseButtons(state & Qt::MouseButtonMask), Qt::KeyboardModifier(state & Qt::KeyboardModifierMask), orient); if (QApplication::sendSpontaneousEvent(w, &e)) #else Q_UNUSED(orient); #endif //QT_NO_WHEELEVENT return true; } // send the event to the widget that has the focus or its ancestors, if different if (w != qApp->focusWidget() && (w = qApp->focusWidget())) { QWidget* popup = qApp->activePopupWidget(); if (popup && w->window() != popup) popup->close(); #ifndef QT_NO_WHEELEVENT QWheelEvent e(w->mapFromGlobal(globalPos), globalPos, delta, Qt::MouseButtons(state & Qt::MouseButtonMask), Qt::KeyboardModifier(state & Qt::KeyboardModifierMask), orient); if (QApplication::sendSpontaneousEvent(w, &e)) #endif //QT_NO_WHEELEVENT return true; } return false; } #endif // // Paint event translation // bool QETWidget::translatePaintEvent(const QMSG &qmsg) { if (!isWindow() && testAttribute(Qt::WA_NativeWindow)) Q_ASSERT(internalWinId()); HPS displayPS = qt_display_ps(); // Since we don't use WS_CLIPSIBLINGS and WS_CLIPCHILDREN bits (see // qwidget_pm.cpp), we have to validate areas that intersect with our // children and siblings, taking their clip regions into account. d_func()->validateObstacles(); Q_ASSERT(testAttribute(Qt::WA_WState_Created)); HRGN hrgn = GpiCreateRegion(displayPS, 0, NULL); LONG rc = WinQueryUpdateRegion(internalWinId(), hrgn); if (rc == RGN_ERROR) { // The update bounding rect is invalid GpiDestroyRegion(displayPS, hrgn); d_func()->hd = NULLHANDLE; setAttribute(Qt::WA_PendingUpdate, false); return false; } setAttribute(Qt::WA_PendingUpdate, false); // @todo not sure we need it const QRegion dirtyInBackingStore(qt_dirtyRegion(this)); // Make sure the invalidated region contains the region we're about to repaint. // BeginPaint will set the clip to the invalidated region and it is impossible // to enlarge it afterwards (only shrink it). if (!dirtyInBackingStore.isEmpty()) WinInvalidateRegion(internalWinId(), dirtyInBackingStore.handle(), FALSE); RECTL rcl; d_func()->hd = WinBeginPaint(internalWinId(), 0, &rcl); #if defined(QT_DEBUGMSGFLOW) qDebug() << " PAINT BEGIN:" << rcl << "hps:" << qStrHPS(d_func()->hd); #endif // it's possible that the update rectangle is empty if (rcl.xRight <= rcl.xLeft || rcl.yTop <= rcl.yBottom) { WinEndPaint(d_func()->hd); GpiDestroyRegion(displayPS, hrgn); d_func()->hd = NULLHANDLE; setAttribute(Qt::WA_PendingUpdate, false); return true; } // flip y coordinate rcl.yBottom = height() - (rcl.yBottom + 1); rcl.yTop = height() - (rcl.yTop + 1); // note: right top point is exlusive in rcl QRect updRect(QPoint(rcl.xLeft, rcl.yTop + 1), QPoint(rcl.xRight - 1, rcl.yBottom)); // Mapping region from system to qt (32 bit) coordinate system. updRect.translate(data->wrect.topLeft()); #if defined(QT_DEBUGMSGFLOW) qDebug() << " PAINT updRect:" << updRect; #endif // @todo use hrgn here converted to QRegion? d_func()->syncBackingStore(updRect); WinEndPaint(d_func()->hd); d_func()->hd = NULLHANDLE; #if defined(QT_DEBUGMSGFLOW) qDebug() << " PAINT END"; #endif return true; } // // Window move and resize (configure) events // bool QETWidget::translateConfigEvent(const QMSG &qmsg) { if (!testAttribute(Qt::WA_WState_Created)) // in QWidget::create() return true; if (testAttribute(Qt::WA_WState_ConfigPending)) { // it's possible that we're trying to set the frame size smaller // than it possible for WC_FRAME in QWidget::setGeometry_sys(). // here we correct this (crect there is set before WinSetWindowPos() // that sends WM_SIZE). QSize newSize(SHORT1FROMMP(qmsg.mp2), SHORT2FROMMP(qmsg.mp2)); if (qmsg.msg == WM_SIZE && size() != newSize) data->crect.setSize(newSize); return true; } if (testAttribute(Qt::WA_DontShowOnScreen)) return true; // @todo check if this is actually called for !isWindow() widget // if (!isWindow()) // return true; // When the window is minimized, PM moves it to -32000,-32000 and resizes // to 48x50. We don't want these useless actions to be seen by Qt. if (isMinimized()) return true; setAttribute(Qt::WA_WState_ConfigPending); // set config flag HWND fId = NULLHANDLE; ULONG fStyle = 0; if (isWindow()) { fId = d_func()->frameWinId(); fStyle = WinQueryWindowULong(fId, QWL_STYLE); } if (qmsg.msg == WM_SIZE) { // resize event QSize oldSize = data->crect.size(); QSize newSize = QSize(SHORT1FROMMP(qmsg.mp2), SHORT2FROMMP(qmsg.mp2)); data->crect.setSize(newSize); if (isWindow()) { // update title/icon text d_func()->createTLExtra(); QString title; if ((fStyle & WS_MINIMIZED)) title = windowIconText(); if (title.isEmpty()) title = windowTitle(); if (!title.isEmpty()) d_func()->setWindowTitle_helper(title); } if (oldSize != newSize) { // Spontaneous (external to Qt) WM_SIZE messages should occur only // on top-level widgets. If we get them for a non top-level widget, // the result will most likely be incorrect because widget masks will // not be properly processed (i.e. in the way it is done in // QWidget::setGeometry_sys() when the geometry is changed from // within Qt). So far, I see no need to support this (who will ever // need to move a non top-level window of a foreign process?). Q_ASSERT(isWindow()); if (isVisible()) { QTLWExtra *tlwExtra = d_func()->maybeTopData(); static bool slowResize = qgetenv("QT_SLOW_TOPLEVEL_RESIZE").toInt(); const bool hasStaticContents = tlwExtra && tlwExtra->backingStore && tlwExtra->backingStore->hasStaticContents(); // If we have a backing store with static contents, we have to disable the top-level // resize optimization in order to get invalidated regions for resized widgets. // The optimization discards all invalidateBuffer() calls since we're going to // repaint everything anyways, but that's not the case with static contents. if (!slowResize && tlwExtra && !hasStaticContents) tlwExtra->inTopLevelResize = true; QResizeEvent e(newSize, oldSize); QApplication::sendSpontaneousEvent(this, &e); if (d_func()->paintOnScreen()) { QRegion updateRegion(rect()); if (testAttribute(Qt::WA_StaticContents)) updateRegion -= QRect(0, 0, oldSize.width(), oldSize.height()); // syncBackingStore() should have already flushed the widget // contents to the screen, so no need to redraw the exposed // areas in WM_PAINT once more d_func()->syncBackingStore(updateRegion); WinValidateRegion(internalWinId(), updateRegion.handle(newSize.height()), FALSE); } else { d_func()->syncBackingStore(); // see above RECTL rcl = { 0, 0, newSize.width(), newSize.height() }; WinValidateRect(internalWinId(), &rcl, FALSE); } if (!slowResize && tlwExtra) tlwExtra->inTopLevelResize = false; } else { QResizeEvent *e = new QResizeEvent(newSize, oldSize); QApplication::postEvent(this, e); } } } else if (qmsg.msg == WM_MOVE) { // move event QPoint oldPos = data->crect.topLeft(); SWP swp; if (isWindow()) { WinQueryWindowPos(fId, &swp); // flip y coordinate swp.y = QApplication::desktop()->height() - (swp.y + swp.cy); QTLWExtra *top = d_func()->topData(); swp.x += top->frameStrut.left(); swp.y += top->frameStrut.top(); } else { WinQueryWindowPos(internalWinId(), &swp); // flip y coordinate swp.y = parentWidget()->height() - (swp.y + swp.cy); } QPoint newCPos(swp.x, swp.y); if (newCPos != oldPos) { data->crect.moveTopLeft(newCPos); if (isVisible()) { QMoveEvent e(newCPos, oldPos); // cpos (client position) QApplication::sendSpontaneousEvent(this, &e); } else { QMoveEvent *e = new QMoveEvent(newCPos, oldPos); QApplication::postEvent(this, e); } } } setAttribute(Qt::WA_WState_ConfigPending, false); // clear config flag return true; } // // Close window event translation. // // This class is a friend of QApplication because it needs to emit the // lastWindowClosed() signal when the last top level widget is closed. // bool QETWidget::translateCloseEvent(const QMSG &) { return d_func()->close_helper(QWidgetPrivate::CloseWithSpontaneousEvent); } /*! Returns a QWidget pointer or 0 if there is no widget corresponding to the given HWND. As opposed to QWidget::find(), correctly handles WC_FRAME windows created for top level widgets. Used for debugging. */ QWidget *qWidgetFromHWND(HWND hwnd) { char buf[10]; if (WinQueryClassName(hwnd, sizeof(buf), buf)) { if (!strcmp(buf, "#1")) // WC_FRAME hwnd = WinWindowFromID(hwnd, FID_CLIENT); return QWidget::find(hwnd); } return 0; } /*! \internal Returns a human readable widget name in the form "class/name". Used for debugging. */ QDbgStr qWidgetName(QWidget *w) { if (w) return QString() .sprintf("%s.%s", w->metaObject()->className(), w->objectName().isEmpty() ? "" : w->objectName().toUtf8().constData()); return QString(QLatin1String("")); } /***************************************************************************** PM struct/message debug helpers *****************************************************************************/ typedef QLatin1String QCStr; #define myDefFlagEx(var,fl,varstr,flstr) if (var & fl) { \ if (!varstr.isEmpty()) varstr += QCStr("|"); varstr += QCStr(flstr); \ } else do {} while(0) #define myDefFlag(var,fl,varstr) myDefFlagEx(var,fl,varstr,#fl) #define myDefFlagCut(var,fl,varstr,pos) myDefFlagEx(var,fl,varstr,#fl + pos) QDbgStr qStrHWND(HWND hwnd) { return QString().sprintf("%08lX/", hwnd) + qWidgetName(qWidgetFromHWND(hwnd)); } QDbgStr qStrHPS(HPS hps) { return QString().sprintf("%08lX", hps); } QDbgStr qStrHPOINTER(HPOINTER hptr) { return QString().sprintf("%08lX", hptr); } QDbgStr qStrHRGN(HRGN hrgn) { return QString().sprintf("%08lX", hrgn); } QDbgStr qStrQMSG(const QMSG &qmsg) { QString str; #define myCaseBegin(a) case a: { \ str = QString().sprintf(#a ": hwnd %08lX.", qmsg.hwnd); \ str += qWidgetName(qWidgetFromHWND(qmsg.hwnd)); #define myCaseEnd() } switch (qmsg.msg) { myCaseBegin(WM_PAINT) break; myCaseEnd() myCaseBegin(WM_SIZE) str += QString(). sprintf(" old (%hd,%hd) new (%hd,%hd)", SHORT1FROMMP(qmsg.mp1), SHORT2FROMMP(qmsg.mp1), SHORT1FROMMP(qmsg.mp2), SHORT2FROMMP(qmsg.mp2)); SWP swp; WinQueryWindowPos(qmsg.hwnd, &swp); str += QCStr(" ") + qStrSWP(swp); HWND p = WinQueryWindow(qmsg.hwnd, QW_PARENT); if (p != NULLHANDLE && p != WinQueryDesktopWindow(0, 0)) { WinQueryWindowPos(p, &swp); str += QCStr(" p ") + qStrSWP(swp); } break; myCaseEnd() myCaseBegin(WM_MOVE) SWP swp; WinQueryWindowPos(qmsg.hwnd, &swp); str += QCStr(" ") + qStrSWP(swp); HWND p = WinQueryWindow(qmsg.hwnd, QW_PARENT); if (p != NULLHANDLE && p != WinQueryDesktopWindow(0, 0)) { WinQueryWindowPos(p, &swp); str += QCStr(" p ") + qStrSWP(swp); } break; myCaseEnd() myCaseBegin(WM_WINDOWPOSCHANGED) str += QCStr(" ") + qStrSWP(*((PSWP) qmsg.mp1)); ULONG awp = LONGFROMMP(qmsg.mp2); QString awpstr; myDefFlagEx(awp, AWP_MINIMIZED, awpstr, "MIN"); myDefFlagEx(awp, AWP_MAXIMIZED, awpstr, "MAX"); myDefFlagEx(awp, AWP_RESTORED, awpstr, "REST"); myDefFlagEx(awp, AWP_ACTIVATE, awpstr, "ACT"); myDefFlagEx(awp, AWP_DEACTIVATE, awpstr, "DEACT"); str += QCStr(" AWP(") + awpstr + QCStr(")"); break; myCaseEnd() myCaseBegin(WM_MINMAXFRAME) str += QCStr(" ") + qStrSWP(*((PSWP) qmsg.mp1)); break; myCaseEnd() default: break; } return str; #undef myCaseEnd #undef myCaseBegin } QDbgStr qStrRECTL(const RECTL &rcl) { return QString().sprintf("RECTL(%ld,%ld %ld,%ld)", rcl.xLeft, rcl.yBottom, rcl.xRight, rcl.yTop); } QDbgStr qStrSWP(const SWP &swp) { QString fl; myDefFlagEx(swp.fl, SWP_SHOW, fl, "SHOW"); myDefFlagEx(swp.fl, SWP_HIDE, fl, "HIDE"); myDefFlagEx(swp.fl, SWP_ACTIVATE, fl, "ACT"); myDefFlagEx(swp.fl, SWP_DEACTIVATE, fl, "DEACT"); myDefFlagEx(swp.fl, SWP_MAXIMIZE, fl, "MAX"); myDefFlagEx(swp.fl, SWP_MINIMIZE, fl, "MIN"); myDefFlagEx(swp.fl, SWP_RESTORE, fl, "REST"); myDefFlagEx(swp.fl, SWP_MOVE, fl, "MOVE"); myDefFlagEx(swp.fl, SWP_SIZE, fl, "SIZE"); myDefFlagEx(swp.fl, SWP_ZORDER, fl, "ZORD"); return QString().sprintf("SWP(%ld,%ld %ldx%ld %08lX ", swp.x, swp.y, swp.cx, swp.cy, swp.hwndInsertBehind) + fl + QLatin1String(")"); } #undef myDefFlagCut #undef myDefFlag #undef myDefFlagEx QT_END_NAMESPACE