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

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

Moved the QApplication::commitData() call to WM_SAVEAPPLICATION, to protect against WinShutdownSystem() killing the app if its parent is a VIO app that is waiting for child termoination, and to support canceling the Extended XWorkplace procedure gracefully (requires an XWP patch that is not accepted by the maintainers thoug).

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