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

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

gui: DnD: Drag party code in progress.

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