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

Last change on this file since 171 was 171, checked in by dmik, 18 years ago

Kernel: Fixed: QWidge::pmEvent() was not called for key press/release events delivered to non-toplevel widgets.

  • Property svn:keywords set to Id
File size: 130.1 KB
Line 
1/****************************************************************************
2** $Id: qapplication_pm.cpp 171 2007-10-01 21:33:02Z 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 // send through widget filter
1202 // (WM_CHAR will be handled later)
1203 if ( msg != WM_CHAR )
1204 if ( widget->pmEvent( &qmsg ) )
1205 RETURN( TRUE );
1206
1207 if ( isMouseEvent ) { // mouse events
1208 if ( qApp->activePopupWidget() != 0 ) { // in popup mode
1209 QWidget* w = QApplication::widgetAt( qmsg.ptl.x, qmsg.ptl.y, TRUE );
1210 if ( w ) {
1211 POINTL ptl = { SHORT1FROMMP(qmsg.mp1), SHORT2FROMMP(qmsg.mp1) };
1212 WinMapWindowPoints( qmsg.hwnd, w->winId(), &ptl, 1 );
1213 qmsg.mp1 = MPFROM2SHORT( ptl.x, ptl.y );
1214 widget = (QETWidget*)w;
1215 }
1216 }
1217 result = widget->translateMouseEvent( qmsg );
1218 rc = (MRESULT) result;
1219#ifndef QT_NO_WHEELEVENT
1220 } else if ( msg == WM_VSCROLL || msg == WM_HSCROLL ) {
1221 result = widget->translateWheelEvent( qmsg );
1222 rc = (MRESULT) result;
1223#endif
1224#ifndef QT_NO_DRAGANDDROP
1225 } else if ( msg >= WM_DRAGFIRST && msg <= WM_DRAGLAST ) {
1226 RETURN( qt_dispatchDragAndDrop( widget, qmsg ) );
1227#endif
1228 } else {
1229 switch ( msg ) {
1230
1231 case WM_TRANSLATEACCEL:
1232 if ( widget->isTopLevel() ) {
1233 rc = WinDefWindowProc( hwnd, msg, mp1, mp2 );
1234 if ( rc ) {
1235 QMSG &qmsg = *(QMSG*) mp1;
1236 if (
1237 qmsg.msg == WM_SYSCOMMAND &&
1238 WinWindowFromID( widget->winFId(), FID_SYSMENU )
1239 ) {
1240 switch ( SHORT1FROMMP(qmsg.mp1) ) {
1241 case SC_CLOSE:
1242 case SC_TASKMANAGER:
1243 RETURN( TRUE );
1244 default:
1245 break;
1246 }
1247 }
1248 }
1249 }
1250 // return FALSE in all other cases to let Qt process keystrokes
1251 // that are in the system-wide frame accelerator table.
1252 RETURN( FALSE );
1253
1254 case WM_CHAR: { // keyboard event
1255#if 0
1256 qDebug( "WM_CHAR: [%s]", widget->name() );
1257#endif
1258 QWidget *g = QWidget::keyboardGrabber();
1259 if ( g )
1260 widget = (QETWidget*)g;
1261 else if ( qApp->focusWidget() )
1262 widget = (QETWidget*)qApp->focusWidget();
1263 else if ( !widget )
1264/// @todo (dmik) currently we don't use WinSetFocus(). what for? Qt seems
1265// to completely handle focus traversal itself.
1266// || widget->winId() == WinQueryFocus( HWND_DESKTOP ) ) // We faked the message to go to exactly that widget.
1267 widget = (QETWidget*)widget->topLevelWidget();
1268
1269 if ( widget->pmEvent( &qmsg ) )
1270 RETURN( TRUE );
1271
1272 if ( widget->isEnabled() ) {
1273/// @todo (dmik) we should not pass WM_CHAR to the default window proc,
1274// otherwise it will come to us again through the widget parent (owner in PM)
1275// if the widget is not top-level, and will be treated by translateKeyEvent()
1276// as a key repeat (in case of key down) or a lost key (in case of key up).
1277// NOTE: currently we don't use WinSetFocus(), so the active top-level window
1278// will always have the focus, so it doesn't matter wheither we pass WM_CHAR
1279// to the default window proc or not.
1280// result = widget->translateKeyEvent( qmsg, g != 0 );
1281// rc = (MRESULT) result;
1282 RETURN( widget->translateKeyEvent( qmsg, g != 0 ) );
1283 }
1284 break;
1285 }
1286
1287 case WM_QUERYCONVERTPOS :
1288 {
1289 // Ooops, how to get caret pos ?
1290 /// @todo (r=dmik) we should make usage of the QIMEvent
1291 // to query widgets about IME data (see how/where this event
1292 // is used across the sources)
1293
1294 PRECTL pcp = ( PRECTL )mp1;
1295 CURSORINFO ci;
1296
1297 WinQueryCursorInfo( HWND_DESKTOP, &ci );
1298
1299 memset( pcp, 0xFF, sizeof( RECTL ));
1300
1301 pcp->xLeft = ci.x;
1302 pcp->yBottom = ci.y;
1303 WinMapWindowPoints( ci.hwnd, hwnd, ( PPOINTL )pcp, 2 );
1304
1305 RETURN( QCP_CONVERT );
1306 }
1307
1308/// @todo (dmik) later
1309// case WM_APPCOMMAND:
1310// {
1311// uint cmd = GET_APPCOMMAND_LPARAM(lParam);
1312// uint uDevice = GET_DEVICE_LPARAM(lParam);
1313// uint dwKeys = GET_KEYSTATE_LPARAM(lParam);
1314//
1315// int state = translateButtonState( dwKeys, QEvent::KeyPress, 0 );
1316//
1317// switch ( uDevice ) {
1318// case FAPPCOMMAND_KEY:
1319// {
1320// int key = 0;
1321//
1322// switch( cmd ) {
1323// case APPCOMMAND_BASS_BOOST:
1324// key = Qt::Key_BassBoost;
1325// break;
1326// case APPCOMMAND_BASS_DOWN:
1327// key = Qt::Key_BassDown;
1328// break;
1329// case APPCOMMAND_BASS_UP:
1330// key = Qt::Key_BassUp;
1331// break;
1332// case APPCOMMAND_BROWSER_BACKWARD:
1333// key = Qt::Key_Back;
1334// break;
1335// case APPCOMMAND_BROWSER_FAVORITES:
1336// key = Qt::Key_Favorites;
1337// break;
1338// case APPCOMMAND_BROWSER_FORWARD:
1339// key = Qt::Key_Forward;
1340// break;
1341// case APPCOMMAND_BROWSER_HOME:
1342// key = Qt::Key_HomePage;
1343// break;
1344// case APPCOMMAND_BROWSER_REFRESH:
1345// key = Qt::Key_Refresh;
1346// break;
1347// case APPCOMMAND_BROWSER_SEARCH:
1348// key = Qt::Key_Search;
1349// break;
1350// case APPCOMMAND_BROWSER_STOP:
1351// key = Qt::Key_Stop;
1352// break;
1353// case APPCOMMAND_LAUNCH_APP1:
1354// key = Qt::Key_Launch0;
1355// break;
1356// case APPCOMMAND_LAUNCH_APP2:
1357// key = Qt::Key_Launch1;
1358// break;
1359// case APPCOMMAND_LAUNCH_MAIL:
1360// key = Qt::Key_LaunchMail;
1361// break;
1362// case APPCOMMAND_LAUNCH_MEDIA_SELECT:
1363// key = Qt::Key_LaunchMedia;
1364// break;
1365// case APPCOMMAND_MEDIA_NEXTTRACK:
1366// key = Qt::Key_MediaNext;
1367// break;
1368// case APPCOMMAND_MEDIA_PLAY_PAUSE:
1369// key = Qt::Key_MediaPlay;
1370// break;
1371// case APPCOMMAND_MEDIA_PREVIOUSTRACK:
1372// key = Qt::Key_MediaPrev;
1373// break;
1374// case APPCOMMAND_MEDIA_STOP:
1375// key = Qt::Key_MediaStop;
1376// break;
1377// case APPCOMMAND_TREBLE_DOWN:
1378// key = Qt::Key_TrebleDown;
1379// break;
1380// case APPCOMMAND_TREBLE_UP:
1381// key = Qt::Key_TrebleUp;
1382// break;
1383// case APPCOMMAND_VOLUME_DOWN:
1384// key = Qt::Key_VolumeDown;
1385// break;
1386// case APPCOMMAND_VOLUME_MUTE:
1387// key = Qt::Key_VolumeMute;
1388// break;
1389// case APPCOMMAND_VOLUME_UP:
1390// key = Qt::Key_VolumeUp;
1391// break;
1392// default:
1393// break;
1394// }
1395// if ( key ) {
1396// bool res = FALSE;
1397// QWidget *g = QWidget::keyboardGrabber();
1398// if ( g )
1399// widget = (QETWidget*)g;
1400// else if ( qApp->focusWidget() )
1401// widget = (QETWidget*)qApp->focusWidget();
1402// else
1403// widget = (QETWidget*)widget->topLevelWidget();
1404// if ( widget->isEnabled() )
1405// res = ((QETWidget*)widget)->sendKeyEvent( QEvent::KeyPress, key, 0, state, FALSE, QString::null, g != 0 );
1406// if ( res )
1407// return TRUE;
1408// }
1409// }
1410// break;
1411//
1412// default:
1413// break;
1414// }
1415//
1416// result = FALSE;
1417// }
1418// break;
1419
1420//@@TODO (dmik):
1421// we can cause WC_FRAME to send equivalents of WM_NCMOUSEMOVE,
1422// but is it really necesary? We already do some similar stuff in WM_HITTEST
1423//#ifndef Q_OS_TEMP
1424// case WM_NCMOUSEMOVE:
1425// {
1426// // span the application wide cursor over the
1427// // non-client area.
1428// QCursor *c = qt_grab_cursor();
1429// if ( !c )
1430// c = QApplication::overrideCursor();
1431// if ( c ) // application cursor defined
1432// SetCursor( c->handle() );
1433// else
1434// result = FALSE;
1435// // generate leave event also when the caret enters
1436// // the non-client area.
1437// qt_dispatchEnterLeave( 0, QWidget::find(curWin) );
1438// curWin = 0;
1439// }
1440// break;
1441//#endif
1442
1443/// @todo (dmik) later (WM_KBDLAYERCHANGED (0xBD4)?)
1444// case WM_SETTINGCHANGE:
1445// if ( !msg.wParam ) {
1446// QString area = QT_WA_INLINE( QString::fromUcs2( (unsigned short *)msg.lParam ),
1447// QString::fromLocal8Bit( (char*)msg.lParam ) );
1448// if ( area == "intl" )
1449// QApplication::postEvent( widget, new QEvent( QEvent::LocaleChange ) );
1450// }
1451// break;
1452//
1453 case WM_PAINT: // paint event
1454 result = widget->translatePaintEvent( qmsg );
1455 break;
1456 case WM_ERASEBACKGROUND: // erase window background
1457 // flush WM_PAINT messages here to update window contents
1458 // instantly while tracking the resize frame (normally these
1459 // messages are delivered after the user has stopped resizing
1460 // for some time). this slows down resizing slightly but gives a
1461 // better look (no invalid window contents can be seen during
1462 // resize). the alternative could be to erase the background only,
1463 // but we need to do it for every non-toplevel window, which can
1464 // also be time-consuming (WM_ERASEBACKGROUND is sent to WC_FRAME
1465 // clients only, so we would have to do all calculations ourselves).
1466 WinUpdateWindow( widget->winId() );
1467 RETURN( FALSE );
1468 break;
1469 case WM_CALCVALIDRECTS:
1470 // we must always return this value here to cause PM to reposition
1471 // our children accordingly (othwerwise we would have to do it
1472 // ourselves to keep them top-left aligned).
1473 RETURN( CVR_ALIGNLEFT | CVR_ALIGNTOP );
1474 break;
1475
1476 case WM_MOVE: // move window
1477 case WM_SIZE: // resize window
1478 result = widget->translateConfigEvent( qmsg );
1479 break;
1480
1481 case WM_ACTIVATE:
1482#if 0
1483 qDebug( "WM_ACTIVATE: [%s] %d", widget->name(), SHORT1FROMMP(mp1) );
1484#endif
1485 qApp->pmFocus( widget, SHORT1FROMMP(mp1) );
1486 break;
1487
1488 case WM_SETFOCUS:
1489#if 0
1490 qDebug( "WM_SETFOCUS: [%s] %s [%s]", widget->name(),
1491 SHORT1FROMMP(mp2) ? "<=" : "=>",
1492 QWidget::find( (HWND)mp1 ) ? QWidget::find( (HWND)mp1 )->name()
1493 : "{foreign}" );
1494#endif
1495 result = FALSE;
1496 if ( !SHORT1FROMMP(mp2) ) {
1497 // we're losing focus
1498 if ( !QWidget::find( (HWND)mp1 ) ) {
1499 if ( QApplication::activePopupWidget() ) {
1500 // Another application was activated while our popups are open,
1501 // then close all popups. In case some popup refuses to close,
1502 // we give up after 1024 attempts (to avoid an infinite loop).
1503 int maxiter = 1024;
1504 QWidget *popup;
1505 while ( (popup=QApplication::activePopupWidget()) && maxiter-- )
1506 popup->close();
1507 }
1508 if (
1509 // non-Qt ownees of our WC_FRAME window (such as
1510 // FID_SYSMENU) should not cause the focus to be lost.
1511 WinQueryWindow( (HWND)mp1, QW_OWNER ) ==
1512 widget->topLevelWidget()->winFId()
1513 )
1514 break;
1515 if ( !widget->isTopLevel() )
1516 qApp->pmFocus( widget, SHORT1FROMMP(mp2) );
1517 }
1518 }
1519 break;
1520
1521/// @todo (dmik) remove?
1522//#ifndef Q_OS_TEMP
1523// case WM_MOUSEACTIVATE:
1524// {
1525// const QWidget *tlw = widget->topLevelWidget();
1526// // Do not change activation if the clicked widget is inside a floating dock window
1527// if ( tlw->inherits( "QDockWindow" ) && qApp->activeWindow()
1528// && !qApp->activeWindow()->inherits("QDockWindow") )
1529// RETURN(MA_NOACTIVATE);
1530// }
1531// result = FALSE;
1532// break;
1533//#endif
1534
1535 case WM_SHOW:
1536 if ( !SHORT1FROMMP(mp1) && autoCaptureWnd == widget->winId() )
1537 releaseAutoCapture();
1538 result = FALSE;
1539 break;
1540/// @todo (dmik) remove later
1541// case WM_SHOWWINDOW:
1542//#ifndef Q_OS_TEMP
1543// if ( lParam == SW_PARENTOPENING ) {
1544// if ( widget->testWState(Qt::WState_ForceHide) )
1545// RETURN(0);
1546// }
1547//#endif
1548// if (!wParam && autoCaptureWnd == widget->winId())
1549// releaseAutoCapture();
1550// result = FALSE;
1551// break;
1552
1553 case WM_REALIZEPALETTE: // realize own palette
1554 if ( QColor::hPal() ) {
1555 HPS hps = WinGetPS( widget->winId() );
1556 GpiSelectPalette( hps, QColor::hPal() );
1557 ULONG cclr;
1558 WinRealizePalette( widget->winId(), hps, &cclr );
1559 WinReleasePS( hps );
1560 // on OS/2, the value returned by WinRealizePalette() does not
1561 // necessarily reflect the number of colors that have been
1562 // remapped. therefore, we cannot rely on it and must always
1563 // invalidate the window.
1564 WinInvalidateRect( widget->winId(), NULL, TRUE );
1565 RETURN( 0 );
1566 }
1567 break;
1568
1569 case WM_CLOSE: // close window
1570 widget->translateCloseEvent( qmsg );
1571 RETURN(0); // always handled
1572
1573/// @todo (dmik) it seems we don't need this
1574// case WM_DESTROY: // destroy window
1575// if ( hwnd == curWin ) {
1576// QEvent leave( QEvent::Leave );
1577// QApplication::sendEvent( widget, &leave );
1578// curWin = 0;
1579// }
1580// // We are blown away when our parent reparents, so we have to
1581// // recreate the handle
1582// if (widget->testWState(Qt::WState_Created))
1583// ((QETWidget*)widget)->reparentWorkaround();
1584// if ( widget == popupButtonFocus )
1585// popupButtonFocus = 0;
1586// result = FALSE;
1587// break;
1588
1589 case WM_CONTEXTMENU:
1590 if ( SHORT2FROMMP(mp2) ) {
1591 // keyboard event
1592 QWidget *fw = qApp->focusWidget();
1593 if ( fw ) {
1594 QContextMenuEvent e(
1595 QContextMenuEvent::Keyboard,
1596 QPoint( 5, 5 ),
1597 fw->mapToGlobal( QPoint( 5, 5 ) ),
1598 0
1599 );
1600 result = qt_sendSpontaneousEvent( fw, &e );
1601 rc = (MRESULT) result;
1602 }
1603 } else {
1604 // mouse event
1605 result = widget->translateMouseEvent( qmsg );
1606 rc = (MRESULT) result;
1607 }
1608 break;
1609
1610/// @todo (dmik) remove?
1611// case WM_IME_STARTCOMPOSITION:
1612// result = QInputContext::startComposition();
1613// break;
1614// case WM_IME_ENDCOMPOSITION:
1615// result = QInputContext::endComposition();
1616// break;
1617// case WM_IME_COMPOSITION:
1618// result = QInputContext::composition( lParam );
1619// break;
1620
1621#ifndef QT_NO_CLIPBOARD
1622 case WM_DRAWCLIPBOARD:
1623 case WM_RENDERFMT:
1624 case WM_RENDERALLFMTS:
1625 case WM_DESTROYCLIPBOARD:
1626 if ( qt_clipboard ) {
1627 QCustomEvent e( QEvent::Clipboard, &qmsg );
1628 qt_sendSpontaneousEvent( qt_clipboard, &e );
1629 RETURN(0);
1630 }
1631 result = FALSE;
1632 break;
1633#endif
1634
1635/// @todo (dmik) remove? functionality is implemented in WM_SETFOCUS above.
1636// case WM_KILLFOCUS:
1637// if ( !QWidget::find( (HWND)wParam ) ) { // we don't get focus, so unset it now
1638// if ( !widget->hasFocus() ) // work around Windows bug after minimizing/restoring
1639// widget = (QETWidget*)qApp->focusWidget();
1640// HWND focus = ::GetFocus();
1641// if ( !widget || (focus && ::IsChild( widget->winId(), focus )) ) {
1642// result = FALSE;
1643// } else {
1644// widget->clearFocus();
1645// result = TRUE;
1646// }
1647// } else {
1648// result = FALSE;
1649// }
1650// break;
1651
1652//@@TODO (dmik): later
1653// case WM_THEMECHANGED:
1654// if ( widget->testWFlags( Qt::WType_Desktop ) || !qApp || qApp->closingDown()
1655// || qApp->type() == QApplication::Tty )
1656// break;
1657//
1658// if ( widget->testWState(Qt::WState_Polished) )
1659// qApp->style().unPolish(widget);
1660//
1661// if ( widget->testWState(Qt::WState_Polished) )
1662// qApp->style().polish(widget);
1663// widget->repolishStyle( qApp->style() );
1664// if ( widget->isVisible() )
1665// widget->update();
1666// break;
1667//
1668// case WM_COMMAND:
1669// result = (wParam == 0x1);
1670// if ( result )
1671// QApplication::postEvent( widget, new QEvent( QEvent::OkRequest ) );
1672// break;
1673// case WM_HELP:
1674// QApplication::postEvent( widget, new QEvent( QEvent::HelpRequest ) );
1675// result = TRUE;
1676// break;
1677//#endif
1678
1679 case WM_U_MOUSELEAVE:
1680 // We receive a mouse leave for curWin, meaning
1681 // the mouse was moved outside our widgets
1682 if ( widget->winId() == curWin && (HWND) mp1 == curWin ) {
1683 bool dispatch = !widget->hasMouse();
1684 // hasMouse is updated when dispatching enter/leave,
1685 // so test if it is actually up-to-date
1686 if ( !dispatch ) {
1687 QRect geom = widget->geometry();
1688 if ( widget->parentWidget() && !widget->isTopLevel() ) {
1689 QPoint gp = widget->parentWidget()->mapToGlobal( widget->pos() );
1690 geom.setX( gp.x() );
1691 geom.setY( gp.y() );
1692 }
1693 QPoint cpos = QCursor::pos();
1694 dispatch = !geom.contains( cpos );
1695 }
1696 if ( dispatch ) {
1697 qt_dispatchEnterLeave( 0, QWidget::find( (WId)curWin ) );
1698 curWin = 0;
1699 }
1700 }
1701 break;
1702
1703/// @todo (dmik) remove? functionality is implemented in WM_SETFOCUS above.
1704// case WM_CANCELMODE:
1705// if ( qApp->focusWidget() ) {
1706// QFocusEvent::setReason( QFocusEvent::ActiveWindow );
1707// QFocusEvent e( QEvent::FocusOut );
1708// QApplication::sendEvent( qApp->focusWidget(), &e );
1709// QFocusEvent::resetReason();
1710// }
1711// break;
1712
1713 default:
1714 result = FALSE; // event was not processed
1715 break;
1716 }
1717 }
1718
1719 if ( evt_type != QEvent::None ) { // simple event
1720 QEvent e( evt_type );
1721 result = qt_sendSpontaneousEvent( widget, &e );
1722 }
1723 if ( result )
1724 RETURN( rc );
1725
1726do_default:
1727 RETURN( WinDefWindowProc( hwnd, msg, mp1, mp2 ) );
1728/// @todo (dmik) remove?
1729// RETURN( QInputContext::DefWindowProc(hwnd,message,wParam,lParam) )
1730}
1731
1732PFNWP QtOldFrameProc = 0;
1733
1734extern "C" MRESULT EXPENTRY QtFrameProc( HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2 )
1735{
1736 // message handling indicators: if result is true at the end of message
1737 // processing, no default window proc is called but rc is returned.
1738 bool result = FALSE;
1739 MRESULT rc = (MRESULT) FALSE;
1740 QETWidget *widget = 0;
1741 HWND hwndC = 0;
1742
1743 if ( !qApp ) // unstable app state
1744 goto do_default;
1745
1746 // make sure we show widgets (e.g. scrollbars) when the user resizes
1747 if ( inLoop && qApp->loopLevel() )
1748 qApp->sendPostedEvents( 0, QEvent::ShowWindowRequest );
1749
1750 inLoop = TRUE;
1751
1752 hwndC = WinWindowFromID( hwnd, FID_CLIENT );
1753 widget = (QETWidget*)QWidget::find( hwndC );
1754 if ( !widget ) // don't know this widget
1755 goto do_default;
1756
1757 switch ( msg ) {
1758 case WM_HITTEST: {
1759 if ( !WinIsWindowEnabled( hwnd ) ) {
1760 if (
1761 qApp->activePopupWidget() &&
1762 (WinQueryQueueStatus( HWND_DESKTOP ) & QS_MOUSEBUTTON)
1763 ) {
1764 // the user has clicked over the Qt window that is disabled
1765 // by some modal widget, therefore we close all popups. In
1766 // case some popup refuses to close, we give up after 1024
1767 // attempts (to avoid an infinite loop).
1768 int maxiter = 1024;
1769 QWidget *popup;
1770 while ( (popup=QApplication::activePopupWidget()) && maxiter-- )
1771 popup->close();
1772 }
1773#ifndef QT_NO_CURSOR
1774 else {
1775 QCursor *c = qt_grab_cursor();
1776 if ( !c )
1777 c = QApplication::overrideCursor();
1778 if ( c ) // application cursor defined
1779 WinSetPointer( HWND_DESKTOP, c->handle() );
1780 else
1781 WinSetPointer( HWND_DESKTOP, Qt::arrowCursor.handle() );
1782 }
1783#endif
1784 }
1785 break;
1786 }
1787
1788 case WM_ADJUSTWINDOWPOS: {
1789 SWP &swp = *(PSWP) mp1;
1790 if ( swp.fl & SWP_MAXIMIZE ) {
1791 QWExtra *x = widget->xtra();
1792 if ( x ) {
1793 result = TRUE;
1794 rc = QtOldFrameProc( hwnd, msg, mp1, mp2 );
1795 int maxw = QWIDGETSIZE_MAX, maxh = QWIDGETSIZE_MAX;
1796 QTLWExtra *top = widget->top();
1797 if ( x->maxw < QWIDGETSIZE_MAX )
1798 maxw = x->maxw + top->fleft + top->fright;
1799 if ( x->maxh < QWIDGETSIZE_MAX )
1800 maxh = x->maxh + top->ftop + top->fbottom;
1801 if ( maxw < QWIDGETSIZE_MAX ) swp.cx = maxw;
1802 if ( maxh < QWIDGETSIZE_MAX ) {
1803 swp.y = swp.y + swp.cy - maxh;
1804 swp.cy = maxh;
1805 }
1806 }
1807 }
1808 if ( (swp.fl & SWP_RESTORE) &&
1809 !(swp.fl & (SWP_MOVE | SWP_SIZE)) ) {
1810 QRect r = widget->top()->normalGeometry;
1811 if ( r.isValid() ) {
1812 // store normal geometry in window words
1813 USHORT x = r.x();
1814 USHORT y = r.y();
1815 USHORT w = r.width();
1816 USHORT h = r.height();
1817 // flip y coordinate
1818 y = QApplication::desktop()->height() - (y + h);
1819 WinSetWindowUShort( hwnd, QWS_XRESTORE, x );
1820 WinSetWindowUShort( hwnd, QWS_YRESTORE, y );
1821 WinSetWindowUShort( hwnd, QWS_CXRESTORE, w );
1822 WinSetWindowUShort( hwnd, QWS_CYRESTORE, h );
1823 widget->top()->normalGeometry.setWidth( 0 );
1824 }
1825 }
1826 if ( swp.fl & (SWP_ACTIVATE | SWP_ZORDER) ) {
1827 // get the modal widget that made this window blocked
1828 QWidget *m =
1829 (QWidget*) WinQueryWindowPtr( widget->winId(), QWL_QTMODAL );
1830 if( m ) {
1831 if ( swp.fl & SWP_ACTIVATE ) {
1832 QWidget *a = qt_modal_stack->first();
1833 if ( !a->isActiveWindow() )
1834 a->setActiveWindow();
1835 swp.fl &= ~SWP_ACTIVATE;
1836 }
1837 if ( swp.fl & SWP_ZORDER ) {
1838 QWidget *mp = m->parentWidget();
1839 if ( mp ) {
1840 mp = mp->topLevelWidget();
1841 if ( !mp->isDesktop() && mp != widget )
1842 m = mp;
1843 }
1844 HWND hm = m->winFId();
1845 if ( swp.hwndInsertBehind != hm ) {
1846 swp.hwndInsertBehind = hm;
1847 }
1848 }
1849 }
1850 }
1851 break;
1852 }
1853
1854 case WM_QUERYTRACKINFO: {
1855 QWExtra *x = widget->xtra();
1856 if ( x ) {
1857 result = TRUE;
1858 rc = QtOldFrameProc( hwnd, msg, mp1, mp2 );
1859 PTRACKINFO pti = (PTRACKINFO) mp2;
1860 int minw = 0, minh = 0;
1861 int maxw = QWIDGETSIZE_MAX, maxh = QWIDGETSIZE_MAX;
1862 QTLWExtra *top = widget->top();
1863 if ( x->minw > 0 )
1864 minw = x->minw + top->fleft + top->fright;
1865 if ( x->minh > 0 )
1866 minh = x->minh + top->ftop + top->fbottom;
1867 if ( x->maxw < QWIDGETSIZE_MAX )
1868 maxw = x->maxw + top->fleft + top->fright;
1869 if ( x->maxh < QWIDGETSIZE_MAX )
1870 maxh = x->maxh + top->ftop + top->fbottom;
1871 // obey system recommended minimum size (to emulate Qt/Win32)
1872 pti->ptlMinTrackSize.x = QMAX( minw, pti->ptlMinTrackSize.x );
1873 pti->ptlMinTrackSize.y = QMAX( minh, pti->ptlMinTrackSize.y );
1874 pti->ptlMaxTrackSize.x = maxw;
1875 pti->ptlMaxTrackSize.y = maxh;
1876 }
1877 break;
1878 }
1879
1880 case WM_TRACKFRAME: {
1881 if ( QApplication::activePopupWidget() ) {
1882 // The user starts to size/move the frame window, therefore
1883 // we close all popups. In case some popup refuses to close,
1884 // we give up after 1024 attempts (to avoid an infinite loop).
1885 int maxiter = 1024;
1886 QWidget *popup;
1887 while ( (popup=QApplication::activePopupWidget()) && maxiter-- )
1888 popup->close();
1889 }
1890 break;
1891 }
1892
1893 case WM_WINDOWPOSCHANGED: {
1894 // We detect spontaneous min/max/restore events here instead of
1895 // WM_MINMAXFRAME, because WM_MINMAXFRAME is a pre-process message
1896 // (i.e. no actual changes have been made) We need actual changes
1897 // in order to update the frame strut and send WindowStateChange.
1898 result = TRUE;
1899 rc = QtOldFrameProc( hwnd, msg, mp1, mp2 );
1900 ULONG awp = LONGFROMMP( mp2 );
1901 bool window_state_change = FALSE;
1902 if ( awp & AWP_MAXIMIZED ) {
1903 window_state_change = TRUE;
1904 widget->setWState( Qt::WState_Maximized );
1905 widget->clearWState( Qt::WState_FullScreen );
1906 if ( widget->isMinimized() ) {
1907 widget->clearWState( Qt::WState_Minimized );
1908 widget->showChildren( TRUE );
1909 QShowEvent e;
1910 qt_sendSpontaneousEvent( widget, &e );
1911 }
1912 } else if ( awp & AWP_MINIMIZED ) {
1913 window_state_change = TRUE;
1914 widget->setWState( Qt::WState_Minimized );
1915 widget->clearWState( Qt::WState_Maximized );
1916 widget->clearWState( Qt::WState_FullScreen );
1917 if ( widget->isVisible() ) {
1918 QHideEvent e;
1919 qt_sendSpontaneousEvent( widget, &e );
1920 widget->hideChildren( TRUE );
1921 }
1922 } else if ( awp & AWP_RESTORED ) {
1923 window_state_change = TRUE;
1924 if ( widget->isMinimized() ) {
1925 widget->showChildren( TRUE );
1926 QShowEvent e;
1927 qt_sendSpontaneousEvent( widget, &e );
1928 }
1929 widget->clearWState( Qt::WState_Minimized );
1930 widget->clearWState( Qt::WState_Maximized );
1931 }
1932 if ( window_state_change ) {
1933 widget->updateFrameStrut();
1934 if ( !widget->top()->in_sendWindowState ) {
1935 // send WindowStateChange event if this message is NOT
1936 // originated from QWidget::setWindowState().
1937 QEvent e( QEvent::WindowStateChange );
1938 qt_sendSpontaneousEvent( widget, &e );
1939 }
1940 }
1941 break;
1942 }
1943
1944 default:
1945 break;
1946 }
1947
1948 if ( result )
1949 RETURN( rc );
1950
1951do_default:
1952 RETURN( QtOldFrameProc( hwnd, msg, mp1, mp2 ) );
1953}
1954
1955/*****************************************************************************
1956 Modal widgets; We have implemented our own modal widget mechanism
1957 to get total control.
1958 A modal widget without a parent becomes application-modal.
1959 A modal widget with a parent becomes modal to its parent and grandparents..
1960
1961//@@TODO (dmik): the above comment is not correct (outdated?): for example,
1962// in accordance with the current Qt logic, a modal widget with a parent
1963// becomes modal to its (grand)parents upto the first of them that has the
1964// WGroupLeader flag, not necessarily to all...
1965
1966 qt_enter_modal()
1967 Enters modal state
1968 Arguments:
1969 QWidget *widget A modal widget
1970
1971 qt_leave_modal()
1972 Leaves modal state for a widget
1973 Arguments:
1974 QWidget *widget A modal widget
1975 *****************************************************************************/
1976
1977Q_EXPORT bool qt_modal_state()
1978{
1979 return app_do_modal;
1980}
1981
1982// helper for qt_dispatchBlocked().
1983// sends block events to the given widget and its children.
1984void qt_sendBlocked( QObject *obj, QWidget *modal, QEvent *e, bool override )
1985{
1986 if ( obj == modal ) {
1987 // don't touch modal itself and its children
1988 return;
1989 }
1990 bool blocked = e->type() == QEvent::WindowBlocked;
1991
1992 if ( obj->isWidgetType() ) {
1993 QWidget *w = (QWidget*) obj;
1994 if ( w->isTopLevel() ) {
1995 if ( w->testWFlags( Qt::WGroupLeader ) && !override ) {
1996 // stop sending on group leaders
1997 return;
1998 }
1999 QWidget *blockedBy =
2000 (QWidget*) WinQueryWindowPtr( w->winId(), QWL_QTMODAL );
2001 if ( blocked ) {
2002 // stop sending on alreay blocked widgets
2003 if ( blockedBy )
2004 return;
2005 } else {
2006 // stop sending on widgets blocked by another modal
2007 if ( blockedBy != modal )
2008 return;
2009 }
2010 WinSetWindowPtr( w->winId(), QWL_QTMODAL, blocked ? modal : NULL );
2011 WinEnableWindow( w->winFId(), !blocked );
2012 }
2013 }
2014 QApplication::sendEvent( obj, e );
2015
2016 // now send blocked to children
2017 if ( obj->children() ) {
2018 QObjectListIt it( *obj->children() );
2019 QObject *o;
2020 while( ( o = it.current() ) != 0 ) {
2021 ++it;
2022 qt_sendBlocked( o, modal, e, blocked ? FALSE : TRUE );
2023 }
2024 }
2025}
2026
2027// sends blocked/unblocked events to top-level widgets depending on the
2028// modal widget given and the WGroupLeader flag presence.
2029static void qt_dispatchBlocked( QWidget *modal, bool blocked )
2030{
2031 // we process those top-level windows that must be blocked
2032 // by the given modal -- this should correlate with the
2033 // qt_tryModalHelper() logic that is widget-centric (i.e. a try
2034 // to block a particular widget given the current set of
2035 // modals) rather than modal-centric (a try to block the current set
2036 // of widgets given a particular modal); currently it blocks events
2037 // for a top-level widget if the widget doesn't have (a parent with
2038 // with) the WGroupLeader flag or it has but (this parent) has a child
2039 // among the current set of modals. So, the modal-centric logic is
2040 // to block any top-level widget unless it has (a parent with) the
2041 // WGroupLeader flag and (this parent) is not (a child of) the modal's
2042 // group leader.
2043
2044 QEvent e( blocked ? QEvent::WindowBlocked : QEvent::WindowUnblocked );
2045
2046 if ( !blocked ) {
2047 // For unblocking, we go through all top-level widgets and unblock
2048 // those that have been blocked by the given modal widget. It means that
2049 // we don't select widgets to unblock based on widget flags (as we do
2050 // below when blocking) because these flags might have been changed
2051 // so that we could skip widgets that we would have to unblock. Although
2052 // normally widget flags should never be changed after widget creation
2053 // (except that by QWidget::reparentSys() that takes blocking into
2054 // account and corrects it as needed), nothing stops one from doing so,
2055 // leading to a case when the QWL_QTMODAL word of some widget still
2056 // points to a widget that has been deleted (so that any attempt to
2057 // access it results into a nasty segfault).
2058 QWidgetList *list = QApplication::topLevelWidgets();
2059 for( QWidget *w = list->first(); w; w = list->next() ) {
2060 if ( WinQueryWindowPtr( w->winId(), QWL_QTMODAL ) == modal )
2061 qt_sendBlocked( w, modal, &e, TRUE );
2062 }
2063 delete list;
2064 return;
2065 }
2066
2067 // find the modal's group leader
2068 QWidget *mgl = modal->parentWidget();
2069 while ( mgl && !mgl->testWFlags( Qt::WGroupLeader ) )
2070 mgl = mgl->parentWidget();
2071 if ( mgl ) {
2072 mgl = mgl->topLevelWidget();
2073 if ( mgl->isDesktop() )
2074 mgl = 0;
2075 }
2076
2077 QWidgetList *list = QApplication::topLevelWidgets();
2078 for( QWidget *w = list->first(); w; w = list->next() ) {
2079 if (
2080 !w->isDesktop() && !w->isPopup() &&
2081 !w->testWFlags( Qt::WGroupLeader ) &&
2082 (!w->parentWidget() || w->parentWidget()->isDesktop())
2083 ) {
2084 qt_sendBlocked( w, modal, &e, FALSE );
2085 }
2086 }
2087 delete list;
2088
2089 if ( mgl ) {
2090 // send blocked to modal's group leader
2091 qt_sendBlocked( mgl, modal, &e, TRUE );
2092 }
2093 // qt_tryModalHelper() also assumes that the toppest modal widget blocks
2094 // other modals, regardless of WGroupLeader flags in parents. do the same.
2095 // Note: the given modal is not yet at the stack here.
2096 if ( qt_modal_stack ) {
2097 QWidget *m = qt_modal_stack->first();
2098 while ( m ) {
2099 qt_sendBlocked( m, modal, &e, TRUE );
2100 m = qt_modal_stack->next();
2101 }
2102 }
2103}
2104
2105static inline bool isChildOf( QWidget * child, QWidget * parent )
2106{
2107 if ( !parent || !child )
2108 return FALSE;
2109 QWidget * w = child;
2110 while( w && w != parent )
2111 w = w->parentWidget();
2112 return w != 0;
2113}
2114
2115Q_EXPORT void qt_enter_modal( QWidget *widget )
2116{
2117 if ( !qt_modal_stack ) { // create modal stack
2118 qt_modal_stack = new QWidgetList;
2119 Q_CHECK_PTR( qt_modal_stack );
2120 }
2121
2122 if ( qt_modal_stack->containsRef( widget ) )
2123 return; // already modal
2124
2125 QWidget *m = qt_modal_stack->first();
2126 while ( m ) {
2127 if ( isChildOf( m, widget ) )
2128 return; // child is already modal (prevent endless recursion)
2129 m = qt_modal_stack->next();
2130 }
2131
2132//@@TODO (dmik): Qt/Win32 sends WindowBlocked/WindowUnblocked events only
2133// to the direct parent of the modal widget. Why? We use qt_dispatchBlocked()
2134// to send them to all windows that do not get input when the given widget
2135// is modal; also we disable/enable that windows there, which is essential
2136// for modality support in Qt/OS2 (code in QtOldFrameProc() and qt_try_modal()
2137// functionality depend on it).
2138//
2139// if (widget->parentWidget()) {
2140// QEvent e(QEvent::WindowBlocked);
2141// QApplication::sendEvent(widget->parentWidget(), &e);
2142// WinEnableWindow( widget->parentWidget()->winFId(), FALSE );
2143// }
2144 qt_dispatchBlocked( widget, TRUE );
2145
2146 releaseAutoCapture();
2147 qt_dispatchEnterLeave( 0, QWidget::find((WId)curWin));
2148 qt_modal_stack->insert( 0, widget );
2149 app_do_modal = TRUE;
2150 curWin = 0;
2151 qt_button_down = 0;
2152 ignoreNextMouseReleaseEvent = FALSE;
2153}
2154
2155
2156Q_EXPORT void qt_leave_modal( QWidget *widget )
2157{
2158 if ( qt_modal_stack && qt_modal_stack->removeRef(widget) ) {
2159 if ( qt_modal_stack->isEmpty() ) {
2160 delete qt_modal_stack;
2161 qt_modal_stack = 0;
2162 QPoint p( QCursor::pos() );
2163 app_do_modal = FALSE; // necessary, we may get recursively into qt_try_modal below
2164 QWidget* w = QApplication::widgetAt( p.x(), p.y(), TRUE );
2165 qt_dispatchEnterLeave( w, QWidget::find( curWin ) ); // send synthetic enter event
2166 curWin = w? w->winId() : 0;
2167 }
2168 ignoreNextMouseReleaseEvent = TRUE;
2169
2170 qt_dispatchBlocked( widget, FALSE );
2171 }
2172 app_do_modal = qt_modal_stack != 0;
2173
2174//@@TODO (dmik): see the comments inside qt_enter_modal()
2175//
2176// if (widget->parentWidget()) {
2177// WinEnableWindow( widget->parentWidget()->winFId(), TRUE );
2178// QEvent e(QEvent::WindowUnblocked);
2179// QApplication::sendEvent(widget->parentWidget(), &e);
2180// }
2181}
2182
2183static bool qt_blocked_modal( QWidget *widget )
2184{
2185 if ( !app_do_modal )
2186 return FALSE;
2187 if ( qApp->activePopupWidget() )
2188 return FALSE;
2189 if ( widget->testWFlags(Qt::WStyle_Tool) ) // allow tool windows
2190 return FALSE;
2191
2192 QWidget *modal=0, *top=qt_modal_stack->getFirst();
2193
2194 widget = widget->topLevelWidget();
2195 if ( widget->testWFlags(Qt::WShowModal) ) // widget is modal
2196 modal = widget;
2197 if ( !top || modal == top ) // don't block event
2198 return FALSE;
2199 return TRUE;
2200}
2201
2202static bool qt_try_modal( QWidget *widget, QMSG *qmsg, int &ret )
2203{
2204 QWidget * top = 0;
2205
2206 if ( qt_tryModalHelper( widget, &top ) )
2207 return TRUE;
2208
2209 bool block_event = FALSE;
2210 ULONG type = qmsg->msg;
2211
2212 if ( type == WM_CLOSE ) {
2213 block_event = TRUE;
2214 }
2215
2216 return !block_event;
2217}
2218
2219
2220/*****************************************************************************
2221 Popup widget mechanism
2222
2223 openPopup()
2224 Adds a widget to the list of popup widgets
2225 Arguments:
2226 QWidget *widget The popup widget to be added
2227
2228 closePopup()
2229 Removes a widget from the list of popup widgets
2230 Arguments:
2231 QWidget *widget The popup widget to be removed
2232 *****************************************************************************/
2233
2234void QApplication::openPopup( QWidget *popup )
2235{
2236 if ( !popupWidgets ) { // create list
2237 popupWidgets = new QWidgetList;
2238 Q_CHECK_PTR( popupWidgets );
2239 }
2240 popupWidgets->append( popup ); // add to end of list
2241 if ( !popup->isEnabled() )
2242 return;
2243
2244 if ( popupWidgets->count() == 1 && !qt_nograb() )
2245 setAutoCapture( popup->winId() ); // grab mouse/keyboard
2246 // Popups are not focus-handled by the window system (the first
2247 // popup grabbed the keyboard), so we have to do that manually: A
2248 // new popup gets the focus
2249 QFocusEvent::setReason( QFocusEvent::Popup );
2250 if ( popup->focusWidget())
2251 popup->focusWidget()->setFocus();
2252 else
2253 popup->setFocus();
2254 QFocusEvent::resetReason();
2255}
2256
2257void QApplication::closePopup( QWidget *popup )
2258{
2259 if ( !popupWidgets )
2260 return;
2261 popupWidgets->removeRef( popup );
2262 POINTL curPos;
2263 WinQueryPointerPos( HWND_DESKTOP, &curPos );
2264 // flip y coordinate
2265 curPos.y = desktop()->height() - (curPos.y + 1);
2266 replayPopupMouseEvent = !popup->geometry().contains( QPoint(curPos.x, curPos.y) );
2267 if ( popupWidgets->count() == 0 ) { // this was the last popup
2268 popupCloseDownMode = TRUE; // control mouse events
2269 delete popupWidgets;
2270 popupWidgets = 0;
2271 if ( !popup->isEnabled() )
2272 return;
2273 if ( !qt_nograb() ) // grabbing not disabled
2274 releaseAutoCapture();
2275 if ( active_window ) {
2276 QFocusEvent::setReason( QFocusEvent::Popup );
2277 if ( active_window->focusWidget() )
2278 active_window->focusWidget()->setFocus();
2279 else
2280 active_window->setFocus();
2281 QFocusEvent::resetReason();
2282 }
2283 } else {
2284 // Popups are not focus-handled by the window system (the
2285 // first popup grabbed the keyboard), so we have to do that
2286 // manually: A popup was closed, so the previous popup gets
2287 // the focus.
2288 QFocusEvent::setReason( QFocusEvent::Popup );
2289 QWidget* aw = popupWidgets->getLast();
2290 if ( popupWidgets->count() == 1 )
2291 setAutoCapture( aw->winId() );
2292 if (aw->focusWidget())
2293 aw->focusWidget()->setFocus();
2294 else
2295 aw->setFocus();
2296 QFocusEvent::resetReason();
2297 }
2298}
2299
2300
2301
2302/*****************************************************************************
2303 Event translation; translates OS/2 PM events to Qt events
2304 *****************************************************************************/
2305
2306// State holder for LWIN/RWIN and ALTGr keys
2307// (ALTGr is also necessary since OS/2 doesn't report ALTGr as KC_ALT)
2308static int qt_extraKeyState = 0;
2309
2310static int mouseButtonState()
2311{
2312 int state = 0;
2313
2314 if ( WinGetKeyState( HWND_DESKTOP, VK_BUTTON1 ) & 0x8000 )
2315 state |= Qt::LeftButton;
2316 if ( WinGetKeyState( HWND_DESKTOP, VK_BUTTON2 ) & 0x8000 )
2317 state |= Qt::RightButton;
2318 if ( WinGetKeyState( HWND_DESKTOP, VK_BUTTON3 ) & 0x8000 )
2319 state |= Qt::MidButton;
2320
2321 return state;
2322}
2323
2324//
2325// Auto-capturing for mouse press and mouse release
2326//
2327
2328static void setAutoCapture( HWND h )
2329{
2330 if ( autoCaptureWnd )
2331 releaseAutoCapture();
2332 autoCaptureWnd = h;
2333
2334 if ( !mouseButtonState() ) {
2335 // all buttons released, we don't actually capture the mouse
2336 // (see QWidget::translateMouseEvent())
2337 autoCaptureReleased = TRUE;
2338 } else {
2339 autoCaptureReleased = FALSE;
2340 WinSetCapture( HWND_DESKTOP, h );
2341 }
2342}
2343
2344static void releaseAutoCapture()
2345{
2346 if ( autoCaptureWnd ) {
2347 if ( !autoCaptureReleased ) {
2348 WinSetCapture( HWND_DESKTOP, 0 );
2349 autoCaptureReleased = TRUE;
2350 }
2351 autoCaptureWnd = 0;
2352 }
2353}
2354
2355
2356//
2357// Mouse event translation
2358//
2359
2360static ushort mouseTbl[] = {
2361 WM_MOUSEMOVE, QEvent::MouseMove, 0,
2362 WM_BUTTON1DOWN, QEvent::MouseButtonPress, Qt::LeftButton,
2363 WM_BUTTON1UP, QEvent::MouseButtonRelease, Qt::LeftButton,
2364 WM_BUTTON1DBLCLK, QEvent::MouseButtonDblClick, Qt::LeftButton,
2365 WM_BUTTON2DOWN, QEvent::MouseButtonPress, Qt::RightButton,
2366 WM_BUTTON2UP, QEvent::MouseButtonRelease, Qt::RightButton,
2367 WM_BUTTON2DBLCLK, QEvent::MouseButtonDblClick, Qt::RightButton,
2368 WM_BUTTON3DOWN, QEvent::MouseButtonPress, Qt::MidButton,
2369 WM_BUTTON3UP, QEvent::MouseButtonRelease, Qt::MidButton,
2370 WM_BUTTON3DBLCLK, QEvent::MouseButtonDblClick, Qt::MidButton,
2371//@@TODO (dmik): later (extra buttons)
2372// WM_XBUTTONDOWN, QEvent::MouseButtonPress, Qt::MidButton*2, //### Qt::XButton1/2
2373// WM_XBUTTONUP, QEvent::MouseButtonRelease, Qt::MidButton*2,
2374// WM_XBUTTONDBLCLK, QEvent::MouseButtonDblClick, Qt::MidButton*2,
2375 WM_CONTEXTMENU, QEvent::ContextMenu, 0,
2376 0, 0, 0
2377};
2378
2379static int translateButtonState( USHORT s, int type, int button )
2380{
2381 int bst = mouseButtonState();
2382
2383 if ( type == QEvent::ContextMenu ) {
2384 if ( WinGetKeyState( HWND_DESKTOP, VK_SHIFT ) & 0x8000 )
2385 bst |= Qt::ShiftButton;
2386 if ( WinGetKeyState( HWND_DESKTOP, VK_ALT ) & 0x8000 )
2387 bst |= Qt::AltButton;
2388 if ( WinGetKeyState( HWND_DESKTOP, VK_CTRL ) & 0x8000 )
2389 bst |= Qt::ControlButton;
2390 } else {
2391 if ( s & KC_SHIFT )
2392 bst |= Qt::ShiftButton;
2393 if ( (s & KC_ALT) )
2394 bst |= Qt::AltButton;
2395 if ( s & KC_CTRL )
2396 bst |= Qt::ControlButton;
2397 }
2398 if ( (qt_extraKeyState & Qt::AltButton) )
2399 bst |= Qt::AltButton;
2400 if ( qt_extraKeyState & Qt::MetaButton )
2401 bst |= Qt::MetaButton;
2402
2403 // Translate from OS/2-style "state after event"
2404 // to X-style "state before event"
2405 if ( type == QEvent::MouseButtonPress ||
2406 type == QEvent::MouseButtonDblClick )
2407 bst &= ~button;
2408 else if ( type == QEvent::MouseButtonRelease )
2409 bst |= button;
2410
2411 return bst;
2412}
2413
2414/*! \internal
2415 In DnD, the mouse release event never appears, so the
2416 mouse button state machine must be manually reset
2417*/
2418void qt_pmMouseButtonUp()
2419{
2420 // release any stored mouse capture
2421 qt_button_down = 0;
2422 autoCaptureReleased = TRUE;
2423 releaseAutoCapture();
2424}
2425
2426bool QETWidget::translateMouseEvent( const QMSG &qmsg )
2427{
2428#if 0
2429 static const char *msgNames[] = { // 11 items
2430 "WM_MOUSEMOVE",
2431 "WM_BUTTON1DOWN", "WM_BUTTON1UP", "WM_BUTTON1DBLCLK",
2432 "WM_BUTTON2DOWN", "WM_BUTTON2UP", "WM_BUTTON2DBLCLK",
2433 "WM_BUTTON3DOWN", "WM_BUTTON3UP", "WM_BUTTON3DBLCLK",
2434 "WM_???"
2435 };
2436 int msgIdx = qmsg.msg - WM_MOUSEMOVE;
2437 if (msgIdx < 0 || msgIdx > 9)
2438 msgIdx = 10;
2439 qDebug( "%s (%04lX): [%08lX/%p:%s/%s] %04hd,%04hd hit=%04hX fl=%04hX",
2440 msgNames[msgIdx], qmsg.msg, qmsg.hwnd, this, name(), className(),
2441 SHORT1FROMMP(qmsg.mp1), SHORT2FROMMP(qmsg.mp1),
2442 SHORT1FROMMP(qmsg.mp2), SHORT2FROMMP(qmsg.mp2) );
2443#endif
2444
2445 static QPoint pos; // window pos (y flipped)
2446 static POINTL gpos = { -1, -1 }; // global pos (y flipped)
2447 QEvent::Type type; // event parameters
2448 int button;
2449 int state;
2450 int i;
2451
2452 // candidate for a double click event
2453 static HWND dblClickCandidateWin = 0;
2454
2455 if ( sm_blockUserInput ) //block user interaction during session management
2456 return TRUE;
2457
2458 // Compress mouse move events
2459 if ( qmsg.msg == WM_MOUSEMOVE ) {
2460 QMSG mouseMsg;
2461 mouseMsg.msg = WM_NULL;
2462 while (
2463 WinPeekMsg( 0, &mouseMsg, qmsg.hwnd, WM_MOUSEMOVE,
2464 WM_MOUSEMOVE, PM_NOREMOVE )
2465 ) {
2466 if ( mouseMsg.mp2 != qmsg.mp2 )
2467 break; // leave the message in the queue because
2468 // the key state has changed
2469 // Remove the mouse move message
2470 WinPeekMsg( 0, &mouseMsg, qmsg.hwnd, WM_MOUSEMOVE,
2471 WM_MOUSEMOVE, PM_REMOVE );
2472 }
2473 // Update the passed in QMSG structure with the
2474 // most recent one.
2475 if ( mouseMsg.msg != WM_NULL ) {
2476 PQMSG pqmsg = (PQMSG)&qmsg;
2477 pqmsg->mp1 = mouseMsg.mp1;
2478 pqmsg->mp2 = mouseMsg.mp2;
2479 pqmsg->time = mouseMsg.time;
2480 pqmsg->ptl.x = (short)SHORT1FROMMP(mouseMsg.mp1);
2481 pqmsg->ptl.y = (short)SHORT2FROMMP(mouseMsg.mp1);
2482 WinMapWindowPoints( pqmsg->hwnd, HWND_DESKTOP, &pqmsg->ptl, 1 );
2483 // flip y coordinate
2484 pqmsg->ptl.y = QApplication::desktop()->height() - (pqmsg->ptl.y + 1);
2485 }
2486 }
2487
2488 for ( i = 0; mouseTbl[i] && (ULONG)mouseTbl[i] != qmsg.msg; i += 3 )
2489 ;
2490 if ( !mouseTbl[i] )
2491 return FALSE;
2492
2493 type = (QEvent::Type)mouseTbl[++i]; // event type
2494 button = mouseTbl[++i]; // which button
2495 state = translateButtonState( SHORT2FROMMP(qmsg.mp2), type, button ); // button state
2496
2497 // It seems, that PM remembers only the WM_BUTTONxDOWN message (instead of
2498 // the WM_BUTTONxDOWN + WM_BUTTONxUP pair) to detect whether the next button
2499 // press should be converted to WM_BUTTONxDBLCLK or not. As a result, the
2500 // window gets WM_BUTTONxDBLCLK even if it didn't receive the preceeding
2501 // WM_BUTTONxUP (this happens if we issue WinSetCapture() on the first
2502 // WM_BUTTONxDOWN), which is obviously wrong and makes problems for QWorkspace
2503 // and QTitleBar system menu handlers that don't expect a double click after
2504 // they opened a popup menu. dblClickCandidateWin is reset to 0 (see a ***
2505 // remmark below) when WinSetCapture is issued that directs messages
2506 // to a window other than one received the first WM_BUTTONxDOWN,
2507 // so we can fix it here. Note that if there is more than one popup window,
2508 // WinSetCapture is issued only for the first of them, so this code doesn't
2509 // prevent MouseButtonDblClick from being delivered to a popup when another
2510 // popup gets closed on the first WM_BUTTONxDOWN (Qt/Win32 behaves in the
2511 // same way, so it's left for compatibility).
2512 if ( type == QEvent::MouseButtonPress ) {
2513 dblClickCandidateWin = qmsg.hwnd;
2514 } else if ( type == QEvent::MouseButtonDblClick ) {
2515 if ( dblClickCandidateWin != qmsg.hwnd )
2516 type = QEvent::MouseButtonPress;
2517 dblClickCandidateWin = 0;
2518 }
2519
2520 if ( type == QEvent::ContextMenu ) {
2521 QPoint g = QPoint( qmsg.ptl.x, qmsg.ptl.y );
2522 QContextMenuEvent e( QContextMenuEvent::Mouse, mapFromGlobal( g ), g, state );
2523 QApplication::sendSpontaneousEvent( this, &e );
2524 return TRUE;
2525 }
2526
2527 if ( type == QEvent::MouseMove ) {
2528 if ( !(state & MouseButtonMask) )
2529 qt_button_down = 0;
2530#ifndef QT_NO_CURSOR
2531 QCursor *c = qt_grab_cursor();
2532 if ( !c )
2533 c = QApplication::overrideCursor();
2534 if ( c ) // application cursor defined
2535 WinSetPointer( HWND_DESKTOP, c->handle() );
2536 else if ( isEnabled() ) // use widget cursor if widget is enabled
2537 WinSetPointer( HWND_DESKTOP, cursor().handle() );
2538 else {
2539 QWidget *parent = parentWidget();
2540 while ( parent && !parent->isEnabled() )
2541 parent = parent->parentWidget();
2542 if ( parent )
2543 WinSetPointer( HWND_DESKTOP, parent->cursor().handle() );
2544 }
2545#else
2546 // pass the msg to the default proc to let it change the pointer shape
2547 WinDefWindowProc( qmsg.hwnd, qmsg.msg, qmsg.mp1, qmsg.mp2 );
2548#endif
2549 if ( curWin != winId() ) { // new current window
2550/// @todo (dmik)
2551// add CS_HITTEST to our window classes and handle WM_HITTEST,
2552// otherwise disabled windows will not get mouse events?
2553 qt_dispatchEnterLeave( this, QWidget::find(curWin) );
2554 curWin = winId();
2555 }
2556
2557 // *** PM posts a dummy WM_MOUSEMOVE message (with the same, uncahnged
2558 // pointer coordinates) after every WinSetCapture that actually changes
2559 // the capture target. I.e., if the argument of WinSetCapture is
2560 // NULLHANDLE, a window under the mouse pointer gets this message,
2561 // otherwise the specified window gets it unless it is already under the
2562 // pointer. We use this info to check whether the window can be a double
2563 // click candidate (see above).
2564 if ( qmsg.ptl.x == gpos.x && qmsg.ptl.y == gpos.y ) {
2565 if ( dblClickCandidateWin != qmsg.hwnd )
2566 dblClickCandidateWin = 0;
2567 return TRUE;
2568 }
2569
2570 gpos = qmsg.ptl;
2571
2572 if ( state == 0 && autoCaptureWnd == 0 && !hasMouseTracking() &&
2573 !QApplication::hasGlobalMouseTracking() )
2574 return TRUE; // no button
2575
2576 pos = mapFromGlobal( QPoint(gpos.x, gpos.y) );
2577 } else {
2578 if ( type == QEvent::MouseButtonPress && !isActiveWindow() )
2579 setActiveWindow();
2580
2581 gpos = qmsg.ptl;
2582 pos = mapFromGlobal( QPoint(gpos.x, gpos.y) );
2583
2584 if ( type == QEvent::MouseButtonPress || type == QEvent::MouseButtonDblClick ) { // mouse button pressed
2585 // Magic for masked widgets
2586 qt_button_down = findChildWidget( this, pos );
2587 if ( !qt_button_down || !qt_button_down->testWFlags(WMouseNoMask) )
2588 qt_button_down = this;
2589 }
2590 }
2591
2592 // detect special button states
2593 enum { Other, SinglePressed, AllReleased } btnState = Other;
2594 int bs = state & MouseButtonMask;
2595 if ( (type == QEvent::MouseButtonPress ||
2596 type == QEvent::MouseButtonDblClick) && bs == 0
2597 ) {
2598 btnState = SinglePressed;
2599 } else if ( type == QEvent::MouseButtonRelease && bs == button ) {
2600 btnState = AllReleased;
2601 }
2602
2603 if ( qApp->inPopupMode() ) { // in popup mode
2604 if ( !autoCaptureReleased && btnState == AllReleased ) {
2605 // in order to give non-Qt windows the opportunity to see mouse
2606 // messages while our popups are active we need to release the
2607 // mouse capture which is absolute in OS/2. we do it directly
2608 // (not through releaseAutoCapture()) in order to keep
2609 // autoCaptureWnd nonzero to keep forwarding mouse move events
2610 // (actually sent to one of Qt widgets) to the active popup.
2611 autoCaptureReleased = TRUE;
2612 WinSetCapture( HWND_DESKTOP, 0 );
2613 } else if ( autoCaptureReleased && btnState == SinglePressed ) {
2614 // set the mouse capture back if a button is pressed.
2615 if ( autoCaptureWnd ) {
2616 autoCaptureReleased = FALSE;
2617 WinSetCapture( HWND_DESKTOP, autoCaptureWnd );
2618 }
2619 }
2620
2621 replayPopupMouseEvent = FALSE;
2622 QWidget* activePopupWidget = qApp->activePopupWidget();
2623 QWidget *popup = activePopupWidget;
2624
2625 if ( popup != this ) {
2626 if ( testWFlags(WType_Popup) && rect().contains(pos) )
2627 popup = this;
2628 else // send to last popup
2629 pos = popup->mapFromGlobal( QPoint(gpos.x, gpos.y) );
2630 }
2631 QWidget *popupChild = findChildWidget( popup, pos );
2632 bool releaseAfter = FALSE;
2633 switch ( type ) {
2634 case QEvent::MouseButtonPress:
2635 case QEvent::MouseButtonDblClick:
2636 popupButtonFocus = popupChild;
2637 break;
2638 case QEvent::MouseButtonRelease:
2639 releaseAfter = TRUE;
2640 break;
2641 default:
2642 break; // nothing for mouse move
2643 }
2644
2645 if ( popupButtonFocus ) {
2646 QMouseEvent e( type,
2647 popupButtonFocus->mapFromGlobal(QPoint(gpos.x,gpos.y)),
2648 QPoint(gpos.x,gpos.y), button, state );
2649 QApplication::sendSpontaneousEvent( popupButtonFocus, &e );
2650 if ( releaseAfter ) {
2651 popupButtonFocus = 0;
2652 }
2653 } else if ( popupChild ) {
2654 QMouseEvent e( type,
2655 popupChild->mapFromGlobal(QPoint(gpos.x,gpos.y)),
2656 QPoint(gpos.x,gpos.y), button, state );
2657 QApplication::sendSpontaneousEvent( popupChild, &e );
2658 } else {
2659 QMouseEvent e( type, pos, QPoint(gpos.x,gpos.y), button, state );
2660 QApplication::sendSpontaneousEvent( popup, &e );
2661 }
2662
2663 if ( releaseAfter )
2664 qt_button_down = 0;
2665
2666 if ( type == QEvent::MouseButtonPress
2667 && qApp->activePopupWidget() != activePopupWidget
2668 && replayPopupMouseEvent ) {
2669 // the popup dissappeared. Replay the event
2670 QWidget* w = QApplication::widgetAt( gpos.x, gpos.y, TRUE );
2671 if ( w && !qt_blocked_modal( w ) ) {
2672 QPoint wpos = w->mapFromGlobal(QPoint(gpos.x, gpos.y));
2673 // flip y coordinate
2674 wpos.ry() = w->height() - (wpos.y() + 1);
2675 // Note: it's important to post this message rather than send:
2676 // QPushButton::popupPressed() depends on this so that
2677 // popup->exec() must return before this message is delivered
2678 // as QWidget::keyPressEvent().
2679 WinPostMsg( w->winId(), qmsg.msg,
2680 MPFROM2SHORT( wpos.x(), wpos.y() ), qmsg.mp2 );
2681 }
2682 }
2683 } else { // not popup mode
2684 if ( btnState == SinglePressed && QWidget::mouseGrabber() == 0 )
2685 setAutoCapture( winId() );
2686 else if ( btnState == AllReleased && QWidget::mouseGrabber() == 0 )
2687 releaseAutoCapture();
2688
2689 QWidget *widget = this;
2690 QWidget *w = QWidget::mouseGrabber();
2691 if ( !w )
2692 w = qt_button_down;
2693 if ( w && w != this ) {
2694 widget = w;
2695 pos = w->mapFromGlobal(QPoint(gpos.x, gpos.y));
2696 }
2697
2698 if ( type == QEvent::MouseButtonRelease &&
2699 (state & (~button) & ( MouseButtonMask )) == 0 ) {
2700 qt_button_down = 0;
2701 }
2702
2703 QMouseEvent e( type, pos, QPoint(gpos.x,gpos.y), button, state );
2704 QApplication::sendSpontaneousEvent( widget, &e );
2705
2706 if ( type != QEvent::MouseMove )
2707 pos.rx() = pos.ry() = -9999; // init for move compression
2708 }
2709 return TRUE;
2710}
2711
2712
2713//
2714// Keyboard event translation
2715//
2716
2717static const ushort KeyTbl[] = { // keyboard mapping table
2718 VK_ESC, Qt::Key_Escape, // misc keys
2719 VK_TAB, Qt::Key_Tab,
2720 VK_BACKTAB, Qt::Key_Backtab,
2721 VK_BACKSPACE, Qt::Key_Backspace,
2722 VK_ENTER, Qt::Key_Enter,
2723 VK_NEWLINE, Qt::Key_Return,
2724 VK_INSERT, Qt::Key_Insert,
2725 VK_DELETE, Qt::Key_Delete,
2726 VK_CLEAR, Qt::Key_Clear,
2727 VK_PAUSE, Qt::Key_Pause,
2728 VK_PRINTSCRN, Qt::Key_Print,
2729 VK_SPACE, Qt::Key_Space,
2730 VK_HOME, Qt::Key_Home, // cursor movement
2731 VK_END, Qt::Key_End,
2732 VK_LEFT, Qt::Key_Left,
2733 VK_UP, Qt::Key_Up,
2734 VK_RIGHT, Qt::Key_Right,
2735 VK_DOWN, Qt::Key_Down,
2736 VK_PAGEUP, Qt::Key_Prior,
2737 VK_PAGEDOWN, Qt::Key_Next,
2738 VK_SHIFT, Qt::Key_Shift, // modifiers
2739 VK_CTRL, Qt::Key_Control,
2740 VK_ALT, Qt::Key_Alt,
2741 VK_CAPSLOCK, Qt::Key_CapsLock,
2742 VK_NUMLOCK, Qt::Key_NumLock,
2743 VK_SCRLLOCK, Qt::Key_ScrollLock,
2744 0, 0
2745};
2746
2747// when the compatibility mode is FALSE Qt/OS2 uses the following rule
2748// to calculate QKeyEvent::key() codes when an alpha-numeric key is
2749// pressed: key code is the ASCII (Latin 1) character code of that key as if
2750// there were no any keyboard modifiers (CTRL, SHIFT, ALT) pressed, with the
2751// exception that alpha characters are uppercased.
2752// when the compatibility mode is TRUE Qt/OS2 behaves mostly like Qt/Win32.
2753Q_EXPORT bool qt_kbd_compatibility = TRUE;
2754
2755/// @todo (dmik) currentlly, qt_kbd_compatibility is TRUE because
2756// qt_scan2Ascii function below is not well implemented yet (in particular,
2757// it uses the 850 code page that may not be available on some systems...).
2758// Once we find a way to translate scans to US ASCII regardless of the current
2759// code page and/or NLS state (keyboard layout), qt_kbd_compatibility may be
2760// set to FALSE. This, in particular, will enable more correct handling of
2761// Alt+letter combinations when the keyboard is in the NLS state:
2762// QKeyEvent::key() will return a non-null Qt::Key_XXX code corresponding to
2763// the ASCII code of a pressed key, which in turn will let Qt process latin
2764// Alt+letter shortcuts in the NLS keyboard mode together with Alt+NLS_letter
2765// shortcuts (nice feature imho). Note that Alt+NLS_letter shortcuts are
2766// correctly processed in any case.
2767
2768// cache table to store Qt::Key_... values for 256 hardware scancodes
2769// (even byte is for non-shifted keys, odd byte is for shifted ones).
2770// used to decrease the number of calls to KbdXlate.
2771static uchar ScanTbl[512] = { 0xFF };
2772
2773static uchar qt_scan2Ascii( uchar scan, bool shift )
2774{
2775 uchar ascii = 0;
2776 HFILE kbd;
2777 ULONG dummy;
2778 DosOpen( "KBD$", &kbd, &dummy, 0,
2779 FILE_NORMAL, OPEN_ACTION_OPEN_IF_EXISTS,
2780 OPEN_SHARE_DENYNONE | OPEN_ACCESS_READWRITE, NULL );
2781 // parameter packet
2782 struct CPID {
2783 USHORT idCodePage;
2784 USHORT reserved;
2785 } cpid = { 850, 0 };
2786 ULONG szCpid = sizeof(CPID);
2787 // data packet
2788 KBDTRANS kt;
2789 ULONG szKt = sizeof(KBDTRANS);
2790 // reset all kbd states and modifiers
2791 memset( &kt, 0, szKt );
2792 // reflect Shift state to distinguish between [ and { etc.
2793 if ( qt_kbd_compatibility && shift )
2794 kt.fsState = KBDSTF_RIGHTSHIFT;
2795 kt.chScan = scan;
2796 DosDevIOCtl( kbd, IOCTL_KEYBOARD, KBD_XLATESCAN,
2797 &cpid, szCpid, &szCpid, &kt, szKt, &szKt );
2798 // store in cache
2799 uint idx = scan << 1;
2800 if ( qt_kbd_compatibility && shift )
2801 idx++;
2802 ascii = ScanTbl[idx] = toupper( kt.chChar );
2803 DosClose( kbd );
2804 return ascii;
2805}
2806
2807// translates WM_CHAR to Qt::Key_..., ascii, state and text
2808static void translateKeyCode( CHRMSG &chm, int &code, int &ascii, int &state,
2809 QString &text )
2810{
2811 if ( chm.fs & KC_SHIFT )
2812 state |= Qt::ShiftButton;
2813 if ( chm.fs & KC_CTRL )
2814 state |= Qt::ControlButton;
2815 if ( chm.fs & KC_ALT )
2816 state |= Qt::AltButton;
2817 if ( qt_extraKeyState & Qt::MetaButton )
2818 state |= Qt::MetaButton;
2819
2820 unsigned char ch = chm.chr;
2821
2822 if ( chm.fs & KC_VIRTUALKEY ) {
2823 if ( !chm.vkey ) {
2824 // The only known situation when KC_VIRTUALKEY is present but
2825 // vkey is zero is when Alt+Shift is pressed to switch the
2826 // keyboard layout state from latin to national and back.
2827 // It seems that this way the system informs applications about
2828 // layout changes: chm.chr is 0xF1 when the user switches
2829 // to the national layout (i.e. presses Alt + Left Shift)
2830 // and it is 0xF0 when he switches back (presses Alt + Right Shift).
2831 // We assume this and restore fs, vkey, scancode and chr accordingly.
2832 if ( chm.chr == 0xF0 || chm.chr == 0xF1 ) {
2833 chm.fs |= KC_ALT | KC_SHIFT;
2834 chm.vkey = VK_SHIFT;
2835 chm.scancode = chm.chr == 0xF1 ? 0x2A : 0x36;
2836 chm.chr = ch = 0;
2837 state |= Qt::AltButton | Qt::ShiftButton;
2838 // code will be assigned by the normal procedure below
2839 }
2840 }
2841 if ( chm.vkey >= VK_F1 && chm.vkey <= VK_F24 ) {
2842 // function keys
2843 code = Qt::Key_F1 + (chm.vkey - VK_F1);
2844 } else if ( chm.vkey == VK_ALTGRAF ) {
2845 code = Qt::Key_Alt;
2846 if ( !(chm.fs & KC_KEYUP) )
2847 qt_extraKeyState |= Qt::AltButton;
2848 else
2849 qt_extraKeyState &= ~Qt::AltButton;
2850 } else {
2851 // any other keys
2852 int i = 0;
2853 while ( KeyTbl[i] ) {
2854 if ( chm.vkey == (int)KeyTbl[i] ) {
2855 code = KeyTbl[i+1];
2856 break;
2857 }
2858 i += 2;
2859 }
2860 }
2861 } else {
2862 if ( qt_kbd_compatibility && ch && ch < 0x80 ) {
2863 code = toupper( ch );
2864 } else if ( !qt_kbd_compatibility && (chm.fs & KC_CHAR) && isalpha( ch ) ) {
2865 code = toupper( ch );
2866 } else {
2867 // detect some special keys that have a pseudo char code
2868 // in the high byte of chm.chr (probably this is less
2869 // device-dependent than scancode)
2870 switch ( chm.chr ) {
2871 case 0xEC00: // LWIN
2872 case 0xED00: // RWIN
2873 code = Qt::Key_Meta;
2874 if ( !(chm.fs & KC_KEYUP) )
2875 qt_extraKeyState |= Qt::MetaButton;
2876 else
2877 qt_extraKeyState &= ~Qt::MetaButton;
2878 break;
2879 case 0xEE00: // WINAPP (menu with arrow)
2880 code = Qt::Key_Menu;
2881 break;
2882 case 0x5600: // additional '\' (0x56 is actually its scancode)
2883 ch = state & Qt::ShiftButton ? '|' : '\\';
2884 if ( qt_kbd_compatibility ) code = ch;
2885 else code = '\\';
2886 break;
2887 default:
2888 // break if qt_kbd_compatibility = TRUE to avoid using
2889 // qt_scan2Ascii(), see comments to qt_kbd_compatibility
2890 if ( qt_kbd_compatibility ) break;
2891 // deduce Qt::Key... from scancode
2892 if ( ScanTbl[0] == 0xFF )
2893 memset( &ScanTbl, 0, sizeof(ScanTbl) );
2894 uint idx = chm.scancode << 1;
2895 if ( qt_kbd_compatibility && (state & Qt::ShiftButton) )
2896 idx++;
2897 code = ScanTbl[idx];
2898 if ( !code )
2899 // not found in cache
2900 code = qt_scan2Ascii( chm.scancode, (state & Qt::ShiftButton) );
2901 break;
2902 }
2903 }
2904 }
2905 // check extraState AFTER updating it
2906 if ( qt_extraKeyState & Qt::AltButton )
2907 state |= Qt::AltButton;
2908
2909 // detect numeric keypad keys
2910 if ( chm.vkey == VK_ENTER || chm.vkey == VK_NUMLOCK ) {
2911 // these always come from the numpad
2912 state |= Qt::Keypad;
2913 } else if (
2914 ((chm.vkey >= VK_PAGEUP && chm.vkey <= VK_DOWN) ||
2915 chm.vkey == VK_INSERT || chm.vkey == VK_DELETE)
2916 ) {
2917 if ( ch != 0xE0 ) {
2918 state |= Qt::Keypad;
2919 if ( (state & Qt::AltButton) && chm.vkey != VK_DELETE ) {
2920 // reset both code and ch to "hide" them from Qt widgets and let
2921 // the standard OS/2 Alt+ddd shortcut (that composed chars from
2922 // typed in ASCII codes) work correctly. If we don't do that,
2923 // widgets will see both individual digits (or cursor movements
2924 // if NumLock is off) and the char composed by Alt+ddd.
2925 code = ch = 0;
2926 } else {
2927 if ( ch ) {
2928 // override code to make it Qt::Key_0..Qt::Key_9 etc.
2929 code = toupper( ch );
2930 }
2931 }
2932 } else {
2933 ch = 0;
2934 }
2935 }
2936 // detect other numpad keys. OS/2 doesn't assign virtual keys to them
2937 // so use scancodes (it can be device-dependent, is there a better way?)
2938 switch ( chm.scancode ) {
2939 case 0x4C: // 5
2940 state |= Qt::Keypad;
2941 if ( state & Qt::AltButton ) {
2942 // reset both code and ch to "hide" them from Qt (see above)
2943 code = ch = 0;
2944 } else {
2945 // scancode is zero if Numlock is set
2946 if ( !code ) code = Qt::Key_Clear;
2947 }
2948 break;
2949 case 0x37: // *
2950 // OS/2 assigns VK_PRINTSCRN to it when pressed with Shift, also
2951 // it sets chr to zero when it is released with Alt or Ctrl
2952 // leaving vkey as zero too, and does few other strange things --
2953 // override them all
2954 code = Qt::Key_Asterisk;
2955 state |= Qt::Keypad;
2956 break;
2957 case 0x5C: // /
2958 code = Qt::Key_Slash;
2959 // fall through
2960 case 0x4A: // -
2961 case 0x4E: // +
2962 // the code for the above two is obtained by KbdXlate above
2963 state |= Qt::Keypad;
2964 break;
2965 }
2966
2967 if ( (state & Qt::ControlButton) && !(state & Qt::Keypad) ) {
2968 if ( !(state & Qt::AltButton) ) {
2969 unsigned char cch = toupper( ch ), newCh = 0;
2970 // Ctrl + A..Z etc. produce ascii from 0x01 to 0x1F
2971 if ( cch >= 0x41 && cch <= 0x5F ) newCh = cch - 0x40;
2972 // the below emulates OS/2 functionality. It differs from
2973 // Win32 one.
2974 else if ( cch == 0x36 && !(state & Qt::Keypad) ) newCh = 0x1E;
2975 else if ( cch == 0x2D ) newCh = 0x1F;
2976 else if ( cch >= 0x7B && cch <= 0x7D ) newCh = cch - 0x60;
2977 if ( newCh )
2978 ch = newCh;
2979 }
2980 }
2981
2982 ascii = ch;
2983 if ( ascii > 0x7F ) ascii = 0;
2984 if ( ch ) {
2985 // Note: Ignore the KC_CHAR flag when generating text for a key to get
2986 // correct (non-null) text even for Alt+Letter combinations (that
2987 // don't have KC_CHAR set) because processing Alt+Letter shortcuts
2988 // for non-ASCII letters in widgets (e.g. QPushButton) depends on that.
2989 if ( (chm.fs & (KC_VIRTUALKEY | KC_CHAR)) == KC_CHAR && (chm.chr & 0xFF00) ) {
2990 // We assime we get a DBCS char if the above condition is met.
2991 // DBCS chars seem to have KC_CHAR set but not KC_VIRTUALKEY; we
2992 // use this to prevent keys like ESC (chm=0x011B) with the non-zero
2993 // high byte to be mistakenly recognized as DBCS.
2994 text = QString::fromLocal8Bit( (char *) &chm.chr, 2 );
2995 }
2996 else
2997 text = QString::fromLocal8Bit( (char*) &ch, 1 );
2998 }
2999}
3000
3001struct KeyRec {
3002 KeyRec(unsigned char s, int c, int a, const QString& t) :
3003 scan(s), code(c), ascii(a), text(t) { }
3004 KeyRec() { }
3005 unsigned char scan;
3006 int code, ascii;
3007 QString text;
3008};
3009
3010static const int maxrecs=64; // User has LOTS of fingers...
3011static KeyRec key_rec[maxrecs];
3012static int nrecs=0;
3013
3014static KeyRec* find_key_rec( unsigned char scan, bool remove )
3015{
3016 KeyRec *result = 0;
3017 if( scan == 0 ) // DBCS chars or user-injected keys
3018 return result;
3019
3020 for (int i=0; i<nrecs; i++) {
3021 if (key_rec[i].scan == scan) {
3022 if (remove) {
3023 static KeyRec tmp;
3024 tmp = key_rec[i];
3025 while (i+1 < nrecs) {
3026 key_rec[i] = key_rec[i+1];
3027 i++;
3028 }
3029 nrecs--;
3030 result = &tmp;
3031 } else {
3032 result = &key_rec[i];
3033 }
3034 break;
3035 }
3036 }
3037 return result;
3038}
3039
3040static KeyRec* find_key_rec( int code, bool remove )
3041{
3042 KeyRec *result = 0;
3043 if( code == 0 ) // DBCS chars or user-injected keys
3044 return result;
3045
3046 for (int i=0; i<nrecs; i++) {
3047 if (key_rec[i].code == code) {
3048 if (remove) {
3049 static KeyRec tmp;
3050 tmp = key_rec[i];
3051 while (i+1 < nrecs) {
3052 key_rec[i] = key_rec[i+1];
3053 i++;
3054 }
3055 nrecs--;
3056 result = &tmp;
3057 } else {
3058 result = &key_rec[i];
3059 }
3060 break;
3061 }
3062 }
3063 return result;
3064}
3065
3066static void store_key_rec( unsigned char scan, int code, int ascii,
3067 const QString& text )
3068{
3069 if( scan == 0 && code == 0 ) // DBCS chars or user-injected keys
3070 return;
3071
3072 if ( nrecs == maxrecs ) {
3073#if defined(QT_CHECK_RANGE)
3074 qWarning( "Qt: Internal keyboard buffer overflow" );
3075#endif
3076 return;
3077 }
3078
3079 key_rec[nrecs++] = KeyRec( scan, code, ascii, text );
3080}
3081
3082bool QETWidget::translateKeyEvent( const QMSG &qmsg, bool grab )
3083{
3084 CHRMSG chm = *((PCHRMSG)(((char*)&qmsg) + sizeof(HWND) + sizeof(ULONG)));
3085
3086#if 0
3087 qDebug( "WM_CHAR: [%s] fs: %04X cRepeat: %03d scancode: %02X chr: %04X vkey: %04X %s",
3088 name(), chm.fs, chm.cRepeat, chm.scancode, chm.chr, chm.vkey, (grab ? "{grab}" : "") );
3089#endif
3090
3091 bool k0 = FALSE, k1 = FALSE;
3092 int code = 0, ascii = 0, state = 0;
3093 QString text;
3094
3095 if ( sm_blockUserInput ) // block user interaction during session management
3096 return TRUE;
3097
3098 translateKeyCode( chm, code, ascii, state, text );
3099
3100 // Note: code and/or chm.scancode may be zero here. We cannot ignore such
3101 // events because, for example, all non-ASCII letters have zero virtual
3102 // codes, and DBCS characters entered via IME have both zero virtual codes
3103 // and zero scancodes. However, if both code and chm.scancode are zero
3104 // (as for DBCS), store_key_rec()/find_key_rec() will do nothing which
3105 // means that:
3106 // 1) QKeyEvents will not have the auto-repeat flag set when a key is
3107 // being auto-repeated by the system;
3108 // 2) there will be no QEvent::KeyRelease event corresponding to the
3109 // QEvent::KeyPress event.
3110
3111 // Invert state logic
3112 if ( code == Key_Alt )
3113 state = state ^ AltButton;
3114 else if ( code == Key_Control )
3115 state = state ^ ControlButton;
3116 else if ( code == Key_Shift )
3117 state = state ^ ShiftButton;
3118
3119 if ( !(chm.fs & KC_KEYUP) ) {
3120 // KEYDOWN
3121 KeyRec* rec = find_key_rec( chm.scancode, FALSE );
3122
3123 if ( state == Qt::AltButton ) {
3124 // Special handling of global PM hotkeys
3125 switch ( code ) {
3126 case Qt::Key_Space:
3127 if ( qt_show_system_menu( topLevelWidget() ) ) {
3128 // remove the Key_Alt from the buffer (otherwise we will
3129 // not get the next "Alt pressed" event because the
3130 // "Alt depressed" event, that must preceed it, will be
3131 // eaten by the system)
3132 find_key_rec( Qt::Key_Alt, TRUE );
3133/// @todo (dmik) do the same for other global keys (ALT+TAB, ALT+ESC, CTRL+ESC)
3134// by handling this situation when we obtain/loose focus)
3135/// @todo (dmik) update: I don't actually think the above should be done, because
3136// it will not solve the problem of stuck modifier keys in general (there may be
3137// other combinations stolen by the system or other apps). More over, I guess
3138// that find_key_rec() above should also be removed to get identical behavior for
3139// all stolen keys. This will allow to solve the problem on the Qt application
3140// level if needed (and even in a platform-independent manner).
3141 }
3142 return TRUE;
3143 case Qt::Key_F4:
3144 // we handle this key combination ourselves because not
3145 // all top-level widgets have the system menu
3146 WinPostMsg( topLevelWidget()->winFId(), WM_CLOSE, 0, 0 );
3147 // see the comment above
3148 find_key_rec( Qt::Key_Alt, TRUE );
3149 return TRUE;
3150 default:
3151 break;
3152 }
3153 }
3154
3155 if ( rec ) {
3156 // it is already down (so it is auto-repeating)
3157 if ( rec->code < Key_Shift || rec->code > Key_ScrollLock ) {
3158 k0 = sendKeyEvent( QEvent::KeyRelease, rec->code, rec->ascii,
3159 state, grab, rec->text, TRUE );
3160 k1 = sendKeyEvent( QEvent::KeyPress, rec->code, rec->ascii,
3161 state, grab, rec->text, TRUE );
3162 }
3163 } else {
3164 // map shift+tab to shift+backtab, QAccel knows about it
3165 // and will handle it
3166 if ( code == Key_Tab && ( state & ShiftButton ) == ShiftButton )
3167 code = Key_BackTab;
3168 store_key_rec( chm.scancode, code, ascii, text );
3169 k0 = sendKeyEvent( QEvent::KeyPress, code, ascii,
3170 state, grab, text );
3171 }
3172 } else {
3173 // KEYUP
3174 KeyRec* rec = find_key_rec( chm.scancode, TRUE );
3175 if ( !rec ) {
3176 // Someone ate the key down event
3177 } else {
3178 k0 = sendKeyEvent( QEvent::KeyRelease, rec->code, rec->ascii,
3179 state, grab, rec->text );
3180
3181 // keyboard context menu event
3182 if ( rec->code == Key_Menu && !state )
3183 WinPostMsg( qmsg.hwnd, WM_CONTEXTMENU, 0, MPFROM2SHORT( 0, 1 ) );
3184 }
3185 }
3186
3187#if 0
3188 qDebug("WM_CHAR: RESULT = %d", (k0 || k1));
3189#endif
3190 return k0 || k1;
3191}
3192
3193#ifndef QT_NO_WHEELEVENT
3194bool QETWidget::translateWheelEvent( const QMSG &qmsg )
3195{
3196 enum { WHEEL_DELTA = 120 };
3197
3198 if ( sm_blockUserInput ) // block user interaction during session management
3199 return TRUE;
3200
3201 // consume duplicate wheel events sent by the AMouse driver to emulate
3202 // multiline scrolls. we need this since currently Qt (QScrollBar, for
3203 // instance) maintains the number of lines to scroll per wheel rotation
3204 // (including the special handling of CTRL and SHIFT modifiers) on its own
3205 // and doesn't have a setting to tell it to be aware of system settings
3206 // for the mouse wheel. if we had processed events as they are, we would
3207 // get a confusing behavior (too many lines scrolled etc.).
3208 {
3209 int devh = QApplication::desktop()->height();
3210 QMSG wheelMsg;
3211 while (
3212 WinPeekMsg( 0, &wheelMsg, qmsg.hwnd, qmsg.msg, qmsg.msg, PM_NOREMOVE )
3213 ) {
3214 // PM bug: ptl contains SHORT coordinates although fields are LONG
3215 wheelMsg.ptl.x = (short) wheelMsg.ptl.x;
3216 wheelMsg.ptl.y = (short) wheelMsg.ptl.y;
3217 // flip y coordinate
3218 wheelMsg.ptl.y = devh - (wheelMsg.ptl.y + 1);
3219 if (
3220 wheelMsg.mp1 != qmsg.mp1 ||
3221 wheelMsg.mp2 != qmsg.mp2 ||
3222 wheelMsg.ptl.x != qmsg.ptl.x ||
3223 wheelMsg.ptl.y != qmsg.ptl.y
3224 )
3225 break;
3226 WinPeekMsg( 0, &wheelMsg, qmsg.hwnd, qmsg.msg, qmsg.msg, PM_REMOVE );
3227 }
3228 }
3229
3230 int delta;
3231 USHORT cmd = SHORT2FROMMP(qmsg.mp2);
3232 switch ( cmd ) {
3233 case SB_LINEUP:
3234 case SB_PAGEUP:
3235 delta = WHEEL_DELTA;
3236 break;
3237 case SB_LINEDOWN:
3238 case SB_PAGEDOWN:
3239 delta = -WHEEL_DELTA;
3240 break;
3241 default:
3242 return FALSE;
3243 }
3244
3245 int state = 0;
3246 if ( WinGetKeyState( HWND_DESKTOP, VK_SHIFT ) & 0x8000 )
3247 state |= ShiftButton;
3248 if ( WinGetKeyState( HWND_DESKTOP, VK_ALT ) & 0x8000 ||
3249 (qt_extraKeyState & Qt::AltButton)
3250 )
3251 state |= AltButton;
3252 if ( WinGetKeyState( HWND_DESKTOP, VK_CTRL ) & 0x8000 )
3253 state |= ControlButton;
3254 if ( qt_extraKeyState & Qt::MetaButton )
3255 state |= MetaButton;
3256
3257 Orientation orient;
3258 // Alt inverts scroll orientation (Qt/Win32 behavior)
3259 if ( state & AltButton )
3260 orient = qmsg.msg == WM_VSCROLL ? Horizontal : Vertical;
3261 else
3262 orient = qmsg.msg == WM_VSCROLL ? Vertical : Horizontal;
3263
3264 QPoint globalPos (qmsg.ptl.x, qmsg.ptl.y);
3265
3266 // if there is a widget under the mouse and it is not shadowed
3267 // by modality, we send the event to it first
3268 int ret = 0;
3269 QWidget* w = QApplication::widgetAt( globalPos, TRUE );
3270 if ( !w || !qt_try_modal( w, (QMSG*)&qmsg, ret ) )
3271 w = this;
3272
3273 // send the event to the widget or its ancestors
3274 {
3275 QWidget* popup = qApp->activePopupWidget();
3276 if ( popup && w->topLevelWidget() != popup )
3277 popup->close();
3278 QWheelEvent e( w->mapFromGlobal( globalPos ), globalPos, delta, state, orient );
3279 if ( QApplication::sendSpontaneousEvent( w, &e ) )
3280 return TRUE;
3281 }
3282
3283 // send the event to the widget that has the focus or its ancestors, if different
3284 if ( w != qApp->focusWidget() && ( w = qApp->focusWidget() ) ) {
3285 QWidget* popup = qApp->activePopupWidget();
3286 if ( popup && w->topLevelWidget() != popup )
3287 popup->close();
3288 QWheelEvent e( w->mapFromGlobal( globalPos ), globalPos, delta, state, orient );
3289 if ( QApplication::sendSpontaneousEvent( w, &e ) )
3290 return TRUE;
3291 }
3292 return FALSE;
3293}
3294#endif
3295
3296static bool isModifierKey(int code)
3297{
3298 return code >= Qt::Key_Shift && code <= Qt::Key_ScrollLock;
3299}
3300
3301bool QETWidget::sendKeyEvent( QEvent::Type type, int code, int ascii,
3302 int state, bool grab, const QString& text,
3303 bool autor )
3304{
3305 if ( type == QEvent::KeyPress && !grab ) {
3306 // send accel events if the keyboard is not grabbed
3307 QKeyEvent a( type, code, ascii, state, text, autor, QMAX(1, int(text.length())) );
3308 if ( qt_tryAccelEvent( this, &a ) )
3309 return TRUE;
3310 }
3311 if ( !isEnabled() )
3312 return FALSE;
3313 QKeyEvent e( type, code, ascii, state, text, autor, QMAX(1, int(text.length())) );
3314 QApplication::sendSpontaneousEvent( this, &e );
3315 if ( !isModifierKey(code) && state == Qt::AltButton
3316 && ((code>=Key_A && code<=Key_Z) || (code>=Key_0 && code<=Key_9))
3317 && type == QEvent::KeyPress && !e.isAccepted() )
3318 QApplication::beep(); // emulate PM behavior
3319 return e.isAccepted();
3320}
3321
3322
3323//
3324// Paint event translation
3325//
3326bool QETWidget::translatePaintEvent( const QMSG & )
3327{
3328 HPS displayPS = qt_display_ps();
3329
3330#if !defined (QT_PM_NO_WIDGETMASK)
3331 // Since we don't use WS_CLIPSIBLINGS and WS_CLIPCHILDREN bits (see
3332 // qwidget_pm.cpp), we have to validate areas that intersect with our
3333 // children and siblings, taking their clip regions into account.
3334 validateObstacles();
3335#endif
3336
3337 HRGN hrgn = GpiCreateRegion( displayPS, 0, NULL );
3338 LONG rc = WinQueryUpdateRegion( winId(), hrgn );
3339 if ( rc == RGN_ERROR ) {
3340 GpiDestroyRegion( displayPS, hrgn );
3341 hps = 0;
3342 clearWState( WState_InPaintEvent );
3343 return FALSE;
3344 }
3345
3346 setWState( WState_InPaintEvent );
3347 RECTL rcl;
3348 hps = WinBeginPaint( winId(), 0, &rcl );
3349
3350 // convert to width and height
3351 rcl.xRight -= rcl.xLeft;
3352 rcl.yTop -= rcl.yBottom;
3353
3354 // it's possible that the update rectangle is empty
3355 if ( !rcl.xRight || !rcl.yTop ) {
3356 WinEndPaint( hps );
3357 GpiDestroyRegion( displayPS, hrgn );
3358 hps = 0;
3359 clearWState( WState_InPaintEvent );
3360 return TRUE;
3361 }
3362
3363#if !defined (QT_PM_NO_WIDGETMASK)
3364 if ( WinQueryClipRegion( winId(), 0 ) != QCRGN_NO_CLIP_REGION ) {
3365 // Correct the update region by intersecting it with the clip
3366 // region (PM doesn't do that itself). It is necessary
3367 // to have a correct QRegion in QPaintEvent.
3368 HRGN hcrgn = GpiCreateRegion( displayPS, 0, NULL );
3369 WinQueryClipRegion( winId(), hcrgn );
3370 GpiCombineRegion( displayPS, hrgn, hrgn, hcrgn, CRGN_AND );
3371 GpiDestroyRegion( displayPS, hcrgn );
3372 }
3373#endif
3374
3375 // flip y coordinate
3376 rcl.yBottom = height() - (rcl.yBottom + rcl.yTop);
3377
3378 // erase background
3379#if !defined (DEBUG_REPAINTRESIZE)
3380 bool erase = !( testWFlags( WRepaintNoErase ) );
3381#else
3382 // Some oldish Qt widgets think that if they specify WRepaintNoErase but
3383 // not WResizeNoErase, the background should still be erased for them
3384 // in *repaint* events. The code below is left to debug these widgets
3385 // (to ensure this is the exact cause of repaint problems)
3386 bool erase = testWFlags( WRepaintNoErase | WResizeNoErase ) != WRepaintNoErase | WResizeNoErase;
3387#endif
3388 if ( erase )
3389 this->erase( rcl.xLeft, rcl.yBottom,
3390 rcl.xRight, rcl.yTop );
3391
3392#if defined (DEBUG_REPAINTRESIZE)
3393 qDebug( "WM_PAINT: [%s/%s/%08X] %ld,%ld; %ld,%ld erase: %d",
3394 name(), className(), widget_flags,
3395 rcl.xLeft, rcl.yBottom, rcl.xRight, rcl.yTop, erase );
3396#endif
3397
3398 // create a region that will take ownership of hrgn
3399 QRegion rgn( hrgn, height() );
3400
3401 QPaintEvent e( rgn, QRect( rcl.xLeft, rcl.yBottom, rcl.xRight, rcl.yTop ),
3402 erase );
3403 QApplication::sendSpontaneousEvent( this, (QEvent*) &e );
3404
3405 WinEndPaint( hps );
3406 hps = 0;
3407 clearWState( WState_InPaintEvent );
3408 return TRUE;
3409}
3410
3411//
3412// Window move and resize (configure) events
3413//
3414
3415bool QETWidget::translateConfigEvent( const QMSG &qmsg )
3416{
3417 if ( !testWState(WState_Created) ) // in QWidget::create()
3418 return TRUE;
3419
3420 WId fId = winFId();
3421 ULONG fStyle = WinQueryWindowULong( fId, QWL_STYLE );
3422 if ( testWState( WState_ConfigPending ) ) {
3423 // it's possible that we're trying to set the frame size smaller
3424 // than it possible for WC_FRAME in QWidget::internalSetGeometry().
3425 // here we correct this (crect there is set before WinSetWindowPos()
3426 // that sends WM_SIZE).
3427 QSize newSize( SHORT1FROMMP(qmsg.mp2), SHORT2FROMMP(qmsg.mp2) );
3428 if ( qmsg.msg == WM_SIZE && size() != newSize ) {
3429 crect.setSize( newSize );
3430 }
3431 return TRUE;
3432 }
3433 setWState( WState_ConfigPending ); // set config flag
3434 QRect cr = geometry();
3435 if ( qmsg.msg == WM_SIZE ) { // resize event
3436 QSize oldSize = size();
3437 QSize newSize( SHORT1FROMMP(qmsg.mp2), SHORT2FROMMP(qmsg.mp2) );
3438 cr.setSize( newSize );
3439 crect = cr;
3440 if ( isTopLevel() ) { // update caption/icon text
3441 createTLExtra();
3442 QString txt;
3443 if ( (fStyle & WS_MAXIMIZED) && !!iconText() )
3444 txt = iconText();
3445 else
3446 if ( !caption().isNull() )
3447 txt = caption();
3448
3449 if ( !!txt ) {
3450 WinSetWindowText( fId, txt.local8Bit() );
3451 }
3452 }
3453 if ( oldSize != newSize) {
3454#if !defined (QT_PM_NO_WIDGETMASK)
3455 // Spontaneous (external to Qt) WM_SIZE messages should occur only
3456 // on top-level widgets. If we get them for a non top-level widget,
3457 // the result will most likely be incorrect because widget masks will
3458 // not be properly processed (i.e. in the way it is done in
3459 // QWidget::internalSetGeometry() when the geometry is changed from
3460 // within Qt). So far, I see no need to support this (who will ever
3461 // need to move a non top-level window of a foreign process?).
3462 Q_ASSERT( isTopLevel() );
3463#endif
3464 if ( isVisible() ) {
3465 QResizeEvent e( newSize, oldSize );
3466 QApplication::sendSpontaneousEvent( this, &e );
3467 if ( !testWFlags( WStaticContents ) )
3468 repaint( !testWFlags(WResizeNoErase) );
3469 } else {
3470 QResizeEvent *e = new QResizeEvent( newSize, oldSize );
3471 QApplication::postEvent( this, e );
3472 }
3473 }
3474 } else if ( qmsg.msg == WM_MOVE ) { // move event
3475 QPoint oldPos = geometry().topLeft();
3476 SWP swp;
3477 if ( isTopLevel() ) {
3478 WinQueryWindowPos( fId, &swp );
3479 // flip y coordinate
3480 swp.y = QApplication::desktop()->height() - ( swp.y + swp.cy );
3481 QTLWExtra *top = topData();
3482 swp.x += top->fleft;
3483 swp.y += top->ftop;
3484 } else {
3485 WinQueryWindowPos( winId(), &swp );
3486 // flip y coordinate
3487 swp.y = parentWidget()->height() - ( swp.y + swp.cy );
3488 }
3489 QPoint newCPos( swp.x, swp.y );
3490 if ( newCPos != oldPos ) {
3491 cr.moveTopLeft( newCPos );
3492 crect = cr;
3493 if ( isVisible() ) {
3494 QMoveEvent e( newCPos, oldPos ); // cpos (client position)
3495 QApplication::sendSpontaneousEvent( this, &e );
3496 } else {
3497 QMoveEvent * e = new QMoveEvent( newCPos, oldPos );
3498 QApplication::postEvent( this, e );
3499 }
3500 }
3501 }
3502 clearWState( WState_ConfigPending ); // clear config flag
3503 return TRUE;
3504}
3505
3506
3507//
3508// Close window event translation.
3509//
3510// This class is a friend of QApplication because it needs to emit the
3511// lastWindowClosed() signal when the last top level widget is closed.
3512//
3513
3514bool QETWidget::translateCloseEvent( const QMSG & )
3515{
3516 return close(FALSE);
3517}
3518
3519void QApplication::setCursorFlashTime( int msecs )
3520{
3521 WinSetSysValue( HWND_DESKTOP, SV_CURSORRATE, msecs / 2 );
3522 cursor_flash_time = msecs;
3523}
3524
3525int QApplication::cursorFlashTime()
3526{
3527 int blink = (int) WinQuerySysValue( HWND_DESKTOP, SV_CURSORRATE );
3528 if ( !blink )
3529 return cursor_flash_time;
3530 if (blink > 0)
3531 return 2*blink;
3532 return 0;
3533}
3534
3535void QApplication::setDoubleClickInterval( int ms )
3536{
3537 WinSetSysValue( HWND_DESKTOP, SV_DBLCLKTIME, ms );
3538 mouse_double_click_time = ms;
3539}
3540
3541
3542int QApplication::doubleClickInterval()
3543{
3544 int ms = (int) WinQuerySysValue( HWND_DESKTOP, SV_DBLCLKTIME );
3545 if ( ms != 0 )
3546 return ms;
3547 return mouse_double_click_time;
3548}
3549
3550void QApplication::setWheelScrollLines( int n )
3551{
3552 wheel_scroll_lines = n;
3553}
3554
3555int QApplication::wheelScrollLines()
3556{
3557 return wheel_scroll_lines;
3558}
3559
3560void QApplication::setEffectEnabled( Qt::UIEffect effect, bool enable )
3561{
3562#ifndef QT_NO_EFFECTS
3563 switch (effect) {
3564 case UI_AnimateMenu:
3565 if ( enable ) fade_menu = FALSE;
3566 animate_menu = enable;
3567 break;
3568 case UI_FadeMenu:
3569 if ( enable )
3570 animate_menu = TRUE;
3571 fade_menu = enable;
3572 break;
3573 case UI_AnimateCombo:
3574 animate_combo = enable;
3575 break;
3576 case UI_AnimateTooltip:
3577 if ( enable ) fade_tooltip = FALSE;
3578 animate_tooltip = enable;
3579 break;
3580 case UI_FadeTooltip:
3581 if ( enable )
3582 animate_tooltip = TRUE;
3583 fade_tooltip = enable;
3584 break;
3585 case UI_AnimateToolBox:
3586 animate_toolbox = enable;
3587 break;
3588 default:
3589 animate_ui = enable;
3590 break;
3591 }
3592#endif
3593}
3594
3595bool QApplication::isEffectEnabled( Qt::UIEffect effect )
3596{
3597#ifndef QT_NO_EFFECTS
3598 if ( QColor::numBitPlanes() < 16 || !animate_ui )
3599 return FALSE;
3600
3601 switch( effect ) {
3602 case UI_AnimateMenu:
3603 return animate_menu;
3604 case UI_FadeMenu:
3605 return fade_menu;
3606 case UI_AnimateCombo:
3607 return animate_combo;
3608 case UI_AnimateTooltip:
3609 return animate_tooltip;
3610 case UI_FadeTooltip:
3611 return fade_tooltip;
3612 case UI_AnimateToolBox:
3613 return animate_toolbox;
3614 default:
3615 return animate_ui;
3616 }
3617#else
3618 return FALSE;
3619#endif
3620}
3621
3622void QApplication::flush()
3623{
3624}
3625
3626#if !defined (QT_NO_SESSIONMANAGER)
3627
3628bool qt_app_canQuit()
3629{
3630#if defined (DEBUG_SESSIONMANAGER)
3631 qDebug( "qt_app_canQuit(): sm_smActive=%d qt_about_to_destroy_wnd=%d "
3632 "sm_gracefulShutdown=%d sm_cancel=%d",
3633 sm_smActive, qt_about_to_destroy_wnd,
3634 sm_gracefulShutdown, sm_cancel );
3635#endif
3636
3637 BOOL answer = FALSE;
3638
3639 // We can get multiple WM_QUIT messages while the "session termination
3640 // procedure" (i.e. the QApplication::commitData() call) is still in
3641 // progress. Ignore them.
3642 if ( !sm_smActive ) {
3643 if ( sm_gracefulShutdown ) {
3644 // this is WM_QUIT after WM_SAVEAPPLICATION (either posted by the OS
3645 // or by ourselves), confirm the quit depending on what the user wants
3646 sm_quitSkipped = FALSE;
3647 answer = !sm_cancel;
3648 if ( sm_cancel ) {
3649 // the shutdown has been canceled, reset the flag to let the
3650 // graceful shutdown happen again later
3651 sm_gracefulShutdown = FALSE;
3652 }
3653 } else {
3654 // sm_gracefulShutdown is FALSE, so allowsInteraction() and friends
3655 // will return FALSE during commitData() (assuming that WM_QUIT w/o
3656 // WM_SAVEAPPLICATION is an emergency termination)
3657 sm_smActive = TRUE;
3658 sm_blockUserInput = TRUE; // prevent user-interaction outside interaction windows
3659 sm_cancel = FALSE;
3660 if ( qt_session_manager_self )
3661 qApp->commitData( *qt_session_manager_self );
3662 sm_smActive = FALSE;
3663 answer = TRUE; // ignore sm_cancel
3664 }
3665 } else {
3666 // if this is a WM_QUIT received during WM_SAVEAPPLICATION handling,
3667 // remember we've skipped (refused) it
3668 if ( sm_gracefulShutdown )
3669 sm_quitSkipped = TRUE;
3670 }
3671
3672#if defined (DEBUG_SESSIONMANAGER)
3673 qDebug( "qt_app_canQuit(): answer=%ld", answer );
3674#endif
3675
3676 return answer;
3677}
3678
3679bool QSessionManager::allowsInteraction()
3680{
3681 // Allow interation only when the system is being normally shutdown
3682 // and informs us using WM_SAVEAPPLICATION. When we receive WM_QUIT directly
3683 // (so sm_gracefulShutdown is FALSE), interaction is disallowed.
3684 if ( sm_smActive && sm_gracefulShutdown ) {
3685 sm_blockUserInput = FALSE;
3686 return TRUE;
3687 }
3688
3689 return FALSE;
3690}
3691
3692bool QSessionManager::allowsErrorInteraction()
3693{
3694 // Allow interation only when the system is being normally shutdown
3695 // and informs us using WM_SAVEAPPLICATION. When we receive WM_QUIT directly
3696 // (so sm_gracefulShutdown is FALSE), interaction is disallowed.
3697 if ( sm_smActive && sm_gracefulShutdown ) {
3698 sm_blockUserInput = FALSE;
3699 return TRUE;
3700 }
3701
3702 return FALSE;
3703}
3704
3705void QSessionManager::release()
3706{
3707 if ( sm_smActive && sm_gracefulShutdown )
3708 sm_blockUserInput = TRUE;
3709}
3710
3711void QSessionManager::cancel()
3712{
3713 if ( sm_smActive && sm_gracefulShutdown )
3714 sm_cancel = TRUE;
3715}
3716
3717#endif
Note: See TracBrowser for help on using the repository browser.