source: trunk/src/gui/kernel/qwidget_pm.cpp

Last change on this file was 1108, checked in by Silvan Scherrer, 13 years ago

fix seWindowsIcon problem, reverted 1107 and did different

File size: 99.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 "qwidget.h"
49#include "qwidget_p.h"
50
51#include "qapplication.h"
52#include "qdesktopwidget.h"
53#include "qevent.h"
54
55#include "private/qapplication_p.h"
56#include "private/qbackingstore_p.h"
57#include "private/qwindowsurface_raster_p.h"
58#include "private/qwindowsurface_pm_p.h"
59
60//#define QT_DEBUGWINCREATEDESTROY
61//#define QT_DEBUGWIDGETMASK
62
63QT_BEGIN_NAMESPACE
64
65static QWidget *mouseGrb = 0;
66static QCursor *mouseGrbCur = 0;
67static QWidget *keyboardGrb = 0;
68
69// defined in qapplication_pm.cpp
70extern bool qt_nograb();
71extern MRESULT EXPENTRY QtWndProc(HWND, ULONG, MPARAM, MPARAM);
72extern PFNWP QtOldFrameProc;
73extern MRESULT EXPENTRY QtFrameProc(HWND, ULONG, MPARAM, MPARAM);
74extern PFNWP QtOldFrameCtlProcs[FID_HORZSCROLL - FID_SYSMENU + 1];
75extern MRESULT EXPENTRY QtFrameCtlProc(HWND, ULONG, MPARAM, MPARAM);
76
77#ifndef QT_PM_NATIVEWIDGETMASK
78#define qt_WinSetWindowPos WinSetWindowPos
79#endif
80
81#if !defined(QT_NO_SESSIONMANAGER)
82bool qt_about_to_destroy_wnd = false;
83#endif
84
85typedef QSet<QString> WinClassNameHash;
86Q_GLOBAL_STATIC(WinClassNameHash, winclassNames)
87
88// register window class
89static const QString qt_reg_winclass(QWidget *w)
90{
91 int flags = w->windowFlags();
92 int type = flags & Qt::WindowType_Mask;
93
94 QString cname;
95 ULONG style = 0;
96
97 if (type == Qt::Popup || type == Qt::ToolTip) {
98 cname = QLatin1String("QPopup");
99 style |= CS_SAVEBITS;
100 } else if (w->isWindow()) {
101 // this class is for top level widgets which are client HWNDs of a
102 // WC_FRAME parent HWND internally maintaned for them
103 cname = QLatin1String("QWindow");
104 } else {
105 cname = QLatin1String("QWidget");
106 }
107
108 if (winclassNames()->contains(cname)) // already registered in our list
109 return cname;
110
111 // QT_EXTRAWINDATASIZE is defined in qwindowdefs_pm.h
112 WinRegisterClass(0, cname.toLatin1(), QtWndProc, style, QT_EXTRAWINDATASIZE);
113
114 winclassNames()->insert(cname);
115 return cname;
116
117 // Note: there is no need to unregister private window classes registered by
118 // this function -- it is done automatically upon process termination.
119}
120
121#ifdef QT_PM_NATIVEWIDGETMASK
122
123/*!
124 \internal
125
126 Extended version of WinQueryClipRegion(). If the given window has a clip
127 region, the given region will receive a copy of the clip region clipped to
128 the current window rectangle. If there is no clip region, the given region
129 will contain only the window rectangle on return.
130 */
131void qt_WinQueryClipRegionOrRect(HWND hwnd, HRGN hrgn)
132{
133 RECTL rcl;
134 WinQueryWindowRect(hwnd, &rcl);
135
136 HPS hps = qt_display_ps();
137 GpiSetRegion(hps, hrgn, 1, &rcl);
138 if (WinQueryClipRegion(hwnd, 0) != QCRGN_NO_CLIP_REGION) {
139 HRGN hrgnTemp = GpiCreateRegion(hps, 0, NULL);
140 WinQueryClipRegion(hwnd, hrgnTemp);
141 GpiCombineRegion(hps, hrgn, hrgnTemp, hrgn, CRGN_AND);
142 GpiDestroyRegion(hps, hrgnTemp);
143 }
144}
145
146/*!
147 \internal
148
149 Extended version of WinInvalidateRegion(): invalidates the specified region
150 of the given window and regions of children from \a hwndFrom to \a hwndTo
151 if they intersect with the invalid region. If either of child window
152 handles is NULLHANDLE, children are not invalidated at all. Also, HWND_TOP
153 can be used as \a hwndFrom, HWND_BOTTOM \a can be used as \a hwndTo.
154 */
155static BOOL qt_WinInvalidateRegionEx(HWND hwnd, HRGN hrgn,
156 HWND hwndFrom, HWND hwndTo)
157{
158#if defined(QT_DEBUGWIDGETMASK)
159 qDebug() << "qt_WinInvalidateRegionEx: hwnd" << qDebugHWND(hwnd)
160 << "hwndFrom" << qDebugFmtHex(hwndFrom)
161 << "hwndTo" << qDebugFmtHex(hwndTo);
162#endif
163
164 if (hwndFrom == HWND_TOP)
165 hwndFrom = WinQueryWindow(hwnd, QW_TOP);
166 if (hwndTo == HWND_BOTTOM)
167 hwndTo = WinQueryWindow(hwnd, QW_BOTTOM);
168
169 if (hwndFrom == 0 || hwndTo == 0)
170 return WinInvalidateRegion(hwnd, hrgn, FALSE);
171
172 if (WinQueryWindow(hwndFrom, QW_PARENT) != hwnd ||
173 WinQueryWindow(hwndTo, QW_PARENT) != hwnd)
174 return FALSE;
175
176 HPS hps = qt_display_ps();
177
178 SWP swp;
179 HWND child = hwndFrom;
180 HRGN hrgnChild = GpiCreateRegion(hps, 0, NULL);
181 HRGN hrgnInv = GpiCreateRegion(hps, 0, NULL);
182 GpiCombineRegion(hps, hrgnInv, hrgn, 0, CRGN_COPY);
183
184 LONG cmplx = RGN_RECT;
185
186 while (child) {
187 WinQueryWindowPos(child, &swp);
188#if defined(QT_DEBUGWIDGETMASK)
189 qDebug() << " child" << qDebugHWND(child) << "fl" << qDebugFmtHex(swp.fl);
190#endif
191 // proceed only if not hidden
192 if (swp.fl & SWP_SHOW) {
193 // get sibling's bounds (clip region or rect)
194 qt_WinQueryClipRegionOrRect(child, hrgnChild);
195 // translate the region to the parent's coordinate system
196 POINTL ptl = { swp.x, swp.y };
197 GpiOffsetRegion(hps, hrgnChild, &ptl);
198 // intersect the child's region with the invalid one
199 // and invalidate if not NULL
200 cmplx = GpiCombineRegion(hps, hrgnChild, hrgnChild, hrgnInv,
201 CRGN_AND);
202 if (cmplx != RGN_NULL) {
203 POINTL ptl2 = { -swp.x, -swp.y };
204 GpiOffsetRegion(hps, hrgnChild, &ptl2);
205 WinInvalidateRegion(child, hrgnChild, TRUE);
206 GpiOffsetRegion(hps, hrgnChild, &ptl);
207 // substract the invalidated area from the widget's region
208 // (no need to invalidate it any more)
209 cmplx = GpiCombineRegion(hps, hrgnInv, hrgnInv, hrgnChild,
210 CRGN_DIFF);
211#if defined(QT_DEBUGWIDGETMASK)
212 qDebug(" processed");
213#endif
214 // finish if nothing left
215 if (cmplx == RGN_NULL)
216 break;
217 }
218 }
219 // iterate to the next window (below)
220 if (child == hwndTo)
221 break;
222 child = WinQueryWindow(child, QW_NEXT);
223 }
224
225 BOOL ok = (cmplx == RGN_NULL) || (child == hwndTo);
226
227 if (ok) {
228 // invalidate what's left invalid after substracting children
229 WinInvalidateRegion(hwnd, hrgnInv, FALSE);
230 }
231
232 GpiDestroyRegion(hps, hrgnInv);
233 GpiDestroyRegion(hps, hrgnChild);
234
235 return ok;
236}
237
238/*!
239 \internal
240
241 \enum PWOFlags
242 \relates QWidget
243
244 Flags for qt_WinProcessWindowObstacles() that define which relative windows
245 to process.
246
247 \warning This enum is only available on OS/2.
248
249 \value PWO_Children Child windows.
250 \value PWO_Siblings Sibling windows.
251 \value PWO_Ancestors Siblings of the parent window and all ancestors.
252 \value PWO_Screen Screen area.
253 \value PWO_TopLevel All top level windows.
254 \value PWO_Default Default value suitable for most paint operations
255 (equivalent to specifying all flags except PWO_TopLevel).
256*/
257
258/*!
259 \internal
260
261 \fn LONG qt_WinProcessWindowObstacles(HWND hwnd, RECTL *prcl, HRGN hrgn,
262 LONG op, LONG flags)
263 \relates QWidget
264
265 Collects all relative PM windows intersecting with the given \a hwnd and
266 placed above it in Z-order. The area of interest is limited to the \a prcl
267 rectangle (in window coordinates) which may be \c NULL to indicate the whole
268 window. If \a hrgn is not \c NULL, all found obstacles are combined with
269 the given region using the \a op operation (\c CRGN_*); otherwise they are
270 directly validated on the window. The scope of relativeness is defined by
271 the \a flags argument which is one or more PWOFlags OR-ed together.
272
273 Returns the complexity of the combined region (only when \a hrgn is not
274 \c NULL). Note that if no combining occurs (e.g. no relatives in the
275 requested scope), the return value is \c RGN_NULL regardless of the original
276 complexity of \a hrgn.
277
278 This function is especially useful for 3rd-party applications that embed
279 themselves into a Qt application by painting directly on a PM window of a Qt
280 widget (that gets created when QWidget::winId() is called), bypassing Qt
281 completely. An example of such an application would be a video player that
282 renders and paints frames in an external non-Qt thread or process.
283
284 Qt does not use the \c WS_CLIPSIBLINGS and \c WS_CLIPCHILDREN flags when
285 creating PM windows for non-top-level widgets (because that would break
286 support for non-rectangular widgets due to a bug in PM) and this function
287 acts as a functional replacement for these flags. Any application that
288 paints on the PM window of the Qt widget directy must call
289 qt_WinProcessWindowObstacles() to correctly clip out portions of the widget
290 covered by other widgets placed above it in Z-order and avoid unexpected
291 painting over these widgets.
292
293 \snippet doc/src/snippets/code/src_gui_painting_embedded_pm.cpp 0
294
295 \warning This function is only available on OS/2.
296 */
297LONG APIENTRY qt_WinProcessWindowObstacles(HWND hwnd, RECTL *prcl, HRGN hrgn,
298 LONG op, LONG flags)
299{
300 Q_ASSERT(hwnd);
301
302 if (flags == 0)
303 flags = PWO_Children | PWO_Siblings | PWO_Ancestors | PWO_Screen;
304
305 HPS displayPS = qt_display_ps();
306
307#if defined(QT_DEBUGWIDGETMASK)
308 qDebug() << "qt_WinProcessWindowObstacles: hwnd" << qDebugHWND(hwnd)
309 << "prcl" << prcl << "hrgn" << qDebugFmtHex(hrgn)
310 << "op" << op << "flags" << qDebugFmtHex(flags);
311#endif
312
313 SWP swpSelf;
314 WinQueryWindowPos(hwnd, &swpSelf);
315
316 RECTL rclSelf = { 0, 0, swpSelf.cx, swpSelf.cy };
317 if (prcl)
318 rclSelf = *prcl;
319
320 HRGN whrgn = GpiCreateRegion(displayPS, 0, NULL);
321
322 LONG cmplx = RGN_NULL;
323 HWND relative;
324 SWP swp;
325
326 bool cmplxChanged = false;
327
328 // first, process areas placed outside the screen bounds
329 if (flags & PWO_Screen) {
330 RECTL rclScr = { 0, 0, qt_display_width(), qt_display_height() };
331 WinMapWindowPoints(HWND_DESKTOP, hwnd, (PPOINTL) &rclScr, 2);
332 // rough check of whether some window part is outside bounds
333 if (rclSelf.xLeft < rclScr.xLeft ||
334 rclSelf.yBottom < rclScr.yBottom ||
335 rclSelf.xRight > rclScr.xRight ||
336 rclSelf.yTop > rclScr.yTop) {
337 GpiSetRegion(displayPS, whrgn, 1, &rclSelf);
338 HRGN hrgnScr = GpiCreateRegion(displayPS, 1, &rclScr);
339 // substract the screen region from this window's region
340 // to get parts placed outside
341 GpiCombineRegion(displayPS, whrgn, whrgn, hrgnScr, CRGN_DIFF);
342 GpiDestroyRegion(displayPS, hrgnScr);
343 // process the region
344 if (hrgn != NULLHANDLE) {
345 cmplx = GpiCombineRegion(displayPS, hrgn, hrgn, whrgn, op);
346 cmplxChanged = true;
347 } else {
348 WinValidateRegion(hwnd, whrgn, FALSE);
349 }
350#if defined(QT_DEBUGWIDGETMASK)
351 qDebug(" collected areas outside screen bounds");
352#endif
353 }
354 }
355
356 // next, go through all children (in z-order)
357 if (flags & PWO_Children) {
358 relative = WinQueryWindow(hwnd, QW_BOTTOM);
359 if (relative != NULLHANDLE) {
360 for (; relative != HWND_TOP; relative = swp.hwndInsertBehind) {
361 WinQueryWindowPos(relative, &swp);
362#if defined(QT_DEBUGWIDGETMASK)
363 qDebug() << " child" << qDebugHWND(relative)
364 << "fl" << qDebugFmtHex(swp.fl);
365#endif
366 // skip if hidden
367 if (!(swp.fl & SWP_SHOW))
368 continue;
369 // rough check for intersection
370 if (swp.x >= rclSelf.xRight || swp.y >= rclSelf.yTop ||
371 swp.x + swp.cx <= rclSelf.xLeft ||
372 swp.y + swp.cy <= rclSelf.yBottom)
373 continue;
374 // get the bounds (clip region or rect)
375 qt_WinQueryClipRegionOrRect(relative, whrgn);
376 // translate the region to this window's coordinate system
377 POINTL ptl = { swp.x, swp.y };
378 GpiOffsetRegion(displayPS, whrgn, &ptl);
379 // process the region
380 if (hrgn != NULLHANDLE) {
381 cmplx = GpiCombineRegion(displayPS, hrgn, hrgn, whrgn, op);
382 cmplxChanged = true;
383 } else {
384 WinValidateRegion(hwnd, whrgn, FALSE);
385 }
386#if defined(QT_DEBUGWIDGETMASK)
387 qDebug(" collected");
388#endif
389 }
390 }
391 }
392
393 HWND desktop = WinQueryDesktopWindow(0, 0);
394 HWND parent = WinQueryWindow(hwnd, QW_PARENT);
395
396 // next, go through all siblings placed above (in z-order),
397 // but only if they are not top-level windows (that cannot be
398 // non-rectangular and thus are always correctly clipped by the system)
399 if ((flags & PWO_Siblings) && parent != desktop) {
400 for (relative = swpSelf.hwndInsertBehind;
401 relative != HWND_TOP; relative = swp.hwndInsertBehind) {
402 WinQueryWindowPos(relative, &swp);
403#if defined(QT_DEBUGWIDGETMASK)
404 qDebug() << " sibling" << qDebugHWND(relative)
405 << "fl" << qDebugFmtHex(swp.fl);
406#endif
407 // skip if hidden
408 if (!(swp.fl & SWP_SHOW))
409 continue;
410 // rough check for intersection
411 if (swp.x >= swpSelf.x + rclSelf.xRight ||
412 swp.y >= swpSelf.y + rclSelf.yTop ||
413 swp.x + swp.cx <= swpSelf.x + rclSelf.xLeft ||
414 swp.y + swp.cy <= swpSelf.y + rclSelf.yBottom)
415 continue;
416 // get the bounds (clip region or rect)
417 qt_WinQueryClipRegionOrRect(relative, whrgn);
418 // translate the region to this window's coordinate system
419 POINTL ptl = { swp.x - swpSelf.x, swp.y - swpSelf.y };
420 GpiOffsetRegion(displayPS, whrgn, &ptl);
421 // process the region
422 if (hrgn != NULLHANDLE) {
423 cmplx = GpiCombineRegion(displayPS, hrgn, hrgn, whrgn, op);
424 cmplxChanged = true;
425 } else {
426 WinValidateRegion(hwnd, whrgn, FALSE);
427 }
428#if defined(QT_DEBUGWIDGETMASK)
429 qDebug(" collected");
430#endif
431 }
432 }
433
434 // last, go through all siblings of our parent and its ancestors
435 // placed above (in z-order)
436 if (flags & PWO_Ancestors) {
437 POINTL delta = { swpSelf.x, swpSelf.y };
438 while (parent != desktop) {
439 HWND grandParent = WinQueryWindow(parent, QW_PARENT);
440 if (!(flags & PWO_TopLevel)) {
441 // When PWO_TopLevel is not specified, top-level windows
442 // (children of the desktop) are not processed. It makes sense
443 // when qt_WinProcessWindowObstacles() is used to clip out
444 // overlying windows during regular paint operations (WM_PAINT
445 // processing or drawing in a window directly through
446 // WinGetPS()): in this case, top-level windows are always
447 // correctly clipped out by the system (because they cannot be
448 // non-rectangular).
449 if (grandParent == desktop)
450 break;
451 }
452
453 WinQueryWindowPos(parent, &swp);
454#if defined(QT_DEBUGWIDGETMASK)
455 qDebug() << " parent" << qDebugHWND(parent)
456 << "fl" << qDebugFmtHex(swp.fl);
457#endif
458 delta.x += swp.x;
459 delta.y += swp.y;
460 for (relative = swp.hwndInsertBehind;
461 relative != HWND_TOP; relative = swp.hwndInsertBehind) {
462 WinQueryWindowPos(relative, &swp);
463#if defined(QT_DEBUGWIDGETMASK)
464 qDebug() << " ancestor" << qDebugHWND(relative)
465 << "fl" << qDebugFmtHex(swp.fl);
466#endif
467 // skip if hidden
468 if (!(swp.fl & SWP_SHOW))
469 continue;
470 // rough check for intersection
471 if (swp.x - delta.x >= rclSelf.xRight ||
472 swp.y - delta.y >= rclSelf.yTop ||
473 swp.x - delta.x + swp.cx <= rclSelf.xLeft ||
474 swp.y - delta.y + swp.cy <= rclSelf.yBottom)
475 continue;
476 // get the bounds (clip region or rect)
477 qt_WinQueryClipRegionOrRect(relative, whrgn);
478 // translate the region to this window's coordinate system
479 POINTL ptl = { swp.x - delta.x, swp.y - delta.y };
480 GpiOffsetRegion(displayPS, whrgn, &ptl);
481 // process the region
482 if (hrgn != NULLHANDLE) {
483 cmplx = GpiCombineRegion(displayPS, hrgn, hrgn, whrgn, op);
484 cmplxChanged = true;
485 } else {
486 WinValidateRegion(hwnd, whrgn, FALSE);
487 }
488#if defined(QT_DEBUGWIDGETMASK)
489 qDebug(" collected");
490#endif
491 }
492 parent = grandParent;
493 }
494 }
495
496 GpiDestroyRegion(displayPS, whrgn);
497
498 if (hrgn != NULLHANDLE && cmplxChanged == false) {
499 // make sure to return the original complexity of the region
500 RECTL rclDummy;
501 cmplx = GpiQueryRegionBox(displayPS, hrgn, &rclDummy);
502 }
503
504 return cmplx;
505}
506
507/*!
508 \internal
509
510 Partial reimplementation of the WinSetWindowPos() API that obeys window clip
511 regions. Currently supported flags are SWP_ZORDER, SWP_SHOW, SWP_HIDE,
512 SWP_ACTIVATE, SWP_MOVE, SWP_SIZE and SWP_NOREDRAW; other flags should no be
513 used.
514
515 Note that if the above restrictions are not met or if the given window is a
516 top-level window, this function acts exactly like the original
517 WinSetWindowPos() function.
518 */
519static BOOL qt_WinSetWindowPos(HWND hwnd, HWND hwndInsertBehind,
520 LONG x, LONG y, LONG cx, LONG cy,
521 ULONG fl)
522{
523 // @todo We need to send WM_VRNENABLED/WM_VRNDISABLED to all affected
524 // windows as it turned out that WinSetWindowPos() called with SWP_NOREDRAW
525 // does not do that. The problem here is that it's unknown how to determine
526 // which windows asked to send them visible region change notifications with
527 // WinSetVisibleRegionNotify(). This is the main reason why we do not define
528 // QT_PM_NATIVEWIDGETMASK by default. Not sending those notifications breaks
529 // painting to widgets that depend on this information, e.g. all direct
530 // painting modes using DIVE. Note that this only affects cases when native
531 // windows are created for child widgets. Normally, this is not the case,
532 // all masking is done by Qt and this code is not involved at all, so
533 // disabling it doesn't affect applications.
534
535#if defined(QT_DEBUGWIDGETMASK)
536 qDebug() << "qt_WinSetWindowPos: hwnd" << qDebugHWND(hwnd)
537 << "behind" << qDebugFmtHex(hwndInsertBehind)
538 << x << y << cx << cy
539 << "fl" << qDebugFmtHex(fl);
540#endif
541
542 HWND desktop = WinQueryDesktopWindow(0, 0);
543
544 Q_ASSERT(((fl & ~(SWP_ZORDER | SWP_SHOW | SWP_HIDE | SWP_ACTIVATE |
545 SWP_MOVE | SWP_SIZE | SWP_NOREDRAW)) == 0) ||
546 hwnd == desktop || WinQueryWindow(hwnd, QW_PARENT) == desktop);
547
548 if ((fl & ~(SWP_ZORDER | SWP_SHOW | SWP_HIDE | SWP_ACTIVATE |
549 SWP_MOVE | SWP_SIZE | SWP_NOREDRAW)) != 0 ||
550 hwnd == desktop || WinQueryWindow(hwnd, QW_PARENT) == desktop) {
551 return WinSetWindowPos(hwnd, hwndInsertBehind, x, y, cx, cy, fl);
552 }
553
554 SWP swpOld;
555 WinQueryWindowPos(hwnd, &swpOld);
556
557#if defined(QT_DEBUGWIDGETMASK)
558 qDebug() << " old pos" << swpOld;
559 if (QWidget *w = qt_widget_from_hwnd(hwnd)) {
560 qDebug() << " old geo" << w->geometry();
561 if (w->parentWidget()) {
562 qDebug() << " parent" << w->parentWidget();
563 if (w->parentWidget()->internalWinId()) {
564 SWP swp;
565 WinQueryWindowPos(w->parentWidget()->internalWinId(), &swp);
566 qDebug() << " pos" << swp;
567 }
568 qDebug() << " geo" << w->parentWidget()->geometry();
569 }
570 }
571#endif
572
573 // do some checks
574 if ((fl & SWP_ZORDER) && swpOld.hwndInsertBehind == hwndInsertBehind)
575 fl &= ~SWP_ZORDER;
576 if ((fl & SWP_SHOW) && (swpOld.fl & SWP_SHOW))
577 fl &= ~SWP_SHOW;
578 if ((fl & SWP_HIDE) && (swpOld.fl & SWP_HIDE))
579 fl &= ~SWP_HIDE;
580 if ((fl & (SWP_SHOW | SWP_HIDE)) == (SWP_SHOW | SWP_HIDE))
581 fl &= ~SWP_HIDE;
582
583 // do the job but not invalidate or redraw
584 BOOL rc = WinSetWindowPos(hwnd, hwndInsertBehind, x, y, cx, cy,
585 fl | SWP_NOREDRAW);
586 if (rc == FALSE || (fl & SWP_NOREDRAW))
587 return rc;
588
589 SWP swpNew;
590 WinQueryWindowPos(hwnd, &swpNew);
591
592 if (swpOld.hwndInsertBehind == swpNew.hwndInsertBehind)
593 fl &= ~SWP_ZORDER;
594
595 if ((fl & (SWP_ZORDER | SWP_SHOW | SWP_HIDE | SWP_MOVE | SWP_SIZE)) == 0)
596 return rc;
597
598 HPS hps = qt_display_ps();
599 HWND hwndParent = WinQueryWindow(hwnd, QW_PARENT);
600
601 // get window bounds
602 HRGN hrgnSelf = GpiCreateRegion(hps, 0, NULL);
603 qt_WinQueryClipRegionOrRect(hwnd, hrgnSelf);
604
605 if (fl & SWP_SHOW) {
606 WinInvalidateRegion(hwnd, hrgnSelf, TRUE);
607 } else if (fl & SWP_HIDE) {
608 // translate the region to the parent coordinate system
609 POINTL ptl = { swpNew.x, swpNew.y };
610 GpiOffsetRegion(hps, hrgnSelf, &ptl);
611 // invalidate the parent and children below this window
612 qt_WinInvalidateRegionEx(hwndParent, hrgnSelf,
613 WinQueryWindow(hwnd, QW_NEXT), HWND_BOTTOM);
614 } else {
615 // SWP_ZORDER or SWP_MOVE or SWP_SIZE
616
617 if (fl & SWP_ZORDER) {
618 // below we assume that WinSetWindowPos() returns FALSE if
619 // an incorrect (unrelated) hwndInsertBehind is passed when SWP_ZORDER
620 // is set
621
622 // first, detect whether we are moving up or down
623 BOOL up;
624 HWND hwndFrom, hwndTo;
625 if (swpOld.hwndInsertBehind == HWND_TOP) {
626 up = FALSE;
627 hwndFrom = WinQueryWindow(hwndParent, QW_TOP);
628 hwndTo = swpNew.hwndInsertBehind;
629 } else {
630 up = TRUE;
631 for (HWND hwndAbove = hwnd;
632 (hwndAbove = WinQueryWindow(hwndAbove, QW_PREV)) != 0;) {
633 if (hwndAbove == swpOld.hwndInsertBehind) {
634 up = FALSE;
635 break;
636 }
637 }
638 if (up) {
639 hwndFrom = swpOld.hwndInsertBehind;
640 hwndTo = WinQueryWindow(hwnd, QW_NEXT);
641 } else {
642 hwndFrom = WinQueryWindow(swpOld.hwndInsertBehind, QW_NEXT);
643 hwndTo = swpNew.hwndInsertBehind;
644 }
645 }
646#if defined(QT_DEBUGWIDGETMASK)
647 qDebug() << " moving up?" << up;
648 qDebug() << " hwndFrom" << qDebugHWND(hwndFrom);
649 qDebug() << " hwndTo" << qDebugHWND(hwndTo);
650#endif
651
652 SWP swp;
653 HWND sibling = hwndFrom;
654 HRGN hrgn = GpiCreateRegion(hps, 0, NULL);
655 HRGN hrgnUpd = GpiCreateRegion(hps, 0, NULL);
656
657 if (up) {
658 // go upwards in z-order
659 while (1) {
660 WinQueryWindowPos(sibling, &swp);
661#if defined(QT_DEBUGWIDGETMASK)
662 qDebug() << " sibling" << qDebugHWND(sibling)
663 << "fl" << qDebugFmtHex(swp.fl);
664#endif
665 // proceed only if not hidden
666 if (swp.fl & SWP_SHOW) {
667 // get sibling's bounds (clip region or rect)
668 qt_WinQueryClipRegionOrRect(sibling, hrgn);
669 // translate the region to this window's coordinate system
670 POINTL ptl = { swp.x - swpNew.x, swp.y - swpNew.y };
671 GpiOffsetRegion(hps, hrgn, &ptl);
672 // add to the region of siblings we're moving on top of
673 GpiCombineRegion(hps, hrgnUpd, hrgnUpd, hrgn, CRGN_OR);
674#if defined(QT_DEBUGWIDGETMASK)
675 qDebug(" processed");
676#endif
677 }
678 // iterate to the prev window (above)
679 if (sibling == hwndTo)
680 break;
681 sibling = swp.hwndInsertBehind;
682 }
683 // intersect the resulting region with the widget region and
684 // invalidate
685 GpiCombineRegion(hps, hrgnUpd, hrgnSelf, hrgnUpd, CRGN_AND);
686 WinInvalidateRegion(hwnd, hrgnUpd, TRUE);
687 } else {
688 // go downwards in reverse z-order
689 POINTL ptl = { 0, 0 };
690 while (1) {
691 WinQueryWindowPos(sibling, &swp);
692#if defined(QT_DEBUGWIDGETMASK)
693 qDebug() << " sibling" << qDebugHWND(sibling)
694 << "fl" << qDebugFmtHex(swp.fl);
695#endif
696 // proceed only if not hidden
697 if (swp.fl & SWP_SHOW) {
698 // get sibling's bounds (clip region or rect)
699 qt_WinQueryClipRegionOrRect(sibling, hrgn);
700 // undo the previous translation and translate this window's
701 // region to the siblings's coordinate system
702 ptl.x += swpNew.x - swp.x;
703 ptl.y += swpNew.y - swp.y;
704 GpiOffsetRegion(hps, hrgnSelf, &ptl);
705 // intersect the sibling's region with the translated one
706 // and invalidate the sibling
707 GpiCombineRegion(hps, hrgnUpd, hrgnSelf, hrgn, CRGN_AND);
708 WinInvalidateRegion(sibling, hrgnUpd, TRUE);
709 // substract the invalidated area from the widget's region
710 // (no need to invalidate it any more)
711 GpiCombineRegion(hps, hrgnSelf, hrgnSelf, hrgnUpd, CRGN_DIFF);
712 // prepare the translation from the sibling's
713 // coordinates back to this window's coordinates
714 ptl.x = swp.x - swpNew.x;
715 ptl.y = swp.y - swpNew.y;
716#if defined(QT_DEBUGWIDGETMASK)
717 qDebug(" processed");
718#endif
719 }
720 // iterate to the next window (below)
721 if (sibling == hwndTo)
722 break;
723 sibling = WinQueryWindow(sibling, QW_NEXT);
724 }
725 }
726
727 GpiDestroyRegion(hps, hrgnUpd);
728 GpiDestroyRegion(hps, hrgn);
729 }
730
731 if (fl & (SWP_MOVE | SWP_SIZE)) {
732 // Since we don't use WS_CLIPCHILDREN and WS_CLIPSIBLINGS,
733 // WinSetWindowPos() does not correctly update involved windows.
734 // The fix is to do it ourselves, taking clip regions into account.
735 // set new and old rectangles
736 const RECTL rcls [2] = {
737 // new (relative to parent)
738 { swpNew.x, swpNew.y, swpNew.x + swpNew.cx, swpNew.y + swpNew.cy },
739 // old (relative to parent)
740 { swpOld.x, swpOld.y, swpOld.x + swpOld.cx, swpOld.y + swpOld.cy }
741 };
742 const RECTL &rclNew = rcls [0];
743 const RECTL &rclOld = rcls [1];
744 // delta to shift coordinate space from parent to this widget
745 POINTL ptlToSelf = { -swpNew.x, -swpNew.y };
746 // use parent PS for blitting
747 HPS hps = WinGetPS(hwndParent);
748 // get old and new clip regions (relative to parent)
749 HRGN hrgnOld = GpiCreateRegion(hps, 1, &rclOld);
750 HRGN hrgnNew = GpiCreateRegion(hps, 1, &rclNew);
751 if (WinQueryClipRegion(hwnd, 0) != QCRGN_NO_CLIP_REGION) {
752 HRGN hrgnTemp = GpiCreateRegion(hps, 0, NULL);
753 // old (clipped to the old rect)
754 WinQueryClipRegion(hwnd, hrgnTemp);
755 GpiOffsetRegion(hps, hrgnTemp, (PPOINTL) &rclOld);
756 GpiCombineRegion(hps, hrgnOld, hrgnTemp, hrgnOld, CRGN_AND);
757 // new (clipped to the new rect)
758 WinQueryClipRegion(hwnd, hrgnTemp);
759 if (swpOld.cy != swpNew.cy) {
760 // keep the clip region top-left aligned by adding the
761 // height delta (new size - old size)
762 POINTL ptl = {0, swpNew.cy - swpOld.cy };
763 GpiOffsetRegion(hps, hrgnTemp, &ptl);
764 WinSetClipRegion(hwnd, hrgnTemp);
765 }
766 GpiOffsetRegion(hps, hrgnTemp, (PPOINTL) &rclNew);
767 GpiCombineRegion(hps, hrgnNew, hrgnTemp, hrgnNew, CRGN_AND);
768 GpiDestroyRegion(hps, hrgnTemp);
769 }
770 // the rest is useful only when the widget is visible
771 if (swpNew.fl & SWP_SHOW) {
772 // create affected region (old + new, relative to widget)
773 HRGN hrgnAff = GpiCreateRegion(hps, 0, NULL);
774 GpiCombineRegion(hps, hrgnAff, hrgnOld, hrgnNew, CRGN_OR);
775 GpiOffsetRegion(hps, hrgnAff, &ptlToSelf);
776 // get bounding rectangle of affected region
777 RECTL rclAff;
778 GpiQueryRegionBox(hps, hrgnAff, &rclAff);
779 // get region of obstacles limited to affected rectangle
780 HRGN hrgnObst = GpiCreateRegion(hps, 0, NULL);
781 qt_WinProcessWindowObstacles(hwnd, &rclAff, hrgnObst, CRGN_OR,
782 PWO_Siblings | PWO_Ancestors |
783 PWO_Screen | PWO_TopLevel);
784 // shift region of obstacles and affected region back to
785 // parent coords
786 GpiOffsetRegion(hps, hrgnObst, (PPOINTL) &rclNew);
787 GpiOffsetRegion(hps, hrgnAff, (PPOINTL) &rclNew);
788 // get parent bounds (clip region or rect)
789 HRGN hrgnUpd = GpiCreateRegion(hps, 0, NULL);
790 qt_WinQueryClipRegionOrRect(hwndParent, hrgnUpd);
791 // add parts of old region beyond parent bounds to
792 // region of obstacles
793 GpiCombineRegion(hps, hrgnOld, hrgnOld, hrgnUpd, CRGN_DIFF);
794 GpiCombineRegion(hps, hrgnObst, hrgnObst, hrgnOld, CRGN_OR);
795 // substract region of obstacles from affected region
796 GpiCombineRegion(hps, hrgnAff, hrgnAff, hrgnObst, CRGN_DIFF);
797 // remember it as parent update region (need later)
798 GpiCombineRegion(hps, hrgnUpd, hrgnAff, 0, CRGN_COPY);
799 // copy region of obstacles to delta region and shift it by
800 // delta (note: movement is considered to be top-left aligned)
801 HRGN hrgnDelta = GpiCreateRegion(hps, 0, NULL);
802 GpiCombineRegion(hps, hrgnDelta, hrgnObst, 0, CRGN_COPY);
803 POINTL ptlDelta = { rclNew.xLeft - rclOld.xLeft,
804 rclNew.yTop - rclOld.yTop };
805 GpiOffsetRegion(hps, hrgnDelta, &ptlDelta);
806 // substract region of obstacles from delta region to get
807 // pure delta
808 GpiCombineRegion(hps, hrgnDelta, hrgnDelta, hrgnObst, CRGN_DIFF);
809 // calculate minimal rectangle to blit (top-left aligned)
810 int minw = qMin(swpOld.cx, swpNew.cx);
811 int minh = qMin(swpOld.cy, swpNew.cy);
812 POINTL blitPtls [4] = {
813 // target (new)
814 { rclNew.xLeft, rclNew.yTop - minh },
815 { rclNew.xLeft + minw, rclNew.yTop },
816 // source (old)
817 { rclOld.xLeft, rclOld.yTop - minh },
818 };
819 // proceed with blitting only if target and source rects differ
820 if (blitPtls[0].x != blitPtls[2].x ||
821 blitPtls[0].y != blitPtls[2].y)
822 {
823 // Substract delta region from affected region (to minimize
824 // flicker)
825 GpiCombineRegion(hps, hrgnAff, hrgnAff, hrgnDelta, CRGN_DIFF);
826 // set affected region to parent PS
827 GpiSetClipRegion(hps, hrgnAff, NULL);
828 // blit minimal rectangle
829 GpiBitBlt(hps, hps, 3, blitPtls, ROP_SRCCOPY, BBO_IGNORE);
830 GpiSetClipRegion(hps, 0, NULL);
831 }
832 // substract new widget region from the parent update region
833 // and invalidate it (with underlying children)
834 GpiCombineRegion(hps, hrgnUpd, hrgnUpd, hrgnNew, CRGN_DIFF );
835 qt_WinInvalidateRegionEx(hwndParent, hrgnUpd,
836 WinQueryWindow(hwnd, QW_NEXT),
837 HWND_BOTTOM);
838 // intersect pure delta region with new region
839 // (to detect areas clipped off to minimize flicker when blitting)
840 GpiCombineRegion(hps, hrgnDelta, hrgnDelta, hrgnNew, CRGN_AND);
841 // substract blitted rectangle from new region
842 GpiSetRegion(hps, hrgnAff, 1, (PRECTL) &blitPtls);
843 GpiCombineRegion(hps, hrgnNew, hrgnNew, hrgnAff, CRGN_DIFF);
844 // combine the rest with intersected delta region
845 GpiCombineRegion(hps, hrgnNew, hrgnNew, hrgnDelta, CRGN_OR);
846 // shift the result back to widget coords and invalidate
847 GpiOffsetRegion(hps, hrgnNew, &ptlToSelf);
848 WinInvalidateRegion(hwnd, hrgnNew, TRUE);
849 // free resources
850 GpiDestroyRegion(hps, hrgnDelta);
851 GpiDestroyRegion(hps, hrgnUpd);
852 GpiDestroyRegion(hps, hrgnObst);
853 GpiDestroyRegion(hps, hrgnAff);
854 }
855 // free resources
856 GpiDestroyRegion(hps, hrgnOld);
857 GpiDestroyRegion(hps, hrgnNew);
858 WinReleasePS(hps);
859 }
860 }
861
862 GpiDestroyRegion(hps, hrgnSelf);
863
864 return TRUE;
865}
866
867#endif // QT_PM_NATIVEWIDGETMASK
868
869/*!
870 \internal
871
872 Helper function to extract regions of all windows that overlap the given
873 hwnd subject to their z-order (excluding children of hwnd) from the given
874 hrgn. hps is the presentation space of hrgn.
875 */
876static void qt_WinExcludeOverlappingWindows(HWND hwnd, HPS hps, HRGN hrgn)
877{
878 HRGN vr = GpiCreateRegion(hps, 0, NULL);
879 RECTL r;
880
881 // enumerate all windows that are on top of this hwnd
882 HWND id = hwnd, deskId = QApplication::desktop()->winId();
883 do {
884 HWND i = id;
885 while((i = WinQueryWindow( i, QW_PREV ))) {
886 if (WinIsWindowVisible(i)) {
887 WinQueryWindowRect(i, &r);
888 WinMapWindowPoints(i, hwnd, (PPOINTL) &r, 2);
889 GpiSetRegion(hps, vr, 1, &r);
890 GpiCombineRegion(hps, hrgn, hrgn, vr, CRGN_DIFF);
891 }
892 }
893 id = WinQueryWindow(id, QW_PARENT);
894 } while(id != deskId);
895
896 GpiDestroyRegion(hps, vr);
897}
898
899/*!
900 \internal
901
902 Helper function to scroll window contents. WinScrollWindow() is a bit
903 buggy and corrupts update regions sometimes (which leaves some outdated
904 areas unpainted after scrolling), so we reimplement its functionality in
905 this function. dy and clip->yBottom/yTop should be GPI coordinates, not Qt.
906 all clip coordinates are inclusive.
907 */
908static void qt_WinScrollWindowWell(HWND hwnd, LONG dx, LONG dy,
909 const PRECTL clip = NULL)
910{
911 WinLockVisRegions(HWND_DESKTOP, TRUE);
912
913 HPS lhps = WinGetClipPS(hwnd, HWND_TOP,
914 PSF_LOCKWINDOWUPDATE | PSF_CLIPSIBLINGS);
915 if (clip)
916 GpiIntersectClipRectangle(lhps, clip);
917
918 // remember the update region and validate it. it will be shifted and
919 // invalidated again later
920 HRGN update = GpiCreateRegion(lhps, 0, NULL);
921 WinQueryUpdateRegion(hwnd, update);
922 WinValidateRegion(hwnd, update, TRUE);
923
924 char points[sizeof(POINTL) * 4];
925 register PPOINTL ptls = reinterpret_cast<PPOINTL>(points);
926 RECTL &sr = *reinterpret_cast<PRECTL>(&ptls[2]);
927 RECTL &tr = *reinterpret_cast<PRECTL>(&ptls[0]);
928
929 // get the source rect for scrolling
930 GpiQueryClipBox(lhps, &sr);
931 sr.xRight++; sr.yTop++; // inclusive -> exclusive
932
933 // get the visible region ignoring areas covered by children
934 HRGN visible = GpiCreateRegion(lhps, 1, &sr);
935 qt_WinExcludeOverlappingWindows(hwnd, lhps, visible);
936
937 // scroll visible region rectangles separately to avoid the flicker
938 // that could be produced by scrolling parts of overlapping windows
939 RGNRECT ctl;
940 ctl.ircStart = 1;
941 ctl.crc = 0;
942 ctl.crcReturned = 0;
943 if (dx >= 0) {
944 if (dy >= 0) ctl.ulDirection = RECTDIR_RTLF_TOPBOT;
945 else ctl.ulDirection = RECTDIR_RTLF_BOTTOP;
946 } else {
947 if (dy >= 0) ctl.ulDirection = RECTDIR_LFRT_TOPBOT;
948 else ctl.ulDirection = RECTDIR_LFRT_BOTTOP;
949 }
950 GpiQueryRegionRects(lhps, visible, NULL, &ctl, NULL);
951 ctl.crc = ctl.crcReturned;
952 int rclcnt = ctl.crcReturned;
953 PRECTL rcls = new RECTL[rclcnt];
954 GpiQueryRegionRects(lhps, visible, NULL, &ctl, rcls);
955 PRECTL r = rcls;
956 for (int i = 0; i < rclcnt; i++, r++) {
957 // source rect
958 sr = *r;
959 // target rect
960 tr.xLeft = sr.xLeft + dx;
961 tr.xRight = sr.xRight + dx;
962 tr.yBottom = sr.yBottom + dy;
963 tr.yTop = sr.yTop + dy;
964 GpiBitBlt(lhps, lhps, 3, ptls, ROP_SRCCOPY, BBO_IGNORE);
965 }
966 delete [] rcls;
967
968 // make the scrolled version of the visible region
969 HRGN scrolled = GpiCreateRegion(lhps, 0, NULL);
970 GpiCombineRegion(lhps, scrolled, visible, 0, CRGN_COPY);
971 // invalidate the initial update region
972 GpiCombineRegion(lhps, scrolled, scrolled, update, CRGN_DIFF);
973 // shift the region
974 POINTL dp = { dx, dy };
975 GpiOffsetRegion(lhps, scrolled, &dp);
976 // substract scrolled from visible to get invalid areas
977 GpiCombineRegion(lhps, scrolled, visible, scrolled, CRGN_DIFF);
978
979 WinInvalidateRegion(hwnd, scrolled, TRUE);
980
981 GpiDestroyRegion(lhps, scrolled);
982 GpiDestroyRegion(lhps, visible);
983 GpiDestroyRegion(lhps, update);
984
985 WinReleasePS(lhps);
986
987 WinLockVisRegions(HWND_DESKTOP, FALSE);
988
989 WinUpdateWindow(hwnd);
990}
991
992/*!
993 * \internal
994 * For some unknown reason, PM sends WM_SAVEAPPLICATION to every window
995 * being destroyed, which makes it indistinguishable from WM_SAVEAPPLICATION
996 * sent to top level windows during system shutdown. We use our own version of
997 * WinDestroyWindow() and a special flag (qt_about_to_destroy_wnd) to
998 * distinguish it in qapplication_pm.cpp.
999 */
1000static BOOL qt_WinDestroyWindow(HWND hwnd)
1001{
1002#if !defined(QT_NO_SESSIONMANAGER)
1003 qt_about_to_destroy_wnd = true;
1004#endif
1005 BOOL rc = WinDestroyWindow(hwnd);
1006#if !defined(QT_NO_SESSIONMANAGER)
1007 qt_about_to_destroy_wnd = false;
1008#endif
1009 return rc;
1010}
1011
1012static PFNWP QtOldSysMenuProc;
1013static MRESULT EXPENTRY QtSysMenuProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
1014{
1015 if (msg == WM_MENUEND) {
1016 // the pull-down menu is closed, always dismiss the system menu itself
1017 WinPostMsg(hwnd, MM_ENDMENUMODE, MPFROMSHORT(TRUE), 0);
1018 }
1019 return QtOldSysMenuProc(hwnd, msg, mp1, mp2);
1020}
1021
1022static void removeSysMenuAccels(HWND frame)
1023{
1024 HWND sysMenu = WinWindowFromID(frame, FID_SYSMENU);
1025 if (!sysMenu)
1026 return;
1027
1028 SHORT subId = SHORT1FROMMR(WinSendMsg(sysMenu, MM_ITEMIDFROMPOSITION, 0, 0));
1029 if (subId != MIT_ERROR) {
1030 MENUITEM item;
1031 WinSendMsg(sysMenu, MM_QUERYITEM, MPFROM2SHORT(subId, FALSE), MPFROMP(&item));
1032 HWND subMenu = item.hwndSubMenu;
1033 if (subMenu) {
1034 USHORT cnt = SHORT1FROMMR(WinSendMsg(subMenu, MM_QUERYITEMCOUNT, 0, 0));
1035 for (int i = 0; i < cnt; i++) {
1036 USHORT id = SHORT1FROMMR(
1037 WinSendMsg(subMenu, MM_ITEMIDFROMPOSITION, MPFROMSHORT(i), 0));
1038 if (id == SC_TASKMANAGER || id == SC_CLOSE) {
1039 // accels for these entries always work in Qt, skip them
1040 continue;
1041 }
1042 USHORT len = SHORT1FROMMR(
1043 WinSendMsg(subMenu, MM_QUERYITEMTEXTLENGTH, MPFROMSHORT(id), 0));
1044 if (len++) {
1045 char *text = new char[len];
1046 WinSendMsg(subMenu, MM_QUERYITEMTEXT,
1047 MPFROM2SHORT(id, len), MPFROMP(text));
1048 char *tab = strrchr(text, '\t');
1049 if (tab) {
1050 *tab = 0;
1051 WinSendMsg(subMenu, MM_SETITEMTEXT,
1052 MPFROMSHORT(id), MPFROMP(text));
1053 }
1054 delete[] text;
1055 }
1056 }
1057 // sublclass the system menu to leave the menu mode completely
1058 // when the user presses the ESC key. by default, pressing ESC
1059 // while the pull-down menu is showing brings us to the menu bar,
1060 // which is confusing in the case of the system menu, because
1061 // there is only one item on the menu bar, and we cannot see
1062 // that it is active when the frame window has an icon.
1063 PFNWP oldProc = WinSubclassWindow(sysMenu, QtSysMenuProc);
1064 // set QtOldSysMenuProc only once: it must be the same for
1065 // all FID_SYSMENU windows.
1066 if (!QtOldSysMenuProc)
1067 QtOldSysMenuProc = oldProc;
1068 }
1069 }
1070}
1071
1072/*****************************************************************************
1073 QWidget member functions
1074 *****************************************************************************/
1075
1076void QWidgetPrivate::create_sys(WId window, bool initializeWindow, bool destroyOldWindow)
1077{
1078 // @todo when window is not zero, it represents an existing (external)
1079 // window handle we should create a QWidget "wrapper" for to incorporate it
1080 // to the Qt widget hierarchy. This functionality isn't really necessary on
1081 // OS/2 at the moment, so it is not currently supported (and the window
1082 // argument is simply ignored). Note that destroyOldWindow only makes
1083 // sense when window is != 0 so it is also ignored.
1084
1085 Q_ASSERT(window == 0);
1086 Q_UNUSED(destroyOldWindow);
1087
1088 Q_Q(QWidget);
1089 static int sw = -1, sh = -1;
1090
1091 Qt::WindowType type = q->windowType();
1092 Qt::WindowFlags flags = data.window_flags;
1093
1094 bool topLevel = q->isWindow();
1095 bool popup = (type == Qt::Popup || type == Qt::ToolTip);
1096 bool dialog = (type == Qt::Dialog
1097 || type == Qt::Sheet
1098 || (flags & Qt::MSWindowsFixedSizeDialogHint));
1099 bool desktop = (type == Qt::Desktop);
1100 bool tool = (type == Qt::Tool || type == Qt::Drawer);
1101
1102 WId id;
1103
1104 QByteArray className = qt_reg_winclass(q).toLatin1();
1105
1106 // @todo WindowStaysOnTopHint is ignored for now (does nothing)
1107 if (popup)
1108 flags |= Qt::WindowStaysOnTopHint; // a popup stays on top
1109
1110 if (sw < 0) { // get the screen size
1111 sw = WinQuerySysValue(HWND_DESKTOP, SV_CXSCREEN);
1112 sh = WinQuerySysValue(HWND_DESKTOP, SV_CYSCREEN);
1113 }
1114
1115 if (desktop && !q->testAttribute(Qt::WA_DontShowOnScreen)) { // desktop widget
1116 popup = false; // force this flags off
1117 // @todo use WinGetMaxPosition to take such things as XCenter into account?
1118 data.crect.setRect(0, 0, sw, sh);
1119 }
1120
1121 QByteArray title;
1122 ULONG style = 0;
1123 ULONG fId = 0, fStyle = 0, fcFlags = 0;
1124
1125 if (!desktop) {
1126 // @todo testing for Qt::WA_PaintUnclipped is commented out because it
1127 // is said that it causes some problems on Win32 and we also saw the
1128 // problems with this line enabled in Qt3 on OS/2 in QT_PM_NO_WIDGETMASK
1129 // mode (terrible flicker in QFileDialog because QSplitter used there
1130 // sets WA_PaintUnclipped). Note that in QT_PM_NATIVEWIDGETMASK mode it
1131 // doesn't play any role since all clipping is manually done by us
1132 // anyway (see below).
1133#if 0
1134 if (!testAttribute(Qt::WA_PaintUnclipped))
1135#endif
1136 {
1137#ifdef QT_PM_NATIVEWIDGETMASK
1138 // We don't use WS_CLIPSIBLINGS and WS_CLIPCHILDREN because when these
1139 // styles are set and the child (sibling) window has a non-NULL clip region,
1140 // PM still continues to exclude the entire child's rectangle from the
1141 // parent window's update region, ignoring its clip region. As a result,
1142 // areas outside the clip region are left unpainted. Instead, we correct
1143 // the update region of the window ourselves, on every WM_PAINT event.
1144 // Note: for top-level widgets, we specify WS_CLIPSIBLINGS anyway to let
1145 // the system do correct clipping for us (qt_WinProcessWindowObstacles()
1146 // relies on this). It's ok because top-level widgets cannot be non-
1147 // rectangular and therefore don't require our magic clipping procedure.
1148 if (topLevel)
1149 style |= WS_CLIPSIBLINGS;
1150#else
1151 style |= WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
1152#endif
1153 }
1154
1155 // for all top-level windows except popups we create a WC_FRAME
1156 // as a parent and owner.
1157 if (topLevel && !popup) {
1158 if ((type == Qt::Window || dialog || tool)) {
1159 if (!(flags & Qt::FramelessWindowHint)) {
1160 if (flags & Qt::MSWindowsFixedSizeDialogHint) {
1161 fcFlags |= FCF_DLGBORDER;
1162 } else if (tool) {
1163 // note: while it's common that top-level tool widgets
1164 // have a thiner frame, FCF_BORDER makes it too thin and
1165 // it even cannot be resized. So, use FCF_SIZEBORDER too.
1166 fcFlags |= FCF_SIZEBORDER;
1167 } else {
1168 fcFlags |= FCF_SIZEBORDER;
1169 }
1170 }
1171 if (flags & Qt::WindowTitleHint)
1172 fcFlags |= FCF_TITLEBAR;
1173 if (flags & Qt::WindowSystemMenuHint)
1174 fcFlags |= FCF_SYSMENU | FCF_CLOSEBUTTON;
1175 if (flags & Qt::WindowMinimizeButtonHint)
1176 fcFlags |= FCF_MINBUTTON;
1177 if (flags & Qt::WindowMaximizeButtonHint)
1178 fcFlags |= FCF_MAXBUTTON;
1179 } else {
1180 fcFlags |= FCF_BORDER;
1181 }
1182
1183 fStyle |= FS_NOMOVEWITHOWNER | FS_NOBYTEALIGN;
1184 fStyle |= WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
1185 }
1186 }
1187
1188 if (flags & Qt::WindowTitleHint) {
1189 QString t = topLevel ? qAppName() : q->objectName();
1190 t = t.left(1).toUpper() + t.mid(1).toLower();
1191 title = t.toLocal8Bit();
1192 }
1193
1194 // The Qt::WA_WState_Created flag is checked by translateConfigEvent() in
1195 // qapplication_pm.cpp. We switch it off temporarily to avoid move
1196 // and resize events during creation
1197 q->setAttribute(Qt::WA_WState_Created, false);
1198
1199 if (desktop) { // desktop widget
1200 id = WinQueryDesktopWindow(0, 0);
1201 // @todo commented out on Win32 too
1202// QWidget *otherDesktop = QWidget::find(id); // is there another desktop?
1203// if (otherDesktop && otherDesktop->testWFlags(Qt::WPaintDesktop)) {
1204// otherDesktop->d_func()->setWinId(0); // remove id from widget mapper
1205// setWinId(id); // make sure otherDesktop is found first
1206// otherDesktop->d_func()->setWinId(id);
1207// } else {
1208 setWinId(id);
1209// }
1210 } else if (topLevel) {
1211 // create top-level widget
1212 if (!popup) {
1213 HWND ownerw = NULLHANDLE;
1214 QWidget *parent = q->parentWidget();
1215
1216 if (parent && !(parent->windowType() == Qt::Desktop))
1217 ownerw = parent->window()->d_func()->frameWinId();
1218 // create WC_FRAME
1219 FRAMECDATA fcData;
1220 fcData.cb = sizeof(FRAMECDATA);
1221 fcData.flCreateFlags = fcFlags;
1222 fcData.hmodResources = NULL;
1223 fcData.idResources = 0;
1224 // check whether a default icon is present in .EXE and use it if so
1225 ULONG sz = 0;
1226 if (DosQueryResourceSize(NULL, RT_POINTER, 1, &sz) == 0) {
1227 fcData.flCreateFlags |= FCF_ICON;
1228 fcData.idResources = 1;
1229 }
1230#if defined(QT_DEBUGWINCREATEDESTROY)
1231 qDebug() << "|Creating top level window (frame)" << q
1232 << "\n| owner" << qDebugHWND(ownerw)
1233 << "\n| title" << title
1234 << "\n| style" << qDebugFmtHex(fStyle)
1235 << "\n| fcFlags" << qDebugFmtHex(fcFlags);
1236#endif
1237 fId = WinCreateWindow(HWND_DESKTOP, WC_FRAME, title, fStyle,
1238 0, 0, 0, 0, ownerw, HWND_TOP, 0,
1239 &fcData, NULL);
1240#if defined(QT_DEBUGWINCREATEDESTROY)
1241 qDebug() << "| hwnd" << qDebugFmtHex(fId);
1242#endif
1243 if (fId == NULLHANDLE)
1244 qWarning("QWidget::create(): WinCreateWindow(WC_FRAME) "
1245 "failed with 0x%08lX", WinGetLastError(0));
1246
1247 if (fcData.flCreateFlags & FCF_ICON) {
1248 // mark that we already have the window icon taken from .EXE to
1249 // prevent setWindowIcon_sys() from resetting it to nothing
1250 createTLExtra();
1251 extra->topextra->iconPointer =
1252 (HPOINTER)WinSendMsg(fId, WM_QUERYICON, 0, 0);
1253 }
1254
1255 PFNWP oldProc = WinSubclassWindow(fId, QtFrameProc);
1256 // remember QtOldFrameProc only once: it's the same for
1257 // all WC_FRAME windows.
1258 if (!QtOldFrameProc)
1259 QtOldFrameProc = oldProc;
1260
1261 // subclass all standard frame controls to get non-client mouse events
1262 // (note: the size of the ctls array must match QtOldFrameCtlProcs)
1263 HWND ctls[FID_HORZSCROLL - FID_SYSMENU + 1];
1264 if (WinMultWindowFromIDs(fId, ctls, FID_SYSMENU, FID_HORZSCROLL) > 0) {
1265 for (size_t i = 0; i < sizeof(ctls)/sizeof(ctls[0]); ++i) {
1266 if (ctls[i] != NULLHANDLE) {
1267 oldProc = WinSubclassWindow(ctls[i], QtFrameCtlProc);
1268 // remember the old proc only once: it's the same for
1269 // all standard frame control windows.
1270 if (!QtOldFrameCtlProcs[i])
1271 QtOldFrameCtlProcs[i] = oldProc;
1272 }
1273 }
1274 }
1275
1276 removeSysMenuAccels(fId);
1277
1278 // create client window
1279#if defined(QT_DEBUGWINCREATEDESTROY)
1280 qDebug() << "|Creating top level window (client)" << q
1281 << "\n| owner & parent" << qDebugHWND(fId)
1282 << "\n| class" << className
1283 << "\n| title" << title
1284 << "\n| style" << qDebugFmtHex(style);
1285#endif
1286 // note that we place the client on top (HWND_TOP) to exclude other
1287 // frame controls from being analyzed in qt_WinProcessWindowObstacles
1288 id = WinCreateWindow(fId, className, title, style, 0, 0, 0, 0,
1289 fId, HWND_TOP, FID_CLIENT, NULL, NULL);
1290 } else {
1291#if defined(QT_DEBUGWINCREATEDESTROY)
1292 qDebug() << "|Creating top level window (popup)" << q
1293 << "\n| class" << className
1294 << "\n| title" << title
1295 << "\n| style" << qDebugFmtHex(style);
1296#endif
1297 id = WinCreateWindow(HWND_DESKTOP, className, title, style,
1298 0, 0, 0, 0, NULLHANDLE, HWND_TOP, 0, NULL, NULL);
1299 }
1300#if defined(QT_DEBUGWINCREATEDESTROY)
1301 qDebug() << "| hwnd" << qDebugFmtHex(id);
1302#endif
1303 if (id == NULLHANDLE)
1304 qWarning("QWidget::create(): WinCreateWindow "
1305 "failed with 0x%08lX", WinGetLastError(0));
1306 setWinId(id);
1307
1308 // it isn't mentioned in PMREF that PM is obliged to initialize window
1309 // data with zeroes (although seems to), so do it ourselves
1310 for (LONG i = 0; i <= (LONG) (QT_EXTRAWINDATASIZE - 4); i += 4)
1311 WinSetWindowULong(id, i, 0);
1312
1313 SWP swp;
1314 // Get the default window position and size. Note that when the
1315 // FS_SHELLPOSITION flag is specified during WC_FRAME window
1316 // creation its size and position remains zero until it is shown
1317 // for the first time. So, we don't use FS_SHELLPOSITION but emulate
1318 // its functionality here.
1319 WinQueryTaskSizePos(0, 0, &swp);
1320
1321 // update position & initial size of POPUP window
1322 const bool wasMoved = q->testAttribute(Qt::WA_Moved);
1323 const bool wasResized = q->testAttribute(Qt::WA_Resized);
1324 const bool isVisibleOnScreen = !q->testAttribute(Qt::WA_DontShowOnScreen);
1325 if (popup && initializeWindow && isVisibleOnScreen) {
1326 if (!wasResized) {
1327 swp.cx = sw / 2;
1328 swp.cy = 4 * sh / 10;
1329 } else {
1330 swp.cx = data.crect.width();
1331 swp.cy = data.crect.height();
1332 }
1333 if (!wasMoved) {
1334 swp.x = sw / 2 - swp.cx / 2;
1335 swp.y = sh / 2 - swp.cy / 2;
1336 } else {
1337 swp.x = data.crect.x();
1338 swp.y = data.crect.y();
1339 // flip y coordinate
1340 swp.y = sh - (swp.y + swp.cy);
1341 }
1342 }
1343
1344 ULONG fl = SWP_SIZE | SWP_MOVE;
1345 HWND insertBehind = NULLHANDLE;
1346 if ((flags & Qt::WindowStaysOnTopHint) || (type == Qt::ToolTip)) {
1347 insertBehind = HWND_TOP;
1348 fl |= SWP_ZORDER;
1349 if (flags & Qt::WindowStaysOnBottomHint)
1350 qWarning() << "QWidget: Incompatible window flags: the window "
1351 "can't be on top and on bottom at the same time";
1352 } else if (flags & Qt::WindowStaysOnBottomHint) {
1353 insertBehind = HWND_BOTTOM;
1354 fl |= SWP_ZORDER;
1355 }
1356 WinSetWindowPos(popup ? id : fId, insertBehind,
1357 swp.x, swp.y, swp.cx, swp.cy, fl);
1358
1359 if (!popup) {
1360 QTLWExtra *top = topData();
1361 top->fId = fId;
1362 WinQueryWindowPos(fId, &swp);
1363 SWP cswp;
1364 WinQueryWindowPos(id, &cswp);
1365 // flip y coordinates
1366 swp.y = sh - (swp.y + swp.cy);
1367 cswp.y = swp.cy - (cswp.y + cswp.cy);
1368 // store frame dimensions
1369 QRect &fs = top->frameStrut;
1370 fs.setCoords(cswp.x, cswp.y, swp.cx - cswp.x - cswp.cx,
1371 swp.cy - cswp.y - cswp.cy);
1372 data.fstrut_dirty = false;
1373 if (wasMoved || wasResized) {
1374 // resize & move if necessary (we couldn't do it earlier
1375 // because we didn't know the frame dimensions yet)
1376 if (wasMoved) {
1377 // QWidget::move() includes frame strut so no correction is
1378 // necessary (crect was abused to store the frame position
1379 // until window creation)
1380 swp.x = data.crect.x();
1381 swp.y = data.crect.y();
1382 }
1383 if (wasResized) {
1384 swp.cx = data.crect.width() + fs.left() + fs.right();
1385 swp.cy = data.crect.height() + fs.top() + fs.bottom();
1386 }
1387 // flip y coordinate
1388 swp.y = sh - (swp.y + swp.cy);
1389 WinSetWindowPos(fId, NULLHANDLE, swp.x, swp.y, swp.cx, swp.cy,
1390 SWP_SIZE | SWP_MOVE);
1391 }
1392 }
1393
1394 if (!popup || (initializeWindow && isVisibleOnScreen)) {
1395 // fetch the actual geometry
1396 WinQueryWindowPos(popup ? id : fId, &swp);
1397 // flip y coordinate
1398 swp.y = sh - (swp.y + swp.cy);
1399 if (popup) {
1400 data.crect.setRect(swp.x, swp.y, swp.cx, swp.cy);
1401 } else {
1402 const QRect &fs = topData()->frameStrut;
1403 data.crect.setRect(swp.x + fs.left(), swp.y + fs.top(),
1404 swp.cx - fs.left() - fs.right(),
1405 swp.cy - fs.top() - fs.bottom());
1406 }
1407 }
1408 } else if (q->testAttribute(Qt::WA_NativeWindow) || paintOnScreen()) {
1409 // create child widget
1410 Q_ASSERT(q->parentWidget());
1411 HWND parentw = q->parentWidget()->effectiveWinId();
1412
1413#if defined(QT_DEBUGWINCREATEDESTROY)
1414 qDebug() << "|Creating child window" << q
1415 << "\n| owner & parent" << qDebugHWND(parentw)
1416 << "\n| class" << className
1417 << "\n| title" << title
1418 << "\n| style" << qDebugFmtHex(style);
1419#endif
1420 id = WinCreateWindow(parentw, className, title, style,
1421 data.crect.left(),
1422 // flip y coordinate
1423 q->parentWidget()->height() - data.crect.bottom() - 1,
1424 data.crect.width(), data.crect.height(),
1425 parentw, HWND_TOP, 0, NULL, NULL);
1426#if defined(QT_DEBUGWINCREATEDESTROY)
1427 qDebug() << "| hwnd" << qDebugFmtHex(id);
1428#endif
1429 if (id == NULLHANDLE)
1430 qWarning("QWidget::create(): WinCreateWindow "
1431 "failed with 0x%08lX", WinGetLastError(0));
1432 setWinId(id);
1433 }
1434
1435 if (desktop) {
1436 q->setAttribute(Qt::WA_WState_Visible);
1437 }
1438
1439 q->setAttribute(Qt::WA_WState_Created); // accept move/resize events
1440
1441 hd = 0; // no presentation space
1442
1443 if (extra && !extra->mask.isEmpty())
1444 setMask_sys(extra->mask);
1445
1446 if (topLevel && (data.crect.width() == 0 || data.crect.height() == 0)) {
1447 q->setAttribute(Qt::WA_OutsideWSRange, true);
1448 }
1449
1450 if (!topLevel && q->testAttribute(Qt::WA_NativeWindow) && q->testAttribute(Qt::WA_Mapped)) {
1451 Q_ASSERT(q->internalWinId() != NULLHANDLE);
1452 WinShowWindow(q->internalWinId(), TRUE);
1453 }
1454}
1455
1456void QWidget::destroy(bool destroyWindow, bool destroySubWindows)
1457{
1458 Q_D(QWidget);
1459 if (!isWindow() && parentWidget())
1460 parentWidget()->d_func()->invalidateBuffer(d->effectiveRectFor(geometry()));
1461 d->deactivateWidgetCleanup();
1462 if (testAttribute(Qt::WA_WState_Created)) {
1463 setAttribute(Qt::WA_WState_Created, false);
1464 for(int i = 0; i < d->children.size(); ++i) { // destroy all widget children
1465 register QObject *obj = d->children.at(i);
1466 if (obj->isWidgetType())
1467 ((QWidget*)obj)->destroy(destroySubWindows,
1468 destroySubWindows);
1469 }
1470 if (mouseGrb == this)
1471 releaseMouse();
1472 if (keyboardGrb == this)
1473 releaseKeyboard();
1474 if (testAttribute(Qt::WA_ShowModal)) // just be sure we leave modal
1475 QApplicationPrivate::leaveModal(this);
1476 else if ((windowType() == Qt::Popup))
1477 qApp->d_func()->closePopup(this);
1478 if (destroyWindow && !(windowType() == Qt::Desktop) &&
1479 d->frameWinId() != NULLHANDLE) {
1480 // destroy HWND
1481 HWND id = d->frameWinId();
1482#if defined(QT_DEBUGWINCREATEDESTROY)
1483 qDebug() << "|Destroying window" << this
1484 << "\n| hwnd" << qDebugFmtHex(id);
1485 if (id != internalWinId())
1486 qDebug() << "| hwnd" << qDebugFmtHex(internalWinId()) << "(client)";
1487#endif
1488 qt_WinDestroyWindow(id);
1489 }
1490 QT_TRY {
1491 QTLWExtra *top = d->maybeTopData();
1492 if (top)
1493 top->fId = 0;
1494 d->setWinId(0);
1495 } QT_CATCH (const std::bad_alloc &) {
1496 // swallow - destructors must not throw
1497 }
1498 }
1499}
1500
1501void QWidgetPrivate::reparentChildren()
1502{
1503 Q_Q(QWidget);
1504 QObjectList chlist = q->children();
1505 for (int i = 0; i < chlist.size(); ++i) { // reparent children
1506 QObject *obj = chlist.at(i);
1507 if (obj->isWidgetType()) {
1508 QWidget *w = (QWidget *)obj;
1509 if ((w->windowType() == Qt::Popup)) {
1510 ;
1511 } else if (w->isWindow()) {
1512 bool showIt = w->isVisible();
1513 QPoint old_pos = w->pos();
1514 w->setParent(q, w->windowFlags());
1515 w->move(old_pos);
1516 if (showIt)
1517 w->show();
1518 } else {
1519 w->d_func()->invalidateBuffer(w->rect());
1520 WinSetParent(w->effectiveWinId(), q->effectiveWinId(), FALSE);
1521 WinSetOwner(w->effectiveWinId(), q->effectiveWinId());
1522 w->d_func()->reparentChildren();
1523#if 0 // @todo check if this is really necessary any longer
1524 // bring PM coords into accordance with Qt coords,
1525 // otherwise setGeometry() below will wrongly position
1526 // children if this widget manages their layout.
1527 SWP swp;
1528 int hd = q->height() - old_height;
1529 WinQueryWindowPos(w->effectiveWinId(), &swp);
1530 swp.y += hd;
1531 WinSetWindowPos(w->effectiveWinId(), 0, swp.x, swp.y, 0, 0, SWP_MOVE);
1532#endif
1533 }
1534 }
1535 }
1536}
1537
1538void QWidgetPrivate::setParent_sys(QWidget *parent, Qt::WindowFlags f)