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

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

gui: OS/2: Added operator<<(QDebug debug,...) to debug HRGN hangles like "qDebug() << qDebugHRGN(hrgn)".

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