]> git.saurik.com Git - wxWidgets.git/blame - src/msw/app.cpp
Put OnIdle() back. Without deleting pending objects, frames that have
[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 113 (!defined(__MINGW32__) || wxCHECK_W32API_VERSION( 2, 0 )) && \
4676948b 114 !defined(__CYGWIN__) && !defined(__WXWINCE__)
036bc7d9
VZ
115 #include <shlwapi.h>
116#endif
117
e5c0b16a
VZ
118// ---------------------------------------------------------------------------
119// global variables
120// ---------------------------------------------------------------------------
121
cde9f08e 122extern wxList WXDLLEXPORT wxPendingDelete;
4676948b
JS
123
124#if !defined(__WXMICROWIN__) && !defined(__WXWINCE__)
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(),
fda7962d
JS
193 wxID_ANY,
194 wxEmptyString,
e2478fde
VZ
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
f07dc2e2 273 if ( wxGetOsVersion() != wxWINDOWS_NT && wxGetOsVersion() != wxWINDOWS_CE )
f6bcfd97
BP
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,
f07dc2e2 281 _T("This program uses Unicode and requires Windows NT/2000/XP/CE.\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
04ef50df 290#if defined(__WIN95__) && !defined(__WXMICROWIN__)
a5e0e655 291 InitCommonControls();
e5c0b16a 292#endif // __WIN95__
2bda0e17 293
8cb172b4 294#if wxUSE_OLE || wxUSE_DRAG_AND_DROP
c49245f8
VZ
295
296#ifdef __WIN16__
e5c0b16a 297 // for OLE, enlarge message queue to be as large as possible
aa0b7e1e 298 int iMsg = 96;
c49245f8
VZ
299 while (!SetMessageQueue(iMsg) && (iMsg -= 8))
300 ;
301#endif // Win16
8cb172b4 302
abad5367 303#if wxUSE_OLE
a5e0e655 304 // we need to initialize OLE library
f07dc2e2
JS
305#ifdef __WXWINCE__
306 if ( FAILED(::CoInitializeEx(NULL, COINIT_MULTITHREADED)) )
307 wxLogError(_("Cannot initialize OLE"));
308#else
a5e0e655 309 if ( FAILED(::OleInitialize(NULL)) )
e5c0b16a 310 wxLogError(_("Cannot initialize OLE"));
abad5367 311#endif
f07dc2e2 312#endif
1e6feb95 313
c49245f8 314#endif // wxUSE_OLE
2bda0e17 315
1f112209 316#if wxUSE_CTL3D
a5e0e655 317 if (!Ctl3dRegister(wxhInstance))
223d09f6 318 wxLogError(wxT("Cannot register CTL3D"));
2bda0e17 319
a5e0e655 320 Ctl3dAutoSubclass(wxhInstance);
1e6feb95 321#endif // wxUSE_CTL3D
2bda0e17 322
aa0b7e1e 323 RegisterWindowClasses();
2bda0e17 324
4676948b 325#if defined(__WXMICROWIN__) && !defined(__WXWINCE__)
aa0b7e1e 326 // Create the brush for disabling bitmap buttons
2bda0e17 327
42e69d6b 328 LOGBRUSH lb;
aa0b7e1e 329 lb.lbStyle = BS_PATTERN;
097f29c2 330 lb.lbColor = 0;
223d09f6 331 lb.lbHatch = (int)LoadBitmap( wxhInstance, wxT("wxDISABLE_BUTTON_BITMAP") );
3a5ffa81
VZ
332 if ( lb.lbHatch )
333 {
334 wxDisableButtonBrush = ::CreateBrushIndirect( & lb );
335 ::DeleteObject( (HGDIOBJ)lb.lbHatch );
336 }
337 //else: wxWindows resources are probably not linked in
04ef50df 338#endif
2bda0e17 339
aa0b7e1e 340#if wxUSE_PENWINDOWS
a5e0e655 341 wxRegisterPenWin();
aa0b7e1e 342#endif
2bda0e17 343
1bffa913 344 wxWinHandleHash = new wxWinHashTable(wxKEY_INTEGER, 100);
2bda0e17 345
6e0d9d43 346 // This is to foil optimizations in Visual C++ that throw out dummy.obj.
8cbd2bde 347 // PLEASE DO NOT ALTER THIS.
7fee680b 348#if defined(__VISUALC__) && defined(__WIN16__) && !defined(WXMAKINGDLL)
a5e0e655
VZ
349 extern char wxDummyChar;
350 if (wxDummyChar) wxDummyChar++;
aa0b7e1e 351#endif
a5e0e655 352
4676948b 353#if !defined(__WXMICROWIN__) && !defined(__WXWINCE__)
aa0b7e1e 354 wxSetKeyboardHook(TRUE);
04ef50df 355#endif
2bda0e17 356
94826170
VZ
357 callBaseCleanup.Dismiss();
358
359 return true;
2bda0e17
KB
360}
361
42e69d6b
VZ
362// ---------------------------------------------------------------------------
363// RegisterWindowClasses
364// ---------------------------------------------------------------------------
589f0e3e 365
b782f2e0
VZ
366// TODO we should only register classes really used by the app. For this it
367// would be enough to just delay the class registration until an attempt
368// to create a window of this class is made.
bb6290e3 369bool wxApp::RegisterWindowClasses()
2bda0e17 370{
42e69d6b 371 WNDCLASS wndclass;
03baf031 372 wxZeroMemory(wndclass);
e5c0b16a 373
193fe989
VZ
374 // for each class we register one with CS_(V|H)REDRAW style and one
375 // without for windows created with wxNO_FULL_REDRAW_ON_REPAINT flag
376 static const long styleNormal = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
377 static const long styleNoRedraw = CS_DBLCLKS;
378
42e69d6b 379 // the fields which are common to all classes
e5c0b16a 380 wndclass.lpfnWndProc = (WNDPROC)wxWndProc;
e5c0b16a 381 wndclass.hInstance = wxhInstance;
42e69d6b 382 wndclass.hCursor = ::LoadCursor((HINSTANCE)NULL, IDC_ARROW);
42e69d6b
VZ
383
384 // Register the frame window class.
385 wndclass.hbrBackground = (HBRUSH)(COLOR_APPWORKSPACE + 1);
03baf031 386 wndclass.lpszClassName = wxCanvasClassName;
b782f2e0 387 wndclass.style = styleNormal;
e5c0b16a 388
42e69d6b 389 if ( !RegisterClass(&wndclass) )
e5c0b16a 390 {
f6bcfd97 391 wxLogLastError(wxT("RegisterClass(frame)"));
e5c0b16a
VZ
392 }
393
193fe989 394 // "no redraw" frame
03baf031 395 wndclass.lpszClassName = wxCanvasClassNameNR;
193fe989
VZ
396 wndclass.style = styleNoRedraw;
397
398 if ( !RegisterClass(&wndclass) )
399 {
f6bcfd97 400 wxLogLastError(wxT("RegisterClass(no redraw frame)"));
193fe989
VZ
401 }
402
e5c0b16a 403 // Register the MDI frame window class.
42e69d6b 404 wndclass.hbrBackground = (HBRUSH)NULL; // paint MDI frame ourselves
b782f2e0
VZ
405 wndclass.lpszClassName = wxMDIFrameClassName;
406 wndclass.style = styleNormal;
42e69d6b
VZ
407
408 if ( !RegisterClass(&wndclass) )
e5c0b16a 409 {
f6bcfd97 410 wxLogLastError(wxT("RegisterClass(MDI parent)"));
e5c0b16a
VZ
411 }
412
193fe989 413 // "no redraw" MDI frame
b782f2e0 414 wndclass.lpszClassName = wxMDIFrameClassNameNoRedraw;
193fe989
VZ
415 wndclass.style = styleNoRedraw;
416
417 if ( !RegisterClass(&wndclass) )
418 {
f6bcfd97 419 wxLogLastError(wxT("RegisterClass(no redraw MDI parent frame)"));
193fe989
VZ
420 }
421
e5c0b16a 422 // Register the MDI child frame window class.
42e69d6b
VZ
423 wndclass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
424 wndclass.lpszClassName = wxMDIChildFrameClassName;
b782f2e0 425 wndclass.style = styleNormal;
42e69d6b
VZ
426
427 if ( !RegisterClass(&wndclass) )
e5c0b16a 428 {
f6bcfd97 429 wxLogLastError(wxT("RegisterClass(MDI child)"));
e5c0b16a
VZ
430 }
431
193fe989
VZ
432 // "no redraw" MDI child frame
433 wndclass.lpszClassName = wxMDIChildFrameClassNameNoRedraw;
434 wndclass.style = styleNoRedraw;
435
436 if ( !RegisterClass(&wndclass) )
437 {
f6bcfd97 438 wxLogLastError(wxT("RegisterClass(no redraw MDI child)"));
193fe989
VZ
439 }
440
e5c0b16a 441 return TRUE;
2bda0e17
KB
442}
443
9787a4b6
VZ
444// ---------------------------------------------------------------------------
445// UnregisterWindowClasses
446// ---------------------------------------------------------------------------
447
448bool wxApp::UnregisterWindowClasses()
449{
450 bool retval = TRUE;
451
c67d6888 452#ifndef __WXMICROWIN__
9787a4b6 453 // MDI frame window class.
03baf031 454 if ( !::UnregisterClass(wxMDIFrameClassName, wxhInstance) )
9787a4b6
VZ
455 {
456 wxLogLastError(wxT("UnregisterClass(MDI parent)"));
457
458 retval = FALSE;
459 }
460
461 // "no redraw" MDI frame
03baf031 462 if ( !::UnregisterClass(wxMDIFrameClassNameNoRedraw, wxhInstance) )
9787a4b6
VZ
463 {
464 wxLogLastError(wxT("UnregisterClass(no redraw MDI parent frame)"));
465
466 retval = FALSE;
467 }
468
469 // MDI child frame window class.
03baf031 470 if ( !::UnregisterClass(wxMDIChildFrameClassName, wxhInstance) )
9787a4b6
VZ
471 {
472 wxLogLastError(wxT("UnregisterClass(MDI child)"));
473
474 retval = FALSE;
475 }
476
477 // "no redraw" MDI child frame
03baf031 478 if ( !::UnregisterClass(wxMDIChildFrameClassNameNoRedraw, wxhInstance) )
9787a4b6
VZ
479 {
480 wxLogLastError(wxT("UnregisterClass(no redraw MDI child)"));
481
482 retval = FALSE;
483 }
484
03baf031
VZ
485 // canvas class name
486 if ( !::UnregisterClass(wxCanvasClassName, wxhInstance) )
9787a4b6
VZ
487 {
488 wxLogLastError(wxT("UnregisterClass(canvas)"));
489
490 retval = FALSE;
491 }
492
03baf031 493 if ( !::UnregisterClass(wxCanvasClassNameNR, wxhInstance) )
9787a4b6
VZ
494 {
495 wxLogLastError(wxT("UnregisterClass(no redraw canvas)"));
496
497 retval = FALSE;
498 }
03baf031
VZ
499#endif // __WXMICROWIN__
500
9787a4b6
VZ
501 return retval;
502}
503
bb6290e3 504void wxApp::CleanUp()
2bda0e17 505{
4676948b 506#if !defined(__WXMICROWIN__) && !defined(__WXWINCE__)
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
f07dc2e2
JS
518#ifdef __WXWINCE__
519 ::CoUninitialize();
520#else
e5c0b16a 521 ::OleUninitialize();
f07dc2e2 522#endif
5de5db0e 523#endif
2bda0e17 524
9787a4b6
VZ
525 // for an EXE the classes are unregistered when it terminates but DLL may
526 // be loaded several times (load/unload/load) into the same process in
527 // which case the registration will fail after the first time if we don't
528 // unregister the classes now
529 UnregisterWindowClasses();
9787a4b6 530
1f112209 531#if wxUSE_CTL3D
e5c0b16a 532 Ctl3dUnregister(wxhInstance);
2bda0e17
KB
533#endif
534
1bffa913 535 delete wxWinHandleHash;
94826170 536 wxWinHandleHash = NULL;
f8a3e080 537
94826170 538 wxAppBase::CleanUp();
2bda0e17
KB
539}
540
94826170
VZ
541// ----------------------------------------------------------------------------
542// wxApp ctor/dtor
543// ----------------------------------------------------------------------------
589f0e3e 544
bb6290e3 545wxApp::wxApp()
2bda0e17 546{
e5c0b16a 547 m_printMode = wxPRINT_WINDOWS;
2bda0e17
KB
548}
549
589f0e3e
JS
550wxApp::~wxApp()
551{
94826170
VZ
552 // our cmd line arguments are allocated inside wxEntry(HINSTANCE), they
553 // don't come from main(), so we have to free them
554
555 while ( argc )
e5c0b16a 556 {
94826170
VZ
557 // m_argv elements were allocated by wxStrdup()
558 free(argv[--argc]);
e5c0b16a 559 }
94826170
VZ
560
561 // but m_argv itself -- using new[]
562 delete [] argv;
589f0e3e
JS
563}
564
bb6290e3 565bool wxApp::Initialized()
2bda0e17
KB
566{
567#ifndef _WINDLL
e5c0b16a
VZ
568 if (GetTopWindow())
569 return TRUE;
570 else
571 return FALSE;
3b415ba4 572#else // Assume initialized if DLL (no way of telling)
e5c0b16a 573 return TRUE;
2bda0e17
KB
574#endif
575}
576
577/*
578 * Get and process a message, returning FALSE if WM_QUIT
bee503b0 579 * received (and also set the flag telling the app to exit the main loop)
2bda0e17
KB
580 *
581 */
bb6290e3 582bool wxApp::DoMessage()
2bda0e17 583{
bee503b0
VZ
584 BOOL rc = ::GetMessage(&s_currentMsg, (HWND) NULL, 0, 0);
585 if ( rc == 0 )
586 {
587 // got WM_QUIT
588 m_keepGoing = FALSE;
4a9968f9 589
bee503b0
VZ
590 return FALSE;
591 }
592 else if ( rc == -1 )
593 {
594 // should never happen, but let's test for it nevertheless
f6bcfd97 595 wxLogLastError(wxT("GetMessage"));
bee503b0
VZ
596 }
597 else
598 {
599#if wxUSE_THREADS
600 wxASSERT_MSG( wxThread::IsMain(),
223d09f6 601 wxT("only the main thread can process Windows messages") );
d50b2a58 602
bee503b0
VZ
603 static bool s_hadGuiLock = TRUE;
604 static wxMsgArray s_aSavedMessages;
605
606 // if a secondary thread owns is doing GUI calls, save all messages for
607 // later processing - we can't process them right now because it will
608 // lead to recursive library calls (and we're not reentrant)
609 if ( !wxGuiOwnedByMainThread() )
610 {
611 s_hadGuiLock = FALSE;
612
4a9968f9
VZ
613 // leave out WM_COMMAND messages: too dangerous, sometimes
614 // the message will be processed twice
615 if ( !wxIsWaitingForThread() ||
e5c0b16a 616 s_currentMsg.message != WM_COMMAND )
4a9968f9
VZ
617 {
618 s_aSavedMessages.Add(s_currentMsg);
619 }
bee503b0
VZ
620
621 return TRUE;
622 }
623 else
624 {
625 // have we just regained the GUI lock? if so, post all of the saved
626 // messages
627 //
628 // FIXME of course, it's not _exactly_ the same as processing the
629 // messages normally - expect some things to break...
630 if ( !s_hadGuiLock )
631 {
632 s_hadGuiLock = TRUE;
633
0a95f6d0 634 size_t count = s_aSavedMessages.GetCount();
bee503b0
VZ
635 for ( size_t n = 0; n < count; n++ )
636 {
637 MSG& msg = s_aSavedMessages[n];
638
0a95f6d0 639 DoMessage((WXMSG *)&msg);
bee503b0
VZ
640 }
641
642 s_aSavedMessages.Empty();
643 }
644 }
645#endif // wxUSE_THREADS
646
647 // Process the message
ed45e263 648 DoMessage((WXMSG *)&s_currentMsg);
bee503b0
VZ
649 }
650
651 return TRUE;
2bda0e17
KB
652}
653
ed45e263
VZ
654void wxApp::DoMessage(WXMSG *pMsg)
655{
656 if ( !ProcessMessage(pMsg) )
657 {
658 ::TranslateMessage((MSG *)pMsg);
659 ::DispatchMessage((MSG *)pMsg);
660 }
661}
662
2bda0e17
KB
663/*
664 * Keep trying to process messages until WM_QUIT
665 * received.
666 *
667 * If there are messages to be processed, they will all be
668 * processed and OnIdle will not be called.
669 * When there are no more messages, OnIdle is called.
670 * If OnIdle requests more time,
671 * it will be repeatedly called so long as there are no pending messages.
672 * A 'feature' of this is that once OnIdle has decided that no more processing
673 * is required, then it won't get processing time until further messages
674 * are processed (it'll sit in DoMessage).
675 */
676
bb6290e3 677int wxApp::MainLoop()
2bda0e17 678{
e5c0b16a 679 m_keepGoing = TRUE;
bee503b0 680
e5c0b16a
VZ
681 while ( m_keepGoing )
682 {
683#if wxUSE_THREADS
bee503b0 684 wxMutexGuiLeaveOrEnter();
e5c0b16a 685#endif // wxUSE_THREADS
bee503b0 686
b6c588e1
VZ
687 while ( !Pending() && ProcessIdle() )
688 ;
7214297d 689
b6c588e1 690 // a message came or no more idle processing to do
e5c0b16a
VZ
691 DoMessage();
692 }
2bda0e17 693
e5c0b16a 694 return s_currentMsg.wParam;
2bda0e17
KB
695}
696
bb6290e3 697void wxApp::ExitMainLoop()
2bda0e17 698{
1cbee0b4
VZ
699 // this will set m_keepGoing to FALSE a bit later
700 ::PostQuitMessage(0);
2bda0e17
KB
701}
702
bb6290e3 703bool wxApp::Pending()
2bda0e17 704{
b6c588e1 705 return ::PeekMessage(&s_currentMsg, 0, 0, 0, PM_NOREMOVE) != 0;
2bda0e17
KB
706}
707
bb6290e3 708void wxApp::Dispatch()
2bda0e17 709{
bee503b0 710 DoMessage();
2bda0e17
KB
711}
712
713/*
714 * Give all windows a chance to preprocess
715 * the message. Some may have accelerator tables, or have
716 * MDI complications.
717 */
2a47d3c1 718
d3f0a137 719bool wxApp::ProcessMessage(WXMSG *wxmsg)
2bda0e17 720{
d3f0a137 721 MSG *msg = (MSG *)wxmsg;
2a3caeb5
VZ
722 HWND hwnd = msg->hwnd;
723 wxWindow *wndThis = wxGetWindowFromHWND((WXHWND)hwnd);
2bda0e17 724
761989ff
VZ
725 // this may happen if the event occured in a standard modeless dialog (the
726 // only example of which I know of is the find/replace dialog) - then call
727 // IsDialogMessage() to make TAB navigation in it work
728 if ( !wndThis )
729 {
2a3caeb5
VZ
730 // we need to find the dialog containing this control as
731 // IsDialogMessage() just eats all the messages (i.e. returns TRUE for
732 // them) if we call it for the control itself
de7f0860 733 while ( hwnd && ::GetWindowLong(hwnd, GWL_STYLE) & WS_CHILD )
2a3caeb5
VZ
734 {
735 hwnd = ::GetParent(hwnd);
736 }
737
de7f0860 738 return hwnd && ::IsDialogMessage(hwnd, msg) != 0;
761989ff
VZ
739 }
740
8614c467
VZ
741#if wxUSE_TOOLTIPS
742 // we must relay WM_MOUSEMOVE events to the tooltip ctrl if we want it to
743 // popup the tooltip bubbles
761989ff 744 if ( (msg->message == WM_MOUSEMOVE) )
834362a2 745 {
8614c467
VZ
746 wxToolTip *tt = wndThis->GetToolTip();
747 if ( tt )
748 {
749 tt->RelayEvent(wxmsg);
750 }
834362a2 751 }
8614c467 752#endif // wxUSE_TOOLTIPS
834362a2 753
a37d422a
VZ
754 // allow the window to prevent certain messages from being
755 // translated/processed (this is currently used by wxTextCtrl to always
756 // grab Ctrl-C/V/X, even if they are also accelerators in some parent)
757 if ( !wndThis->MSWShouldPreProcessMessage(wxmsg) )
758 {
759 return FALSE;
760 }
761
2a3caeb5 762 // try translations first: the accelerators override everything
8614c467 763 wxWindow *wnd;
42bcb12b 764
d3f0a137 765 for ( wnd = wndThis; wnd; wnd = wnd->GetParent() )
2bda0e17 766 {
2a3caeb5 767 if ( wnd->MSWTranslateMessage(wxmsg))
d3f0a137 768 return TRUE;
3bdc265d
VZ
769
770 // stop at first top level window, i.e. don't try to process the key
771 // strokes originating in a dialog using the accelerators of the parent
772 // frame - this doesn't make much sense
773 if ( wnd->IsTopLevel() )
2a3caeb5
VZ
774 break;
775 }
776
a37d422a
VZ
777 // now try the other hooks (kbd navigation is handled here): we start from
778 // wndThis->GetParent() because wndThis->MSWProcessMessage() was already
779 // called above
780 for ( wnd = wndThis->GetParent(); wnd; wnd = wnd->GetParent() )
2a3caeb5
VZ
781 {
782 if ( wnd->MSWProcessMessage(wxmsg) )
783 return TRUE;
57a7b7c1 784 }
d3f0a137 785
a37d422a 786 // no special preprocessing for this message, dispatch it normally
e5c0b16a 787 return FALSE;
2bda0e17
KB
788}
789
2b5f62a0
VZ
790// this is a temporary hack and will be replaced by using wxEventLoop in the
791// future
792//
793// it is needed to allow other event loops (currently only one: the modal
794// dialog one) to reset the OnIdle() semaphore because otherwise OnIdle()
795// wouldn't do anything while a modal dialog shown from OnIdle() call is shown.
796bool wxIsInOnIdleFlag = FALSE;
797
2bda0e17
KB
798void wxApp::OnIdle(wxIdleEvent& event)
799{
3222fde2 800 // Avoid recursion (via ProcessEvent default case)
2b5f62a0 801 if ( wxIsInOnIdleFlag )
3222fde2 802 return;
2bda0e17 803
2b5f62a0 804 wxIsInOnIdleFlag = TRUE;
2bda0e17 805
52e52bea
GRG
806 // If there are pending events, we must process them: pending events
807 // are either events to the threads other than main or events posted
808 // with wxPostEvent() functions
809 // GRG: I have moved this here so that all pending events are processed
810 // before starting to delete any objects. This behaves better (in
811 // particular, wrt wxPostEvent) and is coherent with wxGTK's current
812 // behaviour. Changed Feb/2000 before 2.1.14
813 ProcessPendingEvents();
814
3222fde2
VZ
815 // 'Garbage' collection of windows deleted with Close().
816 DeletePendingObjects();
2bda0e17 817
546db2a8 818#if wxUSE_LOG
3222fde2 819 // flush the logged messages if any
2ed3265e 820 wxLog::FlushActive();
546db2a8 821#endif // wxUSE_LOG
c54f78a2 822
aef94d68
JS
823#if wxUSE_DC_CACHEING
824 // automated DC cache management: clear the cached DCs and bitmap
825 // if it's likely that the app has finished with them, that is, we
826 // get an idle event and we're not dragging anything.
4624defa 827 if (!::GetKeyState(MK_LBUTTON) && !::GetKeyState(MK_MBUTTON) && !::GetKeyState(MK_RBUTTON))
aef94d68
JS
828 wxDC::ClearCache();
829#endif // wxUSE_DC_CACHEING
830
5109ae5d
JS
831 // Now done in ProcessIdle()
832#if 0
3222fde2
VZ
833 // Send OnIdle events to all windows
834 if ( SendIdleEvents() )
835 {
836 // SendIdleEvents() returns TRUE if at least one window requested more
837 // idle events
838 event.RequestMore(TRUE);
839 }
5109ae5d 840#endif
2bda0e17 841
2b5f62a0 842 wxIsInOnIdleFlag = FALSE;
2bda0e17
KB
843}
844
e2478fde
VZ
845void wxApp::WakeUpIdle()
846{
847 // Send the top window a dummy message so idle handler processing will
848 // start up again. Doing it this way ensures that the idle handler
849 // wakes up in the right thread (see also wxWakeUpMainThread() which does
850 // the same for the main app thread only)
851 wxWindow *topWindow = wxTheApp->GetTopWindow();
852 if ( topWindow )
853 {
854 if ( !::PostMessage(GetHwndOf(topWindow), WM_NULL, 0, 0) )
855 {
856 // should never happen
857 wxLogLastError(wxT("PostMessage(WM_NULL)"));
858 }
859 }
860}
861
57c208c5 862void wxApp::OnEndSession(wxCloseEvent& WXUNUSED(event))
387a3b02
JS
863{
864 if (GetTopWindow())
865 GetTopWindow()->Close(TRUE);
866}
867
868// Default behaviour: close the application with prompts. The
869// user can veto the close, and therefore the end session.
870void wxApp::OnQueryEndSession(wxCloseEvent& event)
871{
872 if (GetTopWindow())
873 {
874 if (!GetTopWindow()->Close(!event.CanVeto()))
875 event.Veto(TRUE);
876 }
877}
878
ef094fa0
JS
879typedef struct _WXADllVersionInfo
880{
881 DWORD cbSize;
882 DWORD dwMajorVersion; // Major version
883 DWORD dwMinorVersion; // Minor version
884 DWORD dwBuildNumber; // Build number
885 DWORD dwPlatformID; // DLLVER_PLATFORM_*
886} WXADLLVERSIONINFO;
887
888typedef HRESULT (CALLBACK* WXADLLGETVERSIONPROC)(WXADLLVERSIONINFO *);
889
6d167489
VZ
890/* static */
891int wxApp::GetComCtl32Version()
892{
4676948b 893#if defined(__WXMICROWIN__) || defined(__WXWINCE__)
04ef50df
JS
894 return 0;
895#else
6d167489 896 // cache the result
bdc72a22
VZ
897 static int s_verComCtl32 = -1;
898
899 wxCRIT_SECT_DECLARE(csComCtl32);
900 wxCRIT_SECT_LOCKER(lock, csComCtl32);
6d167489
VZ
901
902 if ( s_verComCtl32 == -1 )
903 {
bdc72a22 904 // initally assume no comctl32.dll at all
6d167489
VZ
905 s_verComCtl32 = 0;
906
bdc72a22
VZ
907 // do we have it?
908 HMODULE hModuleComCtl32 = ::GetModuleHandle(wxT("COMCTL32"));
ef094fa0
JS
909 BOOL bFreeComCtl32 = FALSE ;
910 if(!hModuleComCtl32)
911 {
912 hModuleComCtl32 = ::LoadLibrary(wxT("COMCTL32.DLL")) ;
913 if(hModuleComCtl32)
914 {
915 bFreeComCtl32 = TRUE ;
916 }
917 }
6d167489
VZ
918
919 // if so, then we can check for the version
bdc72a22 920 if ( hModuleComCtl32 )
bb6290e3 921 {
bdc72a22 922 // try to use DllGetVersion() if available in _headers_
ef094fa0 923 WXADLLGETVERSIONPROC pfnDllGetVersion = (WXADLLGETVERSIONPROC)
f6bcfd97 924 ::GetProcAddress(hModuleComCtl32, "DllGetVersion");
bdc72a22 925 if ( pfnDllGetVersion )
6d167489 926 {
ef094fa0 927 WXADLLVERSIONINFO dvi;
bdc72a22
VZ
928 dvi.cbSize = sizeof(dvi);
929
930 HRESULT hr = (*pfnDllGetVersion)(&dvi);
931 if ( FAILED(hr) )
932 {
933 wxLogApiError(_T("DllGetVersion"), hr);
934 }
935 else
936 {
937 // this is incompatible with _WIN32_IE values, but
938 // compatible with the other values returned by
939 // GetComCtl32Version()
940 s_verComCtl32 = 100*dvi.dwMajorVersion +
941 dvi.dwMinorVersion;
942 }
6d167489 943 }
bdc72a22
VZ
944 // DllGetVersion() unavailable either during compile or
945 // run-time, try to guess the version otherwise
946 if ( !s_verComCtl32 )
947 {
948 // InitCommonControlsEx is unique to 4.70 and later
949 FARPROC theProc = ::GetProcAddress
950 (
951 hModuleComCtl32,
a8ee71c7 952 "InitCommonControlsEx"
bdc72a22
VZ
953 );
954
955 if ( !theProc )
956 {
957 // not found, must be 4.00
958 s_verComCtl32 = 400;
959 }
960 else
961 {
962 // many symbols appeared in comctl32 4.71, could use
963 // any of them except may be DllInstall
964 theProc = ::GetProcAddress
965 (
966 hModuleComCtl32,
a8ee71c7 967 "InitializeFlatSB"
bdc72a22
VZ
968 );
969 if ( !theProc )
970 {
971 // not found, must be 4.70
972 s_verComCtl32 = 470;
973 }
974 else
975 {
976 // found, must be 4.71
977 s_verComCtl32 = 471;
978 }
979 }
6d167489 980 }
bb6290e3 981 }
ef094fa0
JS
982
983 if(bFreeComCtl32)
984 {
985 ::FreeLibrary(hModuleComCtl32) ;
986 }
bb6290e3 987 }
6d167489
VZ
988
989 return s_verComCtl32;
04ef50df 990#endif
bb6290e3
JS
991}
992
2bda0e17 993// Yield to incoming messages
cb2713bf 994
8461e4c2 995bool wxApp::Yield(bool onlyIfNeeded)
2bda0e17 996{
8461e4c2
VZ
997 // MT-FIXME
998 static bool s_inYield = FALSE;
999
e30285ab 1000#if wxUSE_LOG
2ed3265e
VZ
1001 // disable log flushing from here because a call to wxYield() shouldn't
1002 // normally result in message boxes popping up &c
1003 wxLog::Suspend();
e30285ab 1004#endif // wxUSE_LOG
2ed3265e 1005
8461e4c2
VZ
1006 if ( s_inYield )
1007 {
1008 if ( !onlyIfNeeded )
1009 {
1010 wxFAIL_MSG( wxT("wxYield called recursively" ) );
1011 }
33ac7e6f 1012
8461e4c2
VZ
1013 return FALSE;
1014 }
1015
1016 s_inYield = TRUE;
cb2713bf 1017
8e193f38
VZ
1018 // we don't want to process WM_QUIT from here - it should be processed in
1019 // the main event loop in order to stop it
e5c0b16a 1020 MSG msg;
8e193f38
VZ
1021 while ( PeekMessage(&msg, (HWND)0, 0, 0, PM_NOREMOVE) &&
1022 msg.message != WM_QUIT )
e5c0b16a 1023 {
5b615ed8
VZ
1024#if wxUSE_THREADS
1025 wxMutexGuiLeaveOrEnter();
1026#endif // wxUSE_THREADS
1027
e5c0b16a
VZ
1028 if ( !wxTheApp->DoMessage() )
1029 break;
1030 }
8e193f38 1031
8461e4c2
VZ
1032 // if there are pending events, we must process them.
1033 ProcessPendingEvents();
e5c0b16a 1034
e30285ab 1035#if wxUSE_LOG
2ed3265e
VZ
1036 // let the logs be flashed again
1037 wxLog::Resume();
e30285ab 1038#endif // wxUSE_LOG
2ed3265e 1039
8461e4c2 1040 s_inYield = FALSE;
cb2713bf 1041
e5c0b16a 1042 return TRUE;
2bda0e17 1043}
094637f6 1044