source: branches/4.5.1/src/gui/kernel/qapplication_pm.cpp

Last change on this file was 504, checked in by Dmitry A. Kuminov, 16 years ago

gui/kernel: Fixed: Mouse press events replayed after closing the popup by clicking outside it had incorrect coordinates. This resulted into various kinds of misbehavior including spatial text selection in QTextEdit widgets after closing popups by clicking inside these widgets.

  • Property svn:eol-style set to native
  • Property svn:executable set to *
  • Property svn:keywords set to Date Revision Author Id
File size: 101.9 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information (qt-info@nokia.com)
5**
6** Copyright (C) 2009 netlabs.org. OS/2 parts.
7**
8** This file is part of the QtGui module of the Qt Toolkit.
9**
10** $QT_BEGIN_LICENSE:LGPL$
11** Commercial Usage
12** Licensees holding valid Qt Commercial licenses may use this file in
13** accordance with the Qt Commercial License Agreement provided with the
14** Software or, alternatively, in accordance with the terms contained in
15** a written agreement between you and Nokia.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 2.1 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 2.1 requirements
23** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24**
25** In addition, as a special exception, Nokia gives you certain
26** additional rights. These rights are described in the Nokia Qt LGPL
27** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
28** package.
29**
30** GNU General Public License Usage
31** Alternatively, this file may be used under the terms of the GNU
32** General Public License version 3.0 as published by the Free Software
33** Foundation and appearing in the file LICENSE.GPL included in the
34** packaging of this file. Please review the following information to
35** ensure the GNU General Public License version 3.0 requirements will be
36** met: http://www.gnu.org/copyleft/gpl.html.
37**
38** If you are unsure which license is appropriate for your use, please
39** contact the sales department at qt-sales@nokia.com.
40** $QT_END_LICENSE$
41**
42****************************************************************************/
43
44#include "qt_os2.h"
45
46#include "qdebug.h"
47
48#include "qapplication.h"
49#include "qapplication_p.h"
50
51#include "qwidget.h"
52#include "qpointer.h"
53#include "qcolormap.h"
54#include "qpixmapcache.h"
55#include "qdesktopwidget.h"
56#include "qsessionmanager.h"
57
58#include "qset.h"
59
60#include "private/qeventdispatcher_pm_p.h"
61#include "private/qbackingstore_p.h"
62
63#include "qwidget_p.h"
64#include "qkeymapper_p.h"
65#include "qcursor_p.h"
66#include "qdnd_p.h"
67
68#define WM_KBDLAYERCHANGED 0x0BD4 // defined in OS2TK45/h/pmbidi.h
69
70//#define QT_DEBUGMSGFLOW
71
72QT_BEGIN_NAMESPACE
73
74/*****************************************************************************
75 Internal variables and functions
76 *****************************************************************************/
77
78static HWND curWin = 0; // current window
79static HPS displayPS = 0; // display presentation space
80
81#if !defined (QT_NO_SESSIONMANAGER)
82
83//#define DEBUG_SESSIONMANAGER
84
85// Session management
86static bool sm_blockUserInput = false;
87static bool sm_smActive = false;
88static bool sm_cancel = false;
89static bool sm_gracefulShutdown = false;
90static bool sm_quitSkipped = false;
91
92extern QSessionManager *qt_session_manager_self; // defined in qapplication.cpp
93extern bool qt_about_to_destroy_wnd; // defined in qwidget_pm.cpp
94
95#endif
96
97static bool replayPopupMouseEvent = false; // replay handling when popups close
98
99// ignore the next release event if return from a modal widget
100static bool ignoreNextMouseReleaseEvent = false;
101
102#if defined(QT_DEBUG)
103static bool appNoGrab = false; // mouse/keyboard grabbing
104#endif
105
106static bool app_do_modal = false; // modal mode
107extern QWidgetList *qt_modal_stack;
108extern QDesktopWidget *qt_desktopWidget;
109static QPointer<QWidget> popupButtonFocus;
110static bool qt_try_modal(QWidget*, QMSG*, MRESULT&);
111
112QWidget *qt_button_down = 0; // widget got last button-down
113QPointer<QWidget> qt_last_mouse_receiver = 0;
114
115static HWND foreignActiveWnd = NULLHANDLE;
116static HWND foreignFocusWnd = NULLHANDLE;
117
118static HWND autoCaptureWnd = NULLHANDLE;
119static bool autoCaptureReleased = false;
120static void setAutoCapture(HWND); // automatic capture
121static void releaseAutoCapture();
122
123extern QCursor *qt_grab_cursor();
124
125extern void qt_WinQueryClipRegionOrRect(HWND hwnd, HRGN hrgn); // qwidget_pm.cpp
126
127extern bool qt_sendSpontaneousEvent(QObject*, QEvent*); // qapplication.cpp
128
129extern QRegion qt_dirtyRegion(QWidget *); // qbackingstore.cpp
130
131MRESULT EXPENTRY QtWndProc(HWND, ULONG, MPARAM, MPARAM);
132
133class QETWidget : public QWidget // event translator widget
134{
135public:
136 QWExtra *xtra() { return d_func()->extraData(); }
137 QTLWExtra *topData() { return d_func()->topData(); }
138 QTLWExtra *maybeTopData() { return d_func()->maybeTopData(); }
139// @todo later
140// void syncBackingStore(const QRegion &rgn) { d_func()->syncBackingStore(rgn); }
141// void syncBackingStore() { d_func()->syncBackingStore(); }
142 QWidgetData *dataPtr() { return data; }
143 QWidgetPrivate *dptr() { return d_func(); }
144 QRect frameStrut() const { return d_func()->frameStrut(); }
145 bool pmEvent(QMSG *m, MRESULT *r) { return QWidget::pmEvent(m, r); }
146// void markFrameStrutDirty() { data->fstrut_dirty = 1; }
147 bool translateMouseEvent(const QMSG &qmsg);
148 void translateNonClientMouseEvent(const QMSG &qmsg);
149#ifndef QT_NO_WHEELEVENT
150 bool translateWheelEvent(const QMSG &qmsg);
151#endif
152 bool translatePaintEvent(const QMSG &qmsg);
153 bool translateConfigEvent(const QMSG &qmsg);
154 bool translateCloseEvent(const QMSG &qmsg);
155// void repolishStyle(QStyle &style);
156// inline void showChildren(bool spontaneous) { d_func()->showChildren(spontaneous); }
157// inline void hideChildren(bool spontaneous) { d_func()->hideChildren(spontaneous); }
158// inline void validateObstacles() { d_func()->validateObstacles(); }
159// inline uint testWindowState(uint teststate){ return dataPtr()->window_state & teststate; }
160// inline void forceUpdate() {
161// QTLWExtra *tlwExtra = window()->d_func()->maybeTopData();
162// if (tlwExtra && tlwExtra->backingStore)
163// tlwExtra->backingStore->markDirty(rect(), this, true, true);
164// }
165};
166
167static QRgb qt_sysclr2qrgb(LONG sysClr)
168{
169 // QRgb has the same RGB format (0xaarrggbb) as OS/2 uses (ignoring the
170 // highest alpha byte) so we just cast the OS/2 LONG RGB value to qRgb
171 // which is an unsigned int actually.
172 return ((QRgb)WinQuerySysColor(HWND_DESKTOP, sysClr, 0)) & RGB_MASK;
173}
174
175static QFont qt_sysfont2qfont(PCSZ scope)
176{
177 // note: 10.System Proportional is de-facto the default font selected into
178 // the presentation space
179
180 static PCSZ app = "PM_SystemFonts";
181 static PCSZ def = "10.System Proportional";
182
183 ULONG keyLen = 0;
184 QFont f(QLatin1String("System Proportional"), 10);
185
186 if (PrfQueryProfileSize(HINI_USERPROFILE, app, scope, &keyLen) && keyLen) {
187 keyLen++; // reserve space for the dot
188 char *buf = new char[keyLen];
189 ULONG realLen = PrfQueryProfileString(HINI_USERPROFILE, app, scope, def,
190 buf, keyLen);
191 realLen--; // excude terminating NULL
192
193 // parse the font definition
194 int height = 0;
195 char *dot = strchr(buf, '.'), *dot2 = 0;
196 if (dot) {
197 *dot = 0;
198 height = strtoul(buf, NULL, 10);
199 dot2 = strchr(++ dot, '.');
200 if (dot2) {
201 // process simulated styles
202 buf[realLen] = '.';
203 buf[realLen+1] = 0;
204 strupr( dot2 );
205 // @todo currently, simulated bold and italic font styles are not
206 // supported by Qt/OS2 because Qt doesn't support style simulation
207 // explicitly. the code below is commented out to prevent selecting
208 // true fonts when simulated ones are actually requested.
209 // if (strstr(dot2, ".BOLD.")) f.setBold(true);
210 // if (strstr(dot2, ".ITALIC.")) f.setItalic(true);
211 if (strstr(dot2, ".UNDERSCORE.")) f.setUnderline(true);
212 if (strstr(dot2, ".UNDERLINED.")) f.setUnderline(true);
213 if (strstr(dot2, ".STRIKEOUT.")) f.setStrikeOut(true);
214 *dot2 = 0;
215 }
216 // query non-simulated styles
217 FONTMETRICS fm;
218 LONG cnt = 1; // use the first match
219 GpiQueryFonts(qt_display_ps(), QF_PUBLIC, dot, &cnt, sizeof(FONTMETRICS), &fm);
220 if (cnt) {
221 if (fm.fsSelection & FM_SEL_ITALIC) f.setItalic(true);
222 if (fm.fsType & FM_TYPE_FIXED) f.setFixedPitch(true);
223 USHORT weight = fm.usWeightClass;
224 USHORT width = fm.usWidthClass;
225 if (weight < 4) f.setWeight( QFont::Light );
226 else if (weight < 6) f.setWeight(QFont::Normal);
227 else if (weight < 7) f.setWeight(QFont::DemiBold);
228 else if (weight < 8) f.setWeight(QFont::Bold);
229 else f.setWeight(QFont::Black);
230 switch (width) {
231 case 1: f.setStretch(QFont::UltraCondensed); break;
232 case 2: f.setStretch(QFont::ExtraCondensed); break;
233 case 3: f.setStretch(QFont::Condensed); break;
234 case 4: f.setStretch(QFont::SemiCondensed); break;
235 case 5: f.setStretch(QFont::Unstretched); break;
236 case 6: f.setStretch(QFont::SemiExpanded); break;
237 case 7: f.setStretch(QFont::Expanded); break;
238 case 8: f.setStretch(QFont::ExtraExpanded); break;
239 case 9: f.setStretch(QFont::UltraExpanded); break;
240 default: f.setStretch(QFont::Unstretched); break;
241 }
242 f.setFamily(QString::fromLocal8Bit(fm.szFamilyname));
243 f.setPointSize(height);
244 }
245 }
246 delete[] buf;
247 }
248 return f;
249}
250
251static void qt_set_pm_resources()
252{
253 QFont menuFont = qt_sysfont2qfont("Menus");
254 QFont iconFont = qt_sysfont2qfont("IconText");
255 QFont titleFont = qt_sysfont2qfont("WindowTitles");
256
257 QApplication::setFont(menuFont, "QMenu");
258 QApplication::setFont(menuFont, "QMenuBar");
259 QApplication::setFont(titleFont, "Q3TitleBar");
260 QApplication::setFont(titleFont, "QWorkspaceTitleBar");
261 QApplication::setFont(iconFont, "QAbstractItemView");
262 QApplication::setFont(iconFont, "QDockWidgetTitle");
263
264 // let QPallette calculate all colors automatically based on the
265 // base PM window background color -- to be on the safe side in case if we
266 // don't set some role explicitly below (like QPalette::AlternateBase)
267 QPalette pal = QPalette(QColor(qt_sysclr2qrgb(SYSCLR_DIALOGBACKGROUND)));
268
269 pal.setColor(QPalette::WindowText,
270 QColor(qt_sysclr2qrgb(SYSCLR_WINDOWTEXT)));
271 pal.setColor(QPalette::Window,
272 QColor(qt_sysclr2qrgb(SYSCLR_DIALOGBACKGROUND)));
273 pal.setColor(QPalette::ButtonText,
274 QColor(qt_sysclr2qrgb(SYSCLR_MENUTEXT)));
275 pal.setColor(QPalette::Button,
276 QColor(qt_sysclr2qrgb(SYSCLR_BUTTONMIDDLE)));
277 pal.setColor(QPalette::Light,
278 QColor(qt_sysclr2qrgb(SYSCLR_BUTTONLIGHT)));
279 pal.setColor(QPalette::Dark,
280 QColor(qt_sysclr2qrgb(SYSCLR_BUTTONDARK)));
281 pal.setColor(QPalette::Midlight,
282 QColor((pal.light().color().red() + pal.button().color().red()) / 2,
283 (pal.light().color().green() + pal.button().color().green()) / 2,
284 (pal.light().color().blue() + pal.button().color().blue()) / 2));
285 pal.setColor(QPalette::Mid,
286 QColor((pal.dark().color().red() + pal.button().color().red()) / 2,
287 (pal.dark().color().green() + pal.button().color().green()) / 2,
288 (pal.dark().color().blue() + pal.button().color().blue()) / 2));
289 pal.setColor(QPalette::Shadow, // note: SYSCLR_SHADOW often = SYSCLR_BUTTONDARK
290 QColor(qt_sysclr2qrgb(SYSCLR_BUTTONDEFAULT)));
291 pal.setColor(QPalette::Text,
292 QColor(qt_sysclr2qrgb(SYSCLR_WINDOWTEXT)));
293 pal.setColor(QPalette::Base,
294 QColor(qt_sysclr2qrgb(SYSCLR_ENTRYFIELD)));
295 pal.setColor(QPalette::BrightText,
296 QColor(qt_sysclr2qrgb(SYSCLR_BUTTONLIGHT)));
297 pal.setColor(QPalette::Highlight,
298 QColor(qt_sysclr2qrgb(SYSCLR_HILITEBACKGROUND)));
299 pal.setColor(QPalette::HighlightedText,
300 QColor(qt_sysclr2qrgb(SYSCLR_HILITEFOREGROUND)));
301 // these colors are not present in the PM system palette
302 pal.setColor(QPalette::Link, Qt::blue);
303 pal.setColor(QPalette::LinkVisited, Qt::magenta);
304
305 // disabled colors
306 // note: it should be SYSCLR_MENUDISABLEDTEXT but many styles use etched
307 // appearance for disabled elements (in combination with QPalette::Light)
308 // which gives weakly readable text. Make it somewhat darker.
309 pal.setColor(QPalette::Disabled, QPalette::WindowText,
310 QColor(qt_sysclr2qrgb(SYSCLR_BUTTONDARK)));
311 pal.setColor(QPalette::Disabled, QPalette::ButtonText,
312 QColor(qt_sysclr2qrgb(SYSCLR_BUTTONDARK)));
313 pal.setColor(QPalette::Disabled, QPalette::Text,
314 QColor(qt_sysclr2qrgb(SYSCLR_BUTTONDARK)));
315
316 QApplicationPrivate::setSystemPalette(pal);
317
318 // special palete: menus
319 QPalette spal = pal;
320 spal.setColor(QPalette::Window,
321 QColor(qt_sysclr2qrgb(SYSCLR_MENU)));
322 spal.setColor(QPalette::WindowText,
323 QColor(qt_sysclr2qrgb(SYSCLR_MENUTEXT)));
324 spal.setColor(QPalette::Highlight,
325 QColor(qt_sysclr2qrgb( SYSCLR_MENUHILITEBGND)));
326 spal.setColor(QPalette::HighlightedText,
327 QColor(qt_sysclr2qrgb(SYSCLR_MENUHILITE)));
328
329 QApplication::setPalette(spal, "QMenu");
330 QApplication::setPalette(spal, "QMenuBar");
331
332 // special palete: static widget text
333 spal = pal;
334 QColor staticTextCol(qt_sysclr2qrgb( SYSCLR_WINDOWSTATICTEXT));
335 spal.setColor(QPalette::WindowText, staticTextCol);
336 spal.setColor(QPalette::Text, staticTextCol);
337
338 QApplication::setPalette(spal, "QLabel");
339 QApplication::setPalette(spal, "QGroupBox");
340};
341
342/*****************************************************************************
343 qt_init() - initializes Qt for PM
344 *****************************************************************************/
345
346void qt_init(QApplicationPrivate *priv, int)
347{
348 int argc = priv->argc;
349 char **argv = priv->argv;
350 int i, j;
351
352 // Get command line params
353
354 j = argc ? 1 : 0;
355 for (i=1; i<argc; i++) {
356 if (argv[i] && *argv[i] != '-') {
357 argv[j++] = argv[i];
358 continue;
359 }
360#if defined(QT_DEBUG)
361 if (qstrcmp(argv[i], "-nograb") == 0)
362 appNoGrab = !appNoGrab;
363 else
364#endif // QT_DEBUG
365 argv[j++] = argv[i];
366 }
367 if(j < priv->argc) {
368 priv->argv[j] = 0;
369 priv->argc = j;
370 }
371
372 displayPS = WinGetScreenPS(HWND_DESKTOP);
373
374 // initialize key mapper
375 QKeyMapper::changeKeyboard();
376
377 QColormap::initialize();
378 QFont::initialize();
379#ifndef QT_NO_CURSOR
380 QCursorData::initialize();
381#endif
382 qApp->setObjectName(priv->appName());
383
384 // @todo search for QTPM_USE_WINDOWFONT in Qt3 for OS/2 sources for a
385 // discussion on whether to use PM_Fonts/DefaultFont or WindowText as the
386 // default one. So far, the latter is used.
387 QApplicationPrivate::setSystemFont(qt_sysfont2qfont("WindowText"));
388
389 // QFont::locale_init(); ### Uncomment when it does something on OS/2
390
391 if (QApplication::desktopSettingsAware())
392 qt_set_pm_resources();
393}
394
395/*****************************************************************************
396 qt_cleanup() - cleans up when the application is finished
397 *****************************************************************************/
398
399void qt_cleanup()
400{
401 QPixmapCache::clear();
402
403#ifndef QT_NO_CURSOR
404 QCursorData::cleanup();
405#endif
406 QFont::cleanup();
407 QColormap::cleanup();
408
409 WinReleasePS(displayPS);
410 displayPS = 0;
411
412#ifdef QT_LOG_BLITSPEED
413 extern unsigned long long qt_total_blit_ms;
414 extern unsigned long long qt_total_blit_pixels;
415 printf("*** qt_total_blit_ms : %llu\n"
416 "*** qt_total_blit_pixels : %llu\n"
417 "*** average speed, px/ms : %llu\n",
418 qt_total_blit_ms, qt_total_blit_pixels,
419 qt_total_blit_pixels / qt_total_blit_ms);
420#endif
421}
422
423/*****************************************************************************
424 Platform specific global and internal functions
425 *****************************************************************************/
426
427Q_GUI_EXPORT HPS qt_display_ps()
428{
429 Q_ASSERT(qApp);
430 if (!qApp)
431 return NULLHANDLE;
432 return displayPS;
433}
434
435// application no-grab option
436bool qt_nograb()
437{
438#if defined(QT_DEBUG)
439 return appNoGrab;
440#else
441 return false;
442#endif
443}
444
445/*****************************************************************************
446 Safe configuration (move,resize,setGeometry) mechanism to avoid
447 recursion when processing messages.
448 *****************************************************************************/
449
450struct QPMConfigRequest {
451 WId id; // widget to be configured
452 int req; // 0=move, 1=resize, 2=setGeo
453 int x, y, w, h; // request parameters
454};
455
456Q_GLOBAL_STATIC(QList<QPMConfigRequest*>, configRequests);
457
458void qPMRequestConfig(WId id, int req, int x, int y, int w, int h)
459{
460 QPMConfigRequest *r = new QPMConfigRequest;
461 r->id = id;
462 r->req = req;
463 r->x = x;
464 r->y = y;
465 r->w = w;
466 r->h = h;
467 configRequests()->append(r);
468}
469
470/*****************************************************************************
471 GUI event dispatcher
472 *****************************************************************************/
473
474class QGuiEventDispatcherPM : public QEventDispatcherPM
475{
476public:
477 QGuiEventDispatcherPM(QObject *parent = 0);
478 bool processEvents(QEventLoop::ProcessEventsFlags flags);
479};
480
481QGuiEventDispatcherPM::QGuiEventDispatcherPM(QObject *parent)
482 : QEventDispatcherPM(parent)
483{
484 // pre-create the message queue early as we'll need it anyway in GUI mode
485 createMsgQueue();
486}
487
488bool QGuiEventDispatcherPM::processEvents(QEventLoop::ProcessEventsFlags flags)
489{
490 if (!QEventDispatcherPM::processEvents(flags))
491 return false;
492
493 QPMConfigRequest *r;
494 for (;;) {
495 if (configRequests()->isEmpty())
496 break;
497 r = configRequests()->takeLast();
498 QWidget *w = QWidget::find(r->id);
499 QRect rect(r->x, r->y, r->w, r->h);
500 int req = r->req;
501 delete r;
502
503 if (w) { // widget exists
504 if (w->testAttribute(Qt::WA_WState_ConfigPending))
505 break; // biting our tail
506 if (req == 0)
507 w->move(rect.topLeft());
508 else if (req == 1)
509 w->resize(rect.size());
510 else
511 w->setGeometry(rect);
512 }
513 }
514
515 return true;
516}
517
518void QApplicationPrivate::createEventDispatcher()
519{
520 Q_Q(QApplication);
521 if (q->type() != QApplication::Tty)
522 eventDispatcher = new QGuiEventDispatcherPM(q);
523 else
524 eventDispatcher = new QEventDispatcherPM(q);
525}
526
527/*****************************************************************************
528 Platform specific QApplication members
529 *****************************************************************************/
530
531void QApplicationPrivate::initializeWidgetPaletteHash()
532{
533}
534
535QString QApplicationPrivate::appName() const
536{
537 return QCoreApplicationPrivate::appName();
538}
539
540void QApplication::setCursorFlashTime(int msecs)
541{
542 WinSetSysValue(HWND_DESKTOP, SV_CURSORRATE, msecs / 2);
543 QApplicationPrivate::cursor_flash_time = msecs;
544}
545
546int QApplication::cursorFlashTime()
547{
548 int blink = (int)WinQuerySysValue(HWND_DESKTOP, SV_CURSORRATE);
549 if (!blink)
550 return QApplicationPrivate::cursor_flash_time;
551 if (blink > 0)
552 return 2 * blink;
553 return 0;
554}
555
556void QApplication::setDoubleClickInterval(int ms)
557{
558 WinSetSysValue(HWND_DESKTOP, SV_DBLCLKTIME, ms);
559 QApplicationPrivate::mouse_double_click_time = ms;
560}
561
562int QApplication::doubleClickInterval()
563{
564 int ms = (int) WinQuerySysValue(HWND_DESKTOP, SV_DBLCLKTIME);
565 if (ms != 0)
566 return ms;
567 return QApplicationPrivate::mouse_double_click_time;
568}
569
570void QApplication::setKeyboardInputInterval(int ms)
571{
572 QApplicationPrivate::keyboard_input_time = ms;
573}
574
575int QApplication::keyboardInputInterval()
576{
577 // FIXME: get from the system
578 return QApplicationPrivate::keyboard_input_time;
579}
580
581#ifndef QT_NO_WHEELEVENT
582void QApplication::setWheelScrollLines(int n)
583{
584 QApplicationPrivate::wheel_scroll_lines = n;
585}
586
587int QApplication::wheelScrollLines()
588{
589 return QApplicationPrivate::wheel_scroll_lines;
590}
591#endif //QT_NO_WHEELEVENT
592
593void QApplication::setEffectEnabled(Qt::UIEffect effect, bool enable)
594{
595 // @todo implement
596}
597
598bool QApplication::isEffectEnabled(Qt::UIEffect effect)
599{
600 // @todo implement
601 return false;
602}
603
604void QApplication::beep()
605{
606 WinAlarm(HWND_DESKTOP, WA_WARNING);
607}
608
609void QApplication::alert(QWidget *widget, int duration)
610{
611 // @todo implement
612}
613
614/*****************************************************************************
615 QApplication cursor stack
616 *****************************************************************************/
617
618#ifndef QT_NO_CURSOR
619
620void QApplication::setOverrideCursor(const QCursor &cursor)
621{
622 qApp->d_func()->cursor_list.prepend(cursor);
623 WinSetPointer(HWND_DESKTOP, qApp->d_func()->cursor_list.first().handle());
624}
625
626void QApplication::restoreOverrideCursor()
627{
628 if (qApp->d_func()->cursor_list.isEmpty())
629 return;
630 qApp->d_func()->cursor_list.removeFirst();
631
632 if (!qApp->d_func()->cursor_list.isEmpty()) {
633 WinSetPointer(HWND_DESKTOP, qApp->d_func()->cursor_list.first().handle());
634 } else {
635 QWidget *w = QWidget::find(curWin);
636 if (w)
637 WinSetPointer(HWND_DESKTOP, w->cursor().handle());
638 else
639 WinSetPointer(HWND_DESKTOP, QCursor(Qt::ArrowCursor).handle());
640 }
641}
642
643/*
644 Internal function called from QWidget::setCursor()
645
646 force is true if this function is called from dispatchEnterLeave, it means
647 that the mouse is actually directly under this widget.
648*/
649void qt_pm_set_cursor(QWidget *w, bool force)
650{
651 static QPointer<QWidget> lastUnderMouse = 0;
652 if (force) {
653 lastUnderMouse = w;
654 } else if (w->testAttribute(Qt::WA_WState_Created) && lastUnderMouse
655 && lastUnderMouse->effectiveWinId() == w->effectiveWinId()) {
656 w = lastUnderMouse;
657 }
658
659 if (!curWin && w && w->internalWinId())
660 return;
661 QWidget* cW = w && !w->internalWinId() ? w : QWidget::find(curWin);
662 if (!cW || cW->window() != w->window() ||
663 !cW->isVisible() || !cW->underMouse() || QApplication::overrideCursor())
664 return;
665
666 WinSetPointer(HWND_DESKTOP, cW->cursor().handle());
667}
668
669#endif // QT_NO_CURSOR
670
671/*****************************************************************************
672 Routines to find a Qt widget from a screen position
673 *****************************************************************************/
674
675QWidget *QApplication::topLevelAt(const QPoint &pos)
676{
677 // flip y coordinate
678 int y = desktop()->height() - (pos.y() + 1);
679 POINTL ptl = { pos.x(), y };
680 HWND hwnd = WinWindowFromPoint(HWND_DESKTOP, &ptl, FALSE);
681 if (hwnd == NULLHANDLE)
682 return 0;
683
684 QWidget *w = qWidgetFromHWND(hwnd);
685 return w ? w->window() : 0;
686}
687
688/*****************************************************************************
689 Main event loop
690 *****************************************************************************/
691
692// sent to hwnd that has been entered to by a mouse pointer.
693// FID_CLIENT also receives enter messages of its WC_FRAME.
694// mp1 = hwnd that is entered, mp2 = hwnd that is left
695#define WM_U_MOUSEENTER 0x41E
696// sent to hwnd that has been left by a mouse pointer.
697// FID_CLIENT also receives leave messages of its WC_FRAME.
698// mp1 = hwnd that is left, mp2 = hwnd that is entered
699#define WM_U_MOUSELEAVE 0x41F
700
701// some undocumented system values
702#define SV_WORKAREA_YTOP 51
703#define SV_WORKAREA_YBOTTOM 52
704#define SV_WORKAREA_XRIGHT 53
705#define SV_WORKAREA_XLEFT 54
706
707/*!
708 \internal
709 \since 4.1
710
711 If \a gotFocus is true, \a widget will become the active window.
712 Otherwise the active window is reset to 0.
713*/
714void QApplication::pmFocus(QWidget *widget, bool gotFocus)
715{
716 if (gotFocus) {
717 setActiveWindow(widget);
718 if (QApplicationPrivate::active_window
719 && (QApplicationPrivate::active_window->windowType() == Qt::Dialog)) {
720 // raise the entire application, not just the dialog
721 QWidget* mw = QApplicationPrivate::active_window;
722 while(mw->parentWidget() && (mw->windowType() == Qt::Dialog))
723 mw = mw->parentWidget()->window();
724 if (mw->testAttribute(Qt::WA_WState_Created) && mw != QApplicationPrivate::active_window)
725 WinSetWindowPos(mw->d_func()->frameWinId(), HWND_TOP, 0, 0, 0, 0, SWP_ZORDER);
726 }
727 } else {
728 setActiveWindow(0);
729 }
730}
731
732// QMSG wrapper that translates message coordinates from PM to Qt
733struct QtQmsg : public QMSG
734{
735 QtQmsg(HWND aHwnd, ULONG aMsg, MPARAM aMp1, MPARAM aMp2)
736 {
737 hwnd = aHwnd;
738 msg = aMsg;
739 mp1 = aMp1;
740 mp2 = aMp2;
741 time = WinQueryMsgTime(0);
742
743 isTranslatableMouseEvent =
744 (msg >= WM_MOUSEFIRST && msg <= WM_MOUSELAST) ||
745 (msg >= WM_EXTMOUSEFIRST && msg <= WM_EXTMOUSELAST);
746
747 if (isTranslatableMouseEvent || msg == WM_CONTEXTMENU) {
748 ptl.x = (short)SHORT1FROMMP(mp1);
749 ptl.y = (short)SHORT2FROMMP(mp1);
750 WinMapWindowPoints(hwnd, HWND_DESKTOP, &ptl, 1);
751 } else {
752 WinQueryMsgPos(0, &ptl);
753 }
754 // flip y coordinate
755 ptl.y = QApplication::desktop()->height() - (ptl.y + 1);
756 }
757
758 bool isTranslatableMouseEvent;
759};
760
761// QtWndProc() receives all messages from the main event loop
762
763MRESULT EXPENTRY QtWndProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
764{
765 do {
766 if (!qApp) // unstable app state
767 break;
768#if 0
769 // make sure we show widgets (e.g. scrollbars) when the user resizes
770 if (qApp->loopLevel())
771 qApp->sendPostedEvents(0, QEvent::ShowWindowRequest);
772#endif
773
774 MRESULT rc = (MRESULT) FALSE;
775 QETWidget *widget = 0;
776
777 QtQmsg qmsg(hwnd, msg, mp1, mp2);
778
779#if defined(QT_DEBUGMSGFLOW)
780 {
781 QDbgStr str = qStrQMSG(qmsg);
782 if (!str.isEmpty())
783 qDebug() << "*** [W]" << str;
784 }
785#endif
786
787 // send through app filter
788 if (qApp->filterEvent(&qmsg, reinterpret_cast<long *>(&rc)))
789 return rc;
790
791 switch(msg) {
792
793#if !defined(QT_NO_SESSIONMANAGER)
794 case WM_SAVEAPPLICATION: {
795#if defined(DEBUG_SESSIONMANAGER)
796 qDebug("WM_SAVEAPPLICATION: sm_gracefulShutdown %d, "
797 "qt_about_to_destroy_wnd %d, (mp1 %p, mp2 %p)",
798 sm_gracefulShutdown, qt_about_to_destroy_wnd, mp1, mp2);
799#endif
800 // PM seems to post this message to all top-level windows on system
801 // shutdown, so react only to the first one. Also, this message is
802 // always sent by WinDestroyWindow(), where it must be also ignored.
803 if (!qt_about_to_destroy_wnd && !sm_smActive &&
804 !sm_gracefulShutdown) {
805 sm_smActive = true;
806 sm_gracefulShutdown = true;
807 sm_blockUserInput = true; // prevent user-interaction outside interaction windows
808 sm_cancel = false;
809 sm_quitSkipped = false;
810 if (qt_session_manager_self)
811 qApp->commitData(*qt_session_manager_self);
812 sm_smActive = false; // session management has been finished
813 if (sm_cancel) {
814#if defined(DEBUG_SESSIONMANAGER)
815 qDebug("WM_SAVEAPPLICATION: sm_cancel %d", sm_cancel);
816#endif
817 // @todo propagate the patch that does the below to XWP
818 // and enable the code when it appears upstream (see #100)
819#if 0
820 // Here we try to cancel the Extended XWorkplace shutdown.
821 // If it's XWorkplace who sent us WM_SAVEAPPLICATION, then
822 // it probably passed us non-NULL parameters so that
823 // mp1 = its window handle and mp2 = WM_COMMAND code to
824 // cancel the shutdown procedure.
825 HWND shutdownHwnd = HWNDFROMMP(mp1);
826 if (WinIsWindow(0, shutdownHwnd)) {
827 WinPostMsg(shutdownHwnd, WM_COMMAND, mp2, 0);
828 // Ensure we will get WM_QUIT anyway, even if XWP was
829 // not that fast to post it yet (we need it to correctly
830 // finish the graceful shutdown procedure)
831 sm_quitSkipped = true;
832 }
833#endif
834 }
835 // repost WM_QUIT to ourselves because we might have ignored
836 // it in qt_app_canQuit(), so will not get one anymore
837 if (sm_quitSkipped)
838 WinPostMsg(hwnd, WM_QUIT, 0, 0);
839 }
840 // PMREF recommends to pass it to WinDefWindowProc()
841 return WinDefWindowProc(hwnd, msg, mp1, mp2);
842 }
843#endif
844
845 case WM_SYSVALUECHANGED: {
846 // This message is sent to all top-level widgets, handle only once
847 QWidgetList list = QApplication::topLevelWidgets();
848 bool firstWidget = list.first()->winId() == hwnd;
849 if (!firstWidget)
850 break;
851 LONG from = (LONG) mp1;
852 LONG to = (LONG) mp2;
853 #define MY_IS_SV(sv) (from >= (sv) && to <= (sv))
854 if (MY_IS_SV(SV_WORKAREA_XLEFT) || MY_IS_SV(SV_WORKAREA_XRIGHT) ||
855 MY_IS_SV(SV_WORKAREA_YBOTTOM) || MY_IS_SV(SV_WORKAREA_YTOP)) {
856 // send a special invalid resize event to QDesktopWidget
857 QApplication::sendEvent(qt_desktopWidget, 0);
858 // @todo enumerate all top-level widgets and
859 // remaximize those that are maximized
860 } else {
861 /// @todo call qt_set_pm_resources() in the way it is
862 // done in WM_SYSCOLORCHANGE for relevant SV_ values.
863 }
864 #undef MY_IS_SV
865 break;
866 }
867
868 case WM_SYSCOLORCHANGE: {
869 // This message is sent to all top-level widgets, handle only once
870 QWidgetList list = QApplication::topLevelWidgets();
871 bool firstWidget = list.first()->winId() == hwnd;
872 if (!firstWidget)
873 break;
874 if (qApp->type() == QApplication::Tty)
875 break;
876 if (QApplication::desktopSettingsAware())
877 qt_set_pm_resources();
878 break;
879 }
880
881 case WM_BUTTON1DOWN:
882 case WM_BUTTON2DOWN:
883 case WM_BUTTON3DOWN:
884 if (ignoreNextMouseReleaseEvent)
885 ignoreNextMouseReleaseEvent = false;
886 break;
887 case WM_BUTTON1UP:
888 case WM_BUTTON2UP:
889 case WM_BUTTON3UP:
890 if (ignoreNextMouseReleaseEvent) {
891 ignoreNextMouseReleaseEvent = false;
892 if (qt_button_down && qt_button_down->internalWinId() == autoCaptureWnd) {
893 releaseAutoCapture();
894 qt_button_down = 0;
895 }
896 return (MRESULT)TRUE;
897 }
898 break;
899
900 default:
901 break;
902 }
903
904 if (!widget)
905 widget = (QETWidget*)QWidget::find(hwnd);
906 if (!widget) // don't know this widget
907 break;
908
909 if (app_do_modal) { // modal event handling
910 if (!qt_try_modal(widget, &qmsg, rc))
911 return rc;
912 }
913
914 if (widget->pmEvent(&qmsg, &rc)) // send through widget filter
915 return rc;
916
917 if (qmsg.isTranslatableMouseEvent) {
918 if (qApp->activePopupWidget() != 0) { // in popup mode
919 QWidget *w = QApplication::widgetAt(qmsg.ptl.x, qmsg.ptl.y);
920 if (w)
921 widget = (QETWidget*)w;
922 }
923 if (widget->translateMouseEvent(qmsg)) // mouse event
924 return (MRESULT)TRUE;
925#ifndef QT_NO_WHEELEVENT
926 } else if (msg == WM_VSCROLL || msg == WM_HSCROLL) {
927 if (widget->translateWheelEvent(qmsg))
928 return (MRESULT)TRUE;
929#endif
930#ifndef QT_NO_DRAGANDDROP
931 } else if (msg >= WM_DRAGFIRST && msg <= WM_DRAGLAST) {
932 return QDragManager::self()->dispatchDragAndDrop(widget, qmsg);
933#endif
934 } else {
935 switch(msg) {
936
937 case WM_TRANSLATEACCEL: {
938 if (widget->isWindow()) {
939 rc = WinDefWindowProc(hwnd, msg, mp1, mp2);
940 if (rc) {
941 QMSG &qmsg = *(QMSG*)mp1;
942 if (qmsg.msg == WM_SYSCOMMAND &&
943 WinWindowFromID(widget->internalFrameWinId(),
944 FID_SYSMENU)) {
945 switch (SHORT1FROMMP(qmsg.mp1)) {
946 case SC_CLOSE:
947 case SC_TASKMANAGER:
948 return (MRESULT)TRUE;
949 default:
950 break;
951 }
952 }
953 }
954 }
955 // return FALSE in all other cases to let Qt process keystrokes
956 // that are in the system-wide frame accelerator table.
957 return FALSE;
958 }
959
960 case WM_CHAR: { // keyboard event
961 if (!(CHARMSG(&qmsg.msg)->fs & KC_KEYUP))
962 qt_keymapper_private()->updateKeyMap(qmsg);
963
964 QWidget *g = QWidget::keyboardGrabber();
965 if (g)
966 widget = (QETWidget*)g;
967 else if (QApplication::activePopupWidget())
968 widget = (QETWidget*)QApplication::activePopupWidget()->focusWidget()
969 ? (QETWidget*)QApplication::activePopupWidget()->focusWidget()
970 : (QETWidget*)QApplication::activePopupWidget();
971 else if (qApp->focusWidget())
972 widget = (QETWidget*)QApplication::focusWidget();
973 else if (widget->internalWinId() == WinQueryFocus(HWND_DESKTOP)) {
974 // We faked the message to go to exactly that widget.
975 widget = (QETWidget*)widget->window();
976 }
977 if (widget->isEnabled()) {
978 bool result =
979#if !defined (QT_NO_SESSIONMANAGER)
980 sm_blockUserInput ? true :
981#endif
982 qt_keymapper_private()->translateKeyEvent(widget, qmsg, g != 0);
983 if (result)
984 return (MRESULT)TRUE;
985 }
986 break;
987 }
988
989 case WM_KBDLAYERCHANGED: { // Keyboard layout change
990 QKeyMapper::changeKeyboard();
991 break;
992 }
993
994 case WM_QUERYCONVERTPOS: { // IME input box position request
995 // @todo the proper detection of the caret position in the
996 // current widget requires implementing QInputContext. For now,
997 // just send the IME box to the lower left corner of the
998 // top-level window
999 PRECTL pcp = (PRECTL)mp1;
1000 memset(pcp, 0xFF, sizeof(RECTL));
1001 pcp->xLeft = 0;
1002 pcp->yBottom = 0;
1003 return (MRESULT)QCP_CONVERT;
1004 }
1005
1006 case WM_PAINT: { // paint event
1007 if (widget->translatePaintEvent(qmsg))
1008 return (MRESULT)TRUE;
1009 break;
1010 }
1011
1012 case WM_ERASEBACKGROUND: { // erase window background
1013 // flush WM_PAINT messages here to update window contents
1014 // instantly while tracking the resize frame (normally these
1015 // messages are delivered after the user has stopped resizing
1016 // for some time). this slows down resizing slightly but gives a
1017 // better look (no invalid window contents can be seen during
1018 // resize). the alternative could be to erase the background only,
1019 // but we need to do it for every non-toplevel window, which can
1020 // also be time-consuming (WM_ERASEBACKGROUND is sent to WC_FRAME
1021 // clients only, so we would have to do all calculations ourselves).
1022 WinUpdateWindow(widget->effectiveWinId());
1023 return FALSE;
1024 }
1025
1026 case WM_CALCVALIDRECTS: {
1027 // we must always return this value here to cause PM to reposition
1028 // our children accordingly (othwerwise we would have to do it
1029 // ourselves to keep them top-left aligned).
1030 return (MRESULT)(CVR_ALIGNLEFT | CVR_ALIGNTOP);
1031 }
1032
1033 case WM_SIZE: { // resize window
1034 if (widget->translateConfigEvent(qmsg))
1035 return (MRESULT)TRUE;
1036 break;
1037 }
1038
1039 case WM_ACTIVATE: {
1040 qApp->pmFocus(widget, SHORT1FROMMP(mp1));
1041 break;
1042 }
1043
1044 case WM_FOCUSCHANGE: {
1045 HWND hwnd = (HWND)mp1;
1046 bool focus = SHORT1FROMMP(mp2);
1047 if (!focus) {
1048 if (!QWidget::find(hwnd)) {
1049 // we don't get focus, so unset it now
1050 if (QApplication::activePopupWidget()) {
1051 foreignFocusWnd = hwnd;
1052 // Another application was activated while our popups are open,
1053 // then close all popups. In case some popup refuses to close,
1054 // we give up after 1024 attempts (to avoid an infinite loop).
1055 int maxiter = 1024;
1056 QWidget *popup;
1057 while ((popup=QApplication::activePopupWidget()) && maxiter--)
1058 popup->close();
1059 }
1060 // non-Qt ownees of our WC_FRAME window (such as
1061 // FID_SYSMENU) should not cause the focus to be lost.
1062 if (WinQueryWindow(hwnd, QW_OWNER) ==
1063 ((QETWidget*)widget->window())->dptr()->frameWinId())
1064 break;
1065 if (!widget->isWindow())
1066 qApp->pmFocus(widget, focus);
1067 }
1068 }
1069 break;
1070 }
1071
1072 case WM_SHOW: {
1073 // @todo there is some more processing in Qt4, see
1074 // WM_SHOWWINDOW in qapplication_win.cpp
1075 if (!SHORT1FROMMP(mp1) && autoCaptureWnd == widget->internalWinId())
1076 releaseAutoCapture();
1077 break;
1078 }
1079
1080 case WM_CLOSE: { // close window
1081 widget->translateCloseEvent(qmsg);
1082 return (MRESULT)TRUE;
1083 }
1084
1085 case WM_DESTROY: { // destroy window
1086 if (hwnd == curWin) {
1087 QWidget *enter = QWidget::mouseGrabber();
1088 if (enter == widget)
1089 enter = 0;
1090 QApplicationPrivate::dispatchEnterLeave(enter, widget);
1091 curWin = enter ? enter->effectiveWinId() : 0;
1092 qt_last_mouse_receiver = enter;
1093 }
1094 if (widget == popupButtonFocus)
1095 popupButtonFocus = 0;
1096 break;
1097 }
1098
1099#ifndef QT_NO_CONTEXTMENU
1100 case WM_CONTEXTMENU: {
1101 if (SHORT2FROMMP(mp2)) {
1102 // keyboard event
1103 QWidget *fw = qApp->focusWidget();
1104 if (fw && fw->isEnabled()) {
1105 QContextMenuEvent e(QContextMenuEvent::Keyboard,
1106 QPoint(5, 5),
1107 fw->mapToGlobal(QPoint(5, 5)), 0);
1108 if (qt_sendSpontaneousEvent(fw, &e))
1109 return (MRESULT)TRUE;
1110 }
1111 } else {
1112 // mouse event
1113 if (widget->translateMouseEvent(qmsg))
1114 return (MRESULT)TRUE;
1115 }
1116 break;
1117 }
1118#endif
1119
1120 case WM_U_MOUSELEAVE: {
1121 // mp1 = hwndFrom, mp2 = hwndTo
1122 if (hwnd != (HWND)mp1) {
1123 // this must be a LEAVE message from one of the frame
1124 // controls forwarded by WC_FRAME to FID_CLIENT; ignore it
1125 // as it's doesn't actually belong to the given hwnd
1126 break;
1127 }
1128 // We receive a mouse leave for curWin, meaning the mouse was
1129 // moved outside our widgets (in any other case curWin will be
1130 // already set to a different value in response to WM_MOUSEMOVE
1131 // in translateMouseEvent())
1132 if (widget->internalWinId() == curWin) {
1133 bool dispatch = !widget->underMouse();
1134 // hasMouse is updated when dispatching enter/leave,
1135 // so test if it is actually up-to-date
1136 if (!dispatch) {
1137 QRect geom = widget->geometry();
1138 if (widget->parentWidget() && !widget->isWindow()) {
1139 QPoint gp = widget->parentWidget()->mapToGlobal(widget->pos());
1140 geom.setX(gp.x());
1141 geom.setY(gp.y());
1142 }
1143 QPoint cpos = QCursor::pos();
1144 dispatch = !geom.contains(cpos);
1145 if ( !dispatch && !QWidget::mouseGrabber()) {
1146 QWidget *hittest = QApplication::widgetAt(cpos);
1147 dispatch = !hittest || hittest->internalWinId() != curWin;
1148 }
1149 if (!dispatch) {
1150 HPS hps = qt_display_ps();
1151 HRGN hrgn = GpiCreateRegion(hps, 0, NULL);
1152 qt_WinQueryClipRegionOrRect(hwnd, hrgn);
1153 QPoint lcpos = widget->mapFromGlobal(cpos);
1154 // flip y coordinate
1155 POINTL pt = { lcpos.x(), widget->height() - (lcpos.y() + 1) };
1156 dispatch = !GpiPtInRegion(hps, hrgn, &pt);
1157 GpiDestroyRegion(hps, hrgn);
1158 }
1159 }
1160 if (dispatch) {
1161 if (qt_last_mouse_receiver && !qt_last_mouse_receiver->internalWinId())
1162 QApplicationPrivate::dispatchEnterLeave(0, qt_last_mouse_receiver);
1163 else
1164 QApplicationPrivate::dispatchEnterLeave(0, QWidget::find((WId)curWin));
1165 curWin = 0;
1166 qt_last_mouse_receiver = 0;
1167 }
1168 }
1169 break;
1170 }
1171
1172 case WM_MINMAXFRAME: {
1173 PSWP pswp = (PSWP)mp1;
1174
1175 bool window_state_change = false;
1176 Qt::WindowStates oldstate = Qt::WindowStates(widget->dataPtr()->window_state);
1177
1178 if (pswp->fl & SWP_MINIMIZE) {
1179 window_state_change = true;
1180 widget->dataPtr()->window_state |= Qt::WindowMinimized;
1181 if (widget->isVisible()) {
1182 QHideEvent e;
1183 qt_sendSpontaneousEvent(widget, &e);
1184 widget->dptr()->hideChildren(true);
1185 const QString title = widget->windowIconText();
1186 if (!title.isEmpty())
1187 widget->dptr()->setWindowTitle_helper(title);
1188 }
1189 } else if (pswp->fl & (SWP_MAXIMIZE | SWP_RESTORE)) {
1190 window_state_change = true;
1191 if (pswp->fl & SWP_MAXIMIZE) {
1192 widget->topData()->normalGeometry = widget->geometry();
1193 widget->dataPtr()->window_state |= Qt::WindowMaximized;
1194 }
1195 else if (!widget->isMinimized()) {
1196 widget->dataPtr()->window_state &= ~Qt::WindowMaximized;
1197 }
1198
1199 if (widget->isMinimized()) {
1200 widget->dataPtr()->window_state &= ~Qt::WindowMinimized;
1201 widget->dptr()->showChildren(true);
1202 QShowEvent e;
1203 qt_sendSpontaneousEvent(widget, &e);
1204 const QString title = widget->windowTitle();
1205 if (!title.isEmpty())
1206 widget->dptr()->setWindowTitle_helper(title);
1207 }
1208 }
1209
1210 if (window_state_change) {
1211 QWindowStateChangeEvent e(oldstate);
1212 qt_sendSpontaneousEvent(widget, &e);
1213 }
1214
1215 break;
1216 }
1217
1218 default:
1219 break;
1220 }
1221 }
1222
1223 } while(0);
1224
1225 return WinDefWindowProc(hwnd, msg, mp1, mp2);
1226}
1227
1228PFNWP QtOldFrameProc = 0;
1229
1230MRESULT EXPENTRY QtFrameProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
1231{
1232 do {
1233 if (!qApp) // unstable app state
1234 break;
1235#if 0
1236 // make sure we show widgets (e.g. scrollbars) when the user resizes
1237 if (qApp->loopLevel())
1238 qApp->sendPostedEvents(0, QEvent::ShowWindowRequest);
1239#endif
1240
1241 MRESULT rc = (MRESULT) FALSE;
1242
1243 HWND hwndC = WinWindowFromID(hwnd, FID_CLIENT);
1244 QETWidget *widget = (QETWidget*)QWidget::find(hwndC);
1245 if (!widget) // don't know this widget
1246 break;
1247
1248 Q_ASSERT(widget->isWindow());
1249
1250 QtQmsg qmsg(hwnd, msg, mp1, mp2);
1251
1252#if defined(QT_DEBUGMSGFLOW)
1253 {
1254 QDbgStr str = qStrQMSG(qmsg);
1255 if (!str.isEmpty())
1256 qDebug() << "*** [F]" << str;
1257 }
1258#endif
1259
1260 if (qmsg.isTranslatableMouseEvent)
1261 widget->translateNonClientMouseEvent(qmsg);
1262
1263 switch(msg) {
1264
1265 case WM_MOVE: { // move window
1266 // note that we handle it nere instead of having CS_MOVENOTIFY
1267 // on the client and handling in QtWndProc because we don't want
1268 // client inside frame window move messages that would appear during
1269 // minimize/maximize otherwise
1270 if (widget->translateConfigEvent(qmsg))
1271 return (MRESULT)TRUE;
1272 break;
1273 }
1274
1275 case WM_QUERYTRACKINFO: {
1276 QWExtra *x = widget->xtra();
1277 if (x) {
1278 rc = QtOldFrameProc(hwnd, msg, mp1, mp2);
1279 PTRACKINFO pti = (PTRACKINFO) mp2;
1280 int minw = 0, minh = 0;
1281 int maxw = QWIDGETSIZE_MAX, maxh = QWIDGETSIZE_MAX;
1282 QRect fs = widget->frameStrut();
1283 if (x->minw > 0)
1284 minw = x->minw + fs.left() + fs.right();
1285 if (x->minh > 0)
1286 minh = x->minh + fs.top() + fs.bottom();
1287 if (x->maxw < QWIDGETSIZE_MAX)
1288 maxw = x->maxw > x->minw ? x->maxw + fs.left() + fs.right() : minw;
1289 if (x->maxh < QWIDGETSIZE_MAX)
1290 maxh = x->maxh > x->minh ? x->maxh + fs.top() + fs.bottom() : minh;
1291 // obey system recommended minimum size (to emulate Qt/Win32)
1292 pti->ptlMinTrackSize.x = qMax<LONG>(minw, pti->ptlMinTrackSize.x);
1293 pti->ptlMinTrackSize.y = qMax<LONG>(minh, pti->ptlMinTrackSize.y);
1294 // we assume that PM doesn't restrict the maximum size by default
1295 // and use it as an absolute maximum (values above it will cause
1296 // PM to disable window sizing and moving for some reason)
1297 pti->ptlMaxTrackSize.x = qMin<LONG>(maxw, pti->ptlMaxTrackSize.x);
1298 pti->ptlMaxTrackSize.y = qMin<LONG>(maxh, pti->ptlMaxTrackSize.y);
1299 return rc;
1300 }
1301 break;
1302 }
1303
1304 case WM_TRACKFRAME: {
1305 if (QApplication::activePopupWidget()) {
1306 // Another application was activated while our popups are open,
1307 // then close all popups. In case some popup refuses to close,
1308 // we give up after 1024 attempts (to avoid an infinite loop).
1309 int maxiter = 1024;
1310 QWidget *popup;
1311 while ((popup=QApplication::activePopupWidget()) && maxiter--)
1312 popup->close();
1313 }
1314 break;
1315 }
1316
1317 default:
1318 break;
1319 }
1320 } while(0);
1321
1322 return QtOldFrameProc(hwnd, msg, mp1, mp2);
1323}
1324
1325PFNWP QtOldFrameCtlProcs[FID_HORZSCROLL - FID_SYSMENU + 1] = { 0 };
1326
1327MRESULT EXPENTRY QtFrameCtlProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
1328{
1329 // sanity: this procedure is only for standard frame controls
1330 ULONG id = (ULONG)WinQueryWindowUShort(hwnd, QWS_ID);
1331 Q_ASSERT(id >= FID_SYSMENU && id <= FID_HORZSCROLL);
1332 Q_ASSERT(QtOldFrameCtlProcs[id - FID_SYSMENU]);
1333 if (id < FID_SYSMENU || id > FID_HORZSCROLL ||
1334 !QtOldFrameCtlProcs[id - FID_SYSMENU])
1335 return FALSE;
1336
1337 do {
1338 if (!qApp) // unstable app state
1339 break;
1340
1341 HWND hwndF = WinQueryWindow(hwnd, QW_PARENT);
1342 HWND hwndC = WinWindowFromID(hwndF, FID_CLIENT);
1343 QETWidget *widget = (QETWidget*)QWidget::find(hwndC);
1344 if (!widget) // don't know this widget
1345 break;
1346
1347 Q_ASSERT(widget->isWindow());
1348
1349 QtQmsg qmsg(hwnd, msg, mp1, mp2);
1350
1351#if defined(QT_DEBUGMSGFLOW)
1352 {
1353 QDbgStr str = qStrQMSG(qmsg);
1354 if (!str.isEmpty())
1355 qDebug() << "*** [FC]" << str;
1356 }
1357#endif
1358
1359 if (qmsg.isTranslatableMouseEvent)
1360 widget->translateNonClientMouseEvent(qmsg);
1361
1362 } while(0);
1363
1364 // call the original window procedure of this frame control
1365 return QtOldFrameCtlProcs[id - FID_SYSMENU](hwnd, msg, mp1, mp2);
1366}
1367
1368/*****************************************************************************
1369 Modal widgets; We have implemented our own modal widget mechanism
1370 to get total control.
1371 A modal widget without a parent becomes application-modal.
1372 A modal widget with a parent becomes modal to its parent and grandparents..
1373
1374 QApplicationPrivate::enterModal()
1375 Enters modal state
1376 Arguments:
1377 QWidget *widget A modal widget
1378
1379 QApplicationPrivate::leaveModal()
1380 Leaves modal state for a widget
1381 Arguments:
1382 QWidget *widget A modal widget
1383 *****************************************************************************/
1384
1385bool QApplicationPrivate::modalState()
1386{
1387 return app_do_modal;
1388}
1389
1390void QApplicationPrivate::enterModal_sys(QWidget *widget)
1391{
1392 if (!qt_modal_stack)
1393 qt_modal_stack = new QWidgetList;
1394
1395 releaseAutoCapture();
1396 QWidget *leave = qt_last_mouse_receiver;
1397 if (!leave)
1398 leave = QWidget::find(curWin);
1399 QApplicationPrivate::dispatchEnterLeave(0, leave);
1400 qt_modal_stack->insert(0, widget);
1401 app_do_modal = true;
1402 curWin = 0;
1403 qt_last_mouse_receiver = 0;
1404 ignoreNextMouseReleaseEvent = false;
1405
1406 // go through all top-level widgets and disable those that should be
1407 // blocked by the modality (this in particular will disable activation
1408 // through clicking on the title bar and also state change throuhg titlebar
1409 // buttons)
1410 QWidgetList list = QApplication::topLevelWidgets();
1411 foreach(QWidget *w, list) {
1412 if (QApplicationPrivate::isBlockedByModal(w))
1413 WinEnableWindow(w->d_func()->frameWinId(), FALSE);
1414 }
1415}
1416
1417void QApplicationPrivate::leaveModal_sys(QWidget *widget)
1418{
1419 if (qt_modal_stack) {
1420 // go through all affected top-level widgets and re-enable them
1421 QWidgetList list = QApplication::topLevelWidgets();
1422 foreach(QWidget *w, list) {
1423 if (QApplicationPrivate::isBlockedByModal(w))
1424 WinEnableWindow(w->d_func()->frameWinId(), TRUE);
1425 }
1426 if (qt_modal_stack->removeAll(widget)) {
1427 if (qt_modal_stack->isEmpty()) {
1428 delete qt_modal_stack;
1429 qt_modal_stack = 0;
1430 QPoint p(QCursor::pos());
1431 app_do_modal = false; // necessary, we may get recursively into qt_try_modal below
1432 QWidget* w = QApplication::widgetAt(p.x(), p.y());
1433 QWidget *leave = qt_last_mouse_receiver;
1434 if (!leave)
1435 leave = QWidget::find(curWin);
1436 if (QWidget *grabber = QWidget::mouseGrabber()) {
1437 w = grabber;
1438 if (leave == w)
1439 leave = 0;
1440 }
1441 QApplicationPrivate::dispatchEnterLeave(w, leave); // send synthetic enter event
1442 curWin = w ? w->effectiveWinId() : 0;
1443 qt_last_mouse_receiver = w;
1444 }
1445 }
1446 ignoreNextMouseReleaseEvent = true;
1447 }
1448 app_do_modal = qt_modal_stack != 0;
1449}
1450
1451bool qt_try_modal(QWidget *widget, QMSG *qmsg, MRESULT &rc)
1452{
1453 QWidget *top = 0;
1454
1455 if (QApplicationPrivate::tryModalHelper(widget, &top))
1456 return true;
1457
1458 int type = qmsg->msg;
1459
1460 bool block_event = false;
1461 if ((type >= WM_MOUSEFIRST && type <= WM_MOUSELAST) ||
1462 (type >= WM_EXTMOUSEFIRST && type <= WM_EXTMOUSELAST) ||
1463 type == WM_VSCROLL || type == WM_HSCROLL ||
1464 type == WM_U_MOUSELEAVE ||
1465 type == WM_CHAR) {
1466 if (type == WM_MOUSEMOVE) {
1467#ifndef QT_NO_CURSOR
1468 QCursor *c = qt_grab_cursor();
1469 if (!c)
1470 c = QApplication::overrideCursor();
1471 if (c) // application cursor defined
1472 WinSetPointer(HWND_DESKTOP, c->handle());
1473 else
1474 WinSetPointer(HWND_DESKTOP, QCursor(Qt::ArrowCursor).handle());
1475#endif // QT_NO_CURSOR
1476 } else if (type == WM_BUTTON1DOWN || type == WM_BUTTON2DOWN ||
1477 type == WM_BUTTON3DOWN) {
1478 if (!top->isActiveWindow()) {
1479 top->activateWindow();
1480 } else {
1481 QApplication::beep();
1482 }
1483 }
1484 block_event = true;
1485 } else if (type == WM_CLOSE) {
1486 block_event = true;
1487 } else if (type == WM_SYSCOMMAND) {
1488 if (!(SHORT1FROMMP(qmsg->mp1) == SC_RESTORE && widget->isMinimized()))
1489 block_event = true;
1490 }
1491
1492 return !block_event;
1493}
1494
1495/*****************************************************************************
1496 Popup widget mechanism
1497
1498 openPopup()
1499 Adds a widget to the list of popup widgets
1500 Arguments:
1501 QWidget *widget The popup widget to be added
1502
1503 closePopup()
1504 Removes a widget from the list of popup widgets
1505 Arguments:
1506 QWidget *widget The popup widget to be removed
1507 *****************************************************************************/
1508
1509void QApplicationPrivate::openPopup(QWidget *popup)
1510{
1511 if (!QApplicationPrivate::popupWidgets)
1512 QApplicationPrivate::popupWidgets = new QWidgetList;
1513 QApplicationPrivate::popupWidgets->append(popup);
1514 if (!popup->isEnabled())
1515 return;
1516
1517 if (QApplicationPrivate::popupWidgets->count() == 1) {
1518 if (!qt_nograb()) {
1519 Q_ASSERT(popup->testAttribute(Qt::WA_WState_Created));
1520 setAutoCapture(popup->d_func()->frameWinId()); // grab mouse/keyboard
1521 }
1522 if (!qWidgetFromHWND(WinQueryActiveWindow(HWND_DESKTOP))) {
1523 // the popup is opened while another application is active. Steal
1524 // the focus (to receive keyboard input and to make sure we get
1525 // WM_FOCUSCHANGE when clicked outside) but not the active state.
1526 ULONG flags = FC_NOSETACTIVE | FC_NOLOSEACTIVE;
1527 WinFocusChange(HWND_DESKTOP, popup->d_func()->frameWinId(), flags);
1528 foreignActiveWnd = WinQueryActiveWindow(HWND_DESKTOP);
1529 }
1530 }
1531
1532 // Popups are not focus-handled by the window system (the first
1533 // popup grabbed the keyboard), so we have to do that manually: A
1534 // new popup gets the focus
1535 if (popup->focusWidget()) {
1536 popup->focusWidget()->setFocus(Qt::PopupFocusReason);
1537 } else if (QApplicationPrivate::popupWidgets->count() == 1) { // this was the first popup
1538 if (QWidget *fw = q_func()->focusWidget()) {
1539 QFocusEvent e(QEvent::FocusOut, Qt::PopupFocusReason);
1540 q_func()->sendEvent(fw, &e);
1541 }
1542 }
1543}
1544
1545void QApplicationPrivate::closePopup(QWidget *popup)
1546{
1547 if (!QApplicationPrivate::popupWidgets)
1548 return;
1549 QApplicationPrivate::popupWidgets->removeAll(popup);
1550 POINTL curPos;
1551 WinQueryPointerPos(HWND_DESKTOP, &curPos);
1552 // flip y coordinate
1553 curPos.y = q_func()->desktop()->height() - (curPos.y + 1);
1554
1555 if (QApplicationPrivate::popupWidgets->isEmpty()) {
1556 // this was the last popup
1557 if (foreignActiveWnd != NULLHANDLE && foreignFocusWnd != NULLHANDLE) {
1558 // we stole focus from another application so PM won't send it
1559 // WM_ACTIVATE(FALSE). Our duty is to do it for PM.
1560 HWND parent, desktop = WinQueryDesktopWindow(0, NULLHANDLE);
1561 // find the top-level window of the window actually getting focus
1562 while ((parent = WinQueryWindow(foreignFocusWnd, QW_PARENT)) != desktop)
1563 foreignFocusWnd = parent;
1564 // send deactivation to the old active window if it differs
1565 if (foreignFocusWnd != foreignActiveWnd)
1566 WinSendMsg(foreignActiveWnd, WM_ACTIVATE, (MPARAM)FALSE, (MPARAM)foreignActiveWnd);
1567 }
1568 foreignActiveWnd = NULLHANDLE;
1569 foreignFocusWnd = NULLHANDLE;
1570
1571 delete QApplicationPrivate::popupWidgets;
1572 QApplicationPrivate::popupWidgets = 0;
1573 replayPopupMouseEvent = (!popup->geometry().contains(QPoint(curPos.x, curPos.y))
1574 && !popup->testAttribute(Qt::WA_NoMouseReplay));
1575 if (!popup->isEnabled())
1576 return;
1577 if (!qt_nograb()) // grabbing not disabled
1578 releaseAutoCapture();
1579 QWidget *fw = QApplicationPrivate::active_window ? QApplicationPrivate::active_window->focusWidget()
1580 : q_func()->focusWidget();
1581 if (fw) {
1582 if (fw != q_func()->focusWidget()) {
1583 fw->setFocus(Qt::PopupFocusReason);
1584 } else {
1585 QFocusEvent e(QEvent::FocusIn, Qt::PopupFocusReason);
1586 q_func()->sendEvent(fw, &e);
1587 }
1588 }
1589 } else {
1590 // Popups are not focus-handled by the window system (the
1591 // first popup grabbed the keyboard), so we have to do that
1592 // manually: A popup was closed, so the previous popup gets
1593 // the focus.
1594 QWidget* aw = QApplicationPrivate::popupWidgets->last();
1595 if (QApplicationPrivate::popupWidgets->count() == 1) {
1596 Q_ASSERT(aw->testAttribute(Qt::WA_WState_Created));
1597 setAutoCapture(aw->internalWinId());
1598 }
1599 if (QWidget *fw = aw->focusWidget())
1600 fw->setFocus(Qt::PopupFocusReason);
1601 }
1602}
1603
1604/*****************************************************************************
1605 Event translation; translates PM events to Qt events
1606 *****************************************************************************/
1607
1608static int mouseButtonState()
1609{
1610 int state = 0;
1611
1612 if (WinGetKeyState(HWND_DESKTOP, VK_BUTTON1) & 0x8000)
1613 state |= Qt::LeftButton;
1614 if (WinGetKeyState(HWND_DESKTOP, VK_BUTTON2) & 0x8000)
1615 state |= Qt::RightButton;
1616 if (WinGetKeyState(HWND_DESKTOP, VK_BUTTON3) & 0x8000)
1617 state |= Qt::MidButton;
1618
1619 return state;
1620}
1621
1622//
1623// Auto-capturing for mouse press and mouse release
1624//
1625
1626static void setAutoCapture(HWND h)
1627{
1628 if (autoCaptureWnd)
1629 releaseAutoCapture();
1630 autoCaptureWnd = h;
1631
1632 if (!mouseButtonState()) {
1633 // all buttons released, we don't actually capture the mouse
1634 // (see QWidget::translateMouseEvent())
1635 autoCaptureReleased = true;
1636 } else {
1637 autoCaptureReleased = false;
1638 WinSetCapture(HWND_DESKTOP, h);
1639 }
1640}
1641
1642static void releaseAutoCapture()
1643{
1644 if (autoCaptureWnd) {
1645 if (!autoCaptureReleased) {
1646 WinSetCapture(HWND_DESKTOP, NULLHANDLE);
1647 autoCaptureReleased = true;
1648 }
1649 autoCaptureWnd = NULLHANDLE;
1650 }
1651}
1652
1653//
1654// Mouse event translation
1655//
1656
1657static const ushort mouseTbl[] = {
1658 WM_MOUSEMOVE, QEvent::MouseMove, 0,
1659 WM_BUTTON1DOWN, QEvent::MouseButtonPress, Qt::LeftButton,
1660 WM_BUTTON1UP, QEvent::MouseButtonRelease, Qt::LeftButton,
1661 WM_BUTTON1DBLCLK, QEvent::MouseButtonDblClick, Qt::LeftButton,
1662 WM_BUTTON2DOWN, QEvent::MouseButtonPress, Qt::RightButton,
1663 WM_BUTTON2UP, QEvent::MouseButtonRelease, Qt::RightButton,
1664 WM_BUTTON2DBLCLK, QEvent::MouseButtonDblClick, Qt::RightButton,
1665 WM_BUTTON3DOWN, QEvent::MouseButtonPress, Qt::MidButton,
1666 WM_BUTTON3UP, QEvent::MouseButtonRelease, Qt::MidButton,
1667 WM_BUTTON3DBLCLK, QEvent::MouseButtonDblClick, Qt::MidButton,
1668 WM_CONTEXTMENU, QEvent::ContextMenu, 0,
1669 0, 0, 0
1670};
1671
1672static const ushort mouseTblNC[] = {
1673 WM_MOUSEMOVE, QEvent::NonClientAreaMouseMove, 0,
1674 WM_BUTTON1DOWN, QEvent::NonClientAreaMouseButtonPress, Qt::LeftButton,
1675 WM_BUTTON1UP, QEvent::NonClientAreaMouseButtonRelease, Qt::LeftButton,
1676 WM_BUTTON1DBLCLK, QEvent::NonClientAreaMouseButtonDblClick, Qt::LeftButton,
1677 WM_BUTTON2DOWN, QEvent::NonClientAreaMouseButtonPress, Qt::RightButton,
1678 WM_BUTTON2UP, QEvent::NonClientAreaMouseButtonRelease, Qt::RightButton,
1679 WM_BUTTON2DBLCLK, QEvent::NonClientAreaMouseButtonDblClick, Qt::RightButton,
1680 WM_BUTTON3DOWN, QEvent::NonClientAreaMouseButtonPress, Qt::MidButton,
1681 WM_BUTTON3UP, QEvent::NonClientAreaMouseButtonRelease, Qt::MidButton,
1682 WM_BUTTON3DBLCLK, QEvent::NonClientAreaMouseButtonDblClick, Qt::MidButton,
1683 0, 0, 0
1684};
1685
1686static int translateButtonState(USHORT s, int type, int button)
1687{
1688 Q_UNUSED(button);
1689
1690 int bst = mouseButtonState();
1691
1692 if (type == QEvent::ContextMenu) {
1693 if (WinGetKeyState(HWND_DESKTOP, VK_SHIFT) & 0x8000)
1694 bst |= Qt::ShiftModifier;
1695 if (WinGetKeyState(HWND_DESKTOP, VK_ALT) & 0x8000)
1696 bst |= Qt::AltModifier;
1697 if (WinGetKeyState(HWND_DESKTOP, VK_CTRL) & 0x8000)
1698 bst |= Qt::ControlModifier;
1699 } else {
1700 if (s & KC_SHIFT)
1701 bst |= Qt::ShiftModifier;
1702 if ((s & KC_ALT))
1703 bst |= Qt::AltModifier;
1704 if (s & KC_CTRL)
1705 bst |= Qt::ControlModifier;
1706 }
1707 if (qt_keymapper_private()->extraKeyState & Qt::AltModifier)
1708 bst |= Qt::AltModifier;
1709 if (qt_keymapper_private()->extraKeyState & Qt::MetaModifier)
1710 bst |= Qt::MetaModifier;
1711
1712 return bst;
1713}
1714
1715bool QETWidget::translateMouseEvent(const QMSG &qmsg)
1716{
1717#if defined(QT_DEBUGMSGFLOW) && 0
1718 static const char *msgNames[] = { // 11 items
1719 "WM_MOUSEMOVE",
1720 "WM_BUTTON1DOWN", "WM_BUTTON1UP", "WM_BUTTON1DBLCLK",
1721 "WM_BUTTON2DOWN", "WM_BUTTON2UP", "WM_BUTTON2DBLCLK",
1722 "WM_BUTTON3DOWN", "WM_BUTTON3UP", "WM_BUTTON3DBLCLK",
1723 "WM_???"
1724 };
1725 int msgIdx = qmsg.msg - WM_MOUSEMOVE;
1726 if (msgIdx < 0 || msgIdx > 9)
1727 msgIdx = 10;
1728 qDebug("%s (%04lX): [%08lX/%p:%s] %04hd,%04hd hit=%04hX fl=%04hX",
1729 msgNames[msgIdx], qmsg.msg, qmsg.hwnd, this, widgetName(this),
1730 SHORT1FROMMP(qmsg.mp1), SHORT2FROMMP(qmsg.mp1),
1731 SHORT1FROMMP(qmsg.mp2), SHORT2FROMMP(qmsg.mp2));
1732#endif
1733
1734 if (!isWindow() && testAttribute(Qt::WA_NativeWindow))
1735 Q_ASSERT(internalWinId() != NULLHANDLE);
1736
1737 static QPoint pos; // window pos (y flipped)
1738 static POINTL gpos = { -1, -1 }; // global pos (y flipped)
1739 QEvent::Type type; // event parameters
1740 int button;
1741 int state;
1742 int i;
1743
1744 // candidate for the double click event
1745 static HWND dblClickCandidateWin = 0;
1746
1747#if !defined (QT_NO_SESSIONMANAGER)
1748 if (sm_blockUserInput) //block user interaction during session management
1749 return true;
1750#endif
1751
1752 for (i = 0; mouseTbl[i] && (ULONG)mouseTbl[i] != qmsg.msg; i += 3)
1753 ;
1754 if (!mouseTbl[i])
1755 return true;
1756
1757 type = (QEvent::Type)mouseTbl[++i]; // event type
1758 button = mouseTbl[++i]; // which button
1759 state = translateButtonState(SHORT2FROMMP(qmsg.mp2), type, button); // button state
1760
1761 // It seems, that PM remembers only the WM_BUTTONxDOWN message (instead of
1762 // the WM_BUTTONxDOWN + WM_BUTTONxUP pair) to detect whether the next button
1763 // press should be converted to WM_BUTTONxDBLCLK or not. As a result, the
1764 // window gets WM_BUTTONxDBLCLK even if it didn't receive the preceeding
1765 // WM_BUTTONxUP (this happens if we issue WinSetCapture() on the first
1766 // WM_BUTTONxDOWN), which is obviously wrong and makes problems for QWorkspace
1767 // and QTitleBar system menu handlers that don't expect a double click after
1768 // they opened a popup menu. dblClickCandidateWin is reset to 0 (see a ***
1769 // remmark below) when WinSetCapture is issued that directs messages
1770 // to a window other than one received the first WM_BUTTONxDOWN,
1771 // so we can fix it here. Note that if there is more than one popup window,
1772 // WinSetCapture is issued only for the first of them, so this code doesn't
1773 // prevent MouseButtonDblClick from being delivered to a popup when another
1774 // popup gets closed on the first WM_BUTTONxDOWN (Qt/Win32 behaves in the
1775 // same way, so it's left for compatibility).
1776 if (type == QEvent::MouseButtonPress) {
1777 dblClickCandidateWin = qmsg.hwnd;
1778 } else if (type == QEvent::MouseButtonDblClick) {
1779 if (dblClickCandidateWin != qmsg.hwnd)
1780 type = QEvent::MouseButtonPress;
1781 dblClickCandidateWin = 0;
1782 }
1783
1784 const QPoint widgetPos = mapFromGlobal(QPoint(qmsg.ptl.x, qmsg.ptl.y));
1785
1786 QWidget *alienWidget = !internalWinId() ? this : QApplication::widgetAt(qmsg.ptl.x, qmsg.ptl.y);
1787 if (alienWidget && alienWidget->internalWinId())
1788 alienWidget = 0;
1789
1790 if (type == QEvent::MouseMove) {
1791 if (!(state & Qt::MouseButtonMask))
1792 qt_button_down = 0;
1793#ifndef QT_NO_CURSOR
1794 QCursor *c = qt_grab_cursor();
1795 if (!c)
1796 c = QApplication::overrideCursor();
1797 if (c) // application cursor defined
1798 WinSetPointer(HWND_DESKTOP, c->handle());
1799 else if (!qt_button_down) {
1800 QWidget *w = alienWidget ? alienWidget : this;
1801 while (!w->isWindow() && !w->isEnabled())
1802 w = w->parentWidget();
1803 WinSetPointer(HWND_DESKTOP, w->cursor().handle());
1804 }
1805#else
1806 // pass the msg to the default proc to let it change the pointer shape
1807 WinDefWindowProc(qmsg.hwnd, qmsg.msg, qmsg.mp1, qmsg.mp2);
1808#endif
1809
1810 HWND id = effectiveWinId();
1811 QWidget *mouseGrabber = QWidget::mouseGrabber();
1812 QWidget *activePopupWidget = qApp->activePopupWidget();
1813 if (mouseGrabber) {
1814 if (!activePopupWidget || (activePopupWidget == this && !rect().contains(widgetPos)))
1815 id = mouseGrabber->effectiveWinId();
1816 } else if (type == QEvent::NonClientAreaMouseMove) {
1817 id = 0;
1818 }
1819
1820 if (curWin != id) { // new current window
1821 // @todo
1822 // add CS_HITTEST to our window classes and handle WM_HITTEST,
1823 // otherwise disabled windows will not get mouse events?
1824 if (id == 0) {
1825 QWidget *leave = qt_last_mouse_receiver;
1826 if (!leave)
1827 leave = QWidget::find(curWin);
1828 QApplicationPrivate::dispatchEnterLeave(0, leave);
1829 qt_last_mouse_receiver = 0;
1830 curWin = 0;
1831 } else {
1832 QWidget *leave = 0;
1833 if (curWin && qt_last_mouse_receiver)
1834 leave = qt_last_mouse_receiver;
1835 else
1836 leave = QWidget::find(curWin);
1837 QWidget *enter = alienWidget ? alienWidget : this;
1838 if (mouseGrabber && activePopupWidget) {
1839 if (leave != mouseGrabber)
1840 enter = mouseGrabber;
1841 else
1842 enter = activePopupWidget == this ? this : mouseGrabber;
1843 }
1844 QApplicationPrivate::dispatchEnterLeave(enter, leave);
1845 qt_last_mouse_receiver = enter;
1846 curWin = id;
1847 }
1848 }
1849
1850 // *** PM posts a dummy WM_MOUSEMOVE message (with the same, uncahnged
1851 // pointer coordinates) after every WinSetCapture that actually changes
1852 // the capture target. I.e., if the argument of WinSetCapture is
1853 // NULLHANDLE, a window under the mouse pointer gets this message,
1854 // otherwise the specified window gets it unless it is already under the
1855 // pointer. We use this info to check whether the window can be a double
1856 // click candidate (see above).
1857 if (qmsg.ptl.x == gpos.x && qmsg.ptl.y == gpos.y) {
1858 if (dblClickCandidateWin != qmsg.hwnd)
1859 dblClickCandidateWin = 0;
1860 return true;
1861 }
1862
1863 gpos = qmsg.ptl;
1864 pos = widgetPos;
1865
1866 Q_ASSERT(testAttribute(Qt::WA_WState_Created));
1867 } else {
1868 if (type == QEvent::MouseButtonPress && !isActiveWindow())
1869 activateWindow();
1870
1871 gpos = qmsg.ptl;
1872 pos = widgetPos;
1873
1874 // mouse button pressed
1875 if (!qt_button_down && (type == QEvent::MouseButtonPress || type == QEvent::MouseButtonDblClick)) {
1876 QWidget *tlw = window();
1877 if (QWidget *child = tlw->childAt(mapTo(tlw, pos)))
1878 qt_button_down = child;
1879 else
1880 qt_button_down = this;
1881 }
1882 }
1883
1884 // detect special button states
1885 enum { Other, SinglePressed, AllReleased } btnState = Other;
1886 int bs = state & Qt::MouseButtonMask;
1887 if ((type == QEvent::MouseButtonPress ||
1888 type == QEvent::MouseButtonDblClick) && bs == button) {
1889 btnState = SinglePressed;
1890 } else if (type == QEvent::MouseButtonRelease && bs == 0) {
1891 btnState = AllReleased;
1892 }
1893
1894 bool res = false;
1895
1896 if (qApp->d_func()->inPopupMode()) { // in popup mode
1897 if (!autoCaptureReleased && btnState == AllReleased) {
1898 // in order to give non-Qt windows the opportunity to see mouse
1899 // messages while our popups are active we need to release the
1900 // mouse capture which is absolute in OS/2. We do it directly
1901 // (not through releaseAutoCapture()) in order to keep
1902 // autoCaptureWnd nonzero so that mouse move events (actually sent
1903 // to one of Qt widgets) are forwarded to the active popup.
1904 autoCaptureReleased = true;
1905 WinSetCapture(HWND_DESKTOP, 0);
1906 } else if (autoCaptureReleased && btnState == SinglePressed) {
1907 // set the mouse capture back if a button is pressed.
1908 if ( autoCaptureWnd ) {
1909 autoCaptureReleased = false;
1910 WinSetCapture(HWND_DESKTOP, autoCaptureWnd);
1911 }
1912 }
1913
1914 replayPopupMouseEvent = false;
1915 QWidget* activePopupWidget = qApp->activePopupWidget();
1916 QWidget *target = activePopupWidget;
1917 const QPoint globalPos(gpos.x, gpos.y);
1918
1919 if (target != this) {
1920 if ((windowType() == Qt::Popup) && rect().contains(pos) && 0)
1921 target = this;
1922 else // send to last popup
1923 pos = target->mapFromGlobal(globalPos);
1924 }
1925 QWidget *popupChild = target->childAt(pos);
1926 bool releaseAfter = false;
1927 switch (type) {
1928 case QEvent::MouseButtonPress:
1929 case QEvent::MouseButtonDblClick:
1930 popupButtonFocus = popupChild;
1931 break;
1932 case QEvent::MouseButtonRelease:
1933 releaseAfter = true;
1934 break;
1935 default:
1936 break; // nothing for mouse move
1937 }
1938
1939 if (target->isEnabled()) {
1940 if (popupButtonFocus) {
1941 target = popupButtonFocus;
1942 } else if (popupChild) {
1943 // forward mouse events to the popup child. mouse move events
1944 // are only forwarded to popup children that enable mouse tracking.
1945 if (type != QEvent::MouseMove || popupChild->hasMouseTracking())
1946 target = popupChild;
1947 }
1948
1949 pos = target->mapFromGlobal(globalPos);
1950#ifndef QT_NO_CONTEXTMENU
1951 if (type == QEvent::ContextMenu) {
1952 QContextMenuEvent e(QContextMenuEvent::Mouse, pos, globalPos,
1953 Qt::KeyboardModifiers(state & Qt::KeyboardModifierMask));
1954 res = QApplication::sendSpontaneousEvent(target, &e);
1955 res = res && e.isAccepted();
1956 }
1957 else
1958#endif
1959 {
1960 QMouseEvent e(type, pos, globalPos,
1961 Qt::MouseButton(button),
1962 Qt::MouseButtons(state & Qt::MouseButtonMask),
1963 Qt::KeyboardModifiers(state & Qt::KeyboardModifierMask));
1964 res = QApplicationPrivate::sendMouseEvent(target, &e, alienWidget, this, &qt_button_down,
1965 qt_last_mouse_receiver);
1966 res = res && e.isAccepted();
1967 }
1968 } else {
1969 // close disabled popups when a mouse button is pressed or released
1970 switch (type) {
1971 case QEvent::MouseButtonPress:
1972 case QEvent::MouseButtonDblClick:
1973 case QEvent::MouseButtonRelease:
1974 target->close();
1975 break;
1976 default:
1977 break;
1978 }
1979 }
1980
1981 if (releaseAfter) {
1982 popupButtonFocus = 0;
1983 qt_button_down = 0;
1984 }
1985
1986 if (type == QEvent::MouseButtonPress
1987 && qApp->activePopupWidget() != activePopupWidget
1988 && replayPopupMouseEvent) {
1989 // the popup dissappeared. Replay the event
1990 QWidget* w = QApplication::widgetAt(gpos.x, gpos.y);
1991 if (w && !QApplicationPrivate::isBlockedByModal(w)) {
1992 Q_ASSERT(w->testAttribute(Qt::WA_WState_Created));
1993 HWND hwndTarget = w->effectiveWinId();
1994 if (QWidget::mouseGrabber() == 0)
1995 setAutoCapture(hwndTarget);
1996 if (!w->isActiveWindow())
1997 w->activateWindow();
1998 POINTL ptl = gpos;
1999 // flip y coordinate
2000 ptl.y = QApplication::desktop()->height() - (ptl.y + 1);
2001 WinMapWindowPoints(HWND_DESKTOP, hwndTarget, &ptl, 1);
2002 WinPostMsg(hwndTarget, qmsg.msg,
2003 MPFROM2SHORT(ptl.x, ptl.y), qmsg.mp2);
2004 }
2005 }
2006 } else { // not popup mode
2007 if (btnState == SinglePressed && QWidget::mouseGrabber() == 0) {
2008 Q_ASSERT(testAttribute(Qt::WA_WState_Created));
2009 setAutoCapture(internalWinId());
2010 } else if (btnState == AllReleased && QWidget::mouseGrabber() == 0) {
2011 releaseAutoCapture();
2012 }
2013
2014 const QPoint globalPos(gpos.x,gpos.y);
2015 QWidget *widget = QApplicationPrivate::pickMouseReceiver(this, globalPos, pos, type,
2016 Qt::MouseButtons(bs),
2017 qt_button_down, alienWidget);
2018 if (!widget)
2019 return false; // don't send event
2020
2021#ifndef QT_NO_CONTEXTMENU
2022 if (type == QEvent::ContextMenu) {
2023 QContextMenuEvent e(QContextMenuEvent::Mouse, pos, globalPos,
2024 Qt::KeyboardModifiers(state & Qt::KeyboardModifierMask));
2025 res = QApplication::sendSpontaneousEvent(widget, &e);
2026 res = res && e.isAccepted();
2027 } else
2028#endif
2029 {
2030 QMouseEvent e(type, pos, globalPos, Qt::MouseButton(button),
2031 Qt::MouseButtons(state & Qt::MouseButtonMask),
2032 Qt::KeyboardModifiers(state & Qt::KeyboardModifierMask));
2033
2034 res = QApplicationPrivate::sendMouseEvent(widget, &e, alienWidget, this, &qt_button_down,
2035 qt_last_mouse_receiver);
2036 res = res && e.isAccepted();
2037 }
2038 }
2039
2040 return res;
2041}
2042
2043void QETWidget::translateNonClientMouseEvent(const QMSG &qmsg)
2044{
2045 // this is a greatly simplified version of translateMouseEvent() that
2046 // only sends informational non-client area mouse messages to the top-level
2047 // widget
2048
2049 Q_ASSERT(isWindow());
2050 Q_ASSERT(internalWinId() != NULLHANDLE);
2051
2052#if !defined (QT_NO_SESSIONMANAGER)
2053 if (sm_blockUserInput) //block user interaction during session management
2054 return;
2055#endif
2056
2057 if (qApp->d_func()->inPopupMode()) {
2058 // don't report non-client area events in popup mode
2059 // (for compatibility with Windows)
2060 return;
2061 }
2062
2063 int i;
2064 for (i = 0; mouseTblNC[i] && (ULONG)mouseTblNC[i] != qmsg.msg; i += 3)
2065 ;
2066 if (!mouseTblNC[i])
2067 return;
2068
2069 QEvent::Type type = (QEvent::Type)mouseTblNC[++i]; // event type
2070 int button = mouseTblNC[++i]; // which button
2071 int state = translateButtonState(SHORT2FROMMP(qmsg.mp2), type, button); // button state
2072
2073 const QPoint globalPos(QPoint(qmsg.ptl.x, qmsg.ptl.y));
2074 const QPoint widgetPos = mapFromGlobal(globalPos);
2075
2076 QMouseEvent e(type, widgetPos, globalPos, Qt::MouseButton(button),
2077 Qt::MouseButtons(state & Qt::MouseButtonMask),
2078 Qt::KeyboardModifiers(state & Qt::KeyboardModifierMask));
2079
2080 QApplication::sendSpontaneousEvent(this, &e);
2081}
2082
2083#ifndef QT_NO_WHEELEVENT
2084bool QETWidget::translateWheelEvent(const QMSG &qmsg)
2085{
2086 enum { WHEEL_DELTA = 120 };
2087
2088#ifndef QT_NO_SESSIONMANAGER
2089 if (sm_blockUserInput) // block user interaction during session management
2090 return true;
2091#endif
2092
2093 // consume duplicate wheel events sent by the AMouse driver to emulate
2094 // multiline scrolls. we need this since currently Qt (QScrollBar, for
2095 // instance) maintains the number of lines to scroll per wheel rotation
2096 // (including the special handling of CTRL and SHIFT modifiers) on its own
2097 // and doesn't have a setting to tell it to be aware of system settings
2098 // for the mouse wheel. if we had processed events as they are, we would
2099 // get a confusing behavior (too many lines scrolled etc.).
2100 {
2101 int devh = QApplication::desktop()->height();
2102 QMSG wheelMsg;
2103 while (WinPeekMsg(0, &wheelMsg, qmsg.hwnd, qmsg.msg, qmsg.msg, PM_NOREMOVE)) {
2104 // PM bug: ptl contains SHORT coordinates although fields are LONG
2105 wheelMsg.ptl.x = (short) wheelMsg.ptl.x;
2106 wheelMsg.ptl.y = (short) wheelMsg.ptl.y;
2107 // flip y coordinate
2108 wheelMsg.ptl.y = devh - (wheelMsg.ptl.y + 1);
2109 if (wheelMsg.mp1 != qmsg.mp1 ||
2110 wheelMsg.mp2 != qmsg.mp2 ||
2111 wheelMsg.ptl.x != qmsg.ptl.x ||
2112 wheelMsg.ptl.y != qmsg.ptl.y)
2113 break;
2114 WinPeekMsg(0, &wheelMsg, qmsg.hwnd, qmsg.msg, qmsg.msg, PM_REMOVE);
2115 }
2116 }
2117
2118 int delta;
2119 USHORT cmd = SHORT2FROMMP(qmsg.mp2);
2120 switch (cmd) {
2121 case SB_LINEUP:
2122 case SB_PAGEUP:
2123 delta = WHEEL_DELTA;
2124 break;
2125 case SB_LINEDOWN:
2126 case SB_PAGEDOWN:
2127 delta = -WHEEL_DELTA;
2128 break;
2129 default:
2130 return false;
2131 }
2132
2133 int state = 0;
2134 if (WinGetKeyState(HWND_DESKTOP, VK_SHIFT ) & 0x8000)
2135 state |= Qt::ShiftModifier;
2136 if ((WinGetKeyState(HWND_DESKTOP, VK_ALT) & 0x8000) ||
2137 (qt_keymapper_private()->extraKeyState & Qt::AltModifier))
2138 state |= Qt::AltModifier;
2139 if (WinGetKeyState(HWND_DESKTOP, VK_CTRL) & 0x8000)
2140 state |= Qt::ControlModifier;
2141 if (qt_keymapper_private()->extraKeyState & Qt::MetaModifier)
2142 state |= Qt::MetaModifier;
2143
2144 Qt::Orientation orient;
2145 // Alt inverts scroll orientation (Qt/Win32 behavior)
2146 if (state & Qt::AltModifier)
2147 orient = qmsg.msg == WM_VSCROLL ? Qt::Horizontal : Qt::Vertical;
2148 else
2149 orient = qmsg.msg == WM_VSCROLL ? Qt::Vertical : Qt::Horizontal;
2150
2151 QPoint globalPos(qmsg.ptl.x, qmsg.ptl.y);
2152
2153 // if there is a widget under the mouse and it is not shadowed
2154 // by modality, we send the event to it first
2155 MRESULT rc = FALSE;
2156 QWidget* w = QApplication::widgetAt(globalPos);
2157 if (!w || !qt_try_modal(w, (QMSG*)&qmsg, rc)) {
2158 //synaptics touchpad shows its own widget at this position
2159 //so widgetAt() will fail with that HWND, try child of this widget
2160 w = this->childAt(this->mapFromGlobal(globalPos));
2161 if (!w)
2162 w = this;
2163 }
2164
2165 // send the event to the widget or its ancestors
2166 {
2167 QWidget* popup = qApp->activePopupWidget();
2168 if (popup && w->window() != popup)
2169 popup->close();
2170#ifndef QT_NO_WHEELEVENT
2171 QWheelEvent e(w->mapFromGlobal(globalPos), globalPos, delta,
2172 Qt::MouseButtons(state & Qt::MouseButtonMask),
2173 Qt::KeyboardModifier(state & Qt::KeyboardModifierMask), orient);
2174
2175 if (QApplication::sendSpontaneousEvent(w, &e))
2176#else
2177 Q_UNUSED(orient);
2178#endif //QT_NO_WHEELEVENT
2179 return true;
2180 }
2181
2182 // send the event to the widget that has the focus or its ancestors, if different
2183 if (w != qApp->focusWidget() && (w = qApp->focusWidget())) {
2184 QWidget* popup = qApp->activePopupWidget();
2185 if (popup && w->window() != popup)
2186 popup->close();
2187#ifndef QT_NO_WHEELEVENT
2188 QWheelEvent e(w->mapFromGlobal(globalPos), globalPos, delta,
2189 Qt::MouseButtons(state & Qt::MouseButtonMask),
2190 Qt::KeyboardModifier(state & Qt::KeyboardModifierMask), orient);
2191 if (QApplication::sendSpontaneousEvent(w, &e))
2192#endif //QT_NO_WHEELEVENT
2193 return true;
2194 }
2195
2196 return false;
2197}
2198#endif
2199
2200/*!
2201 \internal
2202 In DnD, the mouse release event never appears, so the
2203 mouse button state machine must be manually reset.
2204*/
2205void qt_pmMouseButtonUp()
2206{
2207 // release any stored mouse capture
2208 qt_button_down = 0;
2209 autoCaptureReleased = true;
2210 releaseAutoCapture();
2211}
2212
2213//
2214// Paint event translation
2215//
2216bool QETWidget::translatePaintEvent(const QMSG &qmsg)
2217{
2218 if (!isWindow() && testAttribute(Qt::WA_NativeWindow))
2219 Q_ASSERT(internalWinId());
2220
2221 HPS displayPS = qt_display_ps();
2222
2223 // Since we don't use WS_CLIPSIBLINGS and WS_CLIPCHILDREN bits (see
2224 // qwidget_pm.cpp), we have to validate areas that intersect with our
2225 // children and siblings, taking their clip regions into account.
2226 d_func()->validateObstacles();
2227
2228 Q_ASSERT(testAttribute(Qt::WA_WState_Created));
2229
2230 HRGN hrgn = GpiCreateRegion(displayPS, 0, NULL);
2231 LONG rc = WinQueryUpdateRegion(internalWinId(), hrgn);
2232 if (rc == RGN_ERROR) { // The update bounding rect is invalid
2233 GpiDestroyRegion(displayPS, hrgn);
2234 setAttribute(Qt::WA_PendingUpdate, false);
2235 return false;
2236 }
2237
2238 setAttribute(Qt::WA_PendingUpdate, false);
2239
2240 const QRegion dirtyInBackingStore(qt_dirtyRegion(this));
2241 // Make sure the invalidated region contains the region we're about to repaint.
2242 // BeginPaint will set the clip to the invalidated region and it is impossible
2243 // to enlarge it afterwards (only shrink it).
2244 if (!dirtyInBackingStore.isEmpty())
2245 WinInvalidateRegion(internalWinId(), dirtyInBackingStore.handle(height()), FALSE);
2246
2247 RECTL rcl;
2248 d_func()->hd = WinBeginPaint(internalWinId(), 0, &rcl);
2249
2250#if defined(QT_DEBUGMSGFLOW)
2251 qDebug() << " PAINT BEGIN:" << rcl << "hps:" << qStrHPS(d_func()->hd);
2252#endif
2253
2254 // it's possible that the update rectangle is empty
2255 if (rcl.xRight <= rcl.xLeft || rcl.yTop <= rcl.yBottom) {
2256 WinEndPaint(d_func()->hd);
2257 d_func()->hd = NULLHANDLE;
2258 GpiDestroyRegion(displayPS, hrgn);
2259 setAttribute(Qt::WA_PendingUpdate, false);
2260 return true;
2261 }
2262
2263 // flip y coordinate
2264 // note: right top point is exlusive in rcl
2265 QRect updRect(QPoint(rcl.xLeft, height() - rcl.yTop),
2266 QPoint(rcl.xRight - 1, height() - (rcl.yBottom + 1)));
2267
2268 // Mapping region from system to qt (32 bit) coordinate system.
2269 updRect.translate(data->wrect.topLeft());
2270#if defined(QT_DEBUGMSGFLOW)
2271 qDebug() << " PAINT updRect:" << updRect;
2272#endif
2273
2274 // @todo use hrgn here converted to QRegion?
2275 d_func()->syncBackingStore(updRect);
2276
2277 WinEndPaint(d_func()->hd);
2278 d_func()->hd = NULLHANDLE;
2279
2280#if defined(QT_DEBUGMSGFLOW)
2281 qDebug() << " PAINT END";
2282#endif
2283
2284 return true;
2285}
2286
2287//
2288// Window move and resize (configure) events
2289//
2290
2291bool QETWidget::translateConfigEvent(const QMSG &qmsg)
2292{
2293 if (!testAttribute(Qt::WA_WState_Created)) // in QWidget::create()
2294 return true;
2295 if (testAttribute(Qt::WA_WState_ConfigPending))
2296 return true;
2297 if (testAttribute(Qt::WA_DontShowOnScreen))
2298 return true;
2299
2300 // @todo there are other isWindow() checks below (same in Windows code).
2301 // Either they or this return statement are leftovers. The assertion may
2302 // tell the truth.
2303 Q_ASSERT(isWindow());
2304 if (!isWindow())
2305 return true;
2306
2307 // When the window is minimized, PM moves it to -32000,-32000 and resizes
2308 // to 48x50. We don't want these useless actions to be seen by Qt.
2309 if (isMinimized())
2310 return true;
2311
2312 setAttribute(Qt::WA_WState_ConfigPending); // set config flag
2313
2314 HWND fId = NULLHANDLE;
2315 ULONG fStyle = 0;
2316 if (isWindow()) {
2317 fId = d_func()->frameWinId();
2318 fStyle = WinQueryWindowULong(fId, QWL_STYLE);
2319 }
2320
2321 // Note: due to the vertical coordinate space flip in PM, WM_SIZE events may
2322 // also mean moving the widget in Qt coordinates
2323
2324 if (qmsg.msg == WM_MOVE || qmsg.msg == WM_SIZE) { // move event
2325 QPoint oldPos = data->crect.topLeft();
2326 SWP swp;
2327 if (isWindow()) {
2328 WinQueryWindowPos(fId, &swp);
2329 // flip y coordinate
2330 swp.y = QApplication::desktop()->height() - (swp.y + swp.cy);
2331 QTLWExtra *top = d_func()->topData();
2332 swp.x += top->frameStrut.left();
2333 swp.y += top->frameStrut.top();
2334 } else {
2335 WinQueryWindowPos(internalWinId(), &swp);
2336 // flip y coordinate
2337 swp.y = parentWidget()->height() - (swp.y + swp.cy);
2338 }
2339 QPoint newCPos(swp.x, swp.y);
2340 if (newCPos != oldPos) {
2341 data->crect.moveTopLeft(newCPos);
2342 if (isVisible()) {
2343 QMoveEvent e(newCPos, oldPos); // cpos (client position)
2344 QApplication::sendSpontaneousEvent(this, &e);
2345 } else {
2346 QMoveEvent *e = new QMoveEvent(newCPos, oldPos);
2347 QApplication::postEvent(this, e);
2348 }
2349 }
2350 }
2351 if (qmsg.msg == WM_SIZE) { // resize event
2352 QSize oldSize = data->crect.size();
2353 QSize newSize = QSize(SHORT1FROMMP(qmsg.mp2), SHORT2FROMMP(qmsg.mp2));
2354 data->crect.setSize(newSize);
2355 if (isWindow()) { // update title/icon text
2356 d_func()->createTLExtra();
2357 QString title;
2358 if ((fStyle & WS_MINIMIZED))
2359 title = windowIconText();
2360 if (title.isEmpty())
2361 title = windowTitle();
2362 if (!title.isEmpty())
2363 d_func()->setWindowTitle_helper(title);
2364 }
2365 if (oldSize != newSize) {
2366 // Spontaneous (external to Qt) WM_SIZE messages should occur only
2367 // on top-level widgets. If we get them for a non top-level widget,
2368 // the result will most likely be incorrect because widget masks will
2369 // not be properly processed (i.e. in the way it is done in
2370 // QWidget::setGeometry_sys() when the geometry is changed from
2371 // within Qt). So far, I see no need to support this (who will ever
2372 // need to move a non top-level window of a foreign process?).
2373 Q_ASSERT(isWindow());
2374 if (isVisible()) {
2375 QTLWExtra *tlwExtra = d_func()->maybeTopData();
2376 static bool slowResize = qgetenv("QT_SLOW_TOPLEVEL_RESIZE").toInt();
2377 const bool hasStaticContents = tlwExtra && tlwExtra->backingStore
2378 && tlwExtra->backingStore->hasStaticContents();
2379 // If we have a backing store with static contents, we have to disable the top-level
2380 // resize optimization in order to get invalidated regions for resized widgets.
2381 // The optimization discards all invalidateBuffer() calls since we're going to
2382 // repaint everything anyways, but that's not the case with static contents.
2383 if (!slowResize && tlwExtra && !hasStaticContents)
2384 tlwExtra->inTopLevelResize = true;
2385 QResizeEvent e(newSize, oldSize);
2386 QApplication::sendSpontaneousEvent(this, &e);
2387 if (d_func()->paintOnScreen()) {
2388 QRegion updateRegion(rect());
2389 if (testAttribute(Qt::WA_StaticContents))
2390 updateRegion -= QRect(0, 0, oldSize.width(), oldSize.height());
2391 // syncBackingStore() should have already flushed the widget
2392 // contents to the screen, so no need to redraw the exposed
2393 // areas in WM_PAINT once more
2394 d_func()->syncBackingStore(updateRegion);
2395 WinValidateRegion(internalWinId(),
2396 updateRegion.handle(newSize.height()), FALSE);
2397 } else {
2398 d_func()->syncBackingStore();
2399 // see above
2400 RECTL rcl = { 0, 0, newSize.width(), newSize.height() };
2401 WinValidateRect(internalWinId(), &rcl, FALSE);
2402 }
2403 if (!slowResize && tlwExtra)
2404 tlwExtra->inTopLevelResize = false;
2405 } else {
2406 QResizeEvent *e = new QResizeEvent(newSize, oldSize);
2407 QApplication::postEvent(this, e);
2408 }
2409 }
2410 }
2411 setAttribute(Qt::WA_WState_ConfigPending, false); // clear config flag
2412 return true;
2413}
2414
2415//
2416// Close window event translation.
2417//
2418// This class is a friend of QApplication because it needs to emit the
2419// lastWindowClosed() signal when the last top level widget is closed.
2420//
2421
2422bool QETWidget::translateCloseEvent(const QMSG &)
2423{
2424 return d_func()->close_helper(QWidgetPrivate::CloseWithSpontaneousEvent);
2425}
2426
2427/*****************************************************************************
2428 PM session management
2429 *****************************************************************************/
2430
2431#if !defined(QT_NO_SESSIONMANAGER)
2432
2433bool QApplicationPrivate::canQuit()
2434{
2435#if defined (DEBUG_SESSIONMANAGER)
2436 qDebug("QApplicationPrivate::canQuit: sm_smActive %d,"
2437 "qt_about_to_destroy_wnd %d, sm_gracefulShutdown %d, sm_cancel %d",
2438 sm_smActive, qt_about_to_destroy_wnd,
2439 sm_gracefulShutdown, sm_cancel);
2440#endif
2441
2442 bool quit = false;
2443
2444 // We can get multiple WM_QUIT messages while the "session termination
2445 // procedure" (i.e. the QApplication::commitData() call) is still in
2446 // progress. Ignore them.
2447 if (!sm_smActive) {
2448 if (sm_gracefulShutdown) {
2449 // this is WM_QUIT after WM_SAVEAPPLICATION (either posted by the OS
2450 // or by ourselves), confirm the quit depending on what the user wants
2451 sm_quitSkipped = false;
2452 quit = !sm_cancel;
2453 if (sm_cancel) {
2454 // the shutdown has been canceled, reset the flag to let the
2455 // graceful shutdown happen again later
2456 sm_gracefulShutdown = false;
2457 }
2458 } else {
2459 // sm_gracefulShutdown is false, so allowsInteraction() and friends
2460 // will return FALSE during commitData() (assuming that WM_QUIT w/o
2461 // WM_SAVEAPPLICATION is an emergency termination)
2462 sm_smActive = true;
2463 sm_blockUserInput = true; // prevent user-interaction outside interaction windows
2464 sm_cancel = false;
2465 if (qt_session_manager_self)
2466 qApp->commitData(*qt_session_manager_self);
2467 sm_smActive = false;
2468 quit = true; // ignore sm_cancel
2469 }
2470 } else {
2471 // if this is a WM_QUIT received during WM_SAVEAPPLICATION handling,
2472 // remember we've skipped (refused) it
2473 if (sm_gracefulShutdown)
2474 sm_quitSkipped = true;
2475 }
2476
2477#if defined (DEBUG_SESSIONMANAGER)
2478 qDebug("QApplicationPrivate::canQuit: returns %d", quit);
2479#endif
2480
2481 return quit;
2482}
2483
2484bool QSessionManager::allowsInteraction()
2485{
2486 // Allow interation only when the system is being normally shutdown
2487 // and informs us using WM_SAVEAPPLICATION. When we receive WM_QUIT directly
2488 // (so sm_gracefulShutdown is false), interaction is disallowed.
2489 if (sm_smActive && sm_gracefulShutdown) {
2490 sm_blockUserInput = false;
2491 return true;
2492 }
2493
2494 return false;
2495}
2496
2497bool QSessionManager::allowsErrorInteraction()
2498{
2499 // Allow interation only when the system is being normally shutdown
2500 // and informs us using WM_SAVEAPPLICATION. When we receive WM_QUIT directly
2501 // (so sm_gracefulShutdown is false), interaction is disallowed.
2502 if (sm_smActive && sm_gracefulShutdown) {
2503 sm_blockUserInput = false;
2504 return true;
2505 }
2506
2507 return false;
2508}
2509
2510void QSessionManager::release()
2511{
2512 if (sm_smActive && sm_gracefulShutdown)
2513 sm_blockUserInput = true;
2514}
2515
2516void QSessionManager::cancel()
2517{
2518 if (sm_smActive && sm_gracefulShutdown)
2519 sm_cancel = true;
2520}
2521
2522#endif // QT_NO_SESSIONMANAGER
2523
2524/*****************************************************************************
2525 PM struct/message debug helpers
2526 *****************************************************************************/
2527
2528/*!
2529 Returns a QWidget pointer or 0 if there is no widget corresponding to the
2530 given HWND. As opposed to QWidget::find(), correctly handles WC_FRAME
2531 windows created for top level widgets. Used for debugging.
2532 */
2533QWidget *qWidgetFromHWND(HWND hwnd)
2534{
2535 char buf[10];
2536 if (WinQueryClassName(hwnd, sizeof(buf), buf)) {
2537 if (!strcmp(buf, "#1")) // WC_FRAME
2538 hwnd = WinWindowFromID(hwnd, FID_CLIENT);
2539 return QWidget::find(hwnd);
2540 }
2541 return 0;
2542}
2543
2544/*!
2545 \internal
2546
2547 Returns a human readable widget name in the form "class/name". Used for
2548 debugging.
2549 */
2550QDbgStr qWidgetName(QWidget *w)
2551{
2552 if (w)
2553 return QString()
2554 .sprintf("%s.%s", w->metaObject()->className(),
2555 w->objectName().isEmpty() ? "<noname>" :
2556 w->objectName().toUtf8().constData());
2557 return QString(QLatin1String("<no-widget>"));
2558}
2559
2560typedef QLatin1String QCStr;
2561
2562#define myDefFlagEx(var,fl,varstr,flstr) if (var & fl) { \
2563 if (!varstr.isEmpty()) varstr += QCStr("|"); varstr += QCStr(flstr); \
2564} else do {} while(0)
2565
2566#define myDefFlag(var,fl,varstr) myDefFlagEx(var,fl,varstr,#fl)
2567#define myDefFlagCut(var,fl,varstr,pos) myDefFlagEx(var,fl,varstr,#fl + pos)
2568
2569QDbgStr qStrHWND(HWND hwnd)
2570{
2571 return QString().sprintf("%08lX/", hwnd) +
2572 qWidgetName(qWidgetFromHWND(hwnd));
2573}
2574
2575QDbgStr qStrHPS(HPS hps)
2576{
2577 return QString().sprintf("%08lX", hps);
2578}
2579
2580QDbgStr qStrHPOINTER(HPOINTER hptr)
2581{
2582 return QString().sprintf("%08lX", hptr);
2583}
2584
2585QDbgStr qStrHRGN(HRGN hrgn)
2586{
2587 return QString().sprintf("%08lX", hrgn);
2588}
2589
2590QDbgStr qStrQMSG(const QMSG &qmsg)
2591{
2592 QString str;
2593
2594 #define myCaseBegin(a) case a: { \
2595 str = QString().sprintf(#a ": hwnd %08lX.", qmsg.hwnd); \
2596 str += qWidgetName(qWidgetFromHWND(qmsg.hwnd));
2597 #define myCaseEnd() }
2598
2599 switch (qmsg.msg) {
2600
2601 myCaseBegin(WM_CHAR)
2602 USHORT fl = SHORT1FROMMP(qmsg.mp1);
2603 UCHAR repeat = CHAR3FROMMP(qmsg.mp1);
2604 UCHAR scan = CHAR4FROMMP(qmsg.mp1);
2605 USHORT ch = SHORT1FROMMP(qmsg.mp2);
2606 USHORT vk = SHORT2FROMMP(qmsg.mp2);
2607 str += QString().
2608 sprintf(" rep %02d scan %02X ch %04X (%s) vk %04X",
2609 repeat, scan, ch, (ch > 32 && ch < 254) ?
2610 qPrintable(QString::fromLocal8Bit((char *)&ch, 1)) :
2611 qPrintable(QString(QChar(QLatin1Char(' ')))), vk);
2612 QString flstr;
2613 myDefFlagEx(fl, KC_CHAR, flstr, "CHAR");
2614 myDefFlagEx(fl, KC_VIRTUALKEY, flstr, "VIRT");
2615 myDefFlagEx(fl, KC_SCANCODE, flstr, "SCAN");
2616 myDefFlagEx(fl, KC_SHIFT, flstr, "SHIFT");
2617 myDefFlagEx(fl, KC_CTRL, flstr, "CTRL");
2618 myDefFlagEx(fl, KC_ALT, flstr, "ALT");
2619 myDefFlagEx(fl, KC_KEYUP, flstr, "UP");
2620 myDefFlagEx(fl, KC_PREVDOWN, flstr, "PREVDWN");
2621 myDefFlagEx(fl, KC_LONEKEY, flstr, "LONE");
2622 myDefFlagEx(fl, KC_DEADKEY, flstr, "DEAD");
2623 myDefFlagEx(fl, KC_COMPOSITE, flstr, "COMP");
2624 myDefFlagEx(fl, KC_INVALIDCOMP, flstr, "INVCMP");
2625 myDefFlagEx(fl, KC_TOGGLE, flstr, "TGGL");
2626 myDefFlagEx(fl, KC_INVALIDCHAR, flstr, "INVCHR");
2627 str += QString().sprintf(" KC(%04X,", fl) + flstr + QCStr(")");
2628 break;
2629 myCaseEnd()
2630
2631 myCaseBegin(WM_KBDLAYERCHANGED)
2632 str += QString().sprintf(" mp1 %p mp2 %p", qmsg.mp1, qmsg.mp2);
2633 break;
2634 myCaseEnd()
2635
2636 myCaseBegin(WM_PAINT)
2637 break;
2638 myCaseEnd()
2639
2640 myCaseBegin(WM_SIZE)
2641 str += QString().
2642 sprintf(" old (%hd,%hd) new (%hd,%hd)",
2643 SHORT1FROMMP(qmsg.mp1), SHORT2FROMMP(qmsg.mp1),
2644 SHORT1FROMMP(qmsg.mp2), SHORT2FROMMP(qmsg.mp2));
2645 SWP swp;
2646 WinQueryWindowPos(qmsg.hwnd, &swp);
2647 str += QCStr(" ") + qStrSWP(swp);
2648 HWND p = WinQueryWindow(qmsg.hwnd, QW_PARENT);
2649 if (p != NULLHANDLE && p != WinQueryDesktopWindow(0, 0)) {
2650 WinQueryWindowPos(p, &swp);
2651 str += QCStr(" p ") + qStrSWP(swp);
2652 }
2653 break;
2654 myCaseEnd()
2655
2656 myCaseBegin(WM_MOVE)
2657 SWP swp;
2658 WinQueryWindowPos(qmsg.hwnd, &swp);
2659 str += QCStr(" ") + qStrSWP(swp);
2660 HWND p = WinQueryWindow(qmsg.hwnd, QW_PARENT);
2661 if (p != NULLHANDLE && p != WinQueryDesktopWindow(0, 0)) {
2662 WinQueryWindowPos(p, &swp);
2663 str += QCStr(" p ") + qStrSWP(swp);
2664 }
2665 break;
2666 myCaseEnd()
2667
2668 myCaseBegin(WM_WINDOWPOSCHANGED)
2669 str += QCStr(" ") + qStrSWP(*((PSWP) qmsg.mp1));
2670 ULONG awp = LONGFROMMP(qmsg.mp2);
2671 QString awpstr;
2672 myDefFlagEx(awp, AWP_MINIMIZED, awpstr, "MIN");
2673 myDefFlagEx(awp, AWP_MAXIMIZED, awpstr, "MAX");
2674 myDefFlagEx(awp, AWP_RESTORED, awpstr, "REST");
2675 myDefFlagEx(awp, AWP_ACTIVATE, awpstr, "ACT");
2676 myDefFlagEx(awp, AWP_DEACTIVATE, awpstr, "DEACT");
2677 str += QCStr(" AWP(") + awpstr + QCStr(")");
2678 break;
2679 myCaseEnd()
2680
2681 myCaseBegin(WM_MINMAXFRAME)
2682 str += QCStr(" ") + qStrSWP(*((PSWP) qmsg.mp1));
2683 break;
2684 myCaseEnd()
2685
2686 myCaseBegin(WM_ACTIVATE)
2687 bool active = SHORT1FROMMP(qmsg.mp1);
2688 HWND hwnd = (HWND)qmsg.mp2;
2689 str += QCStr(" Active(") + QCStr(active ? "TRUE) " : "FALSE)");
2690 str += QString().sprintf(" hwndActive %08lX.", hwnd);
2691 str += qWidgetName(qWidgetFromHWND(hwnd));
2692 break;
2693 myCaseEnd()
2694
2695 myCaseBegin(WM_SETFOCUS)
2696 HWND hwnd = (HWND)qmsg.mp1;
2697 bool focus = SHORT1FROMMP(qmsg.mp2);
2698 str += QCStr(" Focus(") + QCStr(focus ? "TRUE) " : "FALSE)");
2699 str += QString().sprintf(" hwndFocus %08lX.", hwnd);
2700 str += qWidgetName(qWidgetFromHWND(hwnd));
2701 break;
2702 myCaseEnd()
2703
2704 myCaseBegin(WM_FOCUSCHANGE)
2705 HWND hwnd = (HWND)qmsg.mp1;
2706 bool focus = SHORT1FROMMP(qmsg.mp2);
2707 bool fl = SHORT2FROMMP(qmsg.mp2);
2708 QString flstr;
2709 myDefFlagEx(fl, FC_NOSETFOCUS, flstr, "NOSETFCS");
2710 myDefFlagEx(fl, FC_NOLOSEFOCUS, flstr, "NOLOSEFCS");
2711 myDefFlagEx(fl, FC_NOSETACTIVE, flstr, "NOSETACT");
2712 myDefFlagEx(fl, FC_NOLOSEACTIVE, flstr, "NOLOSEACT");
2713 myDefFlagEx(fl, FC_NOSETSELECTION, flstr, "NOSETSEL");
2714 myDefFlagEx(fl, FC_NOLOSESELECTION, flstr, "NOSETSEL");
2715 str += QCStr(" Focus(") + QCStr(focus ? "TRUE) " : "FALSE)");
2716 str += QString().sprintf(" hwndFocus %08lX.", hwnd);
2717 str += qWidgetName(qWidgetFromHWND(hwnd));
2718 str += QCStr(" FC(") + flstr + QCStr(")");
2719 break;
2720 myCaseEnd()
2721
2722 default:
2723#if 0
2724 if (qmsg.msg == WM_QUERYICON)
2725 break;
2726 str = QString().sprintf("WM_%04lX: hwnd %08lX.", qmsg.msg, qmsg.hwnd);
2727 str += qWidgetName(qWidgetFromHWND(qmsg.hwnd));
2728#endif
2729 break;
2730 }
2731
2732 return str;
2733
2734 #undef myCaseEnd
2735 #undef myCaseBegin
2736}
2737
2738QDbgStr qStrRECTL(const RECTL &rcl)
2739{
2740 return QString().sprintf("RECTL(%ld,%ld %ld,%ld)",
2741 rcl.xLeft, rcl.yBottom, rcl.xRight, rcl.yTop);
2742}
2743
2744QDbgStr qStrSWP(const SWP &swp)
2745{
2746 QString fl;
2747 myDefFlagEx(swp.fl, SWP_SIZE, fl, "SIZE");
2748 myDefFlagEx(swp.fl, SWP_MOVE, fl, "MOVE");
2749 myDefFlagEx(swp.fl, SWP_ZORDER, fl, "ZORD");
2750 myDefFlagEx(swp.fl, SWP_SHOW, fl, "SHOW");
2751 myDefFlagEx(swp.fl, SWP_HIDE, fl, "HIDE");
2752 myDefFlagEx(swp.fl, SWP_NOREDRAW, fl, "NORDR");
2753 myDefFlagEx(swp.fl, SWP_NOADJUST, fl, "NOADJ");
2754 myDefFlagEx(swp.fl, SWP_ACTIVATE, fl, "ACT");
2755 myDefFlagEx(swp.fl, SWP_DEACTIVATE, fl, "DEACT");
2756 myDefFlagEx(swp.fl, SWP_EXTSTATECHANGE, fl, "EXTST");
2757 myDefFlagEx(swp.fl, SWP_MINIMIZE, fl, "MIN");
2758 myDefFlagEx(swp.fl, SWP_MAXIMIZE, fl, "MAX");
2759 myDefFlagEx(swp.fl, SWP_RESTORE, fl, "REST");
2760 myDefFlagEx(swp.fl, SWP_FOCUSACTIVATE, fl, "FCSACT");
2761 myDefFlagEx(swp.fl, SWP_FOCUSDEACTIVATE, fl, "FCSDEACT");
2762 myDefFlagEx(swp.fl, SWP_NOAUTOCLOSE, fl, "NOACLOSE");
2763
2764 return QString().sprintf("SWP(%ld,%ld %ldx%ld %08lX ",
2765 swp.x, swp.y, swp.cx, swp.cy, swp.hwndInsertBehind) +
2766 fl + QLatin1String(")");
2767}
2768
2769#undef myDefFlagCut
2770#undef myDefFlag
2771#undef myDefFlagEx
2772
2773QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.