]> git.saurik.com Git - wxWidgets.git/blame - src/msw/app.cpp
moved c-runtime functions for CW to wxchar
[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
cde9f08e 122extern wxList WXDLLEXPORT wxPendingDelete;
04ef50df 123#ifndef __WXMICROWIN__
2bda0e17 124extern void wxSetKeyboardHook(bool doIt);
04ef50df 125#endif
2bda0e17 126
42e69d6b 127MSG s_currentMsg;
2bda0e17 128
193fe989
VZ
129// NB: all "NoRedraw" classes must have the same names as the "normal" classes
130// with NR suffix - wxWindow::MSWCreate() supposes this
03baf031
VZ
131const wxChar *wxCanvasClassName = wxT("wxWindowClass");
132const wxChar *wxCanvasClassNameNR = wxT("wxWindowClassNR");
2ffa221c
VZ
133const wxChar *wxMDIFrameClassName = wxT("wxMDIFrameClass");
134const wxChar *wxMDIFrameClassNameNoRedraw = wxT("wxMDIFrameClassNR");
135const wxChar *wxMDIChildFrameClassName = wxT("wxMDIChildFrameClass");
136const wxChar *wxMDIChildFrameClassNameNoRedraw = wxT("wxMDIChildFrameClassNR");
2bda0e17 137
57c208c5 138HBRUSH wxDisableButtonBrush = (HBRUSH) 0;
2bda0e17 139
94826170
VZ
140// ----------------------------------------------------------------------------
141// private functions
142// ----------------------------------------------------------------------------
2bda0e17 143
94826170 144LRESULT WXDLLEXPORT APIENTRY wxWndProc(HWND, UINT, WPARAM, LPARAM);
3b415ba4 145
e5c0b16a 146// ===========================================================================
e2478fde
VZ
147// wxGUIAppTraits implementation
148// ===========================================================================
149
150// private class which we use to pass parameters from BeforeChildWaitLoop() to
151// AfterChildWaitLoop()
152struct ChildWaitLoopData
153{
154 ChildWaitLoopData(wxWindowDisabler *wd_, wxWindow *winActive_)
155 {
156 wd = wd_;
157 winActive = winActive_;
158 }
159
160 wxWindowDisabler *wd;
161 wxWindow *winActive;
162};
163
164void *wxGUIAppTraits::BeforeChildWaitLoop()
165{
166 /*
167 We use a dirty hack here to disable all application windows (which we
168 must do because otherwise the calls to wxYield() could lead to some very
169 unexpected reentrancies in the users code) but to avoid losing
170 focus/activation entirely when the child process terminates which would
171 happen if we simply disabled everything using wxWindowDisabler. Indeed,
172 remember that Windows will never activate a disabled window and when the
173 last childs window is closed and Windows looks for a window to activate
174 all our windows are still disabled. There is no way to enable them in
175 time because we don't know when the childs windows are going to be
176 closed, so the solution we use here is to keep one special tiny frame
177 enabled all the time. Then when the child terminates it will get
178 activated and when we close it below -- after reenabling all the other
179 windows! -- the previously active window becomes activated again and
180 everything is ok.
181 */
182 wxBeginBusyCursor();
183
184 // first disable all existing windows
185 wxWindowDisabler *wd = new wxWindowDisabler;
186
187 // then create an "invisible" frame: it has minimal size, is positioned
188 // (hopefully) outside the screen and doesn't appear on the taskbar
189 wxWindow *winActive = new wxFrame
190 (
191 wxTheApp->GetTopWindow(),
192 -1,
193 _T(""),
194 wxPoint(32600, 32600),
195 wxSize(1, 1),
196 wxDEFAULT_FRAME_STYLE | wxFRAME_NO_TASKBAR
197 );
198 winActive->Show();
199
200 return new ChildWaitLoopData(wd, winActive);
201}
202
203void wxGUIAppTraits::AlwaysYield()
204{
205 wxYield();
206}
207
208void wxGUIAppTraits::AfterChildWaitLoop(void *dataOrig)
209{
210 wxEndBusyCursor();
211
212 const ChildWaitLoopData * const data = (ChildWaitLoopData *)dataOrig;
213
214 delete data->wd;
215
216 // finally delete the dummy frame and, as wd has been already destroyed and
217 // the other windows reenabled, the activation is going to return to the
218 // window which had had it before
219 data->winActive->Destroy();
220}
221
222bool wxGUIAppTraits::DoMessageFromThreadWait()
223{
224 return !wxTheApp || wxTheApp->DoMessage();
225}
226
227// ===========================================================================
228// wxApp implementation
e5c0b16a 229// ===========================================================================
589f0e3e 230
94826170
VZ
231int wxApp::m_nCmdShow = SW_SHOWNORMAL;
232
e5c0b16a 233// ---------------------------------------------------------------------------
e2478fde 234// wxWin macros
e5c0b16a
VZ
235// ---------------------------------------------------------------------------
236
f6bcfd97 237IMPLEMENT_DYNAMIC_CLASS(wxApp, wxEvtHandler)
e5c0b16a 238
f6bcfd97
BP
239BEGIN_EVENT_TABLE(wxApp, wxEvtHandler)
240 EVT_IDLE(wxApp::OnIdle)
241 EVT_END_SESSION(wxApp::OnEndSession)
242 EVT_QUERY_END_SESSION(wxApp::OnQueryEndSession)
243END_EVENT_TABLE()
e5c0b16a 244
94826170
VZ
245// class to ensure that wxAppBase::CleanUp() is called if our Initialize()
246// fails
247class wxCallBaseCleanup
248{
249public:
250 wxCallBaseCleanup(wxApp *app) : m_app(app) { }
251 ~wxCallBaseCleanup() { if ( m_app ) m_app->wxAppBase::CleanUp(); }
252
253 void Dismiss() { m_app = NULL; }
254
255private:
256 wxApp *m_app;
257};
258
e5c0b16a 259//// Initialize
05e2b077 260bool wxApp::Initialize(int& argc, wxChar **argv)
2bda0e17 261{
94826170
VZ
262 if ( !wxAppBase::Initialize(argc, argv) )
263 return false;
264
265 // ensure that base cleanup is done if we return too early
266 wxCallBaseCleanup callBaseCleanup(this);
267
f6bcfd97 268 // the first thing to do is to check if we're trying to run an Unicode
7c9955d1 269 // program under Win9x w/o MSLU emulation layer - if so, abort right now
dfc40ef3 270 // as it has no chance to work
eb5e4d9a 271#if wxUSE_UNICODE && !wxUSE_UNICODE_MSLU
f6bcfd97
BP
272 if ( wxGetOsVersion() != wxWINDOWS_NT )
273 {
274 // note that we can use MessageBoxW() as it's implemented even under
275 // Win9x - OTOH, we can't use wxGetTranslation() because the file APIs
276 // used by wxLocale are not
277 ::MessageBox
278 (
279 NULL,
eb5e4d9a 280 _T("This program uses Unicode and requires Windows NT/2000/XP.\nProgram aborted."),
f6bcfd97
BP
281 _T("wxWindows Fatal Error"),
282 MB_ICONERROR | MB_OK
283 );
284
285 return FALSE;
286 }
eb5e4d9a 287#endif // wxUSE_UNICODE && !wxUSE_UNICODE_MSLU
f6bcfd97 288
04ef50df 289#if defined(__WIN95__) && !defined(__WXMICROWIN__)
a5e0e655 290 InitCommonControls();
e5c0b16a 291#endif // __WIN95__
2bda0e17 292
8cb172b4 293#if wxUSE_OLE || wxUSE_DRAG_AND_DROP
c49245f8
VZ
294
295#ifdef __WIN16__
e5c0b16a 296 // for OLE, enlarge message queue to be as large as possible
aa0b7e1e 297 int iMsg = 96;
c49245f8
VZ
298 while (!SetMessageQueue(iMsg) && (iMsg -= 8))
299 ;
300#endif // Win16
8cb172b4 301
abad5367 302#if wxUSE_OLE
a5e0e655
VZ
303 // we need to initialize OLE library
304 if ( FAILED(::OleInitialize(NULL)) )
e5c0b16a 305 wxLogError(_("Cannot initialize OLE"));
abad5367 306#endif
1e6feb95 307
c49245f8 308#endif // wxUSE_OLE
2bda0e17 309
1f112209 310#if wxUSE_CTL3D
a5e0e655 311 if (!Ctl3dRegister(wxhInstance))
223d09f6 312 wxLogError(wxT("Cannot register CTL3D"));
2bda0e17 313
a5e0e655 314 Ctl3dAutoSubclass(wxhInstance);
1e6feb95 315#endif // wxUSE_CTL3D
2bda0e17 316
aa0b7e1e 317 RegisterWindowClasses();
2bda0e17 318
04ef50df 319#ifndef __WXMICROWIN__
aa0b7e1e 320 // Create the brush for disabling bitmap buttons
2bda0e17 321
42e69d6b 322 LOGBRUSH lb;
aa0b7e1e 323 lb.lbStyle = BS_PATTERN;
097f29c2 324 lb.lbColor = 0;
223d09f6 325 lb.lbHatch = (int)LoadBitmap( wxhInstance, wxT("wxDISABLE_BUTTON_BITMAP") );
3a5ffa81
VZ
326 if ( lb.lbHatch )
327 {
328 wxDisableButtonBrush = ::CreateBrushIndirect( & lb );
329 ::DeleteObject( (HGDIOBJ)lb.lbHatch );
330 }
331 //else: wxWindows resources are probably not linked in
04ef50df 332#endif
2bda0e17 333
aa0b7e1e 334#if wxUSE_PENWINDOWS
a5e0e655 335 wxRegisterPenWin();
aa0b7e1e 336#endif
2bda0e17 337
1bffa913 338 wxWinHandleHash = new wxWinHashTable(wxKEY_INTEGER, 100);
2bda0e17 339
6e0d9d43 340 // This is to foil optimizations in Visual C++ that throw out dummy.obj.
8cbd2bde 341 // PLEASE DO NOT ALTER THIS.
7fee680b 342#if defined(__VISUALC__) && defined(__WIN16__) && !defined(WXMAKINGDLL)
a5e0e655
VZ
343 extern char wxDummyChar;
344 if (wxDummyChar) wxDummyChar++;
aa0b7e1e 345#endif
a5e0e655 346
04ef50df 347#ifndef __WXMICROWIN__
aa0b7e1e 348 wxSetKeyboardHook(TRUE);
04ef50df 349#endif
2bda0e17 350
94826170
VZ
351 callBaseCleanup.Dismiss();
352
353 return true;
2bda0e17
KB
354}
355
42e69d6b
VZ
356// ---------------------------------------------------------------------------
357// RegisterWindowClasses
358// ---------------------------------------------------------------------------
589f0e3e 359
b782f2e0
VZ
360// TODO we should only register classes really used by the app. For this it
361// would be enough to just delay the class registration until an attempt
362// to create a window of this class is made.
bb6290e3 363bool wxApp::RegisterWindowClasses()
2bda0e17 364{
42e69d6b 365 WNDCLASS wndclass;
03baf031 366 wxZeroMemory(wndclass);
e5c0b16a 367
193fe989
VZ
368 // for each class we register one with CS_(V|H)REDRAW style and one
369 // without for windows created with wxNO_FULL_REDRAW_ON_REPAINT flag
370 static const long styleNormal = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
371 static const long styleNoRedraw = CS_DBLCLKS;
372
42e69d6b 373 // the fields which are common to all classes
e5c0b16a 374 wndclass.lpfnWndProc = (WNDPROC)wxWndProc;
e5c0b16a 375 wndclass.hInstance = wxhInstance;
42e69d6b 376 wndclass.hCursor = ::LoadCursor((HINSTANCE)NULL, IDC_ARROW);
42e69d6b
VZ
377
378 // Register the frame window class.
379 wndclass.hbrBackground = (HBRUSH)(COLOR_APPWORKSPACE + 1);
03baf031 380 wndclass.lpszClassName = wxCanvasClassName;
b782f2e0 381 wndclass.style = styleNormal;
e5c0b16a 382
42e69d6b 383 if ( !RegisterClass(&wndclass) )
e5c0b16a 384 {
f6bcfd97 385 wxLogLastError(wxT("RegisterClass(frame)"));
e5c0b16a
VZ
386 }
387
193fe989 388 // "no redraw" frame
03baf031 389 wndclass.lpszClassName = wxCanvasClassNameNR;
193fe989
VZ
390 wndclass.style = styleNoRedraw;
391
392 if ( !RegisterClass(&wndclass) )
393 {
f6bcfd97 394 wxLogLastError(wxT("RegisterClass(no redraw frame)"));
193fe989
VZ
395 }
396
e5c0b16a 397 // Register the MDI frame window class.
42e69d6b 398 wndclass.hbrBackground = (HBRUSH)NULL; // paint MDI frame ourselves
b782f2e0
VZ
399 wndclass.lpszClassName = wxMDIFrameClassName;
400 wndclass.style = styleNormal;
42e69d6b
VZ
401
402 if ( !RegisterClass(&wndclass) )
e5c0b16a 403 {
f6bcfd97 404 wxLogLastError(wxT("RegisterClass(MDI parent)"));
e5c0b16a
VZ
405 }
406
193fe989 407 // "no redraw" MDI frame
b782f2e0 408 wndclass.lpszClassName = wxMDIFrameClassNameNoRedraw;
193fe989
VZ
409 wndclass.style = styleNoRedraw;
410
411 if ( !RegisterClass(&wndclass) )
412 {
f6bcfd97 413 wxLogLastError(wxT("RegisterClass(no redraw MDI parent frame)"));
193fe989
VZ
414 }
415
e5c0b16a 416 // Register the MDI child frame window class.
42e69d6b
VZ
417 wndclass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
418 wndclass.lpszClassName = wxMDIChildFrameClassName;
b782f2e0 419 wndclass.style = styleNormal;
42e69d6b
VZ
420
421 if ( !RegisterClass(&wndclass) )
e5c0b16a 422 {
f6bcfd97 423 wxLogLastError(wxT("RegisterClass(MDI child)"));
e5c0b16a
VZ
424 }
425
193fe989
VZ
426 // "no redraw" MDI child frame
427 wndclass.lpszClassName = wxMDIChildFrameClassNameNoRedraw;
428 wndclass.style = styleNoRedraw;
429
430 if ( !RegisterClass(&wndclass) )
431 {
f6bcfd97 432 wxLogLastError(wxT("RegisterClass(no redraw MDI child)"));
193fe989
VZ
433 }
434
e5c0b16a 435 return TRUE;
2bda0e17
KB
436}
437
9787a4b6
VZ
438// ---------------------------------------------------------------------------
439// UnregisterWindowClasses
440// ---------------------------------------------------------------------------
441
442bool wxApp::UnregisterWindowClasses()
443{
444 bool retval = TRUE;
445
c67d6888 446#ifndef __WXMICROWIN__
9787a4b6 447 // MDI frame window class.
03baf031 448 if ( !::UnregisterClass(wxMDIFrameClassName, wxhInstance) )
9787a4b6
VZ
449 {
450 wxLogLastError(wxT("UnregisterClass(MDI parent)"));
451
452 retval = FALSE;
453 }
454
455 // "no redraw" MDI frame
03baf031 456 if ( !::UnregisterClass(wxMDIFrameClassNameNoRedraw, wxhInstance) )
9787a4b6
VZ
457 {
458 wxLogLastError(wxT("UnregisterClass(no redraw MDI parent frame)"));
459
460 retval = FALSE;
461 }
462
463 // MDI child frame window class.
03baf031 464 if ( !::UnregisterClass(wxMDIChildFrameClassName, wxhInstance) )
9787a4b6
VZ
465 {
466 wxLogLastError(wxT("UnregisterClass(MDI child)"));
467
468 retval = FALSE;
469 }
470
471 // "no redraw" MDI child frame
03baf031 472 if ( !::UnregisterClass(wxMDIChildFrameClassNameNoRedraw, wxhInstance) )
9787a4b6
VZ
473 {
474 wxLogLastError(wxT("UnregisterClass(no redraw MDI child)"));
475
476 retval = FALSE;
477 }
478
03baf031
VZ
479 // canvas class name
480 if ( !::UnregisterClass(wxCanvasClassName, wxhInstance) )
9787a4b6
VZ
481 {
482 wxLogLastError(wxT("UnregisterClass(canvas)"));
483
484 retval = FALSE;
485 }
486
03baf031 487 if ( !::UnregisterClass(wxCanvasClassNameNR, wxhInstance) )
9787a4b6
VZ
488 {
489 wxLogLastError(wxT("UnregisterClass(no redraw canvas)"));
490
491 retval = FALSE;
492 }
03baf031
VZ
493#endif // __WXMICROWIN__
494
9787a4b6
VZ
495 return retval;
496}
497
bb6290e3 498void wxApp::CleanUp()
2bda0e17 499{
04ef50df 500#ifndef __WXMICROWIN__
e5c0b16a 501 wxSetKeyboardHook(FALSE);
04ef50df 502#endif
2bda0e17 503
47d67540 504#if wxUSE_PENWINDOWS
e5c0b16a 505 wxCleanUpPenWin();
2bda0e17
KB
506#endif
507
e5c0b16a 508 if ( wxDisableButtonBrush )
42e69d6b 509 ::DeleteObject( wxDisableButtonBrush );
e5c0b16a
VZ
510
511#if wxUSE_OLE
512 ::OleUninitialize();
5de5db0e 513#endif
2bda0e17 514
9787a4b6
VZ
515 // for an EXE the classes are unregistered when it terminates but DLL may
516 // be loaded several times (load/unload/load) into the same process in
517 // which case the registration will fail after the first time if we don't
518 // unregister the classes now
519 UnregisterWindowClasses();
9787a4b6 520
1f112209 521#if wxUSE_CTL3D
e5c0b16a 522 Ctl3dUnregister(wxhInstance);
2bda0e17
KB
523#endif
524
1bffa913 525 delete wxWinHandleHash;
94826170 526 wxWinHandleHash = NULL;
f8a3e080 527
94826170 528 wxAppBase::CleanUp();
2bda0e17
KB
529}
530
94826170
VZ
531// ----------------------------------------------------------------------------
532// wxApp ctor/dtor
533// ----------------------------------------------------------------------------
589f0e3e 534
bb6290e3 535wxApp::wxApp()
2bda0e17 536{
e5c0b16a 537 m_printMode = wxPRINT_WINDOWS;
2bda0e17
KB
538}
539
589f0e3e
JS
540wxApp::~wxApp()
541{
94826170
VZ
542 // our cmd line arguments are allocated inside wxEntry(HINSTANCE), they
543 // don't come from main(), so we have to free them
544
545 while ( argc )
e5c0b16a 546 {
94826170
VZ
547 // m_argv elements were allocated by wxStrdup()
548 free(argv[--argc]);
e5c0b16a 549 }
94826170
VZ
550
551 // but m_argv itself -- using new[]
552 delete [] argv;
589f0e3e
JS
553}
554
bb6290e3 555bool wxApp::Initialized()
2bda0e17
KB
556{
557#ifndef _WINDLL
e5c0b16a
VZ
558 if (GetTopWindow())
559 return TRUE;
560 else
561 return FALSE;
3b415ba4 562#else // Assume initialized if DLL (no way of telling)
e5c0b16a 563 return TRUE;
2bda0e17
KB
564#endif
565}
566
567/*
568 * Get and process a message, returning FALSE if WM_QUIT
bee503b0 569 * received (and also set the flag telling the app to exit the main loop)
2bda0e17
KB
570 *
571 */
bb6290e3 572bool wxApp::DoMessage()
2bda0e17 573{
bee503b0
VZ
574 BOOL rc = ::GetMessage(&s_currentMsg, (HWND) NULL, 0, 0);
575 if ( rc == 0 )
576 {
577 // got WM_QUIT
578 m_keepGoing = FALSE;
4a9968f9 579
bee503b0
VZ
580 return FALSE;
581 }
582 else if ( rc == -1 )
583 {
584 // should never happen, but let's test for it nevertheless
f6bcfd97 585 wxLogLastError(wxT("GetMessage"));
bee503b0
VZ
586 }
587 else
588 {
589#if wxUSE_THREADS
590 wxASSERT_MSG( wxThread::IsMain(),
223d09f6 591 wxT("only the main thread can process Windows messages") );
d50b2a58 592
bee503b0
VZ
593 static bool s_hadGuiLock = TRUE;
594 static wxMsgArray s_aSavedMessages;
595
596 // if a secondary thread owns is doing GUI calls, save all messages for
597 // later processing - we can't process them right now because it will
598 // lead to recursive library calls (and we're not reentrant)
599 if ( !wxGuiOwnedByMainThread() )
600 {
601 s_hadGuiLock = FALSE;
602
4a9968f9
VZ
603 // leave out WM_COMMAND messages: too dangerous, sometimes
604 // the message will be processed twice
605 if ( !wxIsWaitingForThread() ||
e5c0b16a 606 s_currentMsg.message != WM_COMMAND )
4a9968f9
VZ
607 {
608 s_aSavedMessages.Add(s_currentMsg);
609 }
bee503b0
VZ
610
611 return TRUE;
612 }
613 else
614 {
615 // have we just regained the GUI lock? if so, post all of the saved
616 // messages
617 //
618 // FIXME of course, it's not _exactly_ the same as processing the
619 // messages normally - expect some things to break...
620 if ( !s_hadGuiLock )
621 {
622 s_hadGuiLock = TRUE;
623
0a95f6d0 624 size_t count = s_aSavedMessages.GetCount();
bee503b0
VZ
625 for ( size_t n = 0; n < count; n++ )
626 {
627 MSG& msg = s_aSavedMessages[n];
628
0a95f6d0 629 DoMessage((WXMSG *)&msg);
bee503b0
VZ
630 }
631
632 s_aSavedMessages.Empty();
633 }
634 }
635#endif // wxUSE_THREADS
636
637 // Process the message
ed45e263 638 DoMessage((WXMSG *)&s_currentMsg);
bee503b0
VZ
639 }
640
641 return TRUE;
2bda0e17
KB
642}
643
ed45e263
VZ
644void wxApp::DoMessage(WXMSG *pMsg)
645{
646 if ( !ProcessMessage(pMsg) )
647 {
648 ::TranslateMessage((MSG *)pMsg);
649 ::DispatchMessage((MSG *)pMsg);
650 }
651}
652
2bda0e17
KB
653/*
654 * Keep trying to process messages until WM_QUIT
655 * received.
656 *
657 * If there are messages to be processed, they will all be
658 * processed and OnIdle will not be called.
659 * When there are no more messages, OnIdle is called.
660 * If OnIdle requests more time,
661 * it will be repeatedly called so long as there are no pending messages.
662 * A 'feature' of this is that once OnIdle has decided that no more processing
663 * is required, then it won't get processing time until further messages
664 * are processed (it'll sit in DoMessage).
665 */
666
bb6290e3 667int wxApp::MainLoop()
2bda0e17 668{
e5c0b16a 669 m_keepGoing = TRUE;
bee503b0 670
e5c0b16a
VZ
671 while ( m_keepGoing )
672 {
673#if wxUSE_THREADS
bee503b0 674 wxMutexGuiLeaveOrEnter();
e5c0b16a 675#endif // wxUSE_THREADS
bee503b0 676
b6c588e1
VZ
677 while ( !Pending() && ProcessIdle() )
678 ;
7214297d 679
b6c588e1 680 // a message came or no more idle processing to do
e5c0b16a
VZ
681 DoMessage();
682 }
2bda0e17 683
e5c0b16a 684 return s_currentMsg.wParam;
2bda0e17
KB
685}
686
687// Returns TRUE if more time is needed.
bb6290e3 688bool wxApp::ProcessIdle()
2bda0e17
KB
689{
690 wxIdleEvent event;
691 event.SetEventObject(this);
692 ProcessEvent(event);
693
694 return event.MoreRequested();
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
3222fde2
VZ
831 // Send OnIdle events to all windows
832 if ( SendIdleEvents() )
833 {
834 // SendIdleEvents() returns TRUE if at least one window requested more
835 // idle events
836 event.RequestMore(TRUE);
837 }
2bda0e17 838
2b5f62a0 839 wxIsInOnIdleFlag = FALSE;
2bda0e17
KB
840}
841
842// Send idle event to all top-level windows
bb6290e3 843bool wxApp::SendIdleEvents()
2bda0e17
KB
844{
845 bool needMore = FALSE;
e146b8c8 846
f1d534df 847 wxWindowList::Node* node = wxTopLevelWindows.GetFirst();
3222fde2
VZ
848 while (node)
849 {
e146b8c8 850 wxWindow* win = node->GetData();
3222fde2 851 if (SendIdleEvents(win))
2bda0e17 852 needMore = TRUE;
e146b8c8 853 node = node->GetNext();
3222fde2
VZ
854 }
855
2bda0e17
KB
856 return needMore;
857}
858
859// Send idle event to window and all subwindows
860bool wxApp::SendIdleEvents(wxWindow* win)
861{
e5c0b16a
VZ
862 wxIdleEvent event;
863 event.SetEventObject(win);
864 win->GetEventHandler()->ProcessEvent(event);
2bda0e17 865
d162a7ee 866 bool needMore = event.MoreRequested();
2bda0e17 867
d162a7ee
VZ
868 wxWindowList::Node *node = win->GetChildren().GetFirst();
869 while ( node )
e5c0b16a 870 {
d162a7ee 871 wxWindow *win = node->GetData();
e5c0b16a
VZ
872 if (SendIdleEvents(win))
873 needMore = TRUE;
2bda0e17 874
a0f3867a 875 node = node->GetNext();
e5c0b16a 876 }
d162a7ee 877
42e69d6b 878 return needMore;
2bda0e17
KB
879}
880
e2478fde
VZ
881void wxApp::WakeUpIdle()
882{
883 // Send the top window a dummy message so idle handler processing will
884 // start up again. Doing it this way ensures that the idle handler
885 // wakes up in the right thread (see also wxWakeUpMainThread() which does
886 // the same for the main app thread only)
887 wxWindow *topWindow = wxTheApp->GetTopWindow();
888 if ( topWindow )
889 {
890 if ( !::PostMessage(GetHwndOf(topWindow), WM_NULL, 0, 0) )
891 {
892 // should never happen
893 wxLogLastError(wxT("PostMessage(WM_NULL)"));
894 }
895 }
896}
897
57c208c5 898void wxApp::OnEndSession(wxCloseEvent& WXUNUSED(event))
387a3b02
JS
899{
900 if (GetTopWindow())
901 GetTopWindow()->Close(TRUE);
902}
903
904// Default behaviour: close the application with prompts. The
905// user can veto the close, and therefore the end session.
906void wxApp::OnQueryEndSession(wxCloseEvent& event)
907{
908 if (GetTopWindow())
909 {
910 if (!GetTopWindow()->Close(!event.CanVeto()))
911 event.Veto(TRUE);
912 }
913}
914
ef094fa0
JS
915typedef struct _WXADllVersionInfo
916{
917 DWORD cbSize;
918 DWORD dwMajorVersion; // Major version
919 DWORD dwMinorVersion; // Minor version
920 DWORD dwBuildNumber; // Build number
921 DWORD dwPlatformID; // DLLVER_PLATFORM_*
922} WXADLLVERSIONINFO;
923
924typedef HRESULT (CALLBACK* WXADLLGETVERSIONPROC)(WXADLLVERSIONINFO *);
925
6d167489
VZ
926/* static */
927int wxApp::GetComCtl32Version()
928{
04ef50df
JS
929#ifdef __WXMICROWIN__
930 return 0;
931#else
6d167489 932 // cache the result
bdc72a22
VZ
933 static int s_verComCtl32 = -1;
934
935 wxCRIT_SECT_DECLARE(csComCtl32);
936 wxCRIT_SECT_LOCKER(lock, csComCtl32);
6d167489
VZ
937
938 if ( s_verComCtl32 == -1 )
939 {
bdc72a22 940 // initally assume no comctl32.dll at all
6d167489
VZ
941 s_verComCtl32 = 0;
942
bdc72a22
VZ
943 // do we have it?
944 HMODULE hModuleComCtl32 = ::GetModuleHandle(wxT("COMCTL32"));
ef094fa0
JS
945 BOOL bFreeComCtl32 = FALSE ;
946 if(!hModuleComCtl32)
947 {
948 hModuleComCtl32 = ::LoadLibrary(wxT("COMCTL32.DLL")) ;
949 if(hModuleComCtl32)
950 {
951 bFreeComCtl32 = TRUE ;
952 }
953 }
6d167489
VZ
954
955 // if so, then we can check for the version
bdc72a22 956 if ( hModuleComCtl32 )
bb6290e3 957 {
bdc72a22 958 // try to use DllGetVersion() if available in _headers_
ef094fa0 959 WXADLLGETVERSIONPROC pfnDllGetVersion = (WXADLLGETVERSIONPROC)
f6bcfd97 960 ::GetProcAddress(hModuleComCtl32, "DllGetVersion");
bdc72a22 961 if ( pfnDllGetVersion )
6d167489 962 {
ef094fa0 963 WXADLLVERSIONINFO dvi;
bdc72a22
VZ
964 dvi.cbSize = sizeof(dvi);
965
966 HRESULT hr = (*pfnDllGetVersion)(&dvi);
967 if ( FAILED(hr) )
968 {
969 wxLogApiError(_T("DllGetVersion"), hr);
970 }
971 else
972 {
973 // this is incompatible with _WIN32_IE values, but
974 // compatible with the other values returned by
975 // GetComCtl32Version()
976 s_verComCtl32 = 100*dvi.dwMajorVersion +
977 dvi.dwMinorVersion;
978 }
6d167489 979 }
bdc72a22
VZ
980 // DllGetVersion() unavailable either during compile or
981 // run-time, try to guess the version otherwise
982 if ( !s_verComCtl32 )
983 {
984 // InitCommonControlsEx is unique to 4.70 and later
985 FARPROC theProc = ::GetProcAddress
986 (
987 hModuleComCtl32,
a8ee71c7 988 "InitCommonControlsEx"
bdc72a22
VZ
989 );
990
991 if ( !theProc )
992 {
993 // not found, must be 4.00
994 s_verComCtl32 = 400;
995 }
996 else
997 {
998 // many symbols appeared in comctl32 4.71, could use
999 // any of them except may be DllInstall
1000 theProc = ::GetProcAddress
1001 (
1002 hModuleComCtl32,
a8ee71c7 1003 "InitializeFlatSB"
bdc72a22
VZ
1004 );
1005 if ( !theProc )
1006 {
1007 // not found, must be 4.70
1008 s_verComCtl32 = 470;
1009 }
1010 else
1011 {
1012 // found, must be 4.71
1013 s_verComCtl32 = 471;
1014 }
1015 }
6d167489 1016 }
bb6290e3 1017 }
ef094fa0
JS
1018
1019 if(bFreeComCtl32)
1020 {
1021 ::FreeLibrary(hModuleComCtl32) ;
1022 }
bb6290e3 1023 }
6d167489
VZ
1024
1025 return s_verComCtl32;
04ef50df 1026#endif
bb6290e3
JS
1027}
1028
2bda0e17 1029// Yield to incoming messages
cb2713bf 1030
8461e4c2 1031bool wxApp::Yield(bool onlyIfNeeded)
2bda0e17 1032{
8461e4c2
VZ
1033 // MT-FIXME
1034 static bool s_inYield = FALSE;
1035
e30285ab 1036#if wxUSE_LOG
2ed3265e
VZ
1037 // disable log flushing from here because a call to wxYield() shouldn't
1038 // normally result in message boxes popping up &c
1039 wxLog::Suspend();
e30285ab 1040#endif // wxUSE_LOG
2ed3265e 1041
8461e4c2
VZ
1042 if ( s_inYield )
1043 {
1044 if ( !onlyIfNeeded )
1045 {
1046 wxFAIL_MSG( wxT("wxYield called recursively" ) );
1047 }
33ac7e6f 1048
8461e4c2
VZ
1049 return FALSE;
1050 }
1051
1052 s_inYield = TRUE;
cb2713bf 1053
8e193f38
VZ
1054 // we don't want to process WM_QUIT from here - it should be processed in
1055 // the main event loop in order to stop it
e5c0b16a 1056 MSG msg;
8e193f38
VZ
1057 while ( PeekMessage(&msg, (HWND)0, 0, 0, PM_NOREMOVE) &&
1058 msg.message != WM_QUIT )
e5c0b16a 1059 {
5b615ed8
VZ
1060#if wxUSE_THREADS
1061 wxMutexGuiLeaveOrEnter();
1062#endif // wxUSE_THREADS
1063
e5c0b16a
VZ
1064 if ( !wxTheApp->DoMessage() )
1065 break;
1066 }
8e193f38 1067
8461e4c2
VZ
1068 // if there are pending events, we must process them.
1069 ProcessPendingEvents();
e5c0b16a 1070
e30285ab 1071#if wxUSE_LOG
2ed3265e
VZ
1072 // let the logs be flashed again
1073 wxLog::Resume();
e30285ab 1074#endif // wxUSE_LOG
2ed3265e 1075
8461e4c2 1076 s_inYield = FALSE;
cb2713bf 1077
e5c0b16a 1078 return TRUE;
2bda0e17 1079}
094637f6 1080