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

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

kernel: Created the initial implementation of the Drag'n'Drop support for OS/2 (not fully functiona yet: updating widget contents under the drag pointer will cause screen corruption, setting the drag object's pixmap is not possible). See ticket:22 for more info.

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