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

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

gui: First DnD bits.

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