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

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

Widgets: QTextEdit: Fixed: AltGr+key can produce valid characters in some kbd layouts (i.e. the German one) which we must not ignore.

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