source: trunk/src/gui/kernel/qapplication_pm.cpp@ 564

Last change on this file since 564 was 564, checked in by Dmitry A. Kuminov, 15 years ago

general: Updated headers of OS/2 sources.

File size: 101.9 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** Copyright (C) 2009 netlabs.org. OS/2 parts.
8**
9** This file is part of the QtGui module of the Qt Toolkit.
10**
11** $QT_BEGIN_LICENSE:LGPL$
12** Commercial Usage
13** Licensees holding valid Qt Commercial licenses may use this file in
14** accordance with the Qt Commercial License Agreement provided with the
15** Software or, alternatively, in accordance with the terms contained in
16** a written agreement between you and Nokia.
17**
18** GNU Lesser General Public License Usage
19** Alternatively, this file may be used under the terms of the GNU Lesser
20** General Public License version 2.1 as published by the Free Software
21** Foundation and appearing in the file LICENSE.LGPL included in the
22** packaging of this file. Please review the following information to
23** ensure the GNU Lesser General Public License version 2.1 requirements
24** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25**
26** In addition, as a special exception, Nokia gives you certain additional
27** rights. These rights are described in the Nokia Qt LGPL Exception
28** version 1.1, included in the file LGPL_EXCEPTION.txt in this 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 have questions regarding the use of this file, please contact
39** Nokia at [email protected].
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.