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

Last change on this file since 196 was 195, checked in by rudi, 14 years ago

Fix for redrawing problems (ticket #47)

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