source: trunk/src/kernel/qapplication_pm.cpp@ 154

Last change on this file since 154 was 154, checked in by dmik, 19 years ago

Kernel: Fixed recursion (that ate the entire stack) if the WShowModal flag was requested (or already set) when calling QWidget::reparent() and there was another modal widget visible.

  • Property svn:keywords set to Id
File size: 127.8 KB
Line 
1/****************************************************************************
2** $Id: qapplication_pm.cpp 154 2006-11-12 23:28:25Z dmik $
3**
4** Implementation of OS/2 startup routines and event handling
5**
6** Copyright (C) 1992-2003 Trolltech AS. All rights reserved.
7** Copyright (C) 2004 Norman ASA. Initial OS/2 Port.
8** Copyright (C) 2005-2006 netlabs.org. Further OS/2 Development.
9**
10** This file is part of the kernel module of the Qt GUI Toolkit.
11**
12** This file may be distributed under the terms of the Q Public License
13** as defined by Trolltech AS of Norway and appearing in the file
14** LICENSE.QPL included in the packaging of this file.
15**
16** This file may be distributed and/or modified under the terms of the
17** GNU General Public License version 2 as published by the Free Software
18** Foundation and appearing in the file LICENSE.GPL included in the
19** packaging of this file.
20**
21** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition
22** licenses may use this file in accordance with the Qt Commercial License
23** Agreement provided with the Software.
24**
25** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
26** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
27**
28** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
29** information about Qt Commercial License Agreements.
30** See http://www.trolltech.com/qpl/ for QPL licensing information.
31** See http://www.trolltech.com/gpl/ for GPL licensing information.
32**
33** Contact info@trolltech.com if any conditions of this licensing are
34** not clear to you.
35**
36**********************************************************************/
37
38// GCC headers do not define this when INCL_BASE is defined,
39// but we need it for IOCTL_KEYBOARD etc. constants
40#ifndef INCL_DOSDEVIOCTL
41#define INCL_DOSDEVIOCTL
42#endif
43
44#include "qapplication.h"
45#include "private/qapplication_p.h"
46#include "qwidget.h"
47#include "qwidgetlist.h"
48#include "qwidgetintdict.h"
49#include "qobjectlist.h"
50#include "qpainter.h"
51#include "qpixmapcache.h"
52#include "qdatetime.h"
53#include "qsessionmanager.h"
54#include "qmime.h"
55#include "qguardedptr.h"
56#include "qclipboard.h"
57#include "qthread.h"
58#include "qwhatsthis.h" // ######## dependency
59#include "qthread.h"
60#include "qlibrary.h"
61#include "qt_os2.h"
62#include "qcursor.h"
63#include "private/qinternal_p.h"
64#include "private/qcriticalsection_p.h"
65/// @todo (dmik) remove?
66//#include "private/qinputcontext_p.h"
67#include "qstyle.h"
68#include "qmetaobject.h"
69
70#include <limits.h>
71#include <string.h>
72#include <ctype.h>
73#include <stdlib.h>
74
75extern void qt_ensure_pm();
76
77/// @todo (dmik) hmm, extended keys under OS/2?
78//// support for multi-media-keys on ME/2000/XP
79//#ifndef WM_APPCOMMAND
80//#define WM_APPCOMMAND 0x0319
81//
82//#define FAPPCOMMAND_MOUSE 0x8000
83//#define FAPPCOMMAND_KEY 0
84//#define FAPPCOMMAND_OEM 0x1000
85//#define FAPPCOMMAND_MASK 0xF000
86//#define GET_APPCOMMAND_LPARAM(lParam) ((short)(HIWORD(lParam) & ~FAPPCOMMAND_MASK))
87//#define GET_DEVICE_LPARAM(lParam) ((WORD)(HIWORD(lParam) & FAPPCOMMAND_MASK))
88//#define GET_MOUSEORKEY_LPARAM GET_DEVICE_LPARAM
89//#define GET_FLAGS_LPARAM(lParam) (LOWORD(lParam))
90//#define GET_KEYSTATE_LPARAM(lParam) GET_FLAGS_LPARAM(lParam)
91//
92//#define APPCOMMAND_BROWSER_BACKWARD 1
93//#define APPCOMMAND_BROWSER_FORWARD 2
94//#define APPCOMMAND_BROWSER_REFRESH 3
95//#define APPCOMMAND_BROWSER_STOP 4
96//#define APPCOMMAND_BROWSER_SEARCH 5
97//#define APPCOMMAND_BROWSER_FAVORITES 6
98//#define APPCOMMAND_BROWSER_HOME 7
99//#define APPCOMMAND_VOLUME_MUTE 8
100//#define APPCOMMAND_VOLUME_DOWN 9
101//#define APPCOMMAND_VOLUME_UP 10
102//#define APPCOMMAND_MEDIA_NEXTTRACK 11
103//#define APPCOMMAND_MEDIA_PREVIOUSTRACK 12
104//#define APPCOMMAND_MEDIA_STOP 13
105//#define APPCOMMAND_MEDIA_PLAY_PAUSE 14
106//#define APPCOMMAND_LAUNCH_MAIL 15
107//#define APPCOMMAND_LAUNCH_MEDIA_SELECT 16
108//#define APPCOMMAND_LAUNCH_APP1 17
109//#define APPCOMMAND_LAUNCH_APP2 18
110//#define APPCOMMAND_BASS_DOWN 19
111//#define APPCOMMAND_BASS_BOOST 20
112//#define APPCOMMAND_BASS_UP 21
113//#define APPCOMMAND_TREBLE_DOWN 22
114//#define APPCOMMAND_TREBLE_UP 23
115//#endif
116
117extern void qt_dispatchEnterLeave( QWidget*, QWidget* ); // qapplication.cpp
118
119extern void qt_erase_background(
120 HPS hps, int x, int y, int w, int h,
121 const QColor &bg_color,
122 const QPixmap *bg_pixmap, int off_x, int off_y, int devh
123);
124
125/*
126 Internal functions.
127*/
128
129Q_EXPORT
130void qt_draw_tiled_pixmap( HPS, int, int, int, int,
131 const QPixmap *, int, int, int );
132
133QRgb qt_sysclr2qrgb( LONG sysClr )
134{
135 // QRgb has the same RGB format (0xaarrggbb) as OS/2 uses (ignoring the
136 // highest alpha byte) so we just cast OS/2 LONG RGB value to qRgb
137 // which is an unsigned int actually.
138 return ((QRgb) WinQuerySysColor( HWND_DESKTOP, sysClr, 0 )) & RGB_MASK;
139}
140
141QFont qt_sysfont2qfont( const PSZ scope ) {
142 static const PSZ app = "PM_SystemFonts";
143 static const PSZ def = "8.Helv";
144 ULONG keyLen = 0;
145 QFont f( "Helv", 8 );
146
147 if ( PrfQueryProfileSize( HINI_USERPROFILE, app, scope, &keyLen ) && keyLen ) {
148 keyLen ++; // reserve space for a dot
149 char *buf = new char [keyLen];
150 ULONG realLen =
151 PrfQueryProfileString( HINI_USERPROFILE, app, scope, def, buf, keyLen );
152 realLen --; // excude zero terminator
153
154 // parse the font definition
155 int height = 0;
156 char *dot = strchr( buf, '.' ), *dot2 = 0;
157 if ( dot ) {
158 *dot = 0;
159 height = strtoul( buf, NULL, 10 );
160 dot2 = strchr( ++ dot, '.' );
161 if ( dot2 ) {
162 // process simulated styles
163 buf[realLen] = '.';
164 buf[realLen+1] = 0;
165 strupr( dot2 );
166/// @todo (dmik) currently, simulated bold and italic font styles are not
167// supported by Qt/OS2 because Qt doesn't support style simulation
168// explicitly. the code below is commented out to prevent selecting
169// true fonts when simulated ones are actually requested.
170// if ( strstr( dot2, ".BOLD." ) ) f.setBold( TRUE );
171// if ( strstr( dot2, ".ITALIC.") ) f.setItalic( TRUE );
172 if ( strstr( dot2, ".UNDERSCORE.") ) f.setUnderline( TRUE );
173 if ( strstr( dot2, ".UNDERLINED.") ) f.setUnderline( TRUE );
174 if ( strstr( dot2, ".STRIKEOUT.") ) f.setStrikeOut( TRUE );
175 *dot2 = 0;
176 }
177 // query non-simulated styles
178 FONTMETRICS fm;
179 LONG cnt = 1; // use the first match
180 GpiQueryFonts(
181 qt_display_ps(), QF_PUBLIC, dot, &cnt, sizeof(FONTMETRICS), &fm );
182 if ( cnt ) {
183 // the code below is mostly from QFontDatabasePrivate::reload()
184 if ( fm.fsSelection & FM_SEL_ITALIC ) f.setItalic( TRUE );
185 if ( fm.fsType & FM_TYPE_FIXED ) f.setFixedPitch( TRUE );
186 USHORT weight = fm.usWeightClass;
187 USHORT width = fm.usWidthClass;
188 if ( weight < 4 ) f.setWeight( QFont::Light );
189 else if ( weight < 6 ) f.setWeight( QFont::Normal );
190 else if ( weight < 7 ) f.setWeight( QFont::DemiBold );
191 else if ( weight < 8 ) f.setWeight( QFont::Bold );
192 else f.setWeight( QFont::Black );
193 switch ( width ) {
194 case 1: f.setStretch( QFont::UltraCondensed ); break;
195 case 2: f.setStretch( QFont::ExtraCondensed ); break;
196 case 3: f.setStretch( QFont::Condensed ); break;
197 case 4: f.setStretch( QFont::SemiCondensed ); break;
198 case 5: f.setStretch( QFont::Unstretched ); break;
199 case 6: f.setStretch( QFont::SemiExpanded ); break;
200 case 7: f.setStretch( QFont::Expanded ); break;
201 case 8: f.setStretch( QFont::ExtraExpanded ); break;
202 case 9: f.setStretch( QFont::UltraExpanded ); break;
203 default: f.setStretch( QFont::Unstretched ); break;
204 }
205 f.setFamily( fm.szFamilyname );
206 f.setPointSize( height );
207 }
208 }
209 delete[] buf;
210 }
211 return f;
212}
213
214/*****************************************************************************
215 Internal variables and functions
216 *****************************************************************************/
217
218static HWND curWin = 0; // current window
219static HPS displayPS = 0; // display presentation space
220
221#if !defined (QT_NO_SESSIONMANAGER)
222
223// Session management
224static bool sm_blockUserInput = FALSE;
225static bool sm_smActive = FALSE;
226extern QSessionManager* qt_session_manager_self;
227static bool sm_cancel = FALSE;
228static bool sm_gracefulShutdown = FALSE;
229static bool sm_quitSkipped = FALSE;
230bool qt_about_to_destroy_wnd = FALSE;
231
232//#define DEBUG_SESSIONMANAGER
233
234#endif
235
236static bool replayPopupMouseEvent = FALSE; // replay handling when popups close
237
238static bool ignoreNextMouseReleaseEvent = FALSE; // ignore the next release event if
239 // return from a modal widget
240#if defined(QT_DEBUG)
241static bool appNoGrab = FALSE; // mouse/keyboard grabbing
242#endif
243
244static bool app_do_modal = FALSE; // modal mode
245extern QWidgetList *qt_modal_stack;
246extern QDesktopWidget *qt_desktopWidget;
247static QWidget *popupButtonFocus = 0;
248static bool popupCloseDownMode = FALSE;
249static bool qt_try_modal( QWidget *, QMSG *, int& ret );
250
251QWidget *qt_button_down = 0; // widget got last button-down
252
253extern bool qt_tryAccelEvent( QWidget*, QKeyEvent* ); // def in qaccel.cpp
254
255static HWND autoCaptureWnd = 0;
256static bool autoCaptureReleased = FALSE;
257static void setAutoCapture( HWND ); // automatic capture
258static void releaseAutoCapture();
259
260//@@TODO (dmik):
261// do we really need this filter stuff or qApp->pmEventFilter()
262// is enough?
263typedef int (*QPMEventFilter) (QMSG*);
264static QPMEventFilter qt_pm_event_filter = 0;
265
266Q_EXPORT QPMEventFilter qt_set_pm_event_filter( QPMEventFilter filter )
267{
268 QPMEventFilter old_filter = qt_pm_event_filter;
269 qt_pm_event_filter = filter;
270 return old_filter;
271}
272
273typedef bool (*QPMEventFilterEx) (QMSG*,MRESULT&);
274static QPMEventFilterEx qt_pm_event_filter_ex = 0;
275
276Q_EXPORT QPMEventFilterEx qt_set_pm_event_filter_ex( QPMEventFilterEx filter )
277{
278 QPMEventFilterEx old = qt_pm_event_filter_ex;
279 qt_pm_event_filter_ex = filter;
280 return old;
281}
282
283bool qt_pmEventFilter( QMSG* msg, MRESULT &result )
284{
285 result = NULL;
286 if ( qt_pm_event_filter && qt_pm_event_filter( msg ) )
287 return TRUE;
288 if ( qt_pm_event_filter_ex && qt_pm_event_filter_ex( msg, result ) )
289 return TRUE;
290 return qApp->pmEventFilter( msg );
291}
292
293static void unregWinClasses();
294
295extern QCursor *qt_grab_cursor();
296
297extern "C" MRESULT EXPENTRY QtWndProc( HWND, ULONG, MPARAM, MPARAM );
298
299class QETWidget : public QWidget // event translator widget
300{
301public:
302 void setWFlags( WFlags f ) { QWidget::setWFlags(f); }
303 void clearWFlags( WFlags f ) { QWidget::clearWFlags(f); }
304 void setWState( WState f ) { QWidget::setWState(f); }
305 void clearWState( WState f ) { QWidget::clearWState(f); }
306 QWExtra *xtra() { return QWidget::extraData(); }
307 QTLWExtra *top() { return QWidget::topData(); }
308 bool pmEvent( QMSG *m ) { return QWidget::pmEvent(m); }
309#if defined(Q_CC_GNU)
310 void markFrameStrutDirty() { fstrut_dirty = 1; }
311#else
312 void markFrameStrutDirty() { QWidget::fstrut_dirty = 1; }
313#endif
314 void updateFrameStrut() { QWidget::updateFrameStrut(); }
315 bool translateMouseEvent( const QMSG &qmsg );
316 bool translateKeyEvent( const QMSG &qmsg, bool grab );
317#ifndef QT_NO_WHEELEVENT
318 bool translateWheelEvent( const QMSG &qmsg );
319#endif
320 bool sendKeyEvent( QEvent::Type type, int code, int ascii,
321 int state, bool grab, const QString& text,
322 bool autor = FALSE );
323 bool translatePaintEvent( const QMSG &qmsg );
324 bool translateConfigEvent( const QMSG &qmsg );
325 bool translateCloseEvent( const QMSG &qmsg );
326//@@TODO (dmik): later
327// void repolishStyle( QStyle &style ) { styleChange( style ); }
328//@@TODO (dmik): it seems we don't need this
329// void reparentWorkaround();
330 void showChildren(bool spontaneous) { QWidget::showChildren(spontaneous); }
331 void hideChildren(bool spontaneous) { QWidget::hideChildren(spontaneous); }
332};
333
334//@@TODO (dmik): it seems we don't need this
335//void QETWidget::reparentWorkaround()
336//{
337// ((QWidgetIntDict*)QWidget::wmapper())->remove((long)winid);
338// clearWState(WState_Created | WState_Visible | WState_ForceHide);
339// winid = 0;
340// QRect geom = geometry();
341// create(0, FALSE, FALSE);
342// setGeometry(geom);
343// QWidget *p = parentWidget();
344// while (p) {
345// if (!p->isVisible())
346// return;
347// p = p->parentWidget();
348// }
349// show();
350//}
351
352
353static bool qt_show_system_menu( QWidget* tlw )
354{
355 HWND fId = tlw->winFId();
356 HWND sysMenu = WinWindowFromID( fId, FID_SYSMENU );
357 if ( !sysMenu )
358 return FALSE; // no menu for this window
359 WinPostMsg( sysMenu, MM_STARTMENUMODE, MPFROM2SHORT(TRUE, FALSE), 0 );
360 return TRUE;
361}
362
363// Palette handling
364extern QPalette *qt_std_pal;
365extern void qt_create_std_palette();
366
367static void qt_set_pm_resources()
368{
369 // Do the font settings
370
371 QFont windowFont = qt_sysfont2qfont( "WindowText" );
372 QFont menuFont = qt_sysfont2qfont( "Menus" );
373 QFont iconFont = qt_sysfont2qfont( "IconText" );
374 QFont titleFont = qt_sysfont2qfont( "WindowTitles" );
375
376 QApplication::setFont( windowFont, TRUE );
377 QApplication::setFont( menuFont, TRUE, "QPopupMenu" );
378 QApplication::setFont( menuFont, TRUE, "QMenuBar" );
379 QApplication::setFont( iconFont, TRUE, "QTipLabel" );
380 QApplication::setFont( iconFont, TRUE, "QStatusBar" );
381 QApplication::setFont( titleFont, TRUE, "QTitleBar" );
382 QApplication::setFont( titleFont, TRUE, "QDockWindowTitleBar" );
383
384 if ( qt_std_pal && *qt_std_pal != QApplication::palette() )
385 return;
386
387 // Do the color settings
388
389 // active colors
390 QColorGroup acg;
391 acg.setColor( QColorGroup::Foreground,
392 QColor( qt_sysclr2qrgb( SYSCLR_WINDOWTEXT ) ) );
393 acg.setColor( QColorGroup::Background,
394 QColor( qt_sysclr2qrgb( SYSCLR_DIALOGBACKGROUND ) ) );
395 acg.setColor( QColorGroup::ButtonText,
396 QColor( qt_sysclr2qrgb( SYSCLR_MENUTEXT ) ) );
397 acg.setColor( QColorGroup::Button,
398 QColor( qt_sysclr2qrgb( SYSCLR_BUTTONMIDDLE ) ) );
399 acg.setColor( QColorGroup::Light,
400 QColor( qt_sysclr2qrgb( SYSCLR_BUTTONLIGHT ) ) );
401 acg.setColor( QColorGroup::Dark,
402 QColor( qt_sysclr2qrgb( SYSCLR_BUTTONDARK ) ) );
403 acg.setColor( QColorGroup::Midlight,
404 QColor( (acg.light().red() + acg.button().red()) / 2,
405 (acg.light().green() + acg.button().green()) / 2,
406 (acg.light().blue() + acg.button().blue()) / 2 ) );
407 acg.setColor( QColorGroup::Mid,
408 QColor( (acg.dark().red() + acg.button().red()) / 2,
409 (acg.dark().green() + acg.button().green()) / 2,
410 (acg.dark().blue() + acg.button().blue()) / 2 ) );
411 acg.setColor( QColorGroup::Text,
412 QColor( qt_sysclr2qrgb( SYSCLR_MENUTEXT ) ) );
413 acg.setColor( QColorGroup::Base,
414 QColor( qt_sysclr2qrgb( SYSCLR_ENTRYFIELD ) ) );
415 acg.setColor( QColorGroup::BrightText,
416 QColor( qt_sysclr2qrgb( SYSCLR_BUTTONLIGHT ) ) );
417 acg.setColor( QColorGroup::Shadow,
418 Qt::black );
419 acg.setColor( QColorGroup::Highlight,
420 QColor( qt_sysclr2qrgb( SYSCLR_HILITEBACKGROUND ) ) );
421 acg.setColor( QColorGroup::HighlightedText,
422 QColor( qt_sysclr2qrgb( SYSCLR_HILITEFOREGROUND ) ) );
423
424 // these colors are not present in the PM system palette
425 acg.setColor( QColorGroup::Link, Qt::blue );
426 acg.setColor( QColorGroup::LinkVisited, Qt::magenta );
427
428 // disabled colors
429 QColorGroup dcg = acg;
430 dcg.setColor( QColorGroup::Foreground,
431 QColor( qt_sysclr2qrgb( SYSCLR_MENUDISABLEDTEXT ) ) );
432 dcg.setColor( QColorGroup::Text,
433 QColor( qt_sysclr2qrgb( SYSCLR_MENUDISABLEDTEXT ) ) );
434
435 // inactive colors are currently the same as active
436 QColorGroup icg = acg;
437
438 QPalette pal( acg, dcg, icg );
439 QApplication::setPalette( pal, TRUE );
440 *qt_std_pal = pal;
441
442 // a special palette for menus
443 acg.setColor( QColorGroup::Highlight,
444 QColor( qt_sysclr2qrgb( SYSCLR_MENUHILITEBGND ) ) );
445 acg.setColor( QColorGroup::HighlightedText,
446 QColor( qt_sysclr2qrgb( SYSCLR_MENUHILITE ) ) );
447 QApplication::setPalette( QPalette( acg, dcg, icg ), TRUE, "QPopupMenu");
448 QApplication::setPalette( QPalette( acg, dcg, icg ), TRUE, "QMenuBar");
449}
450
451/*****************************************************************************
452 qt_init() - initializes Qt for OS/2
453 *****************************************************************************/
454
455// wrapper for QtOS2SysXcptMainHandler to provide local access to private data
456class QtOS2SysXcptMainHandlerInternal : public QtOS2SysXcptMainHandler
457{
458public:
459 static bool &installed() { return QtOS2SysXcptMainHandler::installed; }
460 static ERR &libcHandler() { return QtOS2SysXcptMainHandler::libcHandler; }
461 static ERR handler() { return QtOS2SysXcptMainHandler::handler; }
462};
463
464#define XcptMainHandler QtOS2SysXcptMainHandlerInternal
465
466// need to get default font?
467extern bool qt_app_has_font;
468
469void qt_init( int *argcptr, char **argv, QApplication::Type )
470{
471#if !defined(QT_OS2_NO_SYSEXCEPTIONS)
472 // Even if the user didn't install the exception handler for the main
473 // thread using QtOS2SysXcptMainHandler prior to constructing a QApplication
474 // instance, we still want it to be installed to catch system traps. But
475 // since the exception registration record can only be located on the stack,
476 // we can enable our exception handling on the main thread only if the LIBC
477 // library of the compiler provides an exception handler (already allocated
478 // on the stack before main() is called). In this case, we replace the LIBC
479 // exception handler's pointer with our own (not nice but what to do?) and
480 // call the former (to let it do signal processing and the stuff) after we
481 // generate the trap file. Note that in this tricky case, the user has no
482 // possibility to install the exception handler callback (since it's only
483 // possible using QtOS2SysXcptMainHandler).
484
485 PTIB ptib = NULL;
486 DosGetInfoBlocks( &ptib, NULL );
487 Q_ASSERT( ptib && ptib->tib_ptib2 );
488
489 if ( ptib && ptib->tib_ptib2 ) {
490 // must be called only on the main (first) thread
491 Q_ASSERT( ptib->tib_ptib2->tib2_ultid == 1 );
492 // also make sure that QtOS2SysXcptMainHandler was not already
493 // instantiated on the main thread
494 if ( ptib->tib_ptib2->tib2_ultid == 1 &&
495 XcptMainHandler::installed() == FALSE )
496 {
497 if ( ptib->tib_pexchain != END_OF_CHAIN ) {
498 PEXCEPTIONREGISTRATIONRECORD r =
499 (PEXCEPTIONREGISTRATIONRECORD) ptib->tib_pexchain;
500 XcptMainHandler::libcHandler() = r->ExceptionHandler;
501 r->ExceptionHandler = XcptMainHandler::handler();
502 XcptMainHandler::installed() = TRUE;
503 } else {
504 Q_ASSERT( 0 /* no exception handler for the main thread */ );
505 }
506 }
507 }
508#endif
509
510#if defined(QT_DEBUG)
511 int argc = *argcptr;
512 int i, j;
513
514 // Get command line params
515
516 j = argc ? 1 : 0;
517 for ( i=1; i<argc; i++ ) {
518 if ( argv[i] && *argv[i] != '-' ) {
519 argv[j++] = argv[i];
520 continue;
521 }
522 QCString arg = argv[i];
523 if ( arg == "-nograb" )
524 appNoGrab = !appNoGrab;
525 else
526 argv[j++] = argv[i];
527 }
528 *argcptr = j;
529#else
530 Q_UNUSED( argcptr );
531 Q_UNUSED( argv );
532#endif // QT_DEBUG
533
534 // Ensure we are in PM mode
535 qt_ensure_pm();
536 // Force creation of the main event queue to make it possible to
537 // create windows before the application invokes exec()
538 QApplication::eventLoop();
539
540 // Misc. initialization
541 QPMMime::initialize();
542 QColor::initialize();
543 QFont::initialize();
544 QCursor::initialize();
545 QPainter::initialize();
546#if defined(QT_THREAD_SUPPORT)
547 QThread::initialize();
548#endif
549 qApp->setName( qAppName() );
550
551 // default font
552 if ( !qt_app_has_font )
553 QApplication::setFont( QFont( "Helv", 8 ) );
554
555 // QFont::locale_init(); ### Uncomment when it does something on OS/2
556
557 if ( !qt_std_pal )
558 qt_create_std_palette();
559
560 if ( QApplication::desktopSettingsAware() )
561 qt_set_pm_resources();
562
563/// @todo (dmik) remove?
564// QInputContext::init();
565}
566
567/*****************************************************************************
568 qt_cleanup() - cleans up when the application is finished
569 *****************************************************************************/
570
571void qt_cleanup()
572{
573 unregWinClasses();
574 QPixmapCache::clear();
575 QPainter::cleanup();
576 QCursor::cleanup();
577 QFont::cleanup();
578 QColor::cleanup();
579 QSharedDoubleBuffer::cleanup();
580#if defined(QT_THREAD_SUPPORT)
581 QThread::cleanup();
582#endif
583 if ( displayPS ) {
584 WinReleasePS( displayPS );
585 displayPS = 0;
586 }
587
588//@@TODO (dmik): remove?
589// QInputContext::shutdown();
590
591#if !defined(QT_OS2_NO_SYSEXCEPTIONS)
592 // remove the exception handler if it was installed in qt_init()
593 PTIB ptib = NULL;
594 DosGetInfoBlocks( &ptib, NULL );
595 Q_ASSERT( ptib && ptib->tib_ptib2 );
596
597 if ( ptib && ptib->tib_ptib2 ) {
598 // must be called only on the main (first) thread
599 Q_ASSERT( ptib->tib_ptib2->tib2_ultid == 1 );
600 // also make sure the handler was really installed by qt_init()
601 if ( ptib->tib_ptib2->tib2_ultid == 1 &&
602 XcptMainHandler::installed() == TRUE &&
603 XcptMainHandler::libcHandler() != NULL )
604 {
605 Q_ASSERT( ptib->tib_pexchain != END_OF_CHAIN );
606 if ( ptib->tib_pexchain != END_OF_CHAIN ) {
607 PEXCEPTIONREGISTRATIONRECORD r =
608 (PEXCEPTIONREGISTRATIONRECORD) ptib->tib_pexchain;
609 Q_ASSERT( r->ExceptionHandler == XcptMainHandler::handler() );
610 if ( r->ExceptionHandler == XcptMainHandler::handler() ) {
611 r->ExceptionHandler = XcptMainHandler::libcHandler();
612 XcptMainHandler::libcHandler() = NULL;
613 XcptMainHandler::installed() = FALSE;
614 }
615 }
616 }
617 }
618#endif
619}
620
621
622/*****************************************************************************
623 Platform specific global and internal functions
624 *****************************************************************************/
625
626Q_EXPORT const char *qAppFileName() // get application file name
627{
628 static char appFileName[CCHMAXPATH] = "\0";
629 if ( !appFileName[0] ) {
630 PPIB ppib;
631 DosGetInfoBlocks( NULL, &ppib );
632 DosQueryModuleName( ppib->pib_hmte, sizeof(appFileName), appFileName );
633 }
634 return appFileName;
635}
636
637Q_EXPORT const char *qAppName() // get application name
638{
639 static char appName[CCHMAXPATH] = "\0";
640 if ( !appName[0] ) {
641 const char *p = strrchr( qAppFileName(), '\\' ); // skip path
642 if ( p )
643 memcpy( appName, p+1, qstrlen(p) );
644 int l = qstrlen( appName );
645 if ( (l > 4) && !qstricmp( appName + l - 4, ".exe" ) )
646 appName[l-4] = '\0'; // drop .exe extension
647 }
648 return appName;
649}
650
651Q_EXPORT HPS qt_display_ps() // get display PS
652{
653 if ( !displayPS )
654 displayPS = WinGetScreenPS( HWND_DESKTOP );
655 return displayPS;
656}
657
658bool qt_nograb() // application no-grab option
659{
660#if defined(QT_DEBUG)
661 return appNoGrab;
662#else
663 return FALSE;
664#endif
665}
666
667
668static QAsciiDict<int> *winclassNames = 0;
669
670const QString qt_reg_winclass( int flags ) // register window class
671{
672 if ( flags & Qt::WType_Desktop )
673 return QString();
674
675 if ( !winclassNames ) {
676 winclassNames = new QAsciiDict<int>;
677 Q_CHECK_PTR( winclassNames );
678 }
679
680 ULONG style = 0;
681 QString cname;
682 if ( flags & Qt::WType_TopLevel ) {
683 if ( flags & Qt::WType_Popup ) {
684 cname = "QPopup";
685 } else {
686 // this class is for frame window clients when WC_FRAME is used.
687 cname = "QClient";
688 style |= CS_MOVENOTIFY;
689 }
690 } else {
691 cname = "QWidget";
692 }
693
694 if ( winclassNames->find(cname.latin1()) ) // already registered
695 return cname;
696
697 // QT_EXTRAWINDATASIZE is defined in qwindowdefs_pm.h.
698 WinRegisterClass( 0, cname.latin1(), QtWndProc, style, QT_EXTRAWINDATASIZE );
699
700 winclassNames->insert( cname.latin1(), (int*)1 );
701 return cname;
702}
703
704static void unregWinClasses()
705{
706 if ( !winclassNames )
707 return;
708 // there is no need to unregister private window classes -- it is done
709 // automatically upon process termination.
710 delete winclassNames;
711 winclassNames = 0;
712}
713
714
715/*****************************************************************************
716 Platform specific QApplication members
717 *****************************************************************************/
718
719void QApplication::setMainWidget( QWidget *mainWidget )
720{
721 main_widget = mainWidget;
722}
723
724//@@TODO (dmik): later (os/2 version?)
725//Qt::WindowsVersion QApplication::winVersion()
726//{
727// return qt_winver;
728//}
729
730#ifndef QT_NO_CURSOR
731
732/*****************************************************************************
733 QApplication cursor stack
734 *****************************************************************************/
735
736typedef QPtrList<QCursor> QCursorList;
737
738static QCursorList *cursorStack = 0;
739
740void QApplication::setOverrideCursor( const QCursor &cursor, bool replace )
741{
742 if ( !cursorStack ) {
743 cursorStack = new QCursorList;
744 Q_CHECK_PTR( cursorStack );
745 cursorStack->setAutoDelete( TRUE );
746 }
747 app_cursor = new QCursor( cursor );
748 Q_CHECK_PTR( app_cursor );
749 if ( replace )
750 cursorStack->removeLast();
751 cursorStack->append( app_cursor );
752 WinSetPointer( HWND_DESKTOP, app_cursor->handle() );
753}
754
755void QApplication::restoreOverrideCursor()
756{
757 if ( !cursorStack ) // no cursor stack
758 return;
759 cursorStack->removeLast();
760 app_cursor = cursorStack->last();
761 if ( app_cursor ) {
762 WinSetPointer( HWND_DESKTOP, app_cursor->handle() );
763 } else {
764 delete cursorStack;
765 cursorStack = 0;
766 QWidget *w = QWidget::find( curWin );
767 if ( w )
768 WinSetPointer( HWND_DESKTOP, w->cursor().handle() );
769 else
770 WinSetPointer( HWND_DESKTOP, arrowCursor.handle() );
771 }
772}
773
774#endif
775
776/*
777 Internal function called from QWidget::setCursor()
778*/
779
780void qt_set_cursor( QWidget *w, const QCursor& /* c */)
781{
782 if ( !curWin )
783 return;
784 QWidget* cW = QWidget::find( curWin );
785 if ( !cW || cW->topLevelWidget() != w->topLevelWidget() ||
786 !cW->isVisible() || !cW->hasMouse() || cursorStack )
787 return;
788
789 WinSetPointer( HWND_DESKTOP, cW->cursor().handle() );
790}
791
792
793void QApplication::setGlobalMouseTracking( bool enable )
794{
795 bool tellAllWidgets;
796 if ( enable ) {
797 tellAllWidgets = (++app_tracking == 1);
798 } else {
799 tellAllWidgets = (--app_tracking == 0);
800 }
801 if ( tellAllWidgets ) {
802 QWidgetIntDictIt it( *((QWidgetIntDict*)QWidget::mapper) );
803 register QWidget *w;
804 while ( (w=it.current()) ) {
805 if ( app_tracking > 0 ) { // switch on
806 if ( !w->testWState(WState_MouseTracking) ) {
807 w->setMouseTracking( TRUE );
808 }
809 } else { // switch off
810 if ( w->testWState(WState_MouseTracking) ) {
811 w->setMouseTracking( FALSE );
812 }
813 }
814 ++it;
815 }
816 }
817}
818
819
820/*****************************************************************************
821 Routines to find a Qt widget from a screen position
822 *****************************************************************************/
823
824static QWidget *findChildWidget( const QWidget *p, const QPoint &pos )
825{
826 if ( p->children() ) {
827 QWidget *w;
828 QObjectListIt it( *p->children() );
829 it.toLast();
830 while ( it.current() ) {
831 if ( it.current()->isWidgetType() ) {
832 w = (QWidget*)it.current();
833 if ( w->isVisible() && w->geometry().contains(pos) ) {
834 QWidget *c = findChildWidget( w, w->mapFromParent(pos) );
835 return c ? c : w;
836 }
837 }
838 --it;
839 }
840 }
841 return 0;
842}
843
844QWidget *QApplication::widgetAt( int x, int y, bool child )
845{
846 // flip y coordinate
847 y = desktop()->height() - (y + 1);
848 POINTL ptl = { x, y };
849 HWND win = WinWindowFromPoint( HWND_DESKTOP, &ptl, child );
850 if ( !win )
851 return 0;
852
853 QWidget *w = QWidget::find( win );
854 if( !w && !child )
855 return 0;
856//@@TODO (dmik): can it ever happen that a child window is a not Qt window
857// but its (grand) parent is?
858 while (
859 !w && (win = WinQueryWindow( win, QW_PARENT )) != desktop()->winId()
860 ) {
861 w = QWidget::find( win );
862 }
863 return w;
864}
865
866void QApplication::beep()
867{
868 WinAlarm( HWND_DESKTOP, WA_WARNING );
869}
870
871/*****************************************************************************
872 OS/2-specific drawing used here
873 *****************************************************************************/
874
875static void drawTile( HPS hps, int x, int y, int w, int h,
876 const QPixmap *pixmap, int xOffset, int yOffset, int devh )
877{
878 int yPos, xPos, drawH, drawW, yOff, xOff;
879 yPos = y;
880 yOff = yOffset;
881 while( yPos < y + h ) {
882 drawH = pixmap->height() - yOff; // Cropping first row
883 if ( yPos + drawH > y + h ) // Cropping last row
884 drawH = y + h - yPos;
885 xPos = x;
886 xOff = xOffset;
887 while( xPos < x + w ) {
888 drawW = pixmap->width() - xOff; // Cropping first column
889 if ( xPos + drawW > x + w ) // Cropping last column
890 drawW = x + w - xPos;
891//@@TODO (dmik): optimize: flip y only once and change loops accodringly
892 // flip y coordinates
893 POINTL ptls[] = {
894 { xPos, yPos }, { xPos + drawW, drawH },
895 { xOff, pixmap->height() - (yOff + drawH) }
896 };
897 if ( devh ) ptls[0].y = devh - (ptls[0].y + drawH);
898 ptls[1].y += ptls[0].y;
899 GpiBitBlt( hps, pixmap->handle(), 3, ptls, ROP_SRCCOPY, BBO_IGNORE );
900 xPos += drawW;
901 xOff = 0;
902 }
903 yPos += drawH;
904 yOff = 0;
905 }
906}
907
908//@@TODO (dmik): implement as fill pattern?
909// masks and transforms are not used here.
910Q_EXPORT
911void qt_fill_tile( QPixmap *tile, const QPixmap &pixmap )
912{
913 copyBlt( tile, 0, 0, &pixmap, 0, 0, -1, -1);
914 int x = pixmap.width();
915 while ( x < tile->width() ) {
916 copyBlt( tile, x, 0, tile, 0, 0, x, pixmap.height() );
917 x *= 2;
918 }
919 int y = pixmap.height();
920 while ( y < tile->height() ) {
921 copyBlt( tile, 0, y, tile, 0, 0, tile->width(), y );
922 y *= 2;
923 }
924}
925
926//@@TODO (dmik): implement as fill pattern?
927// masks and transforms are not used here.
928Q_EXPORT
929void qt_draw_tiled_pixmap( HPS hps, int x, int y, int w, int h,
930 const QPixmap *bg_pixmap,
931 int off_x, int off_y, int devh )
932{
933 QPixmap *tile = 0;
934 QPixmap *pm;
935 int sw = bg_pixmap->width(), sh = bg_pixmap->height();
936 if ( sw*sh < 8192 && sw*sh < 16*w*h ) {
937 int tw = sw, th = sh;
938 while ( tw*th < 32678 && tw < w/2 )
939 tw *= 2;
940 while ( tw*th < 32678 && th < h/2 )
941 th *= 2;
942 tile = new QPixmap( tw, th, bg_pixmap->depth(),
943 QPixmap::NormalOptim );
944 qt_fill_tile( tile, *bg_pixmap );
945 pm = tile;
946 } else {
947 pm = (QPixmap*)bg_pixmap;
948 }
949 drawTile( hps, x, y, w, h, pm, off_x, off_y, devh );
950 if ( tile )
951 delete tile;
952}
953
954
955
956/*****************************************************************************
957 Main event loop
958 *****************************************************************************/
959
960extern uint qGlobalPostedEventsCount();
961
962#ifndef QT_NO_DRAGANDDROP
963extern MRESULT qt_dispatchDragAndDrop( QWidget *, const QMSG & ); // qdnd_pm.cpp
964#endif
965
966/*!
967 The message procedure calls this function for every message
968 received. Reimplement this function if you want to process window
969 messages \e msg that are not processed by Qt. If you don't want
970 the event to be processed by Qt, then return TRUE; otherwise
971 return FALSE.
972*/
973bool QApplication::pmEventFilter( QMSG * /*msg*/ ) // OS/2 PM event filter
974{
975 return FALSE;
976}
977
978/*!
979 If \a gotFocus is TRUE, \a widget will become the active window.
980 Otherwise the active window is reset to NULL.
981*/
982void QApplication::pmFocus( QWidget *widget, bool gotFocus )
983{
984 if ( gotFocus ) {
985 setActiveWindow( widget );
986 if ( active_window && active_window->testWFlags( WType_Dialog ) ) {
987 // raise the entire application, not just the dialog
988 QWidget* mw = active_window;
989 while( mw->parentWidget() && mw->testWFlags( WType_Dialog) )
990 mw = mw->parentWidget()->topLevelWidget();
991 if ( mw != active_window )
992 WinSetWindowPos( mw->winFId(), HWND_TOP, 0, 0, 0, 0, SWP_ZORDER );
993 }
994 } else {
995 setActiveWindow( 0 );
996 }
997}
998
999//
1000// QtWndProc() receives all messages from the main event loop
1001//
1002
1003enum {
1004 // some undocumented messages (they have the WM_U_ prefix for clarity)
1005
1006 // sent to hwnd that has been entered to by a mouse pointer.
1007 // FID_CLIENT also receives enter messages of its WC_FRAME.
1008 // mp1 = hwnd that is entered, mp2 = hwnd that is left
1009 WM_U_MOUSEENTER = 0x41E,
1010 // sent to hwnd that has been left by a mouse pointer.
1011 // FID_CLIENT also receives leave messages of its WC_FRAME.
1012 // mp1 = hwnd that is left, mp2 = hwnd that is entered
1013 WM_U_MOUSELEAVE = 0x41F,
1014
1015 // some undocumented system values
1016
1017 SV_WORKAREA_YTOP = 51,
1018 SV_WORKAREA_YBOTTOM = 52,
1019 SV_WORKAREA_XRIGHT = 53,
1020 SV_WORKAREA_XLEFT = 54,
1021};
1022
1023static bool inLoop = FALSE;
1024
1025#define RETURN(x) { inLoop = FALSE; return (MRESULT) (x); }
1026
1027inline bool qt_sendSpontaneousEvent( QObject *receiver, QEvent *event )
1028{
1029 return QApplication::sendSpontaneousEvent( receiver, event );
1030}
1031
1032extern "C" MRESULT EXPENTRY QtWndProc( HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2 )
1033{
1034 // message handling indicators: if result is true at the end of message
1035 // processing, no default window proc is called but rc is returned.
1036 bool result = TRUE;
1037 MRESULT rc = (MRESULT) 0;
1038
1039 QEvent::Type evt_type = QEvent::None;
1040 QETWidget *widget = 0;
1041 bool isMouseEvent = FALSE;
1042
1043 if ( !qApp ) // unstable app state
1044 goto do_default;
1045
1046 // make sure we show widgets (e.g. scrollbars) when the user resizes
1047 if ( inLoop && qApp->loopLevel() )
1048 qApp->sendPostedEvents( 0, QEvent::ShowWindowRequest );
1049
1050 inLoop = TRUE;
1051
1052 isMouseEvent =
1053 (msg >= WM_MOUSEFIRST && msg <= WM_MOUSELAST) ||
1054 (msg >= WM_EXTMOUSEFIRST && msg <= WM_EXTMOUSELAST);
1055//@@TODO (dmik): later (extra buttons)
1056// (message >= WM_XBUTTONDOWN && message <= WM_XBUTTONDBLCLK)
1057
1058 QMSG qmsg; // create QMSG structure
1059 qmsg.hwnd = hwnd;
1060 qmsg.msg = msg;
1061 qmsg.mp1 = mp1;
1062 qmsg.mp2 = mp2;
1063 qmsg.time = WinQueryMsgTime( 0 );
1064 if ( isMouseEvent || msg == WM_CONTEXTMENU ) {
1065 qmsg.ptl.x = (short)SHORT1FROMMP(mp1);
1066 qmsg.ptl.y = (short)SHORT2FROMMP(mp1);
1067 WinMapWindowPoints( qmsg.hwnd, HWND_DESKTOP, &qmsg.ptl, 1 );
1068 } else {
1069 WinQueryMsgPos( 0, &qmsg.ptl );
1070 }
1071 // flip y coordinate
1072 qmsg.ptl.y = QApplication::desktop()->height() - (qmsg.ptl.y + 1);
1073
1074 if ( qt_pmEventFilter( &qmsg, rc ) ) // send through app filter
1075 RETURN( rc );
1076
1077 switch ( msg ) {
1078#if !defined (QT_NO_SESSIONMANAGER)
1079 case WM_SAVEAPPLICATION: {
1080#if defined (DEBUG_SESSIONMANAGER)
1081 qDebug( "WM_SAVEAPPLICATION: sm_gracefulShutdown=%d "
1082 "qt_about_to_destroy_wnd=%d (mp1=%p mp2=%p)",
1083 sm_gracefulShutdown, qt_about_to_destroy_wnd,
1084 mp1, mp2 );
1085#endif
1086 // PM seems to post this message to all top-level windows on system
1087 // shutdown, so react only to the first one. Also, this message is
1088 // always sent by WinDestroyWindow(), where it must be also ignored.
1089 if ( !qt_about_to_destroy_wnd && !sm_smActive &&
1090 !sm_gracefulShutdown ) {
1091 sm_smActive = TRUE;
1092 sm_gracefulShutdown = TRUE;
1093 sm_blockUserInput = TRUE; // prevent user-interaction outside interaction windows
1094 sm_cancel = FALSE;
1095 sm_quitSkipped = FALSE;
1096 if ( qt_session_manager_self )
1097 qApp->commitData( *qt_session_manager_self );
1098 sm_smActive = FALSE; // session management has been finished
1099 if ( sm_cancel ) {
1100 // Here we try to cancel the Extended XWorkplace shutdown.
1101 // If it's XWorkplace who sent us WM_SAVEAPPLICATION, then
1102 // it probably passed us non-NULL parameters, so that
1103 // mp1 = it's window handle and mp2 = WM_COMMAND code to
1104 // cancel the shutdown procedure.
1105 HWND shutdownHwnd = HWNDFROMMP(mp1);
1106 if ( WinIsWindow( 0, shutdownHwnd ) ) {
1107 WinPostMsg( shutdownHwnd, WM_COMMAND, mp2, 0 );
1108 // Ensure we will get WM_QUIT anyway, even if xwp was
1109 // not that fast to post it yet (we need it to correctly
1110 // finish the graceful shutdown procedure)
1111 sm_quitSkipped = TRUE;
1112 }
1113 }
1114 // repost WM_QUIT to ourselves because we might have ignored
1115 // it in qt_app_canQuit(), so will not get one anymore
1116 if ( sm_quitSkipped )
1117 WinPostMsg( hwnd, WM_QUIT, 0, 0 );
1118 }
1119 // PMREF recommends to pass it to WinDefWindowProc()
1120 rc = WinDefWindowProc( hwnd, msg, mp1, mp2 );
1121 RETURN( rc );
1122 }
1123#endif
1124
1125 case WM_SYSVALUECHANGED: {
1126 // This message is sent to all top-level widgets, handle only once
1127 QWidgetList *list = QApplication::topLevelWidgets();
1128 bool firstWidget = list->first()->winId() == hwnd;
1129 delete list;
1130 if ( !firstWidget )
1131 break;
1132 LONG from = (LONG) mp1;
1133 LONG to = (LONG) mp2;
1134 #define _IS_SV(sv) (from >= (sv) && to <= (sv))
1135 if ( _IS_SV( SV_WORKAREA_XLEFT ) || _IS_SV( SV_WORKAREA_XRIGHT ) ||
1136 _IS_SV( SV_WORKAREA_YBOTTOM ) || _IS_SV( SV_WORKAREA_YTOP ) ) {
1137 // send a special invalid resize event to QDesktopWidget
1138 QResizeEvent re( QSize( -1, -1 ), QSize( -1, -1 ) );
1139 QApplication::sendEvent( qt_desktopWidget, &re );
1140 /// @todo (dmik) enumerate all top-level widgets and
1141 // remaximize those that are maximized
1142 } else {
1143 /// @todo (dmik) call qt_set_pm_resources() in the way it is
1144 // done in WM_SYSCOLORCHANGE for relevant SV_ values.
1145 }
1146 #undef _IS_SV
1147 break;
1148 }
1149
1150 case WM_SYSCOLORCHANGE: {
1151 // This message is sent to all top-level widgets, handle only once
1152 QWidgetList *list = QApplication::topLevelWidgets();
1153 bool firstWidget = list->first()->winId() == hwnd;
1154 delete list;
1155 if ( !firstWidget )
1156 break;
1157 if ( qApp->type() == QApplication::Tty )
1158 break;
1159 if ( QApplication::desktopSettingsAware() ) {
1160 widget = (QETWidget*)QWidget::find( hwnd );
1161 if ( widget && !widget->parentWidget() )
1162 qt_set_pm_resources();
1163 }
1164 break;
1165 }
1166
1167 case WM_BUTTON1DOWN:
1168 case WM_BUTTON2DOWN:
1169 case WM_BUTTON3DOWN:
1170 if ( ignoreNextMouseReleaseEvent )
1171 ignoreNextMouseReleaseEvent = FALSE;
1172 break;
1173 case WM_BUTTON1UP:
1174 case WM_BUTTON2UP:
1175 case WM_BUTTON3UP:
1176 if ( ignoreNextMouseReleaseEvent ) {
1177 ignoreNextMouseReleaseEvent = FALSE;
1178 if ( qt_button_down && qt_button_down->winId() == autoCaptureWnd ) {
1179 releaseAutoCapture();
1180 qt_button_down = 0;
1181 }
1182 RETURN( TRUE );
1183 }
1184 break;
1185
1186 default:
1187 break;
1188 }
1189
1190 if ( !widget )
1191 widget = (QETWidget*)QWidget::find( hwnd );
1192 if ( !widget ) // don't know this widget
1193 goto do_default;
1194
1195 if ( app_do_modal ) { // modal event handling
1196 int ret = 0;
1197 if ( !qt_try_modal( widget, &qmsg, ret ) )
1198 return MRESULT( ret );
1199 }
1200
1201 if ( widget->pmEvent( &qmsg ) ) // send through widget filter
1202 RETURN( TRUE );
1203
1204 if ( isMouseEvent ) { // mouse events
1205 if ( qApp->activePopupWidget() != 0 ) { // in popup mode
1206 QWidget* w = QApplication::widgetAt( qmsg.ptl.x, qmsg.ptl.y, TRUE );
1207 if ( w ) {
1208 POINTL ptl = { SHORT1FROMMP(qmsg.mp1), SHORT2FROMMP(qmsg.mp1) };
1209 WinMapWindowPoints( qmsg.hwnd, w->winId(), &ptl, 1 );
1210 qmsg.mp1 = MPFROM2SHORT( ptl.x, ptl.y );
1211 widget = (QETWidget*)w;
1212 }
1213 }
1214 result = widget->translateMouseEvent( qmsg );
1215 rc = (MRESULT) result;
1216#ifndef QT_NO_WHEELEVENT
1217 } else if ( msg == WM_VSCROLL || msg == WM_HSCROLL ) {
1218 result = widget->translateWheelEvent( qmsg );
1219 rc = (MRESULT) result;
1220#endif
1221#ifndef QT_NO_DRAGANDDROP
1222 } else if ( msg >= WM_DRAGFIRST && msg <= WM_DRAGLAST ) {
1223 RETURN( qt_dispatchDragAndDrop( widget, qmsg ) );
1224#endif
1225 } else {
1226 switch ( msg ) {
1227
1228 case WM_TRANSLATEACCEL:
1229 if ( widget->isTopLevel() ) {
1230 rc = WinDefWindowProc( hwnd, msg, mp1, mp2 );
1231 if ( rc ) {
1232 QMSG &qmsg = *(QMSG*) mp1;
1233 if (
1234 qmsg.msg == WM_SYSCOMMAND &&
1235 WinWindowFromID( widget->winFId(), FID_SYSMENU )
1236 ) {
1237 switch ( SHORT1FROMMP(qmsg.mp1) ) {
1238 case SC_CLOSE:
1239 case SC_TASKMANAGER:
1240 RETURN( TRUE );
1241 default:
1242 break;
1243 }
1244 }
1245 }
1246 }
1247 // return FALSE in all other cases to let Qt process keystrokes
1248 // that are in the system-wide frame accelerator table.
1249 RETURN( FALSE );
1250
1251 case WM_CHAR: { // keyboard event
1252#if 0
1253 qDebug( "WM_CHAR: [%s]", widget->name() );
1254#endif
1255 QWidget *g = QWidget::keyboardGrabber();
1256 if ( g )
1257 widget = (QETWidget*)g;
1258 else if ( qApp->focusWidget() )
1259 widget = (QETWidget*)qApp->focusWidget();
1260 else if ( !widget )
1261/// @todo (dmik) currently we don't use WinSetFocus(). what for? Qt seems
1262// to completely handle focus traversal itself.
1263// || widget->winId() == WinQueryFocus( HWND_DESKTOP ) ) // We faked the message to go to exactly that widget.
1264 widget = (QETWidget*)widget->topLevelWidget();
1265 if ( widget->isEnabled() ) {
1266/// @todo (dmik) we should not pass WM_CHAR to the default window proc,
1267// otherwise it will come to us again through the widget parent (owner in PM)
1268// if the widget is not top-level, and will be treated by translateKeyEvent()
1269// as a key repeat (in case of key down) or a lost key (in case of key up).
1270// NOTE: currently we don't use WinSetFocus(), so the active top-level window
1271// will always have the focus, so it doesn't matter wheither we pass WM_CHAR
1272// to the default window proc or not.
1273// result = widget->translateKeyEvent( qmsg, g != 0 );
1274// rc = (MRESULT) result;
1275 RETURN( widget->translateKeyEvent( qmsg, g != 0 ) );
1276 }
1277 break;
1278 }
1279
1280/// @todo (dmik) later
1281// case WM_APPCOMMAND:
1282// {
1283// uint cmd = GET_APPCOMMAND_LPARAM(lParam);
1284// uint uDevice = GET_DEVICE_LPARAM(lParam);
1285// uint dwKeys = GET_KEYSTATE_LPARAM(lParam);
1286//
1287// int state = translateButtonState( dwKeys, QEvent::KeyPress, 0 );
1288//
1289// switch ( uDevice ) {
1290// case FAPPCOMMAND_KEY:
1291// {
1292// int key = 0;
1293//
1294// switch( cmd ) {
1295// case APPCOMMAND_BASS_BOOST:
1296// key = Qt::Key_BassBoost;
1297// break;
1298// case APPCOMMAND_BASS_DOWN:
1299// key = Qt::Key_BassDown;
1300// break;
1301// case APPCOMMAND_BASS_UP:
1302// key = Qt::Key_BassUp;
1303// break;
1304// case APPCOMMAND_BROWSER_BACKWARD:
1305// key = Qt::Key_Back;
1306// break;
1307// case APPCOMMAND_BROWSER_FAVORITES:
1308// key = Qt::Key_Favorites;
1309// break;
1310// case APPCOMMAND_BROWSER_FORWARD:
1311// key = Qt::Key_Forward;
1312// break;
1313// case APPCOMMAND_BROWSER_HOME:
1314// key = Qt::Key_HomePage;
1315// break;
1316// case APPCOMMAND_BROWSER_REFRESH:
1317// key = Qt::Key_Refresh;
1318// break;
1319// case APPCOMMAND_BROWSER_SEARCH:
1320// key = Qt::Key_Search;
1321// break;
1322// case APPCOMMAND_BROWSER_STOP:
1323// key = Qt::Key_Stop;
1324// break;
1325// case APPCOMMAND_LAUNCH_APP1:
1326// key = Qt::Key_Launch0;
1327// break;
1328// case APPCOMMAND_LAUNCH_APP2:
1329// key = Qt::Key_Launch1;
1330// break;
1331// case APPCOMMAND_LAUNCH_MAIL:
1332// key = Qt::Key_LaunchMail;
1333// break;
1334// case APPCOMMAND_LAUNCH_MEDIA_SELECT:
1335// key = Qt::Key_LaunchMedia;
1336// break;
1337// case APPCOMMAND_MEDIA_NEXTTRACK:
1338// key = Qt::Key_MediaNext;
1339// break;
1340// case APPCOMMAND_MEDIA_PLAY_PAUSE:
1341// key = Qt::Key_MediaPlay;
1342// break;
1343// case APPCOMMAND_MEDIA_PREVIOUSTRACK:
1344// key = Qt::Key_MediaPrev;
1345// break;
1346// case APPCOMMAND_MEDIA_STOP:
1347// key = Qt::Key_MediaStop;
1348// break;
1349// case APPCOMMAND_TREBLE_DOWN:
1350// key = Qt::Key_TrebleDown;
1351// break;
1352// case APPCOMMAND_TREBLE_UP:
1353// key = Qt::Key_TrebleUp;
1354// break;
1355// case APPCOMMAND_VOLUME_DOWN:
1356// key = Qt::Key_VolumeDown;
1357// break;
1358// case APPCOMMAND_VOLUME_MUTE:
1359// key = Qt::Key_VolumeMute;
1360// break;
1361// case APPCOMMAND_VOLUME_UP:
1362// key = Qt::Key_VolumeUp;
1363// break;
1364// default:
1365// break;
1366// }
1367// if ( key ) {
1368// bool res = FALSE;
1369// QWidget *g = QWidget::keyboardGrabber();
1370// if ( g )
1371// widget = (QETWidget*)g;
1372// else if ( qApp->focusWidget() )
1373// widget = (QETWidget*)qApp->focusWidget();
1374// else
1375// widget = (QETWidget*)widget->topLevelWidget();
1376// if ( widget->isEnabled() )
1377// res = ((QETWidget*)widget)->sendKeyEvent( QEvent::KeyPress, key, 0, state, FALSE, QString::null, g != 0 );
1378// if ( res )
1379// return TRUE;
1380// }
1381// }
1382// break;
1383//
1384// default:
1385// break;
1386// }
1387//
1388// result = FALSE;
1389// }
1390// break;
1391
1392//@@TODO (dmik):
1393// we can cause WC_FRAME to send equivalents of WM_NCMOUSEMOVE,
1394// but is it really necesary? We already do some similar stuff in WM_HITTEST
1395//#ifndef Q_OS_TEMP
1396// case WM_NCMOUSEMOVE:
1397// {
1398// // span the application wide cursor over the
1399// // non-client area.
1400// QCursor *c = qt_grab_cursor();
1401// if ( !c )
1402// c = QApplication::overrideCursor();
1403// if ( c ) // application cursor defined
1404// SetCursor( c->handle() );
1405// else
1406// result = FALSE;
1407// // generate leave event also when the caret enters
1408// // the non-client area.
1409// qt_dispatchEnterLeave( 0, QWidget::find(curWin) );
1410// curWin = 0;
1411// }
1412// break;
1413//#endif
1414
1415/// @todo (dmik) later (WM_KBDLAYERCHANGED (0xBD4)?)
1416// case WM_SETTINGCHANGE:
1417// if ( !msg.wParam ) {
1418// QString area = QT_WA_INLINE( QString::fromUcs2( (unsigned short *)msg.lParam ),
1419// QString::fromLocal8Bit( (char*)msg.lParam ) );
1420// if ( area == "intl" )
1421// QApplication::postEvent( widget, new QEvent( QEvent::LocaleChange ) );
1422// }
1423// break;
1424//
1425 case WM_PAINT: // paint event
1426 result = widget->translatePaintEvent( qmsg );
1427 break;
1428 case WM_ERASEBACKGROUND: // erase window background
1429 // flush WM_PAINT messages here to update window contents
1430 // instantly while tracking the resize frame (normally these
1431 // messages are delivered after the user has stopped resizing
1432 // for some time). this slows down resizing slightly but gives a
1433 // better look (no invalid window contents can be seen during
1434 // resize). the alternative could be to erase the background only,
1435 // but we need to do it for every non-toplevel window, which can
1436 // also be time-consuming (WM_ERASEBACKGROUND is sent to WC_FRAME
1437 // clients only, so we would have to do all calculations ourselves).
1438 WinUpdateWindow( widget->winId() );
1439 RETURN( FALSE );
1440 break;
1441 case WM_CALCVALIDRECTS:
1442 // we must always return this value here to cause PM to reposition
1443 // our children accordingly (othwerwise we would have to do it
1444 // ourselves to keep them top-left aligned).
1445 RETURN( CVR_ALIGNLEFT | CVR_ALIGNTOP );
1446 break;
1447
1448 case WM_MOVE: // move window
1449 case WM_SIZE: // resize window
1450 result = widget->translateConfigEvent( qmsg );
1451 break;
1452
1453 case WM_ACTIVATE:
1454#if 0
1455 qDebug( "WM_ACTIVATE: [%s] %d", widget->name(), SHORT1FROMMP(mp1) );
1456#endif
1457 qApp->pmFocus( widget, SHORT1FROMMP(mp1) );
1458 break;
1459
1460 case WM_SETFOCUS:
1461#if 0
1462 qDebug( "WM_SETFOCUS: [%s] %s [%s]", widget->name(),
1463 SHORT1FROMMP(mp2) ? "<=" : "=>",
1464 QWidget::find( (HWND)mp1 ) ? QWidget::find( (HWND)mp1 )->name()
1465 : "{foreign}" );
1466#endif
1467 result = FALSE;
1468 if ( !SHORT1FROMMP(mp2) ) {
1469 // we're losing focus
1470 if ( !QWidget::find( (HWND)mp1 ) ) {
1471 if ( QApplication::activePopupWidget() ) {
1472 // Another application was activated while our popups are open,
1473 // then close all popups. In case some popup refuses to close,
1474 // we give up after 1024 attempts (to avoid an infinite loop).
1475 int maxiter = 1024;
1476 QWidget *popup;
1477 while ( (popup=QApplication::activePopupWidget()) && maxiter-- )
1478 popup->close();
1479 }
1480 if (
1481 // non-Qt ownees of our WC_FRAME window (such as
1482 // FID_SYSMENU) should not cause the focus to be lost.
1483 WinQueryWindow( (HWND)mp1, QW_OWNER ) ==
1484 widget->topLevelWidget()->winFId()
1485 )
1486 break;
1487 if ( !widget->isTopLevel() )
1488 qApp->pmFocus( widget, SHORT1FROMMP(mp2) );
1489 }
1490 }
1491 break;
1492
1493/// @todo (dmik) remove?
1494//#ifndef Q_OS_TEMP
1495// case WM_MOUSEACTIVATE:
1496// {
1497// const QWidget *tlw = widget->topLevelWidget();
1498// // Do not change activation if the clicked widget is inside a floating dock window
1499// if ( tlw->inherits( "QDockWindow" ) && qApp->activeWindow()
1500// && !qApp->activeWindow()->inherits("QDockWindow") )
1501// RETURN(MA_NOACTIVATE);
1502// }
1503// result = FALSE;
1504// break;
1505//#endif
1506
1507 case WM_SHOW:
1508 if ( !SHORT1FROMMP(mp1) && autoCaptureWnd == widget->winId() )
1509 releaseAutoCapture();
1510 result = FALSE;
1511 break;
1512/// @todo (dmik) remove later
1513// case WM_SHOWWINDOW:
1514//#ifndef Q_OS_TEMP
1515// if ( lParam == SW_PARENTOPENING ) {
1516// if ( widget->testWState(Qt::WState_ForceHide) )
1517// RETURN(0);
1518// }
1519//#endif
1520// if (!wParam && autoCaptureWnd == widget->winId())
1521// releaseAutoCapture();
1522// result = FALSE;
1523// break;
1524
1525 case WM_REALIZEPALETTE: // realize own palette
1526 if ( QColor::hPal() ) {
1527 HPS hps = WinGetPS( widget->winId() );
1528 GpiSelectPalette( hps, QColor::hPal() );
1529 ULONG cclr;
1530 WinRealizePalette( widget->winId(), hps, &cclr );
1531 WinReleasePS( hps );
1532 // on OS/2, the value returned by WinRealizePalette() does not
1533 // necessarily reflect the number of colors that have been
1534 // remapped. therefore, we cannot rely on it and must always
1535 // invalidate the window.
1536 WinInvalidateRect( widget->winId(), NULL, TRUE );
1537 RETURN( 0 );
1538 }
1539 break;
1540
1541 case WM_CLOSE: // close window
1542 widget->translateCloseEvent( qmsg );
1543 RETURN(0); // always handled
1544
1545/// @todo (dmik) it seems we don't need this
1546// case WM_DESTROY: // destroy window
1547// if ( hwnd == curWin ) {
1548// QEvent leave( QEvent::Leave );
1549// QApplication::sendEvent( widget, &leave );
1550// curWin = 0;
1551// }
1552// // We are blown away when our parent reparents, so we have to
1553// // recreate the handle
1554// if (widget->testWState(Qt::WState_Created))
1555// ((QETWidget*)widget)->reparentWorkaround();
1556// if ( widget == popupButtonFocus )
1557// popupButtonFocus = 0;
1558// result = FALSE;
1559// break;
1560
1561 case WM_CONTEXTMENU:
1562 if ( SHORT2FROMMP(mp2) ) {
1563 // keyboard event
1564 QWidget *fw = qApp->focusWidget();
1565 if ( fw ) {
1566 QContextMenuEvent e(
1567 QContextMenuEvent::Keyboard,
1568 QPoint( 5, 5 ),
1569 fw->mapToGlobal( QPoint( 5, 5 ) ),
1570 0
1571 );
1572 result = qt_sendSpontaneousEvent( fw, &e );
1573 rc = (MRESULT) result;
1574 }
1575 } else {
1576 // mouse event
1577 result = widget->translateMouseEvent( qmsg );
1578 rc = (MRESULT) result;
1579 }
1580 break;
1581
1582/// @todo (dmik) remove?
1583// case WM_IME_STARTCOMPOSITION:
1584// result = QInputContext::startComposition();
1585// break;
1586// case WM_IME_ENDCOMPOSITION:
1587// result = QInputContext::endComposition();
1588// break;
1589// case WM_IME_COMPOSITION:
1590// result = QInputContext::composition( lParam );
1591// break;
1592
1593#ifndef QT_NO_CLIPBOARD
1594 case WM_DRAWCLIPBOARD:
1595 case WM_RENDERFMT:
1596 case WM_RENDERALLFMTS:
1597 case WM_DESTROYCLIPBOARD:
1598 if ( qt_clipboard ) {
1599 QCustomEvent e( QEvent::Clipboard, &qmsg );
1600 qt_sendSpontaneousEvent( qt_clipboard, &e );
1601 RETURN(0);
1602 }
1603 result = FALSE;
1604 break;
1605#endif
1606
1607/// @todo (dmik) remove? functionality is implemented in WM_SETFOCUS above.
1608// case WM_KILLFOCUS:
1609// if ( !QWidget::find( (HWND)wParam ) ) { // we don't get focus, so unset it now
1610// if ( !widget->hasFocus() ) // work around Windows bug after minimizing/restoring
1611// widget = (QETWidget*)qApp->focusWidget();
1612// HWND focus = ::GetFocus();
1613// if ( !widget || (focus && ::IsChild( widget->winId(), focus )) ) {
1614// result = FALSE;
1615// } else {
1616// widget->clearFocus();
1617// result = TRUE;
1618// }
1619// } else {
1620// result = FALSE;
1621// }
1622// break;
1623
1624//@@TODO (dmik): later
1625// case WM_THEMECHANGED:
1626// if ( widget->testWFlags( Qt::WType_Desktop ) || !qApp || qApp->closingDown()
1627// || qApp->type() == QApplication::Tty )
1628// break;
1629//
1630// if ( widget->testWState(Qt::WState_Polished) )
1631// qApp->style().unPolish(widget);
1632//
1633// if ( widget->testWState(Qt::WState_Polished) )
1634// qApp->style().polish(widget);
1635// widget->repolishStyle( qApp->style() );
1636// if ( widget->isVisible() )
1637// widget->update();
1638// break;
1639//
1640// case WM_COMMAND:
1641// result = (wParam == 0x1);
1642// if ( result )
1643// QApplication::postEvent( widget, new QEvent( QEvent::OkRequest ) );
1644// break;
1645// case WM_HELP:
1646// QApplication::postEvent( widget, new QEvent( QEvent::HelpRequest ) );
1647// result = TRUE;
1648// break;
1649//#endif
1650
1651 case WM_U_MOUSELEAVE:
1652 // We receive a mouse leave for curWin, meaning
1653 // the mouse was moved outside our widgets
1654 if ( widget->winId() == curWin && (HWND) mp1 == curWin ) {
1655 bool dispatch = !widget->hasMouse();
1656 // hasMouse is updated when dispatching enter/leave,
1657 // so test if it is actually up-to-date
1658 if ( !dispatch ) {
1659 QRect geom = widget->geometry();
1660 if ( widget->parentWidget() && !widget->isTopLevel() ) {
1661 QPoint gp = widget->parentWidget()->mapToGlobal( widget->pos() );
1662 geom.setX( gp.x() );
1663 geom.setY( gp.y() );
1664 }
1665 QPoint cpos = QCursor::pos();
1666 dispatch = !geom.contains( cpos );
1667 }
1668 if ( dispatch ) {
1669 qt_dispatchEnterLeave( 0, QWidget::find( (WId)curWin ) );
1670 curWin = 0;
1671 }
1672 }
1673 break;
1674
1675/// @todo (dmik) remove? functionality is implemented in WM_SETFOCUS above.
1676// case WM_CANCELMODE:
1677// if ( qApp->focusWidget() ) {
1678// QFocusEvent::setReason( QFocusEvent::ActiveWindow );
1679// QFocusEvent e( QEvent::FocusOut );
1680// QApplication::sendEvent( qApp->focusWidget(), &e );
1681// QFocusEvent::resetReason();
1682// }
1683// break;
1684
1685 default:
1686 result = FALSE; // event was not processed
1687 break;
1688 }
1689 }
1690
1691 if ( evt_type != QEvent::None ) { // simple event
1692 QEvent e( evt_type );
1693 result = qt_sendSpontaneousEvent( widget, &e );
1694 }
1695 if ( result )
1696 RETURN( rc );
1697
1698do_default:
1699 RETURN( WinDefWindowProc( hwnd, msg, mp1, mp2 ) );
1700/// @todo (dmik) remove?
1701// RETURN( QInputContext::DefWindowProc(hwnd,message,wParam,lParam) )
1702}
1703
1704PFNWP QtOldFrameProc = 0;
1705
1706extern "C" MRESULT EXPENTRY QtFrameProc( HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2 )
1707{
1708 // message handling indicators: if result is true at the end of message
1709 // processing, no default window proc is called but rc is returned.
1710 bool result = FALSE;
1711 MRESULT rc = (MRESULT) FALSE;
1712 QETWidget *widget = 0;
1713 HWND hwndC = 0;
1714
1715 if ( !qApp ) // unstable app state
1716 goto do_default;
1717
1718 // make sure we show widgets (e.g. scrollbars) when the user resizes
1719 if ( inLoop && qApp->loopLevel() )
1720 qApp->sendPostedEvents( 0, QEvent::ShowWindowRequest );
1721
1722 inLoop = TRUE;
1723
1724 hwndC = WinWindowFromID( hwnd, FID_CLIENT );
1725 widget = (QETWidget*)QWidget::find( hwndC );
1726 if ( !widget ) // don't know this widget
1727 goto do_default;
1728
1729 switch ( msg ) {
1730 case WM_HITTEST: {
1731 if ( !WinIsWindowEnabled( hwnd ) ) {
1732 if (
1733 qApp->activePopupWidget() &&
1734 (WinQueryQueueStatus( HWND_DESKTOP ) & QS_MOUSEBUTTON)
1735 ) {
1736 // the user has clicked over the Qt window that is disabled
1737 // by some modal widget, therefore we close all popups. In
1738 // case some popup refuses to close, we give up after 1024
1739 // attempts (to avoid an infinite loop).
1740 int maxiter = 1024;
1741 QWidget *popup;
1742 while ( (popup=QApplication::activePopupWidget()) && maxiter-- )
1743 popup->close();
1744 }
1745#ifndef QT_NO_CURSOR
1746 else {
1747 QCursor *c = qt_grab_cursor();
1748 if ( !c )
1749 c = QApplication::overrideCursor();
1750 if ( c ) // application cursor defined
1751 WinSetPointer( HWND_DESKTOP, c->handle() );
1752 else
1753 WinSetPointer( HWND_DESKTOP, Qt::arrowCursor.handle() );
1754 }
1755#endif
1756 }
1757 break;
1758 }
1759
1760 case WM_ADJUSTWINDOWPOS: {
1761 SWP &swp = *(PSWP) mp1;
1762 if ( swp.fl & SWP_MAXIMIZE ) {
1763 QWExtra *x = widget->xtra();
1764 if ( x ) {
1765 result = TRUE;
1766 rc = QtOldFrameProc( hwnd, msg, mp1, mp2 );
1767 int maxw = QWIDGETSIZE_MAX, maxh = QWIDGETSIZE_MAX;
1768 QTLWExtra *top = widget->top();
1769 if ( x->maxw < QWIDGETSIZE_MAX )
1770 maxw = x->maxw + top->fleft + top->fright;
1771 if ( x->maxh < QWIDGETSIZE_MAX )
1772 maxh = x->maxh + top->ftop + top->fbottom;
1773 if ( maxw < QWIDGETSIZE_MAX ) swp.cx = maxw;
1774 if ( maxh < QWIDGETSIZE_MAX ) {
1775 swp.y = swp.y + swp.cy - maxh;
1776 swp.cy = maxh;
1777 }
1778 }
1779 }
1780 if ( (swp.fl & SWP_RESTORE) &&
1781 !(swp.fl & (SWP_MOVE | SWP_SIZE)) ) {
1782 QRect r = widget->top()->normalGeometry;
1783 if ( r.isValid() ) {
1784 // store normal geometry in window words
1785 USHORT x = r.x();
1786 USHORT y = r.y();
1787 USHORT w = r.width();
1788 USHORT h = r.height();
1789 // flip y coordinate
1790 y = QApplication::desktop()->height() - (y + h);
1791 WinSetWindowUShort( hwnd, QWS_XRESTORE, x );
1792 WinSetWindowUShort( hwnd, QWS_YRESTORE, y );
1793 WinSetWindowUShort( hwnd, QWS_CXRESTORE, w );
1794 WinSetWindowUShort( hwnd, QWS_CYRESTORE, h );
1795 widget->top()->normalGeometry.setWidth( 0 );
1796 }
1797 }
1798 if ( swp.fl & (SWP_ACTIVATE | SWP_ZORDER) ) {
1799 // get the modal widget that made this window blocked
1800 QWidget *m =
1801 (QWidget*) WinQueryWindowPtr( widget->winId(), QWL_QTMODAL );
1802 if( m ) {
1803 if ( swp.fl & SWP_ACTIVATE ) {
1804 QWidget *a = qt_modal_stack->first();
1805 if ( !a->isActiveWindow() )
1806 a->setActiveWindow();
1807 swp.fl &= ~SWP_ACTIVATE;
1808 }
1809 if ( swp.fl & SWP_ZORDER ) {
1810 QWidget *mp = m->parentWidget();
1811 if ( mp ) {
1812 mp = mp->topLevelWidget();
1813 if ( !mp->isDesktop() && mp != widget )
1814 m = mp;
1815 }
1816 HWND hm = m->winFId();
1817 if ( swp.hwndInsertBehind != hm ) {
1818 swp.hwndInsertBehind = hm;
1819 }
1820 }
1821 }
1822 }
1823 break;
1824 }
1825
1826 case WM_QUERYTRACKINFO: {
1827 QWExtra *x = widget->xtra();
1828 if ( x ) {
1829 result = TRUE;
1830 rc = QtOldFrameProc( hwnd, msg, mp1, mp2 );
1831 PTRACKINFO pti = (PTRACKINFO) mp2;
1832 int minw = 0, minh = 0;
1833 int maxw = QWIDGETSIZE_MAX, maxh = QWIDGETSIZE_MAX;
1834 QTLWExtra *top = widget->top();
1835 if ( x->minw > 0 )
1836 minw = x->minw + top->fleft + top->fright;
1837 if ( x->minh > 0 )
1838 minh = x->minh + top->ftop + top->fbottom;
1839 if ( x->maxw < QWIDGETSIZE_MAX )
1840 maxw = x->maxw + top->fleft + top->fright;
1841 if ( x->maxh < QWIDGETSIZE_MAX )
1842 maxh = x->maxh + top->ftop + top->fbottom;
1843 // obey system recommended minimum size (to emulate Qt/Win32)
1844 pti->ptlMinTrackSize.x = QMAX( minw, pti->ptlMinTrackSize.x );
1845 pti->ptlMinTrackSize.y = QMAX( minh, pti->ptlMinTrackSize.y );
1846 pti->ptlMaxTrackSize.x = maxw;
1847 pti->ptlMaxTrackSize.y = maxh;
1848 }
1849 break;
1850 }
1851
1852 case WM_TRACKFRAME: {
1853 if ( QApplication::activePopupWidget() ) {
1854 // The user starts to size/move the frame window, therefore
1855 // we close all popups. In case some popup refuses to close,
1856 // we give up after 1024 attempts (to avoid an infinite loop).
1857 int maxiter = 1024;
1858 QWidget *popup;
1859 while ( (popup=QApplication::activePopupWidget()) && maxiter-- )
1860 popup->close();
1861 }
1862 break;
1863 }
1864
1865 case WM_WINDOWPOSCHANGED: {
1866 // We detect spontaneous min/max/restore events here instead of
1867 // WM_MINMAXFRAME, because WM_MINMAXFRAME is a pre-process message
1868 // (i.e. no actual changes have been made) We need actual changes
1869 // in order to update the frame strut and send WindowStateChange.
1870 result = TRUE;
1871 rc = QtOldFrameProc( hwnd, msg, mp1, mp2 );
1872 ULONG awp = LONGFROMMP( mp2 );
1873 bool window_state_change = FALSE;
1874 if ( awp & AWP_MAXIMIZED ) {
1875 window_state_change = TRUE;
1876 widget->setWState( Qt::WState_Maximized );
1877 widget->clearWState( Qt::WState_FullScreen );
1878 if ( widget->isMinimized() ) {
1879 widget->clearWState( Qt::WState_Minimized );
1880 widget->showChildren( TRUE );
1881 QShowEvent e;
1882 qt_sendSpontaneousEvent( widget, &e );
1883 }
1884 } else if ( awp & AWP_MINIMIZED ) {
1885 window_state_change = TRUE;
1886 widget->setWState( Qt::WState_Minimized );
1887 widget->clearWState( Qt::WState_Maximized );
1888 widget->clearWState( Qt::WState_FullScreen );
1889 if ( widget->isVisible() ) {
1890 QHideEvent e;
1891 qt_sendSpontaneousEvent( widget, &e );
1892 widget->hideChildren( TRUE );
1893 }
1894 } else if ( awp & AWP_RESTORED ) {
1895 window_state_change = TRUE;
1896 if ( widget->isMinimized() ) {
1897 widget->showChildren( TRUE );
1898 QShowEvent e;
1899 qt_sendSpontaneousEvent( widget, &e );
1900 }
1901 widget->clearWState( Qt::WState_Minimized );
1902 widget->clearWState( Qt::WState_Maximized );
1903 }
1904 if ( window_state_change ) {
1905 widget->updateFrameStrut();
1906 if ( !widget->top()->in_sendWindowState ) {
1907 // send WindowStateChange event if this message is NOT
1908 // originated from QWidget::setWindowState().
1909 QEvent e( QEvent::WindowStateChange );
1910 qt_sendSpontaneousEvent( widget, &e );
1911 }
1912 }
1913 break;
1914 }
1915
1916 default:
1917 break;
1918 }
1919
1920 if ( result )
1921 RETURN( rc );
1922
1923do_default:
1924 RETURN( QtOldFrameProc( hwnd, msg, mp1, mp2 ) );
1925}
1926
1927/*****************************************************************************
1928 Modal widgets; We have implemented our own modal widget mechanism
1929 to get total control.
1930 A modal widget without a parent becomes application-modal.
1931 A modal widget with a parent becomes modal to its parent and grandparents..
1932
1933//@@TODO (dmik): the above comment is not correct (outdated?): for example,
1934// in accordance with the current Qt logic, a modal widget with a parent
1935// becomes modal to its (grand)parents upto the first of them that has the
1936// WGroupLeader flag, not necessarily to all...
1937
1938 qt_enter_modal()
1939 Enters modal state
1940 Arguments:
1941 QWidget *widget A modal widget
1942
1943 qt_leave_modal()
1944 Leaves modal state for a widget
1945 Arguments:
1946 QWidget *widget A modal widget
1947 *****************************************************************************/
1948
1949Q_EXPORT bool qt_modal_state()
1950{
1951 return app_do_modal;
1952}
1953
1954// helper for qt_dispatchBlocked().
1955// sends block events to the given widget and its children.
1956void qt_sendBlocked( QObject *obj, QWidget *modal, QEvent *e, bool override )
1957{
1958 if ( obj == modal ) {
1959 // don't touch modal itself and its children
1960 return;
1961 }
1962 bool blocked = e->type() == QEvent::WindowBlocked;
1963
1964 if ( obj->isWidgetType() ) {
1965 QWidget *w = (QWidget*) obj;
1966 if ( w->isTopLevel() ) {
1967 if ( w->testWFlags( Qt::WGroupLeader ) && !override ) {
1968 // stop sending on group leaders
1969 return;
1970 }
1971 QWidget *blockedBy =
1972 (QWidget*) WinQueryWindowPtr( w->winId(), QWL_QTMODAL );
1973 if ( blocked ) {
1974 // stop sending on alreay blocked widgets
1975 if ( blockedBy )
1976 return;
1977 } else {
1978 // stop sending on widgets blocked by another modal
1979 if ( blockedBy != modal )
1980 return;
1981 }
1982 WinSetWindowPtr( w->winId(), QWL_QTMODAL, blocked ? modal : NULL );
1983 WinEnableWindow( w->winFId(), !blocked );
1984 }
1985 }
1986 QApplication::sendEvent( obj, e );
1987
1988 // now send blocked to children
1989 if ( obj->children() ) {
1990 QObjectListIt it( *obj->children() );
1991 QObject *o;
1992 while( ( o = it.current() ) != 0 ) {
1993 ++it;
1994 qt_sendBlocked( o, modal, e, blocked ? FALSE : TRUE );
1995 }
1996 }
1997}
1998
1999// sends blocked/unblocked events to top-level widgets depending on the
2000// modal widget given and the WGroupLeader flag presence.
2001static void qt_dispatchBlocked( QWidget *modal, bool blocked )
2002{
2003 // we process those top-level windows that must be blocked
2004 // by the given modal -- this should correlate with the
2005 // qt_tryModalHelper() logic that is widget-centric (i.e. a try
2006 // to block a particular widget given the current set of
2007 // modals) rather than modal-centric (a try to block the current set
2008 // of widgets given a particular modal); currently it blocks events
2009 // for a top-level widget if the widget doesn't have (a parent with
2010 // with) the WGroupLeader flag or it has but (this parent) has a child
2011 // among the current set of modals. So, the modal-centric logic is
2012 // to block any top-level widget unless it has (a parent with) the
2013 // WGroupLeader flag and (this parent) is not (a child of) the modal's
2014 // group leader.
2015
2016 QEvent e( blocked ? QEvent::WindowBlocked : QEvent::WindowUnblocked );
2017
2018 if ( !blocked ) {
2019 // For unblocking, we go through all top-level widgets and unblock
2020 // those that have been blocked by the given modal widget. It means that
2021 // we don't select widgets to unblock based on widget flags (as we do
2022 // below when blocking) because these flags might have been changed
2023 // so that we could skip widgets that we would have to unblock. Although
2024 // normally widget flags should never be changed after widget creation
2025 // (except that by QWidget::reparentSys() that takes blocking into
2026 // account and corrects it as needed), nothing stops one from doing so,
2027 // leading to a case when the QWL_QTMODAL word of some widget still
2028 // points to a widget that has been deleted (so that any attempt to
2029 // access it results into a nasty segfault).
2030 QWidgetList *list = QApplication::topLevelWidgets();
2031 for( QWidget *w = list->first(); w; w = list->next() ) {
2032 if ( WinQueryWindowPtr( w->winId(), QWL_QTMODAL ) == modal )
2033 qt_sendBlocked( w, modal, &e, TRUE );
2034 }
2035 delete list;
2036 return;
2037 }
2038
2039 // find the modal's group leader
2040 QWidget *mgl = modal->parentWidget();
2041 while ( mgl && !mgl->testWFlags( Qt::WGroupLeader ) )
2042 mgl = mgl->parentWidget();
2043 if ( mgl ) {
2044 mgl = mgl->topLevelWidget();
2045 if ( mgl->isDesktop() )
2046 mgl = 0;
2047 }
2048
2049 QWidgetList *list = QApplication::topLevelWidgets();
2050 for( QWidget *w = list->first(); w; w = list->next() ) {
2051 if (
2052 !w->isDesktop() && !w->isPopup() &&
2053 !w->testWFlags( Qt::WGroupLeader ) &&
2054 (!w->parentWidget() || w->parentWidget()->isDesktop())
2055 ) {
2056 qt_sendBlocked( w, modal, &e, FALSE );
2057 }
2058 }
2059 delete list;
2060
2061 if ( mgl ) {
2062 // send blocked to modal's group leader
2063 qt_sendBlocked( mgl, modal, &e, TRUE );
2064 }
2065 // qt_tryModalHelper() also assumes that the toppest modal widget blocks
2066 // other modals, regardless of WGroupLeader flags in parents. do the same.
2067 // Note: the given modal is not yet at the stack here.
2068 if ( qt_modal_stack ) {
2069 QWidget *m = qt_modal_stack->first();
2070 while ( m ) {
2071 qt_sendBlocked( m, modal, &e, TRUE );
2072 m = qt_modal_stack->next();
2073 }
2074 }
2075}
2076
2077static inline bool isChildOf( QWidget * child, QWidget * parent )
2078{
2079 if ( !parent || !child )
2080 return FALSE;
2081 QWidget * w = child;
2082 while( w && w != parent )
2083 w = w->parentWidget();
2084 return w != 0;
2085}
2086
2087Q_EXPORT void qt_enter_modal( QWidget *widget )
2088{
2089 if ( !qt_modal_stack ) { // create modal stack
2090 qt_modal_stack = new QWidgetList;
2091 Q_CHECK_PTR( qt_modal_stack );
2092 }
2093
2094 if ( qt_modal_stack->containsRef( widget ) )
2095 return; // already modal
2096
2097 QWidget *m = qt_modal_stack->first();
2098 while ( m ) {
2099 if ( isChildOf( m, widget ) )
2100 return; // child is already modal (prevent endless recursion)
2101 m = qt_modal_stack->next();
2102 }
2103
2104//@@TODO (dmik): Qt/Win32 sends WindowBlocked/WindowUnblocked events only
2105// to the direct parent of the modal widget. Why? We use qt_dispatchBlocked()
2106// to send them to all windows that do not get input when the given widget
2107// is modal; also we disable/enable that windows there, which is essential
2108// for modality support in Qt/OS2 (code in QtOldFrameProc() and qt_try_modal()
2109// functionality depend on it).
2110//
2111// if (widget->parentWidget()) {
2112// QEvent e(QEvent::WindowBlocked);
2113// QApplication::sendEvent(widget->parentWidget(), &e);
2114// WinEnableWindow( widget->parentWidget()->winFId(), FALSE );
2115// }
2116 qt_dispatchBlocked( widget, TRUE );
2117
2118 releaseAutoCapture();
2119 qt_dispatchEnterLeave( 0, QWidget::find((WId)curWin));
2120 qt_modal_stack->insert( 0, widget );
2121 app_do_modal = TRUE;
2122 curWin = 0;
2123 qt_button_down = 0;
2124 ignoreNextMouseReleaseEvent = FALSE;
2125}
2126
2127
2128Q_EXPORT void qt_leave_modal( QWidget *widget )
2129{
2130 if ( qt_modal_stack && qt_modal_stack->removeRef(widget) ) {
2131 if ( qt_modal_stack->isEmpty() ) {
2132 delete qt_modal_stack;
2133 qt_modal_stack = 0;
2134 QPoint p( QCursor::pos() );
2135 app_do_modal = FALSE; // necessary, we may get recursively into qt_try_modal below
2136 QWidget* w = QApplication::widgetAt( p.x(), p.y(), TRUE );
2137 qt_dispatchEnterLeave( w, QWidget::find( curWin ) ); // send synthetic enter event
2138 curWin = w? w->winId() : 0;
2139 }
2140 ignoreNextMouseReleaseEvent = TRUE;
2141
2142 qt_dispatchBlocked( widget, FALSE );
2143 }
2144 app_do_modal = qt_modal_stack != 0;
2145
2146//@@TODO (dmik): see the comments inside qt_enter_modal()
2147//
2148// if (widget->parentWidget()) {
2149// WinEnableWindow( widget->parentWidget()->winFId(), TRUE );
2150// QEvent e(QEvent::WindowUnblocked);
2151// QApplication::sendEvent(widget->parentWidget(), &e);
2152// }
2153}
2154
2155static bool qt_blocked_modal( QWidget *widget )
2156{
2157 if ( !app_do_modal )
2158 return FALSE;
2159 if ( qApp->activePopupWidget() )
2160 return FALSE;
2161 if ( widget->testWFlags(Qt::WStyle_Tool) ) // allow tool windows
2162 return FALSE;
2163
2164 QWidget *modal=0, *top=qt_modal_stack->getFirst();
2165
2166 widget = widget->topLevelWidget();
2167 if ( widget->testWFlags(Qt::WShowModal) ) // widget is modal
2168 modal = widget;
2169 if ( !top || modal == top ) // don't block event
2170 return FALSE;
2171 return TRUE;
2172}
2173
2174static bool qt_try_modal( QWidget *widget, QMSG *qmsg, int &ret )
2175{
2176 QWidget * top = 0;
2177
2178 if ( qt_tryModalHelper( widget, &top ) )
2179 return TRUE;
2180
2181 bool block_event = FALSE;
2182 ULONG type = qmsg->msg;
2183
2184 if ( type == WM_CLOSE ) {
2185 block_event = TRUE;
2186 }
2187
2188 return !block_event;
2189}
2190
2191
2192/*****************************************************************************
2193 Popup widget mechanism
2194
2195 openPopup()
2196 Adds a widget to the list of popup widgets
2197 Arguments:
2198 QWidget *widget The popup widget to be added
2199
2200 closePopup()
2201 Removes a widget from the list of popup widgets
2202 Arguments:
2203 QWidget *widget The popup widget to be removed
2204 *****************************************************************************/
2205
2206void QApplication::openPopup( QWidget *popup )
2207{
2208 if ( !popupWidgets ) { // create list
2209 popupWidgets = new QWidgetList;
2210 Q_CHECK_PTR( popupWidgets );
2211 }
2212 popupWidgets->append( popup ); // add to end of list
2213 if ( !popup->isEnabled() )
2214 return;
2215
2216 if ( popupWidgets->count() == 1 && !qt_nograb() )
2217 setAutoCapture( popup->winId() ); // grab mouse/keyboard
2218 // Popups are not focus-handled by the window system (the first
2219 // popup grabbed the keyboard), so we have to do that manually: A
2220 // new popup gets the focus
2221 QFocusEvent::setReason( QFocusEvent::Popup );
2222 if ( popup->focusWidget())
2223 popup->focusWidget()->setFocus();
2224 else
2225 popup->setFocus();
2226 QFocusEvent::resetReason();
2227}
2228
2229void QApplication::closePopup( QWidget *popup )
2230{
2231 if ( !popupWidgets )
2232 return;
2233 popupWidgets->removeRef( popup );
2234 POINTL curPos;
2235 WinQueryPointerPos( HWND_DESKTOP, &curPos );
2236 // flip y coordinate
2237 curPos.y = desktop()->height() - (curPos.y + 1);
2238 replayPopupMouseEvent = !popup->geometry().contains( QPoint(curPos.x, curPos.y) );
2239 if ( popupWidgets->count() == 0 ) { // this was the last popup
2240 popupCloseDownMode = TRUE; // control mouse events
2241 delete popupWidgets;
2242 popupWidgets = 0;
2243 if ( !popup->isEnabled() )
2244 return;
2245 if ( !qt_nograb() ) // grabbing not disabled
2246 releaseAutoCapture();
2247 if ( active_window ) {
2248 QFocusEvent::setReason( QFocusEvent::Popup );
2249 if ( active_window->focusWidget() )
2250 active_window->focusWidget()->setFocus();
2251 else
2252 active_window->setFocus();
2253 QFocusEvent::resetReason();
2254 }
2255 } else {
2256 // Popups are not focus-handled by the window system (the
2257 // first popup grabbed the keyboard), so we have to do that
2258 // manually: A popup was closed, so the previous popup gets
2259 // the focus.
2260 QFocusEvent::setReason( QFocusEvent::Popup );
2261 QWidget* aw = popupWidgets->getLast();
2262 if ( popupWidgets->count() == 1 )
2263 setAutoCapture( aw->winId() );
2264 if (aw->focusWidget())
2265 aw->focusWidget()->setFocus();
2266 else
2267 aw->setFocus();
2268 QFocusEvent::resetReason();
2269 }
2270}
2271
2272
2273
2274/*****************************************************************************
2275 Event translation; translates OS/2 PM events to Qt events
2276 *****************************************************************************/
2277
2278// State holder for LWIN/RWIN and ALTGr keys
2279// (ALTGr is also necessary since OS/2 doesn't report ALTGr as KC_ALT)
2280static int qt_extraKeyState = 0;
2281
2282static int mouseButtonState()
2283{
2284 int state = 0;
2285
2286 if ( WinGetKeyState( HWND_DESKTOP, VK_BUTTON1 ) & 0x8000 )
2287 state |= Qt::LeftButton;
2288 if ( WinGetKeyState( HWND_DESKTOP, VK_BUTTON2 ) & 0x8000 )
2289 state |= Qt::RightButton;
2290 if ( WinGetKeyState( HWND_DESKTOP, VK_BUTTON3 ) & 0x8000 )
2291 state |= Qt::MidButton;
2292
2293 return state;
2294}
2295
2296//
2297// Auto-capturing for mouse press and mouse release
2298//
2299
2300static void setAutoCapture( HWND h )
2301{
2302 if ( autoCaptureWnd )
2303 releaseAutoCapture();
2304 autoCaptureWnd = h;
2305
2306 if ( !mouseButtonState() ) {
2307 // all buttons released, we don't actually capture the mouse
2308 // (see QWidget::translateMouseEvent())
2309 autoCaptureReleased = TRUE;
2310 } else {
2311 autoCaptureReleased = FALSE;
2312 WinSetCapture( HWND_DESKTOP, h );
2313 }
2314}
2315
2316static void releaseAutoCapture()
2317{
2318 if ( autoCaptureWnd ) {
2319 if ( !autoCaptureReleased ) {
2320 WinSetCapture( HWND_DESKTOP, 0 );
2321 autoCaptureReleased = TRUE;
2322 }
2323 autoCaptureWnd = 0;
2324 }
2325}
2326
2327
2328//
2329// Mouse event translation
2330//
2331
2332static ushort mouseTbl[] = {
2333 WM_MOUSEMOVE, QEvent::MouseMove, 0,
2334 WM_BUTTON1DOWN, QEvent::MouseButtonPress, Qt::LeftButton,
2335 WM_BUTTON1UP, QEvent::MouseButtonRelease, Qt::LeftButton,
2336 WM_BUTTON1DBLCLK, QEvent::MouseButtonDblClick, Qt::LeftButton,
2337 WM_BUTTON2DOWN, QEvent::MouseButtonPress, Qt::RightButton,
2338 WM_BUTTON2UP, QEvent::MouseButtonRelease, Qt::RightButton,
2339 WM_BUTTON2DBLCLK, QEvent::MouseButtonDblClick, Qt::RightButton,
2340 WM_BUTTON3DOWN, QEvent::MouseButtonPress, Qt::MidButton,
2341 WM_BUTTON3UP, QEvent::MouseButtonRelease, Qt::MidButton,
2342 WM_BUTTON3DBLCLK, QEvent::MouseButtonDblClick, Qt::MidButton,
2343//@@TODO (dmik): later (extra buttons)
2344// WM_XBUTTONDOWN, QEvent::MouseButtonPress, Qt::MidButton*2, //### Qt::XButton1/2
2345// WM_XBUTTONUP, QEvent::MouseButtonRelease, Qt::MidButton*2,
2346// WM_XBUTTONDBLCLK, QEvent::MouseButtonDblClick, Qt::MidButton*2,
2347 WM_CONTEXTMENU, QEvent::ContextMenu, 0,
2348 0, 0, 0
2349};
2350
2351static int translateButtonState( USHORT s, int type, int button )
2352{
2353 int bst = mouseButtonState();
2354
2355 if ( type == QEvent::ContextMenu ) {
2356 if ( WinGetKeyState( HWND_DESKTOP, VK_SHIFT ) & 0x8000 )
2357 bst |= Qt::ShiftButton;
2358 if ( WinGetKeyState( HWND_DESKTOP, VK_ALT ) & 0x8000 )
2359 bst |= Qt::AltButton;
2360 if ( WinGetKeyState( HWND_DESKTOP, VK_CTRL ) & 0x8000 )
2361 bst |= Qt::ControlButton;
2362 } else {
2363 if ( s & KC_SHIFT )
2364 bst |= Qt::ShiftButton;
2365 if ( (s & KC_ALT) )
2366 bst |= Qt::AltButton;
2367 if ( s & KC_CTRL )
2368 bst |= Qt::ControlButton;
2369 }
2370 if ( (qt_extraKeyState & Qt::AltButton) )
2371 bst |= Qt::AltButton;
2372 if ( qt_extraKeyState & Qt::MetaButton )
2373 bst |= Qt::MetaButton;
2374
2375 // Translate from OS/2-style "state after event"
2376 // to X-style "state before event"
2377 if ( type == QEvent::MouseButtonPress ||
2378 type == QEvent::MouseButtonDblClick )
2379 bst &= ~button;
2380 else if ( type == QEvent::MouseButtonRelease )
2381 bst |= button;
2382
2383 return bst;
2384}
2385
2386/*! \internal
2387 In DnD, the mouse release event never appears, so the
2388 mouse button state machine must be manually reset
2389*/
2390void qt_pmMouseButtonUp()
2391{
2392 // release any stored mouse capture
2393 qt_button_down = 0;
2394 autoCaptureReleased = TRUE;
2395 releaseAutoCapture();
2396}
2397
2398bool QETWidget::translateMouseEvent( const QMSG &qmsg )
2399{
2400#if 0
2401 static const char *msgNames[] = { // 11 items
2402 "WM_MOUSEMOVE",
2403 "WM_BUTTON1DOWN", "WM_BUTTON1UP", "WM_BUTTON1DBLCLK",
2404 "WM_BUTTON2DOWN", "WM_BUTTON2UP", "WM_BUTTON2DBLCLK",
2405 "WM_BUTTON3DOWN", "WM_BUTTON3UP", "WM_BUTTON3DBLCLK",
2406 "WM_???"
2407 };
2408 int msgIdx = qmsg.msg - WM_MOUSEMOVE;
2409 if (msgIdx < 0 || msgIdx > 9)
2410 msgIdx = 10;
2411 qDebug( "%s (%04lX): [%08lX/%p:%s/%s] %04hd,%04hd hit=%04hX fl=%04hX",
2412 msgNames[msgIdx], qmsg.msg, qmsg.hwnd, this, name(), className(),
2413 SHORT1FROMMP(qmsg.mp1), SHORT2FROMMP(qmsg.mp1),
2414 SHORT1FROMMP(qmsg.mp2), SHORT2FROMMP(qmsg.mp2) );
2415#endif
2416
2417 static QPoint pos; // window pos (y flipped)
2418 static POINTL gpos = { -1, -1 }; // global pos (y flipped)
2419 QEvent::Type type; // event parameters
2420 int button;
2421 int state;
2422 int i;
2423
2424 // candidate for a double click event
2425 static HWND dblClickCandidateWin = 0;
2426
2427 if ( sm_blockUserInput ) //block user interaction during session management
2428 return TRUE;
2429
2430 // Compress mouse move events
2431 if ( qmsg.msg == WM_MOUSEMOVE ) {
2432 QMSG mouseMsg;
2433 mouseMsg.msg = WM_NULL;
2434 while (
2435 WinPeekMsg( 0, &mouseMsg, qmsg.hwnd, WM_MOUSEMOVE,
2436 WM_MOUSEMOVE, PM_NOREMOVE )
2437 ) {
2438 if ( mouseMsg.mp2 != qmsg.mp2 )
2439 break; // leave the message in the queue because
2440 // the key state has changed
2441 // Remove the mouse move message
2442 WinPeekMsg( 0, &mouseMsg, qmsg.hwnd, WM_MOUSEMOVE,
2443 WM_MOUSEMOVE, PM_REMOVE );
2444 }
2445 // Update the passed in QMSG structure with the
2446 // most recent one.
2447 if ( mouseMsg.msg != WM_NULL ) {
2448 PQMSG pqmsg = (PQMSG)&qmsg;
2449 pqmsg->mp1 = mouseMsg.mp1;
2450 pqmsg->mp2 = mouseMsg.mp2;
2451 pqmsg->time = mouseMsg.time;
2452 pqmsg->ptl.x = (short)SHORT1FROMMP(mouseMsg.mp1);
2453 pqmsg->ptl.y = (short)SHORT2FROMMP(mouseMsg.mp1);
2454 WinMapWindowPoints( pqmsg->hwnd, HWND_DESKTOP, &pqmsg->ptl, 1 );
2455 // flip y coordinate
2456 pqmsg->ptl.y = QApplication::desktop()->height() - (pqmsg->ptl.y + 1);
2457 }
2458 }
2459
2460 for ( i = 0; mouseTbl[i] && (ULONG)mouseTbl[i] != qmsg.msg; i += 3 )
2461 ;
2462 if ( !mouseTbl[i] )
2463 return FALSE;
2464
2465 type = (QEvent::Type)mouseTbl[++i]; // event type
2466 button = mouseTbl[++i]; // which button
2467 state = translateButtonState( SHORT2FROMMP(qmsg.mp2), type, button ); // button state
2468
2469 // It seems, that PM remembers only the WM_BUTTONxDOWN message (instead of
2470 // the WM_BUTTONxDOWN + WM_BUTTONxUP pair) to detect whether the next button
2471 // press should be converted to WM_BUTTONxDBLCLK or not. As a result, the
2472 // window gets WM_BUTTONxDBLCLK even if it didn't receive the preceeding
2473 // WM_BUTTONxUP (this happens if we issue WinSetCapture() on the first
2474 // WM_BUTTONxDOWN), which is obviously wrong and makes problems for QWorkspace
2475 // and QTitleBar system menu handlers that don't expect a double click after
2476 // they opened a popup menu. dblClickCandidateWin is reset to 0 (see a ***
2477 // remmark below) when WinSetCapture is issued that directs messages
2478 // to a window other than one received the first WM_BUTTONxDOWN,
2479 // so we can fix it here. Note that if there is more than one popup window,
2480 // WinSetCapture is issued only for the first of them, so this code doesn't
2481 // prevent MouseButtonDblClick from being delivered to a popup when another
2482 // popup gets closed on the first WM_BUTTONxDOWN (Qt/Win32 behaves in the
2483 // same way, so it's left for compatibility).
2484 if ( type == QEvent::MouseButtonPress ) {
2485 dblClickCandidateWin = qmsg.hwnd;
2486 } else if ( type == QEvent::MouseButtonDblClick ) {
2487 if ( dblClickCandidateWin != qmsg.hwnd )
2488 type = QEvent::MouseButtonPress;
2489 dblClickCandidateWin = 0;
2490 }
2491
2492 if ( type == QEvent::ContextMenu ) {
2493 QPoint g = QPoint( qmsg.ptl.x, qmsg.ptl.y );
2494 QContextMenuEvent e( QContextMenuEvent::Mouse, mapFromGlobal( g ), g, state );
2495 QApplication::sendSpontaneousEvent( this, &e );
2496 return TRUE;
2497 }
2498
2499 if ( type == QEvent::MouseMove ) {
2500 if ( !(state & MouseButtonMask) )
2501 qt_button_down = 0;
2502#ifndef QT_NO_CURSOR
2503 QCursor *c = qt_grab_cursor();
2504 if ( !c )
2505 c = QApplication::overrideCursor();
2506 if ( c ) // application cursor defined
2507 WinSetPointer( HWND_DESKTOP, c->handle() );
2508 else if ( isEnabled() ) // use widget cursor if widget is enabled
2509 WinSetPointer( HWND_DESKTOP, cursor().handle() );
2510 else {
2511 QWidget *parent = parentWidget();
2512 while ( parent && !parent->isEnabled() )
2513 parent = parent->parentWidget();
2514 if ( parent )
2515 WinSetPointer( HWND_DESKTOP, parent->cursor().handle() );
2516 }
2517#else
2518 // pass the msg to the default proc to let it change the pointer shape
2519 WinDefWindowProc( qmsg.hwnd, qmsg.msg, qmsg.mp1, qmsg.mp2 );
2520#endif
2521 if ( curWin != winId() ) { // new current window
2522/// @todo (dmik)
2523// add CS_HITTEST to our window classes and handle WM_HITTEST,
2524// otherwise disabled windows will not get mouse events?
2525 qt_dispatchEnterLeave( this, QWidget::find(curWin) );
2526 curWin = winId();
2527 }
2528
2529 // *** PM posts a dummy WM_MOUSEMOVE message (with the same, uncahnged
2530 // pointer coordinates) after every WinSetCapture that actually changes
2531 // the capture target. I.e., if the argument of WinSetCapture is
2532 // NULLHANDLE, a window under the mouse pointer gets this message,
2533 // otherwise the specified window gets it unless it is already under the
2534 // pointer. We use this info to check whether the window can be a double
2535 // click candidate (see above).
2536 if ( qmsg.ptl.x == gpos.x && qmsg.ptl.y == gpos.y ) {
2537 if ( dblClickCandidateWin != qmsg.hwnd )
2538 dblClickCandidateWin = 0;
2539 return TRUE;
2540 }
2541
2542 gpos = qmsg.ptl;
2543
2544 if ( state == 0 && autoCaptureWnd == 0 && !hasMouseTracking() &&
2545 !QApplication::hasGlobalMouseTracking() )
2546 return TRUE; // no button
2547
2548 pos = mapFromGlobal( QPoint(gpos.x, gpos.y) );
2549 } else {
2550 if ( type == QEvent::MouseButtonPress && !isActiveWindow() )
2551 setActiveWindow();
2552
2553 gpos = qmsg.ptl;
2554 pos = mapFromGlobal( QPoint(gpos.x, gpos.y) );
2555
2556 if ( type == QEvent::MouseButtonPress || type == QEvent::MouseButtonDblClick ) { // mouse button pressed
2557 // Magic for masked widgets
2558 qt_button_down = findChildWidget( this, pos );
2559 if ( !qt_button_down || !qt_button_down->testWFlags(WMouseNoMask) )
2560 qt_button_down = this;
2561 }
2562 }
2563
2564 // detect special button states
2565 enum { Other, SinglePressed, AllReleased } btnState = Other;
2566 int bs = state & MouseButtonMask;
2567 if ( (type == QEvent::MouseButtonPress ||
2568 type == QEvent::MouseButtonDblClick) && bs == 0
2569 ) {
2570 btnState = SinglePressed;
2571 } else if ( type == QEvent::MouseButtonRelease && bs == button ) {
2572 btnState = AllReleased;
2573 }
2574
2575 if ( qApp->inPopupMode() ) { // in popup mode
2576 if ( !autoCaptureReleased && btnState == AllReleased ) {
2577 // in order to give non-Qt windows the opportunity to see mouse
2578 // messages while our popups are active we need to release the
2579 // mouse capture which is absolute in OS/2. we do it directly
2580 // (not through releaseAutoCapture()) in order to keep
2581 // autoCaptureWnd nonzero to keep forwarding mouse move events
2582 // (actually sent to one of Qt widgets) to the active popup.
2583 autoCaptureReleased = TRUE;
2584 WinSetCapture( HWND_DESKTOP, 0 );
2585 } else if ( autoCaptureReleased && btnState == SinglePressed ) {
2586 // set the mouse capture back if a button is pressed.
2587 if ( autoCaptureWnd ) {
2588 autoCaptureReleased = FALSE;
2589 WinSetCapture( HWND_DESKTOP, autoCaptureWnd );
2590 }
2591 }
2592
2593 replayPopupMouseEvent = FALSE;
2594 QWidget* activePopupWidget = qApp->activePopupWidget();
2595 QWidget *popup = activePopupWidget;
2596
2597 if ( popup != this ) {
2598 if ( testWFlags(WType_Popup) && rect().contains(pos) )
2599 popup = this;
2600 else // send to last popup
2601 pos = popup->mapFromGlobal( QPoint(gpos.x, gpos.y) );
2602 }
2603 QWidget *popupChild = findChildWidget( popup, pos );
2604 bool releaseAfter = FALSE;
2605 switch ( type ) {
2606 case QEvent::MouseButtonPress:
2607 case QEvent::MouseButtonDblClick:
2608 popupButtonFocus = popupChild;
2609 break;
2610 case QEvent::MouseButtonRelease:
2611 releaseAfter = TRUE;
2612 break;
2613 default:
2614 break; // nothing for mouse move
2615 }
2616
2617 if ( popupButtonFocus ) {
2618 QMouseEvent e( type,
2619 popupButtonFocus->mapFromGlobal(QPoint(gpos.x,gpos.y)),
2620 QPoint(gpos.x,gpos.y), button, state );
2621 QApplication::sendSpontaneousEvent( popupButtonFocus, &e );
2622 if ( releaseAfter ) {
2623 popupButtonFocus = 0;
2624 }
2625 } else if ( popupChild ) {
2626 QMouseEvent e( type,
2627 popupChild->mapFromGlobal(QPoint(gpos.x,gpos.y)),
2628 QPoint(gpos.x,gpos.y), button, state );
2629 QApplication::sendSpontaneousEvent( popupChild, &e );
2630 } else {
2631 QMouseEvent e( type, pos, QPoint(gpos.x,gpos.y), button, state );
2632 QApplication::sendSpontaneousEvent( popup, &e );
2633 }
2634
2635 if ( releaseAfter )
2636 qt_button_down = 0;
2637
2638 if ( type == QEvent::MouseButtonPress
2639 && qApp->activePopupWidget() != activePopupWidget
2640 && replayPopupMouseEvent ) {
2641 // the popup dissappeared. Replay the event
2642 QWidget* w = QApplication::widgetAt( gpos.x, gpos.y, TRUE );
2643 if ( w && !qt_blocked_modal( w ) ) {
2644 QPoint wpos = w->mapFromGlobal(QPoint(gpos.x, gpos.y));
2645 // flip y coordinate
2646 wpos.ry() = w->height() - (wpos.y() + 1);
2647 // Note: it's important to post this message rather than send:
2648 // QPushButton::popupPressed() depends on this so that
2649 // popup->exec() must return before this message is delivered
2650 // as QWidget::keyPressEvent().
2651 WinPostMsg( w->winId(), qmsg.msg,
2652 MPFROM2SHORT( wpos.x(), wpos.y() ), qmsg.mp2 );
2653 }
2654 }
2655 } else { // not popup mode
2656 if ( btnState == SinglePressed && QWidget::mouseGrabber() == 0 )
2657 setAutoCapture( winId() );
2658 else if ( btnState == AllReleased && QWidget::mouseGrabber() == 0 )
2659 releaseAutoCapture();
2660
2661 QWidget *widget = this;
2662 QWidget *w = QWidget::mouseGrabber();
2663 if ( !w )
2664 w = qt_button_down;
2665 if ( w && w != this ) {
2666 widget = w;
2667 pos = w->mapFromGlobal(QPoint(gpos.x, gpos.y));
2668 }
2669
2670 if ( type == QEvent::MouseButtonRelease &&
2671 (state & (~button) & ( MouseButtonMask )) == 0 ) {
2672 qt_button_down = 0;
2673 }
2674
2675 QMouseEvent e( type, pos, QPoint(gpos.x,gpos.y), button, state );
2676 QApplication::sendSpontaneousEvent( widget, &e );
2677
2678 if ( type != QEvent::MouseMove )
2679 pos.rx() = pos.ry() = -9999; // init for move compression
2680 }
2681 return TRUE;
2682}
2683
2684
2685//
2686// Keyboard event translation
2687//
2688
2689static const ushort KeyTbl[] = { // keyboard mapping table
2690 VK_ESC, Qt::Key_Escape, // misc keys
2691 VK_TAB, Qt::Key_Tab,
2692 VK_BACKTAB, Qt::Key_Backtab,
2693 VK_BACKSPACE, Qt::Key_Backspace,
2694 VK_ENTER, Qt::Key_Enter,
2695 VK_NEWLINE, Qt::Key_Return,
2696 VK_INSERT, Qt::Key_Insert,
2697 VK_DELETE, Qt::Key_Delete,
2698 VK_CLEAR, Qt::Key_Clear,
2699 VK_PAUSE, Qt::Key_Pause,
2700 VK_PRINTSCRN, Qt::Key_Print,
2701 VK_SPACE, Qt::Key_Space,
2702 VK_HOME, Qt::Key_Home, // cursor movement
2703 VK_END, Qt::Key_End,
2704 VK_LEFT, Qt::Key_Left,
2705 VK_UP, Qt::Key_Up,
2706 VK_RIGHT, Qt::Key_Right,
2707 VK_DOWN, Qt::Key_Down,
2708 VK_PAGEUP, Qt::Key_Prior,
2709 VK_PAGEDOWN, Qt::Key_Next,
2710 VK_SHIFT, Qt::Key_Shift, // modifiers
2711 VK_CTRL, Qt::Key_Control,
2712 VK_ALT, Qt::Key_Alt,
2713 VK_CAPSLOCK, Qt::Key_CapsLock,
2714 VK_NUMLOCK, Qt::Key_NumLock,
2715 VK_SCRLLOCK, Qt::Key_ScrollLock,
2716 0, 0
2717};
2718
2719// when the compatibility mode is FALSE Qt/OS2 uses the following rule
2720// to calculate QKeyEvent::key() codes when an alpha-numeric key is
2721// pressed: key code is the ASCII (Latin 1) character code of that key as if
2722// there were no any keyboard modifiers (CTRL, SHIFT, ALT) pressed, with the
2723// exception that alpha characters are uppercased.
2724// when the compatibility mode is TRUE Qt/OS2 behaves mostly like Qt/Win32.
2725Q_EXPORT bool qt_kbd_compatibility = TRUE;
2726
2727/// @todo (dmik) currentlly, qt_kbd_compatibility is TRUE because
2728// qt_scan2Ascii function below is not well implemented yet (in particular,
2729// it uses the 850 code page that may not be available on some systems...).
2730// Once we find a way to translate scans to US ASCII regardless of the current
2731// code page and/or NLS state (keyboard layout), qt_kbd_compatibility may be
2732// set to FALSE. This, in particular, will enable more correct handling of
2733// Alt+letter combinations when the keyboard is in the NLS state:
2734// QKeyEvent::key() will return a non-null Qt::Key_XXX code corresponding to
2735// the ASCII code of a pressed key, which in turn will let Qt process latin
2736// Alt+letter shortcuts in the NLS keyboard mode together with Alt+NLS_letter
2737// shortcuts (nice feature imho). Note that Alt+NLS_letter shortcuts are
2738// correctly processed in any case.
2739
2740// cache table to store Qt::Key_... values for 256 hardware scancodes
2741// (even byte is for non-shifted keys, odd byte is for shifted ones).
2742// used to decrease the number of calls to KbdXlate.
2743static uchar ScanTbl[512] = { 0xFF };
2744
2745static uchar qt_scan2Ascii( uchar scan, bool shift )
2746{
2747 uchar ascii = 0;
2748 HFILE kbd;
2749 ULONG dummy;
2750 DosOpen( "KBD$", &kbd, &dummy, 0,
2751 FILE_NORMAL, OPEN_ACTION_OPEN_IF_EXISTS,
2752 OPEN_SHARE_DENYNONE | OPEN_ACCESS_READWRITE, NULL );
2753 // parameter packet
2754 struct CPID {
2755 USHORT idCodePage;
2756 USHORT reserved;
2757 } cpid = { 850, 0 };
2758 ULONG szCpid = sizeof(CPID);
2759 // data packet
2760 KBDTRANS kt;
2761 ULONG szKt = sizeof(KBDTRANS);
2762 // reset all kbd states and modifiers
2763 memset( &kt, 0, szKt );
2764 // reflect Shift state to distinguish between [ and { etc.
2765 if ( qt_kbd_compatibility && shift )
2766 kt.fsState = KBDSTF_RIGHTSHIFT;
2767 kt.chScan = scan;
2768 DosDevIOCtl( kbd, IOCTL_KEYBOARD, KBD_XLATESCAN,
2769 &cpid, szCpid, &szCpid, &kt, szKt, &szKt );
2770 // store in cache
2771 uint idx = scan << 1;
2772 if ( qt_kbd_compatibility && shift )
2773 idx++;
2774 ascii = ScanTbl[idx] = toupper( kt.chChar );
2775 DosClose( kbd );
2776 return ascii;
2777}
2778
2779// translates WM_CHAR to Qt::Key_..., ascii, state and text
2780static void translateKeyCode( CHRMSG &chm, int &code, int &ascii, int &state,
2781 QString &text )
2782{
2783 if ( chm.fs & KC_SHIFT )
2784 state |= Qt::ShiftButton;
2785 if ( chm.fs & KC_CTRL )
2786 state |= Qt::ControlButton;
2787 if ( chm.fs & KC_ALT )
2788 state |= Qt::AltButton;
2789 if ( qt_extraKeyState & Qt::MetaButton )
2790 state |= Qt::MetaButton;
2791
2792 unsigned char ch = chm.chr;
2793
2794 if ( chm.fs & KC_VIRTUALKEY ) {
2795 if ( !chm.vkey ) {
2796 // The only known situation when KC_VIRTUALKEY is present but
2797 // vkey is zero is when Alt+Shift is pressed to switch the
2798 // keyboard layout state from latin to national and back.
2799 // It seems that this way the system informs applications about
2800 // layout changes: chm.chr is 0xF1 when the user switches
2801 // to the national layout (i.e. presses Alt + Left Shift)
2802 // and it is 0xF0 when he switches back (presses Alt + Right Shift).
2803 // We assume this and restore fs, vkey, scancode and chr accordingly.
2804 if ( chm.chr == 0xF0 || chm.chr == 0xF1 ) {
2805 chm.fs |= KC_ALT | KC_SHIFT;
2806 chm.vkey = VK_SHIFT;
2807 chm.scancode = chm.chr == 0xF1 ? 0x2A : 0x36;
2808 chm.chr = ch = 0;
2809 state |= Qt::AltButton | Qt::ShiftButton;
2810 // code will be assigned by the normal procedure below
2811 }
2812 }
2813 if ( chm.vkey >= VK_F1 && chm.vkey <= VK_F24 ) {
2814 // function keys
2815 code = Qt::Key_F1 + (chm.vkey - VK_F1);
2816 } else if ( chm.vkey == VK_ALTGRAF ) {
2817 code = Qt::Key_Alt;
2818 if ( !(chm.fs & KC_KEYUP) )
2819 qt_extraKeyState |= Qt::AltButton;
2820 else
2821 qt_extraKeyState &= ~Qt::AltButton;
2822 } else {
2823 // any other keys
2824 int i = 0;
2825 while ( KeyTbl[i] ) {
2826 if ( chm.vkey == (int)KeyTbl[i] ) {
2827 code = KeyTbl[i+1];
2828 break;
2829 }
2830 i += 2;
2831 }
2832 }
2833 } else {
2834 if ( qt_kbd_compatibility && ch && ch < 0x80 ) {
2835 code = toupper( ch );
2836 } else if ( !qt_kbd_compatibility && (chm.fs & KC_CHAR) && isalpha( ch ) ) {
2837 code = toupper( ch );
2838 } else {
2839 // detect some special keys that have a pseudo char code
2840 // in the high byte of chm.chr (probably this is less
2841 // device-dependent than scancode)
2842 switch ( chm.chr ) {
2843 case 0xEC00: // LWIN
2844 case 0xED00: // RWIN
2845 code = Qt::Key_Meta;
2846 if ( !(chm.fs & KC_KEYUP) )
2847 qt_extraKeyState |= Qt::MetaButton;
2848 else
2849 qt_extraKeyState &= ~Qt::MetaButton;
2850 break;
2851 case 0xEE00: // WINAPP (menu with arrow)
2852 code = Qt::Key_Menu;
2853 break;
2854 case 0x5600: // additional '\' (0x56 is actually its scancode)
2855 ch = state & Qt::ShiftButton ? '|' : '\\';
2856 if ( qt_kbd_compatibility ) code = ch;
2857 else code = '\\';
2858 break;
2859 default:
2860 // break if qt_kbd_compatibility = TRUE to avoid using
2861 // qt_scan2Ascii(), see comments to qt_kbd_compatibility
2862 if ( qt_kbd_compatibility ) break;
2863 // deduce Qt::Key... from scancode
2864 if ( ScanTbl[0] == 0xFF )
2865 memset( &ScanTbl, 0, sizeof(ScanTbl) );
2866 uint idx = chm.scancode << 1;
2867 if ( qt_kbd_compatibility && (state & Qt::ShiftButton) )
2868 idx++;
2869 code = ScanTbl[idx];
2870 if ( !code )
2871 // not found in cache
2872 code = qt_scan2Ascii( chm.scancode, (state & Qt::ShiftButton) );
2873 break;
2874 }
2875 }
2876 }
2877 // check extraState AFTER updating it
2878 if ( qt_extraKeyState & Qt::AltButton )
2879 state |= Qt::AltButton;
2880
2881 // detect numeric keypad keys
2882 if ( chm.vkey == VK_ENTER || chm.vkey == VK_NUMLOCK ) {
2883 // these always come from the numpad
2884 state |= Qt::Keypad;
2885 } else if (
2886 ((chm.vkey >= VK_PAGEUP && chm.vkey <= VK_DOWN) ||
2887 chm.vkey == VK_INSERT || chm.vkey == VK_DELETE)
2888 ) {
2889 if ( ch != 0xE0 ) {
2890 state |= Qt::Keypad;
2891 if ( (state & Qt::AltButton) && chm.vkey != VK_DELETE ) {
2892 // reset both code and ch to "hide" them from Qt widgets and let
2893 // the standard OS/2 Alt+ddd shortcut (that composed chars from
2894 // typed in ASCII codes) work correctly. If we don't do that,
2895 // widgets will see both individual digits (or cursor movements
2896 // if NumLock is off) and the char composed by Alt+ddd.
2897 code = ch = 0;
2898 } else {
2899 if ( ch ) {
2900 // override code to make it Qt::Key_0..Qt::Key_9 etc.
2901 code = toupper( ch );
2902 }
2903 }
2904 } else {
2905 ch = 0;
2906 }
2907 }
2908 // detect other numpad keys. OS/2 doesn't assign virtual keys to them
2909 // so use scancodes (it can be device-dependent, is there a better way?)
2910 switch ( chm.scancode ) {
2911 case 0x4C: // 5
2912 state |= Qt::Keypad;
2913 if ( state & Qt::AltButton ) {
2914 // reset both code and ch to "hide" them from Qt (see above)
2915 code = ch = 0;
2916 } else {
2917 // scancode is zero if Numlock is set
2918 if ( !code ) code = Qt::Key_Clear;
2919 }
2920 break;
2921 case 0x37: // *
2922 // OS/2 assigns VK_PRINTSCRN to it when pressed with Shift, also
2923 // it sets chr to zero when it is released with Alt or Ctrl
2924 // leaving vkey as zero too, and does few other strange things --
2925 // override them all
2926 code = Qt::Key_Asterisk;
2927 state |= Qt::Keypad;
2928 break;
2929 case 0x5C: // /
2930 code = Qt::Key_Slash;
2931 // fall through
2932 case 0x4A: // -
2933 case 0x4E: // +
2934 // the code for the above two is obtained by KbdXlate above
2935 state |= Qt::Keypad;
2936 break;
2937 }
2938
2939 if ( (state & Qt::ControlButton) && !(state & Qt::Keypad) ) {
2940 if ( !(state & Qt::AltButton) ) {
2941 unsigned char cch = toupper( ch ), newCh = 0;
2942 // Ctrl + A..Z etc. produce ascii from 0x01 to 0x1F
2943 if ( cch >= 0x41 && cch <= 0x5F ) newCh = cch - 0x40;
2944 // the below emulates OS/2 functionality. It differs from
2945 // Win32 one.
2946 else if ( cch == 0x36 && !(state & Qt::Keypad) ) newCh = 0x1E;
2947 else if ( cch == 0x2D ) newCh = 0x1F;
2948 else if ( cch >= 0x7B && cch <= 0x7D ) newCh = cch - 0x60;
2949 if ( newCh )
2950 ch = newCh;
2951 }
2952 }
2953
2954 ascii = ch;
2955 if ( ascii > 0x7F ) ascii = 0;
2956 if ( ch )
2957 text = QString::fromLocal8Bit( (char*)&ch, 1 );
2958}
2959
2960struct KeyRec {
2961 KeyRec(unsigned char s, int c, int a, const QString& t) :
2962 scan(s), code(c), ascii(a), text(t) { }
2963 KeyRec() { }
2964 unsigned char scan;
2965 int code, ascii;
2966 QString text;
2967};
2968
2969static const int maxrecs=64; // User has LOTS of fingers...
2970static KeyRec key_rec[maxrecs];
2971static int nrecs=0;
2972
2973static KeyRec* find_key_rec( unsigned char scan, bool remove )
2974{
2975 KeyRec *result = 0;
2976 for (int i=0; i<nrecs; i++) {
2977 if (key_rec[i].scan == scan) {
2978 if (remove) {
2979 static KeyRec tmp;
2980 tmp = key_rec[i];
2981 while (i+1 < nrecs) {
2982 key_rec[i] = key_rec[i+1];
2983 i++;
2984 }
2985 nrecs--;
2986 result = &tmp;
2987 } else {
2988 result = &key_rec[i];
2989 }
2990 break;
2991 }
2992 }
2993 return result;
2994}
2995
2996static KeyRec* find_key_rec( int code, bool remove )
2997{
2998 KeyRec *result = 0;
2999 for (int i=0; i<nrecs; i++) {
3000 if (key_rec[i].code == code) {
3001 if (remove) {
3002 static KeyRec tmp;
3003 tmp = key_rec[i];
3004 while (i+1 < nrecs) {
3005 key_rec[i] = key_rec[i+1];
3006 i++;
3007 }
3008 nrecs--;
3009 result = &tmp;
3010 } else {
3011 result = &key_rec[i];
3012 }
3013 break;
3014 }
3015 }
3016 return result;
3017}
3018
3019static void store_key_rec( unsigned char scan, int code, int ascii,
3020 const QString& text )
3021{
3022 if ( nrecs == maxrecs ) {
3023#if defined(QT_CHECK_RANGE)
3024 qWarning( "Qt: Internal keyboard buffer overflow" );
3025#endif
3026 return;
3027 }
3028
3029 key_rec[nrecs++] = KeyRec( scan, code, ascii, text );
3030}
3031
3032bool QETWidget::translateKeyEvent( const QMSG &qmsg, bool grab )
3033{
3034 CHRMSG chm = *((PCHRMSG)(((char*)&qmsg) + sizeof(HWND) + sizeof(ULONG)));
3035
3036#if 0
3037 qDebug( "WM_CHAR: [%s] fs: %04X cRepeat: %03d scancode: %02X chr: %04X vkey: %04X %s",
3038 name(), chm.fs, chm.cRepeat, chm.scancode, chm.chr, chm.vkey, (grab ? "{grab}" : "") );
3039#endif
3040
3041 bool k0 = FALSE, k1 = FALSE;
3042 int code = 0, ascii = 0, state = 0;
3043 QString text;
3044
3045 if ( sm_blockUserInput ) // block user interaction during session management
3046 return TRUE;
3047
3048 translateKeyCode( chm, code, ascii, state, text );
3049/// @todo (dmik) currently WM_CHARs chars with zero virtual code or zero
3050// scancode are totally ignored. -- are they?
3051// if ( !code || !chm.scancode ) return FALSE;
3052
3053 // Invert state logic
3054 if ( code == Key_Alt )
3055 state = state ^ AltButton;
3056 else if ( code == Key_Control )
3057 state = state ^ ControlButton;
3058 else if ( code == Key_Shift )
3059 state = state ^ ShiftButton;
3060
3061 if ( !(chm.fs & KC_KEYUP) ) {
3062 // KEYDOWN
3063 KeyRec* rec = find_key_rec( chm.scancode, FALSE );
3064
3065 if ( state == Qt::AltButton ) {
3066 // Special handling of global PM hotkeys
3067 switch ( code ) {
3068 case Qt::Key_Space:
3069 if ( qt_show_system_menu( topLevelWidget() ) ) {
3070 // remove the Key_Alt from the buffer (otherwise we will
3071 // not get the next "Alt pressed" event because the
3072 // "Alt depressed" event, that must preceed it, will be
3073 // eaten by the system)
3074 find_key_rec( Qt::Key_Alt, TRUE );
3075/// @todo (dmik) do the same for other global keys (ALT+TAB, ALT+ESC, CTRL+ESC)
3076// by handling this situation when we obtain/loose focus)
3077/// @todo (dmik) update: I don't actually think the above should be done, because
3078// it will not solve the problem of stuck modifier keys in general (there may be
3079// other combinations stolen by the system or other apps). More over, I guess
3080// that find_key_rec() above should also be removed to get identical behavior for
3081// all stolen keys. This will allow to solve the problem on the Qt application
3082// level if needed (and even in a platform-independent manner).
3083 }
3084 return TRUE;
3085 case Qt::Key_F4:
3086 // we handle this key combination ourselves because not
3087 // all top-level widgets have the system menu
3088 WinPostMsg( topLevelWidget()->winFId(), WM_CLOSE, 0, 0 );
3089 // see the comment above
3090 find_key_rec( Qt::Key_Alt, TRUE );
3091 return TRUE;
3092 default:
3093 break;
3094 }
3095 }
3096
3097 if ( rec ) {
3098 // it is already down (so it is auto-repeating)
3099 if ( rec->code < Key_Shift || rec->code > Key_ScrollLock ) {
3100 k0 = sendKeyEvent( QEvent::KeyRelease, rec->code, rec->ascii,
3101 state, grab, rec->text, TRUE );
3102 k1 = sendKeyEvent( QEvent::KeyPress, rec->code, rec->ascii,
3103 state, grab, rec->text, TRUE );
3104 }
3105 } else {
3106 // map shift+tab to shift+backtab, QAccel knows about it
3107 // and will handle it
3108 if ( code == Key_Tab && ( state & ShiftButton ) == ShiftButton )
3109 code = Key_BackTab;
3110 store_key_rec( chm.scancode, code, ascii, text );
3111 k0 = sendKeyEvent( QEvent::KeyPress, code, ascii,
3112 state, grab, text );
3113 }
3114 } else {
3115 // KEYUP
3116 KeyRec* rec = find_key_rec( chm.scancode, TRUE );
3117 if ( !rec ) {
3118 // Someone ate the key down event
3119 } else {
3120 k0 = sendKeyEvent( QEvent::KeyRelease, rec->code, rec->ascii,
3121 state, grab, rec->text );
3122
3123 // keyboard context menu event
3124 if ( rec->code == Key_Menu && !state )
3125 WinPostMsg( qmsg.hwnd, WM_CONTEXTMENU, 0, MPFROM2SHORT( 0, 1 ) );
3126 }
3127 }
3128
3129#if 0
3130 qDebug("WM_CHAR: RESULT = %d", (k0 || k1));
3131#endif
3132 return k0 || k1;
3133}
3134
3135#ifndef QT_NO_WHEELEVENT
3136bool QETWidget::translateWheelEvent( const QMSG &qmsg )
3137{
3138 enum { WHEEL_DELTA = 120 };
3139
3140 if ( sm_blockUserInput ) // block user interaction during session management
3141 return TRUE;
3142
3143 // consume duplicate wheel events sent by the AMouse driver to emulate
3144 // multiline scrolls. we need this since currently Qt (QScrollBar, for
3145 // instance) maintains the number of lines to scroll per wheel rotation
3146 // (including the special handling of CTRL and SHIFT modifiers) on its own
3147 // and doesn't have a setting to tell it to be aware of system settings
3148 // for the mouse wheel. if we had processed events as they are, we would
3149 // get a confusing behavior (too many lines scrolled etc.).
3150 {
3151 int devh = QApplication::desktop()->height();
3152 QMSG wheelMsg;
3153 while (
3154 WinPeekMsg( 0, &wheelMsg, qmsg.hwnd, qmsg.msg, qmsg.msg, PM_NOREMOVE )
3155 ) {
3156 // PM bug: ptl contains SHORT coordinates although fields are LONG
3157 wheelMsg.ptl.x = (short) wheelMsg.ptl.x;
3158 wheelMsg.ptl.y = (short) wheelMsg.ptl.y;
3159 // flip y coordinate
3160 wheelMsg.ptl.y = devh - (wheelMsg.ptl.y + 1);
3161 if (
3162 wheelMsg.mp1 != qmsg.mp1 ||
3163 wheelMsg.mp2 != qmsg.mp2 ||
3164 wheelMsg.ptl.x != qmsg.ptl.x ||
3165 wheelMsg.ptl.y != qmsg.ptl.y
3166 )
3167 break;
3168 WinPeekMsg( 0, &wheelMsg, qmsg.hwnd, qmsg.msg, qmsg.msg, PM_REMOVE );
3169 }
3170 }
3171
3172 int delta;
3173 USHORT cmd = SHORT2FROMMP(qmsg.mp2);
3174 switch ( cmd ) {
3175 case SB_LINEUP:
3176 case SB_PAGEUP:
3177 delta = WHEEL_DELTA;
3178 break;
3179 case SB_LINEDOWN:
3180 case SB_PAGEDOWN:
3181 delta = -WHEEL_DELTA;
3182 break;
3183 default:
3184 return FALSE;
3185 }
3186
3187 int state = 0;
3188 if ( WinGetKeyState( HWND_DESKTOP, VK_SHIFT ) & 0x8000 )
3189 state |= ShiftButton;
3190 if ( WinGetKeyState( HWND_DESKTOP, VK_ALT ) & 0x8000 ||
3191 (qt_extraKeyState & Qt::AltButton)
3192 )
3193 state |= AltButton;
3194 if ( WinGetKeyState( HWND_DESKTOP, VK_CTRL ) & 0x8000 )
3195 state |= ControlButton;
3196 if ( qt_extraKeyState & Qt::MetaButton )
3197 state |= MetaButton;
3198
3199 Orientation orient;
3200 // Alt inverts scroll orientation (Qt/Win32 behavior)
3201 if ( state & AltButton )
3202 orient = qmsg.msg == WM_VSCROLL ? Horizontal : Vertical;
3203 else
3204 orient = qmsg.msg == WM_VSCROLL ? Vertical : Horizontal;
3205
3206 QPoint globalPos (qmsg.ptl.x, qmsg.ptl.y);
3207
3208 // if there is a widget under the mouse and it is not shadowed
3209 // by modality, we send the event to it first
3210 int ret = 0;
3211 QWidget* w = QApplication::widgetAt( globalPos, TRUE );
3212 if ( !w || !qt_try_modal( w, (QMSG*)&qmsg, ret ) )
3213 w = this;
3214
3215 // send the event to the widget or its ancestors
3216 {
3217 QWidget* popup = qApp->activePopupWidget();
3218 if ( popup && w->topLevelWidget() != popup )
3219 popup->close();
3220 QWheelEvent e( w->mapFromGlobal( globalPos ), globalPos, delta, state, orient );
3221 if ( QApplication::sendSpontaneousEvent( w, &e ) )
3222 return TRUE;
3223 }
3224
3225 // send the event to the widget that has the focus or its ancestors, if different
3226 if ( w != qApp->focusWidget() && ( w = qApp->focusWidget() ) ) {
3227 QWidget* popup = qApp->activePopupWidget();
3228 if ( popup && w->topLevelWidget() != popup )
3229 popup->close();
3230 QWheelEvent e( w->mapFromGlobal( globalPos ), globalPos, delta, state, orient );
3231 if ( QApplication::sendSpontaneousEvent( w, &e ) )
3232 return TRUE;
3233 }
3234 return FALSE;
3235}
3236#endif
3237
3238static bool isModifierKey(int code)
3239{
3240 return code >= Qt::Key_Shift && code <= Qt::Key_ScrollLock;
3241}
3242
3243bool QETWidget::sendKeyEvent( QEvent::Type type, int code, int ascii,
3244 int state, bool grab, const QString& text,
3245 bool autor )
3246{
3247 if ( type == QEvent::KeyPress && !grab ) {
3248 // send accel events if the keyboard is not grabbed
3249 QKeyEvent a( type, code, ascii, state, text, autor, QMAX(1, int(text.length())) );
3250 if ( qt_tryAccelEvent( this, &a ) )
3251 return TRUE;
3252 }
3253 if ( !isEnabled() )
3254 return FALSE;
3255 QKeyEvent e( type, code, ascii, state, text, autor, QMAX(1, int(text.length())) );
3256 QApplication::sendSpontaneousEvent( this, &e );
3257 if ( !isModifierKey(code) && state == Qt::AltButton
3258 && ((code>=Key_A && code<=Key_Z) || (code>=Key_0 && code<=Key_9))
3259 && type == QEvent::KeyPress && !e.isAccepted() )
3260 QApplication::beep(); // emulate PM behavior
3261 return e.isAccepted();
3262}
3263
3264
3265//
3266// Paint event translation
3267//
3268bool QETWidget::translatePaintEvent( const QMSG & )
3269{
3270 HPS displayPS = qt_display_ps();
3271
3272#if !defined (QT_PM_NO_WIDGETMASK)
3273 // Since we don't use WS_CLIPSIBLINGS and WS_CLIPCHILDREN bits (see
3274 // qwidget_pm.cpp), we have to validate areas that intersect with our
3275 // children and siblings, taking their clip regions into account.
3276 validateObstacles();
3277#endif
3278
3279 HRGN hrgn = GpiCreateRegion( displayPS, 0, NULL );
3280 LONG rc = WinQueryUpdateRegion( winId(), hrgn );
3281 if ( rc == RGN_ERROR ) {
3282 GpiDestroyRegion( displayPS, hrgn );
3283 hps = 0;
3284 clearWState( WState_InPaintEvent );
3285 return FALSE;
3286 }
3287
3288 setWState( WState_InPaintEvent );
3289 RECTL rcl;
3290 hps = WinBeginPaint( winId(), 0, &rcl );
3291
3292 // convert to width and height
3293 rcl.xRight -= rcl.xLeft;
3294 rcl.yTop -= rcl.yBottom;
3295
3296 // it's possible that the update rectangle is empty
3297 if ( !rcl.xRight || !rcl.yTop ) {
3298 WinEndPaint( hps );
3299 GpiDestroyRegion( displayPS, hrgn );
3300 hps = 0;
3301 clearWState( WState_InPaintEvent );
3302 return TRUE;
3303 }
3304
3305#if !defined (QT_PM_NO_WIDGETMASK)
3306 if ( WinQueryClipRegion( winId(), 0 ) != QCRGN_NO_CLIP_REGION ) {
3307 // Correct the update region by intersecting it with the clip
3308 // region (PM doesn't do that itself). It is necessary
3309 // to have a correct QRegion in QPaintEvent.
3310 HRGN hcrgn = GpiCreateRegion( displayPS, 0, NULL );
3311 WinQueryClipRegion( winId(), hcrgn );
3312 GpiCombineRegion( displayPS, hrgn, hrgn, hcrgn, CRGN_AND );
3313 GpiDestroyRegion( displayPS, hcrgn );
3314 }
3315#endif
3316
3317 // flip y coordinate
3318 rcl.yBottom = height() - (rcl.yBottom + rcl.yTop);
3319
3320 // erase background
3321#if !defined (DEBUG_REPAINTRESIZE)
3322 bool erase = !( testWFlags( WRepaintNoErase ) );
3323#else
3324 // Some oldish Qt widgets think that if they specify WRepaintNoErase but
3325 // not WResizeNoErase, the background should still be erased for them
3326 // in *repaint* events. The code below is left to debug these widgets
3327 // (to ensure this is the exact cause of repaint problems)
3328 bool erase = testWFlags( WRepaintNoErase | WResizeNoErase ) != WRepaintNoErase | WResizeNoErase;
3329#endif
3330 if ( erase )
3331 this->erase( rcl.xLeft, rcl.yBottom,
3332 rcl.xRight, rcl.yTop );
3333
3334#if defined (DEBUG_REPAINTRESIZE)
3335 qDebug( "WM_PAINT: [%s/%s/%08X] %ld,%ld; %ld,%ld erase: %d",
3336 name(), className(), widget_flags,
3337 rcl.xLeft, rcl.yBottom, rcl.xRight, rcl.yTop, erase );
3338#endif
3339
3340 // create a region that will take ownership of hrgn
3341 QRegion rgn( hrgn, height() );
3342
3343 QPaintEvent e( rgn, QRect( rcl.xLeft, rcl.yBottom, rcl.xRight, rcl.yTop ),
3344 erase );
3345 QApplication::sendSpontaneousEvent( this, (QEvent*) &e );
3346
3347 WinEndPaint( hps );
3348 hps = 0;
3349 clearWState( WState_InPaintEvent );
3350 return TRUE;
3351}
3352
3353//
3354// Window move and resize (configure) events
3355//
3356
3357bool QETWidget::translateConfigEvent( const QMSG &qmsg )
3358{
3359 if ( !testWState(WState_Created) ) // in QWidget::create()
3360 return TRUE;
3361
3362 WId fId = winFId();
3363 ULONG fStyle = WinQueryWindowULong( fId, QWL_STYLE );
3364 if ( testWState( WState_ConfigPending ) ) {
3365 // it's possible that we're trying to set the frame size smaller
3366 // than it possible for WC_FRAME in QWidget::internalSetGeometry().
3367 // here we correct this (crect there is set before WinSetWindowPos()
3368 // that sends WM_SIZE).
3369 QSize newSize( SHORT1FROMMP(qmsg.mp2), SHORT2FROMMP(qmsg.mp2) );
3370 if ( qmsg.msg == WM_SIZE && size() != newSize ) {
3371 crect.setSize( newSize );
3372 }
3373 return TRUE;
3374 }
3375 setWState( WState_ConfigPending ); // set config flag
3376 QRect cr = geometry();
3377 if ( qmsg.msg == WM_SIZE ) { // resize event
3378 QSize oldSize = size();
3379 QSize newSize( SHORT1FROMMP(qmsg.mp2), SHORT2FROMMP(qmsg.mp2) );
3380 cr.setSize( newSize );
3381 crect = cr;
3382 if ( isTopLevel() ) { // update caption/icon text
3383 createTLExtra();
3384 QString txt;
3385 if ( (fStyle & WS_MAXIMIZED) && !!iconText() )
3386 txt = iconText();
3387 else
3388 if ( !caption().isNull() )
3389 txt = caption();
3390
3391 if ( !!txt ) {
3392 WinSetWindowText( fId, txt.local8Bit() );
3393 }
3394 }
3395 if ( oldSize != newSize) {
3396#if !defined (QT_PM_NO_WIDGETMASK)
3397 // Spontaneous (external to Qt) WM_SIZE messages should occur only
3398 // on top-level widgets. If we get them for a non top-level widget,
3399 // the result will most likely be incorrect because widget masks will
3400 // not be properly processed (i.e. in the way it is done in
3401 // QWidget::internalSetGeometry() when the geometry is changed from
3402 // within Qt). So far, I see no need to support this (who will ever
3403 // need to move a non top-level window of a foreign process?).
3404 Q_ASSERT( isTopLevel() );
3405#endif
3406 if ( isVisible() ) {
3407 QResizeEvent e( newSize, oldSize );
3408 QApplication::sendSpontaneousEvent( this, &e );
3409 if ( !testWFlags( WStaticContents ) )
3410 repaint( !testWFlags(WResizeNoErase) );
3411 } else {
3412 QResizeEvent *e = new QResizeEvent( newSize, oldSize );
3413 QApplication::postEvent( this, e );
3414 }
3415 }
3416 } else if ( qmsg.msg == WM_MOVE ) { // move event
3417 QPoint oldPos = geometry().topLeft();
3418 SWP swp;
3419 if ( isTopLevel() ) {
3420 WinQueryWindowPos( fId, &swp );
3421 // flip y coordinate
3422 swp.y = QApplication::desktop()->height() - ( swp.y + swp.cy );
3423 QTLWExtra *top = topData();
3424 swp.x += top->fleft;
3425 swp.y += top->ftop;
3426 } else {
3427 WinQueryWindowPos( winId(), &swp );
3428 // flip y coordinate
3429 swp.y = parentWidget()->height() - ( swp.y + swp.cy );
3430 }
3431 QPoint newCPos( swp.x, swp.y );
3432 if ( newCPos != oldPos ) {
3433 cr.moveTopLeft( newCPos );
3434 crect = cr;
3435 if ( isVisible() ) {
3436 QMoveEvent e( newCPos, oldPos ); // cpos (client position)
3437 QApplication::sendSpontaneousEvent( this, &e );
3438 } else {
3439 QMoveEvent * e = new QMoveEvent( newCPos, oldPos );
3440 QApplication::postEvent( this, e );
3441 }
3442 }
3443 }
3444 clearWState( WState_ConfigPending ); // clear config flag
3445 return TRUE;
3446}
3447
3448
3449//
3450// Close window event translation.
3451//
3452// This class is a friend of QApplication because it needs to emit the
3453// lastWindowClosed() signal when the last top level widget is closed.
3454//
3455
3456bool QETWidget::translateCloseEvent( const QMSG & )
3457{
3458 return close(FALSE);
3459}
3460
3461void QApplication::setCursorFlashTime( int msecs )
3462{
3463 WinSetSysValue( HWND_DESKTOP, SV_CURSORRATE, msecs / 2 );
3464 cursor_flash_time = msecs;
3465}
3466
3467int QApplication::cursorFlashTime()
3468{
3469 int blink = (int) WinQuerySysValue( HWND_DESKTOP, SV_CURSORRATE );
3470 if ( !blink )
3471 return cursor_flash_time;
3472 if (blink > 0)
3473 return 2*blink;
3474 return 0;
3475}
3476
3477void QApplication::setDoubleClickInterval( int ms )
3478{
3479 WinSetSysValue( HWND_DESKTOP, SV_DBLCLKTIME, ms );
3480 mouse_double_click_time = ms;
3481}
3482
3483
3484int QApplication::doubleClickInterval()
3485{
3486 int ms = (int) WinQuerySysValue( HWND_DESKTOP, SV_DBLCLKTIME );
3487 if ( ms != 0 )
3488 return ms;
3489 return mouse_double_click_time;
3490}
3491
3492void QApplication::setWheelScrollLines( int n )
3493{
3494 wheel_scroll_lines = n;
3495}
3496
3497int QApplication::wheelScrollLines()
3498{
3499 return wheel_scroll_lines;
3500}
3501
3502void QApplication::setEffectEnabled( Qt::UIEffect effect, bool enable )
3503{
3504#ifndef QT_NO_EFFECTS
3505 switch (effect) {
3506 case UI_AnimateMenu:
3507 if ( enable ) fade_menu = FALSE;
3508 animate_menu = enable;
3509 break;
3510 case UI_FadeMenu:
3511 if ( enable )
3512 animate_menu = TRUE;
3513 fade_menu = enable;
3514 break;
3515 case UI_AnimateCombo:
3516 animate_combo = enable;
3517 break;
3518 case UI_AnimateTooltip:
3519 if ( enable ) fade_tooltip = FALSE;
3520 animate_tooltip = enable;
3521 break;
3522 case UI_FadeTooltip:
3523 if ( enable )
3524 animate_tooltip = TRUE;
3525 fade_tooltip = enable;
3526 break;
3527 case UI_AnimateToolBox:
3528 animate_toolbox = enable;
3529 break;
3530 default:
3531 animate_ui = enable;
3532 break;
3533 }
3534#endif
3535}
3536
3537bool QApplication::isEffectEnabled( Qt::UIEffect effect )
3538{
3539#ifndef QT_NO_EFFECTS
3540 if ( QColor::numBitPlanes() < 16 || !animate_ui )
3541 return FALSE;
3542
3543 switch( effect ) {
3544 case UI_AnimateMenu:
3545 return animate_menu;
3546 case UI_FadeMenu:
3547 return fade_menu;
3548 case UI_AnimateCombo:
3549 return animate_combo;
3550 case UI_AnimateTooltip:
3551 return animate_tooltip;
3552 case UI_FadeTooltip:
3553 return fade_tooltip;
3554 case UI_AnimateToolBox:
3555 return animate_toolbox;
3556 default:
3557 return animate_ui;
3558 }
3559#else
3560 return FALSE;
3561#endif
3562}
3563
3564void QApplication::flush()
3565{
3566}
3567
3568#if !defined (QT_NO_SESSIONMANAGER)
3569
3570bool qt_app_canQuit()
3571{
3572#if defined (DEBUG_SESSIONMANAGER)
3573 qDebug( "qt_app_canQuit(): sm_smActive=%d qt_about_to_destroy_wnd=%d "
3574 "sm_gracefulShutdown=%d sm_cancel=%d",
3575 sm_smActive, qt_about_to_destroy_wnd,
3576 sm_gracefulShutdown, sm_cancel );
3577#endif
3578
3579 BOOL answer = FALSE;
3580
3581 // We can get multiple WM_QUIT messages while the "session termination
3582 // procedure" (i.e. the QApplication::commitData() call) is still in
3583 // progress. Ignore them.
3584 if ( !sm_smActive ) {
3585 if ( sm_gracefulShutdown ) {
3586 // this is WM_QUIT after WM_SAVEAPPLICATION (either posted by the OS
3587 // or by ourselves), confirm the quit depending on what the user wants
3588 sm_quitSkipped = FALSE;
3589 answer = !sm_cancel;
3590 if ( sm_cancel ) {
3591 // the shutdown has been canceled, reset the flag to let the
3592 // graceful shutdown happen again later
3593 sm_gracefulShutdown = FALSE;
3594 }
3595 } else {
3596 // sm_gracefulShutdown is FALSE, so allowsInteraction() and friends
3597 // will return FALSE during commitData() (assuming that WM_QUIT w/o
3598 // WM_SAVEAPPLICATION is an emergency termination)
3599 sm_smActive = TRUE;
3600 sm_blockUserInput = TRUE; // prevent user-interaction outside interaction windows
3601 sm_cancel = FALSE;
3602 if ( qt_session_manager_self )
3603 qApp->commitData( *qt_session_manager_self );
3604 sm_smActive = FALSE;
3605 answer = TRUE; // ignore sm_cancel
3606 }
3607 } else {
3608 // if this is a WM_QUIT received during WM_SAVEAPPLICATION handling,
3609 // remember we've skipped (refused) it
3610 if ( sm_gracefulShutdown )
3611 sm_quitSkipped = TRUE;
3612 }
3613
3614#if defined (DEBUG_SESSIONMANAGER)
3615 qDebug( "qt_app_canQuit(): answer=%ld", answer );
3616#endif
3617
3618 return answer;
3619}
3620
3621bool QSessionManager::allowsInteraction()
3622{
3623 // Allow interation only when the system is being normally shutdown
3624 // and informs us using WM_SAVEAPPLICATION. When we receive WM_QUIT directly
3625 // (so sm_gracefulShutdown is FALSE), interaction is disallowed.
3626 if ( sm_smActive && sm_gracefulShutdown ) {
3627 sm_blockUserInput = FALSE;
3628 return TRUE;
3629 }
3630
3631 return FALSE;
3632}
3633
3634bool QSessionManager::allowsErrorInteraction()
3635{
3636 // Allow interation only when the system is being normally shutdown
3637 // and informs us using WM_SAVEAPPLICATION. When we receive WM_QUIT directly
3638 // (so sm_gracefulShutdown is FALSE), interaction is disallowed.
3639 if ( sm_smActive && sm_gracefulShutdown ) {
3640 sm_blockUserInput = FALSE;
3641 return TRUE;
3642 }
3643
3644 return FALSE;
3645}
3646
3647void QSessionManager::release()
3648{
3649 if ( sm_smActive && sm_gracefulShutdown )
3650 sm_blockUserInput = TRUE;
3651}
3652
3653void QSessionManager::cancel()
3654{
3655 if ( sm_smActive && sm_gracefulShutdown )
3656 sm_cancel = TRUE;
3657}
3658
3659#endif
Note: See TracBrowser for help on using the repository browser.