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

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

Widgets: Improved min/max/fullscreen handling:

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