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

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

Kernel: Fixed keyboard pocessing: Let the standard OS/2 Alt+ddd shortcut (that composed chars from typed in ASCII codes) work correctly by resetting both virtual key code and text (to "hide" them from Qt).

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