]> git.saurik.com Git - wxWidgets.git/blame - src/msw/app.cpp
fixes needed for separate DLL build to work
[wxWidgets.git] / src / msw / app.cpp
CommitLineData
2bda0e17
KB
1/////////////////////////////////////////////////////////////////////////////
2// Name: app.cpp
3// Purpose: wxApp
4// Author: Julian Smart
5// Modified by:
6// Created: 04/01/98
7// RCS-ID: $Id$
6c9a19aa
JS
8// Copyright: (c) Julian Smart
9// Licence: wxWindows licence
2bda0e17
KB
10/////////////////////////////////////////////////////////////////////////////
11
e5c0b16a
VZ
12// ===========================================================================
13// declarations
14// ===========================================================================
15
16// ---------------------------------------------------------------------------
17// headers
18// ---------------------------------------------------------------------------
19
2bda0e17 20#ifdef __GNUG__
e5c0b16a 21 #pragma implementation "app.h"
2bda0e17
KB
22#endif
23
24// For compilers that support precompilation, includes "wx.h".
2bda0e17
KB
25#include "wx/wxprec.h"
26
27#if defined(__BORLANDC__)
e5c0b16a 28 #pragma hdrstop
2bda0e17
KB
29#endif
30
31#ifndef WX_PRECOMP
e5c0b16a
VZ
32 #include "wx/frame.h"
33 #include "wx/app.h"
34 #include "wx/utils.h"
35 #include "wx/gdicmn.h"
36 #include "wx/pen.h"
37 #include "wx/brush.h"
38 #include "wx/cursor.h"
39 #include "wx/icon.h"
40 #include "wx/palette.h"
41 #include "wx/dc.h"
42 #include "wx/dialog.h"
43 #include "wx/msgdlg.h"
44 #include "wx/intl.h"
45 #include "wx/dynarray.h"
72cdf4c9
VZ
46 #include "wx/wxchar.h"
47 #include "wx/icon.h"
31f6de22 48 #include "wx/log.h"
2bda0e17
KB
49#endif
50
e2478fde 51#include "wx/apptrait.h"
7104f65d 52#include "wx/filename.h"
2bda0e17 53#include "wx/module.h"
4bf78aae 54
4286a5b5
RR
55#include "wx/msw/private.h"
56
4bf78aae 57#if wxUSE_THREADS
bee503b0
VZ
58 #include "wx/thread.h"
59
60 // define the array of MSG strutures
61 WX_DECLARE_OBJARRAY(MSG, wxMsgArray);
62
63 #include "wx/arrimpl.cpp"
64
65 WX_DEFINE_OBJARRAY(wxMsgArray);
66#endif // wxUSE_THREADS
2bda0e17 67
8614c467
VZ
68#if wxUSE_TOOLTIPS
69 #include "wx/tooltip.h"
70#endif // wxUSE_TOOLTIPS
71
c42404a5
VZ
72// OLE is used for drag-and-drop, clipboard, OLE Automation..., but some
73// compilers don't support it (missing headers, libs, ...)
2bdf7154 74#if defined(__GNUWIN32_OLD__) || defined(__SYMANTEC__) || defined(__SALFORDC__)
e5c0b16a
VZ
75 #undef wxUSE_OLE
76
77 #define wxUSE_OLE 0
78#endif // broken compilers
79
80#if wxUSE_OLE
6e0d9d43 81 #include <ole2.h>
d05237ea 82#endif
ce3ed50d 83
2bda0e17 84#include <string.h>
a5e0e655 85#include <ctype.h>
2bda0e17 86
b39dbf34 87#if defined(__WIN95__) && !((defined(__GNUWIN32_OLD__) || defined(__WXMICROWIN__)) && !defined(__CYGWIN10__))
e5c0b16a 88 #include <commctrl.h>
2bda0e17
KB
89#endif
90
bdc72a22
VZ
91// ----------------------------------------------------------------------------
92// conditional compilation
93// ----------------------------------------------------------------------------
94
95// The macro _WIN32_IE is defined by commctrl.h (unless it had already been
96// defined before) and shows us what common control features are available
97// during the compile time (it doesn't mean that they will be available during
98// the run-time, use GetComCtl32Version() to test for them!). The possible
99// values are:
100//
101// 0x0200 for comctl32.dll 4.00 shipped with Win95/NT 4.0
102// 0x0300 4.70 IE 3.x
103// 0x0400 4.71 IE 4.0
104// 0x0401 4.72 IE 4.01 and Win98
105// 0x0500 5.00 IE 5.x and NT 5.0 (Win2000)
106
107#ifndef _WIN32_IE
108 // minimal set of features by default
109 #define _WIN32_IE 0x0200
110#endif
111
a4a2e5d2 112#if _WIN32_IE >= 0x0300 && \
7f93875d
JS
113 (!defined(__MINGW32__) || wxCHECK_W32API_VERSION( 2, 0 )) && \
114 !defined(__CYGWIN__)
036bc7d9
VZ
115 #include <shlwapi.h>
116#endif
117
e5c0b16a
VZ
118// ---------------------------------------------------------------------------
119// global variables
120// ---------------------------------------------------------------------------
121
837e5743 122extern wxChar *wxBuffer;
cde9f08e 123extern wxList WXDLLEXPORT wxPendingDelete;
04ef50df 124#ifndef __WXMICROWIN__
2bda0e17 125extern void wxSetKeyboardHook(bool doIt);
04ef50df 126#endif
2bda0e17 127
42e69d6b 128MSG s_currentMsg;
2bda0e17 129
193fe989
VZ
130// NB: all "NoRedraw" classes must have the same names as the "normal" classes
131// with NR suffix - wxWindow::MSWCreate() supposes this
03baf031
VZ
132const wxChar *wxCanvasClassName = wxT("wxWindowClass");
133const wxChar *wxCanvasClassNameNR = wxT("wxWindowClassNR");
2ffa221c
VZ
134const wxChar *wxMDIFrameClassName = wxT("wxMDIFrameClass");
135const wxChar *wxMDIFrameClassNameNoRedraw = wxT("wxMDIFrameClassNR");
136const wxChar *wxMDIChildFrameClassName = wxT("wxMDIChildFrameClass");
137const wxChar *wxMDIChildFrameClassNameNoRedraw = wxT("wxMDIChildFrameClassNR");
2bda0e17 138
57c208c5 139HBRUSH wxDisableButtonBrush = (HBRUSH) 0;
2bda0e17 140
94826170
VZ
141// ----------------------------------------------------------------------------
142// private functions
143// ----------------------------------------------------------------------------
2bda0e17 144
94826170 145LRESULT WXDLLEXPORT APIENTRY wxWndProc(HWND, UINT, WPARAM, LPARAM);
3b415ba4 146
e5c0b16a 147// ===========================================================================
e2478fde
VZ
148// wxGUIAppTraits implementation
149// ===========================================================================
150
151// private class which we use to pass parameters from BeforeChildWaitLoop() to
152// AfterChildWaitLoop()
153struct ChildWaitLoopData
154{
155 ChildWaitLoopData(wxWindowDisabler *wd_, wxWindow *winActive_)
156 {
157 wd = wd_;
158 winActive = winActive_;
159 }
160
161 wxWindowDisabler *wd;
162 wxWindow *winActive;
163};
164
165void *wxGUIAppTraits::BeforeChildWaitLoop()
166{
167 /*
168 We use a dirty hack here to disable all application windows (which we
169 must do because otherwise the calls to wxYield() could lead to some very
170 unexpected reentrancies in the users code) but to avoid losing
171 focus/activation entirely when the child process terminates which would
172 happen if we simply disabled everything using wxWindowDisabler. Indeed,
173 remember that Windows will never activate a disabled window and when the
174 last childs window is closed and Windows looks for a window to activate
175 all our windows are still disabled. There is no way to enable them in
176 time because we don't know when the childs windows are going to be
177 closed, so the solution we use here is to keep one special tiny frame
178 enabled all the time. Then when the child terminates it will get
179 activated and when we close it below -- after reenabling all the other
180 windows! -- the previously active window becomes activated again and
181 everything is ok.
182 */
183 wxBeginBusyCursor();
184
185 // first disable all existing windows
186 wxWindowDisabler *wd = new wxWindowDisabler;
187
188 // then create an "invisible" frame: it has minimal size, is positioned
189 // (hopefully) outside the screen and doesn't appear on the taskbar
190 wxWindow *winActive = new wxFrame
191 (
192 wxTheApp->GetTopWindow(),
193 -1,
194 _T(""),
195 wxPoint(32600, 32600),
196 wxSize(1, 1),
197 wxDEFAULT_FRAME_STYLE | wxFRAME_NO_TASKBAR
198 );
199 winActive->Show();
200
201 return new ChildWaitLoopData(wd, winActive);
202}
203
204void wxGUIAppTraits::AlwaysYield()
205{
206 wxYield();
207}
208
209void wxGUIAppTraits::AfterChildWaitLoop(void *dataOrig)
210{
211 wxEndBusyCursor();
212
213 const ChildWaitLoopData * const data = (ChildWaitLoopData *)dataOrig;
214
215 delete data->wd;
216
217 // finally delete the dummy frame and, as wd has been already destroyed and
218 // the other windows reenabled, the activation is going to return to the
219 // window which had had it before
220 data->winActive->Destroy();
221}
222
223bool wxGUIAppTraits::DoMessageFromThreadWait()
224{
225 return !wxTheApp || wxTheApp->DoMessage();
226}
227
228// ===========================================================================
229// wxApp implementation
e5c0b16a 230// ===========================================================================
589f0e3e 231
94826170
VZ
232int wxApp::m_nCmdShow = SW_SHOWNORMAL;
233
e5c0b16a 234// ---------------------------------------------------------------------------
e2478fde 235// wxWin macros
e5c0b16a
VZ
236// ---------------------------------------------------------------------------
237
f6bcfd97 238IMPLEMENT_DYNAMIC_CLASS(wxApp, wxEvtHandler)
e5c0b16a 239
f6bcfd97
BP
240BEGIN_EVENT_TABLE(wxApp, wxEvtHandler)
241 EVT_IDLE(wxApp::OnIdle)
242 EVT_END_SESSION(wxApp::OnEndSession)
243 EVT_QUERY_END_SESSION(wxApp::OnQueryEndSession)
244END_EVENT_TABLE()
e5c0b16a 245
94826170
VZ
246// class to ensure that wxAppBase::CleanUp() is called if our Initialize()
247// fails
248class wxCallBaseCleanup
249{
250public:
251 wxCallBaseCleanup(wxApp *app) : m_app(app) { }
252 ~wxCallBaseCleanup() { if ( m_app ) m_app->wxAppBase::CleanUp(); }
253
254 void Dismiss() { m_app = NULL; }
255
256private:
257 wxApp *m_app;
258};
259
e5c0b16a 260//// Initialize
05e2b077 261bool wxApp::Initialize(int& argc, wxChar **argv)
2bda0e17 262{
94826170
VZ
263 if ( !wxAppBase::Initialize(argc, argv) )
264 return false;
265
266 // ensure that base cleanup is done if we return too early
267 wxCallBaseCleanup callBaseCleanup(this);
268
f6bcfd97 269 // the first thing to do is to check if we're trying to run an Unicode
7c9955d1 270 // program under Win9x w/o MSLU emulation layer - if so, abort right now
dfc40ef3 271 // as it has no chance to work
eb5e4d9a 272#if wxUSE_UNICODE && !wxUSE_UNICODE_MSLU
f6bcfd97
BP
273 if ( wxGetOsVersion() != wxWINDOWS_NT )
274 {
275 // note that we can use MessageBoxW() as it's implemented even under
276 // Win9x - OTOH, we can't use wxGetTranslation() because the file APIs
277 // used by wxLocale are not
278 ::MessageBox
279 (
280 NULL,
eb5e4d9a 281 _T("This program uses Unicode and requires Windows NT/2000/XP.\nProgram aborted."),
f6bcfd97
BP
282 _T("wxWindows Fatal Error"),
283 MB_ICONERROR | MB_OK
284 );
285
286 return FALSE;
287 }
eb5e4d9a 288#endif // wxUSE_UNICODE && !wxUSE_UNICODE_MSLU
f6bcfd97 289
837e5743 290 wxBuffer = new wxChar[1500]; // FIXME
589f0e3e 291
04ef50df 292#if defined(__WIN95__) && !defined(__WXMICROWIN__)
a5e0e655 293 InitCommonControls();
e5c0b16a 294#endif // __WIN95__
2bda0e17 295
8cb172b4 296#if wxUSE_OLE || wxUSE_DRAG_AND_DROP
c49245f8
VZ
297
298#ifdef __WIN16__
e5c0b16a 299 // for OLE, enlarge message queue to be as large as possible
aa0b7e1e 300 int iMsg = 96;
c49245f8
VZ
301 while (!SetMessageQueue(iMsg) && (iMsg -= 8))
302 ;
303#endif // Win16
8cb172b4 304
abad5367 305#if wxUSE_OLE
a5e0e655
VZ
306 // we need to initialize OLE library
307 if ( FAILED(::OleInitialize(NULL)) )
e5c0b16a 308 wxLogError(_("Cannot initialize OLE"));
abad5367 309#endif
1e6feb95 310
c49245f8 311#endif // wxUSE_OLE
2bda0e17 312
1f112209 313#if wxUSE_CTL3D
a5e0e655 314 if (!Ctl3dRegister(wxhInstance))
223d09f6 315 wxLogError(wxT("Cannot register CTL3D"));
2bda0e17 316
a5e0e655 317 Ctl3dAutoSubclass(wxhInstance);
1e6feb95 318#endif // wxUSE_CTL3D
2bda0e17 319
aa0b7e1e 320 RegisterWindowClasses();
2bda0e17 321
04ef50df 322#ifndef __WXMICROWIN__
aa0b7e1e 323 // Create the brush for disabling bitmap buttons
2bda0e17 324
42e69d6b 325 LOGBRUSH lb;
aa0b7e1e 326 lb.lbStyle = BS_PATTERN;
097f29c2 327 lb.lbColor = 0;
223d09f6 328 lb.lbHatch = (int)LoadBitmap( wxhInstance, wxT("wxDISABLE_BUTTON_BITMAP") );
3a5ffa81
VZ
329 if ( lb.lbHatch )
330 {
331 wxDisableButtonBrush = ::CreateBrushIndirect( & lb );
332 ::DeleteObject( (HGDIOBJ)lb.lbHatch );
333 }
334 //else: wxWindows resources are probably not linked in
04ef50df 335#endif
2bda0e17 336
aa0b7e1e 337#if wxUSE_PENWINDOWS
a5e0e655 338 wxRegisterPenWin();
aa0b7e1e 339#endif
2bda0e17 340
1bffa913 341 wxWinHandleHash = new wxWinHashTable(wxKEY_INTEGER, 100);
2bda0e17 342
6e0d9d43 343 // This is to foil optimizations in Visual C++ that throw out dummy.obj.
8cbd2bde 344 // PLEASE DO NOT ALTER THIS.
7fee680b 345#if defined(__VISUALC__) && defined(__WIN16__) && !defined(WXMAKINGDLL)
a5e0e655
VZ
346 extern char wxDummyChar;
347 if (wxDummyChar) wxDummyChar++;
aa0b7e1e 348#endif
a5e0e655 349
04ef50df 350#ifndef __WXMICROWIN__
aa0b7e1e 351 wxSetKeyboardHook(TRUE);
04ef50df 352#endif
2bda0e17 353
94826170
VZ
354 callBaseCleanup.Dismiss();
355
356 return true;
2bda0e17
KB
357}
358
42e69d6b
VZ
359// ---------------------------------------------------------------------------
360// RegisterWindowClasses
361// ---------------------------------------------------------------------------
589f0e3e 362
b782f2e0
VZ
363// TODO we should only register classes really used by the app. For this it
364// would be enough to just delay the class registration until an attempt
365// to create a window of this class is made.
bb6290e3 366bool wxApp::RegisterWindowClasses()
2bda0e17 367{
42e69d6b 368 WNDCLASS wndclass;
03baf031 369 wxZeroMemory(wndclass);
e5c0b16a 370
193fe989
VZ
371 // for each class we register one with CS_(V|H)REDRAW style and one
372 // without for windows created with wxNO_FULL_REDRAW_ON_REPAINT flag
373 static const long styleNormal = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
374 static const long styleNoRedraw = CS_DBLCLKS;
375
42e69d6b 376 // the fields which are common to all classes
e5c0b16a 377 wndclass.lpfnWndProc = (WNDPROC)wxWndProc;
e5c0b16a 378 wndclass.hInstance = wxhInstance;
42e69d6b 379 wndclass.hCursor = ::LoadCursor((HINSTANCE)NULL, IDC_ARROW);
42e69d6b
VZ
380
381 // Register the frame window class.
382 wndclass.hbrBackground = (HBRUSH)(COLOR_APPWORKSPACE + 1);
03baf031 383 wndclass.lpszClassName = wxCanvasClassName;
b782f2e0 384 wndclass.style = styleNormal;
e5c0b16a 385
42e69d6b 386 if ( !RegisterClass(&wndclass) )
e5c0b16a 387 {
f6bcfd97 388 wxLogLastError(wxT("RegisterClass(frame)"));
e5c0b16a
VZ
389 }
390
193fe989 391 // "no redraw" frame
03baf031 392 wndclass.lpszClassName = wxCanvasClassNameNR;
193fe989
VZ
393 wndclass.style = styleNoRedraw;
394
395 if ( !RegisterClass(&wndclass) )
396 {
f6bcfd97 397 wxLogLastError(wxT("RegisterClass(no redraw frame)"));
193fe989
VZ
398 }
399
e5c0b16a 400 // Register the MDI frame window class.
42e69d6b 401 wndclass.hbrBackground = (HBRUSH)NULL; // paint MDI frame ourselves
b782f2e0
VZ
402 wndclass.lpszClassName = wxMDIFrameClassName;
403 wndclass.style = styleNormal;
42e69d6b
VZ
404
405 if ( !RegisterClass(&wndclass) )
e5c0b16a 406 {
f6bcfd97 407 wxLogLastError(wxT("RegisterClass(MDI parent)"));
e5c0b16a
VZ
408 }
409
193fe989 410 // "no redraw" MDI frame
b782f2e0 411 wndclass.lpszClassName = wxMDIFrameClassNameNoRedraw;
193fe989
VZ
412 wndclass.style = styleNoRedraw;
413
414 if ( !RegisterClass(&wndclass) )
415 {
f6bcfd97 416 wxLogLastError(wxT("RegisterClass(no redraw MDI parent frame)"));
193fe989
VZ
417 }
418
e5c0b16a 419 // Register the MDI child frame window class.
42e69d6b
VZ
420 wndclass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
421 wndclass.lpszClassName = wxMDIChildFrameClassName;
b782f2e0 422 wndclass.style = styleNormal;
42e69d6b
VZ
423
424 if ( !RegisterClass(&wndclass) )
e5c0b16a 425 {
f6bcfd97 426 wxLogLastError(wxT("RegisterClass(MDI child)"));
e5c0b16a
VZ
427 }
428
193fe989
VZ
429 // "no redraw" MDI child frame
430 wndclass.lpszClassName = wxMDIChildFrameClassNameNoRedraw;
431 wndclass.style = styleNoRedraw;
432
433 if ( !RegisterClass(&wndclass) )
434 {
f6bcfd97 435 wxLogLastError(wxT("RegisterClass(no redraw MDI child)"));
193fe989
VZ
436 }
437
e5c0b16a 438 return TRUE;
2bda0e17
KB
439}
440
9787a4b6
VZ
441// ---------------------------------------------------------------------------
442// UnregisterWindowClasses
443// ---------------------------------------------------------------------------
444
445bool wxApp::UnregisterWindowClasses()
446{
447 bool retval = TRUE;
448
c67d6888 449#ifndef __WXMICROWIN__
9787a4b6 450 // MDI frame window class.
03baf031 451 if ( !::UnregisterClass(wxMDIFrameClassName, wxhInstance) )
9787a4b6
VZ
452 {
453 wxLogLastError(wxT("UnregisterClass(MDI parent)"));
454
455 retval = FALSE;
456 }
457
458 // "no redraw" MDI frame
03baf031 459 if ( !::UnregisterClass(wxMDIFrameClassNameNoRedraw, wxhInstance) )
9787a4b6
VZ
460 {
461 wxLogLastError(wxT("UnregisterClass(no redraw MDI parent frame)"));
462
463 retval = FALSE;
464 }
465
466 // MDI child frame window class.
03baf031 467 if ( !::UnregisterClass(wxMDIChildFrameClassName, wxhInstance) )
9787a4b6
VZ
468 {
469 wxLogLastError(wxT("UnregisterClass(MDI child)"));
470
471 retval = FALSE;
472 }
473
474 // "no redraw" MDI child frame
03baf031 475 if ( !::UnregisterClass(wxMDIChildFrameClassNameNoRedraw, wxhInstance) )
9787a4b6
VZ
476 {
477 wxLogLastError(wxT("UnregisterClass(no redraw MDI child)"));
478
479 retval = FALSE;
480 }
481
03baf031
VZ
482 // canvas class name
483 if ( !::UnregisterClass(wxCanvasClassName, wxhInstance) )
9787a4b6
VZ
484 {
485 wxLogLastError(wxT("UnregisterClass(canvas)"));
486
487 retval = FALSE;
488 }
489
03baf031 490 if ( !::UnregisterClass(wxCanvasClassNameNR, wxhInstance) )
9787a4b6
VZ
491 {
492 wxLogLastError(wxT("UnregisterClass(no redraw canvas)"));
493
494 retval = FALSE;
495 }
03baf031
VZ
496#endif // __WXMICROWIN__
497
9787a4b6
VZ
498 return retval;
499}
500
bb6290e3 501void wxApp::CleanUp()
2bda0e17 502{
e5c0b16a
VZ
503 delete[] wxBuffer;
504 wxBuffer = NULL;
589f0e3e 505
04ef50df 506#ifndef __WXMICROWIN__
e5c0b16a 507 wxSetKeyboardHook(FALSE);
04ef50df 508#endif
2bda0e17 509
47d67540 510#if wxUSE_PENWINDOWS
e5c0b16a 511 wxCleanUpPenWin();
2bda0e17
KB
512#endif
513
e5c0b16a 514 if ( wxDisableButtonBrush )
42e69d6b 515 ::DeleteObject( wxDisableButtonBrush );
e5c0b16a
VZ
516
517#if wxUSE_OLE
518 ::OleUninitialize();
5de5db0e 519#endif
2bda0e17 520
9787a4b6
VZ
521 // for an EXE the classes are unregistered when it terminates but DLL may
522 // be loaded several times (load/unload/load) into the same process in
523 // which case the registration will fail after the first time if we don't
524 // unregister the classes now
525 UnregisterWindowClasses();
9787a4b6 526
1f112209 527#if wxUSE_CTL3D
e5c0b16a 528 Ctl3dUnregister(wxhInstance);
2bda0e17
KB
529#endif
530
1bffa913 531 delete wxWinHandleHash;
94826170 532 wxWinHandleHash = NULL;
f8a3e080 533
94826170 534 wxAppBase::CleanUp();
2bda0e17
KB
535}
536
94826170
VZ
537// ----------------------------------------------------------------------------
538// wxApp ctor/dtor
539// ----------------------------------------------------------------------------
589f0e3e 540
bb6290e3 541wxApp::wxApp()
2bda0e17 542{
e5c0b16a 543 m_printMode = wxPRINT_WINDOWS;
2bda0e17
KB
544}
545
589f0e3e
JS
546wxApp::~wxApp()
547{
94826170
VZ
548 // our cmd line arguments are allocated inside wxEntry(HINSTANCE), they
549 // don't come from main(), so we have to free them
550
551 while ( argc )
e5c0b16a 552 {
94826170
VZ
553 // m_argv elements were allocated by wxStrdup()
554 free(argv[--argc]);
e5c0b16a 555 }
94826170
VZ
556
557 // but m_argv itself -- using new[]
558 delete [] argv;
589f0e3e
JS
559}
560
bb6290e3 561bool wxApp::Initialized()
2bda0e17
KB
562{
563#ifndef _WINDLL
e5c0b16a
VZ
564 if (GetTopWindow())
565 return TRUE;
566 else
567 return FALSE;
3b415ba4 568#else // Assume initialized if DLL (no way of telling)
e5c0b16a 569 return TRUE;
2bda0e17
KB
570#endif
571}
572
573/*
574 * Get and process a message, returning FALSE if WM_QUIT
bee503b0 575 * received (and also set the flag telling the app to exit the main loop)
2bda0e17
KB
576 *
577 */
bb6290e3 578bool wxApp::DoMessage()
2bda0e17 579{
bee503b0
VZ
580 BOOL rc = ::GetMessage(&s_currentMsg, (HWND) NULL, 0, 0);
581 if ( rc == 0 )
582 {
583 // got WM_QUIT
584 m_keepGoing = FALSE;
4a9968f9 585
bee503b0
VZ
586 return FALSE;
587 }
588 else if ( rc == -1 )
589 {
590 // should never happen, but let's test for it nevertheless
f6bcfd97 591 wxLogLastError(wxT("GetMessage"));
bee503b0
VZ
592 }
593 else
594 {
595#if wxUSE_THREADS
596 wxASSERT_MSG( wxThread::IsMain(),
223d09f6 597 wxT("only the main thread can process Windows messages") );
d50b2a58 598
bee503b0
VZ
599 static bool s_hadGuiLock = TRUE;
600 static wxMsgArray s_aSavedMessages;
601
602 // if a secondary thread owns is doing GUI calls, save all messages for
603 // later processing - we can't process them right now because it will
604 // lead to recursive library calls (and we're not reentrant)
605 if ( !wxGuiOwnedByMainThread() )
606 {
607 s_hadGuiLock = FALSE;
608
4a9968f9
VZ
609 // leave out WM_COMMAND messages: too dangerous, sometimes
610 // the message will be processed twice
611 if ( !wxIsWaitingForThread() ||
e5c0b16a 612 s_currentMsg.message != WM_COMMAND )
4a9968f9
VZ
613 {
614 s_aSavedMessages.Add(s_currentMsg);
615 }
bee503b0
VZ
616
617 return TRUE;
618 }
619 else
620 {
621 // have we just regained the GUI lock? if so, post all of the saved
622 // messages
623 //
624 // FIXME of course, it's not _exactly_ the same as processing the
625 // messages normally - expect some things to break...
626 if ( !s_hadGuiLock )
627 {
628 s_hadGuiLock = TRUE;
629
0a95f6d0 630 size_t count = s_aSavedMessages.GetCount();
bee503b0
VZ
631 for ( size_t n = 0; n < count; n++ )
632 {
633 MSG& msg = s_aSavedMessages[n];
634
0a95f6d0 635 DoMessage((WXMSG *)&msg);
bee503b0
VZ
636 }
637
638 s_aSavedMessages.Empty();
639 }
640 }
641#endif // wxUSE_THREADS
642
643 // Process the message
ed45e263 644 DoMessage((WXMSG *)&s_currentMsg);
bee503b0
VZ
645 }
646
647 return TRUE;
2bda0e17
KB
648}
649
ed45e263
VZ
650void wxApp::DoMessage(WXMSG *pMsg)
651{
652 if ( !ProcessMessage(pMsg) )
653 {
654 ::TranslateMessage((MSG *)pMsg);
655 ::DispatchMessage((MSG *)pMsg);
656 }
657}
658
2bda0e17
KB
659/*
660 * Keep trying to process messages until WM_QUIT
661 * received.
662 *
663 * If there are messages to be processed, they will all be
664 * processed and OnIdle will not be called.
665 * When there are no more messages, OnIdle is called.
666 * If OnIdle requests more time,
667 * it will be repeatedly called so long as there are no pending messages.
668 * A 'feature' of this is that once OnIdle has decided that no more processing
669 * is required, then it won't get processing time until further messages
670 * are processed (it'll sit in DoMessage).
671 */
672
bb6290e3 673int wxApp::MainLoop()
2bda0e17 674{
e5c0b16a 675 m_keepGoing = TRUE;
bee503b0 676
e5c0b16a
VZ
677 while ( m_keepGoing )
678 {
679#if wxUSE_THREADS
bee503b0 680 wxMutexGuiLeaveOrEnter();
e5c0b16a 681#endif // wxUSE_THREADS
bee503b0 682
b6c588e1
VZ
683 while ( !Pending() && ProcessIdle() )
684 ;
7214297d 685
b6c588e1 686 // a message came or no more idle processing to do
e5c0b16a
VZ
687 DoMessage();
688 }
2bda0e17 689
e5c0b16a 690 return s_currentMsg.wParam;
2bda0e17
KB
691}
692
693// Returns TRUE if more time is needed.
bb6290e3 694bool wxApp::ProcessIdle()
2bda0e17
KB
695{
696 wxIdleEvent event;
697 event.SetEventObject(this);
698 ProcessEvent(event);
699
700 return event.MoreRequested();
701}
702
bb6290e3 703void wxApp::ExitMainLoop()
2bda0e17 704{
1cbee0b4
VZ
705 // this will set m_keepGoing to FALSE a bit later
706 ::PostQuitMessage(0);
2bda0e17
KB
707}
708
bb6290e3 709bool wxApp::Pending()
2bda0e17 710{
b6c588e1 711 return ::PeekMessage(&s_currentMsg, 0, 0, 0, PM_NOREMOVE) != 0;
2bda0e17
KB
712}
713
bb6290e3 714void wxApp::Dispatch()
2bda0e17 715{
bee503b0 716 DoMessage();
2bda0e17
KB
717}
718
719/*
720 * Give all windows a chance to preprocess
721 * the message. Some may have accelerator tables, or have
722 * MDI complications.
723 */
2a47d3c1 724
d3f0a137 725bool wxApp::ProcessMessage(WXMSG *wxmsg)
2bda0e17 726{
d3f0a137 727 MSG *msg = (MSG *)wxmsg;
2a3caeb5
VZ
728 HWND hwnd = msg->hwnd;
729 wxWindow *wndThis = wxGetWindowFromHWND((WXHWND)hwnd);
2bda0e17 730
761989ff
VZ
731 // this may happen if the event occured in a standard modeless dialog (the
732 // only example of which I know of is the find/replace dialog) - then call
733 // IsDialogMessage() to make TAB navigation in it work
734 if ( !wndThis )
735 {
2a3caeb5
VZ
736 // we need to find the dialog containing this control as
737 // IsDialogMessage() just eats all the messages (i.e. returns TRUE for
738 // them) if we call it for the control itself
de7f0860 739 while ( hwnd && ::GetWindowLong(hwnd, GWL_STYLE) & WS_CHILD )
2a3caeb5
VZ
740 {
741 hwnd = ::GetParent(hwnd);
742 }
743
de7f0860 744 return hwnd && ::IsDialogMessage(hwnd, msg) != 0;
761989ff
VZ
745 }
746
8614c467
VZ
747#if wxUSE_TOOLTIPS
748 // we must relay WM_MOUSEMOVE events to the tooltip ctrl if we want it to
749 // popup the tooltip bubbles
761989ff 750 if ( (msg->message == WM_MOUSEMOVE) )
834362a2 751 {
8614c467
VZ
752 wxToolTip *tt = wndThis->GetToolTip();
753 if ( tt )
754 {
755 tt->RelayEvent(wxmsg);
756 }
834362a2 757 }
8614c467 758#endif // wxUSE_TOOLTIPS
834362a2 759
a37d422a
VZ
760 // allow the window to prevent certain messages from being
761 // translated/processed (this is currently used by wxTextCtrl to always
762 // grab Ctrl-C/V/X, even if they are also accelerators in some parent)
763 if ( !wndThis->MSWShouldPreProcessMessage(wxmsg) )
764 {
765 return FALSE;
766 }
767
2a3caeb5 768 // try translations first: the accelerators override everything
8614c467 769 wxWindow *wnd;
42bcb12b 770
d3f0a137 771 for ( wnd = wndThis; wnd; wnd = wnd->GetParent() )
2bda0e17 772 {
2a3caeb5 773 if ( wnd->MSWTranslateMessage(wxmsg))
d3f0a137 774 return TRUE;
3bdc265d
VZ
775
776 // stop at first top level window, i.e. don't try to process the key
777 // strokes originating in a dialog using the accelerators of the parent
778 // frame - this doesn't make much sense
779 if ( wnd->IsTopLevel() )
2a3caeb5
VZ
780 break;
781 }
782
a37d422a
VZ
783 // now try the other hooks (kbd navigation is handled here): we start from
784 // wndThis->GetParent() because wndThis->MSWProcessMessage() was already
785 // called above
786 for ( wnd = wndThis->GetParent(); wnd; wnd = wnd->GetParent() )
2a3caeb5
VZ
787 {
788 if ( wnd->MSWProcessMessage(wxmsg) )
789 return TRUE;
57a7b7c1 790 }
d3f0a137 791
a37d422a 792 // no special preprocessing for this message, dispatch it normally
e5c0b16a 793 return FALSE;
2bda0e17
KB
794}
795
2b5f62a0
VZ
796// this is a temporary hack and will be replaced by using wxEventLoop in the
797// future
798//
799// it is needed to allow other event loops (currently only one: the modal
800// dialog one) to reset the OnIdle() semaphore because otherwise OnIdle()
801// wouldn't do anything while a modal dialog shown from OnIdle() call is shown.
802bool wxIsInOnIdleFlag = FALSE;
803
2bda0e17
KB
804void wxApp::OnIdle(wxIdleEvent& event)
805{
3222fde2 806 // Avoid recursion (via ProcessEvent default case)
2b5f62a0 807 if ( wxIsInOnIdleFlag )
3222fde2 808 return;
2bda0e17 809
2b5f62a0 810 wxIsInOnIdleFlag = TRUE;
2bda0e17 811
52e52bea
GRG
812 // If there are pending events, we must process them: pending events
813 // are either events to the threads other than main or events posted
814 // with wxPostEvent() functions
815 // GRG: I have moved this here so that all pending events are processed
816 // before starting to delete any objects. This behaves better (in
817 // particular, wrt wxPostEvent) and is coherent with wxGTK's current
818 // behaviour. Changed Feb/2000 before 2.1.14
819 ProcessPendingEvents();
820
3222fde2
VZ
821 // 'Garbage' collection of windows deleted with Close().
822 DeletePendingObjects();
2bda0e17 823
546db2a8 824#if wxUSE_LOG
3222fde2 825 // flush the logged messages if any
2ed3265e 826 wxLog::FlushActive();
546db2a8 827#endif // wxUSE_LOG
c54f78a2 828
aef94d68
JS
829#if wxUSE_DC_CACHEING
830 // automated DC cache management: clear the cached DCs and bitmap
831 // if it's likely that the app has finished with them, that is, we
832 // get an idle event and we're not dragging anything.
4624defa 833 if (!::GetKeyState(MK_LBUTTON) && !::GetKeyState(MK_MBUTTON) && !::GetKeyState(MK_RBUTTON))
aef94d68
JS
834 wxDC::ClearCache();
835#endif // wxUSE_DC_CACHEING
836
3222fde2
VZ
837 // Send OnIdle events to all windows
838 if ( SendIdleEvents() )
839 {
840 // SendIdleEvents() returns TRUE if at least one window requested more
841 // idle events
842 event.RequestMore(TRUE);
843 }
2bda0e17 844
2b5f62a0 845 wxIsInOnIdleFlag = FALSE;
2bda0e17
KB
846}
847
848// Send idle event to all top-level windows
bb6290e3 849bool wxApp::SendIdleEvents()
2bda0e17
KB
850{
851 bool needMore = FALSE;
e146b8c8 852
f1d534df 853 wxWindowList::Node* node = wxTopLevelWindows.GetFirst();
3222fde2
VZ
854 while (node)
855 {
e146b8c8 856 wxWindow* win = node->GetData();
3222fde2 857 if (SendIdleEvents(win))
2bda0e17 858 needMore = TRUE;
e146b8c8 859 node = node->GetNext();
3222fde2
VZ
860 }
861
2bda0e17
KB
862 return needMore;
863}
864
865// Send idle event to window and all subwindows
866bool wxApp::SendIdleEvents(wxWindow* win)
867{
e5c0b16a
VZ
868 wxIdleEvent event;
869 event.SetEventObject(win);
870 win->GetEventHandler()->ProcessEvent(event);
2bda0e17 871
d162a7ee 872 bool needMore = event.MoreRequested();
2bda0e17 873
d162a7ee
VZ
874 wxWindowList::Node *node = win->GetChildren().GetFirst();
875 while ( node )
e5c0b16a 876 {
d162a7ee 877 wxWindow *win = node->GetData();
e5c0b16a
VZ
878 if (SendIdleEvents(win))
879 needMore = TRUE;
2bda0e17 880
a0f3867a 881 node = node->GetNext();
e5c0b16a 882 }
d162a7ee 883
42e69d6b 884 return needMore;
2bda0e17
KB
885}
886
e2478fde
VZ
887void wxApp::WakeUpIdle()
888{
889 // Send the top window a dummy message so idle handler processing will
890 // start up again. Doing it this way ensures that the idle handler
891 // wakes up in the right thread (see also wxWakeUpMainThread() which does
892 // the same for the main app thread only)
893 wxWindow *topWindow = wxTheApp->GetTopWindow();
894 if ( topWindow )
895 {
896 if ( !::PostMessage(GetHwndOf(topWindow), WM_NULL, 0, 0) )
897 {
898 // should never happen
899 wxLogLastError(wxT("PostMessage(WM_NULL)"));
900 }
901 }
902}
903
57c208c5 904void wxApp::OnEndSession(wxCloseEvent& WXUNUSED(event))
387a3b02
JS
905{
906 if (GetTopWindow())
907 GetTopWindow()->Close(TRUE);
908}
909
910// Default behaviour: close the application with prompts. The
911// user can veto the close, and therefore the end session.
912void wxApp::OnQueryEndSession(wxCloseEvent& event)
913{
914 if (GetTopWindow())
915 {
916 if (!GetTopWindow()->Close(!event.CanVeto()))
917 event.Veto(TRUE);
918 }
919}
920
ef094fa0
JS
921typedef struct _WXADllVersionInfo
922{
923 DWORD cbSize;
924 DWORD dwMajorVersion; // Major version
925 DWORD dwMinorVersion; // Minor version
926 DWORD dwBuildNumber; // Build number
927 DWORD dwPlatformID; // DLLVER_PLATFORM_*
928} WXADLLVERSIONINFO;
929
930typedef HRESULT (CALLBACK* WXADLLGETVERSIONPROC)(WXADLLVERSIONINFO *);
931
6d167489
VZ
932/* static */
933int wxApp::GetComCtl32Version()
934{
04ef50df
JS
935#ifdef __WXMICROWIN__
936 return 0;
937#else
6d167489 938 // cache the result
bdc72a22
VZ
939 static int s_verComCtl32 = -1;
940
941 wxCRIT_SECT_DECLARE(csComCtl32);
942 wxCRIT_SECT_LOCKER(lock, csComCtl32);
6d167489
VZ
943
944 if ( s_verComCtl32 == -1 )
945 {
bdc72a22 946 // initally assume no comctl32.dll at all
6d167489
VZ
947 s_verComCtl32 = 0;
948
bdc72a22
VZ
949 // do we have it?
950 HMODULE hModuleComCtl32 = ::GetModuleHandle(wxT("COMCTL32"));
ef094fa0
JS
951 BOOL bFreeComCtl32 = FALSE ;
952 if(!hModuleComCtl32)
953 {
954 hModuleComCtl32 = ::LoadLibrary(wxT("COMCTL32.DLL")) ;
955 if(hModuleComCtl32)
956 {
957 bFreeComCtl32 = TRUE ;
958 }
959 }
6d167489
VZ
960
961 // if so, then we can check for the version
bdc72a22 962 if ( hModuleComCtl32 )
bb6290e3 963 {
bdc72a22 964 // try to use DllGetVersion() if available in _headers_
ef094fa0 965 WXADLLGETVERSIONPROC pfnDllGetVersion = (WXADLLGETVERSIONPROC)
f6bcfd97 966 ::GetProcAddress(hModuleComCtl32, "DllGetVersion");
bdc72a22 967 if ( pfnDllGetVersion )
6d167489 968 {
ef094fa0 969 WXADLLVERSIONINFO dvi;
bdc72a22
VZ
970 dvi.cbSize = sizeof(dvi);
971
972 HRESULT hr = (*pfnDllGetVersion)(&dvi);
973 if ( FAILED(hr) )
974 {
975 wxLogApiError(_T("DllGetVersion"), hr);
976 }
977 else
978 {
979 // this is incompatible with _WIN32_IE values, but
980 // compatible with the other values returned by
981 // GetComCtl32Version()
982 s_verComCtl32 = 100*dvi.dwMajorVersion +
983 dvi.dwMinorVersion;
984 }
6d167489 985 }
bdc72a22
VZ
986 // DllGetVersion() unavailable either during compile or
987 // run-time, try to guess the version otherwise
988 if ( !s_verComCtl32 )
989 {
990 // InitCommonControlsEx is unique to 4.70 and later
991 FARPROC theProc = ::GetProcAddress
992 (
993 hModuleComCtl32,
a8ee71c7 994 "InitCommonControlsEx"
bdc72a22
VZ
995 );
996
997 if ( !theProc )
998 {
999 // not found, must be 4.00
1000 s_verComCtl32 = 400;
1001 }
1002 else
1003 {
1004 // many symbols appeared in comctl32 4.71, could use
1005 // any of them except may be DllInstall
1006 theProc = ::GetProcAddress
1007 (
1008 hModuleComCtl32,
a8ee71c7 1009 "InitializeFlatSB"
bdc72a22
VZ
1010 );
1011 if ( !theProc )
1012 {
1013 // not found, must be 4.70
1014 s_verComCtl32 = 470;
1015 }
1016 else
1017 {
1018 // found, must be 4.71
1019 s_verComCtl32 = 471;
1020 }
1021 }
6d167489 1022 }
bb6290e3 1023 }
ef094fa0
JS
1024
1025 if(bFreeComCtl32)
1026 {
1027 ::FreeLibrary(hModuleComCtl32) ;
1028 }
bb6290e3 1029 }
6d167489
VZ
1030
1031 return s_verComCtl32;
04ef50df 1032#endif
bb6290e3
JS
1033}
1034
2bda0e17 1035// Yield to incoming messages
cb2713bf 1036
8461e4c2 1037bool wxApp::Yield(bool onlyIfNeeded)
2bda0e17 1038{
8461e4c2
VZ
1039 // MT-FIXME
1040 static bool s_inYield = FALSE;
1041
e30285ab 1042#if wxUSE_LOG
2ed3265e
VZ
1043 // disable log flushing from here because a call to wxYield() shouldn't
1044 // normally result in message boxes popping up &c
1045 wxLog::Suspend();
e30285ab 1046#endif // wxUSE_LOG
2ed3265e 1047
8461e4c2
VZ
1048 if ( s_inYield )
1049 {
1050 if ( !onlyIfNeeded )
1051 {
1052 wxFAIL_MSG( wxT("wxYield called recursively" ) );
1053 }
33ac7e6f 1054
8461e4c2
VZ
1055 return FALSE;
1056 }
1057
1058 s_inYield = TRUE;
cb2713bf 1059
8e193f38
VZ
1060 // we don't want to process WM_QUIT from here - it should be processed in
1061 // the main event loop in order to stop it
e5c0b16a 1062 MSG msg;
8e193f38
VZ
1063 while ( PeekMessage(&msg, (HWND)0, 0, 0, PM_NOREMOVE) &&
1064 msg.message != WM_QUIT )
e5c0b16a 1065 {
5b615ed8
VZ
1066#if wxUSE_THREADS
1067 wxMutexGuiLeaveOrEnter();
1068#endif // wxUSE_THREADS
1069
e5c0b16a
VZ
1070 if ( !wxTheApp->DoMessage() )
1071 break;
1072 }
8e193f38 1073
8461e4c2
VZ
1074 // if there are pending events, we must process them.
1075 ProcessPendingEvents();
e5c0b16a 1076
e30285ab 1077#if wxUSE_LOG
2ed3265e
VZ
1078 // let the logs be flashed again
1079 wxLog::Resume();
e30285ab 1080#endif // wxUSE_LOG
2ed3265e 1081
8461e4c2 1082 s_inYield = FALSE;
cb2713bf 1083
e5c0b16a 1084 return TRUE;
2bda0e17 1085}
094637f6 1086